diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power index 840f7d64..45000f0d 100644 --- a/Documentation/ABI/testing/sysfs-devices-power +++ b/Documentation/ABI/testing/sysfs-devices-power @@ -96,16 +96,26 @@ Description: is read-only. If the device is not enabled to wake up the system from sleep states, this attribute is not present. -What: /sys/devices/.../power/wakeup_hit_count -Date: September 2010 +What: /sys/devices/.../power/wakeup_abort_count +Date: February 2012 Contact: Rafael J. Wysocki Description: - The /sys/devices/.../wakeup_hit_count attribute contains the + The /sys/devices/.../wakeup_abort_count attribute contains the number of times the processing of a wakeup event associated with - the device might prevent the system from entering a sleep state. - This attribute is read-only. If the device is not enabled to - wake up the system from sleep states, this attribute is not - present. + the device might have aborted system transition into a sleep + state in progress. This attribute is read-only. If the device + is not enabled to wake up the system from sleep states, this + attribute is not present. + +What: /sys/devices/.../power/wakeup_expire_count +Date: February 2012 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../wakeup_expire_count attribute contains the + number of times a wakeup event associated with the device has + been reported with a timeout that expired. This attribute is + read-only. If the device is not enabled to wake up the system + from sleep states, this attribute is not present. What: /sys/devices/.../power/wakeup_active Date: September 2010 @@ -148,6 +158,17 @@ Description: not enabled to wake up the system from sleep states, this attribute is not present. +What: /sys/devices/.../power/wakeup_prevent_sleep_time_ms +Date: February 2012 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../wakeup_prevent_sleep_time_ms attribute + contains the total time the device has been preventing + opportunistic transitions to sleep states from occuring. + This attribute is read-only. If the device is not enabled to + wake up the system from sleep states, this attribute is not + present. + What: /sys/devices/.../power/autosuspend_delay_ms Date: September 2010 Contact: Alan Stern diff --git a/Documentation/ABI/testing/sysfs-power b/Documentation/ABI/testing/sysfs-power index b464d127..31725ffe 100644 --- a/Documentation/ABI/testing/sysfs-power +++ b/Documentation/ABI/testing/sysfs-power @@ -172,3 +172,62 @@ Description: Reading from this file will display the current value, which is set to 1 MB by default. + +What: /sys/power/autosleep +Date: April 2012 +Contact: Rafael J. Wysocki +Description: + The /sys/power/autosleep file can be written one of the strings + returned by reads from /sys/power/state. If that happens, a + work item attempting to trigger a transition of the system to + the sleep state represented by that string is queued up. This + attempt will only succeed if there are no active wakeup sources + in the system at that time. After every execution, regardless + of whether or not the attempt to put the system to sleep has + succeeded, the work item requeues itself until user space + writes "off" to /sys/power/autosleep. + + Reading from this file causes the last string successfully + written to it to be returned. + +What: /sys/power/wake_lock +Date: February 2012 +Contact: Rafael J. Wysocki +Description: + The /sys/power/wake_lock file allows user space to create + wakeup source objects and activate them on demand (if one of + those wakeup sources is active, reads from the + /sys/power/wakeup_count file block or return false). When a + string without white space is written to /sys/power/wake_lock, + it will be assumed to represent a wakeup source name. If there + is a wakeup source object with that name, it will be activated + (unless active already). Otherwise, a new wakeup source object + will be registered, assigned the given name and activated. + If a string written to /sys/power/wake_lock contains white + space, the part of the string preceding the white space will be + regarded as a wakeup source name and handled as descrived above. + The other part of the string will be regarded as a timeout (in + nanoseconds) such that the wakeup source will be automatically + deactivated after it has expired. The timeout, if present, is + set regardless of the current state of the wakeup source object + in question. + + Reads from this file return a string consisting of the names of + wakeup sources created with the help of it that are active at + the moment, separated with spaces. + + +What: /sys/power/wake_unlock +Date: February 2012 +Contact: Rafael J. Wysocki +Description: + The /sys/power/wake_unlock file allows user space to deactivate + wakeup sources created with the help of /sys/power/wake_lock. + When a string is written to /sys/power/wake_unlock, it will be + assumed to represent the name of a wakeup source to deactivate. + If a wakeup source object of that name exists and is active at + the moment, it will be deactivated. + + Reads from this file return a string consisting of the names of + wakeup sources created with the help of /sys/power/wake_lock + that are inactive at the moment, separated with spaces. diff --git a/Documentation/android.txt b/Documentation/android.txt new file mode 100644 index 00000000..72a62afd --- /dev/null +++ b/Documentation/android.txt @@ -0,0 +1,121 @@ + ============= + A N D R O I D + ============= + +Copyright (C) 2009 Google, Inc. +Written by Mike Chan + +CONTENTS: +--------- + +1. Android + 1.1 Required enabled config options + 1.2 Required disabled config options + 1.3 Recommended enabled config options +2. Contact + + +1. Android +========== + +Android (www.android.com) is an open source operating system for mobile devices. +This document describes configurations needed to run the Android framework on +top of the Linux kernel. + +To see a working defconfig look at msm_defconfig or goldfish_defconfig +which can be found at http://android.git.kernel.org in kernel/common.git +and kernel/msm.git + + +1.1 Required enabled config options +----------------------------------- +After building a standard defconfig, ensure that these options are enabled in +your .config or defconfig if they are not already. Based off the msm_defconfig. +You should keep the rest of the default options enabled in the defconfig +unless you know what you are doing. + +ANDROID_PARANOID_NETWORK +ASHMEM +CONFIG_FB_MODE_HELPERS +CONFIG_FONT_8x16 +CONFIG_FONT_8x8 +CONFIG_YAFFS_SHORT_NAMES_IN_RAM +DAB +EARLYSUSPEND +FB +FB_CFB_COPYAREA +FB_CFB_FILLRECT +FB_CFB_IMAGEBLIT +FB_DEFERRED_IO +FB_TILEBLITTING +HIGH_RES_TIMERS +INOTIFY +INOTIFY_USER +INPUT_EVDEV +INPUT_GPIO +INPUT_MISC +LEDS_CLASS +LEDS_GPIO +LOCK_KERNEL +LkOGGER +LOW_MEMORY_KILLER +MISC_DEVICES +NEW_LEDS +NO_HZ +POWER_SUPPLY +PREEMPT +RAMFS +RTC_CLASS +RTC_LIB +SWITCH +SWITCH_GPIO +TMPFS +UID_STAT +UID16 +USB_FUNCTION +USB_FUNCTION_ADB +USER_WAKELOCK +VIDEO_OUTPUT_CONTROL +WAKELOCK +YAFFS_AUTO_YAFFS2 +YAFFS_FS +YAFFS_YAFFS1 +YAFFS_YAFFS2 + + +1.2 Required disabled config options +------------------------------------ +CONFIG_YAFFS_DISABLE_LAZY_LOAD +DNOTIFY + + +1.3 Recommended enabled config options +------------------------------ +ANDROID_PMEM +ANDROID_RAM_CONSOLE +ANDROID_RAM_CONSOLE_ERROR_CORRECTION +SCHEDSTATS +DEBUG_PREEMPT +DEBUG_MUTEXES +DEBUG_SPINLOCK_SLEEP +DEBUG_INFO +FRAME_POINTER +CPU_FREQ +CPU_FREQ_TABLE +CPU_FREQ_DEFAULT_GOV_ONDEMAND +CPU_FREQ_GOV_ONDEMAND +CRC_CCITT +EMBEDDED +INPUT_TOUCHSCREEN +I2C +I2C_BOARDINFO +LOG_BUF_SHIFT=17 +SERIAL_CORE +SERIAL_CORE_CONSOLE + + +2. Contact +========== +website: http://android.git.kernel.org + +mailing-lists: android-kernel@googlegroups.com diff --git a/Documentation/cgroups/cgroups.txt b/Documentation/cgroups/cgroups.txt index 8e74980a..594ff17d 100644 --- a/Documentation/cgroups/cgroups.txt +++ b/Documentation/cgroups/cgroups.txt @@ -592,6 +592,15 @@ there are not tasks in the cgroup. If pre_destroy() returns error code, rmdir() will fail with it. From this behavior, pre_destroy() can be called multiple times against a cgroup. +int allow_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) +(cgroup_mutex held by caller) + +Called prior to moving a task into a cgroup; if the subsystem +returns an error, this will abort the attach operation. Used +to extend the permission checks - if all subsystems in a cgroup +return 0, the attach will be allowed to proceed, even if the +default permission check (root or same user) fails. + int can_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) (cgroup_mutex held by caller) diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt index c7a2eb84..b4ae5e68 100644 --- a/Documentation/cpu-freq/governors.txt +++ b/Documentation/cpu-freq/governors.txt @@ -28,6 +28,7 @@ Contents: 2.3 Userspace 2.4 Ondemand 2.5 Conservative +2.6 Interactive 3. The Governor Interface in the CPUfreq Core @@ -191,6 +192,81 @@ governor but for the opposite direction. For example when set to its default value of '20' it means that if the CPU usage needs to be below 20% between samples to have the frequency decreased. + +2.6 Interactive +--------------- + +The CPUfreq governor "interactive" is designed for latency-sensitive, +interactive workloads. This governor sets the CPU speed depending on +usage, similar to "ondemand" and "conservative" governors, but with a +different set of configurable behaviors. + +The tuneable values for this governor are: + +target_loads: CPU load values used to adjust speed to influence the +current CPU load toward that value. In general, the lower the target +load, the more often the governor will raise CPU speeds to bring load +below the target. The format is a single target load, optionally +followed by pairs of CPU speeds and CPU loads to target at or above +those speeds. Colons can be used between the speeds and associated +target loads for readability. For example: + + 85 1000000:90 1700000:99 + +targets CPU load 85% below speed 1GHz, 90% at or above 1GHz, until +1.7GHz and above, at which load 99% is targeted. If speeds are +specified these must appear in ascending order. Higher target load +values are typically specified for higher speeds, that is, target load +values also usually appear in an ascending order. The default is +target load 90% for all speeds. + +min_sample_time: The minimum amount of time to spend at the current +frequency before ramping down. Default is 80000 uS. + +hispeed_freq: An intermediate "hi speed" at which to initially ramp +when CPU load hits the value specified in go_hispeed_load. If load +stays high for the amount of time specified in above_hispeed_delay, +then speed may be bumped higher. Default is the maximum speed +allowed by the policy at governor initialization time. + +go_hispeed_load: The CPU load at which to ramp to hispeed_freq. +Default is 99%. + +above_hispeed_delay: When speed is at or above hispeed_freq, wait for +this long before raising speed in response to continued high load. +Default is 20000 uS. + +timer_rate: Sample rate for reevaluating CPU load when the CPU is not +idle. A deferrable timer is used, such that the CPU will not be woken +from idle to service this timer until something else needs to run. +(The maximum time to allow deferring this timer when not running at +minimum speed is configurable via timer_slack.) Default is 20000 uS. + +timer_slack: Maximum additional time to defer handling the governor +sampling timer beyond timer_rate when running at speeds above the +minimum. For platforms that consume additional power at idle when +CPUs are running at speeds greater than minimum, this places an upper +bound on how long the timer will be deferred prior to re-evaluating +load and dropping speed. For example, if timer_rate is 20000uS and +timer_slack is 10000uS then timers will be deferred for up to 30msec +when not at lowest speed. A value of -1 means defer timers +indefinitely at all speeds. Default is 80000 uS. + +boost: If non-zero, immediately boost speed of all CPUs to at least +hispeed_freq until zero is written to this attribute. If zero, allow +CPU speeds to drop below hispeed_freq according to load as usual. +Default is zero. + +boostpulse: On each write, immediately boost speed of all CPUs to +hispeed_freq for at least the period of time specified by +boostpulse_duration, after which speeds are allowed to drop below +hispeed_freq according to load as usual. + +boostpulse_duration: Length of time to hold CPU speed at hispeed_freq +on a write to boostpulse, before allowing speed to drop according to +load as usual. Default is 80000 uS. + + 3. The Governor Interface in the CPUfreq Core ============================================= diff --git a/Documentation/dma-buf-sharing.txt b/Documentation/dma-buf-sharing.txt index 3bbd5c51..5ff4d2b8 100644 --- a/Documentation/dma-buf-sharing.txt +++ b/Documentation/dma-buf-sharing.txt @@ -29,13 +29,6 @@ The buffer-user in memory, mapped into its own address space, so it can access the same area of memory. -*IMPORTANT*: [see https://lkml.org/lkml/2011/12/20/211 for more details] -For this first version, A buffer shared using the dma_buf sharing API: -- *may* be exported to user space using "mmap" *ONLY* by exporter, outside of - this framework. -- with this new iteration of the dma-buf api cpu access from the kernel has been - enable, see below for the details. - dma-buf operations for device dma only -------------------------------------- @@ -313,6 +306,83 @@ Access to a dma_buf from the kernel context involves three steps: enum dma_data_direction dir); +Direct Userspace Access/mmap Support +------------------------------------ + +Being able to mmap an export dma-buf buffer object has 2 main use-cases: +- CPU fallback processing in a pipeline and +- supporting existing mmap interfaces in importers. + +1. CPU fallback processing in a pipeline + + In many processing pipelines it is sometimes required that the cpu can access + the data in a dma-buf (e.g. for thumbnail creation, snapshots, ...). To avoid + the need to handle this specially in userspace frameworks for buffer sharing + it's ideal if the dma_buf fd itself can be used to access the backing storage + from userspace using mmap. + + Furthermore Android's ION framework already supports this (and is otherwise + rather similar to dma-buf from a userspace consumer side with using fds as + handles, too). So it's beneficial to support this in a similar fashion on + dma-buf to have a good transition path for existing Android userspace. + + No special interfaces, userspace simply calls mmap on the dma-buf fd. + +2. Supporting existing mmap interfaces in exporters + + Similar to the motivation for kernel cpu access it is again important that + the userspace code of a given importing subsystem can use the same interfaces + with a imported dma-buf buffer object as with a native buffer object. This is + especially important for drm where the userspace part of contemporary OpenGL, + X, and other drivers is huge, and reworking them to use a different way to + mmap a buffer rather invasive. + + The assumption in the current dma-buf interfaces is that redirecting the + initial mmap is all that's needed. A survey of some of the existing + subsystems shows that no driver seems to do any nefarious thing like syncing + up with outstanding asynchronous processing on the device or allocating + special resources at fault time. So hopefully this is good enough, since + adding interfaces to intercept pagefaults and allow pte shootdowns would + increase the complexity quite a bit. + + Interface: + int dma_buf_mmap(struct dma_buf *, struct vm_area_struct *, + unsigned long); + + If the importing subsystem simply provides a special-purpose mmap call to set + up a mapping in userspace, calling do_mmap with dma_buf->file will equally + achieve that for a dma-buf object. + +3. Implementation notes for exporters + + Because dma-buf buffers have invariant size over their lifetime, the dma-buf + core checks whether a vma is too large and rejects such mappings. The + exporter hence does not need to duplicate this check. + + Because existing importing subsystems might presume coherent mappings for + userspace, the exporter needs to set up a coherent mapping. If that's not + possible, it needs to fake coherency by manually shooting down ptes when + leaving the cpu domain and flushing caches at fault time. Note that all the + dma_buf files share the same anon inode, hence the exporter needs to replace + the dma_buf file stored in vma->vm_file with it's own if pte shootdown is + requred. This is because the kernel uses the underlying inode's address_space + for vma tracking (and hence pte tracking at shootdown time with + unmap_mapping_range). + + If the above shootdown dance turns out to be too expensive in certain + scenarios, we can extend dma-buf with a more explicit cache tracking scheme + for userspace mappings. But the current assumption is that using mmap is + always a slower path, so some inefficiencies should be acceptable. + + Exporters that shoot down mappings (for any reasons) shall not do any + synchronization at fault time with outstanding device operations. + Synchronization is an orthogonal issue to sharing the backing storage of a + buffer and hence should not be handled by dma-buf itself. This is explictly + mentioned here because many people seem to want something like this, but if + different exporters handle this differently, buffer sharing can fail in + interesting ways depending upong the exporter (if userspace starts depending + upon this implicit synchronization). + Miscellaneous notes ------------------- @@ -336,6 +406,20 @@ Miscellaneous notes the exporting driver to create a dmabuf fd must provide a way to let userspace control setting of O_CLOEXEC flag passed in to dma_buf_fd(). +- If an exporter needs to manually flush caches and hence needs to fake + coherency for mmap support, it needs to be able to zap all the ptes pointing + at the backing storage. Now linux mm needs a struct address_space associated + with the struct file stored in vma->vm_file to do that with the function + unmap_mapping_range. But the dma_buf framework only backs every dma_buf fd + with the anon_file struct file, i.e. all dma_bufs share the same file. + + Hence exporters need to setup their own file (and address_space) association + by setting vma->vm_file and adjusting vma->vm_pgoff in the dma_buf mmap + callback. In the specific case of a gem driver the exporter could use the + shmem file already provided by gem (and set vm_pgoff = 0). Exporters can then + zap ptes by unmapping the corresponding range of the struct address_space + associated with their own file. + References: [1] struct dma_buf_ops in include/linux/dma-buf.h [2] All interfaces mentioned above defined in include/linux/dma-buf.h diff --git a/Documentation/hid/uhid.txt b/Documentation/hid/uhid.txt new file mode 100644 index 00000000..4627c424 --- /dev/null +++ b/Documentation/hid/uhid.txt @@ -0,0 +1,169 @@ + UHID - User-space I/O driver support for HID subsystem + ======================================================== + +The HID subsystem needs two kinds of drivers. In this document we call them: + + 1. The "HID I/O Driver" is the driver that performs raw data I/O to the + low-level device. Internally, they register an hid_ll_driver structure with + the HID core. They perform device setup, read raw data from the device and + push it into the HID subsystem and they provide a callback so the HID + subsystem can send data to the device. + + 2. The "HID Device Driver" is the driver that parses HID reports and reacts on + them. There are generic drivers like "generic-usb" and "generic-bluetooth" + which adhere to the HID specification and provide the standardizes features. + But there may be special drivers and quirks for each non-standard device out + there. Internally, they use the hid_driver structure. + +Historically, the USB stack was the first subsystem to provide an HID I/O +Driver. However, other standards like Bluetooth have adopted the HID specs and +may provide HID I/O Drivers, too. The UHID driver allows to implement HID I/O +Drivers in user-space and feed the data into the kernel HID-subsystem. + +This allows user-space to operate on the same level as USB-HID, Bluetooth-HID +and similar. It does not provide a way to write HID Device Drivers, though. Use +hidraw for this purpose. + +There is an example user-space application in ./samples/uhid/uhid-example.c + +The UHID API +------------ + +UHID is accessed through a character misc-device. The minor-number is allocated +dynamically so you need to rely on udev (or similar) to create the device node. +This is /dev/uhid by default. + +If a new device is detected by your HID I/O Driver and you want to register this +device with the HID subsystem, then you need to open /dev/uhid once for each +device you want to register. All further communication is done by read()'ing or +write()'ing "struct uhid_event" objects. Non-blocking operations are supported +by setting O_NONBLOCK. + +struct uhid_event { + __u32 type; + union { + struct uhid_create_req create; + struct uhid_data_req data; + ... + } u; +}; + +The "type" field contains the ID of the event. Depending on the ID different +payloads are sent. You must not split a single event across multiple read()'s or +multiple write()'s. A single event must always be sent as a whole. Furthermore, +only a single event can be sent per read() or write(). Pending data is ignored. +If you want to handle multiple events in a single syscall, then use vectored +I/O with readv()/writev(). + +The first thing you should do is sending an UHID_CREATE event. This will +register the device. UHID will respond with an UHID_START event. You can now +start sending data to and reading data from UHID. However, unless UHID sends the +UHID_OPEN event, the internally attached HID Device Driver has no user attached. +That is, you might put your device asleep unless you receive the UHID_OPEN +event. If you receive the UHID_OPEN event, you should start I/O. If the last +user closes the HID device, you will receive an UHID_CLOSE event. This may be +followed by an UHID_OPEN event again and so on. There is no need to perform +reference-counting in user-space. That is, you will never receive multiple +UHID_OPEN events without an UHID_CLOSE event. The HID subsystem performs +ref-counting for you. +You may decide to ignore UHID_OPEN/UHID_CLOSE, though. I/O is allowed even +though the device may have no users. + +If you want to send data to the HID subsystem, you send an HID_INPUT event with +your raw data payload. If the kernel wants to send data to the device, you will +read an UHID_OUTPUT or UHID_OUTPUT_EV event. + +If your device disconnects, you should send an UHID_DESTROY event. This will +unregister the device. You can now send UHID_CREATE again to register a new +device. +If you close() the fd, the device is automatically unregistered and destroyed +internally. + +write() +------- +write() allows you to modify the state of the device and feed input data into +the kernel. The following types are supported: UHID_CREATE, UHID_DESTROY and +UHID_INPUT. The kernel will parse the event immediately and if the event ID is +not supported, it will return -EOPNOTSUPP. If the payload is invalid, then +-EINVAL is returned, otherwise, the amount of data that was read is returned and +the request was handled successfully. + + UHID_CREATE: + This creates the internal HID device. No I/O is possible until you send this + event to the kernel. The payload is of type struct uhid_create_req and + contains information about your device. You can start I/O now. + + UHID_DESTROY: + This destroys the internal HID device. No further I/O will be accepted. There + may still be pending messages that you can receive with read() but no further + UHID_INPUT events can be sent to the kernel. + You can create a new device by sending UHID_CREATE again. There is no need to + reopen the character device. + + UHID_INPUT: + You must send UHID_CREATE before sending input to the kernel! This event + contains a data-payload. This is the raw data that you read from your device. + The kernel will parse the HID reports and react on it. + + UHID_FEATURE_ANSWER: + If you receive a UHID_FEATURE request you must answer with this request. You + must copy the "id" field from the request into the answer. Set the "err" field + to 0 if no error occured or to EIO if an I/O error occurred. + If "err" is 0 then you should fill the buffer of the answer with the results + of the feature request and set "size" correspondingly. + +read() +------ +read() will return a queued ouput report. These output reports can be of type +UHID_START, UHID_STOP, UHID_OPEN, UHID_CLOSE, UHID_OUTPUT or UHID_OUTPUT_EV. No +reaction is required to any of them but you should handle them according to your +needs. Only UHID_OUTPUT and UHID_OUTPUT_EV have payloads. + + UHID_START: + This is sent when the HID device is started. Consider this as an answer to + UHID_CREATE. This is always the first event that is sent. + + UHID_STOP: + This is sent when the HID device is stopped. Consider this as an answer to + UHID_DESTROY. + If the kernel HID device driver closes the device manually (that is, you + didn't send UHID_DESTROY) then you should consider this device closed and send + an UHID_DESTROY event. You may want to reregister your device, though. This is + always the last message that is sent to you unless you reopen the device with + UHID_CREATE. + + UHID_OPEN: + This is sent when the HID device is opened. That is, the data that the HID + device provides is read by some other process. You may ignore this event but + it is useful for power-management. As long as you haven't received this event + there is actually no other process that reads your data so there is no need to + send UHID_INPUT events to the kernel. + + UHID_CLOSE: + This is sent when there are no more processes which read the HID data. It is + the counterpart of UHID_OPEN and you may as well ignore this event. + + UHID_OUTPUT: + This is sent if the HID device driver wants to send raw data to the I/O + device. You should read the payload and forward it to the device. The payload + is of type "struct uhid_data_req". + This may be received even though you haven't received UHID_OPEN, yet. + + UHID_OUTPUT_EV: + Same as UHID_OUTPUT but this contains a "struct input_event" as payload. This + is called for force-feedback, LED or similar events which are received through + an input device by the HID subsystem. You should convert this into raw reports + and send them to your device similar to events of type UHID_OUTPUT. + + UHID_FEATURE: + This event is sent if the kernel driver wants to perform a feature request as + described in the HID specs. The report-type and report-number are available in + the payload. + The kernel serializes feature requests so there will never be two in parallel. + However, if you fail to respond with a UHID_FEATURE_ANSWER in a time-span of 5 + seconds, then the requests will be dropped and a new one might be sent. + Therefore, the payload also contains an "id" field that identifies every + request. + +Document by: + David Herrmann diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 753d18ae..693037fb 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2377,6 +2377,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted. resume= [SWSUSP] Specify the partition device for software suspend + Format: + {/dev/ | PARTUUID= | : | } resume_offset= [SWSUSP] Specify the offset from the beginning of the partition diff --git a/Documentation/power/suspend-and-cpuhotplug.txt b/Documentation/power/suspend-and-cpuhotplug.txt index f28f9a6f..e13dafc8 100644 --- a/Documentation/power/suspend-and-cpuhotplug.txt +++ b/Documentation/power/suspend-and-cpuhotplug.txt @@ -29,7 +29,7 @@ More details follow: Write 'mem' to /sys/power/state - syfs file + sysfs file | v Acquire pm_mutex lock diff --git a/Documentation/sync.txt b/Documentation/sync.txt new file mode 100644 index 00000000..a2d05e7f --- /dev/null +++ b/Documentation/sync.txt @@ -0,0 +1,75 @@ +Motivation: + +In complicated DMA pipelines such as graphics (multimedia, camera, gpu, display) +a consumer of a buffer needs to know when the producer has finished producing +it. Likewise the producer needs to know when the consumer is finished with the +buffer so it can reuse it. A particular buffer may be consumed by multiple +consumers which will retain the buffer for different amounts of time. In +addition, a consumer may consume multiple buffers atomically. +The sync framework adds an API which allows synchronization between the +producers and consumers in a generic way while also allowing platforms which +have shared hardware synchronization primitives to exploit them. + +Goals: + * provide a generic API for expressing synchronization dependencies + * allow drivers to exploit hardware synchronization between hardware + blocks + * provide a userspace API that allows a compositor to manage + dependencies. + * provide rich telemetry data to allow debugging slowdowns and stalls of + the graphics pipeline. + +Objects: + * sync_timeline + * sync_pt + * sync_fence + +sync_timeline: + +A sync_timeline is an abstract monotonically increasing counter. In general, +each driver/hardware block context will have one of these. They can be backed +by the appropriate hardware or rely on the generic sw_sync implementation. +Timelines are only ever created through their specific implementations +(i.e. sw_sync.) + +sync_pt: + +A sync_pt is an abstract value which marks a point on a sync_timeline. Sync_pts +have a single timeline parent. They have 3 states: active, signaled, and error. +They start in active state and transition, once, to either signaled (when the +timeline counter advances beyond the sync_pt’s value) or error state. + +sync_fence: + +Sync_fences are the primary primitives used by drivers to coordinate +synchronization of their buffers. They are a collection of sync_pts which may +or may not have the same timeline parent. A sync_pt can only exist in one fence +and the fence's list of sync_pts is immutable once created. Fences can be +waited on synchronously or asynchronously. Two fences can also be merged to +create a third fence containing a copy of the two fences’ sync_pts. Fences are +backed by file descriptors to allow userspace to coordinate the display pipeline +dependencies. + +Use: + +A driver implementing sync support should have a work submission function which: + * takes a fence argument specifying when to begin work + * asynchronously queues that work to kick off when the fence is signaled + * returns a fence to indicate when its work will be done. + * signals the returned fence once the work is completed. + +Consider an imaginary display driver that has the following API: +/* + * assumes buf is ready to be displayed. + * blocks until the buffer is on screen. + */ + void display_buffer(struct dma_buf *buf); + +The new API will become: +/* + * will display buf when fence is signaled. + * returns immediately with a fence that will signal when buf + * is no longer displayed. + */ +struct sync_fence* display_buffer(struct dma_buf *buf, + struct sync_fence *fence); diff --git a/Documentation/trace/ftrace.txt b/Documentation/trace/ftrace.txt index 6f51fed4..a5c45d7b 100644 --- a/Documentation/trace/ftrace.txt +++ b/Documentation/trace/ftrace.txt @@ -1458,6 +1458,35 @@ will produce: 1) 1.449 us | } +You can disable the hierarchical function call formatting and instead print a +flat list of function entry and return events. This uses the format described +in the Output Formatting section and respects all the trace options that +control that formatting. Hierarchical formatting is the default. + + hierachical: echo nofuncgraph-flat > trace_options + flat: echo funcgraph-flat > trace_options + + ie: + + # tracer: function_graph + # + # entries-in-buffer/entries-written: 68355/68355 #P:2 + # + # _-----=> irqs-off + # / _----=> need-resched + # | / _---=> hardirq/softirq + # || / _--=> preempt-depth + # ||| / delay + # TASK-PID CPU# |||| TIMESTAMP FUNCTION + # | | | |||| | | + sh-1806 [001] d... 198.843443: graph_ent: func=_raw_spin_lock + sh-1806 [001] d... 198.843445: graph_ent: func=__raw_spin_lock + sh-1806 [001] d..1 198.843447: graph_ret: func=__raw_spin_lock + sh-1806 [001] d..1 198.843449: graph_ret: func=_raw_spin_lock + sh-1806 [001] d..1 198.843451: graph_ent: func=_raw_spin_unlock_irqrestore + sh-1806 [001] d... 198.843453: graph_ret: func=_raw_spin_unlock_irqrestore + + You might find other useful features for this tracer in the following "dynamic ftrace" section such as tracing only specific functions or tasks. diff --git a/MAINTAINERS b/MAINTAINERS index c744d9c6..c5b99af4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6855,6 +6855,13 @@ S: Maintained F: Documentation/filesystems/ufs.txt F: fs/ufs/ +UHID USERSPACE HID IO DRIVER: +M: David Herrmann +L: linux-input@vger.kernel.org +S: Maintained +F: drivers/hid/uhid.c +F: include/linux/uhid.h + ULTRA-WIDEBAND (UWB) SUBSYSTEM: L: linux-usb@vger.kernel.org S: Orphan diff --git a/Makefile b/Makefile index 282e8da3..dc06db13 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,9 @@ VERSION = 3 PATCHLEVEL = 4 SUBLEVEL = 35 -EXTRAVERSION = NAME = Saber-toothed Squirrel +ANYKA_VERSION = 1.1.14 +EXTRAVERSION = # *DOCUMENTATION* # To see a list of typical targets execute "make help" @@ -132,6 +133,36 @@ sub-make: FORCE KBUILD_EXTMOD="$(KBUILD_EXTMOD)" -f $(CURDIR)/Makefile \ $(filter-out _all sub-make,$(MAKECMDGOALS)) + +# cdh:add +myPATH := $(KBUILD_OUTPUT)/lib +myFILE := "libpartition.a" +myFILE_SOURCE := "lib/libpartition.a" +$(shell if [ ! -d $(myPATH) ]; then\ + mkdir -p $(myPATH); fi) +$(shell cp -f $(myFILE_SOURCE) $(myPATH)) + +aecPATH := $(KBUILD_OUTPUT)/lib +aecFILE := "libakecho.a" +aecFILE_SOURCE := "lib/libakecho.a" +$(shell if [ ! -d $(aecPATH) ]; then\ + mkdir -p $(aecPATH); fi) +$(shell cp -f $(aecFILE_SOURCE) $(aecPATH)) + +filterPATH := $(KBUILD_OUTPUT)/lib +filterFILE := "libakaudiofilter_kern.a" +filterFILE_SOURCE := "lib/libakaudiofilter_kern.a" +$(shell if [ ! -d $(filterPATH) ]; then\ + mkdir -p $(filterPATH); fi) +$(shell cp -f $(filterFILE_SOURCE) $(filterPATH)) + +ispdrvPATH := $(KBUILD_OUTPUT)/lib +ispdrvFILE := "libispdrv.a" +ispdrvFILE_SOURCE := "lib/libispdrv.a" +$(shell if [ ! -d $(ispdrvPATH) ]; then\ + mkdir -p $(ispdrvPATH); fi) +$(shell cp -f $(ispdrvFILE_SOURCE) $(ispdrvPATH)) + # Leave processing to above invocation of make skip-makefile := 1 endif # ifneq ($(KBUILD_OUTPUT),) @@ -192,8 +223,11 @@ SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ # Default value for CROSS_COMPILE is not to prefix executables # Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile export KBUILD_BUILDHOST := $(SUBARCH) -ARCH ?= $(SUBARCH) -CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%) +# ARCH ?= $(SUBARCH) +# CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%) + +ARCH ?= arm +CROSS_COMPILE ?= arm-anykav200-linux-uclibcgnueabi- # Architecture as present in compile.h UTS_MACHINE := $(ARCH) @@ -350,7 +384,7 @@ CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \ CFLAGS_MODULE = AFLAGS_MODULE = LDFLAGS_MODULE = -CFLAGS_KERNEL = +CFLAGS_KERNEL = -DANYKA_VERSION=$(ANYKA_VERSION) AFLAGS_KERNEL = CFLAGS_GCOV = -fprofile-arcs -ftest-coverage @@ -725,7 +759,7 @@ drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y)) net-y := $(patsubst %/, %/built-in.o, $(net-y)) libs-y1 := $(patsubst %/, %/lib.a, $(libs-y)) libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y)) -libs-y := $(libs-y1) $(libs-y2) +libs-y := $(libs-y1) $(libs-y2) lib/libpartition.a lib/libakecho.a lib/libispdrv.a lib/libakaudiofilter_kern.a # Build vmlinux # --------------------------------------------------------------------------- @@ -1409,7 +1443,7 @@ clean: $(clean-dirs) $(call cmd,rmdirs) $(call cmd,rmfiles) @find $(if $(KBUILD_EXTMOD), $(KBUILD_EXTMOD), .) $(RCS_FIND_IGNORE) \ - \( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \ + \( -name '*.[os]' -o -name 'lib.a' -o -name '*.ko' -o -name '.*.cmd' \ -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \ -o -name '*.symtypes' -o -name 'modules.order' \ -o -name modules.builtin -o -name '.tmp_*.o.*' \ diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 7fe19a38..5f368142 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -233,14 +233,6 @@ config NEED_MACH_MEMORY_H definitions for this platform. The need for mach/memory.h should be avoided when possible. -config PHYS_OFFSET - hex "Physical address of main memory" if MMU - depends on !ARM_PATCH_PHYS_VIRT && !NEED_MACH_MEMORY_H - default DRAM_BASE if !MMU - help - Please provide the physical address corresponding to the - location of main memory in your system. - config GENERIC_BUG def_bool y depends on BUG @@ -332,684 +324,19 @@ config ARCH_VEXPRESS help This enables support for the ARM Ltd Versatile Express boards. -config ARCH_AT91 - bool "Atmel AT91" - select ARCH_REQUIRE_GPIOLIB - select HAVE_CLK - select CLKDEV_LOOKUP - select IRQ_DOMAIN - select NEED_MACH_IO_H if PCCARD - help - This enables support for systems based on the Atmel AT91RM9200, - AT91SAM9 processors. - -config ARCH_BCMRING - bool "Broadcom BCMRING" - depends on MMU - select CPU_V6 - select ARM_AMBA - select ARM_TIMER_SP804 - select CLKDEV_LOOKUP - select GENERIC_CLOCKEVENTS - select ARCH_WANT_OPTIONAL_GPIOLIB - help - Support for Broadcom's BCMRing platform. - -config ARCH_HIGHBANK - bool "Calxeda Highbank-based" - select ARCH_WANT_OPTIONAL_GPIOLIB - select ARM_AMBA - select ARM_GIC - select ARM_TIMER_SP804 - select CACHE_L2X0 - select CLKDEV_LOOKUP - select CPU_V7 - select GENERIC_CLOCKEVENTS - select HAVE_ARM_SCU - select HAVE_SMP - select SPARSE_IRQ - select USE_OF - help - Support for the Calxeda Highbank SoC based boards. - -config ARCH_CLPS711X - bool "Cirrus Logic CLPS711x/EP721x-based" - select CPU_ARM720T - select ARCH_USES_GETTIMEOFFSET - select NEED_MACH_MEMORY_H - help - Support for Cirrus Logic 711x/721x based boards. - -config ARCH_CNS3XXX - bool "Cavium Networks CNS3XXX family" - select CPU_V6K - select GENERIC_CLOCKEVENTS - select ARM_GIC - select MIGHT_HAVE_CACHE_L2X0 - select MIGHT_HAVE_PCI - select PCI_DOMAINS if PCI - help - Support for Cavium Networks CNS3XXX platform. - -config ARCH_GEMINI - bool "Cortina Systems Gemini" - select CPU_FA526 - select ARCH_REQUIRE_GPIOLIB - select ARCH_USES_GETTIMEOFFSET - help - Support for the Cortina Systems Gemini family SoCs - -config ARCH_PRIMA2 - bool "CSR SiRFSoC PRIMA2 ARM Cortex A9 Platform" - select CPU_V7 - select NO_IOPORT - select GENERIC_CLOCKEVENTS - select CLKDEV_LOOKUP - select GENERIC_IRQ_CHIP - select MIGHT_HAVE_CACHE_L2X0 - select USE_OF - select ZONE_DMA - help - Support for CSR SiRFSoC ARM Cortex A9 Platform - -config ARCH_EBSA110 - bool "EBSA-110" - select CPU_SA110 - select ISA - select NO_IOPORT - select ARCH_USES_GETTIMEOFFSET - select NEED_MACH_IO_H - select NEED_MACH_MEMORY_H - help - This is an evaluation board for the StrongARM processor available - from Digital. It has limited hardware on-board, including an - Ethernet interface, two PCMCIA sockets, two serial ports and a - parallel port. - -config ARCH_EP93XX - bool "EP93xx-based" - select CPU_ARM920T - select ARM_AMBA - select ARM_VIC - select CLKDEV_LOOKUP - select ARCH_REQUIRE_GPIOLIB - select ARCH_HAS_HOLES_MEMORYMODEL - select ARCH_USES_GETTIMEOFFSET - select NEED_MACH_MEMORY_H - help - This enables support for the Cirrus EP93xx series of CPUs. - -config ARCH_FOOTBRIDGE - bool "FootBridge" - select CPU_SA110 - select FOOTBRIDGE - select GENERIC_CLOCKEVENTS - select HAVE_IDE - select NEED_MACH_IO_H - select NEED_MACH_MEMORY_H - help - Support for systems based on the DC21285 companion chip - ("FootBridge"), such as the Simtec CATS and the Rebel NetWinder. - -config ARCH_MXC - bool "Freescale MXC/iMX-based" - select GENERIC_CLOCKEVENTS - select ARCH_REQUIRE_GPIOLIB - select CLKDEV_LOOKUP - select CLKSRC_MMIO - select GENERIC_IRQ_CHIP - select MULTI_IRQ_HANDLER - help - Support for Freescale MXC/iMX-based family of processors - -config ARCH_MXS - bool "Freescale MXS-based" - select GENERIC_CLOCKEVENTS - select ARCH_REQUIRE_GPIOLIB - select CLKDEV_LOOKUP - select CLKSRC_MMIO - select HAVE_CLK_PREPARE - help - Support for Freescale MXS-based family of processors - -config ARCH_NETX - bool "Hilscher NetX based" - select CLKSRC_MMIO - select CPU_ARM926T - select ARM_VIC - select GENERIC_CLOCKEVENTS - help - This enables support for systems based on the Hilscher NetX Soc - -config ARCH_H720X - bool "Hynix HMS720x-based" - select CPU_ARM720T - select ISA_DMA_API - select ARCH_USES_GETTIMEOFFSET - help - This enables support for systems based on the Hynix HMS720x - -config ARCH_IOP13XX - bool "IOP13xx-based" - depends on MMU - select CPU_XSC3 - select PLAT_IOP - select PCI - select ARCH_SUPPORTS_MSI - select VMSPLIT_1G - select NEED_MACH_IO_H - select NEED_MACH_MEMORY_H - select NEED_RET_TO_USER - help - Support for Intel's IOP13XX (XScale) family of processors. - -config ARCH_IOP32X - bool "IOP32x-based" - depends on MMU - select CPU_XSCALE - select NEED_MACH_IO_H - select NEED_RET_TO_USER - select PLAT_IOP - select PCI - select ARCH_REQUIRE_GPIOLIB - help - Support for Intel's 80219 and IOP32X (XScale) family of - processors. - -config ARCH_IOP33X - bool "IOP33x-based" - depends on MMU - select CPU_XSCALE - select NEED_MACH_IO_H - select NEED_RET_TO_USER - select PLAT_IOP - select PCI - select ARCH_REQUIRE_GPIOLIB - help - Support for Intel's IOP33X (XScale) family of processors. - -config ARCH_IXP23XX - bool "IXP23XX-based" - depends on MMU - select CPU_XSC3 - select PCI - select ARCH_USES_GETTIMEOFFSET - select NEED_MACH_IO_H - select NEED_MACH_MEMORY_H - help - Support for Intel's IXP23xx (XScale) family of processors. - -config ARCH_IXP2000 - bool "IXP2400/2800-based" - depends on MMU - select CPU_XSCALE - select PCI - select ARCH_USES_GETTIMEOFFSET - select NEED_MACH_IO_H - select NEED_MACH_MEMORY_H - help - Support for Intel's IXP2400/2800 (XScale) family of processors. - -config ARCH_IXP4XX - bool "IXP4xx-based" - depends on MMU - select ARCH_HAS_DMA_SET_COHERENT_MASK - select CLKSRC_MMIO - select CPU_XSCALE - select ARCH_REQUIRE_GPIOLIB - select GENERIC_CLOCKEVENTS - select MIGHT_HAVE_PCI - select NEED_MACH_IO_H - select DMABOUNCE if PCI - help - Support for Intel's IXP4XX (XScale) family of processors. - -config ARCH_DOVE - bool "Marvell Dove" - select CPU_V7 - select PCI - select ARCH_REQUIRE_GPIOLIB - select GENERIC_CLOCKEVENTS - select NEED_MACH_IO_H - select PLAT_ORION - help - Support for the Marvell Dove SoC 88AP510 - -config ARCH_KIRKWOOD - bool "Marvell Kirkwood" - select CPU_FEROCEON - select PCI - select PCI_QUIRKS - select ARCH_REQUIRE_GPIOLIB - select GENERIC_CLOCKEVENTS - select NEED_MACH_IO_H - select PLAT_ORION - help - Support for the following Marvell Kirkwood series SoCs: - 88F6180, 88F6192 and 88F6281. - -config ARCH_LPC32XX - bool "NXP LPC32XX" - select CLKSRC_MMIO +config ARCH_AK39 + bool "ANYKA AK39XX" select CPU_ARM926T - select ARCH_REQUIRE_GPIOLIB - select HAVE_IDE - select ARM_AMBA - select USB_ARCH_HAS_OHCI - select CLKDEV_LOOKUP - select GENERIC_CLOCKEVENTS - help - Support for the NXP LPC32XX family of processors - -config ARCH_MV78XX0 - bool "Marvell MV78xx0" - select CPU_FEROCEON - select PCI - select ARCH_REQUIRE_GPIOLIB - select GENERIC_CLOCKEVENTS - select NEED_MACH_IO_H - select PLAT_ORION - help - Support for the following Marvell MV78xx0 series SoCs: - MV781x0, MV782x0. - -config ARCH_ORION5X - bool "Marvell Orion" - depends on MMU - select CPU_FEROCEON - select PCI - select ARCH_REQUIRE_GPIOLIB - select GENERIC_CLOCKEVENTS - select PLAT_ORION - help - Support for the following Marvell Orion 5x series SoCs: - Orion-1 (5181), Orion-VoIP (5181L), Orion-NAS (5182), - Orion-2 (5281), Orion-1-90 (6183). - -config ARCH_MMP - bool "Marvell PXA168/910/MMP2" - depends on MMU - select ARCH_REQUIRE_GPIOLIB - select CLKDEV_LOOKUP - select GENERIC_CLOCKEVENTS - select GPIO_PXA - select TICK_ONESHOT - select PLAT_PXA - select SPARSE_IRQ - select GENERIC_ALLOCATOR - help - Support for Marvell's PXA168/PXA910(MMP) and MMP2 processor line. - -config ARCH_KS8695 - bool "Micrel/Kendin KS8695" - select CPU_ARM922T - select ARCH_REQUIRE_GPIOLIB - select ARCH_USES_GETTIMEOFFSET - select NEED_MACH_MEMORY_H - help - Support for Micrel/Kendin KS8695 "Centaur" (ARM922T) based - System-on-Chip devices. - -config ARCH_W90X900 - bool "Nuvoton W90X900 CPU" - select CPU_ARM926T - select ARCH_REQUIRE_GPIOLIB - select CLKDEV_LOOKUP - select CLKSRC_MMIO - select GENERIC_CLOCKEVENTS - help - Support for Nuvoton (Winbond logic dept.) ARM9 processor, - At present, the w90x900 has been renamed nuc900, regarding - the ARM series product line, you can login the following - link address to know more. - - - -config ARCH_TEGRA - bool "NVIDIA Tegra" - select CLKDEV_LOOKUP - select CLKSRC_MMIO - select GENERIC_CLOCKEVENTS select GENERIC_GPIO - select HAVE_CLK - select HAVE_SMP - select MIGHT_HAVE_CACHE_L2X0 - select NEED_MACH_IO_H if PCI - select ARCH_HAS_CPUFREQ - help - This enables support for NVIDIA Tegra based systems (Tegra APX, - Tegra 6xx and Tegra 2 series). - -config ARCH_PICOXCELL - bool "Picochip picoXcell" select ARCH_REQUIRE_GPIOLIB - select ARM_PATCH_PHYS_VIRT - select ARM_VIC - select CPU_V6K - select DW_APB_TIMER - select GENERIC_CLOCKEVENTS - select GENERIC_GPIO - select HAVE_TCM - select NO_IOPORT - select SPARSE_IRQ - select USE_OF - help - This enables support for systems based on the Picochip picoXcell - family of Femtocell devices. The picoxcell support requires device tree - for all boards. - -config ARCH_PNX4008 - bool "Philips Nexperia PNX4008 Mobile" - select CPU_ARM926T select CLKDEV_LOOKUP - select ARCH_USES_GETTIMEOFFSET - help - This enables support for Philips PNX4008 mobile platform. - -config ARCH_PXA - bool "PXA2xx/PXA3xx-based" - depends on MMU - select ARCH_MTD_XIP +# select WIRELESS_EXT +# select ARCH_USES_GETTIMEOFFSET select ARCH_HAS_CPUFREQ - select CLKDEV_LOOKUP - select CLKSRC_MMIO - select ARCH_REQUIRE_GPIOLIB - select GENERIC_CLOCKEVENTS - select GPIO_PXA - select TICK_ONESHOT - select PLAT_PXA - select SPARSE_IRQ - select AUTO_ZRELADDR - select MULTI_IRQ_HANDLER - select ARM_CPU_SUSPEND if PM - select HAVE_IDE - help - Support for Intel/Marvell's PXA2xx/PXA3xx processor line. - -config ARCH_MSM - bool "Qualcomm MSM" - select HAVE_CLK select GENERIC_CLOCKEVENTS - select ARCH_REQUIRE_GPIOLIB - select CLKDEV_LOOKUP - help - Support for Qualcomm MSM/QSD based systems. This runs on the - apps processor of the MSM/QSD and depends on a shared memory - interface to the modem processor which runs the baseband - stack and controls some vital subsystems - (clock and power control, etc). - -config ARCH_SHMOBILE - bool "Renesas SH-Mobile / R-Mobile" - select HAVE_CLK - select CLKDEV_LOOKUP - select HAVE_MACH_CLKDEV - select HAVE_SMP - select GENERIC_CLOCKEVENTS - select MIGHT_HAVE_CACHE_L2X0 - select NO_IOPORT - select SPARSE_IRQ - select MULTI_IRQ_HANDLER - select PM_GENERIC_DOMAINS if PM - select NEED_MACH_MEMORY_H help - Support for Renesas's SH-Mobile and R-Mobile ARM platforms. + Support for Anyka AK39xx series Chips platform. -config ARCH_RPC - bool "RiscPC" - select ARCH_ACORN - select FIQ - select ARCH_MAY_HAVE_PC_FDC - select HAVE_PATA_PLATFORM - select ISA_DMA_API - select NO_IOPORT - select ARCH_SPARSEMEM_ENABLE - select ARCH_USES_GETTIMEOFFSET - select HAVE_IDE - select NEED_MACH_IO_H - select NEED_MACH_MEMORY_H - help - On the Acorn Risc-PC, Linux can support the internal IDE disk and - CD-ROM interface, serial and parallel port, and the floppy drive. - -config ARCH_SA1100 - bool "SA1100-based" - select CLKSRC_MMIO - select CPU_SA1100 - select ISA - select ARCH_SPARSEMEM_ENABLE - select ARCH_MTD_XIP - select ARCH_HAS_CPUFREQ - select CPU_FREQ - select GENERIC_CLOCKEVENTS - select CLKDEV_LOOKUP - select TICK_ONESHOT - select ARCH_REQUIRE_GPIOLIB - select HAVE_IDE - select NEED_MACH_MEMORY_H - select SPARSE_IRQ - help - Support for StrongARM 11x0 based boards. - -config ARCH_S3C24XX - bool "Samsung S3C24XX SoCs" - select GENERIC_GPIO - select ARCH_HAS_CPUFREQ - select HAVE_CLK - select CLKDEV_LOOKUP - select ARCH_USES_GETTIMEOFFSET - select HAVE_S3C2410_I2C if I2C - select HAVE_S3C_RTC if RTC_CLASS - select HAVE_S3C2410_WATCHDOG if WATCHDOG - select NEED_MACH_IO_H - help - Samsung S3C2410, S3C2412, S3C2413, S3C2416, S3C2440, S3C2442, S3C2443 - and S3C2450 SoCs based systems, such as the Simtec Electronics BAST - (), the IPAQ 1940 or the - Samsung SMDK2410 development board (and derivatives). - -config ARCH_S3C64XX - bool "Samsung S3C64XX" - select PLAT_SAMSUNG - select CPU_V6 - select ARM_VIC - select HAVE_CLK - select HAVE_TCM - select CLKDEV_LOOKUP - select NO_IOPORT - select ARCH_USES_GETTIMEOFFSET - select ARCH_HAS_CPUFREQ - select ARCH_REQUIRE_GPIOLIB - select SAMSUNG_CLKSRC - select SAMSUNG_IRQ_VIC_TIMER - select S3C_GPIO_TRACK - select S3C_DEV_NAND - select USB_ARCH_HAS_OHCI - select SAMSUNG_GPIOLIB_4BIT - select HAVE_S3C2410_I2C if I2C - select HAVE_S3C2410_WATCHDOG if WATCHDOG - help - Samsung S3C64XX series based systems - -config ARCH_S5P64X0 - bool "Samsung S5P6440 S5P6450" - select CPU_V6 - select GENERIC_GPIO - select HAVE_CLK - select CLKDEV_LOOKUP - select CLKSRC_MMIO - select HAVE_S3C2410_WATCHDOG if WATCHDOG - select GENERIC_CLOCKEVENTS - select HAVE_S3C2410_I2C if I2C - select HAVE_S3C_RTC if RTC_CLASS - help - Samsung S5P64X0 CPU based systems, such as the Samsung SMDK6440, - SMDK6450. - -config ARCH_S5PC100 - bool "Samsung S5PC100" - select GENERIC_GPIO - select HAVE_CLK - select CLKDEV_LOOKUP - select CPU_V7 - select ARCH_USES_GETTIMEOFFSET - select HAVE_S3C2410_I2C if I2C - select HAVE_S3C_RTC if RTC_CLASS - select HAVE_S3C2410_WATCHDOG if WATCHDOG - help - Samsung S5PC100 series based systems - -config ARCH_S5PV210 - bool "Samsung S5PV210/S5PC110" - select CPU_V7 - select ARCH_SPARSEMEM_ENABLE - select ARCH_HAS_HOLES_MEMORYMODEL - select GENERIC_GPIO - select HAVE_CLK - select CLKDEV_LOOKUP - select CLKSRC_MMIO - select ARCH_HAS_CPUFREQ - select GENERIC_CLOCKEVENTS - select HAVE_S3C2410_I2C if I2C - select HAVE_S3C_RTC if RTC_CLASS - select HAVE_S3C2410_WATCHDOG if WATCHDOG - select NEED_MACH_MEMORY_H - help - Samsung S5PV210/S5PC110 series based systems - -config ARCH_EXYNOS - bool "SAMSUNG EXYNOS" - select CPU_V7 - select ARCH_SPARSEMEM_ENABLE - select ARCH_HAS_HOLES_MEMORYMODEL - select GENERIC_GPIO - select HAVE_CLK - select CLKDEV_LOOKUP - select ARCH_HAS_CPUFREQ - select GENERIC_CLOCKEVENTS - select HAVE_S3C_RTC if RTC_CLASS - select HAVE_S3C2410_I2C if I2C - select HAVE_S3C2410_WATCHDOG if WATCHDOG - select NEED_MACH_MEMORY_H - help - Support for SAMSUNG's EXYNOS SoCs (EXYNOS4/5) - -config ARCH_SHARK - bool "Shark" - select CPU_SA110 - select ISA - select ISA_DMA - select ZONE_DMA - select PCI - select ARCH_USES_GETTIMEOFFSET - select NEED_MACH_MEMORY_H - select NEED_MACH_IO_H - help - Support for the StrongARM based Digital DNARD machine, also known - as "Shark" (). - -config ARCH_U300 - bool "ST-Ericsson U300 Series" - depends on MMU - select CLKSRC_MMIO - select CPU_ARM926T - select HAVE_TCM - select ARM_AMBA - select ARM_PATCH_PHYS_VIRT - select ARM_VIC - select GENERIC_CLOCKEVENTS - select CLKDEV_LOOKUP - select HAVE_MACH_CLKDEV - select GENERIC_GPIO - select ARCH_REQUIRE_GPIOLIB - help - Support for ST-Ericsson U300 series mobile platforms. - -config ARCH_U8500 - bool "ST-Ericsson U8500 Series" - depends on MMU - select CPU_V7 - select ARM_AMBA - select GENERIC_CLOCKEVENTS - select CLKDEV_LOOKUP - select ARCH_REQUIRE_GPIOLIB - select ARCH_HAS_CPUFREQ - select HAVE_SMP - select MIGHT_HAVE_CACHE_L2X0 - help - Support for ST-Ericsson's Ux500 architecture - -config ARCH_NOMADIK - bool "STMicroelectronics Nomadik" - select ARM_AMBA - select ARM_VIC - select CPU_ARM926T - select CLKDEV_LOOKUP - select GENERIC_CLOCKEVENTS - select MIGHT_HAVE_CACHE_L2X0 - select ARCH_REQUIRE_GPIOLIB - help - Support for the Nomadik platform by ST-Ericsson - -config ARCH_DAVINCI - bool "TI DaVinci" - select GENERIC_CLOCKEVENTS - select ARCH_REQUIRE_GPIOLIB - select ZONE_DMA - select HAVE_IDE - select CLKDEV_LOOKUP - select GENERIC_ALLOCATOR - select GENERIC_IRQ_CHIP - select ARCH_HAS_HOLES_MEMORYMODEL - help - Support for TI's DaVinci platform. - -config ARCH_OMAP - bool "TI OMAP" - select HAVE_CLK - select ARCH_REQUIRE_GPIOLIB - select ARCH_HAS_CPUFREQ - select CLKSRC_MMIO - select GENERIC_CLOCKEVENTS - select ARCH_HAS_HOLES_MEMORYMODEL - help - Support for TI's OMAP platform (OMAP1/2/3/4). - -config PLAT_SPEAR - bool "ST SPEAr" - select ARM_AMBA - select ARCH_REQUIRE_GPIOLIB - select CLKDEV_LOOKUP - select CLKSRC_MMIO - select GENERIC_CLOCKEVENTS - select HAVE_CLK - help - Support for ST's SPEAr platform (SPEAr3xx, SPEAr6xx and SPEAr13xx). - -config ARCH_VT8500 - bool "VIA/WonderMedia 85xx" - select CPU_ARM926T - select GENERIC_GPIO - select ARCH_HAS_CPUFREQ - select GENERIC_CLOCKEVENTS - select ARCH_REQUIRE_GPIOLIB - select HAVE_PWM - help - Support for VIA/WonderMedia VT8500/WM85xx System-on-Chip. - -config ARCH_ZYNQ - bool "Xilinx Zynq ARM Cortex A9 Platform" - select CPU_V7 - select GENERIC_CLOCKEVENTS - select CLKDEV_LOOKUP - select ARM_GIC - select ARM_AMBA - select ICST - select MIGHT_HAVE_CACHE_L2X0 - select USE_OF - help - Support for Xilinx Zynq ARM Cortex A9 Platform endchoice # @@ -1017,116 +344,9 @@ endchoice # Kconfigs may be included either alphabetically (according to the # plat- suffix) or along side the corresponding mach-* source. # -source "arch/arm/mach-at91/Kconfig" - -source "arch/arm/mach-bcmring/Kconfig" - -source "arch/arm/mach-clps711x/Kconfig" - -source "arch/arm/mach-cns3xxx/Kconfig" - -source "arch/arm/mach-davinci/Kconfig" - -source "arch/arm/mach-dove/Kconfig" - -source "arch/arm/mach-ep93xx/Kconfig" - -source "arch/arm/mach-footbridge/Kconfig" - -source "arch/arm/mach-gemini/Kconfig" - -source "arch/arm/mach-h720x/Kconfig" - -source "arch/arm/mach-integrator/Kconfig" - -source "arch/arm/mach-iop32x/Kconfig" - -source "arch/arm/mach-iop33x/Kconfig" - -source "arch/arm/mach-iop13xx/Kconfig" - -source "arch/arm/mach-ixp4xx/Kconfig" - -source "arch/arm/mach-ixp2000/Kconfig" - -source "arch/arm/mach-ixp23xx/Kconfig" - -source "arch/arm/mach-kirkwood/Kconfig" - -source "arch/arm/mach-ks8695/Kconfig" - -source "arch/arm/mach-lpc32xx/Kconfig" - -source "arch/arm/mach-msm/Kconfig" - -source "arch/arm/mach-mv78xx0/Kconfig" - -source "arch/arm/plat-mxc/Kconfig" - -source "arch/arm/mach-mxs/Kconfig" - -source "arch/arm/mach-netx/Kconfig" -source "arch/arm/mach-nomadik/Kconfig" -source "arch/arm/plat-nomadik/Kconfig" - -source "arch/arm/plat-omap/Kconfig" - -source "arch/arm/mach-omap1/Kconfig" - -source "arch/arm/mach-omap2/Kconfig" - -source "arch/arm/mach-orion5x/Kconfig" - -source "arch/arm/mach-pxa/Kconfig" -source "arch/arm/plat-pxa/Kconfig" - -source "arch/arm/mach-mmp/Kconfig" - -source "arch/arm/mach-realview/Kconfig" - -source "arch/arm/mach-sa1100/Kconfig" - -source "arch/arm/plat-samsung/Kconfig" -source "arch/arm/plat-s3c24xx/Kconfig" -source "arch/arm/plat-s5p/Kconfig" - -source "arch/arm/plat-spear/Kconfig" - -source "arch/arm/mach-s3c24xx/Kconfig" -if ARCH_S3C24XX -source "arch/arm/mach-s3c2412/Kconfig" -source "arch/arm/mach-s3c2440/Kconfig" -endif - -if ARCH_S3C64XX -source "arch/arm/mach-s3c64xx/Kconfig" -endif - -source "arch/arm/mach-s5p64x0/Kconfig" - -source "arch/arm/mach-s5pc100/Kconfig" - -source "arch/arm/mach-s5pv210/Kconfig" - -source "arch/arm/mach-exynos/Kconfig" - -source "arch/arm/mach-shmobile/Kconfig" - -source "arch/arm/mach-tegra/Kconfig" - -source "arch/arm/mach-u300/Kconfig" - -source "arch/arm/mach-ux500/Kconfig" - -source "arch/arm/mach-versatile/Kconfig" - -source "arch/arm/mach-vexpress/Kconfig" -source "arch/arm/plat-versatile/Kconfig" - -source "arch/arm/mach-vt8500/Kconfig" - -source "arch/arm/mach-w90x900/Kconfig" +source "arch/arm/mach-ak39/Kconfig" +source "arch/arm/plat-anyka/Kconfig" # Definitions to make life easier config ARCH_ACORN @@ -1632,11 +852,6 @@ source kernel/Kconfig.preempt config HZ int - default 200 if ARCH_EBSA110 || ARCH_S3C24XX || ARCH_S5P64X0 || \ - ARCH_S5PV210 || ARCH_EXYNOS4 - default OMAP_32K_TIMER_HZ if ARCH_OMAP && OMAP_32K_TIMER - default AT91_TIMER_HZ if ARCH_AT91 - default SHMOBILE_TIMER_HZ if ARCH_SHMOBILE default 100 config THUMB2_KERNEL @@ -1896,6 +1111,15 @@ config DEPRECATED_PARAM_STRUCT This was deprecated in 2001 and announced to live on for 5 years. Some old boot loaders still use this way. +config ARM_FLUSH_CONSOLE_ON_RESTART + bool "Force flush the console on restart" + help + If the console is locked while the system is rebooted, the messages + in the temporary logbuffer would not have propogated to all the + console drivers. This option forces the console lock to be + released if it failed to be acquired, which will cause all the + pending messages to be flushed. + endmenu menu "Boot options" @@ -2113,6 +1337,22 @@ config AUTO_ZRELADDR 0xf8000000. This assumes the zImage being placed in the first 128MB from start of memory. +config RAM_BASE + hex "RAM physical starting address" + depends on ARCH_AK39 + default 0x80000000 + help + ANYKA RAM physical starting address (in hex), default addr is 0x80000000 for Anyka platform. + +config VIDEO_RESERVED_MEM_SIZE + hex "Memory reserved for video decoding" + depends on ARCH_AK39 + default 0x1A00000 + help + ANYKA H.264 decoder requires continuous physical RAM which do NOT cross + 32MB boundary (Decoder IP requirement). So we have to reserve enough RAM + for H.264 decoding. Default size is 0x1A00000 (26MB) for AK39xx. + endmenu menu "CPU Power Management" @@ -2152,52 +1392,6 @@ config CPU_FREQ_PXA select CPU_FREQ_TABLE select CPU_FREQ_DEFAULT_GOV_USERSPACE -config CPU_FREQ_S3C - bool - help - Internal configuration node for common cpufreq on Samsung SoC - -config CPU_FREQ_S3C24XX - bool "CPUfreq driver for Samsung S3C24XX series CPUs (EXPERIMENTAL)" - depends on ARCH_S3C24XX && CPU_FREQ && EXPERIMENTAL - select CPU_FREQ_S3C - help - This enables the CPUfreq driver for the Samsung S3C24XX family - of CPUs. - - For details, take a look at . - - If in doubt, say N. - -config CPU_FREQ_S3C24XX_PLL - bool "Support CPUfreq changing of PLL frequency (EXPERIMENTAL)" - depends on CPU_FREQ_S3C24XX && EXPERIMENTAL - help - Compile in support for changing the PLL frequency from the - S3C24XX series CPUfreq driver. The PLL takes time to settle - after a frequency change, so by default it is not enabled. - - This also means that the PLL tables for the selected CPU(s) will - be built which may increase the size of the kernel image. - -config CPU_FREQ_S3C24XX_DEBUG - bool "Debug CPUfreq Samsung driver core" - depends on CPU_FREQ_S3C24XX - help - Enable s3c_freq_dbg for the Samsung S3C CPUfreq core - -config CPU_FREQ_S3C24XX_IODEBUG - bool "Debug CPUfreq Samsung driver IO timing" - depends on CPU_FREQ_S3C24XX - help - Enable s3c_freq_iodbg for the Samsung S3C CPUfreq core - -config CPU_FREQ_S3C24XX_DEBUGFS - bool "Export debugfs for CPUFreq" - depends on CPU_FREQ_S3C24XX && DEBUG_FS - help - Export status information via debugfs. - endif source "drivers/cpuidle/Kconfig" diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug index 85348a09..037bd5af 100644 --- a/arch/arm/Kconfig.debug +++ b/arch/arm/Kconfig.debug @@ -63,6 +63,27 @@ config DEBUG_USER 8 - SIGSEGV faults 16 - SIGBUS faults +config DEBUG_RODATA + bool "Write protect kernel text section" + default n + depends on DEBUG_KERNEL && MMU + ---help--- + Mark the kernel text section as write-protected in the pagetables, + in order to catch accidental (and incorrect) writes to such const + data. This will cause the size of the kernel, plus up to 4MB, to + be mapped as pages instead of sections, which will increase TLB + pressure. + If in doubt, say "N". + +config DEBUG_RODATA_TEST + bool "Testcase for the DEBUG_RODATA feature" + depends on DEBUG_RODATA + default n + ---help--- + This option enables a testcase for the DEBUG_RODATA + feature. + If in doubt, say "N" + # These options are only for real kernel hackers who want to get their hands dirty. config DEBUG_LL bool "Kernel low-level debugging functions (read help!)" diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 1d6402cb..54bf4034 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -197,6 +197,7 @@ machine-$(CONFIG_MACH_SPEAR310) := spear3xx machine-$(CONFIG_MACH_SPEAR320) := spear3xx machine-$(CONFIG_MACH_SPEAR600) := spear6xx machine-$(CONFIG_ARCH_ZYNQ) := zynq +machine-$(CONFIG_ARCH_AK39) := ak39 # Platform directory name. This list is sorted alphanumerically # by CONFIG_* macro name. @@ -212,6 +213,7 @@ plat-$(CONFIG_PLAT_S3C24XX) := s3c24xx samsung plat-$(CONFIG_PLAT_S5P) := s5p samsung plat-$(CONFIG_PLAT_SPEAR) := spear plat-$(CONFIG_PLAT_VERSATILE) := versatile +plat-$(CONFIG_PLAT_ANYKA) := anyka ifeq ($(CONFIG_ARCH_EBSA110),y) # This is what happens if you forget the IOCS16 line. diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S index 87278fc8..9e631db1 100644 --- a/arch/arm/boot/compressed/head.S +++ b/arch/arm/boot/compressed/head.S @@ -9,6 +9,7 @@ * published by the Free Software Foundation. */ #include +#include /* * Debugging stuff @@ -132,7 +133,21 @@ start: .word start @ absolute load/run zImage address .word _edata @ zImage end address THUMB( .thumb ) -1: mov r7, r1 @ save architecture ID +1: +/* + * Actually `machine type' MUST be provided in register r1 by boot/loader according + * to ARM Linux booting requirements (Documentation/arm/Booting). However, it seems + * that AK39xx nandboot or boot/loader-like program do NOT follow this standard, + * we have to give a fake one here. + * + * NOTE: The following code assumes that machine type is NOT greater than 0xFFFF, that could be true for a very long time, so just keep the code. + */ +#ifdef CONFIG_ARCH_AK39 + mov r1, #(MACH_TYPE_AK39XX & 0xFF) + orr r1, r1, #(MACH_TYPE_AK39XX & 0xFF00) +#endif + + mov r7, r1 @ save architecture ID mov r8, r2 @ save atags pointer #ifndef __ARM_ARCH_2__ @@ -767,6 +782,8 @@ proc_types: @ b __arm6_mmu_cache_off @ b __armv3_mmu_cache_flush +#if !defined(CONFIG_CPU_V7) + /* This collides with some V7 IDs, preventing correct detection */ .word 0x00000000 @ old ARM ID .word 0x0000f000 mov pc, lr @@ -775,6 +792,7 @@ proc_types: THUMB( nop ) mov pc, lr THUMB( nop ) +#endif .word 0x41007000 @ ARM7/710 .word 0xfff8fe00 diff --git a/arch/arm/boot/compressed/misc.c b/arch/arm/boot/compressed/misc.c index 8e2a8fca..b05ee262 100644 --- a/arch/arm/boot/compressed/misc.c +++ b/arch/arm/boot/compressed/misc.c @@ -27,6 +27,9 @@ extern void error(char *x); #include +#define _STR(x) #x +#define STR(x) _STR(x) + #ifdef CONFIG_DEBUG_ICEDCC #if defined(CONFIG_CPU_V6) || defined(CONFIG_CPU_V6K) || defined(CONFIG_CPU_V7) @@ -151,4 +154,6 @@ decompress_kernel(unsigned long output_start, unsigned long free_mem_ptr_p, error("decompressor returned an error"); else putstr(" done, booting the kernel.\n"); + + putstr("Anyka Linux Kernel Version: "STR(ANYKA_VERSION)"\n"); } diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig index 283fa1d8..271dd136 100644 --- a/arch/arm/common/Kconfig +++ b/arch/arm/common/Kconfig @@ -40,3 +40,53 @@ config SHARP_PARAM config SHARP_SCOOP bool + +config FIQ_GLUE + bool + select FIQ + +config FIQ_DEBUGGER + bool "FIQ Mode Serial Debugger" + select FIQ + select FIQ_GLUE + default n + help + The FIQ serial debugger can accept commands even when the + kernel is unresponsive due to being stuck with interrupts + disabled. + + +config FIQ_DEBUGGER_NO_SLEEP + bool "Keep serial debugger active" + depends on FIQ_DEBUGGER + default n + help + Enables the serial debugger at boot. Passing + fiq_debugger.no_sleep on the kernel commandline will + override this config option. + +config FIQ_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON + bool "Don't disable wakeup IRQ when debugger is active" + depends on FIQ_DEBUGGER + default n + help + Don't disable the wakeup irq when enabling the uart clock. This will + cause extra interrupts, but it makes the serial debugger usable with + on some MSM radio builds that ignore the uart clock request in power + collapse. + +config FIQ_DEBUGGER_CONSOLE + bool "Console on FIQ Serial Debugger port" + depends on FIQ_DEBUGGER + default n + help + Enables a console so that printk messages are displayed on + the debugger serial port as the occur. + +config FIQ_DEBUGGER_CONSOLE_DEFAULT_ENABLE + bool "Put the FIQ debugger into console mode by default" + depends on FIQ_DEBUGGER_CONSOLE + default n + help + If enabled, this puts the fiq debugger into console mode by default. + Otherwise, the fiq debugger will start out in debug mode. diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index 215816f1..11670f9d 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile @@ -15,3 +15,5 @@ obj-$(CONFIG_ARCH_IXP2000) += uengine.o obj-$(CONFIG_ARCH_IXP23XX) += uengine.o obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o +obj-$(CONFIG_FIQ_GLUE) += fiq_glue.o fiq_glue_setup.o +obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger.o diff --git a/arch/arm/common/fiq_debugger.c b/arch/arm/common/fiq_debugger.c new file mode 100644 index 00000000..d0686388 --- /dev/null +++ b/arch/arm/common/fiq_debugger.c @@ -0,0 +1,1388 @@ +/* + * arch/arm/common/fiq_debugger.c + * + * Serial Debugger Interface accessed through an FIQ interrupt. + * + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "fiq_debugger_ringbuf.h" + +#define DEBUG_MAX 64 +#define MAX_UNHANDLED_FIQ_COUNT 1000000 + +#define MAX_FIQ_DEBUGGER_PORTS 4 + +#define THREAD_INFO(sp) ((struct thread_info *) \ + ((unsigned long)(sp) & ~(THREAD_SIZE - 1))) + +struct fiq_debugger_state { + struct fiq_glue_handler handler; + + int fiq; + int uart_irq; + int signal_irq; + int wakeup_irq; + bool wakeup_irq_no_set_wake; + struct clk *clk; + struct fiq_debugger_pdata *pdata; + struct platform_device *pdev; + + char debug_cmd[DEBUG_MAX]; + int debug_busy; + int debug_abort; + + char debug_buf[DEBUG_MAX]; + int debug_count; + + bool no_sleep; + bool debug_enable; + bool ignore_next_wakeup_irq; + struct timer_list sleep_timer; + spinlock_t sleep_timer_lock; + bool uart_enabled; + struct wake_lock debugger_wake_lock; + bool console_enable; + int current_cpu; + atomic_t unhandled_fiq_count; + bool in_fiq; + + struct work_struct work; + spinlock_t work_lock; + char work_cmd[DEBUG_MAX]; + +#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE + spinlock_t console_lock; + struct console console; + struct tty_struct *tty; + int tty_open_count; + struct fiq_debugger_ringbuf *tty_rbuf; + bool syslog_dumping; +#endif + + unsigned int last_irqs[NR_IRQS]; + unsigned int last_local_timer_irqs[NR_CPUS]; +}; + +#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE +struct tty_driver *fiq_tty_driver; +#endif + +#ifdef CONFIG_FIQ_DEBUGGER_NO_SLEEP +static bool initial_no_sleep = true; +#else +static bool initial_no_sleep; +#endif + +#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE_DEFAULT_ENABLE +static bool initial_debug_enable = true; +static bool initial_console_enable = true; +#else +static bool initial_debug_enable; +static bool initial_console_enable; +#endif + +static bool fiq_kgdb_enable; + +module_param_named(no_sleep, initial_no_sleep, bool, 0644); +module_param_named(debug_enable, initial_debug_enable, bool, 0644); +module_param_named(console_enable, initial_console_enable, bool, 0644); +module_param_named(kgdb_enable, fiq_kgdb_enable, bool, 0644); + +#ifdef CONFIG_FIQ_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON +static inline void enable_wakeup_irq(struct fiq_debugger_state *state) {} +static inline void disable_wakeup_irq(struct fiq_debugger_state *state) {} +#else +static inline void enable_wakeup_irq(struct fiq_debugger_state *state) +{ + if (state->wakeup_irq < 0) + return; + enable_irq(state->wakeup_irq); + if (!state->wakeup_irq_no_set_wake) + enable_irq_wake(state->wakeup_irq); +} +static inline void disable_wakeup_irq(struct fiq_debugger_state *state) +{ + if (state->wakeup_irq < 0) + return; + disable_irq_nosync(state->wakeup_irq); + if (!state->wakeup_irq_no_set_wake) + disable_irq_wake(state->wakeup_irq); +} +#endif + +static bool inline debug_have_fiq(struct fiq_debugger_state *state) +{ + return (state->fiq >= 0); +} + +static void debug_force_irq(struct fiq_debugger_state *state) +{ + unsigned int irq = state->signal_irq; + + if (WARN_ON(!debug_have_fiq(state))) + return; + if (state->pdata->force_irq) { + state->pdata->force_irq(state->pdev, irq); + } else { + struct irq_chip *chip = irq_get_chip(irq); + if (chip && chip->irq_retrigger) + chip->irq_retrigger(irq_get_irq_data(irq)); + } +} + +static void debug_uart_enable(struct fiq_debugger_state *state) +{ + if (state->clk) + clk_enable(state->clk); + if (state->pdata->uart_enable) + state->pdata->uart_enable(state->pdev); +} + +static void debug_uart_disable(struct fiq_debugger_state *state) +{ + if (state->pdata->uart_disable) + state->pdata->uart_disable(state->pdev); + if (state->clk) + clk_disable(state->clk); +} + +static void debug_uart_flush(struct fiq_debugger_state *state) +{ + if (state->pdata->uart_flush) + state->pdata->uart_flush(state->pdev); +} + +static void debug_putc(struct fiq_debugger_state *state, char c) +{ + state->pdata->uart_putc(state->pdev, c); +} + +static void debug_puts(struct fiq_debugger_state *state, char *s) +{ + unsigned c; + while ((c = *s++)) { + if (c == '\n') + debug_putc(state, '\r'); + debug_putc(state, c); + } +} + +static void debug_prompt(struct fiq_debugger_state *state) +{ + debug_puts(state, "debug> "); +} + +int log_buf_copy(char *dest, int idx, int len); +static void dump_kernel_log(struct fiq_debugger_state *state) +{ + char buf[1024]; + int idx = 0; + int ret; + int saved_oip; + + /* setting oops_in_progress prevents log_buf_copy() + * from trying to take a spinlock which will make it + * very unhappy in some cases... + */ + saved_oip = oops_in_progress; + oops_in_progress = 1; + for (;;) { + ret = log_buf_copy(buf, idx, 1023); + if (ret <= 0) + break; + buf[ret] = 0; + debug_puts(state, buf); + idx += ret; + } + oops_in_progress = saved_oip; +} + +static char *mode_name(unsigned cpsr) +{ + switch (cpsr & MODE_MASK) { + case USR_MODE: return "USR"; + case FIQ_MODE: return "FIQ"; + case IRQ_MODE: return "IRQ"; + case SVC_MODE: return "SVC"; + case ABT_MODE: return "ABT"; + case UND_MODE: return "UND"; + case SYSTEM_MODE: return "SYS"; + default: return "???"; + } +} + +static int debug_printf(void *cookie, const char *fmt, ...) +{ + struct fiq_debugger_state *state = cookie; + char buf[256]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + debug_puts(state, buf); + return state->debug_abort; +} + +/* Safe outside fiq context */ +static int debug_printf_nfiq(void *cookie, const char *fmt, ...) +{ + struct fiq_debugger_state *state = cookie; + char buf[256]; + va_list ap; + unsigned long irq_flags; + + va_start(ap, fmt); + vsnprintf(buf, 128, fmt, ap); + va_end(ap); + + local_irq_save(irq_flags); + debug_puts(state, buf); + debug_uart_flush(state); + local_irq_restore(irq_flags); + return state->debug_abort; +} + +static void dump_regs(struct fiq_debugger_state *state, unsigned *regs) +{ + debug_printf(state, " r0 %08x r1 %08x r2 %08x r3 %08x\n", + regs[0], regs[1], regs[2], regs[3]); + debug_printf(state, " r4 %08x r5 %08x r6 %08x r7 %08x\n", + regs[4], regs[5], regs[6], regs[7]); + debug_printf(state, " r8 %08x r9 %08x r10 %08x r11 %08x mode %s\n", + regs[8], regs[9], regs[10], regs[11], + mode_name(regs[16])); + if ((regs[16] & MODE_MASK) == USR_MODE) + debug_printf(state, " ip %08x sp %08x lr %08x pc %08x " + "cpsr %08x\n", regs[12], regs[13], regs[14], + regs[15], regs[16]); + else + debug_printf(state, " ip %08x sp %08x lr %08x pc %08x " + "cpsr %08x spsr %08x\n", regs[12], regs[13], + regs[14], regs[15], regs[16], regs[17]); +} + +struct mode_regs { + unsigned long sp_svc; + unsigned long lr_svc; + unsigned long spsr_svc; + + unsigned long sp_abt; + unsigned long lr_abt; + unsigned long spsr_abt; + + unsigned long sp_und; + unsigned long lr_und; + unsigned long spsr_und; + + unsigned long sp_irq; + unsigned long lr_irq; + unsigned long spsr_irq; + + unsigned long r8_fiq; + unsigned long r9_fiq; + unsigned long r10_fiq; + unsigned long r11_fiq; + unsigned long r12_fiq; + unsigned long sp_fiq; + unsigned long lr_fiq; + unsigned long spsr_fiq; +}; + +void __naked get_mode_regs(struct mode_regs *regs) +{ + asm volatile ( + "mrs r1, cpsr\n" + "msr cpsr_c, #0xd3 @(SVC_MODE | PSR_I_BIT | PSR_F_BIT)\n" + "stmia r0!, {r13 - r14}\n" + "mrs r2, spsr\n" + "msr cpsr_c, #0xd7 @(ABT_MODE | PSR_I_BIT | PSR_F_BIT)\n" + "stmia r0!, {r2, r13 - r14}\n" + "mrs r2, spsr\n" + "msr cpsr_c, #0xdb @(UND_MODE | PSR_I_BIT | PSR_F_BIT)\n" + "stmia r0!, {r2, r13 - r14}\n" + "mrs r2, spsr\n" + "msr cpsr_c, #0xd2 @(IRQ_MODE | PSR_I_BIT | PSR_F_BIT)\n" + "stmia r0!, {r2, r13 - r14}\n" + "mrs r2, spsr\n" + "msr cpsr_c, #0xd1 @(FIQ_MODE | PSR_I_BIT | PSR_F_BIT)\n" + "stmia r0!, {r2, r8 - r14}\n" + "mrs r2, spsr\n" + "stmia r0!, {r2}\n" + "msr cpsr_c, r1\n" + "bx lr\n"); +} + + +static void dump_allregs(struct fiq_debugger_state *state, unsigned *regs) +{ + struct mode_regs mode_regs; + dump_regs(state, regs); + get_mode_regs(&mode_regs); + debug_printf(state, " svc: sp %08x lr %08x spsr %08x\n", + mode_regs.sp_svc, mode_regs.lr_svc, mode_regs.spsr_svc); + debug_printf(state, " abt: sp %08x lr %08x spsr %08x\n", + mode_regs.sp_abt, mode_regs.lr_abt, mode_regs.spsr_abt); + debug_printf(state, " und: sp %08x lr %08x spsr %08x\n", + mode_regs.sp_und, mode_regs.lr_und, mode_regs.spsr_und); + debug_printf(state, " irq: sp %08x lr %08x spsr %08x\n", + mode_regs.sp_irq, mode_regs.lr_irq, mode_regs.spsr_irq); + debug_printf(state, " fiq: r8 %08x r9 %08x r10 %08x r11 %08x " + "r12 %08x\n", + mode_regs.r8_fiq, mode_regs.r9_fiq, mode_regs.r10_fiq, + mode_regs.r11_fiq, mode_regs.r12_fiq); + debug_printf(state, " fiq: sp %08x lr %08x spsr %08x\n", + mode_regs.sp_fiq, mode_regs.lr_fiq, mode_regs.spsr_fiq); +} + +static void dump_irqs(struct fiq_debugger_state *state) +{ + int n; + + debug_printf(state, "irqnr total since-last status name\n"); + for (n = 0; n < NR_IRQS; n++) { + struct irqaction *act = irq_desc[n].action; + if (!act && !kstat_irqs(n)) + continue; + debug_printf(state, "%5d: %10u %11u %8x %s\n", n, + kstat_irqs(n), + kstat_irqs(n) - state->last_irqs[n], + irq_desc[n].status_use_accessors, + (act && act->name) ? act->name : "???"); + state->last_irqs[n] = kstat_irqs(n); + } +} + +struct stacktrace_state { + struct fiq_debugger_state *state; + unsigned int depth; +}; + +static int report_trace(struct stackframe *frame, void *d) +{ + struct stacktrace_state *sts = d; + + if (sts->depth) { + debug_printf(sts->state, + " pc: %p (%pF), lr %p (%pF), sp %p, fp %p\n", + frame->pc, frame->pc, frame->lr, frame->lr, + frame->sp, frame->fp); + sts->depth--; + return 0; + } + debug_printf(sts->state, " ...\n"); + + return sts->depth == 0; +} + +struct frame_tail { + struct frame_tail *fp; + unsigned long sp; + unsigned long lr; +} __attribute__((packed)); + +static struct frame_tail *user_backtrace(struct fiq_debugger_state *state, + struct frame_tail *tail) +{ + struct frame_tail buftail[2]; + + /* Also check accessibility of one struct frame_tail beyond */ + if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) { + debug_printf(state, " invalid frame pointer %p\n", tail); + return NULL; + } + if (__copy_from_user_inatomic(buftail, tail, sizeof(buftail))) { + debug_printf(state, + " failed to copy frame pointer %p\n", tail); + return NULL; + } + + debug_printf(state, " %p\n", buftail[0].lr); + + /* frame pointers should strictly progress back up the stack + * (towards higher addresses) */ + if (tail >= buftail[0].fp) + return NULL; + + return buftail[0].fp-1; +} + +void dump_stacktrace(struct fiq_debugger_state *state, + struct pt_regs * const regs, unsigned int depth, void *ssp) +{ + struct frame_tail *tail; + struct thread_info *real_thread_info = THREAD_INFO(ssp); + struct stacktrace_state sts; + + sts.depth = depth; + sts.state = state; + *current_thread_info() = *real_thread_info; + + if (!current) + debug_printf(state, "current NULL\n"); + else + debug_printf(state, "pid: %d comm: %s\n", + current->pid, current->comm); + dump_regs(state, (unsigned *)regs); + + if (!user_mode(regs)) { + struct stackframe frame; + frame.fp = regs->ARM_fp; + frame.sp = regs->ARM_sp; + frame.lr = regs->ARM_lr; + frame.pc = regs->ARM_pc; + debug_printf(state, + " pc: %p (%pF), lr %p (%pF), sp %p, fp %p\n", + regs->ARM_pc, regs->ARM_pc, regs->ARM_lr, regs->ARM_lr, + regs->ARM_sp, regs->ARM_fp); + walk_stackframe(&frame, report_trace, &sts); + return; + } + + tail = ((struct frame_tail *) regs->ARM_fp) - 1; + while (depth-- && tail && !((unsigned long) tail & 3)) + tail = user_backtrace(state, tail); +} + +static void do_ps(struct fiq_debugger_state *state) +{ + struct task_struct *g; + struct task_struct *p; + unsigned task_state; + static const char stat_nam[] = "RSDTtZX"; + + debug_printf(state, "pid ppid prio task pc\n"); + read_lock(&tasklist_lock); + do_each_thread(g, p) { + task_state = p->state ? __ffs(p->state) + 1 : 0; + debug_printf(state, + "%5d %5d %4d ", p->pid, p->parent->pid, p->prio); + debug_printf(state, "%-13.13s %c", p->comm, + task_state >= sizeof(stat_nam) ? '?' : stat_nam[task_state]); + if (task_state == TASK_RUNNING) + debug_printf(state, " running\n"); + else + debug_printf(state, " %08lx\n", thread_saved_pc(p)); + } while_each_thread(g, p); + read_unlock(&tasklist_lock); +} + +#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE +static void begin_syslog_dump(struct fiq_debugger_state *state) +{ + state->syslog_dumping = true; +} + +static void end_syslog_dump(struct fiq_debugger_state *state) +{ + state->syslog_dumping = false; +} +#else +extern int do_syslog(int type, char __user *bug, int count); +static void begin_syslog_dump(struct fiq_debugger_state *state) +{ + do_syslog(5 /* clear */, NULL, 0); +} + +static void end_syslog_dump(struct fiq_debugger_state *state) +{ + char buf[128]; + int ret; + int idx = 0; + + while (1) { + ret = log_buf_copy(buf, idx, sizeof(buf) - 1); + if (ret <= 0) + break; + buf[ret] = 0; + debug_printf(state, "%s", buf); + idx += ret; + } +} +#endif + +static void do_sysrq(struct fiq_debugger_state *state, char rq) +{ + if ((rq == 'g' || rq == 'G') && !fiq_kgdb_enable) { + debug_printf(state, "sysrq-g blocked\n"); + return; + } + begin_syslog_dump(state); + handle_sysrq(rq); + end_syslog_dump(state); +} + +#ifdef CONFIG_KGDB +static void do_kgdb(struct fiq_debugger_state *state) +{ + if (!fiq_kgdb_enable) { + debug_printf(state, "kgdb through fiq debugger not enabled\n"); + return; + } + + debug_printf(state, "enabling console and triggering kgdb\n"); + state->console_enable = true; + handle_sysrq('g'); +} +#endif + +static void debug_schedule_work(struct fiq_debugger_state *state, char *cmd) +{ + unsigned long flags; + + spin_lock_irqsave(&state->work_lock, flags); + if (state->work_cmd[0] != '\0') { + debug_printf(state, "work command processor busy\n"); + spin_unlock_irqrestore(&state->work_lock, flags); + return; + } + + strlcpy(state->work_cmd, cmd, sizeof(state->work_cmd)); + spin_unlock_irqrestore(&state->work_lock, flags); + + schedule_work(&state->work); +} + +static void debug_work(struct work_struct *work) +{ + struct fiq_debugger_state *state; + char work_cmd[DEBUG_MAX]; + char *cmd; + unsigned long flags; + + state = container_of(work, struct fiq_debugger_state, work); + + spin_lock_irqsave(&state->work_lock, flags); + + strlcpy(work_cmd, state->work_cmd, sizeof(work_cmd)); + state->work_cmd[0] = '\0'; + + spin_unlock_irqrestore(&state->work_lock, flags); + + cmd = work_cmd; + if (!strncmp(cmd, "reboot", 6)) { + cmd += 6; + while (*cmd == ' ') + cmd++; + if (cmd != '\0') + kernel_restart(cmd); + else + kernel_restart(NULL); + } else { + debug_printf(state, "unknown work command '%s'\n", work_cmd); + } +} + +/* This function CANNOT be called in FIQ context */ +static void debug_irq_exec(struct fiq_debugger_state *state, char *cmd) +{ + if (!strcmp(cmd, "ps")) + do_ps(state); + if (!strcmp(cmd, "sysrq")) + do_sysrq(state, 'h'); + if (!strncmp(cmd, "sysrq ", 6)) + do_sysrq(state, cmd[6]); +#ifdef CONFIG_KGDB + if (!strcmp(cmd, "kgdb")) + do_kgdb(state); +#endif + if (!strncmp(cmd, "reboot", 6)) + debug_schedule_work(state, cmd); +} + +static void debug_help(struct fiq_debugger_state *state) +{ + debug_printf(state, "FIQ Debugger commands:\n" + " pc PC status\n" + " regs Register dump\n" + " allregs Extended Register dump\n" + " bt Stack trace\n" + " reboot [] Reboot with command \n" + " reset [] Hard reset with command \n" + " irqs Interupt status\n" + " kmsg Kernel log\n" + " version Kernel version\n"); + debug_printf(state, " sleep Allow sleep while in FIQ\n" + " nosleep Disable sleep while in FIQ\n" + " console Switch terminal to console\n" + " cpu Current CPU\n" + " cpu Switch to CPU\n"); + debug_printf(state, " ps Process list\n" + " sysrq sysrq options\n" + " sysrq Execute sysrq with \n"); +#ifdef CONFIG_KGDB + debug_printf(state, " kgdb Enter kernel debugger\n"); +#endif +} + +static void take_affinity(void *info) +{ + struct fiq_debugger_state *state = info; + struct cpumask cpumask; + + cpumask_clear(&cpumask); + cpumask_set_cpu(get_cpu(), &cpumask); + + irq_set_affinity(state->uart_irq, &cpumask); +} + +static void switch_cpu(struct fiq_debugger_state *state, int cpu) +{ + if (!debug_have_fiq(state)) + smp_call_function_single(cpu, take_affinity, state, false); + state->current_cpu = cpu; +} + +static bool debug_fiq_exec(struct fiq_debugger_state *state, + const char *cmd, unsigned *regs, void *svc_sp) +{ + bool signal_helper = false; + + if (!strcmp(cmd, "help") || !strcmp(cmd, "?")) { + debug_help(state); + } else if (!strcmp(cmd, "pc")) { + debug_printf(state, " pc %08x cpsr %08x mode %s\n", + regs[15], regs[16], mode_name(regs[16])); + } else if (!strcmp(cmd, "regs")) { + dump_regs(state, regs); + } else if (!strcmp(cmd, "allregs")) { + dump_allregs(state, regs); + } else if (!strcmp(cmd, "bt")) { + dump_stacktrace(state, (struct pt_regs *)regs, 100, svc_sp); + } else if (!strncmp(cmd, "reset", 5)) { + cmd += 5; + while (*cmd == ' ') + cmd++; + if (*cmd) { + char tmp_cmd[32]; + strlcpy(tmp_cmd, cmd, sizeof(tmp_cmd)); + machine_restart(tmp_cmd); + } else { + machine_restart(NULL); + } + } else if (!strcmp(cmd, "irqs")) { + dump_irqs(state); + } else if (!strcmp(cmd, "kmsg")) { + dump_kernel_log(state); + } else if (!strcmp(cmd, "version")) { + debug_printf(state, "%s\n", linux_banner); + } else if (!strcmp(cmd, "sleep")) { + state->no_sleep = false; + debug_printf(state, "enabling sleep\n"); + } else if (!strcmp(cmd, "nosleep")) { + state->no_sleep = true; + debug_printf(state, "disabling sleep\n"); + } else if (!strcmp(cmd, "console")) { + debug_printf(state, "console mode\n"); + debug_uart_flush(state); + state->console_enable = true; + } else if (!strcmp(cmd, "cpu")) { + debug_printf(state, "cpu %d\n", state->current_cpu); + } else if (!strncmp(cmd, "cpu ", 4)) { + unsigned long cpu = 0; + if (strict_strtoul(cmd + 4, 10, &cpu) == 0) + switch_cpu(state, cpu); + else + debug_printf(state, "invalid cpu\n"); + debug_printf(state, "cpu %d\n", state->current_cpu); + } else { + if (state->debug_busy) { + debug_printf(state, + "command processor busy. trying to abort.\n"); + state->debug_abort = -1; + } else { + strcpy(state->debug_cmd, cmd); + state->debug_busy = 1; + } + + return true; + } + if (!state->console_enable) + debug_prompt(state); + + return signal_helper; +} + +static void sleep_timer_expired(unsigned long data) +{ + struct fiq_debugger_state *state = (struct fiq_debugger_state *)data; + unsigned long flags; + + spin_lock_irqsave(&state->sleep_timer_lock, flags); + if (state->uart_enabled && !state->no_sleep) { + if (state->debug_enable && !state->console_enable) { + state->debug_enable = false; + debug_printf_nfiq(state, "suspending fiq debugger\n"); + } + state->ignore_next_wakeup_irq = true; + debug_uart_disable(state); + state->uart_enabled = false; + enable_wakeup_irq(state); + } + wake_unlock(&state->debugger_wake_lock); + spin_unlock_irqrestore(&state->sleep_timer_lock, flags); +} + +static void handle_wakeup(struct fiq_debugger_state *state) +{ + unsigned long flags; + + spin_lock_irqsave(&state->sleep_timer_lock, flags); + if (state->wakeup_irq >= 0 && state->ignore_next_wakeup_irq) { + state->ignore_next_wakeup_irq = false; + } else if (!state->uart_enabled) { + wake_lock(&state->debugger_wake_lock); + debug_uart_enable(state); + state->uart_enabled = true; + disable_wakeup_irq(state); + mod_timer(&state->sleep_timer, jiffies + HZ / 2); + } + spin_unlock_irqrestore(&state->sleep_timer_lock, flags); +} + +static irqreturn_t wakeup_irq_handler(int irq, void *dev) +{ + struct fiq_debugger_state *state = dev; + + if (!state->no_sleep) + debug_puts(state, "WAKEUP\n"); + handle_wakeup(state); + + return IRQ_HANDLED; +} + + +static void debug_handle_irq_context(struct fiq_debugger_state *state) +{ + if (!state->no_sleep) { + unsigned long flags; + + spin_lock_irqsave(&state->sleep_timer_lock, flags); + wake_lock(&state->debugger_wake_lock); + mod_timer(&state->sleep_timer, jiffies + HZ * 5); + spin_unlock_irqrestore(&state->sleep_timer_lock, flags); + } +#if defined(CONFIG_FIQ_DEBUGGER_CONSOLE) + if (state->tty) { + int i; + int count = fiq_debugger_ringbuf_level(state->tty_rbuf); + for (i = 0; i < count; i++) { + int c = fiq_debugger_ringbuf_peek(state->tty_rbuf, 0); + tty_insert_flip_char(state->tty, c, TTY_NORMAL); + if (!fiq_debugger_ringbuf_consume(state->tty_rbuf, 1)) + pr_warn("fiq tty failed to consume byte\n"); + } + tty_flip_buffer_push(state->tty); + } +#endif + if (state->debug_busy) { + debug_irq_exec(state, state->debug_cmd); + if (!state->console_enable) + debug_prompt(state); + state->debug_busy = 0; + } +} + +static int debug_getc(struct fiq_debugger_state *state) +{ + return state->pdata->uart_getc(state->pdev); +} + +static bool debug_handle_uart_interrupt(struct fiq_debugger_state *state, + int this_cpu, void *regs, void *svc_sp) +{ + int c; + static int last_c; + int count = 0; + bool signal_helper = false; + + if (this_cpu != state->current_cpu) { + if (state->in_fiq) + return false; + + if (atomic_inc_return(&state->unhandled_fiq_count) != + MAX_UNHANDLED_FIQ_COUNT) + return false; + + debug_printf(state, "fiq_debugger: cpu %d not responding, " + "reverting to cpu %d\n", state->current_cpu, + this_cpu); + + atomic_set(&state->unhandled_fiq_count, 0); + switch_cpu(state, this_cpu); + return false; + } + + state->in_fiq = true; + + while ((c = debug_getc(state)) != FIQ_DEBUGGER_NO_CHAR) { + count++; + if (!state->debug_enable) { + if ((c == 13) || (c == 10)) { + state->debug_enable = true; + state->debug_count = 0; + debug_prompt(state); + } + } else if (c == FIQ_DEBUGGER_BREAK) { + state->console_enable = false; + debug_puts(state, "fiq debugger mode\n"); + state->debug_count = 0; + debug_prompt(state); +#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE + } else if (state->console_enable && state->tty_rbuf) { + fiq_debugger_ringbuf_push(state->tty_rbuf, c); + signal_helper = true; +#endif + } else if ((c >= ' ') && (c < 127)) { + if (state->debug_count < (DEBUG_MAX - 1)) { + state->debug_buf[state->debug_count++] = c; + debug_putc(state, c); + } + } else if ((c == 8) || (c == 127)) { + if (state->debug_count > 0) { + state->debug_count--; + debug_putc(state, 8); + debug_putc(state, ' '); + debug_putc(state, 8); + } + } else if ((c == 13) || (c == 10)) { + if (c == '\r' || (c == '\n' && last_c != '\r')) { + debug_putc(state, '\r'); + debug_putc(state, '\n'); + } + if (state->debug_count) { + state->debug_buf[state->debug_count] = 0; + state->debug_count = 0; + signal_helper |= + debug_fiq_exec(state, state->debug_buf, + regs, svc_sp); + } else { + debug_prompt(state); + } + } + last_c = c; + } + if (!state->console_enable) + debug_uart_flush(state); + if (state->pdata->fiq_ack) + state->pdata->fiq_ack(state->pdev, state->fiq); + + /* poke sleep timer if necessary */ + if (state->debug_enable && !state->no_sleep) + signal_helper = true; + + atomic_set(&state->unhandled_fiq_count, 0); + state->in_fiq = false; + + return signal_helper; +} + +static void debug_fiq(struct fiq_glue_handler *h, void *regs, void *svc_sp) +{ + struct fiq_debugger_state *state = + container_of(h, struct fiq_debugger_state, handler); + unsigned int this_cpu = THREAD_INFO(svc_sp)->cpu; + bool need_irq; + + need_irq = debug_handle_uart_interrupt(state, this_cpu, regs, svc_sp); + if (need_irq) + debug_force_irq(state); +} + +/* + * When not using FIQs, we only use this single interrupt as an entry point. + * This just effectively takes over the UART interrupt and does all the work + * in this context. + */ +static irqreturn_t debug_uart_irq(int irq, void *dev) +{ + struct fiq_debugger_state *state = dev; + bool not_done; + + handle_wakeup(state); + + /* handle the debugger irq in regular context */ + not_done = debug_handle_uart_interrupt(state, smp_processor_id(), + get_irq_regs(), + current_thread_info()); + if (not_done) + debug_handle_irq_context(state); + + return IRQ_HANDLED; +} + +/* + * If FIQs are used, not everything can happen in fiq context. + * FIQ handler does what it can and then signals this interrupt to finish the + * job in irq context. + */ +static irqreturn_t debug_signal_irq(int irq, void *dev) +{ + struct fiq_debugger_state *state = dev; + + if (state->pdata->force_irq_ack) + state->pdata->force_irq_ack(state->pdev, state->signal_irq); + + debug_handle_irq_context(state); + + return IRQ_HANDLED; +} + +static void debug_resume(struct fiq_glue_handler *h) +{ + struct fiq_debugger_state *state = + container_of(h, struct fiq_debugger_state, handler); + if (state->pdata->uart_resume) + state->pdata->uart_resume(state->pdev); +} + +#if defined(CONFIG_FIQ_DEBUGGER_CONSOLE) +struct tty_driver *debug_console_device(struct console *co, int *index) +{ + *index = co->index; + return fiq_tty_driver; +} + +static void debug_console_write(struct console *co, + const char *s, unsigned int count) +{ + struct fiq_debugger_state *state; + unsigned long flags; + + state = container_of(co, struct fiq_debugger_state, console); + + if (!state->console_enable && !state->syslog_dumping) + return; + + debug_uart_enable(state); + spin_lock_irqsave(&state->console_lock, flags); + while (count--) { + if (*s == '\n') + debug_putc(state, '\r'); + debug_putc(state, *s++); + } + debug_uart_flush(state); + spin_unlock_irqrestore(&state->console_lock, flags); + debug_uart_disable(state); +} + +static struct console fiq_debugger_console = { + .name = "ttyFIQ", + .device = debug_console_device, + .write = debug_console_write, + .flags = CON_PRINTBUFFER | CON_ANYTIME | CON_ENABLED, +}; + +int fiq_tty_open(struct tty_struct *tty, struct file *filp) +{ + int line = tty->index; + struct fiq_debugger_state **states = tty->driver->driver_state; + struct fiq_debugger_state *state = states[line]; + if (state->tty_open_count++) + return 0; + + tty->driver_data = state; + state->tty = tty; + return 0; +} + +void fiq_tty_close(struct tty_struct *tty, struct file *filp) +{ + struct fiq_debugger_state *state = tty->driver_data; + if (--state->tty_open_count) + return; + state->tty = NULL; +} + +int fiq_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) +{ + int i; + struct fiq_debugger_state *state = tty->driver_data; + + if (!state->console_enable) + return count; + + debug_uart_enable(state); + spin_lock_irq(&state->console_lock); + for (i = 0; i < count; i++) + debug_putc(state, *buf++); + spin_unlock_irq(&state->console_lock); + debug_uart_disable(state); + + return count; +} + +int fiq_tty_write_room(struct tty_struct *tty) +{ + return 16; +} + +#ifdef CONFIG_CONSOLE_POLL +static int fiq_tty_poll_init(struct tty_driver *driver, int line, char *options) +{ + return 0; +} + +static int fiq_tty_poll_get_char(struct tty_driver *driver, int line) +{ + struct fiq_debugger_state *state = driver->ttys[line]->driver_data; + int c = NO_POLL_CHAR; + + debug_uart_enable(state); + if (debug_have_fiq(state)) { + int count = fiq_debugger_ringbuf_level(state->tty_rbuf); + if (count > 0) { + c = fiq_debugger_ringbuf_peek(state->tty_rbuf, 0); + fiq_debugger_ringbuf_consume(state->tty_rbuf, 1); + } + } else { + c = debug_getc(state); + if (c == FIQ_DEBUGGER_NO_CHAR) + c = NO_POLL_CHAR; + } + debug_uart_disable(state); + + return c; +} + +static void fiq_tty_poll_put_char(struct tty_driver *driver, int line, char ch) +{ + struct fiq_debugger_state *state = driver->ttys[line]->driver_data; + debug_uart_enable(state); + debug_putc(state, ch); + debug_uart_disable(state); +} +#endif + +static const struct tty_operations fiq_tty_driver_ops = { + .write = fiq_tty_write, + .write_room = fiq_tty_write_room, + .open = fiq_tty_open, + .close = fiq_tty_close, +#ifdef CONFIG_CONSOLE_POLL + .poll_init = fiq_tty_poll_init, + .poll_get_char = fiq_tty_poll_get_char, + .poll_put_char = fiq_tty_poll_put_char, +#endif +}; + +static int fiq_debugger_tty_init(void) +{ + int ret; + struct fiq_debugger_state **states = NULL; + + states = kzalloc(sizeof(*states) * MAX_FIQ_DEBUGGER_PORTS, GFP_KERNEL); + if (!states) { + pr_err("Failed to allocate fiq debugger state structres\n"); + return -ENOMEM; + } + + fiq_tty_driver = alloc_tty_driver(MAX_FIQ_DEBUGGER_PORTS); + if (!fiq_tty_driver) { + pr_err("Failed to allocate fiq debugger tty\n"); + ret = -ENOMEM; + goto err_free_state; + } + + fiq_tty_driver->owner = THIS_MODULE; + fiq_tty_driver->driver_name = "fiq-debugger"; + fiq_tty_driver->name = "ttyFIQ"; + fiq_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + fiq_tty_driver->subtype = SERIAL_TYPE_NORMAL; + fiq_tty_driver->init_termios = tty_std_termios; + fiq_tty_driver->flags = TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV; + fiq_tty_driver->driver_state = states; + + fiq_tty_driver->init_termios.c_cflag = + B115200 | CS8 | CREAD | HUPCL | CLOCAL; + fiq_tty_driver->init_termios.c_ispeed = 115200; + fiq_tty_driver->init_termios.c_ospeed = 115200; + + tty_set_operations(fiq_tty_driver, &fiq_tty_driver_ops); + + ret = tty_register_driver(fiq_tty_driver); + if (ret) { + pr_err("Failed to register fiq tty: %d\n", ret); + goto err_free_tty; + } + + pr_info("Registered FIQ tty driver\n"); + return 0; + +err_free_tty: + put_tty_driver(fiq_tty_driver); + fiq_tty_driver = NULL; +err_free_state: + kfree(states); + return ret; +} + +static int fiq_debugger_tty_init_one(struct fiq_debugger_state *state) +{ + int ret; + struct device *tty_dev; + struct fiq_debugger_state **states = fiq_tty_driver->driver_state; + + states[state->pdev->id] = state; + + state->tty_rbuf = fiq_debugger_ringbuf_alloc(1024); + if (!state->tty_rbuf) { + pr_err("Failed to allocate fiq debugger ringbuf\n"); + ret = -ENOMEM; + goto err; + } + + tty_dev = tty_register_device(fiq_tty_driver, state->pdev->id, + &state->pdev->dev); + if (IS_ERR(tty_dev)) { + pr_err("Failed to register fiq debugger tty device\n"); + ret = PTR_ERR(tty_dev); + goto err; + } + + device_set_wakeup_capable(tty_dev, 1); + + pr_info("Registered fiq debugger ttyFIQ%d\n", state->pdev->id); + + return 0; + +err: + fiq_debugger_ringbuf_free(state->tty_rbuf); + state->tty_rbuf = NULL; + return ret; +} +#endif + +static int fiq_debugger_dev_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct fiq_debugger_state *state = platform_get_drvdata(pdev); + + if (state->pdata->uart_dev_suspend) + return state->pdata->uart_dev_suspend(pdev); + return 0; +} + +static int fiq_debugger_dev_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct fiq_debugger_state *state = platform_get_drvdata(pdev); + + if (state->pdata->uart_dev_resume) + return state->pdata->uart_dev_resume(pdev); + return 0; +} + +static int fiq_debugger_probe(struct platform_device *pdev) +{ + int ret; + struct fiq_debugger_pdata *pdata = dev_get_platdata(&pdev->dev); + struct fiq_debugger_state *state; + int fiq; + int uart_irq; + + if (pdev->id >= MAX_FIQ_DEBUGGER_PORTS) + return -EINVAL; + + if (!pdata->uart_getc || !pdata->uart_putc) + return -EINVAL; + if ((pdata->uart_enable && !pdata->uart_disable) || + (!pdata->uart_enable && pdata->uart_disable)) + return -EINVAL; + + fiq = platform_get_irq_byname(pdev, "fiq"); + uart_irq = platform_get_irq_byname(pdev, "uart_irq"); + + /* uart_irq mode and fiq mode are mutually exclusive, but one of them + * is required */ + if ((uart_irq < 0 && fiq < 0) || (uart_irq >= 0 && fiq >= 0)) + return -EINVAL; + if (fiq >= 0 && !pdata->fiq_enable) + return -EINVAL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + setup_timer(&state->sleep_timer, sleep_timer_expired, + (unsigned long)state); + state->pdata = pdata; + state->pdev = pdev; + state->no_sleep = initial_no_sleep; + state->debug_enable = initial_debug_enable; + state->console_enable = initial_console_enable; + + state->fiq = fiq; + state->uart_irq = uart_irq; + state->signal_irq = platform_get_irq_byname(pdev, "signal"); + state->wakeup_irq = platform_get_irq_byname(pdev, "wakeup"); + + INIT_WORK(&state->work, debug_work); + spin_lock_init(&state->work_lock); + + platform_set_drvdata(pdev, state); + + spin_lock_init(&state->sleep_timer_lock); + + if (state->wakeup_irq < 0 && debug_have_fiq(state)) + state->no_sleep = true; + state->ignore_next_wakeup_irq = !state->no_sleep; + + wake_lock_init(&state->debugger_wake_lock, + WAKE_LOCK_SUSPEND, "serial-debug"); + + state->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(state->clk)) + state->clk = NULL; + + /* do not call pdata->uart_enable here since uart_init may still + * need to do some initialization before uart_enable can work. + * So, only try to manage the clock during init. + */ + if (state->clk) + clk_enable(state->clk); + + if (pdata->uart_init) { + ret = pdata->uart_init(pdev); + if (ret) + goto err_uart_init; + } + + debug_printf_nfiq(state, "\n", + state->no_sleep ? "" : "twice "); + + if (debug_have_fiq(state)) { + state->handler.fiq = debug_fiq; + state->handler.resume = debug_resume; + ret = fiq_glue_register_handler(&state->handler); + if (ret) { + pr_err("%s: could not install fiq handler\n", __func__); + goto err_register_fiq; + } + + pdata->fiq_enable(pdev, state->fiq, 1); + } else { + ret = request_irq(state->uart_irq, debug_uart_irq, + IRQF_NO_SUSPEND, "debug", state); + if (ret) { + pr_err("%s: could not install irq handler\n", __func__); + goto err_register_irq; + } + + /* for irq-only mode, we want this irq to wake us up, if it + * can. + */ + enable_irq_wake(state->uart_irq); + } + + if (state->clk) + clk_disable(state->clk); + + if (state->signal_irq >= 0) { + ret = request_irq(state->signal_irq, debug_signal_irq, + IRQF_TRIGGER_RISING, "debug-signal", state); + if (ret) + pr_err("serial_debugger: could not install signal_irq"); + } + + if (state->wakeup_irq >= 0) { + ret = request_irq(state->wakeup_irq, wakeup_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_DISABLED, + "debug-wakeup", state); + if (ret) { + pr_err("serial_debugger: " + "could not install wakeup irq\n"); + state->wakeup_irq = -1; + } else { + ret = enable_irq_wake(state->wakeup_irq); + if (ret) { + pr_err("serial_debugger: " + "could not enable wakeup\n"); + state->wakeup_irq_no_set_wake = true; + } + } + } + if (state->no_sleep) + handle_wakeup(state); + +#if defined(CONFIG_FIQ_DEBUGGER_CONSOLE) + spin_lock_init(&state->console_lock); + state->console = fiq_debugger_console; + state->console.index = pdev->id; + if (!console_set_on_cmdline) + add_preferred_console(state->console.name, + state->console.index, NULL); + register_console(&state->console); + fiq_debugger_tty_init_one(state); +#endif + return 0; + +err_register_irq: +err_register_fiq: + if (pdata->uart_free) + pdata->uart_free(pdev); +err_uart_init: + if (state->clk) + clk_disable(state->clk); + if (state->clk) + clk_put(state->clk); + wake_lock_destroy(&state->debugger_wake_lock); + platform_set_drvdata(pdev, NULL); + kfree(state); + return ret; +} + +static const struct dev_pm_ops fiq_debugger_dev_pm_ops = { + .suspend = fiq_debugger_dev_suspend, + .resume = fiq_debugger_dev_resume, +}; + +static struct platform_driver fiq_debugger_driver = { + .probe = fiq_debugger_probe, + .driver = { + .name = "fiq_debugger", + .pm = &fiq_debugger_dev_pm_ops, + }, +}; + +static int __init fiq_debugger_init(void) +{ + fiq_debugger_tty_init(); + return platform_driver_register(&fiq_debugger_driver); +} + +postcore_initcall(fiq_debugger_init); diff --git a/arch/arm/common/fiq_debugger_ringbuf.h b/arch/arm/common/fiq_debugger_ringbuf.h new file mode 100644 index 00000000..2649b558 --- /dev/null +++ b/arch/arm/common/fiq_debugger_ringbuf.h @@ -0,0 +1,94 @@ +/* + * arch/arm/common/fiq_debugger_ringbuf.c + * + * simple lockless ringbuffer + * + * Copyright (C) 2010 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +struct fiq_debugger_ringbuf { + int len; + int head; + int tail; + u8 buf[]; +}; + + +static inline struct fiq_debugger_ringbuf *fiq_debugger_ringbuf_alloc(int len) +{ + struct fiq_debugger_ringbuf *rbuf; + + rbuf = kzalloc(sizeof(*rbuf) + len, GFP_KERNEL); + if (rbuf == NULL) + return NULL; + + rbuf->len = len; + rbuf->head = 0; + rbuf->tail = 0; + smp_mb(); + + return rbuf; +} + +static inline void fiq_debugger_ringbuf_free(struct fiq_debugger_ringbuf *rbuf) +{ + kfree(rbuf); +} + +static inline int fiq_debugger_ringbuf_level(struct fiq_debugger_ringbuf *rbuf) +{ + int level = rbuf->head - rbuf->tail; + + if (level < 0) + level = rbuf->len + level; + + return level; +} + +static inline int fiq_debugger_ringbuf_room(struct fiq_debugger_ringbuf *rbuf) +{ + return rbuf->len - fiq_debugger_ringbuf_level(rbuf) - 1; +} + +static inline u8 +fiq_debugger_ringbuf_peek(struct fiq_debugger_ringbuf *rbuf, int i) +{ + return rbuf->buf[(rbuf->tail + i) % rbuf->len]; +} + +static inline int +fiq_debugger_ringbuf_consume(struct fiq_debugger_ringbuf *rbuf, int count) +{ + count = min(count, fiq_debugger_ringbuf_level(rbuf)); + + rbuf->tail = (rbuf->tail + count) % rbuf->len; + smp_mb(); + + return count; +} + +static inline int +fiq_debugger_ringbuf_push(struct fiq_debugger_ringbuf *rbuf, u8 datum) +{ + if (fiq_debugger_ringbuf_room(rbuf) == 0) + return 0; + + rbuf->buf[rbuf->head] = datum; + smp_mb(); + rbuf->head = (rbuf->head + 1) % rbuf->len; + smp_mb(); + + return 1; +} diff --git a/arch/arm/common/fiq_glue.S b/arch/arm/common/fiq_glue.S new file mode 100644 index 00000000..9e3455a0 --- /dev/null +++ b/arch/arm/common/fiq_glue.S @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include + + .text + + .global fiq_glue_end + + /* fiq stack: r0-r15,cpsr,spsr of interrupted mode */ + +ENTRY(fiq_glue) + /* store pc, cpsr from previous mode */ + mrs r12, spsr + sub r11, lr, #4 + subs r10, #1 + bne nested_fiq + + stmfd sp!, {r11-r12, lr} + + /* store r8-r14 from previous mode */ + sub sp, sp, #(7 * 4) + stmia sp, {r8-r14}^ + nop + + /* store r0-r7 from previous mode */ + stmfd sp!, {r0-r7} + + /* setup func(data,regs) arguments */ + mov r0, r9 + mov r1, sp + mov r3, r8 + + mov r7, sp + + /* Get sp and lr from non-user modes */ + and r4, r12, #MODE_MASK + cmp r4, #USR_MODE + beq fiq_from_usr_mode + + mov r7, sp + orr r4, r4, #(PSR_I_BIT | PSR_F_BIT) + msr cpsr_c, r4 + str sp, [r7, #(4 * 13)] + str lr, [r7, #(4 * 14)] + mrs r5, spsr + str r5, [r7, #(4 * 17)] + + cmp r4, #(SVC_MODE | PSR_I_BIT | PSR_F_BIT) + /* use fiq stack if we reenter this mode */ + subne sp, r7, #(4 * 3) + +fiq_from_usr_mode: + msr cpsr_c, #(SVC_MODE | PSR_I_BIT | PSR_F_BIT) + mov r2, sp + sub sp, r7, #12 + stmfd sp!, {r2, ip, lr} + /* call func(data,regs) */ + blx r3 + ldmfd sp, {r2, ip, lr} + mov sp, r2 + + /* restore/discard saved state */ + cmp r4, #USR_MODE + beq fiq_from_usr_mode_exit + + msr cpsr_c, r4 + ldr sp, [r7, #(4 * 13)] + ldr lr, [r7, #(4 * 14)] + msr spsr_cxsf, r5 + +fiq_from_usr_mode_exit: + msr cpsr_c, #(FIQ_MODE | PSR_I_BIT | PSR_F_BIT) + + ldmfd sp!, {r0-r7} + add sp, sp, #(7 * 4) + ldmfd sp!, {r11-r12, lr} +exit_fiq: + msr spsr_cxsf, r12 + add r10, #1 + movs pc, r11 + +nested_fiq: + orr r12, r12, #(PSR_F_BIT) + b exit_fiq + +fiq_glue_end: + +ENTRY(fiq_glue_setup) /* func, data, sp */ + mrs r3, cpsr + msr cpsr_c, #(FIQ_MODE | PSR_I_BIT | PSR_F_BIT) + movs r8, r0 + mov r9, r1 + mov sp, r2 + moveq r10, #0 + movne r10, #1 + msr cpsr_c, r3 + bx lr + diff --git a/arch/arm/common/fiq_glue_setup.c b/arch/arm/common/fiq_glue_setup.c new file mode 100644 index 00000000..4044c7db --- /dev/null +++ b/arch/arm/common/fiq_glue_setup.c @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2010 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +extern unsigned char fiq_glue, fiq_glue_end; +extern void fiq_glue_setup(void *func, void *data, void *sp); + +static struct fiq_handler fiq_debbuger_fiq_handler = { + .name = "fiq_glue", +}; +DEFINE_PER_CPU(void *, fiq_stack); +static struct fiq_glue_handler *current_handler; +static DEFINE_MUTEX(fiq_glue_lock); + +static void fiq_glue_setup_helper(void *info) +{ + struct fiq_glue_handler *handler = info; + fiq_glue_setup(handler->fiq, handler, + __get_cpu_var(fiq_stack) + THREAD_START_SP); +} + +int fiq_glue_register_handler(struct fiq_glue_handler *handler) +{ + int ret; + int cpu; + + if (!handler || !handler->fiq) + return -EINVAL; + + mutex_lock(&fiq_glue_lock); + if (fiq_stack) { + ret = -EBUSY; + goto err_busy; + } + + for_each_possible_cpu(cpu) { + void *stack; + stack = (void *)__get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER); + if (WARN_ON(!stack)) { + ret = -ENOMEM; + goto err_alloc_fiq_stack; + } + per_cpu(fiq_stack, cpu) = stack; + } + + ret = claim_fiq(&fiq_debbuger_fiq_handler); + if (WARN_ON(ret)) + goto err_claim_fiq; + + current_handler = handler; + on_each_cpu(fiq_glue_setup_helper, handler, true); + set_fiq_handler(&fiq_glue, &fiq_glue_end - &fiq_glue); + + mutex_unlock(&fiq_glue_lock); + return 0; + +err_claim_fiq: +err_alloc_fiq_stack: + for_each_possible_cpu(cpu) { + __free_pages(per_cpu(fiq_stack, cpu), THREAD_SIZE_ORDER); + per_cpu(fiq_stack, cpu) = NULL; + } +err_busy: + mutex_unlock(&fiq_glue_lock); + return ret; +} + +/** + * fiq_glue_resume - Restore fiqs after suspend or low power idle states + * + * This must be called before calling local_fiq_enable after returning from a + * power state where the fiq mode registers were lost. If a driver provided + * a resume hook when it registered the handler it will be called. + */ + +void fiq_glue_resume(void) +{ + if (!current_handler) + return; + fiq_glue_setup(current_handler->fiq, current_handler, + __get_cpu_var(fiq_stack) + THREAD_START_SP); + if (current_handler->resume) + current_handler->resume(current_handler); +} + diff --git a/arch/arm/configs/ak3918ev300_hl_xiaofang_defconfig b/arch/arm/configs/ak3918ev300_hl_xiaofang_defconfig new file mode 100644 index 00000000..9f5fbb32 --- /dev/null +++ b/arch/arm/configs/ak3918ev300_hl_xiaofang_defconfig @@ -0,0 +1,1837 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.4.35 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_KTIME_SCALAR=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +# CONFIG_EXPERIMENTAL is not set +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_FHANDLE is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_SHOW=y + +# +# RCU Subsystem +# +CONFIG_TINY_RCU=y +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=16 +# CONFIG_CGROUPS is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=0 +CONFIG_EXPERT=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +# CONFIG_SHMEM is not set +CONFIG_AIO=y +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_SLUB_DEBUG is not set +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_JUMP_LABEL=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y + +# +# GCOV-based kernel profiling +# +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +CONFIG_ARCH_AK39=y +CONFIG_CPU_AK3916=y +# CONFIG_ARCH_CLOUD39E_AK3916E128PIN_MNBD is not set +CONFIG_ARCH_AK3918EV300_HL_XIAOFANG=y +CONFIG_ASIC_CLK_120MHZ=y + +# +# Power management +# +# CONFIG_AK39_PM is not set +CONFIG_AK39_PWM_TIMER=y +CONFIG_PLAT_ANYKA=y + +# +# Processor Type +# +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +CONFIG_CPU_USE_DOMAINS=y + +# +# Processor Features +# +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +# CONFIG_ARM_THUMB is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set +# CONFIG_CACHE_L2X0 is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_NR_BANKS=8 +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_PCCARD=y +CONFIG_PCMCIA=y + +# +# PC-card bridges +# + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_ARCH_NR_GPIO=0 +CONFIG_PREEMPT_NONE=y +CONFIG_PREEMPT_VOLUNTARY=y +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +# CONFIG_HIGHMEM is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_NEED_PER_CPU_KM=y +# CONFIG_CLEANCACHE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_SECCOMP is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y + +# +# Boot options +# +# CONFIG_USE_OF is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_CMDLINE="root=/dev/mtdblock1 ro rootfstype=squashfs init=/sbin/init mem=64M console=ttySAK0,115200" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_AUTO_ZRELADDR is not set +CONFIG_RAM_BASE=0x80000000 +CONFIG_VIDEO_RESERVED_MEM_SIZE=0x2200000 + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_WAKELOCK=y +CONFIG_PM_SLEEP=y +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_APM_EMULATION is not set +CONFIG_PM_CLK=y +CONFIG_CPU_PM=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET_LRO=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +CONFIG_INET_UDP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_IPV6 is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK_ACCT is not set +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +# CONFIG_NETFILTER_NETLINK_LOG is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NETFILTER_XTABLES is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV4 is not set +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_PHONET is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +CONFIG_BQL=y +CONFIG_HAVE_BPF_JIT=y +# CONFIG_BPF_JIT is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=y +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=y +# CONFIG_LIB80211_DEBUG is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y +CONFIG_MAC80211=y +CONFIG_MAC80211_HAS_RC=y +# CONFIG_MAC80211_RC_PID is not set +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +# CONFIG_MAC80211_LEDS is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_SYNC is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOCG3 is not set +CONFIG_MTD_AK_SPIFLASH=y +# CONFIG_MTD_AK_SPINAND is not set +# CONFIG_MTD_NAND_IDS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=2 +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=2 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ATMEL_PWM is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_BMP085 is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_WL127X_RFKILL is not set +CONFIG_AK_SERIAL_NUMBER=y +CONFIG_AK_GETAIN=y +CONFIG_AK_MOTOR=y +# CONFIG_AK_DAC_CLOCK is not set + +# +# user space generic gpio controller +# +CONFIG_GPIOS_AKCUSTOM=y + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set +CONFIG_AK39_PCM=y +CONFIG_H2_ISP_CHAR=y +CONFIG_USER_GPIO=y +CONFIG_AK_INFO_DUMP=m + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +# CONFIG_MII is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set + +# +# CAIF transport drivers +# +CONFIG_ETHERNET=y +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +# CONFIG_NET_VENDOR_FARADAY is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_ETHOC is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +CONFIG_AK_ETHERNET=y +# CONFIG_ETHERNET_MAC_MII is not set +CONFIG_ETHERNET_MAC_RMII=y +# CONFIG_PHYLIB is not set +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# USB Network Adapters +# +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_IPHETH is not set +CONFIG_WLAN=y +CONFIG_PCMCIA_RAYCS=y +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_RTL8187 is not set +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_WIFI_CONTROL_FUNC is not set +# CONFIG_ATH_COMMON is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_BCMDHD is not set +# CONFIG_BRCMFMAC is not set +# CONFIG_HOSTAP is not set +# CONFIG_LIBERTAS is not set +# CONFIG_RT2X00 is not set +# CONFIG_MWIFIEX is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_KEYRESET is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_KEYBOARD_AKKEY=y +# CONFIG_KEYBOARD_AK_KEYPAD is not set +# CONFIG_KEYBOARD_ADKEY is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_UNIX98_PTYS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +# CONFIG_DEVKMEM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +CONFIG_SERIAL_AK39_UART=y +CONFIG_SERIAL_AK39_CONSOLE=y +# CONFIG_SERIAL_GPIO_UART is not set +CONFIG_TTY_PRINTK=y +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_SMBUS=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_SIMTEC is not set +CONFIG_I2C_ANYKA=y +CONFIG_I2C_AK39_HW=y +# CONFIG_I2C_GPIO_SOFT is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +CONFIG_SPI_BITBANG=y +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_ANYKA=y +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_HSI is not set + +# +# PPS support +# + +# +# PPS generators support +# + +# +# PTP clock support +# + +# +# Enable Device Drivers -> PPS to see the PTP clock options. +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_DW_WATCHDOG is not set +# CONFIG_MAX63XX_WATCHDOG is not set +# CONFIG_AK39_WATCHDOG is not set +CONFIG_AK39_WATCHDOG_TOP=y +# CONFIG_AK39_WATCHDOG_NONE is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_S5M_CORE is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_REGULATOR is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +# CONFIG_DVB_CORE is not set +CONFIG_VIDEO_MEDIA=y + +# +# Multimedia drivers +# +# CONFIG_RC_CORE is not set +# CONFIG_MEDIA_ATTACH is not set +CONFIG_MEDIA_TUNER=y +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_MEDIA_TUNER_SIMPLE=y +CONFIG_MEDIA_TUNER_TDA8290=y +CONFIG_MEDIA_TUNER_TDA827X=y +CONFIG_MEDIA_TUNER_TDA18271=y +CONFIG_MEDIA_TUNER_TDA9887=y +CONFIG_MEDIA_TUNER_TEA5767=y +CONFIG_MEDIA_TUNER_MT20XX=y +CONFIG_MEDIA_TUNER_XC2028=y +CONFIG_MEDIA_TUNER_XC5000=y +CONFIG_MEDIA_TUNER_XC4000=y +CONFIG_MEDIA_TUNER_MC44S803=y +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEOBUF_GEN=y +CONFIG_VIDEOBUF_DMA_CONTIG=y +CONFIG_VIDEOBUF2_CORE=y +CONFIG_VIDEO_CAPTURE_DRIVERS=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set + +# +# Encoders, decoders, sensors and other helper chips +# + +# +# Audio decoders, processors and mixers +# +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_VP27SMPX is not set + +# +# RDS decoders +# +# CONFIG_VIDEO_SAA6588 is not set + +# +# Video decoders +# +# CONFIG_VIDEO_ADV7180 is not set +# CONFIG_VIDEO_ADV7183 is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA7191 is not set +# CONFIG_VIDEO_TVP514X is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_TVP7002 is not set +# CONFIG_VIDEO_VPX3220 is not set + +# +# Video and audio decoders +# +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_CX25840 is not set + +# +# MPEG video encoders +# +# CONFIG_VIDEO_CX2341X is not set + +# +# Video encoders +# +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_ADV7343 is not set +# CONFIG_VIDEO_AK881X is not set + +# +# Camera sensor devices +# +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_VS6624 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_SR030PC30 is not set + +# +# Flash devices +# + +# +# Video improvement chips +# +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set + +# +# Miscelaneous helper chips +# +# CONFIG_VIDEO_THS7303 is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_V4L_USB_DRIVERS is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_SOC_CAMERA=y +# CONFIG_SOC_CAMERA_IMX074 is not set +# CONFIG_SOC_CAMERA_MT9M001 is not set +# CONFIG_SOC_CAMERA_MT9M111 is not set +# CONFIG_SOC_CAMERA_MT9T031 is not set +# CONFIG_SOC_CAMERA_MT9T112 is not set +# CONFIG_SOC_CAMERA_MT9V022 is not set +# CONFIG_SOC_CAMERA_RJ54N1 is not set +# CONFIG_SOC_CAMERA_TW9910 is not set +# CONFIG_SOC_CAMERA_PLATFORM is not set +# CONFIG_SOC_CAMERA_OV2640 is not set +# CONFIG_SOC_CAMERA_OV5642 is not set +# CONFIG_SOC_CAMERA_OV6650 is not set +# CONFIG_SOC_CAMERA_OV772X is not set +# CONFIG_SOC_CAMERA_OV9640 is not set +# CONFIG_SOC_CAMERA_OV9740 is not set +CONFIG_LINUX_AKSENSOR=y +CONFIG_AK_SENSOR_I2C=y +CONFIG_AK_SENSOR_COMMON=y +CONFIG_SENSOR_OV9712=m +CONFIG_SENSOR_H42=m +CONFIG_SENSOR_AR0130=m +CONFIG_SENSOR_SC1045=m +CONFIG_SENSOR_SC1035=m +CONFIG_SENSOR_SC1135=m +CONFIG_SENSOR_GC1024=m +# CONFIG_SENSOR_GM7150 is not set +CONFIG_SENSOR_SC1145=m +CONFIG_SENSOR_H61=m +# CONFIG_SENSOR_OV9732 is not set +# CONFIG_VIDEO_SH_MOBILE_CSI2 is not set +# CONFIG_VIDEO_SH_MOBILE_CEU is not set +CONFIG_VIDEO_AK=m +CONFIG_AK_VIDEOBUF_DMA_CONTIG=y +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +# CONFIG_RADIO_ADAPTERS is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_ION=y +CONFIG_ION_AK=y +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_SOUND is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +CONFIG_USB_ANYKA_HCD=y +CONFIG_USB_AKOTG_HS_HCD=m +CONFIG_USB_AKOTG_DMA=y +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_RENESAS_USBHS is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_MV_UDC is not set +CONFIG_USB_AKUDC_PRODUCER=m +# CONFIG_USB_GADGET_AKUDC is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +CONFIG_USB_MASS_STORAGE=m +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_ACM_MS is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_G_WEBCAM is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_OTG_WAKELOCK is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set +# CONFIG_SDIO_WIFI is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_PXAV2 is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +CONFIG_MMC_ANYKA=y +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_GPIO is not set +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA9633 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_BD2802 is not set +CONFIG_LEDS_AK39=y +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_RENESAS_TPU is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_OT200 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_SWITCH is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_AK=y +CONFIG_AK39_RTC_SELECT_CLOCK=y +CONFIG_AK39_RTC_INTERNAL_RC_OSC=y +# CONFIG_AK39_RTC_EXTERNAL_XTAL is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +CONFIG_UIO=y +# CONFIG_UIO_PDRV is not set +# CONFIG_UIO_PDRV_GENIRQ is not set +CONFIG_UIO_VCODEC=y + +# +# Virtio drivers +# +# CONFIG_VIRTIO_BALLOON is not set + +# +# Microsoft Hyper-V guest support +# +# CONFIG_STAGING is not set +CONFIG_CLKDEV_LOOKUP=y + +# +# Hardware Spinlock drivers +# +# CONFIG_IOMMU_SUPPORT is not set + +# +# Remoteproc drivers (EXPERIMENTAL) +# + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_AEC_DUMP_DEBUG is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT/EXFAT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +# CONFIG_FAT_FCBMP is not set +# CONFIG_FAT_FSCK is not set +# CONFIG_FAT_CHK_DISK is not set +CONFIG_FAT_NOT_CHKDSK=y +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set +CONFIG_EXFAT_FS=y +CONFIG_EXFAT_DISCARD=y +# CONFIG_EXFAT_DELAYED_SYNC is not set +# CONFIG_EXFAT_KERNEL_DEBUG is not set +# CONFIG_EXFAT_DEBUG_MSG is not set +CONFIG_EXFAT_DEFAULT_CODEPAGE=437 +CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8" + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_HFSPLUS_FS is not set +# CONFIG_YAFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_WRITEBUFFER is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +# CONFIG_JFFS2_ZLIB is not set +# CONFIG_JFFS2_LZO is not set +# CONFIG_JFFS2_RTIME is not set +# CONFIG_JFFS2_RUBIN is not set +CONFIG_JFFS2_CMODE_NONE=y +# CONFIG_JFFS2_CMODE_PRIORITY is not set +# CONFIG_JFFS2_CMODE_SIZE is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +# CONFIG_CRAMFS is not set +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_XATTR is not set +CONFIG_SQUASHFS_ZLIB=y +# CONFIG_SQUASHFS_LZO is not set +CONFIG_SQUASHFS_XZ=y +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +# CONFIG_NETWORK_FILESYSTEMS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR_NMI is not set +# CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_STACKTRACE is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_RODATA is not set +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_MANAGER2 is not set +# CONFIG_CRYPTO_USER is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +# CONFIG_CRYPTO_DEV is not set +# CONFIG_CRYPTO_HW is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +# CONFIG_CRC_CCITT is not set +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_CRC8 is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_XZ_DEC=y +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_IA64 is not set +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +# CONFIG_XZ_DEC_SPARC is not set +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_AVERAGE=y +# CONFIG_CORDIC is not set diff --git a/arch/arm/configs/ak3918ev300_jw_f37_v10_defconfig b/arch/arm/configs/ak3918ev300_jw_f37_v10_defconfig new file mode 100644 index 00000000..35ab1c1a --- /dev/null +++ b/arch/arm/configs/ak3918ev300_jw_f37_v10_defconfig @@ -0,0 +1,1900 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.4.35 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +CONFIG_ARCH_USES_GETTIMEOFFSET=y +CONFIG_KTIME_SCALAR=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +# CONFIG_EXPERIMENTAL is not set +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_FHANDLE is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_SHOW=y + +# +# RCU Subsystem +# +CONFIG_TINY_RCU=y +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=16 +# CONFIG_CGROUPS is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=0 +CONFIG_EXPERT=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +# CONFIG_SHMEM is not set +CONFIG_AIO=y +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_SLUB_DEBUG is not set +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_JUMP_LABEL=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y + +# +# GCOV-based kernel profiling +# +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +CONFIG_ARCH_AK39=y +CONFIG_CPU_AK3918=y +# CONFIG_ARCH_CLOUD39EV3_AK3916E128PIN_COREBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3916EV300_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3918EV300_MAINBD=y +# CONFIG_ARCH_CLOUD39EV3_AK3918EV300_YTJ_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3919EV300_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_CO38_SC2235 is not set +# CONFIG_ARCH_CLOUD39EV3_AK3919EV300_SQ38_SC2232_V1_0_0 is not set +# CONFIG_ARCH_CLOUD39EV3_AK3919EV300_SQ38_SC2232_V1_0_1 is not set +# CONFIG_ARCH_CLOUD39EV3_SQ38_QFN64_F22_V100 is not set +CONFIG_ARCH_AK3918EV300_JW_F37_V10=y +CONFIG_ASIC_CLK_120MHZ=y + +# +# Power management +# +# CONFIG_AK39_PM is not set +CONFIG_AK39_PWM_TIMER=y +CONFIG_PLAT_ANYKA=y + +# +# Processor Type +# +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +CONFIG_CPU_USE_DOMAINS=y + +# +# Processor Features +# +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +# CONFIG_ARM_THUMB is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set +# CONFIG_CACHE_L2X0 is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_NR_BANKS=8 +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_PCCARD=y +CONFIG_PCMCIA=y + +# +# PC-card bridges +# + +# +# Kernel Features +# +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_ARCH_NR_GPIO=0 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +# CONFIG_HIGHMEM is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_NEED_PER_CPU_KM=y +# CONFIG_CLEANCACHE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_SECCOMP is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y + +# +# Boot options +# +# CONFIG_USE_OF is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_CMDLINE="root=/dev/mtdblock1 ro rootfstype=squashfs init=/sbin/init mem=64M console=ttySAK0,115200" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_AUTO_ZRELADDR is not set +CONFIG_RAM_BASE=0x80000000 +CONFIG_VIDEO_RESERVED_MEM_SIZE=0x2200000 + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_WAKELOCK=y +CONFIG_PM_SLEEP=y +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_APM_EMULATION is not set +CONFIG_PM_CLK=y +CONFIG_CPU_PM=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET_LRO=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +CONFIG_INET_UDP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_IPV6 is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK_ACCT is not set +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +# CONFIG_NETFILTER_NETLINK_LOG is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NETFILTER_XTABLES is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV4 is not set +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_PHONET is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +CONFIG_BQL=y +CONFIG_HAVE_BPF_JIT=y +# CONFIG_BPF_JIT is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=y +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=y +# CONFIG_LIB80211_DEBUG is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y +CONFIG_MAC80211=y +CONFIG_MAC80211_HAS_RC=y +# CONFIG_MAC80211_RC_PID is not set +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +# CONFIG_MAC80211_LEDS is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_SYNC is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOCG3 is not set +CONFIG_MTD_AK_SPIFLASH=y +# CONFIG_MTD_AK_SPINAND is not set +# CONFIG_MTD_NAND_IDS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=2 +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=2 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_ANYKA is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ATMEL_PWM is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_BMP085 is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_WL127X_RFKILL is not set +CONFIG_AK_SERIAL_NUMBER=y +CONFIG_AK_GETAIN=y +# CONFIG_AK_MOTOR is not set +# CONFIG_AK_DAC_CLOCK is not set + +# +# user space generic gpio controller +# +CONFIG_GPIOS_AKCUSTOM=m + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set +CONFIG_AK39_PCM=y +CONFIG_H2_ISP_CHAR=y +CONFIG_USER_GPIO=y +CONFIG_AK_INFO_DUMP=m +CONFIG_AK_PWM_CHAR=y +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +# CONFIG_MII is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_ARCNET is not set + +# +# CAIF transport drivers +# +CONFIG_ETHERNET=y +CONFIG_NET_VENDOR_3COM=y +# CONFIG_PCMCIA_3C574 is not set +# CONFIG_PCMCIA_3C589 is not set +CONFIG_NET_VENDOR_AMD=y +# CONFIG_PCMCIA_NMCLAN is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +# CONFIG_NET_VENDOR_FARADAY is not set +CONFIG_NET_VENDOR_FUJITSU=y +# CONFIG_PCMCIA_FMVJ18X is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_ETHOC is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +CONFIG_NET_VENDOR_XIRCOM=y +# CONFIG_PCMCIA_XIRC2PS is not set +CONFIG_AK_ETHERNET=y +# CONFIG_ETHERNET_MAC_MII is not set +CONFIG_ETHERNET_MAC_RMII=y +# CONFIG_PHYLIB is not set +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_TR is not set + +# +# USB Network Adapters +# +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_IPHETH is not set +CONFIG_WLAN=y +CONFIG_PCMCIA_RAYCS=y +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_ATMEL is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_AIRO_CS is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_RTL8187 is not set +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_WIFI_CONTROL_FUNC is not set +# CONFIG_ATH_COMMON is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_BCMDHD is not set +# CONFIG_BRCMFMAC is not set +# CONFIG_HOSTAP is not set +# CONFIG_LIBERTAS is not set +# CONFIG_HERMES is not set +# CONFIG_RT2X00 is not set +# CONFIG_MWIFIEX is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_KEYRESET is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_KEYBOARD_AKKEY=y +# CONFIG_KEYBOARD_AK_KEYPAD is not set +# CONFIG_KEYBOARD_ADKEY is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_UNIX98_PTYS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +# CONFIG_DEVKMEM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +CONFIG_SERIAL_AK39_UART=y +CONFIG_SERIAL_AK39_CONSOLE=y +# CONFIG_SERIAL_GPIO_UART is not set +CONFIG_TTY_PRINTK=y +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_CARDMAN_4000 is not set +# CONFIG_CARDMAN_4040 is not set +# CONFIG_IPWIRELESS is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_SMBUS=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_SIMTEC is not set +CONFIG_I2C_ANYKA=y +CONFIG_I2C_AK39_HW=y +# CONFIG_I2C_GPIO_SOFT is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +CONFIG_SPI_BITBANG=y +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_ANYKA=y +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_HSI is not set + +# +# PPS support +# + +# +# PPS generators support +# + +# +# PTP clock support +# + +# +# Enable Device Drivers -> PPS to see the PTP clock options. +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_DW_WATCHDOG is not set +# CONFIG_MAX63XX_WATCHDOG is not set +# CONFIG_AK39_WATCHDOG is not set +CONFIG_AK39_WATCHDOG_TOP=y +# CONFIG_AK39_WATCHDOG_NONE is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_S5M_CORE is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_REGULATOR is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +# CONFIG_DVB_CORE is not set +CONFIG_VIDEO_MEDIA=y + +# +# Multimedia drivers +# +# CONFIG_RC_CORE is not set +# CONFIG_MEDIA_ATTACH is not set +CONFIG_MEDIA_TUNER=y +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_MEDIA_TUNER_SIMPLE=y +CONFIG_MEDIA_TUNER_TDA8290=y +CONFIG_MEDIA_TUNER_TDA827X=y +CONFIG_MEDIA_TUNER_TDA18271=y +CONFIG_MEDIA_TUNER_TDA9887=y +CONFIG_MEDIA_TUNER_TEA5767=y +CONFIG_MEDIA_TUNER_MT20XX=y +CONFIG_MEDIA_TUNER_XC2028=y +CONFIG_MEDIA_TUNER_XC5000=y +CONFIG_MEDIA_TUNER_XC4000=y +CONFIG_MEDIA_TUNER_MC44S803=y +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEOBUF_GEN=y +CONFIG_VIDEOBUF_DMA_CONTIG=y +CONFIG_VIDEOBUF2_CORE=y +CONFIG_VIDEO_CAPTURE_DRIVERS=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set + +# +# Encoders, decoders, sensors and other helper chips +# + +# +# Audio decoders, processors and mixers +# +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_VP27SMPX is not set + +# +# RDS decoders +# +# CONFIG_VIDEO_SAA6588 is not set + +# +# Video decoders +# +# CONFIG_VIDEO_ADV7180 is not set +# CONFIG_VIDEO_ADV7183 is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA7191 is not set +# CONFIG_VIDEO_TVP514X is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_TVP7002 is not set +# CONFIG_VIDEO_VPX3220 is not set + +# +# Video and audio decoders +# +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_CX25840 is not set + +# +# MPEG video encoders +# +# CONFIG_VIDEO_CX2341X is not set + +# +# Video encoders +# +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_ADV7343 is not set +# CONFIG_VIDEO_AK881X is not set + +# +# Camera sensor devices +# +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_VS6624 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_SR030PC30 is not set + +# +# Flash devices +# + +# +# Video improvement chips +# +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set + +# +# Miscelaneous helper chips +# +# CONFIG_VIDEO_THS7303 is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_V4L_USB_DRIVERS is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_SOC_CAMERA=y +# CONFIG_SOC_CAMERA_IMX074 is not set +# CONFIG_SOC_CAMERA_MT9M001 is not set +# CONFIG_SOC_CAMERA_MT9M111 is not set +# CONFIG_SOC_CAMERA_MT9T031 is not set +# CONFIG_SOC_CAMERA_MT9T112 is not set +# CONFIG_SOC_CAMERA_MT9V022 is not set +# CONFIG_SOC_CAMERA_RJ54N1 is not set +# CONFIG_SOC_CAMERA_TW9910 is not set +# CONFIG_SOC_CAMERA_PLATFORM is not set +# CONFIG_SOC_CAMERA_OV2640 is not set +# CONFIG_SOC_CAMERA_OV5642 is not set +# CONFIG_SOC_CAMERA_OV6650 is not set +# CONFIG_SOC_CAMERA_OV772X is not set +# CONFIG_SOC_CAMERA_OV9640 is not set +# CONFIG_SOC_CAMERA_OV9740 is not set +CONFIG_LINUX_AKSENSOR=y +CONFIG_AK_SENSOR_I2C=y +CONFIG_AK_SENSOR_COMMON=y +# CONFIG_SENSOR_OV9712 is not set +# CONFIG_SENSOR_H42 is not set +# CONFIG_SENSOR_AR0130 is not set +# CONFIG_SENSOR_SC1045 is not set +# CONFIG_SENSOR_SC1035 is not set +# CONFIG_SENSOR_SC1135 is not set +# CONFIG_SENSOR_GC1024 is not set +# CONFIG_SENSOR_GM7150 is not set +# CONFIG_SENSOR_SC1145 is not set +# CONFIG_SENSOR_H61 is not set +# CONFIG_SENSOR_H65 is not set +# CONFIG_SENSOR_H62 is not set +# CONFIG_SENSOR_OV9732 is not set +# CONFIG_SENSOR_SC1037 is not set +# CONFIG_SENSOR_GC1034 is not set +# CONFIG_SENSOR_GC2033 is not set +# CONFIG_SENSOR_GC2023 is not set +# CONFIG_SENSOR_GC2053 is not set +# CONFIG_SENSOR_SC2235 is not set +# CONFIG_SENSOR_SC2232 is not set +# CONFIG_SENSOR_SC4236 is not set +# CONFIG_SENSOR_SC2145 is not set +# CONFIG_SENSOR_F22 is not set +CONFIG_SENSOR_F23=m +# CONFIG_SENSOR_F28 is not set +# CONFIG_SENSOR_HM2140 is not set +# CONFIG_SENSOR_IMX323 is not set +# CONFIG_SENSOR_IMX307 is not set +# CONFIG_SENSOR_PS5270 is not set +# CONFIG_SENSOR_SC2311 is not set +# CONFIG_SENSOR_PS5260 is not set +# CONFIG_VIDEO_SH_MOBILE_CSI2 is not set +# CONFIG_VIDEO_SH_MOBILE_CEU is not set +CONFIG_VIDEO_AK=m +CONFIG_AK_VIDEOBUF_DMA_CONTIG=y +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +# CONFIG_RADIO_ADAPTERS is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_ION=y +CONFIG_ION_AK=y +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_SOUND is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +CONFIG_USB_ANYKA_HCD=y +CONFIG_USB_AKOTG_HS_HCD=m +# CONFIG_USB_AKOTG_DMA is not set +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_RENESAS_USBHS is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_MV_UDC is not set +CONFIG_USB_AKUDC_PRODUCER=m +# CONFIG_USB_GADGET_AKUDC is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +CONFIG_USB_MASS_STORAGE=m +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_ACM_MS is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_G_WEBCAM is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_OTG_WAKELOCK is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set +# CONFIG_SDIO_WIFI is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_PXAV2 is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +CONFIG_MMC_ANYKA=y +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_GPIO is not set +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA9633 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_BD2802 is not set +CONFIG_LEDS_AK39=y +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_RENESAS_TPU is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_OT200 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_SWITCH is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_AK=y +CONFIG_AK39_RTC_SELECT_CLOCK=y +CONFIG_AK39_RTC_INTERNAL_RC_OSC=y +# CONFIG_AK39_RTC_EXTERNAL_XTAL is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +CONFIG_UIO=y +# CONFIG_UIO_PDRV is not set +# CONFIG_UIO_PDRV_GENIRQ is not set +CONFIG_UIO_VCODEC=y + +# +# Virtio drivers +# +# CONFIG_VIRTIO_BALLOON is not set + +# +# Microsoft Hyper-V guest support +# +# CONFIG_STAGING is not set +CONFIG_CLKDEV_LOOKUP=y + +# +# Hardware Spinlock drivers +# +# CONFIG_IOMMU_SUPPORT is not set + +# +# Remoteproc drivers (EXPERIMENTAL) +# + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_AEC_DUMP_DEBUG is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT/EXFAT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +# CONFIG_FAT_FCBMP is not set +# CONFIG_FAT_FSCK is not set +# CONFIG_FAT_CHK_DISK is not set +CONFIG_FAT_NOT_CHKDSK=y +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set +CONFIG_EXFAT_FS=y +CONFIG_EXFAT_DISCARD=y +# CONFIG_EXFAT_DELAYED_SYNC is not set +# CONFIG_EXFAT_KERNEL_DEBUG is not set +# CONFIG_EXFAT_DEBUG_MSG is not set +CONFIG_EXFAT_DEFAULT_CODEPAGE=437 +CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8" + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_HFSPLUS_FS is not set +# CONFIG_YAFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_WRITEBUFFER is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +# CONFIG_JFFS2_ZLIB is not set +# CONFIG_JFFS2_LZO is not set +# CONFIG_JFFS2_RTIME is not set +# CONFIG_JFFS2_RUBIN is not set +CONFIG_JFFS2_CMODE_NONE=y +# CONFIG_JFFS2_CMODE_PRIORITY is not set +# CONFIG_JFFS2_CMODE_SIZE is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +# CONFIG_CRAMFS is not set +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_XATTR is not set +CONFIG_SQUASHFS_ZLIB=y +# CONFIG_SQUASHFS_LZO is not set +CONFIG_SQUASHFS_XZ=y +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_DEBUG is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR_NMI is not set +# CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_STACKTRACE is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_RODATA is not set +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_MANAGER2 is not set +# CONFIG_CRYPTO_USER is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +# CONFIG_CRYPTO_DEV is not set +# CONFIG_CRYPTO_HW is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +# CONFIG_CRC_CCITT is not set +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_CRC8 is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_XZ_DEC=y +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_IA64 is not set +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +# CONFIG_XZ_DEC_SPARC is not set +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_AVERAGE=y +# CONFIG_CORDIC is not set diff --git a/arch/arm/configs/ak3918ev300_td_c80dx_defconfig b/arch/arm/configs/ak3918ev300_td_c80dx_defconfig new file mode 100644 index 00000000..7eaa7ed9 --- /dev/null +++ b/arch/arm/configs/ak3918ev300_td_c80dx_defconfig @@ -0,0 +1,1895 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.4.35 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_KTIME_SCALAR=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +# CONFIG_EXPERIMENTAL is not set +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_FHANDLE is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_SHOW=y + +# +# RCU Subsystem +# +CONFIG_TINY_RCU=y +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=16 +# CONFIG_CGROUPS is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=0 +CONFIG_EXPERT=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +# CONFIG_SHMEM is not set +CONFIG_AIO=y +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_SLUB_DEBUG is not set +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_JUMP_LABEL=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y + +# +# GCOV-based kernel profiling +# +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +CONFIG_ARCH_AK39=y +CONFIG_CPU_AK3918=y +# CONFIG_ARCH_CLOUD39EV3_AK3916E128PIN_COREBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3916EV300_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3918EV300_MAINBD is not set +CONFIG_ARCH_AK3918EV300_C80DX=y +# CONFIG_ARCH_CLOUD39EV3_AK3919EV300_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_CO38_SC2235 is not set +# CONFIG_ARCH_CLOUD39EV3_AK3919EV300_SQ38_SC2232 is not set +# CONFIG_ARCH_CLOUD39EV3_SQ38_QFN64_F22_V100 is not set +CONFIG_ASIC_CLK_120MHZ=y + +# +# Power management +# +# CONFIG_AK39_PM is not set +CONFIG_AK39_PWM_TIMER=y +CONFIG_PLAT_ANYKA=y + +# +# Processor Type +# +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +CONFIG_CPU_USE_DOMAINS=y + +# +# Processor Features +# +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +# CONFIG_ARM_THUMB is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set +# CONFIG_CACHE_L2X0 is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_NR_BANKS=8 +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_PCCARD=y +CONFIG_PCMCIA=y + +# +# PC-card bridges +# + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_ARCH_NR_GPIO=0 +CONFIG_PREEMPT_NONE=y +CONFIG_PREEMPT_VOLUNTARY=y +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +# CONFIG_HIGHMEM is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_NEED_PER_CPU_KM=y +# CONFIG_CLEANCACHE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_SECCOMP is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y + +# +# Boot options +# +# CONFIG_USE_OF is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_CMDLINE="root=/dev/mtdblock1 ro rootfstype=squashfs init=/sbin/init mem=64M console=ttySAK0,115200" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_AUTO_ZRELADDR is not set +CONFIG_RAM_BASE=0x80000000 +CONFIG_VIDEO_RESERVED_MEM_SIZE=0x2200000 + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_WAKELOCK=y +CONFIG_PM_SLEEP=y +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_APM_EMULATION is not set +CONFIG_PM_CLK=y +CONFIG_CPU_PM=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET_LRO=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +CONFIG_INET_UDP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_IPV6 is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK_ACCT is not set +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +# CONFIG_NETFILTER_NETLINK_LOG is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NETFILTER_XTABLES is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV4 is not set +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_PHONET is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +CONFIG_BQL=y +CONFIG_HAVE_BPF_JIT=y +# CONFIG_BPF_JIT is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=y +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=y +# CONFIG_LIB80211_DEBUG is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y +CONFIG_MAC80211=y +CONFIG_MAC80211_HAS_RC=y +# CONFIG_MAC80211_RC_PID is not set +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +# CONFIG_MAC80211_LEDS is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_SYNC is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOCG3 is not set +CONFIG_MTD_AK_SPIFLASH=y +# CONFIG_MTD_AK_SPINAND is not set +# CONFIG_MTD_NAND_IDS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=2 +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=2 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_ANYKA is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ATMEL_PWM is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_BMP085 is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_WL127X_RFKILL is not set +CONFIG_AK_SERIAL_NUMBER=y +CONFIG_AK_GETAIN=y +CONFIG_AK_MOTOR=y +# CONFIG_AK_DAC_CLOCK is not set + +# +# user space generic gpio controller +# +CONFIG_GPIOS_AKCUSTOM=y + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set +CONFIG_AK39_PCM=y +CONFIG_H2_ISP_CHAR=y +CONFIG_USER_GPIO=y +CONFIG_AK_INFO_DUMP=m +CONFIG_AK_PWM_CHAR=y +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +# CONFIG_MII is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_ARCNET is not set + +# +# CAIF transport drivers +# +CONFIG_ETHERNET=y +CONFIG_NET_VENDOR_3COM=y +# CONFIG_PCMCIA_3C574 is not set +# CONFIG_PCMCIA_3C589 is not set +CONFIG_NET_VENDOR_AMD=y +# CONFIG_PCMCIA_NMCLAN is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +# CONFIG_NET_VENDOR_FARADAY is not set +CONFIG_NET_VENDOR_FUJITSU=y +# CONFIG_PCMCIA_FMVJ18X is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_ETHOC is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +CONFIG_NET_VENDOR_XIRCOM=y +# CONFIG_PCMCIA_XIRC2PS is not set +CONFIG_AK_ETHERNET=y +# CONFIG_ETHERNET_MAC_MII is not set +CONFIG_ETHERNET_MAC_RMII=y +# CONFIG_PHYLIB is not set +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_TR is not set + +# +# USB Network Adapters +# +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_IPHETH is not set +CONFIG_WLAN=y +CONFIG_PCMCIA_RAYCS=y +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_ATMEL is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_AIRO_CS is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_RTL8187 is not set +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_WIFI_CONTROL_FUNC is not set +# CONFIG_ATH_COMMON is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_BCMDHD is not set +# CONFIG_BRCMFMAC is not set +# CONFIG_HOSTAP is not set +# CONFIG_LIBERTAS is not set +# CONFIG_HERMES is not set +# CONFIG_RT2X00 is not set +# CONFIG_MWIFIEX is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_KEYRESET is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_KEYBOARD_AKKEY=y +# CONFIG_KEYBOARD_AK_KEYPAD is not set +CONFIG_KEYBOARD_ADKEY=y +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_UNIX98_PTYS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +# CONFIG_DEVKMEM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +CONFIG_SERIAL_AK39_UART=y +CONFIG_SERIAL_AK39_CONSOLE=y +# CONFIG_SERIAL_GPIO_UART is not set +CONFIG_TTY_PRINTK=y +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_CARDMAN_4000 is not set +# CONFIG_CARDMAN_4040 is not set +# CONFIG_IPWIRELESS is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_SMBUS=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_SIMTEC is not set +CONFIG_I2C_ANYKA=y +CONFIG_I2C_AK39_HW=y +# CONFIG_I2C_GPIO_SOFT is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +CONFIG_SPI_BITBANG=y +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_ANYKA=y +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_HSI is not set + +# +# PPS support +# + +# +# PPS generators support +# + +# +# PTP clock support +# + +# +# Enable Device Drivers -> PPS to see the PTP clock options. +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_DW_WATCHDOG is not set +# CONFIG_MAX63XX_WATCHDOG is not set +# CONFIG_AK39_WATCHDOG is not set +CONFIG_AK39_WATCHDOG_TOP=y +# CONFIG_AK39_WATCHDOG_NONE is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_S5M_CORE is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_REGULATOR is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +# CONFIG_DVB_CORE is not set +CONFIG_VIDEO_MEDIA=y + +# +# Multimedia drivers +# +# CONFIG_RC_CORE is not set +# CONFIG_MEDIA_ATTACH is not set +CONFIG_MEDIA_TUNER=y +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_MEDIA_TUNER_SIMPLE=y +CONFIG_MEDIA_TUNER_TDA8290=y +CONFIG_MEDIA_TUNER_TDA827X=y +CONFIG_MEDIA_TUNER_TDA18271=y +CONFIG_MEDIA_TUNER_TDA9887=y +CONFIG_MEDIA_TUNER_TEA5767=y +CONFIG_MEDIA_TUNER_MT20XX=y +CONFIG_MEDIA_TUNER_XC2028=y +CONFIG_MEDIA_TUNER_XC5000=y +CONFIG_MEDIA_TUNER_XC4000=y +CONFIG_MEDIA_TUNER_MC44S803=y +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEOBUF_GEN=y +CONFIG_VIDEOBUF_DMA_CONTIG=y +CONFIG_VIDEOBUF2_CORE=y +CONFIG_VIDEO_CAPTURE_DRIVERS=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set + +# +# Encoders, decoders, sensors and other helper chips +# + +# +# Audio decoders, processors and mixers +# +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_VP27SMPX is not set + +# +# RDS decoders +# +# CONFIG_VIDEO_SAA6588 is not set + +# +# Video decoders +# +# CONFIG_VIDEO_ADV7180 is not set +# CONFIG_VIDEO_ADV7183 is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA7191 is not set +# CONFIG_VIDEO_TVP514X is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_TVP7002 is not set +# CONFIG_VIDEO_VPX3220 is not set + +# +# Video and audio decoders +# +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_CX25840 is not set + +# +# MPEG video encoders +# +# CONFIG_VIDEO_CX2341X is not set + +# +# Video encoders +# +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_ADV7343 is not set +# CONFIG_VIDEO_AK881X is not set + +# +# Camera sensor devices +# +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_VS6624 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_SR030PC30 is not set + +# +# Flash devices +# + +# +# Video improvement chips +# +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set + +# +# Miscelaneous helper chips +# +# CONFIG_VIDEO_THS7303 is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_V4L_USB_DRIVERS is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_SOC_CAMERA=y +# CONFIG_SOC_CAMERA_IMX074 is not set +# CONFIG_SOC_CAMERA_MT9M001 is not set +# CONFIG_SOC_CAMERA_MT9M111 is not set +# CONFIG_SOC_CAMERA_MT9T031 is not set +# CONFIG_SOC_CAMERA_MT9T112 is not set +# CONFIG_SOC_CAMERA_MT9V022 is not set +# CONFIG_SOC_CAMERA_RJ54N1 is not set +# CONFIG_SOC_CAMERA_TW9910 is not set +# CONFIG_SOC_CAMERA_PLATFORM is not set +# CONFIG_SOC_CAMERA_OV2640 is not set +# CONFIG_SOC_CAMERA_OV5642 is not set +# CONFIG_SOC_CAMERA_OV6650 is not set +# CONFIG_SOC_CAMERA_OV772X is not set +# CONFIG_SOC_CAMERA_OV9640 is not set +# CONFIG_SOC_CAMERA_OV9740 is not set +CONFIG_LINUX_AKSENSOR=y +CONFIG_AK_SENSOR_I2C=y +CONFIG_AK_SENSOR_COMMON=y +CONFIG_SENSOR_OV9712=m +CONFIG_SENSOR_H42=m +CONFIG_SENSOR_AR0130=m +CONFIG_SENSOR_SC1045=m +CONFIG_SENSOR_SC1035=m +CONFIG_SENSOR_SC1135=m +CONFIG_SENSOR_GC1024=m +# CONFIG_SENSOR_GM7150 is not set +CONFIG_SENSOR_SC1145=m +CONFIG_SENSOR_H61=m +# CONFIG_SENSOR_H65 is not set +# CONFIG_SENSOR_H62 is not set +# CONFIG_SENSOR_OV9732 is not set +# CONFIG_SENSOR_SC1037 is not set +# CONFIG_SENSOR_GC1034 is not set +# CONFIG_SENSOR_GC2033 is not set +# CONFIG_SENSOR_GC2023 is not set +# CONFIG_SENSOR_SC2235 is not set +# CONFIG_SENSOR_SC2232 is not set +# CONFIG_SENSOR_SC2145 is not set +CONFIG_SENSOR_F22_F23_F28_F35_F37=m +# CONFIG_SENSOR_HM2140 is not set +# CONFIG_SENSOR_IMX323 is not set +# CONFIG_VIDEO_SH_MOBILE_CSI2 is not set +# CONFIG_VIDEO_SH_MOBILE_CEU is not set +CONFIG_VIDEO_AK=m +CONFIG_AK_VIDEOBUF_DMA_CONTIG=y +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +# CONFIG_RADIO_ADAPTERS is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_ION=y +CONFIG_ION_AK=y +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_SOUND is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +CONFIG_USB_ANYKA_HCD=y +CONFIG_USB_AKOTG_HS_HCD=m +CONFIG_USB_AKOTG_DMA=y +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_RENESAS_USBHS is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_MV_UDC is not set +CONFIG_USB_AKUDC_PRODUCER=m +# CONFIG_USB_GADGET_AKUDC is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +CONFIG_USB_MASS_STORAGE=m +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_ACM_MS is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_G_WEBCAM is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_OTG_WAKELOCK is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set +# CONFIG_SDIO_WIFI is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_PXAV2 is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +CONFIG_MMC_ANYKA=y +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_GPIO is not set +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA9633 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_BD2802 is not set +CONFIG_LEDS_AK39=y +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_RENESAS_TPU is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_OT200 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_SWITCH is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_AK=y +CONFIG_AK39_RTC_SELECT_CLOCK=y +CONFIG_AK39_RTC_INTERNAL_RC_OSC=y +# CONFIG_AK39_RTC_EXTERNAL_XTAL is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +CONFIG_UIO=y +# CONFIG_UIO_PDRV is not set +# CONFIG_UIO_PDRV_GENIRQ is not set +CONFIG_UIO_VCODEC=y + +# +# Virtio drivers +# +# CONFIG_VIRTIO_BALLOON is not set + +# +# Microsoft Hyper-V guest support +# +# CONFIG_STAGING is not set +CONFIG_CLKDEV_LOOKUP=y + +# +# Hardware Spinlock drivers +# +# CONFIG_IOMMU_SUPPORT is not set + +# +# Remoteproc drivers (EXPERIMENTAL) +# + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_AEC_DUMP_DEBUG is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT/EXFAT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +# CONFIG_FAT_FCBMP is not set +# CONFIG_FAT_FSCK is not set +# CONFIG_FAT_CHK_DISK is not set +CONFIG_FAT_NOT_CHKDSK=y +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set +CONFIG_EXFAT_FS=y +CONFIG_EXFAT_DISCARD=y +# CONFIG_EXFAT_DELAYED_SYNC is not set +# CONFIG_EXFAT_KERNEL_DEBUG is not set +# CONFIG_EXFAT_DEBUG_MSG is not set +CONFIG_EXFAT_DEFAULT_CODEPAGE=437 +CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8" + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_HFSPLUS_FS is not set +# CONFIG_YAFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_WRITEBUFFER is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +# CONFIG_JFFS2_ZLIB is not set +# CONFIG_JFFS2_LZO is not set +# CONFIG_JFFS2_RTIME is not set +# CONFIG_JFFS2_RUBIN is not set +CONFIG_JFFS2_CMODE_NONE=y +# CONFIG_JFFS2_CMODE_PRIORITY is not set +# CONFIG_JFFS2_CMODE_SIZE is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +# CONFIG_CRAMFS is not set +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_XATTR is not set +CONFIG_SQUASHFS_ZLIB=y +# CONFIG_SQUASHFS_LZO is not set +CONFIG_SQUASHFS_XZ=y +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_DEBUG is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR_NMI is not set +# CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_STACKTRACE is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_RODATA is not set +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_MANAGER2 is not set +# CONFIG_CRYPTO_USER is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +# CONFIG_CRYPTO_DEV is not set +# CONFIG_CRYPTO_HW is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +# CONFIG_CRC_CCITT is not set +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_CRC8 is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_XZ_DEC=y +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_IA64 is not set +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +# CONFIG_XZ_DEC_SPARC is not set +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_AVERAGE=y +# CONFIG_CORDIC is not set diff --git a/arch/arm/configs/ak3918ev300_ty_sc10008_v100_defconfig b/arch/arm/configs/ak3918ev300_ty_sc10008_v100_defconfig new file mode 100644 index 00000000..43f1abd9 --- /dev/null +++ b/arch/arm/configs/ak3918ev300_ty_sc10008_v100_defconfig @@ -0,0 +1,1895 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.4.35 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_KTIME_SCALAR=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +# CONFIG_EXPERIMENTAL is not set +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_FHANDLE is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_SHOW=y + +# +# RCU Subsystem +# +CONFIG_TINY_RCU=y +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=16 +# CONFIG_CGROUPS is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=0 +CONFIG_EXPERT=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +# CONFIG_SHMEM is not set +CONFIG_AIO=y +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_SLUB_DEBUG is not set +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_JUMP_LABEL=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y + +# +# GCOV-based kernel profiling +# +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +CONFIG_ARCH_AK39=y +CONFIG_CPU_AK3918=y +# CONFIG_ARCH_CLOUD39EV3_AK3916E128PIN_COREBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3916EV300_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3918EV300_MAINBD is not set +CONFIG_ARCH_AK3918EV300_SC10008_V100=y +# CONFIG_ARCH_CLOUD39EV3_AK3919EV300_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_CO38_SC2235 is not set +# CONFIG_ARCH_CLOUD39EV3_AK3919EV300_SQ38_SC2232 is not set +# CONFIG_ARCH_CLOUD39EV3_SQ38_QFN64_F22_V100 is not set +CONFIG_ASIC_CLK_120MHZ=y + +# +# Power management +# +# CONFIG_AK39_PM is not set +CONFIG_AK39_PWM_TIMER=y +CONFIG_PLAT_ANYKA=y + +# +# Processor Type +# +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +CONFIG_CPU_USE_DOMAINS=y + +# +# Processor Features +# +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +# CONFIG_ARM_THUMB is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set +# CONFIG_CACHE_L2X0 is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_NR_BANKS=8 +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_PCCARD=y +CONFIG_PCMCIA=y + +# +# PC-card bridges +# + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_ARCH_NR_GPIO=0 +CONFIG_PREEMPT_NONE=y +CONFIG_PREEMPT_VOLUNTARY=y +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +# CONFIG_HIGHMEM is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_NEED_PER_CPU_KM=y +# CONFIG_CLEANCACHE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_SECCOMP is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y + +# +# Boot options +# +# CONFIG_USE_OF is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_CMDLINE="root=/dev/mtdblock1 ro rootfstype=squashfs init=/sbin/init mem=64M console=ttySAK0,115200" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_AUTO_ZRELADDR is not set +CONFIG_RAM_BASE=0x80000000 +CONFIG_VIDEO_RESERVED_MEM_SIZE=0x2200000 + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_WAKELOCK=y +CONFIG_PM_SLEEP=y +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_APM_EMULATION is not set +CONFIG_PM_CLK=y +CONFIG_CPU_PM=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET_LRO=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +CONFIG_INET_UDP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_IPV6 is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK_ACCT is not set +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +# CONFIG_NETFILTER_NETLINK_LOG is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NETFILTER_XTABLES is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV4 is not set +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_PHONET is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +CONFIG_BQL=y +CONFIG_HAVE_BPF_JIT=y +# CONFIG_BPF_JIT is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=y +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=y +# CONFIG_LIB80211_DEBUG is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y +CONFIG_MAC80211=y +CONFIG_MAC80211_HAS_RC=y +# CONFIG_MAC80211_RC_PID is not set +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +# CONFIG_MAC80211_LEDS is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_SYNC is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOCG3 is not set +CONFIG_MTD_AK_SPIFLASH=y +# CONFIG_MTD_AK_SPINAND is not set +# CONFIG_MTD_NAND_IDS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=2 +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=2 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_ANYKA is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ATMEL_PWM is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_BMP085 is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_WL127X_RFKILL is not set +CONFIG_AK_SERIAL_NUMBER=y +CONFIG_AK_GETAIN=y +CONFIG_AK_MOTOR=y +# CONFIG_AK_DAC_CLOCK is not set + +# +# user space generic gpio controller +# +CONFIG_GPIOS_AKCUSTOM=y + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set +CONFIG_AK39_PCM=y +CONFIG_H2_ISP_CHAR=y +CONFIG_USER_GPIO=y +CONFIG_AK_INFO_DUMP=m +CONFIG_AK_PWM_CHAR=y +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_MII=y +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_ARCNET is not set + +# +# CAIF transport drivers +# +CONFIG_ETHERNET=y +CONFIG_NET_VENDOR_3COM=y +# CONFIG_PCMCIA_3C574 is not set +# CONFIG_PCMCIA_3C589 is not set +CONFIG_NET_VENDOR_AMD=y +# CONFIG_PCMCIA_NMCLAN is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +# CONFIG_NET_VENDOR_FARADAY is not set +CONFIG_NET_VENDOR_FUJITSU=y +# CONFIG_PCMCIA_FMVJ18X is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_ETHOC is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +CONFIG_NET_VENDOR_XIRCOM=y +# CONFIG_PCMCIA_XIRC2PS is not set +CONFIG_AK_ETHERNET=y +# CONFIG_ETHERNET_MAC_MII is not set +CONFIG_ETHERNET_MAC_RMII=y +# CONFIG_PHYLIB is not set +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_TR is not set + +# +# USB Network Adapters +# +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_IPHETH is not set +CONFIG_WLAN=y +CONFIG_PCMCIA_RAYCS=y +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_ATMEL is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_AIRO_CS is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_RTL8187 is not set +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_WIFI_CONTROL_FUNC is not set +# CONFIG_ATH_COMMON is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_BCMDHD is not set +# CONFIG_BRCMFMAC is not set +# CONFIG_HOSTAP is not set +# CONFIG_LIBERTAS is not set +# CONFIG_HERMES is not set +# CONFIG_RT2X00 is not set +# CONFIG_MWIFIEX is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_KEYRESET is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_KEYBOARD_AKKEY=y +# CONFIG_KEYBOARD_AK_KEYPAD is not set +CONFIG_KEYBOARD_ADKEY=y +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_UNIX98_PTYS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +# CONFIG_DEVKMEM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +CONFIG_SERIAL_AK39_UART=y +CONFIG_SERIAL_AK39_CONSOLE=y +# CONFIG_SERIAL_GPIO_UART is not set +CONFIG_TTY_PRINTK=y +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_CARDMAN_4000 is not set +# CONFIG_CARDMAN_4040 is not set +# CONFIG_IPWIRELESS is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_SMBUS=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_SIMTEC is not set +CONFIG_I2C_ANYKA=y +CONFIG_I2C_AK39_HW=y +# CONFIG_I2C_GPIO_SOFT is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +CONFIG_SPI_BITBANG=y +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_ANYKA=y +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_HSI is not set + +# +# PPS support +# + +# +# PPS generators support +# + +# +# PTP clock support +# + +# +# Enable Device Drivers -> PPS to see the PTP clock options. +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_DW_WATCHDOG is not set +# CONFIG_MAX63XX_WATCHDOG is not set +# CONFIG_AK39_WATCHDOG is not set +CONFIG_AK39_WATCHDOG_TOP=y +# CONFIG_AK39_WATCHDOG_NONE is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_S5M_CORE is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_REGULATOR is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +# CONFIG_DVB_CORE is not set +CONFIG_VIDEO_MEDIA=y + +# +# Multimedia drivers +# +# CONFIG_RC_CORE is not set +# CONFIG_MEDIA_ATTACH is not set +CONFIG_MEDIA_TUNER=y +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_MEDIA_TUNER_SIMPLE=y +CONFIG_MEDIA_TUNER_TDA8290=y +CONFIG_MEDIA_TUNER_TDA827X=y +CONFIG_MEDIA_TUNER_TDA18271=y +CONFIG_MEDIA_TUNER_TDA9887=y +CONFIG_MEDIA_TUNER_TEA5767=y +CONFIG_MEDIA_TUNER_MT20XX=y +CONFIG_MEDIA_TUNER_XC2028=y +CONFIG_MEDIA_TUNER_XC5000=y +CONFIG_MEDIA_TUNER_XC4000=y +CONFIG_MEDIA_TUNER_MC44S803=y +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEOBUF_GEN=y +CONFIG_VIDEOBUF_DMA_CONTIG=y +CONFIG_VIDEOBUF2_CORE=y +CONFIG_VIDEO_CAPTURE_DRIVERS=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set + +# +# Encoders, decoders, sensors and other helper chips +# + +# +# Audio decoders, processors and mixers +# +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_VP27SMPX is not set + +# +# RDS decoders +# +# CONFIG_VIDEO_SAA6588 is not set + +# +# Video decoders +# +# CONFIG_VIDEO_ADV7180 is not set +# CONFIG_VIDEO_ADV7183 is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA7191 is not set +# CONFIG_VIDEO_TVP514X is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_TVP7002 is not set +# CONFIG_VIDEO_VPX3220 is not set + +# +# Video and audio decoders +# +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_CX25840 is not set + +# +# MPEG video encoders +# +# CONFIG_VIDEO_CX2341X is not set + +# +# Video encoders +# +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_ADV7343 is not set +# CONFIG_VIDEO_AK881X is not set + +# +# Camera sensor devices +# +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_VS6624 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_SR030PC30 is not set + +# +# Flash devices +# + +# +# Video improvement chips +# +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set + +# +# Miscelaneous helper chips +# +# CONFIG_VIDEO_THS7303 is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_V4L_USB_DRIVERS is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_SOC_CAMERA=y +# CONFIG_SOC_CAMERA_IMX074 is not set +# CONFIG_SOC_CAMERA_MT9M001 is not set +# CONFIG_SOC_CAMERA_MT9M111 is not set +# CONFIG_SOC_CAMERA_MT9T031 is not set +# CONFIG_SOC_CAMERA_MT9T112 is not set +# CONFIG_SOC_CAMERA_MT9V022 is not set +# CONFIG_SOC_CAMERA_RJ54N1 is not set +# CONFIG_SOC_CAMERA_TW9910 is not set +# CONFIG_SOC_CAMERA_PLATFORM is not set +# CONFIG_SOC_CAMERA_OV2640 is not set +# CONFIG_SOC_CAMERA_OV5642 is not set +# CONFIG_SOC_CAMERA_OV6650 is not set +# CONFIG_SOC_CAMERA_OV772X is not set +# CONFIG_SOC_CAMERA_OV9640 is not set +# CONFIG_SOC_CAMERA_OV9740 is not set +CONFIG_LINUX_AKSENSOR=y +CONFIG_AK_SENSOR_I2C=y +CONFIG_AK_SENSOR_COMMON=y +CONFIG_SENSOR_OV9712=m +CONFIG_SENSOR_H42=m +CONFIG_SENSOR_AR0130=m +CONFIG_SENSOR_SC1045=m +CONFIG_SENSOR_SC1035=m +CONFIG_SENSOR_SC1135=m +CONFIG_SENSOR_GC1024=m +# CONFIG_SENSOR_GM7150 is not set +CONFIG_SENSOR_SC1145=m +CONFIG_SENSOR_H61=m +# CONFIG_SENSOR_H65 is not set +# CONFIG_SENSOR_H62 is not set +# CONFIG_SENSOR_OV9732 is not set +# CONFIG_SENSOR_SC1037 is not set +# CONFIG_SENSOR_GC1034 is not set +# CONFIG_SENSOR_GC2033 is not set +# CONFIG_SENSOR_GC2023 is not set +# CONFIG_SENSOR_SC2235 is not set +# CONFIG_SENSOR_SC2232 is not set +# CONFIG_SENSOR_SC2145 is not set +CONFIG_SENSOR_F22_F23_F28_F35_F37=m +# CONFIG_SENSOR_HM2140 is not set +# CONFIG_SENSOR_IMX323 is not set +# CONFIG_VIDEO_SH_MOBILE_CSI2 is not set +# CONFIG_VIDEO_SH_MOBILE_CEU is not set +CONFIG_VIDEO_AK=m +CONFIG_AK_VIDEOBUF_DMA_CONTIG=y +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +# CONFIG_RADIO_ADAPTERS is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_ION=y +CONFIG_ION_AK=y +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_SOUND is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +CONFIG_USB_ANYKA_HCD=y +CONFIG_USB_AKOTG_HS_HCD=m +CONFIG_USB_AKOTG_DMA=y +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_RENESAS_USBHS is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_MV_UDC is not set +CONFIG_USB_AKUDC_PRODUCER=m +# CONFIG_USB_GADGET_AKUDC is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +CONFIG_USB_MASS_STORAGE=m +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_ACM_MS is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_G_WEBCAM is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_OTG_WAKELOCK is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set +# CONFIG_SDIO_WIFI is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_PXAV2 is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +CONFIG_MMC_ANYKA=y +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_GPIO is not set +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA9633 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_BD2802 is not set +CONFIG_LEDS_AK39=y +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_RENESAS_TPU is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_OT200 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_SWITCH is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_AK=y +CONFIG_AK39_RTC_SELECT_CLOCK=y +CONFIG_AK39_RTC_INTERNAL_RC_OSC=y +# CONFIG_AK39_RTC_EXTERNAL_XTAL is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +CONFIG_UIO=y +# CONFIG_UIO_PDRV is not set +# CONFIG_UIO_PDRV_GENIRQ is not set +CONFIG_UIO_VCODEC=y + +# +# Virtio drivers +# +# CONFIG_VIRTIO_BALLOON is not set + +# +# Microsoft Hyper-V guest support +# +# CONFIG_STAGING is not set +CONFIG_CLKDEV_LOOKUP=y + +# +# Hardware Spinlock drivers +# +# CONFIG_IOMMU_SUPPORT is not set + +# +# Remoteproc drivers (EXPERIMENTAL) +# + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_AEC_DUMP_DEBUG is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT/EXFAT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +# CONFIG_FAT_FCBMP is not set +# CONFIG_FAT_FSCK is not set +# CONFIG_FAT_CHK_DISK is not set +CONFIG_FAT_NOT_CHKDSK=y +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set +CONFIG_EXFAT_FS=y +CONFIG_EXFAT_DISCARD=y +# CONFIG_EXFAT_DELAYED_SYNC is not set +# CONFIG_EXFAT_KERNEL_DEBUG is not set +# CONFIG_EXFAT_DEBUG_MSG is not set +CONFIG_EXFAT_DEFAULT_CODEPAGE=437 +CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8" + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_HFSPLUS_FS is not set +# CONFIG_YAFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_WRITEBUFFER is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +# CONFIG_JFFS2_ZLIB is not set +# CONFIG_JFFS2_LZO is not set +# CONFIG_JFFS2_RTIME is not set +# CONFIG_JFFS2_RUBIN is not set +CONFIG_JFFS2_CMODE_NONE=y +# CONFIG_JFFS2_CMODE_PRIORITY is not set +# CONFIG_JFFS2_CMODE_SIZE is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +# CONFIG_CRAMFS is not set +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_XATTR is not set +CONFIG_SQUASHFS_ZLIB=y +# CONFIG_SQUASHFS_LZO is not set +CONFIG_SQUASHFS_XZ=y +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_DEBUG is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR_NMI is not set +# CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_STACKTRACE is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_RODATA is not set +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_MANAGER2 is not set +# CONFIG_CRYPTO_USER is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +# CONFIG_CRYPTO_DEV is not set +# CONFIG_CRYPTO_HW is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +# CONFIG_CRC_CCITT is not set +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_CRC8 is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_XZ_DEC=y +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_IA64 is not set +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +# CONFIG_XZ_DEC_SPARC is not set +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_AVERAGE=y +# CONFIG_CORDIC is not set diff --git a/arch/arm/configs/ak3918ev300_yr_ipc100a_defconfig b/arch/arm/configs/ak3918ev300_yr_ipc100a_defconfig new file mode 100644 index 00000000..47f8f213 --- /dev/null +++ b/arch/arm/configs/ak3918ev300_yr_ipc100a_defconfig @@ -0,0 +1,1899 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.4.35 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_KTIME_SCALAR=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +# CONFIG_EXPERIMENTAL is not set +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_FHANDLE is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_SHOW=y + +# +# RCU Subsystem +# +CONFIG_TINY_RCU=y +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=16 +# CONFIG_CGROUPS is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=0 +CONFIG_EXPERT=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +# CONFIG_SHMEM is not set +CONFIG_AIO=y +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_SLUB_DEBUG is not set +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_JUMP_LABEL=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y + +# +# GCOV-based kernel profiling +# +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +CONFIG_ARCH_AK39=y +CONFIG_CPU_AK3918=y +# CONFIG_ARCH_CLOUD39EV3_AK3916E128PIN_COREBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3916EV300_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3918EV300_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3918EV300_YTJ_MAINBD is not set +CONFIG_ARCH_CLOUD39EV3_AK3918EV300_YR_IPC100A_MAINBD=y +# CONFIG_ARCH_CLOUD39EV3_AK3919EV300_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_CO38_SC2235 is not set +# CONFIG_ARCH_CLOUD39EV3_AK3919EV300_SQ38_SC2232 is not set +# CONFIG_ARCH_CLOUD39EV3_SQ38_QFN64_F22_V100 is not set +CONFIG_ASIC_CLK_120MHZ=y + +# +# Power management +# +# CONFIG_AK39_PM is not set +CONFIG_AK39_PWM_TIMER=y +CONFIG_PLAT_ANYKA=y + +# +# Processor Type +# +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +CONFIG_CPU_USE_DOMAINS=y + +# +# Processor Features +# +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +# CONFIG_ARM_THUMB is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set +# CONFIG_CACHE_L2X0 is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_NR_BANKS=8 +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_PCCARD=y +CONFIG_PCMCIA=y + +# +# PC-card bridges +# + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_ARCH_NR_GPIO=0 +CONFIG_PREEMPT_NONE=y +CONFIG_PREEMPT_VOLUNTARY=y +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +# CONFIG_HIGHMEM is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_NEED_PER_CPU_KM=y +# CONFIG_CLEANCACHE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_SECCOMP is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y + +# +# Boot options +# +# CONFIG_USE_OF is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_CMDLINE="root=/dev/mtdblock1 ro rootfstype=squashfs init=/sbin/init mem=64M console=ttySAK0,115200" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_AUTO_ZRELADDR is not set +CONFIG_RAM_BASE=0x80000000 +CONFIG_VIDEO_RESERVED_MEM_SIZE=0x1a00000 + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_WAKELOCK=y +CONFIG_PM_SLEEP=y +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_APM_EMULATION is not set +CONFIG_PM_CLK=y +CONFIG_CPU_PM=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET_LRO=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +CONFIG_INET_UDP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_IPV6 is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK_ACCT is not set +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +# CONFIG_NETFILTER_NETLINK_LOG is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NETFILTER_XTABLES is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV4 is not set +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_PHONET is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +CONFIG_BQL=y +CONFIG_HAVE_BPF_JIT=y +# CONFIG_BPF_JIT is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=y +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=y +# CONFIG_LIB80211_DEBUG is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y +CONFIG_MAC80211=y +CONFIG_MAC80211_HAS_RC=y +# CONFIG_MAC80211_RC_PID is not set +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +# CONFIG_MAC80211_LEDS is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_SYNC is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOCG3 is not set +CONFIG_MTD_AK_SPIFLASH=y +# CONFIG_MTD_AK_SPINAND is not set +# CONFIG_MTD_NAND_IDS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=2 +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=2 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_ANYKA is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ATMEL_PWM is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_BMP085 is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_WL127X_RFKILL is not set +CONFIG_AK_SERIAL_NUMBER=y +CONFIG_AK_GETAIN=y +CONFIG_AK_MOTOR=y +# CONFIG_AK_DAC_CLOCK is not set + +# +# user space generic gpio controller +# +CONFIG_GPIOS_AKCUSTOM=y + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set +CONFIG_AK39_PCM=y +CONFIG_H2_ISP_CHAR=y +CONFIG_USER_GPIO=y +CONFIG_AK_INFO_DUMP=m +CONFIG_AK_PWM_CHAR=y +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_MII=y +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_ARCNET is not set + +# +# CAIF transport drivers +# +CONFIG_ETHERNET=y +CONFIG_NET_VENDOR_3COM=y +# CONFIG_PCMCIA_3C574 is not set +# CONFIG_PCMCIA_3C589 is not set +CONFIG_NET_VENDOR_AMD=y +# CONFIG_PCMCIA_NMCLAN is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +# CONFIG_NET_VENDOR_FARADAY is not set +CONFIG_NET_VENDOR_FUJITSU=y +# CONFIG_PCMCIA_FMVJ18X is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_ETHOC is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +CONFIG_NET_VENDOR_XIRCOM=y +# CONFIG_PCMCIA_XIRC2PS is not set +CONFIG_AK_ETHERNET=y +# CONFIG_ETHERNET_MAC_MII is not set +CONFIG_ETHERNET_MAC_RMII=y +# CONFIG_PHYLIB is not set +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_TR is not set + +# +# USB Network Adapters +# +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_IPHETH is not set +CONFIG_WLAN=y +CONFIG_PCMCIA_RAYCS=y +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_ATMEL is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_AIRO_CS is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_RTL8187 is not set +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_WIFI_CONTROL_FUNC is not set +# CONFIG_ATH_COMMON is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_BCMDHD is not set +# CONFIG_BRCMFMAC is not set +# CONFIG_HOSTAP is not set +# CONFIG_LIBERTAS is not set +# CONFIG_HERMES is not set +# CONFIG_RT2X00 is not set +# CONFIG_MWIFIEX is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_KEYRESET is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_KEYBOARD_AKKEY=y +# CONFIG_KEYBOARD_AK_KEYPAD is not set +CONFIG_KEYBOARD_ADKEY=y +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_UNIX98_PTYS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +# CONFIG_DEVKMEM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +CONFIG_SERIAL_AK39_UART=y +CONFIG_SERIAL_AK39_CONSOLE=y +# CONFIG_SERIAL_GPIO_UART is not set +CONFIG_TTY_PRINTK=y +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_CARDMAN_4000 is not set +# CONFIG_CARDMAN_4040 is not set +# CONFIG_IPWIRELESS is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_SMBUS=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_SIMTEC is not set +CONFIG_I2C_ANYKA=y +CONFIG_I2C_AK39_HW=y +# CONFIG_I2C_GPIO_SOFT is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +CONFIG_SPI_BITBANG=y +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_ANYKA=y +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_HSI is not set + +# +# PPS support +# + +# +# PPS generators support +# + +# +# PTP clock support +# + +# +# Enable Device Drivers -> PPS to see the PTP clock options. +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_DW_WATCHDOG is not set +# CONFIG_MAX63XX_WATCHDOG is not set +# CONFIG_AK39_WATCHDOG is not set +CONFIG_AK39_WATCHDOG_TOP=y +# CONFIG_AK39_WATCHDOG_NONE is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_S5M_CORE is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_REGULATOR is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +# CONFIG_DVB_CORE is not set +CONFIG_VIDEO_MEDIA=y + +# +# Multimedia drivers +# +# CONFIG_RC_CORE is not set +# CONFIG_MEDIA_ATTACH is not set +CONFIG_MEDIA_TUNER=y +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_MEDIA_TUNER_SIMPLE=y +CONFIG_MEDIA_TUNER_TDA8290=y +CONFIG_MEDIA_TUNER_TDA827X=y +CONFIG_MEDIA_TUNER_TDA18271=y +CONFIG_MEDIA_TUNER_TDA9887=y +CONFIG_MEDIA_TUNER_TEA5767=y +CONFIG_MEDIA_TUNER_MT20XX=y +CONFIG_MEDIA_TUNER_XC2028=y +CONFIG_MEDIA_TUNER_XC5000=y +CONFIG_MEDIA_TUNER_XC4000=y +CONFIG_MEDIA_TUNER_MC44S803=y +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEOBUF_GEN=y +CONFIG_VIDEOBUF_DMA_CONTIG=y +CONFIG_VIDEOBUF2_CORE=y +CONFIG_VIDEO_CAPTURE_DRIVERS=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set + +# +# Encoders, decoders, sensors and other helper chips +# + +# +# Audio decoders, processors and mixers +# +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_VP27SMPX is not set + +# +# RDS decoders +# +# CONFIG_VIDEO_SAA6588 is not set + +# +# Video decoders +# +# CONFIG_VIDEO_ADV7180 is not set +# CONFIG_VIDEO_ADV7183 is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA7191 is not set +# CONFIG_VIDEO_TVP514X is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_TVP7002 is not set +# CONFIG_VIDEO_VPX3220 is not set + +# +# Video and audio decoders +# +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_CX25840 is not set + +# +# MPEG video encoders +# +# CONFIG_VIDEO_CX2341X is not set + +# +# Video encoders +# +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_ADV7343 is not set +# CONFIG_VIDEO_AK881X is not set + +# +# Camera sensor devices +# +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_VS6624 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_SR030PC30 is not set + +# +# Flash devices +# + +# +# Video improvement chips +# +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set + +# +# Miscelaneous helper chips +# +# CONFIG_VIDEO_THS7303 is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_V4L_USB_DRIVERS is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_SOC_CAMERA=y +# CONFIG_SOC_CAMERA_IMX074 is not set +# CONFIG_SOC_CAMERA_MT9M001 is not set +# CONFIG_SOC_CAMERA_MT9M111 is not set +# CONFIG_SOC_CAMERA_MT9T031 is not set +# CONFIG_SOC_CAMERA_MT9T112 is not set +# CONFIG_SOC_CAMERA_MT9V022 is not set +# CONFIG_SOC_CAMERA_RJ54N1 is not set +# CONFIG_SOC_CAMERA_TW9910 is not set +# CONFIG_SOC_CAMERA_PLATFORM is not set +# CONFIG_SOC_CAMERA_OV2640 is not set +# CONFIG_SOC_CAMERA_OV5642 is not set +# CONFIG_SOC_CAMERA_OV6650 is not set +# CONFIG_SOC_CAMERA_OV772X is not set +# CONFIG_SOC_CAMERA_OV9640 is not set +# CONFIG_SOC_CAMERA_OV9740 is not set +CONFIG_LINUX_AKSENSOR=y +CONFIG_AK_SENSOR_I2C=y +CONFIG_AK_SENSOR_COMMON=y +CONFIG_SENSOR_OV9712=m +CONFIG_SENSOR_H42=m +CONFIG_SENSOR_AR0130=m +CONFIG_SENSOR_SC1045=m +CONFIG_SENSOR_SC1035=m +CONFIG_SENSOR_SC1135=m +CONFIG_SENSOR_GC1024=m +# CONFIG_SENSOR_GM7150 is not set +CONFIG_SENSOR_SC1145=m +CONFIG_SENSOR_H61=m +# CONFIG_SENSOR_H65 is not set +# CONFIG_SENSOR_H62 is not set +# CONFIG_SENSOR_OV9732 is not set +# CONFIG_SENSOR_SC1037 is not set +# CONFIG_SENSOR_GC1034 is not set +# CONFIG_SENSOR_GC2033 is not set +# CONFIG_SENSOR_GC2023 is not set +# CONFIG_SENSOR_SC2235 is not set +# CONFIG_SENSOR_SC2232 is not set +# CONFIG_SENSOR_SC2145 is not set +# CONFIG_SENSOR_F22 is not set +# CONFIG_SENSOR_F23 is not set +# CONFIG_SENSOR_F28 is not set +# CONFIG_SENSOR_HM2140 is not set +# CONFIG_SENSOR_IMX323 is not set +# CONFIG_VIDEO_SH_MOBILE_CSI2 is not set +# CONFIG_VIDEO_SH_MOBILE_CEU is not set +CONFIG_VIDEO_AK=m +CONFIG_AK_VIDEOBUF_DMA_CONTIG=y +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +# CONFIG_RADIO_ADAPTERS is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_ION=y +CONFIG_ION_AK=y +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_SOUND is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +CONFIG_USB_ANYKA_HCD=y +CONFIG_USB_AKOTG_HS_HCD=m +CONFIG_USB_AKOTG_DMA=y +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_RENESAS_USBHS is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_MV_UDC is not set +CONFIG_USB_AKUDC_PRODUCER=m +# CONFIG_USB_GADGET_AKUDC is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +CONFIG_USB_MASS_STORAGE=m +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_ACM_MS is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_G_WEBCAM is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_OTG_WAKELOCK is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set +# CONFIG_SDIO_WIFI is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_PXAV2 is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +CONFIG_MMC_ANYKA=y +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_GPIO is not set +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA9633 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_BD2802 is not set +CONFIG_LEDS_AK39=y +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_RENESAS_TPU is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_OT200 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_SWITCH is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_AK=y +CONFIG_AK39_RTC_SELECT_CLOCK=y +CONFIG_AK39_RTC_INTERNAL_RC_OSC=y +# CONFIG_AK39_RTC_EXTERNAL_XTAL is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +CONFIG_UIO=y +# CONFIG_UIO_PDRV is not set +# CONFIG_UIO_PDRV_GENIRQ is not set +CONFIG_UIO_VCODEC=y + +# +# Virtio drivers +# +# CONFIG_VIRTIO_BALLOON is not set + +# +# Microsoft Hyper-V guest support +# +# CONFIG_STAGING is not set +CONFIG_CLKDEV_LOOKUP=y + +# +# Hardware Spinlock drivers +# +# CONFIG_IOMMU_SUPPORT is not set + +# +# Remoteproc drivers (EXPERIMENTAL) +# + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_AEC_DUMP_DEBUG is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT/EXFAT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +# CONFIG_FAT_FCBMP is not set +# CONFIG_FAT_FSCK is not set +# CONFIG_FAT_CHK_DISK is not set +CONFIG_FAT_NOT_CHKDSK=y +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set +CONFIG_EXFAT_FS=y +CONFIG_EXFAT_DISCARD=y +# CONFIG_EXFAT_DELAYED_SYNC is not set +# CONFIG_EXFAT_KERNEL_DEBUG is not set +# CONFIG_EXFAT_DEBUG_MSG is not set +CONFIG_EXFAT_DEFAULT_CODEPAGE=437 +CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8" + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_HFSPLUS_FS is not set +# CONFIG_YAFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_WRITEBUFFER is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +# CONFIG_JFFS2_ZLIB is not set +# CONFIG_JFFS2_LZO is not set +# CONFIG_JFFS2_RTIME is not set +# CONFIG_JFFS2_RUBIN is not set +CONFIG_JFFS2_CMODE_NONE=y +# CONFIG_JFFS2_CMODE_PRIORITY is not set +# CONFIG_JFFS2_CMODE_SIZE is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +# CONFIG_CRAMFS is not set +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_XATTR is not set +CONFIG_SQUASHFS_ZLIB=y +# CONFIG_SQUASHFS_LZO is not set +CONFIG_SQUASHFS_XZ=y +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_DEBUG is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR_NMI is not set +# CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_STACKTRACE is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_RODATA is not set +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_MANAGER2 is not set +# CONFIG_CRYPTO_USER is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_CTR=y +CONFIG_CRYPTO_CTS=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_LRW=y +CONFIG_CRYPTO_PCBC=y +CONFIG_CRYPTO_XTS=y + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_USER_API=y +CONFIG_CRYPTO_USER_API_HASH=y +CONFIG_CRYPTO_USER_API_SKCIPHER=y +CONFIG_CRYPTO_DEV=y +CONFIG_CRYPTO_HW=y +CONFIG_CRYPTO_DEV_ANYKA=y + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +# CONFIG_CRC_CCITT is not set +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_CRC8 is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_XZ_DEC=y +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_IA64 is not set +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +# CONFIG_XZ_DEC_SPARC is not set +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_AVERAGE=y +# CONFIG_CORDIC is not set diff --git a/arch/arm/configs/cloud39ev3_ak3916ev300_corebd_v1.0.0_defconfig b/arch/arm/configs/cloud39ev3_ak3916ev300_corebd_v1.0.0_defconfig new file mode 100644 index 00000000..93d19a1b --- /dev/null +++ b/arch/arm/configs/cloud39ev3_ak3916ev300_corebd_v1.0.0_defconfig @@ -0,0 +1,1876 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.4.35 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_KTIME_SCALAR=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +# CONFIG_EXPERIMENTAL is not set +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_FHANDLE is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_SHOW=y + +# +# RCU Subsystem +# +CONFIG_TINY_RCU=y +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=16 +# CONFIG_CGROUPS is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=0 +CONFIG_EXPERT=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +# CONFIG_SHMEM is not set +CONFIG_AIO=y +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_SLUB_DEBUG is not set +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_JUMP_LABEL=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y + +# +# GCOV-based kernel profiling +# +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +CONFIG_ARCH_AK39=y +CONFIG_CPU_AK3916=y +# CONFIG_ARCH_SKY39EV2_AK3918E80PIN_COREBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3916E128PIN_COREBD is not set +# CONFIG_ARCH_CLOUD39EV2_AK3916E_MAINBD is not set +CONFIG_ARCH_CLOUD39EV3_AK3916EV300_MAINBD=y +# CONFIG_ARCH_CLOUD39EV3_AK3918EV300_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3918E80PIN_COREBD is not set +# CONFIG_ARCH_CLOUD39EV3_CO38_SC2235 is not set +CONFIG_ASIC_CLK_120MHZ=y + +# +# Power management +# +# CONFIG_AK39_PM is not set +CONFIG_AK39_PWM_TIMER=y +CONFIG_PLAT_ANYKA=y + +# +# Processor Type +# +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +CONFIG_CPU_USE_DOMAINS=y + +# +# Processor Features +# +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +# CONFIG_ARM_THUMB is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set +# CONFIG_CACHE_L2X0 is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_NR_BANKS=8 +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_PCCARD=y +CONFIG_PCMCIA=y + +# +# PC-card bridges +# + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_ARCH_NR_GPIO=0 +CONFIG_PREEMPT_NONE=y +CONFIG_PREEMPT_VOLUNTARY=y +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +# CONFIG_HIGHMEM is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_NEED_PER_CPU_KM=y +# CONFIG_CLEANCACHE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_SECCOMP is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y + +# +# Boot options +# +# CONFIG_USE_OF is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_CMDLINE="root=/dev/mtdblock1 ro rootfstype=squashfs init=/sbin/init mem=64M console=ttySAK0,115200" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_AUTO_ZRELADDR is not set +CONFIG_RAM_BASE=0x80000000 +CONFIG_VIDEO_RESERVED_MEM_SIZE=0x2200000 + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_WAKELOCK=y +CONFIG_PM_SLEEP=y +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_APM_EMULATION is not set +CONFIG_PM_CLK=y +CONFIG_CPU_PM=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET_LRO=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +CONFIG_INET_UDP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_IPV6 is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK_ACCT is not set +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +# CONFIG_NETFILTER_NETLINK_LOG is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NETFILTER_XTABLES is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV4 is not set +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_PHONET is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +CONFIG_BQL=y +CONFIG_HAVE_BPF_JIT=y +# CONFIG_BPF_JIT is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=y +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=y +# CONFIG_LIB80211_DEBUG is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y +CONFIG_MAC80211=y +CONFIG_MAC80211_HAS_RC=y +# CONFIG_MAC80211_RC_PID is not set +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +# CONFIG_MAC80211_LEDS is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_SYNC is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOCG3 is not set +CONFIG_MTD_AK_SPIFLASH=y +# CONFIG_MTD_AK_SPINAND is not set +# CONFIG_MTD_NAND_IDS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=2 +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=2 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_ANYKA is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ATMEL_PWM is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_BMP085 is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_WL127X_RFKILL is not set +CONFIG_AK_SERIAL_NUMBER=y +CONFIG_AK_GETAIN=y +CONFIG_AK_MOTOR=y +# CONFIG_AK_DAC_CLOCK is not set + +# +# user space generic gpio controller +# +CONFIG_GPIOS_AKCUSTOM=y + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set +CONFIG_AK39_PCM=y +CONFIG_H2_ISP_CHAR=y +CONFIG_USER_GPIO=y +CONFIG_AK_INFO_DUMP=m +CONFIG_AK_PWM_CHAR=y +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_MII=y +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_ARCNET is not set + +# +# CAIF transport drivers +# +CONFIG_ETHERNET=y +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +# CONFIG_NET_VENDOR_FARADAY is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_ETHOC is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +CONFIG_AK_ETHERNET=y +# CONFIG_ETHERNET_MAC_MII is not set +CONFIG_ETHERNET_MAC_RMII=y +# CONFIG_PHYLIB is not set +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# USB Network Adapters +# +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_IPHETH is not set +CONFIG_WLAN=y +CONFIG_PCMCIA_RAYCS=y +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_RTL8187 is not set +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_WIFI_CONTROL_FUNC is not set +# CONFIG_ATH_COMMON is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_BCMDHD is not set +# CONFIG_BRCMFMAC is not set +# CONFIG_HOSTAP is not set +# CONFIG_LIBERTAS is not set +# CONFIG_RT2X00 is not set +# CONFIG_MWIFIEX is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_KEYRESET is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_AKKEY is not set +# CONFIG_KEYBOARD_AK_KEYPAD is not set +CONFIG_KEYBOARD_ADKEY=y +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_UNIX98_PTYS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +# CONFIG_DEVKMEM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +CONFIG_SERIAL_AK39_UART=y +CONFIG_SERIAL_AK39_CONSOLE=y +# CONFIG_SERIAL_GPIO_UART is not set +CONFIG_TTY_PRINTK=y +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_SMBUS=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_SIMTEC is not set +CONFIG_I2C_ANYKA=y +CONFIG_I2C_AK39_HW=y +# CONFIG_I2C_GPIO_SOFT is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +CONFIG_SPI_BITBANG=y +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_ANYKA=y +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_HSI is not set + +# +# PPS support +# + +# +# PPS generators support +# + +# +# PTP clock support +# + +# +# Enable Device Drivers -> PPS to see the PTP clock options. +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_DW_WATCHDOG is not set +# CONFIG_MAX63XX_WATCHDOG is not set +# CONFIG_AK39_WATCHDOG is not set +CONFIG_AK39_WATCHDOG_TOP=y +# CONFIG_AK39_WATCHDOG_NONE is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_S5M_CORE is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_REGULATOR is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +# CONFIG_DVB_CORE is not set +CONFIG_VIDEO_MEDIA=y + +# +# Multimedia drivers +# +# CONFIG_RC_CORE is not set +# CONFIG_MEDIA_ATTACH is not set +CONFIG_MEDIA_TUNER=y +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_MEDIA_TUNER_SIMPLE=y +CONFIG_MEDIA_TUNER_TDA8290=y +CONFIG_MEDIA_TUNER_TDA827X=y +CONFIG_MEDIA_TUNER_TDA18271=y +CONFIG_MEDIA_TUNER_TDA9887=y +CONFIG_MEDIA_TUNER_TEA5767=y +CONFIG_MEDIA_TUNER_MT20XX=y +CONFIG_MEDIA_TUNER_XC2028=y +CONFIG_MEDIA_TUNER_XC5000=y +CONFIG_MEDIA_TUNER_XC4000=y +CONFIG_MEDIA_TUNER_MC44S803=y +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEOBUF_GEN=y +CONFIG_VIDEOBUF_DMA_CONTIG=y +CONFIG_VIDEOBUF2_CORE=y +CONFIG_VIDEO_CAPTURE_DRIVERS=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set + +# +# Encoders, decoders, sensors and other helper chips +# + +# +# Audio decoders, processors and mixers +# +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_VP27SMPX is not set + +# +# RDS decoders +# +# CONFIG_VIDEO_SAA6588 is not set + +# +# Video decoders +# +# CONFIG_VIDEO_ADV7180 is not set +# CONFIG_VIDEO_ADV7183 is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA7191 is not set +# CONFIG_VIDEO_TVP514X is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_TVP7002 is not set +# CONFIG_VIDEO_VPX3220 is not set + +# +# Video and audio decoders +# +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_CX25840 is not set + +# +# MPEG video encoders +# +# CONFIG_VIDEO_CX2341X is not set + +# +# Video encoders +# +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_ADV7343 is not set +# CONFIG_VIDEO_AK881X is not set + +# +# Camera sensor devices +# +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_VS6624 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_SR030PC30 is not set + +# +# Flash devices +# + +# +# Video improvement chips +# +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set + +# +# Miscelaneous helper chips +# +# CONFIG_VIDEO_THS7303 is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_V4L_USB_DRIVERS is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_SOC_CAMERA=y +# CONFIG_SOC_CAMERA_IMX074 is not set +# CONFIG_SOC_CAMERA_MT9M001 is not set +# CONFIG_SOC_CAMERA_MT9M111 is not set +# CONFIG_SOC_CAMERA_MT9T031 is not set +# CONFIG_SOC_CAMERA_MT9T112 is not set +# CONFIG_SOC_CAMERA_MT9V022 is not set +# CONFIG_SOC_CAMERA_RJ54N1 is not set +# CONFIG_SOC_CAMERA_TW9910 is not set +# CONFIG_SOC_CAMERA_PLATFORM is not set +# CONFIG_SOC_CAMERA_OV2640 is not set +# CONFIG_SOC_CAMERA_OV5642 is not set +# CONFIG_SOC_CAMERA_OV6650 is not set +# CONFIG_SOC_CAMERA_OV772X is not set +# CONFIG_SOC_CAMERA_OV9640 is not set +# CONFIG_SOC_CAMERA_OV9740 is not set +CONFIG_LINUX_AKSENSOR=y +CONFIG_AK_SENSOR_I2C=y +CONFIG_AK_SENSOR_COMMON=y +#CONFIG_SENSOR_OV9712=m +#CONFIG_SENSOR_H42=m +#CONFIG_SENSOR_AR0130=m +#CONFIG_SENSOR_SC1045=m +#CONFIG_SENSOR_SC1035=m +#CONFIG_SENSOR_SC1135=m +#CONFIG_SENSOR_GC1024=m +# CONFIG_SENSOR_GM7150 is not set +#CONFIG_SENSOR_SC1145=m +#CONFIG_SENSOR_H61=m +#CONFIG_SENSOR_SC2235=m +CONFIG_SENSOR_SC2232=m +#CONFIG_SENSOR_GC2033=m +#CONFIG_SENSOR_GC2023=m +CONFIG_SENSOR_GC2053=m +#CONFIG_SENSOR_F28=m +#CONFIG_SENSOR_F23=m +#CONFIG_SENSOR_F22=m +#CONFIG_SENSOR_HM2140=m +#CONFIG_SENSOR_IMX323=m +#CONFIG_SENSOR_SC4236=m +#CONFIG_SENSOR_PS5260=m +#CONFIG_SENSOR_PS5270=m +#CONFIG_SENSOR_SC2311=m +CONFIG_SENSOR_IMX307=m +# CONFIG_SENSOR_OV9732 is not set +# CONFIG_VIDEO_SH_MOBILE_CSI2 is not set +# CONFIG_VIDEO_SH_MOBILE_CEU is not set +CONFIG_VIDEO_AK=m +CONFIG_AK_VIDEOBUF_DMA_CONTIG=y +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +# CONFIG_RADIO_ADAPTERS is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_ION=y +CONFIG_ION_AK=y +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_SOUND is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +CONFIG_USB_ANYKA_HCD=y +CONFIG_USB_AKOTG_HS_HCD=m +CONFIG_USB_AKOTG_DMA=y +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_RENESAS_USBHS is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_MV_UDC is not set +CONFIG_USB_AKUDC_PRODUCER=m +# CONFIG_USB_GADGET_AKUDC is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +CONFIG_USB_MASS_STORAGE=m +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_ACM_MS is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_G_WEBCAM is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_OTG_WAKELOCK is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set +# CONFIG_SDIO_WIFI is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_PXAV2 is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +CONFIG_MMC_ANYKA=y +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_GPIO is not set +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA9633 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_BD2802 is not set +CONFIG_LEDS_AK39=y +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_RENESAS_TPU is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_OT200 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_SWITCH is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_AK=y +CONFIG_AK39_RTC_SELECT_CLOCK=y +# CONFIG_AK39_RTC_INTERNAL_RC_OSC is not set +CONFIG_AK39_RTC_EXTERNAL_XTAL=y +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +CONFIG_UIO=y +# CONFIG_UIO_PDRV is not set +# CONFIG_UIO_PDRV_GENIRQ is not set +CONFIG_UIO_VCODEC=y + +# +# Virtio drivers +# +# CONFIG_VIRTIO_BALLOON is not set + +# +# Microsoft Hyper-V guest support +# +# CONFIG_STAGING is not set +CONFIG_CLKDEV_LOOKUP=y + +# +# Hardware Spinlock drivers +# +# CONFIG_IOMMU_SUPPORT is not set + +# +# Remoteproc drivers (EXPERIMENTAL) +# + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_AEC_DUMP_DEBUG is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT/EXFAT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +# CONFIG_FAT_FCBMP is not set +# CONFIG_FAT_FSCK is not set +# CONFIG_FAT_CHK_DISK is not set +CONFIG_FAT_NOT_CHKDSK=y +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set +CONFIG_EXFAT_FS=y +CONFIG_EXFAT_DISCARD=y +# CONFIG_EXFAT_DELAYED_SYNC is not set +# CONFIG_EXFAT_KERNEL_DEBUG is not set +# CONFIG_EXFAT_DEBUG_MSG is not set +CONFIG_EXFAT_DEFAULT_CODEPAGE=437 +CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8" + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_HFSPLUS_FS is not set +# CONFIG_YAFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_WRITEBUFFER is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +# CONFIG_JFFS2_ZLIB is not set +# CONFIG_JFFS2_LZO is not set +# CONFIG_JFFS2_RTIME is not set +# CONFIG_JFFS2_RUBIN is not set +CONFIG_JFFS2_CMODE_NONE=y +# CONFIG_JFFS2_CMODE_PRIORITY is not set +# CONFIG_JFFS2_CMODE_SIZE is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +# CONFIG_CRAMFS is not set +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_XATTR is not set +CONFIG_SQUASHFS_ZLIB=y +# CONFIG_SQUASHFS_LZO is not set +CONFIG_SQUASHFS_XZ=y +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_DEBUG is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR_NMI is not set +# CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_STACKTRACE is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_RODATA is not set +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_MANAGER2 is not set +# CONFIG_CRYPTO_USER is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +# CONFIG_CRYPTO_DEV is not set +# CONFIG_CRYPTO_HW is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +# CONFIG_CRC_CCITT is not set +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_CRC8 is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_XZ_DEC=y +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_IA64 is not set +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +# CONFIG_XZ_DEC_SPARC is not set +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_AVERAGE=y +# CONFIG_CORDIC is not set diff --git a/arch/arm/configs/cloud39ev3_ak3916ev300_corebd_v1.0.0_spinand_defconfig b/arch/arm/configs/cloud39ev3_ak3916ev300_corebd_v1.0.0_spinand_defconfig new file mode 100644 index 00000000..ee961305 --- /dev/null +++ b/arch/arm/configs/cloud39ev3_ak3916ev300_corebd_v1.0.0_spinand_defconfig @@ -0,0 +1,1880 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.4.35 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_KTIME_SCALAR=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +# CONFIG_EXPERIMENTAL is not set +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_FHANDLE is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_SHOW=y + +# +# RCU Subsystem +# +CONFIG_TINY_RCU=y +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=16 +# CONFIG_CGROUPS is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=0 +CONFIG_EXPERT=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +# CONFIG_SHMEM is not set +CONFIG_AIO=y +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_SLUB_DEBUG is not set +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_JUMP_LABEL=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y + +# +# GCOV-based kernel profiling +# +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +CONFIG_ARCH_AK39=y +CONFIG_CPU_AK3916=y +# CONFIG_ARCH_SKY39EV2_AK3918E80PIN_COREBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3916E128PIN_COREBD is not set +# CONFIG_ARCH_CLOUD39EV2_AK3916E_MAINBD is not set +CONFIG_ARCH_CLOUD39EV3_AK3916EV300_MAINBD=y +# CONFIG_ARCH_CLOUD39EV3_AK3918EV300_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3918E80PIN_COREBD is not set +# CONFIG_ARCH_CLOUD39EV3_CO38_SC2235 is not set +CONFIG_ASIC_CLK_120MHZ=y + +# +# Power management +# +# CONFIG_AK39_PM is not set +CONFIG_AK39_PWM_TIMER=y +CONFIG_PLAT_ANYKA=y + +# +# Processor Type +# +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +CONFIG_CPU_USE_DOMAINS=y + +# +# Processor Features +# +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +# CONFIG_ARM_THUMB is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set +# CONFIG_CACHE_L2X0 is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_NR_BANKS=8 +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_PCCARD=y +CONFIG_PCMCIA=y + +# +# PC-card bridges +# + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_ARCH_NR_GPIO=0 +CONFIG_PREEMPT_NONE=y +CONFIG_PREEMPT_VOLUNTARY=y +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +# CONFIG_HIGHMEM is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_NEED_PER_CPU_KM=y +# CONFIG_CLEANCACHE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_SECCOMP is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y + +# +# Boot options +# +# CONFIG_USE_OF is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_CMDLINE="root=/dev/mtdblock4 ro rootfstype=yaffs2 init=/sbin/init mem=64M console=ttySAK0,115200" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_AUTO_ZRELADDR is not set +CONFIG_RAM_BASE=0x80000000 +CONFIG_VIDEO_RESERVED_MEM_SIZE=0x2200000 + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_WAKELOCK=y +CONFIG_PM_SLEEP=y +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_APM_EMULATION is not set +CONFIG_PM_CLK=y +CONFIG_CPU_PM=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET_LRO=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +CONFIG_INET_UDP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_IPV6 is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK_ACCT is not set +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +# CONFIG_NETFILTER_NETLINK_LOG is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NETFILTER_XTABLES is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV4 is not set +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_PHONET is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +CONFIG_BQL=y +CONFIG_HAVE_BPF_JIT=y +# CONFIG_BPF_JIT is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=y +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=y +# CONFIG_LIB80211_DEBUG is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y +CONFIG_MAC80211=y +CONFIG_MAC80211_HAS_RC=y +# CONFIG_MAC80211_RC_PID is not set +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +# CONFIG_MAC80211_LEDS is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_SYNC is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOCG3 is not set +# CONFIG_MTD_AK_SPIFLASH is not set +CONFIG_MTD_AK_SPINAND=y +# CONFIG_MTD_NAND_IDS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=2 +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=2 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_ANYKA is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ATMEL_PWM is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_BMP085 is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_WL127X_RFKILL is not set +CONFIG_AK_SERIAL_NUMBER=y +CONFIG_AK_GETAIN=y +CONFIG_AK_MOTOR=y +# CONFIG_AK_DAC_CLOCK is not set + +# +# user space generic gpio controller +# +CONFIG_GPIOS_AKCUSTOM=y + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set +CONFIG_AK39_PCM=y +CONFIG_H2_ISP_CHAR=y +CONFIG_USER_GPIO=y +CONFIG_AK_INFO_DUMP=m +CONFIG_AK_PWM_CHAR=y +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_MII=y +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_ARCNET is not set + +# +# CAIF transport drivers +# +CONFIG_ETHERNET=y +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +# CONFIG_NET_VENDOR_FARADAY is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_ETHOC is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +CONFIG_AK_ETHERNET=y +# CONFIG_ETHERNET_MAC_MII is not set +CONFIG_ETHERNET_MAC_RMII=y +# CONFIG_PHYLIB is not set +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# USB Network Adapters +# +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_IPHETH is not set +CONFIG_WLAN=y +CONFIG_PCMCIA_RAYCS=y +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_RTL8187 is not set +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_WIFI_CONTROL_FUNC is not set +# CONFIG_ATH_COMMON is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_BCMDHD is not set +# CONFIG_BRCMFMAC is not set +# CONFIG_HOSTAP is not set +# CONFIG_LIBERTAS is not set +# CONFIG_RT2X00 is not set +# CONFIG_MWIFIEX is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_KEYRESET is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_AKKEY is not set +# CONFIG_KEYBOARD_AK_KEYPAD is not set +CONFIG_KEYBOARD_ADKEY=y +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_UNIX98_PTYS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +# CONFIG_DEVKMEM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +CONFIG_SERIAL_AK39_UART=y +CONFIG_SERIAL_AK39_CONSOLE=y +# CONFIG_SERIAL_GPIO_UART is not set +CONFIG_TTY_PRINTK=y +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_SMBUS=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_SIMTEC is not set +CONFIG_I2C_ANYKA=y +CONFIG_I2C_AK39_HW=y +# CONFIG_I2C_GPIO_SOFT is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +CONFIG_SPI_BITBANG=y +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_ANYKA=y +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_HSI is not set + +# +# PPS support +# + +# +# PPS generators support +# + +# +# PTP clock support +# + +# +# Enable Device Drivers -> PPS to see the PTP clock options. +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_DW_WATCHDOG is not set +# CONFIG_MAX63XX_WATCHDOG is not set +# CONFIG_AK39_WATCHDOG is not set +CONFIG_AK39_WATCHDOG_TOP=y +# CONFIG_AK39_WATCHDOG_NONE is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_S5M_CORE is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_REGULATOR is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +# CONFIG_DVB_CORE is not set +CONFIG_VIDEO_MEDIA=y + +# +# Multimedia drivers +# +# CONFIG_RC_CORE is not set +# CONFIG_MEDIA_ATTACH is not set +CONFIG_MEDIA_TUNER=y +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_MEDIA_TUNER_SIMPLE=y +CONFIG_MEDIA_TUNER_TDA8290=y +CONFIG_MEDIA_TUNER_TDA827X=y +CONFIG_MEDIA_TUNER_TDA18271=y +CONFIG_MEDIA_TUNER_TDA9887=y +CONFIG_MEDIA_TUNER_TEA5767=y +CONFIG_MEDIA_TUNER_MT20XX=y +CONFIG_MEDIA_TUNER_XC2028=y +CONFIG_MEDIA_TUNER_XC5000=y +CONFIG_MEDIA_TUNER_XC4000=y +CONFIG_MEDIA_TUNER_MC44S803=y +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEOBUF_GEN=y +CONFIG_VIDEOBUF_DMA_CONTIG=y +CONFIG_VIDEOBUF2_CORE=y +CONFIG_VIDEO_CAPTURE_DRIVERS=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set + +# +# Encoders, decoders, sensors and other helper chips +# + +# +# Audio decoders, processors and mixers +# +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_VP27SMPX is not set + +# +# RDS decoders +# +# CONFIG_VIDEO_SAA6588 is not set + +# +# Video decoders +# +# CONFIG_VIDEO_ADV7180 is not set +# CONFIG_VIDEO_ADV7183 is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA7191 is not set +# CONFIG_VIDEO_TVP514X is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_TVP7002 is not set +# CONFIG_VIDEO_VPX3220 is not set + +# +# Video and audio decoders +# +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_CX25840 is not set + +# +# MPEG video encoders +# +# CONFIG_VIDEO_CX2341X is not set + +# +# Video encoders +# +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_ADV7343 is not set +# CONFIG_VIDEO_AK881X is not set + +# +# Camera sensor devices +# +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_VS6624 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_SR030PC30 is not set + +# +# Flash devices +# + +# +# Video improvement chips +# +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set + +# +# Miscelaneous helper chips +# +# CONFIG_VIDEO_THS7303 is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_V4L_USB_DRIVERS is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_SOC_CAMERA=y +# CONFIG_SOC_CAMERA_IMX074 is not set +# CONFIG_SOC_CAMERA_MT9M001 is not set +# CONFIG_SOC_CAMERA_MT9M111 is not set +# CONFIG_SOC_CAMERA_MT9T031 is not set +# CONFIG_SOC_CAMERA_MT9T112 is not set +# CONFIG_SOC_CAMERA_MT9V022 is not set +# CONFIG_SOC_CAMERA_RJ54N1 is not set +# CONFIG_SOC_CAMERA_TW9910 is not set +# CONFIG_SOC_CAMERA_PLATFORM is not set +# CONFIG_SOC_CAMERA_OV2640 is not set +# CONFIG_SOC_CAMERA_OV5642 is not set +# CONFIG_SOC_CAMERA_OV6650 is not set +# CONFIG_SOC_CAMERA_OV772X is not set +# CONFIG_SOC_CAMERA_OV9640 is not set +# CONFIG_SOC_CAMERA_OV9740 is not set +CONFIG_LINUX_AKSENSOR=y +CONFIG_AK_SENSOR_I2C=y +CONFIG_AK_SENSOR_COMMON=y +#CONFIG_SENSOR_OV9712=m +#CONFIG_SENSOR_H42=m +#CONFIG_SENSOR_AR0130=m +#CONFIG_SENSOR_SC1045=m +#CONFIG_SENSOR_SC1035=m +#CONFIG_SENSOR_SC1135=m +#CONFIG_SENSOR_GC1024=m +# CONFIG_SENSOR_GM7150 is not set +#CONFIG_SENSOR_SC1145=m +#CONFIG_SENSOR_H61=m +#CONFIG_SENSOR_SC2235=m +CONFIG_SENSOR_SC2232=m +#CONFIG_SENSOR_GC2033=m +#CONFIG_SENSOR_GC2023=m +CONFIG_SENSOR_GC2053=m +#CONFIG_SENSOR_F28=m +#CONFIG_SENSOR_F23=m +#CONFIG_SENSOR_F22=m +#CONFIG_SENSOR_HM2140=m +#CONFIG_SENSOR_IMX323=m +#CONFIG_SENSOR_SC4236=m +#CONFIG_SENSOR_PS5260=m +#CONFIG_SENSOR_PS5270=m +#CONFIG_SENSOR_SC2311=m +CONFIG_SENSOR_IMX307=m +# CONFIG_SENSOR_OV9732 is not set +# CONFIG_VIDEO_SH_MOBILE_CSI2 is not set +# CONFIG_VIDEO_SH_MOBILE_CEU is not set +CONFIG_VIDEO_AK=m +CONFIG_AK_VIDEOBUF_DMA_CONTIG=y +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +# CONFIG_RADIO_ADAPTERS is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_ION=y +CONFIG_ION_AK=y +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_SOUND is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +CONFIG_USB_ANYKA_HCD=y +CONFIG_USB_AKOTG_HS_HCD=m +CONFIG_USB_AKOTG_DMA=y +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_RENESAS_USBHS is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_MV_UDC is not set +CONFIG_USB_AKUDC_PRODUCER=m +# CONFIG_USB_GADGET_AKUDC is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +CONFIG_USB_MASS_STORAGE=m +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_ACM_MS is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_G_WEBCAM is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_OTG_WAKELOCK is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set +# CONFIG_SDIO_WIFI is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_PXAV2 is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +CONFIG_MMC_ANYKA=y +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_GPIO is not set +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA9633 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_BD2802 is not set +CONFIG_LEDS_AK39=y +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_RENESAS_TPU is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_OT200 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_SWITCH is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_AK=y +CONFIG_AK39_RTC_SELECT_CLOCK=y +# CONFIG_AK39_RTC_INTERNAL_RC_OSC is not set +CONFIG_AK39_RTC_EXTERNAL_XTAL=y +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +CONFIG_UIO=y +# CONFIG_UIO_PDRV is not set +# CONFIG_UIO_PDRV_GENIRQ is not set +CONFIG_UIO_VCODEC=y + +# +# Virtio drivers +# +# CONFIG_VIRTIO_BALLOON is not set + +# +# Microsoft Hyper-V guest support +# +# CONFIG_STAGING is not set +CONFIG_CLKDEV_LOOKUP=y + +# +# Hardware Spinlock drivers +# +# CONFIG_IOMMU_SUPPORT is not set + +# +# Remoteproc drivers (EXPERIMENTAL) +# + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_AEC_DUMP_DEBUG is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT/EXFAT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +# CONFIG_FAT_FCBMP is not set +# CONFIG_FAT_FSCK is not set +# CONFIG_FAT_CHK_DISK is not set +CONFIG_FAT_NOT_CHKDSK=y +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set +CONFIG_EXFAT_FS=y +CONFIG_EXFAT_DISCARD=y +# CONFIG_EXFAT_DELAYED_SYNC is not set +# CONFIG_EXFAT_KERNEL_DEBUG is not set +# CONFIG_EXFAT_DEBUG_MSG is not set +CONFIG_EXFAT_DEFAULT_CODEPAGE=437 +CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8" + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_HFSPLUS_FS is not set +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_YAFFS1=y +CONFIG_YAFFS_YAFFS2=y +CONFIG_YAFFS_AUTO_YAFFS2=y +CONFIG_YAFFS_XATTR=y +# CONFIG_JFFS2_FS is not set +# CONFIG_JFFS2_FS_DEBUG is not set +# CONFIG_JFFS2_FS_WRITEBUFFER is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +# CONFIG_JFFS2_ZLIB is not set +# CONFIG_JFFS2_LZO is not set +# CONFIG_JFFS2_RTIME is not set +# CONFIG_JFFS2_RUBIN is not set +# CONFIG_JFFS2_CMODE_NONE is not set +# CONFIG_JFFS2_CMODE_PRIORITY is not set +# CONFIG_JFFS2_CMODE_SIZE is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_SQUASHFS_XATTR is not set +# CONFIG_SQUASHFS_ZLIB is not set +# CONFIG_SQUASHFS_LZO is not set +# CONFIG_SQUASHFS_XZ is not set +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +# CONFIG_SQUASHFS_EMBEDDED is not set +# CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_DEBUG is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR_NMI is not set +# CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_STACKTRACE is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_RODATA is not set +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_MANAGER2 is not set +# CONFIG_CRYPTO_USER is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +# CONFIG_CRYPTO_DEV is not set +# CONFIG_CRYPTO_HW is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +# CONFIG_CRC_CCITT is not set +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_CRC8 is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_XZ_DEC=y +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_IA64 is not set +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +# CONFIG_XZ_DEC_SPARC is not set +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_AVERAGE=y +# CONFIG_CORDIC is not set diff --git a/arch/arm/configs/cloud39ev3_ak3918ev300_YTJ_corebd_v1.0.0_defconfig b/arch/arm/configs/cloud39ev3_ak3918ev300_YTJ_corebd_v1.0.0_defconfig new file mode 100644 index 00000000..787c0068 --- /dev/null +++ b/arch/arm/configs/cloud39ev3_ak3918ev300_YTJ_corebd_v1.0.0_defconfig @@ -0,0 +1,1897 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.4.35 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_KTIME_SCALAR=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +# CONFIG_EXPERIMENTAL is not set +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_FHANDLE is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_SHOW=y + +# +# RCU Subsystem +# +CONFIG_TINY_RCU=y +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=16 +# CONFIG_CGROUPS is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=0 +CONFIG_EXPERT=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +# CONFIG_SHMEM is not set +CONFIG_AIO=y +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_SLUB_DEBUG is not set +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_JUMP_LABEL=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y + +# +# GCOV-based kernel profiling +# +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +CONFIG_ARCH_AK39=y +CONFIG_CPU_AK3918=y +# CONFIG_ARCH_CLOUD39EV3_AK3916E128PIN_COREBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3916EV300_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3918EV300_MAINBD is not set +CONFIG_ARCH_CLOUD39EV3_AK3918EV300_YTJ_MAINBD=y +# CONFIG_ARCH_CLOUD39EV3_AK3919EV300_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_CO38_SC2235 is not set +# CONFIG_ARCH_CLOUD39EV3_AK3919EV300_SQ38_SC2232 is not set +# CONFIG_ARCH_CLOUD39EV3_SQ38_QFN64_F22_V100 is not set +CONFIG_ASIC_CLK_120MHZ=y + +# +# Power management +# +# CONFIG_AK39_PM is not set +CONFIG_AK39_PWM_TIMER=y +CONFIG_PLAT_ANYKA=y + +# +# Processor Type +# +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +CONFIG_CPU_USE_DOMAINS=y + +# +# Processor Features +# +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +# CONFIG_ARM_THUMB is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set +# CONFIG_CACHE_L2X0 is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_NR_BANKS=8 +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_PCCARD=y +CONFIG_PCMCIA=y + +# +# PC-card bridges +# + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_ARCH_NR_GPIO=0 +CONFIG_PREEMPT_NONE=y +CONFIG_PREEMPT_VOLUNTARY=y +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +# CONFIG_HIGHMEM is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_NEED_PER_CPU_KM=y +# CONFIG_CLEANCACHE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_SECCOMP is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y + +# +# Boot options +# +# CONFIG_USE_OF is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_CMDLINE="root=/dev/mtdblock1 ro rootfstype=squashfs init=/sbin/init mem=64M console=ttySAK0,115200" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_AUTO_ZRELADDR is not set +CONFIG_RAM_BASE=0x80000000 +CONFIG_VIDEO_RESERVED_MEM_SIZE=0x2200000 + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_WAKELOCK=y +CONFIG_PM_SLEEP=y +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_APM_EMULATION is not set +CONFIG_PM_CLK=y +CONFIG_CPU_PM=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET_LRO=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +CONFIG_INET_UDP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_IPV6 is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK_ACCT is not set +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +# CONFIG_NETFILTER_NETLINK_LOG is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NETFILTER_XTABLES is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV4 is not set +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_PHONET is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +CONFIG_BQL=y +CONFIG_HAVE_BPF_JIT=y +# CONFIG_BPF_JIT is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=y +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=y +# CONFIG_LIB80211_DEBUG is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y +CONFIG_MAC80211=y +CONFIG_MAC80211_HAS_RC=y +# CONFIG_MAC80211_RC_PID is not set +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +# CONFIG_MAC80211_LEDS is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_SYNC is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOCG3 is not set +CONFIG_MTD_AK_SPIFLASH=y +# CONFIG_MTD_AK_SPINAND is not set +# CONFIG_MTD_NAND_IDS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=2 +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=2 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_ANYKA is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ATMEL_PWM is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_BMP085 is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_WL127X_RFKILL is not set +CONFIG_AK_SERIAL_NUMBER=y +CONFIG_AK_GETAIN=y +CONFIG_AK_MOTOR=y +# CONFIG_AK_DAC_CLOCK is not set + +# +# user space generic gpio controller +# +CONFIG_GPIOS_AKCUSTOM=y + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set +CONFIG_AK39_PCM=y +CONFIG_H2_ISP_CHAR=y +CONFIG_USER_GPIO=y +CONFIG_AK_INFO_DUMP=m +CONFIG_AK_PWM_CHAR=y +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_MII=y +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_ARCNET is not set + +# +# CAIF transport drivers +# +CONFIG_ETHERNET=y +CONFIG_NET_VENDOR_3COM=y +# CONFIG_PCMCIA_3C574 is not set +# CONFIG_PCMCIA_3C589 is not set +CONFIG_NET_VENDOR_AMD=y +# CONFIG_PCMCIA_NMCLAN is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +# CONFIG_NET_VENDOR_FARADAY is not set +CONFIG_NET_VENDOR_FUJITSU=y +# CONFIG_PCMCIA_FMVJ18X is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_ETHOC is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +CONFIG_NET_VENDOR_XIRCOM=y +# CONFIG_PCMCIA_XIRC2PS is not set +CONFIG_AK_ETHERNET=y +# CONFIG_ETHERNET_MAC_MII is not set +CONFIG_ETHERNET_MAC_RMII=y +# CONFIG_PHYLIB is not set +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_TR is not set + +# +# USB Network Adapters +# +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_IPHETH is not set +CONFIG_WLAN=y +CONFIG_PCMCIA_RAYCS=y +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_ATMEL is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_AIRO_CS is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_RTL8187 is not set +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_WIFI_CONTROL_FUNC is not set +# CONFIG_ATH_COMMON is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_BCMDHD is not set +# CONFIG_BRCMFMAC is not set +# CONFIG_HOSTAP is not set +# CONFIG_LIBERTAS is not set +# CONFIG_HERMES is not set +# CONFIG_RT2X00 is not set +# CONFIG_MWIFIEX is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_KEYRESET is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_KEYBOARD_AKKEY=y +# CONFIG_KEYBOARD_AK_KEYPAD is not set +CONFIG_KEYBOARD_ADKEY=y +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_UNIX98_PTYS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +# CONFIG_DEVKMEM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +CONFIG_SERIAL_AK39_UART=y +CONFIG_SERIAL_AK39_CONSOLE=y +# CONFIG_SERIAL_GPIO_UART is not set +CONFIG_TTY_PRINTK=y +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_CARDMAN_4000 is not set +# CONFIG_CARDMAN_4040 is not set +# CONFIG_IPWIRELESS is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_SMBUS=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_SIMTEC is not set +CONFIG_I2C_ANYKA=y +CONFIG_I2C_AK39_HW=y +# CONFIG_I2C_GPIO_SOFT is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +CONFIG_SPI_BITBANG=y +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_ANYKA=y +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_HSI is not set + +# +# PPS support +# + +# +# PPS generators support +# + +# +# PTP clock support +# + +# +# Enable Device Drivers -> PPS to see the PTP clock options. +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_DW_WATCHDOG is not set +# CONFIG_MAX63XX_WATCHDOG is not set +# CONFIG_AK39_WATCHDOG is not set +CONFIG_AK39_WATCHDOG_TOP=y +# CONFIG_AK39_WATCHDOG_NONE is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_S5M_CORE is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_REGULATOR is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +# CONFIG_DVB_CORE is not set +CONFIG_VIDEO_MEDIA=y + +# +# Multimedia drivers +# +# CONFIG_RC_CORE is not set +# CONFIG_MEDIA_ATTACH is not set +CONFIG_MEDIA_TUNER=y +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_MEDIA_TUNER_SIMPLE=y +CONFIG_MEDIA_TUNER_TDA8290=y +CONFIG_MEDIA_TUNER_TDA827X=y +CONFIG_MEDIA_TUNER_TDA18271=y +CONFIG_MEDIA_TUNER_TDA9887=y +CONFIG_MEDIA_TUNER_TEA5767=y +CONFIG_MEDIA_TUNER_MT20XX=y +CONFIG_MEDIA_TUNER_XC2028=y +CONFIG_MEDIA_TUNER_XC5000=y +CONFIG_MEDIA_TUNER_XC4000=y +CONFIG_MEDIA_TUNER_MC44S803=y +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEOBUF_GEN=y +CONFIG_VIDEOBUF_DMA_CONTIG=y +CONFIG_VIDEOBUF2_CORE=y +CONFIG_VIDEO_CAPTURE_DRIVERS=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set + +# +# Encoders, decoders, sensors and other helper chips +# + +# +# Audio decoders, processors and mixers +# +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_VP27SMPX is not set + +# +# RDS decoders +# +# CONFIG_VIDEO_SAA6588 is not set + +# +# Video decoders +# +# CONFIG_VIDEO_ADV7180 is not set +# CONFIG_VIDEO_ADV7183 is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA7191 is not set +# CONFIG_VIDEO_TVP514X is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_TVP7002 is not set +# CONFIG_VIDEO_VPX3220 is not set + +# +# Video and audio decoders +# +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_CX25840 is not set + +# +# MPEG video encoders +# +# CONFIG_VIDEO_CX2341X is not set + +# +# Video encoders +# +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_ADV7343 is not set +# CONFIG_VIDEO_AK881X is not set + +# +# Camera sensor devices +# +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_VS6624 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_SR030PC30 is not set + +# +# Flash devices +# + +# +# Video improvement chips +# +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set + +# +# Miscelaneous helper chips +# +# CONFIG_VIDEO_THS7303 is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_V4L_USB_DRIVERS is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_SOC_CAMERA=y +# CONFIG_SOC_CAMERA_IMX074 is not set +# CONFIG_SOC_CAMERA_MT9M001 is not set +# CONFIG_SOC_CAMERA_MT9M111 is not set +# CONFIG_SOC_CAMERA_MT9T031 is not set +# CONFIG_SOC_CAMERA_MT9T112 is not set +# CONFIG_SOC_CAMERA_MT9V022 is not set +# CONFIG_SOC_CAMERA_RJ54N1 is not set +# CONFIG_SOC_CAMERA_TW9910 is not set +# CONFIG_SOC_CAMERA_PLATFORM is not set +# CONFIG_SOC_CAMERA_OV2640 is not set +# CONFIG_SOC_CAMERA_OV5642 is not set +# CONFIG_SOC_CAMERA_OV6650 is not set +# CONFIG_SOC_CAMERA_OV772X is not set +# CONFIG_SOC_CAMERA_OV9640 is not set +# CONFIG_SOC_CAMERA_OV9740 is not set +CONFIG_LINUX_AKSENSOR=y +CONFIG_AK_SENSOR_I2C=y +CONFIG_AK_SENSOR_COMMON=y +CONFIG_SENSOR_OV9712=m +CONFIG_SENSOR_H42=m +CONFIG_SENSOR_AR0130=m +CONFIG_SENSOR_SC1045=m +CONFIG_SENSOR_SC1035=m +CONFIG_SENSOR_SC1135=m +CONFIG_SENSOR_GC1024=m +# CONFIG_SENSOR_GM7150 is not set +CONFIG_SENSOR_SC1145=m +CONFIG_SENSOR_H61=m +# CONFIG_SENSOR_H65 is not set +# CONFIG_SENSOR_H62 is not set +# CONFIG_SENSOR_OV9732 is not set +# CONFIG_SENSOR_SC1037 is not set +# CONFIG_SENSOR_GC1034 is not set +# CONFIG_SENSOR_GC2033 is not set +# CONFIG_SENSOR_GC2023 is not set +# CONFIG_SENSOR_SC2235 is not set +# CONFIG_SENSOR_SC2232 is not set +# CONFIG_SENSOR_SC2145 is not set +# CONFIG_SENSOR_F22 is not set +# CONFIG_SENSOR_F23 is not set +# CONFIG_SENSOR_F28 is not set +# CONFIG_SENSOR_HM2140 is not set +# CONFIG_SENSOR_IMX323 is not set +# CONFIG_VIDEO_SH_MOBILE_CSI2 is not set +# CONFIG_VIDEO_SH_MOBILE_CEU is not set +CONFIG_VIDEO_AK=m +CONFIG_AK_VIDEOBUF_DMA_CONTIG=y +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +# CONFIG_RADIO_ADAPTERS is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_ION=y +CONFIG_ION_AK=y +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_SOUND is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +CONFIG_USB_ANYKA_HCD=y +CONFIG_USB_AKOTG_HS_HCD=m +CONFIG_USB_AKOTG_DMA=y +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_RENESAS_USBHS is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_MV_UDC is not set +CONFIG_USB_AKUDC_PRODUCER=m +# CONFIG_USB_GADGET_AKUDC is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +CONFIG_USB_MASS_STORAGE=m +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_ACM_MS is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_G_WEBCAM is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_OTG_WAKELOCK is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set +# CONFIG_SDIO_WIFI is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_PXAV2 is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +CONFIG_MMC_ANYKA=y +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_GPIO is not set +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA9633 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_BD2802 is not set +CONFIG_LEDS_AK39=y +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_RENESAS_TPU is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_OT200 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_SWITCH is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_AK=y +CONFIG_AK39_RTC_SELECT_CLOCK=y +CONFIG_AK39_RTC_INTERNAL_RC_OSC=y +# CONFIG_AK39_RTC_EXTERNAL_XTAL is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +CONFIG_UIO=y +# CONFIG_UIO_PDRV is not set +# CONFIG_UIO_PDRV_GENIRQ is not set +CONFIG_UIO_VCODEC=y + +# +# Virtio drivers +# +# CONFIG_VIRTIO_BALLOON is not set + +# +# Microsoft Hyper-V guest support +# +# CONFIG_STAGING is not set +CONFIG_CLKDEV_LOOKUP=y + +# +# Hardware Spinlock drivers +# +# CONFIG_IOMMU_SUPPORT is not set + +# +# Remoteproc drivers (EXPERIMENTAL) +# + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_AEC_DUMP_DEBUG is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT/EXFAT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +# CONFIG_FAT_FCBMP is not set +# CONFIG_FAT_FSCK is not set +# CONFIG_FAT_CHK_DISK is not set +CONFIG_FAT_NOT_CHKDSK=y +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set +CONFIG_EXFAT_FS=y +CONFIG_EXFAT_DISCARD=y +# CONFIG_EXFAT_DELAYED_SYNC is not set +# CONFIG_EXFAT_KERNEL_DEBUG is not set +# CONFIG_EXFAT_DEBUG_MSG is not set +CONFIG_EXFAT_DEFAULT_CODEPAGE=437 +CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8" + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_HFSPLUS_FS is not set +# CONFIG_YAFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_WRITEBUFFER is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +# CONFIG_JFFS2_ZLIB is not set +# CONFIG_JFFS2_LZO is not set +# CONFIG_JFFS2_RTIME is not set +# CONFIG_JFFS2_RUBIN is not set +CONFIG_JFFS2_CMODE_NONE=y +# CONFIG_JFFS2_CMODE_PRIORITY is not set +# CONFIG_JFFS2_CMODE_SIZE is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +# CONFIG_CRAMFS is not set +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_XATTR is not set +CONFIG_SQUASHFS_ZLIB=y +# CONFIG_SQUASHFS_LZO is not set +CONFIG_SQUASHFS_XZ=y +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_DEBUG is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR_NMI is not set +# CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_STACKTRACE is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_RODATA is not set +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_MANAGER2 is not set +# CONFIG_CRYPTO_USER is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +# CONFIG_CRYPTO_DEV is not set +# CONFIG_CRYPTO_HW is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +# CONFIG_CRC_CCITT is not set +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_CRC8 is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_XZ_DEC=y +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_IA64 is not set +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +# CONFIG_XZ_DEC_SPARC is not set +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_AVERAGE=y +# CONFIG_CORDIC is not set diff --git a/arch/arm/configs/cloud39ev3_ak3918ev300_corebd_v1.0.0_defconfig b/arch/arm/configs/cloud39ev3_ak3918ev300_corebd_v1.0.0_defconfig new file mode 100644 index 00000000..d562c058 --- /dev/null +++ b/arch/arm/configs/cloud39ev3_ak3918ev300_corebd_v1.0.0_defconfig @@ -0,0 +1,1891 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.4.35 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_KTIME_SCALAR=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +# CONFIG_EXPERIMENTAL is not set +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_FHANDLE is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_SHOW=y + +# +# RCU Subsystem +# +CONFIG_TINY_RCU=y +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=16 +# CONFIG_CGROUPS is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=0 +CONFIG_EXPERT=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +# CONFIG_SHMEM is not set +CONFIG_AIO=y +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_SLUB_DEBUG is not set +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_JUMP_LABEL=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y + +# +# GCOV-based kernel profiling +# +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +CONFIG_ARCH_AK39=y +CONFIG_CPU_AK3918=y +# CONFIG_ARCH_SKY39EV2_AK3918E80PIN_COREBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3916E128PIN_COREBD is not set +# CONFIG_ARCH_CLOUD39EV2_AK3916E_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3916EV300_MAINBD is not set +CONFIG_ARCH_CLOUD39EV3_AK3918EV300_MAINBD=y +# CONFIG_ARCH_CLOUD39EV3_AK3918E80PIN_COREBD is not set +# CONFIG_ARCH_CLOUD39EV3_CO38_SC2235 is not set +CONFIG_ASIC_CLK_120MHZ=y + +# +# Power management +# +# CONFIG_AK39_PM is not set +CONFIG_AK39_PWM_TIMER=y +CONFIG_PLAT_ANYKA=y + +# +# Processor Type +# +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +CONFIG_CPU_USE_DOMAINS=y + +# +# Processor Features +# +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +# CONFIG_ARM_THUMB is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set +# CONFIG_CACHE_L2X0 is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_NR_BANKS=8 +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_PCCARD=y +CONFIG_PCMCIA=y + +# +# PC-card bridges +# + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_ARCH_NR_GPIO=0 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +# CONFIG_HIGHMEM is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_NEED_PER_CPU_KM=y +# CONFIG_CLEANCACHE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_SECCOMP is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y + +# +# Boot options +# +# CONFIG_USE_OF is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_CMDLINE="root=/dev/mtdblock1 ro rootfstype=squashfs init=/sbin/init mem=64M console=ttySAK0,115200" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_AUTO_ZRELADDR is not set +CONFIG_RAM_BASE=0x80000000 +CONFIG_VIDEO_RESERVED_MEM_SIZE=0x2200000 + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_WAKELOCK=y +CONFIG_PM_SLEEP=y +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_APM_EMULATION is not set +CONFIG_PM_CLK=y +CONFIG_CPU_PM=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET_LRO=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +CONFIG_INET_UDP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_IPV6 is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK_ACCT is not set +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +# CONFIG_NETFILTER_NETLINK_LOG is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NETFILTER_XTABLES is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV4 is not set +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_PHONET is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +CONFIG_BQL=y +CONFIG_HAVE_BPF_JIT=y +# CONFIG_BPF_JIT is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=y +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=y +# CONFIG_LIB80211_DEBUG is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y +CONFIG_MAC80211=y +CONFIG_MAC80211_HAS_RC=y +# CONFIG_MAC80211_RC_PID is not set +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +# CONFIG_MAC80211_LEDS is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_SYNC is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOCG3 is not set +CONFIG_MTD_AK_SPIFLASH=y +# CONFIG_MTD_AK_SPINAND is not set +# CONFIG_MTD_NAND_IDS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=2 +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=2 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_ANYKA is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ATMEL_PWM is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_BMP085 is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_WL127X_RFKILL is not set +CONFIG_AK_SERIAL_NUMBER=y +CONFIG_AK_GETAIN=y +CONFIG_AK_MOTOR=y +# CONFIG_AK_DAC_CLOCK is not set + +# +# user space generic gpio controller +# +CONFIG_GPIOS_AKCUSTOM=y + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set +CONFIG_AK39_PCM=y +CONFIG_H2_ISP_CHAR=y +CONFIG_USER_GPIO=y +CONFIG_AK_INFO_DUMP=m +CONFIG_AK_PWM_CHAR=y +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_MII=y +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_ARCNET is not set + +# +# CAIF transport drivers +# +CONFIG_ETHERNET=y +CONFIG_NET_VENDOR_3COM=y +# CONFIG_PCMCIA_3C574 is not set +# CONFIG_PCMCIA_3C589 is not set +CONFIG_NET_VENDOR_AMD=y +# CONFIG_PCMCIA_NMCLAN is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +# CONFIG_NET_VENDOR_FARADAY is not set +CONFIG_NET_VENDOR_FUJITSU=y +# CONFIG_PCMCIA_FMVJ18X is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_ETHOC is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +CONFIG_NET_VENDOR_XIRCOM=y +# CONFIG_PCMCIA_XIRC2PS is not set +CONFIG_AK_ETHERNET=y +# CONFIG_ETHERNET_MAC_MII is not set +CONFIG_ETHERNET_MAC_RMII=y +# CONFIG_PHYLIB is not set +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_TR is not set + +# +# USB Network Adapters +# +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_IPHETH is not set +CONFIG_WLAN=y +CONFIG_PCMCIA_RAYCS=y +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_ATMEL is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_AIRO_CS is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_RTL8187 is not set +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_WIFI_CONTROL_FUNC is not set +# CONFIG_ATH_COMMON is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_BCMDHD is not set +# CONFIG_BRCMFMAC is not set +# CONFIG_HOSTAP is not set +# CONFIG_LIBERTAS is not set +# CONFIG_HERMES is not set +# CONFIG_RT2X00 is not set +# CONFIG_MWIFIEX is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_KEYRESET is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_KEYBOARD_AKKEY=y +# CONFIG_KEYBOARD_AK_KEYPAD is not set +# CONFIG_KEYBOARD_ADKEY is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_UNIX98_PTYS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +# CONFIG_DEVKMEM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +CONFIG_SERIAL_AK39_UART=y +CONFIG_SERIAL_AK39_CONSOLE=y +# CONFIG_SERIAL_GPIO_UART is not set +CONFIG_TTY_PRINTK=y +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_CARDMAN_4000 is not set +# CONFIG_CARDMAN_4040 is not set +# CONFIG_IPWIRELESS is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_SMBUS=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_SIMTEC is not set +CONFIG_I2C_ANYKA=y +CONFIG_I2C_AK39_HW=y +# CONFIG_I2C_GPIO_SOFT is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +CONFIG_SPI_BITBANG=y +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_ANYKA=y +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_HSI is not set + +# +# PPS support +# + +# +# PPS generators support +# + +# +# PTP clock support +# + +# +# Enable Device Drivers -> PPS to see the PTP clock options. +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_DW_WATCHDOG is not set +# CONFIG_MAX63XX_WATCHDOG is not set +# CONFIG_AK39_WATCHDOG is not set +CONFIG_AK39_WATCHDOG_TOP=y +# CONFIG_AK39_WATCHDOG_NONE is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_S5M_CORE is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_REGULATOR is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +# CONFIG_DVB_CORE is not set +CONFIG_VIDEO_MEDIA=y + +# +# Multimedia drivers +# +# CONFIG_RC_CORE is not set +# CONFIG_MEDIA_ATTACH is not set +CONFIG_MEDIA_TUNER=y +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_MEDIA_TUNER_SIMPLE=y +CONFIG_MEDIA_TUNER_TDA8290=y +CONFIG_MEDIA_TUNER_TDA827X=y +CONFIG_MEDIA_TUNER_TDA18271=y +CONFIG_MEDIA_TUNER_TDA9887=y +CONFIG_MEDIA_TUNER_TEA5767=y +CONFIG_MEDIA_TUNER_MT20XX=y +CONFIG_MEDIA_TUNER_XC2028=y +CONFIG_MEDIA_TUNER_XC5000=y +CONFIG_MEDIA_TUNER_XC4000=y +CONFIG_MEDIA_TUNER_MC44S803=y +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEOBUF_GEN=y +CONFIG_VIDEOBUF_DMA_CONTIG=y +CONFIG_VIDEOBUF2_CORE=y +CONFIG_VIDEO_CAPTURE_DRIVERS=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set + +# +# Encoders, decoders, sensors and other helper chips +# + +# +# Audio decoders, processors and mixers +# +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_VP27SMPX is not set + +# +# RDS decoders +# +# CONFIG_VIDEO_SAA6588 is not set + +# +# Video decoders +# +# CONFIG_VIDEO_ADV7180 is not set +# CONFIG_VIDEO_ADV7183 is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA7191 is not set +# CONFIG_VIDEO_TVP514X is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_TVP7002 is not set +# CONFIG_VIDEO_VPX3220 is not set + +# +# Video and audio decoders +# +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_CX25840 is not set + +# +# MPEG video encoders +# +# CONFIG_VIDEO_CX2341X is not set + +# +# Video encoders +# +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_ADV7343 is not set +# CONFIG_VIDEO_AK881X is not set + +# +# Camera sensor devices +# +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_VS6624 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_SR030PC30 is not set + +# +# Flash devices +# + +# +# Video improvement chips +# +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set + +# +# Miscelaneous helper chips +# +# CONFIG_VIDEO_THS7303 is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_V4L_USB_DRIVERS is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_SOC_CAMERA=y +# CONFIG_SOC_CAMERA_IMX074 is not set +# CONFIG_SOC_CAMERA_MT9M001 is not set +# CONFIG_SOC_CAMERA_MT9M111 is not set +# CONFIG_SOC_CAMERA_MT9T031 is not set +# CONFIG_SOC_CAMERA_MT9T112 is not set +# CONFIG_SOC_CAMERA_MT9V022 is not set +# CONFIG_SOC_CAMERA_RJ54N1 is not set +# CONFIG_SOC_CAMERA_TW9910 is not set +# CONFIG_SOC_CAMERA_PLATFORM is not set +# CONFIG_SOC_CAMERA_OV2640 is not set +# CONFIG_SOC_CAMERA_OV5642 is not set +# CONFIG_SOC_CAMERA_OV6650 is not set +# CONFIG_SOC_CAMERA_OV772X is not set +# CONFIG_SOC_CAMERA_OV9640 is not set +# CONFIG_SOC_CAMERA_OV9740 is not set +CONFIG_LINUX_AKSENSOR=y +CONFIG_AK_SENSOR_I2C=y +CONFIG_AK_SENSOR_COMMON=y +CONFIG_SENSOR_OV9712=m +CONFIG_SENSOR_H42=m +CONFIG_SENSOR_AR0130=m +CONFIG_SENSOR_SC1045=m +CONFIG_SENSOR_SC1035=m +CONFIG_SENSOR_SC1135=m +CONFIG_SENSOR_GC1024=m +CONFIG_SENSOR_GC2053=m +CONFIG_SENSOR_OV9734=m +CONFIG_SENSOR_SC2232=m +CONFIG_SENSOR_SC2232H_SC2135E=m +# CONFIG_SENSOR_GM7150 is not set +CONFIG_SENSOR_SC1145=m +CONFIG_SENSOR_H61=m +# CONFIG_SENSOR_H65 is not set +# CONFIG_SENSOR_H62 is not set +# CONFIG_SENSOR_OV9732 is not set +# CONFIG_SENSOR_SC1037 is not set +# CONFIG_SENSOR_GC1034 is not set +# CONFIG_SENSOR_SC2235 is not set +# CONFIG_VIDEO_SH_MOBILE_CSI2 is not set +# CONFIG_VIDEO_SH_MOBILE_CEU is not set +CONFIG_VIDEO_AK=m +CONFIG_AK_VIDEOBUF_DMA_CONTIG=y +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +# CONFIG_RADIO_ADAPTERS is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_ION=y +CONFIG_ION_AK=y +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_SOUND is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +CONFIG_USB_ANYKA_HCD=y +CONFIG_USB_AKOTG_HS_HCD=m +CONFIG_USB_AKOTG_DMA=y +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_RENESAS_USBHS is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_MV_UDC is not set +CONFIG_USB_AKUDC_PRODUCER=m +# CONFIG_USB_GADGET_AKUDC is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +CONFIG_USB_MASS_STORAGE=m +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_ACM_MS is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_G_WEBCAM is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_OTG_WAKELOCK is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set +# CONFIG_SDIO_WIFI is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_PXAV2 is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +CONFIG_MMC_ANYKA=y +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_GPIO is not set +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA9633 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_BD2802 is not set +CONFIG_LEDS_AK39=y +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_RENESAS_TPU is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_OT200 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_SWITCH is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_AK=y +CONFIG_AK39_RTC_SELECT_CLOCK=y +CONFIG_AK39_RTC_INTERNAL_RC_OSC=y +# CONFIG_AK39_RTC_EXTERNAL_XTAL is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +CONFIG_UIO=y +# CONFIG_UIO_PDRV is not set +# CONFIG_UIO_PDRV_GENIRQ is not set +CONFIG_UIO_VCODEC=y + +# +# Virtio drivers +# +# CONFIG_VIRTIO_BALLOON is not set + +# +# Microsoft Hyper-V guest support +# +# CONFIG_STAGING is not set +CONFIG_CLKDEV_LOOKUP=y + +# +# Hardware Spinlock drivers +# +# CONFIG_IOMMU_SUPPORT is not set + +# +# Remoteproc drivers (EXPERIMENTAL) +# + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_AEC_DUMP_DEBUG is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT/EXFAT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +# CONFIG_FAT_FCBMP is not set +# CONFIG_FAT_FSCK is not set +# CONFIG_FAT_CHK_DISK is not set +CONFIG_FAT_NOT_CHKDSK=y +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set +CONFIG_EXFAT_FS=y +CONFIG_EXFAT_DISCARD=y +# CONFIG_EXFAT_DELAYED_SYNC is not set +# CONFIG_EXFAT_KERNEL_DEBUG is not set +# CONFIG_EXFAT_DEBUG_MSG is not set +CONFIG_EXFAT_DEFAULT_CODEPAGE=437 +CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8" + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_HFSPLUS_FS is not set +# CONFIG_YAFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_WRITEBUFFER is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +# CONFIG_JFFS2_ZLIB is not set +# CONFIG_JFFS2_LZO is not set +# CONFIG_JFFS2_RTIME is not set +# CONFIG_JFFS2_RUBIN is not set +CONFIG_JFFS2_CMODE_NONE=y +# CONFIG_JFFS2_CMODE_PRIORITY is not set +# CONFIG_JFFS2_CMODE_SIZE is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +# CONFIG_CRAMFS is not set +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_XATTR is not set +CONFIG_SQUASHFS_ZLIB=y +# CONFIG_SQUASHFS_LZO is not set +CONFIG_SQUASHFS_XZ=y +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_DEBUG is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR_NMI is not set +# CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_STACKTRACE is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_RODATA is not set +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_MANAGER2 is not set +# CONFIG_CRYPTO_USER is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +# CONFIG_CRYPTO_DEV is not set +# CONFIG_CRYPTO_HW is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +# CONFIG_CRC_CCITT is not set +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_CRC8 is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_XZ_DEC=y +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_IA64 is not set +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +# CONFIG_XZ_DEC_SPARC is not set +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_AVERAGE=y +# CONFIG_CORDIC is not set diff --git a/arch/arm/configs/cloud39ev3_ak3918ev300_corebd_v1.0.0_spinand_defconfig b/arch/arm/configs/cloud39ev3_ak3918ev300_corebd_v1.0.0_spinand_defconfig new file mode 100644 index 00000000..51b5406c --- /dev/null +++ b/arch/arm/configs/cloud39ev3_ak3918ev300_corebd_v1.0.0_spinand_defconfig @@ -0,0 +1,1900 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.4.35 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_KTIME_SCALAR=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +# CONFIG_EXPERIMENTAL is not set +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_FHANDLE is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_SHOW=y + +# +# RCU Subsystem +# +CONFIG_TINY_RCU=y +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=16 +# CONFIG_CGROUPS is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=0 +CONFIG_EXPERT=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +# CONFIG_SHMEM is not set +CONFIG_AIO=y +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_SLUB_DEBUG is not set +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_JUMP_LABEL=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y + +# +# GCOV-based kernel profiling +# +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +CONFIG_ARCH_AK39=y +CONFIG_CPU_AK3918=y +# CONFIG_ARCH_CLOUD39EV3_AK3916E128PIN_COREBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3916EV300_MAINBD is not set +CONFIG_ARCH_CLOUD39EV3_AK3918EV300_MAINBD=y +# CONFIG_ARCH_CLOUD39EV3_AK3918EV300_YTJ_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3919EV300_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_CO38_SC2235 is not set +# CONFIG_ARCH_CLOUD39EV3_AK3919EV300_SQ38_SC2232_V1_0_0 is not set +# CONFIG_ARCH_CLOUD39EV3_AK3919EV300_SQ38_SC2232_V1_0_1 is not set +# CONFIG_ARCH_CLOUD39EV3_SQ38_QFN64_F22_V100 is not set +CONFIG_ASIC_CLK_120MHZ=y + +# +# Power management +# +# CONFIG_AK39_PM is not set +CONFIG_AK39_PWM_TIMER=y +CONFIG_PLAT_ANYKA=y + +# +# Processor Type +# +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +CONFIG_CPU_USE_DOMAINS=y + +# +# Processor Features +# +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +# CONFIG_ARM_THUMB is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set +# CONFIG_CACHE_L2X0 is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_NR_BANKS=8 +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_PCCARD=y +CONFIG_PCMCIA=y + +# +# PC-card bridges +# + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_ARCH_NR_GPIO=0 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +# CONFIG_HIGHMEM is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_NEED_PER_CPU_KM=y +# CONFIG_CLEANCACHE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_SECCOMP is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y + +# +# Boot options +# +# CONFIG_USE_OF is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_CMDLINE="root=/dev/mtdblock4 ro rootfstype=yaffs2 init=/sbin/init mem=64M console=ttySAK0,115200" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_AUTO_ZRELADDR is not set +CONFIG_RAM_BASE=0x80000000 +CONFIG_VIDEO_RESERVED_MEM_SIZE=0x2200000 + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_WAKELOCK=y +CONFIG_PM_SLEEP=y +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_APM_EMULATION is not set +CONFIG_PM_CLK=y +CONFIG_CPU_PM=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET_LRO=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +CONFIG_INET_UDP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_IPV6 is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK_ACCT is not set +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +# CONFIG_NETFILTER_NETLINK_LOG is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NETFILTER_XTABLES is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV4 is not set +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_PHONET is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +CONFIG_BQL=y +CONFIG_HAVE_BPF_JIT=y +# CONFIG_BPF_JIT is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=y +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=y +# CONFIG_LIB80211_DEBUG is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y +CONFIG_MAC80211=y +CONFIG_MAC80211_HAS_RC=y +# CONFIG_MAC80211_RC_PID is not set +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +# CONFIG_MAC80211_LEDS is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_SYNC is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOCG3 is not set +# CONFIG_MTD_AK_SPIFLASH is not set +CONFIG_MTD_AK_SPINAND=y +# CONFIG_MTD_SPINAND_PRODUCER is not set +# CONFIG_MTD_NAND_IDS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=2 +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=2 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_ANYKA is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ATMEL_PWM is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_BMP085 is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_WL127X_RFKILL is not set +CONFIG_AK_SERIAL_NUMBER=y +CONFIG_AK_GETAIN=y +CONFIG_AK_MOTOR=y +# CONFIG_AK_DAC_CLOCK is not set + +# +# user space generic gpio controller +# +CONFIG_GPIOS_AKCUSTOM=y + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set +CONFIG_AK39_PCM=y +CONFIG_H2_ISP_CHAR=y +CONFIG_USER_GPIO=y +CONFIG_AK_INFO_DUMP=m +CONFIG_AK_PWM_CHAR=y +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_MII=y +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_ARCNET is not set + +# +# CAIF transport drivers +# +CONFIG_ETHERNET=y +CONFIG_NET_VENDOR_3COM=y +# CONFIG_PCMCIA_3C574 is not set +# CONFIG_PCMCIA_3C589 is not set +CONFIG_NET_VENDOR_AMD=y +# CONFIG_PCMCIA_NMCLAN is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +# CONFIG_NET_VENDOR_FARADAY is not set +CONFIG_NET_VENDOR_FUJITSU=y +# CONFIG_PCMCIA_FMVJ18X is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_ETHOC is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +CONFIG_NET_VENDOR_XIRCOM=y +# CONFIG_PCMCIA_XIRC2PS is not set +CONFIG_AK_ETHERNET=y +# CONFIG_ETHERNET_MAC_MII is not set +CONFIG_ETHERNET_MAC_RMII=y +# CONFIG_PHYLIB is not set +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_TR is not set + +# +# USB Network Adapters +# +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_IPHETH is not set +CONFIG_WLAN=y +CONFIG_PCMCIA_RAYCS=y +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_ATMEL is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_AIRO_CS is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_RTL8187 is not set +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_WIFI_CONTROL_FUNC is not set +# CONFIG_ATH_COMMON is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_BCMDHD is not set +# CONFIG_BRCMFMAC is not set +# CONFIG_HOSTAP is not set +# CONFIG_LIBERTAS is not set +# CONFIG_HERMES is not set +# CONFIG_RT2X00 is not set +# CONFIG_MWIFIEX is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_KEYRESET is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_KEYBOARD_AKKEY=y +# CONFIG_KEYBOARD_AK_KEYPAD is not set +# CONFIG_KEYBOARD_ADKEY is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_UNIX98_PTYS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +# CONFIG_DEVKMEM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +CONFIG_SERIAL_AK39_UART=y +CONFIG_SERIAL_AK39_CONSOLE=y +# CONFIG_SERIAL_GPIO_UART is not set +CONFIG_TTY_PRINTK=y +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_CARDMAN_4000 is not set +# CONFIG_CARDMAN_4040 is not set +# CONFIG_IPWIRELESS is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_SMBUS=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_SIMTEC is not set +CONFIG_I2C_ANYKA=y +CONFIG_I2C_AK39_HW=y +# CONFIG_I2C_GPIO_SOFT is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +CONFIG_SPI_BITBANG=y +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_ANYKA=y +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_HSI is not set + +# +# PPS support +# + +# +# PPS generators support +# + +# +# PTP clock support +# + +# +# Enable Device Drivers -> PPS to see the PTP clock options. +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_DW_WATCHDOG is not set +# CONFIG_MAX63XX_WATCHDOG is not set +# CONFIG_AK39_WATCHDOG is not set +CONFIG_AK39_WATCHDOG_TOP=y +# CONFIG_AK39_WATCHDOG_NONE is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_S5M_CORE is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_REGULATOR is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +# CONFIG_DVB_CORE is not set +CONFIG_VIDEO_MEDIA=y + +# +# Multimedia drivers +# +# CONFIG_RC_CORE is not set +# CONFIG_MEDIA_ATTACH is not set +CONFIG_MEDIA_TUNER=y +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_MEDIA_TUNER_SIMPLE=y +CONFIG_MEDIA_TUNER_TDA8290=y +CONFIG_MEDIA_TUNER_TDA827X=y +CONFIG_MEDIA_TUNER_TDA18271=y +CONFIG_MEDIA_TUNER_TDA9887=y +CONFIG_MEDIA_TUNER_TEA5767=y +CONFIG_MEDIA_TUNER_MT20XX=y +CONFIG_MEDIA_TUNER_XC2028=y +CONFIG_MEDIA_TUNER_XC5000=y +CONFIG_MEDIA_TUNER_XC4000=y +CONFIG_MEDIA_TUNER_MC44S803=y +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEOBUF_GEN=y +CONFIG_VIDEOBUF_DMA_CONTIG=y +CONFIG_VIDEOBUF2_CORE=y +CONFIG_VIDEO_CAPTURE_DRIVERS=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set + +# +# Encoders, decoders, sensors and other helper chips +# + +# +# Audio decoders, processors and mixers +# +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_VP27SMPX is not set + +# +# RDS decoders +# +# CONFIG_VIDEO_SAA6588 is not set + +# +# Video decoders +# +# CONFIG_VIDEO_ADV7180 is not set +# CONFIG_VIDEO_ADV7183 is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA7191 is not set +# CONFIG_VIDEO_TVP514X is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_TVP7002 is not set +# CONFIG_VIDEO_VPX3220 is not set + +# +# Video and audio decoders +# +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_CX25840 is not set + +# +# MPEG video encoders +# +# CONFIG_VIDEO_CX2341X is not set + +# +# Video encoders +# +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_ADV7343 is not set +# CONFIG_VIDEO_AK881X is not set + +# +# Camera sensor devices +# +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_VS6624 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_SR030PC30 is not set + +# +# Flash devices +# + +# +# Video improvement chips +# +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set + +# +# Miscelaneous helper chips +# +# CONFIG_VIDEO_THS7303 is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_V4L_USB_DRIVERS is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_SOC_CAMERA=y +# CONFIG_SOC_CAMERA_IMX074 is not set +# CONFIG_SOC_CAMERA_MT9M001 is not set +# CONFIG_SOC_CAMERA_MT9M111 is not set +# CONFIG_SOC_CAMERA_MT9T031 is not set +# CONFIG_SOC_CAMERA_MT9T112 is not set +# CONFIG_SOC_CAMERA_MT9V022 is not set +# CONFIG_SOC_CAMERA_RJ54N1 is not set +# CONFIG_SOC_CAMERA_TW9910 is not set +# CONFIG_SOC_CAMERA_PLATFORM is not set +# CONFIG_SOC_CAMERA_OV2640 is not set +# CONFIG_SOC_CAMERA_OV5642 is not set +# CONFIG_SOC_CAMERA_OV6650 is not set +# CONFIG_SOC_CAMERA_OV772X is not set +# CONFIG_SOC_CAMERA_OV9640 is not set +# CONFIG_SOC_CAMERA_OV9740 is not set +CONFIG_LINUX_AKSENSOR=y +CONFIG_AK_SENSOR_I2C=y +CONFIG_AK_SENSOR_COMMON=y +CONFIG_SENSOR_OV9712=m +CONFIG_SENSOR_H42=m +CONFIG_SENSOR_AR0130=m +CONFIG_SENSOR_SC1045=m +CONFIG_SENSOR_SC1035=m +CONFIG_SENSOR_SC1135=m +CONFIG_SENSOR_GC1024=m +# CONFIG_SENSOR_GM7150 is not set +CONFIG_SENSOR_SC1145=m +CONFIG_SENSOR_H61=m +# CONFIG_SENSOR_H65 is not set +# CONFIG_SENSOR_H62 is not set +# CONFIG_SENSOR_OV9732 is not set +CONFIG_SENSOR_OV9734=m +# CONFIG_SENSOR_SC1037 is not set +# CONFIG_SENSOR_GC1034 is not set +# CONFIG_SENSOR_GC2033 is not set +# CONFIG_SENSOR_GC2023 is not set +CONFIG_SENSOR_GC2053=m +# CONFIG_SENSOR_SC2235 is not set +# CONFIG_SENSOR_SC2232 is not set +CONFIG_SENSOR_SH2232=m +# CONFIG_SENSOR_SC4236 is not set +# CONFIG_SENSOR_SC2145 is not set +# CONFIG_SENSOR_F22 is not set +# CONFIG_SENSOR_F23 is not set +# CONFIG_SENSOR_F28 is not set +# CONFIG_SENSOR_HM2140 is not set +# CONFIG_SENSOR_IMX323 is not set +# CONFIG_SENSOR_PS5270 is not set +# CONFIG_SENSOR_SC2311 is not set +# CONFIG_SENSOR_PS5260 is not set +# CONFIG_SENSOR_IMX307 is not set +# CONFIG_VIDEO_SH_MOBILE_CSI2 is not set +# CONFIG_VIDEO_SH_MOBILE_CEU is not set +CONFIG_VIDEO_AK=m +CONFIG_AK_VIDEOBUF_DMA_CONTIG=y +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +# CONFIG_RADIO_ADAPTERS is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_ION=y +CONFIG_ION_AK=y +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_SOUND is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +CONFIG_USB_ANYKA_HCD=y +CONFIG_USB_AKOTG_HS_HCD=m +CONFIG_USB_AKOTG_DMA=y +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_RENESAS_USBHS is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_MV_UDC is not set +CONFIG_USB_AKUDC_PRODUCER=m +# CONFIG_USB_GADGET_AKUDC is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +CONFIG_USB_MASS_STORAGE=m +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_ACM_MS is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_G_WEBCAM is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_OTG_WAKELOCK is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set +# CONFIG_SDIO_WIFI is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_PXAV2 is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +CONFIG_MMC_ANYKA=y +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_GPIO is not set +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA9633 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_BD2802 is not set +CONFIG_LEDS_AK39=y +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_RENESAS_TPU is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_OT200 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_SWITCH is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_AK=y +CONFIG_AK39_RTC_SELECT_CLOCK=y +CONFIG_AK39_RTC_INTERNAL_RC_OSC=y +# CONFIG_AK39_RTC_EXTERNAL_XTAL is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +CONFIG_UIO=y +# CONFIG_UIO_PDRV is not set +# CONFIG_UIO_PDRV_GENIRQ is not set +CONFIG_UIO_VCODEC=y + +# +# Virtio drivers +# +# CONFIG_VIRTIO_BALLOON is not set + +# +# Microsoft Hyper-V guest support +# +# CONFIG_STAGING is not set +CONFIG_CLKDEV_LOOKUP=y + +# +# Hardware Spinlock drivers +# +# CONFIG_IOMMU_SUPPORT is not set + +# +# Remoteproc drivers (EXPERIMENTAL) +# + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_AEC_DUMP_DEBUG is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT/EXFAT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +# CONFIG_FAT_FCBMP is not set +# CONFIG_FAT_FSCK is not set +# CONFIG_FAT_CHK_DISK is not set +CONFIG_FAT_NOT_CHKDSK=y +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set +CONFIG_EXFAT_FS=y +CONFIG_EXFAT_DISCARD=y +# CONFIG_EXFAT_DELAYED_SYNC is not set +# CONFIG_EXFAT_KERNEL_DEBUG is not set +# CONFIG_EXFAT_DEBUG_MSG is not set +CONFIG_EXFAT_DEFAULT_CODEPAGE=437 +CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8" + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_HFSPLUS_FS is not set +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_YAFFS1=y +# CONFIG_YAFFS_9BYTE_TAGS is not set +# CONFIG_YAFFS_DOES_ECC is not set +CONFIG_YAFFS_YAFFS2=y +CONFIG_YAFFS_AUTO_YAFFS2=y +# CONFIG_YAFFS_DISABLE_TAGS_ECC is not set +# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set +# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set +# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set +# CONFIG_YAFFS_DISABLE_BACKGROUND is not set +# CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING is not set +CONFIG_YAFFS_XATTR=y +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_DEBUG is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR_NMI is not set +# CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_STACKTRACE is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_RODATA is not set +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_MANAGER2 is not set +# CONFIG_CRYPTO_USER is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +# CONFIG_CRYPTO_DEV is not set +# CONFIG_CRYPTO_HW is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +# CONFIG_CRC_CCITT is not set +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_CRC8 is not set +CONFIG_XZ_DEC=y +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_IA64 is not set +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +# CONFIG_XZ_DEC_SPARC is not set +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_AVERAGE=y +# CONFIG_CORDIC is not set diff --git a/arch/arm/configs/cloud39ev3_ak3918ev300_sdio_wifi_v1.0.0_defconfig b/arch/arm/configs/cloud39ev3_ak3918ev300_sdio_wifi_v1.0.0_defconfig new file mode 100644 index 00000000..862f7021 --- /dev/null +++ b/arch/arm/configs/cloud39ev3_ak3918ev300_sdio_wifi_v1.0.0_defconfig @@ -0,0 +1,1892 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.4.35 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_KTIME_SCALAR=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +# CONFIG_EXPERIMENTAL is not set +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_FHANDLE is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_SHOW=y + +# +# RCU Subsystem +# +CONFIG_TINY_RCU=y +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=16 +# CONFIG_CGROUPS is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=0 +CONFIG_EXPERT=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +# CONFIG_SHMEM is not set +CONFIG_AIO=y +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_SLUB_DEBUG is not set +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_JUMP_LABEL=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y + +# +# GCOV-based kernel profiling +# +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +CONFIG_ARCH_AK39=y +CONFIG_CPU_AK3918=y +# CONFIG_ARCH_SKY39EV2_AK3918E80PIN_COREBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3916E128PIN_COREBD is not set +# CONFIG_ARCH_CLOUD39EV2_AK3916E_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3916EV300_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3918EV300_MAINBD is not set +CONFIG_ARCH_CLOUD39EV3_AK3918EV300_SDIO_WIFI_MAINBD=y +# CONFIG_ARCH_CLOUD39EV3_AK3918E80PIN_COREBD is not set +# CONFIG_ARCH_CLOUD39EV3_CO38_SC2235 is not set +CONFIG_ASIC_CLK_120MHZ=y + +# +# Power management +# +# CONFIG_AK39_PM is not set +CONFIG_AK39_PWM_TIMER=y +CONFIG_PLAT_ANYKA=y + +# +# Processor Type +# +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +CONFIG_CPU_USE_DOMAINS=y + +# +# Processor Features +# +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +# CONFIG_ARM_THUMB is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set +# CONFIG_CACHE_L2X0 is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_NR_BANKS=8 +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_PCCARD=y +CONFIG_PCMCIA=y + +# +# PC-card bridges +# + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_ARCH_NR_GPIO=0 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +# CONFIG_HIGHMEM is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_NEED_PER_CPU_KM=y +# CONFIG_CLEANCACHE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_SECCOMP is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y + +# +# Boot options +# +# CONFIG_USE_OF is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_CMDLINE="root=/dev/mtdblock1 ro rootfstype=squashfs init=/sbin/init mem=64M console=ttySAK0,115200" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_AUTO_ZRELADDR is not set +CONFIG_RAM_BASE=0x80000000 +CONFIG_VIDEO_RESERVED_MEM_SIZE=0x2200000 + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_WAKELOCK=y +CONFIG_PM_SLEEP=y +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_APM_EMULATION is not set +CONFIG_PM_CLK=y +CONFIG_CPU_PM=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET_LRO=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +CONFIG_INET_UDP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_IPV6 is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK_ACCT is not set +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +# CONFIG_NETFILTER_NETLINK_LOG is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NETFILTER_XTABLES is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV4 is not set +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_PHONET is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +CONFIG_BQL=y +CONFIG_HAVE_BPF_JIT=y +# CONFIG_BPF_JIT is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=y +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=y +# CONFIG_LIB80211_DEBUG is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y +CONFIG_MAC80211=y +CONFIG_MAC80211_HAS_RC=y +# CONFIG_MAC80211_RC_PID is not set +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +# CONFIG_MAC80211_LEDS is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_SYNC is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOCG3 is not set +CONFIG_MTD_AK_SPIFLASH=y +# CONFIG_MTD_AK_SPINAND is not set +# CONFIG_MTD_NAND_IDS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=2 +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=2 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_ANYKA is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ATMEL_PWM is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_BMP085 is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_WL127X_RFKILL is not set +CONFIG_AK_SERIAL_NUMBER=y +CONFIG_AK_GETAIN=y +CONFIG_AK_MOTOR=y +# CONFIG_AK_DAC_CLOCK is not set + +# +# user space generic gpio controller +# +CONFIG_GPIOS_AKCUSTOM=y + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set +CONFIG_AK39_PCM=y +CONFIG_H2_ISP_CHAR=y +CONFIG_USER_GPIO=y +CONFIG_AK_INFO_DUMP=m +CONFIG_AK_PWM_CHAR=y +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_MII=y +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_ARCNET is not set + +# +# CAIF transport drivers +# +CONFIG_ETHERNET=y +CONFIG_NET_VENDOR_3COM=y +# CONFIG_PCMCIA_3C574 is not set +# CONFIG_PCMCIA_3C589 is not set +CONFIG_NET_VENDOR_AMD=y +# CONFIG_PCMCIA_NMCLAN is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +# CONFIG_NET_VENDOR_FARADAY is not set +CONFIG_NET_VENDOR_FUJITSU=y +# CONFIG_PCMCIA_FMVJ18X is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_ETHOC is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +CONFIG_NET_VENDOR_XIRCOM=y +# CONFIG_PCMCIA_XIRC2PS is not set +CONFIG_AK_ETHERNET=y +# CONFIG_ETHERNET_MAC_MII is not set +CONFIG_ETHERNET_MAC_RMII=y +# CONFIG_PHYLIB is not set +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_TR is not set + +# +# USB Network Adapters +# +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_IPHETH is not set +CONFIG_WLAN=y +CONFIG_PCMCIA_RAYCS=y +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_ATMEL is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_AIRO_CS is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_RTL8187 is not set +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_WIFI_CONTROL_FUNC is not set +# CONFIG_ATH_COMMON is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_BCMDHD is not set +# CONFIG_BRCMFMAC is not set +# CONFIG_HOSTAP is not set +# CONFIG_LIBERTAS is not set +# CONFIG_HERMES is not set +# CONFIG_RT2X00 is not set +# CONFIG_MWIFIEX is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_KEYRESET is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_KEYBOARD_AKKEY=y +# CONFIG_KEYBOARD_AK_KEYPAD is not set +# CONFIG_KEYBOARD_ADKEY is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_UNIX98_PTYS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +# CONFIG_DEVKMEM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +CONFIG_SERIAL_AK39_UART=y +CONFIG_SERIAL_AK39_CONSOLE=y +# CONFIG_SERIAL_GPIO_UART is not set +CONFIG_TTY_PRINTK=y +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_CARDMAN_4000 is not set +# CONFIG_CARDMAN_4040 is not set +# CONFIG_IPWIRELESS is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_SMBUS=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_SIMTEC is not set +CONFIG_I2C_ANYKA=y +CONFIG_I2C_AK39_HW=y +# CONFIG_I2C_GPIO_SOFT is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +CONFIG_SPI_BITBANG=y +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_ANYKA=y +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_HSI is not set + +# +# PPS support +# + +# +# PPS generators support +# + +# +# PTP clock support +# + +# +# Enable Device Drivers -> PPS to see the PTP clock options. +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_DW_WATCHDOG is not set +# CONFIG_MAX63XX_WATCHDOG is not set +# CONFIG_AK39_WATCHDOG is not set +CONFIG_AK39_WATCHDOG_TOP=y +# CONFIG_AK39_WATCHDOG_NONE is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_S5M_CORE is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_REGULATOR is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +# CONFIG_DVB_CORE is not set +CONFIG_VIDEO_MEDIA=y + +# +# Multimedia drivers +# +# CONFIG_RC_CORE is not set +# CONFIG_MEDIA_ATTACH is not set +CONFIG_MEDIA_TUNER=y +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_MEDIA_TUNER_SIMPLE=y +CONFIG_MEDIA_TUNER_TDA8290=y +CONFIG_MEDIA_TUNER_TDA827X=y +CONFIG_MEDIA_TUNER_TDA18271=y +CONFIG_MEDIA_TUNER_TDA9887=y +CONFIG_MEDIA_TUNER_TEA5767=y +CONFIG_MEDIA_TUNER_MT20XX=y +CONFIG_MEDIA_TUNER_XC2028=y +CONFIG_MEDIA_TUNER_XC5000=y +CONFIG_MEDIA_TUNER_XC4000=y +CONFIG_MEDIA_TUNER_MC44S803=y +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEOBUF_GEN=y +CONFIG_VIDEOBUF_DMA_CONTIG=y +CONFIG_VIDEOBUF2_CORE=y +CONFIG_VIDEO_CAPTURE_DRIVERS=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set + +# +# Encoders, decoders, sensors and other helper chips +# + +# +# Audio decoders, processors and mixers +# +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_VP27SMPX is not set + +# +# RDS decoders +# +# CONFIG_VIDEO_SAA6588 is not set + +# +# Video decoders +# +# CONFIG_VIDEO_ADV7180 is not set +# CONFIG_VIDEO_ADV7183 is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA7191 is not set +# CONFIG_VIDEO_TVP514X is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_TVP7002 is not set +# CONFIG_VIDEO_VPX3220 is not set + +# +# Video and audio decoders +# +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_CX25840 is not set + +# +# MPEG video encoders +# +# CONFIG_VIDEO_CX2341X is not set + +# +# Video encoders +# +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_ADV7343 is not set +# CONFIG_VIDEO_AK881X is not set + +# +# Camera sensor devices +# +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_VS6624 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_SR030PC30 is not set + +# +# Flash devices +# + +# +# Video improvement chips +# +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set + +# +# Miscelaneous helper chips +# +# CONFIG_VIDEO_THS7303 is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_V4L_USB_DRIVERS is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_SOC_CAMERA=y +# CONFIG_SOC_CAMERA_IMX074 is not set +# CONFIG_SOC_CAMERA_MT9M001 is not set +# CONFIG_SOC_CAMERA_MT9M111 is not set +# CONFIG_SOC_CAMERA_MT9T031 is not set +# CONFIG_SOC_CAMERA_MT9T112 is not set +# CONFIG_SOC_CAMERA_MT9V022 is not set +# CONFIG_SOC_CAMERA_RJ54N1 is not set +# CONFIG_SOC_CAMERA_TW9910 is not set +# CONFIG_SOC_CAMERA_PLATFORM is not set +# CONFIG_SOC_CAMERA_OV2640 is not set +# CONFIG_SOC_CAMERA_OV5642 is not set +# CONFIG_SOC_CAMERA_OV6650 is not set +# CONFIG_SOC_CAMERA_OV772X is not set +# CONFIG_SOC_CAMERA_OV9640 is not set +# CONFIG_SOC_CAMERA_OV9740 is not set +CONFIG_LINUX_AKSENSOR=y +CONFIG_AK_SENSOR_I2C=y +CONFIG_AK_SENSOR_COMMON=y +CONFIG_SENSOR_OV9712=m +CONFIG_SENSOR_H42=m +CONFIG_SENSOR_AR0130=m +CONFIG_SENSOR_SC1045=m +CONFIG_SENSOR_SC1035=m +CONFIG_SENSOR_SC1135=m +CONFIG_SENSOR_GC1024=m +CONFIG_SENSOR_GC2053=m +CONFIG_SENSOR_OV9734=m +CONFIG_SENSOR_SC2232=m +CONFIG_SENSOR_SC2232H_SC2135E=m +# CONFIG_SENSOR_GM7150 is not set +CONFIG_SENSOR_SC1145=m +CONFIG_SENSOR_H61=m +# CONFIG_SENSOR_H65 is not set +# CONFIG_SENSOR_H62 is not set +# CONFIG_SENSOR_OV9732 is not set +# CONFIG_SENSOR_SC1037 is not set +# CONFIG_SENSOR_GC1034 is not set +# CONFIG_SENSOR_SC2235 is not set +# CONFIG_VIDEO_SH_MOBILE_CSI2 is not set +# CONFIG_VIDEO_SH_MOBILE_CEU is not set +CONFIG_VIDEO_AK=m +CONFIG_AK_VIDEOBUF_DMA_CONTIG=y +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +# CONFIG_RADIO_ADAPTERS is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_ION=y +CONFIG_ION_AK=y +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_SOUND is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +CONFIG_USB_ANYKA_HCD=y +CONFIG_USB_AKOTG_HS_HCD=m +CONFIG_USB_AKOTG_DMA=y +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_RENESAS_USBHS is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_MV_UDC is not set +CONFIG_USB_AKUDC_PRODUCER=m +# CONFIG_USB_GADGET_AKUDC is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +CONFIG_USB_MASS_STORAGE=m +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_ACM_MS is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_G_WEBCAM is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_OTG_WAKELOCK is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set +# CONFIG_SDIO_WIFI is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_PXAV2 is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +CONFIG_MMC_ANYKA=y +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_GPIO is not set +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA9633 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_BD2802 is not set +CONFIG_LEDS_AK39=y +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_RENESAS_TPU is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_OT200 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_SWITCH is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_AK=y +CONFIG_AK39_RTC_SELECT_CLOCK=y +CONFIG_AK39_RTC_INTERNAL_RC_OSC=y +# CONFIG_AK39_RTC_EXTERNAL_XTAL is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +CONFIG_UIO=y +# CONFIG_UIO_PDRV is not set +# CONFIG_UIO_PDRV_GENIRQ is not set +CONFIG_UIO_VCODEC=y + +# +# Virtio drivers +# +# CONFIG_VIRTIO_BALLOON is not set + +# +# Microsoft Hyper-V guest support +# +# CONFIG_STAGING is not set +CONFIG_CLKDEV_LOOKUP=y + +# +# Hardware Spinlock drivers +# +# CONFIG_IOMMU_SUPPORT is not set + +# +# Remoteproc drivers (EXPERIMENTAL) +# + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_AEC_DUMP_DEBUG is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT/EXFAT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +# CONFIG_FAT_FCBMP is not set +# CONFIG_FAT_FSCK is not set +# CONFIG_FAT_CHK_DISK is not set +CONFIG_FAT_NOT_CHKDSK=y +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set +CONFIG_EXFAT_FS=y +CONFIG_EXFAT_DISCARD=y +# CONFIG_EXFAT_DELAYED_SYNC is not set +# CONFIG_EXFAT_KERNEL_DEBUG is not set +# CONFIG_EXFAT_DEBUG_MSG is not set +CONFIG_EXFAT_DEFAULT_CODEPAGE=437 +CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8" + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_HFSPLUS_FS is not set +# CONFIG_YAFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_WRITEBUFFER is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +# CONFIG_JFFS2_ZLIB is not set +# CONFIG_JFFS2_LZO is not set +# CONFIG_JFFS2_RTIME is not set +# CONFIG_JFFS2_RUBIN is not set +CONFIG_JFFS2_CMODE_NONE=y +# CONFIG_JFFS2_CMODE_PRIORITY is not set +# CONFIG_JFFS2_CMODE_SIZE is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +# CONFIG_CRAMFS is not set +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_XATTR is not set +CONFIG_SQUASHFS_ZLIB=y +# CONFIG_SQUASHFS_LZO is not set +CONFIG_SQUASHFS_XZ=y +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_DEBUG is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR_NMI is not set +# CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_STACKTRACE is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_RODATA is not set +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_MANAGER2 is not set +# CONFIG_CRYPTO_USER is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +# CONFIG_CRYPTO_DEV is not set +# CONFIG_CRYPTO_HW is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +# CONFIG_CRC_CCITT is not set +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_CRC8 is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_XZ_DEC=y +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_IA64 is not set +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +# CONFIG_XZ_DEC_SPARC is not set +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_AVERAGE=y +# CONFIG_CORDIC is not set diff --git a/arch/arm/configs/cloud39ev3_ak3919ev300_corebd_v1.0.0_defconfig b/arch/arm/configs/cloud39ev3_ak3919ev300_corebd_v1.0.0_defconfig new file mode 100644 index 00000000..e2c8c066 --- /dev/null +++ b/arch/arm/configs/cloud39ev3_ak3919ev300_corebd_v1.0.0_defconfig @@ -0,0 +1,1890 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.4.35 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_KTIME_SCALAR=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +# CONFIG_EXPERIMENTAL is not set +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_FHANDLE is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_SHOW=y + +# +# RCU Subsystem +# +CONFIG_TINY_RCU=y +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=16 +# CONFIG_CGROUPS is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=0 +CONFIG_EXPERT=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +# CONFIG_SHMEM is not set +CONFIG_AIO=y +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_SLUB_DEBUG is not set +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_JUMP_LABEL=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y + +# +# GCOV-based kernel profiling +# +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +CONFIG_ARCH_AK39=y +CONFIG_CPU_AK3919=y +# CONFIG_ARCH_CLOUD39EV3_AK3916E128PIN_COREBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3916EV300_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3918EV300_MAINBD is not set +CONFIG_ARCH_CLOUD39EV3_AK3919EV300_MAINBD=y +# CONFIG_ARCH_CLOUD39EV3_CO38_SC2235 is not set +# CONFIG_ARCH_CLOUD39EV3_AK3919EV300_SQ38_SC2232 is not set +# CONFIG_ARCH_CLOUD39EV3_SQ38_QFN64_F22_V100 is not set +CONFIG_ASIC_CLK_120MHZ=y + +# +# Power management +# +# CONFIG_AK39_PM is not set +CONFIG_AK39_PWM_TIMER=y +CONFIG_PLAT_ANYKA=y + +# +# Processor Type +# +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +CONFIG_CPU_USE_DOMAINS=y + +# +# Processor Features +# +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +# CONFIG_ARM_THUMB is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set +# CONFIG_CACHE_L2X0 is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_NR_BANKS=8 +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_PCCARD=y +CONFIG_PCMCIA=y + +# +# PC-card bridges +# + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_ARCH_NR_GPIO=0 +CONFIG_PREEMPT_NONE=y +CONFIG_PREEMPT_VOLUNTARY=y +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +# CONFIG_HIGHMEM is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_NEED_PER_CPU_KM=y +# CONFIG_CLEANCACHE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_SECCOMP is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y + +# +# Boot options +# +# CONFIG_USE_OF is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_CMDLINE="root=/dev/mtdblock1 ro rootfstype=squashfs init=/sbin/init mem=64M console=ttySAK0,115200" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_AUTO_ZRELADDR is not set +CONFIG_RAM_BASE=0x80000000 +CONFIG_VIDEO_RESERVED_MEM_SIZE=0x2200000 + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_WAKELOCK=y +CONFIG_PM_SLEEP=y +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_APM_EMULATION is not set +CONFIG_PM_CLK=y +CONFIG_CPU_PM=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET_LRO=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +CONFIG_INET_UDP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_IPV6 is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK_ACCT is not set +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +# CONFIG_NETFILTER_NETLINK_LOG is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NETFILTER_XTABLES is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV4 is not set +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_PHONET is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +CONFIG_BQL=y +CONFIG_HAVE_BPF_JIT=y +# CONFIG_BPF_JIT is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=y +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=y +# CONFIG_LIB80211_DEBUG is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y +CONFIG_MAC80211=y +CONFIG_MAC80211_HAS_RC=y +# CONFIG_MAC80211_RC_PID is not set +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +# CONFIG_MAC80211_LEDS is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_SYNC is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOCG3 is not set +CONFIG_MTD_AK_SPIFLASH=y +# CONFIG_MTD_AK_SPINAND is not set +# CONFIG_MTD_NAND_IDS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=2 +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=2 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_ANYKA is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ATMEL_PWM is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_BMP085 is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_WL127X_RFKILL is not set +CONFIG_AK_SERIAL_NUMBER=y +CONFIG_AK_GETAIN=y +CONFIG_AK_MOTOR=y +# CONFIG_AK_DAC_CLOCK is not set + +# +# user space generic gpio controller +# +CONFIG_GPIOS_AKCUSTOM=y + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set +CONFIG_AK39_PCM=y +CONFIG_H2_ISP_CHAR=y +CONFIG_USER_GPIO=y +CONFIG_AK_INFO_DUMP=m +CONFIG_AK_PWM_CHAR=y +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_MII==y +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_ARCNET is not set + +# +# CAIF transport drivers +# +CONFIG_ETHERNET=y +CONFIG_NET_VENDOR_3COM=y +# CONFIG_PCMCIA_3C574 is not set +# CONFIG_PCMCIA_3C589 is not set +CONFIG_NET_VENDOR_AMD=y +# CONFIG_PCMCIA_NMCLAN is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +# CONFIG_NET_VENDOR_FARADAY is not set +CONFIG_NET_VENDOR_FUJITSU=y +# CONFIG_PCMCIA_FMVJ18X is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_ETHOC is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +CONFIG_NET_VENDOR_XIRCOM=y +# CONFIG_PCMCIA_XIRC2PS is not set +CONFIG_AK_ETHERNET=y +# CONFIG_ETHERNET_MAC_MII is not set +CONFIG_ETHERNET_MAC_RMII=y +# CONFIG_PHYLIB is not set +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_TR is not set + +# +# USB Network Adapters +# +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_IPHETH is not set +CONFIG_WLAN=y +CONFIG_PCMCIA_RAYCS=y +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_ATMEL is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_AIRO_CS is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_RTL8187 is not set +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_WIFI_CONTROL_FUNC is not set +# CONFIG_ATH_COMMON is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_BCMDHD is not set +# CONFIG_BRCMFMAC is not set +# CONFIG_HOSTAP is not set +# CONFIG_LIBERTAS is not set +# CONFIG_HERMES is not set +# CONFIG_RT2X00 is not set +# CONFIG_MWIFIEX is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_KEYRESET is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_AKKEY is not set +# CONFIG_KEYBOARD_AK_KEYPAD is not set +CONFIG_KEYBOARD_ADKEY=y +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_UNIX98_PTYS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +# CONFIG_DEVKMEM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +CONFIG_SERIAL_AK39_UART=y +CONFIG_SERIAL_AK39_CONSOLE=y +# CONFIG_SERIAL_GPIO_UART is not set +CONFIG_TTY_PRINTK=y +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_CARDMAN_4000 is not set +# CONFIG_CARDMAN_4040 is not set +# CONFIG_IPWIRELESS is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_SMBUS=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_SIMTEC is not set +CONFIG_I2C_ANYKA=y +CONFIG_I2C_AK39_HW=y +# CONFIG_I2C_GPIO_SOFT is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +CONFIG_SPI_BITBANG=y +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_ANYKA=y +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_HSI is not set + +# +# PPS support +# + +# +# PPS generators support +# + +# +# PTP clock support +# + +# +# Enable Device Drivers -> PPS to see the PTP clock options. +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_DW_WATCHDOG is not set +# CONFIG_MAX63XX_WATCHDOG is not set +# CONFIG_AK39_WATCHDOG is not set +CONFIG_AK39_WATCHDOG_TOP=y +# CONFIG_AK39_WATCHDOG_NONE is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_S5M_CORE is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_REGULATOR is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +# CONFIG_DVB_CORE is not set +CONFIG_VIDEO_MEDIA=y + +# +# Multimedia drivers +# +# CONFIG_RC_CORE is not set +# CONFIG_MEDIA_ATTACH is not set +CONFIG_MEDIA_TUNER=y +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_MEDIA_TUNER_SIMPLE=y +CONFIG_MEDIA_TUNER_TDA8290=y +CONFIG_MEDIA_TUNER_TDA827X=y +CONFIG_MEDIA_TUNER_TDA18271=y +CONFIG_MEDIA_TUNER_TDA9887=y +CONFIG_MEDIA_TUNER_TEA5767=y +CONFIG_MEDIA_TUNER_MT20XX=y +CONFIG_MEDIA_TUNER_XC2028=y +CONFIG_MEDIA_TUNER_XC5000=y +CONFIG_MEDIA_TUNER_XC4000=y +CONFIG_MEDIA_TUNER_MC44S803=y +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEOBUF_GEN=y +CONFIG_VIDEOBUF_DMA_CONTIG=y +CONFIG_VIDEOBUF2_CORE=y +CONFIG_VIDEO_CAPTURE_DRIVERS=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set + +# +# Encoders, decoders, sensors and other helper chips +# + +# +# Audio decoders, processors and mixers +# +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_VP27SMPX is not set + +# +# RDS decoders +# +# CONFIG_VIDEO_SAA6588 is not set + +# +# Video decoders +# +# CONFIG_VIDEO_ADV7180 is not set +# CONFIG_VIDEO_ADV7183 is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA7191 is not set +# CONFIG_VIDEO_TVP514X is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_TVP7002 is not set +# CONFIG_VIDEO_VPX3220 is not set + +# +# Video and audio decoders +# +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_CX25840 is not set + +# +# MPEG video encoders +# +# CONFIG_VIDEO_CX2341X is not set + +# +# Video encoders +# +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_ADV7343 is not set +# CONFIG_VIDEO_AK881X is not set + +# +# Camera sensor devices +# +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_VS6624 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_SR030PC30 is not set + +# +# Flash devices +# + +# +# Video improvement chips +# +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set + +# +# Miscelaneous helper chips +# +# CONFIG_VIDEO_THS7303 is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_V4L_USB_DRIVERS is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_SOC_CAMERA=y +# CONFIG_SOC_CAMERA_IMX074 is not set +# CONFIG_SOC_CAMERA_MT9M001 is not set +# CONFIG_SOC_CAMERA_MT9M111 is not set +# CONFIG_SOC_CAMERA_MT9T031 is not set +# CONFIG_SOC_CAMERA_MT9T112 is not set +# CONFIG_SOC_CAMERA_MT9V022 is not set +# CONFIG_SOC_CAMERA_RJ54N1 is not set +# CONFIG_SOC_CAMERA_TW9910 is not set +# CONFIG_SOC_CAMERA_PLATFORM is not set +# CONFIG_SOC_CAMERA_OV2640 is not set +# CONFIG_SOC_CAMERA_OV5642 is not set +# CONFIG_SOC_CAMERA_OV6650 is not set +# CONFIG_SOC_CAMERA_OV772X is not set +# CONFIG_SOC_CAMERA_OV9640 is not set +# CONFIG_SOC_CAMERA_OV9740 is not set +CONFIG_LINUX_AKSENSOR=y +CONFIG_AK_SENSOR_I2C=y +CONFIG_AK_SENSOR_COMMON=y +CONFIG_SENSOR_OV9712=m +CONFIG_SENSOR_H42=m +CONFIG_SENSOR_AR0130=m +CONFIG_SENSOR_SC1045=m +CONFIG_SENSOR_SC1035=m +CONFIG_SENSOR_SC1135=m +CONFIG_SENSOR_GC1024=m +# CONFIG_SENSOR_GM7150 is not set +CONFIG_SENSOR_SC1145=m +CONFIG_SENSOR_H61=m +# CONFIG_SENSOR_H65 is not set +# CONFIG_SENSOR_H62 is not set +# CONFIG_SENSOR_OV9732 is not set +# CONFIG_SENSOR_SC1037 is not set +# CONFIG_SENSOR_GC1034 is not set +# CONFIG_SENSOR_GC2033 is not set +# CONFIG_SENSOR_SC2235 is not set +# CONFIG_SENSOR_SC2145 is not set +# CONFIG_SENSOR_F22 is not set +# CONFIG_VIDEO_SH_MOBILE_CSI2 is not set +# CONFIG_VIDEO_SH_MOBILE_CEU is not set +CONFIG_VIDEO_AK=m +CONFIG_AK_VIDEOBUF_DMA_CONTIG=y +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +# CONFIG_RADIO_ADAPTERS is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_ION=y +CONFIG_ION_AK=y +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_SOUND is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +CONFIG_USB_ANYKA_HCD=y +CONFIG_USB_AKOTG_HS_HCD=m +CONFIG_USB_AKOTG_DMA=y +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_RENESAS_USBHS is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_MV_UDC is not set +CONFIG_USB_AKUDC_PRODUCER=m +# CONFIG_USB_GADGET_AKUDC is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +CONFIG_USB_MASS_STORAGE=m +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_ACM_MS is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_G_WEBCAM is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_OTG_WAKELOCK is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set +# CONFIG_SDIO_WIFI is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_PXAV2 is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +CONFIG_MMC_ANYKA=y +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_GPIO is not set +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA9633 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_BD2802 is not set +CONFIG_LEDS_AK39=y +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_RENESAS_TPU is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_OT200 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_SWITCH is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_AK=y +CONFIG_AK39_RTC_SELECT_CLOCK=y +CONFIG_AK39_RTC_INTERNAL_RC_OSC=y +# CONFIG_AK39_RTC_EXTERNAL_XTAL is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +CONFIG_UIO=y +# CONFIG_UIO_PDRV is not set +# CONFIG_UIO_PDRV_GENIRQ is not set +CONFIG_UIO_VCODEC=y + +# +# Virtio drivers +# +# CONFIG_VIRTIO_BALLOON is not set + +# +# Microsoft Hyper-V guest support +# +# CONFIG_STAGING is not set +CONFIG_CLKDEV_LOOKUP=y + +# +# Hardware Spinlock drivers +# +# CONFIG_IOMMU_SUPPORT is not set + +# +# Remoteproc drivers (EXPERIMENTAL) +# + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_AEC_DUMP_DEBUG is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT/EXFAT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +# CONFIG_FAT_FCBMP is not set +# CONFIG_FAT_FSCK is not set +# CONFIG_FAT_CHK_DISK is not set +CONFIG_FAT_NOT_CHKDSK=y +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set +CONFIG_EXFAT_FS=y +CONFIG_EXFAT_DISCARD=y +# CONFIG_EXFAT_DELAYED_SYNC is not set +# CONFIG_EXFAT_KERNEL_DEBUG is not set +# CONFIG_EXFAT_DEBUG_MSG is not set +CONFIG_EXFAT_DEFAULT_CODEPAGE=437 +CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8" + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_HFSPLUS_FS is not set +# CONFIG_YAFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_WRITEBUFFER is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +# CONFIG_JFFS2_ZLIB is not set +# CONFIG_JFFS2_LZO is not set +# CONFIG_JFFS2_RTIME is not set +# CONFIG_JFFS2_RUBIN is not set +CONFIG_JFFS2_CMODE_NONE=y +# CONFIG_JFFS2_CMODE_PRIORITY is not set +# CONFIG_JFFS2_CMODE_SIZE is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +# CONFIG_CRAMFS is not set +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_XATTR is not set +CONFIG_SQUASHFS_ZLIB=y +# CONFIG_SQUASHFS_LZO is not set +CONFIG_SQUASHFS_XZ=y +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_DEBUG is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR_NMI is not set +# CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_STACKTRACE is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_RODATA is not set +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_MANAGER2 is not set +# CONFIG_CRYPTO_USER is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +# CONFIG_CRYPTO_DEV is not set +# CONFIG_CRYPTO_HW is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +# CONFIG_CRC_CCITT is not set +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_CRC8 is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_XZ_DEC=y +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_IA64 is not set +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +# CONFIG_XZ_DEC_SPARC is not set +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_AVERAGE=y +# CONFIG_CORDIC is not set diff --git a/arch/arm/configs/cloud39ev3_ak3919ev300_corebd_v1.0.0_spinand_defconfig b/arch/arm/configs/cloud39ev3_ak3919ev300_corebd_v1.0.0_spinand_defconfig new file mode 100644 index 00000000..5a991a20 --- /dev/null +++ b/arch/arm/configs/cloud39ev3_ak3919ev300_corebd_v1.0.0_spinand_defconfig @@ -0,0 +1,1894 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.4.35 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_KTIME_SCALAR=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +# CONFIG_EXPERIMENTAL is not set +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_FHANDLE is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_SHOW=y + +# +# RCU Subsystem +# +CONFIG_TINY_RCU=y +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=16 +# CONFIG_CGROUPS is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=0 +CONFIG_EXPERT=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +# CONFIG_SHMEM is not set +CONFIG_AIO=y +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_SLUB_DEBUG is not set +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_JUMP_LABEL=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y + +# +# GCOV-based kernel profiling +# +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +CONFIG_ARCH_AK39=y +CONFIG_CPU_AK3919=y +# CONFIG_ARCH_CLOUD39EV3_AK3916E128PIN_COREBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3916EV300_MAINBD is not set +CONFIG_ARCH_CLOUD39EV3_AK3919EV300_MAINBD=y +# CONFIG_ARCH_CLOUD39EV3_AK3918EV300_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_CO38_SC2235 is not set +# CONFIG_ARCH_CLOUD39EV3_AK3919EV300_SQ38_SC2232 is not set +# CONFIG_ARCH_CLOUD39EV3_SQ38_QFN64_F22_V100 is not set +CONFIG_ASIC_CLK_120MHZ=y + +# +# Power management +# +# CONFIG_AK39_PM is not set +CONFIG_AK39_PWM_TIMER=y +CONFIG_PLAT_ANYKA=y + +# +# Processor Type +# +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +CONFIG_CPU_USE_DOMAINS=y + +# +# Processor Features +# +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +# CONFIG_ARM_THUMB is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set +# CONFIG_CACHE_L2X0 is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_NR_BANKS=8 +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_PCCARD=y +CONFIG_PCMCIA=y + +# +# PC-card bridges +# + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_ARCH_NR_GPIO=0 +CONFIG_PREEMPT_NONE=y +CONFIG_PREEMPT_VOLUNTARY=y +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +# CONFIG_HIGHMEM is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_NEED_PER_CPU_KM=y +# CONFIG_CLEANCACHE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_SECCOMP is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y + +# +# Boot options +# +# CONFIG_USE_OF is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_CMDLINE="root=/dev/mtdblock4 ro rootfstype=yaffs2 init=/sbin/init mem=64M console=ttySAK0,115200" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_AUTO_ZRELADDR is not set +CONFIG_RAM_BASE=0x80000000 +CONFIG_VIDEO_RESERVED_MEM_SIZE=0x2200000 + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_WAKELOCK=y +CONFIG_PM_SLEEP=y +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_APM_EMULATION is not set +CONFIG_PM_CLK=y +CONFIG_CPU_PM=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET_LRO=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +CONFIG_INET_UDP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_IPV6 is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK_ACCT is not set +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +# CONFIG_NETFILTER_NETLINK_LOG is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NETFILTER_XTABLES is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV4 is not set +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_PHONET is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +CONFIG_BQL=y +CONFIG_HAVE_BPF_JIT=y +# CONFIG_BPF_JIT is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=y +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=y +# CONFIG_LIB80211_DEBUG is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y +CONFIG_MAC80211=y +CONFIG_MAC80211_HAS_RC=y +# CONFIG_MAC80211_RC_PID is not set +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +# CONFIG_MAC80211_LEDS is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_SYNC is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOCG3 is not set +# CONFIG_MTD_AK_SPIFLASH is not set +CONFIG_MTD_AK_SPINAND=y +# CONFIG_MTD_NAND_IDS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=2 +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=2 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_ANYKA is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ATMEL_PWM is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_BMP085 is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_WL127X_RFKILL is not set +CONFIG_AK_SERIAL_NUMBER=y +CONFIG_AK_GETAIN=y +CONFIG_AK_MOTOR=y +# CONFIG_AK_DAC_CLOCK is not set + +# +# user space generic gpio controller +# +CONFIG_GPIOS_AKCUSTOM=y + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set +CONFIG_AK39_PCM=y +CONFIG_H2_ISP_CHAR=y +CONFIG_USER_GPIO=y +CONFIG_AK_INFO_DUMP=m +CONFIG_AK_PWM_CHAR=y +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_MII=y +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_ARCNET is not set + +# +# CAIF transport drivers +# +CONFIG_ETHERNET=y +CONFIG_NET_VENDOR_3COM=y +# CONFIG_PCMCIA_3C574 is not set +# CONFIG_PCMCIA_3C589 is not set +CONFIG_NET_VENDOR_AMD=y +# CONFIG_PCMCIA_NMCLAN is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +# CONFIG_NET_VENDOR_FARADAY is not set +CONFIG_NET_VENDOR_FUJITSU=y +# CONFIG_PCMCIA_FMVJ18X is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_ETHOC is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +CONFIG_NET_VENDOR_XIRCOM=y +# CONFIG_PCMCIA_XIRC2PS is not set +CONFIG_AK_ETHERNET=y +# CONFIG_ETHERNET_MAC_MII is not set +CONFIG_ETHERNET_MAC_RMII=y +# CONFIG_PHYLIB is not set +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_TR is not set + +# +# USB Network Adapters +# +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_IPHETH is not set +CONFIG_WLAN=y +CONFIG_PCMCIA_RAYCS=y +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_ATMEL is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_AIRO_CS is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_RTL8187 is not set +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_WIFI_CONTROL_FUNC is not set +# CONFIG_ATH_COMMON is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_BCMDHD is not set +# CONFIG_BRCMFMAC is not set +# CONFIG_HOSTAP is not set +# CONFIG_LIBERTAS is not set +# CONFIG_HERMES is not set +# CONFIG_RT2X00 is not set +# CONFIG_MWIFIEX is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_KEYRESET is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_KEYBOARD_ADKEY=y +# CONFIG_KEYBOARD_AK_KEYPAD is not set +# CONFIG_KEYBOARD_AKKEY is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_UNIX98_PTYS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +# CONFIG_DEVKMEM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +CONFIG_SERIAL_AK39_UART=y +CONFIG_SERIAL_AK39_CONSOLE=y +# CONFIG_SERIAL_GPIO_UART is not set +CONFIG_TTY_PRINTK=y +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_CARDMAN_4000 is not set +# CONFIG_CARDMAN_4040 is not set +# CONFIG_IPWIRELESS is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_SMBUS=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_SIMTEC is not set +CONFIG_I2C_ANYKA=y +CONFIG_I2C_AK39_HW=y +# CONFIG_I2C_GPIO_SOFT is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +CONFIG_SPI_BITBANG=y +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_ANYKA=y +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_HSI is not set + +# +# PPS support +# + +# +# PPS generators support +# + +# +# PTP clock support +# + +# +# Enable Device Drivers -> PPS to see the PTP clock options. +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_DW_WATCHDOG is not set +# CONFIG_MAX63XX_WATCHDOG is not set +# CONFIG_AK39_WATCHDOG is not set +CONFIG_AK39_WATCHDOG_TOP=y +# CONFIG_AK39_WATCHDOG_NONE is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_S5M_CORE is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_REGULATOR is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +# CONFIG_DVB_CORE is not set +CONFIG_VIDEO_MEDIA=y + +# +# Multimedia drivers +# +# CONFIG_RC_CORE is not set +# CONFIG_MEDIA_ATTACH is not set +CONFIG_MEDIA_TUNER=y +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_MEDIA_TUNER_SIMPLE=y +CONFIG_MEDIA_TUNER_TDA8290=y +CONFIG_MEDIA_TUNER_TDA827X=y +CONFIG_MEDIA_TUNER_TDA18271=y +CONFIG_MEDIA_TUNER_TDA9887=y +CONFIG_MEDIA_TUNER_TEA5767=y +CONFIG_MEDIA_TUNER_MT20XX=y +CONFIG_MEDIA_TUNER_XC2028=y +CONFIG_MEDIA_TUNER_XC5000=y +CONFIG_MEDIA_TUNER_XC4000=y +CONFIG_MEDIA_TUNER_MC44S803=y +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEOBUF_GEN=y +CONFIG_VIDEOBUF_DMA_CONTIG=y +CONFIG_VIDEOBUF2_CORE=y +CONFIG_VIDEO_CAPTURE_DRIVERS=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set + +# +# Encoders, decoders, sensors and other helper chips +# + +# +# Audio decoders, processors and mixers +# +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_VP27SMPX is not set + +# +# RDS decoders +# +# CONFIG_VIDEO_SAA6588 is not set + +# +# Video decoders +# +# CONFIG_VIDEO_ADV7180 is not set +# CONFIG_VIDEO_ADV7183 is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA7191 is not set +# CONFIG_VIDEO_TVP514X is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_TVP7002 is not set +# CONFIG_VIDEO_VPX3220 is not set + +# +# Video and audio decoders +# +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_CX25840 is not set + +# +# MPEG video encoders +# +# CONFIG_VIDEO_CX2341X is not set + +# +# Video encoders +# +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_ADV7343 is not set +# CONFIG_VIDEO_AK881X is not set + +# +# Camera sensor devices +# +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_VS6624 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_SR030PC30 is not set + +# +# Flash devices +# + +# +# Video improvement chips +# +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set + +# +# Miscelaneous helper chips +# +# CONFIG_VIDEO_THS7303 is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_V4L_USB_DRIVERS is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_SOC_CAMERA=y +# CONFIG_SOC_CAMERA_IMX074 is not set +# CONFIG_SOC_CAMERA_MT9M001 is not set +# CONFIG_SOC_CAMERA_MT9M111 is not set +# CONFIG_SOC_CAMERA_MT9T031 is not set +# CONFIG_SOC_CAMERA_MT9T112 is not set +# CONFIG_SOC_CAMERA_MT9V022 is not set +# CONFIG_SOC_CAMERA_RJ54N1 is not set +# CONFIG_SOC_CAMERA_TW9910 is not set +# CONFIG_SOC_CAMERA_PLATFORM is not set +# CONFIG_SOC_CAMERA_OV2640 is not set +# CONFIG_SOC_CAMERA_OV5642 is not set +# CONFIG_SOC_CAMERA_OV6650 is not set +# CONFIG_SOC_CAMERA_OV772X is not set +# CONFIG_SOC_CAMERA_OV9640 is not set +# CONFIG_SOC_CAMERA_OV9740 is not set +CONFIG_LINUX_AKSENSOR=y +CONFIG_AK_SENSOR_I2C=y +CONFIG_AK_SENSOR_COMMON=y +CONFIG_SENSOR_OV9712=m +CONFIG_SENSOR_H42=m +CONFIG_SENSOR_AR0130=m +CONFIG_SENSOR_SC1045=m +CONFIG_SENSOR_SC1035=m +CONFIG_SENSOR_SC1135=m +CONFIG_SENSOR_GC1024=m +# CONFIG_SENSOR_GM7150 is not set +CONFIG_SENSOR_SC1145=m +CONFIG_SENSOR_H61=m +# CONFIG_SENSOR_H65 is not set +# CONFIG_SENSOR_H62 is not set +# CONFIG_SENSOR_OV9732 is not set +# CONFIG_SENSOR_SC1037 is not set +# CONFIG_SENSOR_GC1034 is not set +# CONFIG_SENSOR_GC2033 is not set +# CONFIG_SENSOR_SC2235 is not set +# CONFIG_SENSOR_SC2145 is not set +# CONFIG_SENSOR_F22 is not set +# CONFIG_VIDEO_SH_MOBILE_CSI2 is not set +# CONFIG_VIDEO_SH_MOBILE_CEU is not set +CONFIG_VIDEO_AK=m +CONFIG_AK_VIDEOBUF_DMA_CONTIG=y +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +# CONFIG_RADIO_ADAPTERS is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_ION=y +CONFIG_ION_AK=y +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_SOUND is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +CONFIG_USB_ANYKA_HCD=y +CONFIG_USB_AKOTG_HS_HCD=m +CONFIG_USB_AKOTG_DMA=y +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_RENESAS_USBHS is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_MV_UDC is not set +CONFIG_USB_AKUDC_PRODUCER=m +# CONFIG_USB_GADGET_AKUDC is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +CONFIG_USB_MASS_STORAGE=m +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_ACM_MS is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_G_WEBCAM is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_OTG_WAKELOCK is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set +# CONFIG_SDIO_WIFI is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_PXAV2 is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +CONFIG_MMC_ANYKA=y +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_GPIO is not set +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA9633 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_BD2802 is not set +CONFIG_LEDS_AK39=y +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_RENESAS_TPU is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_OT200 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_SWITCH is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_AK=y +CONFIG_AK39_RTC_SELECT_CLOCK=y +CONFIG_AK39_RTC_INTERNAL_RC_OSC=y +# CONFIG_AK39_RTC_EXTERNAL_XTAL is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +CONFIG_UIO=y +# CONFIG_UIO_PDRV is not set +# CONFIG_UIO_PDRV_GENIRQ is not set +CONFIG_UIO_VCODEC=y + +# +# Virtio drivers +# +# CONFIG_VIRTIO_BALLOON is not set + +# +# Microsoft Hyper-V guest support +# +# CONFIG_STAGING is not set +CONFIG_CLKDEV_LOOKUP=y + +# +# Hardware Spinlock drivers +# +# CONFIG_IOMMU_SUPPORT is not set + +# +# Remoteproc drivers (EXPERIMENTAL) +# + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_AEC_DUMP_DEBUG is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT/EXFAT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +# CONFIG_FAT_FCBMP is not set +# CONFIG_FAT_FSCK is not set +# CONFIG_FAT_CHK_DISK is not set +CONFIG_FAT_NOT_CHKDSK=y +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set +CONFIG_EXFAT_FS=y +CONFIG_EXFAT_DISCARD=y +# CONFIG_EXFAT_DELAYED_SYNC is not set +# CONFIG_EXFAT_KERNEL_DEBUG is not set +# CONFIG_EXFAT_DEBUG_MSG is not set +CONFIG_EXFAT_DEFAULT_CODEPAGE=437 +CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8" + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_HFSPLUS_FS is not set +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_YAFFS1=y +CONFIG_YAFFS_YAFFS2=y +CONFIG_YAFFS_AUTO_YAFFS2=y +CONFIG_YAFFS_XATTR=y +# CONFIG_JFFS2_FS is not set +# CONFIG_JFFS2_FS_DEBUG is not set +# CONFIG_JFFS2_FS_WRITEBUFFER is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +# CONFIG_JFFS2_ZLIB is not set +# CONFIG_JFFS2_LZO is not set +# CONFIG_JFFS2_RTIME is not set +# CONFIG_JFFS2_RUBIN is not set +# CONFIG_JFFS2_CMODE_NONE is not set +# CONFIG_JFFS2_CMODE_PRIORITY is not set +# CONFIG_JFFS2_CMODE_SIZE is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_SQUASHFS_XATTR is not set +# CONFIG_SQUASHFS_ZLIB is not set +# CONFIG_SQUASHFS_LZO is not set +# CONFIG_SQUASHFS_XZ is not set +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +# CONFIG_SQUASHFS_EMBEDDED is not set +# CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_DEBUG is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR_NMI is not set +# CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_STACKTRACE is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_RODATA is not set +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_MANAGER2 is not set +# CONFIG_CRYPTO_USER is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +# CONFIG_CRYPTO_DEV is not set +# CONFIG_CRYPTO_HW is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +# CONFIG_CRC_CCITT is not set +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_CRC8 is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_XZ_DEC=y +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_IA64 is not set +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +# CONFIG_XZ_DEC_SPARC is not set +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_AVERAGE=y +# CONFIG_CORDIC is not set diff --git a/arch/arm/configs/cloud39ev3_ak3919ev300_sq38_sc2232_v1.0.0_defconfig b/arch/arm/configs/cloud39ev3_ak3919ev300_sq38_sc2232_v1.0.0_defconfig new file mode 100644 index 00000000..5e8270df --- /dev/null +++ b/arch/arm/configs/cloud39ev3_ak3919ev300_sq38_sc2232_v1.0.0_defconfig @@ -0,0 +1,1898 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.4.35 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_KTIME_SCALAR=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +# CONFIG_EXPERIMENTAL is not set +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_FHANDLE is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_SHOW=y + +# +# RCU Subsystem +# +CONFIG_TINY_RCU=y +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=16 +# CONFIG_CGROUPS is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=0 +CONFIG_EXPERT=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +# CONFIG_SHMEM is not set +CONFIG_AIO=y +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_SLUB_DEBUG is not set +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_JUMP_LABEL=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y + +# +# GCOV-based kernel profiling +# +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +CONFIG_ARCH_AK39=y +CONFIG_CPU_AK3919=y +# CONFIG_ARCH_CLOUD39EV3_AK3916E128PIN_COREBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3916EV300_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3918EV300_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3918EV300_YTJ_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3919EV300_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_CO38_SC2235 is not set +CONFIG_ARCH_CLOUD39EV3_AK3919EV300_SQ38_SC2232_V1_0_0=y +# CONFIG_ARCH_CLOUD39EV3_AK3919EV300_SQ38_SC2232_V1_0_1 is not set +# CONFIG_ARCH_CLOUD39EV3_SQ38_QFN64_F22_V100 is not set +CONFIG_ASIC_CLK_120MHZ=y + +# +# Power management +# +# CONFIG_AK39_PM is not set +CONFIG_AK39_PWM_TIMER=y +CONFIG_PLAT_ANYKA=y + +# +# Processor Type +# +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +CONFIG_CPU_USE_DOMAINS=y + +# +# Processor Features +# +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +# CONFIG_ARM_THUMB is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set +# CONFIG_CACHE_L2X0 is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_NR_BANKS=8 +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_PCCARD=y +CONFIG_PCMCIA=y + +# +# PC-card bridges +# + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_ARCH_NR_GPIO=0 +CONFIG_PREEMPT_NONE=y +CONFIG_PREEMPT_VOLUNTARY=y +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +# CONFIG_HIGHMEM is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_NEED_PER_CPU_KM=y +# CONFIG_CLEANCACHE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_SECCOMP is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y + +# +# Boot options +# +# CONFIG_USE_OF is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_CMDLINE="root=/dev/mtdblock1 ro rootfstype=squashfs init=/sbin/init mem=64M console=ttySAK0,115200" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_AUTO_ZRELADDR is not set +CONFIG_RAM_BASE=0x80000000 +CONFIG_VIDEO_RESERVED_MEM_SIZE=0x2200000 + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_WAKELOCK=y +CONFIG_PM_SLEEP=y +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_APM_EMULATION is not set +CONFIG_PM_CLK=y +CONFIG_CPU_PM=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET_LRO=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +CONFIG_INET_UDP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_IPV6 is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK_ACCT is not set +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +# CONFIG_NETFILTER_NETLINK_LOG is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NETFILTER_XTABLES is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV4 is not set +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_PHONET is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +CONFIG_BQL=y +CONFIG_HAVE_BPF_JIT=y +# CONFIG_BPF_JIT is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=y +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=y +# CONFIG_LIB80211_DEBUG is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y +CONFIG_MAC80211=y +CONFIG_MAC80211_HAS_RC=y +# CONFIG_MAC80211_RC_PID is not set +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +# CONFIG_MAC80211_LEDS is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_SYNC is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOCG3 is not set +CONFIG_MTD_AK_SPIFLASH=y +# CONFIG_MTD_AK_SPINAND is not set +# CONFIG_MTD_NAND_IDS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=2 +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=2 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_ANYKA is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ATMEL_PWM is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_BMP085 is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_WL127X_RFKILL is not set +CONFIG_AK_SERIAL_NUMBER=y +CONFIG_AK_GETAIN=y +CONFIG_AK_MOTOR=y +# CONFIG_AK_DAC_CLOCK is not set + +# +# user space generic gpio controller +# +CONFIG_GPIOS_AKCUSTOM=y + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set +CONFIG_AK39_PCM=y +CONFIG_H2_ISP_CHAR=y +CONFIG_USER_GPIO=y +CONFIG_AK_INFO_DUMP=m +CONFIG_AK_PWM_CHAR=y +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_MII=y +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_ARCNET is not set + +# +# CAIF transport drivers +# +CONFIG_ETHERNET=y +CONFIG_NET_VENDOR_3COM=y +# CONFIG_PCMCIA_3C574 is not set +# CONFIG_PCMCIA_3C589 is not set +CONFIG_NET_VENDOR_AMD=y +# CONFIG_PCMCIA_NMCLAN is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +# CONFIG_NET_VENDOR_FARADAY is not set +CONFIG_NET_VENDOR_FUJITSU=y +# CONFIG_PCMCIA_FMVJ18X is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_ETHOC is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +CONFIG_NET_VENDOR_XIRCOM=y +# CONFIG_PCMCIA_XIRC2PS is not set +CONFIG_AK_ETHERNET=y +# CONFIG_ETHERNET_MAC_MII is not set +CONFIG_ETHERNET_MAC_RMII=y +# CONFIG_PHYLIB is not set +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_TR is not set + +# +# USB Network Adapters +# +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_IPHETH is not set +CONFIG_WLAN=y +CONFIG_PCMCIA_RAYCS=y +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_ATMEL is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_AIRO_CS is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_RTL8187 is not set +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_WIFI_CONTROL_FUNC is not set +# CONFIG_ATH_COMMON is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_BCMDHD is not set +# CONFIG_BRCMFMAC is not set +# CONFIG_HOSTAP is not set +# CONFIG_LIBERTAS is not set +# CONFIG_HERMES is not set +# CONFIG_RT2X00 is not set +# CONFIG_MWIFIEX is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_KEYRESET is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_KEYBOARD_AKKEY=y +# CONFIG_KEYBOARD_AK_KEYPAD is not set +# CONFIG_KEYBOARD_ADKEY is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_UNIX98_PTYS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +# CONFIG_DEVKMEM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +CONFIG_SERIAL_AK39_UART=y +CONFIG_SERIAL_AK39_CONSOLE=y +# CONFIG_SERIAL_GPIO_UART is not set +CONFIG_TTY_PRINTK=y +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_CARDMAN_4000 is not set +# CONFIG_CARDMAN_4040 is not set +# CONFIG_IPWIRELESS is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_SMBUS=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_SIMTEC is not set +CONFIG_I2C_ANYKA=y +CONFIG_I2C_AK39_HW=y +# CONFIG_I2C_GPIO_SOFT is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +CONFIG_SPI_BITBANG=y +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_ANYKA=y +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_HSI is not set + +# +# PPS support +# + +# +# PPS generators support +# + +# +# PTP clock support +# + +# +# Enable Device Drivers -> PPS to see the PTP clock options. +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_DW_WATCHDOG is not set +# CONFIG_MAX63XX_WATCHDOG is not set +# CONFIG_AK39_WATCHDOG is not set +CONFIG_AK39_WATCHDOG_TOP=y +# CONFIG_AK39_WATCHDOG_NONE is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_S5M_CORE is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_REGULATOR is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +# CONFIG_DVB_CORE is not set +CONFIG_VIDEO_MEDIA=y + +# +# Multimedia drivers +# +# CONFIG_RC_CORE is not set +# CONFIG_MEDIA_ATTACH is not set +CONFIG_MEDIA_TUNER=y +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_MEDIA_TUNER_SIMPLE=y +CONFIG_MEDIA_TUNER_TDA8290=y +CONFIG_MEDIA_TUNER_TDA827X=y +CONFIG_MEDIA_TUNER_TDA18271=y +CONFIG_MEDIA_TUNER_TDA9887=y +CONFIG_MEDIA_TUNER_TEA5767=y +CONFIG_MEDIA_TUNER_MT20XX=y +CONFIG_MEDIA_TUNER_XC2028=y +CONFIG_MEDIA_TUNER_XC5000=y +CONFIG_MEDIA_TUNER_XC4000=y +CONFIG_MEDIA_TUNER_MC44S803=y +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEOBUF_GEN=y +CONFIG_VIDEOBUF_DMA_CONTIG=y +CONFIG_VIDEOBUF2_CORE=y +CONFIG_VIDEO_CAPTURE_DRIVERS=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set + +# +# Encoders, decoders, sensors and other helper chips +# + +# +# Audio decoders, processors and mixers +# +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_VP27SMPX is not set + +# +# RDS decoders +# +# CONFIG_VIDEO_SAA6588 is not set + +# +# Video decoders +# +# CONFIG_VIDEO_ADV7180 is not set +# CONFIG_VIDEO_ADV7183 is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA7191 is not set +# CONFIG_VIDEO_TVP514X is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_TVP7002 is not set +# CONFIG_VIDEO_VPX3220 is not set + +# +# Video and audio decoders +# +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_CX25840 is not set + +# +# MPEG video encoders +# +# CONFIG_VIDEO_CX2341X is not set + +# +# Video encoders +# +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_ADV7343 is not set +# CONFIG_VIDEO_AK881X is not set + +# +# Camera sensor devices +# +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_VS6624 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_SR030PC30 is not set + +# +# Flash devices +# + +# +# Video improvement chips +# +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set + +# +# Miscelaneous helper chips +# +# CONFIG_VIDEO_THS7303 is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_V4L_USB_DRIVERS is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_SOC_CAMERA=y +# CONFIG_SOC_CAMERA_IMX074 is not set +# CONFIG_SOC_CAMERA_MT9M001 is not set +# CONFIG_SOC_CAMERA_MT9M111 is not set +# CONFIG_SOC_CAMERA_MT9T031 is not set +# CONFIG_SOC_CAMERA_MT9T112 is not set +# CONFIG_SOC_CAMERA_MT9V022 is not set +# CONFIG_SOC_CAMERA_RJ54N1 is not set +# CONFIG_SOC_CAMERA_TW9910 is not set +# CONFIG_SOC_CAMERA_PLATFORM is not set +# CONFIG_SOC_CAMERA_OV2640 is not set +# CONFIG_SOC_CAMERA_OV5642 is not set +# CONFIG_SOC_CAMERA_OV6650 is not set +# CONFIG_SOC_CAMERA_OV772X is not set +# CONFIG_SOC_CAMERA_OV9640 is not set +# CONFIG_SOC_CAMERA_OV9740 is not set +CONFIG_LINUX_AKSENSOR=y +CONFIG_AK_SENSOR_I2C=y +CONFIG_AK_SENSOR_COMMON=y +CONFIG_SENSOR_OV9712=m +CONFIG_SENSOR_H42=m +CONFIG_SENSOR_AR0130=m +CONFIG_SENSOR_SC1045=m +CONFIG_SENSOR_SC1035=m +CONFIG_SENSOR_SC1135=m +CONFIG_SENSOR_GC1024=m +# CONFIG_SENSOR_GM7150 is not set +CONFIG_SENSOR_SC1145=m +CONFIG_SENSOR_H61=m +# CONFIG_SENSOR_H65 is not set +# CONFIG_SENSOR_H62 is not set +# CONFIG_SENSOR_OV9732 is not set +# CONFIG_SENSOR_SC1037 is not set +# CONFIG_SENSOR_GC1034 is not set +# CONFIG_SENSOR_GC2033 is not set +# CONFIG_SENSOR_GC2023 is not set +# CONFIG_SENSOR_SC2235 is not set +CONFIG_SENSOR_SC2232=m +# CONFIG_SENSOR_SC2145 is not set +# CONFIG_SENSOR_F22 is not set +# CONFIG_SENSOR_F23 is not set +# CONFIG_SENSOR_F28 is not set +# CONFIG_SENSOR_HM2140 is not set +# CONFIG_SENSOR_IMX323 is not set +# CONFIG_VIDEO_SH_MOBILE_CSI2 is not set +# CONFIG_VIDEO_SH_MOBILE_CEU is not set +CONFIG_VIDEO_AK=m +CONFIG_AK_VIDEOBUF_DMA_CONTIG=y +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +# CONFIG_RADIO_ADAPTERS is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_ION=y +CONFIG_ION_AK=y +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_SOUND is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +CONFIG_USB_ANYKA_HCD=y +CONFIG_USB_AKOTG_HS_HCD=m +CONFIG_USB_AKOTG_DMA=y +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_RENESAS_USBHS is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_MV_UDC is not set +CONFIG_USB_AKUDC_PRODUCER=m +# CONFIG_USB_GADGET_AKUDC is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +CONFIG_USB_MASS_STORAGE=m +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_ACM_MS is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_G_WEBCAM is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_OTG_WAKELOCK is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set +# CONFIG_SDIO_WIFI is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_PXAV2 is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +CONFIG_MMC_ANYKA=y +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_GPIO is not set +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA9633 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_BD2802 is not set +CONFIG_LEDS_AK39=y +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_RENESAS_TPU is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_OT200 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_SWITCH is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_AK=y +CONFIG_AK39_RTC_SELECT_CLOCK=y +CONFIG_AK39_RTC_INTERNAL_RC_OSC=y +# CONFIG_AK39_RTC_EXTERNAL_XTAL is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +CONFIG_UIO=y +# CONFIG_UIO_PDRV is not set +# CONFIG_UIO_PDRV_GENIRQ is not set +CONFIG_UIO_VCODEC=y + +# +# Virtio drivers +# +# CONFIG_VIRTIO_BALLOON is not set + +# +# Microsoft Hyper-V guest support +# +# CONFIG_STAGING is not set +CONFIG_CLKDEV_LOOKUP=y + +# +# Hardware Spinlock drivers +# +# CONFIG_IOMMU_SUPPORT is not set + +# +# Remoteproc drivers (EXPERIMENTAL) +# + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_AEC_DUMP_DEBUG is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT/EXFAT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +# CONFIG_FAT_FCBMP is not set +# CONFIG_FAT_FSCK is not set +# CONFIG_FAT_CHK_DISK is not set +CONFIG_FAT_NOT_CHKDSK=y +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set +CONFIG_EXFAT_FS=y +CONFIG_EXFAT_DISCARD=y +# CONFIG_EXFAT_DELAYED_SYNC is not set +# CONFIG_EXFAT_KERNEL_DEBUG is not set +# CONFIG_EXFAT_DEBUG_MSG is not set +CONFIG_EXFAT_DEFAULT_CODEPAGE=437 +CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8" + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_HFSPLUS_FS is not set +# CONFIG_YAFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_WRITEBUFFER is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +# CONFIG_JFFS2_ZLIB is not set +# CONFIG_JFFS2_LZO is not set +# CONFIG_JFFS2_RTIME is not set +# CONFIG_JFFS2_RUBIN is not set +CONFIG_JFFS2_CMODE_NONE=y +# CONFIG_JFFS2_CMODE_PRIORITY is not set +# CONFIG_JFFS2_CMODE_SIZE is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +# CONFIG_CRAMFS is not set +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_XATTR is not set +CONFIG_SQUASHFS_ZLIB=y +# CONFIG_SQUASHFS_LZO is not set +CONFIG_SQUASHFS_XZ=y +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_DEBUG is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR_NMI is not set +# CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_STACKTRACE is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_RODATA is not set +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_MANAGER2 is not set +# CONFIG_CRYPTO_USER is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +# CONFIG_CRYPTO_DEV is not set +# CONFIG_CRYPTO_HW is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +# CONFIG_CRC_CCITT is not set +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_CRC8 is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_XZ_DEC=y +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_IA64 is not set +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +# CONFIG_XZ_DEC_SPARC is not set +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_AVERAGE=y +# CONFIG_CORDIC is not set diff --git a/arch/arm/configs/cloud39ev3_ak3919ev300_sq38_sc2232_v1.0.1_defconfig b/arch/arm/configs/cloud39ev3_ak3919ev300_sq38_sc2232_v1.0.1_defconfig new file mode 100644 index 00000000..8b550f08 --- /dev/null +++ b/arch/arm/configs/cloud39ev3_ak3919ev300_sq38_sc2232_v1.0.1_defconfig @@ -0,0 +1,1898 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.4.35 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_KTIME_SCALAR=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +# CONFIG_EXPERIMENTAL is not set +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_FHANDLE is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_SHOW=y + +# +# RCU Subsystem +# +CONFIG_TINY_RCU=y +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=16 +# CONFIG_CGROUPS is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=0 +CONFIG_EXPERT=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +# CONFIG_SHMEM is not set +CONFIG_AIO=y +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_SLUB_DEBUG is not set +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_JUMP_LABEL=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y + +# +# GCOV-based kernel profiling +# +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +CONFIG_ARCH_AK39=y +CONFIG_CPU_AK3919=y +# CONFIG_ARCH_CLOUD39EV3_AK3916E128PIN_COREBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3916EV300_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3918EV300_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3918EV300_YTJ_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3919EV300_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_CO38_SC2235 is not set +# CONFIG_ARCH_CLOUD39EV3_AK3919EV300_SQ38_SC2232_V1_0_0 is not set +CONFIG_ARCH_CLOUD39EV3_AK3919EV300_SQ38_SC2232_V1_0_1=y +# CONFIG_ARCH_CLOUD39EV3_SQ38_QFN64_F22_V100 is not set +CONFIG_ASIC_CLK_120MHZ=y + +# +# Power management +# +# CONFIG_AK39_PM is not set +CONFIG_AK39_PWM_TIMER=y +CONFIG_PLAT_ANYKA=y + +# +# Processor Type +# +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +CONFIG_CPU_USE_DOMAINS=y + +# +# Processor Features +# +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +# CONFIG_ARM_THUMB is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set +# CONFIG_CACHE_L2X0 is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_NR_BANKS=8 +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_PCCARD=y +CONFIG_PCMCIA=y + +# +# PC-card bridges +# + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_ARCH_NR_GPIO=0 +CONFIG_PREEMPT_NONE=y +CONFIG_PREEMPT_VOLUNTARY=y +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +# CONFIG_HIGHMEM is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_NEED_PER_CPU_KM=y +# CONFIG_CLEANCACHE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_SECCOMP is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y + +# +# Boot options +# +# CONFIG_USE_OF is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_CMDLINE="root=/dev/mtdblock1 ro rootfstype=squashfs init=/sbin/init mem=64M console=ttySAK0,115200" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_AUTO_ZRELADDR is not set +CONFIG_RAM_BASE=0x80000000 +CONFIG_VIDEO_RESERVED_MEM_SIZE=0x2200000 + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_WAKELOCK=y +CONFIG_PM_SLEEP=y +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_APM_EMULATION is not set +CONFIG_PM_CLK=y +CONFIG_CPU_PM=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET_LRO=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +CONFIG_INET_UDP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_IPV6 is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK_ACCT is not set +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +# CONFIG_NETFILTER_NETLINK_LOG is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NETFILTER_XTABLES is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV4 is not set +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_PHONET is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +CONFIG_BQL=y +CONFIG_HAVE_BPF_JIT=y +# CONFIG_BPF_JIT is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=y +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=y +# CONFIG_LIB80211_DEBUG is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y +CONFIG_MAC80211=y +CONFIG_MAC80211_HAS_RC=y +# CONFIG_MAC80211_RC_PID is not set +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +# CONFIG_MAC80211_LEDS is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_SYNC is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOCG3 is not set +CONFIG_MTD_AK_SPIFLASH=y +# CONFIG_MTD_AK_SPINAND is not set +# CONFIG_MTD_NAND_IDS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=2 +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=2 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_ANYKA is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ATMEL_PWM is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_BMP085 is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_WL127X_RFKILL is not set +CONFIG_AK_SERIAL_NUMBER=y +CONFIG_AK_GETAIN=y +CONFIG_AK_MOTOR=y +# CONFIG_AK_DAC_CLOCK is not set + +# +# user space generic gpio controller +# +CONFIG_GPIOS_AKCUSTOM=y + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set +CONFIG_AK39_PCM=y +CONFIG_H2_ISP_CHAR=y +CONFIG_USER_GPIO=y +CONFIG_AK_INFO_DUMP=m +CONFIG_AK_PWM_CHAR=y +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_MII=y +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_ARCNET is not set + +# +# CAIF transport drivers +# +CONFIG_ETHERNET=y +CONFIG_NET_VENDOR_3COM=y +# CONFIG_PCMCIA_3C574 is not set +# CONFIG_PCMCIA_3C589 is not set +CONFIG_NET_VENDOR_AMD=y +# CONFIG_PCMCIA_NMCLAN is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +# CONFIG_NET_VENDOR_FARADAY is not set +CONFIG_NET_VENDOR_FUJITSU=y +# CONFIG_PCMCIA_FMVJ18X is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_ETHOC is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +CONFIG_NET_VENDOR_XIRCOM=y +# CONFIG_PCMCIA_XIRC2PS is not set +CONFIG_AK_ETHERNET=y +# CONFIG_ETHERNET_MAC_MII is not set +CONFIG_ETHERNET_MAC_RMII=y +# CONFIG_PHYLIB is not set +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_TR is not set + +# +# USB Network Adapters +# +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_IPHETH is not set +CONFIG_WLAN=y +CONFIG_PCMCIA_RAYCS=y +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_ATMEL is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_AIRO_CS is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_RTL8187 is not set +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_WIFI_CONTROL_FUNC is not set +# CONFIG_ATH_COMMON is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_BCMDHD is not set +# CONFIG_BRCMFMAC is not set +# CONFIG_HOSTAP is not set +# CONFIG_LIBERTAS is not set +# CONFIG_HERMES is not set +# CONFIG_RT2X00 is not set +# CONFIG_MWIFIEX is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_KEYRESET is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_KEYBOARD_AKKEY=y +# CONFIG_KEYBOARD_AK_KEYPAD is not set +# CONFIG_KEYBOARD_ADKEY is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_UNIX98_PTYS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +# CONFIG_DEVKMEM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +CONFIG_SERIAL_AK39_UART=y +CONFIG_SERIAL_AK39_CONSOLE=y +# CONFIG_SERIAL_GPIO_UART is not set +CONFIG_TTY_PRINTK=y +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_CARDMAN_4000 is not set +# CONFIG_CARDMAN_4040 is not set +# CONFIG_IPWIRELESS is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_SMBUS=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_SIMTEC is not set +CONFIG_I2C_ANYKA=y +CONFIG_I2C_AK39_HW=y +# CONFIG_I2C_GPIO_SOFT is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +CONFIG_SPI_BITBANG=y +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_ANYKA=y +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_HSI is not set + +# +# PPS support +# + +# +# PPS generators support +# + +# +# PTP clock support +# + +# +# Enable Device Drivers -> PPS to see the PTP clock options. +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_DW_WATCHDOG is not set +# CONFIG_MAX63XX_WATCHDOG is not set +# CONFIG_AK39_WATCHDOG is not set +CONFIG_AK39_WATCHDOG_TOP=y +# CONFIG_AK39_WATCHDOG_NONE is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_S5M_CORE is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_REGULATOR is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +# CONFIG_DVB_CORE is not set +CONFIG_VIDEO_MEDIA=y + +# +# Multimedia drivers +# +# CONFIG_RC_CORE is not set +# CONFIG_MEDIA_ATTACH is not set +CONFIG_MEDIA_TUNER=y +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_MEDIA_TUNER_SIMPLE=y +CONFIG_MEDIA_TUNER_TDA8290=y +CONFIG_MEDIA_TUNER_TDA827X=y +CONFIG_MEDIA_TUNER_TDA18271=y +CONFIG_MEDIA_TUNER_TDA9887=y +CONFIG_MEDIA_TUNER_TEA5767=y +CONFIG_MEDIA_TUNER_MT20XX=y +CONFIG_MEDIA_TUNER_XC2028=y +CONFIG_MEDIA_TUNER_XC5000=y +CONFIG_MEDIA_TUNER_XC4000=y +CONFIG_MEDIA_TUNER_MC44S803=y +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEOBUF_GEN=y +CONFIG_VIDEOBUF_DMA_CONTIG=y +CONFIG_VIDEOBUF2_CORE=y +CONFIG_VIDEO_CAPTURE_DRIVERS=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set + +# +# Encoders, decoders, sensors and other helper chips +# + +# +# Audio decoders, processors and mixers +# +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_VP27SMPX is not set + +# +# RDS decoders +# +# CONFIG_VIDEO_SAA6588 is not set + +# +# Video decoders +# +# CONFIG_VIDEO_ADV7180 is not set +# CONFIG_VIDEO_ADV7183 is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA7191 is not set +# CONFIG_VIDEO_TVP514X is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_TVP7002 is not set +# CONFIG_VIDEO_VPX3220 is not set + +# +# Video and audio decoders +# +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_CX25840 is not set + +# +# MPEG video encoders +# +# CONFIG_VIDEO_CX2341X is not set + +# +# Video encoders +# +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_ADV7343 is not set +# CONFIG_VIDEO_AK881X is not set + +# +# Camera sensor devices +# +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_VS6624 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_SR030PC30 is not set + +# +# Flash devices +# + +# +# Video improvement chips +# +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set + +# +# Miscelaneous helper chips +# +# CONFIG_VIDEO_THS7303 is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_V4L_USB_DRIVERS is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_SOC_CAMERA=y +# CONFIG_SOC_CAMERA_IMX074 is not set +# CONFIG_SOC_CAMERA_MT9M001 is not set +# CONFIG_SOC_CAMERA_MT9M111 is not set +# CONFIG_SOC_CAMERA_MT9T031 is not set +# CONFIG_SOC_CAMERA_MT9T112 is not set +# CONFIG_SOC_CAMERA_MT9V022 is not set +# CONFIG_SOC_CAMERA_RJ54N1 is not set +# CONFIG_SOC_CAMERA_TW9910 is not set +# CONFIG_SOC_CAMERA_PLATFORM is not set +# CONFIG_SOC_CAMERA_OV2640 is not set +# CONFIG_SOC_CAMERA_OV5642 is not set +# CONFIG_SOC_CAMERA_OV6650 is not set +# CONFIG_SOC_CAMERA_OV772X is not set +# CONFIG_SOC_CAMERA_OV9640 is not set +# CONFIG_SOC_CAMERA_OV9740 is not set +CONFIG_LINUX_AKSENSOR=y +CONFIG_AK_SENSOR_I2C=y +CONFIG_AK_SENSOR_COMMON=y +CONFIG_SENSOR_OV9712=m +CONFIG_SENSOR_H42=m +CONFIG_SENSOR_AR0130=m +CONFIG_SENSOR_SC1045=m +CONFIG_SENSOR_SC1035=m +CONFIG_SENSOR_SC1135=m +CONFIG_SENSOR_GC1024=m +# CONFIG_SENSOR_GM7150 is not set +CONFIG_SENSOR_SC1145=m +CONFIG_SENSOR_H61=m +# CONFIG_SENSOR_H65 is not set +# CONFIG_SENSOR_H62 is not set +# CONFIG_SENSOR_OV9732 is not set +# CONFIG_SENSOR_SC1037 is not set +# CONFIG_SENSOR_GC1034 is not set +# CONFIG_SENSOR_GC2033 is not set +# CONFIG_SENSOR_GC2023 is not set +# CONFIG_SENSOR_SC2235 is not set +CONFIG_SENSOR_SC2232=m +# CONFIG_SENSOR_SC2145 is not set +# CONFIG_SENSOR_F22 is not set +# CONFIG_SENSOR_F23 is not set +# CONFIG_SENSOR_F28 is not set +# CONFIG_SENSOR_HM2140 is not set +# CONFIG_SENSOR_IMX323 is not set +# CONFIG_VIDEO_SH_MOBILE_CSI2 is not set +# CONFIG_VIDEO_SH_MOBILE_CEU is not set +CONFIG_VIDEO_AK=m +CONFIG_AK_VIDEOBUF_DMA_CONTIG=y +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +# CONFIG_RADIO_ADAPTERS is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_ION=y +CONFIG_ION_AK=y +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_SOUND is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +CONFIG_USB_ANYKA_HCD=y +CONFIG_USB_AKOTG_HS_HCD=m +CONFIG_USB_AKOTG_DMA=y +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_RENESAS_USBHS is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_MV_UDC is not set +CONFIG_USB_AKUDC_PRODUCER=m +# CONFIG_USB_GADGET_AKUDC is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +CONFIG_USB_MASS_STORAGE=m +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_ACM_MS is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_G_WEBCAM is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_OTG_WAKELOCK is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set +# CONFIG_SDIO_WIFI is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_PXAV2 is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +CONFIG_MMC_ANYKA=y +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_GPIO is not set +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA9633 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_BD2802 is not set +CONFIG_LEDS_AK39=y +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_RENESAS_TPU is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_OT200 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_SWITCH is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_AK=y +CONFIG_AK39_RTC_SELECT_CLOCK=y +CONFIG_AK39_RTC_INTERNAL_RC_OSC=y +# CONFIG_AK39_RTC_EXTERNAL_XTAL is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +CONFIG_UIO=y +# CONFIG_UIO_PDRV is not set +# CONFIG_UIO_PDRV_GENIRQ is not set +CONFIG_UIO_VCODEC=y + +# +# Virtio drivers +# +# CONFIG_VIRTIO_BALLOON is not set + +# +# Microsoft Hyper-V guest support +# +# CONFIG_STAGING is not set +CONFIG_CLKDEV_LOOKUP=y + +# +# Hardware Spinlock drivers +# +# CONFIG_IOMMU_SUPPORT is not set + +# +# Remoteproc drivers (EXPERIMENTAL) +# + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_AEC_DUMP_DEBUG is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT/EXFAT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +# CONFIG_FAT_FCBMP is not set +# CONFIG_FAT_FSCK is not set +# CONFIG_FAT_CHK_DISK is not set +CONFIG_FAT_NOT_CHKDSK=y +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set +CONFIG_EXFAT_FS=y +CONFIG_EXFAT_DISCARD=y +# CONFIG_EXFAT_DELAYED_SYNC is not set +# CONFIG_EXFAT_KERNEL_DEBUG is not set +# CONFIG_EXFAT_DEBUG_MSG is not set +CONFIG_EXFAT_DEFAULT_CODEPAGE=437 +CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8" + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_HFSPLUS_FS is not set +# CONFIG_YAFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_WRITEBUFFER is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +# CONFIG_JFFS2_ZLIB is not set +# CONFIG_JFFS2_LZO is not set +# CONFIG_JFFS2_RTIME is not set +# CONFIG_JFFS2_RUBIN is not set +CONFIG_JFFS2_CMODE_NONE=y +# CONFIG_JFFS2_CMODE_PRIORITY is not set +# CONFIG_JFFS2_CMODE_SIZE is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +# CONFIG_CRAMFS is not set +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_XATTR is not set +CONFIG_SQUASHFS_ZLIB=y +# CONFIG_SQUASHFS_LZO is not set +CONFIG_SQUASHFS_XZ=y +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_DEBUG is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR_NMI is not set +# CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_STACKTRACE is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_RODATA is not set +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_MANAGER2 is not set +# CONFIG_CRYPTO_USER is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +# CONFIG_CRYPTO_DEV is not set +# CONFIG_CRYPTO_HW is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +# CONFIG_CRC_CCITT is not set +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_CRC8 is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_XZ_DEC=y +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_IA64 is not set +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +# CONFIG_XZ_DEC_SPARC is not set +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_AVERAGE=y +# CONFIG_CORDIC is not set diff --git a/arch/arm/configs/cloud39ev3_co38_sc2235_v1.0.0_defconfig b/arch/arm/configs/cloud39ev3_co38_sc2235_v1.0.0_defconfig new file mode 100644 index 00000000..9aaa110e --- /dev/null +++ b/arch/arm/configs/cloud39ev3_co38_sc2235_v1.0.0_defconfig @@ -0,0 +1,1883 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.4.35 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_KTIME_SCALAR=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +# CONFIG_EXPERIMENTAL is not set +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_FHANDLE is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_SHOW=y + +# +# RCU Subsystem +# +CONFIG_TINY_RCU=y +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=16 +# CONFIG_CGROUPS is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=0 +CONFIG_EXPERT=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +# CONFIG_SHMEM is not set +CONFIG_AIO=y +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_SLUB_DEBUG is not set +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_JUMP_LABEL=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y + +# +# GCOV-based kernel profiling +# +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +CONFIG_ARCH_AK39=y +CONFIG_CPU_AK3918=y +# CONFIG_ARCH_SKY39EV2_AK3918E80PIN_COREBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3916E128PIN_COREBD is not set +# CONFIG_ARCH_CLOUD39EV2_AK3916E_MAINBD is not set +# CONFIG_ARCH_CLOUD39EV3_AK3918E80PIN_COREBD is not set +CONFIG_ARCH_CLOUD39EV3_CO38_SC2235=y +CONFIG_ASIC_CLK_120MHZ=y + +# +# Power management +# +# CONFIG_AK39_PM is not set +CONFIG_AK39_PWM_TIMER=y +CONFIG_PLAT_ANYKA=y + +# +# Processor Type +# +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +CONFIG_CPU_USE_DOMAINS=y + +# +# Processor Features +# +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +# CONFIG_ARM_THUMB is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set +# CONFIG_CACHE_L2X0 is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_NR_BANKS=8 +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_PCCARD=y +CONFIG_PCMCIA=y + +# +# PC-card bridges +# + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_ARCH_NR_GPIO=0 +CONFIG_PREEMPT_NONE=y +CONFIG_PREEMPT_VOLUNTARY=y +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +# CONFIG_HIGHMEM is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_NEED_PER_CPU_KM=y +# CONFIG_CLEANCACHE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_SECCOMP is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y + +# +# Boot options +# +# CONFIG_USE_OF is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_CMDLINE="root=/dev/mtdblock1 ro rootfstype=squashfs init=/sbin/init mem=64M console=ttySAK0,115200" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_AUTO_ZRELADDR is not set +CONFIG_RAM_BASE=0x80000000 +CONFIG_VIDEO_RESERVED_MEM_SIZE=0x2200000 + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_WAKELOCK=y +CONFIG_PM_SLEEP=y +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_APM_EMULATION is not set +CONFIG_PM_CLK=y +CONFIG_CPU_PM=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET_LRO=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +CONFIG_INET_UDP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_IPV6 is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK_ACCT is not set +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +# CONFIG_NETFILTER_NETLINK_LOG is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NETFILTER_XTABLES is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV4 is not set +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_PHONET is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +CONFIG_BQL=y +CONFIG_HAVE_BPF_JIT=y +# CONFIG_BPF_JIT is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=y +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=y +# CONFIG_LIB80211_DEBUG is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y +CONFIG_MAC80211=y +CONFIG_MAC80211_HAS_RC=y +# CONFIG_MAC80211_RC_PID is not set +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +# CONFIG_MAC80211_LEDS is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_SYNC is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOCG3 is not set +CONFIG_MTD_AK_SPIFLASH=y +# CONFIG_MTD_AK_SPINAND is not set +# CONFIG_MTD_NAND_IDS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=2 +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=2 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ATMEL_PWM is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_BMP085 is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_WL127X_RFKILL is not set +CONFIG_AK_SERIAL_NUMBER=y +CONFIG_AK_GETAIN=y +CONFIG_AK_MOTOR=y +# CONFIG_AK_DAC_CLOCK is not set + +# +# user space generic gpio controller +# +CONFIG_GPIOS_AKCUSTOM=y + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set +CONFIG_AK39_PCM=y +CONFIG_H2_ISP_CHAR=y +CONFIG_USER_GPIO=y +CONFIG_AK_INFO_DUMP=m +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_MII=y +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_ARCNET is not set + +# +# CAIF transport drivers +# +CONFIG_ETHERNET=y +CONFIG_NET_VENDOR_3COM=y +# CONFIG_PCMCIA_3C574 is not set +# CONFIG_PCMCIA_3C589 is not set +CONFIG_NET_VENDOR_AMD=y +# CONFIG_PCMCIA_NMCLAN is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +# CONFIG_NET_VENDOR_FARADAY is not set +CONFIG_NET_VENDOR_FUJITSU=y +# CONFIG_PCMCIA_FMVJ18X is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_ETHOC is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +CONFIG_NET_VENDOR_XIRCOM=y +# CONFIG_PCMCIA_XIRC2PS is not set +CONFIG_AK_ETHERNET=y +# CONFIG_ETHERNET_MAC_MII is not set +CONFIG_ETHERNET_MAC_RMII=y +# CONFIG_PHYLIB is not set +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_TR is not set + +# +# USB Network Adapters +# +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_IPHETH is not set +CONFIG_WLAN=y +CONFIG_PCMCIA_RAYCS=y +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_ATMEL is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_AIRO_CS is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_RTL8187 is not set +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_WIFI_CONTROL_FUNC is not set +# CONFIG_ATH_COMMON is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_BCMDHD is not set +# CONFIG_BRCMFMAC is not set +# CONFIG_HOSTAP is not set +# CONFIG_LIBERTAS is not set +# CONFIG_HERMES is not set +# CONFIG_RT2X00 is not set +# CONFIG_MWIFIEX is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_KEYRESET is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_KEYBOARD_AKKEY=y +# CONFIG_KEYBOARD_AK_KEYPAD is not set +# CONFIG_KEYBOARD_ADKEY is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_UNIX98_PTYS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +# CONFIG_DEVKMEM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +CONFIG_SERIAL_AK39_UART=y +CONFIG_SERIAL_AK39_CONSOLE=y +# CONFIG_SERIAL_GPIO_UART is not set +CONFIG_TTY_PRINTK=y +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_CARDMAN_4000 is not set +# CONFIG_CARDMAN_4040 is not set +# CONFIG_IPWIRELESS is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_SMBUS=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_SIMTEC is not set +CONFIG_I2C_ANYKA=y +CONFIG_I2C_AK39_HW=y +# CONFIG_I2C_GPIO_SOFT is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +CONFIG_SPI_BITBANG=y +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_ANYKA=y +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_HSI is not set + +# +# PPS support +# + +# +# PPS generators support +# + +# +# PTP clock support +# + +# +# Enable Device Drivers -> PPS to see the PTP clock options. +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_DW_WATCHDOG is not set +# CONFIG_MAX63XX_WATCHDOG is not set +# CONFIG_AK39_WATCHDOG is not set +CONFIG_AK39_WATCHDOG_TOP=y +# CONFIG_AK39_WATCHDOG_NONE is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_S5M_CORE is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_REGULATOR is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +# CONFIG_DVB_CORE is not set +CONFIG_VIDEO_MEDIA=y + +# +# Multimedia drivers +# +# CONFIG_RC_CORE is not set +# CONFIG_MEDIA_ATTACH is not set +CONFIG_MEDIA_TUNER=y +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_MEDIA_TUNER_SIMPLE=y +CONFIG_MEDIA_TUNER_TDA8290=y +CONFIG_MEDIA_TUNER_TDA827X=y +CONFIG_MEDIA_TUNER_TDA18271=y +CONFIG_MEDIA_TUNER_TDA9887=y +CONFIG_MEDIA_TUNER_TEA5767=y +CONFIG_MEDIA_TUNER_MT20XX=y +CONFIG_MEDIA_TUNER_XC2028=y +CONFIG_MEDIA_TUNER_XC5000=y +CONFIG_MEDIA_TUNER_XC4000=y +CONFIG_MEDIA_TUNER_MC44S803=y +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEOBUF_GEN=y +CONFIG_VIDEOBUF_DMA_CONTIG=y +CONFIG_VIDEOBUF2_CORE=y +CONFIG_VIDEO_CAPTURE_DRIVERS=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set + +# +# Encoders, decoders, sensors and other helper chips +# + +# +# Audio decoders, processors and mixers +# +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_VP27SMPX is not set + +# +# RDS decoders +# +# CONFIG_VIDEO_SAA6588 is not set + +# +# Video decoders +# +# CONFIG_VIDEO_ADV7180 is not set +# CONFIG_VIDEO_ADV7183 is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA7191 is not set +# CONFIG_VIDEO_TVP514X is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_TVP7002 is not set +# CONFIG_VIDEO_VPX3220 is not set + +# +# Video and audio decoders +# +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_CX25840 is not set + +# +# MPEG video encoders +# +# CONFIG_VIDEO_CX2341X is not set + +# +# Video encoders +# +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_ADV7343 is not set +# CONFIG_VIDEO_AK881X is not set + +# +# Camera sensor devices +# +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_VS6624 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_SR030PC30 is not set + +# +# Flash devices +# + +# +# Video improvement chips +# +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set + +# +# Miscelaneous helper chips +# +# CONFIG_VIDEO_THS7303 is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_V4L_USB_DRIVERS is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_SOC_CAMERA=y +# CONFIG_SOC_CAMERA_IMX074 is not set +# CONFIG_SOC_CAMERA_MT9M001 is not set +# CONFIG_SOC_CAMERA_MT9M111 is not set +# CONFIG_SOC_CAMERA_MT9T031 is not set +# CONFIG_SOC_CAMERA_MT9T112 is not set +# CONFIG_SOC_CAMERA_MT9V022 is not set +# CONFIG_SOC_CAMERA_RJ54N1 is not set +# CONFIG_SOC_CAMERA_TW9910 is not set +# CONFIG_SOC_CAMERA_PLATFORM is not set +# CONFIG_SOC_CAMERA_OV2640 is not set +# CONFIG_SOC_CAMERA_OV5642 is not set +# CONFIG_SOC_CAMERA_OV6650 is not set +# CONFIG_SOC_CAMERA_OV772X is not set +# CONFIG_SOC_CAMERA_OV9640 is not set +# CONFIG_SOC_CAMERA_OV9740 is not set +CONFIG_LINUX_AKSENSOR=y +CONFIG_AK_SENSOR_I2C=y +CONFIG_AK_SENSOR_COMMON=y +CONFIG_SENSOR_OV9712=m +CONFIG_SENSOR_H42=m +CONFIG_SENSOR_AR0130=m +CONFIG_SENSOR_SC1045=m +CONFIG_SENSOR_SC1035=m +CONFIG_SENSOR_SC1135=m +CONFIG_SENSOR_GC1024=m +# CONFIG_SENSOR_GM7150 is not set +CONFIG_SENSOR_SC1145=m +CONFIG_SENSOR_H61=m +# CONFIG_SENSOR_H65 is not set +# CONFIG_SENSOR_H62 is not set +# CONFIG_SENSOR_OV9732 is not set +# CONFIG_SENSOR_SC1037 is not set +# CONFIG_SENSOR_GC1034 is not set +# CONFIG_SENSOR_SC2235 is not set +# CONFIG_VIDEO_SH_MOBILE_CSI2 is not set +# CONFIG_VIDEO_SH_MOBILE_CEU is not set +CONFIG_VIDEO_AK=m +CONFIG_AK_VIDEOBUF_DMA_CONTIG=y +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +# CONFIG_RADIO_ADAPTERS is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_ION=y +CONFIG_ION_AK=y +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_SOUND is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +CONFIG_USB_ANYKA_HCD=y +CONFIG_USB_AKOTG_HS_HCD=m +CONFIG_USB_AKOTG_DMA=y +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_RENESAS_USBHS is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_MV_UDC is not set +CONFIG_USB_AKUDC_PRODUCER=m +# CONFIG_USB_GADGET_AKUDC is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +CONFIG_USB_MASS_STORAGE=m +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_ACM_MS is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_G_WEBCAM is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_OTG_WAKELOCK is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set +# CONFIG_SDIO_WIFI is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_PXAV2 is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +CONFIG_MMC_ANYKA=y +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_GPIO is not set +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA9633 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_BD2802 is not set +CONFIG_LEDS_AK39=y +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_RENESAS_TPU is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_OT200 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_SWITCH is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_AK=y +CONFIG_AK39_RTC_SELECT_CLOCK=y +CONFIG_AK39_RTC_INTERNAL_RC_OSC=y +# CONFIG_AK39_RTC_EXTERNAL_XTAL is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +CONFIG_UIO=y +# CONFIG_UIO_PDRV is not set +# CONFIG_UIO_PDRV_GENIRQ is not set +CONFIG_UIO_VCODEC=y + +# +# Virtio drivers +# +# CONFIG_VIRTIO_BALLOON is not set + +# +# Microsoft Hyper-V guest support +# +# CONFIG_STAGING is not set +CONFIG_CLKDEV_LOOKUP=y + +# +# Hardware Spinlock drivers +# +# CONFIG_IOMMU_SUPPORT is not set + +# +# Remoteproc drivers (EXPERIMENTAL) +# + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_AEC_DUMP_DEBUG is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT/EXFAT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +# CONFIG_FAT_FCBMP is not set +# CONFIG_FAT_FSCK is not set +# CONFIG_FAT_CHK_DISK is not set +CONFIG_FAT_NOT_CHKDSK=y +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set +CONFIG_EXFAT_FS=y +CONFIG_EXFAT_DISCARD=y +# CONFIG_EXFAT_DELAYED_SYNC is not set +# CONFIG_EXFAT_KERNEL_DEBUG is not set +# CONFIG_EXFAT_DEBUG_MSG is not set +CONFIG_EXFAT_DEFAULT_CODEPAGE=437 +CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8" + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_HFSPLUS_FS is not set +# CONFIG_YAFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_WRITEBUFFER is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +# CONFIG_JFFS2_ZLIB is not set +# CONFIG_JFFS2_LZO is not set +# CONFIG_JFFS2_RTIME is not set +# CONFIG_JFFS2_RUBIN is not set +CONFIG_JFFS2_CMODE_NONE=y +# CONFIG_JFFS2_CMODE_PRIORITY is not set +# CONFIG_JFFS2_CMODE_SIZE is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +# CONFIG_CRAMFS is not set +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_XATTR is not set +CONFIG_SQUASHFS_ZLIB=y +# CONFIG_SQUASHFS_LZO is not set +CONFIG_SQUASHFS_XZ=y +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_DEBUG is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR_NMI is not set +# CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_STACKTRACE is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_RODATA is not set +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_MANAGER2 is not set +# CONFIG_CRYPTO_USER is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +# CONFIG_CRYPTO_DEV is not set +# CONFIG_CRYPTO_HW is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +# CONFIG_CRC_CCITT is not set +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_CRC8 is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_XZ_DEC=y +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_IA64 is not set +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +# CONFIG_XZ_DEC_SPARC is not set +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_AVERAGE=y +# CONFIG_CORDIC is not set diff --git a/arch/arm/configs/cloud39ev3_sq38x4_corebd_v1.0.0_defconfig b/arch/arm/configs/cloud39ev3_sq38x4_corebd_v1.0.0_defconfig new file mode 100644 index 00000000..dadf562a --- /dev/null +++ b/arch/arm/configs/cloud39ev3_sq38x4_corebd_v1.0.0_defconfig @@ -0,0 +1,1855 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.4.35 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_KTIME_SCALAR=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +# CONFIG_EXPERIMENTAL is not set +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_FHANDLE is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_SHOW=y + +# +# RCU Subsystem +# +CONFIG_TINY_RCU=y +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=16 +# CONFIG_CGROUPS is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=0 +CONFIG_EXPERT=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +# CONFIG_SHMEM is not set +CONFIG_AIO=y +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_SLUB_DEBUG is not set +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_JUMP_LABEL=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y + +# +# GCOV-based kernel profiling +# +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +CONFIG_ARCH_AK39=y +CONFIG_CPU_AK3918=y +# CONFIG_ARCH_CLOUD39E_AK3916E128PIN_MNBD is not set +CONFIG_ARCH_CLOUD39EV3_SQ38X4_COREBD=y +CONFIG_ASIC_CLK_120MHZ=y + +# +# Power management +# +# CONFIG_AK39_PM is not set +CONFIG_AK39_PWM_TIMER=y +CONFIG_PLAT_ANYKA=y + +# +# Processor Type +# +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +CONFIG_CPU_USE_DOMAINS=y + +# +# Processor Features +# +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +# CONFIG_ARM_THUMB is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set +# CONFIG_CACHE_L2X0 is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_NR_BANKS=8 +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_PCCARD=y +CONFIG_PCMCIA=y + +# +# PC-card bridges +# + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_ARCH_NR_GPIO=0 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +# CONFIG_HIGHMEM is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_NEED_PER_CPU_KM=y +# CONFIG_CLEANCACHE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_SECCOMP is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y + +# +# Boot options +# +# CONFIG_USE_OF is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_CMDLINE="root=/dev/mtdblock1 ro rootfstype=squashfs init=/sbin/init mem=64M console=ttySAK0,115200" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_AUTO_ZRELADDR is not set +CONFIG_RAM_BASE=0x80000000 +CONFIG_VIDEO_RESERVED_MEM_SIZE=0x2200000 + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_WAKELOCK=y +CONFIG_PM_SLEEP=y +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_APM_EMULATION is not set +CONFIG_PM_CLK=y +CONFIG_CPU_PM=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET_LRO=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +CONFIG_INET_UDP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_IPV6 is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK_ACCT is not set +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +# CONFIG_NETFILTER_NETLINK_LOG is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NETFILTER_XTABLES is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV4 is not set +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_PHONET is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +CONFIG_BQL=y +CONFIG_HAVE_BPF_JIT=y +# CONFIG_BPF_JIT is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=y +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=y +# CONFIG_LIB80211_DEBUG is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y +CONFIG_MAC80211=y +CONFIG_MAC80211_HAS_RC=y +# CONFIG_MAC80211_RC_PID is not set +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +# CONFIG_MAC80211_LEDS is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_SYNC is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOCG3 is not set +CONFIG_MTD_AK_SPIFLASH=y +# CONFIG_MTD_AK_SPINAND is not set +# CONFIG_MTD_NAND_IDS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=2 +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=2 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ATMEL_PWM is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_BMP085 is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_WL127X_RFKILL is not set +CONFIG_AK_SERIAL_NUMBER=y +CONFIG_AK_GETAIN=y +CONFIG_AK_MOTOR=y +# CONFIG_AK_DAC_CLOCK is not set + +# +# user space generic gpio controller +# +CONFIG_GPIOS_AKCUSTOM=y + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set +CONFIG_AK39_PCM=y +CONFIG_H2_ISP_CHAR=y +CONFIG_USER_GPIO=y +CONFIG_AK_INFO_DUMP=m + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_MII=y +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set + +# +# CAIF transport drivers +# +CONFIG_ETHERNET=y +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +# CONFIG_NET_VENDOR_FARADAY is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_ETHOC is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +CONFIG_AK_ETHERNET=y +# CONFIG_ETHERNET_MAC_MII is not set +CONFIG_ETHERNET_MAC_RMII=y +# CONFIG_PHYLIB is not set +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# USB Network Adapters +# +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_IPHETH is not set +CONFIG_WLAN=y +CONFIG_PCMCIA_RAYCS=y +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_RTL8187 is not set +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_WIFI_CONTROL_FUNC is not set +# CONFIG_ATH_COMMON is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_BCMDHD is not set +# CONFIG_BRCMFMAC is not set +# CONFIG_HOSTAP is not set +# CONFIG_LIBERTAS is not set +# CONFIG_RT2X00 is not set +# CONFIG_MWIFIEX is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_KEYRESET is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_KEYBOARD_AKKEY=y +# CONFIG_KEYBOARD_AK_KEYPAD is not set +# CONFIG_KEYBOARD_ADKEY is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_UNIX98_PTYS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +# CONFIG_DEVKMEM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +CONFIG_SERIAL_AK39_UART=y +CONFIG_SERIAL_AK39_CONSOLE=y +# CONFIG_SERIAL_GPIO_UART is not set +CONFIG_TTY_PRINTK=y +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_SMBUS=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_SIMTEC is not set +CONFIG_I2C_ANYKA=y +CONFIG_I2C_AK39_HW=y +# CONFIG_I2C_GPIO_SOFT is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +CONFIG_SPI_BITBANG=y +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_ANYKA=y +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_HSI is not set + +# +# PPS support +# + +# +# PPS generators support +# + +# +# PTP clock support +# + +# +# Enable Device Drivers -> PPS to see the PTP clock options. +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_DW_WATCHDOG is not set +# CONFIG_MAX63XX_WATCHDOG is not set +# CONFIG_AK39_WATCHDOG is not set +CONFIG_AK39_WATCHDOG_TOP=y +# CONFIG_AK39_WATCHDOG_NONE is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_S5M_CORE is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_REGULATOR is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +# CONFIG_DVB_CORE is not set +CONFIG_VIDEO_MEDIA=y + +# +# Multimedia drivers +# +# CONFIG_RC_CORE is not set +# CONFIG_MEDIA_ATTACH is not set +CONFIG_MEDIA_TUNER=y +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_MEDIA_TUNER_SIMPLE=y +CONFIG_MEDIA_TUNER_TDA8290=y +CONFIG_MEDIA_TUNER_TDA827X=y +CONFIG_MEDIA_TUNER_TDA18271=y +CONFIG_MEDIA_TUNER_TDA9887=y +CONFIG_MEDIA_TUNER_TEA5767=y +CONFIG_MEDIA_TUNER_MT20XX=y +CONFIG_MEDIA_TUNER_XC2028=y +CONFIG_MEDIA_TUNER_XC5000=y +CONFIG_MEDIA_TUNER_XC4000=y +CONFIG_MEDIA_TUNER_MC44S803=y +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEOBUF_GEN=y +CONFIG_VIDEOBUF_DMA_CONTIG=y +CONFIG_VIDEOBUF2_CORE=y +CONFIG_VIDEO_CAPTURE_DRIVERS=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set + +# +# Encoders, decoders, sensors and other helper chips +# + +# +# Audio decoders, processors and mixers +# +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_VP27SMPX is not set + +# +# RDS decoders +# +# CONFIG_VIDEO_SAA6588 is not set + +# +# Video decoders +# +# CONFIG_VIDEO_ADV7180 is not set +# CONFIG_VIDEO_ADV7183 is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA7191 is not set +# CONFIG_VIDEO_TVP514X is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_TVP7002 is not set +# CONFIG_VIDEO_VPX3220 is not set + +# +# Video and audio decoders +# +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_CX25840 is not set + +# +# MPEG video encoders +# +# CONFIG_VIDEO_CX2341X is not set + +# +# Video encoders +# +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_ADV7343 is not set +# CONFIG_VIDEO_AK881X is not set + +# +# Camera sensor devices +# +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_VS6624 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_SR030PC30 is not set + +# +# Flash devices +# + +# +# Video improvement chips +# +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set + +# +# Miscelaneous helper chips +# +# CONFIG_VIDEO_THS7303 is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_V4L_USB_DRIVERS is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_SOC_CAMERA=y +# CONFIG_SOC_CAMERA_IMX074 is not set +# CONFIG_SOC_CAMERA_MT9M001 is not set +# CONFIG_SOC_CAMERA_MT9M111 is not set +# CONFIG_SOC_CAMERA_MT9T031 is not set +# CONFIG_SOC_CAMERA_MT9T112 is not set +# CONFIG_SOC_CAMERA_MT9V022 is not set +# CONFIG_SOC_CAMERA_RJ54N1 is not set +# CONFIG_SOC_CAMERA_TW9910 is not set +# CONFIG_SOC_CAMERA_PLATFORM is not set +# CONFIG_SOC_CAMERA_OV2640 is not set +# CONFIG_SOC_CAMERA_OV5642 is not set +# CONFIG_SOC_CAMERA_OV6650 is not set +# CONFIG_SOC_CAMERA_OV772X is not set +# CONFIG_SOC_CAMERA_OV9640 is not set +# CONFIG_SOC_CAMERA_OV9740 is not set +CONFIG_LINUX_AKSENSOR=y +CONFIG_AK_SENSOR_I2C=y +CONFIG_AK_SENSOR_COMMON=y +# CONFIG_SENSOR_OV9712 is not set +# CONFIG_SENSOR_H42 is not set +# CONFIG_SENSOR_AR0130 is not set +# CONFIG_SENSOR_SC1045 is not set +# CONFIG_SENSOR_SC1035 is not set +# CONFIG_SENSOR_SC1135 is not set +# CONFIG_SENSOR_GC1024 is not set +# CONFIG_SENSOR_GM7150 is not set +# CONFIG_SENSOR_SC1145 is not set +# CONFIG_SENSOR_H61 is not set +# CONFIG_SENSOR_H65 is not set +# CONFIG_SENSOR_H62 is not set +# CONFIG_SENSOR_OV9732 is not set +# CONFIG_SENSOR_SC1037 is not set +# CONFIG_SENSOR_GC1034 is not set +# CONFIG_SENSOR_GC2033 is not set +# CONFIG_SENSOR_GC2023 is not set +# CONFIG_SENSOR_SC2235 is not set +# CONFIG_SENSOR_SC2232 is not set +# CONFIG_SENSOR_SC2145 is not set +# CONFIG_SENSOR_F22 is not set +# CONFIG_SENSOR_F22_F23_F28_F35_F37_DVP is not set +CONFIG_SENSOR_SC2232H_SC2135E=m +CONFIG_SENSOR_F22_F23_F28_F35_F37_MIPI=m +CONFIG_SENSOR_F22_F23_F28_F35_F37=m +CONFIG_SENSOR_GC2053=m +# CONFIG_SENSOR_F28 is not set +# CONFIG_SENSOR_HM2140 is not set +# CONFIG_SENSOR_IMX323 is not set +# CONFIG_VIDEO_SH_MOBILE_CSI2 is not set +# CONFIG_VIDEO_SH_MOBILE_CEU is not set +CONFIG_VIDEO_AK=m +CONFIG_AK_VIDEOBUF_DMA_CONTIG=y +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +# CONFIG_RADIO_ADAPTERS is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_ION=y +CONFIG_ION_AK=y +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_SOUND is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +CONFIG_USB_ANYKA_HCD=y +CONFIG_USB_AKOTG_HS_HCD=m +CONFIG_USB_AKOTG_DMA=y +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_RENESAS_USBHS is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_MV_UDC is not set +CONFIG_USB_AKUDC_PRODUCER=m +# CONFIG_USB_GADGET_AKUDC is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +CONFIG_USB_MASS_STORAGE=m +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_ACM_MS is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_G_WEBCAM is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_OTG_WAKELOCK is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set +# CONFIG_SDIO_WIFI is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_PXAV2 is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +CONFIG_MMC_ANYKA=y +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_GPIO is not set +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA9633 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_BD2802 is not set +CONFIG_LEDS_AK39=y +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_RENESAS_TPU is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_OT200 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_SWITCH is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_AK=y +CONFIG_AK39_RTC_SELECT_CLOCK=y +CONFIG_AK39_RTC_INTERNAL_RC_OSC=y +# CONFIG_AK39_RTC_EXTERNAL_XTAL is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +CONFIG_UIO=y +# CONFIG_UIO_PDRV is not set +# CONFIG_UIO_PDRV_GENIRQ is not set +CONFIG_UIO_VCODEC=y + +# +# Virtio drivers +# +# CONFIG_VIRTIO_BALLOON is not set + +# +# Microsoft Hyper-V guest support +# +# CONFIG_STAGING is not set +CONFIG_CLKDEV_LOOKUP=y + +# +# Hardware Spinlock drivers +# +# CONFIG_IOMMU_SUPPORT is not set + +# +# Remoteproc drivers (EXPERIMENTAL) +# + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_AEC_DUMP_DEBUG is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT/EXFAT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +# CONFIG_FAT_FCBMP is not set +# CONFIG_FAT_FSCK is not set +# CONFIG_FAT_CHK_DISK is not set +CONFIG_FAT_NOT_CHKDSK=y +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set +CONFIG_EXFAT_FS=y +CONFIG_EXFAT_DISCARD=y +# CONFIG_EXFAT_DELAYED_SYNC is not set +# CONFIG_EXFAT_KERNEL_DEBUG is not set +# CONFIG_EXFAT_DEBUG_MSG is not set +CONFIG_EXFAT_DEFAULT_CODEPAGE=437 +CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8" + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_HFSPLUS_FS is not set +# CONFIG_YAFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_WRITEBUFFER is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +# CONFIG_JFFS2_ZLIB is not set +# CONFIG_JFFS2_LZO is not set +# CONFIG_JFFS2_RTIME is not set +# CONFIG_JFFS2_RUBIN is not set +CONFIG_JFFS2_CMODE_NONE=y +# CONFIG_JFFS2_CMODE_PRIORITY is not set +# CONFIG_JFFS2_CMODE_SIZE is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +# CONFIG_CRAMFS is not set +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_XATTR is not set +CONFIG_SQUASHFS_ZLIB=y +# CONFIG_SQUASHFS_LZO is not set +CONFIG_SQUASHFS_XZ=y +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +# CONFIG_NETWORK_FILESYSTEMS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR_NMI is not set +# CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_STACKTRACE is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_RODATA is not set +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_MANAGER2 is not set +# CONFIG_CRYPTO_USER is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +# CONFIG_CRYPTO_DEV is not set +# CONFIG_CRYPTO_HW is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +# CONFIG_CRC_CCITT is not set +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_CRC8 is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_XZ_DEC=y +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_IA64 is not set +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +# CONFIG_XZ_DEC_SPARC is not set +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_AVERAGE=y +# CONFIG_CORDIC is not set diff --git a/arch/arm/configs/config_readme b/arch/arm/configs/config_readme new file mode 100644 index 00000000..5f2d5ad9 --- /dev/null +++ b/arch/arm/configs/config_readme @@ -0,0 +1,5 @@ +cloud39ev3_ak3916ev300_corebd_v1.0.0_defconfig 对应的是 3916ev300 开发板的板级配置文件 +cloud39ev3_ak3918ev300_corebd_v1.0.0_defconfig 对应的是 3918ev300 开发板的板级配置文件 +cloud39ev3_ak3919ev300_corebd_v1.0.0_defconfig 对应的是 3919ev300 开发板的板级配置文件 +cloud39ev3_co38_sc2235_v1.0.0_defconfig 对应的是 3918ev300 38模块的板级配置文件 +cloud39ev3_sq38_qfn64_f22_v100_defconfig 对应的是 3919ev300 38模块的板级配置文件 diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h index 42dec04f..d0366c1e 100644 --- a/arch/arm/include/asm/cacheflush.h +++ b/arch/arm/include/asm/cacheflush.h @@ -16,6 +16,7 @@ #include #include #include +#include #define CACHE_COLOUR(vaddr) ((vaddr & (SHMLBA - 1)) >> PAGE_SHIFT) diff --git a/arch/arm/include/asm/fiq_debugger.h b/arch/arm/include/asm/fiq_debugger.h new file mode 100644 index 00000000..4d274883 --- /dev/null +++ b/arch/arm/include/asm/fiq_debugger.h @@ -0,0 +1,64 @@ +/* + * arch/arm/include/asm/fiq_debugger.h + * + * Copyright (C) 2010 Google, Inc. + * Author: Colin Cross + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _ARCH_ARM_MACH_TEGRA_FIQ_DEBUGGER_H_ +#define _ARCH_ARM_MACH_TEGRA_FIQ_DEBUGGER_H_ + +#include + +#define FIQ_DEBUGGER_NO_CHAR NO_POLL_CHAR +#define FIQ_DEBUGGER_BREAK 0x00ff0100 + +#define FIQ_DEBUGGER_FIQ_IRQ_NAME "fiq" +#define FIQ_DEBUGGER_SIGNAL_IRQ_NAME "signal" +#define FIQ_DEBUGGER_WAKEUP_IRQ_NAME "wakeup" + +/** + * struct fiq_debugger_pdata - fiq debugger platform data + * @uart_resume: used to restore uart state right before enabling + * the fiq. + * @uart_enable: Do the work necessary to communicate with the uart + * hw (enable clocks, etc.). This must be ref-counted. + * @uart_disable: Do the work necessary to disable the uart hw + * (disable clocks, etc.). This must be ref-counted. + * @uart_dev_suspend: called during PM suspend, generally not needed + * for real fiq mode debugger. + * @uart_dev_resume: called during PM resume, generally not needed + * for real fiq mode debugger. + */ +struct fiq_debugger_pdata { + int (*uart_init)(struct platform_device *pdev); + void (*uart_free)(struct platform_device *pdev); + int (*uart_resume)(struct platform_device *pdev); + int (*uart_getc)(struct platform_device *pdev); + void (*uart_putc)(struct platform_device *pdev, unsigned int c); + void (*uart_flush)(struct platform_device *pdev); + void (*uart_enable)(struct platform_device *pdev); + void (*uart_disable)(struct platform_device *pdev); + + int (*uart_dev_suspend)(struct platform_device *pdev); + int (*uart_dev_resume)(struct platform_device *pdev); + + void (*fiq_enable)(struct platform_device *pdev, unsigned int fiq, + bool enable); + void (*fiq_ack)(struct platform_device *pdev, unsigned int fiq); + + void (*force_irq)(struct platform_device *pdev, unsigned int irq); + void (*force_irq_ack)(struct platform_device *pdev, unsigned int irq); +}; + +#endif diff --git a/arch/arm/include/asm/fiq_glue.h b/arch/arm/include/asm/fiq_glue.h new file mode 100644 index 00000000..d54c29db --- /dev/null +++ b/arch/arm/include/asm/fiq_glue.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2010 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ASM_FIQ_GLUE_H +#define __ASM_FIQ_GLUE_H + +struct fiq_glue_handler { + void (*fiq)(struct fiq_glue_handler *h, void *regs, void *svc_sp); + void (*resume)(struct fiq_glue_handler *h); +}; + +int fiq_glue_register_handler(struct fiq_glue_handler *handler); + +#ifdef CONFIG_FIQ_GLUE +void fiq_glue_resume(void); +#else +static inline void fiq_glue_resume(void) {} +#endif + +#endif diff --git a/arch/arm/include/asm/hardirq.h b/arch/arm/include/asm/hardirq.h index 436e60b2..2740c2a2 100644 --- a/arch/arm/include/asm/hardirq.h +++ b/arch/arm/include/asm/hardirq.h @@ -5,7 +5,7 @@ #include #include -#define NR_IPI 5 +#define NR_IPI 6 typedef struct { unsigned int __softirq_pending; diff --git a/arch/arm/include/asm/hardware/cache-l2x0.h b/arch/arm/include/asm/hardware/cache-l2x0.h index c4c87bc1..bd2c6a53 100644 --- a/arch/arm/include/asm/hardware/cache-l2x0.h +++ b/arch/arm/include/asm/hardware/cache-l2x0.h @@ -66,6 +66,7 @@ #define L2X0_STNDBY_MODE_EN (1 << 0) /* Registers shifts and masks */ +#define L2X0_CACHE_ID_REV_MASK (0x3f) #define L2X0_CACHE_ID_PART_MASK (0xf << 6) #define L2X0_CACHE_ID_PART_L210 (1 << 6) #define L2X0_CACHE_ID_PART_L310 (3 << 6) @@ -102,6 +103,8 @@ #define L2X0_ADDR_FILTER_EN 1 +#define REV_PL310_R2P0 4 + #ifndef __ASSEMBLY__ extern void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask); #if defined(CONFIG_CACHE_L2X0) && defined(CONFIG_OF) diff --git a/arch/arm/include/asm/hardware/coresight.h b/arch/arm/include/asm/hardware/coresight.h index 7ecd793b..dcf74d71 100644 --- a/arch/arm/include/asm/hardware/coresight.h +++ b/arch/arm/include/asm/hardware/coresight.h @@ -17,15 +17,23 @@ #define TRACER_ACCESSED_BIT 0 #define TRACER_RUNNING_BIT 1 #define TRACER_CYCLE_ACC_BIT 2 +#define TRACER_TRACE_DATA_BIT 3 +#define TRACER_TIMESTAMP_BIT 4 +#define TRACER_BRANCHOUTPUT_BIT 5 +#define TRACER_RETURN_STACK_BIT 6 #define TRACER_ACCESSED BIT(TRACER_ACCESSED_BIT) #define TRACER_RUNNING BIT(TRACER_RUNNING_BIT) #define TRACER_CYCLE_ACC BIT(TRACER_CYCLE_ACC_BIT) +#define TRACER_TRACE_DATA BIT(TRACER_TRACE_DATA_BIT) +#define TRACER_TIMESTAMP BIT(TRACER_TIMESTAMP_BIT) +#define TRACER_BRANCHOUTPUT BIT(TRACER_BRANCHOUTPUT_BIT) +#define TRACER_RETURN_STACK BIT(TRACER_RETURN_STACK_BIT) #define TRACER_TIMEOUT 10000 -#define etm_writel(t, v, x) \ - (__raw_writel((v), (t)->etm_regs + (x))) -#define etm_readl(t, x) (__raw_readl((t)->etm_regs + (x))) +#define etm_writel(t, id, v, x) \ + (__raw_writel((v), (t)->etm_regs[(id)] + (x))) +#define etm_readl(t, id, x) (__raw_readl((t)->etm_regs[(id)] + (x))) /* CoreSight Management Registers */ #define CSMR_LOCKACCESS 0xfb0 @@ -43,7 +51,7 @@ #define ETMCTRL_POWERDOWN 1 #define ETMCTRL_PROGRAM (1 << 10) #define ETMCTRL_PORTSEL (1 << 11) -#define ETMCTRL_DO_CONTEXTID (3 << 14) +#define ETMCTRL_CONTEXTIDSIZE(x) (((x) & 3) << 14) #define ETMCTRL_PORTMASK1 (7 << 4) #define ETMCTRL_PORTMASK2 (1 << 21) #define ETMCTRL_PORTMASK (ETMCTRL_PORTMASK1 | ETMCTRL_PORTMASK2) @@ -55,9 +63,12 @@ #define ETMCTRL_DATA_DO_BOTH (ETMCTRL_DATA_DO_DATA | ETMCTRL_DATA_DO_ADDR) #define ETMCTRL_BRANCH_OUTPUT (1 << 8) #define ETMCTRL_CYCLEACCURATE (1 << 12) +#define ETMCTRL_TIMESTAMP_EN (1 << 28) +#define ETMCTRL_RETURN_STACK_EN (1 << 29) /* ETM configuration code register */ #define ETMR_CONFCODE (0x04) +#define ETMCCR_ETMIDR_PRESENT BIT(31) /* ETM trace start/stop resource control register */ #define ETMR_TRACESSCTRL (0x18) @@ -113,10 +124,25 @@ #define ETMR_TRACEENCTRL 0x24 #define ETMTE_INCLEXCL BIT(24) #define ETMR_TRACEENEVT 0x20 -#define ETMCTRL_OPTS (ETMCTRL_DO_CPRT | \ - ETMCTRL_DATA_DO_ADDR | \ - ETMCTRL_BRANCH_OUTPUT | \ - ETMCTRL_DO_CONTEXTID) + +#define ETMR_VIEWDATAEVT 0x30 +#define ETMR_VIEWDATACTRL1 0x34 +#define ETMR_VIEWDATACTRL2 0x38 +#define ETMR_VIEWDATACTRL3 0x3c +#define ETMVDC3_EXCLONLY BIT(16) + +#define ETMCTRL_OPTS (ETMCTRL_DO_CPRT) + +#define ETMR_ID 0x1e4 +#define ETMIDR_VERSION(x) (((x) >> 4) & 0xff) +#define ETMIDR_VERSION_3_1 0x21 +#define ETMIDR_VERSION_PFT_1_0 0x30 + +#define ETMR_CCE 0x1e8 +#define ETMCCER_RETURN_STACK_IMPLEMENTED BIT(23) +#define ETMCCER_TIMESTAMPING_IMPLEMENTED BIT(22) + +#define ETMR_TRACEIDR 0x200 /* ETM management registers, "ETM Architecture", 3.5.24 */ #define ETMMR_OSLAR 0x300 @@ -140,14 +166,16 @@ #define ETBFF_TRIGIN BIT(8) #define ETBFF_TRIGEVT BIT(9) #define ETBFF_TRIGFL BIT(10) +#define ETBFF_STOPFL BIT(12) #define etb_writel(t, v, x) \ (__raw_writel((v), (t)->etb_regs + (x))) #define etb_readl(t, x) (__raw_readl((t)->etb_regs + (x))) -#define etm_lock(t) do { etm_writel((t), 0, CSMR_LOCKACCESS); } while (0) -#define etm_unlock(t) \ - do { etm_writel((t), UNLOCK_MAGIC, CSMR_LOCKACCESS); } while (0) +#define etm_lock(t, id) \ + do { etm_writel((t), (id), 0, CSMR_LOCKACCESS); } while (0) +#define etm_unlock(t, id) \ + do { etm_writel((t), (id), UNLOCK_MAGIC, CSMR_LOCKACCESS); } while (0) #define etb_lock(t) do { etb_writel((t), 0, CSMR_LOCKACCESS); } while (0) #define etb_unlock(t) \ diff --git a/arch/arm/include/asm/irq.h b/arch/arm/include/asm/irq.h index 35c21c37..3e0857a6 100644 --- a/arch/arm/include/asm/irq.h +++ b/arch/arm/include/asm/irq.h @@ -30,6 +30,9 @@ extern void asm_do_IRQ(unsigned int, struct pt_regs *); void handle_IRQ(unsigned int, struct pt_regs *); void init_IRQ(void); +void arch_trigger_all_cpu_backtrace(void); +#define arch_trigger_all_cpu_backtrace arch_trigger_all_cpu_backtrace + #endif #endif diff --git a/arch/arm/include/asm/mach/mmc.h b/arch/arm/include/asm/mach/mmc.h new file mode 100644 index 00000000..bca864ac --- /dev/null +++ b/arch/arm/include/asm/mach/mmc.h @@ -0,0 +1,28 @@ +/* + * arch/arm/include/asm/mach/mmc.h + */ +#ifndef ASMARM_MACH_MMC_H +#define ASMARM_MACH_MMC_H + +#include +#include +#include + +struct embedded_sdio_data { + struct sdio_cis cis; + struct sdio_cccr cccr; + struct sdio_embedded_func *funcs; + int num_funcs; +}; + +struct mmc_platform_data { + unsigned int ocr_mask; /* available voltages */ + int built_in; /* built-in device flag */ + int card_present; /* card detect state */ + u32 (*translate_vdd)(struct device *, unsigned int); + unsigned int (*status)(struct device *); + struct embedded_sdio_data *embedded_sdio; + int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id); +}; + +#endif diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h index fcb57574..7b07f81e 100644 --- a/arch/arm/include/asm/memory.h +++ b/arch/arm/include/asm/memory.h @@ -192,7 +192,8 @@ static inline unsigned long __phys_to_virt(unsigned long x) #ifdef PLAT_PHYS_OFFSET #define PHYS_OFFSET PLAT_PHYS_OFFSET #else -#define PHYS_OFFSET UL(CONFIG_PHYS_OFFSET) +//#define PHYS_OFFSET UL(CONFIG_PHYS_OFFSET) +#define PHYS_OFFSET UL(CONFIG_RAM_BASE + CONFIG_VIDEO_RESERVED_MEM_SIZE) #endif #endif diff --git a/arch/arm/include/asm/mmu.h b/arch/arm/include/asm/mmu.h index b8e580a2..14965658 100644 --- a/arch/arm/include/asm/mmu.h +++ b/arch/arm/include/asm/mmu.h @@ -34,11 +34,4 @@ typedef struct { #endif -/* - * switch_mm() may do a full cache flush over the context switch, - * so enable interrupts over the context switch to avoid high - * latency. - */ -#define __ARCH_WANT_INTERRUPTS_ON_CTXSW - #endif diff --git a/arch/arm/include/asm/mmu_context.h b/arch/arm/include/asm/mmu_context.h index a0b3cac0..0306bc64 100644 --- a/arch/arm/include/asm/mmu_context.h +++ b/arch/arm/include/asm/mmu_context.h @@ -43,45 +43,104 @@ void __check_kvm_seq(struct mm_struct *mm); #define ASID_FIRST_VERSION (1 << ASID_BITS) extern unsigned int cpu_last_asid; -#ifdef CONFIG_SMP -DECLARE_PER_CPU(struct mm_struct *, current_mm); -#endif void __init_new_context(struct task_struct *tsk, struct mm_struct *mm); void __new_context(struct mm_struct *mm); +void cpu_set_reserved_ttbr0(void); -static inline void check_context(struct mm_struct *mm) +static inline void switch_new_context(struct mm_struct *mm) { - /* - * This code is executed with interrupts enabled. Therefore, - * mm->context.id cannot be updated to the latest ASID version - * on a different CPU (and condition below not triggered) - * without first getting an IPI to reset the context. The - * alternative is to take a read_lock on mm->context.id_lock - * (after changing its type to rwlock_t). - */ - if (unlikely((mm->context.id ^ cpu_last_asid) >> ASID_BITS)) - __new_context(mm); + unsigned long flags; + + __new_context(mm); + + local_irq_save(flags); + cpu_switch_mm(mm->pgd, mm); + local_irq_restore(flags); +} +static inline void check_and_switch_context(struct mm_struct *mm, + struct task_struct *tsk) +{ if (unlikely(mm->context.kvm_seq != init_mm.context.kvm_seq)) __check_kvm_seq(mm); + + /* + * Required during context switch to avoid speculative page table + * walking with the wrong TTBR. + */ + cpu_set_reserved_ttbr0(); + + if (!((mm->context.id ^ cpu_last_asid) >> ASID_BITS)) + /* + * The ASID is from the current generation, just switch to the + * new pgd. This condition is only true for calls from + * context_switch() and interrupts are already disabled. + */ + cpu_switch_mm(mm->pgd, mm); + else if (irqs_disabled()) + /* + * Defer the new ASID allocation until after the context + * switch critical region since __new_context() cannot be + * called with interrupts disabled (it sends IPIs). + */ + set_ti_thread_flag(task_thread_info(tsk), TIF_SWITCH_MM); + else + /* + * That is a direct call to switch_mm() or activate_mm() with + * interrupts enabled and a new context. + */ + switch_new_context(mm); } #define init_new_context(tsk,mm) (__init_new_context(tsk,mm),0) -#else - -static inline void check_context(struct mm_struct *mm) +#define finish_arch_post_lock_switch \ + finish_arch_post_lock_switch +static inline void finish_arch_post_lock_switch(void) { + if (test_and_clear_thread_flag(TIF_SWITCH_MM)) + switch_new_context(current->mm); +} + +#else /* !CONFIG_CPU_HAS_ASID */ + #ifdef CONFIG_MMU + +static inline void check_and_switch_context(struct mm_struct *mm, + struct task_struct *tsk) +{ if (unlikely(mm->context.kvm_seq != init_mm.context.kvm_seq)) __check_kvm_seq(mm); -#endif + + if (irqs_disabled()) + /* + * cpu_switch_mm() needs to flush the VIVT caches. To avoid + * high interrupt latencies, defer the call and continue + * running with the old mm. Since we only support UP systems + * on non-ASID CPUs, the old mm will remain valid until the + * finish_arch_post_lock_switch() call. + */ + set_ti_thread_flag(task_thread_info(tsk), TIF_SWITCH_MM); + else + cpu_switch_mm(mm->pgd, mm); } +#define finish_arch_post_lock_switch \ + finish_arch_post_lock_switch +static inline void finish_arch_post_lock_switch(void) +{ + if (test_and_clear_thread_flag(TIF_SWITCH_MM)) { + struct mm_struct *mm = current->mm; + cpu_switch_mm(mm->pgd, mm); + } +} + +#endif /* CONFIG_MMU */ + #define init_new_context(tsk,mm) 0 -#endif +#endif /* CONFIG_CPU_HAS_ASID */ #define destroy_context(mm) do { } while(0) @@ -119,12 +178,7 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next, __flush_icache_all(); #endif if (!cpumask_test_and_set_cpu(cpu, mm_cpumask(next)) || prev != next) { -#ifdef CONFIG_SMP - struct mm_struct **crt_mm = &per_cpu(current_mm, cpu); - *crt_mm = next; -#endif - check_context(next); - cpu_switch_mm(next->pgd, next); + check_and_switch_context(next, tsk); if (cache_is_vivt()) cpumask_clear_cpu(cpu, mm_cpumask(prev)); } diff --git a/arch/arm/include/asm/rodata.h b/arch/arm/include/asm/rodata.h new file mode 100644 index 00000000..8c8add87 --- /dev/null +++ b/arch/arm/include/asm/rodata.h @@ -0,0 +1,32 @@ +/* + * arch/arm/include/asm/rodata.h + * + * Copyright (C) 2011 Google, Inc. + * + * Author: Colin Cross + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef _ASMARM_RODATA_H +#define _ASMARM_RODATA_H + +#ifndef __ASSEMBLY__ + +#ifdef CONFIG_DEBUG_RODATA + +int set_memory_rw(unsigned long virt, int numpages); +int set_memory_ro(unsigned long virt, int numpages); + +void mark_rodata_ro(void); +void set_kernel_text_rw(void); +void set_kernel_text_ro(void); +#else +static inline void set_kernel_text_rw(void) { } +static inline void set_kernel_text_ro(void) { } +#endif + +#endif + +#endif diff --git a/arch/arm/include/asm/sched_clock.h b/arch/arm/include/asm/sched_clock.h index e3f75726..05b8e82e 100644 --- a/arch/arm/include/asm/sched_clock.h +++ b/arch/arm/include/asm/sched_clock.h @@ -10,5 +10,7 @@ extern void sched_clock_postinit(void); extern void setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate); +extern void setup_sched_clock_needs_suspend(u32 (*read)(void), int bits, + unsigned long rate); #endif diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h index ae292932..7f74b59f 100644 --- a/arch/arm/include/asm/smp.h +++ b/arch/arm/include/asm/smp.h @@ -93,4 +93,6 @@ extern void platform_cpu_enable(unsigned int cpu); extern void arch_send_call_function_single_ipi(int cpu); extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); +extern void smp_send_all_cpu_backtrace(void); + #endif /* ifndef __ASM_ARM_SMP_H */ diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h index 0f04d845..68388eb4 100644 --- a/arch/arm/include/asm/thread_info.h +++ b/arch/arm/include/asm/thread_info.h @@ -153,6 +153,7 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *, #define TIF_MEMDIE 18 /* is terminating due to OOM killer */ #define TIF_RESTORE_SIGMASK 20 #define TIF_SECCOMP 21 +#define TIF_SWITCH_MM 22 /* deferred switch_mm */ #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c index 36d20bd5..c5fb6c9f 100644 --- a/arch/arm/kernel/etm.c +++ b/arch/arm/kernel/etm.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -37,26 +38,37 @@ MODULE_AUTHOR("Alexander Shishkin"); struct tracectx { unsigned int etb_bufsz; void __iomem *etb_regs; - void __iomem *etm_regs; + void __iomem **etm_regs; + int etm_regs_count; unsigned long flags; int ncmppairs; int etm_portsz; + int etm_contextid_size; + u32 etb_fc; + unsigned long range_start; + unsigned long range_end; + unsigned long data_range_start; + unsigned long data_range_end; + bool dump_initial_etb; struct device *dev; struct clk *emu_clk; struct mutex mutex; }; -static struct tracectx tracer; +static struct tracectx tracer = { + .range_start = (unsigned long)_stext, + .range_end = (unsigned long)_etext, +}; static inline bool trace_isrunning(struct tracectx *t) { return !!(t->flags & TRACER_RUNNING); } -static int etm_setup_address_range(struct tracectx *t, int n, +static int etm_setup_address_range(struct tracectx *t, int id, int n, unsigned long start, unsigned long end, int exclude, int data) { - u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_NSONLY | \ + u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_IGNSECURITY | ETMAAT_NOVALCMP; if (n < 1 || n > t->ncmppairs) @@ -72,95 +84,185 @@ static int etm_setup_address_range(struct tracectx *t, int n, flags |= ETMAAT_IEXEC; /* first comparator for the range */ - etm_writel(t, flags, ETMR_COMP_ACC_TYPE(n * 2)); - etm_writel(t, start, ETMR_COMP_VAL(n * 2)); + etm_writel(t, id, flags, ETMR_COMP_ACC_TYPE(n * 2)); + etm_writel(t, id, start, ETMR_COMP_VAL(n * 2)); /* second comparator is right next to it */ - etm_writel(t, flags, ETMR_COMP_ACC_TYPE(n * 2 + 1)); - etm_writel(t, end, ETMR_COMP_VAL(n * 2 + 1)); - - flags = exclude ? ETMTE_INCLEXCL : 0; - etm_writel(t, flags | (1 << n), ETMR_TRACEENCTRL); + etm_writel(t, id, flags, ETMR_COMP_ACC_TYPE(n * 2 + 1)); + etm_writel(t, id, end, ETMR_COMP_VAL(n * 2 + 1)); + + if (data) { + flags = exclude ? ETMVDC3_EXCLONLY : 0; + if (exclude) + n += 8; + etm_writel(t, id, flags | BIT(n), ETMR_VIEWDATACTRL3); + } else { + flags = exclude ? ETMTE_INCLEXCL : 0; + etm_writel(t, id, flags | (1 << n), ETMR_TRACEENCTRL); + } return 0; } -static int trace_start(struct tracectx *t) +static int trace_start_etm(struct tracectx *t, int id) { u32 v; unsigned long timeout = TRACER_TIMEOUT; - etb_unlock(t); - - etb_writel(t, 0, ETBR_FORMATTERCTRL); - etb_writel(t, 1, ETBR_CTRL); - - etb_lock(t); - - /* configure etm */ v = ETMCTRL_OPTS | ETMCTRL_PROGRAM | ETMCTRL_PORTSIZE(t->etm_portsz); + v |= ETMCTRL_CONTEXTIDSIZE(t->etm_contextid_size); if (t->flags & TRACER_CYCLE_ACC) v |= ETMCTRL_CYCLEACCURATE; - etm_unlock(t); + if (t->flags & TRACER_BRANCHOUTPUT) + v |= ETMCTRL_BRANCH_OUTPUT; + + if (t->flags & TRACER_TRACE_DATA) + v |= ETMCTRL_DATA_DO_ADDR; + + if (t->flags & TRACER_TIMESTAMP) + v |= ETMCTRL_TIMESTAMP_EN; + + if (t->flags & TRACER_RETURN_STACK) + v |= ETMCTRL_RETURN_STACK_EN; - etm_writel(t, v, ETMR_CTRL); + etm_unlock(t, id); - while (!(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) + etm_writel(t, id, v, ETMR_CTRL); + + while (!(etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) ; if (!timeout) { dev_dbg(t->dev, "Waiting for progbit to assert timed out\n"); - etm_lock(t); + etm_lock(t, id); return -EFAULT; } - etm_setup_address_range(t, 1, (unsigned long)_stext, - (unsigned long)_etext, 0, 0); - etm_writel(t, 0, ETMR_TRACEENCTRL2); - etm_writel(t, 0, ETMR_TRACESSCTRL); - etm_writel(t, 0x6f, ETMR_TRACEENEVT); + if (t->range_start || t->range_end) + etm_setup_address_range(t, id, 1, + t->range_start, t->range_end, 0, 0); + else + etm_writel(t, id, ETMTE_INCLEXCL, ETMR_TRACEENCTRL); + + etm_writel(t, id, 0, ETMR_TRACEENCTRL2); + etm_writel(t, id, 0, ETMR_TRACESSCTRL); + etm_writel(t, id, 0x6f, ETMR_TRACEENEVT); + + etm_writel(t, id, 0, ETMR_VIEWDATACTRL1); + etm_writel(t, id, 0, ETMR_VIEWDATACTRL2); + + if (t->data_range_start || t->data_range_end) + etm_setup_address_range(t, id, 2, t->data_range_start, + t->data_range_end, 0, 1); + else + etm_writel(t, id, ETMVDC3_EXCLONLY, ETMR_VIEWDATACTRL3); + + etm_writel(t, id, 0x6f, ETMR_VIEWDATAEVT); v &= ~ETMCTRL_PROGRAM; v |= ETMCTRL_PORTSEL; - etm_writel(t, v, ETMR_CTRL); + etm_writel(t, id, v, ETMR_CTRL); timeout = TRACER_TIMEOUT; - while (etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM && --timeout) + while (etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM && --timeout) ; if (!timeout) { dev_dbg(t->dev, "Waiting for progbit to deassert timed out\n"); - etm_lock(t); + etm_lock(t, id); return -EFAULT; } - etm_lock(t); + etm_lock(t, id); + return 0; +} + +static int trace_start(struct tracectx *t) +{ + int ret; + int id; + u32 etb_fc = t->etb_fc; + + etb_unlock(t); + + t->dump_initial_etb = false; + etb_writel(t, 0, ETBR_WRITEADDR); + etb_writel(t, etb_fc, ETBR_FORMATTERCTRL); + etb_writel(t, 1, ETBR_CTRL); + + etb_lock(t); + + /* configure etm(s) */ + for (id = 0; id < t->etm_regs_count; id++) { + ret = trace_start_etm(t, id); + if (ret) + return ret; + } t->flags |= TRACER_RUNNING; return 0; } -static int trace_stop(struct tracectx *t) +static int trace_stop_etm(struct tracectx *t, int id) { unsigned long timeout = TRACER_TIMEOUT; - etm_unlock(t); + etm_unlock(t, id); - etm_writel(t, 0x440, ETMR_CTRL); - while (!(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) + etm_writel(t, id, 0x440, ETMR_CTRL); + while (!(etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) ; if (!timeout) { - dev_dbg(t->dev, "Waiting for progbit to assert timed out\n"); - etm_lock(t); + dev_err(t->dev, + "etm%d: Waiting for progbit to assert timed out\n", + id); + etm_lock(t, id); return -EFAULT; } - etm_lock(t); + etm_lock(t, id); + return 0; +} + +static int trace_power_down_etm(struct tracectx *t, int id) +{ + unsigned long timeout = TRACER_TIMEOUT; + etm_unlock(t, id); + while (!(etm_readl(t, id, ETMR_STATUS) & ETMST_PROGBIT) && --timeout) + ; + if (!timeout) { + dev_err(t->dev, "etm%d: Waiting for status progbit to assert timed out\n", + id); + etm_lock(t, id); + return -EFAULT; + } + + etm_writel(t, id, 0x441, ETMR_CTRL); + + etm_lock(t, id); + return 0; +} + +static int trace_stop(struct tracectx *t) +{ + int id; + unsigned long timeout = TRACER_TIMEOUT; + u32 etb_fc = t->etb_fc; + + for (id = 0; id < t->etm_regs_count; id++) + trace_stop_etm(t, id); + + for (id = 0; id < t->etm_regs_count; id++) + trace_power_down_etm(t, id); etb_unlock(t); - etb_writel(t, ETBFF_MANUAL_FLUSH, ETBR_FORMATTERCTRL); + if (etb_fc) { + etb_fc |= ETBFF_STOPFL; + etb_writel(t, t->etb_fc, ETBR_FORMATTERCTRL); + } + etb_writel(t, etb_fc | ETBFF_MANUAL_FLUSH, ETBR_FORMATTERCTRL); timeout = TRACER_TIMEOUT; while (etb_readl(t, ETBR_FORMATTERCTRL) & @@ -185,24 +287,15 @@ static int trace_stop(struct tracectx *t) static int etb_getdatalen(struct tracectx *t) { u32 v; - int rp, wp; + int wp; v = etb_readl(t, ETBR_STATUS); if (v & 1) return t->etb_bufsz; - rp = etb_readl(t, ETBR_READADDR); wp = etb_readl(t, ETBR_WRITEADDR); - - if (rp > wp) { - etb_writel(t, 0, ETBR_READADDR); - etb_writel(t, 0, ETBR_WRITEADDR); - - return 0; - } - - return wp - rp; + return wp; } /* sysrq+v will always stop the running trace and leave it at that */ @@ -235,21 +328,18 @@ static void etm_dump(void) printk("%08x", cpu_to_be32(etb_readl(t, ETBR_READMEM))); printk(KERN_INFO "\n--- ETB buffer end ---\n"); - /* deassert the overflow bit */ - etb_writel(t, 1, ETBR_CTRL); - etb_writel(t, 0, ETBR_CTRL); - - etb_writel(t, 0, ETBR_TRIGGERCOUNT); - etb_writel(t, 0, ETBR_READADDR); - etb_writel(t, 0, ETBR_WRITEADDR); - etb_lock(t); } static void sysrq_etm_dump(int key) { + if (!mutex_trylock(&tracer.mutex)) { + printk(KERN_INFO "Tracing hardware busy\n"); + return; + } dev_dbg(tracer.dev, "Dumping ETB buffer\n"); etm_dump(); + mutex_unlock(&tracer.mutex); } static struct sysrq_key_op sysrq_etm_op = { @@ -276,6 +366,10 @@ static ssize_t etb_read(struct file *file, char __user *data, struct tracectx *t = file->private_data; u32 first = 0; u32 *buf; + int wpos; + int skip; + long wlength; + loff_t pos = *ppos; mutex_lock(&t->mutex); @@ -287,31 +381,39 @@ static ssize_t etb_read(struct file *file, char __user *data, etb_unlock(t); total = etb_getdatalen(t); + if (total == 0 && t->dump_initial_etb) + total = t->etb_bufsz; if (total == t->etb_bufsz) first = etb_readl(t, ETBR_WRITEADDR); + if (pos > total * 4) { + skip = 0; + wpos = total; + } else { + skip = (int)pos % 4; + wpos = (int)pos / 4; + } + total -= wpos; + first = (first + wpos) % t->etb_bufsz; + etb_writel(t, first, ETBR_READADDR); - length = min(total * 4, (int)len); - buf = vmalloc(length); + wlength = min(total, DIV_ROUND_UP(skip + (int)len, 4)); + length = min(total * 4 - skip, (int)len); + buf = vmalloc(wlength * 4); - dev_dbg(t->dev, "ETB buffer length: %d\n", total); + dev_dbg(t->dev, "ETB read %ld bytes to %lld from %ld words at %d\n", + length, pos, wlength, first); + dev_dbg(t->dev, "ETB buffer length: %d\n", total + wpos); dev_dbg(t->dev, "ETB status reg: %x\n", etb_readl(t, ETBR_STATUS)); - for (i = 0; i < length / 4; i++) + for (i = 0; i < wlength; i++) buf[i] = etb_readl(t, ETBR_READMEM); - /* the only way to deassert overflow bit in ETB status is this */ - etb_writel(t, 1, ETBR_CTRL); - etb_writel(t, 0, ETBR_CTRL); - - etb_writel(t, 0, ETBR_WRITEADDR); - etb_writel(t, 0, ETBR_READADDR); - etb_writel(t, 0, ETBR_TRIGGERCOUNT); - etb_lock(t); - length -= copy_to_user(data, buf, length); + length -= copy_to_user(data, (u8 *)buf + skip, length); vfree(buf); + *ppos = pos + length; out: mutex_unlock(&t->mutex); @@ -348,28 +450,17 @@ static int __devinit etb_probe(struct amba_device *dev, const struct amba_id *id if (ret) goto out; + mutex_lock(&t->mutex); t->etb_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res)); if (!t->etb_regs) { ret = -ENOMEM; goto out_release; } + t->dev = &dev->dev; + t->dump_initial_etb = true; amba_set_drvdata(dev, t); - etb_miscdev.parent = &dev->dev; - - ret = misc_register(&etb_miscdev); - if (ret) - goto out_unmap; - - t->emu_clk = clk_get(&dev->dev, "emu_src_ck"); - if (IS_ERR(t->emu_clk)) { - dev_dbg(&dev->dev, "Failed to obtain emu_src_ck.\n"); - return -EFAULT; - } - - clk_enable(t->emu_clk); - etb_unlock(t); t->etb_bufsz = etb_readl(t, ETBR_DEPTH); dev_dbg(&dev->dev, "Size: %x\n", t->etb_bufsz); @@ -378,6 +469,20 @@ static int __devinit etb_probe(struct amba_device *dev, const struct amba_id *id etb_writel(t, 0, ETBR_CTRL); etb_writel(t, 0x1000, ETBR_FORMATTERCTRL); etb_lock(t); + mutex_unlock(&t->mutex); + + etb_miscdev.parent = &dev->dev; + + ret = misc_register(&etb_miscdev); + if (ret) + goto out_unmap; + + /* Get optional clock. Currently used to select clock source on omap3 */ + t->emu_clk = clk_get(&dev->dev, "emu_src_ck"); + if (IS_ERR(t->emu_clk)) + dev_dbg(&dev->dev, "Failed to obtain emu_src_ck.\n"); + else + clk_enable(t->emu_clk); dev_dbg(&dev->dev, "ETB AMBA driver initialized.\n"); @@ -385,10 +490,13 @@ out: return ret; out_unmap: + mutex_lock(&t->mutex); amba_set_drvdata(dev, NULL); iounmap(t->etb_regs); + t->etb_regs = NULL; out_release: + mutex_unlock(&t->mutex); amba_release_regions(dev); return ret; @@ -403,8 +511,10 @@ static int etb_remove(struct amba_device *dev) iounmap(t->etb_regs); t->etb_regs = NULL; - clk_disable(t->emu_clk); - clk_put(t->emu_clk); + if (!IS_ERR(t->emu_clk)) { + clk_disable(t->emu_clk); + clk_put(t->emu_clk); + } amba_release_regions(dev); @@ -448,7 +558,10 @@ static ssize_t trace_running_store(struct kobject *kobj, return -EINVAL; mutex_lock(&tracer.mutex); - ret = value ? trace_start(&tracer) : trace_stop(&tracer); + if (!tracer.etb_regs) + ret = -ENODEV; + else + ret = value ? trace_start(&tracer) : trace_stop(&tracer); mutex_unlock(&tracer.mutex); return ret ? : n; @@ -463,36 +576,50 @@ static ssize_t trace_info_show(struct kobject *kobj, { u32 etb_wa, etb_ra, etb_st, etb_fc, etm_ctrl, etm_st; int datalen; + int id; + int ret; - etb_unlock(&tracer); - datalen = etb_getdatalen(&tracer); - etb_wa = etb_readl(&tracer, ETBR_WRITEADDR); - etb_ra = etb_readl(&tracer, ETBR_READADDR); - etb_st = etb_readl(&tracer, ETBR_STATUS); - etb_fc = etb_readl(&tracer, ETBR_FORMATTERCTRL); - etb_lock(&tracer); - - etm_unlock(&tracer); - etm_ctrl = etm_readl(&tracer, ETMR_CTRL); - etm_st = etm_readl(&tracer, ETMR_STATUS); - etm_lock(&tracer); + mutex_lock(&tracer.mutex); + if (tracer.etb_regs) { + etb_unlock(&tracer); + datalen = etb_getdatalen(&tracer); + etb_wa = etb_readl(&tracer, ETBR_WRITEADDR); + etb_ra = etb_readl(&tracer, ETBR_READADDR); + etb_st = etb_readl(&tracer, ETBR_STATUS); + etb_fc = etb_readl(&tracer, ETBR_FORMATTERCTRL); + etb_lock(&tracer); + } else { + etb_wa = etb_ra = etb_st = etb_fc = ~0; + datalen = -1; + } - return sprintf(buf, "Trace buffer len: %d\nComparator pairs: %d\n" + ret = sprintf(buf, "Trace buffer len: %d\nComparator pairs: %d\n" "ETBR_WRITEADDR:\t%08x\n" "ETBR_READADDR:\t%08x\n" "ETBR_STATUS:\t%08x\n" - "ETBR_FORMATTERCTRL:\t%08x\n" - "ETMR_CTRL:\t%08x\n" - "ETMR_STATUS:\t%08x\n", + "ETBR_FORMATTERCTRL:\t%08x\n", datalen, tracer.ncmppairs, etb_wa, etb_ra, etb_st, - etb_fc, + etb_fc + ); + + for (id = 0; id < tracer.etm_regs_count; id++) { + etm_unlock(&tracer, id); + etm_ctrl = etm_readl(&tracer, id, ETMR_CTRL); + etm_st = etm_readl(&tracer, id, ETMR_STATUS); + etm_lock(&tracer, id); + ret += sprintf(buf + ret, "ETMR_CTRL:\t%08x\n" + "ETMR_STATUS:\t%08x\n", etm_ctrl, etm_st ); + } + mutex_unlock(&tracer.mutex); + + return ret; } static struct kobj_attribute trace_info_attr = @@ -531,42 +658,260 @@ static ssize_t trace_mode_store(struct kobject *kobj, static struct kobj_attribute trace_mode_attr = __ATTR(trace_mode, 0644, trace_mode_show, trace_mode_store); +static ssize_t trace_contextid_size_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + /* 0: No context id tracing, 1: One byte, 2: Two bytes, 3: Four bytes */ + return sprintf(buf, "%d\n", (1 << tracer.etm_contextid_size) >> 1); +} + +static ssize_t trace_contextid_size_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned int contextid_size; + + if (sscanf(buf, "%u", &contextid_size) != 1) + return -EINVAL; + + if (contextid_size == 3 || contextid_size > 4) + return -EINVAL; + + mutex_lock(&tracer.mutex); + tracer.etm_contextid_size = fls(contextid_size); + mutex_unlock(&tracer.mutex); + + return n; +} + +static struct kobj_attribute trace_contextid_size_attr = + __ATTR(trace_contextid_size, 0644, + trace_contextid_size_show, trace_contextid_size_store); + +static ssize_t trace_branch_output_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", !!(tracer.flags & TRACER_BRANCHOUTPUT)); +} + +static ssize_t trace_branch_output_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned int branch_output; + + if (sscanf(buf, "%u", &branch_output) != 1) + return -EINVAL; + + mutex_lock(&tracer.mutex); + if (branch_output) { + tracer.flags |= TRACER_BRANCHOUTPUT; + /* Branch broadcasting is incompatible with the return stack */ + tracer.flags &= ~TRACER_RETURN_STACK; + } else { + tracer.flags &= ~TRACER_BRANCHOUTPUT; + } + mutex_unlock(&tracer.mutex); + + return n; +} + +static struct kobj_attribute trace_branch_output_attr = + __ATTR(trace_branch_output, 0644, + trace_branch_output_show, trace_branch_output_store); + +static ssize_t trace_return_stack_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", !!(tracer.flags & TRACER_RETURN_STACK)); +} + +static ssize_t trace_return_stack_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned int return_stack; + + if (sscanf(buf, "%u", &return_stack) != 1) + return -EINVAL; + + mutex_lock(&tracer.mutex); + if (return_stack) { + tracer.flags |= TRACER_RETURN_STACK; + /* Return stack is incompatible with branch broadcasting */ + tracer.flags &= ~TRACER_BRANCHOUTPUT; + } else { + tracer.flags &= ~TRACER_RETURN_STACK; + } + mutex_unlock(&tracer.mutex); + + return n; +} + +static struct kobj_attribute trace_return_stack_attr = + __ATTR(trace_return_stack, 0644, + trace_return_stack_show, trace_return_stack_store); + +static ssize_t trace_timestamp_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", !!(tracer.flags & TRACER_TIMESTAMP)); +} + +static ssize_t trace_timestamp_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned int timestamp; + + if (sscanf(buf, "%u", ×tamp) != 1) + return -EINVAL; + + mutex_lock(&tracer.mutex); + if (timestamp) + tracer.flags |= TRACER_TIMESTAMP; + else + tracer.flags &= ~TRACER_TIMESTAMP; + mutex_unlock(&tracer.mutex); + + return n; +} + +static struct kobj_attribute trace_timestamp_attr = + __ATTR(trace_timestamp, 0644, + trace_timestamp_show, trace_timestamp_store); + +static ssize_t trace_range_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%08lx %08lx\n", + tracer.range_start, tracer.range_end); +} + +static ssize_t trace_range_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long range_start, range_end; + + if (sscanf(buf, "%lx %lx", &range_start, &range_end) != 2) + return -EINVAL; + + mutex_lock(&tracer.mutex); + tracer.range_start = range_start; + tracer.range_end = range_end; + mutex_unlock(&tracer.mutex); + + return n; +} + + +static struct kobj_attribute trace_range_attr = + __ATTR(trace_range, 0644, trace_range_show, trace_range_store); + +static ssize_t trace_data_range_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + unsigned long range_start; + u64 range_end; + mutex_lock(&tracer.mutex); + range_start = tracer.data_range_start; + range_end = tracer.data_range_end; + if (!range_end && (tracer.flags & TRACER_TRACE_DATA)) + range_end = 0x100000000ULL; + mutex_unlock(&tracer.mutex); + return sprintf(buf, "%08lx %08llx\n", range_start, range_end); +} + +static ssize_t trace_data_range_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long range_start; + u64 range_end; + + if (sscanf(buf, "%lx %llx", &range_start, &range_end) != 2) + return -EINVAL; + + mutex_lock(&tracer.mutex); + tracer.data_range_start = range_start; + tracer.data_range_end = (unsigned long)range_end; + if (range_end) + tracer.flags |= TRACER_TRACE_DATA; + else + tracer.flags &= ~TRACER_TRACE_DATA; + mutex_unlock(&tracer.mutex); + + return n; +} + + +static struct kobj_attribute trace_data_range_attr = + __ATTR(trace_data_range, 0644, + trace_data_range_show, trace_data_range_store); + static int __devinit etm_probe(struct amba_device *dev, const struct amba_id *id) { struct tracectx *t = &tracer; int ret = 0; + void __iomem **new_regs; + int new_count; + u32 etmccr; + u32 etmidr; + u32 etmccer = 0; + u8 etm_version = 0; + + mutex_lock(&t->mutex); + new_count = t->etm_regs_count + 1; + new_regs = krealloc(t->etm_regs, + sizeof(t->etm_regs[0]) * new_count, GFP_KERNEL); - if (t->etm_regs) { - dev_dbg(&dev->dev, "ETM already initialized\n"); - ret = -EBUSY; + if (!new_regs) { + dev_dbg(&dev->dev, "Failed to allocate ETM register array\n"); + ret = -ENOMEM; goto out; } + t->etm_regs = new_regs; ret = amba_request_regions(dev, NULL); if (ret) goto out; - t->etm_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res)); - if (!t->etm_regs) { + t->etm_regs[t->etm_regs_count] = + ioremap_nocache(dev->res.start, resource_size(&dev->res)); + if (!t->etm_regs[t->etm_regs_count]) { ret = -ENOMEM; goto out_release; } - amba_set_drvdata(dev, t); + amba_set_drvdata(dev, t->etm_regs[t->etm_regs_count]); - mutex_init(&t->mutex); - t->dev = &dev->dev; - t->flags = TRACER_CYCLE_ACC; + t->flags = TRACER_CYCLE_ACC | TRACER_TRACE_DATA | TRACER_BRANCHOUTPUT; t->etm_portsz = 1; + t->etm_contextid_size = 3; - etm_unlock(t); - (void)etm_readl(t, ETMMR_PDSR); + etm_unlock(t, t->etm_regs_count); + (void)etm_readl(t, t->etm_regs_count, ETMMR_PDSR); /* dummy first read */ - (void)etm_readl(&tracer, ETMMR_OSSRR); - - t->ncmppairs = etm_readl(t, ETMR_CONFCODE) & 0xf; - etm_writel(t, 0x440, ETMR_CTRL); - etm_lock(t); + (void)etm_readl(&tracer, t->etm_regs_count, ETMMR_OSSRR); + + etmccr = etm_readl(t, t->etm_regs_count, ETMR_CONFCODE); + t->ncmppairs = etmccr & 0xf; + if (etmccr & ETMCCR_ETMIDR_PRESENT) { + etmidr = etm_readl(t, t->etm_regs_count, ETMR_ID); + etm_version = ETMIDR_VERSION(etmidr); + if (etm_version >= ETMIDR_VERSION_3_1) + etmccer = etm_readl(t, t->etm_regs_count, ETMR_CCE); + } + etm_writel(t, t->etm_regs_count, 0x441, ETMR_CTRL); + etm_writel(t, t->etm_regs_count, new_count, ETMR_TRACEIDR); + etm_lock(t, t->etm_regs_count); ret = sysfs_create_file(&dev->dev.kobj, &trace_running_attr.attr); @@ -582,36 +927,101 @@ static int __devinit etm_probe(struct amba_device *dev, const struct amba_id *id if (ret) dev_dbg(&dev->dev, "Failed to create trace_mode in sysfs\n"); - dev_dbg(t->dev, "ETM AMBA driver initialized.\n"); + ret = sysfs_create_file(&dev->dev.kobj, + &trace_contextid_size_attr.attr); + if (ret) + dev_dbg(&dev->dev, + "Failed to create trace_contextid_size in sysfs\n"); + + ret = sysfs_create_file(&dev->dev.kobj, + &trace_branch_output_attr.attr); + if (ret) + dev_dbg(&dev->dev, + "Failed to create trace_branch_output in sysfs\n"); + + if (etmccer & ETMCCER_RETURN_STACK_IMPLEMENTED) { + ret = sysfs_create_file(&dev->dev.kobj, + &trace_return_stack_attr.attr); + if (ret) + dev_dbg(&dev->dev, + "Failed to create trace_return_stack in sysfs\n"); + } + + if (etmccer & ETMCCER_TIMESTAMPING_IMPLEMENTED) { + ret = sysfs_create_file(&dev->dev.kobj, + &trace_timestamp_attr.attr); + if (ret) + dev_dbg(&dev->dev, + "Failed to create trace_timestamp in sysfs\n"); + } + + ret = sysfs_create_file(&dev->dev.kobj, &trace_range_attr.attr); + if (ret) + dev_dbg(&dev->dev, "Failed to create trace_range in sysfs\n"); + + if (etm_version < ETMIDR_VERSION_PFT_1_0) { + ret = sysfs_create_file(&dev->dev.kobj, + &trace_data_range_attr.attr); + if (ret) + dev_dbg(&dev->dev, + "Failed to create trace_data_range in sysfs\n"); + } else { + tracer.flags &= ~TRACER_TRACE_DATA; + } + + dev_dbg(&dev->dev, "ETM AMBA driver initialized.\n"); + + /* Enable formatter if there are multiple trace sources */ + if (new_count > 1) + t->etb_fc = ETBFF_ENFCONT | ETBFF_ENFTC; + + t->etm_regs_count = new_count; out: + mutex_unlock(&t->mutex); return ret; out_unmap: amba_set_drvdata(dev, NULL); - iounmap(t->etm_regs); + iounmap(t->etm_regs[t->etm_regs_count]); out_release: amba_release_regions(dev); + mutex_unlock(&t->mutex); return ret; } static int etm_remove(struct amba_device *dev) { - struct tracectx *t = amba_get_drvdata(dev); + int i; + struct tracectx *t = &tracer; + void __iomem *etm_regs = amba_get_drvdata(dev); + + sysfs_remove_file(&dev->dev.kobj, &trace_running_attr.attr); + sysfs_remove_file(&dev->dev.kobj, &trace_info_attr.attr); + sysfs_remove_file(&dev->dev.kobj, &trace_mode_attr.attr); + sysfs_remove_file(&dev->dev.kobj, &trace_range_attr.attr); + sysfs_remove_file(&dev->dev.kobj, &trace_data_range_attr.attr); amba_set_drvdata(dev, NULL); - iounmap(t->etm_regs); - t->etm_regs = NULL; + mutex_lock(&t->mutex); + for (i = 0; i < t->etm_regs_count; i++) + if (t->etm_regs[i] == etm_regs) + break; + for (; i < t->etm_regs_count - 1; i++) + t->etm_regs[i] = t->etm_regs[i + 1]; + t->etm_regs_count--; + if (!t->etm_regs_count) { + kfree(t->etm_regs); + t->etm_regs = NULL; + } + mutex_unlock(&t->mutex); + iounmap(etm_regs); amba_release_regions(dev); - sysfs_remove_file(&dev->dev.kobj, &trace_running_attr.attr); - sysfs_remove_file(&dev->dev.kobj, &trace_info_attr.attr); - sysfs_remove_file(&dev->dev.kobj, &trace_mode_attr.attr); - return 0; } @@ -620,6 +1030,10 @@ static struct amba_id etm_ids[] = { .id = 0x0003b921, .mask = 0x0007ffff, }, + { + .id = 0x0003b950, + .mask = 0x0007ffff, + }, { 0, 0 }, }; @@ -637,6 +1051,8 @@ static int __init etm_init(void) { int retval; + mutex_init(&tracer.mutex); + retval = amba_driver_register(&etb_driver); if (retval) { printk(KERN_ERR "Failed to register etb\n"); diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c index df0bf0c8..6a740a93 100644 --- a/arch/arm/kernel/ftrace.c +++ b/arch/arm/kernel/ftrace.c @@ -13,6 +13,7 @@ */ #include +#include #include #include @@ -63,6 +64,20 @@ static unsigned long adjust_address(struct dyn_ftrace *rec, unsigned long addr) } #endif +int ftrace_arch_code_modify_prepare(void) +{ + set_kernel_text_rw(); + set_all_modules_text_rw(); + return 0; +} + +int ftrace_arch_code_modify_post_process(void) +{ + set_all_modules_text_ro(); + set_kernel_text_ro(); + return 0; +} + static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr) { return arm_gen_branch_link(pc, addr); @@ -179,19 +194,20 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, old = *parent; *parent = return_hooker; - err = ftrace_push_return_trace(old, self_addr, &trace.depth, - frame_pointer); - if (err == -EBUSY) { - *parent = old; - return; - } - trace.func = self_addr; + trace.depth = current->curr_ret_stack + 1; /* Only trace if the calling function expects to */ if (!ftrace_graph_entry(&trace)) { - current->curr_ret_stack--; *parent = old; + return; + } + + err = ftrace_push_return_trace(old, self_addr, &trace.depth, + frame_pointer); + if (err == -EBUSY) { + *parent = old; + return; } } diff --git a/arch/arm/kernel/leds.c b/arch/arm/kernel/leds.c index 1911dae1..2050399e 100644 --- a/arch/arm/kernel/leds.c +++ b/arch/arm/kernel/leds.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include @@ -103,6 +105,25 @@ static struct syscore_ops leds_syscore_ops = { .resume = leds_resume, }; +static int leds_idle_notifier(struct notifier_block *nb, unsigned long val, + void *data) +{ + switch (val) { + case IDLE_START: + leds_event(led_idle_start); + break; + case IDLE_END: + leds_event(led_idle_end); + break; + } + + return 0; +} + +static struct notifier_block leds_idle_nb = { + .notifier_call = leds_idle_notifier, +}; + static int __init leds_init(void) { int ret; @@ -111,8 +132,11 @@ static int __init leds_init(void) ret = device_register(&leds_device); if (ret == 0) ret = device_create_file(&leds_device, &dev_attr_event); - if (ret == 0) + if (ret == 0) { register_syscore_ops(&leds_syscore_ops); + idle_notifier_register(&leds_idle_nb); + } + return ret; } diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 48f36246..ff6c55df 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -31,9 +31,9 @@ #include #include #include +#include #include -#include #include #include #include @@ -60,6 +60,18 @@ extern void setup_mm_for_reboot(void); static volatile int hlt_counter; +#ifdef CONFIG_SMP +void arch_trigger_all_cpu_backtrace(void) +{ + smp_send_all_cpu_backtrace(); +} +#else +void arch_trigger_all_cpu_backtrace(void) +{ + dump_stack(); +} +#endif + void disable_hlt(void) { hlt_counter++; @@ -92,6 +104,31 @@ __setup("hlt", hlt_setup); extern void call_with_stack(void (*fn)(void *), void *arg, void *sp); typedef void (*phys_reset_t)(unsigned long); +#ifdef CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART +void arm_machine_flush_console(void) +{ + printk("\n"); + pr_emerg("Restarting %s\n", linux_banner); + if (console_trylock()) { + console_unlock(); + return; + } + + mdelay(50); + + local_irq_disable(); + if (!console_trylock()) + pr_emerg("arm_restart: Console was locked! Busting\n"); + else + pr_emerg("arm_restart: Console was locked!\n"); + console_unlock(); +} +#else +void arm_machine_flush_console(void) +{ +} +#endif + /* * A temporary stack to use for CPU reset. This is static so that we * don't clobber it with the identity mapping. When running with this @@ -207,9 +244,9 @@ void cpu_idle(void) /* endless idle loop with no priority at all */ while (1) { + idle_notifier_call_chain(IDLE_START); tick_nohz_idle_enter(); rcu_idle_enter(); - leds_event(led_idle_start); while (!need_resched()) { #ifdef CONFIG_HOTPLUG_CPU if (cpu_is_offline(smp_processor_id())) @@ -240,9 +277,9 @@ void cpu_idle(void) } else local_irq_enable(); } - leds_event(led_idle_end); rcu_idle_exit(); tick_nohz_idle_exit(); + idle_notifier_call_chain(IDLE_END); schedule_preempt_disabled(); } } @@ -260,6 +297,15 @@ __setup("reboot=", reboot_setup); void machine_shutdown(void) { #ifdef CONFIG_SMP + /* + * Disable preemption so we're guaranteed to + * run to power off or reboot and prevent + * the possibility of switching to another + * thread that might wind up blocking on + * one of the stopped CPUs. + */ + preempt_disable(); + smp_send_stop(); #endif } @@ -282,6 +328,10 @@ void machine_restart(char *cmd) { machine_shutdown(); + /* Flush the console to make sure all the relevant messages make it + * out to the console drivers */ + arm_machine_flush_console(); + arm_pm_restart(reboot_mode, cmd); /* Give a grace period for failure to restart of 1s */ @@ -293,6 +343,77 @@ void machine_restart(char *cmd) while (1); } +/* + * dump a block of kernel memory from around the given address + */ +static void show_data(unsigned long addr, int nbytes, const char *name) +{ + int i, j; + int nlines; + u32 *p; + + /* + * don't attempt to dump non-kernel addresses or + * values that are probably just small negative numbers + */ + if (addr < PAGE_OFFSET || addr > -256UL) + return; + + printk("\n%s: %#lx:\n", name, addr); + + /* + * round address down to a 32 bit boundary + * and always dump a multiple of 32 bytes + */ + p = (u32 *)(addr & ~(sizeof(u32) - 1)); + nbytes += (addr & (sizeof(u32) - 1)); + nlines = (nbytes + 31) / 32; + + + for (i = 0; i < nlines; i++) { + /* + * just display low 16 bits of address to keep + * each line of the dump < 80 characters + */ + printk("%04lx ", (unsigned long)p & 0xffff); + for (j = 0; j < 8; j++) { + u32 data; + if (probe_kernel_address(p, data)) { + printk(" ********"); + } else { + printk(" %08x", data); + } + ++p; + } + printk("\n"); + } +} + +static void show_extra_register_data(struct pt_regs *regs, int nbytes) +{ + mm_segment_t fs; + + fs = get_fs(); + set_fs(KERNEL_DS); + show_data(regs->ARM_pc - nbytes, nbytes * 2, "PC"); + show_data(regs->ARM_lr - nbytes, nbytes * 2, "LR"); + show_data(regs->ARM_sp - nbytes, nbytes * 2, "SP"); + show_data(regs->ARM_ip - nbytes, nbytes * 2, "IP"); + show_data(regs->ARM_fp - nbytes, nbytes * 2, "FP"); + show_data(regs->ARM_r0 - nbytes, nbytes * 2, "R0"); + show_data(regs->ARM_r1 - nbytes, nbytes * 2, "R1"); + show_data(regs->ARM_r2 - nbytes, nbytes * 2, "R2"); + show_data(regs->ARM_r3 - nbytes, nbytes * 2, "R3"); + show_data(regs->ARM_r4 - nbytes, nbytes * 2, "R4"); + show_data(regs->ARM_r5 - nbytes, nbytes * 2, "R5"); + show_data(regs->ARM_r6 - nbytes, nbytes * 2, "R6"); + show_data(regs->ARM_r7 - nbytes, nbytes * 2, "R7"); + show_data(regs->ARM_r8 - nbytes, nbytes * 2, "R8"); + show_data(regs->ARM_r9 - nbytes, nbytes * 2, "R9"); + show_data(regs->ARM_r10 - nbytes, nbytes * 2, "R10"); + set_fs(fs); +} + void __show_regs(struct pt_regs *regs) { unsigned long flags; @@ -352,6 +473,8 @@ void __show_regs(struct pt_regs *regs) printk("Control: %08x%s\n", ctrl, buf); } #endif + + show_extra_register_data(regs, 128); } void show_regs(struct pt_regs * regs) diff --git a/arch/arm/kernel/sched_clock.c b/arch/arm/kernel/sched_clock.c index 6bbf936b..63bc22c8 100644 --- a/arch/arm/kernel/sched_clock.c +++ b/arch/arm/kernel/sched_clock.c @@ -21,6 +21,8 @@ struct clock_data { u32 epoch_cyc_copy; u32 mult; u32 shift; + bool suspended; + bool needs_suspend; }; static void sched_clock_poll(unsigned long wrap_ticks); @@ -49,6 +51,9 @@ static unsigned long long cyc_to_sched_clock(u32 cyc, u32 mask) u64 epoch_ns; u32 epoch_cyc; + if (cd.suspended) + return cd.epoch_ns; + /* * Load the epoch_cyc and epoch_ns atomically. We do this by * ensuring that we always write epoch_cyc, epoch_ns and @@ -98,6 +103,13 @@ static void sched_clock_poll(unsigned long wrap_ticks) update_sched_clock(); } +void __init setup_sched_clock_needs_suspend(u32 (*read)(void), int bits, + unsigned long rate) +{ + setup_sched_clock(read, bits, rate); + cd.needs_suspend = true; +} + void __init setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate) { unsigned long r, w; @@ -169,11 +181,23 @@ void __init sched_clock_postinit(void) static int sched_clock_suspend(void) { sched_clock_poll(sched_clock_timer.data); + if (cd.needs_suspend) + cd.suspended = true; return 0; } +static void sched_clock_resume(void) +{ + if (cd.needs_suspend) { + cd.epoch_cyc = read_sched_clock(); + cd.epoch_cyc_copy = cd.epoch_cyc; + cd.suspended = false; + } +} + static struct syscore_ops sched_clock_ops = { .suspend = sched_clock_suspend, + .resume = sched_clock_resume, }; static int __init sched_clock_syscore_init(void) diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index ebfac782..5df79774 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -575,6 +575,21 @@ static int __init early_mem(char *p) size = memparse(p, &endp); if (*endp == '@') start = memparse(endp + 1, NULL); + +#ifdef CONFIG_VIDEO_RESERVED_MEM_SIZE + /* + * Workaround for AK39xx H.264 decoder limitation which requires continous + * physical RAM which do NOT cross 32MB boundary (Decoder IP requirement). + * + * To avoid confusing developers, developer still pass something like + * mem=REAL_RAM_SIZE in command line. But we must skip memory reserved for + * H.264 decoder since they are NOT handled by normal kernel VM. They are + * manipulated by pmem-like drivers. + */ + if (meminfo.nr_banks == 0) { + size -= CONFIG_VIDEO_RESERVED_MEM_SIZE; + } +#endif arm_add_memory(start, size); diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index d68d1b69..eb3a2912 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -642,7 +642,7 @@ static void do_signal(struct pt_regs *regs, int syscall) } } - if (try_to_freeze()) + if (try_to_freeze_nowarn()) goto no_signal; /* diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 8f14a1b8..22ad00ad 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -56,6 +56,7 @@ enum ipi_msg_type { IPI_CALL_FUNC, IPI_CALL_FUNC_SINGLE, IPI_CPU_STOP, + IPI_CPU_BACKTRACE, }; static DECLARE_COMPLETION(cpu_running); @@ -389,6 +390,7 @@ static const char *ipi_types[NR_IPI] = { S(IPI_CALL_FUNC, "Function call interrupts"), S(IPI_CALL_FUNC_SINGLE, "Single function call interrupts"), S(IPI_CPU_STOP, "CPU stop interrupts"), + S(IPI_CPU_BACKTRACE, "CPU backtrace"), }; void show_ipi_list(struct seq_file *p, int prec) @@ -520,6 +522,58 @@ static void ipi_cpu_stop(unsigned int cpu) cpu_relax(); } +static cpumask_t backtrace_mask; +static DEFINE_RAW_SPINLOCK(backtrace_lock); + +/* "in progress" flag of arch_trigger_all_cpu_backtrace */ +static unsigned long backtrace_flag; + +void smp_send_all_cpu_backtrace(void) +{ + unsigned int this_cpu = smp_processor_id(); + int i; + + if (test_and_set_bit(0, &backtrace_flag)) + /* + * If there is already a trigger_all_cpu_backtrace() in progress + * (backtrace_flag == 1), don't output double cpu dump infos. + */ + return; + + cpumask_copy(&backtrace_mask, cpu_online_mask); + cpu_clear(this_cpu, backtrace_mask); + + pr_info("Backtrace for cpu %d (current):\n", this_cpu); + dump_stack(); + + pr_info("\nsending IPI to all other CPUs:\n"); + smp_cross_call(&backtrace_mask, IPI_CPU_BACKTRACE); + + /* Wait for up to 10 seconds for all other CPUs to do the backtrace */ + for (i = 0; i < 10 * 1000; i++) { + if (cpumask_empty(&backtrace_mask)) + break; + mdelay(1); + } + + clear_bit(0, &backtrace_flag); + smp_mb__after_clear_bit(); +} + +/* + * ipi_cpu_backtrace - handle IPI from smp_send_all_cpu_backtrace() + */ +static void ipi_cpu_backtrace(unsigned int cpu, struct pt_regs *regs) +{ + if (cpu_isset(cpu, backtrace_mask)) { + raw_spin_lock(&backtrace_lock); + pr_warning("IPI backtrace for cpu %d\n", cpu); + show_regs(regs); + raw_spin_unlock(&backtrace_lock); + cpu_clear(cpu, backtrace_mask); + } +} + /* * Main handler for inter-processor interrupts */ @@ -565,6 +619,10 @@ void handle_IPI(int ipinr, struct pt_regs *regs) irq_exit(); break; + case IPI_CPU_BACKTRACE: + ipi_cpu_backtrace(cpu, regs); + break; + default: printk(KERN_CRIT "CPU%u: Unknown IPI message 0x%x\n", cpu, ipinr); diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index 43a31fb0..1f064916 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S @@ -112,6 +112,7 @@ SECTIONS ARM_CPU_KEEP(PROC_INFO) } + /*L2MEM(standby)*/ RO_DATA(PAGE_SIZE) #ifdef CONFIG_ARM_UNWIND diff --git a/arch/arm/mach-ak39/Kconfig b/arch/arm/mach-ak39/Kconfig new file mode 100644 index 00000000..dc597919 --- /dev/null +++ b/arch/arm/mach-ak39/Kconfig @@ -0,0 +1,185 @@ +# linux/arch/arm/mach-ak39/Kconfig +# +# Copyright 2013 Anaka Microelectronics +# +# Licensed under GPLv2 + +# define CPU TYPE FLAGS +config CPU_AK3916 + bool +config CPU_AK3918 + bool +config CPU_AK3919 + bool + +# Machine support +choice + prompt "ANYKA AK39xx boards" + depends on ARCH_AK39 + +config ARCH_CLOUD39EV3_AK3916E128PIN_COREBD + bool "AK3916E V300 SVT Board" + depends on ARCH_AK39 + select CPU_AK3916 + help + Support for AK3916E V300 SVT board + +config ARCH_CLOUD39EV3_AK3918E80PIN_COREBD + bool "AK3918E V300 SVT board" + depends on ARCH_AK39 + select CPU_AK3918 + help + Support for AK3918E V300 + +config ARCH_CLOUD39EV3_AK3916EV300_MAINBD + bool "AK3916E V300 machine" + depends on ARCH_AK39 + select CPU_AK3916 + help + Support for AK3916E V300 + +config ARCH_CLOUD39EV3_SQ38X4_COREBD + bool "AK3918E V300 SVT board" + depends on ARCH_AK39 + select CPU_AK3918 + help + Support for AK3918E V300 + +config ARCH_CLOUD39EV3_AK3918EV300_MAINBD + bool "AK3918E V300 machine" + depends on ARCH_AK39 + select CPU_AK3918 + help + Support for AK3918E V300 + +config ARCH_CLOUD39EV3_AK3918EV300_SDIO_WIFI_MAINBD + bool "AK3918E V300 sdio wifi machine" + depends on ARCH_AK39 + select CPU_AK3918 + help + Support for AK3918E V300 + +config ARCH_CLOUD39EV3_AK3918EV300_YTJ_MAINBD + bool "AK3918E V300 YTJ machine" + depends on ARCH_AK39 + select CPU_AK3918 + help + Support for AK3918E V300 + +config ARCH_CLOUD39EV3_AK3919EV300_MAINBD + bool "AK3919E V300 machine" + depends on ARCH_AK39 + select CPU_AK3919 + help + Support for AK3919E V300 + +config ARCH_CLOUD39EV3_CO38_SC2235 + bool "AK3918E 38module board" + depends on ARCH_AK39 + select CPU_AK3918 + help + Support for AK3918E V300 + +config ARCH_CLOUD39EV3_AK3919EV300_SQ38_SC2232_V1_0_0 + bool "AK3919E SQ38 module board_V1.0.0" + depends on ARCH_AK39 + select CPU_AK3919 + help + Support for AK3918E V300 + +config ARCH_CLOUD39EV3_AK3919EV300_SQ38_SC2232_V1_0_1 + bool "AK3919E SQ38 module board_V1.0.1" + depends on ARCH_AK39 + select CPU_AK3919 + help + Support for AK3918E V300 + +config ARCH_CLOUD39EV3_SQ38_QFN64_F22_V100 + bool "AK3919E 38module board" + depends on ARCH_AK39 + select CPU_AK3918 + help + Support for AK3919E V300 + +config ARCH_AK3918EV300_C80DX + bool "AK3919E Tenda C80DX Board" + depends on ARCH_AK39 + select CPU_AK3918 + help + Support for TD-C80DX Board + +config ARCH_AK3918EV300_HL_XIAOFANG + bool "AK3919E Tenda C80DX Board" + depends on ARCH_AK39 + select CPU_AK3918 + help + Support for HL-XIAOFANG Board + +config ARCH_CLOUD39EV3_AK3918EV300_YR_IPC100A_MAINBD + bool "AK3918E V300 YTJ machine" + depends on ARCH_AK39 + select CPU_AK3918 + help + Support for AK3918E V300 + +config ARCH_AK3918EV300_SC10008_V100 + bool "AK3918E V300 Board SC10008 V1.0.0" + depends on ARCH_AK39 + select CPU_AK3918 + help + Support for AK3918E V300 Board SC-10008. + +config ARCH_AK3918EV300_JW_F37_V10 + bool "AK3918E V300 Board JW F37 V1.0" + depends on ARCH_AK39 + select CPU_AK3918 + help + Support for AK3918E V300 Board JW-F37. + +endchoice + +# aisc cpufreq set +#config ASIC_FREQ_VALUE +# int "Config asic frequency(if asic>100MHz asic=vclk; else asic=vclk/2)" +# range 45000000 120000000 +# default 90000000 +# depends on ARCH_AK39 +# help +# config ak39xx asic frequency when the kernel uncompress. +# note: vclk be equal to even asic. + +# because ADC2_clk_div_num is 6-bits register, so ASIC PLL should not too high +# if set ADC samplerate 8000Hz. +choice + prompt "Config asic frequency" + default ASIC_CLK_120MHZ + depends on ARCH_AK39 + +config ASIC_CLK_120MHZ + bool "120MHz" + depends on ARCH_AK39 + help + Then ASIC_PLL=480MHz, VCLK=240MHz, ASIC_CLK=120MHz. + +endchoice + +# power managerment +comment "Power management" +config AK39_PM + bool "ak39 Power Management support" + depends on PM && ARCH_AK39 + help + Say Y here if you want support power management for AK39. + +config AK39_CPUFREQ + bool "ak39 Frequency scaling support" + depends on CPU_FREQ && ARCH_AK39 + help + Say Y here, CPU Frequency scaling support for AK39 SoC CPUs. + +# chip extra controller lib +config AK39_PWM_TIMER + bool "ak39 PWM/Timer Control support" + depends on ARCH_AK39 + help + Say Y here if you want support pwm control. diff --git a/arch/arm/mach-ak39/Makefile b/arch/arm/mach-ak39/Makefile new file mode 100644 index 00000000..3be7244c --- /dev/null +++ b/arch/arm/mach-ak39/Makefile @@ -0,0 +1,48 @@ +#linux/arch/arm/mach-ak39/Makefile +# +# Copyright 2013 Anyka Microelectronics +# +# Licensed under GPLv2 + +obj-y := +obj-m := +obj-n := +obj- := + +# Core support for all Anyka SoCs +obj-y += cpu.o time.o irq.o clock.o \ + l2cache.o devices.o \ + ak39-gpio.o gpio.o \ + adc.o \ + reset.o reboot.o \ + timer.o \ + ispdrv_interface.o + + +# chip extra interface +obj-$(CONFIG_AK39_PWM_TIMER) += pwm_timer.o + +# Power Management support +obj-$(CONFIG_AK39_PM) += pm.o + +# Machin support +obj-$(CONFIG_ARCH_CLOUD39EV2_AK3916E_MAINBD) += mach-cloud39ev2_ak3916e_mainbd.o +obj-$(CONFIG_ARCH_CLOUD39EV3_AK3916E128PIN_COREBD) += mach-cloud39ev3_ak3916e128pin_mnbd.o +obj-$(CONFIG_ARCH_CLOUD39EV3_AK3918E80PIN_COREBD) += mach-cloud39ev3_ak3918e80pin_mnbd.o +obj-$(CONFIG_ARCH_SKY39EV2_AK3918E80PIN_COREBD) += mach-sky39e_ak3918e80pin_mnbd.o +obj-$(CONFIG_ARCH_CLOUD39EV3_AK3916EV300_MAINBD) += mach-cloud39ev3_ak3916ev300_mnbd.o +obj-$(CONFIG_ARCH_CLOUD39EV3_AK3918EV300_MAINBD) += mach-cloud39ev3_ak3918ev300_mnbd.o +obj-$(CONFIG_ARCH_CLOUD39EV3_AK3918EV300_YTJ_MAINBD) += mach-cloud39ev3_ak3918ev300_YTJ_mnbd.o +obj-$(CONFIG_ARCH_CLOUD39EV3_AK3918EV300_SDIO_WIFI_MAINBD) += mach-cloud39ev3_ak3918ev300_sdio_wifi_mndb.o +obj-$(CONFIG_ARCH_CLOUD39EV3_AK3919EV300_MAINBD) += mach-cloud39ev3_ak3919ev300_mnbd.o + +obj-$(CONFIG_ARCH_CLOUD39EV3_AK3919EV300_SQ38_SC2232_V1_0_0) += mach-cloud39ev3_ak3919ev300_sq38_sc2232_V1.0.0.o +obj-$(CONFIG_ARCH_CLOUD39EV3_AK3919EV300_SQ38_SC2232_V1_0_1) += mach-cloud39ev3_ak3919ev300_sq38_sc2232_V1.0.1.o +obj-$(CONFIG_ARCH_CLOUD39EV3_CO38_SC2235) += mach-cloud39ev3_co38_sc2235.o +obj-$(CONFIG_ARCH_CLOUD39EV3_SQ38_QFN64_F22_V100) += mach-cloud39ev3_sq38_qfn64_f22_v100.o +obj-$(CONFIG_ARCH_AK3918EV300_C80DX) += mach-ak3918ev300_c80dx.o +obj-$(CONFIG_ARCH_AK3918EV300_SC10008_V100) += mach-ak3918ev300_sc10008_v100.o +obj-$(CONFIG_ARCH_AK3918EV300_HL_XIAOFANG) += mach-ak3918ev300_hl_xiaofang.o +obj-$(CONFIG_ARCH_CLOUD39EV3_AK3918EV300_YR_IPC100A_MAINBD) += mach-cloud39ev3_ak3918ev300_YR_IPC100A_mnbd.o +obj-$(CONFIG_ARCH_CLOUD39EV3_SQ38X4_COREBD) += mach-cloud39ev3_sq38x4_mnbd.o +obj-$(CONFIG_ARCH_AK3918EV300_JW_F37_V10) += mach-ak3918ev300_jw_f37_v10.o diff --git a/arch/arm/mach-ak39/Makefile.boot b/arch/arm/mach-ak39/Makefile.boot new file mode 100644 index 00000000..f2e0fa5e --- /dev/null +++ b/arch/arm/mach-ak39/Makefile.boot @@ -0,0 +1,11 @@ +ifdef CONFIG_VIDEO_RESERVED_MEM_SIZE + __ZRELADDR := $(shell /bin/bash -c 'printf "0x%08x" \ + $$[$(CONFIG_RAM_BASE) + $(CONFIG_VIDEO_RESERVED_MEM_SIZE) + 0x8000]') +__PARAMS_PHYS := $(shell /bin/bash -c 'printf "0x%08x" \ + $$[$(CONFIG_RAM_BASE) + $(CONFIG_VIDEO_RESERVED_MEM_SIZE) + 0x100]') + zreladdr-y := $(__ZRELADDR) +params_phys-y := $(__PARAMS_PHYS) +else + zreladdr-y := 0x80008000 +params_phys-y := 0x80000100 +endif diff --git a/arch/arm/mach-ak39/adc.c b/arch/arm/mach-ak39/adc.c new file mode 100644 index 00000000..07a37b92 --- /dev/null +++ b/arch/arm/mach-ak39/adc.c @@ -0,0 +1,434 @@ +/* + * ak_adc.c - ak ADC1 operation API + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include + +#define PK(fmt...) //printk(fmt) // debug + +static spinlock_t adc1_lock; +void adc_close_channel(int channel_type); +void adc_open_channel(int channel_type); +int adc_open_init(int channel_type); +/** + * @BRIEF config saradc clk +* @AUTHOR luoyongchuang +* @DATE 2016-06 -13 + * @PARAM div SARADC CLK = 12Mhz/(div+1); + * @PARAM div value always 2 + * @RETURN + * @NOTE: + */ +static void adc1_clk_cfg(unsigned long div) +{ + unsigned long saradc_chief_driven; + unsigned long saradc_module_driven; + + //reserve the driven value + saradc_chief_driven = ((REG32(SAR_IF_CFG_REG)) & (0x1<<0)); + saradc_module_driven = ((REG32(SAR_IF_CFG_REG)) & (0x7<<5)); + + //disable module chief driven by sar adc clk + REG32(SAR_IF_CFG_REG) &= (~(1<<0)); + + //disable Ain0_sampling Ain1_sampling Bat_sampling + REG32(SAR_IF_CFG_REG) &= (~(0x7<<5)); + + //close sar adc clk + REG32(AD_DA_CLK1_REG) &= (~(0x1<<3)); + + //cofig div + REG32(AD_DA_CLK1_REG) &= (~0x7); + REG32(AD_DA_CLK1_REG) |= (div & 0x7); + + //open sar adc clk + REG32(AD_DA_CLK1_REG) |= (0x1<<3); + + //get back the driven cfg + REG32(SAR_IF_CFG_REG) |= (saradc_chief_driven | saradc_module_driven); +} + +static void power_on_adc1(void) +{ + REG32(SAR_ADC_ACR_REG2) &= (~(1 << 0)); +} + +static void power_off_adc1(void) +{ + REG32(SAR_ADC_ACR_REG2) |= (1 << 0); +} +#if 0 +static void enable_adc1_channel(int channel) +{ + REG32(SAR_IF_CFG_REG) &= ~(1 << 0); + REG32(SAR_IF_CFG_REG) |= (1 << (channel + 5)); + REG32(SAR_IF_CFG_REG) |= (1 << 0); +} + +static void disable_adc1_channel(int channel) +{ + REG32(SAR_IF_CFG_REG) &= ~(1 << 0); + REG32(SAR_IF_CFG_REG) &= ~(1 << (channel + 5)); + REG32(SAR_IF_CFG_REG) |= (1 << 0); +} +#endif +/** +* @BRIEF get sar adc sample value ж +* @AUTHOR luoyongchuang +* @DATE 2016-06 -13 +* @PARAM p_spl_val +* @PARAM val_index ָʾǵڼͨ +* @RETURN sample value ֵΪ CHANNEL +* @NOTE: 飬˳ain0, ain1, bat +*/ +unsigned long adc_get_sample_val(int channel_type) +{ + unsigned long val = 0; + + //disable sample + REG32(SAR_IF_CFG_REG) &= (~(1<<0)); + + //clear sample done interrupt + REG32(SAR_IF_INT_STATUS_REG) &= (~0x1); + + //eable sample + REG32(SAR_IF_CFG_REG) |= (1<<0); + + //wait sample done + while( (REG32(SAR_IF_INT_STATUS_REG) & 1) == 0) + { + mdelay(1); + PK("sample NOT done.\n"); + } + + //disable sample + REG32(SAR_IF_CFG_REG) &= (~(1<<0)); + + + //check whether enable sample open + if( (channel_type == 0) || (channel_type == 1) ) + { + val = ((REG32(SAR_IF_SMP_DAT_REG)>>(channel_type*12)) & 0xfff); + } + else if(channel_type == 2) + { + val = ((REG32(SAR_IF_INT_STATUS_REG)>>20) & 0xfff); + } + + return val; +} + + +/** + * @brief: Read AD0/AD1/BAT voltage +* @AUTHOR luoyongchuang +* @DATE 2016-06 -13 + * + * @Warning: Please don`t use this function in IRQ handle routine! + */ +unsigned long adc1_read_channel(int channel) +{ + unsigned long val = 0; + unsigned long flags; + +// power_on_adc1(); +// adc_open_channel(AK_ADC1_BAT); +// mdelay(1); + adc_open_init(channel); + spin_lock_irqsave(&adc1_lock, flags); + val = adc_get_sample_val(channel); + + PK("value is %ld mv\n",val); + if (channel == AK_ADC1_BAT) + { + val = val*(2)*(3000)/4096; //324/100HPVDD verfοѹ + PK(" battery val is %ld mv\n",val); + } + else + val = val*(3000)/4096; + +#if 0 + enable_adc1_channel(channel); + mdelay(1); + + val = ((REG32(SAR_IF_SMP_DAT_REG) >> (channel * 10)) & 0x3ff); + if (channel == AK_ADC1_BAT) { + for(count = 0; count < 4; count++) { + mdelay(1); + val += ((REG32(SAR_IF_SMP_DAT_REG) >> (channel * 10)) & 0x3ff); + } + val /= 5; + } +/** + ڲõأĬ CONFIG_AK_GETAIN ģ˴״̬ +**/ +#if defined(CONFIG_AK_GETAIN) +#else + val = (val * AK_AVCC) >> 10; +#endif + + disable_adc1_channel(channel); +#endif + + adc_close_channel(channel); + power_off_adc1(); + spin_unlock_irqrestore(&adc1_lock, flags); + + return val; +} + + +/** +* @BRIEF close channel +* @AUTHOR luoyongchuang +* @DATE 2016-06 -13 +* @PARAM T_SARADC_CHANNEL_TYPE: SARADC_AIN0, SARADC_AIN1, SARADC_BAT +* @PARAM +* @RETURN +* @NOTE: +*/ +void adc_close_channel(int channel_type) +{ + unsigned long state; + + //close channel + if( (REG32(SAR_IF_CFG_REG) & (1<<(channel_type+5))) == 0) + { + PK("this channel already close.\n"); + //return; + } + + //reserve the sample switch state + state = (REG32(SAR_IF_CFG_REG) & (1<<0)); + + //close sample + REG32(SAR_IF_CFG_REG) &= (~(1<<0)); + + //clear channel interrupt state and sample interrupt state + REG32(SAR_IF_INT_STATUS_REG) &= (~((1<<0) | (1<<(channel_type+1)))); + + //close channel + REG32(SAR_IF_CFG_REG) &= (~(1<<(channel_type+5))); + + if( channel_type == AK_ADC1_BAT) + { + //disable ratio + REG32(SAR_ADC_ACR_REG2) &= (~(1<<7)); + } + + //resume the sample switch state + REG32(SAR_IF_CFG_REG) |= state; + +} + +/** +* @BRIEF config channel +* @AUTHOR Zou Tianxiang +* @DATE 2012-11 -10 +* @PARAM T_SARADC_CHANNEL_CFG +* @PARAM T_SARADC_CHANNEL_TYPE : SARADC_AIN0, SARADC_AIN1, SARADC_BAT +* @RETURN AK_TRUE: config channel success +* AK_FALSE: config channel false +* @NOTE: +*/ +unsigned long adc_config_channel( unsigned long clkdiv,int channel_type) +{ + unsigned long samplerate; + unsigned long adc1_clk; + unsigned long spl_cycle, spl_hold, spl_wait; + + //make sure the saradc is power on + if( (REG32(SAR_ADC_ACR_REG2) & (1<<0)) == (1<<0)) + { + PK("saradc is power down, please initial first.\n"); + } + + //reserve the sample state + //ul_state= (REG32(SAR_IF_CFG_REG) & (1<<0)); + + //disable sample + REG32(SAR_IF_CFG_REG) &= (~(1<<0)); + + //clear all interrupt state + REG32(SAR_IF_INT_STATUS_REG) = 0x0; + + //select channel + if((channel_type == 0) || (channel_type == 1)) + REG32(SAR_ADC_ACR_REG2) |= ( 1<<(5+channel_type) ); + else if(channel_type == 2) + REG32(SAR_ADC_ACR_REG2) |= ( 1<<4 ); + + //enable channel + REG32(SAR_IF_CFG_REG) |= ( 1<<(5+channel_type) ); + + + /* one channel one time, default samplerate is 5000 */ + adc1_clk = ADC1_MAIN_CLK / (clkdiv + 1); + samplerate = DEFAULT_SAMPLE; + spl_cycle = adc1_clk / samplerate; + spl_wait = 1; + spl_hold = spl_wait + 16 + 1; + + REG32(SAR_IF_CFG_REG) &= (~(0xff << 14)); + REG32(SAR_IF_CFG_REG) |= (spl_wait << 14); + + REG32(SAR_TIMING_CFG_REG) = 0; + REG32(SAR_TIMING_CFG_REG) = spl_cycle | (spl_hold << 16) ; + + //config sample cnt + REG32(SAR_IF_CFG_REG) &= (~(0x7<<8)); + REG32(SAR_IF_CFG_REG) |= (1<<8); //ó11-7ֵӦDzɼٴκƽֵ + + //config threshold + //if( (p_channel_cfg->thresh_val != 0) && (p_channel_cfg->thresh_val < 0x400)) + { + //config threshold val + REG32(SAR_THRESHOLD_REG) &= (~(0x3ff<<(channel_type*10))); + REG32(SAR_THRESHOLD_REG) |= (100<<(channel_type*10)); + + //config threshold polarity + REG32(SAR_IF_CFG_REG) &= (~(0x1<<(channel_type+11))); + REG32(SAR_IF_CFG_REG) |= (1<<(channel_type+11)); + + //config threshold interrupt + REG32(SAR_IF_CFG_REG) &= (~(0x1<<(channel_type+2))); + REG32(SAR_IF_CFG_REG) |= (1<<(channel_type+2)); + } + if( channel_type == AK_ADC1_BAT) + { + //use 1:2 radio + REG32(SAR_ADC_ACR_REG2) |= (1<<7); + } + + //config sample done interrupt + REG32(SAR_IF_CFG_REG) &= (~(0x1<<1)); + REG32(SAR_IF_CFG_REG) |= (0<<1); + return 0; +} + +/** +* @BRIEF open channel +* @AUTHOR luoyongchuang +* @DATE 2016-06 -13 +* @PARAM T_SARADC_CHANNEL_TYPE: SARADC_AIN0, SARADC_AIN1, SARADC_BAT +* @PARAM +* @RETURN +* @NOTE: +*/ +void adc_open_channel(int channel_type) +{ + unsigned long state; + + //make sure the saradc is power on + if( (REG32(SAR_ADC_ACR_REG2) & (1<<0)) == (1<<0)) + { + PK("saradc is power down, please initial first.\n"); + //while(1); + } + + //check whether the channel already open + if( (REG32(SAR_IF_CFG_REG) & (1<<(5+channel_type)) ) == (1<<(5+channel_type)) ) + { + PK("this channel already open.\n"); + return ; + } + + //reserve the sample switch state + state = (REG32(SAR_IF_CFG_REG) & (1<<0)); + + //close sample + REG32(SAR_IF_CFG_REG) &= (~(1<<0)); + + //clear channel interrupt state and sample interrupt state + REG32(SAR_IF_INT_STATUS_REG) &= (~((1<<0) | (1<<(channel_type+1)))); + + //enable channel + REG32(SAR_IF_CFG_REG) |= ( 1<<(5+channel_type) ); + + + //resume the sample switch state + REG32(SAR_IF_CFG_REG) |= state; + +} +/** + * @brief: ADC1 initialization + * @author: Zhongjunchao + * @data: 2011-7-20 + * @modify: 2013-4-27 + * + * @note: This function will init ADC1 clock and sample rate, and then enable + * ADC1 but not power on. Any device use ADC1 please use the read value API. + * We use some default value, if ADC1 can`t work make be will change it. + * Please use this function in machine init. + */ +int adc1_init(void) +{ + + return 0; +} +/** + * @brief: ADC1 initialization + * @author: Zhongjunchao + * @data: 2011-7-20 + * @modify: 2013-4-27 + * + * @note: This function will init ADC1 clock and sample rate, and then enable + * ADC1 but not power on. Any device use ADC1 please use the read value API. + * We use some default value, if ADC1 can`t work make be will change it. + * Please use this function in machine init. + */ + +int adc_open_init(int channel_type) +{ + + unsigned long clkdiv; + + spin_lock_init(&adc1_lock); + /* reset adc1 */ + REG32(RESET_CTRL_REG) &= (~(1<<30)); + + /* config adc1 clk, default 1.5MHz(BAT use only in 1.5MHz) */ + clkdiv = ADC1_MAIN_CLK/ADC1_DEFAULT_CLK - 1; + clkdiv &= 0x7; + adc1_clk_cfg(clkdiv); //svtֱӸֵ7 + + /* release reset */ + REG32(RESET_CTRL_REG) |= (1<<30); + + /* disable all sample */ + REG32(SAR_IF_CFG_REG) &= ( ~( (1<<0) | (0x7<<5) ) ); + + /* clear all adc1 interrupt state */ + REG32(SAR_IF_INT_STATUS_REG) = 0x0; + + /* mask all adc1 interrupt */ + REG32(SAR_IF_CFG_REG) &= (~ (0xf<<1)); + + /* power on adc1 */ + power_on_adc1(); + + //select vref = 3V + REG32(SAR_ADC_ACR_REG2) &= (~(1<<3)); + + //disable sample + REG32(SAR_IF_CFG_REG) &= (~(1<<0)); + + adc_close_channel(channel_type); + + adc_config_channel(clkdiv , channel_type); + + adc_open_channel(channel_type); + + //power_off_adc1(); + + return 0; +} diff --git a/arch/arm/mach-ak39/ak39-gpio.c b/arch/arm/mach-ak39/ak39-gpio.c new file mode 100644 index 00000000..d3d8e6cb --- /dev/null +++ b/arch/arm/mach-ak39/ak39-gpio.c @@ -0,0 +1,1039 @@ +/* + * arch/arm/mach-ak39/gpio.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include +#include +#include + +//share pin config fore module in AK39xx +struct gpio_sharepin_cfg share_cfg_module[] = { + {ePIN_AS_OPCLK, SHARE_CFG1, (0x3<<9), (1<<9), 0, 0, 0, 0,0, 0}, + {ePIN_AS_JTAG, SHARE_CFG12, (0x3<<5)|(1<<0), (0x3<<5)|(1<<0), (0x3f<<4), (0x3f<<4), 0, 0,0, 0}, + {ePIN_AS_RTCK, SHARE_CFG1, (0x3<<5), (3<<5), 0, 0, 0, 0,0, 0}, + {ePIN_AS_I2S, SHARE_CFG1, ((0xf<<14)|(1<<24)), ((0xf<<14)|(0<<24)), 0, 0, 0, 0,0, 0}, + {ePIN_AS_PWM1, SHARE_CFG1, (0x1<<22), (0x1<<22), 0, 0, 0, 0,0, 0}, + {ePIN_AS_PWM2, SHARE_CFG2, 0, 0, (0x3<<17), (0x3<<17), 0, 0,0, 0}, + {ePIN_AS_PWM3, SHARE_CFG1, (0x3<<6), (0x1<<6), 0, 0, 0, 0,0, 0}, + {ePIN_AS_PWM4, SHARE_CFG2, 0, 0, (0x3<<3), (0x3<<3), 0, 0, 0, 0}, + {ePIN_AS_PWM5, SHARE_CFG1, (0x3<<10), (0x3<<10), 0, 0, 0, 0, 0, 0}, +#ifndef SPI_GPIO_37_TO_40 + {ePIN_AS_SPI1, SHARE_CFG14, (0x1<<25), (0x1<<25), 0, 0, 0,0,((0x3<<0)|(0x1f<<9)), (0x3|(0x1f<<9))}, +#else + {ePIN_AS_SPI1, SHARE_CFG14, (0x1<<25), (0x0<<25), 0, 0, 0,0,((0x3<<0)|(0x3f<<14)), (0x3|(0x3f<<14))}, +#endif + {ePIN_AS_SPI2, SHARE_CFG1, 0, 0, 0, 0, 0,0,0, 0}, + {ePIN_AS_UART1, SHARE_CFG1, (0x3<<2), (0x3<<2), 0, 0, 0, 0,0, 0}, + {ePIN_AS_UART2, SHARE_CFG1, ((0x1<<29)|(0x3<<6)|(0x3<<0)), ((0x1<<29)|(0x2<<6)|(0x2<<0)), 0, 0, 0, 0, 0, 0}, + {ePIN_AS_CAMERA, SHARE_CFG2, 0, 0, (0xE7F807F), (0x0), 0, 0,0, 0}, //dvp0, width: 8bits + {ePIN_AS_MCI, SHARE_CFG4, 0, 0, 0, 0,0, 0, (0x07<<6), (0x07<<6)}, + + {ePIN_AS_SDIO, SHARE_CFG134, (1<<28), (1<<28), 0, 0, ((0xf<<22)|(0xf<<16)), ((0xa<<22)|(0xa<<16)), (0x3<<20), (0x3<<20)}, + //{ePIN_AS_SDIO, SHARE_CFG14, (1<<28), (0<<28), 0, 0 ,0, 0,(0xff<<20),(0x57<<20)}, + + {ePIN_AS_MCI_8LINE, SHARE_CFG4, 0, 0, 0, 0,0, 0, ((0xf<<6)|(0xf<<10)|(0xf<<14)), ((0xe<<6)|(5<<10)|(0x5<<10))}, + //{ePIN_AS_MAC, SHARE_CFG13, (3<<9), (1<<9), 0, 0, (0xfffffff <<0 ), (0x575f5a5 <<0),0, 0}, + {ePIN_AS_MAC, SHARE_CFG13, (3<<10), (1<<10), 0, 0, (0xfffffff <<0 ), (0x575f5a5 <<0),0, 0}, + + //{ePIN_AS_RMAC, SHARE_CFG13, (3<<9), (1<<9), 0, 0, (0xfffffff <<0 ), (0x1450525 <<0),0, 0}, + {ePIN_AS_RMAC, SHARE_CFG13, (3<<10), (1<<10), 0, 0, (0xfffffff <<0 ), (0x1450525 <<0),0, 0}, + + {ePIN_AS_I2C, SHARE_CFG1, (0x3<<8), (0x3<<8), 0, 0, 0, 0,0, 0}, + {ePIN_AS_IRDA, SHARE_CFG3, 0, 0, 0, 0, 0, 0,0, 0}, + {ePIN_AS_MIPI, SHARE_CFG2, (0x1ff<<19), 0, 0, 0, 0, 0, 0, 0}, + {ePIN_AS_DUMMY, EXIT_CFG, 0, 0, 0, 0, 0, 0,0,0} +}; + +struct gpio_pupd_cfg pupd_cfg_info[] = { + //pin, index, register, up/down + {AK_GPIO_0, 0, PUPD_CFG1, PULLUP}, + {AK_GPIO_1, 1, PUPD_CFG1, PULLUP}, + {AK_GPIO_2, 2, PUPD_CFG1, PULLUP}, +// {AK_GPIO_3, 1, PUPD_CFG1, PULLDOWN}, + {AK_GPIO_4, 4, PUPD_CFG1, PULLUP}, + {AK_GPIO_5, 5, PUPD_CFG1, PULLUP}, + {AK_GPIO_6, 4, PUPD_CFG2, PULLDOWN}, + {AK_GPIO_7, 5, PUPD_CFG2, PULLDOWN}, + {AK_GPIO_8, 6, PUPD_CFG2, PULLDOWN}, + {AK_GPIO_9, 7, PUPD_CFG2, PULLDOWN}, + {AK_GPIO_10, 0, PUPD_CFG3, PULLDOWN}, + {AK_GPIO_11, 1, PUPD_CFG3, PULLUP}, + {AK_GPIO_12, 2, PUPD_CFG3, PULLDOWN}, + {AK_GPIO_13, 3, PUPD_CFG3, PULLDOWN}, + {AK_GPIO_14, 5, PUPD_CFG3, PULLDOWN}, + {AK_GPIO_15, 6, PUPD_CFG3, PULLDOWN}, + {AK_GPIO_16, 7, PUPD_CFG3, PULLDOWN}, + {AK_GPIO_17, 8, PUPD_CFG3, PULLDOWN}, + {AK_GPIO_18, 9, PUPD_CFG3, PULLDOWN}, + {AK_GPIO_19, 11, PUPD_CFG3, PULLDOWN}, + {AK_GPIO_20, 12, PUPD_CFG3, PULLDOWN}, + {AK_GPIO_21, 13, PUPD_CFG3, PULLDOWN}, + {AK_GPIO_22, 14, PUPD_CFG3, PULLDOWN}, + {AK_GPIO_23, 15, PUPD_CFG3, PULLDOWN}, + {AK_GPIO_24, 16, PUPD_CFG3, PULLDOWN}, + {AK_GPIO_25, 0, PUPD_CFG4, PULLUP}, + {AK_GPIO_26, 1, PUPD_CFG4, PULLUP}, + {AK_GPIO_27, 6, PUPD_CFG1, PULLUP}, + {AK_GPIO_28, 7, PUPD_CFG1, PULLUP}, + {AK_GPIO_29, 2, PUPD_CFG4, PULLUP}, + {AK_GPIO_30, 3, PUPD_CFG4, PULLUP}, + {AK_GPIO_31, 4, PUPD_CFG4, PULLUP}, + {AK_GPIO_32, 5, PUPD_CFG4, PULLUP}, + {AK_GPIO_33, 6, PUPD_CFG4, PULLUP}, + {AK_GPIO_34, 7, PUPD_CFG4, PULLUP}, + {AK_GPIO_35, 8, PUPD_CFG4, PULLUP}, + {AK_GPIO_36, 9, PUPD_CFG4, PULLUP}, + {AK_GPIO_37, 10, PUPD_CFG4, PULLUP}, + {AK_GPIO_38, 11, PUPD_CFG4, PULLUP}, + {AK_GPIO_39, 12, PUPD_CFG4, PULLUP}, + {AK_GPIO_40, 13, PUPD_CFG4, PULLUP}, + {AK_GPIO_41, 14, PUPD_CFG4, PULLUP}, + {AK_GPIO_42, 15, PUPD_CFG4, PULLUP}, + {AK_GPIO_43, 16, PUPD_CFG4, PULLUP}, + {AK_GPIO_44, 17, PUPD_CFG4, PULLUP}, + {AK_GPIO_45, 18, PUPD_CFG4, PULLUP}, + {AK_GPIO_46, 19, PUPD_CFG4, PULLUP}, + {AK_GPIO_47, 8, PUPD_CFG1, PULLDOWN}, + {AK_GPIO_48, 9, PUPD_CFG1, PULLDOWN}, + {AK_GPIO_49, 27, PUPD_CFG1, PULLDOWN}, //ygh 20170929 add + {AK_GPIO_50, 10, PUPD_CFG1, PULLUP}, + {AK_GPIO_51, 11, PUPD_CFG1, PULLUP}, + {AK_GPIO_52, 12, PUPD_CFG1, PULLUP}, + {AK_GPIO_53, 13, PUPD_CFG1, PULLUP}, + {AK_GPIO_54, 14, PUPD_CFG1, PULLDOWN}, + {AK_GPIO_55, 15, PUPD_CFG1, PULLDOWN}, +// {AK_GPIO_56, 11, PUPD_CFG1, PULLUP}, + {AK_GPIO_57, 17, PUPD_CFG1, PULLUP}, + {AK_GPIO_58, 18, PUPD_CFG1, PULLUP}, + {AK_GPIO_59, 19, PUPD_CFG1, PULLDOWN}, + {AK_GPIO_60, 20, PUPD_CFG1, PULLDOWN}, + {AK_GPIO_61, 21, PUPD_CFG1, PULLDOWN}, + {AK_GPIO_62, 22, PUPD_CFG1, PULLUP}, + {AK_GPIO_63, 23, PUPD_CFG1, PULLUP}, + {AK_GPIO_64, 0, PUPD_CFG2, PULLDOWN}, + {AK_GPIO_65, 1, PUPD_CFG2, PULLDOWN}, + {AK_GPIO_66, 2, PUPD_CFG2, PULLDOWN}, + {AK_GPIO_67, 3, PUPD_CFG2, PULLDOWN}, + {AK_GPIO_68, 8, PUPD_CFG2, PULLDOWN}, + {AK_GPIO_69, 9, PUPD_CFG2, PULLDOWN}, + {AK_GPIO_76, 4, PUPD_CFG3, PULLDOWN}, + {AK_GPIO_77, 10, PUPD_CFG3, PULLDOWN}, + {AK_GPIO_78, 17, PUPD_CFG3, PULLDOWN}, + {AK_GPIO_79, 20, PUPD_CFG4, PULLUP}, + {AK_GPIO_80, 24, PUPD_CFG1, PULLUP}, + {AK_GPIO_81, 25, PUPD_CFG1, PULLUP}, + {AK_GPIO_82, 26, PUPD_CFG1, PULLUP}, + {AK_GPIO_85, 28, PUPD_CFG1, PULLUP}, +}; + +//this used to clr in gpio chare pin cfg1 +struct sharepin_as_gpio sharepin_cfg_gpio1[] = { + {0, 0, 0, AS_GPIO_CFG_BIT2}, + {1, 1, 2, AS_GPIO_CFG_BIT1}, + {2, 2, 3, AS_GPIO_CFG_BIT1}, + {3, 3, 4, AS_GPIO_CFG_BIT1}, + {4, 4, 5, AS_GPIO_CFG_BIT1}, + {5, 5, 6, AS_GPIO_CFG_BIT2}, + {27, 27, 8, AS_GPIO_CFG_BIT1}, + {28, 28, 9, AS_GPIO_CFG_BIT1}, + {47, 47, 10, AS_GPIO_CFG_BIT2}, + {48, 48, 12, AS_GPIO_CFG_BIT2}, + {50, 50, 13, AS_GPIO_CFG_BIT1}, + {51, 51, 14, AS_GPIO_CFG_BIT1}, + {52, 52, 15, AS_GPIO_CFG_BIT1}, + {53, 53, 16, AS_GPIO_CFG_BIT1}, + {54, 54, 17, AS_GPIO_CFG_BIT1}, + {55, 55, 18, AS_GPIO_CFG_BIT1}, + {56, 56, 19, AS_GPIO_CFG_BIT1}, + {57, 57, 20, AS_GPIO_CFG_BIT1}, + {58, 58, 21, AS_GPIO_CFG_BIT1}, + {80, 80, 22, AS_GPIO_CFG_BIT1}, + {81, 81, 23, AS_GPIO_CFG_BIT1}, +}; +//this used to clr in gpio chare pin cfg2 +struct sharepin_as_gpio sharepin_cfg_gpio2[] = { + {64, 64, 0, AS_GPIO_CFG_BIT3}, + {65, 65, 2, AS_GPIO_CFG_BIT1}, + {66, 66, 3, AS_GPIO_CFG_BIT3}, + {67, 67, 5, AS_GPIO_CFG_BIT3}, + {6, 6, 7, AS_GPIO_CFG_BIT3}, + {7, 7, 9, AS_GPIO_CFG_BIT3}, + {8, 8, 11, AS_GPIO_CFG_BIT3}, + {9, 9, 13, AS_GPIO_CFG_BIT3}, + {68, 68, 15, AS_GPIO_CFG_BIT3}, + {69, 69, 17, AS_GPIO_CFG_BIT3}, + {70, 70, 19, AS_GPIO_CFG_BIT3}, + {71, 71, 21, AS_GPIO_CFG_BIT3}, + {72, 72, 23, AS_GPIO_CFG_BIT3}, + {73, 73, 25, AS_GPIO_CFG_BIT3}, + {74, 74, 26, AS_GPIO_CFG_BIT3}, + {75, 75, 27, AS_GPIO_CFG_BIT3}, +}; +//this used to clr in gpio chare pin cfg3 +struct sharepin_as_gpio sharepin_cfg_gpio3[] = { + {10, 10, 0, AS_GPIO_CFG_BIT2}, + {11, 11, 2, AS_GPIO_CFG_BIT2}, + {12, 12, 4, AS_GPIO_CFG_BIT1}, + {13, 13, 5, AS_GPIO_CFG_BIT2}, + {76, 76, 7, AS_GPIO_CFG_BIT1}, + {14, 14, 8, AS_GPIO_CFG_BIT2}, + {15, 15, 10, AS_GPIO_CFG_BIT2}, + {16, 16, 12, AS_GPIO_CFG_BIT1}, + {17, 17, 13, AS_GPIO_CFG_BIT1}, + {18, 18, 14, AS_GPIO_CFG_BIT1}, + {77, 77, 15, AS_GPIO_CFG_BIT1}, + {19, 19, 16, AS_GPIO_CFG_BIT2}, + {20, 20, 18, AS_GPIO_CFG_BIT2}, + {21, 21, 20, AS_GPIO_CFG_BIT1}, + {22, 22, 21, AS_GPIO_CFG_BIT1}, + {23, 23, 22, AS_GPIO_CFG_BIT2}, + {24, 24, 24, AS_GPIO_CFG_BIT2}, + {78, 78, 26, AS_GPIO_CFG_BIT1}, +}; +//this used to clr in gpio chare pin cfg4 +struct sharepin_as_gpio sharepin_cfg_gpio4[] = { + {25, 25, 0, AS_GPIO_CFG_BIT1}, + {26, 26, 1, AS_GPIO_CFG_BIT1}, + {29, 29, 2, AS_GPIO_CFG_BIT2}, + {30, 30, 4, AS_GPIO_CFG_BIT2}, + {31, 31, 6, AS_GPIO_CFG_BIT1}, + {32, 32, 7, AS_GPIO_CFG_BIT1}, + {33, 33, 8, AS_GPIO_CFG_BIT1}, + {79, 79, 9, AS_GPIO_CFG_BIT1}, + {34, 34, 10, AS_GPIO_CFG_BIT2}, + {35, 36, 12, AS_GPIO_CFG_BIT2}, + {37, 38, 14, AS_GPIO_CFG_BIT2}, + {39, 39, 16, AS_GPIO_CFG_BIT2}, + {40, 40, 18, AS_GPIO_CFG_BIT2}, + {41, 41, 20, AS_GPIO_CFG_BIT1}, + {42, 42, 21, AS_GPIO_CFG_BIT1}, + {43, 43, 22, AS_GPIO_CFG_BIT2}, + {44, 44, 24, AS_GPIO_CFG_BIT2}, + {45, 46, 26, AS_GPIO_CFG_BIT2}, +}; + +struct sharepin_as_gpio sharepin_cfg_peripll_ctr_reg1[] = { + {59, 63, 29, AS_GPIO_CFG_BIT1}, +}; + +struct sharepin_as_gpio sharepin_cfg_analog_ctr_reg3[] = { + {83, 83, 25, AS_GPIO_CFG_BIT4}, // cdh:notes + {84, 84, 27, AS_GPIO_CFG_BIT4}, // cdh:notes +}; + + +#define INVALID_WK_BIT 0xff +struct t_gpio_wakeup_cfg gpio_wakeup_cfg[] = { + //gpio_start gpio_end start_bit + {AK_GPIO_0, AK_GPIO_2, 0}, + {AK_GPIO_4, AK_GPIO_11, 4}, + {AK_GPIO_13, AK_GPIO_15, 12}, + {AK_GPIO_19, AK_GPIO_20, 15}, + {AK_GPIO_23, AK_GPIO_24, 17}, + {AK_GPIO_49, AK_GPIO_49, 19}, + {AK_GPIO_52, AK_GPIO_55, 20}, + {AK_GPIO_57, AK_GPIO_63, 25}, +}; + + +unsigned int ak3910_invalid_gpio[] = { +// AK_GPIO_37, AK_GPIO_38, AK_GPIO_39, AK_GPIO_40, AK_GPIO_56, +// AK_GPIO_58, AK_GPIO_59, AK_GPIO_60, AK_GPIO_61, AK_GPIO_62, +// AK_GPIO_63, + AK_GPIO_3,AK_GPIO_56, +}; + +unsigned int ak3916_invalid_gpio[] = { +}; + +static unsigned char get_bit_by_pin_wk(unsigned char pin) +{ + int i, n; + n = ARRAY_SIZE(gpio_wakeup_cfg); + + for (i=0; i= gpio_wakeup_cfg[i].gpio_start && pin <= gpio_wakeup_cfg[i].gpio_end) + return gpio_wakeup_cfg[i].start_bit + (pin - gpio_wakeup_cfg[i].gpio_start); + } + + return INVALID_WK_BIT; +} + +/* when the specific bit is set to 0, the wake-up GPIO is rising triggered + * when the specific bit is set to 1, the wake-up GPIO is falling triggered + */ +void ak_gpio_wakeup_pol(unsigned int pin, unsigned char pol) +{ + unsigned char bit = get_bit_by_pin_wk(pin); + unsigned int val; + + if (bit == INVALID_WK_BIT) { + panic("this pin %u doesn't support wakeup function\n", pin); + return; + } + + val = REG32(AK_WGPIO_POLARITY); + val &= ~(1 << bit); + val |= (pol << bit); + REG32(AK_WGPIO_POLARITY) = val; +} +EXPORT_SYMBOL(ak_gpio_wakeup_pol); + +int ak_gpio_wakeup(unsigned int pin, unsigned char enable) +{ + unsigned char bit = get_bit_by_pin_wk(pin); + unsigned int val; + + if (bit == INVALID_WK_BIT) { + panic("this pin %d doesn't support wakeup function\n", pin); + return -1; + } + //clear wake gpio status + val = REG32(AK_WGPIO_CLEAR); + val |= (1 << bit); + REG32(AK_WGPIO_CLEAR) = val; + val &= ~(1 << bit); + REG32(AK_WGPIO_CLEAR) = val; + + val = REG32(AK_WGPIO_ENABLE); + if (enable == AK_WAKEUP_ENABLE) { + val |= (1 << bit); + } else if (enable == AK_WAKEUP_DISABLE) { + val &= ~(1 << bit); + } else + panic("wrong enable value in ak_gpio_wakeup\n"); + REG32(AK_WGPIO_ENABLE) = val; + + return 0; +} +EXPORT_SYMBOL(ak_gpio_wakeup); + +/* + * @brief set gpio pin group as specified module used + * @param[in] PinCfg enum data. the specified module + */ +void ak_group_config(T_GPIO_SHAREPIN_CFG mod_name) +{ + unsigned long i, flags, val = 0; + + if(ePIN_AS_GPIO == mod_name) { + //set all pin as gpio except uart0 + local_irq_save(flags); + __raw_writel(0xc000, AK_SHAREPIN_CON1); + __raw_writel(0xf, AK_SHAREPIN_CON2); + __raw_writel(0x0, AK_SHAREPIN_CON3); + local_irq_restore(flags); + return; + } + + for(i = 0; ; i++) { + if(ePIN_AS_DUMMY == share_cfg_module[i].func_module) + break; + + if(mod_name == share_cfg_module[i].func_module) { + //set pull attribute for module + g_ak39_setgroup_attribute(mod_name); + + local_irq_save(flags); + switch(share_cfg_module[i].share_config) { + case SHARE_CFG1: //set share pin cfg reg1 + val = __raw_readl(AK_SHAREPIN_CON1); + val &= ~(share_cfg_module[i].reg1_bit_mask); + val |= (share_cfg_module[i].reg1_bit_value); + __raw_writel(val, AK_SHAREPIN_CON1); + break; + + case SHARE_CFG2: //set share pin cfg reg2 + val = __raw_readl(AK_SHAREPIN_CON2); + val &= ~(share_cfg_module[i].reg2_bit_mask); + val |= (share_cfg_module[i].reg2_bit_value); + __raw_writel(val, AK_SHAREPIN_CON2); + break; + + case SHARE_CFG3: //set share pin cfg reg3 + val = __raw_readl(AK_SHAREPIN_CON3); + val &= ~(share_cfg_module[i].reg3_bit_mask); + val |= (share_cfg_module[i].reg3_bit_value); + __raw_writel(val, AK_SHAREPIN_CON3); + break; + case SHARE_CFG4: //set share pin cfg reg3 + val = __raw_readl(AK_SHAREPIN_CON4); + val &= ~(share_cfg_module[i].reg4_bit_mask); + val |= (share_cfg_module[i].reg4_bit_value); + __raw_writel(val, AK_SHAREPIN_CON4); + break; + + case SHARE_CFG12: + val = __raw_readl(AK_SHAREPIN_CON1); + val &= ~(share_cfg_module[i].reg1_bit_mask); + val |= (share_cfg_module[i].reg1_bit_value); + __raw_writel(val, AK_SHAREPIN_CON1); + + val = __raw_readl(AK_SHAREPIN_CON2); + val &= ~(share_cfg_module[i].reg2_bit_mask); + val |= (share_cfg_module[i].reg2_bit_value); + __raw_writel(val, AK_SHAREPIN_CON2); + break; + case SHARE_CFG13: + val = __raw_readl(AK_SHAREPIN_CON1); + val &= ~(share_cfg_module[i].reg1_bit_mask); + val |= (share_cfg_module[i].reg1_bit_value); + __raw_writel(val, AK_SHAREPIN_CON1); + + val = __raw_readl(AK_SHAREPIN_CON3); + val &= ~(share_cfg_module[i].reg3_bit_mask); + val |= (share_cfg_module[i].reg3_bit_value); + __raw_writel(val, AK_SHAREPIN_CON3); + break; + case SHARE_CFG14: + val = __raw_readl(AK_SHAREPIN_CON1); + val &= ~(share_cfg_module[i].reg1_bit_mask); + val |= (share_cfg_module[i].reg1_bit_value); + __raw_writel(val, AK_SHAREPIN_CON1); + + val = __raw_readl(AK_SHAREPIN_CON4); + val &= ~(share_cfg_module[i].reg4_bit_mask); + val |= (share_cfg_module[i].reg4_bit_value); + __raw_writel(val, AK_SHAREPIN_CON4); + break; + + + case SHARE_CFG23: + val = __raw_readl(AK_SHAREPIN_CON2); + val &= ~(share_cfg_module[i].reg2_bit_mask); + val |= (share_cfg_module[i].reg2_bit_value); + __raw_writel(val, AK_SHAREPIN_CON2); + + val = __raw_readl(AK_SHAREPIN_CON3); + val &= ~(share_cfg_module[i].reg3_bit_mask); + val |= (share_cfg_module[i].reg3_bit_value); + __raw_writel(val, AK_SHAREPIN_CON3); + break; + + case SHARE_CFG123: + val = __raw_readl(AK_SHAREPIN_CON1); + val &= ~(share_cfg_module[i].reg1_bit_mask); + val |= (share_cfg_module[i].reg1_bit_value); + __raw_writel(val, AK_SHAREPIN_CON1); + + val = __raw_readl(AK_SHAREPIN_CON2); + val &= ~(share_cfg_module[i].reg2_bit_mask); + val |= (share_cfg_module[i].reg2_bit_value); + __raw_writel(val, AK_SHAREPIN_CON2); + + val = __raw_readl(AK_SHAREPIN_CON3); + val &= ~(share_cfg_module[i].reg3_bit_mask); + val |= (share_cfg_module[i].reg3_bit_value); + __raw_writel(val, AK_SHAREPIN_CON3); + break; + + case SHARE_CFG134: + + val = __raw_readl(AK_SHAREPIN_CON1); + val &= ~(share_cfg_module[i].reg1_bit_mask); + val |= (share_cfg_module[i].reg1_bit_value); + __raw_writel(val, AK_SHAREPIN_CON1); + + val = __raw_readl(AK_SHAREPIN_CON3); + val &= ~(share_cfg_module[i].reg3_bit_mask); + val |= (share_cfg_module[i].reg3_bit_value); + __raw_writel(val, AK_SHAREPIN_CON3); + + val = __raw_readl(AK_SHAREPIN_CON4); + val &= ~(share_cfg_module[i].reg4_bit_mask); + val |= (share_cfg_module[i].reg4_bit_value); + __raw_writel(val, AK_SHAREPIN_CON4); + + break; + + default: + break; + } + local_irq_restore(flags); + return ; + } + } + return ; +} +EXPORT_SYMBOL(ak_group_config); + +static unsigned char gpio_assert_legal(unsigned long pin) +{ + int i, len; + unsigned int *gpio_legal; + + if ((pin < 0) || (pin > GPIO_UPLIMIT)) { + return AK_FALSE; + } + + len = ARRAY_SIZE(ak3916_invalid_gpio); + gpio_legal = ak3916_invalid_gpio; + + + for(i = 0; i < len; i++) { + if(gpio_legal[i] == pin) + return AK_FALSE; + } + return AK_TRUE; +} + + +/** + * @brief set gpio share pin as gpio + * @param pin [in] gpio pin ID + */ +int g_ak39_setpin_as_gpio(unsigned int pin) +{ + int i, bit = 0; + unsigned long flags; + + //check param + if(!gpio_assert_legal(pin)) { + panic("Error, Invalid gpio %u configuration!\n", pin); + return -1; + } + + //loop to find the correct bits to clr in share ping cfg1 + for(i = 0; i < ARRAY_SIZE(sharepin_cfg_gpio1); i++){ + if((pin >= sharepin_cfg_gpio1[i].gpio_start) + && (pin <= sharepin_cfg_gpio1[i].gpio_end)) + { + local_irq_save(flags); + + bit = sharepin_cfg_gpio1[i].index; + if (sharepin_cfg_gpio1[i].flag == AS_GPIO_CFG_BIT1) + REG32(AK_SHAREPIN_CON1) &= ~(1 << bit); + else if (sharepin_cfg_gpio1[i].flag == AS_GPIO_CFG_BIT2) + REG32(AK_SHAREPIN_CON1) &= ~(0x3 << bit); + else if (sharepin_cfg_gpio1[i].flag == AS_GPIO_CFG_BIT3) + { + REG32(AK_SHAREPIN_CON1) &= ~(0x3 << bit); + REG32(AK_SHAREPIN_CON1) |= (0x1 << bit); + } + local_irq_restore(flags); + return 0; + } + } + for(i = 0; i < ARRAY_SIZE(sharepin_cfg_gpio2); i++){ + if((pin >= sharepin_cfg_gpio2[i].gpio_start) + && (pin <= sharepin_cfg_gpio2[i].gpio_end)) + { + local_irq_save(flags); + + bit = sharepin_cfg_gpio2[i].index; + if (sharepin_cfg_gpio2[i].flag == AS_GPIO_CFG_BIT1) + REG32(AK_SHAREPIN_CON2) |= (0x1 << bit); + else if (sharepin_cfg_gpio2[i].flag == AS_GPIO_CFG_BIT2) + REG32(AK_SHAREPIN_CON2) &= ~(0x3 << bit); + else if (sharepin_cfg_gpio2[i].flag == AS_GPIO_CFG_BIT3) + { + REG32(AK_SHAREPIN_CON2) &= ~(0x3 << bit); + REG32(AK_SHAREPIN_CON2) |= (0x1 << bit); + } + local_irq_restore(flags); + + return 0; + } + + } + for(i = 0; i < ARRAY_SIZE(sharepin_cfg_gpio3); i++){ + if((pin >= sharepin_cfg_gpio3[i].gpio_start) + && (pin <= sharepin_cfg_gpio3[i].gpio_end)) + { + local_irq_save(flags); + + bit = sharepin_cfg_gpio3[i].index; + if (sharepin_cfg_gpio3[i].flag == AS_GPIO_CFG_BIT1) + REG32(AK_SHAREPIN_CON3) &= ~(1 << bit); + else if (sharepin_cfg_gpio3[i].flag == AS_GPIO_CFG_BIT2) + REG32(AK_SHAREPIN_CON3) &= ~(0x3 << bit); + else if (sharepin_cfg_gpio3[i].flag == AS_GPIO_CFG_BIT3) + { + REG32(AK_SHAREPIN_CON3) &= ~(0x3 << bit); + REG32(AK_SHAREPIN_CON3) |= (0x1 << bit); + } + local_irq_restore(flags); + return 0; + } + } + for(i = 0; i < ARRAY_SIZE(sharepin_cfg_gpio4); i++){ + if((pin >= sharepin_cfg_gpio4[i].gpio_start) + && (pin <= sharepin_cfg_gpio4[i].gpio_end)) + { + local_irq_save(flags); + + bit = sharepin_cfg_gpio4[i].index; + if (sharepin_cfg_gpio4[i].flag == AS_GPIO_CFG_BIT1) + REG32(AK_SHAREPIN_CON4) &= ~(1 << bit); + else if (sharepin_cfg_gpio4[i].flag == AS_GPIO_CFG_BIT2) + REG32(AK_SHAREPIN_CON4) &= ~(0x3 << bit); + + local_irq_restore(flags); + return 0; + } + } + + // cdh:add new gpio cfg + for(i = 0; i < ARRAY_SIZE(sharepin_cfg_peripll_ctr_reg1); i++){ + if((pin >= sharepin_cfg_peripll_ctr_reg1[i].gpio_start) + && (pin <= sharepin_cfg_peripll_ctr_reg1[i].gpio_end)) + { + local_irq_save(flags); + + bit = sharepin_cfg_peripll_ctr_reg1[i].index; + if (sharepin_cfg_peripll_ctr_reg1[i].flag == AS_GPIO_CFG_BIT1) + REG32(AK_PERIPLL_CTRL_REG1) &= ~(1 << bit); + + local_irq_restore(flags); + return 0; + } + } + + // cdh:add new gpio cfg + for(i = 0; i < ARRAY_SIZE(sharepin_cfg_analog_ctr_reg3); i++){ + if((pin >= sharepin_cfg_analog_ctr_reg3[i].gpio_start) + && (pin <= sharepin_cfg_analog_ctr_reg3[i].gpio_end)) + { + local_irq_save(flags); + + bit = sharepin_cfg_analog_ctr_reg3[i].index; + if (sharepin_cfg_analog_ctr_reg3[i].flag == AS_GPIO_CFG_BIT4) + REG32(AK_ALALOG_CTRL_REG3) |= (0x3 << bit); + + local_irq_restore(flags); + return 0; + } + } + + return 0; +} + +/* + enable: 1:enable pullup 0:disable pullup function + if the pin is attached pullup and pulldown resistor, then writing 1 to enable + pullup, 0 to enable pulldown, if you want to disable pullup/pulldown, then + disable the PE parameter +*/ +int g_ak39_gpio_pullup(unsigned int pin, unsigned char enable) +{ + void __iomem *base = AK_PPU_PPD_BASE(pin); + unsigned long flags; + int i; + + if(!gpio_assert_legal(pin)) { + panic("Error, Invalid gpio %u configuration!\n", pin); + return -1; + } + + for (i = 0; i < ARRAY_SIZE(pupd_cfg_info); i++) { + if (pin == pupd_cfg_info[i].pin) { + + if (pupd_cfg_info[i].pupd_type == PULLDOWN) + panic("Invalid GPIO[%d] pullup config.\n", pin); + + switch(pupd_cfg_info[i].pupd_cfg) { + case PUPD_CFG1: + base = AK_PPU_PPD1; + break; + case PUPD_CFG2: + base = AK_PPU_PPD2; + break; + case PUPD_CFG3: + base = AK_PPU_PPD3; + break; + case PUPD_CFG4: + base = AK_PPU_PPD4; + break; + + } + + local_irq_save(flags); + if (enable == AK_PULLUP_ENABLE) + REG32(base) &= ~(1 << pupd_cfg_info[i].index); + else if (enable == AK_PULLUP_DISABLE) + REG32(base) |= (1 << pupd_cfg_info[i].index); + local_irq_restore(flags); + break; + } + } + + return 0; +} + +//1.enable pulldown 0.disable pulldown + int g_ak39_gpio_pulldown(unsigned int pin, unsigned char enable) +{ + void __iomem *base = AK_PPU_PPD_BASE(pin); + unsigned long flags; + int i; + + if(!gpio_assert_legal(pin)) { + panic("Error, Invalid gpio %u configuration!\n", pin); + return -1; + } + + for (i = 0; i < ARRAY_SIZE(pupd_cfg_info); i++) { + if (pin == pupd_cfg_info[i].pin) { + + if (pupd_cfg_info[i].pupd_type == PULLUP) + panic("Invalid GPIO[%d] pulldown config.\n", pin); + + switch(pupd_cfg_info[i].pupd_cfg) { + case PUPD_CFG1: + base = AK_PPU_PPD1; + break; + case PUPD_CFG2: + base = AK_PPU_PPD2; + break; + case PUPD_CFG3: + base = AK_PPU_PPD3; + break; + case PUPD_CFG4: + base = AK_PPU_PPD4; + break; + } + + local_irq_save(flags); + if (enable == AK_PULLDOWN_ENABLE) + REG32(base) &= ~(1 << pupd_cfg_info[i].index); + else if (enable == AK_PULLDOWN_DISABLE) + REG32(base) |= (1 << pupd_cfg_info[i].index); + local_irq_restore(flags); + break; + } + } + + return 0; +} + +void g_ak39_setgroup_attribute(T_GPIO_SHAREPIN_CFG mod_name) +{ + unsigned long pin, start_pin = 0, end_pin = 0; + + switch (mod_name) { + case ePIN_AS_MCI: + start_pin = 31, end_pin = 36; + for (pin = start_pin; pin <= end_pin; pin++) + { + g_ak39_gpio_pullup(pin, AK_TRUE); + } + break; + + case ePIN_AS_MCI_8LINE: + start_pin = 31, end_pin = 40; + for (pin = start_pin; pin <= end_pin; pin++) + { + g_ak39_gpio_pullup(pin, AK_TRUE); + } + break; + + case ePIN_AS_SDIO: + #ifdef CONFIG_ARCH_SKY39EV2_AK3918E80PIN_COREBD + start_pin = 41, end_pin = 42; + for (pin = start_pin; pin <= end_pin; pin++) + { + g_ak39_gpio_pullup(pin, AK_TRUE); + } + g_ak39_gpio_pulldown(19, AK_FALSE); + g_ak39_gpio_pulldown(20, AK_FALSE); + g_ak39_gpio_pulldown(23, AK_FALSE); + g_ak39_gpio_pulldown(24, AK_FALSE); + + #else + start_pin = 41, end_pin = 46; + for (pin = start_pin; pin <= end_pin; pin++) + { + g_ak39_gpio_pullup(pin, AK_TRUE); + } + #endif + break; + + case ePIN_AS_SPI1: + /* spi_clk #spi_cs */ + start_pin = 25, end_pin = 26; + for (pin = start_pin; pin <= end_pin; pin++) + { + g_ak39_gpio_pullup(pin, AK_TRUE); + } + +#ifndef SPI_GPIO_37_TO_40 + start_pin = 33, end_pin = 36; +#else + start_pin = 37, end_pin = 40; +#endif + for (pin = start_pin; pin <= end_pin; pin++) + { + g_ak39_gpio_pullup(pin, AK_TRUE); + } + + break; + + case ePIN_AS_SPI2: + /* spi_clk #spi_cs */ + start_pin = 29, end_pin = 30; + for (pin = start_pin; pin <= end_pin; pin++) + { + g_ak39_gpio_pullup(pin, AK_TRUE); + } + + start_pin = 43, end_pin = 46; + for (pin = start_pin; pin <= end_pin; pin++) + { + g_ak39_gpio_pullup(pin, AK_TRUE); + } + break; + + case ePIN_AS_I2S: + start_pin = 52, end_pin = 53; + for (pin = start_pin; pin <= end_pin; pin++) + { + g_ak39_gpio_pullup(pin, AK_FALSE); + } + g_ak39_gpio_pulldown(AK_GPIO_54, AK_FALSE); + g_ak39_gpio_pulldown(AK_GPIO_55, AK_FALSE); +// g_ak39_gpio_pullup(AK_GPIO_55, AK_FALSE); + break; + + case ePIN_AS_UART1: + start_pin = 1, end_pin = 2; + for (pin = start_pin; pin <= end_pin; pin++) + { + g_ak39_gpio_pullup(pin, AK_TRUE); + } + break; + + case ePIN_AS_UART2: + start_pin = 4, end_pin = 5; + for (pin = start_pin; pin <= end_pin; pin++) + { + g_ak39_gpio_pullup(pin, AK_TRUE); + } + g_ak39_gpio_pulldown(AK_GPIO_6, AK_FALSE); + g_ak39_gpio_pulldown(AK_GPIO_7, AK_FALSE); + break; + + case ePIN_AS_I2C: + start_pin = 27, end_pin = 28; + for (pin = start_pin; pin <= end_pin; pin++) + { + g_ak39_gpio_pullup(pin, AK_FALSE); + } + break; + default: + break; + } +} + +/* + * configuration gpio pin + * 0: corresponding port is input mode + * 1: corresponding port is output mode + */ +int g_ak39_gpio_cfgpin(unsigned int pin, unsigned int to) +{ + void __iomem *base = AK_GPIO_DIR_BASE(pin); + unsigned int offset = ((pin) & 31); + unsigned long flags; + + if(!gpio_assert_legal(pin)) { + panic("Error, Invalid gpio %d configuration!\n", pin); + return -1; + } + +#if 0 //ygh 20170929 + /* gpio[49] can't set output mode for ak39xx*/ + if ((to == AK_GPIO_DIR_OUTPUT)&&(pin == AK_GPIO_49)) { + panic("Error, gpio %d isn't config output mode\n", pin); + return -1; + } +#endif + + local_irq_save(flags); + if (AK_GPIO_DIR_INPUT == to) + REG32(base) &= ~(1 << offset); + else if (AK_GPIO_DIR_OUTPUT == to) + REG32(base) |= (1 << offset); + local_irq_restore(flags); + + return 0; +} + +/* hold the real-time output value from GPIO[x] */ +int g_ak39_gpio_setpin(unsigned int pin, unsigned int to) +{ + void __iomem *base = AK_GPIO_OUT_BASE(pin); + unsigned int offset = ((pin) & 31); + unsigned long flags; + + if(!gpio_assert_legal(pin)) { + panic("Error, Invalid gpio %d configuration!\n", pin); + return -1; + } + +#if 0 //ygh 20170929 + /* gpio[49] can't set output level for ak39xx*/ + if (pin == AK_GPIO_49) { + panic("Error, gpio %d isn't config outpu level\n", pin); + return -1; + } +#endif + + local_irq_save(flags); + if (AK_GPIO_OUT_LOW == to) + REG32(base) &= ~(1 << offset); + else if (AK_GPIO_OUT_HIGH == to) + REG32(base) |= (1 << offset); + local_irq_restore(flags); + + return 0; +} + + +/* hold the real-time input value of GPIO[x] */ + int g_ak39_gpio_getpin(unsigned int pin) +{ + void __iomem *base = AK_GPIO_IN_BASE(pin); + unsigned int offset = ((pin) & 31); + + if(!gpio_assert_legal(pin)) { + panic("Error, read invalid gpio %d status!\n", pin); + return -1; + } + return ((__raw_readl(base) & (1 << offset)) == (1 << offset)); +} + + +/* + * enalbe/disable the interrupt function of GPIO[X] + * 1: interrupt function of corresponding port is enable + * 0: interrupt function of corresponding port is disable + */ +int g_ak39_gpio_inten(unsigned int pin, unsigned int enable) +{ + void __iomem *base = AK_GPIO_INTEN_BASE(pin); + unsigned int offset = ((pin) & 31); + unsigned long flags; + + if(!gpio_assert_legal(pin)) { + panic("Error, invalid gpio %d!\n", pin); + return -1; + } + + local_irq_save(flags); + if (AK_GPIO_INT_ENABLE == enable) + REG32(base) |= (1 << offset); + else if (AK_GPIO_INT_DISABLE == enable) + REG32(base) &= ~(1 << offset); + local_irq_restore(flags); + + return 0; +} + + +/* + * interrupt polarity selection + * 0: the input interrupt polarity of GPIO[X] is active high + * 1: the input interrupt polarity of GPIO[X] is active low + */ +int g_ak39_gpio_intpol(unsigned int pin, unsigned int level) +{ + void __iomem *base = AK_GPIO_INTPOL_BASE(pin); + unsigned int offset = ((pin) & 31); + unsigned long flags; + + if(!gpio_assert_legal(pin)) { + panic("Error, invalid gpio %d!\n", pin); + return -1; + } + + local_irq_save(flags); + if (AK_GPIO_INT_HIGHLEVEL == level) + REG32(base) &= ~(1 << offset); + else if (AK_GPIO_INT_LOWLEVEL == level) + REG32(base) |= (1 << offset); + local_irq_restore(flags); + + return 0; +} + +int reg_set_mutli_bit(void __iomem *reg, unsigned int value, int bit, int index) +{ + unsigned long con, flags; + + if (bit <= 0) + return -1; + + local_irq_save(flags); + + con = __raw_readl(reg); + con &= ~(((1 << bit) - 1) << index); + con |= (value << index); + __raw_writel(con, reg); + + local_irq_restore(flags); + return 0; +} +EXPORT_SYMBOL(reg_set_mutli_bit); + +int g_ak39_gpio_to_irq(unsigned int pin) +{ + if(!gpio_assert_legal(pin)) { + panic("Error, invalid gpio %d!\n", pin); + return -1; + } + return (IRQ_GPIO_0 + (pin - AK_GPIO_0)); +} + +int g_ak39_irq_to_gpio(unsigned int irq) +{ + return (AK_GPIO_0 + (irq - IRQ_GPIO_0)); +} + + +#if 0 +static const char *ak39_gpio_list[AK_GPIO_MAX]; + +int ak_gpio_request(unsigned long gpio, const char *label) +{ + if (gpio > GPIO_UPLIMIT) + return -EINVAL; + + if (ak39_gpio_list[gpio]) + return -EBUSY; + + if (label) + ak39_gpio_list[gpio] = label; + else + ak39_gpio_list[gpio] = "busy"; + + return 0; +} +EXPORT_SYMBOL(ak_gpio_request); + +void ak_gpio_free(unsigned long gpio) +{ + BUG_ON(!ak39_gpio_list[gpio]); + + ak39_gpio_list[gpio] = NULL; +} +EXPORT_SYMBOL(ak_gpio_free); + +#else + +int ak_gpio_request(unsigned long gpio, const char *label) +{ + return 0; +} +EXPORT_SYMBOL(ak_gpio_request); + +void ak_gpio_free(unsigned long gpio) +{ +} +EXPORT_SYMBOL(ak_gpio_free); + +#endif + diff --git a/arch/arm/mach-ak39/clock.c b/arch/arm/mach-ak39/clock.c new file mode 100644 index 00000000..dc3e5bce --- /dev/null +++ b/arch/arm/mach-ak39/clock.c @@ -0,0 +1,1268 @@ +/* + * linux/arch/arm/mach-ak39/clock.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + +#include +#include +#include +#include + + +/* clock information */ + +static LIST_HEAD(clocks); + +/* We originally used an mutex here, but some contexts (see resume) + * are calling functions such as clk_set_parent() with IRQs disabled + * causing an BUG to be triggered. + */ +DEFINE_SPINLOCK(clocks_lock); + +/* enable and disable calls for use with the clk struct */ + +static int clk_null_enable(struct clk *clk, int enable) +{ + return 0; +} + + +/** + * @brief: enable module clock. + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] *clk: struct clk, module clock info + */ +int clk_enable(struct clk *clk) +{ + unsigned long flags; + + if (IS_ERR(clk) || clk == NULL) + return -EINVAL; + + clk_enable(clk->parent); + + spin_lock_irqsave(&clocks_lock, flags); + + if ((clk->usage++) == 0) + (clk->enable)(clk, 1); + + spin_unlock_irqrestore(&clocks_lock, flags); + return 0; +} + +/** + * @brief: disable module clock. + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] *clk: struct clk, module clock info + */ +void clk_disable(struct clk *clk) +{ + unsigned long flags; + + if (IS_ERR(clk) || clk == NULL) + return; + + spin_lock_irqsave(&clocks_lock, flags); + + if ((--clk->usage) == 0) + (clk->enable)(clk, 0); + + spin_unlock_irqrestore(&clocks_lock, flags); + clk_disable(clk->parent); +} + +/** + * @brief: get module clock. + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] *clk: struct clk, module clock info + */ +unsigned long clk_get_rate(struct clk *clk) +{ + if (IS_ERR(clk)) + return 0; + + if (clk->rate != 0) + return clk->rate; + + if (clk->ops != NULL && clk->ops->get_rate != NULL) + return (clk->ops->get_rate)(clk); + + if (clk->parent != NULL) + return clk_get_rate(clk->parent); + + return clk->rate; +} + +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + if (!IS_ERR(clk) && clk->ops && clk->ops->round_rate) + return (clk->ops->round_rate)(clk, rate); + + return rate; +} + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + int ret; + + if (IS_ERR(clk)) + return -EINVAL; + + /* We do not default just do a clk->rate = rate as + * the clock may have been made this way by choice. + */ + + WARN_ON(clk->ops == NULL); + WARN_ON(clk->ops && clk->ops->set_rate == NULL); + + if (clk->ops == NULL || clk->ops->set_rate == NULL) + return -EINVAL; + + spin_lock(&clocks_lock); + ret = (clk->ops->set_rate)(clk, rate); + spin_unlock(&clocks_lock); + + return ret; +} + +/** + * @brief: get parent clock of module. + * + * @author: caolianming + * @date: 2014-01-06 + * @param [in] *clk: struct clk, module clock info + */ +struct clk *clk_get_parent(struct clk *clk) +{ + return clk->parent; +} + +/** + * @brief: set parent clock of module. + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] *clk: struct clk, module clock info + */ +int clk_set_parent(struct clk *clk, struct clk *parent) +{ + int ret = 0; + + if (IS_ERR(clk)) + return -EINVAL; + + spin_lock(&clocks_lock); + + if (clk->ops && clk->ops->set_parent) + ret = (clk->ops->set_parent)(clk, parent); + + spin_unlock(&clocks_lock); + + return ret; +} + +EXPORT_SYMBOL(clk_enable); +EXPORT_SYMBOL(clk_disable); +EXPORT_SYMBOL(clk_get_rate); +EXPORT_SYMBOL(clk_round_rate); +EXPORT_SYMBOL(clk_set_rate); +EXPORT_SYMBOL(clk_get_parent); +EXPORT_SYMBOL(clk_set_parent); +/* base clocks */ + +#if 0 +static u32 __power2(u32 x) +{ + u32 s = 1; + + while (x--) + s <<= 1; + + return s; +} +#endif + +static int clk_default_setrate(struct clk *clk, unsigned long rate) +{ + clk->rate = rate; + return 0; +} + + +/** + * @brief: setting module clock of drivers + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] *clk: struct clk, module clock info + * @param [in] *reg: clock register address of SOC chip + * @param [in] enable: enable bit + */ +static int inline ak39xx_gate(void __iomem *reg, + struct clk *clk, int enable) +{ + unsigned int ctrlbit = clk->ctrlbit; + u32 con; + + con = __raw_readl(reg); + + if (enable) + con &= ~ctrlbit; + else + con |= ctrlbit; + + __raw_writel(con, reg); + return 0; +} + + +/** + * @brief: setting 12M clock + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] *clk: struct clk, module clock info + * @param [in] enable: enable bit + */ +static int ak39xx_12M_enable(struct clk *clk, int enable) +{ + return ak39xx_gate(CLOCK_PERI_PLL_CTRL1, clk, enable); +} + + +/** + * @brief: setting 25M clock + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] *clk: struct clk, module clock info + * @param [in] enable: enable bit + */ +static int ak39xx_25M_enable(struct clk *clk, int enable) +{ + unsigned int ctrlbit = clk->ctrlbit; + u32 con; + + con = __raw_readl(CLOCK_PERI_PLL_CTRL1); + + if (enable) + con |= (ctrlbit|AK_CLKCON_CLK_25M_IN); + else + con &= ~(ctrlbit|AK_CLKCON_CLK_25M_IN); + + __raw_writel(con, CLOCK_PERI_PLL_CTRL1); + return 0; +} + +struct clk clk_xtal_12M = { + .name = "xtal_12M", + .usage = 0, + .rate = 12 * MHz, + .parent = NULL, + .enable = ak39xx_12M_enable, + .ctrlbit = AK_CLKCON_CLK_12M, +}; + +struct clk clk_xtal_25M = { + .name = "xtal_25M", + .rate = 25 * MHz, + .parent = NULL, + .enable = ak39xx_25M_enable, + .ctrlbit = AK_CLKCON_CLK_25M, +}; + +struct clk clk_xtal_32K = { + .name = "xtal_32K", + .id = -1, + .rate = 32768, + .parent = NULL, +}; + + +struct clk clk_cpu_pll = { + .name = "cpu_pll", + .usage = 0, + .rate = 0, + .parent = NULL, +}; + +struct clk clk_asic_pll = { + .name = "asic_pll", + .usage = 0, + .rate = 0, + .parent = NULL, +}; + +struct clk clk_peri_pll = { + .name = "peri_pll", + .usage = 0, + .rate = 0, + .parent = NULL, +}; + +struct clk clk_cpu = { + .name = "cpu_pll", + .usage = 0, + .rate = 0, + .parent = &clk_cpu_pll, +}; + +/* AHB clock maybe from cpu pll, also maybe vclk[0] */ +struct clk clk_ahb = { + .name = "ahb_clk", + .usage = 0, + .rate = 0, + .parent = &clk_cpu_pll, +}; + +struct clk clk_mem = { + .name = "mem_clk", + .usage = 0, + .rate = 0, + .parent = &clk_cpu_pll, +}; + + +struct clk clk_vclk = { + .name = "vclk", + .usage = 0, + .rate = 0, + .parent = &clk_asic_pll, +}; + +struct clk clk_asic = { + .name = "asic_clk", + .usage = 0, + .rate = 0, + .parent = &clk_vclk, +}; + + +/** + * @brief: setting opclk clock + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] *clk: struct clk, module clock info + * @param [in] enable: enable bit + */ +static int ak39xx_opclk_ctrl(struct clk *clk, int enable) +{ + return ak39xx_gate(CLOCK_PERI_PLL_CTRL2, clk, enable); +} + +struct clk clk_opclk = { + .name = "clk_opclk", + .usage = 0, + .parent = &clk_asic, + .enable = ak39xx_opclk_ctrl, + .ctrlbit = AK_CLKCON_ASICCLK_MAC, +}; + +/** + * @brief: setting mclk clock + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] *clk: struct clk, module clock info + * @param [in] enable: enable bit + */ +static int ak39xx_mclk_ctrl(struct clk *clk, int enable) +{ + return ak39xx_gate(CLOCK_GATE_CTRL1, clk, enable); +} + +/** + * @brief: setting video module clock + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] *clk: struct clk, module clock info + * @param [in] enable: enable bit + */ +static int ak39xx_video_ctrl(struct clk *clk, int enable) +{ + return ak39xx_gate(CLOCK_GATE_CTRL1, clk, enable); +} + + +/** + * @brief: setting camera module clock + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] *clk: struct clk, module clock info + * @param [in] enable: enable bit + */ +static int ak39xx_camera_ctrl(struct clk *clk, int enable) +{ + return ak39xx_gate(CLOCK_GATE_CTRL1, clk, enable); +} + +/** + * @brief: setting asic clock + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] *clk: struct clk, module clock info + * @param [in] enable: enable bit + */ +static int ak39xx_asicclk_ctrl(struct clk *clk, int enable) +{ + return ak39xx_gate(CLOCK_GATE_CTRL1, clk, enable); +} + + +/** + * @brief: setting clk dac module clock + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] *clk: struct clk, module clock info + * @param [in] enable: enable bit + */ +static int ak39xx_clk_dac(struct clk *clk, int enable) +{ + u32 con; + + ak39xx_gate(CLOCK_GATE_CTRL1, clk, enable); + + con = __raw_readl(CLOCK_ADC2_DAC_CTRL); + + if (enable) + con |= AK_CLKCON_CLK_DAC; + else + con &= ~AK_CLKCON_CLK_DAC; + + __raw_writel(con, CLOCK_ADC2_DAC_CTRL); + return 0; +} + + +/** + * @brief: setting hclk dac module clock + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] *clk: struct clk, module clock info + * @param [in] enable: enable bit + */ +static int ak39xx_hclk_dac(struct clk *clk, int enable) +{ + u32 con; + + ak39xx_gate(CLOCK_GATE_CTRL1, clk, enable); + + con = __raw_readl(CLOCK_ADC2_DAC_HS_CTRL); + + if (enable) + con |= AK_CLKCON_HCLK_DAC; + else + con &= ~AK_CLKCON_HCLK_DAC; + + __raw_writel(con, CLOCK_ADC2_DAC_HS_CTRL); + return 0; +} + +/** + * @brief: setting adc1 module clock + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] *clk: struct clk, module clock info + * @param [in] *reg: clock register address of SOC chip + * @param [in] enable: enable bit + */ +static int ak39xx_clk_adc1(struct clk *clk, int enable) +{ + u32 con; + + ak39xx_gate(CLOCK_GATE_CTRL1, clk, enable); + + con = __raw_readl(CLOCK_ADC2_DAC_CTRL); + + if (enable) + con |= AK_CLKCON_CLK_ADC1; + else + con &= ~AK_CLKCON_CLK_ADC1; + + __raw_writel(con, CLOCK_ADC2_DAC_CTRL); + return 0; +} + + +/** + * @brief: setting adc2 module clock + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] *clk: struct clk, module clock info + * @param [in] enable: enable bit + */ +static int ak39xx_clk_adc2(struct clk *clk, int enable) +{ + u32 con; + + ak39xx_gate(CLOCK_GATE_CTRL1, clk, enable); + + con = __raw_readl(CLOCK_ADC2_DAC_HS_CTRL); + + if (enable) + con |= AK_CLKCON_CLK_ADC2; + else + con &= ~AK_CLKCON_CLK_ADC2; + + __raw_writel(con, CLOCK_ADC2_DAC_HS_CTRL); + return 0; +} + +/** + * @brief: setting hclk module clock + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] *clk: struct clk, module clock info + * @param [in] enable: enable bit + */ +static int ak39xx_hclk_adc2(struct clk *clk, int enable) +{ + u32 con; + + ak39xx_gate(CLOCK_GATE_CTRL1, clk, enable); + + con = __raw_readl(CLOCK_ADC2_DAC_HS_CTRL); + + if (enable) + con |= AK_CLKCON_HCLK_ADC2; + else + con &= ~AK_CLKCON_HCLK_ADC2; + + __raw_writel(con, CLOCK_ADC2_DAC_HS_CTRL); + return 0; +} + +/** + * @brief: setting sensor module clock + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] *clk: struct clk, module clock info + * @param [in] enable: enable bit + */ +static int ak39xx_sensor_ctrl(struct clk *clk, int enable) +{ + unsigned int ctrlbit = clk->ctrlbit; + u32 con; + + con = __raw_readl(CLOCK_PERI_PLL_CTRL2); + + if (enable) + con |= ctrlbit; + else + con &= ~ctrlbit; + + __raw_writel(con, CLOCK_PERI_PLL_CTRL2); + return 0; +} + +static struct clk init_clocks[] = { + { + .name = "dram", + .usage = 0, + .parent = &clk_cpu, + .enable = ak39xx_mclk_ctrl, + .ctrlbit = AK_CLKCON_MCLK_DRAM, + }, { + .name = "video", + .usage = 0, + .parent = &clk_vclk, + .enable = ak39xx_video_ctrl, + .ctrlbit = AK_CLKCON_VCLK2_VIDEO, + }, { + .name = "camera", + .usage = 0, + .parent = &clk_vclk, + .enable = ak39xx_camera_ctrl, + .ctrlbit = AK_CLKCON_VCLK1_CAMERA, + }, { + .name = "usb-host", + .usage = 0, + .parent = &clk_asic, + .enable = ak39xx_asicclk_ctrl, + .ctrlbit = AK_CLKCON_ASICCLK_USB, + }, { + .name = "usb-slave", + .usage = 0, + .parent = &clk_asic, + .enable = ak39xx_asicclk_ctrl, + .ctrlbit = AK_CLKCON_ASICCLK_USB, + }, { + .name = "encryption", + .usage = 0, + .parent = &clk_asic, + .enable = ak39xx_asicclk_ctrl, + .ctrlbit = AK_CLKCON_ASICCLK_ENCRY, + }, { + .name = "mac", + .usage = 0, + .parent = &clk_opclk, + .enable = ak39xx_asicclk_ctrl, + .ctrlbit = AK_CLKCON_ASICCLK_MAC, + }, { + .name = "gpio", + .usage = 0, + .parent = &clk_asic, + .enable = ak39xx_asicclk_ctrl, + .ctrlbit = AK_CLKCON_ASICCLK_GPIO, + }, { + .name = "IrDA", + .usage = 0, + .parent = &clk_asic, + .enable = ak39xx_asicclk_ctrl, + .ctrlbit = AK_CLKCON_ASICCLK_IRDA, + }, { + .name = "i2c", + .usage = 0, + .parent = &clk_asic, + .enable = ak39xx_asicclk_ctrl, + .ctrlbit = AK_CLKCON_ASICCLK_I2C, + }, { + .name = "l2mem", + .usage = 0, + .parent = &clk_asic, + .enable = ak39xx_asicclk_ctrl, + .ctrlbit = AK_CLKCON_ASICCLK_L2MEM, + }, { + .name = "uart1", + .devname = "ak39xx-uart.1", + .usage = 0, + .parent = &clk_asic, + .enable = ak39xx_asicclk_ctrl, + .ctrlbit = AK_CLKCON_ASICCLK_UART2, + }, { + .name = "uart0", + .devname = "ak39xx-uart.0", + .usage = 0, + .parent = &clk_asic, + .enable = ak39xx_asicclk_ctrl, + .ctrlbit = AK_CLKCON_ASICCLK_UART1, + }, { + .name = "spi2", + .usage = 0, + .parent = &clk_asic, + .enable = ak39xx_asicclk_ctrl, + .ctrlbit = AK_CLKCON_ASICCLK_SPI2, + }, { + .name = "spi1", + .usage = 0, + .parent = &clk_asic, + .enable = ak39xx_asicclk_ctrl, + .ctrlbit = AK_CLKCON_ASICCLK_SPI1, + }, { + .name = "dac_clk", + .usage = 0, + .parent = &clk_asic, + .enable = ak39xx_clk_dac, + .ctrlbit = AK_CLKCON_ASICCLK_DAC, + }, { + .name = "dac_hclk", + .usage = 0, + .parent = &clk_asic, + .enable = ak39xx_hclk_dac, + .ctrlbit = AK_CLKCON_ASICCLK_DAC, + }, { + .name = "adc1_clk", + .usage = 0, + .parent = &clk_asic, + .enable = ak39xx_clk_adc1, + .ctrlbit = AK_CLKCON_ASICCLK_ADC, + }, { + .name = "adc2_clk", + .usage = 0, + .parent = &clk_asic, + .enable = ak39xx_clk_adc2, + .ctrlbit = AK_CLKCON_ASICCLK_ADC, + }, { + .name = "adc2_hclk", + .usage = 0, + .parent = &clk_asic, + .enable = ak39xx_hclk_adc2, + .ctrlbit = AK_CLKCON_ASICCLK_ADC, + + }, { + .name = "sdio", + .usage = 0, + .parent = &clk_asic, + .enable = ak39xx_asicclk_ctrl, + .ctrlbit = AK_CLKCON_ASICCLK_SDIO, + }, { + .name = "mci", + .usage = 0, + .parent = &clk_asic, + .enable = ak39xx_asicclk_ctrl, + .ctrlbit = AK_CLKCON_ASICCLK_MCI, + }, { + .name = "sensor", + .usage = 0, + .parent = &clk_peri_pll, + .enable = ak39xx_sensor_ctrl, + .ctrlbit = AK_CLKCON_SCLK_CIS, + }, { + .name = "mipi", + .usage = 0, + .parent = &clk_asic, + .enable = ak39xx_asicclk_ctrl, + .ctrlbit = AK_CLKCON_ASICCLK_MIPI, + }, +}; + + +/** + * ak39xx_register_clock() - register a clock + * @clk: The clock to register + * + * Add the specified clock to the list of clocks known by the system. + */ +int ak39xx_register_clock(struct clk *clk) +{ + if (clk->enable == NULL) + clk->enable = clk_null_enable; + + /* fill up the clk_lookup structure and register it*/ + clk->lookup.dev_id = clk->devname; + clk->lookup.con_id = clk->name; + clk->lookup.clk = clk; + clkdev_add(&clk->lookup); + + return 0; +} + +#if 0 +/** + * ak39xx_register_clocks() - register an array of clock pointers + * @clks: Pointer to an array of struct clk pointers + * @nr_clks: The number of clocks in the @clks array. + * + * Call ak39xx_register_clock() for all the clock pointers contained + * in the @clks list. Returns the number of failures. + */ +int ak39xx_register_clocks(struct clk **clks, int nr_clks) +{ + int fails = 0; + + for (; nr_clks > 0; nr_clks--, clks++) { + if (ak39xx_register_clock(*clks) < 0) { + struct clk *clk = *clks; + printk(KERN_ERR "%s: failed to register %p: %s\n", + __func__, clk, clk->name); + fails++; + } + } + + return fails; +} +#endif + +/** + * s3c_register_clocks() - register an array of clocks + * @clkp: Pointer to the first clock in the array. + * @nr_clks: Number of clocks to register. + * + * Call s3c24xx_register_clock() on the @clkp array given, printing an + * error if it fails to register the clock (unlikely). + */ +void __init ak39xx_register_clocks(struct clk *clkp, int nr_clks) +{ + int ret; + + for (; nr_clks > 0; nr_clks--, clkp++) { + ret = ak39xx_register_clock(clkp); + if (ret < 0) { + printk(KERN_ERR "Failed to register clock %s (%d)\n", + clkp->name, ret); + } + } +} + + +/* initialise the clock system +* cdh:check h3 ok +*/ +T_cpu_mode ak_get_cpu_mode(void) +{ + unsigned long regval = __raw_readl(CLOCK_CPU_PLL_CTRL); + + if (regval & AK39_CLK_CPU2X_MODE) + return CPU_MODE_CPU2X; + return CPU_MODE_NORMAL; +} +EXPORT_SYMBOL(ak_get_cpu_mode); + +// cdh:add for H3 +T_mem_mode ak_get_mem_mode(void) +{ + unsigned long regval = __raw_readl(CLOCK_CPU_PLL_CTRL); + + if (regval & AK39_CLK_MEM2X_MODE) + return MEM_MODE_CPU2X; + return MEM_MODE_NORMAL; +} +EXPORT_SYMBOL(ak_get_mem_mode); + + +// cdh:check h3 ok +bool ak_cpu_is_normal_mode(void) +{ + return ak_get_cpu_mode() == CPU_MODE_NORMAL; +} +EXPORT_SYMBOL(ak_cpu_is_normal_mode); + +// cdh:check h3 ok +bool ak_cpu_is_2x_mode(void) +{ + return ak_get_cpu_mode() == CPU_MODE_CPU2X; +} +EXPORT_SYMBOL(ak_cpu_is_2x_mode); + +// cdh:check h3 ok, h3 no cpu3x mode +#if 0 +bool ak_cpu_is_3x_mode(void) +{ + return ak_get_cpu_mode() == CPU_MODE_CPU3X; +} +EXPORT_SYMBOL(ak_cpu_is_3x_mode); +#endif + +// cdh:check h3 ok, add for H3 +bool ak_mem_is_2x_mode(void) +{ + return ak_get_mem_mode() == MEM_MODE_CPU2X; +} +EXPORT_SYMBOL(ak_mem_is_2x_mode); + + +// cdh:check h3 ok +unsigned long ak_get_cpu_pll_clk(void) +{ + unsigned long pll_m, pll_n, pll_od; + unsigned long cpu_pll_clk; + unsigned long regval; + + regval = __raw_readl(CLOCK_CPU_PLL_CTRL); + pll_od = (regval & (0x3 << 12)) >> 12; + pll_n = (regval & (0xf << 8)) >> 8; + pll_m = regval & 0xff; + + //cpu_pll_clk = 12 * pll_m /(pll_n * __power2(pll_od)); // clk unit: MHz + cpu_pll_clk = 12 * pll_m /(pll_n * (1 << pll_od)); // clk unit: MHz +#if 0 //ygh 20170929 + if ((pll_od >= 1) && ((pll_n >= 2) && (pll_n <= 6)) + && ((pll_m >= 84) && (pll_m <= 254))) +#else + //printk(KERN_ERR "ygh %s od:%lu, n:%lu, m:%lu, cpu_pll:%lu\n", __func__, pll_od, pll_n, pll_m, cpu_pll_clk); + if ((pll_od >= 1) && ((pll_n >= 1) && (pll_n <= 12)) + && ((pll_m >= 84) && (pll_m <= 254))) +#endif + return cpu_pll_clk * MHz; + + panic("cpu pll clk: %ld(Mhz) is unusable\n", cpu_pll_clk); +} +EXPORT_SYMBOL(ak_get_cpu_pll_clk); + +// cdh:check h3 ok +unsigned long ak_get_asic_pll_clk(void) +{ + unsigned long pll_m, pll_n, pll_od; + unsigned long asic_pll_clk; + unsigned long regval; + + regval = __raw_readl(CLOCK_ASIC_PLL_CTRL); + //printk("\n\n\nASIC_PLL_CTRL:0x%X.\n\n\n", regval); + pll_od = (regval & (0x3 << 12)) >> 12; + pll_n = (regval & (0xf << 8)) >> 8; + pll_m = regval & 0xfe; + + asic_pll_clk = (12 * pll_m)/(pll_n * (1 << pll_od)); // clk unit: MHz + +#if 0 //ygh 20170929 + if ((pll_od >= 1) && ((pll_n >= 2) && (pll_n <= 6)) + && ((pll_m >= 84) && (pll_m <= 254))) +#else + //printk(KERN_ERR "ygh %s od:%lu, n:%lu, m:%lu, cpu_pll:%lu\n", __func__, pll_od, pll_n, pll_m, asic_pll_clk); + if ((pll_od >= 1) && ((pll_n >= 1) && (pll_n <= 12)) + && ((pll_m >= 84) && (pll_m <= 254))) +#endif + return asic_pll_clk * MHz; + + panic("asic pll clk: %ld(Mhz) is unusable\n", asic_pll_clk); +} +EXPORT_SYMBOL(ak_get_asic_pll_clk); + +// cdh:check h3 ok +unsigned long ak_get_peri_pll_clk(void) +{ + unsigned long pll_m, pll_n, pll_od; + unsigned long peri_pll_clk; + unsigned long regval; + + regval = __raw_readl(CLOCK_PERI_PLL_CTRL1); + pll_od = (regval & (0x3 << 12)) >> 12; + pll_n = (regval & (0xf << 8)) >> 8; + pll_m = regval & 0xff; + + peri_pll_clk = (12 * pll_m)/(pll_n * (1 << pll_od)); // clk unit: MHz +#if 0 //ygh 20170929 + if ((pll_od >= 1) && ((pll_n >= 2) && (pll_n <= 6)) + && ((pll_m >= 84) && (pll_m <= 254))) +#else + //printk(KERN_ERR "ygh %s od:%lu, n:%lu, m:%lu, cpu_pll:%lu\n", __func__, pll_od, pll_n, pll_m, peri_pll_clk); + if ((pll_od >= 1) && ((pll_n >= 1) && (pll_n <= 12)) + && ((pll_m >= 84) && (pll_m <= 254))) +#endif + + return peri_pll_clk * MHz; + + panic("peri pll clk: %ld(Mhz) is unusable\n", peri_pll_clk); +} +EXPORT_SYMBOL(ak_get_peri_pll_clk); + +int ak_set_cis_pclk(int pclk_mhz) +{ + int div; + int timeout = 0xffff; + unsigned long regval; + + //cis_pclk dissable + regval = __raw_readl(CLOCK_PERI_PLL_CTRL2); + regval &= (~(0x1 << 28)); + __raw_writel(regval, CLOCK_PERI_PLL_CTRL2); + + //wait the state finish + do { + regval = __raw_readl(CLOCK_PERI_PLL_CTRL2); + } while(((regval & (1<<29)) != 0) && (timeout-- > 0)); + + //cis_pclk div cfg + div = ak_get_peri_pll_clk() / MHz / pclk_mhz; + regval = __raw_readl(CLOCK_PERI_PLL_CTRL2); + regval &= ~(0x3f << 20); + regval |= (div - 1) << 20; + __raw_writel(regval, CLOCK_PERI_PLL_CTRL2); + //printk(KERN_ERR "%s peri_pll:%ld, div:%d, pclk_mhz:%d\n", __func__, ak_get_peri_pll_clk(), div, pclk_mhz); + + + //cis_pclk enable div valid + regval = __raw_readl(CLOCK_PERI_PLL_CTRL2); + regval |= (0x1<<29); + __raw_writel(regval, CLOCK_PERI_PLL_CTRL2); + + //wait the state finish + timeout = 0xffff; + do { + regval = __raw_readl(CLOCK_PERI_PLL_CTRL2); + } while(((regval & (1<<29)) != 0) && (timeout-- > 0)); + + //cis_pclk enable + regval = __raw_readl(CLOCK_PERI_PLL_CTRL2); + regval |= 0x1 << 28; + __raw_writel(regval, CLOCK_PERI_PLL_CTRL2); + + //wait the state finish + timeout = 0xffff; + do { + regval = __raw_readl(CLOCK_PERI_PLL_CTRL2); + } while(((regval & (1<<29)) != 0) && (timeout-- > 0)); + + return 0; +} +EXPORT_SYMBOL(ak_set_cis_pclk); + +int ak_set_cis_pclk_sel(int is_internal) +{ + unsigned long con; + + con = __raw_readl(CLOCK_PERI_PLL_CTRL1); + if (is_internal) + con |= 0x1 << 24; + else + con &= ~(0x1 << 24); + __raw_writel(con, CLOCK_PERI_PLL_CTRL1); + + return 0; +} +EXPORT_SYMBOL(ak_set_cis_pclk_sel); + +int ak_set_cis_mipi_dvp_sel(int is_mipi) +{ + unsigned long con; + + con = __raw_readl(CLOCK_PERI_PLL_CTRL1); + if (is_mipi) + con |= 0x1 << 26; + else + con &= ~(0x1 << 26); + __raw_writel(con, CLOCK_PERI_PLL_CTRL1); + + return 0; +} +EXPORT_SYMBOL(ak_set_cis_mipi_dvp_sel); + +int ak_set_mipi_byte_rst(int is_rst) +{ + unsigned long con; + + con = __raw_readl(CLOCK_PERI_PLL_CTRL1); + if (is_rst) + con |= 0x1 << 27; + else + con &= ~(0x1 << 27); + __raw_writel(con, CLOCK_PERI_PLL_CTRL1); + + return 0; +} +EXPORT_SYMBOL(ak_set_mipi_byte_rst); + +// cdh:check h3 ok +static unsigned long ak_get_cpu_hclk(void) +{ +#if 0 //ygh 20170930 + unsigned long regval; + unsigned long div; + + regval = __raw_readl(CLOCK_CPU_PLL_CTRL); + div = (regval & (0x7 << 17)) >> 17; + + if (div == 0) + return ak_get_cpu_pll_clk() >> 1; + + return ak_get_cpu_pll_clk() >> div; +#else + return ak_get_cpu_pll_clk() >> 1; // cdh:cpu_hclk default equal cpu_pll_clk/2 +#endif +} + +#if 0 +// cdh:check h3 ok +static unsigned long ak_get_cpu_dclk(void) +{ +#if 0 //ygh 20170930 + + unsigned long regval; + unsigned long div; + + regval = __raw_readl(CLOCK_CPU_PLL_CTRL); + div = (regval & (0x7 << 20)) >> 20; + + if (div == 0) + return ak_get_cpu_pll_clk() >> 1; + + return ak_get_cpu_pll_clk() >> div; +#else + return ak_get_cpu_pll_clk() >> 1; // cdh:cpu_dclk default equal cpu_pll_clk/2 +#endif +} +#endif + +// cdh:check h3 ok +unsigned long ak_get_cpu_clk(void) +{ + if (ak_cpu_is_normal_mode()) + return ak_get_cpu_hclk(); + + return ak_get_cpu_pll_clk(); +} +EXPORT_SYMBOL(ak_get_cpu_clk); + +// cdh:check h3 ok +unsigned long ak_get_ahb_clk(void) +{ +#if 0 // cdh:check ok, h3 no cpu3x mode + if (ak_cpu_is_3x_mode()) + return ak_get_cpu_pll_clk()/3; +#endif + + return ak_get_cpu_hclk(); +} +EXPORT_SYMBOL(ak_get_ahb_clk); + +// cdh:check h3 ok +unsigned long ak_get_mem_clk(void) +{ + if (ak_mem_is_2x_mode()) + return ak_get_cpu_pll_clk(); + + return ak_get_cpu_hclk(); +} +EXPORT_SYMBOL(ak_get_mem_clk); + +// cdh:check h3 ok +unsigned long ak_get_vclk(void) +{ + unsigned long regval; + unsigned long div; + + regval = __raw_readl(CLOCK_ASIC_PLL_CTRL); + div = (regval & (0x7 << 17)) >> 17; + if (div == 0) + return ak_get_asic_pll_clk() >> 1; + + return ak_get_asic_pll_clk() >> div; +} +EXPORT_SYMBOL(ak_get_vclk); + +// cdh:check h3 ok +unsigned long ak_get_asic_clk(void) +{ + unsigned long regval; + unsigned long div; + + regval = __raw_readl(CLOCK_ASIC_PLL_CTRL); + div = regval & (1 << 24); + if (div == 0) + return ak_get_vclk(); + + return ak_get_vclk() >> 1; +} +EXPORT_SYMBOL(ak_get_asic_clk); + +// cdh:check h3 ok +static unsigned long get_asic_clk(void) +{ + unsigned long asicclk; + +#if defined(CONFIG_ASIC_CLK_120MHZ) + asicclk = 120 * MHz; +#else +#error "Please config ASIC CLK!\n" +#endif + + return asicclk; +} + +// cdh:check h3 ok +void aisc_freq_set(void) +{ + unsigned long asicclk, asicclk_pll; + unsigned long div_od, div_n, div_m; + unsigned long uartdiv; + + asicclk = get_asic_clk(); // cdh:kernel config decided asic clk size,now fixed 120MHz + div_od = 1;//2; + div_n = 3;//2; + if ((div_n < 2) || (div_n > 6) + || (div_od < 1) || (div_od > 3)) + panic("Asic frequency parameter Error"); + +#if 0 + if (asicclk > 100*MHz) { + /* asic_pll = 2vclk = 2asic */ + asicclk_pll = (asicclk/MHz) << 1; + div_m = (asicclk_pll*(div_n * (1 << div_od)))/12; + uartdiv = asicclk/115200-1; + /* set asic frequency */ + REG32(AK_VA_SYSCTRL + 0x08) = ((1 << 23)|(1 <<17)|(div_od << 12)|(div_n << 8)|(div_m)); + } else { +#endif + /* asic_pll = 480M=2vclk = 240M=4asic=120M */ + asicclk_pll = (asicclk/MHz) << 2; + div_m = (asicclk_pll*(div_n * (1 << div_od)))/12; + uartdiv = asicclk/115200-1; + /* set asic frequency */ + REG32(CLOCK_ASIC_PLL_CTRL) = ((1 << 24)|(1 << 23)|(1 <<17)|(div_od << 12)|(div_n << 8)|(div_m)); + //} + + /* enable asic freq change valid */ + REG32(CLOCK_CPU_PLL_CTRL) |= (1 << 28); + /* set uart baudrate */ + REG32(AK_VA_UART + 0x0) = ((3<<28)|(3<<21)|(uartdiv)); +} +EXPORT_SYMBOL(aisc_freq_set); + +/***** end extern call for comm drivers compatible *****/ + +/* initalise all the clocks */ +static int __init ak39xx_init_clocks(void) +{ + clk_default_setrate(&clk_cpu_pll, ak_get_cpu_pll_clk()); + clk_default_setrate(&clk_asic_pll, ak_get_asic_pll_clk()); + clk_default_setrate(&clk_peri_pll, ak_get_peri_pll_clk()); + + clk_default_setrate(&clk_cpu, ak_get_cpu_clk()); + clk_default_setrate(&clk_ahb, ak_get_ahb_clk()); + clk_default_setrate(&clk_mem, ak_get_mem_clk()); + clk_default_setrate(&clk_vclk, ak_get_vclk()); + clk_default_setrate(&clk_asic, ak_get_asic_clk()); + + printk("AK39 clocks: CPU %ldMHz, MEM %ldMHz, ASIC %ldMHz\n", + clk_cpu.rate/MHz, clk_mem.rate/MHz, clk_asic.rate/MHz); + + /* register clocks */ + if (ak39xx_register_clock(&clk_xtal_12M) < 0) + printk(KERN_ERR "failed to register 12M xtal\n"); + + if (ak39xx_register_clock(&clk_xtal_25M) < 0) + printk(KERN_ERR "failed to register 25M xtal\n"); + + if (ak39xx_register_clock(&clk_xtal_32K) < 0) + printk(KERN_ERR "failed to register 32K xtal\n"); + + if (ak39xx_register_clock(&clk_cpu_pll) < 0) + printk(KERN_ERR "failed to register cpu pll clk\n"); + + if (ak39xx_register_clock(&clk_asic_pll) < 0) + printk(KERN_ERR "failed to register asic pll clk\n"); + + if (ak39xx_register_clock(&clk_peri_pll) < 0) + printk(KERN_ERR "failed to register peri pll clk\n"); + + if (ak39xx_register_clock(&clk_cpu) < 0) + printk(KERN_ERR "failed to register cpu clk\n"); + + if (ak39xx_register_clock(&clk_ahb) < 0) + printk(KERN_ERR "failed to register AHB clk\n"); + + if (ak39xx_register_clock(&clk_mem) < 0) + printk(KERN_ERR "failed to register memory clk\n"); + + if (ak39xx_register_clock(&clk_vclk) < 0) + printk(KERN_ERR "failed to register vclk\n"); + + if (ak39xx_register_clock(&clk_asic) < 0) + printk(KERN_ERR "failed to register asic clk\n"); + + if (ak39xx_register_clock(&clk_opclk) < 0) + printk(KERN_ERR "failed to register opclk\n"); + + ak39xx_register_clocks(init_clocks, ARRAY_SIZE(init_clocks)); + +#if 0 + printk("ak39xx_init_clocks: clk gate reg=0x%p\n", REG32(CLOCK_GATE_CTRL1)); + /* + * Enable L2buf clock by default, Disable all other clocks. + * uart0 clock has been open in uncompreess.h + */ + int i; + for (i = 0; i < ARRAY_SIZE(init_clocks); i++) { + if (strcmp(init_clocks[i].name, "uart0") == 0) { + clk_enable(&init_clocks[i]); + } + } +#endif + + return 0; +} + +arch_initcall(ak39xx_init_clocks); + diff --git a/arch/arm/mach-ak39/cpu.c b/arch/arm/mach-ak39/cpu.c new file mode 100644 index 00000000..a9c2d374 --- /dev/null +++ b/arch/arm/mach-ak39/cpu.c @@ -0,0 +1,71 @@ +/* + * init cpu freq, clock + * + * report cpu id + */ +#include +#include +#include +#include + +#include +#include +#include + +#define AK_CPU_ID (AK_VA_SYSCTRL + 0x00) + +#if defined(CONFIG_CPU_AK3910) +#define AKCPU_VALUE 0x20140100 // cdh:modify and check ok +#define AKCPU_TYPE "AK3910" +#elif defined(CONFIG_CPU_AK3916) +#define AKCPU_VALUE 0x20160100 // cdh:modify and check ok +#define AKCPU_TYPE "AK3916" +#elif defined(CONFIG_CPU_AK3918) +#define AKCPU_VALUE 0x20160100 // cdh:modify and check ok +#define AKCPU_TYPE "AK3918" +#elif defined(CONFIG_CPU_AK3919) +#define AKCPU_VALUE 0x20160100 // cdh:modify and check ok +#define AKCPU_TYPE "AK3919" +#else +#error AK39xx Board NOT supported +#endif + +#define IODESC_ENT(x) \ +{ \ + .virtual = (unsigned long)AK_VA_##x, \ + .pfn = __phys_to_pfn(AK_PA_##x), \ + .length = AK_SZ_##x, \ + .type = MT_DEVICE \ +} + +static struct map_desc ak39_iodesc[] __initdata = { + IODESC_ENT(SYSCTRL), + IODESC_ENT(CAMERA), + IODESC_ENT(VENCODE), + IODESC_ENT(SUBCTRL), + IODESC_ENT(MAC), + IODESC_ENT(REGRAM), + IODESC_ENT(L2MEM), + IODESC_ENT(MIPI), + IODESC_ENT(VIDEO_BUFF), +}; + +void __init ak39_map_io(void) +{ + unsigned long regval = 0x0; + + /* initialise the io descriptors we need for initialisation */ + iotable_init(ak39_iodesc, ARRAY_SIZE(ak39_iodesc)); + + regval = __raw_readl(AK_CPU_ID); + if (regval == AKCPU_VALUE) + printk("ANYKA CPU %s (ID 0x%lx)\n", AKCPU_TYPE, regval); + else + printk("Unknown ANYKA CPU ID: 0x%lx\n", regval); + + /* need to change asic freq is here, Because higher asic freq was affected usb function, + * I don't know essential reason of the problem + */ + aisc_freq_set(); +} + diff --git a/arch/arm/mach-ak39/cpu.h b/arch/arm/mach-ak39/cpu.h new file mode 100644 index 00000000..1968ab0f --- /dev/null +++ b/arch/arm/mach-ak39/cpu.h @@ -0,0 +1,4 @@ + +extern struct sys_timer ak39_timer; + +void __init ak39_map_io(void); diff --git a/arch/arm/mach-ak39/cpufreq.c b/arch/arm/mach-ak39/cpufreq.c new file mode 100644 index 00000000..95849dc5 --- /dev/null +++ b/arch/arm/mach-ak39/cpufreq.c @@ -0,0 +1,773 @@ +/* arch/arm/mach-ak39/cpufreq.c + * + * AK98 CPUfreq Support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#include +#include + +#define SIZE_1K 0x00000400 /* 1K */ +extern atomic_t suspend_flags; + +static struct cpufreq_freqs freqs; +static T_OPERATION_MODE current_mode; +static int previous_mode_flag; +static int cpufreq_in_ddr2 = 1; +static unsigned int clkdiv = 1000000; + +typedef enum { + CPU_LOW_MODE = 0, + CPU_NORMAL_MODE, + CPU_NORMAL_2XMODE, +} T_cpufreq_mode; + +#if 1 +struct cpufreq_mode_clkdiv cpufreq_divs[] = { + //mode_name pll_sel clk168_div cpu_div mem_div asic_div low_clock is_3x (cpu,mem,asic[MHz]) + {LOW_MODE_CLOCK_0, 0x2d, 0, 0, 2, 4, 1, 0}, /* (90,180,90) */ + {LOW_MODE_CLOCK_1, 0x2d, 0, 0, 2, 4, 1, 0}, /* (90,180,90) */ + {LOW_MODE_CLOCK_2, 0x2d, 0, 0, 2, 4, 1, 0}, /* (90,180,90) */ + {LOW_MODE_CLOCK_3, 0x2d, 0, 0, 2, 4, 1, 0}, /* (90,180,90) */ + {LOW_MODE_CLOCK_4, 0x2d, 0, 0, 2, 4, 1, 0}, /* (90,180,90) */ + {LOW_MODE_CLOCK_5, 0x2d, 0, 0, 2, 4, 1, 0}, /* (90,180,90) */ + {LOW_MODE_CLOCK_6, 0x2d, 0, 0, 2, 4, 1, 0}, /* (90,180,90) */ + {LOW_MODE_CLOCK_7, 0x2d, 0, 0, 2, 4, 1, 0}, /* (90,180,90) */ + + {NORMAL_MODE_CLOCK_0, 0x2d, 0, 0, 2, 4, 0, 0}, /* (180,180,90) */ + {NORMAL_MODE_CLOCK_1, 0x2d, 0, 0, 2, 4, 0, 0}, /* (180,180,90) */ + {NORMAL_MODE_CLOCK_2, 0x2d, 0, 0, 2, 4, 0, 0}, /* (180,180,90) */ + {NORMAL_MODE_CLOCK_3, 0x2d, 0, 0, 2, 4, 0, 0}, /* (180,180,90) */ + {NORMAL_MODE_CLOCK_4, 0x2d, 0, 0, 2, 4, 0, 0}, /* (180,180,90) */ + {NORMAL_MODE_CLOCK_5, 0x2d, 0, 1, 2, 4, 0, 0}, /* (360,180,90) */ + {NORMAL_MODE_CLOCK_6, 0x2d, 0, 1, 2, 4, 0, 0}, /* (360,180,90) */ + {NORMAL_MODE_CLOCK_7, 0x2d, 0, 1, 2, 4, 0, 0}, /* (360,180,90) */ + + {VIDEO_MODE_CLOCK_0, 0x2d, 0, 1, 2, 4, 0, 0}, /* (360,180,90) */ + {VIDEO_MODE_CLOCK_1, 0x2d, 0, 1, 2, 4, 0, 0}, /* (360,180,90) */ + {VIDEO_MODE_CLOCK_2, 0x2d, 0, 1, 2, 4, 0, 0}, /* (360,180,90) */ + {VIDEO_MODE_CLOCK_3, 0x2d, 0, 1, 2, 4, 0, 0}, /* (360,180,90) */ + {VIDEO_MODE_CLOCK_4, 0x2d, 0, 1, 2, 4, 0, 0}, /* (360,180,90) */ + {VIDEO_MODE_CLOCK_5, 0x2d, 0, 1, 2, 4, 0, 0}, /* (360,180,90) */ + {VIDEO_MODE_CLOCK_6, 0x2d, 0, 1, 2, 4, 0, 0}, /* (360,180,90) */ + {VIDEO_MODE_CLOCK_7, 0x2d, 0, 1, 2, 4, 0, 0}, /* (360,180,90) */ + {LOWBATTERY_MODE_CLOCK, 0x2d, 0, 0, 2, 4, 0, 0}, /* (180,180,90) */ + {USBPLUG_MODE_CLOCK, 0x2d, 0, 1, 2, 4, 0, 0}, /* (360,180,90) */ + +}; +#else +struct cpufreq_mode_clkdiv cpufreq_divs[] = { + //mode_name pll_sel clk168_div cpu_div mem_div asic_div low_clock is_3x (cpu,mem,asic[MHz]) + {LOW_MODE_CLOCK_0, 0x14, 0, 0, 2, 4, 1, 0}, /* (65,130,65) */ + {LOW_MODE_CLOCK_1, 0x14, 0, 0, 2, 4, 0, 0}, /* (65,130,65) */ + {LOW_MODE_CLOCK_2, 0x14, 0, 1, 2, 4, 0, 0}, /* (130,130,65) */ + {LOW_MODE_CLOCK_3, 0x14, 0, 0, 2, 2, 0, 0}, /* (130,130,65) */ + {LOW_MODE_CLOCK_4, 0x28, 0, 0, 2, 4, 0, 0}, /* (170,170,85) */ + {LOW_MODE_CLOCK_5, 0x2D, 0, 0, 2, 4, 0, 0}, /* (180,180,90) */ + {LOW_MODE_CLOCK_6, 0x37, 0, 0, 2, 4, 0, 0}, /* (200,200,100) */ + {LOW_MODE_CLOCK_7, 0x37, 0, 1, 2, 4, 0, 0}, /* (400,200,100) */ + + {NORMAL_MODE_CLOCK_0, 0x37, 0, 0, 2, 4, 0, 0}, /* (200,200,100) */ + {NORMAL_MODE_CLOCK_1, 0x37, 0, 0, 2, 4, 0, 0}, /* (200,200,100) */ + {NORMAL_MODE_CLOCK_2, 0x37, 0, 0, 2, 4, 0, 0}, /* (200,200,100) */ + {NORMAL_MODE_CLOCK_3, 0x37, 0, 0, 2, 4, 0, 0}, /* (200,200,100) */ + {NORMAL_MODE_CLOCK_4, 0x37, 0, 0, 2, 4, 0, 0}, /* (200,200,100) */ + {NORMAL_MODE_CLOCK_5, 0x37, 0, 1, 2, 4, 0, 0}, /* (400,200,100) */ + {NORMAL_MODE_CLOCK_6, 0x37, 0, 1, 2, 4, 0, 0}, /* (400,200,100) */ + {NORMAL_MODE_CLOCK_7, 0x37, 0, 1, 2, 4, 0, 0}, /* (400,200,100) */ + + {VIDEO_MODE_CLOCK_0, 0x36, 0, 1, 0, 0, 0, 1}, /* (396,132,132) */ + {VIDEO_MODE_CLOCK_1, 0x36, 0, 1, 0, 0, 0, 1}, /* (396,132,132) */ + {VIDEO_MODE_CLOCK_2, 0x36, 0, 1, 0, 0, 0, 1}, /* (396,132,132) */ + {VIDEO_MODE_CLOCK_3, 0x36, 0, 1, 0, 0, 0, 1}, /* (396,132,132) */ + {VIDEO_MODE_CLOCK_4, 0x36, 0, 1, 0, 0, 0, 1}, /* (396,132,132) */ + {VIDEO_MODE_CLOCK_5, 0x36, 0, 1, 0, 0, 0, 1}, /* (396,132,132) */ + {VIDEO_MODE_CLOCK_6, 0x36, 0, 1, 0, 0, 0, 1}, /* (396,132,132) */ + {VIDEO_MODE_CLOCK_7, 0x36, 0, 1, 0, 0, 0, 1}, /* (396,132,132) */ +}; +#endif + +static T_OPERATION_MODE prev_suspend_mode; +#define SUSPEND_NORMAL_MODE NORMAL_MODE_CLOCK_2 + +static int get_cpufreq_mode_clkdiv(T_OPERATION_MODE mode, struct cpufreq_mode_clkdiv *clkdiv) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cpufreq_divs); i++) { + if (cpufreq_divs[i].mode_name == mode) { + *clkdiv = cpufreq_divs[i]; + return 0; + } + } + return -1; +} + +static unsigned int calcue_power(unsigned int num) +{ + unsigned int i; + + if(num < 0) + return -1; + if((num == 2)||(num == 0)) + return 0; + + for(i = 0; num % 2 == 0; i++){ + num /= 2; + } + if(num > 2) + return -1; + + return i; +} + +static int current_is_lowmode(struct cpufreq_mode_clkdiv *cpufreq) +{ + if (freqs.old_cpufreq.low_clock == 1) + return 1; + else + return 0; +} + +static int new_is_lowmode(struct cpufreq_mode_clkdiv *cpufreq) +{ + if ((cpufreq->low_clock == 1)) + return 1; + else + return 0; +} + +static int new_is_2x_mode(struct cpufreq_mode_clkdiv *cpufreq) +{ + if ((cpufreq->cpu_div == 1)) + return 1; + else + return 0; +} + +static void config_asicclk_parameter(struct cpufreq_mode_clkdiv *cpufreq, + unsigned long *ratio) +{ + unsigned long clk_ratio = *ratio; + unsigned int asicdiv; + + asicdiv = calcue_power(cpufreq->asic_div); + if( asicdiv < 0) + printk("Error, calcue asic_div."); + + clk_ratio &= ~(0x7 << 6); + clk_ratio |= ((asicdiv << 6) | CLOCK_ASIC_MEM_ENA); + + *ratio = clk_ratio; +} + +static void config_clock_parameter(struct cpufreq_mode_clkdiv *cpufreq, + unsigned long *ratio) +{ + unsigned long clk_ratio = *ratio; + unsigned int memdiv, asicdiv; + + memdiv = calcue_power(cpufreq->mem_div); + if( memdiv < 0) + printk("Error, calcue mem_div."); + asicdiv = calcue_power(cpufreq->asic_div); + if( asicdiv < 0) + printk("Error, calcue asic_div."); + + clk_ratio &= ~((1 << 28)|(1 << 22)|(0xF << 17)|(1 << 15)|(0x7 << 9)|(0x7 << 6)|(0x3F << 0)); + + clk_ratio |= (cpufreq->is_3x << 28); //is 3x ? + clk_ratio |= (cpufreq->clk168_div << 17); //clk168 div + clk_ratio |= (cpufreq->cpu_div << 15); //cpu clk = mem clk or cpu clk = asic clk + clk_ratio |= (cpufreq->low_clock << 22); //cpu clk = asic clk + clk_ratio |= (memdiv << 9)|(asicdiv << 6); //mem div and asic div + clk_ratio |= (cpufreq->pll_sel << 0); //pll_sel + clk_ratio |= PLL_CHANGE_ENA; + + *ratio = clk_ratio; +} + +/* + * *function: enter L2 modify register parameters of clock for change sys clcok + * */ +void L2_LINK(freqchange) L2FUNC_NAME(freqchange)(unsigned long param1, + unsigned long param2,unsigned long param3,unsigned long param4) +{ + DISABLE_CACHE_MMU(); + + // check this bit and unitil both are empty + while(!((REG32(PHY_RAM_CFG_REG4) & (FIFO_R_EMPTY | FIFO_CMD_EMPTY)) == (FIFO_R_EMPTY | FIFO_CMD_EMPTY))) + ; + + // setup periodic of refresh interval and disable auto-refresh + REG32(PHY_RAM_CFG_REG4) = REFRESH_PERIOD_INTERVAL; + + DDR2_ENTER_POWERDOWN(); + // after send enter self - refresh, delay stable clock at least more than 2 tck + PM_DELAY(0x4); + + //disable ram clock + REG32(PHY_CLOCK_CTRL_REG) |= (1<<10); + + // other mode change to normal mode + //cpu clock from other mode to normal + REG32(PHY_CLOCK_DIV_REG) &= ~((1 << 28)|(1 << 22)); + PM_DELAY(0x100); + + //set clock div and check pll[12] + REG32(PHY_CLOCK_DIV_REG) = param1; + while (REG32(PHY_CLOCK_DIV_REG) & PLL_CHANGE_ENA); + PM_DELAY(0x10); + + //enable ram clock + REG32(PHY_CLOCK_CTRL_REG) &= ~(1<<10); + // new clock stable at least more than 1tck,here is ignore because follow has few instruction. + + // softreset ddr2 memory controller + REG32(0x0800000c) |= (0x1 << 26); + PM_DELAY(0x10); + REG32(0x0800000c) &= ~(0x1 << 26); + + // re-init ram controller + REG32(0x2000e05c) = 0x00000200; // bypass DCC + REG32(0x2000e078) = 0x43020100; // initial sstl = 00(12ma), tsel = 10(150ohm) + REG32(0x2000e000) = 0x00004e90; // 32 bit bus width + + // exit precharge power-down mode before delay at least 1 tck + PM_DELAY(10); + DDR2_EXIT_POWERDOWN(); + + // load mr, reset dll and delay for 200 tck, and set odt high + REG32(PHY_RAM_CPU_CMD) = 0x02800532; + // send nop, delay for 200 tck for dll reset, + PM_DELAY(0x10); + + // load mr, clean reset dll and remain odt low in ddr2 memory + REG32(PHY_RAM_CPU_CMD) = 0x1a800432; + + //enable dll and wate for dll stable in ram controller + REG32(0x2000e020) = 0x00000003; + while (!(REG32(0x2000e020) & (1 << 2))); + + // open auto-referesh + // default as mclk = 120mhz for calc tck=8.3ns, trefi=7.7us + REG32(0x2000e00c) = (0x39f<<1)|0x1; + + //calibration sart and wait for finish + REG32(0x2000e024) = ((param2>>0x7)<<10)|0x1; + while (!(REG32(0x2000e024) & (1 << 1))); + + ENABLE_CACHE_MMU(); +} + +/* + *function: change pll clock, include mem clock,asic clock and cpu clock. + */ +static void cpufreq_change_clocks(struct cpufreq_mode_clkdiv *cpufreq) +{ + unsigned long ratio; + void *addr; + unsigned long phy_addr; + + addr = kzalloc(512, GFP_KERNEL | GFP_ATOMIC); + if (addr == NULL) + return ; + phy_addr = virt_to_phys(addr); + + ratio = REG32(CLOCK_DIV_REG); + config_clock_parameter(cpufreq, &ratio); + + SPECIFIC_L2BUF_EXEC(freqchange, ratio, phy_addr,0,0); + + kfree(addr); +} + +/* + *function: according to needed new clocks and determine branch of cpufreq + * @cpufreq: structure include mode name and clock div + * note: the mode enter L2 cpufreq + */ +static int l2_cpu_freq_change(struct cpufreq_mode_clkdiv *cpufreq) +{ + int error; + + error = usermodehelper_disable(); + if (error) + goto Finish; + + // freeze process and kernel task. + error = cpufreq_freeze_processes(); + if (error) + goto freeze; + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + cpufreq_change_clocks(cpufreq); + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + thaw_processes(); + usermodehelper_enable(); + + return 0; + +freeze: + thaw_processes(); +Finish: + usermodehelper_enable(); + return error; +} + +static void ddr2_transfrom_clock_mode(T_cpufreq_mode state) +{ + unsigned long ratio; + + ratio = REG32(CLOCK_DIV_REG); + ratio &= ~(1 << 22); + switch (state) { + case CPU_LOW_MODE: + ratio |= (1 << 22); //|(1 << 14); + ratio &= ~(1 << 15); + break; + case CPU_NORMAL_MODE: + ratio &= ~(1 << 15); + break; + case CPU_NORMAL_2XMODE: + ratio |= (1 << 15); + break; + default: + printk("CPUFREQ: need mode is error.\n"); + break; + } + REG32(CLOCK_DIV_REG) = ratio; +} + +/* + *function: according to needed new clocks and determine branch of cpufreq + * @cpufreq: structure include mode name and clock div + * note: the mode cpufreq in ddr2 + */ +static int ddr2_cpu_freq_change(struct cpufreq_mode_clkdiv *cpufreq) +{ + unsigned long ratio, flags; + unsigned long cpuid = 0; + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + local_irq_save(flags); + + // check if cpu clock from other mode to normal and cutover to normal + if (current_is_lowmode(cpufreq)) { + ddr2_transfrom_clock_mode(CPU_NORMAL_MODE); + //cpuid = REG32(CPU_CHIP_ID); // used as delay + udelay(10); + } + + // change asic clock + if (freqs.old_cpufreq.asic_clk != freqs.new_cpufreq.asic_clk) { + ratio = REG32(CLOCK_DIV_REG); + config_asicclk_parameter(cpufreq, &ratio); + + REG32(CLOCK_DIV_REG) = ratio; + while (REG32(CLOCK_DIV_REG) & CLOCK_ASIC_MEM_ENA); + } + + // change cpu clock + if (freqs.old_cpufreq.cpu_clk != freqs.new_cpufreq.cpu_clk) { + if (new_is_2x_mode(cpufreq)) + ddr2_transfrom_clock_mode(CPU_NORMAL_2XMODE); + else if (!new_is_2x_mode(cpufreq)) { + if (new_is_lowmode(cpufreq)) + ddr2_transfrom_clock_mode(CPU_LOW_MODE); + else if (!current_is_lowmode(cpufreq)) + ddr2_transfrom_clock_mode(CPU_NORMAL_MODE); + } + udelay(10); + } + + local_irq_restore(flags); + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return 0; +} + +static int cpu_freq_change(struct cpufreq_mode_clkdiv *cpufreq) +{ + int error; + static int l2_cpufreq_flag = 0; + + if (freqs.old_cpufreq.pll_sel == freqs.new_cpufreq.pll_sel) { + if (current_is_lowmode(cpufreq) && l2_cpufreq_flag) { + error = l2_cpu_freq_change(cpufreq); + if (error) + goto exit_cpufreq; + + cpufreq_in_ddr2 = 0; + } else { + error = ddr2_cpu_freq_change(cpufreq); + if (error < 0) + goto exit_cpufreq; + + cpufreq_in_ddr2 = 1; + l2_cpufreq_flag = 0; + } + } else { + error = l2_cpu_freq_change(cpufreq); + if (error) + goto exit_cpufreq; + + cpufreq_in_ddr2 = 0; + l2_cpufreq_flag = 1; + } + + return 0; + +exit_cpufreq: + return error; +} + +/* + * change from low mode to normal mode for suspend (low mode --> normal mode) + */ +void cpu_freq_suspend_check(void) +{ + struct cpufreq_mode_clkdiv cpufreq; + unsigned long ratio; + + // change to normal mode + if (freqs.old_cpufreq.low_clock == 1) { + if (cpufreq_in_ddr2) { + ddr2_transfrom_clock_mode(CPU_NORMAL_MODE); + udelay(10); + } else { + // save low mode before suspend change normal + prev_suspend_mode = current_mode; + + if(!get_cpufreq_mode_clkdiv(SUSPEND_NORMAL_MODE, &cpufreq)){ + cpufreq_change_clocks(&cpufreq); + } + } + } +} +EXPORT_SYMBOL(cpu_freq_suspend_check); + +/* + * restore low mode after resume (normal mode --> low mode) + */ +void cpu_freq_resume_check(void) +{ + struct cpufreq_mode_clkdiv cpufreq; + unsigned long ratio; + + // chang to low mode + if (freqs.old_cpufreq.low_clock == 1) { + if (cpufreq_in_ddr2) { + ddr2_transfrom_clock_mode(CPU_LOW_MODE); + udelay(10); + } else { + if(!get_cpufreq_mode_clkdiv(prev_suspend_mode, &cpufreq)){ + cpufreq_change_clocks(&cpufreq); + } + } + } +} +EXPORT_SYMBOL(cpu_freq_resume_check); + +static void info_clock_value(void) +{ + printk("Cpufreq: system clocks(unit:MHz)[cpu,mem,asic] from (%d,%d,%d) to (%d,%d,%d)\n", + freqs.old_cpufreq.cpu_clk/clkdiv, freqs.old_cpufreq.mem_clk/clkdiv, + freqs.old_cpufreq.asic_clk/clkdiv, ak98_get_cpu_clk()/clkdiv, + ak98_get_mem_clk()/clkdiv, ak98_get_asic_clk()/clkdiv); +} + +static int clock_need_changing(void) +{ + if((freqs.new_cpufreq.cpu_clk == freqs.old_cpufreq.cpu_clk)&& + (freqs.new_cpufreq.mem_clk == freqs.old_cpufreq.mem_clk)&& + (freqs.new_cpufreq.asic_clk == freqs.old_cpufreq.asic_clk)) + return 0; + else + return 1; +} + +static unsigned int get_asic_clk(struct cpufreq_mode_clkdiv *cpufreq) +{ + unsigned int asicclk; + + if(cpufreq->is_3x) + asicclk = ((PLL_CLK_MIN+(cpufreq->pll_sel*4))/(cpufreq->clk168_div+1))/3; + else + asicclk = ((PLL_CLK_MIN+(cpufreq->pll_sel*4))/(cpufreq->clk168_div+1))/cpufreq->asic_div; + + return asicclk; +} + +static unsigned int get_mem_clk(struct cpufreq_mode_clkdiv *cpufreq) +{ + unsigned int memclk; + + if(cpufreq->is_3x) + memclk = ((PLL_CLK_MIN+(cpufreq->pll_sel*4))/(cpufreq->clk168_div+1))/3; + else + memclk = ((PLL_CLK_MIN+(cpufreq->pll_sel*4))/(cpufreq->clk168_div+1))/cpufreq->mem_div; + + return memclk; +} + +static unsigned int get_cpu_clk(struct cpufreq_mode_clkdiv *cpufreq) +{ + unsigned int cpuclk; + + if(cpufreq->is_3x) { + cpuclk = (PLL_CLK_MIN+(cpufreq->pll_sel*4))/(cpufreq->clk168_div+1); + } else { + if(cpufreq->cpu_div) { + cpuclk = (PLL_CLK_MIN+(cpufreq->pll_sel*4))/(cpufreq->clk168_div+1); + } else { + if(cpufreq->low_clock) + cpuclk = get_asic_clk(cpufreq); + else + cpuclk = get_mem_clk(cpufreq); + } + } + + return cpuclk; +} + +static void update_current_clock(void) +{ + freqs.old_cpufreq.cpu_clk = freqs.new_cpufreq.cpu_clk; + freqs.old_cpufreq.mem_clk = freqs.new_cpufreq.mem_clk; + freqs.old_cpufreq.asic_clk = freqs.new_cpufreq.asic_clk; + freqs.old = freqs.new; + freqs.old_cpufreq.pll_sel = freqs.new_cpufreq.pll_sel; + freqs.old_cpufreq.low_clock = freqs.new_cpufreq.low_clock; +} + +static void get_newmode_clock(struct cpufreq_mode_clkdiv *cpufreq) +{ + freqs.new_cpufreq.cpu_clk = get_cpu_clk(cpufreq)*clkdiv; + freqs.new_cpufreq.mem_clk = get_mem_clk(cpufreq)*clkdiv; + freqs.new_cpufreq.asic_clk = get_asic_clk(cpufreq)*clkdiv; + freqs.new = freqs.new_cpufreq.cpu_clk; + freqs.new_cpufreq.pll_sel = cpufreq->pll_sel; + freqs.new_cpufreq.low_clock = cpufreq->low_clock; +} + +void update_pre_mode(void) +{ + // assign to save old mode + previous_mode_flag = freqs.old_cpufreq.low_clock; +} + +unsigned int get_pll_sel(T_OPERATION_MODE state) +{ + int i, len; + + len = ARRAY_SIZE(cpufreq_divs); + for (i = 0; i < len; i++) { + if (state == cpufreq_divs[i].mode_name) + break; + } + if (likely(i < len)) + return cpufreq_divs[i].pll_sel; + + return -1; +} +EXPORT_SYMBOL(get_pll_sel); + +int current_mode_is_low_mode(void) +{ + return freqs.old_cpufreq.low_clock; +} +EXPORT_SYMBOL(current_mode_is_low_mode); + +/* + *function: get system boot's mode + */ +T_OPERATION_MODE get_current_mode(void) +{ + return current_mode; +} +EXPORT_SYMBOL(get_current_mode); + +/* function: enter change cpufreq + * @state: requested mode name + * return: + * -1: if system init mode is not surpport. + * 0: if cpufreq change successful. + */ +int request_cpufreq_enter(T_OPERATION_MODE state) +{ + int i, len; + int error; + + // check if request suspending, prevent cpufreq when suspending + if (atomic_read(&suspend_flags)) + return 0; + + if (state == current_mode) { + //printk("requset new mode equal to current mode.\n"); + return 0; + } + + len = ARRAY_SIZE(cpufreq_divs); + for (i = 0; i < len; i++) { + if (state == cpufreq_divs[i].mode_name) + break; + } + if (likely(i < len)) { + get_newmode_clock(&cpufreq_divs[i]); + update_pre_mode(); + + if (!clock_need_changing()) { + //printk("Cpufreq: new mode clocks equal to old mode clocks, exit changing.\n"); + return 0; + } + + error = cpu_freq_change(&cpufreq_divs[i]); + if (error) { + printk("CPUFREQ: request new mode changed fail. working mode is current mode.\n\n"); + goto cpufreq_err; + } + } else { + printk("CPUFREQ: requset new mode is not surpport.\n"); + goto cpufreq_err; + } + + info_clock_value(); + update_current_clock(); + current_mode = state; + + return 0; + +cpufreq_err: + return -1; +} +EXPORT_SYMBOL(request_cpufreq_enter); + +/* + * function: get system boot's mode + */ +static int get_init_mode(void) +{ + int i; + unsigned int pllclk, clk168, cpuclk, memclk, asicclk; + unsigned int pllsel, clk168div, cpudiv, memdiv, asicdiv, lowclock; + + pllclk = ak98_get_pll_clk()/clkdiv; + clk168 = ak98_get_clk168m_clk()/clkdiv; + cpuclk = freqs.old_cpufreq.cpu_clk/clkdiv; + memclk = freqs.old_cpufreq.mem_clk/clkdiv; + asicclk = freqs.old_cpufreq.asic_clk/clkdiv; + pllsel = ((pllclk - PLL_CLK_MIN)/4) & 0x3f; + + if(pllclk == clk168) + clk168div = 0; + else + clk168div = pllclk/clk168; + + //system is 3x mode + if((cpuclk / memclk == 3)&&(cpuclk / asicclk == 3)&& + (cpuclk % memclk == 0)&&(cpuclk % asicclk == 0)) { + for(i = 0; i < ARRAY_SIZE(cpufreq_divs); i++) { + if((cpufreq_divs[i].is_3x == 1) && + (cpufreq_divs[i].clk168_div == clk168div) && + (cpufreq_divs[i].pll_sel == pllsel)){ + + current_mode = cpufreq_divs[i].mode_name; + freqs.old_cpufreq.low_clock = cpufreq_divs[i].low_clock; + return 0; + } + } + return -1; + } + + //system is normal mode + if(cpuclk == clk168) { + lowclock = 0; + cpudiv = 1; + } else if(cpuclk == memclk) { + lowclock = 0; + cpudiv = 0; + } else if(cpuclk == asicclk) { + lowclock = 1; + cpudiv = 0; + } + memdiv = clk168/memclk; + asicdiv = clk168/asicclk; + + for(i = 0; i < ARRAY_SIZE(cpufreq_divs); i++){ + if((cpufreq_divs[i].cpu_div == cpudiv) && + (cpufreq_divs[i].low_clock == lowclock)&& + (cpufreq_divs[i].mem_div == memdiv) && + (cpufreq_divs[i].asic_div == asicdiv) && + (cpufreq_divs[i].pll_sel == pllsel) && + (cpufreq_divs[i].clk168_div == clk168div)){ + + current_mode = cpufreq_divs[i].mode_name; + freqs.old_cpufreq.low_clock = cpufreq_divs[i].low_clock; + return 0; + } + } + return -1; +} + +static void cpufreq_operation_init(void) +{ + int i, error; + unsigned int tmp; + + freqs.old_cpufreq.cpu_clk = ak98_get_cpu_clk(); + freqs.old_cpufreq.mem_clk = ak98_get_mem_clk(); + freqs.old_cpufreq.asic_clk = ak98_get_asic_clk(); + freqs.old = freqs.old_cpufreq.cpu_clk; + + freqs.flags = 0; + + tmp = cpufreq_divs[0].pll_sel; + for(i = 1; i < ARRAY_SIZE(cpufreq_divs); i++){ + if(cpufreq_divs[i].pll_sel > tmp) + tmp = cpufreq_divs[i].pll_sel; + } + freqs.old_cpufreq.pll_sel = tmp; + + error = get_init_mode(); + if(error < 0) + current_mode = error; + + return; +} + +/* ak98_cpufreq_init + * + * Attach the cpu frequence scaling functions. This should be called + * from the board specific initialisation if the board supports + * it. +*/ +int __init ak98_cpufreq_init(void) +{ + printk("AK98 cpu frequence change support, (c) 2011 ANYAK\n"); + cpufreq_operation_init(); + + return 0; +} +module_init(ak98_cpufreq_init); + +#endif + diff --git a/arch/arm/mach-ak39/devices.c b/arch/arm/mach-ak39/devices.c new file mode 100644 index 00000000..6adcf718 --- /dev/null +++ b/arch/arm/mach-ak39/devices.c @@ -0,0 +1,542 @@ +/* linux/arch/arm/mach-ak39/devices.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/** + * @brief: uart0 device info + * + * @author: caolianming + * @date: 2014-01-09 + */ +struct platform_device ak39_uart0_device = { + .name = "ak39-uart", + .id = 0, +}; +EXPORT_SYMBOL(ak39_uart0_device); + +/** + * @brief: uart1 device info + * + * @author: caolianming + * @date: 2014-01-09 + */ +struct platform_device ak39_uart1_device = { + .name = "ak39-uart", + .id = 1, +}; +EXPORT_SYMBOL(ak39_uart1_device); + +/** + * @brief: gpio uart device info + * + * @author: caolianming + * @date: 2014-01-09 + */ +struct platform_device ak39_gpio_uart_device = { + .name = "gpio-uart", + .id = 0, +}; +EXPORT_SYMBOL(ak39_gpio_uart_device); + + +/** + * @brief: MCI device info + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct resource ak39_mmc_resource[] = { + [0] = { + .start = 0x20100000, + .end = 0x20100000 + 0x43, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_MCI, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device ak39_mci1_device = { + .name = "ak_mci", + .id = 0, + .num_resources = ARRAY_SIZE(ak39_mmc_resource), + .resource = ak39_mmc_resource, +}; +EXPORT_SYMBOL(ak39_mci1_device); + +/** + * @brief: SDIO device info + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct resource ak39_sdio_resource[] = { + [0] = { + .start = 0x20108000, + .end = 0x20108000 + 0x43, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_SDIO, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device ak39_mci2_device = { + .name = "ak_sdio", + .id = 1, + .num_resources = ARRAY_SIZE(ak39_sdio_resource), + .resource = ak39_sdio_resource, +}; +EXPORT_SYMBOL(ak39_mci2_device); + + +/** + * @brief: I2C device info + * + * @author: caolianming + * @date: 2014-01-09 + */ +#if defined(CONFIG_I2C_AK39_HW) +struct gpio_info i2c_gpios[] = { + { + .pin = AK_GPIO_27, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .dir = AK_GPIO_DIR_OUTPUT, + .value = AK_GPIO_OUT_HIGH, + .int_pol = -1, + }, + { + .pin = AK_GPIO_28, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .dir = AK_GPIO_DIR_OUTPUT, + .value = AK_GPIO_OUT_HIGH, + .int_pol = -1, + + }, +}; + +static struct ak39_platform_i2c ak39_default_i2c_data = { + .flags = 0, + .bus_num = 0, + .slave_addr = 0x10, + .frequency = 300*1000, + .sda_delay = 100, + .gpios = i2c_gpios, + .npins = ARRAY_SIZE(i2c_gpios), +}; + +static struct resource ak39_i2c_resource[] = { + [0] = { + .start = 0x20150000, + .end = 0x20150000+SZ_256, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_I2C, + .end = IRQ_I2C, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device ak39_i2c_device = { + .name = "i2c-ak39", + .id = -1, + .dev = { + .platform_data = &ak39_default_i2c_data, + }, + .num_resources = ARRAY_SIZE(ak39_i2c_resource), + .resource = ak39_i2c_resource, +}; +EXPORT_SYMBOL(ak39_i2c_device); + +#elif defined(CONFIG_I2C_GPIO_SOFT) +#else +struct platform_device ak39_i2c_device = { + .name = "i2c", + .id = -1, +}; +EXPORT_SYMBOL(ak39_i2c_device); +#endif + + +/** + * @brief: USB udc device info + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct resource usb_otg_udc_resource[] = { + [0] = { + .start = 0x20200000, + .end = 0x202003ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "usb mcu irq", + .start = IRQ_USBOTG_MCU, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .name = "usb dma irq", + .start = IRQ_USBOTG_DMA, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device ak39_usb_udc_device = { + .name = "ak-hsudc", + .id = -1, + .num_resources = ARRAY_SIZE(usb_otg_udc_resource), + .resource = usb_otg_udc_resource, +}; +EXPORT_SYMBOL(ak39_usb_udc_device); + + +/** + * @brief: USB otg host device info + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct resource usb_otg_hcd_resource[] = { + [0] = { + .start = 0x20200000, + .end = 0x202003ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "usb mcu irq", + .start = IRQ_USBOTG_MCU, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .name = "usb dma irq", + .start = IRQ_USBOTG_DMA, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device ak39_usb_otg_hcd_device = { + .name = "usb-host", + .id = -1, + .num_resources = ARRAY_SIZE(usb_otg_hcd_resource), + .resource = usb_otg_hcd_resource, +}; +EXPORT_SYMBOL(ak39_usb_otg_hcd_device); + +/** + * @brief: MAC device info + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct resource ak39_mac_resource[] = { + [0] = { + .start = 0x20300000, + .end = 0x20301fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "mac irq", + .start = IRQ_MAC, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device ak39_mac_device = { + .name = "ak_ethernet", + .id = 0, + .num_resources = ARRAY_SIZE(ak39_mac_resource), + .resource = ak39_mac_resource, +}; +EXPORT_SYMBOL(ak39_mac_device); + +/** + * @brief: SPI device info + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct resource ak39_spi1_resource[] = { + [0] = { + .start = 0x20120000, + .end = 0x20120027, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_SPI1, + .end = IRQ_SPI1, + .flags = IORESOURCE_IRQ, + } +}; + +struct platform_device ak39_spi1_device = { + .name = "ak-spi", + .id = -1, + .num_resources = ARRAY_SIZE(ak39_spi1_resource), + .resource = ak39_spi1_resource, +}; +EXPORT_SYMBOL(ak39_spi1_device); + + +/** + * @brief: Camera interface resource info + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct resource ak39_camera_resource[] = { + [0] = { + .name = "camera if irq", + .start = IRQ_CAMERA, + .flags = IORESOURCE_IRQ, + }, +}; + +/* camera interface */ +struct platform_device ak39_camera_interface = { + .name = "ak_camera", + .id = 39, + .num_resources = ARRAY_SIZE(ak39_camera_resource), + .resource = ak39_camera_resource, +}; + +EXPORT_SYMBOL(ak39_camera_interface); + + +/** + * @brief: PCM device resource info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static u64 snd_dma_mask = DMA_BIT_MASK(32); + +struct platform_device ak39_pcm_device = { + .name = "snd_akpcm", + .id = 0, + .dev = { + .dma_mask = &snd_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; +EXPORT_SYMBOL(ak39_pcm_device); + + +/** + * @brief: ION device resource info + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ion_platform_data ak39_ion_pdata = { + .nr = 1, + .heaps = { + { + .type = ION_HEAP_TYPE_CARVEOUT, + .id = 1, + .name = "Reserved phys Memory", + .base = CONFIG_RAM_BASE, + .size = CONFIG_VIDEO_RESERVED_MEM_SIZE, /* the first reserved size */ + .align = PAGE_SIZE, + }, + } +}; + +struct platform_device ak39_ion_device = { + .name = "ion-ak", + .id = -1, + .dev = { + .platform_data = &ak39_ion_pdata, + }, +}; +EXPORT_SYMBOL(ak39_ion_device); + +/** + * @brief: LDE device resource info + * + * @author: caolianming + * @date: 2014-01-09 + */ +struct platform_device ak39_led_pdev = { + .name = "ak_led", + .id = -1, +}; +EXPORT_SYMBOL(ak39_led_pdev); + +/** + * @brief: fha device resource info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct platform_device akfha_char_device = { + .name = "ak-fhachar", + .id = -1, + .dev = { + .platform_data = NULL, + }, +}; +EXPORT_SYMBOL(akfha_char_device); + +/** + * @brief: gpio buttons device resource info + * + * @author: caolianming + * @date: 2014-01-09 + */ +struct platform_device ak39_gpio_keys_device = { + .name = "akgpio-keys", + .id = -1, +}; +EXPORT_SYMBOL(ak39_gpio_keys_device); + +/** + * @brief: battery device resource info + * + * @author: caolianming + * @date: 2014-01-09 + */ +struct platform_device ak39_battery_power = { + .name = "battery", + .id = -1, +}; +EXPORT_SYMBOL(ak39_battery_power); + +static struct resource ak39mmx_resources[] = { + { + .name = "video-base", + .start = 0x20020000, + .end = 0x2002ffff, + .flags = IORESOURCE_MEM, + }, +}; + +static struct uio_info akmmx_uioinfo = { + .name = "video_codec", + .version = "0.1.0", +#ifdef CONFIG_UIODMA + .use_dma = true, +#endif + .irq = UIO_IRQ_CUSTOM, +}; + +struct platform_device ak39_mmx_device = { + .name = "uio_vcodec", + .id = 0, + .dev = { + .platform_data = &akmmx_uioinfo, + }, + .num_resources = ARRAY_SIZE(ak39mmx_resources), + .resource = ak39mmx_resources, +}; +EXPORT_SYMBOL(ak39_mmx_device); + +/** + * @brief: rtc device resource info + * + * @author: caolianming + * @date: 2014-01-09 + */ +struct platform_device ak39_rtc_device = { + .name = "ak-rtc", + .id = -1, +}; +EXPORT_SYMBOL(ak39_rtc_device); + +/** + * @brief: pwm device resource info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct platform_device ak39_pwm_device = { + .name = "ak-pwm", + .id = 0, +}; +EXPORT_SYMBOL(ak39_pwm_device); + + +/** + * @brief: motor0 device resource info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct platform_device ak39_motor0_device = { + .name = "ak-motor", + .id = 0, +}; +EXPORT_SYMBOL(ak39_motor0_device); + +/** + * @brief: motor1 device resource info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct platform_device ak39_motor1_device = { + .name = "ak-motor", + .id = 1, +}; +EXPORT_SYMBOL(ak39_motor1_device); + +/** + * @brief: Crypto device resource info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct resource ak39_crypto_resource[] = { + [0] = { + .start = 0x20180000, + .end = 0x20180067, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_ENCRYTION, + .end = IRQ_ENCRYTION, + .flags = IORESOURCE_IRQ, + } +}; + +struct platform_device ak39_crypto_device = { + .name = "ak-crypto", + .id = -1, + .num_resources = ARRAY_SIZE(ak39_crypto_resource), + .resource = ak39_crypto_resource, +}; +EXPORT_SYMBOL(ak39_crypto_device); + +/** + * @brief: power manage resource info + * + * @author: ye_guohong + * @date: 2014-12-09 + */ +struct platform_device ak39_pm_device = { + .name = "pm-anyka", + .id = -1, +}; +EXPORT_SYMBOL(ak39_pm_device ); diff --git a/arch/arm/mach-ak39/gpio.c b/arch/arm/mach-ak39/gpio.c new file mode 100644 index 00000000..eeae82bb --- /dev/null +++ b/arch/arm/mach-ak39/gpio.c @@ -0,0 +1,315 @@ +/** +* @file arch/arm/mach-ak39/gpio.c +* @brief dispatch the call of GPIO API +* Copyright C 2011 Anyka CO.,LTD +* 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. +* @author zhou wenyong +* @date 2011-08-11 +* @note 2010-10-21 created by caolianming +* @note 2011-06-14 move this file of old version to the ak39-gpio.c +* -- unify the GPIO API +* @note 2010-08-11 add more comments and change some identifiers' name +*/ +#include +#include +#include + +/* + Look up table in which we look up the implementation function of GPIO API by + gpio pin +*/ +static struct gpio_api_lut ak39_gpio_api_lut[] = +{ + { + .pin_start = AK_GPIO_MIN, + .pin_end = AK_GPIO_MAX, + .setpin_as_gpio = g_ak39_setpin_as_gpio, + .gpio_pullup = g_ak39_gpio_pullup, + .gpio_pulldown = g_ak39_gpio_pulldown, + .gpio_dircfg = g_ak39_gpio_cfgpin, + .gpio_intcfg = g_ak39_gpio_inten, + .gpio_set_intpol= g_ak39_gpio_intpol, + .gpio_setpin = g_ak39_gpio_setpin, + .gpio_getpin = g_ak39_gpio_getpin, + .gpio_to_irq = g_ak39_gpio_to_irq, + .irq_to_gpio = g_ak39_irq_to_gpio, + }, +}; + +#define GPIO_API_LUT ak39_gpio_api_lut +/* + Look up the table to get the implementation function of corresponding + GPIO API +*/ +#define GET_GPIO_FUNC(pfun, pin, name)\ + do{\ + int _i_name;\ + pfun=NULL;\ + for (_i_name=0; _i_name= GPIO_API_LUT[_i_name].pin_start && pin <= GPIO_API_LUT[_i_name].pin_end)\ + { pfun = GPIO_API_LUT[_i_name].name; break;}\ + }while(0); + + + +/** +* @brief set pin as GPIO port +* @author zhou wenyong +* @date 2011-08-11 +* @param[in] pin +* @return int +*/ +int ak_setpin_as_gpio(unsigned int pin) +{ + int (*pfunc)(unsigned int); + GET_GPIO_FUNC(pfunc, pin, setpin_as_gpio); + if (pfunc) + return pfunc(pin); + return 0; +} +EXPORT_SYMBOL(ak_setpin_as_gpio); + + +/** +* @brief configure GPIO PULLUP function +* @author zhou wenyong +* @date 2011-08-11 +* @param[in] pin +* @param[in] enable +* @return int +*/ +int ak_gpio_pullup(unsigned int pin, unsigned char enable) +{ + int (*pfunc)(unsigned int, unsigned char); + GET_GPIO_FUNC(pfunc, pin, gpio_pullup); + if (pfunc) + return pfunc(pin, enable); + + return 0; +} +EXPORT_SYMBOL(ak_gpio_pullup); + +/** +* @brief configure GPIO PULLDOWN function +* @author zhou wenyong +* @date 2011-08-11 +* @param[in] pin +* @param[in] enable +* @return int +*/ +int ak_gpio_pulldown(unsigned int pin, unsigned char enable) +{ + int (*pfunc)(unsigned int, unsigned char); + GET_GPIO_FUNC(pfunc, pin, gpio_pulldown); + if (pfunc) + return pfunc(pin, enable); + + return 0; +} +EXPORT_SYMBOL(ak_gpio_pulldown); + + +/** +* @brief configure GPIO direction +* @author zhou wenyong +* @date 2011-08-11 +* @param[in] pin +* @param[in] direction +* @return int +*/ +int ak_gpio_dircfg(unsigned int pin, unsigned int direction) +{ + int (*pfunc)(unsigned int, unsigned int); + GET_GPIO_FUNC(pfunc, pin, gpio_dircfg); + if (pfunc) + return pfunc(pin, direction); + + return 0; +} +EXPORT_SYMBOL(ak_gpio_dircfg); +/** +* @brief old version of ak_gpio_dircfg +* @author zhou wenyong +* @date 2011-08-11 +* @param[in] pin +* @param[in] to +* @return int +*/ +int ak_gpio_cfgpin(unsigned int pin, unsigned int to) +{ + return ak_gpio_dircfg(pin, to); +} +EXPORT_SYMBOL(ak_gpio_cfgpin); + + +/** +* @brief enable/disable GPIO interrupt +* @author zhou wenyong +* @date 2011-08-11 +* @param[in] pin +* @param[in] enable +* @return int +*/ +int ak_gpio_intcfg(unsigned int pin, unsigned int enable) +{ + int (*pfunc)(unsigned int, unsigned int); + GET_GPIO_FUNC(pfunc, pin, gpio_intcfg); + if (pfunc) + return pfunc(pin, enable); + + return 0; +} +EXPORT_SYMBOL(ak_gpio_intcfg); +/** +* @brief old version of ak_gpio_intcfg +* @author zhou wenyong +* @date 2011-08-11 +* @param[in] pin +* @param[in] enable +* @return int +*/ +int ak_gpio_inten(unsigned int pin, unsigned int enable) +{ + return ak_gpio_intcfg(pin, enable); +} +EXPORT_SYMBOL(ak_gpio_inten); + + +/** +* @brief configure GPIO interrupt polarity +* @author zhou wenyong +* @date 2011-08-11 +* @param[in] pin +* @param[in] level +* @return int +*/ +int ak_gpio_set_intpol(unsigned int pin, unsigned int level) +{ + int (*pfunc)(unsigned int, unsigned int); + GET_GPIO_FUNC(pfunc, pin, gpio_set_intpol); + if (pfunc) + return pfunc(pin, level); + + return 0; +} +EXPORT_SYMBOL(ak_gpio_set_intpol); +/** +* @brief old version of ak_gpio_set_intpol +* @author zhou wenyong +* @date 2011-08-11 +* @param[in] pin +* @param[in] level +* @return int +*/ +int ak_gpio_intpol(unsigned int pin, unsigned int level) +{ + return ak_gpio_set_intpol(pin, level); +} +EXPORT_SYMBOL(ak_gpio_intpol); + + +/** +* @brief configure GPIO output state +* @author zhou wenyong +* @date 2011-08-11 +* @param[in] pin +* @param[in] to +* @return int +*/ +int ak_gpio_setpin(unsigned int pin, unsigned int to) +{ + int (*pfunc)(unsigned int, unsigned int); + GET_GPIO_FUNC(pfunc, pin, gpio_setpin); + if (pfunc) + return pfunc(pin, to); + + return 0; +} +EXPORT_SYMBOL(ak_gpio_setpin); + +/** +* @brief read GPIO input state +* @author zhou wenyong +* @date 2011-08-11 +* @param[in] pin +* @return int +*/ + int ak_gpio_getpin(unsigned int pin) +{ + int (*pfunc)(unsigned int); + GET_GPIO_FUNC(pfunc, pin, gpio_getpin); + if (pfunc) + return pfunc(pin); + + return 0; +} +EXPORT_SYMBOL(ak_gpio_getpin); + + +/** +* @brief get corresponding irq by pin +* @author zhou wenyong +* @date 2011-08-11 +* @param[in] pin +* @return int +*/ + int ak_gpio_to_irq(unsigned int pin) +{ + int (*pfunc)(unsigned int); + GET_GPIO_FUNC(pfunc, pin, gpio_to_irq); + if (pfunc) + return pfunc(pin); + + return 0; +} +EXPORT_SYMBOL(ak_gpio_to_irq); + +/** +* @brief get corresponding gpio pin by irq +* @author zhou wenyong +* @date 2011-08-11 +* @param[in] irq +* @return int +*/ + int ak_irq_to_gpio(unsigned int irq) +{ + if ( irq >= IRQ_GPIO_0 && irq<= NR_IRQS - 1) + return AK_GPIO_0 + (irq-IRQ_GPIO_0); + else + panic("wrong irq number %u passed to ak_irq_to_gpio.\n", irq); + + return -1; +} +EXPORT_SYMBOL(ak_irq_to_gpio); + + +/** +* @brief configure GPIO followed properties specified in info +* @author zhou wenyong +* @date 2011-08-11 +* @param[out] *info +* @return void +*/ +void ak_gpio_set(const struct gpio_info *info) +{ + if ( ! (info->pin >= AK_GPIO_MIN && info->pin <= AK_GPIO_MAX )) + return ; + + ak_setpin_as_gpio(info->pin); + + if (info->pullup == AK_PULLUP_ENABLE || info->pullup == AK_PULLUP_DISABLE) + ak_gpio_pullup(info->pin, info->pullup); + if (info->pulldown == AK_PULLDOWN_ENABLE || info->pulldown == AK_PULLDOWN_DISABLE) + ak_gpio_pulldown(info->pin, info->pulldown); + if (info->value == AK_GPIO_OUT_HIGH || info->value == AK_GPIO_OUT_LOW) + ak_gpio_setpin(info->pin, info->value); + if (info->int_pol == AK_GPIO_INT_LOWLEVEL || info->int_pol == AK_GPIO_INT_HIGHLEVEL) + ak_gpio_set_intpol(info->pin, info->int_pol); + if (info->dir == AK_GPIO_DIR_OUTPUT || info->dir == AK_GPIO_DIR_INPUT) + ak_gpio_dircfg(info->pin, info->dir); +} +EXPORT_SYMBOL(ak_gpio_set); + diff --git a/arch/arm/mach-ak39/include/mach/adc.h b/arch/arm/mach-ak39/include/mach/adc.h new file mode 100644 index 00000000..16d38d5d --- /dev/null +++ b/arch/arm/mach-ak39/include/mach/adc.h @@ -0,0 +1,44 @@ +/* + * ak_adc.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __AK_ADC_H_ +#define __AK_ADC_H_ + +#include + +#define RESET_CTRL_REG (AK_VA_SYSCTRL + 0x20) +#define AD_DA_CLK1_REG (AK_VA_SYSCTRL + 0x0C) +#define SAR_ADC_CFG_REG (AK_VA_SYSCTRL + 0x98) +#define SAR_ADC_ACR_REG1 (AK_VA_SYSCTRL + 0x9C) +#define SAR_ADC_ACR_REG2 (AK_VA_SYSCTRL + 0xA0) +#define SAR_ADC_ACR_REG3 (AK_VA_SYSCTRL + 0xA4) +#define SAR_IF_CFG_REG (AK_VA_SYSCTRL + 0x5C) +#define SAR_TIMING_CFG_REG (AK_VA_SYSCTRL + 0x60) +#define SAR_THRESHOLD_REG (AK_VA_SYSCTRL + 0x64) +#define SAR_IF_SMP_DAT_REG (AK_VA_SYSCTRL + 0x68) +#define SAR_IF_INT_STATUS_REG (AK_VA_SYSCTRL + 0x6C) + + +#define ADC1_MAIN_CLK (12000000) +#define ADC1_DEFAULT_CLK (1500000) /* FIXME: bat work in 1.5MHz */ +#define DEFAULT_SAMPLE (1000) /* FIXME: somebody need change it */ +#define AK_AVCC (3300) /* AVCC always 3.3V ? */ + +#define AK_ADC1_AD0 (0) /* FIXME: AD0=AIN0=BAT ? AD1=AIM1 ? AD2=AIN2 ? */ +#define AK_ADC1_AD1 (1) +#define AK_ADC1_BAT (2) + +int adc1_init(void); +unsigned long adc1_read_channel(int channel); + +#define adc1_read_bat() adc1_read_channel(AK_ADC1_BAT) +#define adc1_read_ad5() adc1_read_channel(AK_ADC1_AD1) /* is it right ? */ +#define adc1_read_ain0() adc1_read_channel(AK_ADC1_AD0) +#define adc1_read_ain1() adc1_read_channel(AK_ADC1_AD1) /* is it right ? */ +#endif diff --git a/arch/arm/mach-ak39/include/mach/akpcmL0.h b/arch/arm/mach-ak39/include/mach/akpcmL0.h new file mode 100644 index 00000000..9c8447ce --- /dev/null +++ b/arch/arm/mach-ak39/include/mach/akpcmL0.h @@ -0,0 +1,233 @@ +#ifndef AKPCML0_H +#define AKPCML0_H + +#include +#include + +typedef struct { + void *base0800; + void *base2002E; + void *base20072; +}AUDIO_REG, *PAUDIO_REG; + +enum akpcm_detect_flag { + AKPCM_DYNAMIC_DETECT, + AKPCM_LINEIN_ALWAY, + AKPCM_MIC_ALWAY, +}; + +//pAddress0800 0x08000000-0x080000FF +#define CPUPLLCHN_CLOCK_CTRL_REG 0x0004 + +#define CLOCK_CTRL_REG 0x000C +#define HIGHSPEED_CLOCK_CTRL_REG 0x0010 +#define CLOCK_GATE_CTRL_REG 0x001c +#define SOFT_RST_CTRL_REG 0x0020 +#define MULTIPLE_FUN_CTRL_REG1 0x0058 +#define FADEOUT_CTRL_REG 0x0070 + +#define ADC1_CONF_REG1 0x0098 + +#define ANALOG_CTRL_REG1 0x009c +#define ANALOG_CTRL_REG2 0x00A0 +#define ANALOG_CTRL_REG3 0x00A4 + +//pAddress2011 0x2011000-0x20118008 +#define DAC_CONFIG_REG 0x0000 +#define I2S_CONFIG_REG 0x0004 +#define CPU_DATA_REG 0x0008 + +#define ADC2_CONFIG_REG 0x8000 +#define ADC2_DATA_REG 0x8004 + +//ADC2_CONFIG_REG(0x20118000) +#define WORD_LENGTH_MASK (0XF << 8) +#define I2S_EN (1 << 4) +#define CH_POLARITY_SEL (1 << 3) +#define HOST_RD_INT_EN (1 << 2) +#define ADC2MODE_L2_EN (1 << 1) +#define ADC2_CTRL_EN (1 <<0) + +//CLOCK_CTRL_REG(0x08000004) +#define ASIC_FREQ_ADJ (1 << 28) + +//CLOCK_CTRL_REG(0x0800000C) +#define DAC_DIV_VLD (1 << 29) +#define DAC_CLK_EN (1 << 28) +#define MASK_DAC_DIV_FRAC (0xFFF << 12) +#define DAC_DIV_FRAC(val) (((val)&0xFFF) << 12) +#define MASK_DAC_DIV_INT (0xFF << 4) +#define DAC_DIV_INT(val) (((val)&0xFF) << 4) + +//HIGHSPEED_CLOCK_CTRL_REG(0x0800 0010) +#define ADC2_HSDIV_VLD (1 << 29) +#define ADC2_HCLK_EN (1 << 28) +#define MASK_ADC2_HCLK_DIV (0x3F << 20) +#define ADC2_HCLK_DIV(val) (((val)&0x3F)<<20) +#define DAC_HSDIV_VLD (1 << 19) +#define DAC_HCLK_EN (1 << 18) +#define MASK_DAC_HCLK_DIV (0xFF << 10) +#define DAC_HCLK_DIV(val) (((val)&0xFF)<<10) +#define ADC2_DIV_VLD (1 << 9) +#define ADC2_CLK_EN (1 << 8) +#define MASK_ADC2_DIV (0xFF << 0) +#define ADC2_DIV(val) ((val)&0xFF) + + +//SOFT_RST_CTRL_REG(0x08000020) +#define DAC_SOFT_RST ((1 << 26)|(1 << 28)) +#define ADC2_SOFT_RST ((1 << 27)|(1 << 29)) + + +//MULTIPLE_FUN_CTRL_REG1(0x08000058) +#define IN_DAAD_EN (1 << 25) //ENABLE INTERNAL DAC ADC via i2s + +//FADEOUT_CTRL_REG(0x0080 0070) +#define OSR_MASK (0x7 << 0) +#define OSR(value) (((value)&0x7) << 0) +#define ADC2_OSR_BIT 31 +#define DAC_FILTER_EN (1<<3) + +//ADC_CONF_REG1(0x0080 0098) +#define SEL_VREF (1<< 23) + +//ANALOG_CTRL_REG1(0x0080 009c) +#define DISCHG_HP (29) +#define PD_HP_CTRL (25) +#define PD2_HP (1 << 24) +#define PD1_HP (1 << 23) +#define MASK_HP_GAIN (0x1F << 18) +#define HP_GAIN(val) (((val)&0x1F)<<18) +#define PRE_EN (1 << 17) +#define HP_IN (14) +#define RST_DAC (1 << 13) +#define PD_OP (1 << 12) +#define PD_CK (1 << 11) +#define MASK_DIS_CHG_VCM2 (0x1f<<6) +#define DIS_CHG_VCM2(val) (((val)&0x1f) <<6) +#define EN_VP2V5 (1<<5) +#define PON_VP (1<<4) +#define PL_VCM3 (1 << 2) +#define PD_VCM3 (1 << 1) +#define PD_BIAS (1 << 0) + +//ANALOG_CTRL_REG2(0x0080 00A0) +#define ADC_IN_SEL (29) +#define LIMEN (1<<28) +#define PD_S2D (1 << 27) +#define PD_ADC2 (1 << 26) +#define LINE_GAIN (22) +#define PD_LINE_R (1<<21) +#define PD_MICN (1<<20) +#define PD_MICP (1<<19) +#define MIC_GAINBST (1<<18) +#define MIC_GAIN (15) +#define VREF_SEL (1<<14) +#define MASK_ANTIPOP_EN (0xf<<10) +#define ANTIPOP_EN(val) (((val)&0xf)<<10) +#define AN1_WK_EN (1<<9) +#define AN0_WK_EN (1<<8) +#define EN_BATDIV (1<<7) +#define AIN1_SEL (1<<6) +#define AIN0_SEL (1<<5) +#define BAT_SEL (1<<4) +#define PD_SARADCREF (1<<3) +#define SARADC_IBCTRL (1<<2) +#define SARADC_RESET (1<<1) +#define PD_SARADC (1<<0) + +//ANALOG_CTRL_REG2(0x0080 00A0) +#define VREF_TEST_EN (1<<31) + + +//DAC_CONFIG_REG(0x2011000) +#define ARM_INT (1 << 3) //ARM interrupt enable +#define MUTE (1 << 2) // repeat to sent the Last data to DAC +#define FORMAT (1 << 4) // 1 is used memeory saving format. +#define L2_EN (1 << 1) +#define DAC_CTRL_EN (1 << 0) + +//I2S_CONFIG_REG(0x20110004) +#define LR_CLK (1 << 6) +#define POLARITY_SEL (1 << 5) +#define I2S_CONFIG_WORDLENGTH_MASK (0x1F << 0) + +/////////////HP_IN ADC23_IN +#define SOURCE_DAC (0b100) +#define SOURCE_LINEIN (0b010) +#define SOURCE_MIC (0b001) +#define SIGNAL_SRC_MUTE 0 +#define SIGNAL_SRC_MAX (SOURCE_DAC|SOURCE_LINEIN|SOURCE_MIC) + +#define SOURCE_DAC_MASK (0b100) +#define SOURCE_LINEIN_MASK (0b010) +#define SOURCE_MIC_MASK (0b001) +#define SOURCE_MIXED_ALL_MASK (SOURCE_DAC_MASK|SOURCE_LINEIN_MASK|SOURCE_MIC_MASK) + +//H240 +#define SOURCE_ADC_DAC (0b001) +#define SOURCE_ADC_LINEIN (0b100) +#define SOURCE_ADC_MIC (0b010) +#define SIGNAL_ADC_SRC_MUTE 0 +#define SIGNAL_ADC_SRC_MAX (SOURCE_ADC_DAC|SOURCE_ADC_LINEIN|SOURCE_ADC_MIC) + + + +#define HEADPHONE_GAIN_MIN 0 +#define HEADPHONE_GAIN_MAX 5 +#define LINEIN_GAIN_MIN 0 +#define LINEIN_GAIN_MAX 15 +#define MIC_GAIN_MIN 0 +#define MIC_GAIN_MAX 7 + + +#define PLAYMODE_AUTO_SWITCH (0) /*hp & sp switch*/ +#define PLAYMODE_HP (1) +#define PLAYMODE_SPEAKER (2) + +/** + * platform data of the ak39 pcm driver + */ +struct ak39_codec_platform_data +{ + struct gpio_info hpdet_gpio; /* gpio for headphone detecting */ + struct gpio_info spk_down_gpio; /* gpio for shutdown speaker */ + struct gpio_info hpmute_gpio; /* gpio for headphone de-pipa */ + struct gpio_info linindet_gpio; /* gpio for linein detecting */ + int hp_on_value; /* the gpio value when headphone is pulg */ + int hpdet_irq; /* the irq of heaphone detecting */ + int linindet_irq; /* the irq of linein detecting */ + int bIsHPmuteUsed; /* does this board has headphone de-pipa hardware or not */ + int hp_mute_enable_value; /* the gpio value when enable headphone de-pipa hardware */ + int bIsMetalfixed; /* the ak37 SoC is metal fixed version or not */ + int boutput_only; /* use only 0: hp & sp, 1: hp only, 2:sp only*/ + int detect_flag; +}; + +void ak39_codec_dac_open(void); +void ak39_dac_mute(void); +void ak39_codec_dac_close(void); +unsigned long ak39_codec_set_dac_samplerate(unsigned int samplerate); +void ak39_codec_set_dac_channels(unsigned int chnl); +void ak39_set_hp_power(bool bOn, bool soft_de_pipa); +void ak39_set_hp_gain(unsigned long gain); +void ak39_set_hp_in(unsigned long signal); +void ak39_codec_adc2_open(void); +void ak39_codec_adc2_close(void); +void ak39_set_mic_gain(unsigned long gain); +void ak39_set_linein_gain(unsigned long gain); +void ak39_set_adc2_in(unsigned long signal); +void ak39_set_adc2_channels(unsigned int chnl); +unsigned long ak39_codec_set_adc2_samplerate(unsigned int samplerate); +void ak39_set_linein_power(bool bOn); +void ak39_set_mic_power(bool bOn); +void ak39_set_vcm_ref_power(bool bOn); +void ak39_set_src_power(int dst, int src); +void ak39_set_sp_power(unsigned int pin, bool bOn); +int ak39_codec_probe(struct platform_device *pdev); +int ak39_codec_remove(struct platform_device *pdev); +unsigned long get_dac_actual_samplerate(unsigned long samplerate); +unsigned long get_adc2_actual_samplerate(unsigned long samplerate); + +#endif + diff --git a/arch/arm/mach-ak39/include/mach/clock.h b/arch/arm/mach-ak39/include/mach/clock.h new file mode 100644 index 00000000..bcf89757 --- /dev/null +++ b/arch/arm/mach-ak39/include/mach/clock.h @@ -0,0 +1,164 @@ +/* + * arch/arm/mach-ak39/include/mach/clock.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef _ANYKA_CLK_H_ +#define _ANYKA_CLK_H_ + +#include + +#define CLOCK_CPU_PLL_CTRL (AK_VA_SYSCTRL + 0x04) +#define CLOCK_ASIC_PLL_CTRL (AK_VA_SYSCTRL + 0x08) +#define CLOCK_ADC2_DAC_CTRL (AK_VA_SYSCTRL + 0x0C) +#define CLOCK_ADC2_DAC_HS_CTRL (AK_VA_SYSCTRL + 0x10) +#define CLOCK_PERI_PLL_CTRL1 (AK_VA_SYSCTRL + 0x14) +#define CLOCK_PERI_PLL_CTRL2 (AK_VA_SYSCTRL + 0x18) +#define CLOCK_GATE_CTRL1 (AK_VA_SYSCTRL + 0x1C) +#define CLOCK_SOFT_RESET (AK_VA_SYSCTRL + 0x20) + +/* clock gate control register bit */ +#define AK_CLKCON_MCLK_DRAM (1 << 24) +#define AK_CLKCON_VCLK2_VIDEO (1 << 20) +#define AK_CLKCON_VCLK1_CAMERA (1 << 19) +#define AK_CLKCON_ASICCLK_MIPI (1 << 16) +#define AK_CLKCON_ASICCLK_USB (1 << 15) +#define AK_CLKCON_ASICCLK_ENCRY (1 << 14) +#define AK_CLKCON_ASICCLK_MAC (1 << 13) +#define AK_CLKCON_ASICCLK_GPIO (1 << 12) +#define AK_CLKCON_ASICCLK_IRDA (1 << 11) +#define AK_CLKCON_ASICCLK_I2C (1 << 10) +#define AK_CLKCON_ASICCLK_L2MEM (1 << 9) +#define AK_CLKCON_ASICCLK_UART2 (1 << 8) +#define AK_CLKCON_ASICCLK_UART1 (1 << 7) +#define AK_CLKCON_ASICCLK_SPI2 (1 << 6) +#define AK_CLKCON_ASICCLK_SPI1 (1 << 5) +#define AK_CLKCON_ASICCLK_DAC (1 << 4) +#define AK_CLKCON_ASICCLK_ADC (1 << 3) +#define AK_CLKCON_ASICCLK_SDIO (1 << 2) +#define AK_CLKCON_ASICCLK_MCI (1 << 1) + +/* ADC2/DAC clock control register */ +#define AK_CLKCON_CLK_DAC (1 << 28) +#define AK_CLKCON_CLK_ADC1 (1 << 3) + +/* ADC2/DAC high speed clock control register */ +#define AK_CLKCON_HCLK_ADC2 (1 << 28) +#define AK_CLKCON_HCLK_DAC (1 << 18) +#define AK_CLKCON_CLK_ADC2 (1 << 8) + + +/* PERI PLL channel clock control register1 */ +//1: peri pll 0: external 12MHz +#define AK_CLKCON_CLK_PHY_SEL (1 << 19) +//1: peri pll 0: external 25MHz +#define AK_CLKCON_CLK_MAC_SEL (1 << 18) +#define AK_CLKCON_CLK_12M (1 << 17) +#define AK_CLKCON_CLK_25M (1 << 16) +#define AK_CLKCON_CLK_25M_IN (1 << 14) + + +/* PERI PLL channel clock control register2 */ +/* 1: positive clk 0: negative clk */ +#define AK_CLKCON_PCLK_CIS (1 << 20) //camera +#define AK_CLKCON_SCLK_CIS (1 << 18) //sensor +#define AK_CLKCON_CLK_OPCLK (1 << 8) //MAC + + +/** + * struct clk_ops - standard clock operations + * @set_rate: set the clock rate, see clk_set_rate(). + * @get_rate: get the clock rate, see clk_get_rate(). + * @round_rate: round a given clock rate, see clk_round_rate(). + * @set_parent: set the clock's parent, see clk_set_parent(). + * + * Group the common clock implementations together so that we + * don't have to keep setting the same fields again. We leave + * enable in struct clk. + * + * Adding an extra layer of indirection into the process should + * not be a problem as it is unlikely these operations are going + * to need to be called quickly. + */ +struct clk_ops { + int (*set_rate)(struct clk *c, unsigned long rate); + unsigned long (*get_rate)(struct clk *c); + unsigned long (*round_rate)(struct clk *c, unsigned long rate); + int (*set_parent)(struct clk *c, struct clk *parent); +}; + +struct clk { + struct list_head list; + struct module *owner; + struct clk *parent; + const char *name; + const char *devname; + int id; + int usage; + unsigned long rate; + unsigned long ctrlbit; + + struct clk_lookup lookup; + struct clk_ops *ops; + int (*enable)(struct clk *, int enable); +#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS) + struct dentry *dent; /* For visible tree hierarchy */ +#endif +}; + + +/* core clock support */ +extern struct clk clk_xtal_12M; +extern struct clk clk_xtal_25M; +extern struct clk clk_xtal_32K; +extern struct clk clk_pll; +extern struct clk clk_cpu; +extern struct clk clk_ahb; +extern struct clk clk_mem; +extern struct clk clk_vclk; +extern struct clk clk_asic; + + +/* other clocks which may be registered by board support */ + +typedef enum { + CPU_MODE_NORMAL, + CPU_MODE_CPU2X, +} T_cpu_mode; + +typedef enum { + MEM_MODE_NORMAL, + MEM_MODE_CPU2X, +} T_mem_mode; + + +#define AK39_CLK_CPU2X_MODE (1 << 24) +#define AK39_CLK_MEM2X_MODE (1 << 30) // CDH:H3 ADD DPHYCLK2X + + +#define MHz 1000000UL + +T_cpu_mode ak_get_cpu_mode(void); +//bool ak_cpu_is_3x_mode(void); +bool ak_cpu_is_2x_mode(void); +bool ak_cpu_is_normal_mode(void); + +unsigned long ak_get_cpu_pll_clk(void); +unsigned long ak_get_asic_pll_clk(void); +unsigned long ak_get_peri_pll_clk(void); +int ak_set_cis_pclk(int pclk_mhz); +int ak_set_cis_pclk_sel(int is_internal); +int ak_set_cis_mipi_dvp_sel(int is_mipi); +int ak_set_mipi_byte_rst(int is_rst); +unsigned long ak_get_pll_clk(void); +unsigned long ak_get_cpu_clk(void); +unsigned long ak_get_ahb_clk(void); +unsigned long ak_get_mem_clk(void); +unsigned long ak_get_vclk(void); +unsigned long ak_get_asic_clk(void); +void aisc_freq_set(void); + +#endif diff --git a/arch/arm/mach-ak39/include/mach/cpufreq.h b/arch/arm/mach-ak39/include/mach/cpufreq.h new file mode 100644 index 00000000..e1a6ccc4 --- /dev/null +++ b/arch/arm/mach-ak39/include/mach/cpufreq.h @@ -0,0 +1,33 @@ +#ifndef __CPUFREQ_H__ +#define __CPUFREQ_H__ + +#if 0 +#include +#include + +#define MEM_CLK_DIV 0x7 +#define ASIC_CLK_DIV 0x7 + +#define PLL_CLK_MIN 180 +#define PLL_CLK_MAX (PLL_CLK_MIN + 4*(0x3F)) + +//#define CPUFREQ_DEBUG + +/* clock divider register */ +#define PLL_CHANGE_ENA (1 << 12) +#define CLOCK_ASIC_MEM_ENA (1 << 14) + +struct cpufreq_mode_clkdiv { + T_OPERATION_MODE mode_name; + unsigned int pll_sel; + unsigned int clk168_div; + unsigned int cpu_div; + unsigned int mem_div; + unsigned int asic_div; + unsigned int low_clock; + unsigned int is_3x; +}; + +#endif + +#endif /* end __CPUFREQ_H__ */ diff --git a/arch/arm/mach-ak39/include/mach/devices.h b/arch/arm/mach-ak39/include/mach/devices.h new file mode 100644 index 00000000..c1eaac97 --- /dev/null +++ b/arch/arm/mach-ak39/include/mach/devices.h @@ -0,0 +1,40 @@ +#ifndef _MACH_DEVICES_H +#define _MACH_DEVICES_H + + +extern struct platform_device ak39_uart0_device; +extern struct platform_device ak39_uart1_device; +extern struct platform_device ak39_gpio_uart_device; + +extern struct platform_device ak39_mci1_device; +extern struct platform_device ak39_mci2_device; +extern struct platform_device ak39_pwm_device; + +extern struct platform_device ak39_i2c_device; + +extern struct platform_device ak39_usb_udc_device; +extern struct platform_device ak39_usb_otg_hcd_device; +extern struct platform_device ak39_mac_device; + +extern struct platform_device ak39_spi1_device; +extern struct platform_device ak39_camera_interface; +extern struct platform_device ak39_pcm_device; +extern struct platform_device ak39_mmx_device; + +extern struct platform_device ak39_ion_device; +extern struct platform_device ak39_led_pdev; +extern struct platform_device ak39_gpio_keys_device; +extern struct platform_device ak39_battery_power; +extern struct platform_device ak39_rtc_device; + +extern struct platform_device akfha_char_device; + +extern struct platform_device ak39_motor0_device; +extern struct platform_device ak39_motor1_device; + +extern struct platform_device ak39_crypto_device; + +extern struct platform_device ak39_pm_device; + +#endif /* endif _MACH_DEVICES_H */ + diff --git a/arch/arm/mach-ak39/include/mach/entry-macro.S b/arch/arm/mach-ak39/include/mach/entry-macro.S new file mode 100644 index 00000000..279c7aab --- /dev/null +++ b/arch/arm/mach-ak39/include/mach/entry-macro.S @@ -0,0 +1,144 @@ +/* + * include/asm-arm/arch-ak39/entry-macro.S + * + * Low-level IRQ helper macros for AK39-based platforms + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. +*/ + +#include +#include + +#define AK39_IRQ_INTMASK (AK_VA_SYSCTRL + 0x24) +#define AK39_FIQ_INTMASK (AK_VA_SYSCTRL + 0x28) +#define AK39_INT_STATUS (AK_VA_SYSCTRL + 0x4C) + + .macro get_irqnr_preamble, base, tmp + .endm + + .macro arch_ret_to_user, tmp1, tmp2 + .endm + + .macro get_irqnr_and_base, irqnr, irqstat, base, tmp + + ldr \base, =AK39_INT_STATUS + ldr \irqstat, [\base] @ get interrupts status + teq \irqstat, #0x0 + beq 1002f + + ldr \base, =AK39_IRQ_INTMASK @ get interrupts mask + ldr \base, [\base] + and \irqstat, \irqstat, \base + + tst \irqstat, #(1< +#include +#include "map.h" + + +#define AK_WAKEUP_ENABLE 1 +#define AK_WAKEUP_DISABLE 0 +#define AK_FALLING_TRIGGERED 1 +#define AK_RISING_TRIGGERED 0 + +#define AK_GPIO_DIR_OUTPUT 1 +#define AK_GPIO_DIR_INPUT 0 +#define AK_GPIO_INT_DISABLE 0 +#define AK_GPIO_INT_ENABLE 1 +#define AK_GPIO_INT_LOWLEVEL 0 +#define AK_GPIO_INT_HIGHLEVEL 1 + +#define AK_GPIO_DISABLE (0) +#define AK_GPIO_ENABLE (1) +#define AK_GPIO_OUT_LOW 0 +#define AK_GPIO_OUT_HIGH 1 + +#define AK_PULLUP_DISABLE 0 +#define AK_PULLUP_ENABLE 1 +#define AK_PULLDOWN_DISABLE 0 +#define AK_PULLDOWN_ENABLE 1 + +#define GPIO_PIN_MODE_GPIO 0 +#define GPIO_PIN_MODE_INT 1 + +#define ATTR_FIXED_1 1 +#define ATTR_FIXED_0 0 +#define PIN_ATTE_LINE 6 + +#undef END_FLAG +#define END_FLAG 0xff +#define INVALID_GPIO (-1) + + +#define AK_FALSE 0 +#define AK_TRUE 1 +#undef AK_NULL +#define AK_NULL ((void *)(0)) + + +/**************** gpio offsets ************************/ +#define AK_GPIO_GROUP1 (32*0) +#define AK_GPIO_GROUP2 (32*1) + +#define AK_GPIO_GROUP1_NO(offset) ( AK_GPIO_GROUP1 + (offset)) +#define AK_GPIO_GROUP2_NO(offset) ( AK_GPIO_GROUP2 + (offset)) + +#define AK_GPIO_0 AK_GPIO_GROUP1_NO(0) +#define AK_GPIO_1 AK_GPIO_GROUP1_NO(1) +#define AK_GPIO_2 AK_GPIO_GROUP1_NO(2) +#define AK_GPIO_3 AK_GPIO_GROUP1_NO(3) +#define AK_GPIO_4 AK_GPIO_GROUP1_NO(4) +#define AK_GPIO_5 AK_GPIO_GROUP1_NO(5) +#define AK_GPIO_6 AK_GPIO_GROUP1_NO(6) +#define AK_GPIO_7 AK_GPIO_GROUP1_NO(7) +#define AK_GPIO_8 AK_GPIO_GROUP1_NO(8) +#define AK_GPIO_9 AK_GPIO_GROUP1_NO(9) +#define AK_GPIO_10 AK_GPIO_GROUP1_NO(10) +#define AK_GPIO_11 AK_GPIO_GROUP1_NO(11) +#define AK_GPIO_12 AK_GPIO_GROUP1_NO(12) +#define AK_GPIO_13 AK_GPIO_GROUP1_NO(13) +#define AK_GPIO_14 AK_GPIO_GROUP1_NO(14) +#define AK_GPIO_15 AK_GPIO_GROUP1_NO(15) +#define AK_GPIO_16 AK_GPIO_GROUP1_NO(16) +#define AK_GPIO_17 AK_GPIO_GROUP1_NO(17) +#define AK_GPIO_18 AK_GPIO_GROUP1_NO(18) +#define AK_GPIO_19 AK_GPIO_GROUP1_NO(19) +#define AK_GPIO_20 AK_GPIO_GROUP1_NO(20) +#define AK_GPIO_21 AK_GPIO_GROUP1_NO(21) +#define AK_GPIO_22 AK_GPIO_GROUP1_NO(22) +#define AK_GPIO_23 AK_GPIO_GROUP1_NO(23) +#define AK_GPIO_24 AK_GPIO_GROUP1_NO(24) +#define AK_GPIO_25 AK_GPIO_GROUP1_NO(25) +#define AK_GPIO_26 AK_GPIO_GROUP1_NO(26) +#define AK_GPIO_27 AK_GPIO_GROUP1_NO(27) +#define AK_GPIO_28 AK_GPIO_GROUP1_NO(28) +#define AK_GPIO_29 AK_GPIO_GROUP1_NO(29) +#define AK_GPIO_30 AK_GPIO_GROUP1_NO(30) +#define AK_GPIO_31 AK_GPIO_GROUP1_NO(31) + +#define AK_GPIO_32 AK_GPIO_GROUP2_NO(0) +#define AK_GPIO_33 AK_GPIO_GROUP2_NO(1) +#define AK_GPIO_34 AK_GPIO_GROUP2_NO(2) +#define AK_GPIO_35 AK_GPIO_GROUP2_NO(3) +#define AK_GPIO_36 AK_GPIO_GROUP2_NO(4) +#define AK_GPIO_37 AK_GPIO_GROUP2_NO(5) +#define AK_GPIO_38 AK_GPIO_GROUP2_NO(6) +#define AK_GPIO_39 AK_GPIO_GROUP2_NO(7) +#define AK_GPIO_40 AK_GPIO_GROUP2_NO(8) +#define AK_GPIO_41 AK_GPIO_GROUP2_NO(9) +#define AK_GPIO_42 AK_GPIO_GROUP2_NO(10) +#define AK_GPIO_43 AK_GPIO_GROUP2_NO(11) +#define AK_GPIO_44 AK_GPIO_GROUP2_NO(12) +#define AK_GPIO_45 AK_GPIO_GROUP2_NO(13) +#define AK_GPIO_46 AK_GPIO_GROUP2_NO(14) +#define AK_GPIO_47 AK_GPIO_GROUP2_NO(15) +#define AK_GPIO_48 AK_GPIO_GROUP2_NO(16) +#define AK_GPIO_49 AK_GPIO_GROUP2_NO(17) +#define AK_GPIO_50 AK_GPIO_GROUP2_NO(18) +#define AK_GPIO_51 AK_GPIO_GROUP2_NO(19) +#define AK_GPIO_52 AK_GPIO_GROUP2_NO(20) +#define AK_GPIO_53 AK_GPIO_GROUP2_NO(21) +#define AK_GPIO_54 AK_GPIO_GROUP2_NO(22) +#define AK_GPIO_55 AK_GPIO_GROUP2_NO(23) +#define AK_GPIO_56 AK_GPIO_GROUP2_NO(24) +#define AK_GPIO_57 AK_GPIO_GROUP2_NO(25) +#define AK_GPIO_58 AK_GPIO_GROUP2_NO(26) +#define AK_GPIO_59 AK_GPIO_GROUP2_NO(27) +#define AK_GPIO_60 AK_GPIO_GROUP2_NO(28) +#define AK_GPIO_61 AK_GPIO_GROUP2_NO(29) +#define AK_GPIO_62 AK_GPIO_GROUP2_NO(30) +#define AK_GPIO_63 AK_GPIO_GROUP2_NO(31) +#define AK_GPIO_64 AK_GPIO_GROUP2_NO(32) +#define AK_GPIO_65 AK_GPIO_GROUP2_NO(33) +#define AK_GPIO_66 AK_GPIO_GROUP2_NO(34) +#define AK_GPIO_67 AK_GPIO_GROUP2_NO(35) +#define AK_GPIO_68 AK_GPIO_GROUP2_NO(36) +#define AK_GPIO_69 AK_GPIO_GROUP2_NO(37) +#define AK_GPIO_70 AK_GPIO_GROUP2_NO(38) +#define AK_GPIO_71 AK_GPIO_GROUP2_NO(39) +#define AK_GPIO_72 AK_GPIO_GROUP2_NO(40) +#define AK_GPIO_73 AK_GPIO_GROUP2_NO(41) +#define AK_GPIO_74 AK_GPIO_GROUP2_NO(42) +#define AK_GPIO_75 AK_GPIO_GROUP2_NO(43) +#define AK_GPIO_76 AK_GPIO_GROUP2_NO(44) +#define AK_GPIO_77 AK_GPIO_GROUP2_NO(45) +#define AK_GPIO_78 AK_GPIO_GROUP2_NO(46) +#define AK_GPIO_79 AK_GPIO_GROUP2_NO(47) +#define AK_GPIO_80 AK_GPIO_GROUP2_NO(48) +#define AK_GPIO_81 AK_GPIO_GROUP2_NO(49) +#define AK_GPIO_82 AK_GPIO_GROUP2_NO(50) +#define AK_GPIO_83 AK_GPIO_GROUP2_NO(51) +#define AK_GPIO_84 AK_GPIO_GROUP2_NO(52) +#define AK_GPIO_85 AK_GPIO_GROUP2_NO(53) + + +#define AK_GPIO_MIN AK_GPIO_0 +#define AK_GPIO_MAX AK_GPIO_85 +#define GPIO_UPLIMIT AK_GPIO_MAX + +/****************** access gpio register addr **********************/ +#define AK_GPIO_DIR1 (AK_VA_GPIO + 0x00) +#define AK_GPIO_DIR2 (AK_VA_GPIO + 0x04) +#define AK_GPIO_DIR3 (AK_VA_GPIO + 0x08)//adh:caohao 2018.11.5 + +#define AK_GPIO_OUT1 (AK_VA_GPIO + 0x0C) +#define AK_GPIO_OUT2 (AK_VA_GPIO + 0x10) +#define AK_GPIO_OUT3 (AK_VA_GPIO + 0x14) + +#define AK_GPIO_INPUT1 (AK_VA_GPIO + 0x18) +#define AK_GPIO_INPUT2 (AK_VA_GPIO + 0x1C) +#define AK_GPIO_INPUT3 (AK_VA_GPIO + 0x20) + + +#define AK_GPIO_INT_MASK1 (AK_VA_GPIO + 0x24) +#define AK_GPIO_INT_MASK2 (AK_VA_GPIO + 0x28) +#define AK_GPIO_INT_MASK3 (AK_VA_GPIO + 0x2C) + +#define AK_GPIO_INT_MODE1 (AK_VA_GPIO + 0x30) +#define AK_GPIO_INT_MODE2 (AK_VA_GPIO + 0x34) +#define AK_GPIO_INT_MODE3 (AK_VA_GPIO + 0x38) + +#define AK_GPIO_INTP1 (AK_VA_GPIO + 0x3C) +#define AK_GPIO_INTP2 (AK_VA_GPIO + 0x40) +#define AK_GPIO_INTP3 (AK_VA_GPIO + 0x44) + +#define AK_GPIO_EDGE_STATUS1 (AK_VA_GPIO + 0x48) +#define AK_GPIO_EDGE_STATUS2 (AK_VA_GPIO + 0x4C) +#define AK_GPIO_EDGE_STATUS3 (AK_VA_GPIO + 0x50) + +#define AK_PPU_PPD1 (AK_VA_SYSCTRL + 0x80) +#define AK_PPU_PPD2 (AK_VA_SYSCTRL + 0x84) +#define AK_PPU_PPD3 (AK_VA_SYSCTRL + 0x88) +#define AK_PPU_PPD4 (AK_VA_SYSCTRL + 0xE0) + +#define AK_SHAREPIN_CON1 (AK_VA_SYSCTRL + 0x74) +#define AK_SHAREPIN_CON2 (AK_VA_SYSCTRL + 0x78) +#define AK_SHAREPIN_CON3 (AK_VA_SYSCTRL + 0x7C) +#define AK_SHAREPIN_CON4 (AK_VA_SYSCTRL + 0xDC) +#define AK_PERIPLL_CTRL_REG1 (AK_VA_SYSCTRL + 0x14) // cdh:add +#define AK_ALALOG_CTRL_REG3 (AK_VA_SYSCTRL + 0xA4) // cdh:add +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#define AK_GPIO_DIR_BASE(pin) (((pin)>>5)*4 + AK_GPIO_DIR1) +#define AK_GPIO_OUT_BASE(pin) (((pin)>>5)*4 + AK_GPIO_OUT1) + +#define AK_GPIO_IN_BASE(pin) (((pin)>>5)*4 + AK_GPIO_INPUT1) +#define AK_GPIO_INTEN_BASE(pin) (((pin)>>5)*4 + AK_GPIO_INT_MASK1) +#define AK_GPIO_INTPOL_BASE(pin) (((pin)>>5)*4 + AK_GPIO_INTP1) +#define AK_PPU_PPD_BASE(pin) (((pin)>>5)*4 + AK_PPU_PPD1) + +/****************** end access gpio register addr **********************/ + +#define AK_WGPIO_POLARITY (AK_VA_SYSCTRL + 0x3C) +#define AK_WGPIO_CLEAR (AK_VA_SYSCTRL + 0x40) +#define AK_WGPIO_ENABLE (AK_VA_SYSCTRL + 0x44) +#define AK_WGPIO_STATUS (AK_VA_SYSCTRL + 0x48) + +#define AK_PA_WGPIO_POLARITY (AK_PA_SYSCTRL + 0x3C) +#define AK_PA_WGPIO_CLEAR (AK_PA_SYSCTRL + 0x40) +#define AK_PA_WGPIO_ENABLE (AK_PA_SYSCTRL + 0x44) +#define AK_PA_WGPIO_STATUS (AK_PA_SYSCTRL + 0x48) + +#undef REG32 +#define REG32(_reg) (*(volatile unsigned long *)(_reg)) +#undef REG16 +#define REG16(_reg) (*(volatile unsigned short *)(_reg)) + +#define AK_GPIO_UART1_FLOW(x) REG32(AK_VA_SYSCTRL + 0x78) &= ~(0xa << 8) + +/****************** enum defined **********************/ +typedef enum { + ePIN_AS_GPIO = 0, // All pin as gpio + ePIN_AS_OPCLK, + ePIN_AS_JTAG, // share pin as JTAG + ePIN_AS_RTCK, // share pin as watch dog + ePIN_AS_I2S, // share pin as I2S + ePIN_AS_PWM1, // share pin80 as PWM1 + ePIN_AS_PWM2, // share pin69 as PWM2 + ePIN_AS_PWM3, // share pin5 as PWM3 + ePIN_AS_PWM4, // share pin66 as PWM4 + ePIN_AS_PWM5, // share pin47 as PWM5 + ePIN_AS_UART1, // share pin as UART1 + ePIN_AS_UART2, // share pin as UART2 + ePIN_AS_CAMERA, // share pin as CAMERA + ePIN_AS_MCI, // share pin as MDAT1, 4 lines + ePIN_AS_MCI_8LINE, // share pin as MDAT1, 8 lines + ePIN_AS_SDIO, // share pin as SDIO + ePIN_AS_SPI1, // share pin as SPI1 + ePIN_AS_SPI2, // share pin as SPI2 + ePIN_AS_MAC, // share pin as Ethernet MAC with MII interface + ePIN_AS_RMAC, // share pin as Ethernet RMAC with RMII interface + ePIN_AS_I2C, // share pin as I2C + ePIN_AS_IRDA, // share png as IrDA + ePIN_AS_RAM, // share pin as RAM Controller + ePIN_AS_MIPI, // share pin as MIPI Controller + + ePIN_AS_DUMMY + } T_GPIO_SHAREPIN_CFG ; + +typedef enum { + SHARE_CONFG1 = 0, + SHARE_CONFG2 +} T_SHARE_CONFG; + +typedef enum { + SHARE_CFG1 = 0, // share cfg1 + SHARE_CFG2, // share cfg2 + SHARE_CFG3, // share cfg2 + SHARE_CFG4, // share cfg4 + SHARE_CFG12, // share cfg1 and share cfg2 as used + SHARE_CFG13, // share cfg1 and share cfg3 as used + SHARE_CFG14, // share cfg1 and share cfg3 as used + SHARE_CFG23, // share cfg2 and share cfg2 as used + SHARE_CFG123, // share cfg1, share config2 and cfg3 as used + SHARE_CFG134, // share cfg1, share config3 and cfg4 as used + EXIT_CFG +}T_SHARE_CFG; + +struct gpio_sharepin_cfg { + T_GPIO_SHAREPIN_CFG func_module; + T_SHARE_CFG share_config; + unsigned long reg1_bit_mask; + unsigned long reg1_bit_value; + unsigned long reg2_bit_mask; + unsigned long reg2_bit_value; + unsigned long reg3_bit_mask; + unsigned long reg3_bit_value; + unsigned long reg4_bit_mask; + unsigned long reg4_bit_value; +}; + +typedef enum { + PULLUP = 0, + PULLDOWN, + PULLUPDOWN, + UNDEFINED + } T_PUPD_TYPE ; + +typedef enum { + PUPD_CFG1 = 0, // share cfg1 + PUPD_CFG2, // share cfg2 + PUPD_CFG3, // share cfg3 + PUPD_CFG4, // share cfg4 +}T_PUPD_CFG; + +typedef enum { + AS_GPIO_CFG_BIT1 = 0, // share cfg1 1 + AS_GPIO_CFG_BIT2, // share cfg2 00 + AS_GPIO_CFG_BIT3, // share cfg2 01 + AS_GPIO_CFG_BIT4, // share cfg2 11 +}T_AS_GPIO_CFG; + +struct gpio_pupd_cfg { + int pin; + int index; + T_PUPD_CFG pupd_cfg; + T_PUPD_TYPE pupd_type; +}; + +struct sharepin_as_gpio { + unsigned char gpio_start; + unsigned char gpio_end; + int index; + T_AS_GPIO_CFG flag; +}; + +struct t_gpio_wakeup_cfg { + unsigned char gpio_start; + unsigned char gpio_end; + unsigned char start_bit; +}; + +struct gpio_info { + int pin; + char pulldown; + char pullup; + char value; + char dir; + char int_pol; + char pwr_dn; ///< Power Down Level Defination, Default = 0. +}; + +struct user_gpio_info { + const char *name; + struct gpio_info info; +}; + +struct gpio_api_lut { + unsigned int pin_start; + unsigned int pin_end; + + int (*setpin_as_gpio) (unsigned int pin); + int (*gpio_pullup)(unsigned int pin, unsigned char enable); + int (*gpio_pulldown)(unsigned int pin, unsigned char enable); + + int (*gpio_dircfg)(unsigned int pin, unsigned int to); + + int (*gpio_intcfg)(unsigned int pin, unsigned int enable); + int (*gpio_set_intpol)(unsigned int pin, unsigned int level); + + int (*gpio_setpin)(unsigned int pin, unsigned int to); + int (*gpio_getpin)(unsigned int pin); + + int (*gpio_to_irq)(unsigned int pin); + int (*irq_to_gpio)(unsigned int irq); +}; + +void ak_group_config(T_GPIO_SHAREPIN_CFG mod_name); + +/* set gpio's wake up polarity*/ +void ak_gpio_wakeup_pol(unsigned int pin, unsigned char pol); +/* enable/disable gpio wake up function*/ +int ak_gpio_wakeup(unsigned int pin, unsigned char enable); + +int ak_setpin_as_gpio (unsigned int pin); +int ak_gpio_setpin(unsigned int pin, unsigned int to); +int ak_gpio_getpin(unsigned int pin); +int ak_gpio_pullup(unsigned int pin, unsigned char enable); +int ak_gpio_pulldown(unsigned int pin, unsigned char enable); +/* new version of ak_gpio_cfgpin */ +int ak_gpio_dircfg(unsigned int pin, unsigned int to); +/* new version of ak_gpio_inten*/ +int ak_gpio_intcfg(unsigned int pin, unsigned int enable); +/* new version of ak_gpio_intpol*/ +int ak_gpio_set_intpol(unsigned int pin, unsigned int level); + +/* to support backward compatibility*/ +int ak_gpio_cfgpin(unsigned int pin, unsigned int to); +int ak_gpio_inten(unsigned int pin, unsigned int enable); +int ak_gpio_intpol(unsigned int pin, unsigned int level); +int reg_set_mutli_bit(void __iomem *reg, unsigned int value, int bit, int index); + +int ak_gpio_to_irq(unsigned int pin); +int ak_irq_to_gpio(unsigned int irq); + +extern int ak_gpio_request(unsigned long gpio, const char *label); +extern void ak_gpio_free(unsigned long gpio); + + +/*************** wrap gpio interface again ****************/ +void ak_gpio_set(const struct gpio_info *info); + +int g_ak39_setpin_as_gpio(unsigned int pin); +void g_ak39_setgroup_attribute(T_GPIO_SHAREPIN_CFG mod_name); +int g_ak39_gpio_pullup(unsigned int pin, unsigned char enable); +int g_ak39_gpio_pulldown(unsigned int pin, unsigned char enable); +int g_ak39_gpio_cfgpin(unsigned int pin, unsigned int to); +int g_ak39_gpio_setpin(unsigned int pin, unsigned int to); +int g_ak39_gpio_getpin(unsigned int pin); +int g_ak39_gpio_inten(unsigned int pin, unsigned int enable); +int g_ak39_gpio_intpol(unsigned int pin, unsigned int level); + +int g_ak39_gpio_to_irq(unsigned int pin); +int g_ak39_irq_to_gpio(unsigned int irq); + +/*************** end wrap gpio interface again ****************/ + +/*************** sharepin set again ****************/ +//# define SPI_GPIO_37_TO_40 +/*************** end sharepin set again****************/ + +#endif /* __GPIO_H__ */ + diff --git a/arch/arm/mach-ak39/include/mach/hardware.h b/arch/arm/mach-ak39/include/mach/hardware.h new file mode 100644 index 00000000..66488e52 --- /dev/null +++ b/arch/arm/mach-ak39/include/mach/hardware.h @@ -0,0 +1,24 @@ +/* arch/arm/mach-s3c2410/include/mach/hardware.h + * + * Copyright (c) 2003 Simtec Electronics + * Ben Dooks + * + * S3C2410 - hardware + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __ASM_ARCH_HARDWARE_H +#define __ASM_ARCH_HARDWARE_H + +#ifndef __ASSEMBLY__ +#endif /* __ASSEMBLY__ */ + +#include +#include + +/* machine specific hardware definitions should go after this */ + +#endif /* __ASM_ARCH_HARDWARE_H */ diff --git a/arch/arm/mach-ak39/include/mach/i2c.h b/arch/arm/mach-ak39/include/mach/i2c.h new file mode 100644 index 00000000..f89434f2 --- /dev/null +++ b/arch/arm/mach-ak39/include/mach/i2c.h @@ -0,0 +1,57 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __AK39_IIC_H +#define __AK39_IIC_H __FILE__ + +#include + +#define I2C_CLKPIN (27) +#define I2C_DATPIN (28) +#define I2C_INTPIN (24) + +#define AK39_I2C_NACKEN (1 << 17) +#define AK39_I2C_NOSTOP (1 << 16) +#define AK39_I2C_ACKEN (1 << 15) +#define AK39_I2C_START (1 << 14) +#define AK39_I2C_TXRXSEL (1 << 13) +#define AK39_I2C_TRX_BYTE (9) +#define AK39_I2C_CLR_DELAY (0x3 << 7) +#define AK39_I2C_SDA_DELAY (0x2 << 7) +#define AK39_I2C_TXDIV_512 (1 << 6) +#define AK39_I2C_INTEN (1 << 5) + +#define INT_PEND_FLAG (1 << 4) +#define AK39_TX_CLK_DIV (0xf) + +#define AK39_I2C_CMD_EN (1 << 18) +#define AK39_I2C_START_BIT (1 << 17) + +#define AK39_I2C_READ 1 +#define AK39_I2C_WRITE 0 + +#define AK39_I2C_CTRL REG_VA_ADDR(AK_VA_I2C, 0x00) +#define AK39_I2C_CMD1 REG_VA_ADDR(AK_VA_I2C, 0x10) +#define AK39_I2C_CMD2 REG_VA_ADDR(AK_VA_I2C, 0x14) +#define AK39_I2C_CMD3 REG_VA_ADDR(AK_VA_I2C, 0x18) +#define AK39_I2C_CMD4 REG_VA_ADDR(AK_VA_I2C, 0x1C) +#define AK39_I2C_DATA0 REG_VA_ADDR(AK_VA_I2C, 0x20) +#define AK39_I2C_DATA1 REG_VA_ADDR(AK_VA_I2C, 0x24) +#define AK39_I2C_DATA2 REG_VA_ADDR(AK_VA_I2C, 0x28) +#define AK39_I2C_DATA3 REG_VA_ADDR(AK_VA_I2C, 0x2C) + + +struct ak39_platform_i2c { + int bus_num; + unsigned int flags; + unsigned int slave_addr; + unsigned long frequency; + unsigned int sda_delay; + struct gpio_info *gpios; + int npins; +}; + +#endif /* __AK39_IIC_H */ diff --git a/arch/arm/mach-ak39/include/mach/irqs.h b/arch/arm/mach-ak39/include/mach/irqs.h new file mode 100644 index 00000000..a93ba1c0 --- /dev/null +++ b/arch/arm/mach-ak39/include/mach/irqs.h @@ -0,0 +1,154 @@ +/* linux/arch/arm/mach-ak39/include/mach/irqs.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __ASM_ARCH_IRQS_H_ +#define __ASM_ARCH_IRQS_H_ + +#define AK39_IRQ(x) (x) + +/* + * Main CPU Interrupts + */ +#define IRQ_MEM AK39_IRQ(0) +#define IRQ_CAMERA AK39_IRQ(1) +#define IRQ_VIDEO_ENCODER AK39_IRQ(2) +#define IRQ_SYSCTRL AK39_IRQ(3) +#define IRQ_MCI AK39_IRQ(4) +#define IRQ_SDIO AK39_IRQ(5) +#define IRQ_ADC2 AK39_IRQ(6) +#define IRQ_DAC AK39_IRQ(7) +#define IRQ_SPI1 AK39_IRQ(8) +#define IRQ_SPI2 AK39_IRQ(9) +#define IRQ_UART0 AK39_IRQ(10) +#define IRQ_UART1 AK39_IRQ(11) +#define IRQ_L2MEM AK39_IRQ(12) +#define IRQ_I2C AK39_IRQ(13) +#define IRQ_IRDA AK39_IRQ(14) +#define IRQ_GPIO AK39_IRQ(15) +#define IRQ_MAC AK39_IRQ(16) +#define IRQ_ENCRYTION AK39_IRQ(17) +#define IRQ_USBOTG_MCU AK39_IRQ(18) +#define IRQ_USBOTG_DMA AK39_IRQ(19) + +/* + * System Control Module Sub-IRQs + */ +#define IRQ_SYSCTRL_START (IRQ_USBOTG_DMA + 1) +#define AK39_SYSCTRL_IRQ(x) (IRQ_SYSCTRL_START + (x)) + +#define IRQ_SARADC AK39_SYSCTRL_IRQ(0) +#define IRQ_TIMER5 AK39_SYSCTRL_IRQ(1) +#define IRQ_TIMER4 AK39_SYSCTRL_IRQ(2) +#define IRQ_TIMER3 AK39_SYSCTRL_IRQ(3) +#define IRQ_TIMER2 AK39_SYSCTRL_IRQ(4) +#define IRQ_TIMER1 AK39_SYSCTRL_IRQ(5) +#define IRQ_WGPIO AK39_SYSCTRL_IRQ(6) +#define IRQ_RTC_RDY AK39_SYSCTRL_IRQ(7) +#define IRQ_RTC_ALARM AK39_SYSCTRL_IRQ(8) +#define IRQ_RTC_TIMER AK39_SYSCTRL_IRQ(9) +#define IRQ_RTC_WATCHDOG AK39_SYSCTRL_IRQ(10) + +/* + * GPIO IRQs + */ +#define IRQ_GPIO_START (IRQ_RTC_WATCHDOG + 1) +#define AK39_GPIO_IRQ(x) (IRQ_GPIO_START + (x)) + +#define IRQ_GPIO_0 AK39_GPIO_IRQ(0) +#define IRQ_GPIO_1 AK39_GPIO_IRQ(1) +#define IRQ_GPIO_2 AK39_GPIO_IRQ(2) +#define IRQ_GPIO_3 AK39_GPIO_IRQ(3) +#define IRQ_GPIO_4 AK39_GPIO_IRQ(4) +#define IRQ_GPIO_5 AK39_GPIO_IRQ(5) +#define IRQ_GPIO_6 AK39_GPIO_IRQ(6) +#define IRQ_GPIO_7 AK39_GPIO_IRQ(7) +#define IRQ_GPIO_8 AK39_GPIO_IRQ(8) +#define IRQ_GPIO_9 AK39_GPIO_IRQ(9) +#define IRQ_GPIO_10 AK39_GPIO_IRQ(10) +#define IRQ_GPIO_11 AK39_GPIO_IRQ(11) +#define IRQ_GPIO_12 AK39_GPIO_IRQ(12) +#define IRQ_GPIO_13 AK39_GPIO_IRQ(13) +#define IRQ_GPIO_14 AK39_GPIO_IRQ(14) +#define IRQ_GPIO_15 AK39_GPIO_IRQ(15) +#define IRQ_GPIO_16 AK39_GPIO_IRQ(16) +#define IRQ_GPIO_17 AK39_GPIO_IRQ(17) +#define IRQ_GPIO_18 AK39_GPIO_IRQ(18) +#define IRQ_GPIO_19 AK39_GPIO_IRQ(19) +#define IRQ_GPIO_20 AK39_GPIO_IRQ(20) +#define IRQ_GPIO_21 AK39_GPIO_IRQ(21) +#define IRQ_GPIO_22 AK39_GPIO_IRQ(22) +#define IRQ_GPIO_23 AK39_GPIO_IRQ(23) +#define IRQ_GPIO_24 AK39_GPIO_IRQ(24) +#define IRQ_GPIO_25 AK39_GPIO_IRQ(25) +#define IRQ_GPIO_26 AK39_GPIO_IRQ(26) +#define IRQ_GPIO_27 AK39_GPIO_IRQ(27) +#define IRQ_GPIO_28 AK39_GPIO_IRQ(28) +#define IRQ_GPIO_29 AK39_GPIO_IRQ(29) +#define IRQ_GPIO_30 AK39_GPIO_IRQ(30) +#define IRQ_GPIO_31 AK39_GPIO_IRQ(31) + +#define IRQ_GPIO_32 AK39_GPIO_IRQ(32) +#define IRQ_GPIO_33 AK39_GPIO_IRQ(33) +#define IRQ_GPIO_34 AK39_GPIO_IRQ(34) +#define IRQ_GPIO_35 AK39_GPIO_IRQ(35) +#define IRQ_GPIO_36 AK39_GPIO_IRQ(36) +#define IRQ_GPIO_37 AK39_GPIO_IRQ(37) +#define IRQ_GPIO_38 AK39_GPIO_IRQ(38) +#define IRQ_GPIO_39 AK39_GPIO_IRQ(39) +#define IRQ_GPIO_40 AK39_GPIO_IRQ(40) +#define IRQ_GPIO_41 AK39_GPIO_IRQ(41) +#define IRQ_GPIO_42 AK39_GPIO_IRQ(42) +#define IRQ_GPIO_43 AK39_GPIO_IRQ(43) +#define IRQ_GPIO_44 AK39_GPIO_IRQ(44) +#define IRQ_GPIO_45 AK39_GPIO_IRQ(45) +#define IRQ_GPIO_46 AK39_GPIO_IRQ(46) +#define IRQ_GPIO_47 AK39_GPIO_IRQ(47) +#define IRQ_GPIO_48 AK39_GPIO_IRQ(48) +#define IRQ_GPIO_49 AK39_GPIO_IRQ(49) +#define IRQ_GPIO_50 AK39_GPIO_IRQ(50) +#define IRQ_GPIO_51 AK39_GPIO_IRQ(51) +#define IRQ_GPIO_52 AK39_GPIO_IRQ(52) +#define IRQ_GPIO_53 AK39_GPIO_IRQ(53) +#define IRQ_GPIO_54 AK39_GPIO_IRQ(54) +#define IRQ_GPIO_55 AK39_GPIO_IRQ(55) +#define IRQ_GPIO_56 AK39_GPIO_IRQ(56) +#define IRQ_GPIO_57 AK39_GPIO_IRQ(57) +#define IRQ_GPIO_58 AK39_GPIO_IRQ(58) +#define IRQ_GPIO_59 AK39_GPIO_IRQ(59) +#define IRQ_GPIO_60 AK39_GPIO_IRQ(60) +#define IRQ_GPIO_61 AK39_GPIO_IRQ(61) +#define IRQ_GPIO_62 AK39_GPIO_IRQ(62) +#define IRQ_GPIO_63 AK39_GPIO_IRQ(63) +#define IRQ_GPIO_64 AK39_GPIO_IRQ(64) +#define IRQ_GPIO_65 AK39_GPIO_IRQ(65) +#define IRQ_GPIO_66 AK39_GPIO_IRQ(66) +#define IRQ_GPIO_67 AK39_GPIO_IRQ(67) +#define IRQ_GPIO_68 AK39_GPIO_IRQ(68) +#define IRQ_GPIO_69 AK39_GPIO_IRQ(69) +#define IRQ_GPIO_70 AK39_GPIO_IRQ(70) +#define IRQ_GPIO_71 AK39_GPIO_IRQ(71) +#define IRQ_GPIO_72 AK39_GPIO_IRQ(72) +#define IRQ_GPIO_73 AK39_GPIO_IRQ(73) +#define IRQ_GPIO_74 AK39_GPIO_IRQ(74) +#define IRQ_GPIO_75 AK39_GPIO_IRQ(75) +#define IRQ_GPIO_76 AK39_GPIO_IRQ(76) +#define IRQ_GPIO_77 AK39_GPIO_IRQ(77) +#define IRQ_GPIO_78 AK39_GPIO_IRQ(78) +#define IRQ_GPIO_79 AK39_GPIO_IRQ(79) +#define IRQ_GPIO_80 AK39_GPIO_IRQ(80) +#define IRQ_GPIO_81 AK39_GPIO_IRQ(81) +#define IRQ_GPIO_82 AK39_GPIO_IRQ(82) +#define IRQ_GPIO_83 AK39_GPIO_IRQ(83) +#define IRQ_GPIO_84 AK39_GPIO_IRQ(84) +#define IRQ_GPIO_85 AK39_GPIO_IRQ(85) + + +/* total irq number */ +#define NR_IRQS (IRQ_GPIO_85 + 1) + +#endif /* __ASM_ARCH_IRQS_H_ */ + diff --git a/arch/arm/mach-ak39/include/mach/l2cache.h b/arch/arm/mach-ak39/include/mach/l2cache.h new file mode 100644 index 00000000..9b4084c2 --- /dev/null +++ b/arch/arm/mach-ak39/include/mach/l2cache.h @@ -0,0 +1,17 @@ +/* + * linux/arch/arm/mach-ak39/include/l2cache.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __ASM_ARCH_L2CACHE_H +#define __ASM_ARCH_L2CACHE_H + +void l2cache_init(void); +void l2cache_clean_finish(void); +void l2cache_invalidate(void); + +#endif /* __ASM_ARCH_L2CACHE_H */ diff --git a/arch/arm/mach-ak39/include/mach/leds-gpio.h b/arch/arm/mach-ak39/include/mach/leds-gpio.h new file mode 100644 index 00000000..82cf4678 --- /dev/null +++ b/arch/arm/mach-ak39/include/mach/leds-gpio.h @@ -0,0 +1,30 @@ +/* arch/arm/mach-ak98/include/mach/leds-gpio.h + * + * Copyright (c) Anyka + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __ASM_ARCH_LEDSGPIO_H +#define __ASM_ARCH_LEDSGPIO_H "leds-gpio.h" + +#define AK_LEDF_ACTLOW (1<<0) /* LED is on when GPIO low */ +#define AK_LEDF_ACTHIGH (1<<1) /* LED is on when GPIO hight */ +#define AK_LEDF_TRISTATE (1<<2) /* tristate to turn off */ + +struct ak_led_data { + char *name; + char *def_trigger; + char effective_level; + struct gpio_info gpio; +}; + +struct ak_led_pdata { + struct ak_led_data *leds; + int nr_led; +}; + +#endif /* __ASM_ARCH_LEDSGPIO_H */ diff --git a/arch/arm/mach-ak39/include/mach/map.h b/arch/arm/mach-ak39/include/mach/map.h new file mode 100644 index 00000000..948e93bd --- /dev/null +++ b/arch/arm/mach-ak39/include/mach/map.h @@ -0,0 +1,123 @@ +/* arch/arm/arch-ak39/include/mach/map.h + * + * AK39 - Memory map definitions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __ASM_ARCH_MAP_H +#define __ASM_ARCH_MAP_H + +#ifndef __ASSEMBLY__ +#define AK39_ADDR(x) ((void __iomem *)0xF0000000 + (x)) +#else +#define AK39_ADDR(x) (0xF0000000 + (x)) +#endif + +#define AK_VA_OCROM AK39_ADDR(0x00000000) +#define AK_PA_OCROM 0x00000000 +#define AK_SZ_OCROM SZ_32K /* 32KB */ + +#define AK_VA_SYSCTRL AK39_ADDR(0x00008000) +#define AK_PA_SYSCTRL (0x08000000) +#define AK_SZ_SYSCTRL SZ_32K /* 32KB */ + +#define AK_VA_CAMERA AK39_ADDR(0x00010000) +#define AK_PA_CAMERA (0x20000000) +#define AK_SZ_CAMERA SZ_64K /* 64KB */ + +#define AK_VA_VENCODE AK39_ADDR(0x00020000) +#define AK_PA_VENCODE (0x20020000) +#define AK_SZ_VENCODE SZ_64K /* 64KB */ + +/* some sub system control register */ +#define AK_VA_SUBCTRL AK39_ADDR(0x00030000) +#define AK_PA_SUBCTRL (0x20100000) +#define AK_SZ_SUBCTRL SZ_2M /* 2MB */ + +#define AK_VA_MAC AK39_ADDR(0x00230000) +#define AK_PA_MAC (0x20300000) +#define AK_SZ_MAC SZ_8K /* 8KB */ + +#define AK_VA_REGRAM AK39_ADDR(0x00232000) +#define AK_PA_REGRAM (0x21000000) +#define AK_SZ_REGRAM SZ_8K /* 8KB */ + +#define AK_VA_L2MEM AK39_ADDR(0x00234000) +#define AK_PA_L2MEM (0x4800C000) +#define AK_SZ_L2MEM SZ_8K /* 8KB */ + +#define AK_VA_MIPI AK39_ADDR(0x00236000) +#define AK_PA_MIPI (0x20400000) +#define AK_SZ_MIPI SZ_8K /* 8KB */ + +#define AK_VA_VIDEO_BUFF AK39_ADDR(0x00238000) +#define AK_PA_VIDEO_BUFF (0x80000000) +#define AK_SZ_VIDEO_BUFF CONFIG_VIDEO_RESERVED_MEM_SIZE /* 8KB */ + + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +#define AK_VA_MCI (AK_VA_SUBCTRL + 0x0000) +#define AK_PA_MCI (AK_PA_SUBCTRL + 0x0000) + +#define AK_VA_SDIO (AK_VA_SUBCTRL + 0x8000) +#define AK_PA_SDIO (AK_PA_SUBCTRL + 0x8000) + +#define AK_VA_DAC (AK_VA_SUBCTRL + 0x10000) +#define AK_PA_DAC (AK_PA_SUBCTRL + 0x10000) + +#define AK_VA_ADC (AK_VA_SUBCTRL + 0x18000) +#define AK_PA_ADC (AK_PA_SUBCTRL + 0x18000) + +#define AK_VA_SPI1 (AK_VA_SUBCTRL + 0x20000) +#define AK_PA_SPI1 (AK_PA_SUBCTRL + 0x20000) + +#define AK_VA_SPI2 (AK_VA_SUBCTRL + 0x28000) +#define AK_PA_SPI2 (AK_PA_SUBCTRL + 0x28000) + +#define AK_VA_UART (AK_VA_SUBCTRL + 0x30000) +#define AK_PA_UART (AK_PA_SUBCTRL + 0x30000) + +#define AK_VA_L2CTRL (AK_VA_SUBCTRL + 0x40000) +#define AK_PA_L2CTRL (AK_PA_SUBCTRL + 0x40000) + +#define AK_VA_I2C (AK_VA_SUBCTRL + 0x50000) +#define AK_PA_I2C (AK_VA_SUBCTRL + 0x50000) + +#define AK_VA_IRDA (AK_VA_SUBCTRL + 0x60000) +#define AK_PA_IRDA (AK_PA_SUBCTRL + 0x60000) + +#define AK_VA_GPIO (AK_VA_SUBCTRL + 0x70000) +#define AK_PA_GPIO (AK_PA_SUBCTRL + 0x70000) + +/* encryption register */ +#define AK_VA_ENCRY (AK_VA_SUBCTRL + 0x80000) +#define AK_PA_ENCRY (AK_PA_SUBCTRL + 0x80000) + +/* usb register */ +#define AK_VA_USB (AK_VA_SUBCTRL + 0x100000) +#define AK_PA_USB (AK_PA_SUBCTRL + 0x100000) + + +#define write_ramb(v, p) (*(volatile unsigned char *)(p) = (v)) +#define write_ramw(v, p) (*(volatile unsigned short *)(p) = (v)) +#define write_raml(v, p) (*(volatile unsigned long *)(p) = (v)) + +#define read_ramb(p) (*(volatile unsigned char *)(p)) +#define read_ramw(p) (*(volatile unsigned short *)(p)) +#define read_raml(p) (*(volatile unsigned long *)(p)) + +#define write_buf(v, p) (*(volatile unsigned long *)(p) = (v)) +#define read_buf(p) (*(volatile unsigned long *)(p)) + +#define REG_VA_VAL(base_addr, offset) (*(volatile unsigned long *)((base_addr) + (offset))) +#define REG_VA_ADDR(base_addr, offset) ((base_addr) + (offset)) + +#define REG_PA_VAL(base_addr, offset) (*(volatile unsigned long *)((base_addr) + (offset))) +#define REG_PA_ADDR(base_addr, offset) ((base_addr) + (offset)) + + +#endif /* __ASM_ARCH_MAP_H */ + diff --git a/arch/arm/mach-ak39/include/mach/pm.h b/arch/arm/mach-ak39/include/mach/pm.h new file mode 100644 index 00000000..40b8a8dc --- /dev/null +++ b/arch/arm/mach-ak39/include/mach/pm.h @@ -0,0 +1,31 @@ +#ifndef __PM_H +#define __PM_H + +struct ak_wakeup_gpio { + int pin; + char wakeup_pol; +}; + +struct ak_pm_pdata { + struct ak_wakeup_gpio *gpios; + int nr_gpios; +}; + +/* ak39_pm_init + * + * called from board at initialisation time to setup the power + * management +*/ + +#ifdef CONFIG_PM +//extern int __init ak39_pm_init(void); +#else +static inline int ak39_pm_init(void) +{ + return 0; +} +#endif + +#endif /* __PM_H */ + + diff --git a/arch/arm/mach-ak39/include/mach/pwm_timer.h b/arch/arm/mach-ak39/include/mach/pwm_timer.h new file mode 100644 index 00000000..3c2238f3 --- /dev/null +++ b/arch/arm/mach-ak39/include/mach/pwm_timer.h @@ -0,0 +1,131 @@ +#ifndef __PWM_TIMER_H__ +#define __PWM_TIMER_H__ + + +#define timer_cnt pt_u.timer.cnt +#define timer_irq pt_u.timer.irq +#define timer_cb pt_u.timer.callback +#define timer_reload pt_u.timer.auto_reload +#define timer_priv pt_u.timer.priv_data + +#define pwm_hlimit pt_u.pwm.high_limit +#define pwm_llimit pt_u.pwm.low_limit +#define pwm_pin pt_u.pwm.pin + +/*pwm/timer object*/ +struct ak_pwm_timer +{ + int id; + u8 mode; + u8 pre_div; + u8 __iomem *base; + bool status; + + union { + struct { + u32 cnt; + bool auto_reload; + void *priv_data; + int irq; + int (*callback)(void*); + }timer; /*timer private element*/ + struct { + u16 high_limit; + u16 low_limit; + int pin; + }pwm; /*pwm private element*/ + }pt_u; +}; + + +struct ak_platform_pwm_bl_data { + int pwm_id; + unsigned int max_brightness; + unsigned int dft_brightness; + unsigned int high_limit; + unsigned int low_limit; + unsigned int pwm_clk; + int (*init)(struct ak_pwm_timer *dev); + int (*notify)(int brightness); + void (*exit)(struct ak_pwm_timer *dev); +}; + + + +#define AK_PWM1_CTRL (AK_VA_SYSCTRL+0xB4) +#define AK_PWM2_CTRL (AK_VA_SYSCTRL+0xBC) +#define AK_PWM3_CTRL (AK_VA_SYSCTRL+0xC4) +#define AK_PWM4_CTRL (AK_VA_SYSCTRL+0xCC) +#define AK_PWM5_CTRL (AK_VA_SYSCTRL+0xD4) + + +#define AK_PWM_TIMER_CTRL1 (0x00) +#define AK_PWM_TIMER_CTRL2 (0x04) + +#define AK_PWM_HIGH_LEVEL(x) ((x) << 16) +#define AK_PWM_LOW_LEVEL(x) (x) + +#define AK_TIMER_TIMEOUT_CLR (1<<30) +#define AK_TIMER_FEED_TIMER (1<<29) +#define AK_PWM_TIMER_EN (1<<28) +#define AK_TIMER_TIMEOUT_STA (1<<27) +#define AK_TIMER_READ_SEL (1<<26) + +#define AK_TIMER_WORK_MODE_MASK (0x3<<24) +#define AK_TIMER_WORK_MODE(x) ((x)<<24) +#define AK_PWM_TIMER_PRE_DIV(x) ((x) << 16) +#define AK_PWM_TIMER_PRE_DIV_MASK ((0xff) << 16) +#define AK_PWM_TIMER_PRE_DIV_MAX 0xff + + +#define REAL_CRYSTAL_FREQ (12*1000*1000) +#define PWM_MAX_FREQ (6*1000*1000) +#define PWM_MIN_FREQ (92) + +#define AK_PWM_TIMER_CNT (5) + +/*the pwm/timer number.*/ +enum ak_pwm_timer_nr { + AK_PWM_TIMER1, + AK_PWM_TIMER2, + AK_PWM_TIMER3, + AK_PWM_TIMER4, + AK_PWM_TIMER5, + AK_PWM_TIMER_NR, +}; + + +enum ak_pwm_timer_status { + PWM_TIMER_UNUSED, + PWM_TIMER_BUSY, + PWM_TIMER_RESERVED, +}; + + +enum ak_pwm_timer_mode +{ + AK_PT_MODE_TIMER_AUTO_LOAD = 0, + AK_PT_MODE_TIMER_ONE_SHOT, + AK_PT_MODE_PWM, +}; + + +int ak_pwm_get_duty_cycle(struct ak_pwm_timer *pwm, unsigned short *high, unsigned short *low); +int ak_pwm_config(struct ak_pwm_timer *pwm, unsigned short high, unsigned short low, unsigned int freq); + +int ak_pwm_enable(struct ak_pwm_timer *pwm); +void ak_pwm_disable(struct ak_pwm_timer *pwm); +int ak_timer_enable(struct ak_pwm_timer *timer); +int ak_timer_enable_sync(struct ak_pwm_timer *timer); +void ak_timer_disable(struct ak_pwm_timer *timer); + +struct ak_pwm_timer *ak_pwm_request(int pwm_id); +struct ak_pwm_timer *ak_timer_request(int timer_id, bool auto_reload, int (*cb)(void* data)); + +void ak_pwm_release(struct ak_pwm_timer *pwm); +void ak_timer_release(struct ak_pwm_timer *timer); + +int ak_timer_config(struct ak_pwm_timer *timer, u32 cnt, u8 pre_div); + +#endif + diff --git a/arch/arm/mach-ak39/include/mach/reboot.h b/arch/arm/mach-ak39/include/mach/reboot.h new file mode 100644 index 00000000..292f6507 --- /dev/null +++ b/arch/arm/mach-ak39/include/mach/reboot.h @@ -0,0 +1,7 @@ +#ifndef __AK39_REBOOT_H +#define __AK39_REBOOT_H + +void ak39_reboot_sys_by_soft(void); +void ak39_jump_to_rom(unsigned long addr); + +#endif diff --git a/arch/arm/mach-ak39/include/mach/reset.h b/arch/arm/mach-ak39/include/mach/reset.h new file mode 100644 index 00000000..3f3e0320 --- /dev/null +++ b/arch/arm/mach-ak39/include/mach/reset.h @@ -0,0 +1,63 @@ +/* + * mach/reset.h + */ +#ifndef _AK39_RESET_H_ +#define _AK39_RESET_H_ __FILE__ + +#include + +extern void (*ak39_arch_reset) (void); + +#define MODULE_RESET_CON1 (AK_VA_SYSCTRL + 0x20) +#define MODULE_WDT_CFG1 (AK_VA_SYSCTRL + 0xe4) +#define MODULE_WDT_CFG2 (AK_VA_SYSCTRL + 0Xe8) + +#define AK39_SRESET_MMCSD (1) +#define AK39_SRESET_SDIO (2) +#define AK39_SRESET_ADC (3) +#define AK39_SRESET_DAC (4) +#define AK39_SRESET_SPI1 (5) +#define AK39_SRESET_SPI2 (6) +#define AK39_SRESET_UART1 (7) +#define AK39_SRESET_UART2 (8) +#define AK39_SRESET_L2MEM (9) +#define AK39_SRESET_I2C (10) +#define AK39_SRESET_IRDA (11) +#define AK39_SRESET_GPIO (12) +#define AK39_SRESET_MAC (13) +#define AK39_SRESET_ENCRY (14) +#define AK39_SRESET_USBHS (15) +#define AK39_SRESET_MIPI (16) +#define AK39_SRESET_I2C_2 (17) +#define AK39_SRESET_CAMERA (19) +#define AK39_SRESET_VIDEO (20) +#define AK39_SRESET_DRAM (24) +#define AK39_SRESET_DPHYCLK (25) + +int ak39_soft_reset(u32 module); + +/***** extern call for comm drivers compatible *****/ +#define AK_SRESET_MMCSD AK39_SRESET_MMCSD +#define AK_SRESET_SDIO AK39_SRESET_SDIO +#define AK_SRESET_ADC AK39_SRESET_ADC +#define AK_SRESET_DAC AK39_SRESET_DAC +#define AK_SRESET_SPI1 AK39_SRESET_SPI1 +#define AK_SRESET_SPI2 AK39_SRESET_SPI2 +#define AK_SRESET_UART1 AK39_SRESET_UART1 +#define AK_SRESET_UART2 AK39_SRESET_UART2 +#define AK_SRESET_L2MEM AK39_SRESET_L2MEM +#define AK_SRESET_I2C AK39_SRESET_I2C +#define AK_SRESET_IRDA AK39_SRESET_IRDA +#define AK_SRESET_GPIO AK39_SRESET_GPIO +#define AK_SRESET_MAC AK39_SRESET_MAC +#define AK_SRESET_ENCRY AK39_SRESET_ENCRY +#define AK_SRESET_USBHS AK39_SRESET_USBHS +#define AK_SRESET_CAMERA AK39_SRESET_CAMERA +#define AK_SRESET_VIDEO AK39_SRESET_VIDEO +#define AK_SRESET_DRAM AK39_SRESET_DRAM + + +int ak_soft_reset(u32 module); +/*** end extern call for comm drivers compatible ***/ + +#endif diff --git a/arch/arm/mach-ak39/include/mach/spi.h b/arch/arm/mach-ak39/include/mach/spi.h new file mode 100644 index 00000000..86072ff2 --- /dev/null +++ b/arch/arm/mach-ak39/include/mach/spi.h @@ -0,0 +1,93 @@ +/* + * include/asm-arm/arch-ak37/spi.h + */ + +#ifndef __SPI_H__ +#define __SPI_H__ + +struct ak_spi_info { + unsigned long* pin_cs; + unsigned short num_cs; + unsigned long board_size; + unsigned short bus_num; + unsigned short mode_bits; + char clk_name[20]; + int xfer_mode; /*use for dma or cpu*/ +}; + +enum akspi_cs_num { + AKSPI_ONCHIP_CS = 0, /*on chip control cs index*/ + /*AKSPI_CS1,*/ + /*AKSPI_CS2,*/ + AKSPI_CS_NUM, +}; + +enum akspi_bus_num { + AKSPI_BUS_NUM1, + AKSPI_BUS_NUM2, + AKSPI_MAX_BUS_NUM, +}; + + +#define AKSPI_1DATAWIRE (0b00<<16) +#define AKSPI_2DATAWIRE (0b01<<16) +#define AKSPI_4DATAWIRE (0b10<<16) + +#define AKSPI_XFER_MODE_DMA (1) +#define AKSPI_XFER_MODE_CPU (2) + + +#define AK_SPICON (0x00) +#define AK_SPICON_WIRE (0x3<<16) +#define AK_SPICON_CLKDIV (0x7F<<8) +#define AK_SPICON_EN (1<<6) +#define AK_SPICON_CS (1<<5) +#define AK_SPICON_MS (1<<4) +#define AK_SPICON_CPHA (1<<3) +#define AK_SPICON_CPOL (1<<2) +#define AK_SPICON_ARRM (1<<1) +#define AK_SPICON_TGDM (1<<0) + +#define AK_SPISTA (0x04) +#define AK_SPISTA_TIMEOUT (1<<10) +#define AK_SPISTA_MPROC (1<<9) +#define AK_SPISTA_TRANSF (1<<8) +#define AK_SPISTA_RXOVER (1<<7) +#define AK_SPISTA_RXHFULL (1<<6) +#define AK_SPISTA_RXFULL (1<<5) +#define AK_SPISTA_RXEMP (1<<4) +#define AK_SPISTA_TXUNDER (1<<3) +#define AK_SPISTA_TXHEMP (1<<2) +#define AK_SPISTA_TXFULL (1<<1) +#define AK_SPISTA_TXEMP (1<<0) + +#define AK_SPIINT (0x08) +#define AK_SPIINT_TIMEOUT (1<<10) +#define AK_SPIINT_MPROC (1<<9) +#define AK_SPIINT_TRANSF (1<<8) +#define AK_SPIINT_RXOVER (1<<7) +#define AK_SPIINT_RXHFULL (1<<6) +#define AK_SPIINT_RXFULL (1<<5) +#define AK_SPIINT_RXEMP (1<<4) +#define AK_SPIINT_TXUNDER (1<<3) +#define AK_SPIINT_TXHEMP (1<<2) +#define AK_SPIINT_TXFULL (1<<1) +#define AK_SPIINT_TXEMP (1<<0) + +#define AK_SPICNT (0x0C) + +#define AK_SPIEXTX (0x10) +#define AK_SPIEXTX_BUFEN (1<<0) +#define AK_SPIEXTX_DMAEN (1<<16) + + +#define AK_SPIEXRX (0x14) +#define AK_SPIEXRX_BUFEN (1<<0) +#define AK_SPIEXRX_DMAEN (1<<16) + +#define AK_SPIOUT (0x18) + +#define AK_SPIIN (0x1C) + +#endif + diff --git a/arch/arm/mach-ak39/include/mach/timer.h b/arch/arm/mach-ak39/include/mach/timer.h new file mode 100644 index 00000000..fd4a11d9 --- /dev/null +++ b/arch/arm/mach-ak39/include/mach/timer.h @@ -0,0 +1,20 @@ +#ifndef _TIMERS_H_ +#define _TIMERS_H_ + +#if 0 +struct ak39_timer_plat_data { + int which_timer; + int hz; + void *data; + timer_handler handler; +}; + +#endif + +typedef int (*timer_handler) (void *data); +void * ak39_timer_probe(int which_timer); +int ak39_timer_remove(void *priv); +int ak39_timer_start(timer_handler handler, void *data, void *priv, int hz); +int ak39_timer_stop(void *priv); + +#endif diff --git a/arch/arm/mach-ak39/include/mach/timex.h b/arch/arm/mach-ak39/include/mach/timex.h new file mode 100644 index 00000000..da916374 --- /dev/null +++ b/arch/arm/mach-ak39/include/mach/timex.h @@ -0,0 +1,24 @@ +/* arch/arm/mach-ak39/include/mach/timex.h + * + * Copyright (c) 2003-2005 Simtec Electronics + * Ben Dooks + * + * AK39XX - time parameters + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __ASM_ARCH_TIMEX_H +#define __ASM_ARCH_TIMEX_H + +/* CLOCK_TICK_RATE needs to be evaluatable by the cpp, so making it + * a variable is useless. It seems as long as we make our timers an + * exact multiple of HZ, any value that makes a 1->1 correspondence + * for the time conversion functions to/from jiffies is acceptable. +*/ + +#define CLOCK_TICK_RATE 12000000 + +#endif /* end __ASM_ARCH_TIMEX_H */ diff --git a/arch/arm/mach-ak39/include/mach/uncompress.h b/arch/arm/mach-ak39/include/mach/uncompress.h new file mode 100644 index 00000000..ae22e528 --- /dev/null +++ b/arch/arm/mach-ak39/include/mach/uncompress.h @@ -0,0 +1,249 @@ +/* + * linux/arch/arm/mach-ak39/include/mach/uncompress.h + * + */ +#ifndef __UNCOMPRESS_H_ +#define __UNCOMPRESS_H_ + +#include +#include + +#if defined(CONFIG_CPU_AK3910) || defined(CONFIG_CPU_AK3916) || defined(CONFIG_CPU_AK3918)|| defined(CONFIG_CPU_AK3919) +#define CONFIG_UART0_INIT +#endif + +#define BAUD_RATE 115200 +#define ENDDING_OFFSET 60 + +#undef REG32 +#define REG32(_reg) (*(volatile unsigned long *)(_reg)) + +#define CLK_ASIC_PLL_CTRL (AK_PA_SYSCTRL + 0x08) + +/* L2 buffer address */ +#define UART0_TXBUF_ADDR REG_PA_ADDR(0x48000000, 0x1000) //0x48001000 +#define UART0_RXBUF_ADDR REG_PA_ADDR(0x48000000, 0x1080) +#define UART1_TXBUF_ADDR REG_PA_ADDR(0x48000000, 0x1100) +#define UART1_RXBUF_ADDR REG_PA_ADDR(0x48000000, 0x1180) + +/* L2 buffer control register */ +#define L2BUF_CONF2_REG REG_PA_VAL(0x20140000, 0x008C) //0x2014008c +#define UART0_TXBUF_CLR_BIT 16 //cdh:check ok +#define UART0_RXBUF_CLR_BIT 17 //cdh:check ok +#define UART1_TXBUF_CLR_BIT 18 //cdh:check ok +#define UART1_RXBUF_CLR_BIT 19 //cdh:check ok + +/* pullup/pulldown configure registers */ +#define PPU_PPD1_REG REG_PA_VAL(AK_PA_SYSCTRL, 0x80) //0x08000080 +#define RTS1_PU_BIT 7 // cdh:ak39E, old 24 +#define CTS1_PU_BIT 6 // cdh:ak39E, old 23 +#define TXD1_PU_BIT 5 // cdh:ak39E, old 22 +#define RXD1_PU_BIT 4 // cdh:ak39E, old 21 +#define TXD0_PU_BIT 2 // cdh:ak39E, old 20 +#define RXD0_PU_BIT 1 // cdh:ak39E, old 19 + +/* Clock control register */ +#define CLK_CTRL_REG1 REG_PA_VAL(AK_PA_SYSCTRL, 0x1C) //0x0800000C +#define UART0_CLKEN_BIT 7 //0x0800,001C ,cdh:check ok +#define UART1_CLKEN_BIT 8 //0x0800,001C ,cdh:check ok + +/*********** Shared pin control reigsters ********/ +#define SRDPIN_CTRL1_REG REG_PA_VAL(AK_PA_SYSCTRL, 0x74) //0x08000074 +#define SRDPIN_CTRL2_REG REG_PA_VAL(AK_PA_SYSCTRL, 0x74) //0x0800,0078, cdh:check ok + +#define UART0_RXD 2 // cdh:ak39E, old 14 +#define UART0_TXD 3 // cdh:ak39E, old 15 +#define UART1_RXD 1 // cdh:ak39E(4:5), old 16 +#define UART1_TXD 7 // cdh:ak39E(6:7), old 18 +#define UART1_CTS 8 // cdh:ak39E(8:9), old 20 +#define UART1_RTS 10 // cdh:ak39E(10:11), old 22 + +/** ************ UART registers *****************************/ +#define UART0_CONF1_REG REG_PA_VAL(0x20130000, 0x00) //0x20130000 +#define UART0_CONF2_REG REG_PA_VAL(0x20130000, 0x04) +#define UART0_DATA_CONF_REG REG_PA_VAL(0x20130000, 0x08) +#define UART0_BUF_THRE_REG REG_PA_VAL(0x20130000, 0x0C) +#define UART0_BUF_RX_REG REG_PA_VAL(0x20130000, 0x10) +#define UART0_BUF_RX_BACKUP_REG REG_PA_VAL(0x20130000, 0x14) +#define UART0_BUF_STOPBIT_REG REG_PA_VAL(0x20130000, 0x18) + +#define UART1_CONF1_REG REG_PA_VAL(0x20138000, 0x00) //0x20138000 +#define UART1_CONF2_REG REG_PA_VAL(0x20138000, 0x04) +#define UART1_DATA_CONF_REG REG_PA_VAL(0x20138000, 0x08) +#define UART1_BUF_THRE_REG REG_PA_VAL(0x20138000, 0x0C) +#define UART1_BUF_RX_REG REG_PA_VAL(0x20138000, 0x10) +#define UART1_BUF_RX_BACKUP_REG REG_PA_VAL(0x20138000, 0x14) +#define UART1_BUF_STOPBIT_REG REG_PA_VAL(0x20138000, 0x18) + +/* bit define of UARTx_CONF1_REG */ +#define CTS_SEL_BIT 18 +#define RTS_SEL_BIT 19 +#define PORT_ENABLE_BIT 21 //0: disable, 1:enable +#define TX_STATUS_CLR_BIT 28 +#define RX_STATUS_CLR_BIT 29 + +/* bit define of UARTx_CONF2_REG */ +#define TX_COUNT_BIT 4 +#define TX_COUNT_VALID_BIT 16 +#define TX_END_BIT 19 +#define TX_END_MASK (1 << TX_END_BIT) + +#if defined CONFIG_UART0_INIT //cdh:check ok +#define UART_TXBUF_CLR_BIT UART0_TXBUF_CLR_BIT //cdh:check ok +#define SRDPIN_UART_RXTX_BIT ((1 << UART0_RXD)|(1 << UART0_TXD)) // cdh:GPIO 1&2, 0x0800,0074 bit[1]&bit[2] == 1&1 +#define RXD_PU_BIT RXD0_PU_BIT +#define TXD_PU_BIT TXD0_PU_BIT +#define UART_CLKEN_BIT UART0_CLKEN_BIT +#define UART_TXBUF_ADDR UART0_TXBUF_ADDR +#define UART_CONF1_REG UART0_CONF1_REG +#define UART_CONF2_REG UART0_CONF2_REG +#define UART_DATA_CONF_REG UART0_DATA_CONF_REG +#define UART_BUF_STOPBIT_REG UART0_BUF_STOPBIT_REG +#elif defined CONFIG_UART1_INIT //cdh:check ok +#define UART_TXBUF_CLR_BIT UART1_TXBUF_CLR_BIT +#define SRDPIN_UART_RXTX_BIT ((0x2 << UART1_RXD)|(0x2 << UART1_TXD)) // cdh:GPIO 6&7, 0x0800,0078 bit[5:4]&bit[7:6] == 2&2 +#define RXD_PU_BIT RXD1_PU_BIT +#define TXD_PU_BIT TXD1_PU_BIT +#define UART_CLKEN_BIT UART1_CLKEN_BIT +#define UART_TXBUF_ADDR UART1_TXBUF_ADDR +#define UART_CONF1_REG UART1_CONF1_REG +#define UART_CONF2_REG UART1_CONF2_REG +#define UART_DATA_CONF_REG UART1_DATA_CONF_REG +#define UART_BUF_STOPBIT_REG UART1_BUF_STOPBIT_REG + +#else +#error One of UART0 ~ UART1 Must be defined +#endif + +static unsigned int __uidiv(unsigned int num, unsigned int den) +{ + unsigned int i; + + if (den == 1) + return num; + + i = 1; + while (den * i < num) + i++; + + return i-1; +} + +// cdh:check ok +static unsigned long __get_asic_pll_clk(void) +{ + unsigned long pll_m, pll_n, pll_od; + unsigned long asic_pll_clk; + unsigned long regval; + + regval = REG32(CLK_ASIC_PLL_CTRL); + pll_od = (regval & (0x3 << 12)) >> 12; + pll_n = (regval & (0xf << 8)) >> 8; + pll_m = regval & 0xff; + + asic_pll_clk = (12 * pll_m)/(pll_n * (1 << pll_od)); // clk unit: MHz + + if ((pll_od >= 1) && ((pll_n >= 2) && (pll_n <= 6)) + && ((pll_m >= 84) && (pll_m <= 254))) + return asic_pll_clk; + return 0; +} + +// cdh:check ok +static unsigned long __get_vclk(void) +{ + unsigned long regval; + unsigned long div; + + regval = REG32(CLK_ASIC_PLL_CTRL); + div = (regval & (0x7 << 17)) >> 17; + if (div == 0) + return __get_asic_pll_clk() >> 1; + + return __get_asic_pll_clk() >> div; +} + +// cdh:check ok +unsigned long __get_asic_clk(void) +{ + unsigned long regval; + unsigned long div; + + regval = REG32(CLK_ASIC_PLL_CTRL); + div = regval & (1 << 24); + if (div == 0) + return __get_vclk(); + + return __get_vclk() >> 1; +} + +// cdh:check ok +static void uart_init(void) +{ + unsigned int asic_clk, clk_div; + + /* adjust DMA priority */ + REG32(0x21000018) = 0x00FFFFFF; + + /* enable uart clock control cdh:check ok*/ + CLK_CTRL_REG1 &= ~(0x1 << UART_CLKEN_BIT); + + /* configuration shared pins to UART0 cdh:check ok*/ + SRDPIN_CTRL1_REG |= SRDPIN_UART_RXTX_BIT; + + /* configuration uart pin pullup disable cdh:check ok*/ + PPU_PPD1_REG |= (0x1 << RXD_PU_BIT) | (0x1 << TXD_PU_BIT); + + // cdh:check ok + asic_clk = __get_asic_clk()*1000000; + clk_div = __uidiv(asic_clk, BAUD_RATE) - 1; + // cdh:check ok + UART_CONF1_REG &= ~((0x1 << TX_STATUS_CLR_BIT) | (0x1 << RX_STATUS_CLR_BIT) | 0xFF); + UART_CONF1_REG |= (0x1 << TX_STATUS_CLR_BIT) | (0x1 << RX_STATUS_CLR_BIT) | clk_div; + +#ifdef CONFIG_UART1_INIT + /* Disable flow control */ + UART_CONF1_REG |= (0x1 << CTS_SEL_BIT) | (0x1 << RTS_SEL_BIT); +#endif + // cdh:check ok + UART_BUF_STOPBIT_REG = (0x1F << 16) | (0x1 << 0); + + /* enable uart port */ + UART_CONF1_REG |= (0x1 << PORT_ENABLE_BIT); // cdh:check ok +} + + +/* print a char to uart cdh:check ok*/ +static void putc(char c) +{ + /* Clear uart tx buffer cdh:check ok*/ + L2BUF_CONF2_REG |= (0x1 << UART_TXBUF_CLR_BIT); + + /* write char to uart buffer cdh:check ok */ + REG32(UART_TXBUF_ADDR) = (unsigned long)c; + REG32(UART_TXBUF_ADDR + ENDDING_OFFSET) = (unsigned long)'\0'; + + /* Clear uart tx count register cdh:check ok*/ + UART_CONF1_REG |= (0x1 << TX_STATUS_CLR_BIT); + + /* Send buffer, each time only send 1 byte */ + UART_CONF2_REG |= (1 << TX_COUNT_BIT) | (0x1 << TX_COUNT_VALID_BIT); + + /* Wait for finish */ + while((UART_CONF2_REG & TX_END_MASK) == 0) { + } +} + +static inline void flush(void) +{ +} + +static inline void arch_decomp_setup(void) +{ + uart_init(); +} + +/* nothing to do */ +#define arch_decomp_wdog() + +#endif /* __UNCOMPRESS_H_ */ diff --git a/arch/arm/mach-ak39/irq.c b/arch/arm/mach-ak39/irq.c new file mode 100644 index 00000000..a546b4ea --- /dev/null +++ b/arch/arm/mach-ak39/irq.c @@ -0,0 +1,435 @@ +/* + * arch/arm/mach-ak39/irq.c + */ + +#include +#include +#include +#include +#include +#include + +#include + + +/* interrupt mask 0: mask 1: unmask */ +#define AK_IRQ_MASK (AK_VA_SYSCTRL + 0x24) +#define AK_FIQ_MASK (AK_VA_SYSCTRL + 0x28) +#define AK_INT_STATUS (AK_VA_SYSCTRL + 0x4C) +#define AK_SYSCTRL_INT_MASK (AK_VA_SYSCTRL + 0x2C) +#define AK_SYSCTRL_INT_STATUS (AK_VA_SYSCTRL + 0x30) + +#define AK_L2MEM_IRQ_ENABLE (AK_VA_L2CTRL + 0x9C) + + +#ifdef CONFIG_FIQ +/** + * s3c24xx_set_fiq - set the FIQ routing + * @irq: IRQ number to route to FIQ on processor. + * @on: Whether to route @irq to the FIQ, or to remove the FIQ routing. + * + * Change the state of the IRQ to FIQ routing depending on @irq and @on. If + * @on is true, the @irq is checked to see if it can be routed and the + * interrupt controller updated to route the IRQ. If @on is false, the FIQ + * routing is cleared, regardless of which @irq is specified. + */ +int ak39_set_fiq(unsigned int irq, bool on) +{ + u32 intmod; + unsigned offs; + unsigned long regval; + + regval = __raw_readl(AK_IRQ_MASK); + if (on) + regval |= (1UL << d->irq); + else + regval &= ~(1UL << d->irq); + __raw_writel(regval, AK_IRQ_MASK); + +#if 0 + if (on) { + offs = irq - FIQ_START; + if (offs > 31) + return -EINVAL; + + intmod = 1 << offs; + } else { + intmod = 0; + } + __raw_writel(intmod, AK_FIQ_MASK); +#endif + + return 0; +} + +EXPORT_SYMBOL_GPL(ak39_set_fiq); +#endif + +/* + * Disable interrupt number "irq" + */ +static void ak39_mask_irq(struct irq_data *d) +{ + unsigned long regval; + + regval = __raw_readl(AK_IRQ_MASK); + regval &= ~(1UL << d->irq); + __raw_writel(regval, AK_IRQ_MASK); +} + +/* + * Enable interrupt number "irq" + */ +static void ak39_unmask_irq(struct irq_data *d) +{ + unsigned long regval; + + regval = __raw_readl(AK_IRQ_MASK); + regval |= (1UL << d->irq); + __raw_writel(regval, AK_IRQ_MASK); +} + +static struct irq_chip ak39_irq_chip = { + .name = "module-irq", + .irq_mask_ack = ak39_mask_irq, + .irq_mask = ak39_mask_irq, + .irq_unmask = ak39_unmask_irq, +}; + +static void sysctrl_mask_irq(struct irq_data *d) +{ + unsigned long regval; + + regval = __raw_readl(AK_SYSCTRL_INT_MASK); + regval &= ~(1 << (d->irq - IRQ_SYSCTRL_START)); + __raw_writel(regval, AK_SYSCTRL_INT_MASK); +} + +static void sysctrl_unmask_irq(struct irq_data *d) +{ + unsigned long regval; + + regval = __raw_readl(AK_SYSCTRL_INT_MASK); + regval |= (1 << (d->irq - IRQ_SYSCTRL_START)); + __raw_writel(regval, AK_SYSCTRL_INT_MASK); +} + +/* enable rtc alarm to wake up systerm */ +static int sysctrl_set_wake(struct irq_data *d, unsigned int on) +{ + printk("%s,%d\n",__func__,__LINE__); + +#if 0 // wait for programming + + unsigned long clkdiv1; + + if (d->irq == IRQ_RTC_ALARM) { + + clkdiv1 = __raw_readl(AK98_CLKDIV1); + + if (on == 1) + clkdiv1 |= (1 << 16); + else + clkdiv1 &= ~(1 << 16); + + __raw_writel(clkdiv1, AK98_CLKDIV1); + + return 0; + } +#endif + return 0; +} + +static struct irq_chip ak39_sysctrl_chip = { + .name = "sysctrl-irq", + .irq_mask_ack = sysctrl_mask_irq, + .irq_mask = sysctrl_mask_irq, + .irq_unmask = sysctrl_unmask_irq, + .irq_set_wake = sysctrl_set_wake, +}; + +/** + * @brief: system module irq handler + * + * @author: caolianming + * @param [in] irq: irq number + * @param [in] *desc: irq info description + */ +static void ak39_sysctrl_handler(unsigned int irq, struct irq_desc *desc) +{ + unsigned long regval_mask, regval_sta; + unsigned long intpnd; + unsigned int offset; + + regval_mask = __raw_readl(AK_SYSCTRL_INT_MASK); + regval_sta = __raw_readl(AK_SYSCTRL_INT_STATUS); + + intpnd = (regval_mask & 0x7FF) & (regval_sta & 0x7FF); + + for (offset = 0; intpnd && offset < 11; offset++) { + + if (intpnd & (1 << offset)) + intpnd &= ~(1 << offset); + else + continue; + + irq = AK39_SYSCTRL_IRQ(offset); //come back debug + generic_handle_irq(irq); + } +} + +/** + * @brief: mask GPIO irq + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] *d: system irq data info + */ +static void ak39_gpioirq_mask(struct irq_data *d) +{ + void __iomem *gpio_ctrl = AK_GPIO_INT_MASK1; + unsigned long regval; + unsigned int irq = d->irq; + + irq -= IRQ_GPIO_0; + gpio_ctrl += (irq / 32) * 4; + + regval = __raw_readl(gpio_ctrl); + regval &= ~(1 << (irq & 31)); + __raw_writel(regval, gpio_ctrl); +} + +/** + * @brief: unmask GPIO irq + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] *d: system irq data info + */ +static void ak39_gpioirq_unmask(struct irq_data *d) +{ + void __iomem *gpio_ctrl = AK_GPIO_INT_MASK1; + unsigned long regval; + unsigned int irq = d->irq; + + irq -= IRQ_GPIO_0; + gpio_ctrl += (irq / 32) * 4; + + regval = __raw_readl(gpio_ctrl); + regval |= (1 << (irq & 31)); + __raw_writel(regval, gpio_ctrl); +} + + +/** + * @brief: setting irq polarity type + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] *d: system irq data info + * @param [in] type: irq type: IRQ_TYPE_EDGE_RISING, IRQ_TYPE_EDGE_FALLING + * IRQ_TYPE_LEVEL_HIGH, IRQ_TYPE_LEVEL_LOW + */ +static int ak39_gpioirq_set_type(struct irq_data *d, unsigned int type) +{ + void __iomem *reg_irqmod = AK_GPIO_INT_MODE1; + void __iomem *reg_irqpol = AK_GPIO_INTP1; + unsigned int irq = d->irq; + unsigned long regval_pol, regval_mod, offset; + int p, l; + + irq -= IRQ_GPIO_0; + + offset = irq & 31; + + reg_irqmod += (irq / 32) * 4; + reg_irqpol += (irq / 32) * 4; + + regval_mod = __raw_readl(reg_irqmod); + regval_pol = __raw_readl(reg_irqpol); + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + p = 1; l = 0; break; + case IRQ_TYPE_EDGE_FALLING: + p = 1; l = 1; break; + case IRQ_TYPE_LEVEL_HIGH: + p = 0; l = 0; break; + case IRQ_TYPE_LEVEL_LOW: + p = 0; l = 1; break; + default: + pr_debug("%s: Incorrect GPIO interrupt type 0x%x\n", + __func__, type); + return -ENXIO; + } + + if (p) + regval_mod |= (1 << (offset)); + else + regval_mod &= ~(1 << (offset)); + if (l) + regval_pol |= (1 << (offset)); + else + regval_pol &= ~(1 << (offset)); + + __raw_writel(regval_mod, reg_irqmod); + __raw_writel(regval_pol, reg_irqpol); + + return 0; +} + +/** + * @brief: setting wake up function of irq + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] *d: system irq data info + * @param [in] on: enable + */ +static int ak39_gpio_irq_set_wake(struct irq_data *d, unsigned int on) +{ + unsigned long regval; + printk("%s,%d\n",__func__,__LINE__); + + regval = __raw_readl(AK_WGPIO_ENABLE); + + if (d->irq >= IRQ_GPIO_0 && d->irq <= IRQ_GPIO_2) + regval |= (1 << (d->irq - IRQ_GPIO_0)); + + else if (d->irq>= IRQ_GPIO_4 && d->irq <= IRQ_GPIO_11) + regval |= (1 << (d->irq - IRQ_GPIO_3 + 3)); + + else if (d->irq>= IRQ_GPIO_13 && d->irq <= IRQ_GPIO_15) + regval |= (1 << (d->irq - IRQ_GPIO_12 + 11)); + + else if (d->irq>= IRQ_GPIO_19 && d->irq <= IRQ_GPIO_20) + regval |= (1 << (d->irq - IRQ_GPIO_18 + 14)); + + else if (d->irq>= IRQ_GPIO_23 && d->irq <= IRQ_GPIO_24) + regval |= (1 << (d->irq - IRQ_GPIO_22 + 16)); + + else if (d->irq == IRQ_GPIO_49) + regval |= (1 << (d->irq - IRQ_GPIO_48 + 18)); + + else if (d->irq >= IRQ_GPIO_52 && d->irq <= IRQ_GPIO_55) + regval |= (1 << (d->irq - IRQ_GPIO_51 + 19)); + + else if (d->irq >= IRQ_GPIO_57 && d->irq <= IRQ_GPIO_63) + regval |= (1 << (d->irq- IRQ_GPIO_56 + 24)); + + else { + printk("Not WGPIO IRQ: %d\n", d->irq); + return -1; + } + + __raw_writel(regval, AK_WGPIO_ENABLE); + + return 0; +} + +static struct irq_chip ak39_gpioirq_chip = { + .name = "gpio-irq", + .irq_mask_ack = ak39_gpioirq_mask, + .irq_mask = ak39_gpioirq_mask, + .irq_unmask = ak39_gpioirq_unmask, + .irq_set_type = ak39_gpioirq_set_type, + .irq_set_wake = ak39_gpio_irq_set_wake, +}; + + +/** + * @brief: GPIO irq handler function. + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] irq: irq number + * @param [in] *desc: irq info description + */ +static void ak39_gpio_irqhandler(unsigned int irq, struct irq_desc *desc) +{ + unsigned long enabled_irq; + unsigned int i; + unsigned int off; + unsigned int edge_sta; + unsigned int mode; + + + for (i = 0; i < 3; i++) { + enabled_irq = __raw_readl(AK_GPIO_INT_MASK1 + i * 4); + edge_sta = __raw_readl(AK_GPIO_EDGE_STATUS1 + i * 4);//clean irq + mode = __raw_readl(AK_GPIO_INT_MODE1 + i * 4 ); + + + while (enabled_irq) { + int find = 0; + off = __ffs(enabled_irq); + enabled_irq &= ~(1 << off); + + + if (mode & (1< + +#include + +int ispdrv_irq_work(void) +{ + return ak_isp_irq_work(); +} +EXPORT_SYMBOL(ispdrv_irq_work); + +int ispdrv_awb_work(void) +{ + return ak_isp_awb_work(); +} +EXPORT_SYMBOL(ispdrv_awb_work); + +int ispdrv_ae_work(void) +{ + return ak_isp_ae_work(); +} +EXPORT_SYMBOL(ispdrv_ae_work); + +void ispdrv_vo_get_flip_mirror(int *flip_en, int *mirror_en, int *height_block_num) +{ + ak_isp_get_flip_mirror(flip_en, mirror_en, height_block_num); +} +EXPORT_SYMBOL(ispdrv_vo_get_flip_mirror); + +int ispdrv_vo_get_using_frame_buf_id(void) +{ + return ak_isp_vo_get_using_frame_buf_id(); +} +EXPORT_SYMBOL(ispdrv_vo_get_using_frame_buf_id); + +int ispdrv_vo_set_main_channel_scale(int width, int height) +{ + return ak_isp_vo_set_main_channel_scale(width, height); +} +EXPORT_SYMBOL(ispdrv_vo_set_main_channel_scale); + +int ispdrv_vo_set_sub_channel_scale(int width, int height) +{ + return ak_isp_vo_set_sub_channel_scale(width, height); +} +EXPORT_SYMBOL(ispdrv_vo_set_sub_channel_scale); + +int ispdrv_vi_set_crop(int sx, int sy, int width, int height) +{ + return ak_isp_vi_set_crop(sx, sy, width, height); +} +EXPORT_SYMBOL(ispdrv_vi_set_crop); + +int ispdrv_vi_apply_mode(enum isp_working_mode mode) +{ + return ak_isp_vi_apply_mode(mode); +} +EXPORT_SYMBOL(ispdrv_vi_apply_mode); + +int ispdrv_vo_enable_buffer(enum buffer_id id) +{ + return ak_isp_vo_enable_buffer(id); +} +EXPORT_SYMBOL(ispdrv_vo_enable_buffer); + +int ispdrv_vo_disable_buffer(enum buffer_id id) +{ + return ak_isp_vo_disable_buffer(id); +} +EXPORT_SYMBOL(ispdrv_vo_disable_buffer); + +int ispdrv_vo_set_buffer_addr(enum buffer_id id,unsigned long yaddr_main_chan_addr, + unsigned long yaddr_sub_chan_addr) +{ + return ak_isp_vo_set_buffer_addr(id, yaddr_main_chan_addr, yaddr_sub_chan_addr); +} +EXPORT_SYMBOL(ispdrv_vo_set_buffer_addr); + +int ispdrv_vo_enable_irq_status(int bit) +{ + return ak_isp_vo_enable_irq_status(bit); +} +EXPORT_SYMBOL(ispdrv_vo_enable_irq_status); + +int ispdrv_vo_clear_irq_status(int bit) +{ + return ak_isp_vo_clear_irq_status(bit); +} +EXPORT_SYMBOL(ispdrv_vo_clear_irq_status); + +int ispdrv_vo_check_irq_status(void) +{ + return ak_isp_vo_check_irq_status(); +} +EXPORT_SYMBOL(ispdrv_vo_check_irq_status); + +int ispdrv_vi_start_capturing(void) +{ + return ak_isp_vi_start_capturing(); +} +EXPORT_SYMBOL(ispdrv_vi_start_capturing); + +int ispdrv_vi_stop_capturing(void) +{ + return ak_isp_vi_stop_capturing(); +} +EXPORT_SYMBOL(ispdrv_vi_stop_capturing); + +int ispdrv_is_continuous(void) +{ + return ak_isp_is_continuous(); +} +EXPORT_SYMBOL(ispdrv_is_continuous); + +int ispdrv_register_sensor(void *sensor_info) +{ + return ak_isp_register_sensor(sensor_info); +} +EXPORT_SYMBOL(ispdrv_register_sensor); + +void *ispdrv_get_sensor(int *index) +{ + return ak_isp_get_sensor(index); +} +EXPORT_SYMBOL(ispdrv_get_sensor); + +void ispdrv_remove_all_sensors(void) +{ + ak_isp_remove_all_sensors(); +} +EXPORT_SYMBOL(ispdrv_remove_all_sensors); + +AK_ISP_PCLK_POLAR ispdrv_get_pclk_polar(void) +{ + return ak_isp_get_pclk_polar(); +} +EXPORT_SYMBOL(ispdrv_get_pclk_polar); + +int ispdrv_set_isp_pause(void) +{ + return ak_isp_set_isp_capturing(0); +} +EXPORT_SYMBOL(ispdrv_set_isp_pause); + +int ispdrv_set_isp_resume(void) +{ + return ak_isp_set_isp_capturing(1); +} +EXPORT_SYMBOL(ispdrv_set_isp_resume); + +int ispdrv_set_td(void) +{ + return ak_isp_set_td(); +} +EXPORT_SYMBOL(ispdrv_set_td); + +int ispdrv_reload_td(void) +{ + return ak_isp_reload_td(); +} +EXPORT_SYMBOL(ispdrv_reload_td); + +int ispdrv_get_yuvaddr_and_mdinfo(int id, void **yuv, void **mdinfo) +{ + return ak_isp_get_yuvaddr_and_mdinfo(id, yuv, mdinfo); +} +EXPORT_SYMBOL(ispdrv_get_yuvaddr_and_mdinfo); + +int ispdrv_vo_get_inputdataw(void) +{ + return ak_isp_vo_get_inputdataw(); +} +EXPORT_SYMBOL(ispdrv_vo_get_inputdataw); diff --git a/arch/arm/mach-ak39/l2cache.c b/arch/arm/mach-ak39/l2cache.c new file mode 100644 index 00000000..72322cc4 --- /dev/null +++ b/arch/arm/mach-ak39/l2cache.c @@ -0,0 +1,27 @@ +/* + * linux/arch/arm/mach-ak39/l2cache.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include + +void l2cache_init(void) +{ +} +EXPORT_SYMBOL(l2cache_init); + +void l2cache_clean_finish(void) +{ +} +EXPORT_SYMBOL(l2cache_clean_finish); + +void l2cache_invalidate(void) +{ +} +EXPORT_SYMBOL(l2cache_invalidate); + diff --git a/arch/arm/mach-ak39/mach-ak3918ev300_c80dx.c b/arch/arm/mach-ak39/mach-ak3918ev300_c80dx.c new file mode 100644 index 00000000..91821f08 --- /dev/null +++ b/arch/arm/mach-ak39/mach-ak3918ev300_c80dx.c @@ -0,0 +1,940 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "cpu.h" +#include "irq.h" +#include +#include +#include +#include +#include + +/** + * @brief: spi device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +#define SPI_ONCHIP_CS (0) /*means not need gpio*/ +static unsigned long ak39_spidev_cs[AKSPI_CS_NUM] = { + [AKSPI_ONCHIP_CS] = SPI_ONCHIP_CS, /*gpio 25, spidev0: ak-spiflash*/ +}; + +struct ak_spi_info ak39_spi1_info = { + .pin_cs = ak39_spidev_cs, + .num_cs = ARRAY_SIZE(ak39_spidev_cs), + .bus_num = AKSPI_BUS_NUM1, + .clk_name = "spi1", + .mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH, + .xfer_mode = AKSPI_XFER_MODE_DMA, +}; + +/** + * @brief: spiflash device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct flash_platform_data ak39_spiflash_info= { + .bus_width = FLASH_BUS_WIDTH_4WIRE | FLASH_BUS_WIDTH_2WIRE | FLASH_BUS_WIDTH_1WIRE, + .type = NULL, +}; + +/** + * @brief: spi bus device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct spi_board_info ak39_spi_board_dev[] = { + { + .modalias = "ak-spiflash", + .bus_num = AKSPI_BUS_NUM1, + .chip_select = AKSPI_ONCHIP_CS, + .mode = SPI_MODE_0, + .max_speed_hz = 20*1000*1000, + .platform_data = &ak39_spiflash_info, + }, +}; + + +/** + * @brief: motor0 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor0_pdata = { + .gpio_phase[0] = { + .pin = AK_GPIO_66, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = AK_GPIO_67, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = AK_GPIO_6, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = AK_GPIO_7, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + .gpio_hit[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + //.angular_speed = 100, /* angle/s */ + .angular_speed = 200, /* angle/s */ +}; + + +/** + * @brief: motor1 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor1_pdata = { + .gpio_phase[0] = { + .pin = AK_GPIO_8, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = AK_GPIO_9, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = AK_GPIO_68, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = AK_GPIO_69, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + + .gpio_hit[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + .angular_speed = 100, /* angle/s */ +}; + +/** + * @brief: mci2 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci2_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_PLUGIN_ALWAY, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_SDIO, + .gpio_init = ak_gpio_set, + .max_speed_hz = 25*1000*1000, + .gpio_cd = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + +/** + * @brief: mci1 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci1_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_DETECT_MODE_GPIO, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_MMC_SD, + .max_speed_hz = 25*1000*1000, + .gpio_init = ak_gpio_set, + .bus_width = MCI_BUS_WIDTH_1, + .gpio_cd = { + .pin = AK_GPIO_84, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + + +struct ak_crypto_plat_data akcrypto_pdata = { + .encrypt_mode = CRYPTO_MULTI_GROUP_MODE, +}; + + +/* akwifi platform data */ +struct akwifi_platform_data akwifi_pdata = { + .gpio_init = ak_gpio_set, + .gpio_cs = { + .pin = -1, + .pulldown = -1, + .pullup = AK_PULLDOWN_DISABLE, + .value = AK_GPIO_OUT_HIGH, // cs 脫脨脨搂脰碌 + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_on = { + .pin = AK_GPIO_82, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_off = { + .pin = AK_GPIO_82, + .pulldown = -1, + .pullup = AK_PULLUP_ENABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .power_on_delay = 2000, + .power_off_delay = 200, +}; + +struct platform_device anyka_wifi_device = { + .name = "anyka-wifi", + .id = -1, + .dev = { + .platform_data = &akwifi_pdata, + }, +}; + + +/** + * @brief: usb bus device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct akotghc_usb_platform_data akotghc_plat_data = { + .gpio_init = ak_gpio_set, + .gpio_pwr_on = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_pwr_off = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .switch_onboard = { + .pin = -1, + }, + .switch_extport = { + .pin = -1, + }, +}; + + +/** + * @brief: ethenet device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_mac_data ak39_mac_pdata = { + .gpio_init = ak_gpio_set, + .pwr_gpio = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .phy_rst_gpio = { + .pin = -1, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, +}; + + +/** +* @brief ak pcm device struct + hp and spk can identify by GPIO or AD. + wo can initialize it in this struct. +* @author dengzhou +* @date 2012-07-19 +*/ +struct ak39_codec_platform_data ak39_codec_pdata = +{ + .hpdet_gpio = + { + .pin = -1, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = -1, + .int_pol = -1, + }, + .spk_down_gpio = + { + .pin = AK_GPIO_65, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + .value = AK_GPIO_OUT_HIGH, + .int_pol = -1, + }, + .hpmute_gpio = + { + .pin = INVALID_GPIO, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .linindet_gpio = + { + .pin = -1, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, /* linein detect level */ + .int_pol = -1, + }, + + .hp_on_value = AK_GPIO_OUT_LOW, + .hpdet_irq = -1, + .linindet_irq = -1, + .bIsHPmuteUsed = 0, + .hp_mute_enable_value = AK_GPIO_OUT_HIGH, + .bIsMetalfixed = 0, + .boutput_only = 1, + .detect_flag = AKPCM_DYNAMIC_DETECT, +}; + +struct resource ak39_codec_resources[] = { + [0] = { + .start = 0x08000000, + .end = 0x0800FFFF, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_AnalogCtrlRegs", + }, + [1] = { + .start = 0x20110000, + .end = 0x2011800F, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_ADC2ModeCfgRegs", + }, +}; + + +struct platform_device ak39_codec_device = { + .name = "ak39-codec", + .id = -1, + .resource = ak39_codec_resources, + .num_resources = ARRAY_SIZE(ak39_codec_resources), + .dev = { + .platform_data = &ak39_codec_pdata, + }, +}; + + +/** + * @brief: sensor device platform data, this info is fake. + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct i2c_board_info ak_camara_devices[] = { + { + I2C_BOARD_INFO("aksensor", 0x1), + }, +}; + +static struct aksensor_camera_info ak_soc_camera_info = { + .buswidth = SOCAM_DATAWIDTH_8, + .pin_pwdn = INVALID_GPIO, //initialize GPIO for the power of camera. + .pin_reset = AK_GPIO_49, + .link = { + .bus_id = 39, + .power = NULL, + .board_info = &ak_camara_devices[0], + .i2c_adapter_id = 0, + .priv = &ak_soc_camera_info, + } +}; + +/* fake device for soc_camera subsystem */ +static struct platform_device soc_camera_interface = { + .name = "soc-camera-pdrv", + .id = -1, + .dev = { + .platform_data = &ak_soc_camera_info.link, + } +}; + + +/** + * @brief: LED platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_led_data leds[] = { + +// { +// .name = "blue_led", +// .def_trigger = "none", +// .gpio = { +// .pin = -1, +// .pulldown = AK_PULLDOWN_DISABLE, +// .pullup = -1, +// .value = AK_GPIO_OUT_HIGH, +// .dir = AK_GPIO_DIR_OUTPUT, +// .int_pol = -1, +// } +// }, + { + .name = "red_led", + .def_trigger = "none", + .gpio = { + .pin = AK_GPIO_80, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + } + }, + { + .name = "ir_led", + .def_trigger = "none", + .gpio = { + .pin = AK_GPIO_81, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + } + }, + +}; + +static struct ak_led_pdata led_pdata = { + .leds = leds, + .nr_led = ARRAY_SIZE(leds), +}; + +#if 0 +/* unused GPIO number for the machine board is left*/ +static unsigned int ak39_custom_gpiopin[] = { + //AK_GPIO_5, + //AK_GPIO_61, +}; + +static struct custom_gpio_data ak39_custom_gpios= { + .gpiopin = ak39_custom_gpiopin, + .ngpiopin = ARRAY_SIZE(ak39_custom_gpiopin), +}; + +static struct platform_device ak39_custom_gpio = { + .name = "akgpio", + .id = -1, + .dev = { + .platform_data = &ak39_custom_gpios, + }, +}; +#endif + +/** + * @brief: camera platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_camera_pdata ak39_camera_info = { + .mclk = 24, + .flags = 0, + //.interface = DVP_INTERFACE, +}; + +static struct user_gpio_info user_gpios[] = { + { + .name = "gpio-ircut_a", + .info = { + .pin = AK_GPIO_41, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, /* in daytime level */ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "gpio-ircut_b", + .info = { + .pin = AK_GPIO_42, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, // in daytime level + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "ir-led", + .info = { + .pin = AK_GPIO_81, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, // in daytime level ir-led closed + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, +}; + +static struct ak_user_gpio_pdata user_gpio_pdata = { + .user_gpios = user_gpios, + .nr_user_gpios = ARRAY_SIZE(user_gpios), +}; + +static struct platform_device user_gpio_device = { + .name = "user_gpio", + .id = -1, + .dev = { + .platform_data = &user_gpio_pdata, + }, +}; + +/** + * @brief: GPIO buttons platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct gpio_keys_button gpio_keys_button[] = { + { + .code = KEY_0, + .type = EV_KEY, + .gpio = AK_GPIO_5, + .active_low = 1, + .wakeup = 1, + .debounce_interval = 100, /* ms */ + .desc = "soft_boot", + .pullup = AK_PULLUP_ENABLE, + .pulldown = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = AK_GPIO_INT_LOWLEVEL, + }, +}; + +static struct akgpio_keys_platform_data gpio_keys_platform_data = { + .buttons = gpio_keys_button, + .nbuttons = ARRAY_SIZE(gpio_keys_button), + .rep = 0, +}; + +/** + * @brief: power manage gpios info + * + * @author: ye_guohong + * @date: 2014-12-09 + */ +static struct ak_wakeup_gpio pm_gpios[] = { + { + .pin = AK_GPIO_63, + .wakeup_pol = AK_FALLING_TRIGGERED, + }, +}; + +static struct ak_pm_pdata pm_pdata = { + .gpios = pm_gpios, + .nr_gpios = ARRAY_SIZE(pm_gpios), +}; + +#if 1 +/** +* @brief ad-key platform device struct + we should initialize the correct voltage for each key. +* @author: caolianming +* @date: 2014-01-09 +*/ +struct adgpio_key adkey[][3] = { + { + { .code = KEY_1, .min = 0, .max = 100}, //145+30/-40 + { .code = KEY_0, .min = 1600, .max = 1720}, //696+30/-40 + }, +}; + + +struct multi_addetect multi_det[] = { + {.unpress_min = 0, .unpress_max = 2100, .fixkeys = adkey[0], .plugdev = PLUGIN_DEV1}, // = null 3251+/-20 +}; + +struct analog_gpio_key ak39_adkey_data = { + .desc = "adkey", + .interval = 120, /* ms */ + .debounce_interval = 20, /* ms */ + .addet = multi_det, + .naddet = ARRAY_SIZE(multi_det), + .nkey = ARRAY_SIZE(adkey[0]), + .wakeup = 1, /* enable ad-key wakeup from standby mode */ +}; + +static struct platform_device ak39_adkey_device = { + .name = "ad-keys", + .id = -1, + .dev = { + .platform_data = &ak39_adkey_data, + } +}; +#endif + + +#if 0 +/** + * @brif: ak39 battery mach info + + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_bat_mach_info ak39_bat_info = { + .gpio_init = ak_gpio_set, + .usb_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .ac_gpio = { + .is_detect_mode = BAT_CHARGE_ADC_DETECT, + .active = -1, + .irq = -1, + .delay = 500, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pulldown = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .full_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .bat_mach_info = { + .voltage_sample = 6, // the sample of read voltage + .power_on_voltage = 3650, // discharge power on voltage limit + .power_on_correct = 64, // mv + .charge_min_voltage = 3550, // charge minute voltage (mv) + .max_voltage = 4200, // max battery voltage + .min_voltage = 3500, // min battery voltage + .power_off = poweroff_disable, + .full_capacity = 100, // battery full + .poweroff_cap = 0, // user read value to power off + .low_cap = 5, // user read value to low power warring + .recover_cap = 30, + .cpower_on_voltage = 3700, // charge power on voltage limit + .full_delay = 30, // unit is minute + .full_voltage = 4100, + }, + + .bat_adc = { + .up_resistance = 10, + .dw_resistance = 10, + .voltage_correct = 32, // battery correct factor + .adc_avdd = 3300, // avdd voltage + }, + +}; +#endif + +#if defined(CONFIG_I2C_GPIO_SOFT) +struct i2c_gpio_platform_data ak39_i2c_data={ + .sda_pin = AK_GPIO_28, + .scl_pin = AK_GPIO_27, + .udelay = 30, + .timeout = 300, +}; + +struct platform_device ak39_i2c_device = { + .name = "i2c-gpio", + .id = -1, + .dev = { + .platform_data = &ak39_i2c_data, + }, +}; +#endif + + /* @brief: ak39 platform devices table + * + * @author: caolianming + * @date: 2014-01-09 + */ + static struct platform_device *ak3918_platform_devices[] __initdata = { + &akfha_char_device, + &ak39_uart0_device, + //&ak39_uart1_device, + //&ak39_pwm_device, + &ak39_spi1_device, + &ak39_mci1_device, + //&ak39_mci2_device, + &ak39_i2c_device, + //&ak39_custom_gpio, + &ak39_usb_udc_device, + &ak39_usb_otg_hcd_device, + &anyka_wifi_device, + &soc_camera_interface, + &ak39_camera_interface, + &ak39_ion_device, + &ak39_pcm_device, + &ak39_codec_device, + &ak39_mmx_device, + &ak39_mac_device, + &ak39_led_pdev, + &ak39_gpio_keys_device, + //&ak39_adkey_device, + //&ak39_battery_power, + &ak39_rtc_device, + &ak39_crypto_device, + //&ak39_pm_device, + &user_gpio_device, + &ak39_motor0_device, + &ak39_motor1_device, +}; + +void wdt_enable(void); +void wdt_keepalive(unsigned int heartbeat); + +/** + * @brief: restart by "reboot" cmd + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void ak39_restart(char str, const char *cmd) +{ + //ak39_reboot_sys_by_soft(); +#if defined CONFIG_AK39_WATCHDOG || defined CONFIG_AK39_WATCHDOG_TOP + wdt_enable(); + wdt_keepalive(2); +#endif +} + + +/** + * @brief: initial ak3918 machine + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void __init ak3918_init_machine(void) +{ + adc1_init(); + + spi_register_board_info(ak39_spi_board_dev, ARRAY_SIZE(ak39_spi_board_dev)); + + ak39_spi1_device.dev.platform_data = &ak39_spi1_info; + + ak39_motor0_device.dev.platform_data = &ak39_motor0_pdata; + ak39_motor1_device.dev.platform_data = &ak39_motor1_pdata; + + ak39_mci1_device.dev.platform_data = &mci1_plat_data; + //ak39_mci2_device.dev.platform_data = &mci2_plat_data; + + ak39_crypto_device.dev.platform_data = &akcrypto_pdata; + + ak39_usb_otg_hcd_device.dev.platform_data = &akotghc_plat_data; + ak39_mac_device.dev.platform_data = &ak39_mac_pdata; + + ak39_led_pdev.dev.platform_data = &led_pdata; + ak39_gpio_keys_device.dev.platform_data = &gpio_keys_platform_data; + //ak39_battery_power.dev.platform_data = &ak39_bat_info; + + ak39_camera_interface.dev.platform_data = &ak39_camera_info; + + ak39_pm_device.dev.platform_data = &pm_pdata; + + + platform_add_devices(ak3918_platform_devices, + ARRAY_SIZE(ak3918_platform_devices)); + + l2_init(); + + return; +} + + +MACHINE_START(AK39XX, "CLOUD39EV300_AK3918E_YTJ_MAINBD(SC2232)_V1.0.0") +/* Maintainer: */ + .atag_offset = 0x100, + .fixup = NULL, + .map_io = ak39_map_io, + .reserve = NULL, + .init_irq = ak39_init_irq, + .init_machine = ak3918_init_machine, + .init_early = NULL, + .timer = &ak39_timer, + .restart = ak39_restart, + +MACHINE_END + diff --git a/arch/arm/mach-ak39/mach-ak3918ev300_hl_xiaofang.c b/arch/arm/mach-ak39/mach-ak3918ev300_hl_xiaofang.c new file mode 100644 index 00000000..1d947702 --- /dev/null +++ b/arch/arm/mach-ak39/mach-ak3918ev300_hl_xiaofang.c @@ -0,0 +1,830 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "cpu.h" +#include "irq.h" +#include +#include +#include +#include +#include + +/** + * @brief: spi device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +#define SPI_ONCHIP_CS (0) /*means not need gpio*/ +static unsigned long ak39_spidev_cs[AKSPI_CS_NUM] = { + [AKSPI_ONCHIP_CS] = SPI_ONCHIP_CS, /*gpio 25, spidev0: ak-spiflash*/ +}; + +struct ak_spi_info ak39_spi1_info = { + .pin_cs = ak39_spidev_cs, + .num_cs = ARRAY_SIZE(ak39_spidev_cs), + .bus_num = AKSPI_BUS_NUM1, + .clk_name = "spi1", + .mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH, + .xfer_mode = AKSPI_XFER_MODE_DMA, +}; + +/** + * @brief: spiflash device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct flash_platform_data ak39_spiflash_info= { + .bus_width = FLASH_BUS_WIDTH_4WIRE | FLASH_BUS_WIDTH_2WIRE | FLASH_BUS_WIDTH_1WIRE, + .type = NULL, +}; + +/** + * @brief: spi bus device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct spi_board_info ak39_spi_board_dev[] = { + { + .modalias = "ak-spiflash", + .bus_num = AKSPI_BUS_NUM1, + .chip_select = AKSPI_ONCHIP_CS, + .mode = SPI_MODE_0, + .max_speed_hz = 20*1000*1000, + .platform_data = &ak39_spiflash_info, + }, +}; + + +/** + * @brief: mci2 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci2_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .cap_highspeed = 1, + .detect_mode = AKMCI_PLUGIN_ALWAY, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_SDIO, + .gpio_init = ak_gpio_set, + .bus_width = MCI_BUS_WIDTH_4, + .max_speed_hz = 30*1000*1000, + .gpio_cd = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, +}; + +/** + * @brief: mci1 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci1_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_DETECT_MODE_GPIO, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_MMC_SD, + .max_speed_hz = 50*1000*1000, + .gpio_init = ak_gpio_set, + .bus_width = MCI_BUS_WIDTH_1, + .cap_highspeed = 1, + .gpio_cd = { + .pin = AK_GPIO_6, // cdh: acohao,2018,11,20 + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, +}; + + +struct ak_crypto_plat_data akcrypto_pdata = { + .encrypt_mode = CRYPTO_MULTI_GROUP_MODE, +}; + + +/* akwifi platform data */ +struct akwifi_platform_data akwifi_pdata = { + .gpio_init = ak_gpio_set, + .gpio_cs = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, // cs ÓÐЧֵ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_on = { + .pin = AK_GPIO_20, + .pulldown = AK_PULLDOWN_DISABLE, //-1, + .pullup = -1,//AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, //AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_off = { + .pin = AK_GPIO_20, + .pulldown = AK_PULLDOWN_DISABLE,//-1, + .pullup = -1,//AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_HIGH, //AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .power_on_delay = 2000, + .power_off_delay = 200, +}; + +struct platform_device anyka_wifi_device = { + .name = "anyka-wifi", + .id = -1, + .dev = { + .platform_data = &akwifi_pdata, + }, +}; + + +/** + * @brief: usb bus device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct akotghc_usb_platform_data akotghc_plat_data = { + .gpio_init = ak_gpio_set, + .gpio_pwr_on = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, //AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_pwr_off = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, //AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .switch_onboard = { + .pin = -1, + }, + .switch_extport = { + .pin = -1, + }, +}; + + + +/** +* @brief ak pcm device struct + hp and spk can identify by GPIO or AD. + wo can initialize it in this struct. +* @author dengzhou +* @date 2012-07-19 +*/ +struct ak39_codec_platform_data ak39_codec_pdata = +{ + .hpdet_gpio = + { + .pin = -1, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = -1, + .int_pol = -1, + }, + .spk_down_gpio = + { + .pin = AK_GPIO_13, //adh:caohao,2018.11.21,speak_en(gpio13) exchange with WL_CL + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .hpmute_gpio = + { + .pin = INVALID_GPIO, //AK_GPIO_29, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .linindet_gpio = + { + .pin = -1, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, /* linein detect level */ + .int_pol = -1, + }, + + .hp_on_value = AK_GPIO_OUT_LOW, + .hpdet_irq = -1, + .linindet_irq = -1, + .bIsHPmuteUsed = 0, + .hp_mute_enable_value = AK_GPIO_OUT_HIGH, + .bIsMetalfixed = 0, + .boutput_only = 1, + .detect_flag = AKPCM_MIC_ALWAY, +}; + +struct resource ak39_codec_resources[] = { + [0] = { + .start = 0x08000000, + .end = 0x0800FFFF, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_AnalogCtrlRegs", + }, + [1] = { + .start = 0x20110000, + .end = 0x2011800F, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_ADC2ModeCfgRegs", + }, +}; + + +struct platform_device ak39_codec_device = { + .name = "ak39-codec", + .id = -1, + .resource = ak39_codec_resources, + .num_resources = ARRAY_SIZE(ak39_codec_resources), + .dev = { + .platform_data = &ak39_codec_pdata, + }, +}; + + +/** + * @brief: sensor device platform data, this info is fake. + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct i2c_board_info ak_camara_devices[] = { + { + I2C_BOARD_INFO("aksensor", 0x1), + }, +}; + +static struct aksensor_camera_info ak_soc_camera_info = { + .buswidth = SOCAM_DATAWIDTH_8, + .pin_pwdn = INVALID_GPIO, //initialize GPIO for the power of camera. + .pin_reset = AK_GPIO_49, + .link = { + .bus_id = 39, + .power = NULL, + .board_info = &ak_camara_devices[0], + .i2c_adapter_id = 0, + .priv = &ak_soc_camera_info, + } +}; + +/* fake device for soc_camera subsystem */ +static struct platform_device soc_camera_interface = { + .name = "soc-camera-pdrv", + .id = -1, + .dev = { + .platform_data = &ak_soc_camera_info.link, + } +}; + + +/** + * @brief: LED platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_led_data leds[] = { + { + .name = "blue_led", + .def_trigger = "none", + .gpio = { + .pin = AK_GPIO_80, + .pulldown = -1, + .pullup = AK_PULLUP_ENABLE, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + } + }, + { + .name = "yellow_led", + .def_trigger = "none", + .gpio = { + .pin = AK_GPIO_81, + .pulldown = -1, + .pullup = AK_PULLUP_ENABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + } + }, + +}; + +static struct ak_led_pdata led_pdata = { + .leds = leds, + .nr_led = ARRAY_SIZE(leds), +}; + +#if 0 +/* unused GPIO number for the machine board is left*/ +static unsigned int ak39_custom_gpiopin[] = { + //AK_GPIO_5, + //AK_GPIO_61, +}; + +static struct custom_gpio_data ak39_custom_gpios= { + .gpiopin = ak39_custom_gpiopin, + .ngpiopin = ARRAY_SIZE(ak39_custom_gpiopin), +}; + +static struct platform_device ak39_custom_gpio = { + .name = "akgpio", + .id = -1, + .dev = { + .platform_data = &ak39_custom_gpios, + }, +}; +#endif + +/** + * @brief: camera platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_camera_pdata ak39_camera_info = { + .mclk = 24, + .flags = 0, + //.interface = DVP_INTERFACE, +}; + +static struct user_gpio_info user_gpios[] = { + { + .name = "gpio-ircut_a", + .info = { + .pin = AK_GPIO_10, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, /* in daytime level */ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "gpio-ircut_b", + .info = { + .pin = AK_GPIO_11, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, // in daytime level + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "gpio-wl_reg_en", + .info = { + .pin = AK_GPIO_15, + .pulldown = AK_PULLUP_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, // in daytime level + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "gpio-wl_cl", + .info = { + .pin = AK_GPIO_82, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, // in daytime level + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "led-yellow", + .info = { + .pin = AK_GPIO_81, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "led-blue", + .info = { + .pin = AK_GPIO_80, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "motor-reset", + .info = { + .pin = AK_GPIO_10, + .pulldown = AK_PULLDOWN_ENABLE, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "spi-cs", + .info = { + .pin = AK_GPIO_11, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, +}; + +static struct ak_user_gpio_pdata user_gpio_pdata = { + .user_gpios = user_gpios, + .nr_user_gpios = ARRAY_SIZE(user_gpios), +}; + +static struct platform_device user_gpio = { + .name = "user_gpio", + .id = -1, + .dev = { + .platform_data = &user_gpio_pdata, + }, +}; + +/** + * @brief: GPIO buttons platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct gpio_keys_button gpio_keys_button[] = { + { + .code = KEY_0, + .type = EV_KEY, + .gpio = AK_GPIO_70, + .active_low = 1, + .wakeup = 1, + .debounce_interval = 100, /* ms */ + .desc = "soft_boot", + .pullup = -1, + .pulldown = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = AK_GPIO_INT_LOWLEVEL, + }, +}; + +static struct akgpio_keys_platform_data gpio_keys_platform_data = { + .buttons = gpio_keys_button, + .nbuttons = ARRAY_SIZE(gpio_keys_button), + .rep = 0, +}; + +/** + * @brief: power manage gpios info + * + * @author: ye_guohong + * @date: 2014-12-09 + */ +static struct ak_wakeup_gpio pm_gpios[] = { + { + .pin = AK_GPIO_63, + .wakeup_pol = AK_FALLING_TRIGGERED, + }, +}; + +static struct ak_pm_pdata pm_pdata = { + .gpios = pm_gpios, + .nr_gpios = ARRAY_SIZE(pm_gpios), +}; + +/** +* @brief ad-key platform device struct + we should initialize the correct voltage for each key. +* @author: caolianming +* @date: 2014-01-09 +*/ + +struct adgpio_key adkey[][1] = { + {{ .code = KEY_0, .min = 0, .max = 700}, + }, +}; + + + +struct multi_addetect multi_det[] = { + {.unpress_min = 0, .unpress_max = 3000, .fixkeys = adkey[0], .plugdev = PLUGIN_DEV1}, // = null 3251+/-20 +}; + +struct analog_gpio_key ak39_adkey_data = { + .desc = "adkey", + .interval = 120, /* ms */ + .debounce_interval = 20, /* ms */ + .addet = multi_det, + .naddet = ARRAY_SIZE(multi_det), + .nkey = ARRAY_SIZE(adkey[0]), + .wakeup = 1, /* enable ad-key wakeup from standby mode */ +}; + +static struct platform_device ak39_adkey_device = { + .name = "ad-keys", + .id = -1, + .dev = { + .platform_data = &ak39_adkey_data, + } +}; + + +#if 0 +/** + * @brif: ak39 battery mach info + + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_bat_mach_info ak39_bat_info = { + .gpio_init = ak_gpio_set, + .usb_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .ac_gpio = { + .is_detect_mode = BAT_CHARGE_ADC_DETECT, + .active = -1, + .irq = -1, + .delay = 500, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pulldown = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .full_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .bat_mach_info = { + .voltage_sample = 6, // the sample of read voltage + .power_on_voltage = 3650, // discharge power on voltage limit + .power_on_correct = 64, // mv + .charge_min_voltage = 3550, // charge minute voltage (mv) + .max_voltage = 4200, // max battery voltage + .min_voltage = 3500, // min battery voltage + .power_off = poweroff_disable, + .full_capacity = 100, // battery full + .poweroff_cap = 0, // user read value to power off + .low_cap = 5, // user read value to low power warring + .recover_cap = 30, + .cpower_on_voltage = 3700, // charge power on voltage limit + .full_delay = 30, // unit is minute + .full_voltage = 4100, + }, + + .bat_adc = { + .up_resistance = 10, + .dw_resistance = 10, + .voltage_correct = 32, // battery correct factor + .adc_avdd = 3300, // avdd voltage + }, + +}; +#endif + +#if defined(CONFIG_I2C_GPIO_SOFT) +struct i2c_gpio_platform_data ak39_i2c_data={ + .sda_pin = AK_GPIO_28, + .scl_pin = AK_GPIO_27, + .udelay = 30, + .timeout = 300, +}; + +struct platform_device ak39_i2c_device = { + .name = "i2c-gpio", + .id = -1, + .dev = { + .platform_data = &ak39_i2c_data, + }, +}; +#endif + + /* @brief: ak39 platform devices table + * + * @author: caolianming + * @date: 2014-01-09 + */ + static struct platform_device *ak3918_platform_devices[] __initdata = { + &akfha_char_device, + &ak39_uart0_device, + //&ak39_uart1_device, + //&ak39_motor0_device, + //&ak39_motor1_device, + &ak39_pwm_device, + &ak39_spi1_device, + //&ak39_spi2_device, + &ak39_mci1_device, + &ak39_mci2_device, + &ak39_i2c_device, + //&ak39_custom_gpio, + &ak39_usb_udc_device, + &ak39_usb_otg_hcd_device, + &anyka_wifi_device, + &soc_camera_interface, + &ak39_camera_interface, + &ak39_ion_device, + &ak39_pcm_device, + &ak39_codec_device, + &ak39_mmx_device, + //&ak39_mac_device, + &ak39_led_pdev, + &ak39_gpio_keys_device, + &ak39_adkey_device, + //&ak39_battery_power, + &ak39_rtc_device, + &ak39_crypto_device, + &ak39_pm_device, + &user_gpio, +}; + +void wdt_enable(void); +void wdt_keepalive(unsigned int heartbeat); + +/** + * @brief: restart by "reboot" cmd + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void ak39_restart(char str, const char *cmd) +{ + //ak39_reboot_sys_by_soft(); +#if defined CONFIG_AK39_WATCHDOG || defined CONFIG_AK39_WATCHDOG_TOP + wdt_enable(); + wdt_keepalive(2); +#endif +} + + +/** + * @brief: initial ak3918 machine + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void __init ak3918_init_machine(void) +{ + adc1_init(); + + spi_register_board_info(ak39_spi_board_dev, ARRAY_SIZE(ak39_spi_board_dev)); + + ak39_spi1_device.dev.platform_data = &ak39_spi1_info; + + //ak39_motor0_device.dev.platform_data = &ak39_motor0_pdata; + //ak39_motor1_device.dev.platform_data = &ak39_motor1_pdata; + + ak39_mci1_device.dev.platform_data = &mci1_plat_data; + ak39_mci2_device.dev.platform_data = &mci2_plat_data; + + ak39_crypto_device.dev.platform_data = &akcrypto_pdata; + + ak39_usb_otg_hcd_device.dev.platform_data = &akotghc_plat_data; + //ak39_mac_device.dev.platform_data = &ak39_mac_pdata; + + ak39_led_pdev.dev.platform_data = &led_pdata; + ak39_gpio_keys_device.dev.platform_data = &gpio_keys_platform_data; + //ak39_battery_power.dev.platform_data = &ak39_bat_info; + + ak39_camera_interface.dev.platform_data = &ak39_camera_info; + + ak39_pm_device.dev.platform_data = &pm_pdata; + + platform_add_devices(ak3918_platform_devices, + ARRAY_SIZE(ak3918_platform_devices)); + + l2_init(); + + return; +} + + +MACHINE_START(AK39XX, "AK3918EV300_HL_XIAOFANG") +/* Maintainer: */ + .atag_offset = 0x100, + .fixup = NULL, + .map_io = ak39_map_io, + .reserve = NULL, + .init_irq = ak39_init_irq, + .init_machine = ak3918_init_machine, + .init_early = NULL, + .timer = &ak39_timer, + .restart = ak39_restart, + +MACHINE_END + diff --git a/arch/arm/mach-ak39/mach-ak3918ev300_jw_f37_v10.c b/arch/arm/mach-ak39/mach-ak3918ev300_jw_f37_v10.c new file mode 100644 index 00000000..bc6e5dd7 --- /dev/null +++ b/arch/arm/mach-ak39/mach-ak3918ev300_jw_f37_v10.c @@ -0,0 +1,961 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "cpu.h" +#include "irq.h" +#include +#include +#include +#include +#include + +/** + * @brief: spi device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +#define SPI_ONCHIP_CS (0) /*means not need gpio*/ +static unsigned long ak39_spidev_cs[AKSPI_CS_NUM] = { + [AKSPI_ONCHIP_CS] = SPI_ONCHIP_CS, /*gpio 25, spidev0: ak-spiflash*/ +}; + +struct ak_spi_info ak39_spi1_info = { + .pin_cs = ak39_spidev_cs, + .num_cs = ARRAY_SIZE(ak39_spidev_cs), + .bus_num = AKSPI_BUS_NUM1, + .clk_name = "spi1", + .mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH, + .xfer_mode = AKSPI_XFER_MODE_DMA, +}; + +/** + * @brief: spiflash device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct flash_platform_data ak39_spiflash_info= { + .bus_width = FLASH_BUS_WIDTH_4WIRE | FLASH_BUS_WIDTH_2WIRE | FLASH_BUS_WIDTH_1WIRE, + .type = NULL, +}; + +/** + * @brief: spi bus device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct spi_board_info ak39_spi_board_dev[] = { + { + .modalias = "ak-spiflash", + .bus_num = AKSPI_BUS_NUM1, + .chip_select = AKSPI_ONCHIP_CS, + .mode = SPI_MODE_0, + .max_speed_hz = 20*1000*1000, + .platform_data = &ak39_spiflash_info, + }, +}; + + +/** + * @brief: motor0 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor0_pdata = { + .gpio_phase[0] = { + .pin = AK_GPIO_65, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = AK_GPIO_66, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = AK_GPIO_67, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = AK_GPIO_6, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + .gpio_hit[0] = { + .pin = -1,//AK_GPIO_62, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = -1,//AK_GPIO_63, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + //.angular_speed = 100, /* angle/s */ + .angular_speed = 200, /* angle/s */ +}; + + +/** + * @brief: motor1 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor1_pdata = { + .gpio_phase[0] = { + .pin = AK_GPIO_7, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = AK_GPIO_8, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = AK_GPIO_9, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = AK_GPIO_68, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + .gpio_hit[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + .angular_speed = 100, /* angle/s */ +}; + +/** + * @brief: mci2 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci2_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_PLUGIN_ALWAY, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_SDIO, + .gpio_init = ak_gpio_set, + .max_speed_hz = 25*1000*1000, + .gpio_cd = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_pwr = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, +}; + +/** + * @brief: mci1 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci1_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_DETECT_MODE_GPIO, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_MMC_SD, + .max_speed_hz = 30*1000*1000,//25*1000*1000, + .cap_highspeed = 1, + .gpio_init = ak_gpio_set, + .bus_width = MCI_BUS_WIDTH_1, + .gpio_cd = { + .pin = AK_GPIO_82, // cdh: AK_GPIO_57 + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_pwr = { + .pin = AK_GPIO_42, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_ENABLE, ///< 这里统一 value=1 为有效即默认电源启用,具体底层高低电平有效操作通过 pwr_dn 在内部实现。 + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + .pwr_dn = AK_GPIO_OUT_HIGH, ///< 高电平时停用。 + } +}; + + +struct ak_crypto_plat_data akcrypto_pdata = { + .encrypt_mode = CRYPTO_MULTI_GROUP_MODE, +}; + + +/* akwifi platform data */ +struct akwifi_platform_data akwifi_pdata = { + .gpio_init = ak_gpio_set, + .gpio_cs = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, // cs 脫脨脨搂脰碌 + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_on = { + .pin = AK_GPIO_0, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_off = { + .pin = AK_GPIO_0, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .power_on_delay = 2000, + .power_off_delay = 200, +}; + +struct platform_device anyka_wifi_device = { + .name = "anyka-wifi", + .id = -1, + .dev = { + .platform_data = &akwifi_pdata, + }, +}; + + +/** + * @brief: usb bus device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct akotghc_usb_platform_data akotghc_plat_data = { + .gpio_init = ak_gpio_set, + .gpio_pwr_on = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_pwr_off = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .switch_onboard = { + .pin = -1, + }, + .switch_extport = { + .pin = -1, + }, +}; + + +/** + * @brief: ethenet device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_mac_data ak39_mac_pdata = { + .gpio_init = ak_gpio_set, + .pwr_gpio = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .phy_rst_gpio = { + .pin = -1,//AK_GPIO_81, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, +}; + + +/** +* @brief ak pcm device struct + hp and spk can identify by GPIO or AD. + wo can initialize it in this struct. +* @author dengzhou +* @date 2012-07-19 +*/ +struct ak39_codec_platform_data ak39_codec_pdata = +{ + .hpdet_gpio = + { + .pin = -1, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = -1, + .int_pol = -1, + }, + .spk_down_gpio = + { + .pin = AK_GPIO_41, //AK_GPIO_16, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .hpmute_gpio = + { + .pin = INVALID_GPIO, //AK_GPIO_29, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .linindet_gpio = + { + .pin = -1,//AK_GPIO_82, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, /* linein detect level */ + .int_pol = -1, + }, + + .hp_on_value = AK_GPIO_OUT_LOW, + .hpdet_irq = -1, + .linindet_irq = -1,//IRQ_GPIO_82, + .bIsHPmuteUsed = 0, + .hp_mute_enable_value = AK_GPIO_OUT_HIGH, + .bIsMetalfixed = 0, + .boutput_only = 1, + .detect_flag = AKPCM_DYNAMIC_DETECT, +}; + +struct resource ak39_codec_resources[] = { + [0] = { + .start = 0x08000000, + .end = 0x0800FFFF, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_AnalogCtrlRegs", + }, + [1] = { + .start = 0x20110000, + .end = 0x2011800F, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_ADC2ModeCfgRegs", + }, +}; + + +struct platform_device ak39_codec_device = { + .name = "ak39-codec", + .id = -1, + .resource = ak39_codec_resources, + .num_resources = ARRAY_SIZE(ak39_codec_resources), + .dev = { + .platform_data = &ak39_codec_pdata, + }, +}; + + +/** + * @brief: sensor device platform data, this info is fake. + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct i2c_board_info ak_camara_devices[] = { + { + I2C_BOARD_INFO("aksensor", 0x1), + }, +}; + +static struct aksensor_camera_info ak_soc_camera_info = { + .buswidth = SOCAM_DATAWIDTH_8, + .pin_pwdn = INVALID_GPIO, //initialize GPIO for the power of camera. + .pin_reset = AK_GPIO_49, + .link = { + .bus_id = 39, + .power = NULL, + .board_info = &ak_camara_devices[0], + .i2c_adapter_id = 0, + .priv = &ak_soc_camera_info, + } +}; + +/* fake device for soc_camera subsystem */ +static struct platform_device soc_camera_interface = { + .name = "soc-camera-pdrv", + .id = -1, + .dev = { + .platform_data = &ak_soc_camera_info.link, + } +}; + + +/** + * @brief: LED platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_led_data leds[] = { + { + .name = "system_state_led", + .def_trigger = "none", + .effective_level = AK_GPIO_OUT_HIGH, //LED灯控制有效电平 + .gpio = { + .pin = AK_GPIO_5, + .pulldown = -1, + .pullup = AK_PULLUP_ENABLE, + .value = AK_GPIO_OUT_HIGH, //系统启动LED管脚默认电平 + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + } + }, + +}; + +static struct ak_led_pdata led_pdata = { + .leds = leds, + .nr_led = ARRAY_SIZE(leds), +}; + +#if 1 +/* unused GPIO number for the machine board is left*/ +static unsigned int ak39_custom_gpiopin[] = { + AK_GPIO_5,AK_GPIO_6,AK_GPIO_7,AK_GPIO_8,AK_GPIO_9,AK_GPIO_41,AK_GPIO_42,\ + AK_GPIO_65,AK_GPIO_66,AK_GPIO_67,AK_GPIO_68,AK_GPIO_69,AK_GPIO_81,AK_GPIO_84, +}; + +static struct custom_gpio_data ak39_custom_gpios= { + .gpiopin = ak39_custom_gpiopin, + .ngpiopin = ARRAY_SIZE(ak39_custom_gpiopin), +}; + +static struct platform_device ak39_custom_gpio = { + .name = "akgpio", + .id = -1, + .dev = { + .platform_data = &ak39_custom_gpios, + }, +}; +#endif + +/** + * @brief: camera platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_camera_pdata ak39_camera_info = { + .mclk = 24, + .flags = 0, + //.interface = DVP_INTERFACE, +}; + +static struct user_gpio_info user_gpios[] = { + { + .name = "gpio-ircut_a", + .info = { + .pin = AK_GPIO_80, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, /* in daytime level */ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "TF-POWER", + .info = { + .pin = AK_GPIO_42, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, // in daytime level + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "gpio84-PIR", + .info = { + .pin = AK_GPIO_84, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, /* in daytime level */ + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + }, + { + .name = "gpio41-SPK", + .info = { + .pin = AK_GPIO_41, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, /* in daytime level */ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "gpio81-RST", + .info = { + .pin = AK_GPIO_81, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_HIGH, /* in daytime level */ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "ir-led", + .info = { + .pin = AK_GPIO_69, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, // in daytime level ir-led closed + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, +}; + +static struct ak_user_gpio_pdata user_gpio_pdata = { + .user_gpios = user_gpios, + .nr_user_gpios = ARRAY_SIZE(user_gpios), +}; + +static struct platform_device user_gpio_device = { + .name = "user_gpio", + .id = -1, + .dev = { + .platform_data = &user_gpio_pdata, + }, +}; + +/** + * @brief: GPIO buttons platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct gpio_keys_button gpio_keys_button[] = { + { + .code = KEY_0, + .type = EV_KEY, + .gpio = AK_GPIO_81, + .active_low = 1, + .wakeup = 1, + .debounce_interval = 100, /* ms */ + .desc = "soft_boot", + .pullup = AK_PULLUP_ENABLE, + .pulldown = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = AK_GPIO_INT_LOWLEVEL, + }, +}; + +static struct akgpio_keys_platform_data gpio_keys_platform_data = { + .buttons = gpio_keys_button, + .nbuttons = ARRAY_SIZE(gpio_keys_button), + .rep = 0, +}; + +/** + * @brief: power manage gpios info + * + * @author: ye_guohong + * @date: 2014-12-09 + */ +static struct ak_wakeup_gpio pm_gpios[] = { + { + .pin = AK_GPIO_63, + .wakeup_pol = AK_FALLING_TRIGGERED, + }, +}; + +static struct ak_pm_pdata pm_pdata = { + .gpios = pm_gpios, + .nr_gpios = ARRAY_SIZE(pm_gpios), +}; + +#if 0 +/** +* @brief ad-key platform device struct + we should initialize the correct voltage for each key. +* @author: caolianming +* @date: 2014-01-09 +*/ +struct multi_addetect multi_det[] = { + {.unpress_min = 3220, .unpress_max = 3258, .fixkeys = NULL, .plugdev = PLUGIN_NODEV}, // = null 3251+/-20 + {.unpress_min = 3259, .unpress_max = 3300, .fixkeys = NULL, .plugdev = PLUGIN_AC}, // = sddet 3261(+/-30) + {.unpress_min = 2230, .unpress_max = 2290, .fixkeys = NULL, .plugdev = PLUGIN_MMC}, // = sddet 2262(+/-30) + {.unpress_min = 1700, .unpress_max = 1780, .fixkeys = NULL, .plugdev = PLUGIN_MMC_AC}, // = sddet 1742(+/-30) +}; + +struct analog_gpio_key ak39_adkey_data = { + .desc = "adkey", + .interval = 300, /* ms */ + .debounce_interval = 20, /* ms */ + .addet = multi_det, + .naddet = ARRAY_SIZE(multi_det), + .nkey = 0, + .wakeup = 1, /* enable ad-key wakeup from standby mode */ +}; + +static struct platform_device ak39_adkey_device = { + .name = "ad-keys", + .id = -1, + .dev = { + .platform_data = &ak39_adkey_data, + } +}; +#endif + + +#if 0 +/** + * @brif: ak39 battery mach info + + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_bat_mach_info ak39_bat_info = { + .gpio_init = ak_gpio_set, + .usb_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .ac_gpio = { + .is_detect_mode = BAT_CHARGE_ADC_DETECT, + .active = -1, + .irq = -1, + .delay = 500, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pulldown = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .full_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .bat_mach_info = { + .voltage_sample = 6, // the sample of read voltage + .power_on_voltage = 3650, // discharge power on voltage limit + .power_on_correct = 64, // mv + .charge_min_voltage = 3550, // charge minute voltage (mv) + .max_voltage = 4200, // max battery voltage + .min_voltage = 3500, // min battery voltage + .power_off = poweroff_disable, + .full_capacity = 100, // battery full + .poweroff_cap = 0, // user read value to power off + .low_cap = 5, // user read value to low power warring + .recover_cap = 30, + .cpower_on_voltage = 3700, // charge power on voltage limit + .full_delay = 30, // unit is minute + .full_voltage = 4100, + }, + + .bat_adc = { + .up_resistance = 10, + .dw_resistance = 10, + .voltage_correct = 32, // battery correct factor + .adc_avdd = 3300, // avdd voltage + }, + +}; +#endif + +#if defined(CONFIG_I2C_GPIO_SOFT) +struct i2c_gpio_platform_data ak39_i2c_data={ + .sda_pin = AK_GPIO_28, + .scl_pin = AK_GPIO_27, + .udelay = 30, + .timeout = 300, +}; + +struct platform_device ak39_i2c_device = { + .name = "i2c-gpio", + .id = -1, + .dev = { + .platform_data = &ak39_i2c_data, + }, +}; +#endif + + /* @brief: ak39 platform devices table + * + * @author: caolianming + * @date: 2014-01-09 + */ + static struct platform_device *ak3918_platform_devices[] __initdata = { + &akfha_char_device, + &ak39_uart0_device, + &ak39_uart1_device, + //&ak39_motor0_device, + //&ak39_motor1_device, + &ak39_pwm_device, + &ak39_spi1_device, + &ak39_mci1_device, + //&ak39_mci2_device, + &ak39_i2c_device, + &ak39_custom_gpio, + &ak39_usb_udc_device, + &ak39_usb_otg_hcd_device, + &anyka_wifi_device, + &soc_camera_interface, + &ak39_camera_interface, + &ak39_ion_device, + &ak39_pcm_device, + &ak39_codec_device, + &ak39_mmx_device, + &ak39_mac_device, + &ak39_led_pdev, + &ak39_gpio_keys_device, + //&ak39_adkey_device, + //&ak39_battery_power, + &ak39_rtc_device, + &ak39_crypto_device, + &ak39_pm_device, + &user_gpio_device, +}; + +void wdt_enable(void); +void wdt_keepalive(unsigned int heartbeat); + +/** + * @brief: restart by "reboot" cmd + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void ak39_restart(char str, const char *cmd) +{ + //ak39_reboot_sys_by_soft(); +#if defined CONFIG_AK39_WATCHDOG || defined CONFIG_AK39_WATCHDOG_TOP + wdt_enable(); + wdt_keepalive(2); +#endif +} + + +/** + * @brief: initial ak3918 machine + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void __init ak3918_init_machine(void) +{ + adc1_init(); + + spi_register_board_info(ak39_spi_board_dev, ARRAY_SIZE(ak39_spi_board_dev)); + + ak39_spi1_device.dev.platform_data = &ak39_spi1_info; + + //ak39_motor0_device.dev.platform_data = &ak39_motor0_pdata; + //ak39_motor1_device.dev.platform_data = &ak39_motor1_pdata; + + ak39_mci1_device.dev.platform_data = &mci1_plat_data; + //ak39_mci2_device.dev.platform_data = &mci2_plat_data; + + ak39_crypto_device.dev.platform_data = &akcrypto_pdata; + + ak39_usb_otg_hcd_device.dev.platform_data = &akotghc_plat_data; + ak39_mac_device.dev.platform_data = &ak39_mac_pdata; + + ak39_led_pdev.dev.platform_data = &led_pdata; + ak39_gpio_keys_device.dev.platform_data = &gpio_keys_platform_data; + //ak39_battery_power.dev.platform_data = &ak39_bat_info; + + ak39_camera_interface.dev.platform_data = &ak39_camera_info; + + ak39_pm_device.dev.platform_data = &pm_pdata; + + + platform_add_devices(ak3918_platform_devices, + ARRAY_SIZE(ak3918_platform_devices)); + + l2_init(); + + return; +} + + +MACHINE_START(AK39XX, "AK3918EV300_JW_F37_V10") +/* Maintainer: */ + .atag_offset = 0x100, + .fixup = NULL, + .map_io = ak39_map_io, + .reserve = NULL, + .init_irq = ak39_init_irq, + .init_machine = ak3918_init_machine, + .init_early = NULL, + .timer = &ak39_timer, + .restart = ak39_restart, + +MACHINE_END + diff --git a/arch/arm/mach-ak39/mach-ak3918ev300_sc10008_v100.c b/arch/arm/mach-ak39/mach-ak3918ev300_sc10008_v100.c new file mode 100644 index 00000000..21244528 --- /dev/null +++ b/arch/arm/mach-ak39/mach-ak3918ev300_sc10008_v100.c @@ -0,0 +1,958 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "cpu.h" +#include "irq.h" +#include +#include +#include +#include +#include + +/** + * @brief: spi device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +#define SPI_ONCHIP_CS (0) /*means not need gpio*/ +static unsigned long ak39_spidev_cs[AKSPI_CS_NUM] = { + [AKSPI_ONCHIP_CS] = SPI_ONCHIP_CS, /*gpio 25, spidev0: ak-spiflash*/ +}; + +struct ak_spi_info ak39_spi1_info = { + .pin_cs = ak39_spidev_cs, + .num_cs = ARRAY_SIZE(ak39_spidev_cs), + .bus_num = AKSPI_BUS_NUM1, + .clk_name = "spi1", + .mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH, + .xfer_mode = AKSPI_XFER_MODE_DMA, +}; + +/** + * @brief: spiflash device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct flash_platform_data ak39_spiflash_info= { + .bus_width = FLASH_BUS_WIDTH_4WIRE | FLASH_BUS_WIDTH_2WIRE | FLASH_BUS_WIDTH_1WIRE, + .type = NULL, +}; + +/** + * @brief: spi bus device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct spi_board_info ak39_spi_board_dev[] = { + { + .modalias = "ak-spiflash", + .bus_num = AKSPI_BUS_NUM1, + .chip_select = AKSPI_ONCHIP_CS, + .mode = SPI_MODE_0, + .max_speed_hz = 20*1000*1000, + .platform_data = &ak39_spiflash_info, + }, +}; + + +/** + * @brief: motor0 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor0_pdata = { + .gpio_phase[0] = { + .pin = AK_GPIO_37, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = AK_GPIO_38, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = AK_GPIO_39, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = AK_GPIO_40, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + .gpio_hit[0] = { + .pin = AK_GPIO_62, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = AK_GPIO_63, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + //.angular_speed = 100, /* angle/s */ + .angular_speed = 200, /* angle/s */ +}; + + +/** + * @brief: motor1 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor1_pdata = { + .gpio_phase[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + .gpio_hit[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + .angular_speed = 100, /* angle/s */ +}; + +/** + * @brief: mci2 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci2_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_PLUGIN_ALWAY, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_SDIO, + .gpio_init = ak_gpio_set, + .max_speed_hz = 25*1000*1000, + .gpio_cd = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + +/** + * @brief: mci1 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci1_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_DETECT_MODE_GPIO, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_MMC_SD, + .max_speed_hz = 25*1000*1000, + .gpio_init = ak_gpio_set, + .bus_width = MCI_BUS_WIDTH_1, + .gpio_cd = { + .pin = AK_GPIO_82, // cdh: AK_GPIO_57 + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + + +struct ak_crypto_plat_data akcrypto_pdata = { + .encrypt_mode = CRYPTO_MULTI_GROUP_MODE, +}; + + +/* akwifi platform data */ +struct akwifi_platform_data akwifi_pdata = { + .gpio_init = ak_gpio_set, + .gpio_cs = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, // cs 脫脨脨搂脰碌 + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_on = { + .pin = AK_GPIO_50, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_off = { + .pin = AK_GPIO_50, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .power_on_delay = 2000, + .power_off_delay = 200, +}; + +struct platform_device anyka_wifi_device = { + .name = "anyka-wifi", + .id = -1, + .dev = { + .platform_data = &akwifi_pdata, + }, +}; + + +/** + * @brief: usb bus device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct akotghc_usb_platform_data akotghc_plat_data = { + .gpio_init = ak_gpio_set, + .gpio_pwr_on = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_pwr_off = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .switch_onboard = { + .pin = -1, + }, + .switch_extport = { + .pin = -1, + }, +}; + + +/** + * @brief: ethenet device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_mac_data ak39_mac_pdata = { + .gpio_init = ak_gpio_set, + .pwr_gpio = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .phy_rst_gpio = { + .pin = AK_GPIO_81, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, +}; + + +/** +* @brief ak pcm device struct + hp and spk can identify by GPIO or AD. + wo can initialize it in this struct. +* @author dengzhou +* @date 2012-07-19 +*/ +struct ak39_codec_platform_data ak39_codec_pdata = +{ + .hpdet_gpio = + { + .pin = -1, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = -1, + .int_pol = -1, + }, + .spk_down_gpio = + { + .pin = AK_GPIO_14, //AK_GPIO_16, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .hpmute_gpio = + { + .pin = INVALID_GPIO, //AK_GPIO_29, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .linindet_gpio = + { + //.pin = AK_GPIO_82, + .pin = -1, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, /* linein detect level */ + .int_pol = -1, + }, + + .hp_on_value = AK_GPIO_OUT_LOW, + .hpdet_irq = -1, + //.linindet_irq = IRQ_GPIO_82, + .linindet_irq = -1, + .bIsHPmuteUsed = 0, + .hp_mute_enable_value = AK_GPIO_OUT_HIGH, + .bIsMetalfixed = 0, + .boutput_only = 1, + .detect_flag = AKPCM_DYNAMIC_DETECT, +}; + +struct resource ak39_codec_resources[] = { + [0] = { + .start = 0x08000000, + .end = 0x0800FFFF, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_AnalogCtrlRegs", + }, + [1] = { + .start = 0x20110000, + .end = 0x2011800F, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_ADC2ModeCfgRegs", + }, +}; + + +struct platform_device ak39_codec_device = { + .name = "ak39-codec", + .id = -1, + .resource = ak39_codec_resources, + .num_resources = ARRAY_SIZE(ak39_codec_resources), + .dev = { + .platform_data = &ak39_codec_pdata, + }, +}; + + +/** + * @brief: sensor device platform data, this info is fake. + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct i2c_board_info ak_camara_devices[] = { + { + I2C_BOARD_INFO("aksensor", 0x1), + }, +}; + +static struct aksensor_camera_info ak_soc_camera_info = { + .buswidth = SOCAM_DATAWIDTH_8, + .pin_pwdn = AK_GPIO_65, //initialize GPIO for the power of camera. + .pin_reset = AK_GPIO_49, + .link = { + .bus_id = 39, + .power = NULL, + .board_info = &ak_camara_devices[0], + .i2c_adapter_id = 0, + .priv = &ak_soc_camera_info, + } +}; + +/* fake device for soc_camera subsystem */ +static struct platform_device soc_camera_interface = { + .name = "soc-camera-pdrv", + .id = -1, + .dev = { + .platform_data = &ak_soc_camera_info.link, + } +}; + + +/** + * @brief: LED platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_led_data leds[] = { + { + .name = "system_state_led", + .def_trigger = "none", + .effective_level = AK_GPIO_OUT_HIGH, //LED灯控制有效电平 + .gpio = { + .pin = AK_GPIO_80, + .pulldown = -1, + .pullup = AK_PULLUP_ENABLE, + .value = AK_GPIO_OUT_HIGH, //系统启动LED管脚默认电平 + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + } + }, + +}; + +static struct ak_led_pdata led_pdata = { + .leds = leds, + .nr_led = ARRAY_SIZE(leds), +}; + +#if 0 +/* unused GPIO number for the machine board is left*/ +static unsigned int ak39_custom_gpiopin[] = { + //AK_GPIO_5, + //AK_GPIO_61, +}; + +static struct custom_gpio_data ak39_custom_gpios= { + .gpiopin = ak39_custom_gpiopin, + .ngpiopin = ARRAY_SIZE(ak39_custom_gpiopin), +}; + +static struct platform_device ak39_custom_gpio = { + .name = "akgpio", + .id = -1, + .dev = { + .platform_data = &ak39_custom_gpios, + }, +}; +#endif + +/** + * @brief: camera platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_camera_pdata ak39_camera_info = { + .mclk = 24, + .flags = 0, + //.interface = DVP_INTERFACE, +}; + +static struct user_gpio_info user_gpios[] = { + { + .name = "gpio-ircut_a", + .info = { + .pin = AK_GPIO_81, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, /* in daytime level */ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "gpio-ircut_b", + .info = { + .pin = AK_GPIO_80, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, // in daytime level + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "wifi_power", + .info = { + .pin = AK_GPIO_11, + .pulldown = -1, + .pullup =-1, + .value = AK_GPIO_OUT_HIGH, // in daytime level + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "ir-feed", + .info = { + .pin = AK_GPIO_5, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, // in daytime level ir-led closed + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "LED_RED", + .info = { + .pin = AK_GPIO_41, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, // in daytime level ir-led closed + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "LED_GREEN", + .info = { + .pin = AK_GPIO_42, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, // in daytime level ir-led closed + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, +#if 0 + { + .name = "ir-led", + .info = { + .pin = AK_GPIO_82, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, // in daytime level ir-led closed + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, +#endif +}; + +static struct ak_user_gpio_pdata user_gpio_pdata = { + .user_gpios = user_gpios, + .nr_user_gpios = ARRAY_SIZE(user_gpios), +}; + +static struct platform_device user_gpio_device = { + .name = "user_gpio", + .id = -1, + .dev = { + .platform_data = &user_gpio_pdata, + }, +}; + +/** + * @brief: GPIO buttons platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct gpio_keys_button gpio_keys_button[] = { + { + .code = KEY_0, + .type = EV_KEY, + .gpio = AK_GPIO_10, + .active_low = 1, + .wakeup = 1, + .debounce_interval = 100, /* ms */ + .desc = "soft_boot", + .pullup = -1, + .pulldown = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = AK_GPIO_INT_LOWLEVEL, + }, +}; + +static struct akgpio_keys_platform_data gpio_keys_platform_data = { + .buttons = gpio_keys_button, + .nbuttons = ARRAY_SIZE(gpio_keys_button), + .rep = 0, +}; + +/** + * @brief: power manage gpios info + * + * @author: ye_guohong + * @date: 2014-12-09 + */ +static struct ak_wakeup_gpio pm_gpios[] = { + { + .pin = AK_GPIO_63, + .wakeup_pol = AK_FALLING_TRIGGERED, + }, +}; + +static struct ak_pm_pdata pm_pdata = { + .gpios = pm_gpios, + .nr_gpios = ARRAY_SIZE(pm_gpios), +}; + +#if 0 +/** +* @brief ad-key platform device struct + we should initialize the correct voltage for each key. +* @author: caolianming +* @date: 2014-01-09 +*/ +struct multi_addetect multi_det[] = { + {.unpress_min = 3220, .unpress_max = 3258, .fixkeys = NULL, .plugdev = PLUGIN_NODEV}, // = null 3251+/-20 + {.unpress_min = 3259, .unpress_max = 3300, .fixkeys = NULL, .plugdev = PLUGIN_AC}, // = sddet 3261(+/-30) + {.unpress_min = 2230, .unpress_max = 2290, .fixkeys = NULL, .plugdev = PLUGIN_MMC}, // = sddet 2262(+/-30) + {.unpress_min = 1700, .unpress_max = 1780, .fixkeys = NULL, .plugdev = PLUGIN_MMC_AC}, // = sddet 1742(+/-30) +}; + +struct analog_gpio_key ak39_adkey_data = { + .desc = "adkey", + .interval = 300, /* ms */ + .debounce_interval = 20, /* ms */ + .addet = multi_det, + .naddet = ARRAY_SIZE(multi_det), + .nkey = 0, + .wakeup = 1, /* enable ad-key wakeup from standby mode */ +}; + +static struct platform_device ak39_adkey_device = { + .name = "ad-keys", + .id = -1, + .dev = { + .platform_data = &ak39_adkey_data, + } +}; +#endif + + +#if 0 +/** + * @brif: ak39 battery mach info + + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_bat_mach_info ak39_bat_info = { + .gpio_init = ak_gpio_set, + .usb_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .ac_gpio = { + .is_detect_mode = BAT_CHARGE_ADC_DETECT, + .active = -1, + .irq = -1, + .delay = 500, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pulldown = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .full_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .bat_mach_info = { + .voltage_sample = 6, // the sample of read voltage + .power_on_voltage = 3650, // discharge power on voltage limit + .power_on_correct = 64, // mv + .charge_min_voltage = 3550, // charge minute voltage (mv) + .max_voltage = 4200, // max battery voltage + .min_voltage = 3500, // min battery voltage + .power_off = poweroff_disable, + .full_capacity = 100, // battery full + .poweroff_cap = 0, // user read value to power off + .low_cap = 5, // user read value to low power warring + .recover_cap = 30, + .cpower_on_voltage = 3700, // charge power on voltage limit + .full_delay = 30, // unit is minute + .full_voltage = 4100, + }, + + .bat_adc = { + .up_resistance = 10, + .dw_resistance = 10, + .voltage_correct = 32, // battery correct factor + .adc_avdd = 3300, // avdd voltage + }, + +}; +#endif + +#if defined(CONFIG_I2C_GPIO_SOFT) +struct i2c_gpio_platform_data ak39_i2c_data={ + .sda_pin = AK_GPIO_28, + .scl_pin = AK_GPIO_27, + .udelay = 30, + .timeout = 300, +}; + +struct platform_device ak39_i2c_device = { + .name = "i2c-gpio", + .id = -1, + .dev = { + .platform_data = &ak39_i2c_data, + }, +}; +#endif + + /* @brief: ak39 platform devices table + * + * @author: caolianming + * @date: 2014-01-09 + */ + static struct platform_device *ak3918_platform_devices[] __initdata = { + &akfha_char_device, + &ak39_uart0_device, + &ak39_uart1_device, + //&ak39_motor0_device, + //&ak39_motor1_device, + &ak39_pwm_device, + &ak39_spi1_device, + &ak39_mci1_device, + //&ak39_mci2_device, + &ak39_i2c_device, + //&ak39_custom_gpio, + &ak39_usb_udc_device, + &ak39_usb_otg_hcd_device, + &anyka_wifi_device, + &soc_camera_interface, + &ak39_camera_interface, + &ak39_ion_device, + &ak39_pcm_device, + &ak39_codec_device, + &ak39_mmx_device, +// &ak39_mac_device, + &ak39_led_pdev, + &ak39_gpio_keys_device, + //&ak39_adkey_device, + //&ak39_battery_power, + &ak39_rtc_device, + &ak39_crypto_device, + &ak39_pm_device, + &user_gpio_device, +}; + +void wdt_enable(void); +void wdt_keepalive(unsigned int heartbeat); + +/** + * @brief: restart by "reboot" cmd + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void ak39_restart(char str, const char *cmd) +{ + //ak39_reboot_sys_by_soft(); +#if defined CONFIG_AK39_WATCHDOG || defined CONFIG_AK39_WATCHDOG_TOP + wdt_enable(); + wdt_keepalive(2); +#endif +} + + +/** + * @brief: initial ak3918 machine + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void __init ak3918_init_machine(void) +{ + adc1_init(); + + spi_register_board_info(ak39_spi_board_dev, ARRAY_SIZE(ak39_spi_board_dev)); + + ak39_spi1_device.dev.platform_data = &ak39_spi1_info; + + ak39_motor0_device.dev.platform_data = &ak39_motor0_pdata; + ak39_motor1_device.dev.platform_data = &ak39_motor1_pdata; + + ak39_mci1_device.dev.platform_data = &mci1_plat_data; + //ak39_mci2_device.dev.platform_data = &mci2_plat_data; + + ak39_crypto_device.dev.platform_data = &akcrypto_pdata; + + ak39_usb_otg_hcd_device.dev.platform_data = &akotghc_plat_data; +// ak39_mac_device.dev.platform_data = &ak39_mac_pdata; + + ak39_led_pdev.dev.platform_data = &led_pdata; + ak39_gpio_keys_device.dev.platform_data = &gpio_keys_platform_data; + //ak39_battery_power.dev.platform_data = &ak39_bat_info; + + ak39_camera_interface.dev.platform_data = &ak39_camera_info; + + ak39_pm_device.dev.platform_data = &pm_pdata; + + + platform_add_devices(ak3918_platform_devices, + ARRAY_SIZE(ak3918_platform_devices)); + + l2_init(); + + return; +} + + +MACHINE_START(AK39XX, "AK3918EV300_SC10008_V100") +/* Maintainer: */ + .atag_offset = 0x100, + .fixup = NULL, + .map_io = ak39_map_io, + .reserve = NULL, + .init_irq = ak39_init_irq, + .init_machine = ak3918_init_machine, + .init_early = NULL, + .timer = &ak39_timer, + .restart = ak39_restart, + +MACHINE_END + diff --git a/arch/arm/mach-ak39/mach-cloud39ev3_ak3916ev300_mnbd.c b/arch/arm/mach-ak39/mach-cloud39ev3_ak3916ev300_mnbd.c new file mode 100644 index 00000000..4e5ab051 --- /dev/null +++ b/arch/arm/mach-ak39/mach-cloud39ev3_ak3916ev300_mnbd.c @@ -0,0 +1,915 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "cpu.h" +#include "irq.h" +#include +#include +#include +#include +#include + +/** + * @brief: spi device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +#define SPI_ONCHIP_CS (0) /*means not need gpio*/ +static unsigned long ak39_spidev_cs[AKSPI_CS_NUM] = { + [AKSPI_ONCHIP_CS] = SPI_ONCHIP_CS, /*gpio 25, spidev0: ak-spiflash*/ +}; + +struct ak_spi_info ak39_spi1_info = { + .pin_cs = ak39_spidev_cs, + .num_cs = ARRAY_SIZE(ak39_spidev_cs), + .bus_num = AKSPI_BUS_NUM1, + .clk_name = "spi1", + .mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH, + .xfer_mode = AKSPI_XFER_MODE_DMA, +}; + +/** + * @brief: spiflash device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct flash_platform_data ak39_spiflash_info= { + .bus_width = FLASH_BUS_WIDTH_4WIRE | FLASH_BUS_WIDTH_2WIRE | FLASH_BUS_WIDTH_1WIRE, + .type = NULL, +}; + +/** + * @brief: spi bus device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct spi_board_info ak39_spi_board_dev[] = { + { + .modalias = "ak-spiflash", + .bus_num = AKSPI_BUS_NUM1, + .chip_select = AKSPI_ONCHIP_CS, + .mode = SPI_MODE_0, + .max_speed_hz = 20*1000*1000, + .platform_data = &ak39_spiflash_info, + }, +}; + + +/** + * @brief: motor0 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor0_pdata = { + .gpio_phase[0] = { + .pin = AK_GPIO_37, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = AK_GPIO_38, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = AK_GPIO_39, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = AK_GPIO_40, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + .gpio_hit[0] = { + .pin = AK_GPIO_62, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = AK_GPIO_63, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + //.angular_speed = 100, /* angle/s */ + .angular_speed = 200, /* angle/s */ +}; + + +/** + * @brief: motor1 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor1_pdata = { + .gpio_phase[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + .gpio_hit[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + .angular_speed = 100, /* angle/s */ +}; + +/** + * @brief: mci2 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci2_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_PLUGIN_ALWAY, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_SDIO, + .gpio_init = ak_gpio_set, + .max_speed_hz = 25*1000*1000, + .gpio_cd = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + +/** + * @brief: mci1 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci1_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_DETECT_MODE_GPIO, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_MMC_SD, + .max_speed_hz = 25*1000*1000, + .gpio_init = ak_gpio_set, + .bus_width = MCI_BUS_WIDTH_1, + .gpio_cd = { + .pin = AK_GPIO_46, // cdh: AK_GPIO_57 + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + + +struct ak_crypto_plat_data akcrypto_pdata = { + .encrypt_mode = CRYPTO_MULTI_GROUP_MODE, +}; + + +/* akwifi platform data */ +struct akwifi_platform_data akwifi_pdata = { + .gpio_init = ak_gpio_set, + .gpio_cs = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, // cs ÓÐЧֵ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_on = { + .pin = AK_GPIO_50, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_off = { + .pin = AK_GPIO_50, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .power_on_delay = 2000, + .power_off_delay = 200, +}; + +struct platform_device anyka_wifi_device = { + .name = "anyka-wifi", + .id = -1, + .dev = { + .platform_data = &akwifi_pdata, + }, +}; + + +/** + * @brief: usb bus device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct akotghc_usb_platform_data akotghc_plat_data = { + .gpio_init = ak_gpio_set, + .gpio_pwr_on = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_pwr_off = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .switch_onboard = { + .pin = -1, + }, + .switch_extport = { + .pin = -1, + }, +}; + + +/** + * @brief: ethenet device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_mac_data ak39_mac_pdata = { + .gpio_init = ak_gpio_set, + .pwr_gpio = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .phy_rst_gpio = { + .pin = AK_GPIO_81, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, +}; + + +/** +* @brief ak pcm device struct + hp and spk can identify by GPIO or AD. + wo can initialize it in this struct. +* @author dengzhou +* @date 2012-07-19 +*/ +struct ak39_codec_platform_data ak39_codec_pdata = +{ + .hpdet_gpio = + { + .pin = -1, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = -1, + .int_pol = -1, + }, + .spk_down_gpio = + { + .pin = AK_GPIO_44, //AK_GPIO_16, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .hpmute_gpio = + { + .pin = INVALID_GPIO, //AK_GPIO_29, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .linindet_gpio = + { + .pin = -1, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, /* linein detect level */ + .int_pol = -1, + }, + + .hp_on_value = AK_GPIO_OUT_LOW, + .hpdet_irq = -1, + .linindet_irq = -1, + .bIsHPmuteUsed = 0, + .hp_mute_enable_value = AK_GPIO_OUT_HIGH, + .bIsMetalfixed = 0, + .boutput_only = 1, + .detect_flag = AKPCM_DYNAMIC_DETECT, +}; + +struct resource ak39_codec_resources[] = { + [0] = { + .start = 0x08000000, + .end = 0x0800FFFF, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_AnalogCtrlRegs", + }, + [1] = { + .start = 0x20110000, + .end = 0x2011800F, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_ADC2ModeCfgRegs", + }, +}; + + +struct platform_device ak39_codec_device = { + .name = "ak39-codec", + .id = -1, + .resource = ak39_codec_resources, + .num_resources = ARRAY_SIZE(ak39_codec_resources), + .dev = { + .platform_data = &ak39_codec_pdata, + }, +}; + + +/** + * @brief: sensor device platform data, this info is fake. + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct i2c_board_info ak_camara_devices[] = { + { + I2C_BOARD_INFO("aksensor", 0x1), + }, +}; + +static struct aksensor_camera_info ak_soc_camera_info = { + .buswidth = SOCAM_DATAWIDTH_8, + .pin_pwdn = INVALID_GPIO, //initialize GPIO for the power of camera. + .pin_reset = AK_GPIO_49, + .link = { + .bus_id = 39, + .power = NULL, + .board_info = &ak_camara_devices[0], + .i2c_adapter_id = 0, + .priv = &ak_soc_camera_info, + } +}; + +/* fake device for soc_camera subsystem */ +static struct platform_device soc_camera_interface = { + .name = "soc-camera-pdrv", + .id = -1, + .dev = { + .platform_data = &ak_soc_camera_info.link, + } +}; + + +/** + * @brief: LED platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_led_data leds[] = { + { + .name = "system_state_led", + .def_trigger = "none", + .effective_level = AK_GPIO_OUT_HIGH, //LED灯控制有效电平 + .gpio = { + .pin = AK_GPIO_80, + .pulldown = -1, + .pullup = AK_PULLUP_ENABLE, + .value = AK_GPIO_OUT_HIGH, //系统启动LED管脚默认电平 + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + } + }, + +}; + +static struct ak_led_pdata led_pdata = { + .leds = leds, + .nr_led = ARRAY_SIZE(leds), +}; + +#if 0 +/* unused GPIO number for the machine board is left*/ +static unsigned int ak39_custom_gpiopin[] = { + //AK_GPIO_5, + //AK_GPIO_61, +}; + +static struct custom_gpio_data ak39_custom_gpios= { + .gpiopin = ak39_custom_gpiopin, + .ngpiopin = ARRAY_SIZE(ak39_custom_gpiopin), +}; + +static struct platform_device ak39_custom_gpio = { + .name = "akgpio", + .id = -1, + .dev = { + .platform_data = &ak39_custom_gpios, + }, +}; +#endif + +/** + * @brief: camera platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_camera_pdata ak39_camera_info = { + .mclk = 24, + .flags = 0, + //.interface = DVP_INTERFACE, +}; + +static struct user_gpio_info user_gpios[] = { + { + .name = "gpio-ircut_a", + .info = { + .pin = AK_GPIO_30, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, /* in daytime level */ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "gpio-ircut_b", + .info = { + .pin = -1,//AK_GPIO_60, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, // in daytime level + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "ir-led", + .info = { + .pin = AK_GPIO_82, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, // in daytime level ir-led closed + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + } +}; + +static struct ak_user_gpio_pdata user_gpio_pdata = { + .user_gpios = user_gpios, + .nr_user_gpios = ARRAY_SIZE(user_gpios), +}; + +static struct platform_device user_gpio_device = { + .name = "user_gpio", + .id = -1, + .dev = { + .platform_data = &user_gpio_pdata, + }, +}; + +/** + * @brief: GPIO buttons platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct gpio_keys_button gpio_keys_button[] = { + { + .code = KEY_0, + .type = EV_KEY, + .gpio = AK_GPIO_5, + .active_low = 1, + .wakeup = 1, + .debounce_interval = 100, /* ms */ + .desc = "soft_boot", + .pullup = AK_PULLUP_ENABLE, + .pulldown = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = AK_GPIO_INT_LOWLEVEL, + }, +}; + +static struct akgpio_keys_platform_data gpio_keys_platform_data = { + .buttons = gpio_keys_button, + .nbuttons = ARRAY_SIZE(gpio_keys_button), + .rep = 0, +}; + +/** + * @brief: power manage gpios info + * + * @author: ye_guohong + * @date: 2014-12-09 + */ +static struct ak_wakeup_gpio pm_gpios[] = { + { + .pin = AK_GPIO_63, + .wakeup_pol = AK_FALLING_TRIGGERED, + }, +}; + +static struct ak_pm_pdata pm_pdata = { + .gpios = pm_gpios, + .nr_gpios = ARRAY_SIZE(pm_gpios), +}; + +#if 1 +/** +* @brief ad-key platform device struct + we should initialize the correct voltage for each key. +* @author: caolianming +* @date: 2014-01-09 +*/ +struct adgpio_key adkey[][3] = { + {{ .code = KEY_0, .min = 0, .max = 100}, //145+30/-40 + { .code = KEY_1, .min = 1000, .max = 1100}, //696+30/-40 + { .code = KEY_2, .min = 1880, .max = 2080}}, //1156+/-50 +}; + + +struct multi_addetect multi_det[] = { + {.unpress_min = 0, .unpress_max = 2100, .fixkeys = adkey[0], .plugdev = PLUGIN_DEV1}, // = null 3251+/-20 +}; + +struct analog_gpio_key ak39_adkey_data = { + .desc = "adkey", + .interval = 120, /* ms */ + .debounce_interval = 20, /* ms */ + .addet = multi_det, + .naddet = ARRAY_SIZE(multi_det), + .nkey = ARRAY_SIZE(adkey[0]), + .wakeup = 1, /* enable ad-key wakeup from standby mode */ +}; + +static struct platform_device ak39_adkey_device = { + .name = "ad-keys", + .id = -1, + .dev = { + .platform_data = &ak39_adkey_data, + } +}; +#endif + + +#if 0 +/** + * @brif: ak39 battery mach info + + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_bat_mach_info ak39_bat_info = { + .gpio_init = ak_gpio_set, + .usb_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .ac_gpio = { + .is_detect_mode = BAT_CHARGE_ADC_DETECT, + .active = -1, + .irq = -1, + .delay = 500, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pulldown = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .full_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .bat_mach_info = { + .voltage_sample = 6, // the sample of read voltage + .power_on_voltage = 3650, // discharge power on voltage limit + .power_on_correct = 64, // mv + .charge_min_voltage = 3550, // charge minute voltage (mv) + .max_voltage = 4200, // max battery voltage + .min_voltage = 3500, // min battery voltage + .power_off = poweroff_disable, + .full_capacity = 100, // battery full + .poweroff_cap = 0, // user read value to power off + .low_cap = 5, // user read value to low power warring + .recover_cap = 30, + .cpower_on_voltage = 3700, // charge power on voltage limit + .full_delay = 30, // unit is minute + .full_voltage = 4100, + }, + + .bat_adc = { + .up_resistance = 10, + .dw_resistance = 10, + .voltage_correct = 32, // battery correct factor + .adc_avdd = 3300, // avdd voltage + }, + +}; +#endif + +#if defined(CONFIG_I2C_GPIO_SOFT) +struct i2c_gpio_platform_data ak39_i2c_data={ + .sda_pin = AK_GPIO_28, + .scl_pin = AK_GPIO_27, + .udelay = 30, + .timeout = 300, +}; + +struct platform_device ak39_i2c_device = { + .name = "i2c-gpio", + .id = -1, + .dev = { + .platform_data = &ak39_i2c_data, + }, +}; +#endif + + /* @brief: ak39 platform devices table + * + * @author: caolianming + * @date: 2014-01-09 + */ + static struct platform_device *ak3918_platform_devices[] __initdata = { + &akfha_char_device, + &ak39_uart0_device, + &ak39_uart1_device, + //&ak39_motor0_device, + //&ak39_motor1_device, + &ak39_pwm_device, + &ak39_spi1_device, + &ak39_mci1_device, + //&ak39_mci2_device, + &ak39_i2c_device, + //&ak39_custom_gpio, + &ak39_usb_udc_device, + &ak39_usb_otg_hcd_device, + &anyka_wifi_device, + &soc_camera_interface, + &ak39_camera_interface, + &ak39_ion_device, + &ak39_pcm_device, + &ak39_codec_device, + &ak39_mmx_device, + &ak39_mac_device, + &ak39_led_pdev, + //&ak39_gpio_keys_device, + &ak39_adkey_device, + //&ak39_battery_power, + &ak39_rtc_device, + &ak39_crypto_device, + &ak39_pm_device, + &user_gpio_device, +}; + +void wdt_enable(void); +void wdt_keepalive(unsigned int heartbeat); + +/** + * @brief: restart by "reboot" cmd + * + * @author: caolianming + * @date: 2014-01-09 + */ +//static +void ak39_restart(char str, const char *cmd) +{ + //ak39_reboot_sys_by_soft(); +#if defined CONFIG_AK39_WATCHDOG || defined CONFIG_AK39_WATCHDOG_TOP + wdt_enable(); + wdt_keepalive(2); +#endif +} + + +/** + * @brief: initial ak3918 machine + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void __init ak3918_init_machine(void) +{ + adc1_init(); + + spi_register_board_info(ak39_spi_board_dev, ARRAY_SIZE(ak39_spi_board_dev)); + + ak39_spi1_device.dev.platform_data = &ak39_spi1_info; + + ak39_motor0_device.dev.platform_data = &ak39_motor0_pdata; + ak39_motor1_device.dev.platform_data = &ak39_motor1_pdata; + + ak39_mci1_device.dev.platform_data = &mci1_plat_data; + ak39_mci2_device.dev.platform_data = &mci2_plat_data; + + ak39_crypto_device.dev.platform_data = &akcrypto_pdata; + + ak39_usb_otg_hcd_device.dev.platform_data = &akotghc_plat_data; + ak39_mac_device.dev.platform_data = &ak39_mac_pdata; + + ak39_led_pdev.dev.platform_data = &led_pdata; + ak39_gpio_keys_device.dev.platform_data = &gpio_keys_platform_data; + //ak39_battery_power.dev.platform_data = &ak39_bat_info; + + ak39_camera_interface.dev.platform_data = &ak39_camera_info; + + ak39_pm_device.dev.platform_data = &pm_pdata; + + + platform_add_devices(ak3918_platform_devices, + ARRAY_SIZE(ak3918_platform_devices)); + + l2_init(); + + return; +} + + +MACHINE_START(AK39XX, "CLOUD39EV3_AK3916EV300_MNBD") +/* Maintainer: */ + .atag_offset = 0x100, + .fixup = NULL, + .map_io = ak39_map_io, + .reserve = NULL, + .init_irq = ak39_init_irq, + .init_machine = ak3918_init_machine, + .init_early = NULL, + .timer = &ak39_timer, + .restart = ak39_restart, + +MACHINE_END + diff --git a/arch/arm/mach-ak39/mach-cloud39ev3_ak3918ev300_YR_IPC100A_mnbd.c b/arch/arm/mach-ak39/mach-cloud39ev3_ak3918ev300_YR_IPC100A_mnbd.c new file mode 100644 index 00000000..f2ab1bfd --- /dev/null +++ b/arch/arm/mach-ak39/mach-cloud39ev3_ak3918ev300_YR_IPC100A_mnbd.c @@ -0,0 +1,941 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "cpu.h" +#include "irq.h" +#include +#include +#include +#include +#include + +/** + * @brief: spi device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +#define SPI_ONCHIP_CS (0) /*means not need gpio*/ +static unsigned long ak39_spidev_cs[AKSPI_CS_NUM] = { + [AKSPI_ONCHIP_CS] = SPI_ONCHIP_CS, /*gpio 25, spidev0: ak-spiflash*/ +}; + +struct ak_spi_info ak39_spi1_info = { + .pin_cs = ak39_spidev_cs, + .num_cs = ARRAY_SIZE(ak39_spidev_cs), + .bus_num = AKSPI_BUS_NUM1, + .clk_name = "spi1", + .mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH, + .xfer_mode = AKSPI_XFER_MODE_DMA, +}; + +/** + * @brief: spiflash device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct flash_platform_data ak39_spiflash_info= { + .bus_width = FLASH_BUS_WIDTH_4WIRE | FLASH_BUS_WIDTH_2WIRE | FLASH_BUS_WIDTH_1WIRE, + .type = NULL, +}; + +/** + * @brief: spi bus device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct spi_board_info ak39_spi_board_dev[] = { + { + .modalias = "ak-spiflash", + .bus_num = AKSPI_BUS_NUM1, + .chip_select = AKSPI_ONCHIP_CS, + .mode = SPI_MODE_0, + .max_speed_hz = 20*1000*1000, + .platform_data = &ak39_spiflash_info, + }, +}; + + +/** + * @brief: motor0 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor0_pdata = { + .gpio_phase[0] = { + .pin = AK_GPIO_7, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = AK_GPIO_8, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = AK_GPIO_9, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = AK_GPIO_68, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + .gpio_hit[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + //.angular_speed = 100, /* angle/s */ + .angular_speed = 200, /* angle/s */ +}; + + +/** + * @brief: motor1 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor1_pdata = { + .gpio_phase[0] = { + .pin = AK_GPIO_65, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = AK_GPIO_66, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = AK_GPIO_67, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = AK_GPIO_6, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + + .gpio_hit[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + .angular_speed = 100, /* angle/s */ +}; + +/** + * @brief: mci2 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci2_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_PLUGIN_ALWAY, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_SDIO, + .gpio_init = ak_gpio_set, + .max_speed_hz = 25*1000*1000, + .gpio_cd = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + //.pin = -1, + .pin = AK_GPIO_11, + .pulldown = -1, +// .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + +/** + * @brief: mci1 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci1_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_DETECT_MODE_GPIO, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_MMC_SD, + .max_speed_hz = 25*1000*1000, + .gpio_init = ak_gpio_set, + .bus_width = MCI_BUS_WIDTH_1, + .gpio_cd = { + .pin = AK_GPIO_82, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + + +struct ak_crypto_plat_data akcrypto_pdata = { + .encrypt_mode = CRYPTO_MULTI_GROUP_MODE, +}; + + +/* akwifi platform data */ +struct akwifi_platform_data akwifi_pdata = { + .gpio_init = ak_gpio_set, + .gpio_cs = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, // cs 脫脨脨搂脰碌 + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_on = { + .pin = -1, + .pulldown = AK_PULLUP_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_off = { + .pin = -1, + .pulldown = AK_PULLUP_ENABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .power_on_delay = 2000, + .power_off_delay = 200, +}; + +struct platform_device anyka_wifi_device = { + .name = "anyka-wifi", + .id = -1, + .dev = { + .platform_data = &akwifi_pdata, + }, +}; + + +/** + * @brief: usb bus device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct akotghc_usb_platform_data akotghc_plat_data = { + .gpio_init = ak_gpio_set, + .gpio_pwr_on = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_pwr_off = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .switch_onboard = { + .pin = -1, + }, + .switch_extport = { + .pin = -1, + }, +}; + + +/** + * @brief: ethenet device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_mac_data ak39_mac_pdata = { + .gpio_init = ak_gpio_set, + .pwr_gpio = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .phy_rst_gpio = { + .pin = -1, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, +}; + + +/** +* @brief ak pcm device struct + hp and spk can identify by GPIO or AD. + wo can initialize it in this struct. +* @author dengzhou +* @date 2012-07-19 +*/ +struct ak39_codec_platform_data ak39_codec_pdata = +{ + .hpdet_gpio = + { + .pin = -1, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = -1, + .int_pol = -1, + }, + .spk_down_gpio = + { + .pin = AK_GPIO_14, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .hpmute_gpio = + { + .pin = INVALID_GPIO, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .linindet_gpio = + { + .pin = -1, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, /* linein detect level */ + .int_pol = -1, + }, + + .hp_on_value = AK_GPIO_OUT_LOW, + .hpdet_irq = -1, + .linindet_irq = -1, + .bIsHPmuteUsed = 0, + .hp_mute_enable_value = AK_GPIO_OUT_HIGH, + .bIsMetalfixed = 0, + .boutput_only = 1, + .detect_flag = AKPCM_DYNAMIC_DETECT, +}; + +struct resource ak39_codec_resources[] = { + [0] = { + .start = 0x08000000, + .end = 0x0800FFFF, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_AnalogCtrlRegs", + }, + [1] = { + .start = 0x20110000, + .end = 0x2011800F, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_ADC2ModeCfgRegs", + }, +}; + + +struct platform_device ak39_codec_device = { + .name = "ak39-codec", + .id = -1, + .resource = ak39_codec_resources, + .num_resources = ARRAY_SIZE(ak39_codec_resources), + .dev = { + .platform_data = &ak39_codec_pdata, + }, +}; + + +/** + * @brief: sensor device platform data, this info is fake. + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct i2c_board_info ak_camara_devices[] = { + { + I2C_BOARD_INFO("aksensor", 0x1), + }, +}; + +static struct aksensor_camera_info ak_soc_camera_info = { + .buswidth = SOCAM_DATAWIDTH_8, + .pin_pwdn = INVALID_GPIO, //initialize GPIO for the power of camera. + .pin_reset = AK_GPIO_49, + .link = { + .bus_id = 39, + .power = NULL, + .board_info = &ak_camara_devices[0], + .i2c_adapter_id = 0, + .priv = &ak_soc_camera_info, + } +}; + +/* fake device for soc_camera subsystem */ +static struct platform_device soc_camera_interface = { + .name = "soc-camera-pdrv", + .id = -1, + .dev = { + .platform_data = &ak_soc_camera_info.link, + } +}; + + +/** + * @brief: LED platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_led_data leds[] = { + { + .name = "red_led", + .def_trigger = "none", + .gpio = { + .pin = AK_GPIO_47, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + } + }, + { + .name = "blue_led", + .def_trigger = "none", + .gpio = { + .pin = AK_GPIO_15, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + } + }, + { + .name = "ir_led", + .def_trigger = "none", + .gpio = { + .pin = AK_GPIO_5, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + } + }, + +}; + +static struct ak_led_pdata led_pdata = { + .leds = leds, + .nr_led = ARRAY_SIZE(leds), +}; + +#if 0 +/* unused GPIO number for the machine board is left*/ +static unsigned int ak39_custom_gpiopin[] = { + //AK_GPIO_5, + //AK_GPIO_61, +}; + +static struct custom_gpio_data ak39_custom_gpios= { + .gpiopin = ak39_custom_gpiopin, + .ngpiopin = ARRAY_SIZE(ak39_custom_gpiopin), +}; + +static struct platform_device ak39_custom_gpio = { + .name = "akgpio", + .id = -1, + .dev = { + .platform_data = &ak39_custom_gpios, + }, +}; +#endif + +/** + * @brief: camera platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_camera_pdata ak39_camera_info = { + .mclk = 24, + .flags = 0, + //.interface = DVP_INTERFACE, +}; + +static struct user_gpio_info user_gpios[] = { + { + .name = "gpio-ircut_a", + .info = { + .pin = AK_GPIO_81, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, /* in daytime level */ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "gpio-ircut_b", + .info = { + .pin = AK_GPIO_80, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, // in daytime level + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "ir-led", + .info = { + .pin = AK_GPIO_5, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, // in daytime level ir-led closed + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, +}; + +static struct ak_user_gpio_pdata user_gpio_pdata = { + .user_gpios = user_gpios, + .nr_user_gpios = ARRAY_SIZE(user_gpios), +}; + +static struct platform_device user_gpio_device = { + .name = "user_gpio", + .id = -1, + .dev = { + .platform_data = &user_gpio_pdata, + }, +}; + +/** + * @brief: GPIO buttons platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct gpio_keys_button gpio_keys_button[] = { + { + .code = KEY_0, + .type = EV_KEY, + .gpio = AK_GPIO_10, + .active_low = 1, + .wakeup = 1, + .debounce_interval = 100, /* ms */ + .desc = "soft_boot", + .pullup = -1, + .pulldown = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = AK_GPIO_INT_LOWLEVEL, + }, +}; + +static struct akgpio_keys_platform_data gpio_keys_platform_data = { + .buttons = gpio_keys_button, + .nbuttons = ARRAY_SIZE(gpio_keys_button), + .rep = 0, +}; + +/** + * @brief: power manage gpios info + * + * @author: ye_guohong + * @date: 2014-12-09 + */ +static struct ak_wakeup_gpio pm_gpios[] = { + { + .pin = AK_GPIO_63, + .wakeup_pol = AK_FALLING_TRIGGERED, + }, +}; + +static struct ak_pm_pdata pm_pdata = { + .gpios = pm_gpios, + .nr_gpios = ARRAY_SIZE(pm_gpios), +}; + +#if 1 +/** +* @brief ad-key platform device struct + we should initialize the correct voltage for each key. +* @author: caolianming +* @date: 2014-01-09 +*/ +struct adgpio_key adkey[][3] = { + { + { .code = KEY_1, .min = 0, .max = 100}, //145+30/-40 + { .code = KEY_0, .min = 1600, .max = 1720}, //696+30/-40 + }, +}; + + +struct multi_addetect multi_det[] = { + {.unpress_min = 0, .unpress_max = 2100, .fixkeys = adkey[0], .plugdev = PLUGIN_DEV1}, // = null 3251+/-20 +}; + +struct analog_gpio_key ak39_adkey_data = { + .desc = "adkey", + .interval = 120, /* ms */ + .debounce_interval = 20, /* ms */ + .addet = multi_det, + .naddet = ARRAY_SIZE(multi_det), + .nkey = ARRAY_SIZE(adkey[0]), + .wakeup = 1, /* enable ad-key wakeup from standby mode */ +}; + +static struct platform_device ak39_adkey_device = { + .name = "ad-keys", + .id = -1, + .dev = { + .platform_data = &ak39_adkey_data, + } +}; +#endif + + +#if 0 +/** + * @brif: ak39 battery mach info + + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_bat_mach_info ak39_bat_info = { + .gpio_init = ak_gpio_set, + .usb_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .ac_gpio = { + .is_detect_mode = BAT_CHARGE_ADC_DETECT, + .active = -1, + .irq = -1, + .delay = 500, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pulldown = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .full_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .bat_mach_info = { + .voltage_sample = 6, // the sample of read voltage + .power_on_voltage = 3650, // discharge power on voltage limit + .power_on_correct = 64, // mv + .charge_min_voltage = 3550, // charge minute voltage (mv) + .max_voltage = 4200, // max battery voltage + .min_voltage = 3500, // min battery voltage + .power_off = poweroff_disable, + .full_capacity = 100, // battery full + .poweroff_cap = 0, // user read value to power off + .low_cap = 5, // user read value to low power warring + .recover_cap = 30, + .cpower_on_voltage = 3700, // charge power on voltage limit + .full_delay = 30, // unit is minute + .full_voltage = 4100, + }, + + .bat_adc = { + .up_resistance = 10, + .dw_resistance = 10, + .voltage_correct = 32, // battery correct factor + .adc_avdd = 3300, // avdd voltage + }, + +}; +#endif + +#if defined(CONFIG_I2C_GPIO_SOFT) +struct i2c_gpio_platform_data ak39_i2c_data={ + .sda_pin = AK_GPIO_28, + .scl_pin = AK_GPIO_27, + .udelay = 30, + .timeout = 300, +}; + +struct platform_device ak39_i2c_device = { + .name = "i2c-gpio", + .id = -1, + .dev = { + .platform_data = &ak39_i2c_data, + }, +}; +#endif + + /* @brief: ak39 platform devices table + * + * @author: caolianming + * @date: 2014-01-09 + */ + static struct platform_device *ak3918_platform_devices[] __initdata = { + &akfha_char_device, + &ak39_uart0_device, + //&ak39_uart1_device, + //&ak39_pwm_device, + &ak39_spi1_device, + &ak39_mci1_device, + &ak39_mci2_device, + &ak39_i2c_device, + //&ak39_custom_gpio, + &ak39_usb_udc_device, + &ak39_usb_otg_hcd_device, + //&anyka_wifi_device, + &soc_camera_interface, + &ak39_camera_interface, + &ak39_ion_device, + &ak39_pcm_device, + &ak39_codec_device, + &ak39_mmx_device, + //&ak39_mac_device, + &ak39_led_pdev, + &ak39_gpio_keys_device, + //&ak39_adkey_device, + //&ak39_battery_power, + &ak39_rtc_device, + &ak39_crypto_device, + //&ak39_pm_device, + &user_gpio_device, + &ak39_motor0_device, + &ak39_motor1_device, +}; + +void wdt_enable(void); +void wdt_keepalive(unsigned int heartbeat); + +/** + * @brief: restart by "reboot" cmd + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void ak39_restart(char str, const char *cmd) +{ + //ak39_reboot_sys_by_soft(); +#if defined CONFIG_AK39_WATCHDOG || defined CONFIG_AK39_WATCHDOG_TOP + wdt_enable(); + wdt_keepalive(2); +#endif +} + + +/** + * @brief: initial ak3918 machine + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void __init ak3918_init_machine(void) +{ + adc1_init(); + + spi_register_board_info(ak39_spi_board_dev, ARRAY_SIZE(ak39_spi_board_dev)); + + ak39_spi1_device.dev.platform_data = &ak39_spi1_info; + + ak39_motor0_device.dev.platform_data = &ak39_motor0_pdata; + ak39_motor1_device.dev.platform_data = &ak39_motor1_pdata; + + ak39_mci1_device.dev.platform_data = &mci1_plat_data; + ak39_mci2_device.dev.platform_data = &mci2_plat_data; + + ak39_crypto_device.dev.platform_data = &akcrypto_pdata; + + ak39_usb_otg_hcd_device.dev.platform_data = &akotghc_plat_data; + //ak39_mac_device.dev.platform_data = &ak39_mac_pdata; + + ak39_led_pdev.dev.platform_data = &led_pdata; + ak39_gpio_keys_device.dev.platform_data = &gpio_keys_platform_data; + //ak39_battery_power.dev.platform_data = &ak39_bat_info; + + ak39_camera_interface.dev.platform_data = &ak39_camera_info; + + ak39_pm_device.dev.platform_data = &pm_pdata; + + + platform_add_devices(ak3918_platform_devices, + ARRAY_SIZE(ak3918_platform_devices)); + + l2_init(); + + return; +} + + +MACHINE_START(AK39XX, "CLOUD39EV300_AK3918E_YR_IPC100A_MAINBD_V1.0.0") +/* Maintainer: */ + .atag_offset = 0x100, + .fixup = NULL, + .map_io = ak39_map_io, + .reserve = NULL, + .init_irq = ak39_init_irq, + .init_machine = ak3918_init_machine, + .init_early = NULL, + .timer = &ak39_timer, + .restart = ak39_restart, + +MACHINE_END + diff --git a/arch/arm/mach-ak39/mach-cloud39ev3_ak3918ev300_YTJ_mnbd.c b/arch/arm/mach-ak39/mach-cloud39ev3_ak3918ev300_YTJ_mnbd.c new file mode 100644 index 00000000..a1915d9b --- /dev/null +++ b/arch/arm/mach-ak39/mach-cloud39ev3_ak3918ev300_YTJ_mnbd.c @@ -0,0 +1,940 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "cpu.h" +#include "irq.h" +#include +#include +#include +#include +#include + +/** + * @brief: spi device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +#define SPI_ONCHIP_CS (0) /*means not need gpio*/ +static unsigned long ak39_spidev_cs[AKSPI_CS_NUM] = { + [AKSPI_ONCHIP_CS] = SPI_ONCHIP_CS, /*gpio 25, spidev0: ak-spiflash*/ +}; + +struct ak_spi_info ak39_spi1_info = { + .pin_cs = ak39_spidev_cs, + .num_cs = ARRAY_SIZE(ak39_spidev_cs), + .bus_num = AKSPI_BUS_NUM1, + .clk_name = "spi1", + .mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH, + .xfer_mode = AKSPI_XFER_MODE_DMA, +}; + +/** + * @brief: spiflash device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct flash_platform_data ak39_spiflash_info= { + .bus_width = FLASH_BUS_WIDTH_4WIRE | FLASH_BUS_WIDTH_2WIRE | FLASH_BUS_WIDTH_1WIRE, + .type = NULL, +}; + +/** + * @brief: spi bus device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct spi_board_info ak39_spi_board_dev[] = { + { + .modalias = "ak-spiflash", + .bus_num = AKSPI_BUS_NUM1, + .chip_select = AKSPI_ONCHIP_CS, + .mode = SPI_MODE_0, + .max_speed_hz = 20*1000*1000, + .platform_data = &ak39_spiflash_info, + }, +}; + + +/** + * @brief: motor0 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor0_pdata = { + .gpio_phase[0] = { + .pin = AK_GPIO_8, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = AK_GPIO_9, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = AK_GPIO_68, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = AK_GPIO_69, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + .gpio_hit[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + //.angular_speed = 100, /* angle/s */ + .angular_speed = 200, /* angle/s */ +}; + + +/** + * @brief: motor1 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor1_pdata = { + .gpio_phase[0] = { + .pin = AK_GPIO_66, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = AK_GPIO_67, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = AK_GPIO_6, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = AK_GPIO_7, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + + .gpio_hit[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + .angular_speed = 100, /* angle/s */ +}; + +/** + * @brief: mci2 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci2_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_PLUGIN_ALWAY, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_SDIO, + .gpio_init = ak_gpio_set, + .max_speed_hz = 25*1000*1000, + .gpio_cd = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + +/** + * @brief: mci1 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci1_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_DETECT_MODE_GPIO, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_MMC_SD, + .max_speed_hz = 25*1000*1000, + .gpio_init = ak_gpio_set, + .bus_width = MCI_BUS_WIDTH_1, + .gpio_cd = { + .pin = AK_GPIO_84, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + + +struct ak_crypto_plat_data akcrypto_pdata = { + .encrypt_mode = CRYPTO_MULTI_GROUP_MODE, +}; + + +/* akwifi platform data */ +struct akwifi_platform_data akwifi_pdata = { + .gpio_init = ak_gpio_set, + .gpio_cs = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, // cs 脫脨脨搂脰碌 + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_on = { + .pin = AK_GPIO_82, + .pulldown = AK_PULLUP_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_off = { + .pin = AK_GPIO_82, + .pulldown = AK_PULLUP_ENABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .power_on_delay = 2000, + .power_off_delay = 200, +}; + +struct platform_device anyka_wifi_device = { + .name = "anyka-wifi", + .id = -1, + .dev = { + .platform_data = &akwifi_pdata, + }, +}; + + +/** + * @brief: usb bus device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct akotghc_usb_platform_data akotghc_plat_data = { + .gpio_init = ak_gpio_set, + .gpio_pwr_on = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_pwr_off = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .switch_onboard = { + .pin = -1, + }, + .switch_extport = { + .pin = -1, + }, +}; + + +/** + * @brief: ethenet device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_mac_data ak39_mac_pdata = { + .gpio_init = ak_gpio_set, + .pwr_gpio = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .phy_rst_gpio = { + .pin = AK_GPIO_81, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, +}; + + +/** +* @brief ak pcm device struct + hp and spk can identify by GPIO or AD. + wo can initialize it in this struct. +* @author dengzhou +* @date 2012-07-19 +*/ +struct ak39_codec_platform_data ak39_codec_pdata = +{ + .hpdet_gpio = + { + .pin = -1, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = -1, + .int_pol = -1, + }, + .spk_down_gpio = + { + .pin = AK_GPIO_65, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + .value = AK_GPIO_OUT_HIGH, + .int_pol = -1, + }, + .hpmute_gpio = + { + .pin = INVALID_GPIO, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .linindet_gpio = + { + .pin = -1, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, /* linein detect level */ + .int_pol = -1, + }, + + .hp_on_value = AK_GPIO_OUT_LOW, + .hpdet_irq = -1, + .linindet_irq = -1, + .bIsHPmuteUsed = 0, + .hp_mute_enable_value = AK_GPIO_OUT_HIGH, + .bIsMetalfixed = 0, + .boutput_only = 1, + .detect_flag = AKPCM_DYNAMIC_DETECT, +}; + +struct resource ak39_codec_resources[] = { + [0] = { + .start = 0x08000000, + .end = 0x0800FFFF, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_AnalogCtrlRegs", + }, + [1] = { + .start = 0x20110000, + .end = 0x2011800F, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_ADC2ModeCfgRegs", + }, +}; + + +struct platform_device ak39_codec_device = { + .name = "ak39-codec", + .id = -1, + .resource = ak39_codec_resources, + .num_resources = ARRAY_SIZE(ak39_codec_resources), + .dev = { + .platform_data = &ak39_codec_pdata, + }, +}; + + +/** + * @brief: sensor device platform data, this info is fake. + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct i2c_board_info ak_camara_devices[] = { + { + I2C_BOARD_INFO("aksensor", 0x1), + }, +}; + +static struct aksensor_camera_info ak_soc_camera_info = { + .buswidth = SOCAM_DATAWIDTH_8, + .pin_pwdn = INVALID_GPIO, //initialize GPIO for the power of camera. + .pin_reset = AK_GPIO_49, + .link = { + .bus_id = 39, + .power = NULL, + .board_info = &ak_camara_devices[0], + .i2c_adapter_id = 0, + .priv = &ak_soc_camera_info, + } +}; + +/* fake device for soc_camera subsystem */ +static struct platform_device soc_camera_interface = { + .name = "soc-camera-pdrv", + .id = -1, + .dev = { + .platform_data = &ak_soc_camera_info.link, + } +}; + + +/** + * @brief: LED platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_led_data leds[] = { + + { + .name = "red_led", + .def_trigger = "none", + .gpio = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + } + }, + { + .name = "blue_led", + .def_trigger = "none", + .gpio = { + .pin = AK_GPIO_80, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + } + }, + { + .name = "ir_led", + .def_trigger = "none", + .gpio = { + .pin = AK_GPIO_81, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + } + }, + +}; + +static struct ak_led_pdata led_pdata = { + .leds = leds, + .nr_led = ARRAY_SIZE(leds), +}; + +#if 0 +/* unused GPIO number for the machine board is left*/ +static unsigned int ak39_custom_gpiopin[] = { + //AK_GPIO_5, + //AK_GPIO_61, +}; + +static struct custom_gpio_data ak39_custom_gpios= { + .gpiopin = ak39_custom_gpiopin, + .ngpiopin = ARRAY_SIZE(ak39_custom_gpiopin), +}; + +static struct platform_device ak39_custom_gpio = { + .name = "akgpio", + .id = -1, + .dev = { + .platform_data = &ak39_custom_gpios, + }, +}; +#endif + +/** + * @brief: camera platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_camera_pdata ak39_camera_info = { + .mclk = 24, + .flags = 0, + //.interface = DVP_INTERFACE, +}; + +static struct user_gpio_info user_gpios[] = { + { + .name = "gpio-ircut_a", + .info = { + .pin = AK_GPIO_42, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, /* in daytime level */ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "gpio-ircut_b", + .info = { + .pin = AK_GPIO_41, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, // in daytime level + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "ir-led", + .info = { + .pin = AK_GPIO_81, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, // in daytime level ir-led closed + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, +}; + +static struct ak_user_gpio_pdata user_gpio_pdata = { + .user_gpios = user_gpios, + .nr_user_gpios = ARRAY_SIZE(user_gpios), +}; + +static struct platform_device user_gpio_device = { + .name = "user_gpio", + .id = -1, + .dev = { + .platform_data = &user_gpio_pdata, + }, +}; + +/** + * @brief: GPIO buttons platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct gpio_keys_button gpio_keys_button[] = { + { + .code = KEY_0, + .type = EV_KEY, + .gpio = AK_GPIO_5, + .active_low = 1, + .wakeup = 1, + .debounce_interval = 100, /* ms */ + .desc = "soft_boot", + .pullup = AK_PULLUP_ENABLE, + .pulldown = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = AK_GPIO_INT_LOWLEVEL, + }, +}; + +static struct akgpio_keys_platform_data gpio_keys_platform_data = { + .buttons = gpio_keys_button, + .nbuttons = ARRAY_SIZE(gpio_keys_button), + .rep = 0, +}; + +/** + * @brief: power manage gpios info + * + * @author: ye_guohong + * @date: 2014-12-09 + */ +static struct ak_wakeup_gpio pm_gpios[] = { + { + .pin = AK_GPIO_63, + .wakeup_pol = AK_FALLING_TRIGGERED, + }, +}; + +static struct ak_pm_pdata pm_pdata = { + .gpios = pm_gpios, + .nr_gpios = ARRAY_SIZE(pm_gpios), +}; + +#if 1 +/** +* @brief ad-key platform device struct + we should initialize the correct voltage for each key. +* @author: caolianming +* @date: 2014-01-09 +*/ +struct adgpio_key adkey[][3] = { + { + { .code = KEY_1, .min = 0, .max = 100}, //145+30/-40 + { .code = KEY_0, .min = 1600, .max = 1720}, //696+30/-40 + }, +}; + + +struct multi_addetect multi_det[] = { + {.unpress_min = 0, .unpress_max = 2100, .fixkeys = adkey[0], .plugdev = PLUGIN_DEV1}, // = null 3251+/-20 +}; + +struct analog_gpio_key ak39_adkey_data = { + .desc = "adkey", + .interval = 120, /* ms */ + .debounce_interval = 20, /* ms */ + .addet = multi_det, + .naddet = ARRAY_SIZE(multi_det), + .nkey = ARRAY_SIZE(adkey[0]), + .wakeup = 1, /* enable ad-key wakeup from standby mode */ +}; + +static struct platform_device ak39_adkey_device = { + .name = "ad-keys", + .id = -1, + .dev = { + .platform_data = &ak39_adkey_data, + } +}; +#endif + + +#if 0 +/** + * @brif: ak39 battery mach info + + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_bat_mach_info ak39_bat_info = { + .gpio_init = ak_gpio_set, + .usb_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .ac_gpio = { + .is_detect_mode = BAT_CHARGE_ADC_DETECT, + .active = -1, + .irq = -1, + .delay = 500, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pulldown = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .full_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .bat_mach_info = { + .voltage_sample = 6, // the sample of read voltage + .power_on_voltage = 3650, // discharge power on voltage limit + .power_on_correct = 64, // mv + .charge_min_voltage = 3550, // charge minute voltage (mv) + .max_voltage = 4200, // max battery voltage + .min_voltage = 3500, // min battery voltage + .power_off = poweroff_disable, + .full_capacity = 100, // battery full + .poweroff_cap = 0, // user read value to power off + .low_cap = 5, // user read value to low power warring + .recover_cap = 30, + .cpower_on_voltage = 3700, // charge power on voltage limit + .full_delay = 30, // unit is minute + .full_voltage = 4100, + }, + + .bat_adc = { + .up_resistance = 10, + .dw_resistance = 10, + .voltage_correct = 32, // battery correct factor + .adc_avdd = 3300, // avdd voltage + }, + +}; +#endif + +#if defined(CONFIG_I2C_GPIO_SOFT) +struct i2c_gpio_platform_data ak39_i2c_data={ + .sda_pin = AK_GPIO_28, + .scl_pin = AK_GPIO_27, + .udelay = 30, + .timeout = 300, +}; + +struct platform_device ak39_i2c_device = { + .name = "i2c-gpio", + .id = -1, + .dev = { + .platform_data = &ak39_i2c_data, + }, +}; +#endif + + /* @brief: ak39 platform devices table + * + * @author: caolianming + * @date: 2014-01-09 + */ + static struct platform_device *ak3918_platform_devices[] __initdata = { + &akfha_char_device, + &ak39_uart0_device, + //&ak39_uart1_device, + //&ak39_pwm_device, + &ak39_spi1_device, + &ak39_mci1_device, + //&ak39_mci2_device, + &ak39_i2c_device, + //&ak39_custom_gpio, + &ak39_usb_udc_device, + &ak39_usb_otg_hcd_device, + &anyka_wifi_device, + &soc_camera_interface, + &ak39_camera_interface, + &ak39_ion_device, + &ak39_pcm_device, + &ak39_codec_device, + &ak39_mmx_device, + &ak39_mac_device, + &ak39_led_pdev, + &ak39_gpio_keys_device, + //&ak39_adkey_device, + //&ak39_battery_power, + &ak39_rtc_device, + &ak39_crypto_device, + //&ak39_pm_device, + &user_gpio_device, + &ak39_motor0_device, + &ak39_motor1_device, +}; + +void wdt_enable(void); +void wdt_keepalive(unsigned int heartbeat); + +/** + * @brief: restart by "reboot" cmd + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void ak39_restart(char str, const char *cmd) +{ + //ak39_reboot_sys_by_soft(); +#if defined CONFIG_AK39_WATCHDOG || defined CONFIG_AK39_WATCHDOG_TOP + wdt_enable(); + wdt_keepalive(2); +#endif +} + + +/** + * @brief: initial ak3918 machine + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void __init ak3918_init_machine(void) +{ + adc1_init(); + + spi_register_board_info(ak39_spi_board_dev, ARRAY_SIZE(ak39_spi_board_dev)); + + ak39_spi1_device.dev.platform_data = &ak39_spi1_info; + + ak39_motor0_device.dev.platform_data = &ak39_motor0_pdata; + ak39_motor1_device.dev.platform_data = &ak39_motor1_pdata; + + ak39_mci1_device.dev.platform_data = &mci1_plat_data; + //ak39_mci2_device.dev.platform_data = &mci2_plat_data; + + ak39_crypto_device.dev.platform_data = &akcrypto_pdata; + + ak39_usb_otg_hcd_device.dev.platform_data = &akotghc_plat_data; + ak39_mac_device.dev.platform_data = &ak39_mac_pdata; + + ak39_led_pdev.dev.platform_data = &led_pdata; + ak39_gpio_keys_device.dev.platform_data = &gpio_keys_platform_data; + //ak39_battery_power.dev.platform_data = &ak39_bat_info; + + ak39_camera_interface.dev.platform_data = &ak39_camera_info; + + ak39_pm_device.dev.platform_data = &pm_pdata; + + + platform_add_devices(ak3918_platform_devices, + ARRAY_SIZE(ak3918_platform_devices)); + + l2_init(); + + return; +} + + +MACHINE_START(AK39XX, "CLOUD39EV300_AK3918E_YTJ_MAINBD(SC2232)_V1.0.0") +/* Maintainer: */ + .atag_offset = 0x100, + .fixup = NULL, + .map_io = ak39_map_io, + .reserve = NULL, + .init_irq = ak39_init_irq, + .init_machine = ak3918_init_machine, + .init_early = NULL, + .timer = &ak39_timer, + .restart = ak39_restart, + +MACHINE_END + diff --git a/arch/arm/mach-ak39/mach-cloud39ev3_ak3918ev300_mnbd.c b/arch/arm/mach-ak39/mach-cloud39ev3_ak3918ev300_mnbd.c new file mode 100644 index 00000000..f0a5c09f --- /dev/null +++ b/arch/arm/mach-ak39/mach-cloud39ev3_ak3918ev300_mnbd.c @@ -0,0 +1,911 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "cpu.h" +#include "irq.h" +#include +#include +#include +#include +#include + +/** + * @brief: spi device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +#define SPI_ONCHIP_CS (0) /*means not need gpio*/ +static unsigned long ak39_spidev_cs[AKSPI_CS_NUM] = { + [AKSPI_ONCHIP_CS] = SPI_ONCHIP_CS, /*gpio 25, spidev0: ak-spiflash*/ +}; + +struct ak_spi_info ak39_spi1_info = { + .pin_cs = ak39_spidev_cs, + .num_cs = ARRAY_SIZE(ak39_spidev_cs), + .bus_num = AKSPI_BUS_NUM1, + .clk_name = "spi1", + .mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH, + .xfer_mode = AKSPI_XFER_MODE_DMA, +}; + +/** + * @brief: spiflash device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct flash_platform_data ak39_spiflash_info= { + .bus_width = FLASH_BUS_WIDTH_4WIRE | FLASH_BUS_WIDTH_2WIRE | FLASH_BUS_WIDTH_1WIRE, + .type = NULL, +}; + +/** + * @brief: spi bus device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct spi_board_info ak39_spi_board_dev[] = { + { + .modalias = "ak-spiflash", + .bus_num = AKSPI_BUS_NUM1, + .chip_select = AKSPI_ONCHIP_CS, + .mode = SPI_MODE_0, + .max_speed_hz = 20*1000*1000, + .platform_data = &ak39_spiflash_info, + }, +}; + + +/** + * @brief: motor0 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor0_pdata = { + .gpio_phase[0] = { + .pin = AK_GPIO_37, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = AK_GPIO_38, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = AK_GPIO_39, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = AK_GPIO_40, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + .gpio_hit[0] = { + .pin = AK_GPIO_62, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = AK_GPIO_63, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + //.angular_speed = 100, /* angle/s */ + .angular_speed = 200, /* angle/s */ +}; + + +/** + * @brief: motor1 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor1_pdata = { + .gpio_phase[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + .gpio_hit[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + .angular_speed = 100, /* angle/s */ +}; + +/** + * @brief: mci2 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci2_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_PLUGIN_ALWAY, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_SDIO, + .gpio_init = ak_gpio_set, + .max_speed_hz = 25*1000*1000, + .gpio_cd = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + +/** + * @brief: mci1 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci1_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_DETECT_MODE_GPIO, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_MMC_SD, + .max_speed_hz = 25*1000*1000, + .gpio_init = ak_gpio_set, + .bus_width = MCI_BUS_WIDTH_1, + .gpio_cd = { + .pin = AK_GPIO_41, // cdh: AK_GPIO_57 + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + + +struct ak_crypto_plat_data akcrypto_pdata = { + .encrypt_mode = CRYPTO_MULTI_GROUP_MODE, +}; + + +/* akwifi platform data */ +struct akwifi_platform_data akwifi_pdata = { + .gpio_init = ak_gpio_set, + .gpio_cs = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, // cs ÓÐЧֵ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_on = { + .pin = AK_GPIO_50, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_off = { + .pin = AK_GPIO_50, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .power_on_delay = 2000, + .power_off_delay = 200, +}; + +struct platform_device anyka_wifi_device = { + .name = "anyka-wifi", + .id = -1, + .dev = { + .platform_data = &akwifi_pdata, + }, +}; + + +/** + * @brief: usb bus device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct akotghc_usb_platform_data akotghc_plat_data = { + .gpio_init = ak_gpio_set, + .gpio_pwr_on = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_pwr_off = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .switch_onboard = { + .pin = -1, + }, + .switch_extport = { + .pin = -1, + }, +}; + + +/** + * @brief: ethenet device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_mac_data ak39_mac_pdata = { + .gpio_init = ak_gpio_set, + .pwr_gpio = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .phy_rst_gpio = { + .pin = AK_GPIO_81, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, +}; + + +/** +* @brief ak pcm device struct + hp and spk can identify by GPIO or AD. + wo can initialize it in this struct. +* @author dengzhou +* @date 2012-07-19 +*/ +struct ak39_codec_platform_data ak39_codec_pdata = +{ + .hpdet_gpio = + { + .pin = -1, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = -1, + .int_pol = -1, + }, + .spk_down_gpio = + { + .pin = AK_GPIO_42, //AK_GPIO_16, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .hpmute_gpio = + { + .pin = INVALID_GPIO, //AK_GPIO_29, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .linindet_gpio = + { + .pin = AK_GPIO_82, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, /* linein detect level */ + .int_pol = -1, + }, + + .hp_on_value = AK_GPIO_OUT_LOW, + .hpdet_irq = -1, + .linindet_irq = IRQ_GPIO_82, + .bIsHPmuteUsed = 0, + .hp_mute_enable_value = AK_GPIO_OUT_HIGH, + .bIsMetalfixed = 0, + .boutput_only = 1, + .detect_flag = AKPCM_DYNAMIC_DETECT, +}; + +struct resource ak39_codec_resources[] = { + [0] = { + .start = 0x08000000, + .end = 0x0800FFFF, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_AnalogCtrlRegs", + }, + [1] = { + .start = 0x20110000, + .end = 0x2011800F, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_ADC2ModeCfgRegs", + }, +}; + + +struct platform_device ak39_codec_device = { + .name = "ak39-codec", + .id = -1, + .resource = ak39_codec_resources, + .num_resources = ARRAY_SIZE(ak39_codec_resources), + .dev = { + .platform_data = &ak39_codec_pdata, + }, +}; + + +/** + * @brief: sensor device platform data, this info is fake. + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct i2c_board_info ak_camara_devices[] = { + { + I2C_BOARD_INFO("aksensor", 0x1), + }, +}; + +static struct aksensor_camera_info ak_soc_camera_info = { + .buswidth = SOCAM_DATAWIDTH_8, + .pin_pwdn = INVALID_GPIO, //initialize GPIO for the power of camera. + .pin_reset = AK_GPIO_49, + .link = { + .bus_id = 39, + .power = NULL, + .board_info = &ak_camara_devices[0], + .i2c_adapter_id = 0, + .priv = &ak_soc_camera_info, + } +}; + +/* fake device for soc_camera subsystem */ +static struct platform_device soc_camera_interface = { + .name = "soc-camera-pdrv", + .id = -1, + .dev = { + .platform_data = &ak_soc_camera_info.link, + } +}; + + +/** + * @brief: LED platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_led_data leds[] = { + { + .name = "system_state_led", + .def_trigger = "none", + .effective_level = AK_GPIO_OUT_HIGH, //LEDµƿٖǓѐ§µ膽 + .gpio = { + .pin = AK_GPIO_80, + .pulldown = -1, + .pullup = AK_PULLUP_ENABLE, + .value = AK_GPIO_OUT_HIGH, //ϵͳǴ¶¯LED¹ܽƄ¬ɏµ膽 + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + } + }, + +}; + +static struct ak_led_pdata led_pdata = { + .leds = leds, + .nr_led = ARRAY_SIZE(leds), +}; + +#if 0 +/* unused GPIO number for the machine board is left*/ +static unsigned int ak39_custom_gpiopin[] = { + //AK_GPIO_5, + //AK_GPIO_61, +}; + +static struct custom_gpio_data ak39_custom_gpios= { + .gpiopin = ak39_custom_gpiopin, + .ngpiopin = ARRAY_SIZE(ak39_custom_gpiopin), +}; + +static struct platform_device ak39_custom_gpio = { + .name = "akgpio", + .id = -1, + .dev = { + .platform_data = &ak39_custom_gpios, + }, +}; +#endif + +/** + * @brief: camera platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_camera_pdata ak39_camera_info = { + .mclk = 24, + .flags = 0, + //.interface = DVP_INTERFACE, +}; + +static struct user_gpio_info user_gpios[] = { + { + .name = "gpio-ircut_a", + .info = { + .pin = AK_GPIO_0, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, /* in daytime level */ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "gpio-ircut_b", + .info = { + .pin = -1,//AK_GPIO_60, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, // in daytime level + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "ir-led", + .info = { + .pin = AK_GPIO_82, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, // in daytime level ir-led closed + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, +}; + +static struct ak_user_gpio_pdata user_gpio_pdata = { + .user_gpios = user_gpios, + .nr_user_gpios = ARRAY_SIZE(user_gpios), +}; + +static struct platform_device user_gpio_device = { + .name = "user_gpio", + .id = -1, + .dev = { + .platform_data = &user_gpio_pdata, + }, +}; + +/** + * @brief: GPIO buttons platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct gpio_keys_button gpio_keys_button[] = { + { + .code = KEY_0, + .type = EV_KEY, + .gpio = AK_GPIO_5, + .active_low = 1, + .wakeup = 1, + .debounce_interval = 100, /* ms */ + .desc = "soft_boot", + .pullup = AK_PULLUP_ENABLE, + .pulldown = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = AK_GPIO_INT_LOWLEVEL, + }, +}; + +static struct akgpio_keys_platform_data gpio_keys_platform_data = { + .buttons = gpio_keys_button, + .nbuttons = ARRAY_SIZE(gpio_keys_button), + .rep = 0, +}; + +/** + * @brief: power manage gpios info + * + * @author: ye_guohong + * @date: 2014-12-09 + */ +static struct ak_wakeup_gpio pm_gpios[] = { + { + .pin = AK_GPIO_63, + .wakeup_pol = AK_FALLING_TRIGGERED, + }, +}; + +static struct ak_pm_pdata pm_pdata = { + .gpios = pm_gpios, + .nr_gpios = ARRAY_SIZE(pm_gpios), +}; + +#if 0 +/** +* @brief ad-key platform device struct + we should initialize the correct voltage for each key. +* @author: caolianming +* @date: 2014-01-09 +*/ +struct multi_addetect multi_det[] = { + {.unpress_min = 3220, .unpress_max = 3258, .fixkeys = NULL, .plugdev = PLUGIN_NODEV}, // = null 3251+/-20 + {.unpress_min = 3259, .unpress_max = 3300, .fixkeys = NULL, .plugdev = PLUGIN_AC}, // = sddet 3261(+/-30) + {.unpress_min = 2230, .unpress_max = 2290, .fixkeys = NULL, .plugdev = PLUGIN_MMC}, // = sddet 2262(+/-30) + {.unpress_min = 1700, .unpress_max = 1780, .fixkeys = NULL, .plugdev = PLUGIN_MMC_AC}, // = sddet 1742(+/-30) +}; + +struct analog_gpio_key ak39_adkey_data = { + .desc = "adkey", + .interval = 300, /* ms */ + .debounce_interval = 20, /* ms */ + .addet = multi_det, + .naddet = ARRAY_SIZE(multi_det), + .nkey = 0, + .wakeup = 1, /* enable ad-key wakeup from standby mode */ +}; + +static struct platform_device ak39_adkey_device = { + .name = "ad-keys", + .id = -1, + .dev = { + .platform_data = &ak39_adkey_data, + } +}; +#endif + + +#if 0 +/** + * @brif: ak39 battery mach info + + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_bat_mach_info ak39_bat_info = { + .gpio_init = ak_gpio_set, + .usb_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .ac_gpio = { + .is_detect_mode = BAT_CHARGE_ADC_DETECT, + .active = -1, + .irq = -1, + .delay = 500, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pulldown = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .full_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .bat_mach_info = { + .voltage_sample = 6, // the sample of read voltage + .power_on_voltage = 3650, // discharge power on voltage limit + .power_on_correct = 64, // mv + .charge_min_voltage = 3550, // charge minute voltage (mv) + .max_voltage = 4200, // max battery voltage + .min_voltage = 3500, // min battery voltage + .power_off = poweroff_disable, + .full_capacity = 100, // battery full + .poweroff_cap = 0, // user read value to power off + .low_cap = 5, // user read value to low power warring + .recover_cap = 30, + .cpower_on_voltage = 3700, // charge power on voltage limit + .full_delay = 30, // unit is minute + .full_voltage = 4100, + }, + + .bat_adc = { + .up_resistance = 10, + .dw_resistance = 10, + .voltage_correct = 32, // battery correct factor + .adc_avdd = 3300, // avdd voltage + }, + +}; +#endif + +#if defined(CONFIG_I2C_GPIO_SOFT) +struct i2c_gpio_platform_data ak39_i2c_data={ + .sda_pin = AK_GPIO_28, + .scl_pin = AK_GPIO_27, + .udelay = 30, + .timeout = 300, +}; + +struct platform_device ak39_i2c_device = { + .name = "i2c-gpio", + .id = -1, + .dev = { + .platform_data = &ak39_i2c_data, + }, +}; +#endif + + /* @brief: ak39 platform devices table + * + * @author: caolianming + * @date: 2014-01-09 + */ + static struct platform_device *ak3918_platform_devices[] __initdata = { + &akfha_char_device, + &ak39_uart0_device, + &ak39_uart1_device, + //&ak39_motor0_device, + //&ak39_motor1_device, + &ak39_pwm_device, + &ak39_spi1_device, + &ak39_mci1_device, + //&ak39_mci2_device, + &ak39_i2c_device, + //&ak39_custom_gpio, + &ak39_usb_udc_device, + &ak39_usb_otg_hcd_device, + &anyka_wifi_device, + &soc_camera_interface, + &ak39_camera_interface, + &ak39_ion_device, + &ak39_pcm_device, + &ak39_codec_device, + &ak39_mmx_device, + &ak39_mac_device, + &ak39_led_pdev, + &ak39_gpio_keys_device, + //&ak39_adkey_device, + //&ak39_battery_power, + &ak39_rtc_device, + &ak39_crypto_device, + &ak39_pm_device, + &user_gpio_device, +}; + +void wdt_enable(void); +void wdt_keepalive(unsigned int heartbeat); + +/** + * @brief: restart by "reboot" cmd + * + * @author: caolianming + * @date: 2014-01-09 + */ +//static +void ak39_restart(char str, const char *cmd) +{ + //ak39_reboot_sys_by_soft(); +#if defined CONFIG_AK39_WATCHDOG || defined CONFIG_AK39_WATCHDOG_TOP + wdt_enable(); + wdt_keepalive(2); +#endif +} + + +/** + * @brief: initial ak3918 machine + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void __init ak3918_init_machine(void) +{ + adc1_init(); + + spi_register_board_info(ak39_spi_board_dev, ARRAY_SIZE(ak39_spi_board_dev)); + + ak39_spi1_device.dev.platform_data = &ak39_spi1_info; + + ak39_motor0_device.dev.platform_data = &ak39_motor0_pdata; + ak39_motor1_device.dev.platform_data = &ak39_motor1_pdata; + + ak39_mci1_device.dev.platform_data = &mci1_plat_data; + //ak39_mci2_device.dev.platform_data = &mci2_plat_data; + + ak39_crypto_device.dev.platform_data = &akcrypto_pdata; + + ak39_usb_otg_hcd_device.dev.platform_data = &akotghc_plat_data; + ak39_mac_device.dev.platform_data = &ak39_mac_pdata; + + ak39_led_pdev.dev.platform_data = &led_pdata; + ak39_gpio_keys_device.dev.platform_data = &gpio_keys_platform_data; + //ak39_battery_power.dev.platform_data = &ak39_bat_info; + + ak39_camera_interface.dev.platform_data = &ak39_camera_info; + + ak39_pm_device.dev.platform_data = &pm_pdata; + + + platform_add_devices(ak3918_platform_devices, + ARRAY_SIZE(ak3918_platform_devices)); + + l2_init(); + + return; +} + + +MACHINE_START(AK39XX, "CLOUD39EV3_AK3918EV300_MNBD") +/* Maintainer: */ + .atag_offset = 0x100, + .fixup = NULL, + .map_io = ak39_map_io, + .reserve = NULL, + .init_irq = ak39_init_irq, + .init_machine = ak3918_init_machine, + .init_early = NULL, + .timer = &ak39_timer, + .restart = ak39_restart, + +MACHINE_END + diff --git a/arch/arm/mach-ak39/mach-cloud39ev3_ak3918ev300_sdio_wifi_mndb.c b/arch/arm/mach-ak39/mach-cloud39ev3_ak3918ev300_sdio_wifi_mndb.c new file mode 100644 index 00000000..a62c1e78 --- /dev/null +++ b/arch/arm/mach-ak39/mach-cloud39ev3_ak3918ev300_sdio_wifi_mndb.c @@ -0,0 +1,913 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "cpu.h" +#include "irq.h" +#include +#include +#include +#include +#include + +/** + * @brief: spi device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +#define SPI_ONCHIP_CS (0) /*means not need gpio*/ +static unsigned long ak39_spidev_cs[AKSPI_CS_NUM] = { + [AKSPI_ONCHIP_CS] = SPI_ONCHIP_CS, /*gpio 25, spidev0: ak-spiflash*/ +}; + +struct ak_spi_info ak39_spi1_info = { + .pin_cs = ak39_spidev_cs, + .num_cs = ARRAY_SIZE(ak39_spidev_cs), + .bus_num = AKSPI_BUS_NUM1, + .clk_name = "spi1", + .mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH, + .xfer_mode = AKSPI_XFER_MODE_DMA, +}; + +/** + * @brief: spiflash device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct flash_platform_data ak39_spiflash_info= { + .bus_width = FLASH_BUS_WIDTH_4WIRE | FLASH_BUS_WIDTH_2WIRE | FLASH_BUS_WIDTH_1WIRE, + .type = NULL, +}; + +/** + * @brief: spi bus device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct spi_board_info ak39_spi_board_dev[] = { + { + .modalias = "ak-spiflash", + .bus_num = AKSPI_BUS_NUM1, + .chip_select = AKSPI_ONCHIP_CS, + .mode = SPI_MODE_0, + .max_speed_hz = 20*1000*1000, + .platform_data = &ak39_spiflash_info, + }, +}; + + +/** + * @brief: motor0 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor0_pdata = { + .gpio_phase[0] = { + .pin = AK_GPIO_37, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = AK_GPIO_38, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = AK_GPIO_39, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = AK_GPIO_40, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + .gpio_hit[0] = { + .pin = AK_GPIO_62, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = AK_GPIO_63, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + //.angular_speed = 100, /* angle/s */ + .angular_speed = 200, /* angle/s */ +}; + + +/** + * @brief: motor1 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor1_pdata = { + .gpio_phase[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + .gpio_hit[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + .angular_speed = 100, /* angle/s */ +}; + +/** + * @brief: mci2 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci2_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .cap_highspeed = 1, + .detect_mode = AKMCI_PLUGIN_ALWAY, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_SDIO, + .gpio_init = ak_gpio_set, + .bus_width = MCI_BUS_WIDTH_4, + .max_speed_hz = 50*1000*1000, + .gpio_cd = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = AK_GPIO_11, + .pulldown = -1, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + +/** + * @brief: mci1 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci1_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_DETECT_MODE_GPIO, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_MMC_SD, + .max_speed_hz = 25*1000*1000, + .gpio_init = ak_gpio_set, + .bus_width = MCI_BUS_WIDTH_1, + .gpio_cd = { + .pin = AK_GPIO_41, // cdh: AK_GPIO_57 + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + + +struct ak_crypto_plat_data akcrypto_pdata = { + .encrypt_mode = CRYPTO_MULTI_GROUP_MODE, +}; + + +/* akwifi platform data */ +struct akwifi_platform_data akwifi_pdata = { + .gpio_init = ak_gpio_set, + .gpio_cs = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, // cs ÓÐЧֵ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_on = { + .pin = AK_GPIO_13, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_off = { + .pin = AK_GPIO_13, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .power_on_delay = 2000, + .power_off_delay = 200, +}; + +struct platform_device anyka_wifi_device = { + .name = "anyka-wifi", + .id = -1, + .dev = { + .platform_data = &akwifi_pdata, + }, +}; + + +/** + * @brief: usb bus device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct akotghc_usb_platform_data akotghc_plat_data = { + .gpio_init = ak_gpio_set, + .gpio_pwr_on = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_pwr_off = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .switch_onboard = { + .pin = -1, + }, + .switch_extport = { + .pin = -1, + }, +}; + + +/** + * @brief: ethenet device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +//static +struct ak_mac_data ak39_mac_pdata = { + .gpio_init = ak_gpio_set, + .pwr_gpio = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .phy_rst_gpio = { + .pin = AK_GPIO_81, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, +}; + + +/** +* @brief ak pcm device struct + hp and spk can identify by GPIO or AD. + wo can initialize it in this struct. +* @author dengzhou +* @date 2012-07-19 +*/ +struct ak39_codec_platform_data ak39_codec_pdata = +{ + .hpdet_gpio = + { + .pin = -1, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = -1, + .int_pol = -1, + }, + .spk_down_gpio = + { + .pin = AK_GPIO_42, //AK_GPIO_16, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .hpmute_gpio = + { + .pin = INVALID_GPIO, //AK_GPIO_29, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .linindet_gpio = + { + .pin = AK_GPIO_82, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, /* linein detect level */ + .int_pol = -1, + }, + + .hp_on_value = AK_GPIO_OUT_LOW, + .hpdet_irq = -1, + .linindet_irq = IRQ_GPIO_82, + .bIsHPmuteUsed = 0, + .hp_mute_enable_value = AK_GPIO_OUT_HIGH, + .bIsMetalfixed = 0, + .boutput_only = 1, + .detect_flag = AKPCM_DYNAMIC_DETECT, +}; + +struct resource ak39_codec_resources[] = { + [0] = { + .start = 0x08000000, + .end = 0x0800FFFF, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_AnalogCtrlRegs", + }, + [1] = { + .start = 0x20110000, + .end = 0x2011800F, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_ADC2ModeCfgRegs", + }, +}; + +struct platform_device ak39_codec_device = { + .name = "ak39-codec", + .id = -1, + .resource = ak39_codec_resources, + .num_resources = ARRAY_SIZE(ak39_codec_resources), + .dev = { + .platform_data = &ak39_codec_pdata, + }, +}; + + +/** + * @brief: sensor device platform data, this info is fake. + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct i2c_board_info ak_camara_devices[] = { + { + I2C_BOARD_INFO("aksensor", 0x1), + }, +}; + +static struct aksensor_camera_info ak_soc_camera_info = { + .buswidth = SOCAM_DATAWIDTH_8, + .pin_pwdn = INVALID_GPIO, //initialize GPIO for the power of camera. + .pin_reset = AK_GPIO_49, + .link = { + .bus_id = 39, + .power = NULL, + .board_info = &ak_camara_devices[0], + .i2c_adapter_id = 0, + .priv = &ak_soc_camera_info, + } +}; + +/* fake device for soc_camera subsystem */ +static struct platform_device soc_camera_interface = { + .name = "soc-camera-pdrv", + .id = -1, + .dev = { + .platform_data = &ak_soc_camera_info.link, + } +}; + + +/** + * @brief: LED platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_led_data leds[] = { + { + .name = "system_state_led", + .def_trigger = "none", + .effective_level = AK_GPIO_OUT_HIGH, //LEDµƿٖǓѐ§µ膽 + .gpio = { + .pin = AK_GPIO_80, + .pulldown = -1, + .pullup = AK_PULLUP_ENABLE, + .value = AK_GPIO_OUT_HIGH, //ϵͳǴ¶¯LED¹ܽƄ¬ɏµ膽 + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + } + }, + +}; + +static struct ak_led_pdata led_pdata = { + .leds = leds, + .nr_led = ARRAY_SIZE(leds), +}; + +#if 0 +/* unused GPIO number for the machine board is left*/ +static unsigned int ak39_custom_gpiopin[] = { + //AK_GPIO_5, + //AK_GPIO_61, +}; + +static struct custom_gpio_data ak39_custom_gpios= { + .gpiopin = ak39_custom_gpiopin, + .ngpiopin = ARRAY_SIZE(ak39_custom_gpiopin), +}; + +static struct platform_device ak39_custom_gpio = { + .name = "akgpio", + .id = -1, + .dev = { + .platform_data = &ak39_custom_gpios, + }, +}; +#endif + +/** + * @brief: camera platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_camera_pdata ak39_camera_info = { + .mclk = 24, + .flags = 0, + //.interface = DVP_INTERFACE, +}; + +static struct user_gpio_info user_gpios[] = { + { + .name = "gpio-ircut_a", + .info = { + .pin = AK_GPIO_0, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, /* in daytime level */ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "gpio-ircut_b", + .info = { + .pin = -1,//AK_GPIO_60, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, // in daytime level + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "ir-led", + .info = { + .pin = AK_GPIO_82, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, // in daytime level ir-led closed + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, +}; + +static struct ak_user_gpio_pdata user_gpio_pdata = { + .user_gpios = user_gpios, + .nr_user_gpios = ARRAY_SIZE(user_gpios), +}; + +static struct platform_device user_gpio_device = { + .name = "user_gpio", + .id = -1, + .dev = { + .platform_data = &user_gpio_pdata, + }, +}; + +/** + * @brief: GPIO buttons platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct gpio_keys_button gpio_keys_button[] = { + { + .code = KEY_0, + .type = EV_KEY, + .gpio = AK_GPIO_5, + .active_low = 1, + .wakeup = 1, + .debounce_interval = 100, /* ms */ + .desc = "soft_boot", + .pullup = AK_PULLUP_ENABLE, + .pulldown = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = AK_GPIO_INT_LOWLEVEL, + }, +}; + +static struct akgpio_keys_platform_data gpio_keys_platform_data = { + .buttons = gpio_keys_button, + .nbuttons = ARRAY_SIZE(gpio_keys_button), + .rep = 0, +}; + +/** + * @brief: power manage gpios info + * + * @author: ye_guohong + * @date: 2014-12-09 + */ +static struct ak_wakeup_gpio pm_gpios[] = { + { + .pin = AK_GPIO_63, + .wakeup_pol = AK_FALLING_TRIGGERED, + }, +}; + +static struct ak_pm_pdata pm_pdata = { + .gpios = pm_gpios, + .nr_gpios = ARRAY_SIZE(pm_gpios), +}; + +#if 0 +/** +* @brief ad-key platform device struct + we should initialize the correct voltage for each key. +* @author: caolianming +* @date: 2014-01-09 +*/ +struct multi_addetect multi_det[] = { + {.unpress_min = 3220, .unpress_max = 3258, .fixkeys = NULL, .plugdev = PLUGIN_NODEV}, // = null 3251+/-20 + {.unpress_min = 3259, .unpress_max = 3300, .fixkeys = NULL, .plugdev = PLUGIN_AC}, // = sddet 3261(+/-30) + {.unpress_min = 2230, .unpress_max = 2290, .fixkeys = NULL, .plugdev = PLUGIN_MMC}, // = sddet 2262(+/-30) + {.unpress_min = 1700, .unpress_max = 1780, .fixkeys = NULL, .plugdev = PLUGIN_MMC_AC}, // = sddet 1742(+/-30) +}; + +struct analog_gpio_key ak39_adkey_data = { + .desc = "adkey", + .interval = 300, /* ms */ + .debounce_interval = 20, /* ms */ + .addet = multi_det, + .naddet = ARRAY_SIZE(multi_det), + .nkey = 0, + .wakeup = 1, /* enable ad-key wakeup from standby mode */ +}; + +static struct platform_device ak39_adkey_device = { + .name = "ad-keys", + .id = -1, + .dev = { + .platform_data = &ak39_adkey_data, + } +}; +#endif + + +#if 0 +/** + * @brif: ak39 battery mach info + + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_bat_mach_info ak39_bat_info = { + .gpio_init = ak_gpio_set, + .usb_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .ac_gpio = { + .is_detect_mode = BAT_CHARGE_ADC_DETECT, + .active = -1, + .irq = -1, + .delay = 500, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pulldown = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .full_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .bat_mach_info = { + .voltage_sample = 6, // the sample of read voltage + .power_on_voltage = 3650, // discharge power on voltage limit + .power_on_correct = 64, // mv + .charge_min_voltage = 3550, // charge minute voltage (mv) + .max_voltage = 4200, // max battery voltage + .min_voltage = 3500, // min battery voltage + .power_off = poweroff_disable, + .full_capacity = 100, // battery full + .poweroff_cap = 0, // user read value to power off + .low_cap = 5, // user read value to low power warring + .recover_cap = 30, + .cpower_on_voltage = 3700, // charge power on voltage limit + .full_delay = 30, // unit is minute + .full_voltage = 4100, + }, + + .bat_adc = { + .up_resistance = 10, + .dw_resistance = 10, + .voltage_correct = 32, // battery correct factor + .adc_avdd = 3300, // avdd voltage + }, + +}; +#endif + +#if defined(CONFIG_I2C_GPIO_SOFT) +struct i2c_gpio_platform_data ak39_i2c_data={ + .sda_pin = AK_GPIO_28, + .scl_pin = AK_GPIO_27, + .udelay = 30, + .timeout = 300, +}; + +struct platform_device ak39_i2c_device = { + .name = "i2c-gpio", + .id = -1, + .dev = { + .platform_data = &ak39_i2c_data, + }, +}; +#endif + + /* @brief: ak39 platform devices table + * + * @author: caolianming + * @date: 2014-01-09 + */ + static struct platform_device *ak3918_platform_devices[] __initdata = { + &akfha_char_device, + &ak39_uart0_device, + &ak39_uart1_device, + //&ak39_motor0_device, + //&ak39_motor1_device, + &ak39_pwm_device, + &ak39_spi1_device, + &ak39_mci1_device, + &ak39_mci2_device, + &ak39_i2c_device, + //&ak39_custom_gpio, + &ak39_usb_udc_device, + &ak39_usb_otg_hcd_device, + &anyka_wifi_device, + &soc_camera_interface, + &ak39_camera_interface, + &ak39_ion_device, + &ak39_pcm_device, + &ak39_codec_device, + &ak39_mmx_device, + //&ak39_mac_device, + &ak39_led_pdev, + &ak39_gpio_keys_device, + //&ak39_adkey_device, + //&ak39_battery_power, + &ak39_rtc_device, + &ak39_crypto_device, + &ak39_pm_device, + &user_gpio_device, +}; + +void wdt_enable(void); +void wdt_keepalive(unsigned int heartbeat); + +/** + * @brief: restart by "reboot" cmd + * + * @author: caolianming + * @date: 2014-01-09 + */ +//static +void ak39_restart(char str, const char *cmd) +{ + //ak39_reboot_sys_by_soft(); +#if defined CONFIG_AK39_WATCHDOG || defined CONFIG_AK39_WATCHDOG_TOP + wdt_enable(); + wdt_keepalive(2); +#endif +} + + +/** + * @brief: initial ak3918 machine + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void __init ak3918_init_machine(void) +{ + adc1_init(); + + spi_register_board_info(ak39_spi_board_dev, ARRAY_SIZE(ak39_spi_board_dev)); + + ak39_spi1_device.dev.platform_data = &ak39_spi1_info; + + //ak39_motor0_device.dev.platform_data = &ak39_motor0_pdata; + //ak39_motor1_device.dev.platform_data = &ak39_motor1_pdata; + + ak39_mci1_device.dev.platform_data = &mci1_plat_data; + ak39_mci2_device.dev.platform_data = &mci2_plat_data; + + ak39_crypto_device.dev.platform_data = &akcrypto_pdata; + + ak39_usb_otg_hcd_device.dev.platform_data = &akotghc_plat_data; + //ak39_mac_device.dev.platform_data = &ak39_mac_pdata; + + ak39_led_pdev.dev.platform_data = &led_pdata; + ak39_gpio_keys_device.dev.platform_data = &gpio_keys_platform_data; + //ak39_battery_power.dev.platform_data = &ak39_bat_info; + + ak39_camera_interface.dev.platform_data = &ak39_camera_info; + + ak39_pm_device.dev.platform_data = &pm_pdata; + + + platform_add_devices(ak3918_platform_devices, + ARRAY_SIZE(ak3918_platform_devices)); + + l2_init(); + + return; +} + + +MACHINE_START(AK39XX, "CLOUD39EV3_AK3918EV300_MNBD") +/* Maintainer: */ + .atag_offset = 0x100, + .fixup = NULL, + .map_io = ak39_map_io, + .reserve = NULL, + .init_irq = ak39_init_irq, + .init_machine = ak3918_init_machine, + .init_early = NULL, + .timer = &ak39_timer, + .restart = ak39_restart, + +MACHINE_END + diff --git a/arch/arm/mach-ak39/mach-cloud39ev3_ak3919ev300_mnbd.c b/arch/arm/mach-ak39/mach-cloud39ev3_ak3919ev300_mnbd.c new file mode 100644 index 00000000..39c932b6 --- /dev/null +++ b/arch/arm/mach-ak39/mach-cloud39ev3_ak3919ev300_mnbd.c @@ -0,0 +1,915 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "cpu.h" +#include "irq.h" +#include +#include +#include +#include +#include + +/** + * @brief: spi device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +#define SPI_ONCHIP_CS (0) /*means not need gpio*/ +static unsigned long ak39_spidev_cs[AKSPI_CS_NUM] = { + [AKSPI_ONCHIP_CS] = SPI_ONCHIP_CS, /*gpio 25, spidev0: ak-spiflash*/ +}; + +struct ak_spi_info ak39_spi1_info = { + .pin_cs = ak39_spidev_cs, + .num_cs = ARRAY_SIZE(ak39_spidev_cs), + .bus_num = AKSPI_BUS_NUM1, + .clk_name = "spi1", + .mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH, + .xfer_mode = AKSPI_XFER_MODE_DMA, +}; + +/** + * @brief: spiflash device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct flash_platform_data ak39_spiflash_info= { + .bus_width = FLASH_BUS_WIDTH_4WIRE | FLASH_BUS_WIDTH_2WIRE | FLASH_BUS_WIDTH_1WIRE, + .type = NULL, +}; + +/** + * @brief: spi bus device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct spi_board_info ak39_spi_board_dev[] = { + { + .modalias = "ak-spiflash", + .bus_num = AKSPI_BUS_NUM1, + .chip_select = AKSPI_ONCHIP_CS, + .mode = SPI_MODE_0, + .max_speed_hz = 20*1000*1000, + .platform_data = &ak39_spiflash_info, + }, +}; + + +/** + * @brief: motor0 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor0_pdata = { + .gpio_phase[0] = { + .pin = AK_GPIO_37, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = AK_GPIO_38, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = AK_GPIO_39, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = AK_GPIO_40, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + .gpio_hit[0] = { + .pin = AK_GPIO_62, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = AK_GPIO_63, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + //.angular_speed = 100, /* angle/s */ + .angular_speed = 200, /* angle/s */ +}; + + +/** + * @brief: motor1 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor1_pdata = { + .gpio_phase[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + .gpio_hit[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + .angular_speed = 100, /* angle/s */ +}; + +/** + * @brief: mci2 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci2_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_PLUGIN_ALWAY, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_SDIO, + .gpio_init = ak_gpio_set, + .max_speed_hz = 20*1000*1000, + .gpio_cd = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + +/** + * @brief: mci1 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci1_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_DETECT_MODE_GPIO, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_MMC_SD, + .max_speed_hz = 25*1000*1000, + .gpio_init = ak_gpio_set, + .bus_width = MCI_BUS_WIDTH_1, + .gpio_cd = { + .pin = -1, /* GPIO20 are used by MAC_RXD1, must be disable */ + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + + +struct ak_crypto_plat_data akcrypto_pdata = { + .encrypt_mode = CRYPTO_MULTI_GROUP_MODE, +}; + + +/* akwifi platform data */ +struct akwifi_platform_data akwifi_pdata = { + .gpio_init = ak_gpio_set, + .gpio_cs = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_on = { + .pin = AK_GPIO_50, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_off = { + .pin = AK_GPIO_50, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .power_on_delay = 2000, + .power_off_delay = 200, +}; + +struct platform_device anyka_wifi_device = { + .name = "anyka-wifi", + .id = -1, + .dev = { + .platform_data = &akwifi_pdata, + }, +}; + + +/** + * @brief: usb bus device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct akotghc_usb_platform_data akotghc_plat_data = { + .gpio_init = ak_gpio_set, + .gpio_pwr_on = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_pwr_off = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .switch_onboard = { + .pin = -1, + }, + .switch_extport = { + .pin = -1, + }, +}; + + +/** + * @brief: ethenet device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_mac_data ak39_mac_pdata = { + .gpio_init = ak_gpio_set, + .pwr_gpio = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .phy_rst_gpio = { + .pin = AK_GPIO_42, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, +}; + + +/** +* @brief ak pcm device struct + hp and spk can identify by GPIO or AD. + wo can initialize it in this struct. +* @author dengzhou +* @date 2012-07-19 +*/ +struct ak39_codec_platform_data ak39_codec_pdata = +{ + .hpdet_gpio = + { + .pin = -1, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = -1, + .int_pol = -1, + }, + .spk_down_gpio = + { + .pin = -1, /*GPIO47 are used by MII_50M, must be disable */ + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .hpmute_gpio = + { + .pin = INVALID_GPIO, //AK_GPIO_29, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .linindet_gpio = + { + .pin = -1, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, /* linein detect level */ + .int_pol = -1, + }, + + .hp_on_value = AK_GPIO_OUT_LOW, + .hpdet_irq = -1, + .linindet_irq = -1, + .bIsHPmuteUsed = 0, + .hp_mute_enable_value = AK_GPIO_OUT_HIGH, + .bIsMetalfixed = 0, + .boutput_only = 1, + .detect_flag = AKPCM_DYNAMIC_DETECT, +}; + +struct resource ak39_codec_resources[] = { + [0] = { + .start = 0x08000000, + .end = 0x0800FFFF, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_AnalogCtrlRegs", + }, + [1] = { + .start = 0x20110000, + .end = 0x2011800F, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_ADC2ModeCfgRegs", + }, +}; + + +struct platform_device ak39_codec_device = { + .name = "ak39-codec", + .id = -1, + .resource = ak39_codec_resources, + .num_resources = ARRAY_SIZE(ak39_codec_resources), + .dev = { + .platform_data = &ak39_codec_pdata, + }, +}; + + +/** + * @brief: sensor device platform data, this info is fake. + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct i2c_board_info ak_camara_devices[] = { + { + I2C_BOARD_INFO("aksensor", 0x1), + }, +}; + +static struct aksensor_camera_info ak_soc_camera_info = { + .buswidth = SOCAM_DATAWIDTH_8, + .pin_pwdn = INVALID_GPIO, //initialize GPIO for the power of camera. + .pin_reset = AK_GPIO_23, + .link = { + .bus_id = 39, + .power = NULL, + .board_info = &ak_camara_devices[0], + .i2c_adapter_id = 0, + .priv = &ak_soc_camera_info, + } +}; + +/* fake device for soc_camera subsystem */ +static struct platform_device soc_camera_interface = { + .name = "soc-camera-pdrv", + .id = -1, + .dev = { + .platform_data = &ak_soc_camera_info.link, + } +}; + + +/** + * @brief: LED platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_led_data leds[] = { + { + .name = "system_state_led", + .def_trigger = "none", + .effective_level = AK_GPIO_OUT_HIGH, + .gpio = { + .pin = -1, /* GPIO24 are used by MAC_RXDV, must be disable */ + .pulldown = -1, + .pullup = AK_PULLUP_ENABLE, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + } + }, + +}; + +static struct ak_led_pdata led_pdata = { + .leds = leds, + .nr_led = ARRAY_SIZE(leds), +}; + +#if 0 +/* unused GPIO number for the machine board is left*/ +static unsigned int ak39_custom_gpiopin[] = { + //AK_GPIO_5, + //AK_GPIO_61, +}; + +static struct custom_gpio_data ak39_custom_gpios= { + .gpiopin = ak39_custom_gpiopin, + .ngpiopin = ARRAY_SIZE(ak39_custom_gpiopin), +}; + +static struct platform_device ak39_custom_gpio = { + .name = "akgpio", + .id = -1, + .dev = { + .platform_data = &ak39_custom_gpios, + }, +}; +#endif + +/** + * @brief: camera platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_camera_pdata ak39_camera_info = { + .mclk = 24, + .flags = 0, + //.interface = MIPI_INTERFACE, +}; + +static struct user_gpio_info user_gpios[] = { + { + .name = "gpio-ircut_a", + .info = { + .pin = AK_GPIO_0, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, /* in daytime level */ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "gpio-ircut_b", + .info = { + .pin = -1,//AK_GPIO_60, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, // in daytime level + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "ir-led", + .info = { + .pin = AK_GPIO_41, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, // in daytime level ir-led closed + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + } +}; + +static struct ak_user_gpio_pdata user_gpio_pdata = { + .user_gpios = user_gpios, + .nr_user_gpios = ARRAY_SIZE(user_gpios), +}; + +static struct platform_device user_gpio_device = { + .name = "user_gpio", + .id = -1, + .dev = { + .platform_data = &user_gpio_pdata, + }, +}; + +/** + * @brief: GPIO buttons platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct gpio_keys_button gpio_keys_button[] = { + { + .code = KEY_0, + .type = EV_KEY, + .gpio = AK_GPIO_5, + .active_low = 1, + .wakeup = 1, + .debounce_interval = 100, /* ms */ + .desc = "soft_boot", + .pullup = AK_PULLUP_ENABLE, + .pulldown = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = AK_GPIO_INT_LOWLEVEL, + }, +}; + +static struct akgpio_keys_platform_data gpio_keys_platform_data = { + .buttons = gpio_keys_button, + .nbuttons = ARRAY_SIZE(gpio_keys_button), + .rep = 0, +}; + +/** + * @brief: power manage gpios info + * + * @author: ye_guohong + * @date: 2014-12-09 + */ +static struct ak_wakeup_gpio pm_gpios[] = { + { + .pin = AK_GPIO_63, + .wakeup_pol = AK_FALLING_TRIGGERED, + }, +}; + +static struct ak_pm_pdata pm_pdata = { + .gpios = pm_gpios, + .nr_gpios = ARRAY_SIZE(pm_gpios), +}; + +#if 1 +/** +* @brief ad-key platform device struct + we should initialize the correct voltage for each key. +* @author: caolianming +* @date: 2014-01-09 +*/ +struct adgpio_key adkey[][3] = { + {{ .code = KEY_0, .min = 0, .max = 100}, //145+30/-40 + { .code = KEY_1, .min = 1000, .max = 1100}, //696+30/-40 + { .code = KEY_2, .min = 1880, .max = 2080}}, //1156+/-50 +}; + + +struct multi_addetect multi_det[] = { + {.unpress_min = 0, .unpress_max = 2100, .fixkeys = adkey[0], .plugdev = PLUGIN_DEV1}, // = null 3251+/-20 +}; + +struct analog_gpio_key ak39_adkey_data = { + .desc = "adkey", + .interval = 120, /* ms */ + .debounce_interval = 20, /* ms */ + .addet = multi_det, + .naddet = ARRAY_SIZE(multi_det), + .nkey = ARRAY_SIZE(adkey[0]), + .wakeup = 1, /* enable ad-key wakeup from standby mode */ +}; + +static struct platform_device ak39_adkey_device = { + .name = "ad-keys", + .id = -1, + .dev = { + .platform_data = &ak39_adkey_data, + } +}; +#endif + + +#if 0 +/** + * @brif: ak39 battery mach info + + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_bat_mach_info ak39_bat_info = { + .gpio_init = ak_gpio_set, + .usb_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .ac_gpio = { + .is_detect_mode = BAT_CHARGE_ADC_DETECT, + .active = -1, + .irq = -1, + .delay = 500, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pulldown = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .full_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .bat_mach_info = { + .voltage_sample = 6, // the sample of read voltage + .power_on_voltage = 3650, // discharge power on voltage limit + .power_on_correct = 64, // mv + .charge_min_voltage = 3550, // charge minute voltage (mv) + .max_voltage = 4200, // max battery voltage + .min_voltage = 3500, // min battery voltage + .power_off = poweroff_disable, + .full_capacity = 100, // battery full + .poweroff_cap = 0, // user read value to power off + .low_cap = 5, // user read value to low power warring + .recover_cap = 30, + .cpower_on_voltage = 3700, // charge power on voltage limit + .full_delay = 30, // unit is minute + .full_voltage = 4100, + }, + + .bat_adc = { + .up_resistance = 10, + .dw_resistance = 10, + .voltage_correct = 32, // battery correct factor + .adc_avdd = 3300, // avdd voltage + }, + +}; +#endif + +#if defined(CONFIG_I2C_GPIO_SOFT) +struct i2c_gpio_platform_data ak39_i2c_data={ + .sda_pin = AK_GPIO_28, + .scl_pin = AK_GPIO_27, + .udelay = 30, + .timeout = 300, +}; + +struct platform_device ak39_i2c_device = { + .name = "i2c-gpio", + .id = -1, + .dev = { + .platform_data = &ak39_i2c_data, + }, +}; +#endif + + /* @brief: ak39 platform devices table + * + * @author: caolianming + * @date: 2014-01-09 + */ + static struct platform_device *ak3918_platform_devices[] __initdata = { + &akfha_char_device, + &ak39_uart0_device, + &ak39_uart1_device, + //&ak39_motor0_device, + //&ak39_motor1_device, + &ak39_pwm_device, + &ak39_spi1_device, + &ak39_mci1_device, + //&ak39_mci2_device, + &ak39_i2c_device, + //&ak39_custom_gpio, + &ak39_usb_udc_device, + &ak39_usb_otg_hcd_device, + &anyka_wifi_device, + &soc_camera_interface, + &ak39_camera_interface, + &ak39_ion_device, + &ak39_pcm_device, + &ak39_codec_device, + &ak39_mmx_device, + &ak39_mac_device, + &ak39_led_pdev, + //&ak39_gpio_keys_device, + &ak39_adkey_device, + //&ak39_battery_power, + &ak39_rtc_device, + &ak39_crypto_device, + &ak39_pm_device, + &user_gpio_device, +}; + +void wdt_enable(void); +void wdt_keepalive(unsigned int heartbeat); + +/** + * @brief: restart by "reboot" cmd + * + * @author: caolianming + * @date: 2014-01-09 + */ +//static +void ak39_restart(char str, const char *cmd) +{ + //ak39_reboot_sys_by_soft(); +#if defined CONFIG_AK39_WATCHDOG || defined CONFIG_AK39_WATCHDOG_TOP + wdt_enable(); + wdt_keepalive(2); +#endif +} + + +/** + * @brief: initial ak3918 machine + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void __init ak3918_init_machine(void) +{ + adc1_init(); + + spi_register_board_info(ak39_spi_board_dev, ARRAY_SIZE(ak39_spi_board_dev)); + + ak39_spi1_device.dev.platform_data = &ak39_spi1_info; + + ak39_motor0_device.dev.platform_data = &ak39_motor0_pdata; + ak39_motor1_device.dev.platform_data = &ak39_motor1_pdata; + + ak39_mci1_device.dev.platform_data = &mci1_plat_data; + //ak39_mci2_device.dev.platform_data = &mci2_plat_data; + + ak39_crypto_device.dev.platform_data = &akcrypto_pdata; + + ak39_usb_otg_hcd_device.dev.platform_data = &akotghc_plat_data; + ak39_mac_device.dev.platform_data = &ak39_mac_pdata; + + ak39_led_pdev.dev.platform_data = &led_pdata; + ak39_gpio_keys_device.dev.platform_data = &gpio_keys_platform_data; + //ak39_battery_power.dev.platform_data = &ak39_bat_info; + + ak39_camera_interface.dev.platform_data = &ak39_camera_info; + + ak39_pm_device.dev.platform_data = &pm_pdata; + + + platform_add_devices(ak3918_platform_devices, + ARRAY_SIZE(ak3918_platform_devices)); + + l2_init(); + + return; +} + + +MACHINE_START(AK39XX, "CLOUD39EV3_AK3919EV300_MNBD") +/* Maintainer: */ + .atag_offset = 0x100, + .fixup = NULL, + .map_io = ak39_map_io, + .reserve = NULL, + .init_irq = ak39_init_irq, + .init_machine = ak3918_init_machine, + .init_early = NULL, + .timer = &ak39_timer, + .restart = ak39_restart, + +MACHINE_END + diff --git a/arch/arm/mach-ak39/mach-cloud39ev3_ak3919ev300_sq38_sc2232.c b/arch/arm/mach-ak39/mach-cloud39ev3_ak3919ev300_sq38_sc2232.c new file mode 100644 index 00000000..ec2a383d --- /dev/null +++ b/arch/arm/mach-ak39/mach-cloud39ev3_ak3919ev300_sq38_sc2232.c @@ -0,0 +1,901 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "cpu.h" +#include "irq.h" +#include +#include +#include +#include +#include + +/** + * @brief: spi device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +#define SPI_ONCHIP_CS (0) /*means not need gpio*/ +static unsigned long ak39_spidev_cs[AKSPI_CS_NUM] = { + [AKSPI_ONCHIP_CS] = SPI_ONCHIP_CS, /*gpio 25, spidev0: ak-spiflash*/ +}; + +struct ak_spi_info ak39_spi1_info = { + .pin_cs = ak39_spidev_cs, + .num_cs = ARRAY_SIZE(ak39_spidev_cs), + .bus_num = AKSPI_BUS_NUM1, + .clk_name = "spi1", + .mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH, + .xfer_mode = AKSPI_XFER_MODE_DMA, +}; + +/** + * @brief: spiflash device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct flash_platform_data ak39_spiflash_info= { + .bus_width = FLASH_BUS_WIDTH_4WIRE | FLASH_BUS_WIDTH_2WIRE | FLASH_BUS_WIDTH_1WIRE, + .type = NULL, +}; + +/** + * @brief: spi bus device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct spi_board_info ak39_spi_board_dev[] = { + { + .modalias = "ak-spiflash", + .bus_num = AKSPI_BUS_NUM1, + .chip_select = AKSPI_ONCHIP_CS, + .mode = SPI_MODE_0, + .max_speed_hz = 20*1000*1000, + .platform_data = &ak39_spiflash_info, + }, +}; + + +/** + * @brief: motor0 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor0_pdata = { + .gpio_phase[0] = { + .pin = AK_GPIO_37, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = AK_GPIO_38, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = AK_GPIO_39, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = AK_GPIO_40, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + .gpio_hit[0] = { + .pin = AK_GPIO_62, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = AK_GPIO_63, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + //.angular_speed = 100, /* angle/s */ + .angular_speed = 200, /* angle/s */ +}; + + +/** + * @brief: motor1 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor1_pdata = { + .gpio_phase[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + .gpio_hit[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + .angular_speed = 100, /* angle/s */ +}; + +/** + * @brief: mci2 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci2_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_PLUGIN_ALWAY, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_SDIO, + .gpio_init = ak_gpio_set, + .max_speed_hz = 25*1000*1000, + .gpio_cd = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + +/** + * @brief: mci1 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci1_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_PLUGIN_ALWAY, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_MMC_SD, + .max_speed_hz = 25*1000*1000, + .gpio_init = ak_gpio_set, + .bus_width = MCI_BUS_WIDTH_1, + .gpio_cd = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + + +struct ak_crypto_plat_data akcrypto_pdata = { + .encrypt_mode = CRYPTO_MULTI_GROUP_MODE, +}; + + +/* akwifi platform data */ +struct akwifi_platform_data akwifi_pdata = { + .gpio_init = ak_gpio_set, + .gpio_cs = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, // cs Чֵ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_on = { + .pin = AK_GPIO_50, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_off = { + .pin = AK_GPIO_50, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .power_on_delay = 2000, + .power_off_delay = 200, +}; + +struct platform_device anyka_wifi_device = { + .name = "anyka-wifi", + .id = -1, + .dev = { + .platform_data = &akwifi_pdata, + }, +}; + + +/** + * @brief: usb bus device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct akotghc_usb_platform_data akotghc_plat_data = { + .gpio_init = ak_gpio_set, + .gpio_pwr_on = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_pwr_off = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .switch_onboard = { + .pin = -1, + }, + .switch_extport = { + .pin = -1, + }, +}; + + +/** + * @brief: ethenet device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_mac_data ak39_mac_pdata = { + .gpio_init = ak_gpio_set, + .pwr_gpio = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .phy_rst_gpio = { + .pin = AK_GPIO_0, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, +}; + + +/** +* @brief ak pcm device struct + hp and spk can identify by GPIO or AD. + wo can initialize it in this struct. +* @author dengzhou +* @date 2012-07-19 +*/ +struct ak39_codec_platform_data ak39_codec_pdata = +{ + .hpdet_gpio = + { + .pin = INVALID_GPIO, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = -1, + .int_pol = -1, + }, + .spk_down_gpio = + { + .pin = AK_GPIO_82, //AK_GPIO_16, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .hpmute_gpio = + { + .pin = INVALID_GPIO, //AK_GPIO_29, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .linindet_gpio = + { + .pin = -1, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, /* linein detect level */ + .int_pol = -1, + }, + + .hp_on_value = AK_GPIO_OUT_LOW, + .hpdet_irq = -1, + .linindet_irq = -1, + .bIsHPmuteUsed = 0, + .hp_mute_enable_value = AK_GPIO_OUT_HIGH, + .bIsMetalfixed = 0, + .boutput_only = 1, + .detect_flag = AKPCM_MIC_ALWAY, +}; + +struct resource ak39_codec_resources[] = { + [0] = { + .start = 0x08000000, + .end = 0x0800FFFF, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_AnalogCtrlRegs", + }, + [1] = { + .start = 0x20110000, + .end = 0x2011800F, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_ADC2ModeCfgRegs", + }, +}; + + +struct platform_device ak39_codec_device = { + .name = "ak39-codec", + .id = -1, + .resource = ak39_codec_resources, + .num_resources = ARRAY_SIZE(ak39_codec_resources), + .dev = { + .platform_data = &ak39_codec_pdata, + }, +}; + + +/** + * @brief: sensor device platform data, this info is fake. + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct i2c_board_info ak_camara_devices[] = { + { + I2C_BOARD_INFO("aksensor", 0x1), + }, +}; + +static struct aksensor_camera_info ak_soc_camera_info = { + .buswidth = SOCAM_DATAWIDTH_8, + .pin_pwdn = AK_GPIO_33,//AK_GPIO_0, //initialize GPIO for the power of camera. + .pin_reset = AK_GPIO_5, + .link = { + .bus_id = 39, + .power = NULL, + .board_info = &ak_camara_devices[0], + .i2c_adapter_id = 0, + .priv = &ak_soc_camera_info, + } +}; + +/* fake device for soc_camera subsystem */ +static struct platform_device soc_camera_interface = { + .name = "soc-camera-pdrv", + .id = -1, + .dev = { + .platform_data = &ak_soc_camera_info.link, + } +}; + + +/** + * @brief: LED platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_led_data leds[] = { + { + .name = "system_state_led", + .def_trigger = "none", + .effective_level = AK_GPIO_OUT_HIGH, //LEDƿЧƽ + .gpio = { + .pin = AK_GPIO_81, + .pulldown = -1, + .pullup = AK_PULLUP_ENABLE, + .value = AK_GPIO_OUT_HIGH, //ϵͳLEDܽĬϵƽ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + } + }, +}; + +static struct ak_led_pdata led_pdata = { + .leds = leds, + .nr_led = ARRAY_SIZE(leds), +}; + +#if 0 +/* unused GPIO number for the machine board is left*/ +static unsigned int ak39_custom_gpiopin[] = { + //AK_GPIO_5, + //AK_GPIO_61, +}; + +static struct custom_gpio_data ak39_custom_gpios= { + .gpiopin = ak39_custom_gpiopin, + .ngpiopin = ARRAY_SIZE(ak39_custom_gpiopin), +}; + +static struct platform_device ak39_custom_gpio = { + .name = "akgpio", + .id = -1, + .dev = { + .platform_data = &ak39_custom_gpios, + }, +}; +#endif + +/** + * @brief: camera platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_camera_pdata ak39_camera_info = { + .mclk = 24, + .flags = 0, + //.interface = MIPI_INTERFACE, +}; + +static struct user_gpio_info user_gpios[] = { + + { + .name = "gpio-ircut_a", + .info = { + .pin = AK_GPIO_42, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, /* in daytime level */ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "gpio-ircut_b", + .info = { + .pin = AK_GPIO_41, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, /* in daytime level */ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, +}; + +static struct ak_user_gpio_pdata user_gpio_pdata = { + .user_gpios = user_gpios, + .nr_user_gpios = ARRAY_SIZE(user_gpios), +}; + +static struct platform_device user_gpio = { + .name = "user_gpio", + .id = -1, + .dev = { + .platform_data = &user_gpio_pdata, + }, +}; + +/** + * @brief: GPIO buttons platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +/*deamon process need to open the gpio_key device. +** If fail,deamon process will exit and lead to +** watchdog rebooting the system. +*/ +static struct gpio_keys_button gpio_keys_button[] = { + { + .code = KEY_0, + .type = EV_KEY, + .gpio = AK_GPIO_81, //AK_GPIO_5(CAM_RESET), /*To make sure gpio_key device exist. */ + .active_low = 1, + .wakeup = 1, + .debounce_interval = 100, /* ms */ + .desc = "boot0", + .pullup = AK_PULLUP_ENABLE, + .pulldown = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = AK_GPIO_INT_LOWLEVEL, + }, +}; + +static struct akgpio_keys_platform_data gpio_keys_platform_data = { + .buttons = gpio_keys_button, + .nbuttons = ARRAY_SIZE(gpio_keys_button), + .rep = 0, +}; + +/** + * @brief: power manage gpios info + * + * @author: ye_guohong + * @date: 2014-12-09 + */ +static struct ak_wakeup_gpio pm_gpios[] = { + { + .pin = AK_GPIO_63, + .wakeup_pol = AK_FALLING_TRIGGERED, + }, +}; + +static struct ak_pm_pdata pm_pdata = { + .gpios = pm_gpios, + .nr_gpios = ARRAY_SIZE(pm_gpios), +}; + +#if 0 +/** +* @brief ad-key platform device struct + we should initialize the correct voltage for each key. +* @author: caolianming +* @date: 2014-01-09 +*/ +struct multi_addetect multi_det[] = { + {.unpress_min = 3220, .unpress_max = 3258, .fixkeys = NULL, .plugdev = PLUGIN_NODEV}, // = null 3251+/-20 + {.unpress_min = 3259, .unpress_max = 3300, .fixkeys = NULL, .plugdev = PLUGIN_AC}, // = sddet 3261(+/-30) + {.unpress_min = 2230, .unpress_max = 2290, .fixkeys = NULL, .plugdev = PLUGIN_MMC}, // = sddet 2262(+/-30) + {.unpress_min = 1700, .unpress_max = 1780, .fixkeys = NULL, .plugdev = PLUGIN_MMC_AC}, // = sddet 1742(+/-30) +}; + +struct analog_gpio_key ak39_adkey_data = { + .desc = "adkey", + .interval = 300, /* ms */ + .debounce_interval = 20, /* ms */ + .addet = multi_det, + .naddet = ARRAY_SIZE(multi_det), + .nkey = 0, + .wakeup = 1, /* enable ad-key wakeup from standby mode */ +}; + +static struct platform_device ak39_adkey_device = { + .name = "ad-keys", + .id = -1, + .dev = { + .platform_data = &ak39_adkey_data, + } +}; +#endif + + +#if 0 +/** + * @brif: ak39 battery mach info + + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_bat_mach_info ak39_bat_info = { + .gpio_init = ak_gpio_set, + .usb_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .ac_gpio = { + .is_detect_mode = BAT_CHARGE_ADC_DETECT, + .active = -1, + .irq = -1, + .delay = 500, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pulldown = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .full_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .bat_mach_info = { + .voltage_sample = 6, // the sample of read voltage + .power_on_voltage = 3650, // discharge power on voltage limit + .power_on_correct = 64, // mv + .charge_min_voltage = 3550, // charge minute voltage (mv) + .max_voltage = 4200, // max battery voltage + .min_voltage = 3500, // min battery voltage + .power_off = poweroff_disable, + .full_capacity = 100, // battery full + .poweroff_cap = 0, // user read value to power off + .low_cap = 5, // user read value to low power warring + .recover_cap = 30, + .cpower_on_voltage = 3700, // charge power on voltage limit + .full_delay = 30, // unit is minute + .full_voltage = 4100, + }, + + .bat_adc = { + .up_resistance = 10, + .dw_resistance = 10, + .voltage_correct = 32, // battery correct factor + .adc_avdd = 3300, // avdd voltage + }, + +}; +#endif + + +#if defined(CONFIG_I2C_GPIO_SOFT) +struct i2c_gpio_platform_data ak39_i2c_data={ + .sda_pin = AK_GPIO_28, + .scl_pin = AK_GPIO_27, + .udelay = 30, + .timeout = 300, +}; + +struct platform_device ak39_i2c_device = { + .name = "i2c-gpio", + .id = -1, + .dev = { + .platform_data = &ak39_i2c_data, + }, +}; +#endif + + /* @brief: ak39 platform devices table + * + * @author: caolianming + * @date: 2014-01-09 + */ + static struct platform_device *ak3918_platform_devices[] __initdata = { + &akfha_char_device, + &ak39_uart0_device, + //&ak39_motor0_device, + //&ak39_motor1_device, + &ak39_spi1_device, + //&ak39_mci1_device, + &ak39_i2c_device, + //&ak39_custom_gpio, + //&ak39_usb_udc_device, + //&ak39_usb_otg_hcd_device, +// &anyka_wifi_device, + &soc_camera_interface, + &ak39_camera_interface, + &ak39_ion_device, + //&ak39_pcm_device, + &ak39_codec_device, + &ak39_mmx_device, + &ak39_mac_device, + //&ak39_led_pdev, + &ak39_gpio_keys_device, + //&ak39_adkey_device, + //&ak39_battery_power, + &ak39_rtc_device, + &ak39_crypto_device, + //&ak39_pm_device, + &user_gpio, +}; + +void wdt_enable(void); +void wdt_keepalive(unsigned int heartbeat); + +/** + * @brief: restart by "reboot" cmd + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void ak39_restart(char str, const char *cmd) +{ + //ak39_reboot_sys_by_soft(); +#if defined CONFIG_AK39_WATCHDOG || defined CONFIG_AK39_WATCHDOG_TOP + wdt_enable(); + wdt_keepalive(2); +#endif +} + + +/** + * @brief: initial ak3918 machine + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void __init ak3918_init_machine(void) +{ + adc1_init(); + + spi_register_board_info(ak39_spi_board_dev, ARRAY_SIZE(ak39_spi_board_dev)); + + ak39_spi1_device.dev.platform_data = &ak39_spi1_info; + + //ak39_motor0_device.dev.platform_data = &ak39_motor0_pdata; + //ak39_motor1_device.dev.platform_data = &ak39_motor1_pdata; + + ak39_mci1_device.dev.platform_data = &mci1_plat_data; + + ak39_crypto_device.dev.platform_data = &akcrypto_pdata; + + ak39_usb_otg_hcd_device.dev.platform_data = &akotghc_plat_data; + ak39_mac_device.dev.platform_data = &ak39_mac_pdata; + + ak39_led_pdev.dev.platform_data = &led_pdata; + ak39_gpio_keys_device.dev.platform_data = &gpio_keys_platform_data; + //ak39_battery_power.dev.platform_data = &ak39_bat_info; + + ak39_camera_interface.dev.platform_data = &ak39_camera_info; + + ak39_pm_device.dev.platform_data = &pm_pdata; + + + platform_add_devices(ak3918_platform_devices, + ARRAY_SIZE(ak3918_platform_devices)); + + l2_init(); + + return; +} + + +MACHINE_START(AK39XX, "Cloud39EV3_AK3918E80PIN_MNBD") +/* Maintainer: */ + .atag_offset = 0x100, + .fixup = NULL, + .map_io = ak39_map_io, + .reserve = NULL, + .init_irq = ak39_init_irq, + .init_machine = ak3918_init_machine, + .init_early = NULL, + .timer = &ak39_timer, + .restart = ak39_restart, + +MACHINE_END + + diff --git a/arch/arm/mach-ak39/mach-cloud39ev3_ak3919ev300_sq38_sc2232_V1.0.0.c b/arch/arm/mach-ak39/mach-cloud39ev3_ak3919ev300_sq38_sc2232_V1.0.0.c new file mode 100644 index 00000000..ec2a383d --- /dev/null +++ b/arch/arm/mach-ak39/mach-cloud39ev3_ak3919ev300_sq38_sc2232_V1.0.0.c @@ -0,0 +1,901 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "cpu.h" +#include "irq.h" +#include +#include +#include +#include +#include + +/** + * @brief: spi device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +#define SPI_ONCHIP_CS (0) /*means not need gpio*/ +static unsigned long ak39_spidev_cs[AKSPI_CS_NUM] = { + [AKSPI_ONCHIP_CS] = SPI_ONCHIP_CS, /*gpio 25, spidev0: ak-spiflash*/ +}; + +struct ak_spi_info ak39_spi1_info = { + .pin_cs = ak39_spidev_cs, + .num_cs = ARRAY_SIZE(ak39_spidev_cs), + .bus_num = AKSPI_BUS_NUM1, + .clk_name = "spi1", + .mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH, + .xfer_mode = AKSPI_XFER_MODE_DMA, +}; + +/** + * @brief: spiflash device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct flash_platform_data ak39_spiflash_info= { + .bus_width = FLASH_BUS_WIDTH_4WIRE | FLASH_BUS_WIDTH_2WIRE | FLASH_BUS_WIDTH_1WIRE, + .type = NULL, +}; + +/** + * @brief: spi bus device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct spi_board_info ak39_spi_board_dev[] = { + { + .modalias = "ak-spiflash", + .bus_num = AKSPI_BUS_NUM1, + .chip_select = AKSPI_ONCHIP_CS, + .mode = SPI_MODE_0, + .max_speed_hz = 20*1000*1000, + .platform_data = &ak39_spiflash_info, + }, +}; + + +/** + * @brief: motor0 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor0_pdata = { + .gpio_phase[0] = { + .pin = AK_GPIO_37, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = AK_GPIO_38, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = AK_GPIO_39, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = AK_GPIO_40, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + .gpio_hit[0] = { + .pin = AK_GPIO_62, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = AK_GPIO_63, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + //.angular_speed = 100, /* angle/s */ + .angular_speed = 200, /* angle/s */ +}; + + +/** + * @brief: motor1 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor1_pdata = { + .gpio_phase[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + .gpio_hit[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + .angular_speed = 100, /* angle/s */ +}; + +/** + * @brief: mci2 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci2_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_PLUGIN_ALWAY, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_SDIO, + .gpio_init = ak_gpio_set, + .max_speed_hz = 25*1000*1000, + .gpio_cd = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + +/** + * @brief: mci1 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci1_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_PLUGIN_ALWAY, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_MMC_SD, + .max_speed_hz = 25*1000*1000, + .gpio_init = ak_gpio_set, + .bus_width = MCI_BUS_WIDTH_1, + .gpio_cd = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + + +struct ak_crypto_plat_data akcrypto_pdata = { + .encrypt_mode = CRYPTO_MULTI_GROUP_MODE, +}; + + +/* akwifi platform data */ +struct akwifi_platform_data akwifi_pdata = { + .gpio_init = ak_gpio_set, + .gpio_cs = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, // cs Чֵ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_on = { + .pin = AK_GPIO_50, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_off = { + .pin = AK_GPIO_50, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .power_on_delay = 2000, + .power_off_delay = 200, +}; + +struct platform_device anyka_wifi_device = { + .name = "anyka-wifi", + .id = -1, + .dev = { + .platform_data = &akwifi_pdata, + }, +}; + + +/** + * @brief: usb bus device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct akotghc_usb_platform_data akotghc_plat_data = { + .gpio_init = ak_gpio_set, + .gpio_pwr_on = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_pwr_off = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .switch_onboard = { + .pin = -1, + }, + .switch_extport = { + .pin = -1, + }, +}; + + +/** + * @brief: ethenet device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_mac_data ak39_mac_pdata = { + .gpio_init = ak_gpio_set, + .pwr_gpio = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .phy_rst_gpio = { + .pin = AK_GPIO_0, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, +}; + + +/** +* @brief ak pcm device struct + hp and spk can identify by GPIO or AD. + wo can initialize it in this struct. +* @author dengzhou +* @date 2012-07-19 +*/ +struct ak39_codec_platform_data ak39_codec_pdata = +{ + .hpdet_gpio = + { + .pin = INVALID_GPIO, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = -1, + .int_pol = -1, + }, + .spk_down_gpio = + { + .pin = AK_GPIO_82, //AK_GPIO_16, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .hpmute_gpio = + { + .pin = INVALID_GPIO, //AK_GPIO_29, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .linindet_gpio = + { + .pin = -1, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, /* linein detect level */ + .int_pol = -1, + }, + + .hp_on_value = AK_GPIO_OUT_LOW, + .hpdet_irq = -1, + .linindet_irq = -1, + .bIsHPmuteUsed = 0, + .hp_mute_enable_value = AK_GPIO_OUT_HIGH, + .bIsMetalfixed = 0, + .boutput_only = 1, + .detect_flag = AKPCM_MIC_ALWAY, +}; + +struct resource ak39_codec_resources[] = { + [0] = { + .start = 0x08000000, + .end = 0x0800FFFF, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_AnalogCtrlRegs", + }, + [1] = { + .start = 0x20110000, + .end = 0x2011800F, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_ADC2ModeCfgRegs", + }, +}; + + +struct platform_device ak39_codec_device = { + .name = "ak39-codec", + .id = -1, + .resource = ak39_codec_resources, + .num_resources = ARRAY_SIZE(ak39_codec_resources), + .dev = { + .platform_data = &ak39_codec_pdata, + }, +}; + + +/** + * @brief: sensor device platform data, this info is fake. + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct i2c_board_info ak_camara_devices[] = { + { + I2C_BOARD_INFO("aksensor", 0x1), + }, +}; + +static struct aksensor_camera_info ak_soc_camera_info = { + .buswidth = SOCAM_DATAWIDTH_8, + .pin_pwdn = AK_GPIO_33,//AK_GPIO_0, //initialize GPIO for the power of camera. + .pin_reset = AK_GPIO_5, + .link = { + .bus_id = 39, + .power = NULL, + .board_info = &ak_camara_devices[0], + .i2c_adapter_id = 0, + .priv = &ak_soc_camera_info, + } +}; + +/* fake device for soc_camera subsystem */ +static struct platform_device soc_camera_interface = { + .name = "soc-camera-pdrv", + .id = -1, + .dev = { + .platform_data = &ak_soc_camera_info.link, + } +}; + + +/** + * @brief: LED platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_led_data leds[] = { + { + .name = "system_state_led", + .def_trigger = "none", + .effective_level = AK_GPIO_OUT_HIGH, //LEDƿЧƽ + .gpio = { + .pin = AK_GPIO_81, + .pulldown = -1, + .pullup = AK_PULLUP_ENABLE, + .value = AK_GPIO_OUT_HIGH, //ϵͳLEDܽĬϵƽ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + } + }, +}; + +static struct ak_led_pdata led_pdata = { + .leds = leds, + .nr_led = ARRAY_SIZE(leds), +}; + +#if 0 +/* unused GPIO number for the machine board is left*/ +static unsigned int ak39_custom_gpiopin[] = { + //AK_GPIO_5, + //AK_GPIO_61, +}; + +static struct custom_gpio_data ak39_custom_gpios= { + .gpiopin = ak39_custom_gpiopin, + .ngpiopin = ARRAY_SIZE(ak39_custom_gpiopin), +}; + +static struct platform_device ak39_custom_gpio = { + .name = "akgpio", + .id = -1, + .dev = { + .platform_data = &ak39_custom_gpios, + }, +}; +#endif + +/** + * @brief: camera platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_camera_pdata ak39_camera_info = { + .mclk = 24, + .flags = 0, + //.interface = MIPI_INTERFACE, +}; + +static struct user_gpio_info user_gpios[] = { + + { + .name = "gpio-ircut_a", + .info = { + .pin = AK_GPIO_42, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, /* in daytime level */ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "gpio-ircut_b", + .info = { + .pin = AK_GPIO_41, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, /* in daytime level */ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, +}; + +static struct ak_user_gpio_pdata user_gpio_pdata = { + .user_gpios = user_gpios, + .nr_user_gpios = ARRAY_SIZE(user_gpios), +}; + +static struct platform_device user_gpio = { + .name = "user_gpio", + .id = -1, + .dev = { + .platform_data = &user_gpio_pdata, + }, +}; + +/** + * @brief: GPIO buttons platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +/*deamon process need to open the gpio_key device. +** If fail,deamon process will exit and lead to +** watchdog rebooting the system. +*/ +static struct gpio_keys_button gpio_keys_button[] = { + { + .code = KEY_0, + .type = EV_KEY, + .gpio = AK_GPIO_81, //AK_GPIO_5(CAM_RESET), /*To make sure gpio_key device exist. */ + .active_low = 1, + .wakeup = 1, + .debounce_interval = 100, /* ms */ + .desc = "boot0", + .pullup = AK_PULLUP_ENABLE, + .pulldown = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = AK_GPIO_INT_LOWLEVEL, + }, +}; + +static struct akgpio_keys_platform_data gpio_keys_platform_data = { + .buttons = gpio_keys_button, + .nbuttons = ARRAY_SIZE(gpio_keys_button), + .rep = 0, +}; + +/** + * @brief: power manage gpios info + * + * @author: ye_guohong + * @date: 2014-12-09 + */ +static struct ak_wakeup_gpio pm_gpios[] = { + { + .pin = AK_GPIO_63, + .wakeup_pol = AK_FALLING_TRIGGERED, + }, +}; + +static struct ak_pm_pdata pm_pdata = { + .gpios = pm_gpios, + .nr_gpios = ARRAY_SIZE(pm_gpios), +}; + +#if 0 +/** +* @brief ad-key platform device struct + we should initialize the correct voltage for each key. +* @author: caolianming +* @date: 2014-01-09 +*/ +struct multi_addetect multi_det[] = { + {.unpress_min = 3220, .unpress_max = 3258, .fixkeys = NULL, .plugdev = PLUGIN_NODEV}, // = null 3251+/-20 + {.unpress_min = 3259, .unpress_max = 3300, .fixkeys = NULL, .plugdev = PLUGIN_AC}, // = sddet 3261(+/-30) + {.unpress_min = 2230, .unpress_max = 2290, .fixkeys = NULL, .plugdev = PLUGIN_MMC}, // = sddet 2262(+/-30) + {.unpress_min = 1700, .unpress_max = 1780, .fixkeys = NULL, .plugdev = PLUGIN_MMC_AC}, // = sddet 1742(+/-30) +}; + +struct analog_gpio_key ak39_adkey_data = { + .desc = "adkey", + .interval = 300, /* ms */ + .debounce_interval = 20, /* ms */ + .addet = multi_det, + .naddet = ARRAY_SIZE(multi_det), + .nkey = 0, + .wakeup = 1, /* enable ad-key wakeup from standby mode */ +}; + +static struct platform_device ak39_adkey_device = { + .name = "ad-keys", + .id = -1, + .dev = { + .platform_data = &ak39_adkey_data, + } +}; +#endif + + +#if 0 +/** + * @brif: ak39 battery mach info + + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_bat_mach_info ak39_bat_info = { + .gpio_init = ak_gpio_set, + .usb_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .ac_gpio = { + .is_detect_mode = BAT_CHARGE_ADC_DETECT, + .active = -1, + .irq = -1, + .delay = 500, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pulldown = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .full_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .bat_mach_info = { + .voltage_sample = 6, // the sample of read voltage + .power_on_voltage = 3650, // discharge power on voltage limit + .power_on_correct = 64, // mv + .charge_min_voltage = 3550, // charge minute voltage (mv) + .max_voltage = 4200, // max battery voltage + .min_voltage = 3500, // min battery voltage + .power_off = poweroff_disable, + .full_capacity = 100, // battery full + .poweroff_cap = 0, // user read value to power off + .low_cap = 5, // user read value to low power warring + .recover_cap = 30, + .cpower_on_voltage = 3700, // charge power on voltage limit + .full_delay = 30, // unit is minute + .full_voltage = 4100, + }, + + .bat_adc = { + .up_resistance = 10, + .dw_resistance = 10, + .voltage_correct = 32, // battery correct factor + .adc_avdd = 3300, // avdd voltage + }, + +}; +#endif + + +#if defined(CONFIG_I2C_GPIO_SOFT) +struct i2c_gpio_platform_data ak39_i2c_data={ + .sda_pin = AK_GPIO_28, + .scl_pin = AK_GPIO_27, + .udelay = 30, + .timeout = 300, +}; + +struct platform_device ak39_i2c_device = { + .name = "i2c-gpio", + .id = -1, + .dev = { + .platform_data = &ak39_i2c_data, + }, +}; +#endif + + /* @brief: ak39 platform devices table + * + * @author: caolianming + * @date: 2014-01-09 + */ + static struct platform_device *ak3918_platform_devices[] __initdata = { + &akfha_char_device, + &ak39_uart0_device, + //&ak39_motor0_device, + //&ak39_motor1_device, + &ak39_spi1_device, + //&ak39_mci1_device, + &ak39_i2c_device, + //&ak39_custom_gpio, + //&ak39_usb_udc_device, + //&ak39_usb_otg_hcd_device, +// &anyka_wifi_device, + &soc_camera_interface, + &ak39_camera_interface, + &ak39_ion_device, + //&ak39_pcm_device, + &ak39_codec_device, + &ak39_mmx_device, + &ak39_mac_device, + //&ak39_led_pdev, + &ak39_gpio_keys_device, + //&ak39_adkey_device, + //&ak39_battery_power, + &ak39_rtc_device, + &ak39_crypto_device, + //&ak39_pm_device, + &user_gpio, +}; + +void wdt_enable(void); +void wdt_keepalive(unsigned int heartbeat); + +/** + * @brief: restart by "reboot" cmd + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void ak39_restart(char str, const char *cmd) +{ + //ak39_reboot_sys_by_soft(); +#if defined CONFIG_AK39_WATCHDOG || defined CONFIG_AK39_WATCHDOG_TOP + wdt_enable(); + wdt_keepalive(2); +#endif +} + + +/** + * @brief: initial ak3918 machine + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void __init ak3918_init_machine(void) +{ + adc1_init(); + + spi_register_board_info(ak39_spi_board_dev, ARRAY_SIZE(ak39_spi_board_dev)); + + ak39_spi1_device.dev.platform_data = &ak39_spi1_info; + + //ak39_motor0_device.dev.platform_data = &ak39_motor0_pdata; + //ak39_motor1_device.dev.platform_data = &ak39_motor1_pdata; + + ak39_mci1_device.dev.platform_data = &mci1_plat_data; + + ak39_crypto_device.dev.platform_data = &akcrypto_pdata; + + ak39_usb_otg_hcd_device.dev.platform_data = &akotghc_plat_data; + ak39_mac_device.dev.platform_data = &ak39_mac_pdata; + + ak39_led_pdev.dev.platform_data = &led_pdata; + ak39_gpio_keys_device.dev.platform_data = &gpio_keys_platform_data; + //ak39_battery_power.dev.platform_data = &ak39_bat_info; + + ak39_camera_interface.dev.platform_data = &ak39_camera_info; + + ak39_pm_device.dev.platform_data = &pm_pdata; + + + platform_add_devices(ak3918_platform_devices, + ARRAY_SIZE(ak3918_platform_devices)); + + l2_init(); + + return; +} + + +MACHINE_START(AK39XX, "Cloud39EV3_AK3918E80PIN_MNBD") +/* Maintainer: */ + .atag_offset = 0x100, + .fixup = NULL, + .map_io = ak39_map_io, + .reserve = NULL, + .init_irq = ak39_init_irq, + .init_machine = ak3918_init_machine, + .init_early = NULL, + .timer = &ak39_timer, + .restart = ak39_restart, + +MACHINE_END + + diff --git a/arch/arm/mach-ak39/mach-cloud39ev3_ak3919ev300_sq38_sc2232_V1.0.1.c b/arch/arm/mach-ak39/mach-cloud39ev3_ak3919ev300_sq38_sc2232_V1.0.1.c new file mode 100644 index 00000000..e374ed2f --- /dev/null +++ b/arch/arm/mach-ak39/mach-cloud39ev3_ak3919ev300_sq38_sc2232_V1.0.1.c @@ -0,0 +1,901 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "cpu.h" +#include "irq.h" +#include +#include +#include +#include +#include + +/** + * @brief: spi device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +#define SPI_ONCHIP_CS (0) /*means not need gpio*/ +static unsigned long ak39_spidev_cs[AKSPI_CS_NUM] = { + [AKSPI_ONCHIP_CS] = SPI_ONCHIP_CS, /*gpio 25, spidev0: ak-spiflash*/ +}; + +struct ak_spi_info ak39_spi1_info = { + .pin_cs = ak39_spidev_cs, + .num_cs = ARRAY_SIZE(ak39_spidev_cs), + .bus_num = AKSPI_BUS_NUM1, + .clk_name = "spi1", + .mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH, + .xfer_mode = AKSPI_XFER_MODE_DMA, +}; + +/** + * @brief: spiflash device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct flash_platform_data ak39_spiflash_info= { + .bus_width = FLASH_BUS_WIDTH_4WIRE | FLASH_BUS_WIDTH_2WIRE | FLASH_BUS_WIDTH_1WIRE, + .type = NULL, +}; + +/** + * @brief: spi bus device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct spi_board_info ak39_spi_board_dev[] = { + { + .modalias = "ak-spiflash", + .bus_num = AKSPI_BUS_NUM1, + .chip_select = AKSPI_ONCHIP_CS, + .mode = SPI_MODE_0, + .max_speed_hz = 20*1000*1000, + .platform_data = &ak39_spiflash_info, + }, +}; + + +/** + * @brief: motor0 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor0_pdata = { + .gpio_phase[0] = { + .pin = AK_GPIO_37, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = AK_GPIO_38, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = AK_GPIO_39, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = AK_GPIO_40, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + .gpio_hit[0] = { + .pin = AK_GPIO_62, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = AK_GPIO_63, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + //.angular_speed = 100, /* angle/s */ + .angular_speed = 200, /* angle/s */ +}; + + +/** + * @brief: motor1 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor1_pdata = { + .gpio_phase[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + .gpio_hit[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + .angular_speed = 100, /* angle/s */ +}; + +/** + * @brief: mci2 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci2_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_PLUGIN_ALWAY, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_SDIO, + .gpio_init = ak_gpio_set, + .max_speed_hz = 25*1000*1000, + .gpio_cd = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + +/** + * @brief: mci1 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci1_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_PLUGIN_ALWAY, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_MMC_SD, + .max_speed_hz = 25*1000*1000, + .gpio_init = ak_gpio_set, + .bus_width = MCI_BUS_WIDTH_1, + .gpio_cd = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + + +struct ak_crypto_plat_data akcrypto_pdata = { + .encrypt_mode = CRYPTO_MULTI_GROUP_MODE, +}; + + +/* akwifi platform data */ +struct akwifi_platform_data akwifi_pdata = { + .gpio_init = ak_gpio_set, + .gpio_cs = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, // cs Чֵ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_on = { + .pin = AK_GPIO_50, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_off = { + .pin = AK_GPIO_50, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .power_on_delay = 2000, + .power_off_delay = 200, +}; + +struct platform_device anyka_wifi_device = { + .name = "anyka-wifi", + .id = -1, + .dev = { + .platform_data = &akwifi_pdata, + }, +}; + + +/** + * @brief: usb bus device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct akotghc_usb_platform_data akotghc_plat_data = { + .gpio_init = ak_gpio_set, + .gpio_pwr_on = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_pwr_off = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .switch_onboard = { + .pin = -1, + }, + .switch_extport = { + .pin = -1, + }, +}; + + +/** + * @brief: ethenet device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_mac_data ak39_mac_pdata = { + .gpio_init = ak_gpio_set, + .pwr_gpio = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .phy_rst_gpio = { + .pin = AK_GPIO_0, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, +}; + + +/** +* @brief ak pcm device struct + hp and spk can identify by GPIO or AD. + wo can initialize it in this struct. +* @author dengzhou +* @date 2012-07-19 +*/ +struct ak39_codec_platform_data ak39_codec_pdata = +{ + .hpdet_gpio = + { + .pin = INVALID_GPIO, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = -1, + .int_pol = -1, + }, + .spk_down_gpio = + { + .pin = AK_GPIO_82, //AK_GPIO_16, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .hpmute_gpio = + { + .pin = INVALID_GPIO, //AK_GPIO_29, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .linindet_gpio = + { + .pin = -1, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, /* linein detect level */ + .int_pol = -1, + }, + + .hp_on_value = AK_GPIO_OUT_LOW, + .hpdet_irq = -1, + .linindet_irq = -1, + .bIsHPmuteUsed = 0, + .hp_mute_enable_value = AK_GPIO_OUT_HIGH, + .bIsMetalfixed = 0, + .boutput_only = 1, + .detect_flag = AKPCM_MIC_ALWAY, +}; + +struct resource ak39_codec_resources[] = { + [0] = { + .start = 0x08000000, + .end = 0x0800FFFF, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_AnalogCtrlRegs", + }, + [1] = { + .start = 0x20110000, + .end = 0x2011800F, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_ADC2ModeCfgRegs", + }, +}; + + +struct platform_device ak39_codec_device = { + .name = "ak39-codec", + .id = -1, + .resource = ak39_codec_resources, + .num_resources = ARRAY_SIZE(ak39_codec_resources), + .dev = { + .platform_data = &ak39_codec_pdata, + }, +}; + + +/** + * @brief: sensor device platform data, this info is fake. + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct i2c_board_info ak_camara_devices[] = { + { + I2C_BOARD_INFO("aksensor", 0x1), + }, +}; + +static struct aksensor_camera_info ak_soc_camera_info = { + .buswidth = SOCAM_DATAWIDTH_8, + .pin_pwdn = INVALID_GPIO,//AK_GPIO_0, //initialize GPIO for the power of camera. + .pin_reset = AK_GPIO_33, + .link = { + .bus_id = 39, + .power = NULL, + .board_info = &ak_camara_devices[0], + .i2c_adapter_id = 0, + .priv = &ak_soc_camera_info, + } +}; + +/* fake device for soc_camera subsystem */ +static struct platform_device soc_camera_interface = { + .name = "soc-camera-pdrv", + .id = -1, + .dev = { + .platform_data = &ak_soc_camera_info.link, + } +}; + + +/** + * @brief: LED platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_led_data leds[] = { + { + .name = "system_state_led", + .def_trigger = "none", + .effective_level = AK_GPIO_OUT_HIGH, //LEDƿЧƽ + .gpio = { + .pin = AK_GPIO_81, + .pulldown = -1, + .pullup = AK_PULLUP_ENABLE, + .value = AK_GPIO_OUT_HIGH, //ϵͳLEDܽĬϵƽ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + } + }, +}; + +static struct ak_led_pdata led_pdata = { + .leds = leds, + .nr_led = ARRAY_SIZE(leds), +}; + +#if 0 +/* unused GPIO number for the machine board is left*/ +static unsigned int ak39_custom_gpiopin[] = { + //AK_GPIO_5, + //AK_GPIO_61, +}; + +static struct custom_gpio_data ak39_custom_gpios= { + .gpiopin = ak39_custom_gpiopin, + .ngpiopin = ARRAY_SIZE(ak39_custom_gpiopin), +}; + +static struct platform_device ak39_custom_gpio = { + .name = "akgpio", + .id = -1, + .dev = { + .platform_data = &ak39_custom_gpios, + }, +}; +#endif + +/** + * @brief: camera platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_camera_pdata ak39_camera_info = { + .mclk = 24, + .flags = 0, + //.interface = MIPI_INTERFACE, +}; + +static struct user_gpio_info user_gpios[] = { + + { + .name = "gpio-ircut_a", + .info = { + .pin = AK_GPIO_42, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, /* in daytime level */ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "gpio-ircut_b", + .info = { + .pin = AK_GPIO_41, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, /* in daytime level */ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, +}; + +static struct ak_user_gpio_pdata user_gpio_pdata = { + .user_gpios = user_gpios, + .nr_user_gpios = ARRAY_SIZE(user_gpios), +}; + +static struct platform_device user_gpio = { + .name = "user_gpio", + .id = -1, + .dev = { + .platform_data = &user_gpio_pdata, + }, +}; + +/** + * @brief: GPIO buttons platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +/*deamon process need to open the gpio_key device. +** If fail,deamon process will exit and lead to +** watchdog rebooting the system. +*/ +static struct gpio_keys_button gpio_keys_button[] = { + { + .code = KEY_0, + .type = EV_KEY, + .gpio = AK_GPIO_81, //AK_GPIO_5(CAM_RESET), /*To make sure gpio_key device exist. */ + .active_low = 1, + .wakeup = 1, + .debounce_interval = 100, /* ms */ + .desc = "boot0", + .pullup = AK_PULLUP_ENABLE, + .pulldown = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = AK_GPIO_INT_LOWLEVEL, + }, +}; + +static struct akgpio_keys_platform_data gpio_keys_platform_data = { + .buttons = gpio_keys_button, + .nbuttons = ARRAY_SIZE(gpio_keys_button), + .rep = 0, +}; + +/** + * @brief: power manage gpios info + * + * @author: ye_guohong + * @date: 2014-12-09 + */ +static struct ak_wakeup_gpio pm_gpios[] = { + { + .pin = AK_GPIO_63, + .wakeup_pol = AK_FALLING_TRIGGERED, + }, +}; + +static struct ak_pm_pdata pm_pdata = { + .gpios = pm_gpios, + .nr_gpios = ARRAY_SIZE(pm_gpios), +}; + +#if 0 +/** +* @brief ad-key platform device struct + we should initialize the correct voltage for each key. +* @author: caolianming +* @date: 2014-01-09 +*/ +struct multi_addetect multi_det[] = { + {.unpress_min = 3220, .unpress_max = 3258, .fixkeys = NULL, .plugdev = PLUGIN_NODEV}, // = null 3251+/-20 + {.unpress_min = 3259, .unpress_max = 3300, .fixkeys = NULL, .plugdev = PLUGIN_AC}, // = sddet 3261(+/-30) + {.unpress_min = 2230, .unpress_max = 2290, .fixkeys = NULL, .plugdev = PLUGIN_MMC}, // = sddet 2262(+/-30) + {.unpress_min = 1700, .unpress_max = 1780, .fixkeys = NULL, .plugdev = PLUGIN_MMC_AC}, // = sddet 1742(+/-30) +}; + +struct analog_gpio_key ak39_adkey_data = { + .desc = "adkey", + .interval = 300, /* ms */ + .debounce_interval = 20, /* ms */ + .addet = multi_det, + .naddet = ARRAY_SIZE(multi_det), + .nkey = 0, + .wakeup = 1, /* enable ad-key wakeup from standby mode */ +}; + +static struct platform_device ak39_adkey_device = { + .name = "ad-keys", + .id = -1, + .dev = { + .platform_data = &ak39_adkey_data, + } +}; +#endif + + +#if 0 +/** + * @brif: ak39 battery mach info + + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_bat_mach_info ak39_bat_info = { + .gpio_init = ak_gpio_set, + .usb_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .ac_gpio = { + .is_detect_mode = BAT_CHARGE_ADC_DETECT, + .active = -1, + .irq = -1, + .delay = 500, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pulldown = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .full_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .bat_mach_info = { + .voltage_sample = 6, // the sample of read voltage + .power_on_voltage = 3650, // discharge power on voltage limit + .power_on_correct = 64, // mv + .charge_min_voltage = 3550, // charge minute voltage (mv) + .max_voltage = 4200, // max battery voltage + .min_voltage = 3500, // min battery voltage + .power_off = poweroff_disable, + .full_capacity = 100, // battery full + .poweroff_cap = 0, // user read value to power off + .low_cap = 5, // user read value to low power warring + .recover_cap = 30, + .cpower_on_voltage = 3700, // charge power on voltage limit + .full_delay = 30, // unit is minute + .full_voltage = 4100, + }, + + .bat_adc = { + .up_resistance = 10, + .dw_resistance = 10, + .voltage_correct = 32, // battery correct factor + .adc_avdd = 3300, // avdd voltage + }, + +}; +#endif + + +#if defined(CONFIG_I2C_GPIO_SOFT) +struct i2c_gpio_platform_data ak39_i2c_data={ + .sda_pin = AK_GPIO_28, + .scl_pin = AK_GPIO_27, + .udelay = 30, + .timeout = 300, +}; + +struct platform_device ak39_i2c_device = { + .name = "i2c-gpio", + .id = -1, + .dev = { + .platform_data = &ak39_i2c_data, + }, +}; +#endif + + /* @brief: ak39 platform devices table + * + * @author: caolianming + * @date: 2014-01-09 + */ + static struct platform_device *ak3918_platform_devices[] __initdata = { + &akfha_char_device, + &ak39_uart0_device, + //&ak39_motor0_device, + //&ak39_motor1_device, + &ak39_spi1_device, + //&ak39_mci1_device, + &ak39_i2c_device, + //&ak39_custom_gpio, + //&ak39_usb_udc_device, + //&ak39_usb_otg_hcd_device, +// &anyka_wifi_device, + &soc_camera_interface, + &ak39_camera_interface, + &ak39_ion_device, + //&ak39_pcm_device, + &ak39_codec_device, + &ak39_mmx_device, + &ak39_mac_device, + //&ak39_led_pdev, + &ak39_gpio_keys_device, + //&ak39_adkey_device, + //&ak39_battery_power, + &ak39_rtc_device, + &ak39_crypto_device, + //&ak39_pm_device, + &user_gpio, +}; + +void wdt_enable(void); +void wdt_keepalive(unsigned int heartbeat); + +/** + * @brief: restart by "reboot" cmd + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void ak39_restart(char str, const char *cmd) +{ + //ak39_reboot_sys_by_soft(); +#if defined CONFIG_AK39_WATCHDOG || defined CONFIG_AK39_WATCHDOG_TOP + wdt_enable(); + wdt_keepalive(2); +#endif +} + + +/** + * @brief: initial ak3918 machine + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void __init ak3918_init_machine(void) +{ + adc1_init(); + + spi_register_board_info(ak39_spi_board_dev, ARRAY_SIZE(ak39_spi_board_dev)); + + ak39_spi1_device.dev.platform_data = &ak39_spi1_info; + + //ak39_motor0_device.dev.platform_data = &ak39_motor0_pdata; + //ak39_motor1_device.dev.platform_data = &ak39_motor1_pdata; + + ak39_mci1_device.dev.platform_data = &mci1_plat_data; + + ak39_crypto_device.dev.platform_data = &akcrypto_pdata; + + ak39_usb_otg_hcd_device.dev.platform_data = &akotghc_plat_data; + ak39_mac_device.dev.platform_data = &ak39_mac_pdata; + + ak39_led_pdev.dev.platform_data = &led_pdata; + ak39_gpio_keys_device.dev.platform_data = &gpio_keys_platform_data; + //ak39_battery_power.dev.platform_data = &ak39_bat_info; + + ak39_camera_interface.dev.platform_data = &ak39_camera_info; + + ak39_pm_device.dev.platform_data = &pm_pdata; + + + platform_add_devices(ak3918_platform_devices, + ARRAY_SIZE(ak3918_platform_devices)); + + l2_init(); + + return; +} + + +MACHINE_START(AK39XX, "Cloud39EV3_AK3918E80PIN_MNBD") +/* Maintainer: */ + .atag_offset = 0x100, + .fixup = NULL, + .map_io = ak39_map_io, + .reserve = NULL, + .init_irq = ak39_init_irq, + .init_machine = ak3918_init_machine, + .init_early = NULL, + .timer = &ak39_timer, + .restart = ak39_restart, + +MACHINE_END + + diff --git a/arch/arm/mach-ak39/mach-cloud39ev3_co38_sc2235.c b/arch/arm/mach-ak39/mach-cloud39ev3_co38_sc2235.c new file mode 100644 index 00000000..e387080d --- /dev/null +++ b/arch/arm/mach-ak39/mach-cloud39ev3_co38_sc2235.c @@ -0,0 +1,901 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "cpu.h" +#include "irq.h" +#include +#include +#include +#include +#include + +/** + * @brief: spi device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +#define SPI_ONCHIP_CS (0) /*means not need gpio*/ +static unsigned long ak39_spidev_cs[AKSPI_CS_NUM] = { + [AKSPI_ONCHIP_CS] = SPI_ONCHIP_CS, /*gpio 25, spidev0: ak-spiflash*/ +}; + +struct ak_spi_info ak39_spi1_info = { + .pin_cs = ak39_spidev_cs, + .num_cs = ARRAY_SIZE(ak39_spidev_cs), + .bus_num = AKSPI_BUS_NUM1, + .clk_name = "spi1", + .mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH, + .xfer_mode = AKSPI_XFER_MODE_DMA, +}; + +/** + * @brief: spiflash device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct flash_platform_data ak39_spiflash_info= { + .bus_width = FLASH_BUS_WIDTH_4WIRE | FLASH_BUS_WIDTH_2WIRE | FLASH_BUS_WIDTH_1WIRE, + .type = NULL, +}; + +/** + * @brief: spi bus device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct spi_board_info ak39_spi_board_dev[] = { + { + .modalias = "ak-spiflash", + .bus_num = AKSPI_BUS_NUM1, + .chip_select = AKSPI_ONCHIP_CS, + .mode = SPI_MODE_0, + .max_speed_hz = 20*1000*1000, + .platform_data = &ak39_spiflash_info, + }, +}; + + +/** + * @brief: motor0 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor0_pdata = { + .gpio_phase[0] = { + .pin = AK_GPIO_37, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = AK_GPIO_38, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = AK_GPIO_39, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = AK_GPIO_40, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + .gpio_hit[0] = { + .pin = AK_GPIO_62, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = AK_GPIO_63, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + //.angular_speed = 100, /* angle/s */ + .angular_speed = 200, /* angle/s */ +}; + + +/** + * @brief: motor1 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor1_pdata = { + .gpio_phase[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + .gpio_hit[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + .angular_speed = 100, /* angle/s */ +}; + +/** + * @brief: mci2 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci2_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_PLUGIN_ALWAY, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_SDIO, + .gpio_init = ak_gpio_set, + .max_speed_hz = 25*1000*1000, + .gpio_cd = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + +/** + * @brief: mci1 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci1_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_PLUGIN_ALWAY, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_MMC_SD, + .max_speed_hz = 25*1000*1000, + .gpio_init = ak_gpio_set, + .bus_width = MCI_BUS_WIDTH_1, + .gpio_cd = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + + +struct ak_crypto_plat_data akcrypto_pdata = { + .encrypt_mode = CRYPTO_MULTI_GROUP_MODE, +}; + + +/* akwifi platform data */ +struct akwifi_platform_data akwifi_pdata = { + .gpio_init = ak_gpio_set, + .gpio_cs = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, // cs Чֵ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_on = { + .pin = AK_GPIO_50, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_off = { + .pin = AK_GPIO_50, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .power_on_delay = 2000, + .power_off_delay = 200, +}; + +struct platform_device anyka_wifi_device = { + .name = "anyka-wifi", + .id = -1, + .dev = { + .platform_data = &akwifi_pdata, + }, +}; + + +/** + * @brief: usb bus device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct akotghc_usb_platform_data akotghc_plat_data = { + .gpio_init = ak_gpio_set, + .gpio_pwr_on = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_pwr_off = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .switch_onboard = { + .pin = -1, + }, + .switch_extport = { + .pin = -1, + }, +}; + + +/** + * @brief: ethenet device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_mac_data ak39_mac_pdata = { + .gpio_init = ak_gpio_set, + .pwr_gpio = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .phy_rst_gpio = { + .pin = AK_GPIO_0, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, +}; + + +/** +* @brief ak pcm device struct + hp and spk can identify by GPIO or AD. + wo can initialize it in this struct. +* @author dengzhou +* @date 2012-07-19 +*/ +struct ak39_codec_platform_data ak39_codec_pdata = +{ + .hpdet_gpio = + { + .pin = INVALID_GPIO, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = -1, + .int_pol = -1, + }, + .spk_down_gpio = + { + .pin = AK_GPIO_82, //AK_GPIO_16, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .hpmute_gpio = + { + .pin = INVALID_GPIO, //AK_GPIO_29, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .linindet_gpio = + { + .pin = -1, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, /* linein detect level */ + .int_pol = -1, + }, + + .hp_on_value = AK_GPIO_OUT_LOW, + .hpdet_irq = -1, + .linindet_irq = -1, + .bIsHPmuteUsed = 0, + .hp_mute_enable_value = AK_GPIO_OUT_HIGH, + .bIsMetalfixed = 0, + .boutput_only = 1, + .detect_flag = AKPCM_MIC_ALWAY, +}; + +struct resource ak39_codec_resources[] = { + [0] = { + .start = 0x08000000, + .end = 0x0800FFFF, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_AnalogCtrlRegs", + }, + [1] = { + .start = 0x20110000, + .end = 0x2011800F, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_ADC2ModeCfgRegs", + }, +}; + + +struct platform_device ak39_codec_device = { + .name = "ak39-codec", + .id = -1, + .resource = ak39_codec_resources, + .num_resources = ARRAY_SIZE(ak39_codec_resources), + .dev = { + .platform_data = &ak39_codec_pdata, + }, +}; + + +/** + * @brief: sensor device platform data, this info is fake. + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct i2c_board_info ak_camara_devices[] = { + { + I2C_BOARD_INFO("aksensor", 0x1), + }, +}; + +static struct aksensor_camera_info ak_soc_camera_info = { + .buswidth = SOCAM_DATAWIDTH_8, + .pin_pwdn = -1,//AK_GPIO_0, //initialize GPIO for the power of camera. + .pin_reset = AK_GPIO_49, + .link = { + .bus_id = 39, + .power = NULL, + .board_info = &ak_camara_devices[0], + .i2c_adapter_id = 0, + .priv = &ak_soc_camera_info, + } +}; + +/* fake device for soc_camera subsystem */ +static struct platform_device soc_camera_interface = { + .name = "soc-camera-pdrv", + .id = -1, + .dev = { + .platform_data = &ak_soc_camera_info.link, + } +}; + + +/** + * @brief: LED platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_led_data leds[] = { + { + .name = "system_state_led", + .def_trigger = "none", + .effective_level = AK_GPIO_OUT_HIGH, //LEDƿЧƽ + .gpio = { + .pin = AK_GPIO_81, + .pulldown = -1, + .pullup = AK_PULLUP_ENABLE, + .value = AK_GPIO_OUT_HIGH, //ϵͳLEDܽĬϵƽ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + } + }, +}; + +static struct ak_led_pdata led_pdata = { + .leds = leds, + .nr_led = ARRAY_SIZE(leds), +}; + +#if 0 +/* unused GPIO number for the machine board is left*/ +static unsigned int ak39_custom_gpiopin[] = { + //AK_GPIO_5, + //AK_GPIO_61, +}; + +static struct custom_gpio_data ak39_custom_gpios= { + .gpiopin = ak39_custom_gpiopin, + .ngpiopin = ARRAY_SIZE(ak39_custom_gpiopin), +}; + +static struct platform_device ak39_custom_gpio = { + .name = "akgpio", + .id = -1, + .dev = { + .platform_data = &ak39_custom_gpios, + }, +}; +#endif + +/** + * @brief: camera platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_camera_pdata ak39_camera_info = { + .mclk = 24, + .flags = 0, + //.interface = DVP_INTERFACE, +}; + +static struct user_gpio_info user_gpios[] = { + + { + .name = "gpio-ircut_a", + .info = { + .pin = AK_GPIO_42, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, /* in daytime level */ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "gpio-ircut_b", + .info = { + .pin = AK_GPIO_41, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, /* in daytime level */ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, +}; + +static struct ak_user_gpio_pdata user_gpio_pdata = { + .user_gpios = user_gpios, + .nr_user_gpios = ARRAY_SIZE(user_gpios), +}; + +static struct platform_device user_gpio = { + .name = "user_gpio", + .id = -1, + .dev = { + .platform_data = &user_gpio_pdata, + }, +}; + +/** + * @brief: GPIO buttons platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +/*deamon process need to open the gpio_key device. +** If fail,deamon process will exit and lead to +** watchdog rebooting the system. +*/ +static struct gpio_keys_button gpio_keys_button[] = { + { + .code = KEY_0, + .type = EV_KEY, + .gpio = AK_GPIO_5, /*To make sure gpio_key device exist. */ + .active_low = 1, + .wakeup = 1, + .debounce_interval = 100, /* ms */ + .desc = "boot0", + .pullup = AK_PULLUP_ENABLE, + .pulldown = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = AK_GPIO_INT_LOWLEVEL, + }, +}; + +static struct akgpio_keys_platform_data gpio_keys_platform_data = { + .buttons = gpio_keys_button, + .nbuttons = ARRAY_SIZE(gpio_keys_button), + .rep = 0, +}; + +/** + * @brief: power manage gpios info + * + * @author: ye_guohong + * @date: 2014-12-09 + */ +static struct ak_wakeup_gpio pm_gpios[] = { + { + .pin = AK_GPIO_63, + .wakeup_pol = AK_FALLING_TRIGGERED, + }, +}; + +static struct ak_pm_pdata pm_pdata = { + .gpios = pm_gpios, + .nr_gpios = ARRAY_SIZE(pm_gpios), +}; + +#if 0 +/** +* @brief ad-key platform device struct + we should initialize the correct voltage for each key. +* @author: caolianming +* @date: 2014-01-09 +*/ +struct multi_addetect multi_det[] = { + {.unpress_min = 3220, .unpress_max = 3258, .fixkeys = NULL, .plugdev = PLUGIN_NODEV}, // = null 3251+/-20 + {.unpress_min = 3259, .unpress_max = 3300, .fixkeys = NULL, .plugdev = PLUGIN_AC}, // = sddet 3261(+/-30) + {.unpress_min = 2230, .unpress_max = 2290, .fixkeys = NULL, .plugdev = PLUGIN_MMC}, // = sddet 2262(+/-30) + {.unpress_min = 1700, .unpress_max = 1780, .fixkeys = NULL, .plugdev = PLUGIN_MMC_AC}, // = sddet 1742(+/-30) +}; + +struct analog_gpio_key ak39_adkey_data = { + .desc = "adkey", + .interval = 300, /* ms */ + .debounce_interval = 20, /* ms */ + .addet = multi_det, + .naddet = ARRAY_SIZE(multi_det), + .nkey = 0, + .wakeup = 1, /* enable ad-key wakeup from standby mode */ +}; + +static struct platform_device ak39_adkey_device = { + .name = "ad-keys", + .id = -1, + .dev = { + .platform_data = &ak39_adkey_data, + } +}; +#endif + + +#if 0 +/** + * @brif: ak39 battery mach info + + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_bat_mach_info ak39_bat_info = { + .gpio_init = ak_gpio_set, + .usb_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .ac_gpio = { + .is_detect_mode = BAT_CHARGE_ADC_DETECT, + .active = -1, + .irq = -1, + .delay = 500, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pulldown = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .full_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .bat_mach_info = { + .voltage_sample = 6, // the sample of read voltage + .power_on_voltage = 3650, // discharge power on voltage limit + .power_on_correct = 64, // mv + .charge_min_voltage = 3550, // charge minute voltage (mv) + .max_voltage = 4200, // max battery voltage + .min_voltage = 3500, // min battery voltage + .power_off = poweroff_disable, + .full_capacity = 100, // battery full + .poweroff_cap = 0, // user read value to power off + .low_cap = 5, // user read value to low power warring + .recover_cap = 30, + .cpower_on_voltage = 3700, // charge power on voltage limit + .full_delay = 30, // unit is minute + .full_voltage = 4100, + }, + + .bat_adc = { + .up_resistance = 10, + .dw_resistance = 10, + .voltage_correct = 32, // battery correct factor + .adc_avdd = 3300, // avdd voltage + }, + +}; +#endif + + +#if defined(CONFIG_I2C_GPIO_SOFT) +struct i2c_gpio_platform_data ak39_i2c_data={ + .sda_pin = AK_GPIO_28, + .scl_pin = AK_GPIO_27, + .udelay = 30, + .timeout = 300, +}; + +struct platform_device ak39_i2c_device = { + .name = "i2c-gpio", + .id = -1, + .dev = { + .platform_data = &ak39_i2c_data, + }, +}; +#endif + + /* @brief: ak39 platform devices table + * + * @author: caolianming + * @date: 2014-01-09 + */ + static struct platform_device *ak3918_platform_devices[] __initdata = { + &akfha_char_device, + &ak39_uart0_device, + //&ak39_motor0_device, + //&ak39_motor1_device, + &ak39_spi1_device, + //&ak39_mci1_device, + &ak39_i2c_device, + //&ak39_custom_gpio, + //&ak39_usb_udc_device, + //&ak39_usb_otg_hcd_device, +// &anyka_wifi_device, + &soc_camera_interface, + &ak39_camera_interface, + &ak39_ion_device, + //&ak39_pcm_device, + &ak39_codec_device, + &ak39_mmx_device, + &ak39_mac_device, + //&ak39_led_pdev, + &ak39_gpio_keys_device, + //&ak39_adkey_device, + //&ak39_battery_power, + &ak39_rtc_device, + &ak39_crypto_device, + //&ak39_pm_device, + &user_gpio, +}; + +void wdt_enable(void); +void wdt_keepalive(unsigned int heartbeat); + +/** + * @brief: restart by "reboot" cmd + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void ak39_restart(char str, const char *cmd) +{ + //ak39_reboot_sys_by_soft(); +#if defined CONFIG_AK39_WATCHDOG || defined CONFIG_AK39_WATCHDOG_TOP + wdt_enable(); + wdt_keepalive(2); +#endif +} + + +/** + * @brief: initial ak3918 machine + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void __init ak3918_init_machine(void) +{ + adc1_init(); + + spi_register_board_info(ak39_spi_board_dev, ARRAY_SIZE(ak39_spi_board_dev)); + + ak39_spi1_device.dev.platform_data = &ak39_spi1_info; + + //ak39_motor0_device.dev.platform_data = &ak39_motor0_pdata; + //ak39_motor1_device.dev.platform_data = &ak39_motor1_pdata; + + ak39_mci1_device.dev.platform_data = &mci1_plat_data; + + ak39_crypto_device.dev.platform_data = &akcrypto_pdata; + + ak39_usb_otg_hcd_device.dev.platform_data = &akotghc_plat_data; + ak39_mac_device.dev.platform_data = &ak39_mac_pdata; + + ak39_led_pdev.dev.platform_data = &led_pdata; + ak39_gpio_keys_device.dev.platform_data = &gpio_keys_platform_data; + //ak39_battery_power.dev.platform_data = &ak39_bat_info; + + ak39_camera_interface.dev.platform_data = &ak39_camera_info; + + ak39_pm_device.dev.platform_data = &pm_pdata; + + + platform_add_devices(ak3918_platform_devices, + ARRAY_SIZE(ak3918_platform_devices)); + + l2_init(); + + return; +} + + +MACHINE_START(AK39XX, "Cloud39EV3_AK3918E80PIN_MNBD") +/* Maintainer: */ + .atag_offset = 0x100, + .fixup = NULL, + .map_io = ak39_map_io, + .reserve = NULL, + .init_irq = ak39_init_irq, + .init_machine = ak3918_init_machine, + .init_early = NULL, + .timer = &ak39_timer, + .restart = ak39_restart, + +MACHINE_END + + diff --git a/arch/arm/mach-ak39/mach-cloud39ev3_sq38x4_mnbd.c b/arch/arm/mach-ak39/mach-cloud39ev3_sq38x4_mnbd.c new file mode 100644 index 00000000..9b1a7134 --- /dev/null +++ b/arch/arm/mach-ak39/mach-cloud39ev3_sq38x4_mnbd.c @@ -0,0 +1,896 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "cpu.h" +#include "irq.h" +#include +#include +#include +#include +#include + +/** + * @brief: spi device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +#define SPI_ONCHIP_CS (0) /*means not need gpio*/ +static unsigned long ak39_spidev_cs[AKSPI_CS_NUM] = { + [AKSPI_ONCHIP_CS] = SPI_ONCHIP_CS, /*gpio 25, spidev0: ak-spiflash*/ +}; + +struct ak_spi_info ak39_spi1_info = { + .pin_cs = ak39_spidev_cs, + .num_cs = ARRAY_SIZE(ak39_spidev_cs), + .bus_num = AKSPI_BUS_NUM1, + .clk_name = "spi1", + .mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH, + .xfer_mode = AKSPI_XFER_MODE_DMA, +}; + +/** + * @brief: spiflash device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct flash_platform_data ak39_spiflash_info= { + .bus_width = FLASH_BUS_WIDTH_4WIRE | FLASH_BUS_WIDTH_2WIRE | FLASH_BUS_WIDTH_1WIRE, + .type = NULL, +}; + +/** + * @brief: spi bus device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct spi_board_info ak39_spi_board_dev[] = { + { + .modalias = "ak-spiflash", + .bus_num = AKSPI_BUS_NUM1, + .chip_select = AKSPI_ONCHIP_CS, + .mode = SPI_MODE_0, + .max_speed_hz = 20*1000*1000, + .platform_data = &ak39_spiflash_info, + }, +}; + + +/** + * @brief: motor0 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor0_pdata = { + .gpio_phase[0] = { + .pin = AK_GPIO_37, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = AK_GPIO_38, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = AK_GPIO_39, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = AK_GPIO_40, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + .gpio_hit[0] = { + .pin = AK_GPIO_62, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = AK_GPIO_63, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + //.angular_speed = 100, /* angle/s */ + .angular_speed = 200, /* angle/s */ +}; + + +/** + * @brief: motor1 device info + * + * @author: lixinhai + * @date: 2014-01-09 + */ +static struct ak_motor_plat_data ak39_motor1_pdata = { + .gpio_phase[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[1] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[2] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_phase[3] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + + .gpio_hit[0] = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_hit[1] ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, + .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, + + .angular_speed = 100, /* angle/s */ +}; + +/** + * @brief: mci2 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci2_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_PLUGIN_ALWAY, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_SDIO, + .gpio_init = ak_gpio_set, + .max_speed_hz = 25*1000*1000, + .gpio_cd = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + +/** + * @brief: mci1 device platform data + * + * @author: lixinhai + * @date: 2014-01-09 + */ +struct ak_mci_platform_data mci1_plat_data = { + .irq_cd_type = IRQ_TYPE_LEVEL_LOW, + .detect_mode = AKMCI_PLUGIN_ALWAY, + .xfer_mode = AKMCI_XFER_L2DMA, + .mci_mode = MCI_MODE_MMC_SD, + .max_speed_hz = 25*1000*1000, + .gpio_init = ak_gpio_set, + .bus_width = MCI_BUS_WIDTH_1, + .gpio_cd = { + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + .gpio_wp = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = AK_PULLUP_ENABLE, + .value = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + } +}; + + +struct ak_crypto_plat_data akcrypto_pdata = { + .encrypt_mode = CRYPTO_MULTI_GROUP_MODE, +}; + + +/* akwifi platform data */ +struct akwifi_platform_data akwifi_pdata = { + .gpio_init = ak_gpio_set, + .gpio_cs = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, // cs Чֵ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_on = { + .pin = -1, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_off = { + .pin = -1, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .power_on_delay = 2000, + .power_off_delay = 200, +}; + +struct platform_device anyka_wifi_device = { + .name = "anyka-wifi", + .id = -1, + .dev = { + .platform_data = &akwifi_pdata, + }, +}; + + +/** + * @brief: usb bus device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct akotghc_usb_platform_data akotghc_plat_data = { + .gpio_init = ak_gpio_set, + .gpio_pwr_on = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .gpio_pwr_off = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .switch_onboard = { + .pin = -1, + }, + .switch_extport = { + .pin = -1, + }, +}; + + +/** + * @brief: ethenet device platform data + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_mac_data ak39_mac_pdata = { + .gpio_init = ak_gpio_set, + .pwr_gpio = { + .pin = -1, + .pulldown = AK_PULLDOWN_DISABLE, + .pullup = -1, + .value = AK_GPIO_OUT_HIGH, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + .phy_rst_gpio = { + .pin = AK_GPIO_0, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, +}; + + +/** +* @brief ak pcm device struct + hp and spk can identify by GPIO or AD. + wo can initialize it in this struct. +* @author dengzhou +* @date 2012-07-19 +*/ +struct ak39_codec_platform_data ak39_codec_pdata = +{ + .hpdet_gpio = + { + .pin = INVALID_GPIO, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = -1, + .int_pol = -1, + }, + .spk_down_gpio = + { + .pin = AK_GPIO_82, //AK_GPIO_16, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .hpmute_gpio = + { + .pin = INVALID_GPIO, //AK_GPIO_29, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = -1, + .pulldown = -1, + + .value = AK_GPIO_OUT_LOW, + .int_pol = -1, + }, + .linindet_gpio = + { + .pin = -1, + .dir = AK_GPIO_DIR_INPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = AK_GPIO_OUT_LOW, /* linein detect level */ + .int_pol = -1, + }, + + .hp_on_value = AK_GPIO_OUT_LOW, + .hpdet_irq = -1, + .linindet_irq = -1, + .bIsHPmuteUsed = 0, + .hp_mute_enable_value = AK_GPIO_OUT_HIGH, + .bIsMetalfixed = 0, + .boutput_only = 1, + .detect_flag = AKPCM_MIC_ALWAY, +}; + +struct resource ak39_codec_resources[] = { + [0] = { + .start = 0x08000000, + .end = 0x0800FFFF, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_AnalogCtrlRegs", + }, + [1] = { + .start = 0x20110000, + .end = 0x2011800F, + .flags = (int)IORESOURCE_MEM, + .name = "akpcm_ADC2ModeCfgRegs", + }, +}; + + +struct platform_device ak39_codec_device = { + .name = "ak39-codec", + .id = -1, + .resource = ak39_codec_resources, + .num_resources = ARRAY_SIZE(ak39_codec_resources), + .dev = { + .platform_data = &ak39_codec_pdata, + }, +}; + + +/** + * @brief: sensor device platform data, this info is fake. + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct i2c_board_info ak_camara_devices[] = { + { + I2C_BOARD_INFO("aksensor", 0x1), + }, +}; + +static struct aksensor_camera_info ak_soc_camera_info = { + .buswidth = SOCAM_DATAWIDTH_8, + .pin_pwdn = -1,//AK_GPIO_0, //initialize GPIO for the power of camera. + .pin_reset = AK_GPIO_49, + .link = { + .bus_id = 39, + .power = NULL, + .board_info = &ak_camara_devices[0], + .i2c_adapter_id = 0, + .priv = &ak_soc_camera_info, + } +}; + +/* fake device for soc_camera subsystem */ +static struct platform_device soc_camera_interface = { + .name = "soc-camera-pdrv", + .id = -1, + .dev = { + .platform_data = &ak_soc_camera_info.link, + } +}; + + +/** + * @brief: LED platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_led_data leds[] = { + { + .name = "system_state_led", + .def_trigger = "none", + .effective_level = AK_GPIO_OUT_HIGH, //LEDƿЧƽ + .gpio = { + .pin = AK_GPIO_81, + .pulldown = -1, + .pullup = AK_PULLUP_ENABLE, + .value = AK_GPIO_OUT_HIGH, //ϵͳLEDܽĬϵƽ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + } + }, +}; + +static struct ak_led_pdata led_pdata = { + .leds = leds, + .nr_led = ARRAY_SIZE(leds), +}; + +#if 0 +/* unused GPIO number for the machine board is left*/ +static unsigned int ak39_custom_gpiopin[] = { + //AK_GPIO_5, + //AK_GPIO_61, +}; + +static struct custom_gpio_data ak39_custom_gpios= { + .gpiopin = ak39_custom_gpiopin, + .ngpiopin = ARRAY_SIZE(ak39_custom_gpiopin), +}; + +static struct platform_device ak39_custom_gpio = { + .name = "akgpio", + .id = -1, + .dev = { + .platform_data = &ak39_custom_gpios, + }, +}; +#endif + +/** + * @brief: camera platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_camera_pdata ak39_camera_info = { + .mclk = 24, + .flags = 0, + //.interface = DVP_INTERFACE, +}; + +static struct user_gpio_info user_gpios[] = { + { + .name = "gpio-ircut_a", + .info = { + .pin = AK_GPIO_42, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, /* in daytime level */ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, + { + .name = "gpio-ircut_b", + .info = { + .pin = AK_GPIO_41, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_OUT_LOW, /* in daytime level */ + .dir = AK_GPIO_DIR_OUTPUT, + .int_pol = -1, + }, + }, +}; + +static struct ak_user_gpio_pdata user_gpio_pdata = { + .user_gpios = user_gpios, + .nr_user_gpios = ARRAY_SIZE(user_gpios), +}; + +static struct platform_device user_gpio = { + .name = "user_gpio", + .id = -1, + .dev = { + .platform_data = &user_gpio_pdata, + }, +}; + +/** + * @brief: GPIO buttons platform data and initialis status + * + * @author: caolianming + * @date: 2014-01-09 + */ +static struct gpio_keys_button gpio_keys_button[] = { + { + .code = KEY_0, + .type = EV_KEY, + .gpio = AK_GPIO_43,//daemon need key + .active_low = 1, + .wakeup = 1, + .debounce_interval = 100, /* ms */ + .desc = "boot0", + .pullup = AK_PULLUP_ENABLE, + .pulldown = -1, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = AK_GPIO_INT_LOWLEVEL, + }, +}; + +static struct akgpio_keys_platform_data gpio_keys_platform_data = { + .buttons = gpio_keys_button, + .nbuttons = ARRAY_SIZE(gpio_keys_button), + .rep = 0, +}; + +/** + * @brief: power manage gpios info + * + * @author: ye_guohong + * @date: 2014-12-09 + */ +static struct ak_wakeup_gpio pm_gpios[] = { + { + .pin = AK_GPIO_63, + .wakeup_pol = AK_FALLING_TRIGGERED, + }, +}; + +static struct ak_pm_pdata pm_pdata = { + .gpios = pm_gpios, + .nr_gpios = ARRAY_SIZE(pm_gpios), +}; + +#if 0 +/** +* @brief ad-key platform device struct + we should initialize the correct voltage for each key. +* @author: caolianming +* @date: 2014-01-09 +*/ +struct multi_addetect multi_det[] = { + {.unpress_min = 3220, .unpress_max = 3258, .fixkeys = NULL, .plugdev = PLUGIN_NODEV}, // = null 3251+/-20 + {.unpress_min = 3259, .unpress_max = 3300, .fixkeys = NULL, .plugdev = PLUGIN_AC}, // = sddet 3261(+/-30) + {.unpress_min = 2230, .unpress_max = 2290, .fixkeys = NULL, .plugdev = PLUGIN_MMC}, // = sddet 2262(+/-30) + {.unpress_min = 1700, .unpress_max = 1780, .fixkeys = NULL, .plugdev = PLUGIN_MMC_AC}, // = sddet 1742(+/-30) +}; + +struct analog_gpio_key ak39_adkey_data = { + .desc = "adkey", + .interval = 300, /* ms */ + .debounce_interval = 20, /* ms */ + .addet = multi_det, + .naddet = ARRAY_SIZE(multi_det), + .nkey = 0, + .wakeup = 1, /* enable ad-key wakeup from standby mode */ +}; + +static struct platform_device ak39_adkey_device = { + .name = "ad-keys", + .id = -1, + .dev = { + .platform_data = &ak39_adkey_data, + } +}; +#endif + + +#if 0 +/** + * @brif: ak39 battery mach info + + * @author: caolianming + * @date: 2014-01-09 + */ +static struct ak_bat_mach_info ak39_bat_info = { + .gpio_init = ak_gpio_set, + .usb_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .ac_gpio = { + .is_detect_mode = BAT_CHARGE_ADC_DETECT, + .active = -1, + .irq = -1, + .delay = 500, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pulldown = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .full_gpio = { + .active = -1, + .irq = -ENOSYS, + .delay = 0, + .pindata ={ + .pin = -1, + .pulldown = -1, + .pullup = -1, + .value = -1, + .dir = -1, + .int_pol = -1, + }, + }, + + .bat_mach_info = { + .voltage_sample = 6, // the sample of read voltage + .power_on_voltage = 3650, // discharge power on voltage limit + .power_on_correct = 64, // mv + .charge_min_voltage = 3550, // charge minute voltage (mv) + .max_voltage = 4200, // max battery voltage + .min_voltage = 3500, // min battery voltage + .power_off = poweroff_disable, + .full_capacity = 100, // battery full + .poweroff_cap = 0, // user read value to power off + .low_cap = 5, // user read value to low power warring + .recover_cap = 30, + .cpower_on_voltage = 3700, // charge power on voltage limit + .full_delay = 30, // unit is minute + .full_voltage = 4100, + }, + + .bat_adc = { + .up_resistance = 10, + .dw_resistance = 10, + .voltage_correct = 32, // battery correct factor + .adc_avdd = 3300, // avdd voltage + }, + +}; +#endif + + +#if defined(CONFIG_I2C_GPIO_SOFT) +struct i2c_gpio_platform_data ak39_i2c_data={ + .sda_pin = AK_GPIO_28, + .scl_pin = AK_GPIO_27, + .udelay = 30, + .timeout = 300, +}; + +struct platform_device ak39_i2c_device = { + .name = "i2c-gpio", + .id = -1, + .dev = { + .platform_data = &ak39_i2c_data, + }, +}; +#endif + + /* @brief: ak39 platform devices table + * + * @author: caolianming + * @date: 2014-01-09 + */ + static struct platform_device *ak3918_platform_devices[] __initdata = { + &akfha_char_device, + &ak39_uart0_device, + //&ak39_motor0_device, + //&ak39_motor1_device, + &ak39_spi1_device, + //&ak39_mci1_device, + &ak39_i2c_device, + //&ak39_custom_gpio, + //&ak39_usb_udc_device, + //&ak39_usb_otg_hcd_device, +// &anyka_wifi_device, + &soc_camera_interface, + &ak39_camera_interface, + &ak39_ion_device, + //&ak39_pcm_device, + &ak39_codec_device, + &ak39_mmx_device, + &ak39_mac_device, + //&ak39_led_pdev, + &ak39_gpio_keys_device, + //&ak39_adkey_device, + //&ak39_battery_power, + &ak39_rtc_device, + &ak39_crypto_device, + //&ak39_pm_device, + &user_gpio, +}; + +void wdt_enable(void); +void wdt_keepalive(unsigned int heartbeat); + +/** + * @brief: restart by "reboot" cmd + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void ak39_restart(char str, const char *cmd) +{ + //ak39_reboot_sys_by_soft(); +#if defined CONFIG_AK39_WATCHDOG || defined CONFIG_AK39_WATCHDOG_TOP + wdt_enable(); + wdt_keepalive(2); +#endif +} + + +/** + * @brief: initial ak3918 machine + * + * @author: caolianming + * @date: 2014-01-09 + */ +static void __init ak3918_init_machine(void) +{ + adc1_init(); + + spi_register_board_info(ak39_spi_board_dev, ARRAY_SIZE(ak39_spi_board_dev)); + + ak39_spi1_device.dev.platform_data = &ak39_spi1_info; + + //ak39_motor0_device.dev.platform_data = &ak39_motor0_pdata; + //ak39_motor1_device.dev.platform_data = &ak39_motor1_pdata; + + ak39_mci1_device.dev.platform_data = &mci1_plat_data; + + ak39_crypto_device.dev.platform_data = &akcrypto_pdata; + + ak39_usb_otg_hcd_device.dev.platform_data = &akotghc_plat_data; + ak39_mac_device.dev.platform_data = &ak39_mac_pdata; + + ak39_led_pdev.dev.platform_data = &led_pdata; + ak39_gpio_keys_device.dev.platform_data = &gpio_keys_platform_data; + //ak39_battery_power.dev.platform_data = &ak39_bat_info; + + ak39_camera_interface.dev.platform_data = &ak39_camera_info; + + ak39_pm_device.dev.platform_data = &pm_pdata; + + + platform_add_devices(ak3918_platform_devices, + ARRAY_SIZE(ak3918_platform_devices)); + + l2_init(); + + return; +} + + +MACHINE_START(AK39XX, "Cloud39EV3_AK3918E80PIN_MNBD") +/* Maintainer: */ + .atag_offset = 0x100, + .fixup = NULL, + .map_io = ak39_map_io, + .reserve = NULL, + .init_irq = ak39_init_irq, + .init_machine = ak3918_init_machine, + .init_early = NULL, + .timer = &ak39_timer, + .restart = ak39_restart, + +MACHINE_END + + diff --git a/arch/arm/mach-ak39/mach_readme b/arch/arm/mach-ak39/mach_readme new file mode 100644 index 00000000..cdfaa4fb --- /dev/null +++ b/arch/arm/mach-ak39/mach_readme @@ -0,0 +1,2 @@ +mach-cloud39ev3_ak3916e128pin_mnbd.c 对应的是 3916ev300 开发板的板级文件 +mach-cloud39ev3_ak3918e80pin_mnbd.c 对应的是 3918ev300 开发板的板级文件 diff --git a/arch/arm/mach-ak39/pm.c b/arch/arm/mach-ak39/pm.c new file mode 100644 index 00000000..b4155733 --- /dev/null +++ b/arch/arm/mach-ak39/pm.c @@ -0,0 +1,179 @@ +/* + * linux/arch/arm/mach-ak39/pm.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include + +void check_poweroff(void); + +static suspend_state_t target_state; + +#define ak39_pm_debug_init() do { } while(0) + +static int ak39_pm_valid_state(suspend_state_t state) +{ + switch (state) { + case PM_SUSPEND_ON: + case PM_SUSPEND_STANDBY: + case PM_SUSPEND_MEM: + return 1; + default: + return 0; + } +} + +/* + * Called after processes are frozen, but before we shutdown devices. + */ +static int ak39_pm_begin(suspend_state_t state) +{ + target_state = state; + return 0; +} + +static int ak39_pm_gpios_wakeup_enable(struct ak_pm_pdata *pdata) +{ + struct ak_wakeup_gpio *pgpio = pdata->gpios; + int i; + + for (i = 0; i < pdata->nr_gpios; i++, pgpio++) { + ak_gpio_wakeup_pol(pgpio->pin, pgpio->wakeup_pol); /* AK_RISING_TRIGGERED or AK_FALLING_TRIGGERED */ + + ak_gpio_wakeup(pgpio->pin, AK_WAKEUP_ENABLE); + } + + return 0; +} + +#define ak39_pm_gpio_status_clear() do { \ + REG32(GPI0_WAKEUP_STACLR) = 0x00000000; \ + REG32(GPI0_WAKEUP_STACLR) = 0xFFFFFFFF ; \ + \ + while(REG32(GPI0_WAKEUP_STA) != 0) \ + REG32(GPI0_WAKEUP_STACLR) = 0xFFFFFFFF ; \ + REG32(GPI0_WAKEUP_STACLR) = 0x00000000; \ + } while (0) + +void L2_LINK(standby) L2FUNC_NAME(standby)(unsigned long param1, + unsigned long param2,unsigned long param3, unsigned long param4) +{ +// unsigned long val,read_val; + + + DISABLE_CACHE_MMU();// invalidate and disable mmu + + // check this bit and unitil both are empty + while(!((REG32(PHY_RAM_CFG_REG4) & (FIFO_R_EMPTY | FIFO_CMD_EMPTY)) == (FIFO_R_EMPTY | FIFO_CMD_EMPTY))); + + PM_DELAY(0x10);//at least more than 1 tck + + REG32(PHY_RAM_CFG_REG4) &= ~(AUTO_REFRESH_EN);// setup periodic of refresh interval and disable auto-refresh + + DDR2_ENTER_SELFREFRESH();// send all bank precharge + PM_DELAY(0x10);//at least more than 1 tck + + ak39_pm_gpio_status_clear(); + + PM_DELAY(0x2000); //at least more than 3 tck only for selfresh + + REG32(0x21800000) = 0x1 ; //goto standby + + // the system is standby ...... + + /* instruction: + * PM_DELAY(0x1) = 128.8ns when mem clk = 200M + */ + PM_DELAY(0x10); // at least more than 1 tck prior exit self refresh + + // exit DDR2 self-refresch + DDR2_EXIT_SELFREFRESH(); + + // send auto refresh and open odt high + DDR2_ENTER_AUTOREFRESH(); + + // enable auto-refresh + REG32(PHY_RAM_CFG_REG4) |= AUTO_REFRESH_EN; + PM_DELAY(0x100); + + // enable ICache & DCache, mmu + ENABLE_CACHE_MMU(); +} + +static int ak39_pm_enter(suspend_state_t state) +{ + unsigned long flags; + + local_irq_save(flags); + ak39_pm_debug_init(); + flush_cache_all(); + SPECIFIC_L2BUF_EXEC(standby, 0,0,0,0); + local_irq_restore(flags); + return 0; +} + +/* + * Called right prior to thawing processes. + */ +static void ak39_pm_end(void) +{ + target_state = PM_SUSPEND_ON; +} + +static struct platform_suspend_ops ak39_pm_ops = { + .valid = ak39_pm_valid_state, + .begin = ak39_pm_begin, + .enter = ak39_pm_enter, + .end = ak39_pm_end, +}; + +static int __init ak39_pm_probe(struct platform_device *pdev) +{ + struct ak_pm_pdata *pdata; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "cannot get platform data\n"); + return -ENOENT; + } + + ak39_pm_gpios_wakeup_enable(pdata); + + suspend_set_ops(&ak39_pm_ops); + + + return 0; +} + +static int __exit ak39_pm_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver ak39_pm_driver = { + .driver = { + .name = "pm-anyka", + .owner = THIS_MODULE, + }, + .remove = __exit_p(davinci_pm_remove), +}; + +static int __init ak39_pm_init(void) +{ + return platform_driver_probe(&ak39_pm_driver, ak39_pm_probe); +} +late_initcall(ak39_pm_init); + diff --git a/arch/arm/mach-ak39/pwm_timer.c b/arch/arm/mach-ak39/pwm_timer.c new file mode 100644 index 00000000..c5d4fed2 --- /dev/null +++ b/arch/arm/mach-ak39/pwm_timer.c @@ -0,0 +1,437 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ak_pwm_timer_info +{ + int id; + int gpio; + int timerirq; + u8 __iomem *base; + T_GPIO_SHAREPIN_CFG sharepin; + int is_reserved; +}; + + +static struct ak_pwm_timer_info pt_init_info[] = { + {AK_PWM_TIMER1, AK_GPIO_80, IRQ_TIMER1, AK_PWM1_CTRL, ePIN_AS_PWM1, 1}, + {AK_PWM_TIMER2, AK_GPIO_69, IRQ_TIMER2, AK_PWM2_CTRL, ePIN_AS_PWM2, 0}, + {AK_PWM_TIMER3, AK_GPIO_5, IRQ_TIMER3, AK_PWM3_CTRL, ePIN_AS_PWM3, 0}, + {AK_PWM_TIMER4, AK_GPIO_66, IRQ_TIMER4, AK_PWM4_CTRL, ePIN_AS_PWM4, 0}, + {AK_PWM_TIMER5, AK_GPIO_47, IRQ_TIMER5, AK_PWM5_CTRL, ePIN_AS_PWM5, 1}, +}; + + +static DEFINE_MUTEX(timer_lock); +static struct ak_pwm_timer pwm_timer[AK_PWM_TIMER_CNT]; +static int is_initilize = 0; + +#define for_each_pwm_timer(i, timer) \ + for((i)=0, timer=pwm_timer; i= PWM_MIN_FREQ) && (freq <= PWM_MAX_FREQ)); +} + + +static inline int id_is_vaild(int id) +{ + return ((id >= AK_PWM_TIMER1) && (id < AK_PWM_TIMER_NR)); +} + +static inline int timer_is_busy(int id) +{ + return (pwm_timer[id].status == PWM_TIMER_BUSY); +} + +static inline int timer_is_reserved(int id) +{ + return (pwm_timer[id].status == PWM_TIMER_RESERVED); +} + +/** +* @brief set ducy cycle +* write the value into corresponding regester +* @author zhou wenyong +* @date 2011-08-29 +* @param[in] *pwm +* @param[in] high +* @param[in] low +* @return int +*/ +static int ak_pwm_set_duty_cycle(struct ak_pwm_timer *pwm, u16 high, u16 low, u32 pre_div) +{ + u32 regval; + REG32(pwm->base + AK_PWM_TIMER_CTRL1) = high << 16 | low; + + regval = REG32(pwm->base + AK_PWM_TIMER_CTRL2); + regval &= ~AK_PWM_TIMER_PRE_DIV_MASK; + regval |= AK_PWM_TIMER_PRE_DIV(pre_div); + REG32(pwm->base + AK_PWM_TIMER_CTRL2) = regval; + + return 0; +} + + +/** +* @brief get current ducy cycle +* @author zhou wenyong +* @date 2011-08-29 +* @param[in] *pwm +* @param[out] *high +* @param[out] *low +* @return int +*/ +int ak_pwm_get_duty_cycle(struct ak_pwm_timer *pwm, unsigned short *high, unsigned short *low) +{ + unsigned long regval; + + regval = __raw_readl(pwm->base + AK_PWM_TIMER_CTRL1); + + *high = regval >> 16; + *low = regval & 0xFFFF; + + return 0; +} +EXPORT_SYMBOL(ak_pwm_get_duty_cycle); + +/** +* @brief ak_pwm_config +* @author zhou wenyong +* @date 2011-08-29 +* @param[out] *pwm +* @param[in] high +* @param[in] low +* @return int +*/ +int ak_pwm_config(struct ak_pwm_timer *pwm, unsigned short high, unsigned short low, unsigned int freq) +{ + u32 regval; + u32 pre_div; + //BUG_ON(!pwm_freq_is_vaild(freq)); + if(!pwm_freq_is_vaild(freq)){ + return -1; + } + regval = REG32(pwm->base + AK_PWM_TIMER_CTRL2); + regval &= ~AK_TIMER_WORK_MODE_MASK; + regval |= AK_TIMER_WORK_MODE(pwm->mode); + REG32(pwm->base + AK_PWM_TIMER_CTRL2) = regval; + + //pre_div = (REAL_CRYSTAL_FREQ * (high + 1)+(low + 1))/freq - 1; + pre_div = (REAL_CRYSTAL_FREQ / ((high+1)+(low+1)))/freq - 1; + //printk(KERN_ERR "div:%u, h:%u, l:%u, f:%u, act_f:%d\n", pre_div, high, low, freq, (REAL_CRYSTAL_FREQ) / (pre_div + 1) / (high +1+ low+1)); + + pwm->pre_div = pre_div; + pwm->pwm_hlimit = high; + pwm->pwm_llimit = low; + + return ak_pwm_set_duty_cycle(pwm, high, low, pre_div); +} +EXPORT_SYMBOL(ak_pwm_config); + +/** +* @brief enable corresponding pwm +* @author zhou wenyong +* @date 2011-08-29 +* @param[in] *pwm +* @return int +*/ +int ak_pwm_enable(struct ak_pwm_timer *pwm) +{ + T_GPIO_SHAREPIN_CFG sharepin; + + sharepin = pt_init_info[pwm->id].sharepin; + ak_group_config(sharepin); + return 0; +} +EXPORT_SYMBOL(ak_pwm_enable); + + +/** +* @brief disable corresponding pwm +* @author zhou wenyong +* @date 2011-08-29 +* @param[out] *pwm +* @return void +*/ +void ak_pwm_disable(struct ak_pwm_timer *pwm) +{ + ak_setpin_as_gpio(pwm->pwm_pin); +} +EXPORT_SYMBOL(ak_pwm_disable); + +/** +* @brief initilize the pwm/timer module. +* @author lixinhai +* @date 2013-06-9 +* @return return 0 when success. +*/ +static int ak_pwm_timer_init(void) +{ + int i; + struct ak_pwm_timer *timer; + + if(is_initilize == 1) + return 0; + + for_each_pwm_timer(i, timer) { + timer->id = i; + timer->base = pt_init_info[i].base; + if(pt_init_info[i].is_reserved == 0) + timer->status = PWM_TIMER_UNUSED; + else + timer->status = PWM_TIMER_RESERVED; + } + is_initilize = 1; + return 0; +} +EXPORT_SYMBOL(ak_pwm_timer_init); + + +/** +* @brief request a timer. +* @author lixinhai +* @param[in] the pwm/timer id +* @param[in] mode +* @date 2013-06-9 +* @return return the ak_pwm_timer handle. +*/ +static struct ak_pwm_timer *__pwm_timer_request(int id, u8 mode) +{ + struct ak_pwm_timer *pt; + //BUG_ON(!id_is_vaild(id)); + if(is_initilize == 0) + ak_pwm_timer_init(); + + /*if timer is reserved or busy, request fail.*/ + if(!id_is_vaild(id) || timer_is_reserved(id) || timer_is_busy(id)) { + printk(KERN_ERR"pwm[%d] is working or invalid id. request fail.\n", id); + return NULL; + } + + pt = &pwm_timer[id]; + pt->mode = mode; + //pt->status = PWM_TIMER_BUSY; + + return pt; +} + +/** +* @brief release the timer. +* @author lixinhai +* @param[in] ak_pwm_timer pointer. +* @date 2013-06-9 +* @return void +*/ +static void __pwm_timer_release(struct ak_pwm_timer *pt) +{ + pt->status = PWM_TIMER_UNUSED; + is_initilize = 0; +} + + +/** +* @brief config the timer. +* @author lixinhai +* @param[in] ak_pwm_timer pointer +* @param[in] timer count +* @param[in] pre div +* @date 2013-06-9 +* @return success return 0, otherwise return negative value +*/ +int ak_timer_config(struct ak_pwm_timer *timer, u32 cnt, u8 pre_div) +{ + u32 regval; + + timer->timer_cnt = cnt; + timer->pre_div = pre_div; + + + REG32(timer->base + AK_PWM_TIMER_CTRL1) = timer->timer_cnt; + + REG32(timer->base + AK_PWM_TIMER_CTRL2) = AK_TIMER_TIMEOUT_CLR; + + regval = AK_PWM_TIMER_PRE_DIV(timer->pre_div) | + AK_TIMER_WORK_MODE(timer->mode); + + REG32(timer->base + AK_PWM_TIMER_CTRL2) |= regval; + return 0; +} +EXPORT_SYMBOL(ak_timer_config); + + +/** +* @brief enable the timer. +* @author lixinhai +* @param[in] ak_pwm_timer pointer +* @date 2013-06-9 +* @return success return 0, otherwise return negative value +*/ +int ak_timer_enable(struct ak_pwm_timer *timer) +{ + u32 regval; + + regval = AK_TIMER_FEED_TIMER | AK_PWM_TIMER_EN; + REG32(timer->base + AK_PWM_TIMER_CTRL2) |= regval; + + return 0; +} +EXPORT_SYMBOL(ak_timer_enable); + + +/** +* @brief enable the timer and wait for timeout. +* @author lixinhai +* @param[in] ak_pwm_timer pointer +* @date 2013-06-9 +* @return success return 0, otherwise return negative value +*/ +int ak_timer_enable_sync(struct ak_pwm_timer *timer) +{ + ak_timer_enable(timer); + + while(!(REG32(timer->base + AK_PWM_TIMER_CTRL2)&(1<<27))) + ; + return 0; +} +EXPORT_SYMBOL(ak_timer_enable_sync); + + +/** +* @brief disable the timer. +* @param[in] ak_pwm_timer pointer +* @author lixinhai +* @date 2013-06-9 +* @return void +*/ +void ak_timer_disable(struct ak_pwm_timer *timer) +{ + u32 regval; + + regval = REG32(timer->base + AK_PWM_TIMER_CTRL2); + regval &= ~AK_PWM_TIMER_EN; + REG32(timer->base + AK_PWM_TIMER_CTRL2) = regval; +} +EXPORT_SYMBOL(ak_timer_disable); + + +/** +* @brief request a pwm by pwm_id. +* @param[in] pwm id +* @author lixinhai +* @date 2013-06-9 +* @return ak_pwm_timer handle. +*/ +struct ak_pwm_timer *ak_pwm_request(int pwm_id) +{ + struct ak_pwm_timer *pwm; + + mutex_lock(&timer_lock); + pwm = __pwm_timer_request(pwm_id, AK_PT_MODE_PWM); + mutex_unlock(&timer_lock); + return pwm; +} +EXPORT_SYMBOL(ak_pwm_request); + + +/** +* @brief the timer interrupt handler. +* @author lixinhai +* @date 2013-06-9 +* @return irqreturn_t +*/ +static irqreturn_t timer_timeout_irq(int irq, void *dev_id) +{ + struct ak_pwm_timer *timer = dev_id; + + REG32(timer->base + AK_PWM_TIMER_CTRL2) |= AK_TIMER_TIMEOUT_CLR; + timer->timer_cb(timer->timer_priv); + + return IRQ_HANDLED; +} + + +/** +* @brief request a timer by timer_id. +* @param[in] timer id +* @author lixinhai +* @date 2013-06-9 +* @return success return 0, otherwise return negative value +*/ +struct ak_pwm_timer *ak_timer_request(int timer_id, bool auto_reload, int (*cb)(void* data)) +{ + int ret; + u8 mode; + + struct ak_pwm_timer *timer; + mode = auto_reload ?AK_PT_MODE_TIMER_AUTO_LOAD: + AK_PT_MODE_TIMER_ONE_SHOT; + + mutex_lock(&timer_lock); + timer = __pwm_timer_request(timer_id, mode); + + if(cb != NULL) { + timer->timer_cb = cb; + + timer->timer_irq = pt_init_info[timer_id].timerirq; + + /*if callback function not NULL, request timer irq.*/ + ret = request_irq(timer->timer_irq, timer_timeout_irq, IRQF_DISABLED, "timer", timer); + disable_irq(timer->timer_irq); + if(ret) { + printk("request timer irq fail.\n"); + __pwm_timer_release(timer); + return NULL; + } + } + + mutex_unlock(&timer_lock); + return timer; +} +EXPORT_SYMBOL(ak_timer_request); + + +/** +* @brief release the pwm. +* @param[in] ak_pwm_timer pointer +* @author lixinhai +* @date 2013-06-9 +* @return success return 0, otherwise return negative value +*/ +void ak_pwm_release(struct ak_pwm_timer *pwm) +{ + + mutex_lock(&timer_lock); + __pwm_timer_release(pwm); + mutex_unlock(&timer_lock); +} +EXPORT_SYMBOL(ak_pwm_release); + + +/** +* @brief release the timer. +* @param[in] ak_pwm_timer pointer +* @author lixinhai +* @date 2013-06-9 +* @return success return 0, otherwise return negative value +*/ +void ak_timer_release(struct ak_pwm_timer *timer) +{ + mutex_lock(&timer_lock); + free_irq(timer->timer_irq, timer); + __pwm_timer_release(timer); + mutex_unlock(&timer_lock); +} +EXPORT_SYMBOL(ak_timer_release); + + diff --git a/arch/arm/mach-ak39/reboot.c b/arch/arm/mach-ak39/reboot.c new file mode 100644 index 00000000..4730b726 --- /dev/null +++ b/arch/arm/mach-ak39/reboot.c @@ -0,0 +1,122 @@ +/* + * reboot.c - implement a interface jump to ROM code + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +//#include +#include +#include +#include + +#define DIS_INTERRUPTS (0x00001FFF) +#define RESET_DEVICE1 (0xE9A70000) +#define RESET_DEVICE2 (0xBF7F0000) +#define CLOSE_CLOCK1 (0x0000E9A7) +#define CLOSE_CLOCK2 (0x0000BF7F) +#define RESET_MULTI1 (0x003003C8) +#define RESET_MULTI2 (0x24000000) +#define RESET_SHARE1 (0x00000203) +#define RESET_SHARE2 (0x00000001) +/** + * @brief: ak39_jump_to_rom + * @author: zhongjunchao + * @date: 2011-10-13 + * + * @note: Invalidate I & D cache and TLBs, and then disable I & D cache and MMU + * At last, we jump to ROM + */ +void ak39_jump_to_rom(unsigned long addr) +{ + __asm__ __volatile__( + "1: mrc p15, 0, pc, c7, c14, 3\n\t" + "bne 1b\n\t" + "mov %0, #0x0\n\t" + "mcr p15, 0, %0, c8, c7, 0\n\t" + "mcr p15, 0, %0, c7, c5, 0\n\t" + "mrc p15, 0, %0, c1, c0, 0\n\t" + "bic %0, %0, #0x3000\n\t" + "bic %0, %0, #0x0005\n\t" + "mcr p15, 0, %0, c1, c0, 0\n\t" + "mov pc, %1\n\t" + : : "r"(0),"r"(addr)); +} +EXPORT_SYMBOL(ak39_jump_to_rom); + +/** + * @brief: ak39_reboot_sys_by_soft + * @author: zhongjunchao + * @date: 2011-10-11 + * + * @note: I jump to 0x0 to reboot our system. + * TODO: Now on, the register config only for ak39xx. + */ +void ak39_reboot_sys_by_soft(void) +{ + /* disable some interrupt */ + REG32(AK_VA_SYSCTRL + 0x4c) &= ~DIS_INTERRUPTS; + printk("After disable some interrupt\n"); + + /* mask all normal interrupt */ + REG32(AK_VA_SYSCTRL + 0x34) = 0x0; + printk("After mask all normal interrupt\n"); + + /* mask all GPIO interrupt */ + REG32(AK_GPIO_INT_MASK1) = 0x0; + REG32(AK_GPIO_INT_MASK2) = 0x0; + printk("After disable all GPIO interrupt\n"); + + /* reset and close clock except UART1,RAM and L2 FIFO */ + REG32(AK_VA_SYSCTRL + 0x0C) |= RESET_DEVICE1; + udelay(500); + REG32(AK_VA_SYSCTRL + 0x0C) &= ~RESET_DEVICE1; + + REG32(AK_VA_SYSCTRL + 0x10) |= RESET_DEVICE2; + udelay(500); + REG32(AK_VA_SYSCTRL + 0x10) &= ~RESET_DEVICE2; + + REG32(AK_VA_SYSCTRL + 0x0C) |= CLOSE_CLOCK1; + REG32(AK_VA_SYSCTRL + 0x10) |= CLOSE_CLOCK2; + printk("After reset all device and close clock\n"); + + /* reset multiple-function control */ + REG32(AK_VA_SYSCTRL + 0x58) = RESET_MULTI1; + REG32(AK_VA_SYSCTRL + 0x14) = RESET_MULTI2; + printk("After reset multiple-function\n"); + + /* reset share pin control */ + REG32(AK_VA_SYSCTRL + 0x78) = RESET_SHARE1; + REG32(AK_VA_SYSCTRL + 0x74) = RESET_SHARE2; + printk("After reset all share pin\n"); + + /* reset GPIO interrupt polarity */ + REG32(AK_GPIO_INTP1) = 0x0; + REG32(AK_GPIO_INTP2) = 0x0; + printk("After reset all GPIO int pol\n"); + + /* reset GPIO direction */ + REG32(AK_GPIO_DIR1) = 0x0; + REG32(AK_GPIO_DIR2) = 0x0; + printk("After reset all GPIO dir\n"); + + /* reset GPIO pull up/down */ + REG32(AK_VA_SYSCTRL + 0x9C) = 0x0; + REG32(AK_VA_SYSCTRL + 0xA0) = 0x0; + REG32(AK_VA_SYSCTRL + 0xA4) = 0x0; + REG32(AK_VA_SYSCTRL + 0xA8) = 0x0; + printk("After reset all GPIO PDU\n"); + + /* disable l2 cache */ + l2cache_clean_finish(); + l2cache_invalidate(); + printk("After disable l2 cache\n"); + + /* jump to 0x0 anddress */ + ak39_jump_to_rom(0x0); +} +EXPORT_SYMBOL(ak39_reboot_sys_by_soft); diff --git a/arch/arm/mach-ak39/reset.c b/arch/arm/mach-ak39/reset.c new file mode 100644 index 00000000..e4abbe4c --- /dev/null +++ b/arch/arm/mach-ak39/reset.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) anyka 2012 + * Wangsheng Gao + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#include +#include +#include +#include + +#undef REG32 +#define REG32(_reg) (*(volatile unsigned long *)(_reg)) + + +/** +* @Copyright (C) Anyka 2012 +* @brief soft reset module +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-10-23 +* @param[out] void +* @param[in] which module +* @param[in] delay millisecond second +* @return fail or not +*/ +int ak39_soft_reset(u32 module) +{ + BUG_ON(module < AK39_SRESET_MMCSD || module > AK39_SRESET_DPHYCLK); + + REG32(MODULE_RESET_CON1) |= (0x1 << module); + mdelay(5); + REG32(MODULE_RESET_CON1) &= ~(0x1 << module); + return 0; +} +EXPORT_SYMBOL(ak39_soft_reset); + +/***** extern call for comm drivers compatible *****/ +int ak_soft_reset(u32 module) +{ + return ak39_soft_reset(module); +} +EXPORT_SYMBOL(ak_soft_reset); diff --git a/arch/arm/mach-ak39/time.c b/arch/arm/mach-ak39/time.c new file mode 100644 index 00000000..dd8a47f1 --- /dev/null +++ b/arch/arm/mach-ak39/time.c @@ -0,0 +1,400 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +//#include +//#include +#include +#include + +#include +#include +#include + +#include + + +#define AK39_TIMER1_CTRL1 (AK_VA_SYSCTRL + 0xB4) +#define AK39_TIMER1_CTRL2 (AK_VA_SYSCTRL + 0xB8) +#define AK39_TIMER2_CTRL1 (AK_VA_SYSCTRL + 0xBC) +#define AK39_TIMER2_CTRL2 (AK_VA_SYSCTRL + 0xC0) +#define AK39_TIMER3_CTRL1 (AK_VA_SYSCTRL + 0xC4) +#define AK39_TIMER3_CTRL2 (AK_VA_SYSCTRL + 0xC8) +#define AK39_TIMER4_CTRL1 (AK_VA_SYSCTRL + 0xCC) +#define AK39_TIMER4_CTRL2 (AK_VA_SYSCTRL + 0xD0) +#define AK39_TIMER5_CTRL1 (AK_VA_SYSCTRL + 0xD4) +#define AK39_TIMER5_CTRL2 (AK_VA_SYSCTRL + 0xD8) + +//#define AK39_TIMER_CTRL1 AK39_TIMER1_CTRL1 +#define AK39_CE_CTRL1 AK39_TIMER1_CTRL1 +#define AK39_CE_CTRL2 AK39_TIMER1_CTRL2 +#define AK39_CS_CTRL1 AK39_TIMER5_CTRL1 +#define AK39_CS_CTRL2 AK39_TIMER5_CTRL2 +#define IRQ_TIMER IRQ_TIMER1 +#if 0 +#define TIMER_CNT (12000000/HZ) +#define TIMER_USEC_SHIFT 16 +#define TIMER_CNT_MASK (0x3F<<26) +#else +#define TIMER_CLK_INPUT (12000000) +#define TIMER_CNT (TIMER_CLK_INPUT/HZ) +#endif +//define timer register bits +#define TIMER_CLEAR_BIT (1<<30) +#define TIMER_FEED_BIT (1<<29) +#define TIMER_ENABLE_BIT (1<<28) +#define TIMER_STATUS_BIT (1<<27) +#define TIMER_READ_SEL_BIT (1<<26) + +//define pwm/pwm mode +#if 0 +#define MODE_AUTO_RELOAD_TIMER 0x0 +#define MODE_ONE_SHOT_TIMER 0x1 +#define MODE_PWM 0x2 +#else + +#define MODE_AUTO_RELOAD_TIMER (0x0<<24) +#define MODE_ONE_SHOT_TIMER (0x1<<24) +#define MODE_PWM (0x2<<24) + +#endif +#if 0 +static u_int64_t ghrtick = 0; +static unsigned long usec_per_tick; /* usec per tick, left shift 16 */ +static spinlock_t tick_lock; /* lock to protect tick */ + +/* copy from plat-s3c/time.c + * + * timer_mask_usec_ticks + * + * given a clock and divisor, make the value to pass into timer_ticks_to_usec + * to scale the ticks into usecs + */ +static inline unsigned long timer_mask_usec_ticks(unsigned long scaler, + unsigned long pclk) +{ + unsigned long den = pclk / 1000; + + return ((1000 << TIMER_USEC_SHIFT) * scaler + (den >> 1)) / den; +} + +static inline unsigned long timer_ticks_to_usec(unsigned long ticks) +{ + unsigned long ret; + + ret = ticks * usec_per_tick; + ret += 1 << (TIMER_USEC_SHIFT - 4); + + return ret >> TIMER_USEC_SHIFT; +} + +/* + * Returns microsecond since last clock interrupt. Note that interrupts + * will have been disabled by do_gettimeoffset() + * IRQs are disabled before entering here from do_gettimeofday() + * + * FIXME: this need be checked + */ +static unsigned long ak39_gettimeoffset(void) +{ + unsigned long tdone; + unsigned long tcnt; + + /* work out how many ticks have gone since last timer interrupt */ + + //select read current count mode + tdone = __raw_readl(AK39_TIMER_CTRL2); + __raw_writel(tdone | TIMER_READ_SEL_BIT, AK39_TIMER_CTRL2); + + tcnt = __raw_readl(AK39_TIMER_CTRL1); + + //recover read mode + tdone = __raw_readl(AK39_TIMER_CTRL2); + __raw_writel(tdone & (~TIMER_READ_SEL_BIT), AK39_TIMER_CTRL2); + + tdone = TIMER_CNT - tcnt; + + if (__raw_readl(AK39_TIMER_CTRL2) & TIMER_STATUS_BIT) { /* Timer1 has generated interrupt, and not clear */ + + /* Reread timer counter */ + //select read current count mode + tdone = __raw_readl(AK39_TIMER_CTRL2); + __raw_writel(tdone | TIMER_READ_SEL_BIT, AK39_TIMER_CTRL2); + + tcnt = __raw_readl(AK39_TIMER_CTRL1); + + //recover read mode + tdone = __raw_readl(AK39_TIMER_CTRL2); + __raw_writel(tdone & (~TIMER_READ_SEL_BIT), AK39_TIMER_CTRL2); + + tdone = TIMER_CNT - tcnt; + + if (tcnt != 0) + tdone += TIMER_CNT; + } + + return timer_ticks_to_usec(tdone); +} + +static inline void ak39_timer_setup(void) +{ + unsigned long regval; + + /* clear timeout puls, reload */ + regval = __raw_readl(AK39_TIMER_CTRL2); + __raw_writel(regval | TIMER_CLEAR_BIT, AK39_TIMER_CTRL2); +} + +/* + * IRQ handler for the timer + */ +static irqreturn_t ak39_timer_interrupt(int irq, void *dev_id) +{ + if (__raw_readl(AK39_TIMER_CTRL2) & TIMER_STATUS_BIT) { + + ghrtick += TIMER_CNT; + + timer_tick(); + + ak39_timer_setup(); + } + + return IRQ_HANDLED; +} + +#if 1 +u_int64_t ak39_gethrtick(void) +{ + unsigned long timecnt; + unsigned long tdone; + u_int64_t hrtick; + unsigned long flags; + + spin_lock_irqsave(&tick_lock, flags); + + //select read current count mode + tdone = __raw_readl(AK39_TIMER_CTRL2); + __raw_writel(tdone | TIMER_READ_SEL_BIT, AK39_TIMER_CTRL2); + + hrtick = ghrtick; + timecnt = __raw_readl(AK39_TIMER_CTRL1) & (~TIMER_CNT_MASK); + + if (__raw_readl(AK39_TIMER_CTRL2) & TIMER_STATUS_BIT) { + // timer is just reloaded, we may just miss an timer intr + // reread timer counter + hrtick += TIMER_CNT; + timecnt = __raw_readl(AK39_TIMER_CTRL1) & (~TIMER_CNT_MASK); + } + + //recover read mode + tdone = __raw_readl(AK39_TIMER_CTRL2); + __raw_writel(tdone & (~TIMER_READ_SEL_BIT), AK39_TIMER_CTRL2); + + spin_unlock_irqrestore(&tick_lock, flags); + + return (hrtick + (u_int64_t)(TIMER_CNT-timecnt)); +#if 0 + hrtick += (u_int64_t)(TIMER_CNT-timecnt); + + hrtick = ((unsigned long)(hrtick>>2)) / 3; // unit us + + return hrtick; +#endif +} +#endif + +static struct irqaction ak39_timer_irq = { + .name = "timer tick", + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, + .handler = ak39_timer_interrupt, +}; + +static void __init ak39_timer_init(void) +{ + unsigned long timecnt = TIMER_CNT - 1; + + usec_per_tick = timer_mask_usec_ticks(1, 12000000); + + __raw_writel(timecnt, AK39_TIMER_CTRL1); + __raw_writel((TIMER_ENABLE_BIT | TIMER_FEED_BIT | (MODE_AUTO_RELOAD_TIMER << 24)), + AK39_TIMER_CTRL2); + + /* setup irq handler for IRQ_TIMER */ + ghrtick = 0; + spin_lock_init(&tick_lock); + setup_irq(IRQ_TIMER, &ak39_timer_irq); +} + +struct sys_timer ak39_timer = { + .init = ak39_timer_init, + .offset = ak39_gettimeoffset, + //.resume = ak39_timer_setup +}; + +#else + +static cycle_t ak_timer5_read(struct clocksource *cs) +{ + u32 ctrl1, ctrl2; + unsigned long flags; + + local_irq_save(flags); + + /* select read current count mode */ + ctrl2 = __raw_readl(AK39_CS_CTRL2); + __raw_writel(ctrl2 | TIMER_READ_SEL_BIT, AK39_CS_CTRL2); + + ctrl1 = __raw_readl(AK39_CS_CTRL1); + + /* resume read mode */ + ctrl2 = __raw_readl(AK39_CS_CTRL2); + __raw_writel(ctrl2 & (~TIMER_READ_SEL_BIT), AK39_CS_CTRL2); + + local_irq_restore(flags); + + return (cycle_t)~ctrl1; +} + +static struct clocksource ak_cs = { + .name = "ak_timer5 cs", + .rating = 100, //lhd + .read = ak_timer5_read, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +/* use ak timer1 as clock event device */ +static void ak_timer1_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + __raw_writel((TIMER_CNT-1), AK39_CE_CTRL1); + __raw_writel((TIMER_ENABLE_BIT | TIMER_FEED_BIT | MODE_AUTO_RELOAD_TIMER),AK39_CE_CTRL2); + break; + + case CLOCK_EVT_MODE_ONESHOT: + __raw_writel(0xffffffff, AK39_CE_CTRL1); + __raw_writel((TIMER_ENABLE_BIT | TIMER_FEED_BIT | MODE_ONE_SHOT_TIMER), AK39_CE_CTRL2); + break; + + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + default: + break; + } +} + + + +static int ak_timer1_set_next_event(unsigned long next, + struct clock_event_device *evt) +{ + __raw_writel(next, AK39_CE_CTRL1); + __raw_writel((TIMER_ENABLE_BIT | TIMER_FEED_BIT | MODE_ONE_SHOT_TIMER), AK39_CE_CTRL2); + + return 0; +} + +static struct clock_event_device ak_ced = { + .name = "ak_timer1 ce", + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .shift = 32, + .rating = 100, //lhd + .irq = IRQ_TIMER, + .set_next_event = ak_timer1_set_next_event, + .set_mode = ak_timer1_set_mode, +}; + +/* interrupt handler of ak timer1 */ +static irqreturn_t ak39_timer1_interrupt(int irq, void *handle) +{ + struct clock_event_device *dev = handle; + u32 ctrl2; + + ctrl2 = __raw_readl(AK39_CE_CTRL2); + if (ctrl2 & TIMER_STATUS_BIT) { + dev->event_handler(dev); + __raw_writel(ctrl2 | TIMER_CLEAR_BIT, AK39_CE_CTRL2); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static struct irqaction ak39_timer1_irq = { + .name = "ak_timer1 irq", + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, + .handler = ak39_timer1_interrupt, + .dev_id = &ak_ced, +}; + +/* use ak timer5 as sched clock */ +static u32 ak_read_sched_clock(void) +{ + u32 ctrl1, ctrl2; + unsigned long flags; + + local_irq_save(flags); + + /* select read current count mode */ + ctrl2 = __raw_readl(AK39_CS_CTRL2); + __raw_writel(ctrl2 | TIMER_READ_SEL_BIT, AK39_CS_CTRL2); + + ctrl1 = __raw_readl(AK39_CS_CTRL1); + + /* resume read mode */ + ctrl2 = __raw_readl(AK39_CS_CTRL2); + __raw_writel(ctrl2 & (~TIMER_READ_SEL_BIT), AK39_CS_CTRL2); + + local_irq_restore(flags); + + return ~ctrl1; +} + +/* ak39 system timer initialize */ +static void __init ak39_sys_timer_init(void) +{ + + /* ak timer5 init */ + __raw_writel(0xffffffff, AK39_CS_CTRL1); + __raw_writel((TIMER_ENABLE_BIT | TIMER_FEED_BIT | MODE_AUTO_RELOAD_TIMER), + AK39_CS_CTRL2); + + /* register to clocksource framework */ + if (clocksource_register_hz(&ak_cs, TIMER_CLK_INPUT)) + printk(KERN_ERR "ak39_sys_timer_init: clocksource_register failed for %s\n",ak_cs.name); + + /* register to clock event framework */ + ak_ced.cpumask = cpumask_of(0); + clockevents_config_and_register(&ak_ced, TIMER_CLK_INPUT, 12*1000, 0xffffffff); + + if (setup_irq(IRQ_TIMER, &ak39_timer1_irq)) + printk(KERN_ERR "ak39_sys_timer_init: irq register failed for %s\n", ak39_timer1_irq.name); + + /* register to 64bit general sched clock framework */ + setup_sched_clock(ak_read_sched_clock, 32, TIMER_CLK_INPUT); +} + +struct sys_timer ak39_timer = { + .init = ak39_sys_timer_init, +}; + +#endif diff --git a/arch/arm/mach-ak39/timer.c b/arch/arm/mach-ak39/timer.c new file mode 100644 index 00000000..641e55f7 --- /dev/null +++ b/arch/arm/mach-ak39/timer.c @@ -0,0 +1,247 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#define AK39_TIMER1_CTRL1 (AK_VA_SYSCTRL + 0xB4) +#define AK39_TIMER1_CTRL2 (AK_VA_SYSCTRL + 0xB8) +#define AK39_TIMER2_CTRL1 (AK_VA_SYSCTRL + 0xBC) +#define AK39_TIMER2_CTRL2 (AK_VA_SYSCTRL + 0xC0) +#define AK39_TIMER3_CTRL1 (AK_VA_SYSCTRL + 0xC4) +#define AK39_TIMER3_CTRL2 (AK_VA_SYSCTRL + 0xC8) +#define AK39_TIMER4_CTRL1 (AK_VA_SYSCTRL + 0xCC) +#define AK39_TIMER4_CTRL2 (AK_VA_SYSCTRL + 0xD0) +#define AK39_TIMER5_CTRL1 (AK_VA_SYSCTRL + 0xD4) +#define AK39_TIMER5_CTRL2 (AK_VA_SYSCTRL + 0xD8) + +#define AK39_TIMER_CTRL1 AK39_TIMER2_CTRL1 +#define AK39_TIMER_CTRL2 AK39_TIMER2_CTRL2 +#define IRQ_TIMER IRQ_TIMER2 + +#define TIMER_CNT (12000000/HZ) +#define TIMER_USEC_SHIFT 16 +#define TIMER_CNT_MASK (0x3F<<26) + +//define timer register bits +#define TIMER_CLEAR_BIT (1<<30) +#define TIMER_FEED_BIT (1<<29) +#define TIMER_ENABLE_BIT (1<<28) +#define TIMER_STATUS_BIT (1<<27) +#define TIMER_READ_SEL_BIT (1<<26) + +//define pwm/pwm mode +#define MODE_AUTO_RELOAD_TIMER 0x0 +#define MODE_ONE_SHOT_TIMER 0x1 +#define MODE_PWM 0x2 + +#if 1 +#define PDEBUG(fmt, args...) printk( KERN_ERR fmt,## args) +#else +#define PDEBUG(fmt, args...) +#endif + +struct aktimer { + volatile unsigned int __force *ctrl1; + volatile unsigned int __force *ctrl2; + + int timer_bit; + int irq; + timer_handler handler; + void *data; +}; +static unsigned long flags; + +/* copy from plat-s3c/time.c + * + * timer_mask_usec_ticks + * + * given a clock and divisor, make the value to pass into timer_ticks_to_usec + * to scale the ticks into usecs +*/ +static inline unsigned long +timer_mask_usec_ticks(unsigned long scaler, unsigned long pclk) +{ + unsigned long den = pclk / 1000; + + return ((1000 << TIMER_USEC_SHIFT) * scaler + (den >> 1)) / den; +} + +static inline void ak39_timer_setup(struct aktimer *ptimer) +{ + unsigned long regval; + + /* clear timeout puls, reload */ + regval = __raw_readl(ptimer->ctrl2); + __raw_writel(regval | TIMER_CLEAR_BIT, ptimer->ctrl2); +} + +/* + * IRQ handler for the timer + */ +static irqreturn_t ak39_timer_interrupt(int irq, void *dev_id) +{ + struct aktimer *ptimer = dev_id; + + if (__raw_readl(ptimer->ctrl2) & TIMER_STATUS_BIT) { + + ptimer->handler(ptimer->data); + + ak39_timer_setup(ptimer); + } + + return IRQ_HANDLED; +} + +void * ak39_timer_probe(int which_timer) +{ + int ret; + int timer_bit; + struct aktimer *ptimer; + + ptimer = kmalloc(sizeof(struct aktimer), GFP_KERNEL); + if (ptimer == NULL) { + PDEBUG("%s kmalloc failed.\n", __func__); + goto err1; + } + + memset(ptimer, 0, sizeof(*ptimer)); + + switch (which_timer) { + case 1: + ptimer->ctrl1 = AK39_TIMER1_CTRL1; + ptimer->ctrl2 = AK39_TIMER1_CTRL2; + ptimer->irq = IRQ_TIMER1; + break; + case 2: + ptimer->ctrl1 = AK39_TIMER2_CTRL1; + ptimer->ctrl2 = AK39_TIMER2_CTRL2; + ptimer->irq = IRQ_TIMER2; + break; + case 3: + ptimer->ctrl1 = AK39_TIMER3_CTRL1; + ptimer->ctrl2 = AK39_TIMER3_CTRL2; + ptimer->irq = IRQ_TIMER3; + break; + case 4: + ptimer->ctrl1 = AK39_TIMER4_CTRL1; + ptimer->ctrl2 = AK39_TIMER4_CTRL2; + ptimer->irq = IRQ_TIMER4; + break; + case 5: + ptimer->ctrl1 = AK39_TIMER5_CTRL1; + ptimer->ctrl2 = AK39_TIMER5_CTRL2; + ptimer->irq = IRQ_TIMER5; + break; + default: + PDEBUG("error ak39 only 5 timers.\n"); + goto err2; + break; + } + + timer_bit = 1<<(which_timer - 1); + ptimer->timer_bit = timer_bit; + + /* setup irq handler for IRQ_TIMER */ + ret = request_irq(ptimer->irq, ak39_timer_interrupt, IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, "timer for ptz", ptimer); + if (ret) { + PDEBUG("request irq for timer failed.\n"); + goto err2; + } + return ptimer; + +err2: + kfree(ptimer); +err1: + return NULL; +} +EXPORT_SYMBOL(ak39_timer_probe); + +int ak39_timer_remove(void *priv) +{ + struct aktimer *ptimer = priv; + + ak39_timer_stop(priv); + free_irq(ptimer->irq, ptimer); + kfree(ptimer); + + return 0; +} +EXPORT_SYMBOL(ak39_timer_remove); + +/* + *which_timer: 1~5, total five timers. + * */ +int ak39_timer_start(timer_handler handler, void *data, void *priv, int hz) +{ + int ret = 0; + unsigned long timecnt = (12000000/hz) - 1; + struct aktimer *ptimer = priv; + int timer_bit = ptimer->timer_bit; + + if (handler == NULL) { + PDEBUG("%s handler NULL", __func__); + ret = -EINVAL; + goto err1; + } + +#if 0 + if (test_bit(timer_bit, &flags)) { + PDEBUG("The timer be used.\n"); + ret = -EBUSY; + goto err1; + } +#endif + + set_bit(timer_bit, &flags); + + ptimer->handler = handler; + ptimer->data = data; + + __raw_writel(timecnt, ptimer->ctrl1); + __raw_writel((TIMER_ENABLE_BIT | TIMER_FEED_BIT | (MODE_AUTO_RELOAD_TIMER << 24)), + ptimer->ctrl2); + +err1: + return ret; +} +EXPORT_SYMBOL(ak39_timer_start); + +int ak39_timer_stop(void *priv) +{ + struct aktimer *ptimer = priv; + + __raw_writel(~TIMER_ENABLE_BIT, ptimer->ctrl2); + clear_bit(ptimer->timer_bit, &flags); + + return 0; +} +EXPORT_SYMBOL(ak39_timer_stop); \ No newline at end of file diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile index bca7e619..37da2cc8 100644 --- a/arch/arm/mm/Makefile +++ b/arch/arm/mm/Makefile @@ -7,6 +7,7 @@ obj-y := dma-mapping.o extable.o fault.o init.o \ obj-$(CONFIG_MMU) += fault-armv.o flush.o idmap.o ioremap.o \ mmap.o pgd.o mmu.o vmregion.o +obj-$(CONFIG_DEBUG_RODATA) += rodata.o ifneq ($(CONFIG_MMU),y) obj-y += nommu.o diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c index 9107231a..eac5dcb4 100644 --- a/arch/arm/mm/alignment.c +++ b/arch/arm/mm/alignment.c @@ -746,7 +746,9 @@ do_alignment_t32_to_handler(unsigned long *pinstr, struct pt_regs *regs, static int do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { - union offset_union offset; + union offset_union offset= { + .un = 0 + }; unsigned long instr = 0, instrptr; int (*handler)(unsigned long addr, unsigned long instr, struct pt_regs *regs); unsigned int type; diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 2a8e3805..eaa6847e 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -32,8 +32,18 @@ static void __iomem *l2x0_base; static DEFINE_RAW_SPINLOCK(l2x0_lock); static u32 l2x0_way_mask; /* Bitmask of active ways */ static u32 l2x0_size; +static u32 l2x0_cache_id; +static unsigned int l2x0_sets; +static unsigned int l2x0_ways; static unsigned long sync_reg_offset = L2X0_CACHE_SYNC; +static inline bool is_pl310_rev(int rev) +{ + return (l2x0_cache_id & + (L2X0_CACHE_ID_PART_MASK | L2X0_CACHE_ID_REV_MASK)) == + (L2X0_CACHE_ID_PART_L310 | rev); +} + struct l2x0_regs l2x0_saved_regs; struct l2x0_of_data { @@ -130,6 +140,23 @@ static void l2x0_cache_sync(void) raw_spin_unlock_irqrestore(&l2x0_lock, flags); } +#ifdef CONFIG_PL310_ERRATA_727915 +static void l2x0_for_each_set_way(void __iomem *reg) +{ + int set; + int way; + unsigned long flags; + + for (way = 0; way < l2x0_ways; way++) { + raw_spin_lock_irqsave(&l2x0_lock, flags); + for (set = 0; set < l2x0_sets; set++) + writel_relaxed((way << 28) | (set << 5), reg); + cache_sync(); + raw_spin_unlock_irqrestore(&l2x0_lock, flags); + } +} +#endif + static void __l2x0_flush_all(void) { debug_writel(0x03); @@ -143,6 +170,13 @@ static void l2x0_flush_all(void) { unsigned long flags; +#ifdef CONFIG_PL310_ERRATA_727915 + if (is_pl310_rev(REV_PL310_R2P0)) { + l2x0_for_each_set_way(l2x0_base + L2X0_CLEAN_INV_LINE_IDX); + return; + } +#endif + /* clean all ways */ raw_spin_lock_irqsave(&l2x0_lock, flags); __l2x0_flush_all(); @@ -153,11 +187,20 @@ static void l2x0_clean_all(void) { unsigned long flags; +#ifdef CONFIG_PL310_ERRATA_727915 + if (is_pl310_rev(REV_PL310_R2P0)) { + l2x0_for_each_set_way(l2x0_base + L2X0_CLEAN_LINE_IDX); + return; + } +#endif + /* clean all ways */ raw_spin_lock_irqsave(&l2x0_lock, flags); + debug_writel(0x03); writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_WAY); cache_wait_way(l2x0_base + L2X0_CLEAN_WAY, l2x0_way_mask); cache_sync(); + debug_writel(0x00); raw_spin_unlock_irqrestore(&l2x0_lock, flags); } @@ -309,26 +352,24 @@ static void l2x0_unlock(u32 cache_id) void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) { u32 aux; - u32 cache_id; u32 way_size = 0; - int ways; const char *type; l2x0_base = base; - cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID); + l2x0_cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID); aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL); aux &= aux_mask; aux |= aux_val; /* Determine the number of ways */ - switch (cache_id & L2X0_CACHE_ID_PART_MASK) { + switch (l2x0_cache_id & L2X0_CACHE_ID_PART_MASK) { case L2X0_CACHE_ID_PART_L310: if (aux & (1 << 16)) - ways = 16; + l2x0_ways = 16; else - ways = 8; + l2x0_ways = 8; type = "L310"; #ifdef CONFIG_PL310_ERRATA_753970 /* Unmapped register. */ @@ -337,24 +378,25 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) outer_cache.set_debug = pl310_set_debug; break; case L2X0_CACHE_ID_PART_L210: - ways = (aux >> 13) & 0xf; + l2x0_ways = (aux >> 13) & 0xf; type = "L210"; break; default: /* Assume unknown chips have 8 ways */ - ways = 8; + l2x0_ways = 8; type = "L2x0 series"; break; } - l2x0_way_mask = (1 << ways) - 1; + l2x0_way_mask = (1 << l2x0_ways) - 1; /* * L2 cache Size = Way size * Number of ways */ way_size = (aux & L2X0_AUX_CTRL_WAY_SIZE_MASK) >> 17; - way_size = 1 << (way_size + 3); - l2x0_size = ways * way_size * SZ_1K; + way_size = SZ_1K << (way_size + 3); + l2x0_size = l2x0_ways * way_size; + l2x0_sets = way_size / CACHE_LINE_SIZE; /* * Check if l2x0 controller is already enabled. @@ -363,7 +405,7 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) */ if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & 1)) { /* Make sure that I&D is not locked down when starting */ - l2x0_unlock(cache_id); + l2x0_unlock(l2x0_cache_id); /* l2x0 controller is disabled */ writel_relaxed(aux, l2x0_base + L2X0_AUX_CTRL); @@ -386,7 +428,7 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) printk(KERN_INFO "%s cache controller enabled\n", type); printk(KERN_INFO "l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, Cache size: %d B\n", - ways, cache_id, aux, l2x0_size); + l2x0_ways, l2x0_cache_id, aux, l2x0_size); } #ifdef CONFIG_OF diff --git a/arch/arm/mm/cache-v6.S b/arch/arm/mm/cache-v6.S index 74c2e5a3..2edb6f67 100644 --- a/arch/arm/mm/cache-v6.S +++ b/arch/arm/mm/cache-v6.S @@ -272,6 +272,11 @@ v6_dma_clean_range: * - end - virtual end address of region */ ENTRY(v6_dma_flush_range) +#ifdef CONFIG_CACHE_FLUSH_RANGE_LIMIT + sub r2, r1, r0 + cmp r2, #CONFIG_CACHE_FLUSH_RANGE_LIMIT + bhi v6_dma_flush_dcache_all +#endif #ifdef CONFIG_DMA_CACHE_RWFO ldrb r2, [r0] @ read for ownership strb r2, [r0] @ write for ownership @@ -294,6 +299,18 @@ ENTRY(v6_dma_flush_range) mcr p15, 0, r0, c7, c10, 4 @ drain write buffer mov pc, lr +#ifdef CONFIG_CACHE_FLUSH_RANGE_LIMIT +v6_dma_flush_dcache_all: + mov r0, #0 +#ifdef HARVARD_CACHE + mcr p15, 0, r0, c7, c14, 0 @ D cache clean+invalidate +#else + mcr p15, 0, r0, c7, c15, 0 @ Cache clean+invalidate +#endif + mcr p15, 0, r0, c7, c10, 4 @ drain write buffer + mov pc, lr +#endif + /* * dma_map_area(start, size, dir) * - start - kernel virtual start address diff --git a/arch/arm/mm/context.c b/arch/arm/mm/context.c index ee9bb363..806cc4f6 100644 --- a/arch/arm/mm/context.c +++ b/arch/arm/mm/context.c @@ -18,30 +18,39 @@ static DEFINE_RAW_SPINLOCK(cpu_asid_lock); unsigned int cpu_last_asid = ASID_FIRST_VERSION; -#ifdef CONFIG_SMP -DEFINE_PER_CPU(struct mm_struct *, current_mm); -#endif #ifdef CONFIG_ARM_LPAE -#define cpu_set_asid(asid) { \ - unsigned long ttbl, ttbh; \ - asm volatile( \ - " mrrc p15, 0, %0, %1, c2 @ read TTBR0\n" \ - " mov %1, %2, lsl #(48 - 32) @ set ASID\n" \ - " mcrr p15, 0, %0, %1, c2 @ set TTBR0\n" \ - : "=&r" (ttbl), "=&r" (ttbh) \ - : "r" (asid & ~ASID_MASK)); \ +void cpu_set_reserved_ttbr0(void) +{ + unsigned long ttbl = __pa(swapper_pg_dir); + unsigned long ttbh = 0; + + /* + * Set TTBR0 to swapper_pg_dir which contains only global entries. The + * ASID is set to 0. + */ + asm volatile( + " mcrr p15, 0, %0, %1, c2 @ set TTBR0\n" + : + : "r" (ttbl), "r" (ttbh)); + isb(); } #else -#define cpu_set_asid(asid) \ - asm(" mcr p15, 0, %0, c13, c0, 1\n" : : "r" (asid)) +void cpu_set_reserved_ttbr0(void) +{ + u32 ttb; + /* Copy TTBR1 into TTBR0 */ + asm volatile( + " mrc p15, 0, %0, c2, c0, 1 @ read TTBR1\n" + " mcr p15, 0, %0, c2, c0, 0 @ set TTBR0\n" + : "=r" (ttb)); + isb(); +} #endif /* * We fork()ed a process, and we need a new context for the child - * to run in. We reserve version 0 for initial tasks so we will - * always allocate an ASID. The ASID 0 is reserved for the TTBR - * register changing sequence. + * to run in. */ void __init_new_context(struct task_struct *tsk, struct mm_struct *mm) { @@ -51,9 +60,7 @@ void __init_new_context(struct task_struct *tsk, struct mm_struct *mm) static void flush_context(void) { - /* set the reserved ASID before flushing the TLB */ - cpu_set_asid(0); - isb(); + cpu_set_reserved_ttbr0(); local_flush_tlb_all(); if (icache_is_vivt_asid_tagged()) { __flush_icache_all(); @@ -98,14 +105,7 @@ static void reset_context(void *info) { unsigned int asid; unsigned int cpu = smp_processor_id(); - struct mm_struct *mm = per_cpu(current_mm, cpu); - - /* - * Check if a current_mm was set on this CPU as it might still - * be in the early booting stages and using the reserved ASID. - */ - if (!mm) - return; + struct mm_struct *mm = current->active_mm; smp_rmb(); asid = cpu_last_asid + cpu + 1; @@ -114,8 +114,7 @@ static void reset_context(void *info) set_mm_context(mm, asid); /* set the new ASID */ - cpu_set_asid(mm->context.id); - isb(); + cpu_switch_mm(mm->pgd, mm); } #else diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index 5df54924..cfda588c 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -563,30 +563,53 @@ static void __init *early_alloc(unsigned long sz) return early_alloc_aligned(sz, sz); } -static pte_t * __init early_pte_alloc(pmd_t *pmd, unsigned long addr, unsigned long prot) +static pte_t * __init early_pte_alloc(pmd_t *pmd) +{ + if (pmd_none(*pmd) || pmd_bad(*pmd)) + return early_alloc(PTE_HWTABLE_OFF + PTE_HWTABLE_SIZE); + return pmd_page_vaddr(*pmd); +} + +static void __init early_pte_install(pmd_t *pmd, pte_t *pte, unsigned long prot) +{ + __pmd_populate(pmd, __pa(pte), prot); + BUG_ON(pmd_bad(*pmd)); +} + +#ifdef CONFIG_HIGHMEM +static pte_t * __init early_pte_alloc_and_install(pmd_t *pmd, + unsigned long addr, unsigned long prot) { if (pmd_none(*pmd)) { - pte_t *pte = early_alloc(PTE_HWTABLE_OFF + PTE_HWTABLE_SIZE); - __pmd_populate(pmd, __pa(pte), prot); + pte_t *pte = early_pte_alloc(pmd); + early_pte_install(pmd, pte, prot); } BUG_ON(pmd_bad(*pmd)); return pte_offset_kernel(pmd, addr); } +#endif static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr, unsigned long end, unsigned long pfn, const struct mem_type *type) { - pte_t *pte = early_pte_alloc(pmd, addr, type->prot_l1); + pte_t *start_pte = early_pte_alloc(pmd); + pte_t *pte = start_pte + pte_index(addr); + + /* If replacing a section mapping, the whole section must be replaced */ + BUG_ON(pmd_bad(*pmd) && ((addr | end) & ~PMD_MASK)); + do { set_pte_ext(pte, pfn_pte(pfn, __pgprot(type->prot_pte)), 0); pfn++; } while (pte++, addr += PAGE_SIZE, addr != end); + early_pte_install(pmd, start_pte, type->prot_l1); } static void __init alloc_init_section(pud_t *pud, unsigned long addr, unsigned long end, phys_addr_t phys, - const struct mem_type *type) + const struct mem_type *type, + bool force_pages) { pmd_t *pmd = pmd_offset(pud, addr); @@ -596,7 +619,7 @@ static void __init alloc_init_section(pud_t *pud, unsigned long addr, * L1 entries, whereas PGDs refer to a group of L1 entries making * up one logical pointer to an L2 table. */ - if (((addr | end | phys) & ~SECTION_MASK) == 0) { + if (((addr | end | phys) & ~SECTION_MASK) == 0 && !force_pages) { pmd_t *p = pmd; #ifndef CONFIG_ARM_LPAE @@ -620,14 +643,15 @@ static void __init alloc_init_section(pud_t *pud, unsigned long addr, } static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr, - unsigned long end, unsigned long phys, const struct mem_type *type) + unsigned long end, unsigned long phys, const struct mem_type *type, + bool force_pages) { pud_t *pud = pud_offset(pgd, addr); unsigned long next; do { next = pud_addr_end(addr, end); - alloc_init_section(pud, addr, next, phys, type); + alloc_init_section(pud, addr, next, phys, type, force_pages); phys += next - addr; } while (pud++, addr = next, addr != end); } @@ -701,7 +725,7 @@ static void __init create_36bit_mapping(struct map_desc *md, * offsets, and we take full advantage of sections and * supersections. */ -static void __init create_mapping(struct map_desc *md) +static void __init create_mapping(struct map_desc *md, bool force_pages) { unsigned long addr, length, end; phys_addr_t phys; @@ -751,7 +775,7 @@ static void __init create_mapping(struct map_desc *md) do { unsigned long next = pgd_addr_end(addr, end); - alloc_init_pud(pgd, addr, next, phys, type); + alloc_init_pud(pgd, addr, next, phys, type, force_pages); phys += next - addr; addr = next; @@ -772,7 +796,7 @@ void __init iotable_init(struct map_desc *io_desc, int nr) vm = early_alloc_aligned(sizeof(*vm) * nr, __alignof__(*vm)); for (md = io_desc; nr; md++, nr--) { - create_mapping(md); + create_mapping(md, false); vm->addr = (void *)(md->virtual & PAGE_MASK); vm->size = PAGE_ALIGN(md->length + (md->virtual & ~PAGE_MASK)); vm->phys_addr = __pfn_to_phys(md->pfn); @@ -1124,12 +1148,12 @@ static void __init devicemaps_init(struct machine_desc *mdesc) map.virtual = 0xffff0000; map.length = PAGE_SIZE; map.type = MT_HIGH_VECTORS; - create_mapping(&map); + create_mapping(&map, false); if (!vectors_high()) { map.virtual = 0; map.type = MT_LOW_VECTORS; - create_mapping(&map); + create_mapping(&map, false); } /* @@ -1152,20 +1176,23 @@ static void __init devicemaps_init(struct machine_desc *mdesc) static void __init kmap_init(void) { #ifdef CONFIG_HIGHMEM - pkmap_page_table = early_pte_alloc(pmd_off_k(PKMAP_BASE), + pkmap_page_table = early_pte_alloc_and_install(pmd_off_k(PKMAP_BASE), PKMAP_BASE, _PAGE_KERNEL_TABLE); #endif } + static void __init map_lowmem(void) { struct memblock_region *reg; + phys_addr_t start; + phys_addr_t end; + struct map_desc map; /* Map all the lowmem memory banks. */ for_each_memblock(memory, reg) { - phys_addr_t start = reg->base; - phys_addr_t end = start + reg->size; - struct map_desc map; + start = reg->base; + end = start + reg->size; if (end > lowmem_limit) end = lowmem_limit; @@ -1177,8 +1204,20 @@ static void __init map_lowmem(void) map.length = end - start; map.type = MT_MEMORY; - create_mapping(&map); + create_mapping(&map, false); } + +#ifdef CONFIG_DEBUG_RODATA + start = __pa(_stext) & PMD_MASK; + end = ALIGN(__pa(__end_rodata), PMD_SIZE); + + map.pfn = __phys_to_pfn(start); + map.virtual = __phys_to_virt(start); + map.length = end - start; + map.type = MT_MEMORY; + + create_mapping(&map, true); +#endif } /* diff --git a/arch/arm/mm/proc-v7-2level.S b/arch/arm/mm/proc-v7-2level.S index 3a4b3e7b..42ac069c 100644 --- a/arch/arm/mm/proc-v7-2level.S +++ b/arch/arm/mm/proc-v7-2level.S @@ -46,18 +46,13 @@ ENTRY(cpu_v7_switch_mm) #ifdef CONFIG_ARM_ERRATA_430973 mcr p15, 0, r2, c7, c5, 6 @ flush BTAC/BTB #endif -#ifdef CONFIG_ARM_ERRATA_754322 - dsb -#endif - mcr p15, 0, r2, c13, c0, 1 @ set reserved context ID - isb -1: mcr p15, 0, r0, c2, c0, 0 @ set TTB 0 - isb #ifdef CONFIG_ARM_ERRATA_754322 dsb #endif mcr p15, 0, r1, c13, c0, 1 @ set context ID isb + mcr p15, 0, r0, c2, c0, 0 @ set TTB 0 + isb #endif mov pc, lr ENDPROC(cpu_v7_switch_mm) diff --git a/arch/arm/mm/rodata.c b/arch/arm/mm/rodata.c new file mode 100644 index 00000000..9a8eb841 --- /dev/null +++ b/arch/arm/mm/rodata.c @@ -0,0 +1,159 @@ +/* + * linux/arch/arm/mm/rodata.c + * + * Copyright (C) 2011 Google, Inc. + * + * Author: Colin Cross + * + * Based on x86 implementation in arch/x86/mm/init_32.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "mm.h" + +static int kernel_set_to_readonly __read_mostly; + +#ifdef CONFIG_DEBUG_RODATA_TEST +static const int rodata_test_data = 0xC3; + +static noinline void rodata_test(void) +{ + int result; + + pr_info("%s: attempting to write to read-only section:\n", __func__); + + if (*(volatile int *)&rodata_test_data != 0xC3) { + pr_err("read only data changed before test\n"); + return; + } + + /* + * Attempt to to write to rodata_test_data, trapping the expected + * data abort. If the trap executed, result will be 1. If it didn't, + * result will be 0xFF. + */ + asm volatile( + "0: str %[zero], [%[rodata_test_data]]\n" + " mov %[result], #0xFF\n" + " b 2f\n" + "1: mov %[result], #1\n" + "2:\n" + + /* Exception fixup - if store at label 0 faults, jumps to 1 */ + ".pushsection __ex_table, \"a\"\n" + " .long 0b, 1b\n" + ".popsection\n" + + : [result] "=r" (result) + : [rodata_test_data] "r" (&rodata_test_data), [zero] "r" (0) + : "memory" + ); + + if (result == 1) + pr_info("write to read-only section trapped, success\n"); + else + pr_err("write to read-only section NOT trapped, test failed\n"); + + if (*(volatile int *)&rodata_test_data != 0xC3) + pr_err("read only data changed during write\n"); +} +#else +static inline void rodata_test(void) { } +#endif + +static int set_page_attributes(unsigned long virt, int numpages, + pte_t (*f)(pte_t)) +{ + pmd_t *pmd; + pte_t *pte; + unsigned long start = virt; + unsigned long end = virt + (numpages << PAGE_SHIFT); + unsigned long pmd_end; + + while (virt < end) { + pmd = pmd_off_k(virt); + pmd_end = min(ALIGN(virt + 1, PMD_SIZE), end); + + if ((pmd_val(*pmd) & PMD_TYPE_MASK) != PMD_TYPE_TABLE) { + pr_err("%s: pmd %p=%08lx for %08lx not page table\n", + __func__, pmd, pmd_val(*pmd), virt); + virt = pmd_end; + continue; + } + + while (virt < pmd_end) { + pte = pte_offset_kernel(pmd, virt); + set_pte_ext(pte, f(*pte), 0); + virt += PAGE_SIZE; + } + } + + flush_tlb_kernel_range(start, end); + + return 0; +} + +int set_memory_ro(unsigned long virt, int numpages) +{ + return set_page_attributes(virt, numpages, pte_wrprotect); +} +EXPORT_SYMBOL(set_memory_ro); + +int set_memory_rw(unsigned long virt, int numpages) +{ + return set_page_attributes(virt, numpages, pte_mkwrite); +} +EXPORT_SYMBOL(set_memory_rw); + +void set_kernel_text_rw(void) +{ + unsigned long start = PAGE_ALIGN((unsigned long)_text); + unsigned long size = PAGE_ALIGN((unsigned long)__end_rodata) - start; + + if (!kernel_set_to_readonly) + return; + + pr_debug("Set kernel text: %lx - %lx to read-write\n", + start, start + size); + + set_memory_rw(start, size >> PAGE_SHIFT); +} + +void set_kernel_text_ro(void) +{ + unsigned long start = PAGE_ALIGN((unsigned long)_text); + unsigned long size = PAGE_ALIGN((unsigned long)__end_rodata) - start; + + if (!kernel_set_to_readonly) + return; + + pr_info_once("Write protecting the kernel text section %lx - %lx\n", + start, start + size); + + pr_debug("Set kernel text: %lx - %lx to read only\n", + start, start + size); + + set_memory_ro(start, size >> PAGE_SHIFT); +} + +void mark_rodata_ro(void) +{ + kernel_set_to_readonly = 1; + + set_kernel_text_ro(); + + rodata_test(); +} diff --git a/arch/arm/plat-anyka/Kconfig b/arch/arm/plat-anyka/Kconfig new file mode 100644 index 00000000..d5539923 --- /dev/null +++ b/arch/arm/plat-anyka/Kconfig @@ -0,0 +1,15 @@ +# arch/arm/plat-anyka/Kconfig +# +# Copyright 2012 Anaka Microelectronics +# +# Licensed under GPLv2 + +config PLAT_ANYKA + bool + depends on ARCH_AK39 + default y + help + Base platform code for ak39xx chips common device + +# low-level serial option nodes + diff --git a/arch/arm/plat-anyka/Makefile b/arch/arm/plat-anyka/Makefile new file mode 100644 index 00000000..857eada7 --- /dev/null +++ b/arch/arm/plat-anyka/Makefile @@ -0,0 +1,19 @@ +# arch/arm/plat-anyka/Makefile +# +# Copyright 2012 Anyka Microelectronics +# +# Licensed under GPLv2 + +obj-y := +obj-m := +obj-n := +obj- := + +# Core support for all Anyka SoCs + +obj-y += l2.o +obj-y += rtc.o +obj-y += drv_module_lock.o +obj-y += l2_exebuf.o +obj-y += notify.o +obj-y += reg.o diff --git a/arch/arm/plat-anyka/drv_module_lock.c b/arch/arm/plat-anyka/drv_module_lock.c new file mode 100644 index 00000000..3d2dea5a --- /dev/null +++ b/arch/arm/plat-anyka/drv_module_lock.c @@ -0,0 +1,281 @@ + +/** + * arch/arm/plat-anyka/drv_module_lock.c + */ + +#include +#include +#include +#include +#include +#include + +//#define DRV_LOCK_DEBUG +#ifdef DRV_LOCK_DEBUG +#define PK(fmt...) printk(const char * fmt,...) +#endif + +#if defined CONFIG_ARCH_AK39 +/* the param drv_shpin_lock[i].name indicate different lock */ +struct ak_drv_module_lock drv_mod_lock[] = { +#ifndef SPI_GPIO_37_TO_40 + {DRV_MODULE_SDMMC,AK_MODULE_LOCK_1, TYPE_LOCK_MUTEX, 0, ePIN_AS_MCI}, + {DRV_MODULE_SPI,AK_MODULE_LOCK_1, TYPE_LOCK_MUTEX, 0, ePIN_AS_SPI1}, +#endif + +}; +#endif + +static void *module_lock_array[AK_MODULE_COUNT] = { 0 }; + +#define GET_DRV_LOCK_INFO(type, table, len, lock_name, lock) \ +{ \ + int i; \ + type *drv_lock = table; \ + for (i = 0; i < len; i++) { \ + if (drv_lock[i].lock_name == lock_name) { \ + lock = &drv_lock[i]; \ + break; \ + } \ + } \ +} + +static void ak_acquire_lock(void **lock_array, + E_LOCK_NAME lock_name, E_LOCK_TYPE lock_type, unsigned long *flags) +{ + unsigned long flag; + + switch(lock_type) { + case TYPE_LOCK_MUTEX: + mutex_lock((struct mutex *)lock_array[lock_name]); + break; + case TYPE_LOCK_SEMAPHORE: + down((struct semaphore *)lock_array[lock_name]); + break; + case TYPE_LOCK_SPINLOCK: + spin_lock((spinlock_t *)lock_array[lock_name]); + break; + case TYPE_LOCK_SPINLOCK_IRQ: + spin_lock_irqsave((spinlock_t *)lock_array[lock_name], flag); + *flags = flag; + break; + default: + BUG(); + } +} + +static void ak_release_lock(void **lock_array, + E_LOCK_NAME lock_name, E_LOCK_TYPE lock_type, unsigned long flags) +{ + switch(lock_type) { + case TYPE_LOCK_MUTEX: + mutex_unlock((struct mutex *)lock_array[lock_name]); + break; + case TYPE_LOCK_SEMAPHORE: + up((struct semaphore *)lock_array[lock_name]); + break; + case TYPE_LOCK_SPINLOCK: + spin_unlock((spinlock_t *)lock_array[lock_name]); + break; + case TYPE_LOCK_SPINLOCK_IRQ: + spin_unlock_irqrestore((spinlock_t *)lock_array[lock_name], flags); + break; + default: + BUG(); + } +} + +static int ak_module_lock(E_DRV_MODULE drv_mod_name, + struct ak_drv_module_lock *mod_lock, int len, void **lock_array) +{ + struct ak_drv_module_lock *lock = NULL; + GET_DRV_LOCK_INFO(struct ak_drv_module_lock, mod_lock, len, drv_mod_name, lock); + if (lock == NULL) + return -1; + + ak_acquire_lock(lock_array, lock->lock_name, lock->lock_type, &lock->flags); + ak_group_config(lock->sharepin_cfg); + return 0; +} + +static void ak_module_unlock(E_DRV_MODULE drv_mod_name, + struct ak_drv_module_lock *mod_lock, int len, void **lock_array) +{ + struct ak_drv_module_lock *lock = NULL; + GET_DRV_LOCK_INFO(struct ak_drv_module_lock, mod_lock, len, drv_mod_name, lock); + if (lock == NULL) + return; + + ak_release_lock(lock_array, lock->lock_name, lock->lock_type, lock->flags); +} + +int ak_drv_module_lock(E_DRV_MODULE drv_mod_name) +{ + return ak_module_lock(drv_mod_name, drv_mod_lock, + ARRAY_SIZE(drv_mod_lock), module_lock_array); +} +EXPORT_SYMBOL(ak_drv_module_lock); + +void ak_drv_module_unlock(E_DRV_MODULE drv_mod_name) +{ + ak_module_unlock(drv_mod_name, drv_mod_lock, + ARRAY_SIZE(drv_mod_lock), module_lock_array); +} +EXPORT_SYMBOL(ak_drv_module_unlock); + + +static void ak_init_lock_array(void **lock_array, int len) +{ + int i; + + for (i = 0; i < len; i++) + lock_array[i] = NULL; +} + +static void ak_init_lock(void **lock_array, E_LOCK_NAME lock_name, E_LOCK_TYPE lock_type) +{ + if (lock_array[lock_name] == NULL) { + switch (lock_type) { + case TYPE_LOCK_MUTEX: + lock_array[lock_name] = kmalloc(sizeof(struct mutex), GFP_KERNEL); + mutex_init((struct mutex *)lock_array[lock_name]); + break; + case TYPE_LOCK_SEMAPHORE: + lock_array[lock_name] = kmalloc(sizeof(struct semaphore), GFP_KERNEL); + sema_init((struct semaphore *)lock_array[lock_name], 1); + break; + case TYPE_LOCK_SPINLOCK: + case TYPE_LOCK_SPINLOCK_IRQ: + lock_array[lock_name] = kmalloc(sizeof(spinlock_t), GFP_KERNEL); + spin_lock_init((spinlock_t *)lock_array[lock_name]); + break; + default: + BUG(); + } + } +} + +static int ak_init_module_locks(void) +{ + int i, len; + + if ((len = ARRAY_SIZE(drv_mod_lock)) == 0) + return -1; + + ak_init_lock_array(module_lock_array, AK_MODULE_COUNT); + + for (i = 0; i < len; i++) { + ak_init_lock(module_lock_array, drv_mod_lock[i].lock_name, drv_mod_lock[i].lock_type); + drv_mod_lock[i].flags = 0; + } + + return 0; +} + + +/* ************* CONFIG GPIO' SHARE FUNC FOR MACHINE ************ */ +struct ak_drv_sharepin_lock *shpin_lock = NULL; +int shpin_lock_count = 0; +void **shpin_lock_array = NULL; + +/** + * ak_set_sharepin_lock_table - get machine sharepin lock table valid callback + */ +void ak_set_sharepin_lock_table( + struct ak_drv_sharepin_lock *shpin_lock_table, + int count, void **lock_array) +{ + shpin_lock = shpin_lock_table; + shpin_lock_count = count; + shpin_lock_array = lock_array; +} +EXPORT_SYMBOL(ak_set_sharepin_lock_table); + +static int ak_sharepin_lock(E_DRV_MODULE drv_mod_name, + struct ak_drv_sharepin_lock *shpin_lock, int len, void **lock_array) +{ + struct ak_drv_sharepin_lock *lock = NULL; + GET_DRV_LOCK_INFO(struct ak_drv_sharepin_lock, shpin_lock, len, drv_mod_name, lock); + if (lock == NULL) + return -1; + + ak_acquire_lock(lock_array, lock->lock_name, lock->lock_type, &lock->flags); + if (lock->config_share_pin != NULL) + lock->config_share_pin(); + return 0; +} + +static void ak_sharepin_unlock(E_DRV_MODULE drv_mod_name, + struct ak_drv_sharepin_lock *shpin_lock, int len, void **lock_array) +{ + struct ak_drv_sharepin_lock *lock = NULL; + GET_DRV_LOCK_INFO(struct ak_drv_sharepin_lock, shpin_lock, len, drv_mod_name, lock); + if (lock == NULL) + return; + + ak_release_lock(lock_array, lock->lock_name, lock->lock_type, lock->flags); +} + +int ak_drv_sharepin_lock(E_DRV_MODULE drv_mod_name) +{ + return ak_sharepin_lock(drv_mod_name, shpin_lock, + shpin_lock_count, shpin_lock_array); +} +EXPORT_SYMBOL(ak_drv_sharepin_lock); + +void ak_drv_sharepin_unlock(E_DRV_MODULE drv_mod_name) +{ + ak_sharepin_unlock(drv_mod_name, shpin_lock, + shpin_lock_count, shpin_lock_array); +} +EXPORT_SYMBOL(ak_drv_sharepin_unlock); + + +/* Brief: Init sharepin locks for drivers module + * param: void + * ret: + * 0: if acquire lock successed; + * <0: indicate the Board or products being not sharepin for mutual drivers + */ +static int ak_init_sharepin_locks(void) +{ + int i; + + if (shpin_lock_count == 0) + return -1; + + ak_init_lock_array(shpin_lock_array, AK_MODULE_COUNT); + + for (i = 0; i < shpin_lock_count; i++) { + ak_init_lock(shpin_lock_array, shpin_lock[i].lock_name, shpin_lock[i].lock_type); + shpin_lock[i].flags = 0; + } + + return 0; +} + + +void ak_drv_module_protect(E_DRV_MODULE drv_mod_name) +{ + ak_drv_module_lock(drv_mod_name); + //ak_drv_sharepin_lock(drv_mod_name); +} +EXPORT_SYMBOL(ak_drv_module_protect); + +void ak_drv_module_unprotect(E_DRV_MODULE drv_mod_name) +{ + //ak_drv_sharepin_unlock(drv_mod_name); + ak_drv_module_unlock(drv_mod_name); +} +EXPORT_SYMBOL(ak_drv_module_unprotect); + +static int __init ak_init_module_lock(void) +{ + printk("Anyka platform share gpio locks initialize.\n"); + + ak_init_module_locks(); + ak_init_sharepin_locks(); + return 0; +} +subsys_initcall(ak_init_module_lock); + diff --git a/arch/arm/plat-anyka/include/mach/reg.h b/arch/arm/plat-anyka/include/mach/reg.h new file mode 100644 index 00000000..751117b6 --- /dev/null +++ b/arch/arm/plat-anyka/include/mach/reg.h @@ -0,0 +1,16 @@ +/* + * reg.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef _REG_H_ +#define _REG_H_ + +void sys_ctrl_reg_set(unsigned long reg_phy_addr, unsigned long reg_mask, unsigned long reg_val); + +#endif /* _REG_H_ */ + diff --git a/arch/arm/plat-anyka/include/mach/regs-l2.h b/arch/arm/plat-anyka/include/mach/regs-l2.h new file mode 100644 index 00000000..cc4dd35f --- /dev/null +++ b/arch/arm/plat-anyka/include/mach/regs-l2.h @@ -0,0 +1,54 @@ + +/* + * arch/arm/plat-anyka/include/mach/regs-l2.h + */ +#ifndef __REGS_L2_H_ +#define __REGS_L2_H_ + +#include + +#define vL2DMA_ADDRBUF0 REG_VA_ADDR(AK_VA_L2CTRL, 0x00) +#define vL2DMA_CONBUF0 REG_VA_ADDR(AK_VA_L2CTRL, 0x40) + +#define L2_DMA_ADDR (AK_VA_L2CTRL + 0x00) +#define L2_DMA_CON (AK_VA_L2CTRL + 0x40) +#define L2_DMAREQ (AK_VA_L2CTRL + 0x80) +#define L2_FRACDMAADDR (AK_VA_L2CTRL + 0x84) +#define L2_CONBUF0_7 (AK_VA_L2CTRL + 0x88) +#define L2_CONBUF8_15 (AK_VA_L2CTRL + 0x8C) +#define L2_BUFASSIGN1 (AK_VA_L2CTRL + 0x90) +#define L2_BUFASSIGN2 (AK_VA_L2CTRL + 0x94) +#define L2_LDMACON (AK_VA_L2CTRL + 0x98) +#define L2_BUFINTEN (AK_VA_L2CTRL + 0x9C) +#define L2_BUFSTAT1 (AK_VA_L2CTRL + 0xA0) +#define L2_BUFSTAT2 (AK_VA_L2CTRL + 0xA8) + +/*************************** L2 MEMORY CONTROL *********************/ +#define rL2_DMAREQ REG_VA_VAL(AK_VA_L2CTRL, 0x80) +#define rL2_FRACDMAADDR REG_VA_VAL(AK_VA_L2CTRL, 0x84) +#define rL2_CONBUF0_7 REG_VA_VAL(AK_VA_L2CTRL, 0x88) +#define rL2_CONBUF8_15 REG_VA_VAL(AK_VA_L2CTRL, 0x8C) +#define rL2_BUFASSIGN1 REG_VA_VAL(AK_VA_L2CTRL, 0x90) +#define rL2_BUFASSIGN2 REG_VA_VAL(AK_VA_L2CTRL, 0x94) +#define rL2_LDMACON REG_VA_VAL(AK_VA_L2CTRL, 0x98) +#define rL2_BUFINTEN REG_VA_VAL(AK_VA_L2CTRL, 0x9C) +#define rL2_BUFSTAT1 REG_VA_VAL(AK_VA_L2CTRL, 0xA0) +#define rL2_BUFSTAT2 REG_VA_VAL(AK_VA_L2CTRL, 0xA8) + +/*************************** L2 MEMORY BUFFER **********************/ +#define rL2_ADDRBUF0 REG_VA_VAL(AK_VA_L2MEM, 0x0000) +#define rL2_ADDRBUF1 REG_VA_VAL(AK_VA_L2MEM, 0x0200) +#define rL2_ADDRBUF2 REG_VA_VAL(AK_VA_L2MEM, 0x0400) +#define rL2_ADDRBUF3 REG_VA_VAL(AK_VA_L2MEM, 0x0600) +#define rL2_ADDRBUF4 REG_VA_VAL(AK_VA_L2MEM, 0x0800) +#define rL2_ADDRBUF5 REG_VA_VAL(AK_VA_L2MEM, 0x0A00) +#define rL2_ADDRBUF6 REG_VA_VAL(AK_VA_L2MEM, 0x0C00) +#define rL2_ADDRBUF7 REG_VA_VAL(AK_VA_L2MEM, 0x0E00) + +#define rL2_ADDRTX1_BUF8 REG_VA_VAL(AK_VA_L2MEM, 0x1000) +#define rL2_ADDRRX1_BUF9 REG_VA_VAL(AK_VA_L2MEM, 0x1080) +#define rL2_ADDRTX2_BUF10 REG_VA_VAL(AK_VA_L2MEM, 0x1100) +#define rL2_ADDRRX2_BUF11 REG_VA_VAL(AK_VA_L2MEM, 0x1180) + +#endif /* __REGS_L2_H_ */ + diff --git a/arch/arm/plat-anyka/include/plat/gpio_keys.h b/arch/arm/plat-anyka/include/plat/gpio_keys.h new file mode 100644 index 00000000..4f01565c --- /dev/null +++ b/arch/arm/plat-anyka/include/plat/gpio_keys.h @@ -0,0 +1,27 @@ +#ifndef __GPIO_KEYS_H_ +#define __GPIO_KEYS_H_ + +struct ak_gpio_keys_button { + /* Configuration parameters */ + int code; /* input event code (KEY_*, SW_*) */ + int gpio; + int active_low; + char *desc; + int type; /* input event type (EV_KEY, EV_SW) */ + int wakeup; /* configure the button as a wake-up source */ + int debounce_interval; /* debounce ticks interval in msecs */ + + char pulldown; //pulldown function flag + char pullup; //pullup function flag + char dir; //direction input/output + char int_pol; //interrupt polarity +}; + +struct ak_gpio_keys_platform_data { + struct ak_gpio_keys_button *buttons; + int nbuttons; + unsigned int rep:1; /* enable input subsystem auto repeat */ +}; + + +#endif /* __GPIO_KEYS_H_ */ diff --git a/arch/arm/plat-anyka/include/plat/l2.h b/arch/arm/plat-anyka/include/plat/l2.h new file mode 100644 index 00000000..c59fe216 --- /dev/null +++ b/arch/arm/plat-anyka/include/plat/l2.h @@ -0,0 +1,375 @@ +/* + * linux/arch/arm/plat-anyka/include/plat/l2.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __ASM_ARCH_L2_H +#define __ASM_ARCH_L2_H + +#include +#include + +#define L2_DEBUG 1 +//#undef L2_DEBUG + +#undef REG32 +#define REG32(_reg) (*(volatile unsigned long *)(_reg)) + +#undef REG16 +#define REG16(_reg) (*(volatile unsigned short *)(_reg)) + +/* + * ANYKA L2 Control Register List and Bit map definition + * TODO: Add all register bit maps and move all to map.h in the future. +*/ +#define L2_DMA_REQ_BUF_START 24 +#define L2_DMA_REQ_BUF_REQ_MASK (0xFFFF << 16) +#define L2_DMA_REQ_UART_BUF_REQ_START 16 +#define L2_DMA_REQ_FRAC_DMA_LEN_START 10 +#define L2_DMA_REQ_FRAC_DMA_LEN_MASK (0x3F << L2_DMA_REQ_FRAC_DMA_LEN_START) +#define L2_DMA_REQ_FRAC_DMA_REQ (1 << 9) +#define L2_DMA_REQ_FRAC_DMA_DIR_WR (1 << 8) +#define L2_DMA_REQ_FRAC_DMA_L2_ADDR_START 1 +#define L2_DMA_REQ_FRAC_DMA_L2_ADDR_MASK (0x7F << L2_DMA_REQ_FRAC_DMA_L2_ADDR_START) +#define L2_DMA_REQ_EN (1 << 0) + +#define L2_FRAC_DMA_AHB_FLAG_EN (1 << 29) +#define L2_FRAC_DMA_LDMA_FLAG_EN (1 << 28) + +#define L2_FRAC_DMA_LOW_ADDR_MASK (0xFFFFFFF << 0) + +#define L2_COMMON_BUF_CFG_BUF7_CLR (1 << 31) +#define L2_COMMON_BUF_CFG_BUF6_CLR (1 << 30) +#define L2_COMMON_BUF_CFG_BUF5_CLR (1 << 29) +#define L2_COMMON_BUF_CFG_BUF4_CLR (1 << 28) +#define L2_COMMON_BUF_CFG_BUF3_CLR (1 << 27) +#define L2_COMMON_BUF_CFG_BUF2_CLR (1 << 26) +#define L2_COMMON_BUF_CFG_BUF1_CLR (1 << 25) +#define L2_COMMON_BUF_CFG_BUF0_CLR (1 << 24) +#define L2_COMMON_BUF_CFG_BUF_CLR_START 24 +#define L2_COMMON_BUF_CFG_BUF0_7_CLR_MASK (0xFF << L2_COMMON_BUF_CFG_BUF_START) +#define L2_COMMON_BUF_CFG_BUF_VLD_START 16 +#define L2_COMMON_BUF_CFG_BUF0_7_VLD_MASK (0xFF << L2_COMMON_BUF_CFG_BUF_VLD_START +#define L2_COMMON_BUF_CFG_BUF_DMA_VLD_START 0 +#define L2_COMMON_BUF_CFG_BUF_DIR_START 8 + +#define L2_UART_BUF_CFG_BUF_START 16 + +#define L2_UART_BUF_CFG_UART_EN_MASK (0xF << 28) +#define L2_UART_BUF_CFG_UART_CLR_MASK (0xFF << 16) +#define L2_UART_BUF_CFG_CPU_BUF_SEL_EN (1 << 3) +#define L2_UART_BUF_CFG_CPU_BUF_NUM_START 4 +#define L2_UART_BUF_CFG_CPU_BUF_NUM_MASK (0xF << L2_UART_BUF_CFG_CPU_BUF_NUM_START) +#define L2_UART_BUF_CFG_CPU_BUF_SEL_START 0 +#define L2_UART_BUF_CFG_CPU_BUF_SEL_MASK (0x7 << L2_UART_BUF_CFG_CPU_BUF_SEL_START) + +#define L2_DMA_INTR_ENABLE_BUF_START 9 +#define L2_DMA_INTR_ENABLE_UART_BUF_START 1 +#define L2_DMA_INTR_ENABLE_FRAC_INTR_EN (1 << 0) + +/* + * ANYKA L2 buffer size(buffer 0 to buffer 7), all 512Bytes. + * NOTE: L2 buffer 8 to 15 dedicate to UART 1 to 4 & USB 2.0 Controller, + * and the corresponding L2 buffer size could be 64, 128 and 256 Bytes. + * See ANYKA Programmer's Guide for details (ANYKA preferably). + */ +#define L2_BUFFER_SIZE 512 + +/* + * ANYKA L2 DMA waiting times in loop + * TODO: Must be change to waiting time based on CPU frequency when frequency APIs are done. + */ +#define L2_MAX_DMA_WAIT_TIME 50 * 1000000UL + +/* + * ANYKA DMA size in bytes per transfer (Always 64Bytes) + * L2 DMA transfer follows this definition. + */ +#define DMA_ONE_SHOT_LEN 64 + +/* + * ANYKA L2 Buffer Status Multiply Ratio(64) + * Buffer Status Register 1 & 2: The number of data = Bufn_sta * L2_BUF_STATUS_MULTIPLY_RATIO + */ +#define L2_BUF_STATUS_MULTIPLY_RATIO 64 + +/* + * L2 Buffer ID Assignment: + * 0 - 7: L2 common buffer, could be used by different peripherals + * 8 - 15: Dedicate L2 buffer for UART + * 16 - 18: Dedicate L2 buffer for USB + */ +#define L2_COMMON_BUFFER_NUM 8 +#define L2_UART_BUFFER_NUM 4 + +#define L2_UART_BUFFER_INDEX L2_COMMON_BUFFER_NUM +#define L2_USB_HOST_BUFFER_INDEX (L2_UART_BUFFER_INDEX + L2_UART_BUFFER_NUM) + + +#define L2_COMMON_BUFFER_LEN 512 +#define L2_UART_BUFFER_LEN 128 + +#define L2_COMMON_BUFFER_OFFSET 0 +#define L2_UART_BUFFER_OFFSET (L2_COMMON_BUFFER_LEN * L2_COMMON_BUFFER_NUM) + +/* + * L2 controller working clock define + */ +#define L2_CLOCK_EN (0x1 << 9) +#define L2_CLOCK_REG (AK_VA_SYSCTRL + 0x1C) + +/* + * AK39XX L2 device list which may use L2 memory. + * The devices are defined according to L2 Buffer Assignement 1 & 2 register bit sequence. + */ +typedef enum { + ADDR_USB_EP1 = 0, /* USB 2.0 HS Controller: Endpoint 1 */ + ADDR_USB_EP2, /* USB 2.0 HS Controller: Endpoint 2 */ + ADDR_USB_EP3, /* USB 2.0 HS Controller: Endpoint 3 */ + ADDR_RESERVED, /* Reserved */ + ADDR_MMC_SD, /* MMC/SD interface */ + ADDR_SDIO, /* SDIO interface */ + ADDR_SPI1_RX = 7, /* Rx buffer of SPI1 Controller */ + ADDR_SPI1_TX, /* Tx buffer of SPI1 Controller */ + ADDR_DAC, /* DAC control module */ + + ADDR_SPI2_RX, /* Rx buffer of SPI2 Controller */ + ADDR_SPI2_TX, /* Tx buffer of SPI2 Controller */ + ADDR_GPS, /* GPS interface */ + ADDR_PCM_TX, /* Tx buffer of PCM Controller */ + ADDR_ADC, /* ADC2 */ + ADDR_USB_EP4, /* USB 2.0 HS Controller: Endpoint 4 */ //=15 +} l2_device_t; + + +#define BUF_NULL 0xFF /* Invalid L2 buffer ID */ +#define L2_UART_BUF_START_ID L2_COMMON_BUFFER_NUM /* UART used buffer ID from 8 */ + +/* + * Maximum L2 DMA status value (The value in CPU-Controlled Buffer and Buffer8 ~ Buffer15 Configuration Register) + * The maximum DMA transfer bytes = MAX_L2_DMA_STATUS_VALUE * 64 + */ +#define MAX_L2_DMA_STATUS_VALUE 0x8 +#define MAX_L2_BUFFER_USED_TIMES 0xFFFF + +/* + * Data transfer direction between L2 memory and external RAM + */ +typedef enum { + BUF2MEM = 0, /* Data transfer from L2 buffer to external RAM */ + MEM2BUF, /* Data transfer from external RAM to L2 buffer */ +} l2_dma_transfer_direction_t; + +/* + * Callback function when L2 DMA/fraction DMA interrupt handler is done + */ +typedef void (*l2_callback_func_t)(unsigned long data); + +/* + * L2 buffer status + */ +typedef enum { + L2_STAT_USED = 0, /* Current L2 buffer is used by some device */ + L2_STAT_IDLE, /* Current L2 buffer is NOT used by some device, thus could be allocated */ +} l2_buffer_status_t; + +/* + * L2 buffer information + */ +typedef struct { + u8 id; /* L2 buffer ID (0~17) */ + l2_buffer_status_t usable; /* L2 buffer status(used or idle) */ + u16 used_time; /* Counter on L2 buffer used times */ +} l2_buffer_info_t; + +/* + * Information on device which use L2 memory + */ +typedef struct { + l2_device_t device; /* Device ID */ + u8 id; /* TODO: Remove id in the future since array index already represent buffer id */ +} l2_device_info_t; + +/* + * L2 DMA usage information (including DMA/fraction DMA/external RAM/Callback function) + */ +typedef struct { + bool dma_start; + bool intr_enable; + l2_dma_transfer_direction_t direction; + void *dma_addr; + u32 dma_op_times; + bool need_frac; + bool dma_frac_start; + void *dma_frac_addr; + u32 dma_frac_offset; + u32 dma_frac_data_len; + l2_callback_func_t callback_func; + unsigned long data; +} l2_dma_info_t; + +/** + * l2_init - Initialize linux kernel L2 memory support + */ +void __init l2_init(void); + +/** + * l2_alloc - Allocate a common L2 buffer for given device + * @device: Device ID which need common L2 buffer + * Return L2 buffer ID (0 ~ 7) + * + * Only common L2 buffers(ID 0 ~ 7) could be allocated by l2_alloc. + * Other L2 buffers (UART/USB used) is handled by corresponding devices directly. + */ +u8 l2_alloc(l2_device_t device); + +/** + * l2_alloc_nowait - Allocate a common L2 buffer for given device + * @device: Device ID which need common L2 buffer + * Return L2 buffer ID (0 ~ 7) + * + * Only common L2 buffers(ID 0 ~ 7) could be allocated by l2_alloc_nowait. + * Other L2 buffers (UART/USB used) is handled by corresponding devices directly. + * return BUF_NULL immediately if no buf was alloc . + */ +u8 l2_alloc_nowait(l2_device_t device); + +/** + * l2_free - Free L2 common buffer for given device + * @device: Device ID which need common L2 buffer + * Return L2 buffer ID (0 ~ 7) + * + * Only common L2 buffers(ID 0 ~ 7) could be allocated by l2_alloc. + * Other L2 buffers (UART/USB used) is handled by corresponding devices directly. + * NOTE: Return the previous L2 buffer ID if a L2 buffer has been allocated to the device. + * This means one device could get only one L2 buffer maximum. + */ +void l2_free(l2_device_t device); + +/** + * l2_set_dma_callback - Set callback function when L2 DMA/fraction DMA interrupt handler is done + * @id: L2 buffer ID + * @func: Callback function + * @data: Arguments used by callback function + * Return true(Always) + * + * NOTE: Caller MUST guarantee that L2 buffer ID is valid. And since the callback function is called + * in interrupt handler, it MUST NOT call any functions which may sleep. + */ +bool l2_set_dma_callback(u8 id, l2_callback_func_t func, unsigned long data); + +/** + * l2_combuf_dma - Start data tranferring between memory and l2 common buffer in DMA mode + * @ram_addr: External RAM address(Physical) + * @id: L2 buffer ID involved in DMA transfer + * @bytes: Data transfer size + * @direction: Data transfer direction between L2 memory and external RAM + * @intr_enable: Open interrupt for this L2 buffer or not + */ +void l2_combuf_dma(unsigned long ram_addr, u8 id, unsigned int bytes, l2_dma_transfer_direction_t direction, bool intr_enable); + +/** + * l2_combuf_wait_dma_finish - Wait for L2 DMA finish + * @id: L2 buffer ID involved in DMA transfer + * Return true: DMA transfer finished successfully. + * false: DMA transfer failed. + * NOTE: DMA transfer is started by l2_combuf_dma. + */ +bool l2_combuf_wait_dma_finish(u8 id); + +/** + * l2_combuf_cpu - Transfer data between memory and l2 common buffer in CPU mode + * @ram_addr: External RAM address(Physical) + * @id: L2 buffer ID + * @bytes: Data transfer size + * @direction: Data transfer direction between L2 memory and external RAM + * + * NOTE: According to XuChang, if one transfer data from Peripheral --> L2 Buffer --> RAM, + * special care need to be taken when data size is NOT multiple of 64Bytes. + * Pheripheral driver must check hardware signals to confirm data has been transfer from + * peripheral to L2 buffer since L2 do NOT provide some mechanism to confirm data has + * been in L2 Buffer. Driver can and only can call l2_combuf_cpu() to copy data from L2 + * Buffer --> RAM after checking hardware signals. + * As to 64Bytes * n size data, L2 could check Buffer Status Status Counter to confirm that + * Data has been transfer from peripheral to L2 buffer, so no hardware signals checking needed. + */ +void l2_combuf_cpu(unsigned long ram_addr, u8 id, unsigned int bytes, l2_dma_transfer_direction_t direction); + +/** + * l2_get_status - Get L2 buffer status + * @id: L2 buffer ID + */ +u8 l2_get_status(u8 id); + +/** + * l2_clr_status - Clear L2 buffer status + * @id: L2 buffer ID + */ +void l2_clr_status(u8 id); + +/** + * l2_set_status - Clear L2 buffer status + * @id: L2 buffer ID + * @status: Status to be set (0 ~ 8) + */ +void l2_set_status(u8 id, u8 status); + +static inline void l2_enable_clock(int enable) +{ + if (enable) + REG32(L2_CLOCK_REG) &= ~L2_CLOCK_EN; + else + REG32(L2_CLOCK_REG) |= L2_CLOCK_EN; + return; +} + +#ifdef L2_DEBUG +#define L2_PRINT_FUNCLINES() do { printk("%s(): line: %d\n", __func__, __LINE__); } while (0) + +static inline void l2_dump_registers(void) +{ + printk("ANYKA L2 Register Dumping Begin:\n"); + + printk(" rL2_DMAREQ(C080) = 0x%08X, rL2_FRACDMAADDR(C084) = 0x%0X\n", + (unsigned int)rL2_DMAREQ, (unsigned int)rL2_FRACDMAADDR); + printk(" rL2_CONBUF0_7(C088) = 0x%08X, rL2_CONBUF8_15(C08C) = 0x%0X\n", + (unsigned int)rL2_CONBUF0_7, (unsigned int)rL2_CONBUF8_15); + printk(" rL2_BUFASSIGN1(C090) = 0x%08X, rL2_BUFINTEN(C09C) = 0x%0X\n", + (unsigned int)rL2_BUFASSIGN1, (unsigned int)rL2_BUFINTEN); + printk(" rL2_BUFSTAT1(C0A0) = 0x%08X, rL2_BUFSTAT2(C0A8) = 0x%0X\n", + (unsigned int)rL2_BUFSTAT1, (unsigned int)rL2_BUFSTAT2); + + printk("ANYKA L2 Register Dumping End.\n"); +} + +static inline void l2_print_array(const char *name, unsigned char *array, int len) +{ + int i; + + printk("%s[%d] = {\n ", name, len); + for (i = 0; i < len; i++) { + printk(" 0x%02X,", array[i]); + if (i % 16 == 15) + printk("\n "); + } + printk("};\n"); + +} + +#else +#define L2_PRINT_FUNCLINES() do { } while (0) + +static inline void l2_dump_registers(void) +{ +} +static inline void l2_print_array(const char *name, unsigned int *array, int len) +{ +} +#endif + +#endif /* __ASM_ARCH_L2_H */ + diff --git a/arch/arm/plat-anyka/include/plat/l2_exebuf.h b/arch/arm/plat-anyka/include/plat/l2_exebuf.h new file mode 100644 index 00000000..d5c966a3 --- /dev/null +++ b/arch/arm/plat-anyka/include/plat/l2_exebuf.h @@ -0,0 +1,179 @@ +#ifndef __L2_EXEBUF +#define __L2_EXEBUF + + +#include +#include +#include + + +#define CPU_CHIP_ID (AK_VA_SYSCTRL + 0x00) +#define CLOCK_DIV_REG (AK_VA_SYSCTRL + 0x04) +#define CLOCK_CTRL_REG (AK_VA_SYSCTRL + 0x0C) +#define PHY_CLOCK_CTRL_REG (AK_PA_SYSCTRL + 0x0C) +#define PHY_CLOCK_DIV_REG (AK_PA_SYSCTRL + 0x04) +#define GPI0_WAKEUP_POL_REG (AK_PA_SYSCTRL + 0x3C) +#define GPI0_WAKEUP_STACLR (AK_PA_SYSCTRL + 0x40) +#define GPI0_WAKEUP_FUN_EN (AK_PA_SYSCTRL + 0x44) +#define GPI0_WAKEUP_STA (AK_PA_SYSCTRL + 0x48) +#define SHAREPIN_CTL1_REG (AK_PA_SYSCTRL + 0x74) +#define SHAREPIN_CTL2_REG (AK_PA_SYSCTRL + 0x78) +#define SHAREPIN_CTL3_REG (AK_PA_SYSCTRL + 0x7C) +#define PULL_UP_DOWN_REG (AK_PA_SYSCTRL + 0x80) + + +#define PHY_RAM_CFG_REG4 (AK_PA_REGRAM + 0x0C) +#define RAM_CFG_REG1 (AK_VA_REGRAM + 0x00) +#define RAM_CFG_REG2 (AK_VA_REGRAM + 0x04) +#define RAM_CFG_REG3 (AK_VA_REGRAM + 0x08) +#define RAM_CFG_REG4 (AK_VA_REGRAM + 0x0C) +#define RAM_CPU_CMD (AK_VA_REGRAM + 0x10) +#define PHY_RAM_CFG_REG1 (AK_PA_REGRAM + 0x00) +#define PHY_RAM_CFG_REG2 (AK_PA_REGRAM + 0x04) +#define PHY_RAM_CFG_REG3 (AK_PA_REGRAM + 0x08) +#define PHY_RAM_CFG_REG4 (AK_PA_REGRAM + 0x0C) +#define PHY_RAM_CPU_CMD (AK_PA_REGRAM + 0x10) + +#define GPIO_DIR_REG (AK_PA_GPIO + 0x00) +#define GPIO_DIR_REG1 (AK_PA_GPIO + 0x04) +#define GPIO_OUT_DATA_REG1 (AK_PA_GPIO + 0x08) +#define GPIO_OUT_DATA_REG2 (AK_PA_GPIO + 0x0C) +#define GPIO_IN_DATA_REG1 (AK_PA_GPIO + 0x10) +#define GPIO_IN_DATA_REG2 (AK_PA_GPIO + 0x14) + + +#define FIFO_R_EMPTY (1 << 16) +#define FIFO_CMD_EMPTY (1 << 14) +#define AUTO_REFRESH_EN (1 << 0) +#define RAM_CLOCK_DISABLE (1 << 10) +#define ENTER_STANDBY (1 << 13) +#define REFRESH_PERIOD_INTERVAL (0x39f << 1) +#define USB_WAKEUP_EN (1 << 14) +#define RTC_TIME_WAKEUP_EN (1 << 13) +#define RTC_INT_WAKEUP_EN (1 << 12) +#define AIN0_WAKEUP_EN (1 << 11) +#define AIN1_WAKEUP_EN (1 << 10) + +#if 0 +#define LED_INIT_DEBUG do {\ + unsigned long value;\ + REG32(AK_VA_SYSCTRL + 0x00a8) |= (1 << 16);\ + REG32(AK_VA_SYSCTRL + 0x0094) &= ~(1 << 16);\ + } while(0) +#define LED_PHY_ON do {\ + REG32(AK_PA_SYSCTRL + 0x0098) &= ~(1 << 16);\ + } while(0) + +#define LED_PHY_OFF do {\ + REG32(AK_PA_SYSCTRL + 0x0098) |= (1 << 16);\ + } while(0) + +#define LED_VIRT_ON do {\ + REG32(AK_VA_SYSCTRL + 0x0098) &= ~(1 << 16);\ + } while(0) + +#define LED_VIRT_OFF do {\ + REG32(AK_VA_SYSCTRL + 0x0098) |= (1 << 16);\ + } while(0) +#endif + +#define DISABLE_CACHE_MMU() do { \ + __asm__ __volatile__( \ + "tci_loop: mrc p15, 0, r15, c7, c14, 3\n\t" /* test,clean,invalidate D cache */\ + "bne tci_loop\n\t" \ + "mcr p15, 0, %0, c8, c7, 0\n\t" /* invalidate I & D TLBs */ \ + "mcr p15, 0, %0, c7, c5, 0\n\t" /* invalidate I caches */ \ + "mrc p15, 0, %0, c1, c0, 0\n\t" \ + "bic %0, %0, #0x1000\n\t" /* disable Icache */ \ + "bic %0, %0, #0x0005\n\t" /* disable Dcache,mmu*/ \ + "ldr %1, =l2_phys_run\n\t" /* load 0x480000xx address */ \ + "b suspend_turn_off_mmu\n\t" \ + " .align 5\n\t" /* 32 byte aligned */ \ + "suspend_turn_off_mmu:\n\t"\ + "mcr p15, 0, %0, c1, c0, 0\n\t"\ + "mov pc, %1\n\t" /* jumpto 0x480000xx then run */ \ + "l2_phys_run:\n\t" /* mark the real running addr--> L2 buff */\ + : : "r"(0),"r"(1)); \ + } while(0) + +#define ENABLE_CACHE_MMU() do { \ + __asm__ __volatile__( \ + "mcr p15, 0, %0, c8, c7, 0\n\t" /* invalidate I & D TLBs */ \ + "mcr p15, 0, %0, c7, c7, 0\n\t" /* invalidate I & D caches */\ + "mcr p15, 0, %0, c7, c10, 4\n\t" /* Drain write buffer */ \ + "mrc p15, 0, %0, c1, c0, 0\n\t" \ + "orr %0, %0, #0x1000\n\t" \ + "orr %0, %0, #0x0005\n\t" \ + "b resume_turn_on_mmu\n\t" \ + " .align 5\n\t" \ + "resume_turn_on_mmu:\n\t" \ + "mcr p15, 0, %0, c1, c0, 0\n\t" \ + ::"r"(2)); \ + } while(0) + +#define PM_DELAY(time) do { \ + __asm__ __volatile__( \ + "1:\n\t" \ + "subs %0, %0, #1\n\t" \ + "bne 1b\n\t" \ + ::"r"(time)); \ + } while(0) + + +#define DDR2_ENTER_POWERDOWN() do {\ + /* send precharge all banks */\ + __raw_writel(0x0aa00400, PHY_RAM_CPU_CMD);\ + /* close odt and asserting low on cke ,close odt and send enter powerdown command */\ + __raw_writel(0x04f00000, PHY_RAM_CPU_CMD);\ + }while(0) + +#define DDR2_EXIT_POWERDOWN() do {\ + /* exit precharge power-down mode after delay at least 3 tck */\ + /* by asserting high on cke and odt remain low */\ + __raw_writel(0x02f00000, PHY_RAM_CPU_CMD);\ + }while(0) + + +#define DDR2_ENTER_SELFREFRESH() do {\ + /* send precharge all banks */\ + __raw_writel(0x0aa00400, PHY_RAM_CPU_CMD);\ + /* close odt and delay taofd=2.5 tck */\ + __raw_writel(0x02f00000, PHY_RAM_CPU_CMD);\ + PM_DELAY(0x1);\ + /* asserting low on cke ,close odt and entry self-refresh mode */\ + __raw_writel(0x04c00000, PHY_RAM_CPU_CMD);\ + }while(0) + +#define DDR2_EXIT_SELFREFRESH() do {\ + /* exit self-refresh by asserting high on cke and odt remain low */\ + __raw_writel(0x02f00000, PHY_RAM_CPU_CMD);\ + /* delay txsrd=200tck as send nop cmd */ \ + __raw_writel(0x02f00000, PHY_RAM_CPU_CMD);\ + PM_DELAY(0x8);\ + }while(0) + +#define DDR2_ENTER_AUTOREFRESH() do {\ + /* send auto refresh and open odt high */\ + __raw_writel(0x0ac00000, PHY_RAM_CPU_CMD);\ + __raw_writel(0x0ac00000, PHY_RAM_CPU_CMD);\ + __raw_writel(0x0ac00000, PHY_RAM_CPU_CMD);\ + }while(0) + + +#define L2_LINK(flag) __section(.l2mem_##flag) +#define L2FUNC_NAME(name) l2_enter_##name + +#define SPECIFIC_L2BUF_EXEC(flag, param1,param2,param3,param4) do {\ + extern char _end_##flag[], _start_##flag[];\ + int len;\ + len = _end_##flag - _start_##flag;\ + l2_exec_buf(_start_##flag,len, param1,param2,param3,param4);\ +}while(0) + +int l2_exec_buf(const char *vaddr, int len, unsigned long param1, + unsigned long param2,unsigned long param3, unsigned long param4); + + +#endif /* L2_EXEBUF */ + + diff --git a/arch/arm/plat-anyka/include/plat/rtc.h b/arch/arm/plat-anyka/include/plat/rtc.h new file mode 100644 index 00000000..92b9ac6a --- /dev/null +++ b/arch/arm/plat-anyka/include/plat/rtc.h @@ -0,0 +1,141 @@ +/* + * linux/arch/arm/plat-anyka/include/plat/rtc.h + * + * AK RTC related routines + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __ASM_ARCH_RTC_H +#define __ASM_ARCH_RTC_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define EPOCH_START_YEAR (1900) +#define RTC_START_YEAR (1980) +#define RTC_YEAR_COUNT (127) + +#define AK_RTC_CONF (AK_VA_SYSCTRL + 0x50) +#define AK_RTC_DATA (AK_VA_SYSCTRL + 0x54) +#define OTHER_WAKEUP_CTRL (AK_VA_SYSCTRL + 0x34) +#define OTHER_WAKEUP_STAT (AK_VA_SYSCTRL + 0x38) + +#define RTC_RDY_INT_CTRL (AK_VA_SYSCTRL + 0x2C) +#define RTC_RDY_INT_STAT (AK_VA_SYSCTRL + 0x30) +#define RTC_RDY_CTRL_BIT (1 << 7) +#define RTC_RDY_STAT_BIT (1 << 7) +#define RTC_TIMER0_CTRL_BIT (1 << 5) +#define RTC_TIMER0_STAT_BIT (1 << 5) + +#define RTC_WAKEUP_EN (1 << 12) +#define RTC_CONF_RTC_WR_EN (1 << 25) +#define RTC_CONF_RTC_EN (1 << 24) +#define RTC_CONF_RTC_READ ((1 << 21) | (2 << 18) | (1 << 17)) +#define RTC_CONF_RTC_WRITE ((1 << 21) | (2 << 18) | (0 << 17)) + +#define AK_RTC_REAL_TIME1 (0x0) +#define AK_RTC_REAL_TIME2 (0x1) +#define AK_RTC_REAL_TIME3 (0x2) +#define AK_RTC_ALARM_TIME1 (0x3) +#define AK_RTC_ALARM_TIME2 (0x4) +#define AK_RTC_ALARM_TIME3 (0x5) +#define AK_WDT_RTC_TIMER_CONF (0x6) +#define AK_RTC_SETTING (0x7) +#define AK_RTC_REG_MAX AK_RTC_SETTING + +#define RTC_ON 1 +#define RTC_OFF 0 +#define RTC_INTERNAL_RC_OSC 1 +#define RTC_EXTERNAL_XTAL 0 + +#define RTC_SETTING_REAL_TIME_RE (1 << 4) +#define RTC_SETTING_REAL_TIME_WR (1 << 3) +//#define RTC_WAIT_TIME_OUT 2000 // +#define RTC_WAIT_TIME_OUT 500 // 500ms + +/* + * When the RTC module begins to receive/send data, bit [24] of Interrupt Enable/Status + * Register of System Control Module (Add: 0x0800, 004C) is set to 0; and then this + * bit is set to 1 automatically to indicate that the data has been well received/sent + */ +static void inline ak_rtc_wait_ready(void) +{ + unsigned long timeout = 0; + + while (!(__raw_readl(RTC_RDY_INT_STAT) & RTC_RDY_STAT_BIT)) { + ++timeout; + msleep(1); + if (timeout >= RTC_WAIT_TIME_OUT) { + printk("ak_rtc_wait_ready timeout\n"); + break; + } + } +} + +static void inline rtc_ready_irq_enable(void) +{ + unsigned long regval; + + /* + * Enable RTC Ready Interrupt + */ + regval = __raw_readl(RTC_RDY_INT_CTRL); + __raw_writel(regval | (RTC_RDY_CTRL_BIT), RTC_RDY_INT_CTRL); + + /* + * Wait for RTC Ready Interrupt to be cleared + */ + ak_rtc_wait_ready(); + + /* + * Enable RTC Register Read/Write + */ + regval = __raw_readl(AK_RTC_CONF); + regval |= RTC_CONF_RTC_WR_EN; + __raw_writel(regval, AK_RTC_CONF); +} + +static void inline rtc_ready_irq_disable(void) +{ + unsigned long regval; + /* + * Disable RTC Register Read/Write + */ + regval = __raw_readl(AK_RTC_CONF); + regval &= ~RTC_CONF_RTC_WR_EN; + __raw_writel(regval, AK_RTC_CONF); + + /* + * Disable RTC Ready Interrupt + */ + regval = __raw_readl(RTC_RDY_INT_CTRL); + __raw_writel(regval & ~RTC_RDY_CTRL_BIT, RTC_RDY_INT_CTRL); +} + +void ak_rtc_power(int op); +unsigned int read_rtc_reg_try(unsigned int addr); +unsigned int ak_rtc_read(unsigned int addr); +unsigned int ak_rtc_write(unsigned int addr, unsigned int value); +unsigned int ak_rtc_set_wpin(bool level); +void ak_reboot_sys_by_wtd(void); +void ak_reboot_sys_by_wakeup(void); +int test_rtc_inter_reg(void); + + +#endif /* __ASM_ARCH_RTC_H */ diff --git a/arch/arm/plat-anyka/l2.c b/arch/arm/plat-anyka/l2.c new file mode 100644 index 00000000..17436533 --- /dev/null +++ b/arch/arm/plat-anyka/l2.c @@ -0,0 +1,1180 @@ +/* + * linux/arch/arm/plat-anyka/l2.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +static l2_buffer_info_t l2_buffer_info[L2_COMMON_BUFFER_NUM]; +static l2_dma_info_t l2_dma_info[L2_COMMON_BUFFER_NUM + L2_UART_BUFFER_NUM]; +static bool l2_frac_started = false; /* L2 fraction DMA start flag */ + +static l2_device_info_t l2_device_info[] = { + { ADDR_USB_EP1, BUF_NULL }, + { ADDR_USB_EP2, BUF_NULL }, + { ADDR_USB_EP3, BUF_NULL }, + { ADDR_RESERVED, BUF_NULL }, + { ADDR_MMC_SD, BUF_NULL }, + { ADDR_SDIO, BUF_NULL }, + { ADDR_RESERVED, BUF_NULL }, + { ADDR_SPI1_RX, BUF_NULL }, + { ADDR_SPI1_TX, BUF_NULL }, + { ADDR_DAC, BUF_NULL }, + { ADDR_SPI2_RX, BUF_NULL }, + { ADDR_SPI2_TX, BUF_NULL }, + { ADDR_GPS, BUF_NULL }, + { ADDR_PCM_TX, BUF_NULL }, + { ADDR_ADC, BUF_NULL }, + { ADDR_USB_EP4, BUF_NULL }, + { ADDR_RESERVED, BUF_NULL }, +}; + +static int l2_wait = 0; +static wait_queue_head_t l2_wq; + +extern void l2cache_invalidate(void); + +static void l2_combuf_ctrl(u8 id, bool enable); +static void l2_select_combuf(l2_device_t device, u8 id); +static void l2_assert_combuf_id(u8 id); +static void l2_assert_buf_id(u8 id); +static void l2_clear_dma(u8 id); +static void l2_frac_dma(unsigned long ram_addr, u8 id, u8 frac_offset, + unsigned int bytes, l2_dma_transfer_direction_t direction, bool intr_enable); +static u32 l2_get_addr(u8 id); +static bool l2_get_dma_param(unsigned int bytes, unsigned int *low, unsigned int *high); +static void l2_dma(unsigned long ram_addr, u8 id, unsigned int bytes, + l2_dma_transfer_direction_t direction, bool intr_enable); +static bool l2_wait_dma_finish(u8 id); +static void l2_cpu(unsigned long ram_addr, u8 id, + unsigned long buf_offset, unsigned int bytes, l2_dma_transfer_direction_t direction); +static irqreturn_t l2_interrupt_handler(int irq, void *dev_id); + +/** + * l2_assert_buf_id - Assert a L2 buffer ID is valid + * @id: L2 buffer ID + * + * NOTE: Assert only L2 common buffer and UART buffer, USB buffer is not checked. + * Since this function is called internally by other L2 API, invalid id will cause + * linux kernel to oops for bug tracking. + */ +static void l2_assert_buf_id(u8 id) +{ + if (id >= L2_COMMON_BUFFER_NUM + L2_UART_BUFFER_NUM) + BUG(); +} + +/** + * l2_assert_combuf_id - Assert a L2 common buffer ID is valid + * @id: L2 buffer ID + * + * NOTE: Assert only L2 common buffer, UART & USB buffer is not checked. + * Since this function is called internally by other L2 API, invalid id will cause + * linux kernel to oops for bug tracking. + */ +static void l2_assert_combuf_id(u8 id) +{ + if (id >= L2_COMMON_BUFFER_NUM) + BUG(); +} + +/** + * l2_combuf_ctrl - L2 buffer enable/disable + * @id: L2 buffer ID + * @enable: true to enable L2 buffer, false to disable L2 buffer + */ +static void l2_combuf_ctrl(u8 id, bool enable) +{ + unsigned long regval; + unsigned long flags; + + l2_assert_buf_id(id); + + local_irq_save(flags); + + regval = rL2_CONBUF0_7; + if (enable) { + /* Enable L2 buffer & L2 Buffer DMA */ + regval |= (1 << (id + L2_COMMON_BUF_CFG_BUF_DMA_VLD_START)) | + (1 << (id + L2_COMMON_BUF_CFG_BUF_VLD_START)); + } else { + /* Disable L2 buffer & L2 Buffer DMA */ + regval &= ~((1 << (id + L2_COMMON_BUF_CFG_BUF_DMA_VLD_START)) | + (1 << (id + L2_COMMON_BUF_CFG_BUF_VLD_START))); + } + rL2_CONBUF0_7 = regval; + + local_irq_restore(flags); + +} + +/** + * l2_select_combuf - Select a L2 buffer for given device + * @device: Device which need to assign a L2 buffer + * @id: L2 buffer ID + */ +static void l2_select_combuf(l2_device_t device, u8 id) +{ + unsigned long regval; + unsigned long bits_offset; + + l2_assert_combuf_id(id); + + if ((u8)device < 10) { + /* + * USB Bulkout ~ DAC (Device 0 ~ 9) is controlled by Buffer Assignment Register 1 + */ + regval = rL2_BUFASSIGN1; + bits_offset = (u8)device * 3; + regval &= ~(0x7 << bits_offset); + regval |= ((id & 0x7) << bits_offset); + rL2_BUFASSIGN1 = regval; + } else { + /* + * SPI2 Rx ~ ADC (Device 10 ~ 14) is controlled by Buffer Assignment Register 2 + */ + regval = rL2_BUFASSIGN2; + bits_offset = ((u8)device - 10) * 3; + regval &= ~(0x7 << bits_offset); + regval |= ((id & 0x7) << bits_offset); + rL2_BUFASSIGN2 = regval; + } + +} + + +/** + * l2_deselect_combuf - deselect a L2 buffer for given device + * @device: Device which need to assign a L2 buffer + * @id: L2 buffer ID + * + * note: buffer 0 is reserved for softuse, no hardware is assigned + * so when free a l2 buffer, select to this device use buffer 0 as deselect + */ +static void l2_deselect_combuf(l2_device_t device, u8 id) +{ + unsigned long regval; + unsigned long bits_offset; + + l2_assert_combuf_id(id); + + if ((u8)device < 10) { + /* + * USB Bulkout ~ DAC (Device 0 ~ 9) is controlled by Buffer Assignment Register 1 + */ + regval = rL2_BUFASSIGN1; + bits_offset = (u8)device * 3; + regval &= ~(0x7 << bits_offset); + rL2_BUFASSIGN1 = regval; + } else { + /* + * SPI2 Rx ~ ADC (Device 10 ~ 14) is controlled by Buffer Assignment Register 2 + */ + regval = rL2_BUFASSIGN2; + bits_offset = ((u8)device - 10) * 3; + regval &= ~(0x7 << bits_offset); + rL2_BUFASSIGN2 = regval; + } + +} + +/** + * l2_clear_dma - Clear L2 buffer DMA status + * @id: L2 buffer ID which need to clear DMA status + */ +static void l2_clear_dma(u8 id) +{ + bool dmapending; + u8 status; + + dmapending = rL2_DMAREQ & (1 << (id + L2_DMA_REQ_BUF_START)); + status = l2_get_status(id); + + if (status == 0) { + return ; /* NO DMA request, so do nothing */ + } + + if(l2_dma_info[id].direction == BUF2MEM) { + printk("l2r:[%d]..", id); + while (dmapending) { + l2_set_status(id, 8); + dmapending = rL2_DMAREQ & (1 << (id + L2_DMA_REQ_BUF_START)); + } + printk("done\n"); + } else { + /* + * Wait until DMA request of this L2 buffer is finished. + */ + printk("l2t:[%d]..", id); + while (dmapending) { + l2_clr_status(id); + dmapending = rL2_DMAREQ & (1 << (id + L2_DMA_REQ_BUF_START)); + } + printk("done\n"); + } +} + +/** + * l2_frac_dma - Start data tranferring between memory and l2 common buffer in fraction DMA mode + * @ram_addr: External RAM address(Physical) + * @id: L2 buffer ID involved in DMA transfer + * @frac_offset: The region offset between buffer start address and transfer start address + * @bytes: Data transfer size + * @direction: Data transfer direction between L2 memory and external RAM + * @intr_enable: Open interrupt for this L2 buffer or not + * + * NOTE: Data transfer size should be 1~64Bytes, frac_offset should be 0~7 (*64Bytes) + */ +static void l2_frac_dma(unsigned long ram_addr, u8 id, u8 frac_offset, + unsigned int bytes, l2_dma_transfer_direction_t direction, bool intr_enable) +{ + u32 bufaddr; + u32 highaddr; + unsigned long regval; + unsigned long flags; + +#if 0 + printk("%s(): ram_addr=0x%08X, l2 buffer id=%d, frac_offset=%d, bytes=%d, direction=%s, intr_enable=%d.\n", + __func__, (unsigned int)ram_addr, id, frac_offset, bytes, (direction == BUF2MEM)?"BUF2MEM":"MEM2BUF", intr_enable); +#endif + + if (bytes == 0) { + printk("l2: no need to start fraction dma transfer: bytes=0.\n"); + return ; + } + + local_irq_save(flags); + + /* + * Set fraction external RAM address. + */ + highaddr = (ram_addr << 2) & 0xC0000000; + + regval = rL2_FRACDMAADDR; + regval &= ~(L2_FRAC_DMA_LOW_ADDR_MASK | (3<<30)); //modified by anyka chenyingyu + regval |= (ram_addr & L2_FRAC_DMA_LOW_ADDR_MASK) | highaddr; + rL2_FRACDMAADDR = regval; + + /* Set fraction DMA address */ + bufaddr = (id < L2_COMMON_BUFFER_NUM) ? ((id & 0x7) << 3) | (frac_offset & 0x7) : + (0x40 + ((id - L2_COMMON_BUFFER_NUM) << 1)) | (frac_offset & 0x1); + + /* Clear other fraction DMA request and info */ + regval = rL2_DMAREQ; + regval &= ~(L2_DMA_REQ_FRAC_DMA_LEN_MASK | L2_DMA_REQ_FRAC_DMA_L2_ADDR_MASK | + L2_DMA_REQ_FRAC_DMA_REQ | L2_DMA_REQ_BUF_REQ_MASK); + + switch (direction) { + case MEM2BUF: + if (bytes & 0x1) + bytes = bytes + 1; /* Round to even number when read data from external ram */ + regval |= L2_DMA_REQ_FRAC_DMA_REQ | L2_DMA_REQ_FRAC_DMA_DIR_WR | + (bufaddr << L2_DMA_REQ_FRAC_DMA_L2_ADDR_START) | + ((bytes - 1) << L2_DMA_REQ_FRAC_DMA_LEN_START); + rL2_DMAREQ = regval; + break; + case BUF2MEM: + regval &= ~(L2_DMA_REQ_FRAC_DMA_DIR_WR); + regval |= L2_DMA_REQ_FRAC_DMA_REQ | + (bufaddr << L2_DMA_REQ_FRAC_DMA_L2_ADDR_START) | + ((bytes - 1) << L2_DMA_REQ_FRAC_DMA_LEN_START); + rL2_DMAREQ = regval; + break; + default: + BUG(); + } + + if (intr_enable) { + regval = rL2_BUFINTEN; + regval |= L2_DMA_INTR_ENABLE_FRAC_INTR_EN; + rL2_BUFINTEN = regval; + } + + local_irq_restore(flags); +} + +/** + * l2_get_addr - Get L2 memory start address for given L2 buffer + * @id: L2 buffer ID + * Return L2 memory start address(Logical/Virtual) (NOT physical address) + */ +static u32 l2_get_addr(u8 id) +{ + u32 bufaddr = 0; + + if (id < L2_UART_BUFFER_INDEX) { /* L2 common buffer */ + bufaddr = (u32)AK_VA_L2MEM + L2_COMMON_BUFFER_OFFSET + + id * L2_COMMON_BUFFER_LEN; + } else if (id < L2_USB_HOST_BUFFER_INDEX) { /* UART L2 buffer */ + bufaddr = (u32)AK_VA_L2MEM + L2_UART_BUFFER_OFFSET + + (id - L2_COMMON_BUFFER_NUM) * L2_UART_BUFFER_LEN; + } else { + printk("l2: invalid buffer id %d.\n", (int)id); + } + + return bufaddr; +} + +/** + * l2_get_dma_param - Calculate l2 buffer big loop/small loop counter value + * @bytes: L2 buffer ID + * @low: CNT_cfg (bit[7:0] of DMA Operation Times Configuration Register) + * @high: CNT_cfg_H (bit[23:16] of DMA Operation Times Configuration Register) + * Return true when correct counter value (high/low) is found, else return false. + * + * NOTE: Use a simplified calculation method for L2 buffer 0~7 and 8~15 for bytes > 8KB + */ +static bool l2_get_dma_param(unsigned int bytes, unsigned int *low, unsigned int *high) +{ + unsigned int factor; + unsigned int dma_times = bytes / DMA_ONE_SHOT_LEN; + + if (bytes <= 8 * 1024) { + *low = dma_times; + *high = 0; + + return true; + } else if (dma_times & 0x7) { + printk("l2: Invalid L2 DMA buffer size(%u).\n", bytes); + return false; + } + + factor = 16 * 8; + + while (factor > 0) { + if ((dma_times % factor) == 0) { + *low = factor; + *high = dma_times / factor - 1; + + return (*high < 0xFF) ? true : false; + } + + factor -= 8; + } + + return false; +} + + +/** + * l2_dma - Start data tranferring between memory and l2 buffer in DMA mode + * @ram_addr: External RAM address(Physical) + * @id: L2 buffer ID involved in DMA transfer + * @bytes: Data transfer size + * @direction: Data transfer direction between L2 memory and external RAM + * @intr_enable: Open interrupt for this L2 buffer or not + */ +static void l2_dma(unsigned long ram_addr, u8 id, unsigned int bytes, + l2_dma_transfer_direction_t direction, bool intr_enable) +{ + unsigned long regid; + unsigned long regval; + unsigned long flags; + unsigned int cnt_low; + unsigned int cnt_high; + +#if 0 + printk("%s(): ram_addr=0x%0X, id=%d, bytes=%d, direction=%d, intr_enable=%d.\n", + __func__, (unsigned int)ram_addr, id, bytes, direction, intr_enable); +#endif + if (bytes == 0) { + printk("l2: no need to start dma transfer: bytes=0.\n"); + return ; + } + + if (!l2_get_dma_param(bytes, &cnt_low, &cnt_high)) { + printk("l2: L2 DMA buffer size error: bytes=%d.\n", bytes); + return ; + } + + if (l2_dma_info[id].dma_start || l2_dma_info[id].dma_frac_start) { + printk("l2: unable to start dma, dma NOT finished, buf id=%d.\n", (int)id); + return ; + } + + l2_dma_info[id].dma_op_times = bytes / DMA_ONE_SHOT_LEN; + l2_dma_info[id].dma_frac_data_len = bytes % DMA_ONE_SHOT_LEN; + l2_dma_info[id].dma_addr = (void *)ram_addr; + l2_dma_info[id].direction = direction; + l2_dma_info[id].intr_enable = intr_enable; + l2_dma_info[id].need_frac = false; + + if (l2_dma_info[id].dma_frac_data_len > 0) { + l2_dma_info[id].need_frac = true; + l2_dma_info[id].dma_frac_addr = (void *)(u8 *)l2_dma_info[id].dma_addr + + l2_dma_info[id].dma_op_times* DMA_ONE_SHOT_LEN; + l2_dma_info[id].dma_frac_offset = l2_dma_info[id].dma_op_times; + } + + if (l2_dma_info[id].dma_op_times== 0) { + /* + * If DMA transfer size < 64, we start fraction DMA immediately. + */ + + l2_dma_info[id].dma_start = false; + l2_dma_info[id].dma_frac_start = true; + + l2_frac_dma((unsigned long)l2_dma_info[id].dma_frac_addr, id, + l2_dma_info[id].dma_frac_offset, l2_dma_info[id].dma_frac_data_len, + l2_dma_info[id].direction, intr_enable); + return ; + } + l2_dma_info[id].dma_start = true; + + local_irq_save(flags); + + l2cache_invalidate(); + asm("MMU_Clean_Invalidate_Dcache:\n" "mrc p15,0,r15,c7,c14,3\n" "bne MMU_Clean_Invalidate_Dcache"); + + /* + * Set address of external RAM + */ + regval = (unsigned long)l2_dma_info[id].dma_addr; + regid = (unsigned long)vL2DMA_ADDRBUF0 + id * 4; + __raw_writel(regval, regid); + + /* + * Set DMA operation times + */ + regid = (unsigned long)vL2DMA_CONBUF0 + id * 4; + regval = (cnt_high << 16) | (cnt_low & 0xFF); + __raw_writel(regval, regid); + + /* + * Set DMA direction for L2 common buffer + */ + if (id < L2_COMMON_BUFFER_NUM) { + regval = rL2_CONBUF0_7; + if (l2_dma_info[id].direction == MEM2BUF) { + regval |= (1 << (id + L2_COMMON_BUF_CFG_BUF_DIR_START));; + } else { + regval &= ~(1 << (id + L2_COMMON_BUF_CFG_BUF_DIR_START)); + } + rL2_CONBUF0_7 = regval; + } + + + /* + * Start buffer DMA request + */ + regval = rL2_DMAREQ; + regval &= ~(L2_DMA_REQ_FRAC_DMA_REQ | L2_DMA_REQ_BUF_REQ_MASK); + if (id < L2_COMMON_BUFFER_NUM) { + regval |= (1 << (id + L2_DMA_REQ_BUF_START)); + } else { + regval |= (1 << ((id - L2_UART_BUF_START_ID + L2_UART_BUF_CFG_BUF_START))); + } + rL2_DMAREQ = regval; + + + /* + * Enable DMA interrupt now + */ + if (intr_enable) { + regval = rL2_BUFINTEN; + if (id < L2_COMMON_BUFFER_NUM) { + regval |= 1 << (id + L2_DMA_INTR_ENABLE_BUF_START); + } else { + regval |= 1 << (id - L2_COMMON_BUFFER_NUM + L2_DMA_INTR_ENABLE_UART_BUF_START); + } + rL2_BUFINTEN = regval; + } + + local_irq_restore(flags); +} + +/** + * l2_wait_dma_finish - Wait for L2 DMA to finish + * @id: L2 buffer ID involved in DMA transfer + * Return true: DMA transfer finished successfully. + * false: DMA transfer failed. + * NOTE: DMA transfer is started by l2_dma. + */ +static bool l2_wait_dma_finish(u8 id) +{ + unsigned int timeout; + unsigned long dmareq; + unsigned long dma_bit; + const unsigned int max_wait_time = L2_MAX_DMA_WAIT_TIME; + + timeout = 0; + if (l2_dma_info[id].dma_start) { + dma_bit = (id < L2_COMMON_BUFFER_NUM) ? (1 << (id + L2_DMA_REQ_BUF_START)) : + (1 << (id - L2_COMMON_BUFFER_NUM + L2_DMA_REQ_UART_BUF_REQ_START)); + do { + dmareq = rL2_DMAREQ; + } while((dmareq & dma_bit) && timeout++ < max_wait_time); + + l2_dma_info[id].dma_start = false; + + if (timeout >= max_wait_time) { + printk("l2: wait dma timeout, buf id=%d, status=%d.\n", id, l2_get_status(id)); + l2_clear_dma(id); + __raw_writel(0x0, vL2DMA_CONBUF0 + id * 4); + return false; + } + + /* + * If fraction DMA is NOT need, then everything is done. + */ + if (!l2_dma_info[id].need_frac) { + return true; + } + + + /* + * Start fraction DMA here for remain bytes transfer (<64Bytes). + */ + l2_dma_info[id].dma_frac_start = true; + l2_frac_dma((unsigned long)l2_dma_info[id].dma_frac_addr, id, + l2_dma_info[id].dma_frac_offset, l2_dma_info[id].dma_frac_data_len, + l2_dma_info[id].direction, false); + + } + + /* + * Fraction DMA handling starts here. + */ + if (l2_dma_info[id].dma_frac_start) { + timeout = 0; + do { + dmareq = rL2_DMAREQ; + } while((dmareq & L2_DMA_REQ_FRAC_DMA_REQ) && (timeout++ < max_wait_time)); + + l2_dma_info[id].dma_frac_start = false; + + if (timeout >= max_wait_time) { + printk("l2:wait frac dma timeout, buf id=%d, status=%d.\n", id, l2_get_status(id)); + return false; + } + + if ((l2_dma_info[id].direction == MEM2BUF) && + (l2_dma_info[id].dma_frac_data_len < 60)) { + + unsigned int bufaddr; + + bufaddr = l2_get_addr(id); + write_buf(0, bufaddr + (l2_dma_info[id].dma_frac_offset & 0x1FF) + 60); + } + } + + return true; +} + +/** + * l2_interrupt_handler - L2 memory interrupt handler + * @irq: IRQ number for L2 memory (Must be IRQ_L2MEM) + * @dev_id: Device specific information used by interrupt handler + * + * NOTE: Only shared IRQ need to check @irq & @dev_id. + * No need to check them here since L2 memory IRQ is NOT shared IRQ. + */ +static irqreturn_t l2_interrupt_handler(int irq, void *dev_id) +{ + unsigned long regval; + int i = 0; + + regval = rL2_DMAREQ; + + for (i = 0; i < L2_COMMON_BUFFER_NUM; i++) { + unsigned long dmapending = regval & (1 << ( i + L2_DMA_REQ_BUF_START)); + + if (l2_dma_info[i].dma_start && !dmapending) { + if (!l2_frac_started && l2_dma_info[i].need_frac) { + l2_dma_info[i].dma_frac_start = true; + l2_dma_info[i].dma_start = false; + + l2_frac_dma((unsigned long)l2_dma_info[i].dma_frac_addr, i, + l2_dma_info[i].dma_frac_offset, l2_dma_info[i].dma_frac_data_len, + l2_dma_info[i].direction, true); + + l2_frac_started = true; + } else { + /* DMA has finished */ + unsigned long regval; + + regval = rL2_BUFINTEN; + regval &= ~(1 << (i + L2_DMA_INTR_ENABLE_BUF_START)); + rL2_BUFINTEN = regval; + + l2_dma_info[i].dma_start = false; + + if (l2_dma_info[i].callback_func != NULL) + l2_dma_info[i].callback_func(l2_dma_info[i].data); + + } + } + + if (l2_dma_info[i].dma_frac_start) { + unsigned long frac_dmapending = regval & L2_DMA_REQ_FRAC_DMA_REQ; + if (l2_frac_started && !frac_dmapending) { + l2_frac_started = false; + + switch (l2_dma_info[i].direction) { + case MEM2BUF: + if (l2_dma_info[i].dma_frac_data_len <= 60) + __raw_writel(0x0, AK_VA_L2MEM + i * 512 + 0x1FC); + break; + case BUF2MEM: + if (l2_dma_info[i].dma_frac_data_len <= 512 - 4) + l2_clear_dma(i); + break; + default: + BUG(); + } + l2_dma_info[i].dma_frac_start = false; + + if (l2_dma_info[i].callback_func != NULL) + l2_dma_info[i].callback_func(l2_dma_info[i].data); + + } + } + + } + + return IRQ_HANDLED; +} + +/** + * l2_cpu - Transfer data between memory and l2 buffer in CPU mode + * @ram_addr: External RAM address(Physical) + * @id: L2 buffer ID + * @buf_offset: The buffer offset + * @bytes: Data transfer size + * @direction: Data transfer direction between L2 memory and external RAM + */ +static void l2_cpu(unsigned long ram_addr, u8 id, + unsigned long buf_offset, unsigned int bytes, l2_dma_transfer_direction_t direction) +{ + int i; + int j; + unsigned long trans_no; + unsigned long frac_no; + unsigned long buf_count; + unsigned long buf_remain; + unsigned long temp_ram; + unsigned long temp_buf; + unsigned long bufaddr; + + /* + * L2 buffer caller MUST guarantee L2 buffer offset is 4-byte aligned + */ + if (unlikely(buf_offset % 4)) + BUG(); + + bufaddr = l2_get_addr(id); + + if (bufaddr == 0) { + return ; + } + + bufaddr += buf_offset; + trans_no = bytes / 4; + frac_no = bytes % 4; + + buf_count = (buf_offset + bytes) / L2_BUF_STATUS_MULTIPLY_RATIO; + buf_remain = (buf_offset + bytes) % L2_BUF_STATUS_MULTIPLY_RATIO; + + switch (direction) { + case MEM2BUF: + if (ram_addr % 4) { + for (i = 0; i < trans_no; i++) { + temp_ram = 0; + for (j = 0; j < 4; j++) + temp_ram |= ((read_ramb(ram_addr + i*4 + j))<<(j*8)); + write_buf(temp_ram, (bufaddr + i * 4)); + } + if (frac_no) { + temp_ram = 0; + for (j = 0; j < frac_no; j++) + temp_ram |= ((read_ramb(ram_addr + trans_no*4 + j))<<(j*8)); + write_buf(temp_ram, (bufaddr + trans_no * 4)); + } + } else { + for (i = 0; i < trans_no; i++) + write_buf(read_raml(ram_addr + i*4), (bufaddr + i*4)); + if (frac_no) + write_buf(read_raml(ram_addr + trans_no*4), (bufaddr + trans_no*4)); + } + + /* + * If we do NOT write data to L2 in multiple of 64Bytes, we must write something to the 4Bytes in 64Bytes- + * boundary so that CPU knows writing ends.. + */ + if ((buf_remain > 0) && (buf_remain <= L2_BUF_STATUS_MULTIPLY_RATIO - 4)) + write_buf(0, (bufaddr - buf_offset + buf_count*L2_BUF_STATUS_MULTIPLY_RATIO + L2_BUF_STATUS_MULTIPLY_RATIO - 4)); + break; + case BUF2MEM: + if (ram_addr % 4) { + for (i = 0; i < trans_no; i++) { + temp_buf = read_buf(bufaddr + i * 4); + for (j = 0; j < 4; j++) + write_ramb((u8)((temp_buf>>j*8) & 0xFF), (ram_addr + i*4 + j)); + } + if (frac_no) { + temp_buf = read_buf(bufaddr+trans_no*4); + for (j = 0; j < frac_no; j++) + write_ramb((u8)((temp_buf>>j*8) & 0xFF), (ram_addr + trans_no*4 + j)); + } + } else { + for (i = 0; i < trans_no; i++) + write_raml(read_buf(bufaddr+i*4), (ram_addr+i*4)); + if (frac_no) { + temp_buf = read_buf(bufaddr+trans_no*4); + temp_ram = read_raml(ram_addr+trans_no*4); + temp_buf &= ((1<<(frac_no*8+1))-1); + temp_ram &= ~((1<<(frac_no*8+1))-1); + temp_ram |= temp_buf; + write_raml(temp_ram, (ram_addr+trans_no*4)); + } + } + + /* + * If we do NOT read data from L2 in multiple of 64Bytes, we must read the 4Bytes in 64Bytes- + * boundary so that CPU knows reading ends.. + */ + if ((buf_remain > 0) && (buf_remain <= L2_BUF_STATUS_MULTIPLY_RATIO - 4)) + temp_buf = read_buf(bufaddr-buf_offset+buf_count*L2_BUF_STATUS_MULTIPLY_RATIO+L2_BUF_STATUS_MULTIPLY_RATIO - 4); + break; + default: + BUG(); + } + +} + + +/** + * l2_init - Initialize linux kernel L2 memory support + */ +void __init l2_init(void) +{ + int i; + int retval; + + /* + * Enable L2 controller working clock + */ + l2_enable_clock(true); + + /* + * Initialize all L2 common buffer status to IDLE(could be allocated) + */ + for (i = 0; i < L2_COMMON_BUFFER_NUM; i++) { + l2_buffer_info[i].id = (u8)i; + l2_buffer_info[i].usable = L2_STAT_IDLE; + l2_buffer_info[i].used_time = 0; + } + + /* L2 Memory Register initializations */ + rL2_DMAREQ = L2_DMA_REQ_EN; + rL2_FRACDMAADDR = L2_FRAC_DMA_AHB_FLAG_EN | L2_FRAC_DMA_LDMA_FLAG_EN; + rL2_CONBUF0_7 = 0x0; + rL2_CONBUF8_15 = L2_UART_BUF_CFG_UART_EN_MASK | L2_UART_BUF_CFG_UART_CLR_MASK; + rL2_BUFINTEN = 0x0; + rL2_BUFASSIGN1 = 0x0; + rL2_BUFASSIGN2 = 0x0; + + /* Initialize L2 DMA information status */ + memset(l2_dma_info, 0, ARRAY_SIZE(l2_dma_info)); + + /* Initialize global L2 fraction DMA start flag */ + l2_frac_started = false; + + init_waitqueue_head(&l2_wq); + + /* L2 Memory Interrupt handler registered */ + if ((retval = request_irq(IRQ_L2MEM, &l2_interrupt_handler, IRQF_DISABLED, "l2", NULL)) < 0) + printk(KERN_ERR "l2: failed to request_irq, irq number: %d, retval=%d.\n", IRQ_L2MEM, retval); + + printk("On-chip L2 memory initialized\n"); +} + +/** + * __l2_alloc - Allocate a common L2 buffer for given device + * @device: Device ID which need common L2 buffer + * Return L2 buffer ID (0 ~ 7) + * + * Only common L2 buffers(ID 0 ~ 7) could be allocated by __l2_alloc. + * Other L2 buffers (UART/USB used) is handled by corresponding devices directly. + */ +static u8 __l2_alloc(l2_device_t device, bool need_wait) +{ + int i; + u16 used_times = MAX_L2_BUFFER_USED_TIMES; + u8 id = BUF_NULL; + u8 first_id = BUF_NULL; + unsigned long flags; + bool l2_allocated = false; + + if (unlikely(device == ADDR_RESERVED)) { + printk("l2: unable to allocate l2 buffer for reserved device.\n"); + + return BUF_NULL; + } + + if (unlikely(l2_device_info[(u8)device].id != BUF_NULL)) { + printk("l2: device %d already have a l2 buffer %d\n", + (int)(u8)device, (int)(u8)l2_device_info[(u8)device].id); + + return l2_device_info[(u8)device].id; + } + + do { + local_irq_save(flags); + + l2_allocated = false; + + for (i = 1; i < L2_COMMON_BUFFER_NUM; i++) { + if (l2_buffer_info[i].usable == L2_STAT_IDLE) { + if (first_id == BUF_NULL) { + first_id = l2_buffer_info[i].id; + used_times = l2_buffer_info[i].used_time; + id = first_id; + } + if (l2_buffer_info[i].used_time < used_times) { + used_times = l2_buffer_info[i].used_time; + id = l2_buffer_info[i].id; + } + } + } + + if (unlikely(first_id == BUF_NULL)) { + if(!need_wait) { + local_irq_restore(flags); + return BUF_NULL; + } + local_irq_restore(flags); + l2_wait = 0; + wait_event(l2_wq, l2_wait); + } else { + l2_allocated = true; + } + } while (!l2_allocated); + + /* + * Got a L2 buffer successfully... + */ + l2_buffer_info[id].usable = L2_STAT_USED; + l2_buffer_info[id].used_time++; + if (l2_buffer_info[id].used_time == 0) { + /* + * In case when the new allocated L2 buffer has been used MAX_L2_BUFFER_USED_TIMES, + * we just clear all L2 buffer used times as a simpfied method of balancing 8 L2 buffer usage. + */ + for (i = 0; i < L2_COMMON_BUFFER_NUM; i++) + l2_buffer_info[i].used_time = 0; + } + + /* Enable L2 buffer */ + l2_combuf_ctrl(id, true); + + /* Change device info */ + l2_device_info[device].id = id; + + /* Select L2 common buffer for device */ + l2_select_combuf(device, id); + + local_irq_restore(flags); + + /* Clear L2 buffer status */ + l2_clr_status(id); + + return id; +} + +u8 l2_alloc(l2_device_t device) +{ + return __l2_alloc(device, true); +} +EXPORT_SYMBOL(l2_alloc); + +u8 l2_alloc_nowait(l2_device_t device) +{ + return __l2_alloc(device, false); +} +EXPORT_SYMBOL(l2_alloc_nowait); + +/** + * l2_free - Free L2 common buffer for given device + * @device: Device ID which need common L2 buffer + * Return L2 buffer ID (0 ~ 7) + * + * Only common L2 buffers(ID 0 ~ 7) could be allocated by l2_alloc. + * Other L2 buffers (UART/USB used) is handled by corresponding devices directly. + * NOTE: Return the previous L2 buffer ID if a L2 buffer has been allocated to the device. + * This means one device could get only one L2 buffer maximum. + */ +void l2_free(l2_device_t device) +{ + u8 id; + unsigned long regval; + unsigned long flags; + + id = l2_device_info[(u8)device].id; + if (unlikely(id == BUF_NULL)) { + printk("l2: trying to free invalid buffer id %d\n", (int)id); + return ; + } + + l2_clear_dma(id); + + local_irq_save(flags); + + /* + * Disable DMA interrupt of this L2 buffer. + */ + regval = rL2_BUFINTEN; + regval &= ~(1 << (id + L2_DMA_INTR_ENABLE_BUF_START)); + rL2_BUFINTEN = regval; + + /* Set DMA count to 0 */ + __raw_writel(0x0, vL2DMA_CONBUF0 + id * 4); + + /* Disable this L2 buffer */ + l2_combuf_ctrl(id, false); + l2_deselect_combuf(device, id); + + /* Clear DMA & DMA fraction flags */ + if (l2_dma_info[id].dma_start || l2_dma_info[id].dma_frac_start) { + l2_dma_info[id].dma_start = false; + l2_dma_info[id].dma_frac_start = false; + } + + l2_dma_info[id].callback_func = NULL; + l2_dma_info[id].data = 0; + + l2_device_info[(u8)device].id = BUF_NULL; + l2_buffer_info[id].usable = L2_STAT_IDLE; + + l2_wait = 1; + wake_up(&l2_wq); + + local_irq_restore(flags); + +} +EXPORT_SYMBOL(l2_free); + +/** + * l2_set_dma_callback - Set callback function when L2 DMA/fraction DMA interrupt handler is done + * @id: L2 buffer ID + * @func: Callback function + * Return true(Always) + * + * NOTE: Caller MUST guarantee that L2 buffer ID is valid. And since the callback function is called + * in interrupt handler, it MUST NOT call any functions which may sleep. + */ +bool l2_set_dma_callback(u8 id, l2_callback_func_t func, unsigned long data) +{ + if (unlikely(id >= L2_COMMON_BUFFER_NUM)) { + printk(KERN_ERR "l2: Set dma callback, invalid buf id[%d].\n", id); + return false; + } + + if (unlikely(l2_dma_info[id].dma_start || l2_dma_info[id].dma_frac_start)) { + printk(KERN_ERR "l2: Set dma callback, dma not finished.\n"); + return false; + } + + l2_dma_info[id].callback_func = func; + l2_dma_info[id].data = data; + + return true; +} +EXPORT_SYMBOL(l2_set_dma_callback); + +/** + * l2_combuf_dma - Start data tranferring between memory and l2 common buffer in DMA mode + * @ram_addr: External RAM address(Physical) + * @id: L2 buffer ID involved in DMA transfer + * @bytes: Data transfer size + * @direction: Data transfer direction between L2 memory and external RAM + * @intr_enable: Open interrupt for this L2 buffer or not + */ +void l2_combuf_dma(unsigned long ram_addr, u8 id, unsigned int bytes, l2_dma_transfer_direction_t direction, bool intr_enable) +{ + if (unlikely(id >= L2_COMMON_BUFFER_NUM)) { + printk("l2: begin common buffer dma, error buf id=[%d].\n", id); + return ; + } + + l2_dma(ram_addr, id, bytes, direction, intr_enable); +} +EXPORT_SYMBOL(l2_combuf_dma); + +/** + * l2_combuf_wait_dma_finish - Wait for L2 DMA to finish + * @id: L2 buffer ID involved in DMA transfer + * Return true: DMA transfer finished successfully. + * false: DMA transfer failed. + * NOTE: DMA transfer is started by l2_combuf_dma. + */ +bool l2_combuf_wait_dma_finish(u8 id) +{ + if (unlikely(id >= L2_COMMON_BUFFER_NUM)) { + printk("l2: begin common buffer dma, error buf id=[%d].\n", id); + return false; + } + return l2_wait_dma_finish(id); +} +EXPORT_SYMBOL(l2_combuf_wait_dma_finish); + +/** + * l2_combuf_cpu - Transfer data between memory and l2 common buffer in CPU mode + * @ram_addr: External RAM address(Physical) + * @id: L2 buffer ID + * @bytes: Data transfer size + * @direction: Data transfer direction between L2 memory and external RAM + * + * NOTE: According to XuChang, if one transfer data from Peripheral --> L2 Buffer --> RAM, + * special care need to be taken when data size is NOT multiple of 64Bytes. + * Pheripheral driver must check hardware signals to confirm data has been transfer from + * peripheral to L2 buffer since L2 do NOT provide some mechanism to confirm data has + * been in L2 Buffer. Driver can and only can call l2_combuf_cpu() to copy data from L2 + * Buffer --> RAM after checking hardware signals. + * As to 64Bytes * n size data, L2 could check Buffer Status Status Counter to confirm that + * Data has been transfer from peripheral to L2 buffer, so no hardware signals checking needed. + */ +void l2_combuf_cpu(unsigned long ram_addr, u8 id, + unsigned int bytes, l2_dma_transfer_direction_t direction) +{ + int i; + int loop; + int remain; + + loop = bytes / L2_BUF_STATUS_MULTIPLY_RATIO; + remain = bytes % L2_BUF_STATUS_MULTIPLY_RATIO; + + switch (direction) { + case MEM2BUF: + for (i = 0; i < loop; i++) { + + while (l2_get_status(id) == (L2_BUFFER_SIZE / L2_BUF_STATUS_MULTIPLY_RATIO)) + ; /* Waiting for L2 buffer to NOT full(means writable) */ + + l2_cpu(ram_addr + i * L2_BUF_STATUS_MULTIPLY_RATIO, id, + (i % 8) * L2_BUF_STATUS_MULTIPLY_RATIO, L2_BUF_STATUS_MULTIPLY_RATIO, direction); + } + if (remain > 0) { + while (l2_get_status(id) > 0) + ; /* Waiting for L2 buffer to empty */ + + l2_cpu(ram_addr + loop * L2_BUF_STATUS_MULTIPLY_RATIO, id, + (loop % 8) * L2_BUF_STATUS_MULTIPLY_RATIO, remain, direction); + } + break; + case BUF2MEM: + for (i = 0; i < loop; i++) { + while (l2_get_status(id) == 0) + ; /* Waiting for L2 buffer to be not empty (means readable) */ + + l2_cpu(ram_addr + i * L2_BUF_STATUS_MULTIPLY_RATIO, id, + (i % 8) * L2_BUF_STATUS_MULTIPLY_RATIO, L2_BUF_STATUS_MULTIPLY_RATIO, direction); + + } + if (remain > 0) { + l2_cpu(ram_addr + loop * L2_BUF_STATUS_MULTIPLY_RATIO, id, + (loop % 8) * L2_BUF_STATUS_MULTIPLY_RATIO, remain, direction); + } + break; + default: + BUG(); + } +} +EXPORT_SYMBOL(l2_combuf_cpu); + +/** + * l2_get_status - Get L2 buffer status + * @id: L2 buffer ID + */ +u8 l2_get_status(u8 id) +{ + l2_assert_buf_id(id); + + return (id < L2_COMMON_BUFFER_NUM) ? (rL2_BUFSTAT1 >> (id * 4)) & 0xF : + (rL2_BUFSTAT2 >> ((id - L2_UART_BUF_START_ID) << 1)) & 0x3; +} +EXPORT_SYMBOL(l2_get_status); + +/** + * l2_clr_status - Clear L2 buffer status + * @id: L2 buffer ID + */ +void l2_clr_status(u8 id) +{ + unsigned long flags; + + l2_assert_buf_id(id); + + local_irq_save(flags); + + if (id < L2_COMMON_BUFFER_NUM) { + rL2_CONBUF0_7 |= 1 << (id + L2_COMMON_BUF_CFG_BUF_CLR_START); + } else { + rL2_CONBUF8_15 |= (1 << (id - L2_UART_BUF_START_ID + L2_UART_BUF_CFG_BUF_START)); + } + + local_irq_restore(flags); + +} +EXPORT_SYMBOL(l2_clr_status); + +/** + * l2_set_status - Clear L2 buffer status + * @id: L2 buffer ID + * @status: Status to be set (0 ~ 8) + */ +void l2_set_status(u8 id, u8 status) +{ + unsigned long regval; + unsigned long flags; + + l2_assert_buf_id(id); + + if ((id >= L2_COMMON_BUFFER_NUM) || status > MAX_L2_DMA_STATUS_VALUE) + BUG(); + + local_irq_save(flags); + + /* + * Enable CPU-controlled buffer function and set L2 buffer `id' status + * status = current number of data in the CPU controlled buffer. + */ + regval = rL2_CONBUF8_15; + regval &= ~(L2_UART_BUF_CFG_CPU_BUF_NUM_MASK | L2_UART_BUF_CFG_CPU_BUF_SEL_EN | + L2_UART_BUF_CFG_CPU_BUF_SEL_MASK); + regval |= (id << L2_UART_BUF_CFG_CPU_BUF_SEL_START) | L2_UART_BUF_CFG_CPU_BUF_SEL_EN | + (status << L2_UART_BUF_CFG_CPU_BUF_NUM_START); + rL2_CONBUF8_15 = regval; + + /* + * Disable CPU-controlled buffer function + */ + regval = rL2_CONBUF8_15; + regval &= ~(L2_UART_BUF_CFG_CPU_BUF_NUM_MASK | L2_UART_BUF_CFG_CPU_BUF_SEL_EN | + L2_UART_BUF_CFG_CPU_BUF_SEL_MASK); + rL2_CONBUF8_15 = regval; + + local_irq_restore(flags); + +} +EXPORT_SYMBOL(l2_set_status); diff --git a/arch/arm/plat-anyka/l2_exebuf.c b/arch/arm/plat-anyka/l2_exebuf.c new file mode 100644 index 00000000..50cb314e --- /dev/null +++ b/arch/arm/plat-anyka/l2_exebuf.c @@ -0,0 +1,67 @@ +/* + * arch/arm/plat-anyka/l2_exebuf.c + */ +#include +#include +#include + +// #define PM_DEBUG +#define L2_BUFFER0_SIZE 512 + +void (*jumpto_L2)(unsigned long param1,unsigned long param2, + unsigned long param3,unsigned long param4); + +static void pm_print_info(const char *start, int len) +{ +#ifdef PM_DEBUG + int i; + unsigned char local_l2mem[L2_BUFFER0_SIZE] = {0} ; + + printk("start = %p, len = %d\n", start, len); + for (i = 0; i < len; i += 4) { + *(unsigned long *)(local_l2mem + i) = + *(volatile unsigned long *)(AK_VA_L2MEM + i); + } + for (i = 0; i < len; i++) { + printk(" 0x%02x", local_l2mem[i]); + if (i % 16 == 15) printk("\n"); + } +#endif +} + + +/* + * copy from ddr2 to l2 memory to run, and exit standby + */ +int l2_exec_buf(const char *vaddr, int len, unsigned long param1, +unsigned long param2,unsigned long param3,unsigned long param4) +{ + unsigned long i, flags ; + + //disable ARM interrupt + local_irq_save(flags); + + memset((void *)AK_VA_L2MEM, 0, L2_BUFFER0_SIZE); + + //copy from ddr2 to l2 memory + for (i = 0; i < len; i += 4) { + *(volatile unsigned long *)(AK_VA_L2MEM + i) = + *(unsigned long *)(vaddr + i); + } + + pm_print_info(vaddr, len); + + REG32(AK_VA_L2CTRL + 0x84) &= ~(1 << 29); + + //jumpto_L2 run + jumpto_L2 = (void *)(AK_VA_L2MEM); + jumpto_L2(param1,param2,param3,param4); + + REG32(AK_VA_L2CTRL + 0x84) |= (1 << 29); + + //enable ARM interrupt + local_irq_restore(flags); + return 0; +} + + diff --git a/arch/arm/plat-anyka/notify.c b/arch/arm/plat-anyka/notify.c new file mode 100644 index 00000000..32e8127c --- /dev/null +++ b/arch/arm/plat-anyka/notify.c @@ -0,0 +1,97 @@ +/* + * power_notify.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include +#include +#include +#include + +static BLOCKING_NOTIFIER_HEAD(power_notifier_list); +static BLOCKING_NOTIFIER_HEAD(addetect_notifier_list); + +/* + * @brief register a power client notifier + * @author Li Xiaoping + * @date 2011-08-02 + * @param [in] nb notifier block to callback on events + * @return 0 + */ +int power_register_client(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&power_notifier_list, nb); +} +EXPORT_SYMBOL(power_register_client); + +/* + * @brief unregister a power client notifier + * @author Li Xiaoping + * @date 2011-08-02 + * @param [in] nb notifier block to callback on events + * @return 0 + */ +int power_unregister_client(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&power_notifier_list, nb); +} +EXPORT_SYMBOL(power_unregister_client); + +/* + * @brief notify clients of power_events + * @author Li Xiaoping + * @date 2011-08-02 + * @param [in] val - notifier events dispatch to clients + * @param [in] v - event data associated with events + * @return 0 + */ +int power_notifier_call_chain(unsigned long val, void *v) +{ + return blocking_notifier_call_chain(&power_notifier_list, val, v); +} +EXPORT_SYMBOL_GPL(power_notifier_call_chain); + + +/* + * @brief register a ad detect notifier + * @author caolianming + * @date 2012-09-28 + * @param [in] nb notifier block to callback on events + * @return 0 + */ +int addetect_register_client(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&addetect_notifier_list, nb); +} +EXPORT_SYMBOL(addetect_register_client); + +/* + * @brief unregister a ad detect client notifier + * @author caolianming + * @date 2012-09-28 + * @param [in] nb notifier block to callback on events + * @return 0 + */ +int addetect_unregister_client(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&addetect_notifier_list, nb); +} +EXPORT_SYMBOL(addetect_unregister_client); + +/* + * @brief notify clients of ad deteced events + * @author caolianming + * @date 2012-09-28 + * @param [in] val - notifier events dispatch to clients + * @param [in] v - event data associated with events + * @return 0 + */ +int addetect_notifier_call_chain(unsigned long val, void *v) +{ + return blocking_notifier_call_chain(&addetect_notifier_list, val, v); +} +EXPORT_SYMBOL_GPL(addetect_notifier_call_chain); + diff --git a/arch/arm/plat-anyka/reg.c b/arch/arm/plat-anyka/reg.c new file mode 100644 index 00000000..b00ee54a --- /dev/null +++ b/arch/arm/plat-anyka/reg.c @@ -0,0 +1,50 @@ +/* + * reg.c - Register Access Routines + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include + +static DEFINE_SPINLOCK(sys_ctrl_reg_lock); + +/* + * @brief Anyka system control register setting routine + * @author Li Xiaoping + * @date 2011-07-04 + * @param reg_phy_addr [in] Register Physical Address + * @param reg_mask [in] Bit mask of the bits which need to be set + * @param reg_val [in] The value of the bits which need to be set + * @return void + * @note This routine is created in order to solve the problem of access the sam system control + * register from different kernel path (ISR, system call, etc...). + * @note This routine depends on that corresponding registers are mapped, currently this is + * done in akxx_map_io(), so be careful about akxx_map_io() changes. + * the akxx_ is instead of ak37_. + * @sample sys_ctrl_reg_set(0x0800000C, (1 << 29), (1 << 29)) will set bit 29 of + * Clock Control and Soft Reset Control Register to 1 + */ +void sys_ctrl_reg_set(unsigned long reg_phy_addr, unsigned long reg_mask, unsigned long reg_val) +{ + unsigned long flags; + unsigned long val; + unsigned long reg_virt_addr; + + BUG_ON((reg_phy_addr < AK_PA_SYSCTRL) || (reg_phy_addr > (AK_PA_SYSCTRL + AK_SZ_SYSCTRL))); + + spin_lock_irqsave(&sys_ctrl_reg_lock, flags); + + reg_virt_addr = (unsigned long)AK_VA_SYSCTRL + reg_phy_addr - AK_PA_SYSCTRL; + val = __raw_readl(reg_virt_addr); + val = (val & ~reg_mask) | (reg_val & reg_mask); + __raw_writel(val, reg_virt_addr); + + spin_unlock_irqrestore(&sys_ctrl_reg_lock, flags); +} +EXPORT_SYMBOL(sys_ctrl_reg_set); diff --git a/arch/arm/plat-anyka/rtc.c b/arch/arm/plat-anyka/rtc.c new file mode 100644 index 00000000..b7be9860 --- /dev/null +++ b/arch/arm/plat-anyka/rtc.c @@ -0,0 +1,372 @@ +/* + * linux/arch/arm/plat-anyka/rtc.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include + +static int rtc_cnt = 0; + +#undef REG32 +#define REG32(_reg_) (*(volatile unsigned long *)(_reg_)) + +//reboot system by watchdog +void ak_reboot_sys_by_wtd(void) +{ + unsigned long val; + unsigned long flags; + //static spinlock_t loc_lock; + + //spin_lock_init(&loc_lock); + //spin_lock(&loc_lock); + local_irq_save(flags); + ak_rtc_power(RTC_ON); + + //select wdt + val = ak_rtc_read(AK_RTC_SETTING); + val |= (1 << 10); + ak_rtc_write(AK_RTC_SETTING, val); + + //clear timer + val = ak_rtc_read(AK_RTC_SETTING); + val |= (1<<6); + ak_rtc_write(AK_RTC_SETTING, val); + + + //enable watchdog timer + val = ak_rtc_read(AK_WDT_RTC_TIMER_CONF); + val |= (1<<13); + ak_rtc_write(AK_WDT_RTC_TIMER_CONF, val); + + //set timer + val = ak_rtc_read(AK_WDT_RTC_TIMER_CONF); + val &= (1<<13); + val |= (5 & 0x1FFF); + ak_rtc_write(AK_WDT_RTC_TIMER_CONF, val); + + + //open watchdog and watchdog output + val = ak_rtc_read(AK_RTC_SETTING); + val |= ((1<<5) | (1<<2)); + val &= ~(1<<11); + ak_rtc_write(AK_RTC_SETTING, val); + + local_irq_restore(flags); +// spin_unlock(&loc_lock); +} +EXPORT_SYMBOL(ak_reboot_sys_by_wtd); + +void ak_reboot_sys_by_wakeup(void) +{ + //static spinlock_t loc_lock; + struct rtc_time ptm; + struct rtc_time *tm = &ptm; + //unsigned char mdays[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31}; + unsigned long flags; + + unsigned long rtcset; + unsigned long rtc_time1; + unsigned long rtc_time2; + unsigned long rtc_time3; + + unsigned long rtc_alarm1; + unsigned long rtc_alarm2; + unsigned long rtc_alarm3; + unsigned int val_1, val_2; + unsigned long time; + + //spin_lock_init(&loc_lock); + //spin_lock(&loc_lock); + local_irq_save(flags); + ak_rtc_power(RTC_ON); + + rtcset = ak_rtc_read(AK_RTC_SETTING); + rtcset |= RTC_SETTING_REAL_TIME_RE; + ak_rtc_write(AK_RTC_SETTING, rtcset); + + rtc_time1 = ak_rtc_read(AK_RTC_REAL_TIME1); + rtc_time2 = ak_rtc_read(AK_RTC_REAL_TIME2); + rtc_time3 = ak_rtc_read(AK_RTC_REAL_TIME3); + + tm->tm_year = ((rtc_time3 >> 4) & 0x7F) - EPOCH_START_YEAR + RTC_START_YEAR; + tm->tm_mon = (rtc_time3 & 0xF) - 1; + tm->tm_mday = (rtc_time2 >> 5) & 0x1F; + tm->tm_hour = rtc_time2 & 0x1F; + tm->tm_min = (rtc_time1 >> 6) & 0x3F; + tm->tm_sec = rtc_time1 & 0x3F; + tm->tm_wday = (rtc_time2 >> 10) & 0x7; + tm->tm_isdst = -1; + + rtc_alarm1 = ak_rtc_read(AK_RTC_ALARM_TIME1); + rtc_alarm2 = ak_rtc_read(AK_RTC_ALARM_TIME2); + rtc_alarm3 = ak_rtc_read(AK_RTC_ALARM_TIME3); + + + rtc_alarm1 &= ~(0xFFF); + rtc_alarm2 &= ~(0x3FF); + rtc_alarm3 &= ~(0x7FF); + + #define DELAY_TIME 5 + + rtc_tm_to_time(tm, &time); + + time += DELAY_TIME; + + rtc_time_to_tm(time, tm); + /*tm->tm_sec += DELAY_TIME; + + if (tm->tm_sec >= 60) + { + tm->tm_sec -= 60; + tm->tm_min++; + if (tm->tm_min >= 60) + { + tm->tm_min = 0; + tm->tm_hour++; + if (tm->tm_hour >= 24) + { + tm->tm_hour = 0; + tm->tm_mday++; + if ((tm->tm_year%400==0) || ((tm->tm_year%100!=0) && (tm->tm_year%4==0))) + mdays[2] = 29; + if (tm->tm_mday > mdays[tm->tm_mon]) + { + tm->tm_mday = 1; + tm->tm_mon++; + if (tm->tm_mon > 12) + { + tm->tm_mon = 1; + tm->tm_year++; + } + } + } + } + + }*/ + rtc_alarm1 |= ((tm->tm_min << 6) + tm->tm_sec); + rtc_alarm2 |= ((tm->tm_mday << 5) + tm->tm_hour); + rtc_alarm3 |= (((tm->tm_year + EPOCH_START_YEAR - RTC_START_YEAR) << 4) + (tm->tm_mon + 1)); + + ak_rtc_write(AK_RTC_ALARM_TIME1, rtc_alarm1); + ak_rtc_write(AK_RTC_ALARM_TIME2, rtc_alarm2); + ak_rtc_write(AK_RTC_ALARM_TIME3, rtc_alarm3); + + val_1 = REG32(OTHER_WAKEUP_CTRL); + + + val_2 = ak_rtc_read(AK_RTC_SETTING); + + if (1) + { + /* enable wakeup signal */ + val_1 |= RTC_WAKEUP_EN;// | RTC_WAKEUP_SIGNAL_LOWACTIVE); + REG32(OTHER_WAKEUP_CTRL) = val_1; + + val_2 |= (1<<2); + ak_rtc_write(AK_RTC_SETTING, val_2); + } + + rtc_alarm1 = ak_rtc_read(AK_RTC_ALARM_TIME1); + rtc_alarm2 = ak_rtc_read(AK_RTC_ALARM_TIME2); + rtc_alarm3 = ak_rtc_read(AK_RTC_ALARM_TIME3); + + rtc_alarm1 |= (1<<13); + rtc_alarm2 |= (1<<13); + rtc_alarm3 |= (1<<13); + + ak_rtc_write(AK_RTC_ALARM_TIME1, rtc_alarm1); + ak_rtc_write(AK_RTC_ALARM_TIME2, rtc_alarm2); + ak_rtc_write(AK_RTC_ALARM_TIME3, rtc_alarm3); + + local_irq_restore(flags); +// spin_unlock(&loc_lock); + ak_rtc_set_wpin(0); +} + +EXPORT_SYMBOL(ak_reboot_sys_by_wakeup); + +void ak_rtc_power(int op) +{ + unsigned long rtcconf; + + switch(op) + { + case RTC_ON: + if (++rtc_cnt == 1) + { + rtcconf = __raw_readl(AK_RTC_CONF); + rtcconf |= RTC_CONF_RTC_EN | RTC_CONF_RTC_WR_EN; //lhd + //rtcconf |= RTC_CONF_RTC_EN; //lhd + __raw_writel(rtcconf, AK_RTC_CONF); + } + break; + /* + When RTC is powered off, this bit(AK_RTC_CONF [24] ) should be set to 0 + */ + case RTC_OFF: + if (!(--rtc_cnt)) + { + rtcconf = __raw_readl(AK_RTC_CONF); + rtcconf &= ~RTC_CONF_RTC_EN; + __raw_writel(rtcconf, AK_RTC_CONF); + } + break; + default: + printk("Error RTC power operation.\n"); + break; + } +} + +EXPORT_SYMBOL(ak_rtc_power); + +/* +* check if internal rtc works or not +* return -1 no rtc device;0 have rtc device. +*/ +int test_rtc_inter_reg(void) +{ + unsigned long regval = 0; + unsigned long timeout = 0; + int ret = 0; + + //local_irq_save(flags); + + // Enable RTC Ready Interrupt + regval = __raw_readl(RTC_RDY_INT_CTRL); + __raw_writel(regval | (RTC_RDY_CTRL_BIT), RTC_RDY_INT_CTRL); + + /* wait for 1ms to access rtc register + * + * When the RTC module begins to receive/send data, bit [7] of Interrupt Enable/Status + * Register of System Control Module (Add: 0x0800, 004C) is set to 0; and then this + * bit is set to 1 automatically to indicate that the data has been well received/sent + */ + while (!(__raw_readl(RTC_RDY_INT_STAT) & RTC_RDY_STAT_BIT)) + { + if (timeout++ > 500000) + { + ret = -1; + printk("%s_%d: timeout\n",__func__, __LINE__); + break; + } + udelay(1); + } + + // Disable rtc_ready irq + regval = __raw_readl(RTC_RDY_INT_CTRL); + __raw_writel(regval & ~(RTC_RDY_CTRL_BIT), RTC_RDY_INT_CTRL); + + //local_irq_restore(flags); + return ret; +} +EXPORT_SYMBOL(test_rtc_inter_reg); + +unsigned int ak_rtc_read(unsigned int addr) +{ + unsigned long regval = 0; + //unsigned long flags; + + if (addr > AK_RTC_REG_MAX) { + printk("%s(): Invalid RTC Register, address=%d\n", + __func__, addr); + return -1; + } + //local_irq_save(flags); + rtc_ready_irq_enable(); + + regval = __raw_readl(AK_RTC_CONF); + regval &= ~(0x3FFFFF) ; + regval |= (RTC_CONF_RTC_READ | (addr << 14)); + __raw_writel(regval, AK_RTC_CONF); + + udelay(100); + ak_rtc_wait_ready(); + + rtc_ready_irq_disable(); + //local_irq_restore(flags); + + // according to ATC drivers ,this want to wait 1/32K s here + udelay(312); + + regval = __raw_readl(AK_RTC_DATA); + regval &= 0x3FFF; + + return regval; +} +EXPORT_SYMBOL(ak_rtc_read); + +unsigned int ak_rtc_write(unsigned int addr, unsigned int value) +{ + unsigned long regval = 0; + //unsigned long flags; + + if (addr > AK_RTC_REG_MAX) { + printk("%s(): Invalid RTC Register, address=%d\n", + __func__, addr); + return -1; + } + + //local_irq_save(flags); + + rtc_ready_irq_enable(); + + regval = __raw_readl(AK_RTC_CONF); + regval &= ~0x3FFFFF; + regval |= (RTC_CONF_RTC_WRITE | (addr << 14) | value); + __raw_writel(regval, AK_RTC_CONF); + + udelay(100); + + ak_rtc_wait_ready(); + + rtc_ready_irq_disable(); + + //local_irq_restore(flags); + + // according to ATC drivers ,this want to wait 1/32K s here + udelay(312); + + return 0; +} +EXPORT_SYMBOL(ak_rtc_write); + +unsigned int ak_rtc_set_wpin(bool level) +{ + unsigned long regval; + unsigned int bit; + unsigned int timeout = 0; + + ak_rtc_power(RTC_ON); + + if (test_rtc_inter_reg() < 0) { + printk("Board has not RTC support. exit %s\n", __func__); + ak_rtc_power(RTC_OFF); + return -ENODEV; + } + + bit = level ? 8 : 7; + printk("---ak_rtc_set_wpin:%d---\r\n", bit); + regval = ak_rtc_read(AK_RTC_SETTING); + regval |= (1 << bit); + ak_rtc_write(AK_RTC_SETTING, regval); + + while (ak_rtc_read(AK_RTC_SETTING) & (1 << bit)) { + ++timeout; + msleep(1); + if (timeout >= RTC_WAIT_TIME_OUT) { + printk("ak_rtc_set_wpin timeout--\n"); + break; + } + } + + return 0; +} +EXPORT_SYMBOL(ak_rtc_set_wpin); diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types index f9c9f33f..9ee01ada 100644 --- a/arch/arm/tools/mach-types +++ b/arch/arm/tools/mach-types @@ -1169,3 +1169,4 @@ elite_ulk MACH_ELITE_ULK ELITE_ULK 3888 pov2 MACH_POV2 POV2 3889 ipod_touch_2g MACH_IPOD_TOUCH_2G IPOD_TOUCH_2G 3890 da850_pqab MACH_DA850_PQAB DA850_PQAB 3891 +anyka_ak39xx MACH_AK39XX AK39XX 3892 diff --git a/block/genhd.c b/block/genhd.c index 60108d9f..2ade7561 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -1110,6 +1110,22 @@ static void disk_release(struct device *dev) blk_put_queue(disk->queue); kfree(disk); } + +static int disk_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct gendisk *disk = dev_to_disk(dev); + struct disk_part_iter piter; + struct hd_struct *part; + int cnt = 0; + + disk_part_iter_init(&piter, disk, 0); + while((part = disk_part_iter_next(&piter))) + cnt++; + disk_part_iter_exit(&piter); + add_uevent_var(env, "NPARTS=%u", cnt); + return 0; +} + struct class block_class = { .name = "block", }; @@ -1128,6 +1144,7 @@ static struct device_type disk_type = { .groups = disk_attr_groups, .release = disk_release, .devnode = block_devnode, + .uevent = disk_uevent, }; #ifdef CONFIG_PROC_FS diff --git a/block/partition-generic.c b/block/partition-generic.c index 7b8b8d17..264028c3 100644 --- a/block/partition-generic.c +++ b/block/partition-generic.c @@ -216,10 +216,21 @@ static void part_release(struct device *dev) kfree(p); } +static int part_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct hd_struct *part = dev_to_part(dev); + + add_uevent_var(env, "PARTN=%u", part->partno); + if (part->info && part->info->volname[0]) + add_uevent_var(env, "PARTNAME=%s", part->info->volname); + return 0; +} + struct device_type part_type = { .name = "partition", .groups = part_attr_groups, .release = part_release, + .uevent = part_uevent, }; static void delete_partition_rcu_cb(struct rcu_head *head) diff --git a/crypto/Kconfig b/crypto/Kconfig index 8e84225c..5e0f0a74 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -977,6 +977,18 @@ config CRYPTO_USER_API_SKCIPHER This option enables the user-spaces interface for symmetric key cipher algorithms. + +config CRYPTO_DEV + tristate "User-space interface by dev" + depends on CRYPTO + select CRYPTO_BLKCIPHER + select CRYPTO_AEAD + select CRYPTO_HASH + help + This option enables the user-spaces interface, can use it in + /dev/crypto + + source "drivers/crypto/Kconfig" endif # if CRYPTO diff --git a/crypto/Makefile b/crypto/Makefile index 30f33d67..78aad758 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -91,6 +91,8 @@ obj-$(CONFIG_CRYPTO_USER_API) += af_alg.o obj-$(CONFIG_CRYPTO_USER_API_HASH) += algif_hash.o obj-$(CONFIG_CRYPTO_USER_API_SKCIPHER) += algif_skcipher.o +obj-$(CONFIG_CRYPTO_DEV) += crypto_dev/ + # # generic algorithms and the async_tx api # diff --git a/crypto/crypto_dev/Makefile b/crypto/crypto_dev/Makefile new file mode 100644 index 00000000..2dbcaf2e --- /dev/null +++ b/crypto/crypto_dev/Makefile @@ -0,0 +1,10 @@ +# +# Since version 1.6 the asynchronous mode has been +# disabled by default. To re-enable it uncomment the +# corresponding CFLAG. +# + +cryptodev-objs = crypto_run.o cryptdev.o cryptlib.o authenc.o zc.o util.o + +obj-y += cryptodev.o + diff --git a/crypto/crypto_dev/authenc.c b/crypto/crypto_dev/authenc.c new file mode 100644 index 00000000..8bff6776 --- /dev/null +++ b/crypto/crypto_dev/authenc.c @@ -0,0 +1,742 @@ +/* + * Driver for /dev/crypto device (aka CryptoDev) + * + * Copyright (c) 2011, 2012 OpenSSL Software Foundation, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of linux cryptodev. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * This file handles the AEAD part of /dev/crypto. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cryptodev_int.h" +#include "zc.h" +#include "util.h" +#include "cryptlib.h" +#include "version.h" + + +/* make caop->dst available in scatterlist. + * (caop->src is assumed to be equal to caop->dst) + */ +static int get_userbuf_tls(struct csession *ses, struct kernel_crypt_auth_op *kcaop, + struct scatterlist **dst_sg) +{ + int pagecount = 0; + struct crypt_auth_op *caop = &kcaop->caop; + int rc; + + if (caop->dst == NULL) + return -EINVAL; + + if (ses->alignmask) { + if (!IS_ALIGNED((unsigned long)caop->dst, ses->alignmask)) + dprintk(2, KERN_WARNING, "careful - source address %lx is not %d byte aligned\n", + (unsigned long)caop->dst, ses->alignmask + 1); + } + + if (kcaop->dst_len == 0) { + dprintk(1, KERN_WARNING, "Destination length cannot be zero\n"); + return -EINVAL; + } + + pagecount = PAGECOUNT(caop->dst, kcaop->dst_len); + + ses->used_pages = pagecount; + ses->readonly_pages = 0; + + rc = adjust_sg_array(ses, pagecount); + if (rc) + return rc; + + rc = __get_userbuf(caop->dst, kcaop->dst_len, 1, pagecount, + ses->pages, ses->sg, kcaop->task, kcaop->mm); + if (unlikely(rc)) { + dprintk(1, KERN_ERR, + "failed to get user pages for data input\n"); + return -EINVAL; + } + + (*dst_sg) = ses->sg; + + return 0; +} + + +#define MAX_SRTP_AUTH_DATA_DIFF 256 + +/* Makes caop->auth_src available as scatterlist. + * It also provides a pointer to caop->dst, which however, + * is assumed to be within the caop->auth_src buffer. If not + * (if their difference exceeds MAX_SRTP_AUTH_DATA_DIFF) it + * returns error. + */ +static int get_userbuf_srtp(struct csession *ses, struct kernel_crypt_auth_op *kcaop, + struct scatterlist **auth_sg, struct scatterlist **dst_sg) +{ + int pagecount, diff; + int auth_pagecount = 0; + struct crypt_auth_op *caop = &kcaop->caop; + int rc; + + if (caop->dst == NULL && caop->auth_src == NULL) { + dprintk(1, KERN_ERR, "dst and auth_src cannot be both null\n"); + return -EINVAL; + } + + if (ses->alignmask) { + if (!IS_ALIGNED((unsigned long)caop->dst, ses->alignmask)) + dprintk(2, KERN_WARNING, "careful - source address %lx is not %d byte aligned\n", + (unsigned long)caop->dst, ses->alignmask + 1); + if (!IS_ALIGNED((unsigned long)caop->auth_src, ses->alignmask)) + dprintk(2, KERN_WARNING, "careful - source address %lx is not %d byte aligned\n", + (unsigned long)caop->auth_src, ses->alignmask + 1); + } + + if (unlikely(kcaop->dst_len == 0 || caop->auth_len == 0)) { + dprintk(1, KERN_WARNING, "Destination length cannot be zero\n"); + return -EINVAL; + } + + /* Note that in SRTP auth data overlap with data to be encrypted (dst) + */ + + auth_pagecount = PAGECOUNT(caop->auth_src, caop->auth_len); + diff = (int)(caop->src - caop->auth_src); + if (diff > MAX_SRTP_AUTH_DATA_DIFF || diff < 0) { + dprintk(1, KERN_WARNING, "auth_src must overlap with src (diff: %d).\n", diff); + return -EINVAL; + } + + pagecount = auth_pagecount; + + rc = adjust_sg_array(ses, pagecount*2); /* double pages to have pages for dst(=auth_src) */ + if (rc) { + dprintk(1, KERN_ERR, "cannot adjust sg array\n"); + return rc; + } + + rc = __get_userbuf(caop->auth_src, caop->auth_len, 1, auth_pagecount, + ses->pages, ses->sg, kcaop->task, kcaop->mm); + if (unlikely(rc)) { + dprintk(1, KERN_ERR, + "failed to get user pages for data input\n"); + return -EINVAL; + } + + ses->used_pages = pagecount; + ses->readonly_pages = 0; + + (*auth_sg) = ses->sg; + + (*dst_sg) = ses->sg + auth_pagecount; + sg_init_table(*dst_sg, auth_pagecount); + sg_copy(ses->sg, (*dst_sg), caop->auth_len); + (*dst_sg) = sg_advance(*dst_sg, diff); + if (*dst_sg == NULL) { + release_user_pages(ses); + dprintk(1, KERN_ERR, + "failed to get enough pages for auth data\n"); + return -EINVAL; + } + + return 0; +} + +static int fill_kcaop_from_caop(struct kernel_crypt_auth_op *kcaop, struct fcrypt *fcr) +{ + struct crypt_auth_op *caop = &kcaop->caop; + struct csession *ses_ptr; + int ret; + + /* this also enters ses_ptr->sem */ + ses_ptr = crypto_get_session_by_sid(fcr, caop->ses); + if (unlikely(!ses_ptr)) { + dprintk(1, KERN_ERR, "invalid session ID=0x%08X\n", caop->ses); + return -EINVAL; + } + + if (caop->flags & COP_FLAG_AEAD_TLS_TYPE || caop->flags & COP_FLAG_AEAD_SRTP_TYPE) { + if (caop->src != caop->dst) { + dprintk(1, KERN_ERR, + "Non-inplace encryption and decryption is not efficient and not implemented\n"); + ret = -EINVAL; + goto out_unlock; + } + } + + if (caop->tag_len == 0) + caop->tag_len = ses_ptr->hdata.digestsize; + + kcaop->ivlen = caop->iv ? ses_ptr->cdata.ivsize : 0; + + if (caop->flags & COP_FLAG_AEAD_TLS_TYPE) + kcaop->dst_len = caop->len + ses_ptr->cdata.blocksize /* pad */ + caop->tag_len; + else + kcaop->dst_len = caop->len; + + kcaop->task = current; + kcaop->mm = current->mm; + + if (caop->iv) { + ret = copy_from_user(kcaop->iv, caop->iv, kcaop->ivlen); + if (unlikely(ret)) { + dprintk(1, KERN_ERR, + "error copying IV (%d bytes), copy_from_user returned %d for address %lx\n", + kcaop->ivlen, ret, (unsigned long)caop->iv); + ret = -EFAULT; + goto out_unlock; + } + } + + ret = 0; + +out_unlock: + crypto_put_session(ses_ptr); + return ret; + +} + +static int fill_caop_from_kcaop(struct kernel_crypt_auth_op *kcaop, struct fcrypt *fcr) +{ + int ret; + + kcaop->caop.len = kcaop->dst_len; + + if (kcaop->ivlen && kcaop->caop.flags & COP_FLAG_WRITE_IV) { + ret = copy_to_user(kcaop->caop.iv, + kcaop->iv, kcaop->ivlen); + if (unlikely(ret)) { + dprintk(1, KERN_ERR, "Error in copying to userspace\n"); + return -EFAULT; + } + } + return 0; +} + + +int kcaop_from_user(struct kernel_crypt_auth_op *kcaop, + struct fcrypt *fcr, void __user *arg) +{ + if (unlikely(copy_from_user(&kcaop->caop, arg, sizeof(kcaop->caop)))) { + dprintk(1, KERN_ERR, "Error in copying from userspace\n"); + return -EFAULT; + } + + return fill_kcaop_from_caop(kcaop, fcr); +} + +int kcaop_to_user(struct kernel_crypt_auth_op *kcaop, + struct fcrypt *fcr, void __user *arg) +{ + int ret; + + ret = fill_caop_from_kcaop(kcaop, fcr); + if (unlikely(ret)) { + dprintk(1, KERN_ERR, "fill_caop_from_kcaop\n"); + return ret; + } + + if (unlikely(copy_to_user(arg, &kcaop->caop, sizeof(kcaop->caop)))) { + dprintk(1, KERN_ERR, "Error in copying to userspace\n"); + return -EFAULT; + } + return 0; +} + +static void copy_tls_hash( struct scatterlist *dst_sg, int len, void* hash, int hash_len) +{ + scatterwalk_map_and_copy(hash, dst_sg, len, hash_len, 1); +} + +static void read_tls_hash( struct scatterlist *dst_sg, int len, void* hash, int hash_len) +{ + scatterwalk_map_and_copy(hash, dst_sg, len-hash_len, hash_len, 0); +} + +static int pad_record( struct scatterlist *dst_sg, int len, int block_size) +{ + uint8_t pad[block_size]; + int pad_size = block_size - (len % block_size); + + memset(pad, pad_size-1, pad_size); + + scatterwalk_map_and_copy(pad, dst_sg, len, pad_size, 1); + + return pad_size; +} + +static int verify_tls_record_pad( struct scatterlist *dst_sg, int len, int block_size) +{ + uint8_t pad[256]; /* the maximum allowed */ + uint8_t pad_size; + int i; + + scatterwalk_map_and_copy(&pad_size, dst_sg, len-1, 1, 0); + + if (pad_size+1 > len) { + dprintk(1, KERN_ERR, "Pad size: %d\n", pad_size); + return -EBADMSG; + } + + scatterwalk_map_and_copy(pad, dst_sg, len-pad_size-1, pad_size+1, 0); + + for (i=0;icaop; + uint8_t vhash[AALG_MAX_RESULT_LEN]; + uint8_t hash_output[AALG_MAX_RESULT_LEN]; + + /* TLS authenticates the plaintext except for the padding. + */ + if (caop->op == COP_ENCRYPT) { + if (ses_ptr->hdata.init != 0) { + if (auth_len > 0) { + ret = cryptodev_hash_update(&ses_ptr->hdata, + auth_sg, auth_len); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret); + return ret; + } + } + + if (len > 0) { + ret = cryptodev_hash_update(&ses_ptr->hdata, + dst_sg, len); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret); + return ret; + } + } + + ret = cryptodev_hash_final(&ses_ptr->hdata, hash_output); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "cryptodev_hash_final: %d\n", ret); + return ret; + } + + copy_tls_hash( dst_sg, len, hash_output, caop->tag_len); + len += caop->tag_len; + } + + if (ses_ptr->cdata.init != 0) { + if (ses_ptr->cdata.blocksize > 1) { + ret = pad_record(dst_sg, len, ses_ptr->cdata.blocksize); + len += ret; + } + + ret = cryptodev_cipher_encrypt(&ses_ptr->cdata, + dst_sg, dst_sg, len); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "cryptodev_cipher_encrypt: %d\n", ret); + return ret; + } + } + } else { + if (ses_ptr->cdata.init != 0) { + ret = cryptodev_cipher_decrypt(&ses_ptr->cdata, + dst_sg, dst_sg, len); + + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "cryptodev_cipher_decrypt: %d\n", ret); + return ret; + } + + if (ses_ptr->cdata.blocksize > 1) { + ret = verify_tls_record_pad(dst_sg, len, ses_ptr->cdata.blocksize); + if (unlikely(ret < 0)) { + dprintk(2, KERN_ERR, "verify_record_pad: %d\n", ret); + fail = 1; + } else { + len -= ret; + } + } + } + + if (ses_ptr->hdata.init != 0) { + if (unlikely(caop->tag_len > sizeof(vhash) || caop->tag_len > len)) { + dprintk(1, KERN_ERR, "Illegal tag len size\n"); + return -EINVAL; + } + + read_tls_hash( dst_sg, len, vhash, caop->tag_len); + len -= caop->tag_len; + + if (auth_len > 0) { + ret = cryptodev_hash_update(&ses_ptr->hdata, + auth_sg, auth_len); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret); + return ret; + } + } + + if (len > 0) { + ret = cryptodev_hash_update(&ses_ptr->hdata, + dst_sg, len); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret); + return ret; + } + } + + ret = cryptodev_hash_final(&ses_ptr->hdata, hash_output); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "cryptodev_hash_final: %d\n", ret); + return ret; + } + + if (memcmp(vhash, hash_output, caop->tag_len) != 0 || fail != 0) { + dprintk(2, KERN_ERR, "MAC verification failed (tag_len: %d)\n", caop->tag_len); + return -EBADMSG; + } + } + } + kcaop->dst_len = len; + return 0; +} + +/* Authenticate and encrypt the SRTP way. During decryption + * it verifies the tag and returns -EBADMSG on error. + */ +static int +srtp_auth_n_crypt(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcaop, + struct scatterlist *auth_sg, uint32_t auth_len, + struct scatterlist *dst_sg, uint32_t len) +{ + int ret, fail = 0; + struct crypt_auth_op *caop = &kcaop->caop; + uint8_t vhash[AALG_MAX_RESULT_LEN]; + uint8_t hash_output[AALG_MAX_RESULT_LEN]; + + /* SRTP authenticates the encrypted data. + */ + if (caop->op == COP_ENCRYPT) { + if (ses_ptr->cdata.init != 0) { + ret = cryptodev_cipher_encrypt(&ses_ptr->cdata, + dst_sg, dst_sg, len); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "cryptodev_cipher_encrypt: %d\n", ret); + return ret; + } + } + + if (ses_ptr->hdata.init != 0) { + if (auth_len > 0) { + ret = cryptodev_hash_update(&ses_ptr->hdata, + auth_sg, auth_len); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret); + return ret; + } + } + + ret = cryptodev_hash_final(&ses_ptr->hdata, hash_output); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "cryptodev_hash_final: %d\n", ret); + return ret; + } + + if (unlikely(copy_to_user(caop->tag, hash_output, caop->tag_len))) { + return -EFAULT; + } + } + + } else { + if (ses_ptr->hdata.init != 0) { + if (unlikely(caop->tag_len > sizeof(vhash) || caop->tag_len > len)) { + dprintk(1, KERN_ERR, "Illegal tag len size\n"); + return -EINVAL; + } + + if (unlikely(copy_from_user(vhash, caop->tag, caop->tag_len))) { + return -EFAULT; + } + + ret = cryptodev_hash_update(&ses_ptr->hdata, + auth_sg, auth_len); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret); + return ret; + } + + ret = cryptodev_hash_final(&ses_ptr->hdata, hash_output); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "cryptodev_hash_final: %d\n", ret); + return ret; + } + + if (memcmp(vhash, hash_output, caop->tag_len) != 0 || fail != 0) { + dprintk(2, KERN_ERR, "MAC verification failed\n"); + return -EBADMSG; + } + } + + if (ses_ptr->cdata.init != 0) { + ret = cryptodev_cipher_decrypt(&ses_ptr->cdata, + dst_sg, dst_sg, len); + + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "cryptodev_cipher_decrypt: %d\n", ret); + return ret; + } + } + + } + kcaop->dst_len = len; + return 0; +} + +/* Typical AEAD (i.e. GCM) encryption/decryption. + * During decryption the tag is verified. + */ +static int +auth_n_crypt(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcaop, + struct scatterlist *auth_sg, uint32_t auth_len, + struct scatterlist *src_sg, + struct scatterlist *dst_sg, uint32_t len) +{ + int ret; + struct crypt_auth_op *caop = &kcaop->caop; + int max_tag_len; + + max_tag_len = cryptodev_cipher_get_tag_size(&ses_ptr->cdata); + if (unlikely(caop->tag_len > max_tag_len)) { + dprintk(0, KERN_ERR, "Illegal tag length: %d\n", caop->tag_len); + return -EINVAL; + } + + if (caop->tag_len) + cryptodev_cipher_set_tag_size(&ses_ptr->cdata, caop->tag_len); + else + caop->tag_len = max_tag_len; + + cryptodev_cipher_auth(&ses_ptr->cdata, auth_sg, auth_len); + + if (caop->op == COP_ENCRYPT) { + ret = cryptodev_cipher_encrypt(&ses_ptr->cdata, + src_sg, dst_sg, len); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "cryptodev_cipher_encrypt: %d\n", ret); + return ret; + } + kcaop->dst_len = len + caop->tag_len; + caop->tag = caop->dst + len; + } else { + ret = cryptodev_cipher_decrypt(&ses_ptr->cdata, + src_sg, dst_sg, len); + + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "cryptodev_cipher_decrypt: %d\n", ret); + return ret; + } + kcaop->dst_len = len - caop->tag_len; + caop->tag = caop->dst + len - caop->tag_len; + } + + return 0; +} + +/* This is the main crypto function - zero-copy edition */ +static int +__crypto_auth_run_zc(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcaop) +{ + struct scatterlist *dst_sg, *auth_sg, *src_sg; + struct crypt_auth_op *caop = &kcaop->caop; + int ret = 0; + + if (caop->flags & COP_FLAG_AEAD_SRTP_TYPE) { + if (unlikely(ses_ptr->cdata.init != 0 && + (ses_ptr->cdata.stream == 0 || ses_ptr->cdata.aead != 0))) + { + dprintk(0, KERN_ERR, "Only stream modes are allowed in SRTP mode (but not AEAD)\n"); + return -EINVAL; + } + + ret = get_userbuf_srtp(ses_ptr, kcaop, &auth_sg, &dst_sg); + if (unlikely(ret)) { + dprintk(1, KERN_ERR, "get_userbuf_srtp(): Error getting user pages.\n"); + return ret; + } + + ret = srtp_auth_n_crypt(ses_ptr, kcaop, auth_sg, caop->auth_len, + dst_sg, caop->len); + + release_user_pages(ses_ptr); + } else { /* TLS and normal cases. Here auth data are usually small + * so we just copy them to a free page, instead of trying + * to map them. + */ + unsigned char* auth_buf = NULL; + struct scatterlist tmp; + + if (unlikely(caop->auth_len > PAGE_SIZE)) { + dprintk(1, KERN_ERR, "auth data len is excessive.\n"); + return -EINVAL; + } + + auth_buf = (char *)__get_free_page(GFP_KERNEL); + if (unlikely(!auth_buf)) { + dprintk(1, KERN_ERR, "unable to get a free page.\n"); + return -ENOMEM; + } + + if (caop->auth_src && caop->auth_len > 0) { + if (unlikely(copy_from_user(auth_buf, caop->auth_src, caop->auth_len))) { + dprintk(1, KERN_ERR, "unable to copy auth data from userspace.\n"); + ret = -EFAULT; + goto free_auth_buf; + } + + sg_init_one(&tmp, auth_buf, caop->auth_len); + auth_sg = &tmp; + } else { + auth_sg = NULL; + } + + if (caop->flags & COP_FLAG_AEAD_TLS_TYPE && ses_ptr->cdata.aead == 0) { + ret = get_userbuf_tls(ses_ptr, kcaop, &dst_sg); + if (unlikely(ret)) { + dprintk(1, KERN_ERR, "get_userbuf_tls(): Error getting user pages.\n"); + goto free_auth_buf; + } + + ret = tls_auth_n_crypt(ses_ptr, kcaop, auth_sg, caop->auth_len, + dst_sg, caop->len); + } else { + int dst_len; + + if (unlikely(ses_ptr->cdata.init == 0 || + ses_ptr->cdata.stream == 0 || + ses_ptr->cdata.aead == 0)) + { + dprintk(0, KERN_ERR, "Only stream and AEAD ciphers are allowed for authenc\n"); + ret = -EINVAL; + goto free_auth_buf; + } + + if (caop->op == COP_ENCRYPT) dst_len = caop->len + cryptodev_cipher_get_tag_size(&ses_ptr->cdata); + else dst_len = caop->len; + + ret = get_userbuf(ses_ptr, caop->src, caop->len, caop->dst, dst_len, + kcaop->task, kcaop->mm, &src_sg, &dst_sg); + if (unlikely(ret)) { + dprintk(1, KERN_ERR, "get_userbuf(): Error getting user pages.\n"); + goto free_auth_buf; + } + + ret = auth_n_crypt(ses_ptr, kcaop, auth_sg, caop->auth_len, + src_sg, dst_sg, caop->len); + } + + release_user_pages(ses_ptr); + +free_auth_buf: + free_page((unsigned long)auth_buf); + } + + return ret; +} + + +int crypto_auth_run(struct fcrypt *fcr, struct kernel_crypt_auth_op *kcaop) +{ + struct csession *ses_ptr; + struct crypt_auth_op *caop = &kcaop->caop; + int ret; + + if (unlikely(caop->op != COP_ENCRYPT && caop->op != COP_DECRYPT)) { + dprintk(1, KERN_DEBUG, "invalid operation op=%u\n", caop->op); + return -EINVAL; + } + + /* this also enters ses_ptr->sem */ + ses_ptr = crypto_get_session_by_sid(fcr, caop->ses); + if (unlikely(!ses_ptr)) { + dprintk(1, KERN_ERR, "invalid session ID=0x%08X\n", caop->ses); + return -EINVAL; + } + + if (unlikely(ses_ptr->cdata.init == 0)) { + dprintk(1, KERN_ERR, "cipher context not initialized\n"); + ret = -EINVAL; + goto out_unlock; + } + + /* If we have a hash/mac handle reset its state */ + if (ses_ptr->hdata.init != 0) { + ret = cryptodev_hash_reset(&ses_ptr->hdata); + if (unlikely(ret)) { + dprintk(1, KERN_ERR, + "error in cryptodev_hash_reset()\n"); + goto out_unlock; + } + } + + cryptodev_cipher_set_iv(&ses_ptr->cdata, kcaop->iv, + min(ses_ptr->cdata.ivsize, kcaop->ivlen)); + + ret = __crypto_auth_run_zc(ses_ptr, kcaop); + if (unlikely(ret)) { + dprintk(1, KERN_ERR, + "error in __crypto_auth_run_zc()\n"); + goto out_unlock; + } + + ret = 0; + + cryptodev_cipher_get_iv(&ses_ptr->cdata, kcaop->iv, + min(ses_ptr->cdata.ivsize, kcaop->ivlen)); + +out_unlock: + crypto_put_session(ses_ptr); + return ret; +} diff --git a/crypto/crypto_dev/cryptdev.c b/crypto/crypto_dev/cryptdev.c new file mode 100644 index 00000000..20a78322 --- /dev/null +++ b/crypto/crypto_dev/cryptdev.c @@ -0,0 +1,1162 @@ +/* + * Driver for /dev/crypto device (aka CryptoDev) + * + * Copyright (c) 2004 Michal Ludvig , SuSE Labs + * Copyright (c) 2009,2010,2011 Nikos Mavrogiannopoulos + * Copyright (c) 2010 Phil Sutter + * + * This file is part of linux cryptodev. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * Device /dev/crypto provides an interface for + * accessing kernel CryptoAPI algorithms (ciphers, + * hashes) from userspace programs. + * + * /dev/crypto interface was originally introduced in + * OpenBSD and this module attempts to keep the API. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cryptodev_int.h" +#include "zc.h" +#include "version.h" + +MODULE_AUTHOR("Nikos Mavrogiannopoulos "); +MODULE_DESCRIPTION("CryptoDev driver"); +MODULE_LICENSE("GPL"); + +/* ====== Compile-time config ====== */ + +/* Default (pre-allocated) and maximum size of the job queue. + * These are free, pending and done items all together. */ +#define DEF_COP_RINGSIZE 16 +#define MAX_COP_RINGSIZE 64 + +/* ====== Module parameters ====== */ + +int cryptodev_verbosity; +module_param(cryptodev_verbosity, int, 0644); +MODULE_PARM_DESC(cryptodev_verbosity, "0: normal, 1: verbose, 2: debug"); + +/* ====== CryptoAPI ====== */ +struct todo_list_item { + struct list_head __hook; + struct kernel_crypt_op kcop; + int result; +}; + +struct locked_list { + struct list_head list; + struct mutex lock; +}; + +struct crypt_priv { + struct fcrypt fcrypt; + struct locked_list free, todo, done; + int itemcount; + struct work_struct cryptask; + wait_queue_head_t user_waiter; +}; + +#define FILL_SG(sg, ptr, len) \ + do { \ + (sg)->page = virt_to_page(ptr); \ + (sg)->offset = offset_in_page(ptr); \ + (sg)->length = len; \ + (sg)->dma_address = 0; \ + } while (0) + +/* cryptodev's own workqueue, keeps crypto tasks from disturbing the force */ +static struct workqueue_struct *cryptodev_wq; + +/* Prepare session for future use. */ +static int +crypto_create_session(struct fcrypt *fcr, struct session_op *sop) +{ + struct csession *ses_new = NULL, *ses_ptr; + int ret = 0; + const char *alg_name = NULL; + const char *hash_name = NULL; + int hmac_mode = 1, stream = 0, aead = 0; + + /* Does the request make sense? */ + if (unlikely(!sop->cipher && !sop->mac)) { + dprintk(1, KERN_DEBUG, "Both 'cipher' and 'mac' unset.\n"); + return -EINVAL; + } + + switch (sop->cipher) { + case 0: + break; + case CRYPTO_DES_CBC: + alg_name = "cbc(des)"; + break; + case CRYPTO_3DES_CBC: + alg_name = "cbc(des3_ede)"; + break; + case CRYPTO_BLF_CBC: + alg_name = "cbc(blowfish)"; + break; + case CRYPTO_AES_CBC: + alg_name = "cbc(aes)"; + break; + case CRYPTO_AES_ECB: + alg_name = "ecb(aes)"; + break; + case CRYPTO_CAMELLIA_CBC: + alg_name = "cbc(camellia)"; + break; + case CRYPTO_AES_CTR: + alg_name = "ctr(aes)"; + stream = 1; + break; + /*add*/ + case CRYPTO_AES_CFB: + alg_name = "cfb(aes)"; + break; + case CRYPTO_AES_OFB: + alg_name = "ofb(aes)"; + break; + case CRYPTO_DES_ECB: + alg_name = "ecb(des)"; + break; + case CRYPTO_DES_CFB: + alg_name = "cfb(des)"; + break; + case CRYPTO_DES_OFB: + alg_name = "ofb(des)"; + break; + case CRYPTO_3DES_ECB: + alg_name = "ecb(des3_ede)"; + break; + case CRYPTO_3DES_CFB: + alg_name = "cfb(des3_ede)"; + break; + case CRYPTO_3DES_OFB: + alg_name = "ofb(des3_ede)"; + break; + /*edd addition*/ + case CRYPTO_AES_GCM: + alg_name = "gcm(aes)"; + stream = 1; + aead = 1; + break; + case CRYPTO_NULL: + alg_name = "ecb(cipher_null)"; + stream = 1; + break; + default: + dprintk(1, KERN_DEBUG, "bad cipher: %d\n", sop->cipher); + return -EINVAL; + } + + switch (sop->mac) { + case 0: + break; + case CRYPTO_MD5_HMAC: + hash_name = "hmac(md5)"; + break; + case CRYPTO_RIPEMD160_HMAC: + hash_name = "hmac(rmd160)"; + break; + case CRYPTO_SHA1_HMAC: + hash_name = "hmac(sha1)"; + break; + case CRYPTO_SHA2_224_HMAC: + hash_name = "hmac(sha224)"; + break; + + case CRYPTO_SHA2_256_HMAC: + hash_name = "hmac(sha256)"; + break; + case CRYPTO_SHA2_384_HMAC: + hash_name = "hmac(sha384)"; + break; + case CRYPTO_SHA2_512_HMAC: + hash_name = "hmac(sha512)"; + break; + + /* non-hmac cases */ + case CRYPTO_MD5: + hash_name = "md5"; + hmac_mode = 0; + break; + case CRYPTO_RIPEMD160: + hash_name = "rmd160"; + hmac_mode = 0; + break; + case CRYPTO_SHA1: + hash_name = "sha1"; + hmac_mode = 0; + break; + case CRYPTO_SHA2_224: + hash_name = "sha224"; + hmac_mode = 0; + break; + case CRYPTO_SHA2_256: + hash_name = "sha256"; + hmac_mode = 0; + break; + case CRYPTO_SHA2_384: + hash_name = "sha384"; + hmac_mode = 0; + break; + case CRYPTO_SHA2_512: + hash_name = "sha512"; + hmac_mode = 0; + break; + default: + dprintk(1, KERN_DEBUG, "bad mac: %d\n", sop->mac); + return -EINVAL; + } + + /* Create a session and put it to the list. */ + ses_new = kzalloc(sizeof(*ses_new), GFP_KERNEL); + if (!ses_new) + return -ENOMEM; + + /* Set-up crypto transform. */ + if (alg_name) { + uint8_t keyp[CRYPTO_CIPHER_MAX_KEY_LEN]; + + if (unlikely(sop->keylen > CRYPTO_CIPHER_MAX_KEY_LEN)) { + dprintk(1, KERN_DEBUG, + "Setting key failed for %s-%zu.\n", + alg_name, (size_t)sop->keylen*8); + ret = -EINVAL; + goto error_cipher; + } + + if (unlikely(copy_from_user(keyp, sop->key, sop->keylen))) { + ret = -EFAULT; + goto error_cipher; + } + + ret = cryptodev_cipher_init(&ses_new->cdata, alg_name, keyp, + sop->keylen, stream, aead); + if (ret < 0) { + dprintk(1, KERN_DEBUG, + "Failed to load cipher for %s\n", alg_name); + ret = -EINVAL; + goto error_cipher; + } + } + + if (hash_name && aead == 0) { + uint8_t keyp[CRYPTO_HMAC_MAX_KEY_LEN]; + + if (unlikely(sop->mackeylen > CRYPTO_HMAC_MAX_KEY_LEN)) { + dprintk(1, KERN_DEBUG, + "Setting key failed for %s-%zu.\n", + alg_name, (size_t)sop->mackeylen*8); + ret = -EINVAL; + goto error_hash; + } + + if (sop->mackey && unlikely(copy_from_user(keyp, sop->mackey, + sop->mackeylen))) { + ret = -EFAULT; + goto error_hash; + } + + ret = cryptodev_hash_init(&ses_new->hdata, hash_name, hmac_mode, + keyp, sop->mackeylen); + if (ret != 0) { + dprintk(1, KERN_DEBUG, "Failed to load hash for %s\n", hash_name); + ret = -EINVAL; + goto error_hash; + } + } + + ses_new->alignmask = max(ses_new->cdata.alignmask, + ses_new->hdata.alignmask); + dprintk(2, KERN_DEBUG, "got alignmask %d\n", ses_new->alignmask); + + ses_new->array_size = DEFAULT_PREALLOC_PAGES; + dprintk(2, KERN_DEBUG, "preallocating for %d user pages\n", + ses_new->array_size); + ses_new->pages = kzalloc(ses_new->array_size * + sizeof(struct page *), GFP_KERNEL); + ses_new->sg = kzalloc(ses_new->array_size * + sizeof(struct scatterlist), GFP_KERNEL); + if (ses_new->sg == NULL || ses_new->pages == NULL) { + dprintk(0, KERN_DEBUG, "Memory error\n"); + ret = -ENOMEM; + goto error_hash; + } + + /* put the new session to the list */ + get_random_bytes(&ses_new->sid, sizeof(ses_new->sid)); + mutex_init(&ses_new->sem); + + mutex_lock(&fcr->sem); +restart: + list_for_each_entry(ses_ptr, &fcr->list, entry) { + /* Check for duplicate SID */ + if (unlikely(ses_new->sid == ses_ptr->sid)) { + get_random_bytes(&ses_new->sid, sizeof(ses_new->sid)); + /* Unless we have a broken RNG this + shouldn't loop forever... ;-) */ + goto restart; + } + } + + list_add(&ses_new->entry, &fcr->list); + mutex_unlock(&fcr->sem); + + /* Fill in some values for the user. */ + sop->ses = ses_new->sid; + + return 0; + +error_hash: + cryptodev_cipher_deinit(&ses_new->cdata); + kfree(ses_new->sg); + kfree(ses_new->pages); +error_cipher: + kfree(ses_new); + + return ret; + +} + +/* Everything that needs to be done when remowing a session. */ +static inline void +crypto_destroy_session(struct csession *ses_ptr) +{ + if (!mutex_trylock(&ses_ptr->sem)) { + dprintk(2, KERN_DEBUG, "Waiting for semaphore of sid=0x%08X\n", + ses_ptr->sid); + mutex_lock(&ses_ptr->sem); + } + dprintk(2, KERN_DEBUG, "Removed session 0x%08X\n", ses_ptr->sid); + cryptodev_cipher_deinit(&ses_ptr->cdata); + cryptodev_hash_deinit(&ses_ptr->hdata); + dprintk(2, KERN_DEBUG, "freeing space for %d user pages\n", + ses_ptr->array_size); + kfree(ses_ptr->pages); + kfree(ses_ptr->sg); + mutex_unlock(&ses_ptr->sem); + kfree(ses_ptr); +} + +/* Look up a session by ID and remove. */ +static int +crypto_finish_session(struct fcrypt *fcr, uint32_t sid) +{ + struct csession *tmp, *ses_ptr; + struct list_head *head; + int ret = 0; + + mutex_lock(&fcr->sem); + head = &fcr->list; + list_for_each_entry_safe(ses_ptr, tmp, head, entry) { + if (ses_ptr->sid == sid) { + list_del(&ses_ptr->entry); + crypto_destroy_session(ses_ptr); + break; + } + } + + if (unlikely(!ses_ptr)) { + dprintk(1, KERN_ERR, "Session with sid=0x%08X not found!\n", + sid); + ret = -ENOENT; + } + mutex_unlock(&fcr->sem); + + return ret; +} + +/* Remove all sessions when closing the file */ +static int +crypto_finish_all_sessions(struct fcrypt *fcr) +{ + struct csession *tmp, *ses_ptr; + struct list_head *head; + + mutex_lock(&fcr->sem); + + head = &fcr->list; + list_for_each_entry_safe(ses_ptr, tmp, head, entry) { + list_del(&ses_ptr->entry); + crypto_destroy_session(ses_ptr); + } + mutex_unlock(&fcr->sem); + + return 0; +} + +/* Look up session by session ID. The returned session is locked. */ +struct csession * +crypto_get_session_by_sid(struct fcrypt *fcr, uint32_t sid) +{ + struct csession *ses_ptr, *retval = NULL; + + if (unlikely(fcr == NULL)) + return NULL; + + mutex_lock(&fcr->sem); + list_for_each_entry(ses_ptr, &fcr->list, entry) { + if (ses_ptr->sid == sid) { + mutex_lock(&ses_ptr->sem); + retval = ses_ptr; + break; + } + } + mutex_unlock(&fcr->sem); + + return retval; +} + +static void cryptask_routine(struct work_struct *work) +{ + struct crypt_priv *pcr = container_of(work, struct crypt_priv, cryptask); + struct todo_list_item *item; + LIST_HEAD(tmp); + + /* fetch all pending jobs into the temporary list */ + mutex_lock(&pcr->todo.lock); + list_cut_position(&tmp, &pcr->todo.list, pcr->todo.list.prev); + mutex_unlock(&pcr->todo.lock); + + /* handle each job locklessly */ + list_for_each_entry(item, &tmp, __hook) { + item->result = crypto_run(&pcr->fcrypt, &item->kcop); + if (unlikely(item->result)) + dprintk(0, KERN_ERR, "crypto_run() failed: %d\n", + item->result); + } + + /* push all handled jobs to the done list at once */ + mutex_lock(&pcr->done.lock); + list_splice_tail(&tmp, &pcr->done.list); + mutex_unlock(&pcr->done.lock); + + /* wake for POLLIN */ + wake_up_interruptible(&pcr->user_waiter); +} + +/* ====== /dev/crypto ====== */ + +static int +cryptodev_open(struct inode *inode, struct file *filp) +{ + struct todo_list_item *tmp; + struct crypt_priv *pcr; + int i; + + pcr = kmalloc(sizeof(*pcr), GFP_KERNEL); + if (!pcr) + return -ENOMEM; + + memset(pcr, 0, sizeof(*pcr)); + mutex_init(&pcr->fcrypt.sem); + INIT_LIST_HEAD(&pcr->fcrypt.list); + + INIT_LIST_HEAD(&pcr->free.list); + INIT_LIST_HEAD(&pcr->todo.list); + INIT_LIST_HEAD(&pcr->done.list); + INIT_WORK(&pcr->cryptask, cryptask_routine); + mutex_init(&pcr->free.lock); + mutex_init(&pcr->todo.lock); + mutex_init(&pcr->done.lock); + init_waitqueue_head(&pcr->user_waiter); + + for (i = 0; i < DEF_COP_RINGSIZE; i++) { + tmp = kzalloc(sizeof(struct todo_list_item), GFP_KERNEL); + if (!tmp) + return -ENOMEM; + pcr->itemcount++; + dprintk(2, KERN_DEBUG, "allocated new item at %lx\n", + (unsigned long)tmp); + list_add(&tmp->__hook, &pcr->free.list); + } + + filp->private_data = pcr; + dprintk(2, KERN_DEBUG, + "Cryptodev handle initialised, %d elements in queue\n", + DEF_COP_RINGSIZE); + return 0; +} + +static int +cryptodev_release(struct inode *inode, struct file *filp) +{ + struct crypt_priv *pcr = filp->private_data; + struct todo_list_item *item, *item_safe; + int items_freed = 0; + + if (!pcr) + return 0; + + cancel_work_sync(&pcr->cryptask); + + mutex_destroy(&pcr->todo.lock); + mutex_destroy(&pcr->done.lock); + mutex_destroy(&pcr->free.lock); + + list_splice_tail(&pcr->todo.list, &pcr->free.list); + list_splice_tail(&pcr->done.list, &pcr->free.list); + + list_for_each_entry_safe(item, item_safe, &pcr->free.list, __hook) { + dprintk(2, KERN_DEBUG, "freeing item at %lx\n", + (unsigned long)item); + list_del(&item->__hook); + kfree(item); + items_freed++; + + } + if (items_freed != pcr->itemcount) { + dprintk(0, KERN_ERR, + "freed %d items, but %d should exist!\n", + items_freed, pcr->itemcount); + } + + crypto_finish_all_sessions(&pcr->fcrypt); + kfree(pcr); + filp->private_data = NULL; + + dprintk(2, KERN_DEBUG, + "Cryptodev handle deinitialised, %d elements freed\n", + items_freed); + return 0; +} + +static int +clonefd(struct file *filp) +{ + int ret; + ret = get_unused_fd(); + if (ret >= 0) { + get_file(filp); + fd_install(ret, filp); + } + + return ret; +} + +#ifdef ENABLE_ASYNC +/* enqueue a job for asynchronous completion + * + * returns: + * -EBUSY when there are no free queue slots left + * (and the number of slots has reached it MAX_COP_RINGSIZE) + * -EFAULT when there was a memory allocation error + * 0 on success */ +static int crypto_async_run(struct crypt_priv *pcr, struct kernel_crypt_op *kcop) +{ + struct todo_list_item *item = NULL; + + if (unlikely(kcop->cop.flags & COP_FLAG_NO_ZC)) + return -EINVAL; + + mutex_lock(&pcr->free.lock); + if (likely(!list_empty(&pcr->free.list))) { + item = list_first_entry(&pcr->free.list, + struct todo_list_item, __hook); + list_del(&item->__hook); + } else if (pcr->itemcount < MAX_COP_RINGSIZE) { + pcr->itemcount++; + } else { + mutex_unlock(&pcr->free.lock); + return -EBUSY; + } + mutex_unlock(&pcr->free.lock); + + if (unlikely(!item)) { + item = kzalloc(sizeof(struct todo_list_item), GFP_KERNEL); + if (unlikely(!item)) + return -EFAULT; + dprintk(1, KERN_INFO, "increased item count to %d\n", + pcr->itemcount); + } + + memcpy(&item->kcop, kcop, sizeof(struct kernel_crypt_op)); + + mutex_lock(&pcr->todo.lock); + list_add_tail(&item->__hook, &pcr->todo.list); + mutex_unlock(&pcr->todo.lock); + + queue_work(cryptodev_wq, &pcr->cryptask); + return 0; +} + +/* get the first completed job from the "done" queue + * + * returns: + * -EBUSY if no completed jobs are ready (yet) + * the return value of crypto_run() otherwise */ +static int crypto_async_fetch(struct crypt_priv *pcr, + struct kernel_crypt_op *kcop) +{ + struct todo_list_item *item; + int retval; + + mutex_lock(&pcr->done.lock); + if (list_empty(&pcr->done.list)) { + mutex_unlock(&pcr->done.lock); + return -EBUSY; + } + item = list_first_entry(&pcr->done.list, struct todo_list_item, __hook); + list_del(&item->__hook); + mutex_unlock(&pcr->done.lock); + + memcpy(kcop, &item->kcop, sizeof(struct kernel_crypt_op)); + retval = item->result; + + mutex_lock(&pcr->free.lock); + list_add_tail(&item->__hook, &pcr->free.list); + mutex_unlock(&pcr->free.lock); + + /* wake for POLLOUT */ + wake_up_interruptible(&pcr->user_waiter); + + return retval; +} +#endif + +/* this function has to be called from process context */ +static int fill_kcop_from_cop(struct kernel_crypt_op *kcop, struct fcrypt *fcr) +{ + struct crypt_op *cop = &kcop->cop; + struct csession *ses_ptr; + int rc; + + /* this also enters ses_ptr->sem */ + ses_ptr = crypto_get_session_by_sid(fcr, cop->ses); + if (unlikely(!ses_ptr)) { + dprintk(1, KERN_ERR, "invalid session ID=0x%08X\n", cop->ses); + return -EINVAL; + } + kcop->ivlen = cop->iv ? ses_ptr->cdata.ivsize : 0; + kcop->digestsize = 0; /* will be updated during operation */ + + crypto_put_session(ses_ptr); + + kcop->task = current; + kcop->mm = current->mm; + + if (cop->iv) { + rc = copy_from_user(kcop->iv, cop->iv, kcop->ivlen); + if (unlikely(rc)) { + dprintk(1, KERN_ERR, + "error copying IV (%d bytes), copy_from_user returned %d for address %lx\n", + kcop->ivlen, rc, (unsigned long)cop->iv); + return -EFAULT; + } + } + + return 0; +} + +/* this function has to be called from process context */ +static int fill_cop_from_kcop(struct kernel_crypt_op *kcop, struct fcrypt *fcr) +{ + int ret; + + if (kcop->digestsize) { + ret = copy_to_user(kcop->cop.mac, + kcop->hash_output, kcop->digestsize); + if (unlikely(ret)) + return -EFAULT; + } + if (kcop->ivlen && kcop->cop.flags & COP_FLAG_WRITE_IV) { + ret = copy_to_user(kcop->cop.iv, + kcop->iv, kcop->ivlen); + if (unlikely(ret)) + return -EFAULT; + } + return 0; +} + +static int kcop_from_user(struct kernel_crypt_op *kcop, + struct fcrypt *fcr, void __user *arg) +{ + if (unlikely(copy_from_user(&kcop->cop, arg, sizeof(kcop->cop)))) + return -EFAULT; + + return fill_kcop_from_cop(kcop, fcr); +} + +static int kcop_to_user(struct kernel_crypt_op *kcop, + struct fcrypt *fcr, void __user *arg) +{ + int ret; + + ret = fill_cop_from_kcop(kcop, fcr); + if (unlikely(ret)) { + dprintk(1, KERN_ERR, "Error in fill_cop_from_kcop\n"); + return ret; + } + + if (unlikely(copy_to_user(arg, &kcop->cop, sizeof(kcop->cop)))) { + dprintk(1, KERN_ERR, "Cannot copy to userspace\n"); + return -EFAULT; + } + return 0; +} + +static inline void tfm_info_to_alg_info(struct alg_info *dst, struct crypto_tfm *tfm) +{ + snprintf(dst->cra_name, CRYPTODEV_MAX_ALG_NAME, + "%s", crypto_tfm_alg_name(tfm)); + snprintf(dst->cra_driver_name, CRYPTODEV_MAX_ALG_NAME, + "%s", crypto_tfm_alg_driver_name(tfm)); +} + +static unsigned int is_known_accelerated(struct crypto_tfm *tfm) +{ +const char* name = crypto_tfm_alg_driver_name(tfm); + + if (name == NULL) + return 1; /* assume accelerated */ + + if (strstr(name, "-talitos")) + return 1; + else if (strncmp(name, "mv-", 3) == 0) + return 1; + else if (strstr(name, "geode")) + return 1; + else if (strstr(name, "hifn")) + return 1; + else if (strstr(name, "-ixp4xx")) + return 1; + else if (strstr(name, "-omap")) + return 1; + else if (strstr(name, "-picoxcell")) + return 1; + else if (strstr(name, "-s5p")) + return 1; + else if (strstr(name, "-ppc4xx")) + return 1; + else if (strstr(name, "-caam")) + return 1; + else if (strstr(name, "-n2")) + return 1; + + return 0; +} + +static int get_session_info(struct fcrypt *fcr, struct session_info_op *siop) +{ + struct csession *ses_ptr; + struct crypto_tfm *tfm; + + /* this also enters ses_ptr->sem */ + ses_ptr = crypto_get_session_by_sid(fcr, siop->ses); + if (unlikely(!ses_ptr)) { + dprintk(1, KERN_ERR, "invalid session ID=0x%08X\n", siop->ses); + return -EINVAL; + } + + siop->flags = 0; + + if (ses_ptr->cdata.init) { + if (ses_ptr->cdata.aead == 0) { + tfm = crypto_ablkcipher_tfm(ses_ptr->cdata.async.s); + } else { + tfm = crypto_aead_tfm(ses_ptr->cdata.async.as); + } + tfm_info_to_alg_info(&siop->cipher_info, tfm); +#ifdef CRYPTO_ALG_KERN_DRIVER_ONLY + if (tfm->__crt_alg->cra_flags & CRYPTO_ALG_KERN_DRIVER_ONLY) + siop->flags |= SIOP_FLAG_KERNEL_DRIVER_ONLY; +#else + if (is_known_accelerated(tfm)) + siop->flags |= SIOP_FLAG_KERNEL_DRIVER_ONLY; +#endif + } + if (ses_ptr->hdata.init) { + tfm = crypto_ahash_tfm(ses_ptr->hdata.async.s); + tfm_info_to_alg_info(&siop->hash_info, tfm); +#ifdef CRYPTO_ALG_KERN_DRIVER_ONLY + if (tfm->__crt_alg->cra_flags & CRYPTO_ALG_KERN_DRIVER_ONLY) + siop->flags |= SIOP_FLAG_KERNEL_DRIVER_ONLY; +#else + if (is_known_accelerated(tfm)) + siop->flags |= SIOP_FLAG_KERNEL_DRIVER_ONLY; +#endif + } + + siop->alignmask = ses_ptr->alignmask; + + crypto_put_session(ses_ptr); + return 0; +} + +static long +cryptodev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg_) +{ + void __user *arg = (void __user *)arg_; + int __user *p = arg; + struct session_op sop; + struct kernel_crypt_op kcop; + struct kernel_crypt_auth_op kcaop; + struct crypt_priv *pcr = filp->private_data; + struct fcrypt *fcr; + struct session_info_op siop; + uint32_t ses; + int ret, fd; + + if (unlikely(!pcr)) + BUG(); + + fcr = &pcr->fcrypt; + + switch (cmd) { + case CIOCASYMFEAT: + return put_user(0, p); + case CRIOGET: + fd = clonefd(filp); + ret = put_user(fd, p); + if (unlikely(ret)) { + sys_close(fd); + return ret; + } + return ret; + case CIOCGSESSION: + if (unlikely(copy_from_user(&sop, arg, sizeof(sop)))) + return -EFAULT; + + ret = crypto_create_session(fcr, &sop); + if (unlikely(ret)) + return ret; + ret = copy_to_user(arg, &sop, sizeof(sop)); + if (unlikely(ret)) { + crypto_finish_session(fcr, sop.ses); + return -EFAULT; + } + return ret; + case CIOCFSESSION: + ret = get_user(ses, (uint32_t __user *)arg); + if (unlikely(ret)) + return ret; + ret = crypto_finish_session(fcr, ses); + return ret; + case CIOCGSESSINFO: + if (unlikely(copy_from_user(&siop, arg, sizeof(siop)))) + return -EFAULT; + + ret = get_session_info(fcr, &siop); + if (unlikely(ret)) + return ret; + return copy_to_user(arg, &siop, sizeof(siop)); + case CIOCCRYPT: + if (unlikely(ret = kcop_from_user(&kcop, fcr, arg))) { + dprintk(1, KERN_WARNING, "Error copying from user\n"); + return ret; + } + + ret = crypto_run(fcr, &kcop); + if (unlikely(ret)) { + dprintk(1, KERN_WARNING, "Error in crypto_run\n"); + return ret; + } + + return kcop_to_user(&kcop, fcr, arg); + case CIOCAUTHCRYPT: + if (unlikely(ret = kcaop_from_user(&kcaop, fcr, arg))) { + dprintk(1, KERN_WARNING, "Error copying from user\n"); + return ret; + } + + ret = crypto_auth_run(fcr, &kcaop); + if (unlikely(ret)) { + dprintk(1, KERN_WARNING, "Error in crypto_auth_run\n"); + return ret; + } + return kcaop_to_user(&kcaop, fcr, arg); +#ifdef ENABLE_ASYNC + case CIOCASYNCCRYPT: + if (unlikely(ret = kcop_from_user(&kcop, fcr, arg))) + return ret; + + return crypto_async_run(pcr, &kcop); + case CIOCASYNCFETCH: + ret = crypto_async_fetch(pcr, &kcop); + if (unlikely(ret)) + return ret; + + return kcop_to_user(&kcop, fcr, arg); +#endif + default: + return -EINVAL; + } +} + +/* compatibility code for 32bit userlands */ +#ifdef CONFIG_COMPAT + +static inline void +compat_to_session_op(struct compat_session_op *compat, struct session_op *sop) +{ + sop->cipher = compat->cipher; + sop->mac = compat->mac; + sop->keylen = compat->keylen; + + sop->key = compat_ptr(compat->key); + sop->mackeylen = compat->mackeylen; + sop->mackey = compat_ptr(compat->mackey); + sop->ses = compat->ses; +} + +static inline void +session_op_to_compat(struct session_op *sop, struct compat_session_op *compat) +{ + compat->cipher = sop->cipher; + compat->mac = sop->mac; + compat->keylen = sop->keylen; + + compat->key = ptr_to_compat(sop->key); + compat->mackeylen = sop->mackeylen; + compat->mackey = ptr_to_compat(sop->mackey); + compat->ses = sop->ses; +} + +static inline void +compat_to_crypt_op(struct compat_crypt_op *compat, struct crypt_op *cop) +{ + cop->ses = compat->ses; + cop->op = compat->op; + cop->flags = compat->flags; + cop->len = compat->len; + + cop->src = compat_ptr(compat->src); + cop->dst = compat_ptr(compat->dst); + cop->mac = compat_ptr(compat->mac); + cop->iv = compat_ptr(compat->iv); +} + +static inline void +crypt_op_to_compat(struct crypt_op *cop, struct compat_crypt_op *compat) +{ + compat->ses = cop->ses; + compat->op = cop->op; + compat->flags = cop->flags; + compat->len = cop->len; + + compat->src = ptr_to_compat(cop->src); + compat->dst = ptr_to_compat(cop->dst); + compat->mac = ptr_to_compat(cop->mac); + compat->iv = ptr_to_compat(cop->iv); +} + +static int compat_kcop_from_user(struct kernel_crypt_op *kcop, + struct fcrypt *fcr, void __user *arg) +{ + struct compat_crypt_op compat_cop; + + if (unlikely(copy_from_user(&compat_cop, arg, sizeof(compat_cop)))) + return -EFAULT; + compat_to_crypt_op(&compat_cop, &kcop->cop); + + return fill_kcop_from_cop(kcop, fcr); +} + +static int compat_kcop_to_user(struct kernel_crypt_op *kcop, + struct fcrypt *fcr, void __user *arg) +{ + int ret; + struct compat_crypt_op compat_cop; + + ret = fill_cop_from_kcop(kcop, fcr); + if (unlikely(ret)) { + dprintk(1, KERN_WARNING, "Error in fill_cop_from_kcop\n"); + return ret; + } + crypt_op_to_compat(&kcop->cop, &compat_cop); + + if (unlikely(copy_to_user(arg, &compat_cop, sizeof(compat_cop)))) { + dprintk(1, KERN_WARNING, "Error copying to user\n"); + return -EFAULT; + } + return 0; +} + +static long +cryptodev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg_) +{ + void __user *arg = (void __user *)arg_; + struct crypt_priv *pcr = file->private_data; + struct fcrypt *fcr; + struct session_op sop; + struct compat_session_op compat_sop; + struct kernel_crypt_op kcop; + int ret; + + if (unlikely(!pcr)) + BUG(); + + fcr = &pcr->fcrypt; + + switch (cmd) { + case CIOCASYMFEAT: + case CRIOGET: + case CIOCFSESSION: + case CIOCGSESSINFO: + return cryptodev_ioctl(file, cmd, arg_); + + case COMPAT_CIOCGSESSION: + if (unlikely(copy_from_user(&compat_sop, arg, + sizeof(compat_sop)))) + return -EFAULT; + compat_to_session_op(&compat_sop, &sop); + + ret = crypto_create_session(fcr, &sop); + if (unlikely(ret)) + return ret; + + session_op_to_compat(&sop, &compat_sop); + ret = copy_to_user(arg, &compat_sop, sizeof(compat_sop)); + if (unlikely(ret)) { + crypto_finish_session(fcr, sop.ses); + return -EFAULT; + } + return ret; + + case COMPAT_CIOCCRYPT: + ret = compat_kcop_from_user(&kcop, fcr, arg); + if (unlikely(ret)) + return ret; + + ret = crypto_run(fcr, &kcop); + if (unlikely(ret)) + return ret; + + return compat_kcop_to_user(&kcop, fcr, arg); +#ifdef ENABLE_ASYNC + case COMPAT_CIOCASYNCCRYPT: + if (unlikely(ret = compat_kcop_from_user(&kcop, fcr, arg))) + return ret; + + return crypto_async_run(pcr, &kcop); + case COMPAT_CIOCASYNCFETCH: + ret = crypto_async_fetch(pcr, &kcop); + if (unlikely(ret)) + return ret; + + return compat_kcop_to_user(&kcop, fcr, arg); +#endif + default: + return -EINVAL; + } +} + +#endif /* CONFIG_COMPAT */ + +static unsigned int cryptodev_poll(struct file *file, poll_table *wait) +{ + struct crypt_priv *pcr = file->private_data; + int ret = 0; + + poll_wait(file, &pcr->user_waiter, wait); + + if (!list_empty_careful(&pcr->done.list)) + ret |= POLLIN | POLLRDNORM; + if (!list_empty_careful(&pcr->free.list) || pcr->itemcount < MAX_COP_RINGSIZE) + ret |= POLLOUT | POLLWRNORM; + + return ret; +} + +static const struct file_operations cryptodev_fops = { + .owner = THIS_MODULE, + .open = cryptodev_open, + .release = cryptodev_release, + .unlocked_ioctl = cryptodev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = cryptodev_compat_ioctl, +#endif /* CONFIG_COMPAT */ + .poll = cryptodev_poll, +}; + +static struct miscdevice cryptodev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "crypto", + .fops = &cryptodev_fops, + .mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, +}; + +static int __init +cryptodev_register(void) +{ + int rc; + + rc = misc_register(&cryptodev); + if (unlikely(rc)) { + printk(KERN_ERR PFX "registration of /dev/crypto failed\n"); + return rc; + } + + return 0; +} + +static void __exit +cryptodev_deregister(void) +{ + misc_deregister(&cryptodev); +} + +/* ====== Module init/exit ====== */ +static int __init init_cryptodev(void) +{ + int rc; + + cryptodev_wq = create_workqueue("cryptodev_queue"); + if (unlikely(!cryptodev_wq)) { + printk(KERN_ERR PFX "failed to allocate the cryptodev workqueue\n"); + return -EFAULT; + } + + rc = cryptodev_register(); + if (unlikely(rc)) { + destroy_workqueue(cryptodev_wq); + return rc; + } + + printk(KERN_INFO PFX "driver %s loaded.\n", VERSION); + + return 0; +} + +static void __exit exit_cryptodev(void) +{ + flush_workqueue(cryptodev_wq); + destroy_workqueue(cryptodev_wq); + + cryptodev_deregister(); + printk(KERN_INFO PFX "driver unloaded.\n"); +} + +module_init(init_cryptodev); +module_exit(exit_cryptodev); + diff --git a/crypto/crypto_dev/cryptlib.c b/crypto/crypto_dev/cryptlib.c new file mode 100644 index 00000000..fad5ba6c --- /dev/null +++ b/crypto/crypto_dev/cryptlib.c @@ -0,0 +1,377 @@ +/* + * Driver for /dev/crypto device (aka CryptoDev) + * + * Copyright (c) 2010,2011 Nikos Mavrogiannopoulos + * Portions Copyright (c) 2010 Michael Weiser + * Portions Copyright (c) 2010 Phil Sutter + * + * This file is part of linux cryptodev. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cryptodev_int.h" + + +struct cryptodev_result { + struct completion completion; + int err; +}; + +static void cryptodev_complete(struct crypto_async_request *req, int err) +{ + struct cryptodev_result *res = req->data; + + if (err == -EINPROGRESS) + return; + + res->err = err; + complete(&res->completion); +} + +int cryptodev_cipher_init(struct cipher_data *out, const char *alg_name, + uint8_t *keyp, size_t keylen, int stream, int aead) +{ + int ret; + + memset(out, 0, sizeof(*out)); + + if (aead == 0) { + struct ablkcipher_alg *alg; + + out->async.s = crypto_alloc_ablkcipher(alg_name, 0, 0); + if (unlikely(IS_ERR(out->async.s))) { + dprintk(1, KERN_DEBUG, "Failed to load cipher %s\n", alg_name); + return -EINVAL; + } + + alg = crypto_ablkcipher_alg(out->async.s); + if (alg != NULL) { + /* Was correct key length supplied? */ + if (alg->max_keysize > 0 && + unlikely((keylen < alg->min_keysize) || + (keylen > alg->max_keysize))) { + dprintk(1, KERN_DEBUG, + "Wrong keylen '%zu' for algorithm '%s'. \ + Use %u to %u.\n", + keylen, alg_name, alg->min_keysize, + alg->max_keysize); + ret = -EINVAL; + goto error; + } + } + + out->blocksize = crypto_ablkcipher_blocksize(out->async.s); + out->ivsize = crypto_ablkcipher_ivsize(out->async.s); + out->alignmask = crypto_ablkcipher_alignmask(out->async.s); + + ret = crypto_ablkcipher_setkey(out->async.s, keyp, keylen); + } else { + out->async.as = crypto_alloc_aead(alg_name, 0, 0); + if (unlikely(IS_ERR(out->async.as))) { + dprintk(1, KERN_DEBUG, "Failed to load cipher %s\n", alg_name); + return -EINVAL; + } + + out->blocksize = crypto_aead_blocksize(out->async.as); + out->ivsize = crypto_aead_ivsize(out->async.as); + out->alignmask = crypto_aead_alignmask(out->async.as); + + ret = crypto_aead_setkey(out->async.as, keyp, keylen); + } + + if (unlikely(ret)) { + dprintk(1, KERN_DEBUG, "Setting key failed for %s-%zu.\n", + alg_name, keylen*8); + ret = -EINVAL; + goto error; + } + + out->stream = stream; + out->aead = aead; + + out->async.result = kmalloc(sizeof(*out->async.result), GFP_KERNEL); + if (unlikely(!out->async.result)) { + ret = -ENOMEM; + goto error; + } + + memset(out->async.result, 0, sizeof(*out->async.result)); + init_completion(&out->async.result->completion); + + if (aead == 0) { + out->async.request = ablkcipher_request_alloc(out->async.s, GFP_KERNEL); + if (unlikely(!out->async.request)) { + dprintk(1, KERN_ERR, "error allocating async crypto request\n"); + ret = -ENOMEM; + goto error; + } + + ablkcipher_request_set_callback(out->async.request, + CRYPTO_TFM_REQ_MAY_BACKLOG, + cryptodev_complete, out->async.result); + } else { + out->async.arequest = aead_request_alloc(out->async.as, GFP_KERNEL); + if (unlikely(!out->async.arequest)) { + dprintk(1, KERN_ERR, "error allocating async crypto request\n"); + ret = -ENOMEM; + goto error; + } + + aead_request_set_callback(out->async.arequest, + CRYPTO_TFM_REQ_MAY_BACKLOG, + cryptodev_complete, out->async.result); + } + + out->init = 1; + return 0; +error: + if (aead == 0) { + if (out->async.request) + ablkcipher_request_free(out->async.request); + if (out->async.s) + crypto_free_ablkcipher(out->async.s); + } else { + if (out->async.arequest) + aead_request_free(out->async.arequest); + if (out->async.s) + crypto_free_aead(out->async.as); + } + kfree(out->async.result); + + return ret; +} + +void cryptodev_cipher_deinit(struct cipher_data *cdata) +{ + if (cdata->init) { + if (cdata->aead == 0) { + if (cdata->async.request) + ablkcipher_request_free(cdata->async.request); + if (cdata->async.s) + crypto_free_ablkcipher(cdata->async.s); + } else { + if (cdata->async.arequest) + aead_request_free(cdata->async.arequest); + if (cdata->async.as) + crypto_free_aead(cdata->async.as); + } + + kfree(cdata->async.result); + cdata->init = 0; + } +} + +static inline int waitfor(struct cryptodev_result *cr, ssize_t ret) +{ + switch (ret) { + case 0: + break; + case -EINPROGRESS: + case -EBUSY: + wait_for_completion(&cr->completion); + /* At this point we known for sure the request has finished, + * because wait_for_completion above was not interruptible. + * This is important because otherwise hardware or driver + * might try to access memory which will be freed or reused for + * another request. */ + + if (unlikely(cr->err)) { + dprintk(0, KERN_ERR, "error from async request: %d\n", + cr->err); + return cr->err; + } + + break; + default: + return ret; + } + + return 0; +} + +ssize_t cryptodev_cipher_encrypt(struct cipher_data *cdata, + const struct scatterlist *src, struct scatterlist *dst, + size_t len) +{ + int ret; + + INIT_COMPLETION(cdata->async.result->completion); + + if (cdata->aead == 0) { + ablkcipher_request_set_crypt(cdata->async.request, + (struct scatterlist *)src, dst, + len, cdata->async.iv); + ret = crypto_ablkcipher_encrypt(cdata->async.request); + } else { + aead_request_set_crypt(cdata->async.arequest, + (struct scatterlist *)src, dst, + len, cdata->async.iv); + ret = crypto_aead_encrypt(cdata->async.arequest); + } + + return waitfor(cdata->async.result, ret); +} + +ssize_t cryptodev_cipher_decrypt(struct cipher_data *cdata, + const struct scatterlist *src, struct scatterlist *dst, + size_t len) +{ + int ret; + + INIT_COMPLETION(cdata->async.result->completion); + if (cdata->aead == 0) { + ablkcipher_request_set_crypt(cdata->async.request, + (struct scatterlist *)src, dst, + len, cdata->async.iv); + ret = crypto_ablkcipher_decrypt(cdata->async.request); + } else { + aead_request_set_crypt(cdata->async.arequest, + (struct scatterlist *)src, dst, + len, cdata->async.iv); + ret = crypto_aead_decrypt(cdata->async.arequest); + } + + return waitfor(cdata->async.result, ret); +} + +/* Hash functions */ + +int cryptodev_hash_init(struct hash_data *hdata, const char *alg_name, + int hmac_mode, void *mackey, size_t mackeylen) +{ + int ret; + + hdata->async.s = crypto_alloc_ahash(alg_name, 0, 0); + if (unlikely(IS_ERR(hdata->async.s))) { + dprintk(1, KERN_DEBUG, "Failed to load transform for %s\n", alg_name); + return -EINVAL; + } + + /* Copy the key from user and set to TFM. */ + if (hmac_mode != 0) { + ret = crypto_ahash_setkey(hdata->async.s, mackey, mackeylen); + if (unlikely(ret)) { + dprintk(1, KERN_DEBUG, + "Setting hmac key failed for %s-%zu.\n", + alg_name, mackeylen*8); + ret = -EINVAL; + goto error; + } + } + + hdata->digestsize = crypto_ahash_digestsize(hdata->async.s); + hdata->alignmask = crypto_ahash_alignmask(hdata->async.s); + + hdata->async.result = kmalloc(sizeof(*hdata->async.result), GFP_KERNEL); + if (unlikely(!hdata->async.result)) { + ret = -ENOMEM; + goto error; + } + + memset(hdata->async.result, 0, sizeof(*hdata->async.result)); + init_completion(&hdata->async.result->completion); + + hdata->async.request = ahash_request_alloc(hdata->async.s, GFP_KERNEL); + if (unlikely(!hdata->async.request)) { + dprintk(0, KERN_ERR, "error allocating async crypto request\n"); + ret = -ENOMEM; + goto error; + } + + ahash_request_set_callback(hdata->async.request, + CRYPTO_TFM_REQ_MAY_BACKLOG, + cryptodev_complete, hdata->async.result); + + ret = crypto_ahash_init(hdata->async.request); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "error in crypto_hash_init()\n"); + goto error_request; + } + + hdata->init = 1; + return 0; + +error_request: + ahash_request_free(hdata->async.request); +error: + kfree(hdata->async.result); + crypto_free_ahash(hdata->async.s); + return ret; +} + +void cryptodev_hash_deinit(struct hash_data *hdata) +{ + if (hdata->init) { + if (hdata->async.request) + ahash_request_free(hdata->async.request); + kfree(hdata->async.result); + if (hdata->async.s) + crypto_free_ahash(hdata->async.s); + hdata->init = 0; + } +} + +int cryptodev_hash_reset(struct hash_data *hdata) +{ + int ret; + + ret = crypto_ahash_init(hdata->async.request); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "error in crypto_hash_init()\n"); + return ret; + } + + return 0; + +} + +ssize_t cryptodev_hash_update(struct hash_data *hdata, + struct scatterlist *sg, size_t len) +{ + int ret; + + INIT_COMPLETION(hdata->async.result->completion); + ahash_request_set_crypt(hdata->async.request, sg, NULL, len); + + ret = crypto_ahash_update(hdata->async.request); + + return waitfor(hdata->async.result, ret); +} + +int cryptodev_hash_final(struct hash_data *hdata, void* output) +{ + int ret; + + INIT_COMPLETION(hdata->async.result->completion); + ahash_request_set_crypt(hdata->async.request, NULL, output, 0); + + ret = crypto_ahash_final(hdata->async.request); + + return waitfor(hdata->async.result, ret); +} + diff --git a/crypto/crypto_dev/cryptlib.h b/crypto/crypto_dev/cryptlib.h new file mode 100644 index 00000000..1745d0fe --- /dev/null +++ b/crypto/crypto_dev/cryptlib.h @@ -0,0 +1,90 @@ +#ifndef CRYPTLIB_H +# define CRYPTLIB_H + +struct cipher_data { + int init; /* 0 uninitialized */ + int blocksize; + int aead; + int stream; + int ivsize; + int alignmask; + struct { + /* block ciphers */ + struct crypto_ablkcipher *s; + struct ablkcipher_request *request; + + /* AEAD ciphers */ + struct crypto_aead *as; + struct aead_request *arequest; + + struct cryptodev_result *result; + uint8_t iv[EALG_MAX_BLOCK_LEN]; + } async; +}; + +int cryptodev_cipher_init(struct cipher_data *out, const char *alg_name, + uint8_t *key, size_t keylen, int stream, int aead); +void cryptodev_cipher_deinit(struct cipher_data *cdata); +ssize_t cryptodev_cipher_decrypt(struct cipher_data *cdata, + const struct scatterlist *sg1, + struct scatterlist *sg2, size_t len); +ssize_t cryptodev_cipher_encrypt(struct cipher_data *cdata, + const struct scatterlist *sg1, + struct scatterlist *sg2, size_t len); + +/* AEAD */ +inline static void cryptodev_cipher_auth(struct cipher_data *cdata, + struct scatterlist *sg1, size_t len) +{ + /* for some reason we _have_ to call that even for zero length sgs */ + aead_request_set_assoc(cdata->async.arequest, len ? sg1 : NULL, len); +} + +inline static void cryptodev_cipher_set_tag_size(struct cipher_data *cdata, int size) +{ + if (likely(cdata->aead != 0)) + crypto_aead_setauthsize(cdata->async.as, size); +} + +inline static int cryptodev_cipher_get_tag_size(struct cipher_data *cdata) +{ + if (likely(cdata->init && cdata->aead != 0)) + return crypto_aead_authsize(cdata->async.as); + else + return 0; +} + +inline static void cryptodev_cipher_set_iv(struct cipher_data *cdata, + void *iv, size_t iv_size) +{ + memcpy(cdata->async.iv, iv, min(iv_size, sizeof(cdata->async.iv))); +} + +inline static void cryptodev_cipher_get_iv(struct cipher_data *cdata, + void *iv, size_t iv_size) +{ + memcpy(iv, cdata->async.iv, min(iv_size, sizeof(cdata->async.iv))); +} + +/* Hash */ +struct hash_data { + int init; /* 0 uninitialized */ + int digestsize; + int alignmask; + struct { + struct crypto_ahash *s; + struct cryptodev_result *result; + struct ahash_request *request; + } async; +}; + +int cryptodev_hash_final(struct hash_data *hdata, void *output); +ssize_t cryptodev_hash_update(struct hash_data *hdata, + struct scatterlist *sg, size_t len); +int cryptodev_hash_reset(struct hash_data *hdata); +void cryptodev_hash_deinit(struct hash_data *hdata); +int cryptodev_hash_init(struct hash_data *hdata, const char *alg_name, + int hmac_mode, void *mackey, size_t mackeylen); + + +#endif diff --git a/crypto/crypto_dev/crypto_run.c b/crypto/crypto_dev/crypto_run.c new file mode 100644 index 00000000..fe6d3909 --- /dev/null +++ b/crypto/crypto_dev/crypto_run.c @@ -0,0 +1,257 @@ +/* + * Driver for /dev/crypto device (aka CryptoDev) + * + * Copyright (c) 2004 Michal Ludvig , SuSE Labs + * Copyright (c) 2009,2010 Nikos Mavrogiannopoulos + * + * This file is part of linux cryptodev. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * Device /dev/crypto provides an interface for + * accessing kernel CryptoAPI algorithms (ciphers, + * hashes) from userspace programs. + * + * /dev/crypto interface was originally introduced in + * OpenBSD and this module attempts to keep the API. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cryptodev_int.h" +#include "zc.h" +#include "cryptlib.h" +#include "version.h" + +/* This file contains the traditional operations of encryption + * and hashing of /dev/crypto. + */ + +static int +hash_n_crypt(struct csession *ses_ptr, struct crypt_op *cop, + struct scatterlist *src_sg, struct scatterlist *dst_sg, + uint32_t len) +{ + int ret; + + /* Always hash before encryption and after decryption. Maybe + * we should introduce a flag to switch... TBD later on. + */ + if (cop->op == COP_ENCRYPT) { + if (ses_ptr->hdata.init != 0) { + ret = cryptodev_hash_update(&ses_ptr->hdata, + src_sg, len); + if (unlikely(ret)) + goto out_err; + } + if (ses_ptr->cdata.init != 0) { + ret = cryptodev_cipher_encrypt(&ses_ptr->cdata, + src_sg, dst_sg, len); + + if (unlikely(ret)) + goto out_err; + } + } else { + if (ses_ptr->cdata.init != 0) { + ret = cryptodev_cipher_decrypt(&ses_ptr->cdata, + src_sg, dst_sg, len); + + if (unlikely(ret)) + goto out_err; + } + + if (ses_ptr->hdata.init != 0) { + ret = cryptodev_hash_update(&ses_ptr->hdata, + dst_sg, len); + if (unlikely(ret)) + goto out_err; + } + } + return 0; +out_err: + dprintk(0, KERN_ERR, "CryptoAPI failure: %d\n", ret); + return ret; +} + +/* This is the main crypto function - feed it with plaintext + and get a ciphertext (or vice versa :-) */ +static int +__crypto_run_std(struct csession *ses_ptr, struct crypt_op *cop) +{ + char *data; + char __user *src, *dst; + struct scatterlist sg; + size_t nbytes, bufsize; + int ret = 0; + + nbytes = cop->len; + data = (char *)__get_free_page(GFP_KERNEL); + + if (unlikely(!data)) { + dprintk(1, KERN_ERR, "Error getting free page.\n"); + return -ENOMEM; + } + + bufsize = PAGE_SIZE < nbytes ? PAGE_SIZE : nbytes; + + src = cop->src; + dst = cop->dst; + + while (nbytes > 0) { + size_t current_len = nbytes > bufsize ? bufsize : nbytes; + + if (unlikely(copy_from_user(data, src, current_len))) { + dprintk(1, KERN_ERR, "Error copying %d bytes from user address %p.\n", (int)current_len, src); + ret = -EFAULT; + break; + } + + sg_init_one(&sg, data, current_len); + + ret = hash_n_crypt(ses_ptr, cop, &sg, &sg, current_len); + + if (unlikely(ret)) { + dprintk(1, KERN_ERR, "hash_n_crypt failed.\n"); + break; + } + + if (ses_ptr->cdata.init != 0) { + if (unlikely(copy_to_user(dst, data, current_len))) { + dprintk(1, KERN_ERR, "could not copy to user.\n"); + ret = -EFAULT; + break; + } + } + + dst += current_len; + nbytes -= current_len; + src += current_len; + } + + free_page((unsigned long)data); + return ret; +} + + + +/* This is the main crypto function - zero-copy edition */ +static int +__crypto_run_zc(struct csession *ses_ptr, struct kernel_crypt_op *kcop) +{ + struct scatterlist *src_sg, *dst_sg; + struct crypt_op *cop = &kcop->cop; + int ret = 0; + + ret = get_userbuf(ses_ptr, cop->src, cop->len, cop->dst, cop->len, + kcop->task, kcop->mm, &src_sg, &dst_sg); + if (unlikely(ret)) { + dprintk(1, KERN_ERR, "Error getting user pages. " + "Falling back to non zero copy.\n"); + return __crypto_run_std(ses_ptr, cop); + } + + ret = hash_n_crypt(ses_ptr, cop, src_sg, dst_sg, cop->len); + + release_user_pages(ses_ptr); + return ret; +} + +int crypto_run(struct fcrypt *fcr, struct kernel_crypt_op *kcop) +{ + struct csession *ses_ptr; + struct crypt_op *cop = &kcop->cop; + int ret; + + if (unlikely(cop->op != COP_ENCRYPT && cop->op != COP_DECRYPT)) { + dprintk(1, KERN_DEBUG, "invalid operation op=%u\n", cop->op); + return -EINVAL; + } + + /* this also enters ses_ptr->sem */ + ses_ptr = crypto_get_session_by_sid(fcr, cop->ses); + if (unlikely(!ses_ptr)) { + dprintk(1, KERN_ERR, "invalid session ID=0x%08X\n", cop->ses); + return -EINVAL; + } + + if (ses_ptr->hdata.init != 0 && (cop->flags == 0 || cop->flags & COP_FLAG_RESET)) { + ret = cryptodev_hash_reset(&ses_ptr->hdata); + if (unlikely(ret)) { + dprintk(1, KERN_ERR, + "error in cryptodev_hash_reset()\n"); + goto out_unlock; + } + } + + if (ses_ptr->cdata.init != 0) { + int blocksize = ses_ptr->cdata.blocksize; + + if (unlikely(cop->len % blocksize)) { + dprintk(1, KERN_ERR, + "data size (%u) isn't a multiple " + "of block size (%u)\n", + cop->len, blocksize); + ret = -EINVAL; + goto out_unlock; + } + + cryptodev_cipher_set_iv(&ses_ptr->cdata, kcop->iv, + min(ses_ptr->cdata.ivsize, kcop->ivlen)); + } + + if (likely(cop->len)) { + if (cop->flags & COP_FLAG_NO_ZC) + ret = __crypto_run_std(ses_ptr, &kcop->cop); + else + ret = __crypto_run_zc(ses_ptr, kcop); + if (unlikely(ret)) + goto out_unlock; + } + + if (ses_ptr->cdata.init != 0) { + cryptodev_cipher_get_iv(&ses_ptr->cdata, kcop->iv, + min(ses_ptr->cdata.ivsize, kcop->ivlen)); + } + + if (ses_ptr->hdata.init != 0 && + ((cop->flags & COP_FLAG_FINAL) || + (!(cop->flags & COP_FLAG_UPDATE) || cop->len == 0))) { + + ret = cryptodev_hash_final(&ses_ptr->hdata, kcop->hash_output); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "CryptoAPI failure: %d\n", ret); + goto out_unlock; + } + kcop->digestsize = ses_ptr->hdata.digestsize; + } + +out_unlock: + crypto_put_session(ses_ptr); + return ret; +} diff --git a/crypto/crypto_dev/cryptodev_int.h b/crypto/crypto_dev/cryptodev_int.h new file mode 100644 index 00000000..12dd5b15 --- /dev/null +++ b/crypto/crypto_dev/cryptodev_int.h @@ -0,0 +1,134 @@ +/* cipher stuff */ +#ifndef CRYPTODEV_INT_H +# define CRYPTODEV_INT_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PFX "cryptodev: " +#define dprintk(level, severity, format, a...) \ + do { \ + if (level <= cryptodev_verbosity) \ + printk(severity PFX "%s[%u] (%s:%u): " format, \ + current->comm, current->pid, \ + __func__, __LINE__, \ + ##a); \ + } while (0) + +extern int cryptodev_verbosity; + +struct fcrypt { + struct list_head list; + struct mutex sem; +}; + +/* compatibility stuff */ +#ifdef CONFIG_COMPAT +#include + +/* input of CIOCGSESSION */ +struct compat_session_op { + /* Specify either cipher or mac + */ + uint32_t cipher; /* cryptodev_crypto_op_t */ + uint32_t mac; /* cryptodev_crypto_op_t */ + + uint32_t keylen; + compat_uptr_t key; /* pointer to key data */ + uint32_t mackeylen; + compat_uptr_t mackey; /* pointer to mac key data */ + + uint32_t ses; /* session identifier */ +}; + +/* input of CIOCCRYPT */ + struct compat_crypt_op { + uint32_t ses; /* session identifier */ + uint16_t op; /* COP_ENCRYPT or COP_DECRYPT */ + uint16_t flags; /* see COP_FLAG_* */ + uint32_t len; /* length of source data */ + compat_uptr_t src; /* source data */ + compat_uptr_t dst; /* pointer to output data */ + compat_uptr_t mac;/* pointer to output data for hash/MAC operations */ + compat_uptr_t iv;/* initialization vector for encryption operations */ +}; + +/* compat ioctls, defined for the above structs */ +#define COMPAT_CIOCGSESSION _IOWR('c', 102, struct compat_session_op) +#define COMPAT_CIOCCRYPT _IOWR('c', 104, struct compat_crypt_op) +#define COMPAT_CIOCASYNCCRYPT _IOW('c', 107, struct compat_crypt_op) +#define COMPAT_CIOCASYNCFETCH _IOR('c', 108, struct compat_crypt_op) + +#endif /* CONFIG_COMPAT */ + +/* kernel-internal extension to struct crypt_op */ +struct kernel_crypt_op { + struct crypt_op cop; + + int ivlen; + __u8 iv[EALG_MAX_BLOCK_LEN]; + + int digestsize; + uint8_t hash_output[AALG_MAX_RESULT_LEN]; + + struct task_struct *task; + struct mm_struct *mm; +}; + +struct kernel_crypt_auth_op { + struct crypt_auth_op caop; + + int dst_len; /* based on src_len + pad + tag */ + int ivlen; + __u8 iv[EALG_MAX_BLOCK_LEN]; + + struct task_struct *task; + struct mm_struct *mm; +}; + +/* auth */ + +int kcaop_from_user(struct kernel_crypt_auth_op *kcop, + struct fcrypt *fcr, void __user *arg); +int kcaop_to_user(struct kernel_crypt_auth_op *kcaop, + struct fcrypt *fcr, void __user *arg); +int crypto_auth_run(struct fcrypt *fcr, struct kernel_crypt_auth_op *kcaop); +int crypto_run(struct fcrypt *fcr, struct kernel_crypt_op *kcop); + +#include + +/* other internal structs */ +struct csession { + struct list_head entry; + struct mutex sem; + struct cipher_data cdata; + struct hash_data hdata; + uint32_t sid; + uint32_t alignmask; + + unsigned int array_size; + unsigned int used_pages; /* the number of pages that are used */ + /* the number of pages marked as NOT-writable; they preceed writeables */ + unsigned int readonly_pages; + struct page **pages; + struct scatterlist *sg; +}; + +struct csession *crypto_get_session_by_sid(struct fcrypt *fcr, uint32_t sid); + +inline static void crypto_put_session(struct csession * ses_ptr) +{ + mutex_unlock(&ses_ptr->sem); +} +int adjust_sg_array(struct csession * ses, int pagecount); + +#endif /* CRYPTODEV_INT_H */ diff --git a/crypto/crypto_dev/util.c b/crypto/crypto_dev/util.c new file mode 100644 index 00000000..9eba4836 --- /dev/null +++ b/crypto/crypto_dev/util.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2011 Maxim Levitsky + * + * This file is part of linux cryptodev. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include "util.h" + +/* These were taken from Maxim Levitsky's patch to lkml. + */ +struct scatterlist *sg_advance(struct scatterlist *sg, int consumed) +{ + while (consumed >= sg->length) { + consumed -= sg->length; + + sg = sg_next(sg); + if (!sg) + break; + } + + WARN_ON(!sg && consumed); + + if (!sg) + return NULL; + + sg->offset += consumed; + sg->length -= consumed; + + if (sg->offset >= PAGE_SIZE) { + struct page *page = + nth_page(sg_page(sg), sg->offset / PAGE_SIZE); + sg_set_page(sg, page, sg->length, sg->offset % PAGE_SIZE); + } + + return sg; +} + +/** + * sg_copy - copies sg entries from sg_from to sg_to, such + * as sg_to covers first 'len' bytes from sg_from. + */ +int sg_copy(struct scatterlist *sg_from, struct scatterlist *sg_to, int len) +{ + while (len > sg_from->length) { + len -= sg_from->length; + + sg_set_page(sg_to, sg_page(sg_from), + sg_from->length, sg_from->offset); + + sg_to = sg_next(sg_to); + sg_from = sg_next(sg_from); + + if (len && (!sg_from || !sg_to)) + return -ENOMEM; + } + + if (len) + sg_set_page(sg_to, sg_page(sg_from), + len, sg_from->offset); + sg_mark_end(sg_to); + return 0; +} + diff --git a/crypto/crypto_dev/util.h b/crypto/crypto_dev/util.h new file mode 100644 index 00000000..204de758 --- /dev/null +++ b/crypto/crypto_dev/util.h @@ -0,0 +1,2 @@ +int sg_copy(struct scatterlist *sg_from, struct scatterlist *sg_to, int len); +struct scatterlist *sg_advance(struct scatterlist *sg, int consumed); diff --git a/crypto/crypto_dev/version.h b/crypto/crypto_dev/version.h new file mode 100644 index 00000000..83d49dae --- /dev/null +++ b/crypto/crypto_dev/version.h @@ -0,0 +1 @@ +#define VERSION "1.6" diff --git a/crypto/crypto_dev/zc.c b/crypto/crypto_dev/zc.c new file mode 100644 index 00000000..884dbab0 --- /dev/null +++ b/crypto/crypto_dev/zc.c @@ -0,0 +1,217 @@ +/* + * Driver for /dev/crypto device (aka CryptoDev) + * + * Copyright (c) 2009-2011 Nikos Mavrogiannopoulos + * Copyright (c) 2010 Phil Sutter + * Copyright (c) 2011, 2012 OpenSSL Software Foundation, Inc. + * + * This file is part of linux cryptodev. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cryptodev_int.h" +#include "zc.h" +#include "version.h" + +/* Helper functions to assist zero copy. + * This needs to be redesigned and moved out of the session. --nmav + */ + +/* offset of buf in it's first page */ +#define PAGEOFFSET(buf) ((unsigned long)buf & ~PAGE_MASK) + +/* fetch the pages addr resides in into pg and initialise sg with them */ +int __get_userbuf(uint8_t __user *addr, uint32_t len, int write, + unsigned int pgcount, struct page **pg, struct scatterlist *sg, + struct task_struct *task, struct mm_struct *mm) +{ + int ret, pglen, i = 0; + struct scatterlist *sgp; + + if (unlikely(!pgcount || !len || !addr)) { + sg_mark_end(sg); + return 0; + } + + down_read(&mm->mmap_sem); + ret = get_user_pages(task, mm, + (unsigned long)addr, pgcount, write, 0, pg, NULL); + up_read(&mm->mmap_sem); + if (ret != pgcount) + return -EINVAL; + + sg_init_table(sg, pgcount); + + pglen = min((ptrdiff_t)(PAGE_SIZE - PAGEOFFSET(addr)), (ptrdiff_t)len); + sg_set_page(sg, pg[i++], pglen, PAGEOFFSET(addr)); + + len -= pglen; + for (sgp = sg_next(sg); len; sgp = sg_next(sgp)) { + pglen = min((uint32_t)PAGE_SIZE, len); + sg_set_page(sgp, pg[i++], pglen, 0); + len -= pglen; + } + sg_mark_end(sg_last(sg, pgcount)); + return 0; +} + +int adjust_sg_array(struct csession * ses, int pagecount) +{ + struct scatterlist *sg; + struct page **pages; + int array_size; + + for (array_size = ses->array_size; array_size < pagecount; + array_size *= 2) + ; + dprintk(0, KERN_DEBUG, "reallocating from %d to %d pages\n", + ses->array_size, array_size); + pages = krealloc(ses->pages, array_size * sizeof(struct page *), + GFP_KERNEL); + if (unlikely(!pages)) + return -ENOMEM; + ses->pages = pages; + sg = krealloc(ses->sg, array_size * sizeof(struct scatterlist), + GFP_KERNEL); + if (unlikely(!sg)) + return -ENOMEM; + ses->sg = sg; + ses->array_size = array_size; + + return 0; +} + +void release_user_pages(struct csession *ses) +{ + unsigned int i; + + for (i=0;iused_pages;i++) { + if (!PageReserved(ses->pages[i])) + SetPageDirty(ses->pages[i]); + + if (ses->readonly_pages == 0) + flush_dcache_page(ses->pages[i]); + else + ses->readonly_pages--; + + page_cache_release(ses->pages[i]); + } + ses->used_pages = 0; +} + +/* make src and dst available in scatterlists. + * dst might be the same as src. + */ +int get_userbuf(struct csession *ses, + void* __user src, unsigned int src_len, + void* __user dst, unsigned int dst_len, + struct task_struct *task, struct mm_struct *mm, + struct scatterlist **src_sg, + struct scatterlist **dst_sg) +{ + int src_pagecount, dst_pagecount; + int rc; + + /* Empty input is a valid option to many algorithms & is tested by NIST/FIPS */ + /* Make sure NULL input has 0 length */ + if (!src && src_len) + src_len = 0; + + /* I don't know that null output is ever useful, but we can handle it gracefully */ + /* Make sure NULL output has 0 length */ + if (!dst && dst_len) + dst_len = 0; + + if (ses->alignmask && !IS_ALIGNED((unsigned long)src, ses->alignmask)) { + dprintk(2, KERN_WARNING, "careful - source address %lx is not %d byte aligned\n", + (unsigned long)src, ses->alignmask + 1); + } + + if (ses->alignmask && !IS_ALIGNED((unsigned long)dst, ses->alignmask)) { + dprintk(2, KERN_WARNING, "careful - destination address %lx is not %d byte aligned\n", + (unsigned long)dst, ses->alignmask + 1); + } + + src_pagecount = PAGECOUNT(src, src_len); + dst_pagecount = PAGECOUNT(dst, dst_len); + + ses->used_pages = (src == dst) ? max(src_pagecount, dst_pagecount) + : src_pagecount + dst_pagecount; + + ses->readonly_pages = (src == dst) ? 0 : src_pagecount; + + if (ses->used_pages > ses->array_size) { + rc = adjust_sg_array(ses, ses->used_pages); + if (rc) + return rc; + } + + if (src == dst) { /* inplace operation */ + rc = __get_userbuf(src, src_len, 1, ses->used_pages, + ses->pages, ses->sg, task, mm); + if (unlikely(rc)) { + dprintk(1, KERN_ERR, + "failed to get user pages for data IO\n"); + return rc; + } + (*src_sg) = (*dst_sg) = ses->sg; + return 0; + } + + *src_sg = NULL; // default to no input + *dst_sg = NULL; // default to ignore output + + if(likely(src)) { + rc = __get_userbuf(src, src_len, 0, ses->readonly_pages, + ses->pages, ses->sg, task, mm); + if (unlikely(rc)) { + dprintk(1, KERN_ERR, + "failed to get user pages for data input\n"); + return rc; + } + *src_sg = ses->sg; + } + + if(likely(dst)) { + const unsigned int writable_pages = + ses->used_pages - ses->readonly_pages; + struct page **dst_pages = ses->pages + ses->readonly_pages; + *dst_sg = ses->sg + ses->readonly_pages; + + rc = __get_userbuf(dst, dst_len, 1, writable_pages, + dst_pages, *dst_sg, task, mm); + if (unlikely(rc)) { + dprintk(1, KERN_ERR, + "failed to get user pages for data output\n"); + release_user_pages(ses); /* FIXME: use __release_userbuf(src, ...) */ + return rc; + } + } + return 0; +} + diff --git a/crypto/crypto_dev/zc.h b/crypto/crypto_dev/zc.h new file mode 100644 index 00000000..b52616ea --- /dev/null +++ b/crypto/crypto_dev/zc.h @@ -0,0 +1,27 @@ +#ifndef ZC_H +# define ZC_H + +#include "cryptodev_int.h" + +/* For zero copy */ +int __get_userbuf(uint8_t __user *addr, uint32_t len, int write, + unsigned int pgcount, struct page **pg, struct scatterlist *sg, + struct task_struct *task, struct mm_struct *mm); +void release_user_pages(struct csession* ses); + +int get_userbuf(struct csession *ses, + void* __user src, unsigned int src_len, + void* __user dst, unsigned int dst_len, + struct task_struct *task, struct mm_struct *mm, + struct scatterlist **src_sg, + struct scatterlist **dst_sg); + +/* buflen ? (last page - first page + 1) : 0 */ +#define PAGECOUNT(buf, buflen) ((buflen) \ + ? ((((unsigned long)(buf + buflen - 1)) >> PAGE_SHIFT) - \ + (((unsigned long)(buf )) >> PAGE_SHIFT) + 1) \ + : 0) + +#define DEFAULT_PREALLOC_PAGES 32 + +#endif diff --git a/drivers/Kconfig b/drivers/Kconfig index d236aef7..be0f4586 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -96,6 +96,8 @@ source "drivers/memstick/Kconfig" source "drivers/leds/Kconfig" +source "drivers/switch/Kconfig" + source "drivers/accessibility/Kconfig" source "drivers/infiniband/Kconfig" @@ -140,4 +142,11 @@ source "drivers/virt/Kconfig" source "drivers/devfreq/Kconfig" +config AEC_DUMP_DEBUG + bool "Enable AEC dump debug function" + default n + help + Say Y if you want to use console to dump AEC debug data. + Only for debugging purpose. + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 95952c82..b5d2823d 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -100,6 +100,7 @@ obj-$(CONFIG_CPU_IDLE) += cpuidle/ obj-y += mmc/ obj-$(CONFIG_MEMSTICK) += memstick/ obj-y += leds/ +obj-$(CONFIG_SWITCH) += switch/ obj-$(CONFIG_INFINIBAND) += infiniband/ obj-$(CONFIG_SGI_SN) += sn/ obj-y += firmware/ diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 9aa618ac..1131dd73 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -192,4 +192,30 @@ config DMA_SHARED_BUFFER APIs extension; the file's descriptor can then be passed on to other driver. +config SYNC + bool "Synchronization framework" + default n + select ANON_INODES + help + This option enables the framework for synchronization between multiple + drivers. Sync implementations can take advantage of hardware + synchronization built into devices like GPUs. + +config SW_SYNC + bool "Software synchronization objects" + default n + depends on SYNC + help + A sync object driver that uses a 32bit counter to coordinate + syncrhronization. Useful when there is no hardware primitive backing + the synchronization. + +config SW_SYNC_USER + bool "Userspace API for SW_SYNC" + default n + depends on SW_SYNC + help + Provides a user space API to the sw sync object. + *WARNING* improper use of this can result in deadlocking kernel + drivers from userspace. endmenu diff --git a/drivers/base/Makefile b/drivers/base/Makefile index b6d1b9c4..0e4d3dad 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -21,5 +21,8 @@ obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o obj-$(CONFIG_REGMAP) += regmap/ obj-$(CONFIG_SOC_BUS) += soc.o +obj-$(CONFIG_SYNC) += sync.o +obj-$(CONFIG_SW_SYNC) += sw_sync.o + ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG diff --git a/drivers/base/dma-buf.c b/drivers/base/dma-buf.c index 07cbbc6f..7cfb405b 100644 --- a/drivers/base/dma-buf.c +++ b/drivers/base/dma-buf.c @@ -44,8 +44,26 @@ static int dma_buf_release(struct inode *inode, struct file *file) return 0; } +static int dma_buf_mmap_internal(struct file *file, struct vm_area_struct *vma) +{ + struct dma_buf *dmabuf; + + if (!is_dma_buf_file(file)) + return -EINVAL; + + dmabuf = file->private_data; + + /* check for overflowing the buffer's size */ + if (vma->vm_pgoff + ((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) > + dmabuf->size >> PAGE_SHIFT) + return -EINVAL; + + return dmabuf->ops->mmap(dmabuf, vma); +} + static const struct file_operations dma_buf_fops = { .release = dma_buf_release, + .mmap = dma_buf_mmap_internal, }; /* @@ -82,7 +100,8 @@ struct dma_buf *dma_buf_export(void *priv, const struct dma_buf_ops *ops, || !ops->unmap_dma_buf || !ops->release || !ops->kmap_atomic - || !ops->kmap)) { + || !ops->kmap + || !ops->mmap)) { return ERR_PTR(-EINVAL); } @@ -406,3 +425,46 @@ void dma_buf_kunmap(struct dma_buf *dmabuf, unsigned long page_num, dmabuf->ops->kunmap(dmabuf, page_num, vaddr); } EXPORT_SYMBOL_GPL(dma_buf_kunmap); + + +/** + * dma_buf_mmap - Setup up a userspace mmap with the given vma + * @dma_buf: [in] buffer that should back the vma + * @vma: [in] vma for the mmap + * @pgoff: [in] offset in pages where this mmap should start within the + * dma-buf buffer. + * + * This function adjusts the passed in vma so that it points at the file of the + * dma_buf operation. It alsog adjusts the starting pgoff and does bounds + * checking on the size of the vma. Then it calls the exporters mmap function to + * set up the mapping. + * + * Can return negative error values, returns 0 on success. + */ +int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma, + unsigned long pgoff) +{ + if (WARN_ON(!dmabuf || !vma)) + return -EINVAL; + + /* check for offset overflow */ + if (pgoff + ((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) < pgoff) + return -EOVERFLOW; + + /* check for overflowing the buffer's size */ + if (pgoff + ((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) > + dmabuf->size >> PAGE_SHIFT) + return -EINVAL; + + /* readjust the vma */ + if (vma->vm_file) + fput(vma->vm_file); + + vma->vm_file = dmabuf->file; + get_file(vma->vm_file); + + vma->vm_pgoff = pgoff; + + return dmabuf->ops->mmap(dmabuf, vma); +} +EXPORT_SYMBOL_GPL(dma_buf_mmap); diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 73ce9fbe..83aa694a 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -38,11 +39,13 @@ ktime_t __start = ktime_get(); \ type __retval = GENPD_DEV_CALLBACK(genpd, type, callback, dev); \ s64 __elapsed = ktime_to_ns(ktime_sub(ktime_get(), __start)); \ - struct generic_pm_domain_data *__gpd_data = dev_gpd_data(dev); \ - if (__elapsed > __gpd_data->td.field) { \ - __gpd_data->td.field = __elapsed; \ + struct gpd_timing_data *__td = &dev_gpd_data(dev)->td; \ + if (!__retval && __elapsed > __td->field) { \ + __td->field = __elapsed; \ dev_warn(dev, name " latency exceeded, new value %lld ns\n", \ __elapsed); \ + genpd->max_off_time_changed = true; \ + __td->constraint_changed = true; \ } \ __retval; \ }) @@ -211,6 +214,7 @@ int __pm_genpd_poweron(struct generic_pm_domain *genpd) elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); if (elapsed_ns > genpd->power_on_latency_ns) { genpd->power_on_latency_ns = elapsed_ns; + genpd->max_off_time_changed = true; if (genpd->name) pr_warning("%s: Power-on latency exceeded, " "new value %lld ns\n", genpd->name, @@ -247,6 +251,53 @@ int pm_genpd_poweron(struct generic_pm_domain *genpd) #ifdef CONFIG_PM_RUNTIME +static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, + unsigned long val, void *ptr) +{ + struct generic_pm_domain_data *gpd_data; + struct device *dev; + + gpd_data = container_of(nb, struct generic_pm_domain_data, nb); + + mutex_lock(&gpd_data->lock); + dev = gpd_data->base.dev; + if (!dev) { + mutex_unlock(&gpd_data->lock); + return NOTIFY_DONE; + } + mutex_unlock(&gpd_data->lock); + + for (;;) { + struct generic_pm_domain *genpd; + struct pm_domain_data *pdd; + + spin_lock_irq(&dev->power.lock); + + pdd = dev->power.subsys_data ? + dev->power.subsys_data->domain_data : NULL; + if (pdd) { + to_gpd_data(pdd)->td.constraint_changed = true; + genpd = dev_to_genpd(dev); + } else { + genpd = ERR_PTR(-ENODATA); + } + + spin_unlock_irq(&dev->power.lock); + + if (!IS_ERR(genpd)) { + mutex_lock(&genpd->lock); + genpd->max_off_time_changed = true; + mutex_unlock(&genpd->lock); + } + + dev = dev->parent; + if (!dev || dev->power.ignore_children) + break; + } + + return NOTIFY_DONE; +} + /** * __pm_genpd_save_device - Save the pre-suspend state of a device. * @pdd: Domain data of the device to save the state of. @@ -435,6 +486,7 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); if (elapsed_ns > genpd->power_off_latency_ns) { genpd->power_off_latency_ns = elapsed_ns; + genpd->max_off_time_changed = true; if (genpd->name) pr_warning("%s: Power-off latency exceeded, " "new value %lld ns\n", genpd->name, @@ -443,17 +495,6 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) } genpd->status = GPD_STATE_POWER_OFF; - genpd->power_off_time = ktime_get(); - - /* Update PM QoS information for devices in the domain. */ - list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) { - struct gpd_timing_data *td = &to_gpd_data(pdd)->td; - - pm_runtime_update_max_time_suspended(pdd->dev, - td->start_latency_ns + - td->restore_state_latency_ns + - genpd->power_on_latency_ns); - } list_for_each_entry(link, &genpd->slave_links, slave_node) { genpd_sd_counter_dec(link->master); @@ -514,9 +555,6 @@ static int pm_genpd_runtime_suspend(struct device *dev) if (ret) return ret; - pm_runtime_update_max_time_suspended(dev, - dev_gpd_data(dev)->td.start_latency_ns); - /* * If power.irq_safe is set, this routine will be run with interrupts * off, so it can't use mutexes. @@ -613,6 +651,12 @@ void pm_genpd_poweroff_unused(void) #else +static inline int genpd_dev_pm_qos_notifier(struct notifier_block *nb, + unsigned long val, void *ptr) +{ + return NOTIFY_DONE; +} + static inline void genpd_power_off_work_fn(struct work_struct *work) {} #define pm_genpd_runtime_suspend NULL @@ -1209,12 +1253,15 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) return -EINVAL; - genpd_acquire_lock(genpd); + gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); + if (!gpd_data) + return -ENOMEM; - if (genpd->status == GPD_STATE_POWER_OFF) { - ret = -EINVAL; - goto out; - } + mutex_init(&gpd_data->lock); + gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; + dev_pm_qos_add_notifier(dev, &gpd_data->nb); + + genpd_acquire_lock(genpd); if (genpd->prepared_count > 0) { ret = -EAGAIN; @@ -1227,26 +1274,35 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, goto out; } - gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); - if (!gpd_data) { - ret = -ENOMEM; - goto out; - } - genpd->device_count++; + genpd->max_off_time_changed = true; - dev->pm_domain = &genpd->domain; dev_pm_get_subsys_data(dev); + + mutex_lock(&gpd_data->lock); + spin_lock_irq(&dev->power.lock); + dev->pm_domain = &genpd->domain; dev->power.subsys_data->domain_data = &gpd_data->base; gpd_data->base.dev = dev; - gpd_data->need_restore = false; list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); + gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF; if (td) gpd_data->td = *td; + gpd_data->td.constraint_changed = true; + gpd_data->td.effective_constraint_ns = -1; + spin_unlock_irq(&dev->power.lock); + mutex_unlock(&gpd_data->lock); + + genpd_release_lock(genpd); + + return 0; + out: genpd_release_lock(genpd); + dev_pm_qos_remove_notifier(dev, &gpd_data->nb); + kfree(gpd_data); return ret; } @@ -1290,12 +1346,15 @@ int __pm_genpd_of_add_device(struct device_node *genpd_node, struct device *dev, int pm_genpd_remove_device(struct generic_pm_domain *genpd, struct device *dev) { + struct generic_pm_domain_data *gpd_data; struct pm_domain_data *pdd; - int ret = -EINVAL; + int ret = 0; dev_dbg(dev, "%s()\n", __func__); - if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) + if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev) + || IS_ERR_OR_NULL(dev->pm_domain) + || pd_to_genpd(dev->pm_domain) != genpd) return -EINVAL; genpd_acquire_lock(genpd); @@ -1305,21 +1364,27 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, goto out; } - list_for_each_entry(pdd, &genpd->dev_list, list_node) { - if (pdd->dev != dev) - continue; + genpd->device_count--; + genpd->max_off_time_changed = true; - list_del_init(&pdd->list_node); - pdd->dev = NULL; - dev_pm_put_subsys_data(dev); - dev->pm_domain = NULL; - kfree(to_gpd_data(pdd)); + spin_lock_irq(&dev->power.lock); + dev->pm_domain = NULL; + pdd = dev->power.subsys_data->domain_data; + list_del_init(&pdd->list_node); + dev->power.subsys_data->domain_data = NULL; + spin_unlock_irq(&dev->power.lock); - genpd->device_count--; + gpd_data = to_gpd_data(pdd); + mutex_lock(&gpd_data->lock); + pdd->dev = NULL; + mutex_unlock(&gpd_data->lock); - ret = 0; - break; - } + genpd_release_lock(genpd); + + dev_pm_qos_remove_notifier(dev, &gpd_data->nb); + kfree(gpd_data); + dev_pm_put_subsys_data(dev); + return 0; out: genpd_release_lock(genpd); @@ -1347,6 +1412,26 @@ void pm_genpd_dev_always_on(struct device *dev, bool val) } EXPORT_SYMBOL_GPL(pm_genpd_dev_always_on); +/** + * pm_genpd_dev_need_restore - Set/unset the device's "need restore" flag. + * @dev: Device to set/unset the flag for. + * @val: The new value of the device's "need restore" flag. + */ +void pm_genpd_dev_need_restore(struct device *dev, bool val) +{ + struct pm_subsys_data *psd; + unsigned long flags; + + spin_lock_irqsave(&dev->power.lock, flags); + + psd = dev_to_psd(dev); + if (psd && psd->domain_data) + to_gpd_data(psd->domain_data)->need_restore = val; + + spin_unlock_irqrestore(&dev->power.lock, flags); +} +EXPORT_SYMBOL_GPL(pm_genpd_dev_need_restore); + /** * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. * @genpd: Master PM domain to add the subdomain to. @@ -1378,7 +1463,7 @@ int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, goto out; } - list_for_each_entry(link, &genpd->slave_links, slave_node) { + list_for_each_entry(link, &genpd->master_links, master_node) { if (link->slave == subdomain && link->master == genpd) { ret = -EINVAL; goto out; @@ -1690,6 +1775,7 @@ void pm_genpd_init(struct generic_pm_domain *genpd, genpd->resume_count = 0; genpd->device_count = 0; genpd->max_off_time_ns = -1; + genpd->max_off_time_changed = true; genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend; genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume; genpd->domain.ops.runtime_idle = pm_generic_runtime_idle; diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c index 66a265bf..28dee305 100644 --- a/drivers/base/power/domain_governor.c +++ b/drivers/base/power/domain_governor.c @@ -14,6 +14,31 @@ #ifdef CONFIG_PM_RUNTIME +static int dev_update_qos_constraint(struct device *dev, void *data) +{ + s64 *constraint_ns_p = data; + s32 constraint_ns = -1; + + if (dev->power.subsys_data && dev->power.subsys_data->domain_data) + constraint_ns = dev_gpd_data(dev)->td.effective_constraint_ns; + + if (constraint_ns < 0) { + constraint_ns = dev_pm_qos_read_value(dev); + constraint_ns *= NSEC_PER_USEC; + } + if (constraint_ns == 0) + return 0; + + /* + * constraint_ns cannot be negative here, because the device has been + * suspended. + */ + if (constraint_ns < *constraint_ns_p || *constraint_ns_p == 0) + *constraint_ns_p = constraint_ns; + + return 0; +} + /** * default_stop_ok - Default PM domain governor routine for stopping devices. * @dev: Device to check. @@ -21,14 +46,52 @@ bool default_stop_ok(struct device *dev) { struct gpd_timing_data *td = &dev_gpd_data(dev)->td; + unsigned long flags; + s64 constraint_ns; dev_dbg(dev, "%s()\n", __func__); - if (dev->power.max_time_suspended_ns < 0 || td->break_even_ns == 0) - return true; + spin_lock_irqsave(&dev->power.lock, flags); + + if (!td->constraint_changed) { + bool ret = td->cached_stop_ok; - return td->stop_latency_ns + td->start_latency_ns < td->break_even_ns - && td->break_even_ns < dev->power.max_time_suspended_ns; + spin_unlock_irqrestore(&dev->power.lock, flags); + return ret; + } + td->constraint_changed = false; + td->cached_stop_ok = false; + td->effective_constraint_ns = -1; + constraint_ns = __dev_pm_qos_read_value(dev); + + spin_unlock_irqrestore(&dev->power.lock, flags); + + if (constraint_ns < 0) + return false; + + constraint_ns *= NSEC_PER_USEC; + /* + * We can walk the children without any additional locking, because + * they all have been suspended at this point and their + * effective_constraint_ns fields won't be modified in parallel with us. + */ + if (!dev->power.ignore_children) + device_for_each_child(dev, &constraint_ns, + dev_update_qos_constraint); + + if (constraint_ns > 0) { + constraint_ns -= td->start_latency_ns; + if (constraint_ns == 0) + return false; + } + td->effective_constraint_ns = constraint_ns; + td->cached_stop_ok = constraint_ns > td->stop_latency_ns || + constraint_ns == 0; + /* + * The children have been suspended already, so we don't need to take + * their stop latencies into account here. + */ + return td->cached_stop_ok; } /** @@ -42,9 +105,27 @@ static bool default_power_down_ok(struct dev_pm_domain *pd) struct generic_pm_domain *genpd = pd_to_genpd(pd); struct gpd_link *link; struct pm_domain_data *pdd; - s64 min_dev_off_time_ns; + s64 min_off_time_ns; s64 off_on_time_ns; - ktime_t time_now = ktime_get(); + + if (genpd->max_off_time_changed) { + struct gpd_link *link; + + /* + * We have to invalidate the cached results for the masters, so + * use the observation that default_power_down_ok() is not + * going to be called for any master until this instance + * returns. + */ + list_for_each_entry(link, &genpd->slave_links, slave_node) + link->master->max_off_time_changed = true; + + genpd->max_off_time_changed = false; + genpd->cached_power_down_ok = false; + genpd->max_off_time_ns = -1; + } else { + return genpd->cached_power_down_ok; + } off_on_time_ns = genpd->power_off_latency_ns + genpd->power_on_latency_ns; @@ -61,6 +142,7 @@ static bool default_power_down_ok(struct dev_pm_domain *pd) to_gpd_data(pdd)->td.save_state_latency_ns; } + min_off_time_ns = -1; /* * Check if subdomains can be off for enough time. * @@ -73,8 +155,6 @@ static bool default_power_down_ok(struct dev_pm_domain *pd) if (sd_max_off_ns < 0) continue; - sd_max_off_ns -= ktime_to_ns(ktime_sub(time_now, - sd->power_off_time)); /* * Check if the subdomain is allowed to be off long enough for * the current domain to turn off and on (that's how much time @@ -82,60 +162,64 @@ static bool default_power_down_ok(struct dev_pm_domain *pd) */ if (sd_max_off_ns <= off_on_time_ns) return false; + + if (min_off_time_ns > sd_max_off_ns || min_off_time_ns < 0) + min_off_time_ns = sd_max_off_ns; } /* * Check if the devices in the domain can be off enough time. */ - min_dev_off_time_ns = -1; list_for_each_entry(pdd, &genpd->dev_list, list_node) { struct gpd_timing_data *td; - struct device *dev = pdd->dev; - s64 dev_off_time_ns; + s64 constraint_ns; - if (!dev->driver || dev->power.max_time_suspended_ns < 0) + if (!pdd->dev->driver) continue; + /* + * Check if the device is allowed to be off long enough for the + * domain to turn off and on (that's how much time it will + * have to wait worst case). + */ td = &to_gpd_data(pdd)->td; - dev_off_time_ns = dev->power.max_time_suspended_ns - - (td->start_latency_ns + td->restore_state_latency_ns + - ktime_to_ns(ktime_sub(time_now, - dev->power.suspend_time))); - if (dev_off_time_ns <= off_on_time_ns) - return false; - - if (min_dev_off_time_ns > dev_off_time_ns - || min_dev_off_time_ns < 0) - min_dev_off_time_ns = dev_off_time_ns; - } + constraint_ns = td->effective_constraint_ns; + /* default_stop_ok() need not be called before us. */ + if (constraint_ns < 0) { + constraint_ns = dev_pm_qos_read_value(pdd->dev); + constraint_ns *= NSEC_PER_USEC; + } + if (constraint_ns == 0) + continue; - if (min_dev_off_time_ns < 0) { /* - * There are no latency constraints, so the domain can spend - * arbitrary time in the "off" state. + * constraint_ns cannot be negative here, because the device has + * been suspended. */ - genpd->max_off_time_ns = -1; - return true; + constraint_ns -= td->restore_state_latency_ns; + if (constraint_ns <= off_on_time_ns) + return false; + + if (min_off_time_ns > constraint_ns || min_off_time_ns < 0) + min_off_time_ns = constraint_ns; } + genpd->cached_power_down_ok = true; + /* - * The difference between the computed minimum delta and the time needed - * to turn the domain on is the maximum theoretical time this domain can - * spend in the "off" state. + * If the computed minimum device off time is negative, there are no + * latency constraints, so the domain can spend arbitrary time in the + * "off" state. */ - min_dev_off_time_ns -= genpd->power_on_latency_ns; + if (min_off_time_ns < 0) + return true; /* - * If the difference between the computed minimum delta and the time - * needed to turn the domain off and back on on is smaller than the - * domain's power break even time, removing power from the domain is not - * worth it. + * The difference between the computed minimum subdomain or device off + * time and the time needed to turn the domain on is the maximum + * theoretical time this domain can spend in the "off" state. */ - if (genpd->break_even_ns > - min_dev_off_time_ns - genpd->power_off_latency_ns) - return false; - - genpd->max_off_time_ns = min_dev_off_time_ns; + genpd->max_off_time_ns = min_off_time_ns - genpd->power_on_latency_ns; return true; } diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index ebc272f8..77e3b97b 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "../base.h" #include "power.h" @@ -54,6 +55,12 @@ struct suspend_stats suspend_stats; static DEFINE_MUTEX(dpm_list_mtx); static pm_message_t pm_transition; +struct dpm_watchdog { + struct device *dev; + struct task_struct *tsk; + struct timer_list timer; +}; + static int async_error; /** @@ -389,6 +396,56 @@ static int dpm_run_callback(pm_callback_t cb, struct device *dev, return error; } +/** + * dpm_wd_handler - Driver suspend / resume watchdog handler. + * + * Called when a driver has timed out suspending or resuming. + * There's not much we can do here to recover so BUG() out for + * a crash-dump + */ +static void dpm_wd_handler(unsigned long data) +{ + struct dpm_watchdog *wd = (void *)data; + struct device *dev = wd->dev; + struct task_struct *tsk = wd->tsk; + + dev_emerg(dev, "**** DPM device timeout ****\n"); + show_stack(tsk, NULL); + + BUG(); +} + +/** + * dpm_wd_set - Enable pm watchdog for given device. + * @wd: Watchdog. Must be allocated on the stack. + * @dev: Device to handle. + */ +static void dpm_wd_set(struct dpm_watchdog *wd, struct device *dev) +{ + struct timer_list *timer = &wd->timer; + + wd->dev = dev; + wd->tsk = get_current(); + + init_timer_on_stack(timer); + timer->expires = jiffies + HZ * 12; + timer->function = dpm_wd_handler; + timer->data = (unsigned long)wd; + add_timer(timer); +} + +/** + * dpm_wd_clear - Disable pm watchdog. + * @wd: Watchdog to disable. + */ +static void dpm_wd_clear(struct dpm_watchdog *wd) +{ + struct timer_list *timer = &wd->timer; + + del_timer_sync(timer); + destroy_timer_on_stack(timer); +} + /*------------------------- Resume routines -------------------------*/ /** @@ -565,6 +622,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) char *info = NULL; int error = 0; bool put = false; + struct dpm_watchdog wd; TRACE_DEVICE(dev); TRACE_RESUME(0); @@ -577,6 +635,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) * a resumed device, even if the device hasn't been completed yet. */ dev->power.is_prepared = false; + dpm_wd_set(&wd, dev); if (!dev->power.is_suspended) goto Unlock; @@ -631,6 +690,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) Unlock: device_unlock(dev); + dpm_wd_clear(&wd); complete_all(&dev->power.completion); TRACE_RESUME(error); @@ -889,6 +949,11 @@ static int dpm_suspend_noirq(pm_message_t state) if (!list_empty(&dev->power.entry)) list_move(&dev->power.entry, &dpm_noirq_list); put_device(dev); + + if (pm_wakeup_pending()) { + error = -EBUSY; + break; + } } mutex_unlock(&dpm_list_mtx); if (error) @@ -962,6 +1027,11 @@ static int dpm_suspend_late(pm_message_t state) if (!list_empty(&dev->power.entry)) list_move(&dev->power.entry, &dpm_late_early_list); put_device(dev); + + if (pm_wakeup_pending()) { + error = -EBUSY; + break; + } } mutex_unlock(&dpm_list_mtx); if (error) @@ -1025,6 +1095,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) pm_callback_t callback = NULL; char *info = NULL; int error = 0; + struct dpm_watchdog wd; dpm_wait_for_children(dev, async); @@ -1041,6 +1112,8 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) goto Complete; } + dpm_wd_set(&wd, dev); + device_lock(dev); if (dev->pm_domain) { @@ -1096,6 +1169,8 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) device_unlock(dev); + dpm_wd_clear(&wd); + Complete: complete_all(&dev->power.completion); diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index c365c93a..b95ebf2d 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -352,21 +352,26 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request); * * Will register the notifier into a notification chain that gets called * upon changes to the target value for the device. + * + * If the device's constraints object doesn't exist when this routine is called, + * it will be created (or error code will be returned if that fails). */ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier) { - int retval = 0; + int ret = 0; mutex_lock(&dev_pm_qos_mtx); - /* Silently return if the constraints object is not present. */ - if (dev->power.constraints) - retval = blocking_notifier_chain_register( - dev->power.constraints->notifiers, - notifier); + if (!dev->power.constraints) + ret = dev->power.power_state.event != PM_EVENT_INVALID ? + dev_pm_qos_constraints_allocate(dev) : -ENODEV; + + if (!ret) + ret = blocking_notifier_chain_register( + dev->power.constraints->notifiers, notifier); mutex_unlock(&dev_pm_qos_mtx); - return retval; + return ret; } EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier); diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index bb82b181..b6e9d9b7 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -282,47 +282,6 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev) return retval != -EACCES ? retval : -EIO; } -struct rpm_qos_data { - ktime_t time_now; - s64 constraint_ns; -}; - -/** - * rpm_update_qos_constraint - Update a given PM QoS constraint data. - * @dev: Device whose timing data to use. - * @data: PM QoS constraint data to update. - * - * Use the suspend timing data of @dev to update PM QoS constraint data pointed - * to by @data. - */ -static int rpm_update_qos_constraint(struct device *dev, void *data) -{ - struct rpm_qos_data *qos = data; - unsigned long flags; - s64 delta_ns; - int ret = 0; - - spin_lock_irqsave(&dev->power.lock, flags); - - if (dev->power.max_time_suspended_ns < 0) - goto out; - - delta_ns = dev->power.max_time_suspended_ns - - ktime_to_ns(ktime_sub(qos->time_now, dev->power.suspend_time)); - if (delta_ns <= 0) { - ret = -EBUSY; - goto out; - } - - if (qos->constraint_ns > delta_ns || qos->constraint_ns == 0) - qos->constraint_ns = delta_ns; - - out: - spin_unlock_irqrestore(&dev->power.lock, flags); - - return ret; -} - /** * rpm_suspend - Carry out runtime suspend of given device. * @dev: Device to suspend. @@ -349,7 +308,6 @@ static int rpm_suspend(struct device *dev, int rpmflags) { int (*callback)(struct device *); struct device *parent = NULL; - struct rpm_qos_data qos; int retval; trace_rpm_suspend(dev, rpmflags); @@ -444,38 +402,14 @@ static int rpm_suspend(struct device *dev, int rpmflags) goto out; } - qos.constraint_ns = __dev_pm_qos_read_value(dev); - if (qos.constraint_ns < 0) { - /* Negative constraint means "never suspend". */ + if (__dev_pm_qos_read_value(dev) < 0) { + /* Negative PM QoS constraint means "never suspend". */ retval = -EPERM; goto out; } - qos.constraint_ns *= NSEC_PER_USEC; - qos.time_now = ktime_get(); __update_runtime_status(dev, RPM_SUSPENDING); - if (!dev->power.ignore_children) { - if (dev->power.irq_safe) - spin_unlock(&dev->power.lock); - else - spin_unlock_irq(&dev->power.lock); - - retval = device_for_each_child(dev, &qos, - rpm_update_qos_constraint); - - if (dev->power.irq_safe) - spin_lock(&dev->power.lock); - else - spin_lock_irq(&dev->power.lock); - - if (retval) - goto fail; - } - - dev->power.suspend_time = qos.time_now; - dev->power.max_time_suspended_ns = qos.constraint_ns ? : -1; - if (dev->pm_domain) callback = dev->pm_domain->ops.runtime_suspend; else if (dev->type && dev->type->pm) @@ -529,8 +463,6 @@ static int rpm_suspend(struct device *dev, int rpmflags) fail: __update_runtime_status(dev, RPM_ACTIVE); - dev->power.suspend_time = ktime_set(0, 0); - dev->power.max_time_suspended_ns = -1; dev->power.deferred_resume = false; wake_up_all(&dev->power.wait_queue); @@ -705,9 +637,6 @@ static int rpm_resume(struct device *dev, int rpmflags) if (dev->power.no_callbacks) goto no_callback; /* Assume success. */ - dev->power.suspend_time = ktime_set(0, 0); - dev->power.max_time_suspended_ns = -1; - __update_runtime_status(dev, RPM_RESUMING); if (dev->pm_domain) @@ -1370,9 +1299,6 @@ void pm_runtime_init(struct device *dev) setup_timer(&dev->power.suspend_timer, pm_suspend_timer_fn, (unsigned long)dev); - dev->power.suspend_time = ktime_set(0, 0); - dev->power.max_time_suspended_ns = -1; - init_waitqueue_head(&dev->power.wait_queue); } @@ -1390,28 +1316,3 @@ void pm_runtime_remove(struct device *dev) if (dev->power.irq_safe && dev->parent) pm_runtime_put_sync(dev->parent); } - -/** - * pm_runtime_update_max_time_suspended - Update device's suspend time data. - * @dev: Device to handle. - * @delta_ns: Value to subtract from the device's max_time_suspended_ns field. - * - * Update the device's power.max_time_suspended_ns field by subtracting - * @delta_ns from it. The resulting value of power.max_time_suspended_ns is - * never negative. - */ -void pm_runtime_update_max_time_suspended(struct device *dev, s64 delta_ns) -{ - unsigned long flags; - - spin_lock_irqsave(&dev->power.lock, flags); - - if (delta_ns > 0 && dev->power.max_time_suspended_ns > 0) { - if (dev->power.max_time_suspended_ns > delta_ns) - dev->power.max_time_suspended_ns -= delta_ns; - else - dev->power.max_time_suspended_ns = 0; - } - - spin_unlock_irqrestore(&dev->power.lock, flags); -} diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 95c12f6c..48be2ad4 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -314,22 +314,41 @@ static ssize_t wakeup_active_count_show(struct device *dev, static DEVICE_ATTR(wakeup_active_count, 0444, wakeup_active_count_show, NULL); -static ssize_t wakeup_hit_count_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t wakeup_abort_count_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long count = 0; + bool enabled = false; + + spin_lock_irq(&dev->power.lock); + if (dev->power.wakeup) { + count = dev->power.wakeup->wakeup_count; + enabled = true; + } + spin_unlock_irq(&dev->power.lock); + return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n"); +} + +static DEVICE_ATTR(wakeup_abort_count, 0444, wakeup_abort_count_show, NULL); + +static ssize_t wakeup_expire_count_show(struct device *dev, + struct device_attribute *attr, + char *buf) { unsigned long count = 0; bool enabled = false; spin_lock_irq(&dev->power.lock); if (dev->power.wakeup) { - count = dev->power.wakeup->hit_count; + count = dev->power.wakeup->expire_count; enabled = true; } spin_unlock_irq(&dev->power.lock); return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n"); } -static DEVICE_ATTR(wakeup_hit_count, 0444, wakeup_hit_count_show, NULL); +static DEVICE_ATTR(wakeup_expire_count, 0444, wakeup_expire_count_show, NULL); static ssize_t wakeup_active_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -398,6 +417,27 @@ static ssize_t wakeup_last_time_show(struct device *dev, } static DEVICE_ATTR(wakeup_last_time_ms, 0444, wakeup_last_time_show, NULL); + +#ifdef CONFIG_PM_AUTOSLEEP +static ssize_t wakeup_prevent_sleep_time_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + s64 msec = 0; + bool enabled = false; + + spin_lock_irq(&dev->power.lock); + if (dev->power.wakeup) { + msec = ktime_to_ms(dev->power.wakeup->prevent_sleep_time); + enabled = true; + } + spin_unlock_irq(&dev->power.lock); + return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n"); +} + +static DEVICE_ATTR(wakeup_prevent_sleep_time_ms, 0444, + wakeup_prevent_sleep_time_show, NULL); +#endif /* CONFIG_PM_AUTOSLEEP */ #endif /* CONFIG_PM_SLEEP */ #ifdef CONFIG_PM_ADVANCED_DEBUG @@ -486,11 +526,15 @@ static struct attribute *wakeup_attrs[] = { &dev_attr_wakeup.attr, &dev_attr_wakeup_count.attr, &dev_attr_wakeup_active_count.attr, - &dev_attr_wakeup_hit_count.attr, + &dev_attr_wakeup_abort_count.attr, + &dev_attr_wakeup_expire_count.attr, &dev_attr_wakeup_active.attr, &dev_attr_wakeup_total_time_ms.attr, &dev_attr_wakeup_max_time_ms.attr, &dev_attr_wakeup_last_time_ms.attr, +#ifdef CONFIG_PM_AUTOSLEEP + &dev_attr_wakeup_prevent_sleep_time_ms.attr, +#endif #endif NULL, }; diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 2a3e581b..e6ee5e80 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -14,16 +14,15 @@ #include #include #include +#include #include "power.h" -#define TIMEOUT 100 - /* * If set, the suspend/hibernate code will abort transitions to a sleep state * if wakeup events are registered during or immediately before the transition. */ -bool events_check_enabled; +bool events_check_enabled __read_mostly; /* * Combined counters of registered wakeup events and wakeup events in progress. @@ -52,6 +51,8 @@ static void pm_wakeup_timer_fn(unsigned long data); static LIST_HEAD(wakeup_sources); +static DECLARE_WAIT_QUEUE_HEAD(wakeup_count_wait_queue); + /** * wakeup_source_prepare - Prepare a new wakeup source for initialization. * @ws: Wakeup source to prepare. @@ -126,16 +127,19 @@ EXPORT_SYMBOL_GPL(wakeup_source_destroy); */ void wakeup_source_add(struct wakeup_source *ws) { + unsigned long flags; + if (WARN_ON(!ws)) return; spin_lock_init(&ws->lock); setup_timer(&ws->timer, pm_wakeup_timer_fn, (unsigned long)ws); ws->active = false; + ws->last_time = ktime_get(); - spin_lock_irq(&events_lock); + spin_lock_irqsave(&events_lock, flags); list_add_rcu(&ws->entry, &wakeup_sources); - spin_unlock_irq(&events_lock); + spin_unlock_irqrestore(&events_lock, flags); } EXPORT_SYMBOL_GPL(wakeup_source_add); @@ -145,12 +149,14 @@ EXPORT_SYMBOL_GPL(wakeup_source_add); */ void wakeup_source_remove(struct wakeup_source *ws) { + unsigned long flags; + if (WARN_ON(!ws)) return; - spin_lock_irq(&events_lock); + spin_lock_irqsave(&events_lock, flags); list_del_rcu(&ws->entry); - spin_unlock_irq(&events_lock); + spin_unlock_irqrestore(&events_lock, flags); synchronize_rcu(); } EXPORT_SYMBOL_GPL(wakeup_source_remove); @@ -374,12 +380,33 @@ EXPORT_SYMBOL_GPL(device_set_wakeup_enable); */ static void wakeup_source_activate(struct wakeup_source *ws) { + unsigned int cec; + ws->active = true; ws->active_count++; ws->last_time = ktime_get(); + if (ws->autosleep_enabled) + ws->start_prevent_time = ws->last_time; /* Increment the counter of events in progress. */ - atomic_inc(&combined_event_count); + cec = atomic_inc_return(&combined_event_count); + + trace_wakeup_source_activate(ws->name, cec); +} + +/** + * wakeup_source_report_event - Report wakeup event using the given source. + * @ws: Wakeup source to report the event for. + */ +static void wakeup_source_report_event(struct wakeup_source *ws) +{ + ws->event_count++; + /* This is racy, but the counter is approximate anyway. */ + if (events_check_enabled) + ws->wakeup_count++; + + if (!ws->active) + wakeup_source_activate(ws); } /** @@ -397,10 +424,7 @@ void __pm_stay_awake(struct wakeup_source *ws) spin_lock_irqsave(&ws->lock, flags); - ws->event_count++; - if (!ws->active) - wakeup_source_activate(ws); - + wakeup_source_report_event(ws); del_timer(&ws->timer); ws->timer_expires = 0; @@ -432,6 +456,17 @@ void pm_stay_awake(struct device *dev) } EXPORT_SYMBOL_GPL(pm_stay_awake); +#ifdef CONFIG_PM_AUTOSLEEP +static void update_prevent_sleep_time(struct wakeup_source *ws, ktime_t now) +{ + ktime_t delta = ktime_sub(now, ws->start_prevent_time); + ws->prevent_sleep_time = ktime_add(ws->prevent_sleep_time, delta); +} +#else +static inline void update_prevent_sleep_time(struct wakeup_source *ws, + ktime_t now) {} +#endif + /** * wakup_source_deactivate - Mark given wakeup source as inactive. * @ws: Wakeup source to handle. @@ -442,6 +477,7 @@ EXPORT_SYMBOL_GPL(pm_stay_awake); */ static void wakeup_source_deactivate(struct wakeup_source *ws) { + unsigned int cnt, inpr, cec; ktime_t duration; ktime_t now; @@ -468,14 +504,23 @@ static void wakeup_source_deactivate(struct wakeup_source *ws) if (ktime_to_ns(duration) > ktime_to_ns(ws->max_time)) ws->max_time = duration; + ws->last_time = now; del_timer(&ws->timer); ws->timer_expires = 0; + if (ws->autosleep_enabled) + update_prevent_sleep_time(ws, now); + /* * Increment the counter of registered wakeup events and decrement the * couter of wakeup events in progress simultaneously. */ - atomic_add(MAX_IN_PROGRESS, &combined_event_count); + cec = atomic_add_return(MAX_IN_PROGRESS, &combined_event_count); + trace_wakeup_source_deactivate(ws->name, cec); + + split_counters(&cnt, &inpr); + if (!inpr && waitqueue_active(&wakeup_count_wait_queue)) + wake_up(&wakeup_count_wait_queue); } /** @@ -536,8 +581,10 @@ static void pm_wakeup_timer_fn(unsigned long data) spin_lock_irqsave(&ws->lock, flags); if (ws->active && ws->timer_expires - && time_after_eq(jiffies, ws->timer_expires)) + && time_after_eq(jiffies, ws->timer_expires)) { wakeup_source_deactivate(ws); + ws->expire_count++; + } spin_unlock_irqrestore(&ws->lock, flags); } @@ -564,9 +611,7 @@ void __pm_wakeup_event(struct wakeup_source *ws, unsigned int msec) spin_lock_irqsave(&ws->lock, flags); - ws->event_count++; - if (!ws->active) - wakeup_source_activate(ws); + wakeup_source_report_event(ws); if (!msec) { wakeup_source_deactivate(ws); @@ -608,21 +653,28 @@ void pm_wakeup_event(struct device *dev, unsigned int msec) } EXPORT_SYMBOL_GPL(pm_wakeup_event); -/** - * pm_wakeup_update_hit_counts - Update hit counts of all active wakeup sources. - */ -static void pm_wakeup_update_hit_counts(void) +static void print_active_wakeup_sources(void) { - unsigned long flags; struct wakeup_source *ws; + int active = 0; + struct wakeup_source *last_activity_ws = NULL; rcu_read_lock(); list_for_each_entry_rcu(ws, &wakeup_sources, entry) { - spin_lock_irqsave(&ws->lock, flags); - if (ws->active) - ws->hit_count++; - spin_unlock_irqrestore(&ws->lock, flags); + if (ws->active) { + pr_info("active wakeup source: %s\n", ws->name); + active = 1; + } else if (!active && + (!last_activity_ws || + ktime_to_ns(ws->last_time) > + ktime_to_ns(last_activity_ws->last_time))) { + last_activity_ws = ws; + } } + + if (!active && last_activity_ws) + pr_info("last active wakeup source: %s\n", + last_activity_ws->name); rcu_read_unlock(); } @@ -648,32 +700,42 @@ bool pm_wakeup_pending(void) events_check_enabled = !ret; } spin_unlock_irqrestore(&events_lock, flags); + if (ret) - pm_wakeup_update_hit_counts(); + print_active_wakeup_sources(); + return ret; } /** * pm_get_wakeup_count - Read the number of registered wakeup events. * @count: Address to store the value at. + * @block: Whether or not to block. * - * Store the number of registered wakeup events at the address in @count. Block - * if the current number of wakeup events being processed is nonzero. + * Store the number of registered wakeup events at the address in @count. If + * @block is set, block until the current number of wakeup events being + * processed is zero. * - * Return 'false' if the wait for the number of wakeup events being processed to - * drop down to zero has been interrupted by a signal (and the current number - * of wakeup events being processed is still nonzero). Otherwise return 'true'. + * Return 'false' if the current number of wakeup events being processed is + * nonzero. Otherwise return 'true'. */ -bool pm_get_wakeup_count(unsigned int *count) +bool pm_get_wakeup_count(unsigned int *count, bool block) { unsigned int cnt, inpr; - for (;;) { - split_counters(&cnt, &inpr); - if (inpr == 0 || signal_pending(current)) - break; - pm_wakeup_update_hit_counts(); - schedule_timeout_interruptible(msecs_to_jiffies(TIMEOUT)); + if (block) { + DEFINE_WAIT(wait); + + for (;;) { + prepare_to_wait(&wakeup_count_wait_queue, &wait, + TASK_INTERRUPTIBLE); + split_counters(&cnt, &inpr); + if (inpr == 0 || signal_pending(current)) + break; + + schedule(); + } + finish_wait(&wakeup_count_wait_queue, &wait); } split_counters(&cnt, &inpr); @@ -694,20 +756,47 @@ bool pm_get_wakeup_count(unsigned int *count) bool pm_save_wakeup_count(unsigned int count) { unsigned int cnt, inpr; + unsigned long flags; events_check_enabled = false; - spin_lock_irq(&events_lock); + spin_lock_irqsave(&events_lock, flags); split_counters(&cnt, &inpr); if (cnt == count && inpr == 0) { saved_count = count; events_check_enabled = true; } - spin_unlock_irq(&events_lock); - if (!events_check_enabled) - pm_wakeup_update_hit_counts(); + spin_unlock_irqrestore(&events_lock, flags); return events_check_enabled; } +#ifdef CONFIG_PM_AUTOSLEEP +/** + * pm_wakep_autosleep_enabled - Modify autosleep_enabled for all wakeup sources. + * @enabled: Whether to set or to clear the autosleep_enabled flags. + */ +void pm_wakep_autosleep_enabled(bool set) +{ + struct wakeup_source *ws; + ktime_t now = ktime_get(); + + rcu_read_lock(); + list_for_each_entry_rcu(ws, &wakeup_sources, entry) { + spin_lock_irq(&ws->lock); + if (ws->autosleep_enabled != set) { + ws->autosleep_enabled = set; + if (ws->active) { + if (set) + ws->start_prevent_time = now; + else + update_prevent_sleep_time(ws, now); + } + } + spin_unlock_irq(&ws->lock); + } + rcu_read_unlock(); +} +#endif /* CONFIG_PM_AUTOSLEEP */ + static struct dentry *wakeup_sources_stats_dentry; /** @@ -723,27 +812,37 @@ static int print_wakeup_source_stats(struct seq_file *m, ktime_t max_time; unsigned long active_count; ktime_t active_time; + ktime_t prevent_sleep_time; int ret; spin_lock_irqsave(&ws->lock, flags); total_time = ws->total_time; max_time = ws->max_time; + prevent_sleep_time = ws->prevent_sleep_time; active_count = ws->active_count; if (ws->active) { - active_time = ktime_sub(ktime_get(), ws->last_time); + ktime_t now = ktime_get(); + + active_time = ktime_sub(now, ws->last_time); total_time = ktime_add(total_time, active_time); if (active_time.tv64 > max_time.tv64) max_time = active_time; + + if (ws->autosleep_enabled) + prevent_sleep_time = ktime_add(prevent_sleep_time, + ktime_sub(now, ws->start_prevent_time)); } else { active_time = ktime_set(0, 0); } - ret = seq_printf(m, "%-12s\t%lu\t\t%lu\t\t%lu\t\t" - "%lld\t\t%lld\t\t%lld\t\t%lld\n", - ws->name, active_count, ws->event_count, ws->hit_count, + ret = seq_printf(m, "%-12s\t%lu\t\t%lu\t\t%lu\t\t%lu\t\t" + "%lld\t\t%lld\t\t%lld\t\t%lld\t\t%lld\n", + ws->name, active_count, ws->event_count, + ws->wakeup_count, ws->expire_count, ktime_to_ms(active_time), ktime_to_ms(total_time), - ktime_to_ms(max_time), ktime_to_ms(ws->last_time)); + ktime_to_ms(max_time), ktime_to_ms(ws->last_time), + ktime_to_ms(prevent_sleep_time)); spin_unlock_irqrestore(&ws->lock, flags); @@ -758,8 +857,9 @@ static int wakeup_sources_stats_show(struct seq_file *m, void *unused) { struct wakeup_source *ws; - seq_puts(m, "name\t\tactive_count\tevent_count\thit_count\t" - "active_since\ttotal_time\tmax_time\tlast_change\n"); + seq_puts(m, "name\t\tactive_count\tevent_count\twakeup_count\t" + "expire_count\tactive_since\ttotal_time\tmax_time\t" + "last_change\tprevent_suspend_time\n"); rcu_read_lock(); list_for_each_entry_rcu(ws, &wakeup_sources, entry) diff --git a/drivers/base/sw_sync.c b/drivers/base/sw_sync.c new file mode 100644 index 00000000..b4d8529e --- /dev/null +++ b/drivers/base/sw_sync.c @@ -0,0 +1,262 @@ +/* + * drivers/base/sw_sync.c + * + * Copyright (C) 2012 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int sw_sync_cmp(u32 a, u32 b) +{ + if (a == b) + return 0; + + return ((s32)a - (s32)b) < 0 ? -1 : 1; +} + +struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value) +{ + struct sw_sync_pt *pt; + + pt = (struct sw_sync_pt *) + sync_pt_create(&obj->obj, sizeof(struct sw_sync_pt)); + + pt->value = value; + + return (struct sync_pt *)pt; +} +EXPORT_SYMBOL(sw_sync_pt_create); + +static struct sync_pt *sw_sync_pt_dup(struct sync_pt *sync_pt) +{ + struct sw_sync_pt *pt = (struct sw_sync_pt *) sync_pt; + struct sw_sync_timeline *obj = + (struct sw_sync_timeline *)sync_pt->parent; + + return (struct sync_pt *) sw_sync_pt_create(obj, pt->value); +} + +static int sw_sync_pt_has_signaled(struct sync_pt *sync_pt) +{ + struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt; + struct sw_sync_timeline *obj = + (struct sw_sync_timeline *)sync_pt->parent; + + return sw_sync_cmp(obj->value, pt->value) >= 0; +} + +static int sw_sync_pt_compare(struct sync_pt *a, struct sync_pt *b) +{ + struct sw_sync_pt *pt_a = (struct sw_sync_pt *)a; + struct sw_sync_pt *pt_b = (struct sw_sync_pt *)b; + + return sw_sync_cmp(pt_a->value, pt_b->value); +} + +static int sw_sync_fill_driver_data(struct sync_pt *sync_pt, + void *data, int size) +{ + struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt; + + if (size < sizeof(pt->value)) + return -ENOMEM; + + memcpy(data, &pt->value, sizeof(pt->value)); + + return sizeof(pt->value); +} + +static void sw_sync_timeline_value_str(struct sync_timeline *sync_timeline, + char *str, int size) +{ + struct sw_sync_timeline *timeline = + (struct sw_sync_timeline *)sync_timeline; + snprintf(str, size, "%d", timeline->value); +} + +static void sw_sync_pt_value_str(struct sync_pt *sync_pt, + char *str, int size) +{ + struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt; + snprintf(str, size, "%d", pt->value); +} + +struct sync_timeline_ops sw_sync_timeline_ops = { + .driver_name = "sw_sync", + .dup = sw_sync_pt_dup, + .has_signaled = sw_sync_pt_has_signaled, + .compare = sw_sync_pt_compare, + .fill_driver_data = sw_sync_fill_driver_data, + .timeline_value_str = sw_sync_timeline_value_str, + .pt_value_str = sw_sync_pt_value_str, +}; + + +struct sw_sync_timeline *sw_sync_timeline_create(const char *name) +{ + struct sw_sync_timeline *obj = (struct sw_sync_timeline *) + sync_timeline_create(&sw_sync_timeline_ops, + sizeof(struct sw_sync_timeline), + name); + + return obj; +} +EXPORT_SYMBOL(sw_sync_timeline_create); + +void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc) +{ + obj->value += inc; + + sync_timeline_signal(&obj->obj); +} +EXPORT_SYMBOL(sw_sync_timeline_inc); + +#ifdef CONFIG_SW_SYNC_USER +/* *WARNING* + * + * improper use of this can result in deadlocking kernel drivers from userspace. + */ + +/* opening sw_sync create a new sync obj */ +int sw_sync_open(struct inode *inode, struct file *file) +{ + struct sw_sync_timeline *obj; + char task_comm[TASK_COMM_LEN]; + + get_task_comm(task_comm, current); + + obj = sw_sync_timeline_create(task_comm); + if (obj == NULL) + return -ENOMEM; + + file->private_data = obj; + + return 0; +} + +int sw_sync_release(struct inode *inode, struct file *file) +{ + struct sw_sync_timeline *obj = file->private_data; + sync_timeline_destroy(&obj->obj); + return 0; +} + +long sw_sync_ioctl_create_fence(struct sw_sync_timeline *obj, unsigned long arg) +{ + int fd = get_unused_fd(); + int err; + struct sync_pt *pt; + struct sync_fence *fence; + struct sw_sync_create_fence_data data; + + if (fd < 0) + return fd; + + if (copy_from_user(&data, (void __user *)arg, sizeof(data))) { + err = -EFAULT; + goto err; + } + + pt = sw_sync_pt_create(obj, data.value); + if (pt == NULL) { + err = -ENOMEM; + goto err; + } + + data.name[sizeof(data.name) - 1] = '\0'; + fence = sync_fence_create(data.name, pt); + if (fence == NULL) { + sync_pt_free(pt); + err = -ENOMEM; + goto err; + } + + data.fence = fd; + if (copy_to_user((void __user *)arg, &data, sizeof(data))) { + sync_fence_put(fence); + err = -EFAULT; + goto err; + } + + sync_fence_install(fence, fd); + + return 0; + +err: + put_unused_fd(fd); + return err; +} + +long sw_sync_ioctl_inc(struct sw_sync_timeline *obj, unsigned long arg) +{ + u32 value; + + if (copy_from_user(&value, (void __user *)arg, sizeof(value))) + return -EFAULT; + + sw_sync_timeline_inc(obj, value); + + return 0; +} + +long sw_sync_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct sw_sync_timeline *obj = file->private_data; + + switch (cmd) { + case SW_SYNC_IOC_CREATE_FENCE: + return sw_sync_ioctl_create_fence(obj, arg); + + case SW_SYNC_IOC_INC: + return sw_sync_ioctl_inc(obj, arg); + + default: + return -ENOTTY; + } +} + +static const struct file_operations sw_sync_fops = { + .owner = THIS_MODULE, + .open = sw_sync_open, + .release = sw_sync_release, + .unlocked_ioctl = sw_sync_ioctl, +}; + +static struct miscdevice sw_sync_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "sw_sync", + .fops = &sw_sync_fops, +}; + +int __init sw_sync_device_init(void) +{ + return misc_register(&sw_sync_dev); +} + +void __exit sw_sync_device_remove(void) +{ + misc_deregister(&sw_sync_dev); +} + +module_init(sw_sync_device_init); +module_exit(sw_sync_device_remove); + +#endif /* CONFIG_SW_SYNC_USER */ diff --git a/drivers/base/sync.c b/drivers/base/sync.c new file mode 100644 index 00000000..2e359968 --- /dev/null +++ b/drivers/base/sync.c @@ -0,0 +1,1015 @@ +/* + * drivers/base/sync.c + * + * Copyright (C) 2012 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define CREATE_TRACE_POINTS +#include + +static void sync_fence_signal_pt(struct sync_pt *pt); +static int _sync_pt_has_signaled(struct sync_pt *pt); +static void sync_fence_free(struct kref *kref); +static void sync_dump(void); + +static LIST_HEAD(sync_timeline_list_head); +static DEFINE_SPINLOCK(sync_timeline_list_lock); + +static LIST_HEAD(sync_fence_list_head); +static DEFINE_SPINLOCK(sync_fence_list_lock); + +struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops, + int size, const char *name) +{ + struct sync_timeline *obj; + unsigned long flags; + + if (size < sizeof(struct sync_timeline)) + return NULL; + + obj = kzalloc(size, GFP_KERNEL); + if (obj == NULL) + return NULL; + + kref_init(&obj->kref); + obj->ops = ops; + strlcpy(obj->name, name, sizeof(obj->name)); + + INIT_LIST_HEAD(&obj->child_list_head); + spin_lock_init(&obj->child_list_lock); + + INIT_LIST_HEAD(&obj->active_list_head); + spin_lock_init(&obj->active_list_lock); + + spin_lock_irqsave(&sync_timeline_list_lock, flags); + list_add_tail(&obj->sync_timeline_list, &sync_timeline_list_head); + spin_unlock_irqrestore(&sync_timeline_list_lock, flags); + + return obj; +} +EXPORT_SYMBOL(sync_timeline_create); + +static void sync_timeline_free(struct kref *kref) +{ + struct sync_timeline *obj = + container_of(kref, struct sync_timeline, kref); + unsigned long flags; + + if (obj->ops->release_obj) + obj->ops->release_obj(obj); + + spin_lock_irqsave(&sync_timeline_list_lock, flags); + list_del(&obj->sync_timeline_list); + spin_unlock_irqrestore(&sync_timeline_list_lock, flags); + + kfree(obj); +} + +void sync_timeline_destroy(struct sync_timeline *obj) +{ + obj->destroyed = true; + + /* + * If this is not the last reference, signal any children + * that their parent is going away. + */ + + if (!kref_put(&obj->kref, sync_timeline_free)) + sync_timeline_signal(obj); +} +EXPORT_SYMBOL(sync_timeline_destroy); + +static void sync_timeline_add_pt(struct sync_timeline *obj, struct sync_pt *pt) +{ + unsigned long flags; + + pt->parent = obj; + + spin_lock_irqsave(&obj->child_list_lock, flags); + list_add_tail(&pt->child_list, &obj->child_list_head); + spin_unlock_irqrestore(&obj->child_list_lock, flags); +} + +static void sync_timeline_remove_pt(struct sync_pt *pt) +{ + struct sync_timeline *obj = pt->parent; + unsigned long flags; + + spin_lock_irqsave(&obj->active_list_lock, flags); + if (!list_empty(&pt->active_list)) + list_del_init(&pt->active_list); + spin_unlock_irqrestore(&obj->active_list_lock, flags); + + spin_lock_irqsave(&obj->child_list_lock, flags); + if (!list_empty(&pt->child_list)) { + list_del_init(&pt->child_list); + } + spin_unlock_irqrestore(&obj->child_list_lock, flags); +} + +void sync_timeline_signal(struct sync_timeline *obj) +{ + unsigned long flags; + LIST_HEAD(signaled_pts); + struct list_head *pos, *n; + + trace_sync_timeline(obj); + + spin_lock_irqsave(&obj->active_list_lock, flags); + + list_for_each_safe(pos, n, &obj->active_list_head) { + struct sync_pt *pt = + container_of(pos, struct sync_pt, active_list); + + if (_sync_pt_has_signaled(pt)) { + list_del_init(pos); + list_add(&pt->signaled_list, &signaled_pts); + kref_get(&pt->fence->kref); + } + } + + spin_unlock_irqrestore(&obj->active_list_lock, flags); + + list_for_each_safe(pos, n, &signaled_pts) { + struct sync_pt *pt = + container_of(pos, struct sync_pt, signaled_list); + + list_del_init(pos); + sync_fence_signal_pt(pt); + kref_put(&pt->fence->kref, sync_fence_free); + } +} +EXPORT_SYMBOL(sync_timeline_signal); + +struct sync_pt *sync_pt_create(struct sync_timeline *parent, int size) +{ + struct sync_pt *pt; + + if (size < sizeof(struct sync_pt)) + return NULL; + + pt = kzalloc(size, GFP_KERNEL); + if (pt == NULL) + return NULL; + + INIT_LIST_HEAD(&pt->active_list); + kref_get(&parent->kref); + sync_timeline_add_pt(parent, pt); + + return pt; +} +EXPORT_SYMBOL(sync_pt_create); + +void sync_pt_free(struct sync_pt *pt) +{ + if (pt->parent->ops->free_pt) + pt->parent->ops->free_pt(pt); + + sync_timeline_remove_pt(pt); + + kref_put(&pt->parent->kref, sync_timeline_free); + + kfree(pt); +} +EXPORT_SYMBOL(sync_pt_free); + +/* call with pt->parent->active_list_lock held */ +static int _sync_pt_has_signaled(struct sync_pt *pt) +{ + int old_status = pt->status; + + if (!pt->status) + pt->status = pt->parent->ops->has_signaled(pt); + + if (!pt->status && pt->parent->destroyed) + pt->status = -ENOENT; + + if (pt->status != old_status) + pt->timestamp = ktime_get(); + + return pt->status; +} + +static struct sync_pt *sync_pt_dup(struct sync_pt *pt) +{ + return pt->parent->ops->dup(pt); +} + +/* Adds a sync pt to the active queue. Called when added to a fence */ +static void sync_pt_activate(struct sync_pt *pt) +{ + struct sync_timeline *obj = pt->parent; + unsigned long flags; + int err; + + spin_lock_irqsave(&obj->active_list_lock, flags); + + err = _sync_pt_has_signaled(pt); + if (err != 0) + goto out; + + list_add_tail(&pt->active_list, &obj->active_list_head); + +out: + spin_unlock_irqrestore(&obj->active_list_lock, flags); +} + +static int sync_fence_release(struct inode *inode, struct file *file); +static unsigned int sync_fence_poll(struct file *file, poll_table *wait); +static long sync_fence_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); + + +static const struct file_operations sync_fence_fops = { + .release = sync_fence_release, + .poll = sync_fence_poll, + .unlocked_ioctl = sync_fence_ioctl, +}; + +static struct sync_fence *sync_fence_alloc(const char *name) +{ + struct sync_fence *fence; + unsigned long flags; + + fence = kzalloc(sizeof(struct sync_fence), GFP_KERNEL); + if (fence == NULL) + return NULL; + + fence->file = anon_inode_getfile("sync_fence", &sync_fence_fops, + fence, 0); + if (fence->file == NULL) + goto err; + + kref_init(&fence->kref); + strlcpy(fence->name, name, sizeof(fence->name)); + + INIT_LIST_HEAD(&fence->pt_list_head); + INIT_LIST_HEAD(&fence->waiter_list_head); + spin_lock_init(&fence->waiter_list_lock); + + init_waitqueue_head(&fence->wq); + + spin_lock_irqsave(&sync_fence_list_lock, flags); + list_add_tail(&fence->sync_fence_list, &sync_fence_list_head); + spin_unlock_irqrestore(&sync_fence_list_lock, flags); + + return fence; + +err: + kfree(fence); + return NULL; +} + +/* TODO: implement a create which takes more that one sync_pt */ +struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt) +{ + struct sync_fence *fence; + + if (pt->fence) + return NULL; + + fence = sync_fence_alloc(name); + if (fence == NULL) + return NULL; + + pt->fence = fence; + list_add(&pt->pt_list, &fence->pt_list_head); + sync_pt_activate(pt); + + /* + * signal the fence in case pt was activated before + * sync_pt_activate(pt) was called + */ + sync_fence_signal_pt(pt); + + return fence; +} +EXPORT_SYMBOL(sync_fence_create); + +static int sync_fence_copy_pts(struct sync_fence *dst, struct sync_fence *src) +{ + struct list_head *pos; + + list_for_each(pos, &src->pt_list_head) { + struct sync_pt *orig_pt = + container_of(pos, struct sync_pt, pt_list); + struct sync_pt *new_pt = sync_pt_dup(orig_pt); + + if (new_pt == NULL) + return -ENOMEM; + + new_pt->fence = dst; + list_add(&new_pt->pt_list, &dst->pt_list_head); + } + + return 0; +} + +static int sync_fence_merge_pts(struct sync_fence *dst, struct sync_fence *src) +{ + struct list_head *src_pos, *dst_pos, *n; + + list_for_each(src_pos, &src->pt_list_head) { + struct sync_pt *src_pt = + container_of(src_pos, struct sync_pt, pt_list); + bool collapsed = false; + + list_for_each_safe(dst_pos, n, &dst->pt_list_head) { + struct sync_pt *dst_pt = + container_of(dst_pos, struct sync_pt, pt_list); + /* collapse two sync_pts on the same timeline + * to a single sync_pt that will signal at + * the later of the two + */ + if (dst_pt->parent == src_pt->parent) { + if (dst_pt->parent->ops->compare(dst_pt, src_pt) == -1) { + struct sync_pt *new_pt = + sync_pt_dup(src_pt); + if (new_pt == NULL) + return -ENOMEM; + + new_pt->fence = dst; + list_replace(&dst_pt->pt_list, + &new_pt->pt_list); + sync_pt_free(dst_pt); + } + collapsed = true; + break; + } + } + + if (!collapsed) { + struct sync_pt *new_pt = sync_pt_dup(src_pt); + + if (new_pt == NULL) + return -ENOMEM; + + new_pt->fence = dst; + list_add(&new_pt->pt_list, &dst->pt_list_head); + } + } + + return 0; +} + +static void sync_fence_detach_pts(struct sync_fence *fence) +{ + struct list_head *pos, *n; + + list_for_each_safe(pos, n, &fence->pt_list_head) { + struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list); + sync_timeline_remove_pt(pt); + } +} + +static void sync_fence_free_pts(struct sync_fence *fence) +{ + struct list_head *pos, *n; + + list_for_each_safe(pos, n, &fence->pt_list_head) { + struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list); + sync_pt_free(pt); + } +} + +struct sync_fence *sync_fence_fdget(int fd) +{ + struct file *file = fget(fd); + + if (file == NULL) + return NULL; + + if (file->f_op != &sync_fence_fops) + goto err; + + return file->private_data; + +err: + fput(file); + return NULL; +} +EXPORT_SYMBOL(sync_fence_fdget); + +void sync_fence_put(struct sync_fence *fence) +{ + fput(fence->file); +} +EXPORT_SYMBOL(sync_fence_put); + +void sync_fence_install(struct sync_fence *fence, int fd) +{ + fd_install(fd, fence->file); +} +EXPORT_SYMBOL(sync_fence_install); + +static int sync_fence_get_status(struct sync_fence *fence) +{ + struct list_head *pos; + int status = 1; + + list_for_each(pos, &fence->pt_list_head) { + struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list); + int pt_status = pt->status; + + if (pt_status < 0) { + status = pt_status; + break; + } else if (status == 1) { + status = pt_status; + } + } + + return status; +} + +struct sync_fence *sync_fence_merge(const char *name, + struct sync_fence *a, struct sync_fence *b) +{ + struct sync_fence *fence; + struct list_head *pos; + int err; + + fence = sync_fence_alloc(name); + if (fence == NULL) + return NULL; + + err = sync_fence_copy_pts(fence, a); + if (err < 0) + goto err; + + err = sync_fence_merge_pts(fence, b); + if (err < 0) + goto err; + + list_for_each(pos, &fence->pt_list_head) { + struct sync_pt *pt = + container_of(pos, struct sync_pt, pt_list); + sync_pt_activate(pt); + } + + /* + * signal the fence in case one of it's pts were activated before + * they were activated + */ + sync_fence_signal_pt(list_first_entry(&fence->pt_list_head, + struct sync_pt, + pt_list)); + + return fence; +err: + sync_fence_free_pts(fence); + kfree(fence); + return NULL; +} +EXPORT_SYMBOL(sync_fence_merge); + +static void sync_fence_signal_pt(struct sync_pt *pt) +{ + LIST_HEAD(signaled_waiters); + struct sync_fence *fence = pt->fence; + struct list_head *pos; + struct list_head *n; + unsigned long flags; + int status; + + status = sync_fence_get_status(fence); + + spin_lock_irqsave(&fence->waiter_list_lock, flags); + /* + * this should protect against two threads racing on the signaled + * false -> true transition + */ + if (status && !fence->status) { + list_for_each_safe(pos, n, &fence->waiter_list_head) + list_move(pos, &signaled_waiters); + + fence->status = status; + } else { + status = 0; + } + spin_unlock_irqrestore(&fence->waiter_list_lock, flags); + + if (status) { + list_for_each_safe(pos, n, &signaled_waiters) { + struct sync_fence_waiter *waiter = + container_of(pos, struct sync_fence_waiter, + waiter_list); + + list_del(pos); + waiter->callback(fence, waiter); + } + wake_up(&fence->wq); + } +} + +int sync_fence_wait_async(struct sync_fence *fence, + struct sync_fence_waiter *waiter) +{ + unsigned long flags; + int err = 0; + + spin_lock_irqsave(&fence->waiter_list_lock, flags); + + if (fence->status) { + err = fence->status; + goto out; + } + + list_add_tail(&waiter->waiter_list, &fence->waiter_list_head); +out: + spin_unlock_irqrestore(&fence->waiter_list_lock, flags); + + return err; +} +EXPORT_SYMBOL(sync_fence_wait_async); + +int sync_fence_cancel_async(struct sync_fence *fence, + struct sync_fence_waiter *waiter) +{ + struct list_head *pos; + struct list_head *n; + unsigned long flags; + int ret = -ENOENT; + + spin_lock_irqsave(&fence->waiter_list_lock, flags); + /* + * Make sure waiter is still in waiter_list because it is possible for + * the waiter to be removed from the list while the callback is still + * pending. + */ + list_for_each_safe(pos, n, &fence->waiter_list_head) { + struct sync_fence_waiter *list_waiter = + container_of(pos, struct sync_fence_waiter, + waiter_list); + if (list_waiter == waiter) { + list_del(pos); + ret = 0; + break; + } + } + spin_unlock_irqrestore(&fence->waiter_list_lock, flags); + return ret; +} +EXPORT_SYMBOL(sync_fence_cancel_async); + +static bool sync_fence_check(struct sync_fence *fence) +{ + /* + * Make sure that reads to fence->status are ordered with the + * wait queue event triggering + */ + smp_rmb(); + return fence->status != 0; +} + +int sync_fence_wait(struct sync_fence *fence, long timeout) +{ + int err = 0; + struct sync_pt *pt; + + trace_sync_wait(fence, 1); + list_for_each_entry(pt, &fence->pt_list_head, pt_list) + trace_sync_pt(pt); + + if (timeout > 0) { + timeout = msecs_to_jiffies(timeout); + err = wait_event_interruptible_timeout(fence->wq, + sync_fence_check(fence), + timeout); + } else if (timeout < 0) { + err = wait_event_interruptible(fence->wq, + sync_fence_check(fence)); + } + trace_sync_wait(fence, 0); + + if (err < 0) + return err; + + if (fence->status < 0) { + pr_info("fence error %d on [%p]\n", fence->status, fence); + sync_dump(); + return fence->status; + } + + if (fence->status == 0) { + if (timeout > 0) { + pr_info("fence timeout on [%p] after %dms\n", fence, + jiffies_to_msecs(timeout)); + sync_dump(); + } + return -ETIME; + } + + return 0; +} +EXPORT_SYMBOL(sync_fence_wait); + +static void sync_fence_free(struct kref *kref) +{ + struct sync_fence *fence = container_of(kref, struct sync_fence, kref); + + sync_fence_free_pts(fence); + + kfree(fence); +} + +static int sync_fence_release(struct inode *inode, struct file *file) +{ + struct sync_fence *fence = file->private_data; + unsigned long flags; + + /* + * We need to remove all ways to access this fence before droping + * our ref. + * + * start with its membership in the global fence list + */ + spin_lock_irqsave(&sync_fence_list_lock, flags); + list_del(&fence->sync_fence_list); + spin_unlock_irqrestore(&sync_fence_list_lock, flags); + + /* + * remove its pts from their parents so that sync_timeline_signal() + * can't reference the fence. + */ + sync_fence_detach_pts(fence); + + kref_put(&fence->kref, sync_fence_free); + + return 0; +} + +static unsigned int sync_fence_poll(struct file *file, poll_table *wait) +{ + struct sync_fence *fence = file->private_data; + + poll_wait(file, &fence->wq, wait); + + /* + * Make sure that reads to fence->status are ordered with the + * wait queue event triggering + */ + smp_rmb(); + + if (fence->status == 1) + return POLLIN; + else if (fence->status < 0) + return POLLERR; + else + return 0; +} + +static long sync_fence_ioctl_wait(struct sync_fence *fence, unsigned long arg) +{ + __s32 value; + + if (copy_from_user(&value, (void __user *)arg, sizeof(value))) + return -EFAULT; + + return sync_fence_wait(fence, value); +} + +static long sync_fence_ioctl_merge(struct sync_fence *fence, unsigned long arg) +{ + int fd = get_unused_fd(); + int err; + struct sync_fence *fence2, *fence3; + struct sync_merge_data data; + + if (fd < 0) + return fd; + + if (copy_from_user(&data, (void __user *)arg, sizeof(data))) { + err = -EFAULT; + goto err_put_fd; + } + + fence2 = sync_fence_fdget(data.fd2); + if (fence2 == NULL) { + err = -ENOENT; + goto err_put_fd; + } + + data.name[sizeof(data.name) - 1] = '\0'; + fence3 = sync_fence_merge(data.name, fence, fence2); + if (fence3 == NULL) { + err = -ENOMEM; + goto err_put_fence2; + } + + data.fence = fd; + if (copy_to_user((void __user *)arg, &data, sizeof(data))) { + err = -EFAULT; + goto err_put_fence3; + } + + sync_fence_install(fence3, fd); + sync_fence_put(fence2); + return 0; + +err_put_fence3: + sync_fence_put(fence3); + +err_put_fence2: + sync_fence_put(fence2); + +err_put_fd: + put_unused_fd(fd); + return err; +} + +static int sync_fill_pt_info(struct sync_pt *pt, void *data, int size) +{ + struct sync_pt_info *info = data; + int ret; + + if (size < sizeof(struct sync_pt_info)) + return -ENOMEM; + + info->len = sizeof(struct sync_pt_info); + + if (pt->parent->ops->fill_driver_data) { + ret = pt->parent->ops->fill_driver_data(pt, info->driver_data, + size - sizeof(*info)); + if (ret < 0) + return ret; + + info->len += ret; + } + + strlcpy(info->obj_name, pt->parent->name, sizeof(info->obj_name)); + strlcpy(info->driver_name, pt->parent->ops->driver_name, + sizeof(info->driver_name)); + info->status = pt->status; + info->timestamp_ns = ktime_to_ns(pt->timestamp); + + return info->len; +} + +static long sync_fence_ioctl_fence_info(struct sync_fence *fence, + unsigned long arg) +{ + struct sync_fence_info_data *data; + struct list_head *pos; + __u32 size; + __u32 len = 0; + int ret; + + if (copy_from_user(&size, (void __user *)arg, sizeof(size))) + return -EFAULT; + + if (size < sizeof(struct sync_fence_info_data)) + return -EINVAL; + + if (size > 4096) + size = 4096; + + data = kzalloc(size, GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + strlcpy(data->name, fence->name, sizeof(data->name)); + data->status = fence->status; + len = sizeof(struct sync_fence_info_data); + + list_for_each(pos, &fence->pt_list_head) { + struct sync_pt *pt = + container_of(pos, struct sync_pt, pt_list); + + ret = sync_fill_pt_info(pt, (u8 *)data + len, size - len); + + if (ret < 0) + goto out; + + len += ret; + } + + data->len = len; + + if (copy_to_user((void __user *)arg, data, len)) + ret = -EFAULT; + else + ret = 0; + +out: + kfree(data); + + return ret; +} + +static long sync_fence_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct sync_fence *fence = file->private_data; + switch (cmd) { + case SYNC_IOC_WAIT: + return sync_fence_ioctl_wait(fence, arg); + + case SYNC_IOC_MERGE: + return sync_fence_ioctl_merge(fence, arg); + + case SYNC_IOC_FENCE_INFO: + return sync_fence_ioctl_fence_info(fence, arg); + + default: + return -ENOTTY; + } +} + +#ifdef CONFIG_DEBUG_FS +static const char *sync_status_str(int status) +{ + if (status > 0) + return "signaled"; + else if (status == 0) + return "active"; + else + return "error"; +} + +static void sync_print_pt(struct seq_file *s, struct sync_pt *pt, bool fence) +{ + int status = pt->status; + seq_printf(s, " %s%spt %s", + fence ? pt->parent->name : "", + fence ? "_" : "", + sync_status_str(status)); + if (pt->status) { + struct timeval tv = ktime_to_timeval(pt->timestamp); + seq_printf(s, "@%ld.%06ld", tv.tv_sec, tv.tv_usec); + } + + if (pt->parent->ops->timeline_value_str && + pt->parent->ops->pt_value_str) { + char value[64]; + pt->parent->ops->pt_value_str(pt, value, sizeof(value)); + seq_printf(s, ": %s", value); + if (fence) { + pt->parent->ops->timeline_value_str(pt->parent, value, + sizeof(value)); + seq_printf(s, " / %s", value); + } + } else if (pt->parent->ops->print_pt) { + seq_printf(s, ": "); + pt->parent->ops->print_pt(s, pt); + } + + seq_printf(s, "\n"); +} + +static void sync_print_obj(struct seq_file *s, struct sync_timeline *obj) +{ + struct list_head *pos; + unsigned long flags; + + seq_printf(s, "%s %s", obj->name, obj->ops->driver_name); + + if (obj->ops->timeline_value_str) { + char value[64]; + obj->ops->timeline_value_str(obj, value, sizeof(value)); + seq_printf(s, ": %s", value); + } else if (obj->ops->print_obj) { + seq_printf(s, ": "); + obj->ops->print_obj(s, obj); + } + + seq_printf(s, "\n"); + + spin_lock_irqsave(&obj->child_list_lock, flags); + list_for_each(pos, &obj->child_list_head) { + struct sync_pt *pt = + container_of(pos, struct sync_pt, child_list); + sync_print_pt(s, pt, false); + } + spin_unlock_irqrestore(&obj->child_list_lock, flags); +} + +static void sync_print_fence(struct seq_file *s, struct sync_fence *fence) +{ + struct list_head *pos; + unsigned long flags; + + seq_printf(s, "[%p] %s: %s\n", fence, fence->name, + sync_status_str(fence->status)); + + list_for_each(pos, &fence->pt_list_head) { + struct sync_pt *pt = + container_of(pos, struct sync_pt, pt_list); + sync_print_pt(s, pt, true); + } + + spin_lock_irqsave(&fence->waiter_list_lock, flags); + list_for_each(pos, &fence->waiter_list_head) { + struct sync_fence_waiter *waiter = + container_of(pos, struct sync_fence_waiter, + waiter_list); + + seq_printf(s, "waiter %pF\n", waiter->callback); + } + spin_unlock_irqrestore(&fence->waiter_list_lock, flags); +} + +static int sync_debugfs_show(struct seq_file *s, void *unused) +{ + unsigned long flags; + struct list_head *pos; + + seq_printf(s, "objs:\n--------------\n"); + + spin_lock_irqsave(&sync_timeline_list_lock, flags); + list_for_each(pos, &sync_timeline_list_head) { + struct sync_timeline *obj = + container_of(pos, struct sync_timeline, + sync_timeline_list); + + sync_print_obj(s, obj); + seq_printf(s, "\n"); + } + spin_unlock_irqrestore(&sync_timeline_list_lock, flags); + + seq_printf(s, "fences:\n--------------\n"); + + spin_lock_irqsave(&sync_fence_list_lock, flags); + list_for_each(pos, &sync_fence_list_head) { + struct sync_fence *fence = + container_of(pos, struct sync_fence, sync_fence_list); + + sync_print_fence(s, fence); + seq_printf(s, "\n"); + } + spin_unlock_irqrestore(&sync_fence_list_lock, flags); + return 0; +} + +static int sync_debugfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, sync_debugfs_show, inode->i_private); +} + +static const struct file_operations sync_debugfs_fops = { + .open = sync_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static __init int sync_debugfs_init(void) +{ + debugfs_create_file("sync", S_IRUGO, NULL, NULL, &sync_debugfs_fops); + return 0; +} +late_initcall(sync_debugfs_init); + +#define DUMP_CHUNK 256 +static char sync_dump_buf[64 * 1024]; +void sync_dump(void) +{ + struct seq_file s = { + .buf = sync_dump_buf, + .size = sizeof(sync_dump_buf) - 1, + }; + int i; + + sync_debugfs_show(&s, NULL); + + for (i = 0; i < s.count; i += DUMP_CHUNK) { + if ((s.count - i) > DUMP_CHUNK) { + char c = s.buf[i + DUMP_CHUNK]; + s.buf[i + DUMP_CHUNK] = 0; + pr_cont("%s", s.buf + i); + s.buf[i + DUMP_CHUNK] = c; + } else { + s.buf[s.count] = 0; + pr_cont("%s", s.buf + i); + } + } +} +#else +static void sync_dump(void) +{ +} +#endif diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index a7964071..89ebdb9e 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -538,6 +538,12 @@ config BLK_DEV_HD If unsure, say N. +config BLK_DEV_ANYKA + tristate "block dev for anyka video buff" + depends on ARCH_AK39 + help + block dev for anyka video buff. + config BLK_DEV_RBD tristate "Rados block device (RBD)" depends on INET && EXPERIMENTAL && BLOCK diff --git a/drivers/block/Makefile b/drivers/block/Makefile index 5b795059..dae56190 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_VIODASD) += viodasd.o obj-$(CONFIG_BLK_DEV_SX8) += sx8.o obj-$(CONFIG_BLK_DEV_UB) += ub.o obj-$(CONFIG_BLK_DEV_HD) += hd.o +obj-$(CONFIG_BLK_DEV_ANYKA) += ak_videobuf_block.o obj-$(CONFIG_XEN_BLKDEV_FRONTEND) += xen-blkfront.o obj-$(CONFIG_XEN_BLKDEV_BACKEND) += xen-blkback/ diff --git a/drivers/block/ak_videobuf_block.c b/drivers/block/ak_videobuf_block.c new file mode 100644 index 00000000..db7d7e45 --- /dev/null +++ b/drivers/block/ak_videobuf_block.c @@ -0,0 +1,218 @@ +#include //ֶ֧̬Ӻжģ +#include //Ҫдںˣںصͷļ +#include //ʼͷļ + +#include //ļstructĶ +#include //һЩ͵Ķ +#include //ļõغ +#include //vmalloc()ڴַַ +#include //requestʽ 豸Ҫblk_init_queue +#include //Ӳ̲ͷļӲ̼Ĵ˿ڡ״̬ͷϢ +#include + +#define RAMHD_NAME "video_ram" //豸 +#define RAMHD_MAX_DEVICE 1 //豸 +#define RAMHD_MAX_PARTITIONS 1 // + +#define RAMHD_SECTOR_SIZE 512 //С +#define RAMHD_SECTORS 16 // http://www.embedu.org/Column/Column863.htm +#define RAMHD_HEADS 4 //ͷ +#define RAMHD_CYLINDERS 256 //ŵ() + +#define RAMHD_SECTOR_TOTAL (RAMHD_SECTORS * RAMHD_HEADS * RAMHD_CYLINDERS) //ܴС +#define RAMHD_SIZE (RAMHD_SECTOR_SIZE * RAMHD_SECTOR_TOTAL) //8MB + +typedef struct{ + unsigned char *data; //豸ݿռ׵ַ + struct request_queue *queue; //豸 + spinlock_t lock; // + struct gendisk *gd; //ͨô̽ṹ +}RAMHD_DEV; + +static char *sdisk[RAMHD_MAX_DEVICE]; //ڴ׵ַ +static RAMHD_DEV *rdev[RAMHD_MAX_DEVICE]; //ڴ׵ַ + +static dev_t ramhd_major; //豸 + +static unsigned long disk_addr = AK_PA_VIDEO_BUFF; +static unsigned long disk_size = AK_SZ_VIDEO_BUFF; + +module_param(disk_addr, ulong, S_IRUGO); +module_param(disk_size, ulong, S_IRUGO); + +static int ramhd_space_init(void) +{ + int i; + int err = 0; + for(i = 0; i < RAMHD_MAX_DEVICE; i++){ + sdisk[i] = ioremap(disk_addr, disk_size); + if(!sdisk[i]){ + err = -ENOMEM; //errno:12 ڴ治 + return err; + } + memset(sdisk[i], 0, disk_size); + } + + return err; +} + +static void ramhd_space_clean(void) +{ + int i; + for(i = 0; i < RAMHD_MAX_DEVICE; i++){ + iounmap((void*)disk_addr); + } +} + +static int alloc_ramdev(void) +{ + int i; + for(i = 0; i < RAMHD_MAX_DEVICE; i++){ + rdev[i] = kzalloc(sizeof(RAMHD_DEV), GFP_KERNEL); //ںRAMHD_DEVṹڴռ + if(!rdev[i]) + return -ENOMEM; //errno:12 ڴ治 + } + return 0; +} + +static void clean_ramdev(void) +{ + int i; + for(i = 0; i < RAMHD_MAX_DEVICE; i++){ + if(rdev[i]) + kfree(rdev[i]); //ͷŷڴ + } +} + +int ramhd_open(struct block_device *bdev, fmode_t mode) //豸õ +{ + return 0; +} + +int ramhd_release(struct gendisk *gd, fmode_t mode) //豸رõ +{ + return 0; +} + +static int ramhd_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) //IO +{ + int err; + struct hd_geometry geo; //hd_geometryṹͷϢ + + switch(cmd) + { + case HDIO_GETGEO: //ȡ豸 + err = !access_ok(VERIFY_WRITE, arg, sizeof(geo));//ָָĴ洢Ƿд + if(err) return -EFAULT; //errno:14 ַ + + geo.cylinders = RAMHD_CYLINDERS; // + geo.heads = RAMHD_HEADS; //ͷ + geo.sectors = RAMHD_SECTORS; // + geo.start = get_start_sect(bdev); //ʼַ + if(copy_to_user((void *)arg, &geo, sizeof(geo))) + //ں˵ַ&geoָʾݸƵargָûռĵַ + return -EFAULT; //errno:14 ַ + return 0; + } + + return -ENOTTY; //errno:25 ʵIOƲ +} + +static struct block_device_operations ramhd_fops = //һ豸IJ +{ + .owner = THIS_MODULE, + .open = ramhd_open, + .release = ramhd_release, + .ioctl = ramhd_ioctl, +}; + +void ramhd_req_func (struct request_queue *q) //ݸ豸 +{ + struct request *req; //ȡreq + RAMHD_DEV *pdev; + char *pData; + unsigned long addr, size, start; + req = blk_fetch_request(q); //ӿ豸ȡ洢req; + //blk_fetch_request()ԶεãqueueûݣreqNULL + while (req) { //жϵǰrequestǷϷ ѭлȡһҪ + start = blk_rq_pos(req); // ȡǰrequestṹʼ + pdev = (RAMHD_DEV *)req->rq_disk->private_data; //豸ṹָ + pData = pdev->data;//豸ַ + addr = (unsigned long)pData + start * RAMHD_SECTOR_SIZE;//ַ + size = blk_rq_cur_bytes(req); // req һ + if (rq_data_dir(req) == READ) //ݴͷ.0ʾ豸ȡ,ʾд豸. + memcpy(req->buffer, (char *)addr, size); // + else + memcpy((char *)addr, req->buffer, size); //д + + if(!__blk_end_request_cur(req, 0)) //귵false + req = blk_fetch_request(q); //ȡе + } +} + +int ramhd_init(void) //ʼ +{ + int i; + int ret; + + if((disk_addr < 0x80000000) || ((disk_addr + disk_size) > (0x80000000 + AK_SZ_VIDEO_BUFF))){ + printk(KERN_ERR "###error!\n"); + printk(KERN_ERR "disk_addr + disk_size must :0x80000000 - %x!\n", 0x80000000 + AK_SZ_VIDEO_BUFF); + return -1; + } + + ret = ramhd_space_init(); + if(ret < 0){ + printk(KERN_ERR "###ramhd_space_init fail!\n"); + return -1; + } + + ret = alloc_ramdev(); + if(ret < 0){ + printk(KERN_ERR "###alloc_ramdev fail!\n"); + return -1; + } + + ramhd_major = register_blkdev(0, RAMHD_NAME); //豸עᵽں + //majorΪ0ں˻Զһµ豸ţramhd_major + for(i = 0; i < RAMHD_MAX_DEVICE; i++) + { + rdev[i]->data = sdisk[i]; + rdev[i]->gd = alloc_disk(RAMHD_MAX_PARTITIONS); + spin_lock_init(&rdev[i]->lock); //ʼ + rdev[i]->queue = blk_init_queue(ramhd_req_func, &rdev[i]->lock);//ʼramhd_req_funcа + rdev[i]->gd->major = ramhd_major; + rdev[i]->gd->first_minor = i * RAMHD_MAX_PARTITIONS; + rdev[i]->gd->fops = &ramhd_fops; //豸ķ + rdev[i]->gd->queue = rdev[i]->queue; + rdev[i]->gd->private_data = rdev[i]; //ʹԱָ + sprintf(rdev[i]->gd->disk_name, "video_ram%d", i); + //set_capacity(rdev[i]->gd, RAMHD_SECTOR_TOTAL); + set_capacity(rdev[i]->gd, disk_size/RAMHD_SECTOR_SIZE); + add_disk(rdev[i]->gd); //ϵͳ豸 + } + + return 0; +} + +void ramhd_exit(void) //ģжغ +{ + int i; + for(i = 0; i < RAMHD_MAX_DEVICE; i++) + { + del_gendisk(rdev[i]->gd); //ɾgendiskṹ + put_disk(rdev[i]->gd); //gendiskṹü + blk_cleanup_queue(rdev[i]->queue); // + } + unregister_blkdev(ramhd_major,RAMHD_NAME); ; //ע豸 + clean_ramdev(); + ramhd_space_clean(); +} + +module_init(ramhd_init); +module_exit(ramhd_exit); + + +MODULE_AUTHOR("lyl"); +MODULE_DESCRIPTION("The Ramdisk implementation with request function"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index ee946865..f48cd688 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -6,6 +6,19 @@ menu "Character devices" source "drivers/tty/Kconfig" +config DEVMEM + bool "Memory device driver" + default y + help + The memory driver provides two character devices, mem and kmem, which + provide access to the system's memory. The mem device is a view of + physical memory, and each byte in the device corresponds to the + matching physical address. The kmem device is the same as mem, but + the addresses correspond to the kernel's virtual address space rather + than physical memory. These devices are standard parts of a Linux + system and most users should say Y here. You might say N if very + security conscience or memory is tight. + config DEVKMEM bool "/dev/kmem virtual device support" default y @@ -583,6 +596,10 @@ config DEVPORT depends on ISA || PCI default y +config DCC_TTY + tristate "DCC tty driver" + depends on ARM + source "drivers/s390/char/Kconfig" config RAMOOPS diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 0dc5d7ce..8f188917 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_IPMI_HANDLER) += ipmi/ obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o obj-$(CONFIG_TCG_TPM) += tpm/ +obj-$(CONFIG_DCC_TTY) += dcc_tty.o obj-$(CONFIG_PS3_FLASH) += ps3flash.o obj-$(CONFIG_RAMOOPS) += ramoops.o diff --git a/drivers/char/dcc_tty.c b/drivers/char/dcc_tty.c new file mode 100644 index 00000000..a787accd --- /dev/null +++ b/drivers/char/dcc_tty.c @@ -0,0 +1,326 @@ +/* drivers/char/dcc_tty.c + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("DCC TTY Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0"); + +static spinlock_t g_dcc_tty_lock = SPIN_LOCK_UNLOCKED; +static struct hrtimer g_dcc_timer; +static char g_dcc_buffer[16]; +static int g_dcc_buffer_head; +static int g_dcc_buffer_count; +static unsigned g_dcc_write_delay_usecs = 1; +static struct tty_driver *g_dcc_tty_driver; +static struct tty_struct *g_dcc_tty; +static int g_dcc_tty_open_count; + +static void dcc_poll_locked(void) +{ + char ch; + int rch; + int written; + + while (g_dcc_buffer_count) { + ch = g_dcc_buffer[g_dcc_buffer_head]; + asm( + "mrc 14, 0, r15, c0, c1, 0\n" + "mcrcc 14, 0, %1, c0, c5, 0\n" + "movcc %0, #1\n" + "movcs %0, #0\n" + : "=r" (written) + : "r" (ch) + ); + if (written) { + if (ch == '\n') + g_dcc_buffer[g_dcc_buffer_head] = '\r'; + else { + g_dcc_buffer_head = (g_dcc_buffer_head + 1) % ARRAY_SIZE(g_dcc_buffer); + g_dcc_buffer_count--; + if (g_dcc_tty) + tty_wakeup(g_dcc_tty); + } + g_dcc_write_delay_usecs = 1; + } else { + if (g_dcc_write_delay_usecs > 0x100) + break; + g_dcc_write_delay_usecs <<= 1; + udelay(g_dcc_write_delay_usecs); + } + } + + if (g_dcc_tty && !test_bit(TTY_THROTTLED, &g_dcc_tty->flags)) { + asm( + "mrc 14, 0, %0, c0, c1, 0\n" + "tst %0, #(1 << 30)\n" + "moveq %0, #-1\n" + "mrcne 14, 0, %0, c0, c5, 0\n" + : "=r" (rch) + ); + if (rch >= 0) { + ch = rch; + tty_insert_flip_string(g_dcc_tty, &ch, 1); + tty_flip_buffer_push(g_dcc_tty); + } + } + + + if (g_dcc_buffer_count) + hrtimer_start(&g_dcc_timer, ktime_set(0, g_dcc_write_delay_usecs * NSEC_PER_USEC), HRTIMER_MODE_REL); + else + hrtimer_start(&g_dcc_timer, ktime_set(0, 20 * NSEC_PER_MSEC), HRTIMER_MODE_REL); +} + +static int dcc_tty_open(struct tty_struct * tty, struct file * filp) +{ + int ret; + unsigned long irq_flags; + + spin_lock_irqsave(&g_dcc_tty_lock, irq_flags); + if (g_dcc_tty == NULL || g_dcc_tty == tty) { + g_dcc_tty = tty; + g_dcc_tty_open_count++; + ret = 0; + } else + ret = -EBUSY; + spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags); + + printk("dcc_tty_open, tty %p, f_flags %x, returned %d\n", tty, filp->f_flags, ret); + + return ret; +} + +static void dcc_tty_close(struct tty_struct * tty, struct file * filp) +{ + printk("dcc_tty_close, tty %p, f_flags %x\n", tty, filp->f_flags); + if (g_dcc_tty == tty) { + if (--g_dcc_tty_open_count == 0) + g_dcc_tty = NULL; + } +} + +static int dcc_write(const unsigned char *buf_start, int count) +{ + const unsigned char *buf = buf_start; + unsigned long irq_flags; + int copy_len; + int space_left; + int tail; + + if (count < 1) + return 0; + + spin_lock_irqsave(&g_dcc_tty_lock, irq_flags); + do { + tail = (g_dcc_buffer_head + g_dcc_buffer_count) % ARRAY_SIZE(g_dcc_buffer); + copy_len = ARRAY_SIZE(g_dcc_buffer) - tail; + space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count; + if (copy_len > space_left) + copy_len = space_left; + if (copy_len > count) + copy_len = count; + memcpy(&g_dcc_buffer[tail], buf, copy_len); + g_dcc_buffer_count += copy_len; + buf += copy_len; + count -= copy_len; + if (copy_len < count && copy_len < space_left) { + space_left -= copy_len; + copy_len = count; + if (copy_len > space_left) { + copy_len = space_left; + } + memcpy(g_dcc_buffer, buf, copy_len); + buf += copy_len; + count -= copy_len; + g_dcc_buffer_count += copy_len; + } + dcc_poll_locked(); + space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count; + } while(count && space_left); + spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags); + return buf - buf_start; +} + +static int dcc_tty_write(struct tty_struct * tty, const unsigned char *buf, int count) +{ + int ret; + /* printk("dcc_tty_write %p, %d\n", buf, count); */ + ret = dcc_write(buf, count); + if (ret != count) + printk("dcc_tty_write %p, %d, returned %d\n", buf, count, ret); + return ret; +} + +static int dcc_tty_write_room(struct tty_struct *tty) +{ + int space_left; + unsigned long irq_flags; + + spin_lock_irqsave(&g_dcc_tty_lock, irq_flags); + space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count; + spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags); + return space_left; +} + +static int dcc_tty_chars_in_buffer(struct tty_struct *tty) +{ + int ret; + asm( + "mrc 14, 0, %0, c0, c1, 0\n" + "mov %0, %0, LSR #30\n" + "and %0, %0, #1\n" + : "=r" (ret) + ); + return ret; +} + +static void dcc_tty_unthrottle(struct tty_struct * tty) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&g_dcc_tty_lock, irq_flags); + dcc_poll_locked(); + spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags); +} + +static enum hrtimer_restart dcc_tty_timer_func(struct hrtimer *timer) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&g_dcc_tty_lock, irq_flags); + dcc_poll_locked(); + spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags); + return HRTIMER_NORESTART; +} + +void dcc_console_write(struct console *co, const char *b, unsigned count) +{ +#if 1 + dcc_write(b, count); +#else + /* blocking printk */ + while (count > 0) { + int written; + written = dcc_write(b, count); + if (written) { + b += written; + count -= written; + } + } +#endif +} + +static struct tty_driver *dcc_console_device(struct console *c, int *index) +{ + *index = 0; + return g_dcc_tty_driver; +} + +static int __init dcc_console_setup(struct console *co, char *options) +{ + if (co->index != 0) + return -ENODEV; + return 0; +} + + +static struct console dcc_console = +{ + .name = "ttyDCC", + .write = dcc_console_write, + .device = dcc_console_device, + .setup = dcc_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +static struct tty_operations dcc_tty_ops = { + .open = dcc_tty_open, + .close = dcc_tty_close, + .write = dcc_tty_write, + .write_room = dcc_tty_write_room, + .chars_in_buffer = dcc_tty_chars_in_buffer, + .unthrottle = dcc_tty_unthrottle, +}; + +static int __init dcc_tty_init(void) +{ + int ret; + + hrtimer_init(&g_dcc_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + g_dcc_timer.function = dcc_tty_timer_func; + + g_dcc_tty_driver = alloc_tty_driver(1); + if (!g_dcc_tty_driver) { + printk(KERN_ERR "dcc_tty_probe: alloc_tty_driver failed\n"); + ret = -ENOMEM; + goto err_alloc_tty_driver_failed; + } + g_dcc_tty_driver->owner = THIS_MODULE; + g_dcc_tty_driver->driver_name = "dcc"; + g_dcc_tty_driver->name = "ttyDCC"; + g_dcc_tty_driver->major = 0; // auto assign + g_dcc_tty_driver->minor_start = 0; + g_dcc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + g_dcc_tty_driver->subtype = SERIAL_TYPE_NORMAL; + g_dcc_tty_driver->init_termios = tty_std_termios; + g_dcc_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + tty_set_operations(g_dcc_tty_driver, &dcc_tty_ops); + ret = tty_register_driver(g_dcc_tty_driver); + if (ret) { + printk(KERN_ERR "dcc_tty_probe: tty_register_driver failed, %d\n", ret); + goto err_tty_register_driver_failed; + } + tty_register_device(g_dcc_tty_driver, 0, NULL); + + register_console(&dcc_console); + hrtimer_start(&g_dcc_timer, ktime_set(0, 0), HRTIMER_MODE_REL); + + return 0; + +err_tty_register_driver_failed: + put_tty_driver(g_dcc_tty_driver); + g_dcc_tty_driver = NULL; +err_alloc_tty_driver_failed: + return ret; +} + +static void __exit dcc_tty_exit(void) +{ + int ret; + + tty_unregister_device(g_dcc_tty_driver, 0); + ret = tty_unregister_driver(g_dcc_tty_driver); + if (ret < 0) { + printk(KERN_ERR "dcc_tty_remove: tty_unregister_driver failed, %d\n", ret); + } else { + put_tty_driver(g_dcc_tty_driver); + } + g_dcc_tty_driver = NULL; +} + +module_init(dcc_tty_init); +module_exit(dcc_tty_exit); + + diff --git a/drivers/char/mem.c b/drivers/char/mem.c index d6e9d081..67e19b63 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -57,6 +57,7 @@ static inline int valid_mmap_phys_addr_range(unsigned long pfn, size_t size) } #endif +#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) #ifdef CONFIG_STRICT_DEVMEM static inline int range_is_allowed(unsigned long pfn, unsigned long size) { @@ -82,7 +83,9 @@ static inline int range_is_allowed(unsigned long pfn, unsigned long size) return 1; } #endif +#endif +#ifdef CONFIG_DEVMEM void __weak unxlate_dev_mem_ptr(unsigned long phys, void *addr) { } @@ -209,6 +212,9 @@ static ssize_t write_mem(struct file *file, const char __user *buf, *ppos += written; return written; } +#endif /* CONFIG_DEVMEM */ + +#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) int __weak phys_mem_access_prot_allowed(struct file *file, unsigned long pfn, unsigned long size, pgprot_t *vma_prot) @@ -330,6 +336,7 @@ static int mmap_mem(struct file *file, struct vm_area_struct *vma) } return 0; } +#endif /* CONFIG_DEVMEM */ #ifdef CONFIG_DEVKMEM static int mmap_kmem(struct file *file, struct vm_area_struct *vma) @@ -694,6 +701,8 @@ static loff_t null_lseek(struct file *file, loff_t offset, int orig) return file->f_pos = 0; } +#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) || defined(CONFIG_DEVPORT) + /* * The memory devices use the full 32/64 bits of the offset, and so we cannot * check against negative addresses: they are ok. The return value is weird, @@ -727,10 +736,14 @@ static loff_t memory_lseek(struct file *file, loff_t offset, int orig) return ret; } +#endif + +#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) || defined(CONFIG_DEVPORT) static int open_port(struct inode * inode, struct file * filp) { return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; } +#endif #define zero_lseek null_lseek #define full_lseek null_lseek @@ -740,6 +753,7 @@ static int open_port(struct inode * inode, struct file * filp) #define open_kmem open_mem #define open_oldmem open_mem +#ifdef CONFIG_DEVMEM static const struct file_operations mem_fops = { .llseek = memory_lseek, .read = read_mem, @@ -748,6 +762,7 @@ static const struct file_operations mem_fops = { .open = open_mem, .get_unmapped_area = get_unmapped_area_mem, }; +#endif #ifdef CONFIG_DEVKMEM static const struct file_operations kmem_fops = { @@ -851,7 +866,9 @@ static const struct memdev { const struct file_operations *fops; struct backing_dev_info *dev_info; } devlist[] = { +#ifdef CONFIG_DEVMEM [1] = { "mem", 0, &mem_fops, &directly_mappable_cdev_bdi }, +#endif #ifdef CONFIG_DEVKMEM [2] = { "kmem", 0, &kmem_fops, &directly_mappable_cdev_bdi }, #endif diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index e24a2a1b..57f96ebb 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -99,6 +99,16 @@ config CPU_FREQ_DEFAULT_GOV_CONSERVATIVE Be aware that not all cpufreq drivers support the conservative governor. If unsure have a look at the help section of the driver. Fallback governor will be the performance governor. + +config CPU_FREQ_DEFAULT_GOV_INTERACTIVE + bool "interactive" + select CPU_FREQ_GOV_INTERACTIVE + help + Use the CPUFreq governor 'interactive' as default. This allows + you to get a full dynamic cpu frequency capable system by simply + loading your cpufreq low-level hardware driver, using the + 'interactive' governor for latency-sensitive workloads. + endchoice config CPU_FREQ_GOV_PERFORMANCE @@ -156,6 +166,23 @@ config CPU_FREQ_GOV_ONDEMAND If in doubt, say N. +config CPU_FREQ_GOV_INTERACTIVE + tristate "'interactive' cpufreq policy governor" + help + 'interactive' - This driver adds a dynamic cpufreq policy governor + designed for latency-sensitive workloads. + + This governor attempts to reduce the latency of clock + increases so that the system is more responsive to + interactive workloads. + + To compile this driver as a module, choose M here: the + module will be called cpufreq_interactive. + + For details, take a look at linux/Documentation/cpu-freq. + + If in doubt, say N. + config CPU_FREQ_GOV_CONSERVATIVE tristate "'conservative' cpufreq governor" depends on CPU_FREQ diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 9531fc2e..35835b7c 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_POWERSAVE) += cpufreq_powersave.o obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o +obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVE) += cpufreq_interactive.o # CPUfreq cross-arch helpers obj-$(CONFIG_CPU_FREQ_TABLE) += freq_table.o diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c new file mode 100644 index 00000000..7d1952c5 --- /dev/null +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -0,0 +1,1066 @@ +/* + * drivers/cpufreq/cpufreq_interactive.c + * + * Copyright (C) 2010 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + * Author: Mike Chan (mike@android.com) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CREATE_TRACE_POINTS +#include + +static int active_count; + +struct cpufreq_interactive_cpuinfo { + struct timer_list cpu_timer; + struct timer_list cpu_slack_timer; + spinlock_t load_lock; /* protects the next 4 fields */ + u64 time_in_idle; + u64 time_in_idle_timestamp; + u64 cputime_speedadj; + u64 cputime_speedadj_timestamp; + struct cpufreq_policy *policy; + struct cpufreq_frequency_table *freq_table; + unsigned int target_freq; + unsigned int floor_freq; + u64 floor_validate_time; + u64 hispeed_validate_time; + struct rw_semaphore enable_sem; + int governor_enabled; +}; + +static DEFINE_PER_CPU(struct cpufreq_interactive_cpuinfo, cpuinfo); + +/* realtime thread handles frequency scaling */ +static struct task_struct *speedchange_task; +static cpumask_t speedchange_cpumask; +static spinlock_t speedchange_cpumask_lock; +static struct mutex gov_lock; + +/* Hi speed to bump to from lo speed when load burst (default max) */ +static unsigned int hispeed_freq; + +/* Go to hi speed when CPU load at or above this value. */ +#define DEFAULT_GO_HISPEED_LOAD 99 +static unsigned long go_hispeed_load = DEFAULT_GO_HISPEED_LOAD; + +/* Target load. Lower values result in higher CPU speeds. */ +#define DEFAULT_TARGET_LOAD 90 +static unsigned int default_target_loads[] = {DEFAULT_TARGET_LOAD}; +static spinlock_t target_loads_lock; +static unsigned int *target_loads = default_target_loads; +static int ntarget_loads = ARRAY_SIZE(default_target_loads); + +/* + * The minimum amount of time to spend at a frequency before we can ramp down. + */ +#define DEFAULT_MIN_SAMPLE_TIME (80 * USEC_PER_MSEC) +static unsigned long min_sample_time = DEFAULT_MIN_SAMPLE_TIME; + +/* + * The sample rate of the timer used to increase frequency + */ +#define DEFAULT_TIMER_RATE (20 * USEC_PER_MSEC) +static unsigned long timer_rate = DEFAULT_TIMER_RATE; + +/* + * Wait this long before raising speed above hispeed, by default a single + * timer interval. + */ +#define DEFAULT_ABOVE_HISPEED_DELAY DEFAULT_TIMER_RATE +static unsigned long above_hispeed_delay_val = DEFAULT_ABOVE_HISPEED_DELAY; + +/* Non-zero means indefinite speed boost active */ +static int boost_val; +/* Duration of a boot pulse in usecs */ +static int boostpulse_duration_val = DEFAULT_MIN_SAMPLE_TIME; +/* End time of boost pulse in ktime converted to usecs */ +static u64 boostpulse_endtime; + +/* + * Max additional time to wait in idle, beyond timer_rate, at speeds above + * minimum before wakeup to reduce speed, or -1 if unnecessary. + */ +#define DEFAULT_TIMER_SLACK (4 * DEFAULT_TIMER_RATE) +static int timer_slack_val = DEFAULT_TIMER_SLACK; + +static int cpufreq_governor_interactive(struct cpufreq_policy *policy, + unsigned int event); + +#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE +static +#endif +struct cpufreq_governor cpufreq_gov_interactive = { + .name = "interactive", + .governor = cpufreq_governor_interactive, + .max_transition_latency = 10000000, + .owner = THIS_MODULE, +}; + +static void cpufreq_interactive_timer_resched( + struct cpufreq_interactive_cpuinfo *pcpu) +{ + unsigned long expires = jiffies + usecs_to_jiffies(timer_rate); + unsigned long flags; + + mod_timer_pinned(&pcpu->cpu_timer, expires); + if (timer_slack_val >= 0 && pcpu->target_freq > pcpu->policy->min) { + expires += usecs_to_jiffies(timer_slack_val); + mod_timer_pinned(&pcpu->cpu_slack_timer, expires); + } + + spin_lock_irqsave(&pcpu->load_lock, flags); + pcpu->time_in_idle = + get_cpu_idle_time_us(smp_processor_id(), + &pcpu->time_in_idle_timestamp); + pcpu->cputime_speedadj = 0; + pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp; + spin_unlock_irqrestore(&pcpu->load_lock, flags); +} + +static unsigned int freq_to_targetload(unsigned int freq) +{ + int i; + unsigned int ret; + unsigned long flags; + + spin_lock_irqsave(&target_loads_lock, flags); + + for (i = 0; i < ntarget_loads - 1 && freq >= target_loads[i+1]; i += 2) + ; + + ret = target_loads[i]; + spin_unlock_irqrestore(&target_loads_lock, flags); + return ret; +} + +/* + * If increasing frequencies never map to a lower target load then + * choose_freq() will find the minimum frequency that does not exceed its + * target load given the current load. + */ + +static unsigned int choose_freq( + struct cpufreq_interactive_cpuinfo *pcpu, unsigned int loadadjfreq) +{ + unsigned int freq = pcpu->policy->cur; + unsigned int prevfreq, freqmin, freqmax; + unsigned int tl; + int index; + + freqmin = 0; + freqmax = UINT_MAX; + + do { + prevfreq = freq; + tl = freq_to_targetload(freq); + + /* + * Find the lowest frequency where the computed load is less + * than or equal to the target load. + */ + + cpufreq_frequency_table_target( + pcpu->policy, pcpu->freq_table, loadadjfreq / tl, + CPUFREQ_RELATION_L, &index); + freq = pcpu->freq_table[index].frequency; + + if (freq > prevfreq) { + /* The previous frequency is too low. */ + freqmin = prevfreq; + + if (freq >= freqmax) { + /* + * Find the highest frequency that is less + * than freqmax. + */ + cpufreq_frequency_table_target( + pcpu->policy, pcpu->freq_table, + freqmax - 1, CPUFREQ_RELATION_H, + &index); + freq = pcpu->freq_table[index].frequency; + + if (freq == freqmin) { + /* + * The first frequency below freqmax + * has already been found to be too + * low. freqmax is the lowest speed + * we found that is fast enough. + */ + freq = freqmax; + break; + } + } + } else if (freq < prevfreq) { + /* The previous frequency is high enough. */ + freqmax = prevfreq; + + if (freq <= freqmin) { + /* + * Find the lowest frequency that is higher + * than freqmin. + */ + cpufreq_frequency_table_target( + pcpu->policy, pcpu->freq_table, + freqmin + 1, CPUFREQ_RELATION_L, + &index); + freq = pcpu->freq_table[index].frequency; + + /* + * If freqmax is the first frequency above + * freqmin then we have already found that + * this speed is fast enough. + */ + if (freq == freqmax) + break; + } + } + + /* If same frequency chosen as previous then done. */ + } while (freq != prevfreq); + + return freq; +} + +static u64 update_load(int cpu) +{ + struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu); + u64 now; + u64 now_idle; + unsigned int delta_idle; + unsigned int delta_time; + u64 active_time; + + now_idle = get_cpu_idle_time_us(cpu, &now); + delta_idle = (unsigned int)(now_idle - pcpu->time_in_idle); + delta_time = (unsigned int)(now - pcpu->time_in_idle_timestamp); + active_time = delta_time - delta_idle; + pcpu->cputime_speedadj += active_time * pcpu->policy->cur; + + pcpu->time_in_idle = now_idle; + pcpu->time_in_idle_timestamp = now; + return now; +} + +static void cpufreq_interactive_timer(unsigned long data) +{ + u64 now; + unsigned int delta_time; + u64 cputime_speedadj; + int cpu_load; + struct cpufreq_interactive_cpuinfo *pcpu = + &per_cpu(cpuinfo, data); + unsigned int new_freq; + unsigned int loadadjfreq; + unsigned int index; + unsigned long flags; + bool boosted; + + if (!down_read_trylock(&pcpu->enable_sem)) + return; + if (!pcpu->governor_enabled) + goto exit; + + spin_lock_irqsave(&pcpu->load_lock, flags); + now = update_load(data); + delta_time = (unsigned int)(now - pcpu->cputime_speedadj_timestamp); + cputime_speedadj = pcpu->cputime_speedadj; + spin_unlock_irqrestore(&pcpu->load_lock, flags); + + if (WARN_ON_ONCE(!delta_time)) + goto rearm; + + do_div(cputime_speedadj, delta_time); + loadadjfreq = (unsigned int)cputime_speedadj * 100; + cpu_load = loadadjfreq / pcpu->target_freq; + boosted = boost_val || now < boostpulse_endtime; + + if (cpu_load >= go_hispeed_load || boosted) { + if (pcpu->target_freq < hispeed_freq) { + new_freq = hispeed_freq; + } else { + new_freq = choose_freq(pcpu, loadadjfreq); + + if (new_freq < hispeed_freq) + new_freq = hispeed_freq; + } + } else { + new_freq = choose_freq(pcpu, loadadjfreq); + } + + if (pcpu->target_freq >= hispeed_freq && + new_freq > pcpu->target_freq && + now - pcpu->hispeed_validate_time < above_hispeed_delay_val) { + trace_cpufreq_interactive_notyet( + data, cpu_load, pcpu->target_freq, + pcpu->policy->cur, new_freq); + goto rearm; + } + + pcpu->hispeed_validate_time = now; + + if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, + new_freq, CPUFREQ_RELATION_L, + &index)) { + pr_warn_once("timer %d: cpufreq_frequency_table_target error\n", + (int) data); + goto rearm; + } + + new_freq = pcpu->freq_table[index].frequency; + + /* + * Do not scale below floor_freq unless we have been at or above the + * floor frequency for the minimum sample time since last validated. + */ + if (new_freq < pcpu->floor_freq) { + if (now - pcpu->floor_validate_time < min_sample_time) { + trace_cpufreq_interactive_notyet( + data, cpu_load, pcpu->target_freq, + pcpu->policy->cur, new_freq); + goto rearm; + } + } + + /* + * Update the timestamp for checking whether speed has been held at + * or above the selected frequency for a minimum of min_sample_time, + * if not boosted to hispeed_freq. If boosted to hispeed_freq then we + * allow the speed to drop as soon as the boostpulse duration expires + * (or the indefinite boost is turned off). + */ + + if (!boosted || new_freq > hispeed_freq) { + pcpu->floor_freq = new_freq; + pcpu->floor_validate_time = now; + } + + if (pcpu->target_freq == new_freq) { + trace_cpufreq_interactive_already( + data, cpu_load, pcpu->target_freq, + pcpu->policy->cur, new_freq); + goto rearm_if_notmax; + } + + trace_cpufreq_interactive_target(data, cpu_load, pcpu->target_freq, + pcpu->policy->cur, new_freq); + + pcpu->target_freq = new_freq; + spin_lock_irqsave(&speedchange_cpumask_lock, flags); + cpumask_set_cpu(data, &speedchange_cpumask); + spin_unlock_irqrestore(&speedchange_cpumask_lock, flags); + wake_up_process(speedchange_task); + +rearm_if_notmax: + /* + * Already set max speed and don't see a need to change that, + * wait until next idle to re-evaluate, don't need timer. + */ + if (pcpu->target_freq == pcpu->policy->max) + goto exit; + +rearm: + if (!timer_pending(&pcpu->cpu_timer)) + cpufreq_interactive_timer_resched(pcpu); + +exit: + up_read(&pcpu->enable_sem); + return; +} + +static void cpufreq_interactive_idle_start(void) +{ + struct cpufreq_interactive_cpuinfo *pcpu = + &per_cpu(cpuinfo, smp_processor_id()); + int pending; + + if (!down_read_trylock(&pcpu->enable_sem)) + return; + if (!pcpu->governor_enabled) { + up_read(&pcpu->enable_sem); + return; + } + + pending = timer_pending(&pcpu->cpu_timer); + + if (pcpu->target_freq != pcpu->policy->min) { + /* + * Entering idle while not at lowest speed. On some + * platforms this can hold the other CPU(s) at that speed + * even though the CPU is idle. Set a timer to re-evaluate + * speed so this idle CPU doesn't hold the other CPUs above + * min indefinitely. This should probably be a quirk of + * the CPUFreq driver. + */ + if (!pending) + cpufreq_interactive_timer_resched(pcpu); + } + + up_read(&pcpu->enable_sem); +} + +static void cpufreq_interactive_idle_end(void) +{ + struct cpufreq_interactive_cpuinfo *pcpu = + &per_cpu(cpuinfo, smp_processor_id()); + + if (!down_read_trylock(&pcpu->enable_sem)) + return; + if (!pcpu->governor_enabled) { + up_read(&pcpu->enable_sem); + return; + } + + /* Arm the timer for 1-2 ticks later if not already. */ + if (!timer_pending(&pcpu->cpu_timer)) { + cpufreq_interactive_timer_resched(pcpu); + } else if (time_after_eq(jiffies, pcpu->cpu_timer.expires)) { + del_timer(&pcpu->cpu_timer); + del_timer(&pcpu->cpu_slack_timer); + cpufreq_interactive_timer(smp_processor_id()); + } + + up_read(&pcpu->enable_sem); +} + +static int cpufreq_interactive_speedchange_task(void *data) +{ + unsigned int cpu; + cpumask_t tmp_mask; + unsigned long flags; + struct cpufreq_interactive_cpuinfo *pcpu; + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&speedchange_cpumask_lock, flags); + + if (cpumask_empty(&speedchange_cpumask)) { + spin_unlock_irqrestore(&speedchange_cpumask_lock, + flags); + schedule(); + + if (kthread_should_stop()) + break; + + spin_lock_irqsave(&speedchange_cpumask_lock, flags); + } + + set_current_state(TASK_RUNNING); + tmp_mask = speedchange_cpumask; + cpumask_clear(&speedchange_cpumask); + spin_unlock_irqrestore(&speedchange_cpumask_lock, flags); + + for_each_cpu(cpu, &tmp_mask) { + unsigned int j; + unsigned int max_freq = 0; + + pcpu = &per_cpu(cpuinfo, cpu); + if (!down_read_trylock(&pcpu->enable_sem)) + continue; + if (!pcpu->governor_enabled) { + up_read(&pcpu->enable_sem); + continue; + } + + for_each_cpu(j, pcpu->policy->cpus) { + struct cpufreq_interactive_cpuinfo *pjcpu = + &per_cpu(cpuinfo, j); + + if (pjcpu->target_freq > max_freq) + max_freq = pjcpu->target_freq; + } + + if (max_freq != pcpu->policy->cur) + __cpufreq_driver_target(pcpu->policy, + max_freq, + CPUFREQ_RELATION_H); + trace_cpufreq_interactive_setspeed(cpu, + pcpu->target_freq, + pcpu->policy->cur); + + up_read(&pcpu->enable_sem); + } + } + + return 0; +} + +static void cpufreq_interactive_boost(void) +{ + int i; + int anyboost = 0; + unsigned long flags; + struct cpufreq_interactive_cpuinfo *pcpu; + + spin_lock_irqsave(&speedchange_cpumask_lock, flags); + + for_each_online_cpu(i) { + pcpu = &per_cpu(cpuinfo, i); + + if (pcpu->target_freq < hispeed_freq) { + pcpu->target_freq = hispeed_freq; + cpumask_set_cpu(i, &speedchange_cpumask); + pcpu->hispeed_validate_time = + ktime_to_us(ktime_get()); + anyboost = 1; + } + + /* + * Set floor freq and (re)start timer for when last + * validated. + */ + + pcpu->floor_freq = hispeed_freq; + pcpu->floor_validate_time = ktime_to_us(ktime_get()); + } + + spin_unlock_irqrestore(&speedchange_cpumask_lock, flags); + + if (anyboost) + wake_up_process(speedchange_task); +} + +static int cpufreq_interactive_notifier( + struct notifier_block *nb, unsigned long val, void *data) +{ + struct cpufreq_freqs *freq = data; + struct cpufreq_interactive_cpuinfo *pcpu; + int cpu; + unsigned long flags; + + if (val == CPUFREQ_POSTCHANGE) { + pcpu = &per_cpu(cpuinfo, freq->cpu); + if (!down_read_trylock(&pcpu->enable_sem)) + return 0; + if (!pcpu->governor_enabled) { + up_read(&pcpu->enable_sem); + return 0; + } + + for_each_cpu(cpu, pcpu->policy->cpus) { + struct cpufreq_interactive_cpuinfo *pjcpu = + &per_cpu(cpuinfo, cpu); + spin_lock_irqsave(&pjcpu->load_lock, flags); + update_load(cpu); + spin_unlock_irqrestore(&pjcpu->load_lock, flags); + } + + up_read(&pcpu->enable_sem); + } + return 0; +} + +static struct notifier_block cpufreq_notifier_block = { + .notifier_call = cpufreq_interactive_notifier, +}; + +static ssize_t show_target_loads( + struct kobject *kobj, struct attribute *attr, char *buf) +{ + int i; + ssize_t ret = 0; + unsigned long flags; + + spin_lock_irqsave(&target_loads_lock, flags); + + for (i = 0; i < ntarget_loads; i++) + ret += sprintf(buf + ret, "%u%s", target_loads[i], + i & 0x1 ? ":" : " "); + + ret += sprintf(buf + ret, "\n"); + spin_unlock_irqrestore(&target_loads_lock, flags); + return ret; +} + +static ssize_t store_target_loads( + struct kobject *kobj, struct attribute *attr, const char *buf, + size_t count) +{ + int ret; + const char *cp; + unsigned int *new_target_loads = NULL; + int ntokens = 1; + int i; + unsigned long flags; + + cp = buf; + while ((cp = strpbrk(cp + 1, " :"))) + ntokens++; + + if (!(ntokens & 0x1)) + goto err_inval; + + new_target_loads = kmalloc(ntokens * sizeof(unsigned int), GFP_KERNEL); + if (!new_target_loads) { + ret = -ENOMEM; + goto err; + } + + cp = buf; + i = 0; + while (i < ntokens) { + if (sscanf(cp, "%u", &new_target_loads[i++]) != 1) + goto err_inval; + + cp = strpbrk(cp, " :"); + if (!cp) + break; + cp++; + } + + if (i != ntokens) + goto err_inval; + + spin_lock_irqsave(&target_loads_lock, flags); + if (target_loads != default_target_loads) + kfree(target_loads); + target_loads = new_target_loads; + ntarget_loads = ntokens; + spin_unlock_irqrestore(&target_loads_lock, flags); + return count; + +err_inval: + ret = -EINVAL; +err: + kfree(new_target_loads); + return ret; +} + +static struct global_attr target_loads_attr = + __ATTR(target_loads, S_IRUGO | S_IWUSR, + show_target_loads, store_target_loads); + +static ssize_t show_hispeed_freq(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", hispeed_freq); +} + +static ssize_t store_hispeed_freq(struct kobject *kobj, + struct attribute *attr, const char *buf, + size_t count) +{ + int ret; + long unsigned int val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + hispeed_freq = val; + return count; +} + +static struct global_attr hispeed_freq_attr = __ATTR(hispeed_freq, 0644, + show_hispeed_freq, store_hispeed_freq); + + +static ssize_t show_go_hispeed_load(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", go_hispeed_load); +} + +static ssize_t store_go_hispeed_load(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + go_hispeed_load = val; + return count; +} + +static struct global_attr go_hispeed_load_attr = __ATTR(go_hispeed_load, 0644, + show_go_hispeed_load, store_go_hispeed_load); + +static ssize_t show_min_sample_time(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", min_sample_time); +} + +static ssize_t store_min_sample_time(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + min_sample_time = val; + return count; +} + +static struct global_attr min_sample_time_attr = __ATTR(min_sample_time, 0644, + show_min_sample_time, store_min_sample_time); + +static ssize_t show_above_hispeed_delay(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", above_hispeed_delay_val); +} + +static ssize_t store_above_hispeed_delay(struct kobject *kobj, + struct attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + above_hispeed_delay_val = val; + return count; +} + +define_one_global_rw(above_hispeed_delay); + +static ssize_t show_timer_rate(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", timer_rate); +} + +static ssize_t store_timer_rate(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + timer_rate = val; + return count; +} + +static struct global_attr timer_rate_attr = __ATTR(timer_rate, 0644, + show_timer_rate, store_timer_rate); + +static ssize_t show_timer_slack( + struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", timer_slack_val); +} + +static ssize_t store_timer_slack( + struct kobject *kobj, struct attribute *attr, const char *buf, + size_t count) +{ + int ret; + unsigned long val; + + ret = kstrtol(buf, 10, &val); + if (ret < 0) + return ret; + + timer_slack_val = val; + return count; +} + +define_one_global_rw(timer_slack); + +static ssize_t show_boost(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", boost_val); +} + +static ssize_t store_boost(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + + boost_val = val; + + if (boost_val) { + trace_cpufreq_interactive_boost("on"); + cpufreq_interactive_boost(); + } else { + trace_cpufreq_interactive_unboost("off"); + } + + return count; +} + +define_one_global_rw(boost); + +static ssize_t store_boostpulse(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + + boostpulse_endtime = ktime_to_us(ktime_get()) + boostpulse_duration_val; + trace_cpufreq_interactive_boost("pulse"); + cpufreq_interactive_boost(); + return count; +} + +static struct global_attr boostpulse = + __ATTR(boostpulse, 0200, NULL, store_boostpulse); + +static ssize_t show_boostpulse_duration( + struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", boostpulse_duration_val); +} + +static ssize_t store_boostpulse_duration( + struct kobject *kobj, struct attribute *attr, const char *buf, + size_t count) +{ + int ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + + boostpulse_duration_val = val; + return count; +} + +define_one_global_rw(boostpulse_duration); + +static struct attribute *interactive_attributes[] = { + &target_loads_attr.attr, + &hispeed_freq_attr.attr, + &go_hispeed_load_attr.attr, + &above_hispeed_delay.attr, + &min_sample_time_attr.attr, + &timer_rate_attr.attr, + &timer_slack.attr, + &boost.attr, + &boostpulse.attr, + &boostpulse_duration.attr, + NULL, +}; + +static struct attribute_group interactive_attr_group = { + .attrs = interactive_attributes, + .name = "interactive", +}; + +static int cpufreq_interactive_idle_notifier(struct notifier_block *nb, + unsigned long val, + void *data) +{ + switch (val) { + case IDLE_START: + cpufreq_interactive_idle_start(); + break; + case IDLE_END: + cpufreq_interactive_idle_end(); + break; + } + + return 0; +} + +static struct notifier_block cpufreq_interactive_idle_nb = { + .notifier_call = cpufreq_interactive_idle_notifier, +}; + +static int cpufreq_governor_interactive(struct cpufreq_policy *policy, + unsigned int event) +{ + int rc; + unsigned int j; + struct cpufreq_interactive_cpuinfo *pcpu; + struct cpufreq_frequency_table *freq_table; + + switch (event) { + case CPUFREQ_GOV_START: + if (!cpu_online(policy->cpu)) + return -EINVAL; + + mutex_lock(&gov_lock); + + freq_table = + cpufreq_frequency_get_table(policy->cpu); + if (!hispeed_freq) + hispeed_freq = policy->max; + + for_each_cpu(j, policy->cpus) { + unsigned long expires; + + pcpu = &per_cpu(cpuinfo, j); + pcpu->policy = policy; + pcpu->target_freq = policy->cur; + pcpu->freq_table = freq_table; + pcpu->floor_freq = pcpu->target_freq; + pcpu->floor_validate_time = + ktime_to_us(ktime_get()); + pcpu->hispeed_validate_time = + pcpu->floor_validate_time; + down_write(&pcpu->enable_sem); + expires = jiffies + usecs_to_jiffies(timer_rate); + pcpu->cpu_timer.expires = expires; + add_timer_on(&pcpu->cpu_timer, j); + if (timer_slack_val >= 0) { + expires += usecs_to_jiffies(timer_slack_val); + pcpu->cpu_slack_timer.expires = expires; + add_timer_on(&pcpu->cpu_slack_timer, j); + } + pcpu->governor_enabled = 1; + up_write(&pcpu->enable_sem); + } + + /* + * Do not register the idle hook and create sysfs + * entries if we have already done so. + */ + if (++active_count > 1) { + mutex_unlock(&gov_lock); + return 0; + } + + rc = sysfs_create_group(cpufreq_global_kobject, + &interactive_attr_group); + if (rc) { + mutex_unlock(&gov_lock); + return rc; + } + + idle_notifier_register(&cpufreq_interactive_idle_nb); + cpufreq_register_notifier( + &cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); + mutex_unlock(&gov_lock); + break; + + case CPUFREQ_GOV_STOP: + mutex_lock(&gov_lock); + for_each_cpu(j, policy->cpus) { + pcpu = &per_cpu(cpuinfo, j); + down_write(&pcpu->enable_sem); + pcpu->governor_enabled = 0; + del_timer_sync(&pcpu->cpu_timer); + del_timer_sync(&pcpu->cpu_slack_timer); + up_write(&pcpu->enable_sem); + } + + if (--active_count > 0) { + mutex_unlock(&gov_lock); + return 0; + } + + cpufreq_unregister_notifier( + &cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); + idle_notifier_unregister(&cpufreq_interactive_idle_nb); + sysfs_remove_group(cpufreq_global_kobject, + &interactive_attr_group); + mutex_unlock(&gov_lock); + + break; + + case CPUFREQ_GOV_LIMITS: + if (policy->max < policy->cur) + __cpufreq_driver_target(policy, + policy->max, CPUFREQ_RELATION_H); + else if (policy->min > policy->cur) + __cpufreq_driver_target(policy, + policy->min, CPUFREQ_RELATION_L); + break; + } + return 0; +} + +static void cpufreq_interactive_nop_timer(unsigned long data) +{ +} + +static int __init cpufreq_interactive_init(void) +{ + unsigned int i; + struct cpufreq_interactive_cpuinfo *pcpu; + struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; + + /* Initalize per-cpu timers */ + for_each_possible_cpu(i) { + pcpu = &per_cpu(cpuinfo, i); + init_timer_deferrable(&pcpu->cpu_timer); + pcpu->cpu_timer.function = cpufreq_interactive_timer; + pcpu->cpu_timer.data = i; + init_timer(&pcpu->cpu_slack_timer); + pcpu->cpu_slack_timer.function = cpufreq_interactive_nop_timer; + spin_lock_init(&pcpu->load_lock); + init_rwsem(&pcpu->enable_sem); + } + + spin_lock_init(&target_loads_lock); + spin_lock_init(&speedchange_cpumask_lock); + mutex_init(&gov_lock); + speedchange_task = + kthread_create(cpufreq_interactive_speedchange_task, NULL, + "cfinteractive"); + if (IS_ERR(speedchange_task)) + return PTR_ERR(speedchange_task); + + sched_setscheduler_nocheck(speedchange_task, SCHED_FIFO, ¶m); + get_task_struct(speedchange_task); + + /* NB: wake up so the thread does not look hung to the freezer */ + wake_up_process(speedchange_task); + + return cpufreq_register_governor(&cpufreq_gov_interactive); +} + +#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE +fs_initcall(cpufreq_interactive_init); +#else +module_init(cpufreq_interactive_init); +#endif + +static void __exit cpufreq_interactive_exit(void) +{ + cpufreq_unregister_governor(&cpufreq_gov_interactive); + kthread_stop(speedchange_task); + put_task_struct(speedchange_task); +} + +module_exit(cpufreq_interactive_exit); + +MODULE_AUTHOR("Mike Chan "); +MODULE_DESCRIPTION("'cpufreq_interactive' - A cpufreq governor for " + "Latency sensitive workloads"); +MODULE_LICENSE("GPL"); diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index b40ee140..72f0093a 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -316,6 +316,27 @@ static int cpufreq_stat_notifier_trans(struct notifier_block *nb, return 0; } +static int cpufreq_stats_create_table_cpu(unsigned int cpu) +{ + struct cpufreq_policy *policy; + struct cpufreq_frequency_table *table; + int ret = -ENODEV; + + policy = cpufreq_cpu_get(cpu); + if (!policy) + return -ENODEV; + + table = cpufreq_frequency_get_table(cpu); + if (!table) + goto out; + + ret = cpufreq_stats_create_table(policy, table); + +out: + cpufreq_cpu_put(policy); + return ret; +} + static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) @@ -334,6 +355,10 @@ static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb, case CPU_DEAD_FROZEN: cpufreq_stats_free_table(cpu); break; + case CPU_DOWN_FAILED: + case CPU_DOWN_FAILED_FROZEN: + cpufreq_stats_create_table_cpu(cpu); + break; } return NOTIFY_OK; } diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig index 78a666d1..a76b689e 100644 --- a/drivers/cpuidle/Kconfig +++ b/drivers/cpuidle/Kconfig @@ -18,3 +18,6 @@ config CPU_IDLE_GOV_MENU bool depends on CPU_IDLE && NO_HZ default y + +config ARCH_NEEDS_CPU_IDLE_COUPLED + def_bool n diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index 5634f883..38c8f69f 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -3,3 +3,4 @@ # obj-y += cpuidle.o driver.o governor.o sysfs.o governors/ +obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o diff --git a/drivers/cpuidle/coupled.c b/drivers/cpuidle/coupled.c new file mode 100644 index 00000000..e95c72e9 --- /dev/null +++ b/drivers/cpuidle/coupled.c @@ -0,0 +1,727 @@ +/* + * coupled.c - helper functions to enter the same idle state on multiple cpus + * + * Copyright (c) 2011 Google, Inc. + * + * Author: Colin Cross + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "cpuidle.h" + +/** + * DOC: Coupled cpuidle states + * + * On some ARM SMP SoCs (OMAP4460, Tegra 2, and probably more), the + * cpus cannot be independently powered down, either due to + * sequencing restrictions (on Tegra 2, cpu 0 must be the last to + * power down), or due to HW bugs (on OMAP4460, a cpu powering up + * will corrupt the gic state unless the other cpu runs a work + * around). Each cpu has a power state that it can enter without + * coordinating with the other cpu (usually Wait For Interrupt, or + * WFI), and one or more "coupled" power states that affect blocks + * shared between the cpus (L2 cache, interrupt controller, and + * sometimes the whole SoC). Entering a coupled power state must + * be tightly controlled on both cpus. + * + * This file implements a solution, where each cpu will wait in the + * WFI state until all cpus are ready to enter a coupled state, at + * which point the coupled state function will be called on all + * cpus at approximately the same time. + * + * Once all cpus are ready to enter idle, they are woken by an smp + * cross call. At this point, there is a chance that one of the + * cpus will find work to do, and choose not to enter idle. A + * final pass is needed to guarantee that all cpus will call the + * power state enter function at the same time. During this pass, + * each cpu will increment the ready counter, and continue once the + * ready counter matches the number of online coupled cpus. If any + * cpu exits idle, the other cpus will decrement their counter and + * retry. + * + * requested_state stores the deepest coupled idle state each cpu + * is ready for. It is assumed that the states are indexed from + * shallowest (highest power, lowest exit latency) to deepest + * (lowest power, highest exit latency). The requested_state + * variable is not locked. It is only written from the cpu that + * it stores (or by the on/offlining cpu if that cpu is offline), + * and only read after all the cpus are ready for the coupled idle + * state are are no longer updating it. + * + * Three atomic counters are used. alive_count tracks the number + * of cpus in the coupled set that are currently or soon will be + * online. waiting_count tracks the number of cpus that are in + * the waiting loop, in the ready loop, or in the coupled idle state. + * ready_count tracks the number of cpus that are in the ready loop + * or in the coupled idle state. + * + * To use coupled cpuidle states, a cpuidle driver must: + * + * Set struct cpuidle_device.coupled_cpus to the mask of all + * coupled cpus, usually the same as cpu_possible_mask if all cpus + * are part of the same cluster. The coupled_cpus mask must be + * set in the struct cpuidle_device for each cpu. + * + * Set struct cpuidle_device.safe_state to a state that is not a + * coupled state. This is usually WFI. + * + * Set CPUIDLE_FLAG_COUPLED in struct cpuidle_state.flags for each + * state that affects multiple cpus. + * + * Provide a struct cpuidle_state.enter function for each state + * that affects multiple cpus. This function is guaranteed to be + * called on all cpus at approximately the same time. The driver + * should ensure that the cpus all abort together if any cpu tries + * to abort once the function is called. The function should return + * with interrupts still disabled. + */ + +/** + * struct cpuidle_coupled - data for set of cpus that share a coupled idle state + * @coupled_cpus: mask of cpus that are part of the coupled set + * @requested_state: array of requested states for cpus in the coupled set + * @ready_waiting_counts: combined count of cpus in ready or waiting loops + * @online_count: count of cpus that are online + * @refcnt: reference count of cpuidle devices that are using this struct + * @prevent: flag to prevent coupled idle while a cpu is hotplugging + */ +struct cpuidle_coupled { + cpumask_t coupled_cpus; + int requested_state[NR_CPUS]; + atomic_t ready_waiting_counts; + int online_count; + int refcnt; + int prevent; +}; + +#define WAITING_BITS 16 +#define MAX_WAITING_CPUS (1 << WAITING_BITS) +#define WAITING_MASK (MAX_WAITING_CPUS - 1) +#define READY_MASK (~WAITING_MASK) + +#define CPUIDLE_COUPLED_NOT_IDLE (-1) + +static DEFINE_MUTEX(cpuidle_coupled_lock); +static DEFINE_PER_CPU(struct call_single_data, cpuidle_coupled_poke_cb); + +/* + * The cpuidle_coupled_poked_mask mask is used to avoid calling + * __smp_call_function_single with the per cpu call_single_data struct already + * in use. This prevents a deadlock where two cpus are waiting for each others + * call_single_data struct to be available + */ +static cpumask_t cpuidle_coupled_poked_mask; + +/** + * cpuidle_coupled_parallel_barrier - synchronize all online coupled cpus + * @dev: cpuidle_device of the calling cpu + * @a: atomic variable to hold the barrier + * + * No caller to this function will return from this function until all online + * cpus in the same coupled group have called this function. Once any caller + * has returned from this function, the barrier is immediately available for + * reuse. + * + * The atomic variable a must be initialized to 0 before any cpu calls + * this function, will be reset to 0 before any cpu returns from this function. + * + * Must only be called from within a coupled idle state handler + * (state.enter when state.flags has CPUIDLE_FLAG_COUPLED set). + * + * Provides full smp barrier semantics before and after calling. + */ +void cpuidle_coupled_parallel_barrier(struct cpuidle_device *dev, atomic_t *a) +{ + int n = dev->coupled->online_count; + + smp_mb__before_atomic_inc(); + atomic_inc(a); + + while (atomic_read(a) < n) + cpu_relax(); + + if (atomic_inc_return(a) == n * 2) { + atomic_set(a, 0); + return; + } + + while (atomic_read(a) > n) + cpu_relax(); +} + +/** + * cpuidle_state_is_coupled - check if a state is part of a coupled set + * @dev: struct cpuidle_device for the current cpu + * @drv: struct cpuidle_driver for the platform + * @state: index of the target state in drv->states + * + * Returns true if the target state is coupled with cpus besides this one + */ +bool cpuidle_state_is_coupled(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int state) +{ + return drv->states[state].flags & CPUIDLE_FLAG_COUPLED; +} + +/** + * cpuidle_coupled_set_ready - mark a cpu as ready + * @coupled: the struct coupled that contains the current cpu + */ +static inline void cpuidle_coupled_set_ready(struct cpuidle_coupled *coupled) +{ + atomic_add(MAX_WAITING_CPUS, &coupled->ready_waiting_counts); +} + +/** + * cpuidle_coupled_set_not_ready - mark a cpu as not ready + * @coupled: the struct coupled that contains the current cpu + * + * Decrements the ready counter, unless the ready (and thus the waiting) counter + * is equal to the number of online cpus. Prevents a race where one cpu + * decrements the waiting counter and then re-increments it just before another + * cpu has decremented its ready counter, leading to the ready counter going + * down from the number of online cpus without going through the coupled idle + * state. + * + * Returns 0 if the counter was decremented successfully, -EINVAL if the ready + * counter was equal to the number of online cpus. + */ +static +inline int cpuidle_coupled_set_not_ready(struct cpuidle_coupled *coupled) +{ + int all; + int ret; + + all = coupled->online_count || (coupled->online_count << WAITING_BITS); + ret = atomic_add_unless(&coupled->ready_waiting_counts, + -MAX_WAITING_CPUS, all); + + return ret ? 0 : -EINVAL; +} + +/** + * cpuidle_coupled_no_cpus_ready - check if no cpus in a coupled set are ready + * @coupled: the struct coupled that contains the current cpu + * + * Returns true if all of the cpus in a coupled set are out of the ready loop. + */ +static inline int cpuidle_coupled_no_cpus_ready(struct cpuidle_coupled *coupled) +{ + int r = atomic_read(&coupled->ready_waiting_counts) >> WAITING_BITS; + return r == 0; +} + +/** + * cpuidle_coupled_cpus_ready - check if all cpus in a coupled set are ready + * @coupled: the struct coupled that contains the current cpu + * + * Returns true if all cpus coupled to this target state are in the ready loop + */ +static inline bool cpuidle_coupled_cpus_ready(struct cpuidle_coupled *coupled) +{ + int r = atomic_read(&coupled->ready_waiting_counts) >> WAITING_BITS; + return r == coupled->online_count; +} + +/** + * cpuidle_coupled_cpus_waiting - check if all cpus in a coupled set are waiting + * @coupled: the struct coupled that contains the current cpu + * + * Returns true if all cpus coupled to this target state are in the wait loop + */ +static inline bool cpuidle_coupled_cpus_waiting(struct cpuidle_coupled *coupled) +{ + int w = atomic_read(&coupled->ready_waiting_counts) & WAITING_MASK; + return w == coupled->online_count; +} + +/** + * cpuidle_coupled_no_cpus_waiting - check if no cpus in coupled set are waiting + * @coupled: the struct coupled that contains the current cpu + * + * Returns true if all of the cpus in a coupled set are out of the waiting loop. + */ +static inline int cpuidle_coupled_no_cpus_waiting(struct cpuidle_coupled *coupled) +{ + int w = atomic_read(&coupled->ready_waiting_counts) & WAITING_MASK; + return w == 0; +} + +/** + * cpuidle_coupled_get_state - determine the deepest idle state + * @dev: struct cpuidle_device for this cpu + * @coupled: the struct coupled that contains the current cpu + * + * Returns the deepest idle state that all coupled cpus can enter + */ +static inline int cpuidle_coupled_get_state(struct cpuidle_device *dev, + struct cpuidle_coupled *coupled) +{ + int i; + int state = INT_MAX; + + /* + * Read barrier ensures that read of requested_state is ordered after + * reads of ready_count. Matches the write barriers + * cpuidle_set_state_waiting. + */ + smp_rmb(); + + for_each_cpu_mask(i, coupled->coupled_cpus) + if (cpu_online(i) && coupled->requested_state[i] < state) + state = coupled->requested_state[i]; + + return state; +} + +static void cpuidle_coupled_poked(void *info) +{ + int cpu = (unsigned long)info; + cpumask_clear_cpu(cpu, &cpuidle_coupled_poked_mask); +} + +/** + * cpuidle_coupled_poke - wake up a cpu that may be waiting + * @cpu: target cpu + * + * Ensures that the target cpu exits it's waiting idle state (if it is in it) + * and will see updates to waiting_count before it re-enters it's waiting idle + * state. + * + * If cpuidle_coupled_poked_mask is already set for the target cpu, that cpu + * either has or will soon have a pending IPI that will wake it out of idle, + * or it is currently processing the IPI and is not in idle. + */ +static void cpuidle_coupled_poke(int cpu) +{ + struct call_single_data *csd = &per_cpu(cpuidle_coupled_poke_cb, cpu); + + if (!cpumask_test_and_set_cpu(cpu, &cpuidle_coupled_poked_mask)) + __smp_call_function_single(cpu, csd, 0); +} + +/** + * cpuidle_coupled_poke_others - wake up all other cpus that may be waiting + * @dev: struct cpuidle_device for this cpu + * @coupled: the struct coupled that contains the current cpu + * + * Calls cpuidle_coupled_poke on all other online cpus. + */ +static void cpuidle_coupled_poke_others(int this_cpu, + struct cpuidle_coupled *coupled) +{ + int cpu; + + for_each_cpu_mask(cpu, coupled->coupled_cpus) + if (cpu != this_cpu && cpu_online(cpu)) + cpuidle_coupled_poke(cpu); +} + +/** + * cpuidle_coupled_set_waiting - mark this cpu as in the wait loop + * @dev: struct cpuidle_device for this cpu + * @coupled: the struct coupled that contains the current cpu + * @next_state: the index in drv->states of the requested state for this cpu + * + * Updates the requested idle state for the specified cpuidle device, + * poking all coupled cpus out of idle if necessary to let them see the new + * state. + */ +static void cpuidle_coupled_set_waiting(int cpu, + struct cpuidle_coupled *coupled, int next_state) +{ + int w; + + coupled->requested_state[cpu] = next_state; + + /* + * If this is the last cpu to enter the waiting state, poke + * all the other cpus out of their waiting state so they can + * enter a deeper state. This can race with one of the cpus + * exiting the waiting state due to an interrupt and + * decrementing waiting_count, see comment below. + * + * The atomic_inc_return provides a write barrier to order the write + * to requested_state with the later write that increments ready_count. + */ + w = atomic_inc_return(&coupled->ready_waiting_counts) & WAITING_MASK; + if (w == coupled->online_count) + cpuidle_coupled_poke_others(cpu, coupled); +} + +/** + * cpuidle_coupled_set_not_waiting - mark this cpu as leaving the wait loop + * @dev: struct cpuidle_device for this cpu + * @coupled: the struct coupled that contains the current cpu + * + * Removes the requested idle state for the specified cpuidle device. + */ +static void cpuidle_coupled_set_not_waiting(int cpu, + struct cpuidle_coupled *coupled) +{ + /* + * Decrementing waiting count can race with incrementing it in + * cpuidle_coupled_set_waiting, but that's OK. Worst case, some + * cpus will increment ready_count and then spin until they + * notice that this cpu has cleared it's requested_state. + */ + atomic_dec(&coupled->ready_waiting_counts); + + coupled->requested_state[cpu] = CPUIDLE_COUPLED_NOT_IDLE; +} + +/** + * cpuidle_coupled_set_done - mark this cpu as leaving the ready loop + * @cpu: the current cpu + * @coupled: the struct coupled that contains the current cpu + * + * Marks this cpu as no longer in the ready and waiting loops. Decrements + * the waiting count first to prevent another cpu looping back in and seeing + * this cpu as waiting just before it exits idle. + */ +static void cpuidle_coupled_set_done(int cpu, struct cpuidle_coupled *coupled) +{ + cpuidle_coupled_set_not_waiting(cpu, coupled); + atomic_sub(MAX_WAITING_CPUS, &coupled->ready_waiting_counts); +} + +/** + * cpuidle_coupled_clear_pokes - spin until the poke interrupt is processed + * @cpu - this cpu + * + * Turns on interrupts and spins until any outstanding poke interrupts have + * been processed and the poke bit has been cleared. + * + * Other interrupts may also be processed while interrupts are enabled, so + * need_resched() must be tested after turning interrupts off again to make sure + * the interrupt didn't schedule work that should take the cpu out of idle. + * + * Returns 0 if need_resched was false, -EINTR if need_resched was true. + */ +static int cpuidle_coupled_clear_pokes(int cpu) +{ + local_irq_enable(); + while (cpumask_test_cpu(cpu, &cpuidle_coupled_poked_mask)) + cpu_relax(); + local_irq_disable(); + + return need_resched() ? -EINTR : 0; +} + +/** + * cpuidle_enter_state_coupled - attempt to enter a state with coupled cpus + * @dev: struct cpuidle_device for the current cpu + * @drv: struct cpuidle_driver for the platform + * @next_state: index of the requested state in drv->states + * + * Coordinate with coupled cpus to enter the target state. This is a two + * stage process. In the first stage, the cpus are operating independently, + * and may call into cpuidle_enter_state_coupled at completely different times. + * To save as much power as possible, the first cpus to call this function will + * go to an intermediate state (the cpuidle_device's safe state), and wait for + * all the other cpus to call this function. Once all coupled cpus are idle, + * the second stage will start. Each coupled cpu will spin until all cpus have + * guaranteed that they will call the target_state. + * + * This function must be called with interrupts disabled. It may enable + * interrupts while preparing for idle, and it will always return with + * interrupts enabled. + */ +int cpuidle_enter_state_coupled(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int next_state) +{ + int entered_state = -1; + struct cpuidle_coupled *coupled = dev->coupled; + + if (!coupled) + return -EINVAL; + + while (coupled->prevent) { + if (cpuidle_coupled_clear_pokes(dev->cpu)) { + local_irq_enable(); + return entered_state; + } + entered_state = cpuidle_enter_state(dev, drv, + dev->safe_state_index); + } + + /* Read barrier ensures online_count is read after prevent is cleared */ + smp_rmb(); + + cpuidle_coupled_set_waiting(dev->cpu, coupled, next_state); + +retry: + /* + * Wait for all coupled cpus to be idle, using the deepest state + * allowed for a single cpu. + */ + while (!cpuidle_coupled_cpus_waiting(coupled)) { + if (cpuidle_coupled_clear_pokes(dev->cpu)) { + cpuidle_coupled_set_not_waiting(dev->cpu, coupled); + goto out; + } + + if (coupled->prevent) { + cpuidle_coupled_set_not_waiting(dev->cpu, coupled); + goto out; + } + + entered_state = cpuidle_enter_state(dev, drv, + dev->safe_state_index); + } + + if (cpuidle_coupled_clear_pokes(dev->cpu)) { + cpuidle_coupled_set_not_waiting(dev->cpu, coupled); + goto out; + } + + /* + * All coupled cpus are probably idle. There is a small chance that + * one of the other cpus just became active. Increment the ready count, + * and spin until all coupled cpus have incremented the counter. Once a + * cpu has incremented the ready counter, it cannot abort idle and must + * spin until either all cpus have incremented the ready counter, or + * another cpu leaves idle and decrements the waiting counter. + */ + + cpuidle_coupled_set_ready(coupled); + while (!cpuidle_coupled_cpus_ready(coupled)) { + /* Check if any other cpus bailed out of idle. */ + if (!cpuidle_coupled_cpus_waiting(coupled)) + if (!cpuidle_coupled_set_not_ready(coupled)) + goto retry; + + cpu_relax(); + } + + /* all cpus have acked the coupled state */ + next_state = cpuidle_coupled_get_state(dev, coupled); + + entered_state = cpuidle_enter_state(dev, drv, next_state); + + cpuidle_coupled_set_done(dev->cpu, coupled); + +out: + /* + * Normal cpuidle states are expected to return with irqs enabled. + * That leads to an inefficiency where a cpu receiving an interrupt + * that brings it out of idle will process that interrupt before + * exiting the idle enter function and decrementing ready_count. All + * other cpus will need to spin waiting for the cpu that is processing + * the interrupt. If the driver returns with interrupts disabled, + * all other cpus will loop back into the safe idle state instead of + * spinning, saving power. + * + * Calling local_irq_enable here allows coupled states to return with + * interrupts disabled, but won't cause problems for drivers that + * exit with interrupts enabled. + */ + local_irq_enable(); + + /* + * Wait until all coupled cpus have exited idle. There is no risk that + * a cpu exits and re-enters the ready state because this cpu has + * already decremented its waiting_count. + */ + while (!cpuidle_coupled_no_cpus_ready(coupled)) + cpu_relax(); + + return entered_state; +} + +static void cpuidle_coupled_update_online_cpus(struct cpuidle_coupled *coupled) +{ + cpumask_t cpus; + cpumask_and(&cpus, cpu_online_mask, &coupled->coupled_cpus); + coupled->online_count = cpumask_weight(&cpus); +} + +/** + * cpuidle_coupled_register_device - register a coupled cpuidle device + * @dev: struct cpuidle_device for the current cpu + * + * Called from cpuidle_register_device to handle coupled idle init. Finds the + * cpuidle_coupled struct for this set of coupled cpus, or creates one if none + * exists yet. + */ +int cpuidle_coupled_register_device(struct cpuidle_device *dev) +{ + int cpu; + struct cpuidle_device *other_dev; + struct call_single_data *csd; + struct cpuidle_coupled *coupled; + + if (cpumask_empty(&dev->coupled_cpus)) + return 0; + + for_each_cpu_mask(cpu, dev->coupled_cpus) { + other_dev = per_cpu(cpuidle_devices, cpu); + if (other_dev && other_dev->coupled) { + coupled = other_dev->coupled; + goto have_coupled; + } + } + + /* No existing coupled info found, create a new one */ + coupled = kzalloc(sizeof(struct cpuidle_coupled), GFP_KERNEL); + if (!coupled) + return -ENOMEM; + + coupled->coupled_cpus = dev->coupled_cpus; + +have_coupled: + dev->coupled = coupled; + if (WARN_ON(!cpumask_equal(&dev->coupled_cpus, &coupled->coupled_cpus))) + coupled->prevent++; + + cpuidle_coupled_update_online_cpus(coupled); + + coupled->refcnt++; + + csd = &per_cpu(cpuidle_coupled_poke_cb, dev->cpu); + csd->func = cpuidle_coupled_poked; + csd->info = (void *)(unsigned long)dev->cpu; + + return 0; +} + +/** + * cpuidle_coupled_unregister_device - unregister a coupled cpuidle device + * @dev: struct cpuidle_device for the current cpu + * + * Called from cpuidle_unregister_device to tear down coupled idle. Removes the + * cpu from the coupled idle set, and frees the cpuidle_coupled_info struct if + * this was the last cpu in the set. + */ +void cpuidle_coupled_unregister_device(struct cpuidle_device *dev) +{ + struct cpuidle_coupled *coupled = dev->coupled; + + if (cpumask_empty(&dev->coupled_cpus)) + return; + + if (--coupled->refcnt) + kfree(coupled); + dev->coupled = NULL; +} + +/** + * cpuidle_coupled_prevent_idle - prevent cpus from entering a coupled state + * @coupled: the struct coupled that contains the cpu that is changing state + * + * Disables coupled cpuidle on a coupled set of cpus. Used to ensure that + * cpu_online_mask doesn't change while cpus are coordinating coupled idle. + */ +static void cpuidle_coupled_prevent_idle(struct cpuidle_coupled *coupled) +{ + int cpu = get_cpu(); + + /* Force all cpus out of the waiting loop. */ + coupled->prevent++; + cpuidle_coupled_poke_others(cpu, coupled); + put_cpu(); + while (!cpuidle_coupled_no_cpus_waiting(coupled)) + cpu_relax(); +} + +/** + * cpuidle_coupled_allow_idle - allows cpus to enter a coupled state + * @coupled: the struct coupled that contains the cpu that is changing state + * + * Enables coupled cpuidle on a coupled set of cpus. Used to ensure that + * cpu_online_mask doesn't change while cpus are coordinating coupled idle. + */ +static void cpuidle_coupled_allow_idle(struct cpuidle_coupled *coupled) +{ + int cpu = get_cpu(); + + /* + * Write barrier ensures readers see the new online_count when they + * see prevent == false. + */ + smp_wmb(); + coupled->prevent--; + /* Force cpus out of the prevent loop. */ + cpuidle_coupled_poke_others(cpu, coupled); + put_cpu(); +} + +/** + * cpuidle_coupled_cpu_notify - notifier called during hotplug transitions + * @nb: notifier block + * @action: hotplug transition + * @hcpu: target cpu number + * + * Called when a cpu is brought on or offline using hotplug. Updates the + * coupled cpu set appropriately + */ +static int cpuidle_coupled_cpu_notify(struct notifier_block *nb, + unsigned long action, void *hcpu) +{ + int cpu = (unsigned long)hcpu; + struct cpuidle_device *dev; + + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_UP_PREPARE: + case CPU_DOWN_PREPARE: + case CPU_ONLINE: + case CPU_DEAD: + case CPU_UP_CANCELED: + case CPU_DOWN_FAILED: + break; + default: + return NOTIFY_OK; + } + + mutex_lock(&cpuidle_lock); + + dev = per_cpu(cpuidle_devices, cpu); + if (!dev->coupled) + goto out; + + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_UP_PREPARE: + case CPU_DOWN_PREPARE: + cpuidle_coupled_prevent_idle(dev->coupled); + break; + case CPU_ONLINE: + case CPU_DEAD: + cpuidle_coupled_update_online_cpus(dev->coupled); + /* Fall through */ + case CPU_UP_CANCELED: + case CPU_DOWN_FAILED: + cpuidle_coupled_allow_idle(dev->coupled); + break; + } + +out: + mutex_unlock(&cpuidle_lock); + return NOTIFY_OK; +} + +static struct notifier_block cpuidle_coupled_cpu_notifier = { + .notifier_call = cpuidle_coupled_cpu_notify, +}; + +static int __init cpuidle_coupled_init(void) +{ + return register_cpu_notifier(&cpuidle_coupled_cpu_notifier); +} +core_initcall(cpuidle_coupled_init); diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 2f0083a5..e81cfda2 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -102,6 +102,34 @@ int cpuidle_play_dead(void) return -ENODEV; } +/** + * cpuidle_enter_state - enter the state and update stats + * @dev: cpuidle device for this cpu + * @drv: cpuidle driver for this cpu + * @next_state: index into drv->states of the state to enter + */ +int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, + int next_state) +{ + int entered_state; + + entered_state = cpuidle_enter_ops(dev, drv, next_state); + + if (entered_state >= 0) { + /* Update cpuidle counters */ + /* This can be moved to within driver enter routine + * but that results in multiple copies of same code. + */ + dev->states_usage[entered_state].time += + (unsigned long long)dev->last_residency; + dev->states_usage[entered_state].usage++; + } else { + dev->last_residency = 0; + } + + return entered_state; +} + /** * cpuidle_idle_call - the main idle loop * @@ -143,23 +171,15 @@ int cpuidle_idle_call(void) trace_power_start_rcuidle(POWER_CSTATE, next_state, dev->cpu); trace_cpu_idle_rcuidle(next_state, dev->cpu); - entered_state = cpuidle_enter_ops(dev, drv, next_state); + if (cpuidle_state_is_coupled(dev, drv, next_state)) + entered_state = cpuidle_enter_state_coupled(dev, drv, + next_state); + else + entered_state = cpuidle_enter_state(dev, drv, next_state); trace_power_end_rcuidle(dev->cpu); trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); - if (entered_state >= 0) { - /* Update cpuidle counters */ - /* This can be moved to within driver enter routine - * but that results in multiple copies of same code. - */ - dev->states_usage[entered_state].time += - (unsigned long long)dev->last_residency; - dev->states_usage[entered_state].usage++; - } else { - dev->last_residency = 0; - } - /* give the governor an opportunity to reflect on the outcome */ if (cpuidle_curr_governor->reflect) cpuidle_curr_governor->reflect(dev, entered_state); @@ -387,13 +407,25 @@ static int __cpuidle_register_device(struct cpuidle_device *dev) per_cpu(cpuidle_devices, dev->cpu) = dev; list_add(&dev->device_list, &cpuidle_detected_devices); - if ((ret = cpuidle_add_sysfs(cpu_dev))) { - module_put(cpuidle_driver->owner); - return ret; - } + ret = cpuidle_add_sysfs(cpu_dev); + if (ret) + goto err_sysfs; + + ret = cpuidle_coupled_register_device(dev); + if (ret) + goto err_coupled; dev->registered = 1; return 0; + +err_coupled: + cpuidle_remove_sysfs(cpu_dev); + wait_for_completion(&dev->kobj_unregister); +err_sysfs: + list_del(&dev->device_list); + per_cpu(cpuidle_devices, dev->cpu) = NULL; + module_put(cpuidle_driver->owner); + return ret; } /** @@ -443,6 +475,8 @@ void cpuidle_unregister_device(struct cpuidle_device *dev) wait_for_completion(&dev->kobj_unregister); per_cpu(cpuidle_devices, dev->cpu) = NULL; + cpuidle_coupled_unregister_device(dev); + cpuidle_resume_and_unlock(); module_put(cpuidle_driver->owner); diff --git a/drivers/cpuidle/cpuidle.h b/drivers/cpuidle/cpuidle.h index 7db18668..76e7f696 100644 --- a/drivers/cpuidle/cpuidle.h +++ b/drivers/cpuidle/cpuidle.h @@ -14,6 +14,8 @@ extern struct list_head cpuidle_detected_devices; extern struct mutex cpuidle_lock; extern spinlock_t cpuidle_driver_lock; extern int cpuidle_disabled(void); +extern int cpuidle_enter_state(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int next_state); /* idle loop */ extern void cpuidle_install_idle_handler(void); @@ -30,4 +32,34 @@ extern void cpuidle_remove_state_sysfs(struct cpuidle_device *device); extern int cpuidle_add_sysfs(struct device *dev); extern void cpuidle_remove_sysfs(struct device *dev); +#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED +bool cpuidle_state_is_coupled(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int state); +int cpuidle_enter_state_coupled(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int next_state); +int cpuidle_coupled_register_device(struct cpuidle_device *dev); +void cpuidle_coupled_unregister_device(struct cpuidle_device *dev); +#else +static inline bool cpuidle_state_is_coupled(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int state) +{ + return false; +} + +static inline int cpuidle_enter_state_coupled(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int next_state) +{ + return -1; +} + +static inline int cpuidle_coupled_register_device(struct cpuidle_device *dev) +{ + return 0; +} + +static inline void cpuidle_coupled_unregister_device(struct cpuidle_device *dev) +{ +} +#endif + #endif /* __DRIVER_CPUIDLE_H */ diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 06335756..746f1e6b 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -173,7 +173,12 @@ static inline int performance_multiplier(void) /* for higher loadavg, we are more reluctant */ - mult += 2 * get_loadavg(); + /* + * this doesn't work as intended - it is almost always 0, but can + * sometimes, depending on workload, spike very high into the hundreds + * even when the average cpu load is under 10%. + */ + /* mult += 2 * get_loadavg(); */ /* for IO wait tasks (per cpu!) we add 5x each */ mult += 10 * nr_iowait_cpu(smp_processor_id()); diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index dd414d93..a84266df 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -296,4 +296,15 @@ config CRYPTO_DEV_TEGRA_AES To compile this driver as a module, choose M here: the module will be called tegra-aes. +config CRYPTO_DEV_ANYKA + tristate "Support for Anyka crypto engine" + depends on ARCH_AK39 + select CRYPTO_ALGAPI + select CRYPTO_BLKCIPHER + select CRYPTO_AES + help + This option allows you to have support for Anyka crypto engine. + Select this to offload the Anyka crypto module for AES algorithms. + + endif # CRYPTO_HW diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile index f3e64ead..e0faf2e4 100644 --- a/drivers/crypto/Makefile +++ b/drivers/crypto/Makefile @@ -14,3 +14,4 @@ obj-$(CONFIG_CRYPTO_DEV_OMAP_AES) += omap-aes.o obj-$(CONFIG_CRYPTO_DEV_PICOXCELL) += picoxcell_crypto.o obj-$(CONFIG_CRYPTO_DEV_S5P) += s5p-sss.o obj-$(CONFIG_CRYPTO_DEV_TEGRA_AES) += tegra-aes.o +obj-$(CONFIG_CRYPTO_DEV_ANYKA) += ak_crypto.o diff --git a/drivers/crypto/ak_crypto.c b/drivers/crypto/ak_crypto.c new file mode 100644 index 00000000..99da3ec1 --- /dev/null +++ b/drivers/crypto/ak_crypto.c @@ -0,0 +1,1466 @@ +/* + * Cryptographic API. + * + * Support for Anyka AES/DES/3DES encryption. + * + * Copyright (C) 2013 Anyka Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define __hdbg__ //printk("===%s===%s===%d\n", __FILE__, __func__, __LINE__); + +//#define CRYPT_DBG + +#ifdef CRYPT_DBG +#ifdef __KERNEL__ +#define PDEBUG(fmt, args...) printk(KERN_INFO "akcrypto:" fmt, ##args) +#define DUMP_DATA(fmt, args...) dump_data(fmt, ##args) +#else +#define PDEBUG(fmt, args...) fprintf(stderr, "%s %d:" fmt,__FILE__, __LINE__, ## args) +#define DUMP_DATA(fmt, args...) +#endif +#else +#define PDEBUG(fmt, args...) +#define DUMP_DATA(fmt, args...) +#endif + + +struct ak_cipher_reqctx { + unsigned long mode; +}; + +struct ak_crypto { + struct device *dev; /*device handle*/ + struct clk *clk; /* crypto module lock */ + void __iomem *iobase; + struct resource *ioarea; + spinlock_t lock; /*lock use for protect this struct */ + int irq; + struct crypto_queue queue; + struct list_head list; + + unsigned int flags; +#define FLAGS_BUSY (1<<0) +#define FLAGS_MULTI_GROUP_MODE (1<<1) + + struct tasklet_struct queue_task; + + struct ablkcipher_request *req; + struct ak_cipher_ctx *ctx; + + struct scatterlist *sg_src; + u32 src_ofs; + struct scatterlist *sg_dst; + u32 dst_ofs; +}; + + +#define AES_MAX_GRP_INPUT_SIZE (16) + + +#define aes_key key_u.aes +#define des_key key_u.des +#define des3_key key_u.des3 + + +struct ak_cipher_ctx { + struct ak_crypto *dev; + + union { + u8 aes[AES_MAX_KEY_SIZE];/*aes key len : 32*/ + u8 des[DES_KEY_SIZE];/*des key len: 16*/ + u8 des3[3 * DES_KEY_SIZE]; + } key_u; + + int keylen; + unsigned int width_sel; + int encrypt_mode; + bool iv_setted; +}; + +struct ak_cipher_alg { + struct crypto_alg alg; /*crypto alg*/ + struct list_head entry; + u32 enc_type; + u32 dec_type; /*not used*/ +}; + +struct ak_cipher_tmpl +{ + const char *name;/*alg nane*/ + const char *drv_name;/*driver name*/ + u8 block_size; + u32 enc_type; + struct ablkcipher_alg ablkcipher; +}; + +/* keep registered devices data here */ +static LIST_HEAD(dev_list); +static DEFINE_SPINLOCK(dev_list_lock); + + +/* keep registered devices data here */ +static LIST_HEAD(cipher_list); +static DEFINE_SPINLOCK(list_lock); + + +/** + * + * @brief: dump register, use for debug. + * + * @author lixinhai + * @date 2013-12-15 + * @param [in] crypto handle. + * @param [in] description + * @return void. + * + */ +static void dump_regs(struct ak_crypto *crypto, const char *desc) +{ + int i; + u32 con, stat, timeout; + u32 input[4], iv[4], key[8], output[4]; + u32 plint_addr, datalen, cipher_addr; + + PDEBUG("crypto reg list(%s):\n", desc); + con = readl(crypto->iobase + AKENC_CONTROL_REG); + stat = readl(crypto->iobase + AKENC_INT_STATUS_REG); + timeout = readl(crypto->iobase + AKENC_TIMEOUT_REG); + + PDEBUG("con:[%08x], irq_stat:[%08x], timeout_period:[%08x]\n", + con, stat, timeout); + + for(i=1; i<=4; i++) { + input[i]= readl(crypto->iobase + AKENC_GRP_INPUT_REG(i)); + PDEBUG("input(%d):[%08x], ", i, input[i]); + } + PDEBUG("\n"); + + for(i=1; i<=4; i++) { + iv[i] = readl(crypto->iobase + AKENC_VEC_INPUT_REG(i)); + PDEBUG("iv(%d):[%08x], ", i, iv[i]); + } + PDEBUG("\n"); + + for(i=1; i<=8; i++) { + key[i] = readl(crypto->iobase + AKENC_KEY_INPUT_REG(i)); + PDEBUG("key(%d):[%08x], ", i, key[i]); + } + PDEBUG("\n"); + + for(i=1; i<=4; i++) { + output[i] = readl(crypto->iobase + AKENC_GRP_OUTPUT_REG(i)); + PDEBUG("output(%d):[%08x], ", i, output[i]); + } + PDEBUG("\n"); + + plint_addr = readl(crypto->iobase + AKENC_PLAINT_ADDR_REG); + datalen = readl(crypto->iobase + AKENC_DATALEN_REG); + cipher_addr = readl(crypto->iobase + AKENC_CIPHER_ADDR_REG); + + PDEBUG("plint_addr:[%08x], datalen:[%08x], cipher_addr:[%08x]\n", + plint_addr, datalen, cipher_addr); +} + +/** + * @brief: dump data, use for debug. + * + * @author lixinhai + * @date 2013-12-15 + * @param [in] buf + * @param [in] buf len + * @return void. + * + */ +static inline void dump_data(char *desc, void *buf, int len) +{ + int i; + unsigned char *data = buf; + if(!data) + return; + + if(desc != NULL) + printk("%s. len=%d", desc, len); + + for(i=0; i__crt_alg, struct ak_cipher_alg, alg); +} + +static struct ak_crypto *ak_cipher_find_dev(struct ak_cipher_ctx *ctx) +{ + struct ak_crypto *dd = NULL, *tmp; + + spin_lock_bh(&dev_list_lock); + if (!ctx->dev) { + list_for_each_entry(tmp, &dev_list, list) { + /* FIXME: take fist available aes core */ + dd = tmp; + list_del(&tmp->list); + break; + } + ctx->dev = dd; + } else { + /* already found before */ + dd = ctx->dev; + } + spin_unlock_bh(&dev_list_lock); + + return dd; +} + +/** + * @brief: wait for idle. wait status register idle bit. + * + * @author lixinhai + * @date 2013-12-15 + * @param [in] crypto: akcrypto dev pointer. + * @param [in] timeout sec. + * @return 0:success. + * + */ +static int akenc_wait_for_idle(struct ak_crypto *dev, unsigned long timeout) +{ + u32 regval; + + regval = readl(dev->iobase + AKENC_CONTROL_REG); + + while(regval & AK_ENC_OPT_STATUS); + return 0; +} + + +/** + * @brief: notify complet working. + * + * @author lixinhai + * @date 2013-12-15 + * @param [in] crypto: akcrypto dev pointer. + * @param [in] err: is error or not. + * @return void + */ +static void akenc_encrypt_complete(struct ak_crypto *dev, int err) +{ + struct ak_cipher_ctx *ctx = dev->ctx; + + /* holding a lock outside */ + dev->req->base.complete(&dev->req->base, err); + dev->flags &= ~FLAGS_BUSY; + + if(ctx->encrypt_mode == CRYPTO_MULTI_GROUP_MODE) { + dma_unmap_sg(dev->dev, dev->sg_src, 1, DMA_TO_DEVICE); + dma_unmap_sg(dev->dev, dev->sg_dst, 1, DMA_FROM_DEVICE); + } +} + + +/** + * @brief: set single/mutli group input data + * + * @author lixinhai + * @date 2013-12-15 + * @param [in] crypto: akcrypto dev pointer. + * @param [in] sg: input scatterlist data + * @return int + */ +static int akenc_grp_indata(struct ak_crypto *dev, struct scatterlist *sg) +{ + void *grp; + int remain; + int len; + struct ak_cipher_ctx *ctx = dev->ctx; + + if(ctx->encrypt_mode == CRYPTO_MULTI_GROUP_MODE) { + dma_addr_t phyaddr_src; + dma_addr_t phyaddr_dst; + PDEBUG("%s use for multi group mode.\n", __func__); + + phyaddr_src = sg_dma_address(dev->sg_src) + dev->src_ofs; + phyaddr_dst = sg_dma_address(dev->sg_dst) + dev->dst_ofs; + writel(phyaddr_src, dev->iobase + AKENC_PLAINT_ADDR_REG); + writel(phyaddr_dst, dev->iobase + AKENC_CIPHER_ADDR_REG); + writel(dev->sg_src->length, dev->iobase + AKENC_DATALEN_REG); + dev->src_ofs += dev->sg_src->length; + } else { + grp = sg_virt(sg) + dev->src_ofs; + remain = dev->sg_src->length - dev->src_ofs; + len = (remain > ctx->width_sel) ? ctx->width_sel : remain; + + memcpy(dev->iobase + AKENC_GRP_INPUT_REG(1), grp, len); + dev->src_ofs += len; + + DUMP_DATA("indata", grp, len); + } + +next: + if((dev->src_ofs == dev->sg_src->length) && (!sg_is_last(sg))) { + + if(ctx->encrypt_mode == CRYPTO_MULTI_GROUP_MODE) + dma_unmap_sg(dev->dev, dev->sg_src, 1, DMA_TO_DEVICE); + + dev->sg_src = sg = sg_next(sg); + dev->src_ofs = 0; + + if(ctx->encrypt_mode == CRYPTO_MULTI_GROUP_MODE) + dma_map_sg(dev->dev, dev->sg_src, 1, DMA_TO_DEVICE); + + if(dev->sg_src->length == 0) { + goto next; + } + if(len < ctx->width_sel) { + + } + } + + return 0; +} + +/** + * @brief: set single/mutli group output data + * + * @author lixinhai + * @date 2013-12-15 + * @param [in] crypto: akcrypto dev pointer. + * @param [in] sg: output scatterlist data + * @return int + */ +static int akenc_grp_outdata(struct ak_crypto *dev, struct scatterlist *sg) +{ + void *grp; + int len; + int remain; + struct ak_cipher_ctx *ctx = dev->ctx; + + if(ctx->encrypt_mode == CRYPTO_MULTI_GROUP_MODE) { + dev->dst_ofs+= dev->sg_dst->length; + PDEBUG("%s use for multi group mode.\n", __func__); + } else { + grp = sg_virt(sg) + dev->dst_ofs; + remain = dev->sg_dst->length - dev->dst_ofs; + len = (remain > ctx->width_sel) ? ctx->width_sel : remain; + + memcpy(grp, dev->iobase + AKENC_GRP_OUTPUT_REG(1), len); + dev->dst_ofs += len; + DUMP_DATA("outdata", grp, len); + } + + if(dev->dst_ofs == dev->sg_dst->length) { + if(!sg_is_last(sg)) { + + if(ctx->encrypt_mode == CRYPTO_MULTI_GROUP_MODE) + dma_unmap_sg(dev->dev, dev->sg_dst, 1, DMA_FROM_DEVICE); + + dev->sg_dst = sg_next(sg); + dev->dst_ofs = 0; + + if(ctx->encrypt_mode == CRYPTO_MULTI_GROUP_MODE) + dma_map_sg(dev->dev, dev->sg_dst, 1, DMA_FROM_DEVICE); + + } else + akenc_encrypt_complete(dev, 0); + } + + return 0; +} + +/** + * @brief: set encode key and iv. + * + * @author lixinhai + * @date 2013-12-15 + * @param [in]: iv: input vector + * @param [in]: ivlen: input vector length. + * @param [in]: key + * @param [in]: keylen + * @return int + */ +static inline void akenc_set_key_iv(struct ak_crypto *dev, + uint8_t *iv, unsigned int ivlen, uint8_t *key, unsigned int keylen) +{ + if(ivlen > 0) { + DUMP_DATA("iv", iv, ivlen); + memcpy(dev->iobase + AKENC_VEC_INPUT_REG(1), iv, ivlen); + } + + if(keylen > 0) { + DUMP_DATA("key", key, keylen); + memcpy(dev->iobase + AKENC_KEY_INPUT_REG(1), key, keylen); + } +} + +static inline void akenc_set_iv(struct ak_crypto *dev,uint8_t *iv, unsigned int ivlen) +{ + if(ivlen > 0) { + DUMP_DATA("iv", iv, ivlen); + memcpy(dev->iobase + AKENC_VEC_INPUT_REG(1), iv, ivlen); + } +} + +static inline void akenc_set_key(struct ak_crypto *dev,uint8_t *key, unsigned int keylen) +{ + if(keylen > 0) { + DUMP_DATA("key", key, keylen); + memcpy(dev->iobase + AKENC_KEY_INPUT_REG(1), key, keylen); + } +} + + +/** + * @brief: working mode selection + * + * @author lixinhai + * @date 2013-12-15 + * @param [in]: mode: encrypt mode + * @return int + */ +static inline int akenc_work_mode(int mode) +{ + int val; + mode &= FLAGS_WORK_MODE_MASK; + switch(mode) { + case FLAGS_ECB: + val = AK_ENC_OPT_MODE(ENC_WORK_MODE_ECB); + break; + case FLAGS_CBC: + val = AK_ENC_OPT_MODE(ENC_WORK_MODE_CBC); + break; + case FLAGS_CFB: + val = AK_ENC_OPT_MODE(ENC_WORK_MODE_CFB); + break; + case FLAGS_OFB: + val = AK_ENC_OPT_MODE(ENC_WORK_MODE_OFB); + break; + case FLAGS_CTR: + val = AK_ENC_OPT_MODE(ENC_WORK_MODE_CTR); + break; + default: + BUG(); + } + return val; +} + + +/** + * @brief: encrypt algorithm selection + * + * @author lixinhai + * @date 2013-12-15 + * @param [in]: mode: encrypt mode + * @param [in]: width: bit width. + * @return int + */ +static inline int akenc_alg_sel(int mode, int key_len) +{ + int val = 0; + + PDEBUG("alg mode:%d, key len:%d.\n", mode, key_len); + + mode &= FLAGS_ALG_MODE_MASK; + if(mode & FLAGS_AES) { + if (key_len == AES_KEYSIZE_256) + val = AK_ENC_ALG_SEL(ENC_TYPE_ALG_AES256); + else if (key_len == AES_KEYSIZE_192) + val = AK_ENC_ALG_SEL(ENC_TYPE_ALG_AES192); + else + val = AK_ENC_ALG_SEL(ENC_TYPE_ALG_AES128); + } else if(mode & FLAGS_DES) { + val = AK_ENC_ALG_SEL(ENC_TYPE_ALG_DES); + } else if(mode & FLAGS_3DES) { + if (key_len == DES_KEY_2) + val = AK_ENC_ALG_SEL(ENC_TYPE_ALG_3DES_2KEY); + else + val = AK_ENC_ALG_SEL(ENC_TYPE_ALG_3DES_3KEY); + } + + return val; +} + + +/** + * @brief: data bit width selection + * + * @author lixinhai + * @date 2013-12-15 + * @param [in]: mode: encrypt mode + * @param [in]: width: bit width. + * @return int + */ +static inline int akenc_width_sel(int mode, int width) +{ + int val; + + mode &= FLAGS_ALG_MODE_MASK; + if(mode & FLAGS_AES) { + val = AK_ENC_WIDTH_SEL(ENC_WIDTH_AES_128BIT); + } else if(mode & (FLAGS_DES|FLAGS_3DES)) { + val = AK_ENC_WIDTH_SEL(ENC_WIDTH_DES_64BIT); + } + + return val; + +} + + +/** + * @brief: config bit sequence. + * + * @author lixinhai + * @date 2013-12-15 + * @param [in]: mode: encrypt mode + * @return int + */ +static inline int akenc_set_bit_seq(int mode) +{ + int val = 0; + /*ecb enc need set the input_bit, output_bit, iv_bit + besides need set the key bit if used for cbc enc. */ + + mode &= FLAGS_ALG_MODE_MASK; + if(mode & FLAGS_AES) { + val = AK_CRYPT_AES_BIT_SEQ; + } else if(mode & (FLAGS_DES|FLAGS_3DES)) { + val = AK_CRYPT_DES_BIT_SEQ; + } + return val; +} + +static inline void akenc_start_encrypt(struct ak_crypto *dev) +{ + u32 con; + con = readl(dev->iobase + AKENC_CONTROL_REG); + con |= AK_ENC_START; + writel(con, dev->iobase + AKENC_CONTROL_REG); +} + +/** + * @brief: encrypt work function, configure encrypt register, start + * the hardware encrypt working. + * + * @author lixinhai + * @date 2013-12-15 + * @param [in] crypto: akcrypto dev pointer. + * @param [in]: mode: encrypt mode + * @return int + */ +static void ak_cipher_crypto_start(struct ak_crypto *dev, unsigned long mode) +{ + u32 con, ret = 0; + struct ablkcipher_request *req = dev->req; + struct ak_cipher_ctx *ctx = dev->ctx; + struct ak_cipher_reqctx *rctx; + + struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req); + unsigned ivsize = crypto_ablkcipher_ivsize(tfm); + + PDEBUG("%s enter\n", __func__); + con = AK_ENC_INT_EN|AK_ENC_TIMEOUT_INT_EN; + akenc_wait_for_idle(dev, 0); + + /* aes encrypt width is 16.*/ + if(mode & FLAGS_AES) + ctx->width_sel = 16; + else + ctx->width_sel = 8; + + /*multi group mode.*/ + if((dev->flags & FLAGS_MULTI_GROUP_MODE) && !(mode & FLAGS_CTR)) + ctx->encrypt_mode = CRYPTO_MULTI_GROUP_MODE; + else + ctx->encrypt_mode = CRYPTO_SINGLE_GROUP_MODE; + + dev->sg_src = req->src; + dev->src_ofs = 0; + dev->sg_dst = req->dst; + dev->dst_ofs = 0; + + /*multi group mode.*/ + if(ctx->encrypt_mode == CRYPTO_MULTI_GROUP_MODE) { + PDEBUG("%s use for multi group mode.\n", __func__); + dma_map_sg(dev->dev, dev->sg_src, 1, DMA_TO_DEVICE); + dma_map_sg(dev->dev, dev->sg_dst, 1, DMA_FROM_DEVICE); + } + + ret = akenc_grp_indata(dev, req->src); + if(ret) { + goto out; + } + + writel(MAX_TIMEOUT, dev->iobase + AKENC_TIMEOUT_REG); + + + if(mode & FLAGS_AES) + akenc_set_key(dev,ctx->aes_key, ctx->keylen); + if((mode & FLAGS_DES) || (mode & FLAGS_3DES)) + akenc_set_key(dev, ctx->des_key, ctx->keylen); + + + rctx = ablkcipher_request_ctx(req); + if((ctx->iv_setted) && !(rctx->mode & FLAGS_CTR)) + con &= ~AK_ENC_IV_MODE; + else{ + akenc_set_iv(dev, req->info, ivsize); + con |= AK_ENC_IV_MODE; + ctx->iv_setted = true; + } + + + con |= akenc_alg_sel(mode, ctx->keylen); + con |= akenc_width_sel(mode, ctx->width_sel); + + if(ctx->encrypt_mode == CRYPTO_MULTI_GROUP_MODE) + con |= AK_ENC_MULT_GRP_CIPHER; + + con |= AK_ENC_CLK_EN; + con |= akenc_set_bit_seq(mode); + + con |= akenc_work_mode(mode); + + + + writel(con, dev->iobase + AKENC_CONTROL_REG); + + dump_regs(dev, __func__); + akenc_start_encrypt(dev); + PDEBUG("ak crypto con(%08x):%08x.\n", + (unsigned int)(dev->iobase + AKENC_CONTROL_REG), + readl(dev->iobase + AKENC_CONTROL_REG)); + return; + +out: + akenc_encrypt_complete(dev, ret); +} + +/** + * @brief: encrypt tasklet handle. + * + * @author lixinhai + * @date 2013-12-15 + * @param [in]: data:private data. + * @return int + */ +static void akenc_tasklet_cb(unsigned long data) +{ + struct ak_crypto *dev = (struct ak_crypto *)data; + struct crypto_async_request *async_req, *backlog; + struct ak_cipher_reqctx *reqctx; + unsigned long flags; + + PDEBUG("tasklet running.\n"); + spin_lock_irqsave(&dev->lock, flags); + backlog = crypto_get_backlog(&dev->queue); + async_req = crypto_dequeue_request(&dev->queue); + spin_unlock_irqrestore(&dev->lock, flags); + + if (!async_req) + return; + + if (backlog) + backlog->complete(backlog, -EINPROGRESS); + + dev->req = ablkcipher_request_cast(async_req); + dev->ctx = crypto_tfm_ctx(dev->req->base.tfm); + reqctx = ablkcipher_request_ctx(dev->req); + + ak_cipher_crypto_start(dev, reqctx->mode); + +} + + +/** + * @brief: push the request to queue. + * + * @author lixinhai + * @date 2013-12-15 + * @param [in] crypto: akcrypto dev pointer. + * @param [in] req: async cipher request pointer. + * @return int + */ +static int ak_cipher_handle_queue(struct ak_crypto *crypto, + struct ablkcipher_request *req) +{ + unsigned long flags; + int ret = 0; + + PDEBUG("%s enter\n", __func__); + spin_lock_irqsave(&crypto->lock, flags); + + if (crypto->flags & FLAGS_BUSY) { + spin_unlock_irqrestore(&crypto->lock, flags); + return ret; + } + + ret = ablkcipher_enqueue_request(&crypto->queue, req); + + crypto->flags |= FLAGS_BUSY; + spin_unlock_irqrestore(&crypto->lock, flags); + + tasklet_schedule(&crypto->queue_task); + + return ret; /* return ret, which is enqueue return value */ +} + +/** + * @brief: cipher encode entry, get ak_crypto alg from request, push + * the request to queue. + * + * @author lixinhai + * @date 2013-12-15 + * @param [in] req: async cipher request pointer. + * @return int + */ +static int ak_cipher_encrypt(struct ablkcipher_request *req) +{ + struct ak_cipher_ctx *ctx; + struct ak_cipher_reqctx *rctx; + struct ak_crypto *crypto; + struct ak_cipher_alg *alg; + + PDEBUG("%s enter\n", __func__); + rctx= ablkcipher_request_ctx(req); + ctx = crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(req)); + + alg = tfm_to_cipher_alg(&crypto_ablkcipher_reqtfm(req)->base); + + if(!(alg->enc_type & FLAGS_ENCRYPT)) + return -EINVAL; + + PDEBUG("nbytes: %d, enc: %d, mode: %d\n", req->nbytes, + !!(alg->enc_type & FLAGS_ENCRYPT), + (alg->enc_type & FLAGS_WORK_MODE_MASK)); + + crypto = ctx->dev; + if (!crypto) { + printk("warning!!\n"); + crypto = ctx->dev = ak_cipher_find_dev(ctx); + if (!crypto) { + printk("ak crypto: not found the crypto dev.\n"); + return -ENODEV; + } + } + + rctx->mode = alg->enc_type; + + return ak_cipher_handle_queue(crypto, req); +} + +/** + * @brief: set des encrypt key. + * + * @author lixinhai + * @date 2013-12-15 + * @param [in] *cipher: async block cipher + * @param [in] *key + * @param [in] key_len + * @return int + */ +static int ak_des_setkey(struct crypto_ablkcipher *cipher, + const u8 *key, unsigned int keylen) +{ + struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher); + struct ak_cipher_ctx *ctx = crypto_tfm_ctx(tfm); + + PDEBUG("%s enter\n", __func__); + if (keylen != DES_KEY_SIZE) { + crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN); + return -EINVAL; + } + + memcpy(ctx->des_key, key, keylen); + ctx->keylen = keylen; + + PDEBUG("set des key success.\n"); + return 0; + +} + + +/** + * @brief: set 3des encrypt key. + * + * @author lixinhai + * @date 2013-12-15 + * @param [in] *cipher: async block cipher + * @param [in] *key + * @param [in] key_len + * @return int + */ +static int ak_3des_setkey(struct crypto_ablkcipher *cipher, + const u8 *key, unsigned int keylen) +{ + struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher); + struct ak_cipher_ctx *ctx = crypto_tfm_ctx(tfm); + + PDEBUG("%s enter\n", __func__); + if (keylen != 3*DES_KEY_SIZE) { + crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN); + return -EINVAL; + } + + memcpy(ctx->des_key, key, keylen); + ctx->keylen = keylen; + + PDEBUG("set 3des key success.\n"); + return 0; +} + + +/** + * @brief: set aes encrypt key. + * + * @author lixinhai + * @date 2013-12-15 + * @param [in] *cipher: async block cipher + * @param [in] *key + * @param [in] key_len + * @return int + */ +static int ak_aes_setkey(struct crypto_ablkcipher *cipher, + const u8 *key, unsigned int keylen) +{ + struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher); + struct ak_cipher_ctx *ctx = crypto_tfm_ctx(tfm); + + PDEBUG("%s enter\n", __func__); + if (keylen != AES_KEYSIZE_128 && + keylen != AES_KEYSIZE_192 && + keylen != AES_KEYSIZE_256) + return -EINVAL; + + memcpy(ctx->aes_key, key, keylen); + ctx->keylen = keylen; + + PDEBUG("set aes key success.\n"); + return 0; + +} + + +static inline int ak_null_decrypt(struct ablkcipher_request *req) +{ + printk("unsupport the decrypt.\n"); + return -EINVAL; +} + +/** + * @brief: ak_cipher_cra_init. + * + * @author lixinhai + * @date 2013-12-15 + * @param: crypto_tfm: transrate handle. + * @return int + */ +static inline int ak_cipher_cra_init(struct crypto_tfm *tfm) +{ + struct ak_cipher_ctx *ctx = crypto_tfm_ctx(tfm); + + PDEBUG("%s enter\n", __func__); + ctx->iv_setted = false; + ctx->dev = ak_cipher_find_dev(ctx); + if(ctx->dev == NULL) + return -EBUSY; + tfm->crt_ablkcipher.reqsize = sizeof(struct ak_cipher_ctx); + return 0; + +} + +static inline void ak_cipher_cra_exit(struct crypto_tfm *tfm) +{ + struct ak_cipher_ctx *ctx = crypto_tfm_ctx(tfm); + PDEBUG("%s enter\n", __func__); + spin_lock(&dev_list_lock); + list_add_tail(&ctx->dev->list, &dev_list); + spin_unlock(&dev_list_lock); + return; +} + + +/** + * @brief: crypto interrupt handle, completion encrypt or timeout. + * + * @author lixinhai + * @date 2013-12-15 + * @param [in] *irq interrupt number. + * @param [in] *devid dev id + * @return irqreturn_t . + */ +static irqreturn_t ak_crypto_irq(int irq, void *dev_id) +{ + struct ak_crypto *dev = dev_id; + struct ak_cipher_reqctx *rctx; + uint32_t status; + int ret; + + status = readl(dev->iobase + AKENC_INT_STATUS_REG); + + dump_regs(dev, __func__); + PDEBUG("ak crypto interrupt : status is %08x.\n", status); + if (status & AK_ENC_ENCRYPT_DONE) { + akenc_grp_outdata(dev, dev->sg_dst); + if((dev->src_ofs != dev->sg_src->length) || !sg_is_last(dev->sg_src)) { + + ret = akenc_grp_indata(dev, dev->sg_src); + if(ret) { + goto out; + } + + rctx = ablkcipher_request_ctx(dev->req); + if(!(rctx->mode & FLAGS_CTR)) { + u32 con = readl(dev->iobase + AKENC_CONTROL_REG); + con &= ~AK_ENC_IV_MODE; + writel(con, dev->iobase + AKENC_CONTROL_REG); + } else { + u32 con = readl(dev->iobase + AKENC_CONTROL_REG); + con |= AK_ENC_IV_MODE; + writel(con, dev->iobase + AKENC_CONTROL_REG); + } + + akenc_start_encrypt(dev); + } + + } else if(status & AK_ENC_ENCRYPT_TIMEOUT) { + /**FIXME: add the timeout handle.*/ + printk("crypto timeout.\n"); + akenc_encrypt_complete(dev, -EAGAIN); + } + +out: + return IRQ_HANDLED; +} + +static struct ak_cipher_tmpl cipher_tmpls[] = { + { + .name = "ecb(aes)", + .drv_name = "ecb-aes", + .block_size = AES_BLOCK_SIZE, + .enc_type = (FLAGS_ECB|FLAGS_AES|FLAGS_ENCRYPT), + .ablkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = ak_aes_setkey, + .encrypt = ak_cipher_encrypt, + .decrypt = ak_null_decrypt, + } + }, + { + .name = "cbc(aes)", + .drv_name = "cbc-aes", + .block_size = AES_BLOCK_SIZE, + .enc_type = (FLAGS_AES|FLAGS_CBC|FLAGS_ENCRYPT), + .ablkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = ak_aes_setkey, + .encrypt = ak_cipher_encrypt, + .decrypt = ak_null_decrypt, + } + }, + { + .name = "cfb(aes)", + .drv_name = "cfb-aes", + .block_size = AES_BLOCK_SIZE, + .enc_type = (FLAGS_AES|FLAGS_CFB|FLAGS_ENCRYPT), + .ablkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = ak_aes_setkey, + .encrypt = ak_cipher_encrypt, + .decrypt = ak_null_decrypt, + } + }, + { + .name = "ofb(aes)", + .drv_name = "ofb-aes", + .block_size = AES_BLOCK_SIZE, + .enc_type = (FLAGS_AES|FLAGS_OFB|FLAGS_ENCRYPT), + .ablkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = ak_aes_setkey, + .encrypt = ak_cipher_encrypt, + .decrypt = ak_null_decrypt, + } + }, + { + .name = "ctr(aes)", + .drv_name = "ctr-aes", + .block_size = AES_BLOCK_SIZE, + .enc_type = (FLAGS_AES|FLAGS_CTR|FLAGS_ENCRYPT), + .ablkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = ak_aes_setkey, + .encrypt = ak_cipher_encrypt, + .decrypt = ak_null_decrypt, + } + }, + { + .name = "ecb(des)", + .drv_name = "ecb-des", + .block_size = DES_BLOCK_SIZE, + .enc_type = (FLAGS_DES|FLAGS_ECB|FLAGS_ENCRYPT), + .ablkcipher = { + .min_keysize = DES_KEY_SIZE, + .max_keysize = DES_KEY_SIZE, + .ivsize = DES_BLOCK_SIZE, + .setkey = ak_des_setkey, + .encrypt = ak_cipher_encrypt, + .decrypt = ak_null_decrypt, + } + }, + { + .name = "cbc(des)", + .drv_name = "cbc-des", + .block_size = DES_BLOCK_SIZE, + .enc_type = (FLAGS_DES|FLAGS_CBC|FLAGS_ENCRYPT), + .ablkcipher = { + .min_keysize = DES_KEY_SIZE, + .max_keysize = DES_KEY_SIZE, + .ivsize = DES_BLOCK_SIZE, + .setkey = ak_des_setkey, + .encrypt = ak_cipher_encrypt, + .decrypt = ak_null_decrypt, + } + }, + { + .name = "cfb(des)", + .drv_name = "cfb-des", + .block_size = DES_BLOCK_SIZE, + .enc_type = (FLAGS_DES|FLAGS_CBC|FLAGS_ENCRYPT), + .ablkcipher = { + .min_keysize = DES_KEY_SIZE, + .max_keysize = DES_KEY_SIZE, + .ivsize = DES_BLOCK_SIZE, + .setkey = ak_des_setkey, + .encrypt = ak_cipher_encrypt, + .decrypt = ak_null_decrypt, + } + }, + { + .name = "cfb(des)", + .drv_name = "cfb-des", + .block_size = DES_BLOCK_SIZE, + .enc_type = (FLAGS_DES|FLAGS_CFB|FLAGS_ENCRYPT), + .ablkcipher = { + .min_keysize = DES_KEY_SIZE, + .max_keysize = DES_KEY_SIZE, + .ivsize = DES_BLOCK_SIZE, + .setkey = ak_des_setkey, + .encrypt = ak_cipher_encrypt, + .decrypt = ak_null_decrypt, + } + }, + { + .name = "ofb(des)", + .drv_name = "ofb-des", + .block_size = DES_BLOCK_SIZE, + .enc_type = (FLAGS_DES|FLAGS_OFB|FLAGS_ENCRYPT), + .ablkcipher = { + .min_keysize = DES_KEY_SIZE, + .max_keysize = DES_KEY_SIZE, + .ivsize = DES_BLOCK_SIZE, + .setkey = ak_des_setkey, + .encrypt = ak_cipher_encrypt, + .decrypt = ak_null_decrypt, + } + }, + { + .name = "ecb(des3_ede)", + .drv_name = "ecb-des3-ede", + .block_size = DES3_EDE_BLOCK_SIZE, + .enc_type = (FLAGS_3DES|FLAGS_ECB|FLAGS_ENCRYPT), + .ablkcipher = { + .min_keysize = DES3_EDE_KEY_SIZE, + .max_keysize = DES3_EDE_KEY_SIZE, + .ivsize = DES3_EDE_BLOCK_SIZE, + .setkey = ak_3des_setkey, + .encrypt = ak_cipher_encrypt, + .decrypt = ak_null_decrypt, + } + }, + { + .name = "cbc(des3_ede)", + .drv_name = "cbc-des3-ede", + .block_size = DES3_EDE_BLOCK_SIZE, + .enc_type = (FLAGS_3DES|FLAGS_CBC|FLAGS_ENCRYPT), + .ablkcipher = { + .min_keysize = DES3_EDE_KEY_SIZE, + .max_keysize = DES3_EDE_KEY_SIZE, + .ivsize = DES3_EDE_BLOCK_SIZE, + .setkey = ak_3des_setkey, + .encrypt = ak_cipher_encrypt, + .decrypt = ak_null_decrypt, + } + }, + { + .name = "cfb(des3_ede)", + .drv_name = "cfb-des3-ede", + .block_size = DES3_EDE_BLOCK_SIZE, + .enc_type = (FLAGS_3DES|FLAGS_CFB|FLAGS_ENCRYPT), + .ablkcipher = { + .min_keysize = DES3_EDE_KEY_SIZE, + .max_keysize = DES3_EDE_KEY_SIZE, + .ivsize = DES3_EDE_BLOCK_SIZE, + .setkey = ak_3des_setkey, + .encrypt = ak_cipher_encrypt, + .decrypt = ak_null_decrypt, + } + }, + { + .name = "ofb(des3_ede)", + .drv_name = "ofb-des3-ede", + .block_size = DES3_EDE_BLOCK_SIZE, + .enc_type = (FLAGS_3DES|FLAGS_OFB|FLAGS_ENCRYPT), + .ablkcipher = { + .min_keysize = DES3_EDE_KEY_SIZE, + .max_keysize = DES3_EDE_KEY_SIZE, + .ivsize = DES3_EDE_BLOCK_SIZE, + .setkey = ak_3des_setkey, + .encrypt = ak_cipher_encrypt, + .decrypt = ak_null_decrypt, + } + }, +}; + +/** + * @brief: convert ak_cipher_tmpl to alg, register a crypto algorithm. + * + * @author lixinhai + * @date 2013-12-15 + * @param [in] *tmpl information of crypto algorithm. + * @return int. + * @retval -EINVAL no platform data , fail; + * @retval -EBUSY requset mem fail; + * @retval -ENOMEM alloc mem fail; + * + */ +static int __devinit ak_register_one_cipher(const struct ak_cipher_tmpl *tmpl) +{ + struct crypto_alg *alg; + int err; + struct ak_cipher_alg *p = kzalloc(sizeof(*p), GFP_KERNEL); + + if (!p) + return -ENOMEM; + + alg = &p->alg; + + snprintf(alg->cra_name, CRYPTO_MAX_ALG_NAME, "%s", tmpl->name); + snprintf(alg->cra_driver_name, CRYPTO_MAX_ALG_NAME, "ak-%s", tmpl->drv_name); + alg->cra_priority = AK_CRA_PRIORITY; + alg->cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | + CRYPTO_ALG_ASYNC; + + alg->cra_blocksize = tmpl->block_size; + alg->cra_ctxsize = sizeof(struct ak_cipher_ctx); + alg->cra_type = &crypto_ablkcipher_type; + alg->cra_u.ablkcipher = tmpl->ablkcipher; + alg->cra_init = ak_cipher_cra_init; + alg->cra_exit = ak_cipher_cra_exit; + alg->cra_module = THIS_MODULE; + + p->enc_type = tmpl->enc_type; + + spin_lock(&list_lock); + list_add(&p->entry, &cipher_list); + spin_unlock(&list_lock); + + /*register a algorithm.*/ + err = crypto_register_alg(alg); + if (err) { + pr_err("%s alg registration failed\n", alg->cra_name); + list_del(&p->entry); + kfree(p); + } else { + pr_info("%s alg registered\n", alg->cra_name); + } + + return err; +} + +/** + * + * @brief unregister all algorithm + * + * @author lixinhai + * @date 2013-12-15 + * @param void. + * @return void. + */ +static void ak_unregister_algs(void) +{ + struct ak_cipher_alg *cipher, *cipher_tmp; + + list_for_each_entry_safe(cipher, cipher_tmp, &cipher_list, entry) { + crypto_unregister_alg(&cipher->alg); + spin_lock(&list_lock); + list_del(&cipher->entry); + spin_unlock(&list_lock); + kfree(cipher); + } +} + +/** + * @brief crypto driver probe and initilize. + * + * @author lixinhai + * @date 2013-12-15 + * @param [in] *pdev information of platform device ,getting the crypto + * driver resource . + * @return int. + * @retval -EINVAL no platform data , fail; + * @retval -EBUSY requset mem fail; + * @retval -ENOMEM alloc mem fail; + */ +static int ak_crypto_probe(struct platform_device *pdev) +{ + int i, ret = 0; + struct resource *res; + struct ak_crypto *crypto_dev; + struct device *dev = &pdev->dev; + struct ak_crypto_plat_data *pdata; + + pdata = pdev->dev.platform_data; + + if(!pdata) { + printk("not found crypto platform data."); + ret = -EINVAL; + goto probe_out; + } + + crypto_dev = kzalloc(sizeof(struct ak_crypto), GFP_KERNEL); + if (crypto_dev == NULL) { + dev_err(dev, "unable to alloc data struct.\n"); + goto err_nomem; + } + crypto_dev->dev = dev; + + platform_set_drvdata(pdev, crypto_dev); + + /* + * reset crypto controler. + * */ + ak_soft_reset(AK_SRESET_ENCRY); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Cannot get resource.\n"); + ret = -ENXIO; + goto err_no_iores; + } + + crypto_dev->ioarea = request_mem_region(res->start, resource_size(res), + pdev->name); + + if (crypto_dev->ioarea == NULL) { + dev_err(&pdev->dev, "Cannot reserve region\n"); + ret = -ENXIO; + goto err_no_iores; + } + + crypto_dev->iobase = ioremap(crypto_dev->ioarea->start, + resource_size(crypto_dev->ioarea)); + if (crypto_dev->iobase == NULL) { + dev_err(&pdev->dev, "Cannot map IO\n"); + ret = -ENXIO; + goto err_no_iomap; + } + + crypto_dev->irq = platform_get_irq(pdev, 0); + if (crypto_dev->irq < 0) { + dev_err(&pdev->dev, "No IRQ specified\n"); + ret = -ENOENT; + goto err_no_irq; + } + + /* + * request crypto interrupt. + * */ + ret = request_irq(crypto_dev->irq, ak_crypto_irq, 0, pdev->name, crypto_dev); + if (ret) { + dev_err(&pdev->dev, "Cannot claim IRQ\n"); + goto err_no_irq; + } + + crypto_dev->clk = clk_get(dev, "encryption"); + if (IS_ERR(crypto_dev->clk)) { + dev_err(dev, "failed to find secss clock source\n"); + ret = -ENOENT; + goto err_no_clk; + } + + ret = clk_enable(crypto_dev->clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable clock source.\n"); + goto err_clk_en; + } + + tasklet_init(&crypto_dev->queue_task, akenc_tasklet_cb, (unsigned long)crypto_dev); + crypto_init_queue(&crypto_dev->queue, CRYPTO_QUEUE_LEN); + + crypto_dev->flags = 0; + if(pdata->encrypt_mode == CRYPTO_MULTI_GROUP_MODE) { + crypto_dev->flags |= FLAGS_MULTI_GROUP_MODE; + printk("crypto use for multi group mode.\n"); + } + + INIT_LIST_HEAD(&crypto_dev->list); + spin_lock(&dev_list_lock); + list_add_tail(&crypto_dev->list, &dev_list); + spin_unlock(&dev_list_lock); + + + /* + * register all crypto + * */ + for (i = 0; i < ARRAY_SIZE(cipher_tmpls); i++) { + ret = ak_register_one_cipher(&cipher_tmpls[i]); + if (ret) { + ak_unregister_algs(); + goto err_algs; + } + } + + pr_info("Anyka crypto driver registered.\n"); + + return 0; + +err_algs: + clk_disable(crypto_dev->clk); + +err_clk_en: + clk_put(crypto_dev->clk); + +err_no_clk: + free_irq(crypto_dev->irq, crypto_dev); + +err_no_irq: + iounmap(crypto_dev->iobase); + +err_no_iomap: + release_mem_region(crypto_dev->ioarea->start, resource_size(crypto_dev->ioarea)); + +err_no_iores: + kfree(crypto_dev); + +err_nomem: +probe_out: + return ret; +} + + +/** + * @brief crypto driver remove and release + * + * @author lixinhai + * @date 2013-12-15 + * @param [in] *pdev information of platform device ,getting the crypto + * driver resource . + * @return int. + */ +static int ak_crypto_remove(struct platform_device *pdev) +{ + struct ak_crypto *crypto_dev; + + crypto_dev = platform_get_drvdata(pdev); + + if (!crypto_dev) + return -ENODEV; + + spin_lock(&dev_list_lock); + list_del(&crypto_dev->list); + spin_unlock(&dev_list_lock); + + clk_disable(crypto_dev->clk); + clk_put(crypto_dev->clk); + + free_irq(crypto_dev->irq, crypto_dev); + + iounmap(crypto_dev->iobase); + release_mem_region(crypto_dev->ioarea->start, resource_size(crypto_dev->ioarea)); + + kfree(crypto_dev); + + return 0; +} + +static struct platform_driver ak_crypto_driver = { + .probe = ak_crypto_probe, + .remove = ak_crypto_remove, + .driver = { + .owner = THIS_MODULE, + .name = "ak-crypto", + }, +}; + +/** + * @brief crypto driver init to platform. + * + * @author lixinhai + * @date 2013-12-15 + * @return int. + * + */ +static int __init ak_crypto_init(void) +{ + printk("AK Crypto Driver (c) 2013 ANYKA\n"); + return platform_driver_register(&ak_crypto_driver); +} + +/** + * @brief release crypto driver from platform. + * + * @author lixinhai + * @date 2013-12-15 + * @return int. + * + */ +static void __exit ak_crypto_exit(void) +{ + return platform_driver_unregister(&ak_crypto_driver); +} + +module_init(ak_crypto_init); +module_exit(ak_crypto_exit); + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Anyka Inc."); +MODULE_DESCRIPTION("Anyka hardware crypto"); + diff --git a/drivers/devfreq/governor_performance.c b/drivers/devfreq/governor_performance.c index 574a06b1..af75ddd4 100644 --- a/drivers/devfreq/governor_performance.c +++ b/drivers/devfreq/governor_performance.c @@ -10,6 +10,7 @@ */ #include +#include "governor.h" static int devfreq_performance_func(struct devfreq *df, unsigned long *freq) @@ -25,8 +26,14 @@ static int devfreq_performance_func(struct devfreq *df, return 0; } +static int performance_init(struct devfreq *devfreq) +{ + return update_devfreq(devfreq); +} + const struct devfreq_governor devfreq_performance = { .name = "performance", + .init = performance_init, .get_target_freq = devfreq_performance_func, .no_central_polling = true, }; diff --git a/drivers/devfreq/governor_powersave.c b/drivers/devfreq/governor_powersave.c index d742d4a8..fec0cdbd 100644 --- a/drivers/devfreq/governor_powersave.c +++ b/drivers/devfreq/governor_powersave.c @@ -10,6 +10,7 @@ */ #include +#include "governor.h" static int devfreq_powersave_func(struct devfreq *df, unsigned long *freq) @@ -22,8 +23,14 @@ static int devfreq_powersave_func(struct devfreq *df, return 0; } +static int powersave_init(struct devfreq *devfreq) +{ + return update_devfreq(devfreq); +} + const struct devfreq_governor devfreq_powersave = { .name = "powersave", + .init = powersave_init, .get_target_freq = devfreq_powersave_func, .no_central_polling = true, }; diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile index cc927788..ca2d3b34 100644 --- a/drivers/gpu/Makefile +++ b/drivers/gpu/Makefile @@ -1 +1 @@ -obj-y += drm/ vga/ stub/ +obj-y += drm/ vga/ stub/ ion/ diff --git a/drivers/gpu/ion/Kconfig b/drivers/gpu/ion/Kconfig new file mode 100644 index 00000000..cfb11e1e --- /dev/null +++ b/drivers/gpu/ion/Kconfig @@ -0,0 +1,18 @@ +menuconfig ION + tristate "Ion Memory Manager" + select GENERIC_ALLOCATOR + select DMA_SHARED_BUFFER + help + Chose this option to enable the ION Memory Manager. + +config ION_TEGRA + tristate "Ion for Tegra" + depends on ARCH_TEGRA && ION + help + Choose this option if you wish to use ion on an nVidia Tegra. + +config ION_AK + tristate "Ion for AK" + depends on ION + help + Choose this option if you wish to use ion on an AK platform. diff --git a/drivers/gpu/ion/Makefile b/drivers/gpu/ion/Makefile new file mode 100644 index 00000000..f4a7ac1c --- /dev/null +++ b/drivers/gpu/ion/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_ION) += ion.o ion_heap.o ion_page_pool.o ion_system_heap.o \ + ion_carveout_heap.o ion_chunk_heap.o +obj-$(CONFIG_ION_TEGRA) += tegra/ +obj-$(CONFIG_ION_AK) += plat-anyka/ diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c new file mode 100644 index 00000000..cb170959 --- /dev/null +++ b/drivers/gpu/ion/ion.c @@ -0,0 +1,1462 @@ +/* + + * drivers/gpu/ion/ion.c + * + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ion_priv.h" + +/** + * struct ion_device - the metadata of the ion device node + * @dev: the actual misc device + * @buffers: an rb tree of all the existing buffers + * @buffer_lock: lock protecting the tree of buffers + * @lock: rwsem protecting the tree of heaps and clients + * @heaps: list of all the heaps in the system + * @user_clients: list of all the clients created from userspace + */ +struct ion_device { + struct miscdevice dev; + struct rb_root buffers; + struct mutex buffer_lock; + struct rw_semaphore lock; + struct plist_head heaps; + long (*custom_ioctl) (struct ion_client *client, unsigned int cmd, + unsigned long arg); + struct rb_root clients; + struct dentry *debug_root; +}; + +/** + * struct ion_client - a process/hw block local address space + * @node: node in the tree of all clients + * @dev: backpointer to ion device + * @handles: an rb tree of all the handles in this client + * @lock: lock protecting the tree of handles + * @name: used for debugging + * @task: used for debugging + * + * A client represents a list of buffers this client may access. + * The mutex stored here is used to protect both handles tree + * as well as the handles themselves, and should be held while modifying either. + */ +struct ion_client { + struct rb_node node; + struct ion_device *dev; + struct rb_root handles; + struct mutex lock; + const char *name; + struct task_struct *task; + pid_t pid; + struct dentry *debug_root; +}; + +/** + * ion_handle - a client local reference to a buffer + * @ref: reference count + * @client: back pointer to the client the buffer resides in + * @buffer: pointer to the buffer + * @node: node in the client's handle rbtree + * @kmap_cnt: count of times this client has mapped to kernel + * @dmap_cnt: count of times this client has mapped for dma + * + * Modifications to node, map_cnt or mapping should be protected by the + * lock in the client. Other fields are never changed after initialization. + */ +struct ion_handle { + struct kref ref; + struct ion_client *client; + struct ion_buffer *buffer; + struct rb_node node; + unsigned int kmap_cnt; +}; + +bool ion_buffer_fault_user_mappings(struct ion_buffer *buffer) +{ + return ((buffer->flags & ION_FLAG_CACHED) && + !(buffer->flags & ION_FLAG_CACHED_NEEDS_SYNC)); +} + +bool ion_buffer_cached(struct ion_buffer *buffer) +{ + return !!(buffer->flags & ION_FLAG_CACHED); +} + +/* this function should only be called while dev->lock is held */ +static void ion_buffer_add(struct ion_device *dev, + struct ion_buffer *buffer) +{ + struct rb_node **p = &dev->buffers.rb_node; + struct rb_node *parent = NULL; + struct ion_buffer *entry; + + while (*p) { + parent = *p; + entry = rb_entry(parent, struct ion_buffer, node); + + if (buffer < entry) { + p = &(*p)->rb_left; + } else if (buffer > entry) { + p = &(*p)->rb_right; + } else { + pr_err("%s: buffer already found.", __func__); + BUG(); + } + } + + rb_link_node(&buffer->node, parent, p); + rb_insert_color(&buffer->node, &dev->buffers); +} + +static int ion_buffer_alloc_dirty(struct ion_buffer *buffer); + +static bool ion_heap_drain_freelist(struct ion_heap *heap); +/* this function should only be called while dev->lock is held */ +static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, + struct ion_device *dev, + unsigned long len, + unsigned long align, + unsigned long flags) +{ + struct ion_buffer *buffer; + struct sg_table *table; + struct scatterlist *sg; + int i, ret; + + buffer = kzalloc(sizeof(struct ion_buffer), GFP_KERNEL); + if (!buffer) + return ERR_PTR(-ENOMEM); + + buffer->heap = heap; + buffer->flags = flags; + kref_init(&buffer->ref); + + ret = heap->ops->allocate(heap, buffer, len, align, flags); + + if (ret) { + if (!(heap->flags & ION_HEAP_FLAG_DEFER_FREE)) + goto err2; + + ion_heap_drain_freelist(heap); + ret = heap->ops->allocate(heap, buffer, len, align, + flags); + if (ret) + goto err2; + } + + buffer->dev = dev; + buffer->size = len; + + table = heap->ops->map_dma(heap, buffer); + if (IS_ERR_OR_NULL(table)) { + heap->ops->free(buffer); + kfree(buffer); + return ERR_PTR(PTR_ERR(table)); + } + buffer->sg_table = table; + if (ion_buffer_fault_user_mappings(buffer)) { + for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, + i) { + if (sg_dma_len(sg) == PAGE_SIZE) + continue; + pr_err("%s: cached mappings that will be faulted in " + "must have pagewise sg_lists\n", __func__); + ret = -EINVAL; + goto err; + } + + ret = ion_buffer_alloc_dirty(buffer); + if (ret) + goto err; + } + + buffer->dev = dev; + buffer->size = len; + INIT_LIST_HEAD(&buffer->vmas); + mutex_init(&buffer->lock); + /* this will set up dma addresses for the sglist -- it is not + technically correct as per the dma api -- a specific + device isn't really taking ownership here. However, in practice on + our systems the only dma_address space is physical addresses. + Additionally, we can't afford the overhead of invalidating every + allocation via dma_map_sg. The implicit contract here is that + memory comming from the heaps is ready for dma, ie if it has a + cached mapping that mapping has been invalidated */ + for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, i) + sg_dma_address(sg) = sg_phys(sg); + mutex_lock(&dev->buffer_lock); + ion_buffer_add(dev, buffer); + mutex_unlock(&dev->buffer_lock); + return buffer; + +err: + heap->ops->unmap_dma(heap, buffer); + heap->ops->free(buffer); +err2: + kfree(buffer); + return ERR_PTR(ret); +} + +static void _ion_buffer_destroy(struct ion_buffer *buffer) +{ + if (WARN_ON(buffer->kmap_cnt > 0)) + buffer->heap->ops->unmap_kernel(buffer->heap, buffer); + buffer->heap->ops->unmap_dma(buffer->heap, buffer); + buffer->heap->ops->free(buffer); + if (buffer->flags & ION_FLAG_CACHED) + kfree(buffer->dirty); + kfree(buffer); +} + +static void ion_buffer_destroy(struct kref *kref) +{ + struct ion_buffer *buffer = container_of(kref, struct ion_buffer, ref); + struct ion_heap *heap = buffer->heap; + struct ion_device *dev = buffer->dev; + + mutex_lock(&dev->buffer_lock); + rb_erase(&buffer->node, &dev->buffers); + mutex_unlock(&dev->buffer_lock); + + if (heap->flags & ION_HEAP_FLAG_DEFER_FREE) { + rt_mutex_lock(&heap->lock); + list_add(&buffer->list, &heap->free_list); + rt_mutex_unlock(&heap->lock); + wake_up(&heap->waitqueue); + return; + } + _ion_buffer_destroy(buffer); +} + +static void ion_buffer_get(struct ion_buffer *buffer) +{ + kref_get(&buffer->ref); +} + +static int ion_buffer_put(struct ion_buffer *buffer) +{ + return kref_put(&buffer->ref, ion_buffer_destroy); +} + +static void ion_buffer_add_to_handle(struct ion_buffer *buffer) +{ + mutex_lock(&buffer->lock); + buffer->handle_count++; + mutex_unlock(&buffer->lock); +} + +static void ion_buffer_remove_from_handle(struct ion_buffer *buffer) +{ + /* + * when a buffer is removed from a handle, if it is not in + * any other handles, copy the taskcomm and the pid of the + * process it's being removed from into the buffer. At this + * point there will be no way to track what processes this buffer is + * being used by, it only exists as a dma_buf file descriptor. + * The taskcomm and pid can provide a debug hint as to where this fd + * is in the system + */ + mutex_lock(&buffer->lock); + buffer->handle_count--; + BUG_ON(buffer->handle_count < 0); + if (!buffer->handle_count) { + struct task_struct *task; + + task = current->group_leader; + get_task_comm(buffer->task_comm, task); + buffer->pid = task_pid_nr(task); + } + mutex_unlock(&buffer->lock); +} + +static struct ion_handle *ion_handle_create(struct ion_client *client, + struct ion_buffer *buffer) +{ + struct ion_handle *handle; + + handle = kzalloc(sizeof(struct ion_handle), GFP_KERNEL); + if (!handle) + return ERR_PTR(-ENOMEM); + kref_init(&handle->ref); + rb_init_node(&handle->node); + handle->client = client; + ion_buffer_get(buffer); + ion_buffer_add_to_handle(buffer); + handle->buffer = buffer; + + return handle; +} + +static void ion_handle_kmap_put(struct ion_handle *); + +static void ion_handle_destroy(struct kref *kref) +{ + struct ion_handle *handle = container_of(kref, struct ion_handle, ref); + struct ion_client *client = handle->client; + struct ion_buffer *buffer = handle->buffer; + + mutex_lock(&buffer->lock); + while (handle->kmap_cnt) + ion_handle_kmap_put(handle); + mutex_unlock(&buffer->lock); + + if (!RB_EMPTY_NODE(&handle->node)) + rb_erase(&handle->node, &client->handles); + + ion_buffer_remove_from_handle(buffer); + ion_buffer_put(buffer); + + kfree(handle); +} + +struct ion_buffer *ion_handle_buffer(struct ion_handle *handle) +{ + return handle->buffer; +} + +static void ion_handle_get(struct ion_handle *handle) +{ + kref_get(&handle->ref); +} + +static int ion_handle_put(struct ion_handle *handle) +{ + return kref_put(&handle->ref, ion_handle_destroy); +} + +static struct ion_handle *ion_handle_lookup(struct ion_client *client, + struct ion_buffer *buffer) +{ + struct rb_node *n; + + for (n = rb_first(&client->handles); n; n = rb_next(n)) { + struct ion_handle *handle = rb_entry(n, struct ion_handle, + node); + if (handle->buffer == buffer) + return handle; + } + return NULL; +} + +static bool ion_handle_validate(struct ion_client *client, struct ion_handle *handle) +{ + struct rb_node *n = client->handles.rb_node; + + while (n) { + struct ion_handle *handle_node = rb_entry(n, struct ion_handle, + node); + if (handle < handle_node) + n = n->rb_left; + else if (handle > handle_node) + n = n->rb_right; + else + return true; + } + return false; +} + +static void ion_handle_add(struct ion_client *client, struct ion_handle *handle) +{ + struct rb_node **p = &client->handles.rb_node; + struct rb_node *parent = NULL; + struct ion_handle *entry; + + while (*p) { + parent = *p; + entry = rb_entry(parent, struct ion_handle, node); + + if (handle < entry) + p = &(*p)->rb_left; + else if (handle > entry) + p = &(*p)->rb_right; + else + WARN(1, "%s: buffer already found.", __func__); + } + + rb_link_node(&handle->node, parent, p); + rb_insert_color(&handle->node, &client->handles); +} + +struct ion_handle *ion_alloc(struct ion_client *client, size_t len, + size_t align, unsigned int heap_id_mask, + unsigned int flags) +{ + struct ion_handle *handle; + struct ion_device *dev = client->dev; + struct ion_buffer *buffer = NULL; + struct ion_heap *heap; + + pr_debug("%s: len %d align %d heap_id_mask %u flags %x\n", __func__, + len, align, heap_id_mask, flags); + /* + * traverse the list of heaps available in this system in priority + * order. If the heap type is supported by the client, and matches the + * request of the caller allocate from it. Repeat until allocate has + * succeeded or all heaps have been tried + */ + if (WARN_ON(!len)) + return ERR_PTR(-EINVAL); + + len = PAGE_ALIGN(len); + + down_read(&dev->lock); + plist_for_each_entry(heap, &dev->heaps, node) { + /* if the caller didn't specify this heap id */ + if (!((1 << heap->id) & heap_id_mask)) + continue; + buffer = ion_buffer_create(heap, dev, len, align, flags); + if (!IS_ERR_OR_NULL(buffer)) + break; + } + up_read(&dev->lock); + + if (buffer == NULL) + return ERR_PTR(-ENODEV); + + if (IS_ERR(buffer)) + return ERR_PTR(PTR_ERR(buffer)); + + handle = ion_handle_create(client, buffer); + + /* + * ion_buffer_create will create a buffer with a ref_cnt of 1, + * and ion_handle_create will take a second reference, drop one here + */ + ion_buffer_put(buffer); + + if (!IS_ERR(handle)) { + mutex_lock(&client->lock); + ion_handle_add(client, handle); + mutex_unlock(&client->lock); + } + + + return handle; +} +EXPORT_SYMBOL(ion_alloc); + +void ion_free(struct ion_client *client, struct ion_handle *handle) +{ + bool valid_handle; + + BUG_ON(client != handle->client); + + mutex_lock(&client->lock); + valid_handle = ion_handle_validate(client, handle); + + if (!valid_handle) { + WARN(1, "%s: invalid handle passed to free.\n", __func__); + mutex_unlock(&client->lock); + return; + } + ion_handle_put(handle); + mutex_unlock(&client->lock); +} +EXPORT_SYMBOL(ion_free); + +int ion_phys(struct ion_client *client, struct ion_handle *handle, + ion_phys_addr_t *addr, size_t *len) +{ + struct ion_buffer *buffer; + int ret; + + mutex_lock(&client->lock); + if (!ion_handle_validate(client, handle)) { + mutex_unlock(&client->lock); + return -EINVAL; + } + + buffer = handle->buffer; + + if (!buffer->heap->ops->phys) { + pr_err("%s: ion_phys is not implemented by this heap.\n", + __func__); + mutex_unlock(&client->lock); + return -ENODEV; + } + mutex_unlock(&client->lock); + ret = buffer->heap->ops->phys(buffer->heap, buffer, addr, len); + return ret; +} +EXPORT_SYMBOL(ion_phys); + +static void *ion_buffer_kmap_get(struct ion_buffer *buffer) +{ + void *vaddr; + + if (buffer->kmap_cnt) { + buffer->kmap_cnt++; + return buffer->vaddr; + } + vaddr = buffer->heap->ops->map_kernel(buffer->heap, buffer); + if (IS_ERR_OR_NULL(vaddr)) + return vaddr; + buffer->vaddr = vaddr; + buffer->kmap_cnt++; + return vaddr; +} + +static void *ion_handle_kmap_get(struct ion_handle *handle) +{ + struct ion_buffer *buffer = handle->buffer; + void *vaddr; + + if (handle->kmap_cnt) { + handle->kmap_cnt++; + return buffer->vaddr; + } + vaddr = ion_buffer_kmap_get(buffer); + if (IS_ERR_OR_NULL(vaddr)) + return vaddr; + handle->kmap_cnt++; + return vaddr; +} + +static void ion_buffer_kmap_put(struct ion_buffer *buffer) +{ + buffer->kmap_cnt--; + if (!buffer->kmap_cnt) { + buffer->heap->ops->unmap_kernel(buffer->heap, buffer); + buffer->vaddr = NULL; + } +} + +static void ion_handle_kmap_put(struct ion_handle *handle) +{ + struct ion_buffer *buffer = handle->buffer; + + handle->kmap_cnt--; + if (!handle->kmap_cnt) + ion_buffer_kmap_put(buffer); +} + +void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle) +{ + struct ion_buffer *buffer; + void *vaddr; + + mutex_lock(&client->lock); + if (!ion_handle_validate(client, handle)) { + pr_err("%s: invalid handle passed to map_kernel.\n", + __func__); + mutex_unlock(&client->lock); + return ERR_PTR(-EINVAL); + } + + buffer = handle->buffer; + + if (!handle->buffer->heap->ops->map_kernel) { + pr_err("%s: map_kernel is not implemented by this heap.\n", + __func__); + mutex_unlock(&client->lock); + return ERR_PTR(-ENODEV); + } + + mutex_lock(&buffer->lock); + vaddr = ion_handle_kmap_get(handle); + mutex_unlock(&buffer->lock); + mutex_unlock(&client->lock); + return vaddr; +} +EXPORT_SYMBOL(ion_map_kernel); + +void ion_unmap_kernel(struct ion_client *client, struct ion_handle *handle) +{ + struct ion_buffer *buffer; + + mutex_lock(&client->lock); + buffer = handle->buffer; + mutex_lock(&buffer->lock); + ion_handle_kmap_put(handle); + mutex_unlock(&buffer->lock); + mutex_unlock(&client->lock); +} +EXPORT_SYMBOL(ion_unmap_kernel); + +static int ion_debug_client_show(struct seq_file *s, void *unused) +{ + struct ion_client *client = s->private; + struct rb_node *n; + size_t sizes[ION_NUM_HEAP_IDS] = {0}; + const char *names[ION_NUM_HEAP_IDS] = {0}; + int i; + + mutex_lock(&client->lock); + for (n = rb_first(&client->handles); n; n = rb_next(n)) { + struct ion_handle *handle = rb_entry(n, struct ion_handle, + node); + unsigned int id = handle->buffer->heap->id; + + if (!names[id]) + names[id] = handle->buffer->heap->name; + sizes[id] += handle->buffer->size; + } + mutex_unlock(&client->lock); + + seq_printf(s, "%16.16s: %16.16s\n", "heap_name", "size_in_bytes"); + for (i = 0; i < ION_NUM_HEAP_IDS; i++) { + if (!names[i]) + continue; + seq_printf(s, "%16.16s: %16u\n", names[i], sizes[i]); + } + return 0; +} + +static int ion_debug_client_open(struct inode *inode, struct file *file) +{ + return single_open(file, ion_debug_client_show, inode->i_private); +} + +static const struct file_operations debug_client_fops = { + .open = ion_debug_client_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +struct ion_client *ion_client_create(struct ion_device *dev, + const char *name) +{ + struct ion_client *client; + struct task_struct *task; + struct rb_node **p; + struct rb_node *parent = NULL; + struct ion_client *entry; + char debug_name[64]; + pid_t pid; + + get_task_struct(current->group_leader); + task_lock(current->group_leader); + pid = task_pid_nr(current->group_leader); + /* don't bother to store task struct for kernel threads, + they can't be killed anyway */ + if (current->group_leader->flags & PF_KTHREAD) { + put_task_struct(current->group_leader); + task = NULL; + } else { + task = current->group_leader; + } + task_unlock(current->group_leader); + + client = kzalloc(sizeof(struct ion_client), GFP_KERNEL); + if (!client) { + if (task) + put_task_struct(current->group_leader); + return ERR_PTR(-ENOMEM); + } + + client->dev = dev; + client->handles = RB_ROOT; + mutex_init(&client->lock); + client->name = name; + client->task = task; + client->pid = pid; + + down_write(&dev->lock); + p = &dev->clients.rb_node; + while (*p) { + parent = *p; + entry = rb_entry(parent, struct ion_client, node); + + if (client < entry) + p = &(*p)->rb_left; + else if (client > entry) + p = &(*p)->rb_right; + } + rb_link_node(&client->node, parent, p); + rb_insert_color(&client->node, &dev->clients); + + snprintf(debug_name, 64, "%u", client->pid); + client->debug_root = debugfs_create_file(debug_name, 0664, + dev->debug_root, client, + &debug_client_fops); + up_write(&dev->lock); + + return client; +} +EXPORT_SYMBOL(ion_client_create); + +void ion_client_destroy(struct ion_client *client) +{ + struct ion_device *dev = client->dev; + struct rb_node *n; + + pr_debug("%s: %d\n", __func__, __LINE__); + while ((n = rb_first(&client->handles))) { + struct ion_handle *handle = rb_entry(n, struct ion_handle, + node); + ion_handle_destroy(&handle->ref); + } + down_write(&dev->lock); + if (client->task) + put_task_struct(client->task); + rb_erase(&client->node, &dev->clients); + debugfs_remove_recursive(client->debug_root); + up_write(&dev->lock); + + kfree(client); +} +EXPORT_SYMBOL(ion_client_destroy); + +struct sg_table *ion_sg_table(struct ion_client *client, + struct ion_handle *handle) +{ + struct ion_buffer *buffer; + struct sg_table *table; + + mutex_lock(&client->lock); + if (!ion_handle_validate(client, handle)) { + pr_err("%s: invalid handle passed to map_dma.\n", + __func__); + mutex_unlock(&client->lock); + return ERR_PTR(-EINVAL); + } + buffer = handle->buffer; + table = buffer->sg_table; + mutex_unlock(&client->lock); + return table; +} +EXPORT_SYMBOL(ion_sg_table); + +static void ion_buffer_sync_for_device(struct ion_buffer *buffer, + struct device *dev, + enum dma_data_direction direction); + +static struct sg_table *ion_map_dma_buf(struct dma_buf_attachment *attachment, + enum dma_data_direction direction) +{ + struct dma_buf *dmabuf = attachment->dmabuf; + struct ion_buffer *buffer = dmabuf->priv; + + ion_buffer_sync_for_device(buffer, attachment->dev, direction); + return buffer->sg_table; +} + +static void ion_unmap_dma_buf(struct dma_buf_attachment *attachment, + struct sg_table *table, + enum dma_data_direction direction) +{ +} + +static int ion_buffer_alloc_dirty(struct ion_buffer *buffer) +{ + unsigned long pages = buffer->sg_table->nents; + unsigned long length = (pages + BITS_PER_LONG - 1)/BITS_PER_LONG; + + buffer->dirty = kzalloc(length * sizeof(unsigned long), GFP_KERNEL); + if (!buffer->dirty) + return -ENOMEM; + return 0; +} + +struct ion_vma_list { + struct list_head list; + struct vm_area_struct *vma; +}; + +static void ion_buffer_sync_for_device(struct ion_buffer *buffer, + struct device *dev, + enum dma_data_direction dir) +{ + struct scatterlist *sg; + int i; + struct ion_vma_list *vma_list; + + pr_debug("%s: syncing for device %s\n", __func__, + dev ? dev_name(dev) : "null"); + + if (!ion_buffer_fault_user_mappings(buffer)) + return; + + mutex_lock(&buffer->lock); + for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, i) { + if (!test_bit(i, buffer->dirty)) + continue; + dma_sync_sg_for_device(dev, sg, 1, dir); + clear_bit(i, buffer->dirty); + } + list_for_each_entry(vma_list, &buffer->vmas, list) { + struct vm_area_struct *vma = vma_list->vma; + + zap_page_range(vma, vma->vm_start, vma->vm_end - vma->vm_start, + NULL); + } + mutex_unlock(&buffer->lock); +} + +int ion_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct ion_buffer *buffer = vma->vm_private_data; + struct scatterlist *sg; + int i; + + mutex_lock(&buffer->lock); + set_bit(vmf->pgoff, buffer->dirty); + + for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, i) { + if (i != vmf->pgoff) + continue; + dma_sync_sg_for_cpu(NULL, sg, 1, DMA_BIDIRECTIONAL); + vm_insert_page(vma, (unsigned long)vmf->virtual_address, + sg_page(sg)); + break; + } + mutex_unlock(&buffer->lock); + return VM_FAULT_NOPAGE; +} + +static void ion_vm_open(struct vm_area_struct *vma) +{ + struct ion_buffer *buffer = vma->vm_private_data; + struct ion_vma_list *vma_list; + + vma_list = kmalloc(sizeof(struct ion_vma_list), GFP_KERNEL); + if (!vma_list) + return; + vma_list->vma = vma; + mutex_lock(&buffer->lock); + list_add(&vma_list->list, &buffer->vmas); + mutex_unlock(&buffer->lock); + pr_debug("%s: adding %p\n", __func__, vma); +} + +static void ion_vm_close(struct vm_area_struct *vma) +{ + struct ion_buffer *buffer = vma->vm_private_data; + struct ion_vma_list *vma_list, *tmp; + + pr_debug("%s\n", __func__); + mutex_lock(&buffer->lock); + list_for_each_entry_safe(vma_list, tmp, &buffer->vmas, list) { + if (vma_list->vma != vma) + continue; + list_del(&vma_list->list); + kfree(vma_list); + pr_debug("%s: deleting %p\n", __func__, vma); + break; + } + mutex_unlock(&buffer->lock); +} + +struct vm_operations_struct ion_vma_ops = { + .open = ion_vm_open, + .close = ion_vm_close, + .fault = ion_vm_fault, +}; + +static int ion_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) +{ + struct ion_buffer *buffer = dmabuf->priv; + int ret = 0; + + if (!buffer->heap->ops->map_user) { + pr_err("%s: this heap does not define a method for mapping " + "to userspace\n", __func__); + return -EINVAL; + } + + if (ion_buffer_fault_user_mappings(buffer)) { + vma->vm_private_data = buffer; + vma->vm_ops = &ion_vma_ops; + ion_vm_open(vma); + return 0; + } + + if (!(buffer->flags & ION_FLAG_CACHED)) + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + mutex_lock(&buffer->lock); + /* now map it to userspace */ + ret = buffer->heap->ops->map_user(buffer->heap, buffer, vma); + mutex_unlock(&buffer->lock); + + if (ret) + pr_err("%s: failure mapping buffer to userspace\n", + __func__); + + return ret; +} + +static void ion_dma_buf_release(struct dma_buf *dmabuf) +{ + struct ion_buffer *buffer = dmabuf->priv; + ion_buffer_put(buffer); +} + +static void *ion_dma_buf_kmap(struct dma_buf *dmabuf, unsigned long offset) +{ + struct ion_buffer *buffer = dmabuf->priv; + return buffer->vaddr + offset * PAGE_SIZE; +} + +static void ion_dma_buf_kunmap(struct dma_buf *dmabuf, unsigned long offset, + void *ptr) +{ + return; +} + +static int ion_dma_buf_begin_cpu_access(struct dma_buf *dmabuf, size_t start, + size_t len, + enum dma_data_direction direction) +{ + struct ion_buffer *buffer = dmabuf->priv; + void *vaddr; + + if (!buffer->heap->ops->map_kernel) { + pr_err("%s: map kernel is not implemented by this heap.\n", + __func__); + return -ENODEV; + } + + mutex_lock(&buffer->lock); + vaddr = ion_buffer_kmap_get(buffer); + mutex_unlock(&buffer->lock); + if (IS_ERR(vaddr)) + return PTR_ERR(vaddr); + if (!vaddr) + return -ENOMEM; + return 0; +} + +static void ion_dma_buf_end_cpu_access(struct dma_buf *dmabuf, size_t start, + size_t len, + enum dma_data_direction direction) +{ + struct ion_buffer *buffer = dmabuf->priv; + + mutex_lock(&buffer->lock); + ion_buffer_kmap_put(buffer); + mutex_unlock(&buffer->lock); +} + +struct dma_buf_ops dma_buf_ops = { + .map_dma_buf = ion_map_dma_buf, + .unmap_dma_buf = ion_unmap_dma_buf, + .mmap = ion_mmap, + .release = ion_dma_buf_release, + .begin_cpu_access = ion_dma_buf_begin_cpu_access, + .end_cpu_access = ion_dma_buf_end_cpu_access, + .kmap_atomic = ion_dma_buf_kmap, + .kunmap_atomic = ion_dma_buf_kunmap, + .kmap = ion_dma_buf_kmap, + .kunmap = ion_dma_buf_kunmap, +}; + +struct dma_buf *ion_share_dma_buf(struct ion_client *client, + struct ion_handle *handle) +{ + struct ion_buffer *buffer; + struct dma_buf *dmabuf; + bool valid_handle; + + mutex_lock(&client->lock); + valid_handle = ion_handle_validate(client, handle); + mutex_unlock(&client->lock); + if (!valid_handle) { + WARN(1, "%s: invalid handle passed to share.\n", __func__); + return ERR_PTR(-EINVAL); + } + + buffer = handle->buffer; + ion_buffer_get(buffer); + dmabuf = dma_buf_export(buffer, &dma_buf_ops, buffer->size, O_RDWR); + if (IS_ERR(dmabuf)) { + ion_buffer_put(buffer); + return dmabuf; + } + + return dmabuf; +} +EXPORT_SYMBOL(ion_share_dma_buf); + +int ion_share_dma_buf_fd(struct ion_client *client, struct ion_handle *handle) +{ + struct dma_buf *dmabuf; + int fd; + + dmabuf = ion_share_dma_buf(client, handle); + if (IS_ERR(dmabuf)) + return PTR_ERR(dmabuf); + + fd = dma_buf_fd(dmabuf, O_CLOEXEC); + if (fd < 0) + dma_buf_put(dmabuf); + + return fd; +} +EXPORT_SYMBOL(ion_share_dma_buf_fd); + +struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd) +{ + struct dma_buf *dmabuf; + struct ion_buffer *buffer; + struct ion_handle *handle; + + dmabuf = dma_buf_get(fd); + if (IS_ERR_OR_NULL(dmabuf)) + return ERR_PTR(PTR_ERR(dmabuf)); + /* if this memory came from ion */ + + if (dmabuf->ops != &dma_buf_ops) { + pr_err("%s: can not import dmabuf from another exporter\n", + __func__); + dma_buf_put(dmabuf); + return ERR_PTR(-EINVAL); + } + buffer = dmabuf->priv; + + mutex_lock(&client->lock); + /* if a handle exists for this buffer just take a reference to it */ + handle = ion_handle_lookup(client, buffer); + if (!IS_ERR_OR_NULL(handle)) { + ion_handle_get(handle); + goto end; + } + handle = ion_handle_create(client, buffer); + if (IS_ERR_OR_NULL(handle)) + goto end; + ion_handle_add(client, handle); +end: + mutex_unlock(&client->lock); + dma_buf_put(dmabuf); + return handle; +} +EXPORT_SYMBOL(ion_import_dma_buf); + +static int ion_sync_for_device(struct ion_client *client, int fd) +{ + struct dma_buf *dmabuf; + struct ion_buffer *buffer; + + dmabuf = dma_buf_get(fd); + if (IS_ERR_OR_NULL(dmabuf)) + return PTR_ERR(dmabuf); + + /* if this memory came from ion */ + if (dmabuf->ops != &dma_buf_ops) { + pr_err("%s: can not sync dmabuf from another exporter\n", + __func__); + dma_buf_put(dmabuf); + return -EINVAL; + } + buffer = dmabuf->priv; + + dma_sync_sg_for_device(NULL, buffer->sg_table->sgl, + buffer->sg_table->nents, DMA_BIDIRECTIONAL); + dma_buf_put(dmabuf); + return 0; +} + +static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct ion_client *client = filp->private_data; + + switch (cmd) { + case ION_IOC_ALLOC: + { + struct ion_allocation_data data; + + if (copy_from_user(&data, (void __user *)arg, sizeof(data))) + return -EFAULT; + data.handle = ion_alloc(client, data.len, data.align, + data.heap_id_mask, data.flags); + + if (IS_ERR(data.handle)) + return PTR_ERR(data.handle); + + if (copy_to_user((void __user *)arg, &data, sizeof(data))) { + ion_free(client, data.handle); + return -EFAULT; + } + break; + } + case ION_IOC_FREE: + { + struct ion_handle_data data; + bool valid; + + if (copy_from_user(&data, (void __user *)arg, + sizeof(struct ion_handle_data))) + return -EFAULT; + mutex_lock(&client->lock); + valid = ion_handle_validate(client, data.handle); + mutex_unlock(&client->lock); + if (!valid) + return -EINVAL; + ion_free(client, data.handle); + break; + } + case ION_IOC_SHARE: + case ION_IOC_MAP: + { + struct ion_fd_data data; + + if (copy_from_user(&data, (void __user *)arg, sizeof(data))) + return -EFAULT; + data.fd = ion_share_dma_buf_fd(client, data.handle); + if (copy_to_user((void __user *)arg, &data, sizeof(data))) + return -EFAULT; + if (data.fd < 0) + return data.fd; + break; + } + case ION_IOC_IMPORT: + { + struct ion_fd_data data; + int ret = 0; + if (copy_from_user(&data, (void __user *)arg, + sizeof(struct ion_fd_data))) + return -EFAULT; + data.handle = ion_import_dma_buf(client, data.fd); + if (IS_ERR(data.handle)) { + ret = PTR_ERR(data.handle); + data.handle = NULL; + } + if (copy_to_user((void __user *)arg, &data, + sizeof(struct ion_fd_data))) + return -EFAULT; + if (ret < 0) + return ret; + break; + } + case ION_IOC_SYNC: + { + struct ion_fd_data data; + if (copy_from_user(&data, (void __user *)arg, + sizeof(struct ion_fd_data))) + return -EFAULT; + ion_sync_for_device(client, data.fd); + break; + } + case ION_IOC_CUSTOM: + { + struct ion_device *dev = client->dev; + struct ion_custom_data data; + + if (!dev->custom_ioctl) + return -ENOTTY; + if (copy_from_user(&data, (void __user *)arg, + sizeof(struct ion_custom_data))) + return -EFAULT; + return dev->custom_ioctl(client, data.cmd, data.arg); + } + default: + return -ENOTTY; + } + return 0; +} + +static int ion_release(struct inode *inode, struct file *file) +{ + struct ion_client *client = file->private_data; + + pr_debug("%s: %d\n", __func__, __LINE__); + ion_client_destroy(client); + return 0; +} + +static int ion_open(struct inode *inode, struct file *file) +{ + struct miscdevice *miscdev = file->private_data; + struct ion_device *dev = container_of(miscdev, struct ion_device, dev); + struct ion_client *client; + + pr_debug("%s: %d\n", __func__, __LINE__); + client = ion_client_create(dev, "user"); + if (IS_ERR_OR_NULL(client)) + return PTR_ERR(client); + file->private_data = client; + + return 0; +} + +static const struct file_operations ion_fops = { + .owner = THIS_MODULE, + .open = ion_open, + .release = ion_release, + .unlocked_ioctl = ion_ioctl, +}; + +static size_t ion_debug_heap_total(struct ion_client *client, + unsigned int id) +{ + size_t size = 0; + struct rb_node *n; + + mutex_lock(&client->lock); + for (n = rb_first(&client->handles); n; n = rb_next(n)) { + struct ion_handle *handle = rb_entry(n, + struct ion_handle, + node); + if (handle->buffer->heap->id == id) + size += handle->buffer->size; + } + mutex_unlock(&client->lock); + return size; +} + +static int ion_debug_heap_show(struct seq_file *s, void *unused) +{ + struct ion_heap *heap = s->private; + struct ion_device *dev = heap->dev; + struct rb_node *n; + size_t total_size = 0; + size_t total_orphaned_size = 0; + + seq_printf(s, "%16.s %16.s %16.s\n", "client", "pid", "size"); + seq_printf(s, "----------------------------------------------------\n"); + + for (n = rb_first(&dev->clients); n; n = rb_next(n)) { + struct ion_client *client = rb_entry(n, struct ion_client, + node); + size_t size = ion_debug_heap_total(client, heap->id); + if (!size) + continue; + if (client->task) { + char task_comm[TASK_COMM_LEN]; + + get_task_comm(task_comm, client->task); + seq_printf(s, "%16.s %16u %16u\n", task_comm, + client->pid, size); + } else { + seq_printf(s, "%16.s %16u %16u\n", client->name, + client->pid, size); + } + } + seq_printf(s, "----------------------------------------------------\n"); + seq_printf(s, "orphaned allocations (info is from last known client):" + "\n"); + mutex_lock(&dev->buffer_lock); + for (n = rb_first(&dev->buffers); n; n = rb_next(n)) { + struct ion_buffer *buffer = rb_entry(n, struct ion_buffer, + node); + if (buffer->heap->id != heap->id) + continue; + total_size += buffer->size; + if (!buffer->handle_count) { + seq_printf(s, "%16.s %16u %16u %d %d\n", buffer->task_comm, + buffer->pid, buffer->size, buffer->kmap_cnt, + atomic_read(&buffer->ref.refcount)); + total_orphaned_size += buffer->size; + } + } + mutex_unlock(&dev->buffer_lock); + seq_printf(s, "----------------------------------------------------\n"); + seq_printf(s, "%16.s %16u\n", "total orphaned", + total_orphaned_size); + seq_printf(s, "%16.s %16u\n", "total ", total_size); + seq_printf(s, "----------------------------------------------------\n"); + + if (heap->debug_show) + heap->debug_show(heap, s, unused); + + return 0; +} + +static int ion_debug_heap_open(struct inode *inode, struct file *file) +{ + return single_open(file, ion_debug_heap_show, inode->i_private); +} + +static const struct file_operations debug_heap_fops = { + .open = ion_debug_heap_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static size_t ion_heap_free_list_is_empty(struct ion_heap *heap) +{ + bool is_empty; + + rt_mutex_lock(&heap->lock); + is_empty = list_empty(&heap->free_list); + rt_mutex_unlock(&heap->lock); + + return is_empty; +} + +static int ion_heap_deferred_free(void *data) +{ + struct ion_heap *heap = data; + + while (true) { + struct ion_buffer *buffer; + + wait_event_freezable(heap->waitqueue, + !ion_heap_free_list_is_empty(heap)); + + rt_mutex_lock(&heap->lock); + if (list_empty(&heap->free_list)) { + rt_mutex_unlock(&heap->lock); + continue; + } + buffer = list_first_entry(&heap->free_list, struct ion_buffer, + list); + list_del(&buffer->list); + rt_mutex_unlock(&heap->lock); + _ion_buffer_destroy(buffer); + } + + return 0; +} + +static bool ion_heap_drain_freelist(struct ion_heap *heap) +{ + struct ion_buffer *buffer, *tmp; + + if (ion_heap_free_list_is_empty(heap)) + return false; + rt_mutex_lock(&heap->lock); + list_for_each_entry_safe(buffer, tmp, &heap->free_list, list) { + _ion_buffer_destroy(buffer); + list_del(&buffer->list); + } + BUG_ON(!list_empty(&heap->free_list)); + rt_mutex_unlock(&heap->lock); + + + return true; +} + +void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap) +{ + struct sched_param param = { .sched_priority = 0 }; + + if (!heap->ops->allocate || !heap->ops->free || !heap->ops->map_dma || + !heap->ops->unmap_dma) + pr_err("%s: can not add heap with invalid ops struct.\n", + __func__); + + if (heap->flags & ION_HEAP_FLAG_DEFER_FREE) { + INIT_LIST_HEAD(&heap->free_list); + rt_mutex_init(&heap->lock); + init_waitqueue_head(&heap->waitqueue); + heap->task = kthread_run(ion_heap_deferred_free, heap, + "%s", heap->name); + sched_setscheduler(heap->task, SCHED_IDLE, ¶m); + if (IS_ERR(heap->task)) + pr_err("%s: creating thread for deferred free failed\n", + __func__); + } + + heap->dev = dev; + down_write(&dev->lock); + /* use negative heap->id to reverse the priority -- when traversing + the list later attempt higher id numbers first */ + plist_node_init(&heap->node, -heap->id); + plist_add(&heap->node, &dev->heaps); + debugfs_create_file(heap->name, 0664, dev->debug_root, heap, + &debug_heap_fops); + up_write(&dev->lock); +} + +struct ion_device *ion_device_create(long (*custom_ioctl) + (struct ion_client *client, + unsigned int cmd, + unsigned long arg)) +{ + struct ion_device *idev; + int ret; + + idev = kzalloc(sizeof(struct ion_device), GFP_KERNEL); + if (!idev) + return ERR_PTR(-ENOMEM); + + idev->dev.minor = MISC_DYNAMIC_MINOR; + idev->dev.name = "ion"; + idev->dev.fops = &ion_fops; + idev->dev.parent = NULL; + ret = misc_register(&idev->dev); + if (ret) { + pr_err("ion: failed to register misc device.\n"); + return ERR_PTR(ret); + } + + idev->debug_root = debugfs_create_dir("ion", NULL); + if (IS_ERR_OR_NULL(idev->debug_root)) + pr_err("ion: failed to create debug files.\n"); + + idev->custom_ioctl = custom_ioctl; + idev->buffers = RB_ROOT; + mutex_init(&idev->buffer_lock); + init_rwsem(&idev->lock); + plist_head_init(&idev->heaps); + idev->clients = RB_ROOT; + return idev; +} + +void ion_device_destroy(struct ion_device *dev) +{ + misc_deregister(&dev->dev); + /* XXX need to free the heaps and clients ? */ + kfree(dev); +} + +void __init ion_reserve(struct ion_platform_data *data) +{ + int i; + + for (i = 0; i < data->nr; i++) { + if (data->heaps[i].size == 0) + continue; + + if (data->heaps[i].base == 0) { + phys_addr_t paddr; + paddr = memblock_alloc_base(data->heaps[i].size, + data->heaps[i].align, + MEMBLOCK_ALLOC_ANYWHERE); + if (!paddr) { + pr_err("%s: error allocating memblock for " + "heap %d\n", + __func__, i); + continue; + } + data->heaps[i].base = paddr; + } else { + int ret = memblock_reserve(data->heaps[i].base, + data->heaps[i].size); + if (ret) + pr_err("memblock reserve of %x@%lx failed\n", + data->heaps[i].size, + data->heaps[i].base); + } + pr_info("%s: %s reserved base %lx size %d\n", __func__, + data->heaps[i].name, + data->heaps[i].base, + data->heaps[i].size); + } +} diff --git a/drivers/gpu/ion/ion_carveout_heap.c b/drivers/gpu/ion/ion_carveout_heap.c new file mode 100644 index 00000000..ce8d3119 --- /dev/null +++ b/drivers/gpu/ion/ion_carveout_heap.c @@ -0,0 +1,182 @@ +/* + * drivers/gpu/ion/ion_carveout_heap.c + * + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ion_priv.h" + +#include + +struct ion_carveout_heap { + struct ion_heap heap; + struct gen_pool *pool; + ion_phys_addr_t base; +}; + +ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap, + unsigned long size, + unsigned long align) +{ + struct ion_carveout_heap *carveout_heap = + container_of(heap, struct ion_carveout_heap, heap); + unsigned long offset = gen_pool_alloc(carveout_heap->pool, size); + + if (!offset) + return ION_CARVEOUT_ALLOCATE_FAIL; + + return offset; +} + +void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr, + unsigned long size) +{ + struct ion_carveout_heap *carveout_heap = + container_of(heap, struct ion_carveout_heap, heap); + + if (addr == ION_CARVEOUT_ALLOCATE_FAIL) + return; + gen_pool_free(carveout_heap->pool, addr, size); +} + +static int ion_carveout_heap_phys(struct ion_heap *heap, + struct ion_buffer *buffer, + ion_phys_addr_t *addr, size_t *len) +{ + *addr = buffer->priv_phys; + *len = buffer->size; + return 0; +} + +static int ion_carveout_heap_allocate(struct ion_heap *heap, + struct ion_buffer *buffer, + unsigned long size, unsigned long align, + unsigned long flags) +{ + buffer->priv_phys = ion_carveout_allocate(heap, size, align); + return buffer->priv_phys == ION_CARVEOUT_ALLOCATE_FAIL ? -ENOMEM : 0; +} + +static void ion_carveout_heap_free(struct ion_buffer *buffer) +{ + struct ion_heap *heap = buffer->heap; + + ion_carveout_free(heap, buffer->priv_phys, buffer->size); + buffer->priv_phys = ION_CARVEOUT_ALLOCATE_FAIL; +} + +struct sg_table *ion_carveout_heap_map_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + struct sg_table *table; + int ret; + + table = kzalloc(sizeof(struct sg_table), GFP_KERNEL); + if (!table) + return ERR_PTR(-ENOMEM); + ret = sg_alloc_table(table, 1, GFP_KERNEL); + if (ret) { + kfree(table); + return ERR_PTR(ret); + } + sg_set_page(table->sgl, phys_to_page(buffer->priv_phys), buffer->size, + 0); + return table; +} + +void ion_carveout_heap_unmap_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + sg_free_table(buffer->sg_table); +} + +void *ion_carveout_heap_map_kernel(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + int mtype = MT_MEMORY_NONCACHED; + + if (buffer->flags & ION_FLAG_CACHED) + mtype = MT_MEMORY; + + return __arm_ioremap(buffer->priv_phys, buffer->size, + mtype); +} + +void ion_carveout_heap_unmap_kernel(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + __arm_iounmap(buffer->vaddr); + buffer->vaddr = NULL; + return; +} + +int ion_carveout_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer, + struct vm_area_struct *vma) +{ + return remap_pfn_range(vma, vma->vm_start, + __phys_to_pfn(buffer->priv_phys) + vma->vm_pgoff, + vma->vm_end - vma->vm_start, + pgprot_noncached(vma->vm_page_prot)); +} + +static struct ion_heap_ops carveout_heap_ops = { + .allocate = ion_carveout_heap_allocate, + .free = ion_carveout_heap_free, + .phys = ion_carveout_heap_phys, + .map_dma = ion_carveout_heap_map_dma, + .unmap_dma = ion_carveout_heap_unmap_dma, + .map_user = ion_carveout_heap_map_user, + .map_kernel = ion_carveout_heap_map_kernel, + .unmap_kernel = ion_carveout_heap_unmap_kernel, +}; + +struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data) +{ + struct ion_carveout_heap *carveout_heap; + + carveout_heap = kzalloc(sizeof(struct ion_carveout_heap), GFP_KERNEL); + if (!carveout_heap) + return ERR_PTR(-ENOMEM); + + carveout_heap->pool = gen_pool_create(12, -1); + if (!carveout_heap->pool) { + kfree(carveout_heap); + return ERR_PTR(-ENOMEM); + } + carveout_heap->base = heap_data->base; + gen_pool_add(carveout_heap->pool, carveout_heap->base, heap_data->size, + -1); + carveout_heap->heap.ops = &carveout_heap_ops; + carveout_heap->heap.type = ION_HEAP_TYPE_CARVEOUT; + + return &carveout_heap->heap; +} + +void ion_carveout_heap_destroy(struct ion_heap *heap) +{ + struct ion_carveout_heap *carveout_heap = + container_of(heap, struct ion_carveout_heap, heap); + + gen_pool_destroy(carveout_heap->pool); + kfree(carveout_heap); + carveout_heap = NULL; +} diff --git a/drivers/gpu/ion/ion_chunk_heap.c b/drivers/gpu/ion/ion_chunk_heap.c new file mode 100644 index 00000000..7f482b62 --- /dev/null +++ b/drivers/gpu/ion/ion_chunk_heap.c @@ -0,0 +1,178 @@ +/* + * drivers/gpu/ion/ion_chunk_heap.c + * + * Copyright (C) 2012 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ion_priv.h" + +#include + +struct ion_chunk_heap { + struct ion_heap heap; + struct gen_pool *pool; + ion_phys_addr_t base; + unsigned long chunk_size; + unsigned long size; + unsigned long allocated; +}; + +static int ion_chunk_heap_allocate(struct ion_heap *heap, + struct ion_buffer *buffer, + unsigned long size, unsigned long align, + unsigned long flags) +{ + struct ion_chunk_heap *chunk_heap = + container_of(heap, struct ion_chunk_heap, heap); + struct sg_table *table; + struct scatterlist *sg; + int ret, i; + unsigned long num_chunks; + + if (ion_buffer_fault_user_mappings(buffer)) + return -ENOMEM; + + num_chunks = ALIGN(size, chunk_heap->chunk_size) / + chunk_heap->chunk_size; + buffer->size = num_chunks * chunk_heap->chunk_size; + + if (buffer->size > chunk_heap->size - chunk_heap->allocated) + return -ENOMEM; + + table = kzalloc(sizeof(struct sg_table), GFP_KERNEL); + if (!table) + return -ENOMEM; + ret = sg_alloc_table(table, num_chunks, GFP_KERNEL); + if (ret) { + kfree(table); + return ret; + } + + sg = table->sgl; + for (i = 0; i < num_chunks; i++) { + unsigned long paddr = gen_pool_alloc(chunk_heap->pool, + chunk_heap->chunk_size); + if (!paddr) + goto err; + sg_set_page(sg, phys_to_page(paddr), chunk_heap->chunk_size, 0); + sg = sg_next(sg); + } + + buffer->priv_virt = table; + chunk_heap->allocated += buffer->size; + return 0; +err: + sg = table->sgl; + for (i -= 1; i >= 0; i--) { + gen_pool_free(chunk_heap->pool, page_to_phys(sg_page(sg)), + sg_dma_len(sg)); + sg = sg_next(sg); + } + sg_free_table(table); + kfree(table); + return -ENOMEM; +} + +static void ion_chunk_heap_free(struct ion_buffer *buffer) +{ + struct ion_heap *heap = buffer->heap; + struct ion_chunk_heap *chunk_heap = + container_of(heap, struct ion_chunk_heap, heap); + struct sg_table *table = buffer->priv_virt; + struct scatterlist *sg; + int i; + + ion_heap_buffer_zero(buffer); + + for_each_sg(table->sgl, sg, table->nents, i) { + if (ion_buffer_cached(buffer)) + __dma_page_cpu_to_dev(sg_page(sg), 0, sg_dma_len(sg), + DMA_BIDIRECTIONAL); + gen_pool_free(chunk_heap->pool, page_to_phys(sg_page(sg)), + sg_dma_len(sg)); + } + chunk_heap->allocated -= buffer->size; + sg_free_table(table); + kfree(table); +} + +struct sg_table *ion_chunk_heap_map_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + return buffer->priv_virt; +} + +void ion_chunk_heap_unmap_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + return; +} + +static struct ion_heap_ops chunk_heap_ops = { + .allocate = ion_chunk_heap_allocate, + .free = ion_chunk_heap_free, + .map_dma = ion_chunk_heap_map_dma, + .unmap_dma = ion_chunk_heap_unmap_dma, + .map_user = ion_heap_map_user, + .map_kernel = ion_heap_map_kernel, + .unmap_kernel = ion_heap_unmap_kernel, +}; + +struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *heap_data) +{ + struct ion_chunk_heap *chunk_heap; + + chunk_heap = kzalloc(sizeof(struct ion_chunk_heap), GFP_KERNEL); + if (!chunk_heap) + return ERR_PTR(-ENOMEM); + + chunk_heap->chunk_size = (unsigned long)heap_data->priv; + chunk_heap->pool = gen_pool_create(get_order(chunk_heap->chunk_size) + + PAGE_SHIFT, -1); + if (!chunk_heap->pool) { + kfree(chunk_heap); + return ERR_PTR(-ENOMEM); + } + chunk_heap->base = heap_data->base; + chunk_heap->size = heap_data->size; + chunk_heap->allocated = 0; + __dma_page_cpu_to_dev(phys_to_page(heap_data->base), 0, heap_data->size, + DMA_BIDIRECTIONAL); + gen_pool_add(chunk_heap->pool, chunk_heap->base, heap_data->size, -1); + chunk_heap->heap.ops = &chunk_heap_ops; + chunk_heap->heap.type = ION_HEAP_TYPE_CHUNK; + chunk_heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE; + pr_info("%s: base %lu size %u align %ld\n", __func__, chunk_heap->base, + heap_data->size, heap_data->align); + + return &chunk_heap->heap; +} + +void ion_chunk_heap_destroy(struct ion_heap *heap) +{ + struct ion_chunk_heap *chunk_heap = + container_of(heap, struct ion_chunk_heap, heap); + + gen_pool_destroy(chunk_heap->pool); + kfree(chunk_heap); + chunk_heap = NULL; +} diff --git a/drivers/gpu/ion/ion_heap.c b/drivers/gpu/ion/ion_heap.c new file mode 100644 index 00000000..225ef946 --- /dev/null +++ b/drivers/gpu/ion/ion_heap.c @@ -0,0 +1,190 @@ +/* + * drivers/gpu/ion/ion_heap.c + * + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include "ion_priv.h" + +void *ion_heap_map_kernel(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + struct scatterlist *sg; + int i, j; + void *vaddr; + pgprot_t pgprot; + struct sg_table *table = buffer->sg_table; + int npages = PAGE_ALIGN(buffer->size) / PAGE_SIZE; + struct page **pages = vmalloc(sizeof(struct page *) * npages); + struct page **tmp = pages; + + if (!pages) + return 0; + + if (buffer->flags & ION_FLAG_CACHED) + pgprot = PAGE_KERNEL; + else + pgprot = pgprot_writecombine(PAGE_KERNEL); + + for_each_sg(table->sgl, sg, table->nents, i) { + int npages_this_entry = PAGE_ALIGN(sg_dma_len(sg)) / PAGE_SIZE; + struct page *page = sg_page(sg); + BUG_ON(i >= npages); + for (j = 0; j < npages_this_entry; j++) { + *(tmp++) = page++; + } + } + vaddr = vmap(pages, npages, VM_MAP, pgprot); + vfree(pages); + + return vaddr; +} + +void ion_heap_unmap_kernel(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + vunmap(buffer->vaddr); +} + +int ion_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer, + struct vm_area_struct *vma) +{ + struct sg_table *table = buffer->sg_table; + unsigned long addr = vma->vm_start; + unsigned long offset = vma->vm_pgoff * PAGE_SIZE; + struct scatterlist *sg; + int i; + + for_each_sg(table->sgl, sg, table->nents, i) { + struct page *page = sg_page(sg); + unsigned long remainder = vma->vm_end - addr; + unsigned long len = sg_dma_len(sg); + + if (offset >= sg_dma_len(sg)) { + offset -= sg_dma_len(sg); + continue; + } else if (offset) { + page += offset / PAGE_SIZE; + len = sg_dma_len(sg) - offset; + offset = 0; + } + len = min(len, remainder); + remap_pfn_range(vma, addr, page_to_pfn(page), len, + vma->vm_page_prot); + addr += len; + if (addr >= vma->vm_end) + return 0; + } + return 0; +} + +int ion_heap_buffer_zero(struct ion_buffer *buffer) +{ + struct sg_table *table = buffer->sg_table; + pgprot_t pgprot; + struct scatterlist *sg; + struct vm_struct *vm_struct; + int i, j, ret = 0; + + if (buffer->flags & ION_FLAG_CACHED) + pgprot = PAGE_KERNEL; + else + pgprot = pgprot_writecombine(PAGE_KERNEL); + + vm_struct = get_vm_area(PAGE_SIZE, VM_ALLOC); + if (!vm_struct) + return -ENOMEM; + + for_each_sg(table->sgl, sg, table->nents, i) { + struct page *page = sg_page(sg); + unsigned long len = sg_dma_len(sg); + + for (j = 0; j < len / PAGE_SIZE; j++) { + struct page *sub_page = page + j; + struct page **pages = &sub_page; + ret = map_vm_area(vm_struct, pgprot, &pages); + if (ret) + goto end; + memset(vm_struct->addr, 0, PAGE_SIZE); + unmap_kernel_range((unsigned long)vm_struct->addr, + PAGE_SIZE); + } + } +end: + free_vm_area(vm_struct); + return ret; +} + +struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data) +{ + struct ion_heap *heap = NULL; + + switch (heap_data->type) { + case ION_HEAP_TYPE_SYSTEM_CONTIG: + heap = ion_system_contig_heap_create(heap_data); + break; + case ION_HEAP_TYPE_SYSTEM: + heap = ion_system_heap_create(heap_data); + break; + case ION_HEAP_TYPE_CARVEOUT: + heap = ion_carveout_heap_create(heap_data); + break; + case ION_HEAP_TYPE_CHUNK: + heap = ion_chunk_heap_create(heap_data); + break; + default: + pr_err("%s: Invalid heap type %d\n", __func__, + heap_data->type); + return ERR_PTR(-EINVAL); + } + + if (IS_ERR_OR_NULL(heap)) { + pr_err("%s: error creating heap %s type %d base %lu size %u\n", + __func__, heap_data->name, heap_data->type, + heap_data->base, heap_data->size); + return ERR_PTR(-EINVAL); + } + + heap->name = heap_data->name; + heap->id = heap_data->id; + return heap; +} + +void ion_heap_destroy(struct ion_heap *heap) +{ + if (!heap) + return; + + switch (heap->type) { + case ION_HEAP_TYPE_SYSTEM_CONTIG: + ion_system_contig_heap_destroy(heap); + break; + case ION_HEAP_TYPE_SYSTEM: + ion_system_heap_destroy(heap); + break; + case ION_HEAP_TYPE_CARVEOUT: + ion_carveout_heap_destroy(heap); + break; + case ION_HEAP_TYPE_CHUNK: + ion_chunk_heap_destroy(heap); + break; + default: + pr_err("%s: Invalid heap type %d\n", __func__, + heap->type); + } +} diff --git a/drivers/gpu/ion/ion_page_pool.c b/drivers/gpu/ion/ion_page_pool.c new file mode 100644 index 00000000..cd57b30e --- /dev/null +++ b/drivers/gpu/ion/ion_page_pool.c @@ -0,0 +1,281 @@ +/* + * drivers/gpu/ion/ion_mem_pool.c + * + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ion_priv.h" + +/* #define DEBUG_PAGE_POOL_SHRINKER */ + +static struct plist_head pools = PLIST_HEAD_INIT(pools); +static struct shrinker shrinker; + +struct ion_page_pool_item { + struct page *page; + struct list_head list; +}; + +static void *ion_page_pool_alloc_pages(struct ion_page_pool *pool) +{ + struct page *page = alloc_pages(pool->gfp_mask, pool->order); + + if (!page) + return NULL; + /* this is only being used to flush the page for dma, + this api is not really suitable for calling from a driver + but no better way to flush a page for dma exist at this time */ + __dma_page_cpu_to_dev(page, 0, PAGE_SIZE << pool->order, + DMA_BIDIRECTIONAL); + return page; +} + +static void ion_page_pool_free_pages(struct ion_page_pool *pool, + struct page *page) +{ + __free_pages(page, pool->order); +} + +static int ion_page_pool_add(struct ion_page_pool *pool, struct page *page) +{ + struct ion_page_pool_item *item; + + item = kmalloc(sizeof(struct ion_page_pool_item), GFP_KERNEL); + if (!item) + return -ENOMEM; + + mutex_lock(&pool->mutex); + item->page = page; + if (PageHighMem(page)) { + list_add_tail(&item->list, &pool->high_items); + pool->high_count++; + } else { + list_add_tail(&item->list, &pool->low_items); + pool->low_count++; + } + mutex_unlock(&pool->mutex); + return 0; +} + +static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high) +{ + struct ion_page_pool_item *item; + struct page *page; + + if (high) { + BUG_ON(!pool->high_count); + item = list_first_entry(&pool->high_items, + struct ion_page_pool_item, list); + pool->high_count--; + } else { + BUG_ON(!pool->low_count); + item = list_first_entry(&pool->low_items, + struct ion_page_pool_item, list); + pool->low_count--; + } + + list_del(&item->list); + page = item->page; + kfree(item); + return page; +} + +void *ion_page_pool_alloc(struct ion_page_pool *pool) +{ + struct page *page = NULL; + + BUG_ON(!pool); + + mutex_lock(&pool->mutex); + if (pool->high_count) + page = ion_page_pool_remove(pool, true); + else if (pool->low_count) + page = ion_page_pool_remove(pool, false); + mutex_unlock(&pool->mutex); + + if (!page) + page = ion_page_pool_alloc_pages(pool); + + return page; +} + +void ion_page_pool_free(struct ion_page_pool *pool, struct page* page) +{ + int ret; + + ret = ion_page_pool_add(pool, page); + if (ret) + ion_page_pool_free_pages(pool, page); +} + +#ifdef DEBUG_PAGE_POOL_SHRINKER +static int debug_drop_pools_set(void *data, u64 val) +{ + struct shrink_control sc; + int objs; + + sc.gfp_mask = -1; + sc.nr_to_scan = 0; + + if (!val) + return 0; + + objs = shrinker.shrink(&shrinker, &sc); + sc.nr_to_scan = objs; + + shrinker.shrink(&shrinker, &sc); + return 0; +} + +static int debug_drop_pools_get(void *data, u64 *val) +{ + struct shrink_control sc; + int objs; + + sc.gfp_mask = -1; + sc.nr_to_scan = 0; + + objs = shrinker.shrink(&shrinker, &sc); + *val = objs; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(debug_drop_pools_fops, debug_drop_pools_get, + debug_drop_pools_set, "%llu\n"); + +static int debug_grow_pools_set(void *data, u64 val) +{ + struct ion_page_pool *pool; + struct page *page; + + plist_for_each_entry(pool, &pools, list) { + if (val != pool->list.prio) + continue; + page = ion_page_pool_alloc_pages(pool); + if (page) + ion_page_pool_add(pool, page); + } + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(debug_grow_pools_fops, debug_drop_pools_get, + debug_grow_pools_set, "%llu\n"); +#endif + +static int ion_page_pool_total(bool high) +{ + struct ion_page_pool *pool; + int total = 0; + + plist_for_each_entry(pool, &pools, list) { + total += high ? (pool->high_count + pool->low_count) * + (1 << pool->order) : + pool->low_count * (1 << pool->order); + } + return total; +} + +static int ion_page_pool_shrink(struct shrinker *shrinker, + struct shrink_control *sc) +{ + struct ion_page_pool *pool; + int nr_freed = 0; + int i; + bool high; + int nr_to_scan = sc->nr_to_scan; + + if (sc->gfp_mask & __GFP_HIGHMEM) + high = true; + + if (nr_to_scan == 0) + return ion_page_pool_total(high); + + plist_for_each_entry(pool, &pools, list) { + for (i = 0; i < nr_to_scan; i++) { + struct page *page; + + mutex_lock(&pool->mutex); + if (high && pool->high_count) { + page = ion_page_pool_remove(pool, true); + } else if (pool->low_count) { + page = ion_page_pool_remove(pool, false); + } else { + mutex_unlock(&pool->mutex); + break; + } + mutex_unlock(&pool->mutex); + ion_page_pool_free_pages(pool, page); + nr_freed += (1 << pool->order); + } + nr_to_scan -= i; + } + + return ion_page_pool_total(high); +} + +struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order) +{ + struct ion_page_pool *pool = kmalloc(sizeof(struct ion_page_pool), + GFP_KERNEL); + if (!pool) + return NULL; + pool->high_count = 0; + pool->low_count = 0; + INIT_LIST_HEAD(&pool->low_items); + INIT_LIST_HEAD(&pool->high_items); + pool->gfp_mask = gfp_mask; + pool->order = order; + mutex_init(&pool->mutex); + plist_node_init(&pool->list, order); + plist_add(&pool->list, &pools); + + return pool; +} + +void ion_page_pool_destroy(struct ion_page_pool *pool) +{ + plist_del(&pool->list, &pools); + kfree(pool); +} + +static int __init ion_page_pool_init(void) +{ + shrinker.shrink = ion_page_pool_shrink; + shrinker.seeks = DEFAULT_SEEKS; + shrinker.batch = 0; + register_shrinker(&shrinker); +#ifdef DEBUG_PAGE_POOL_SHRINKER + debugfs_create_file("ion_pools_shrink", 0644, NULL, NULL, + &debug_drop_pools_fops); + debugfs_create_file("ion_pools_grow", 0644, NULL, NULL, + &debug_grow_pools_fops); +#endif + return 0; +} + +static void __exit ion_page_pool_exit(void) +{ + unregister_shrinker(&shrinker); +} + +module_init(ion_page_pool_init); +module_exit(ion_page_pool_exit); diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h new file mode 100644 index 00000000..50568147 --- /dev/null +++ b/drivers/gpu/ion/ion_priv.h @@ -0,0 +1,288 @@ +/* + * drivers/gpu/ion/ion_priv.h + * + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _ION_PRIV_H +#define _ION_PRIV_H + +#include +#include +#include +#include +#include +#include +#include +#include + +struct ion_buffer *ion_handle_buffer(struct ion_handle *handle); + +/** + * struct ion_buffer - metadata for a particular buffer + * @ref: refernce count + * @node: node in the ion_device buffers tree + * @dev: back pointer to the ion_device + * @heap: back pointer to the heap the buffer came from + * @flags: buffer specific flags + * @size: size of the buffer + * @priv_virt: private data to the buffer representable as + * a void * + * @priv_phys: private data to the buffer representable as + * an ion_phys_addr_t (and someday a phys_addr_t) + * @lock: protects the buffers cnt fields + * @kmap_cnt: number of times the buffer is mapped to the kernel + * @vaddr: the kenrel mapping if kmap_cnt is not zero + * @dmap_cnt: number of times the buffer is mapped for dma + * @sg_table: the sg table for the buffer if dmap_cnt is not zero + * @dirty: bitmask representing which pages of this buffer have + * been dirtied by the cpu and need cache maintenance + * before dma + * @vmas: list of vma's mapping this buffer + * @handle_count: count of handles referencing this buffer + * @task_comm: taskcomm of last client to reference this buffer in a + * handle, used for debugging + * @pid: pid of last client to reference this buffer in a + * handle, used for debugging +*/ +struct ion_buffer { + struct kref ref; + union { + struct rb_node node; + struct list_head list; + }; + struct ion_device *dev; + struct ion_heap *heap; + unsigned long flags; + size_t size; + union { + void *priv_virt; + ion_phys_addr_t priv_phys; + }; + struct mutex lock; + int kmap_cnt; + void *vaddr; + int dmap_cnt; + struct sg_table *sg_table; + unsigned long *dirty; + struct list_head vmas; + /* used to track orphaned buffers */ + int handle_count; + char task_comm[TASK_COMM_LEN]; + pid_t pid; +}; + +/** + * struct ion_heap_ops - ops to operate on a given heap + * @allocate: allocate memory + * @free: free memory + * @phys get physical address of a buffer (only define on + * physically contiguous heaps) + * @map_dma map the memory for dma to a scatterlist + * @unmap_dma unmap the memory for dma + * @map_kernel map memory to the kernel + * @unmap_kernel unmap memory to the kernel + * @map_user map memory to userspace + */ +struct ion_heap_ops { + int (*allocate) (struct ion_heap *heap, + struct ion_buffer *buffer, unsigned long len, + unsigned long align, unsigned long flags); + void (*free) (struct ion_buffer *buffer); + int (*phys) (struct ion_heap *heap, struct ion_buffer *buffer, + ion_phys_addr_t *addr, size_t *len); + struct sg_table *(*map_dma) (struct ion_heap *heap, + struct ion_buffer *buffer); + void (*unmap_dma) (struct ion_heap *heap, struct ion_buffer *buffer); + void * (*map_kernel) (struct ion_heap *heap, struct ion_buffer *buffer); + void (*unmap_kernel) (struct ion_heap *heap, struct ion_buffer *buffer); + int (*map_user) (struct ion_heap *mapper, struct ion_buffer *buffer, + struct vm_area_struct *vma); +}; + +/** + * heap flags - flags between the heaps and core ion code + */ +#define ION_HEAP_FLAG_DEFER_FREE (1 << 0) + +/** + * struct ion_heap - represents a heap in the system + * @node: rb node to put the heap on the device's tree of heaps + * @dev: back pointer to the ion_device + * @type: type of heap + * @ops: ops struct as above + * @flags: flags + * @id: id of heap, also indicates priority of this heap when + * allocating. These are specified by platform data and + * MUST be unique + * @name: used for debugging + * @free_list: free list head if deferred free is used + * @lock: protects the free list + * @waitqueue: queue to wait on from deferred free thread + * @task: task struct of deferred free thread + * @debug_show: called when heap debug file is read to add any + * heap specific debug info to output + * + * Represents a pool of memory from which buffers can be made. In some + * systems the only heap is regular system memory allocated via vmalloc. + * On others, some blocks might require large physically contiguous buffers + * that are allocated from a specially reserved heap. + */ +struct ion_heap { + struct plist_node node; + struct ion_device *dev; + enum ion_heap_type type; + struct ion_heap_ops *ops; + unsigned long flags; + unsigned int id; + const char *name; + struct list_head free_list; + struct rt_mutex lock; + wait_queue_head_t waitqueue; + struct task_struct *task; + int (*debug_show)(struct ion_heap *heap, struct seq_file *, void *); +}; + +/** + * ion_buffer_cached - this ion buffer is cached + * @buffer: buffer + * + * indicates whether this ion buffer is cached + */ +bool ion_buffer_cached(struct ion_buffer *buffer); + +/** + * ion_buffer_fault_user_mappings - fault in user mappings of this buffer + * @buffer: buffer + * + * indicates whether userspace mappings of this buffer will be faulted + * in, this can affect how buffers are allocated from the heap. + */ +bool ion_buffer_fault_user_mappings(struct ion_buffer *buffer); + +/** + * ion_device_create - allocates and returns an ion device + * @custom_ioctl: arch specific ioctl function if applicable + * + * returns a valid device or -PTR_ERR + */ +struct ion_device *ion_device_create(long (*custom_ioctl) + (struct ion_client *client, + unsigned int cmd, + unsigned long arg)); + +/** + * ion_device_destroy - free and device and it's resource + * @dev: the device + */ +void ion_device_destroy(struct ion_device *dev); + +/** + * ion_device_add_heap - adds a heap to the ion device + * @dev: the device + * @heap: the heap to add + */ +void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap); + +/** + * some helpers for common operations on buffers using the sg_table + * and vaddr fields + */ +void *ion_heap_map_kernel(struct ion_heap *, struct ion_buffer *); +void ion_heap_unmap_kernel(struct ion_heap *, struct ion_buffer *); +int ion_heap_map_user(struct ion_heap *, struct ion_buffer *, + struct vm_area_struct *); +int ion_heap_buffer_zero(struct ion_buffer *buffer); + + +/** + * functions for creating and destroying the built in ion heaps. + * architectures can add their own custom architecture specific + * heaps as appropriate. + */ + +struct ion_heap *ion_heap_create(struct ion_platform_heap *); +void ion_heap_destroy(struct ion_heap *); +struct ion_heap *ion_system_heap_create(struct ion_platform_heap *); +void ion_system_heap_destroy(struct ion_heap *); + +struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *); +void ion_system_contig_heap_destroy(struct ion_heap *); + +struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *); +void ion_carveout_heap_destroy(struct ion_heap *); + +struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *); +void ion_chunk_heap_destroy(struct ion_heap *); +/** + * kernel api to allocate/free from carveout -- used when carveout is + * used to back an architecture specific custom heap + */ +ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap, unsigned long size, + unsigned long align); +void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr, + unsigned long size); +/** + * The carveout heap returns physical addresses, since 0 may be a valid + * physical address, this is used to indicate allocation failed + */ +#define ION_CARVEOUT_ALLOCATE_FAIL -1 + +/** + * functions for creating and destroying a heap pool -- allows you + * to keep a pool of pre allocated memory to use from your heap. Keeping + * a pool of memory that is ready for dma, ie any cached mapping have been + * invalidated from the cache, provides a significant peformance benefit on + * many systems */ + +/** + * struct ion_page_pool - pagepool struct + * @high_count: number of highmem items in the pool + * @low_count: number of lowmem items in the pool + * @high_items: list of highmem items + * @low_items: list of lowmem items + * @shrinker: a shrinker for the items + * @mutex: lock protecting this struct and especially the count + * item list + * @alloc: function to be used to allocate pageory when the pool + * is empty + * @free: function to be used to free pageory back to the system + * when the shrinker fires + * @gfp_mask: gfp_mask to use from alloc + * @order: order of pages in the pool + * @list: plist node for list of pools + * + * Allows you to keep a pool of pre allocated pages to use from your heap. + * Keeping a pool of pages that is ready for dma, ie any cached mapping have + * been invalidated from the cache, provides a significant peformance benefit + * on many systems + */ +struct ion_page_pool { + int high_count; + int low_count; + struct list_head high_items; + struct list_head low_items; + struct mutex mutex; + void *(*alloc)(struct ion_page_pool *pool); + void (*free)(struct ion_page_pool *pool, struct page *page); + gfp_t gfp_mask; + unsigned int order; + struct plist_node list; +}; + +struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order); +void ion_page_pool_destroy(struct ion_page_pool *); +void *ion_page_pool_alloc(struct ion_page_pool *); +void ion_page_pool_free(struct ion_page_pool *, struct page *); + +#endif /* _ION_PRIV_H */ diff --git a/drivers/gpu/ion/ion_system_heap.c b/drivers/gpu/ion/ion_system_heap.c new file mode 100644 index 00000000..6369fe8f --- /dev/null +++ b/drivers/gpu/ion/ion_system_heap.c @@ -0,0 +1,417 @@ +/* + * drivers/gpu/ion/ion_system_heap.c + * + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ion_priv.h" + +static unsigned int high_order_gfp_flags = (GFP_HIGHUSER | __GFP_ZERO | + __GFP_NOWARN | __GFP_NORETRY | + __GFP_NO_KSWAPD) & ~__GFP_WAIT; +static unsigned int low_order_gfp_flags = (GFP_HIGHUSER | __GFP_ZERO | + __GFP_NOWARN); +static const unsigned int orders[] = {8, 4, 0}; +static const int num_orders = ARRAY_SIZE(orders); +static int order_to_index(unsigned int order) +{ + int i; + for (i = 0; i < num_orders; i++) + if (order == orders[i]) + return i; + BUG(); + return -1; +} + +static unsigned int order_to_size(int order) +{ + return PAGE_SIZE << order; +} + +struct ion_system_heap { + struct ion_heap heap; + struct ion_page_pool **pools; +}; + +struct page_info { + struct page *page; + unsigned int order; + struct list_head list; +}; + +static struct page *alloc_buffer_page(struct ion_system_heap *heap, + struct ion_buffer *buffer, + unsigned long order) +{ + bool cached = ion_buffer_cached(buffer); + bool split_pages = ion_buffer_fault_user_mappings(buffer); + struct ion_page_pool *pool = heap->pools[order_to_index(order)]; + struct page *page; + + if (!cached) { + page = ion_page_pool_alloc(pool); + } else { + gfp_t gfp_flags = low_order_gfp_flags; + + if (order > 4) + gfp_flags = high_order_gfp_flags; + page = alloc_pages(gfp_flags, order); + if (!page) + return 0; + __dma_page_cpu_to_dev(page, 0, PAGE_SIZE << order, + DMA_BIDIRECTIONAL); + } + if (!page) + return 0; + + if (split_pages) + split_page(page, order); + return page; +} + +static void free_buffer_page(struct ion_system_heap *heap, + struct ion_buffer *buffer, struct page *page, + unsigned int order) +{ + bool cached = ion_buffer_cached(buffer); + bool split_pages = ion_buffer_fault_user_mappings(buffer); + int i; + + if (!cached) { + struct ion_page_pool *pool = heap->pools[order_to_index(order)]; + ion_page_pool_free(pool, page); + } else if (split_pages) { + for (i = 0; i < (1 << order); i++) + __free_page(page + i); + } else { + __free_pages(page, order); + } +} + + +static struct page_info *alloc_largest_available(struct ion_system_heap *heap, + struct ion_buffer *buffer, + unsigned long size, + unsigned int max_order) +{ + struct page *page; + struct page_info *info; + int i; + + for (i = 0; i < num_orders; i++) { + if (size < order_to_size(orders[i])) + continue; + if (max_order < orders[i]) + continue; + + page = alloc_buffer_page(heap, buffer, orders[i]); + if (!page) + continue; + + info = kmalloc(sizeof(struct page_info), GFP_KERNEL); + info->page = page; + info->order = orders[i]; + return info; + } + return NULL; +} + +static int ion_system_heap_allocate(struct ion_heap *heap, + struct ion_buffer *buffer, + unsigned long size, unsigned long align, + unsigned long flags) +{ + struct ion_system_heap *sys_heap = container_of(heap, + struct ion_system_heap, + heap); + struct sg_table *table; + struct scatterlist *sg; + int ret; + struct list_head pages; + struct page_info *info, *tmp_info; + int i = 0; + long size_remaining = PAGE_ALIGN(size); + unsigned int max_order = orders[0]; + bool split_pages = ion_buffer_fault_user_mappings(buffer); + + INIT_LIST_HEAD(&pages); + while (size_remaining > 0) { + info = alloc_largest_available(sys_heap, buffer, size_remaining, max_order); + if (!info) + goto err; + list_add_tail(&info->list, &pages); + size_remaining -= (1 << info->order) * PAGE_SIZE; + max_order = info->order; + i++; + } + + table = kmalloc(sizeof(struct sg_table), GFP_KERNEL); + if (!table) + goto err; + + if (split_pages) + ret = sg_alloc_table(table, PAGE_ALIGN(size) / PAGE_SIZE, + GFP_KERNEL); + else + ret = sg_alloc_table(table, i, GFP_KERNEL); + + if (ret) + goto err1; + + sg = table->sgl; + list_for_each_entry_safe(info, tmp_info, &pages, list) { + struct page *page = info->page; + if (split_pages) { + for (i = 0; i < (1 << info->order); i++) { + sg_set_page(sg, page + i, PAGE_SIZE, 0); + sg = sg_next(sg); + } + } else { + sg_set_page(sg, page, (1 << info->order) * PAGE_SIZE, + 0); + sg = sg_next(sg); + } + list_del(&info->list); + kfree(info); + } + + buffer->priv_virt = table; + return 0; +err1: + kfree(table); +err: + list_for_each_entry(info, &pages, list) { + free_buffer_page(sys_heap, buffer, info->page, info->order); + kfree(info); + } + return -ENOMEM; +} + +void ion_system_heap_free(struct ion_buffer *buffer) +{ + struct ion_heap *heap = buffer->heap; + struct ion_system_heap *sys_heap = container_of(heap, + struct ion_system_heap, + heap); + struct sg_table *table = buffer->sg_table; + bool cached = ion_buffer_cached(buffer); + struct scatterlist *sg; + LIST_HEAD(pages); + int i; + + /* uncached pages come from the page pools, zero them before returning + for security purposes (other allocations are zerod at alloc time */ + if (!cached) + ion_heap_buffer_zero(buffer); + + for_each_sg(table->sgl, sg, table->nents, i) + free_buffer_page(sys_heap, buffer, sg_page(sg), + get_order(sg_dma_len(sg))); + sg_free_table(table); + kfree(table); +} + +struct sg_table *ion_system_heap_map_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + return buffer->priv_virt; +} + +void ion_system_heap_unmap_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + return; +} + +static struct ion_heap_ops system_heap_ops = { + .allocate = ion_system_heap_allocate, + .free = ion_system_heap_free, + .map_dma = ion_system_heap_map_dma, + .unmap_dma = ion_system_heap_unmap_dma, + .map_kernel = ion_heap_map_kernel, + .unmap_kernel = ion_heap_unmap_kernel, + .map_user = ion_heap_map_user, +}; + +static int ion_system_heap_debug_show(struct ion_heap *heap, struct seq_file *s, + void *unused) +{ + + struct ion_system_heap *sys_heap = container_of(heap, + struct ion_system_heap, + heap); + int i; + for (i = 0; i < num_orders; i++) { + struct ion_page_pool *pool = sys_heap->pools[i]; + seq_printf(s, "%d order %u highmem pages in pool = %lu total\n", + pool->high_count, pool->order, + (1 << pool->order) * PAGE_SIZE * pool->high_count); + seq_printf(s, "%d order %u lowmem pages in pool = %lu total\n", + pool->low_count, pool->order, + (1 << pool->order) * PAGE_SIZE * pool->low_count); + } + return 0; +} + +struct ion_heap *ion_system_heap_create(struct ion_platform_heap *unused) +{ + struct ion_system_heap *heap; + int i; + + heap = kzalloc(sizeof(struct ion_system_heap), GFP_KERNEL); + if (!heap) + return ERR_PTR(-ENOMEM); + heap->heap.ops = &system_heap_ops; + heap->heap.type = ION_HEAP_TYPE_SYSTEM; + heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE; + heap->pools = kzalloc(sizeof(struct ion_page_pool *) * num_orders, + GFP_KERNEL); + if (!heap->pools) + goto err_alloc_pools; + for (i = 0; i < num_orders; i++) { + struct ion_page_pool *pool; + gfp_t gfp_flags = low_order_gfp_flags; + + if (orders[i] > 4) + gfp_flags = high_order_gfp_flags; + pool = ion_page_pool_create(gfp_flags, orders[i]); + if (!pool) + goto err_create_pool; + heap->pools[i] = pool; + } + heap->heap.debug_show = ion_system_heap_debug_show; + return &heap->heap; +err_create_pool: + for (i = 0; i < num_orders; i++) + if (heap->pools[i]) + ion_page_pool_destroy(heap->pools[i]); + kfree(heap->pools); +err_alloc_pools: + kfree(heap); + return ERR_PTR(-ENOMEM); +} + +void ion_system_heap_destroy(struct ion_heap *heap) +{ + struct ion_system_heap *sys_heap = container_of(heap, + struct ion_system_heap, + heap); + int i; + + for (i = 0; i < num_orders; i++) + ion_page_pool_destroy(sys_heap->pools[i]); + kfree(sys_heap->pools); + kfree(sys_heap); +} + +static int ion_system_contig_heap_allocate(struct ion_heap *heap, + struct ion_buffer *buffer, + unsigned long len, + unsigned long align, + unsigned long flags) +{ + buffer->priv_virt = kzalloc(len, GFP_KERNEL); + if (!buffer->priv_virt) + return -ENOMEM; + return 0; +} + +void ion_system_contig_heap_free(struct ion_buffer *buffer) +{ + kfree(buffer->priv_virt); +} + +static int ion_system_contig_heap_phys(struct ion_heap *heap, + struct ion_buffer *buffer, + ion_phys_addr_t *addr, size_t *len) +{ + *addr = virt_to_phys(buffer->priv_virt); + *len = buffer->size; + return 0; +} + +struct sg_table *ion_system_contig_heap_map_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + struct sg_table *table; + int ret; + + table = kzalloc(sizeof(struct sg_table), GFP_KERNEL); + if (!table) + return ERR_PTR(-ENOMEM); + ret = sg_alloc_table(table, 1, GFP_KERNEL); + if (ret) { + kfree(table); + return ERR_PTR(ret); + } + sg_set_page(table->sgl, virt_to_page(buffer->priv_virt), buffer->size, + 0); + return table; +} + +void ion_system_contig_heap_unmap_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + sg_free_table(buffer->sg_table); + kfree(buffer->sg_table); +} + +int ion_system_contig_heap_map_user(struct ion_heap *heap, + struct ion_buffer *buffer, + struct vm_area_struct *vma) +{ + unsigned long pfn = __phys_to_pfn(virt_to_phys(buffer->priv_virt)); + return remap_pfn_range(vma, vma->vm_start, pfn + vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); + +} + +static struct ion_heap_ops kmalloc_ops = { + .allocate = ion_system_contig_heap_allocate, + .free = ion_system_contig_heap_free, + .phys = ion_system_contig_heap_phys, + .map_dma = ion_system_contig_heap_map_dma, + .unmap_dma = ion_system_contig_heap_unmap_dma, + .map_kernel = ion_heap_map_kernel, + .unmap_kernel = ion_heap_unmap_kernel, + .map_user = ion_system_contig_heap_map_user, +}; + +struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *unused) +{ + struct ion_heap *heap; + + heap = kzalloc(sizeof(struct ion_heap), GFP_KERNEL); + if (!heap) + return ERR_PTR(-ENOMEM); + heap->ops = &kmalloc_ops; + heap->type = ION_HEAP_TYPE_SYSTEM_CONTIG; + return heap; +} + +void ion_system_contig_heap_destroy(struct ion_heap *heap) +{ + kfree(heap); +} + diff --git a/drivers/gpu/ion/ion_system_mapper.c b/drivers/gpu/ion/ion_system_mapper.c new file mode 100644 index 00000000..692458e0 --- /dev/null +++ b/drivers/gpu/ion/ion_system_mapper.c @@ -0,0 +1,114 @@ +/* + * drivers/gpu/ion/ion_system_mapper.c + * + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include "ion_priv.h" +/* + * This mapper is valid for any heap that allocates memory that already has + * a kernel mapping, this includes vmalloc'd memory, kmalloc'd memory, + * pages obtained via io_remap, etc. + */ +static void *ion_kernel_mapper_map(struct ion_mapper *mapper, + struct ion_buffer *buffer, + struct ion_mapping **mapping) +{ + if (!((1 << buffer->heap->type) & mapper->heap_mask)) { + pr_err("%s: attempting to map an unsupported heap\n", __func__); + return ERR_PTR(-EINVAL); + } + /* XXX REVISIT ME!!! */ + *((unsigned long *)mapping) = (unsigned long)buffer->priv; + return buffer->priv; +} + +static void ion_kernel_mapper_unmap(struct ion_mapper *mapper, + struct ion_buffer *buffer, + struct ion_mapping *mapping) +{ + if (!((1 << buffer->heap->type) & mapper->heap_mask)) + pr_err("%s: attempting to unmap an unsupported heap\n", + __func__); +} + +static void *ion_kernel_mapper_map_kernel(struct ion_mapper *mapper, + struct ion_buffer *buffer, + struct ion_mapping *mapping) +{ + if (!((1 << buffer->heap->type) & mapper->heap_mask)) { + pr_err("%s: attempting to unmap an unsupported heap\n", + __func__); + return ERR_PTR(-EINVAL); + } + return buffer->priv; +} + +static int ion_kernel_mapper_map_user(struct ion_mapper *mapper, + struct ion_buffer *buffer, + struct vm_area_struct *vma, + struct ion_mapping *mapping) +{ + int ret; + + switch (buffer->heap->type) { + case ION_HEAP_KMALLOC: + { + unsigned long pfn = __phys_to_pfn(virt_to_phys(buffer->priv)); + ret = remap_pfn_range(vma, vma->vm_start, pfn + vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); + break; + } + case ION_HEAP_VMALLOC: + ret = remap_vmalloc_range(vma, buffer->priv, vma->vm_pgoff); + break; + default: + pr_err("%s: attempting to map unsupported heap to userspace\n", + __func__); + return -EINVAL; + } + + return ret; +} + +static struct ion_mapper_ops ops = { + .map = ion_kernel_mapper_map, + .map_kernel = ion_kernel_mapper_map_kernel, + .map_user = ion_kernel_mapper_map_user, + .unmap = ion_kernel_mapper_unmap, +}; + +struct ion_mapper *ion_system_mapper_create(void) +{ + struct ion_mapper *mapper; + mapper = kzalloc(sizeof(struct ion_mapper), GFP_KERNEL); + if (!mapper) + return ERR_PTR(-ENOMEM); + mapper->type = ION_SYSTEM_MAPPER; + mapper->ops = &ops; + mapper->heap_mask = (1 << ION_HEAP_VMALLOC) | (1 << ION_HEAP_KMALLOC); + return mapper; +} + +void ion_system_mapper_destroy(struct ion_mapper *mapper) +{ + kfree(mapper); +} + diff --git a/drivers/gpu/ion/plat-anyka/Makefile b/drivers/gpu/ion/plat-anyka/Makefile new file mode 100644 index 00000000..5d2c60ec --- /dev/null +++ b/drivers/gpu/ion/plat-anyka/Makefile @@ -0,0 +1 @@ +obj-y += ak_ion.o diff --git a/drivers/gpu/ion/plat-anyka/ak_ion.c b/drivers/gpu/ion/plat-anyka/ak_ion.c new file mode 100644 index 00000000..9cc80298 --- /dev/null +++ b/drivers/gpu/ion/plat-anyka/ak_ion.c @@ -0,0 +1,96 @@ +/* + * drivers/gpu/plat-anyka/ak_ion.c + * + * Copyright (C) 2011 Anyka, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include "../ion_priv.h" + +struct ion_device *idev; +struct ion_mapper *anyka_user_mapper; +int num_heaps; +struct ion_heap **heaps; + +int ak_ion_probe(struct platform_device *pdev) +{ + struct ion_platform_data *pdata = pdev->dev.platform_data; + int err; + int i; + + num_heaps = pdata->nr; + + heaps = kzalloc(sizeof(struct ion_heap *) * pdata->nr, GFP_KERNEL); + + idev = ion_device_create(NULL); + if (IS_ERR_OR_NULL(idev)) { + kfree(heaps); + return PTR_ERR(idev); + } + + /* create the heaps as specified in the board file */ + for (i = 0; i < num_heaps; i++) { + struct ion_platform_heap *heap_data = &pdata->heaps[i]; + + heaps[i] = ion_heap_create(heap_data); + if (IS_ERR_OR_NULL(heaps[i])) { + err = PTR_ERR(heaps[i]); + goto err; + } + ion_device_add_heap(idev, heaps[i]); + } + platform_set_drvdata(pdev, idev); + return 0; +err: + for (i = 0; i < num_heaps; i++) { + if (heaps[i]) + ion_heap_destroy(heaps[i]); + } + kfree(heaps); + return err; +} + +int ak_ion_remove(struct platform_device *pdev) +{ + struct ion_device *idev = platform_get_drvdata(pdev); + int i; + + ion_device_destroy(idev); + for (i = 0; i < num_heaps; i++) + ion_heap_destroy(heaps[i]); + kfree(heaps); + return 0; +} + +static struct platform_driver ak_ion_driver = { + .probe = ak_ion_probe, + .remove = ak_ion_remove, + .driver = { .name = "ion-ak" } +}; + +static int __init ak_ion_init(void) +{ + return platform_driver_register(&ak_ion_driver); +} + +static void __exit ak_ion_exit(void) +{ + platform_driver_unregister(&ak_ion_driver); +} + +module_init(ak_ion_init); +module_exit(ak_ion_exit); + diff --git a/drivers/gpu/ion/tegra/Makefile b/drivers/gpu/ion/tegra/Makefile new file mode 100644 index 00000000..11cd003f --- /dev/null +++ b/drivers/gpu/ion/tegra/Makefile @@ -0,0 +1 @@ +obj-y += tegra_ion.o diff --git a/drivers/gpu/ion/tegra/tegra_ion.c b/drivers/gpu/ion/tegra/tegra_ion.c new file mode 100644 index 00000000..7af6e168 --- /dev/null +++ b/drivers/gpu/ion/tegra/tegra_ion.c @@ -0,0 +1,96 @@ +/* + * drivers/gpu/tegra/tegra_ion.c + * + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include "../ion_priv.h" + +struct ion_device *idev; +struct ion_mapper *tegra_user_mapper; +int num_heaps; +struct ion_heap **heaps; + +int tegra_ion_probe(struct platform_device *pdev) +{ + struct ion_platform_data *pdata = pdev->dev.platform_data; + int err; + int i; + + num_heaps = pdata->nr; + + heaps = kzalloc(sizeof(struct ion_heap *) * pdata->nr, GFP_KERNEL); + + idev = ion_device_create(NULL); + if (IS_ERR_OR_NULL(idev)) { + kfree(heaps); + return PTR_ERR(idev); + } + + /* create the heaps as specified in the board file */ + for (i = 0; i < num_heaps; i++) { + struct ion_platform_heap *heap_data = &pdata->heaps[i]; + + heaps[i] = ion_heap_create(heap_data); + if (IS_ERR_OR_NULL(heaps[i])) { + err = PTR_ERR(heaps[i]); + goto err; + } + ion_device_add_heap(idev, heaps[i]); + } + platform_set_drvdata(pdev, idev); + return 0; +err: + for (i = 0; i < num_heaps; i++) { + if (heaps[i]) + ion_heap_destroy(heaps[i]); + } + kfree(heaps); + return err; +} + +int tegra_ion_remove(struct platform_device *pdev) +{ + struct ion_device *idev = platform_get_drvdata(pdev); + int i; + + ion_device_destroy(idev); + for (i = 0; i < num_heaps; i++) + ion_heap_destroy(heaps[i]); + kfree(heaps); + return 0; +} + +static struct platform_driver ion_driver = { + .probe = tegra_ion_probe, + .remove = tegra_ion_remove, + .driver = { .name = "ion-tegra" } +}; + +static int __init ion_init(void) +{ + return platform_driver_register(&ion_driver); +} + +static void __exit ion_exit(void) +{ + platform_driver_unregister(&ion_driver); +} + +module_init(ion_init); +module_exit(ion_exit); + diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index ffddcba3..1283fa3b 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -55,6 +55,27 @@ config HIDRAW If unsure, say Y. +config UHID + tristate "User-space I/O driver support for HID subsystem" + depends on HID + default n + ---help--- + Say Y here if you want to provide HID I/O Drivers from user-space. + This allows to write I/O drivers in user-space and feed the data from + the device into the kernel. The kernel parses the HID reports, loads the + corresponding HID Device Driver or provides input devices on top of your + user-space device. + + This driver cannot be used to parse HID-reports in user-space and write + special HID-drivers. You should use hidraw for that. + Instead, this driver allows to write the transport-layer driver in + user-space like USB-HID and Bluetooth-HID do in kernel-space. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called uhid. + source "drivers/hid/usbhid/Kconfig" menu "Special HID drivers" diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 22f1d16c..9dca8459 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -8,6 +8,7 @@ ifdef CONFIG_DEBUG_FS endif obj-$(CONFIG_HID) += hid.o +obj-$(CONFIG_UHID) += uhid.o hid-$(CONFIG_HIDRAW) += hidraw.o diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 21e473e7..43669731 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1204,6 +1204,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) * UGCI) cram a lot of unrelated inputs into the * same interface. */ hidinput->report = report; + if (hid->driver->input_register && + hid->driver->input_register(hid, hidinput)) + goto out_cleanup; if (input_register_device(hidinput->input)) goto out_cleanup; hidinput = NULL; @@ -1218,6 +1221,10 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) goto out_unwind; } + if (hidinput && hid->driver->input_register && + hid->driver->input_register(hid, hidinput)) + goto out_cleanup; + if (hidinput && input_register_device(hidinput->input)) goto out_cleanup; diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 7cf3ffe4..8427463b 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -387,8 +387,10 @@ static int magicmouse_raw_event(struct hid_device *hdev, return 1; } -static void magicmouse_setup_input(struct input_dev *input, struct hid_device *hdev) +static int magicmouse_setup_input(struct hid_device *hdev, struct hid_input *hi) { + struct input_dev *input = hi->input; + __set_bit(EV_KEY, input->evbit); if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) { @@ -471,6 +473,8 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h __set_bit(EV_MSC, input->evbit); __set_bit(MSC_RAW, input->mscbit); } + + return 0; } static int magicmouse_input_mapping(struct hid_device *hdev, @@ -523,12 +527,6 @@ static int magicmouse_probe(struct hid_device *hdev, goto err_free; } - /* We do this after hid-input is done parsing reports so that - * hid-input uses the most natural button and axis IDs. - */ - if (msc->input) - magicmouse_setup_input(msc->input, hdev); - if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE) report = hid_register_report(hdev, HID_INPUT_REPORT, MOUSE_REPORT_ID); @@ -593,6 +591,7 @@ static struct hid_driver magicmouse_driver = { .remove = magicmouse_remove, .raw_event = magicmouse_raw_event, .input_mapping = magicmouse_input_mapping, + .input_register = magicmouse_setup_input, }; static int __init magicmouse_init(void) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index e754dff1..7a180b99 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -330,6 +330,16 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, if (field->physical == HID_DG_STYLUS) return -1; + /* Only map fields from TouchScreen or TouchPad collections. + * We need to ignore fields that belong to other collections + * such as Mouse that might have the same GenericDesktop usages. */ + if (field->application == HID_DG_TOUCHSCREEN) + set_bit(INPUT_PROP_DIRECT, hi->input->propbit); + else if (field->application == HID_DG_TOUCHPAD) + set_bit(INPUT_PROP_POINTER, hi->input->propbit); + else + return 0; + switch (usage->hid & HID_USAGE_PAGE) { case HID_UP_GENDESK: diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c new file mode 100644 index 00000000..714cd8cc --- /dev/null +++ b/drivers/hid/uhid.c @@ -0,0 +1,572 @@ +/* + * User-space I/O driver support for HID subsystem + * Copyright (c) 2012 David Herrmann + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UHID_NAME "uhid" +#define UHID_BUFSIZE 32 + +struct uhid_device { + struct mutex devlock; + bool running; + + __u8 *rd_data; + uint rd_size; + + struct hid_device *hid; + struct uhid_event input_buf; + + wait_queue_head_t waitq; + spinlock_t qlock; + __u8 head; + __u8 tail; + struct uhid_event *outq[UHID_BUFSIZE]; + + struct mutex report_lock; + wait_queue_head_t report_wait; + atomic_t report_done; + atomic_t report_id; + struct uhid_event report_buf; +}; + +static struct miscdevice uhid_misc; + +static void uhid_queue(struct uhid_device *uhid, struct uhid_event *ev) +{ + __u8 newhead; + + newhead = (uhid->head + 1) % UHID_BUFSIZE; + + if (newhead != uhid->tail) { + uhid->outq[uhid->head] = ev; + uhid->head = newhead; + wake_up_interruptible(&uhid->waitq); + } else { + hid_warn(uhid->hid, "Output queue is full\n"); + kfree(ev); + } +} + +static int uhid_queue_event(struct uhid_device *uhid, __u32 event) +{ + unsigned long flags; + struct uhid_event *ev; + + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) + return -ENOMEM; + + ev->type = event; + + spin_lock_irqsave(&uhid->qlock, flags); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + return 0; +} + +static int uhid_hid_start(struct hid_device *hid) +{ + struct uhid_device *uhid = hid->driver_data; + + return uhid_queue_event(uhid, UHID_START); +} + +static void uhid_hid_stop(struct hid_device *hid) +{ + struct uhid_device *uhid = hid->driver_data; + + hid->claimed = 0; + uhid_queue_event(uhid, UHID_STOP); +} + +static int uhid_hid_open(struct hid_device *hid) +{ + struct uhid_device *uhid = hid->driver_data; + + return uhid_queue_event(uhid, UHID_OPEN); +} + +static void uhid_hid_close(struct hid_device *hid) +{ + struct uhid_device *uhid = hid->driver_data; + + uhid_queue_event(uhid, UHID_CLOSE); +} + +static int uhid_hid_input(struct input_dev *input, unsigned int type, + unsigned int code, int value) +{ + struct hid_device *hid = input_get_drvdata(input); + struct uhid_device *uhid = hid->driver_data; + unsigned long flags; + struct uhid_event *ev; + + ev = kzalloc(sizeof(*ev), GFP_ATOMIC); + if (!ev) + return -ENOMEM; + + ev->type = UHID_OUTPUT_EV; + ev->u.output_ev.type = type; + ev->u.output_ev.code = code; + ev->u.output_ev.value = value; + + spin_lock_irqsave(&uhid->qlock, flags); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + return 0; +} + +static int uhid_hid_parse(struct hid_device *hid) +{ + struct uhid_device *uhid = hid->driver_data; + + return hid_parse_report(hid, uhid->rd_data, uhid->rd_size); +} + +static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, + __u8 *buf, size_t count, unsigned char rtype) +{ + struct uhid_device *uhid = hid->driver_data; + __u8 report_type; + struct uhid_event *ev; + unsigned long flags; + int ret; + size_t uninitialized_var(len); + struct uhid_feature_answer_req *req; + + if (!uhid->running) + return -EIO; + + switch (rtype) { + case HID_FEATURE_REPORT: + report_type = UHID_FEATURE_REPORT; + break; + case HID_OUTPUT_REPORT: + report_type = UHID_OUTPUT_REPORT; + break; + case HID_INPUT_REPORT: + report_type = UHID_INPUT_REPORT; + break; + default: + return -EINVAL; + } + + ret = mutex_lock_interruptible(&uhid->report_lock); + if (ret) + return ret; + + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) { + ret = -ENOMEM; + goto unlock; + } + + spin_lock_irqsave(&uhid->qlock, flags); + ev->type = UHID_FEATURE; + ev->u.feature.id = atomic_inc_return(&uhid->report_id); + ev->u.feature.rnum = rnum; + ev->u.feature.rtype = report_type; + + atomic_set(&uhid->report_done, 0); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + ret = wait_event_interruptible_timeout(uhid->report_wait, + atomic_read(&uhid->report_done), 5 * HZ); + + /* + * Make sure "uhid->running" is cleared on shutdown before + * "uhid->report_done" is set. + */ + smp_rmb(); + if (!ret || !uhid->running) { + ret = -EIO; + } else if (ret < 0) { + ret = -ERESTARTSYS; + } else { + spin_lock_irqsave(&uhid->qlock, flags); + req = &uhid->report_buf.u.feature_answer; + + if (req->err) { + ret = -EIO; + } else { + ret = 0; + len = min(count, + min_t(size_t, req->size, UHID_DATA_MAX)); + memcpy(buf, req->data, len); + } + + spin_unlock_irqrestore(&uhid->qlock, flags); + } + + atomic_set(&uhid->report_done, 1); + +unlock: + mutex_unlock(&uhid->report_lock); + return ret ? ret : len; +} + +static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, + unsigned char report_type) +{ + struct uhid_device *uhid = hid->driver_data; + __u8 rtype; + unsigned long flags; + struct uhid_event *ev; + + switch (report_type) { + case HID_FEATURE_REPORT: + rtype = UHID_FEATURE_REPORT; + break; + case HID_OUTPUT_REPORT: + rtype = UHID_OUTPUT_REPORT; + break; + default: + return -EINVAL; + } + + if (count < 1 || count > UHID_DATA_MAX) + return -EINVAL; + + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) + return -ENOMEM; + + ev->type = UHID_OUTPUT; + ev->u.output.size = count; + ev->u.output.rtype = rtype; + memcpy(ev->u.output.data, buf, count); + + spin_lock_irqsave(&uhid->qlock, flags); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + return count; +} + +static struct hid_ll_driver uhid_hid_driver = { + .start = uhid_hid_start, + .stop = uhid_hid_stop, + .open = uhid_hid_open, + .close = uhid_hid_close, + .hidinput_input_event = uhid_hid_input, + .parse = uhid_hid_parse, +}; + +static int uhid_dev_create(struct uhid_device *uhid, + const struct uhid_event *ev) +{ + struct hid_device *hid; + int ret; + + if (uhid->running) + return -EALREADY; + + uhid->rd_size = ev->u.create.rd_size; + if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE) + return -EINVAL; + + uhid->rd_data = kmalloc(uhid->rd_size, GFP_KERNEL); + if (!uhid->rd_data) + return -ENOMEM; + + if (copy_from_user(uhid->rd_data, ev->u.create.rd_data, + uhid->rd_size)) { + ret = -EFAULT; + goto err_free; + } + + hid = hid_allocate_device(); + if (IS_ERR(hid)) { + ret = PTR_ERR(hid); + goto err_free; + } + + strncpy(hid->name, ev->u.create.name, 127); + hid->name[127] = 0; + strncpy(hid->phys, ev->u.create.phys, 63); + hid->phys[63] = 0; + strncpy(hid->uniq, ev->u.create.uniq, 63); + hid->uniq[63] = 0; + + hid->ll_driver = &uhid_hid_driver; + hid->hid_get_raw_report = uhid_hid_get_raw; + hid->hid_output_raw_report = uhid_hid_output_raw; + hid->bus = ev->u.create.bus; + hid->vendor = ev->u.create.vendor; + hid->product = ev->u.create.product; + hid->version = ev->u.create.version; + hid->country = ev->u.create.country; + hid->driver_data = uhid; + hid->dev.parent = uhid_misc.this_device; + + uhid->hid = hid; + uhid->running = true; + + ret = hid_add_device(hid); + if (ret) { + hid_err(hid, "Cannot register HID device\n"); + goto err_hid; + } + + return 0; + +err_hid: + hid_destroy_device(hid); + uhid->hid = NULL; + uhid->running = false; +err_free: + kfree(uhid->rd_data); + return ret; +} + +static int uhid_dev_destroy(struct uhid_device *uhid) +{ + if (!uhid->running) + return -EINVAL; + + /* clear "running" before setting "report_done" */ + uhid->running = false; + smp_wmb(); + atomic_set(&uhid->report_done, 1); + wake_up_interruptible(&uhid->report_wait); + + hid_destroy_device(uhid->hid); + kfree(uhid->rd_data); + + return 0; +} + +static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev) +{ + if (!uhid->running) + return -EINVAL; + + hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input.data, + min_t(size_t, ev->u.input.size, UHID_DATA_MAX), 0); + + return 0; +} + +static int uhid_dev_feature_answer(struct uhid_device *uhid, + struct uhid_event *ev) +{ + unsigned long flags; + + if (!uhid->running) + return -EINVAL; + + spin_lock_irqsave(&uhid->qlock, flags); + + /* id for old report; drop it silently */ + if (atomic_read(&uhid->report_id) != ev->u.feature_answer.id) + goto unlock; + if (atomic_read(&uhid->report_done)) + goto unlock; + + memcpy(&uhid->report_buf, ev, sizeof(*ev)); + atomic_set(&uhid->report_done, 1); + wake_up_interruptible(&uhid->report_wait); + +unlock: + spin_unlock_irqrestore(&uhid->qlock, flags); + return 0; +} + +static int uhid_char_open(struct inode *inode, struct file *file) +{ + struct uhid_device *uhid; + + uhid = kzalloc(sizeof(*uhid), GFP_KERNEL); + if (!uhid) + return -ENOMEM; + + mutex_init(&uhid->devlock); + mutex_init(&uhid->report_lock); + spin_lock_init(&uhid->qlock); + init_waitqueue_head(&uhid->waitq); + init_waitqueue_head(&uhid->report_wait); + uhid->running = false; + atomic_set(&uhid->report_done, 1); + + file->private_data = uhid; + nonseekable_open(inode, file); + + return 0; +} + +static int uhid_char_release(struct inode *inode, struct file *file) +{ + struct uhid_device *uhid = file->private_data; + unsigned int i; + + uhid_dev_destroy(uhid); + + for (i = 0; i < UHID_BUFSIZE; ++i) + kfree(uhid->outq[i]); + + kfree(uhid); + + return 0; +} + +static ssize_t uhid_char_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct uhid_device *uhid = file->private_data; + int ret; + unsigned long flags; + size_t len; + + /* they need at least the "type" member of uhid_event */ + if (count < sizeof(__u32)) + return -EINVAL; + +try_again: + if (file->f_flags & O_NONBLOCK) { + if (uhid->head == uhid->tail) + return -EAGAIN; + } else { + ret = wait_event_interruptible(uhid->waitq, + uhid->head != uhid->tail); + if (ret) + return ret; + } + + ret = mutex_lock_interruptible(&uhid->devlock); + if (ret) + return ret; + + if (uhid->head == uhid->tail) { + mutex_unlock(&uhid->devlock); + goto try_again; + } else { + len = min(count, sizeof(**uhid->outq)); + if (copy_to_user(buffer, uhid->outq[uhid->tail], len)) { + ret = -EFAULT; + } else { + kfree(uhid->outq[uhid->tail]); + uhid->outq[uhid->tail] = NULL; + + spin_lock_irqsave(&uhid->qlock, flags); + uhid->tail = (uhid->tail + 1) % UHID_BUFSIZE; + spin_unlock_irqrestore(&uhid->qlock, flags); + } + } + + mutex_unlock(&uhid->devlock); + return ret ? ret : len; +} + +static ssize_t uhid_char_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct uhid_device *uhid = file->private_data; + int ret; + size_t len; + + /* we need at least the "type" member of uhid_event */ + if (count < sizeof(__u32)) + return -EINVAL; + + ret = mutex_lock_interruptible(&uhid->devlock); + if (ret) + return ret; + + memset(&uhid->input_buf, 0, sizeof(uhid->input_buf)); + len = min(count, sizeof(uhid->input_buf)); + if (copy_from_user(&uhid->input_buf, buffer, len)) { + ret = -EFAULT; + goto unlock; + } + + switch (uhid->input_buf.type) { + case UHID_CREATE: + ret = uhid_dev_create(uhid, &uhid->input_buf); + break; + case UHID_DESTROY: + ret = uhid_dev_destroy(uhid); + break; + case UHID_INPUT: + ret = uhid_dev_input(uhid, &uhid->input_buf); + break; + case UHID_FEATURE_ANSWER: + ret = uhid_dev_feature_answer(uhid, &uhid->input_buf); + break; + default: + ret = -EOPNOTSUPP; + } + +unlock: + mutex_unlock(&uhid->devlock); + + /* return "count" not "len" to not confuse the caller */ + return ret ? ret : count; +} + +static unsigned int uhid_char_poll(struct file *file, poll_table *wait) +{ + struct uhid_device *uhid = file->private_data; + + poll_wait(file, &uhid->waitq, wait); + + if (uhid->head != uhid->tail) + return POLLIN | POLLRDNORM; + + return 0; +} + +static const struct file_operations uhid_fops = { + .owner = THIS_MODULE, + .open = uhid_char_open, + .release = uhid_char_release, + .read = uhid_char_read, + .write = uhid_char_write, + .poll = uhid_char_poll, + .llseek = no_llseek, +}; + +static struct miscdevice uhid_misc = { + .fops = &uhid_fops, + .minor = MISC_DYNAMIC_MINOR, + .name = UHID_NAME, +}; + +static int __init uhid_init(void) +{ + return misc_register(&uhid_misc); +} + +static void __exit uhid_exit(void) +{ + misc_deregister(&uhid_misc); +} + +module_init(uhid_init); +module_exit(uhid_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Herrmann "); +MODULE_DESCRIPTION("User-space I/O driver support for HID subsystem"); diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index ea8736bc..af663a88 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -723,6 +723,30 @@ config I2C_XLR This driver can also be built as a module. If so, the module will be called i2c-xlr. +config I2C_ANYKA + bool "Anyka I2C drivers support" + depends on ARCH_AK39 + help + Say Y here to enable support for I2C driver for Anyka chip. + +choice + bool "I2C driver" + depends on I2C_ANYKA + +config I2C_AK39_HW + bool "i2c controller" + depends on I2C_ANYKA + help + Say Y here to enable support for I2C driver by chip controller. + +config I2C_GPIO_SOFT + bool "i2c software simulate by gpios" + depends on I2C_ANYKA && I2C_ALGOBIT + help + Say Y here to enable support I2C driver by GPIOs simulate + +endchoice + comment "External I2C/SMBus adapter drivers" config I2C_DIOLAN_U2C diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 2f05d7b6..969fdc8b 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -72,6 +72,8 @@ obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o obj-$(CONFIG_I2C_XLR) += i2c-xlr.o +obj-$(CONFIG_I2C_AK39_HW) += i2c-ak39.o +obj-$(CONFIG_I2C_GPIO_SOFT) += i2c-gpio-soft.o # External I2C/SMBus adapter drivers obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o diff --git a/drivers/i2c/busses/i2c-ak39.c b/drivers/i2c/busses/i2c-ak39.c new file mode 100644 index 00000000..31f43052 --- /dev/null +++ b/drivers/i2c/busses/i2c-ak39.c @@ -0,0 +1,931 @@ +/* linux/drivers/i2c/busses/i2c-ak39.c + * + * Copyright (C) 2010 Anyka + * + * AK39xx I2C Controller + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +//#define AK39_I2C_INTERRUPT_MODE +#define AK39_I2C_POLL_MODE + +/* i2c controller state */ +enum ak39_i2c_state { + STATE_READ, + STATE_WRITE +}; + +enum read_data_addr { + READ_ADDR, + READ_DATA +}; + +struct ak39_i2c { + spinlock_t lock; + wait_queue_head_t wait; + unsigned int suspended:1; + + struct i2c_msg *msg; + unsigned int msg_num; + unsigned int msg_idx; + unsigned int msg_ptr; + + unsigned int irq; + unsigned long clkrate; + + enum ak39_i2c_state state; + enum read_data_addr read_value; + + void __iomem *regs; + struct clk *clk; + struct device *dev; + struct resource *ioarea; + struct i2c_adapter adap; + +#ifdef CONFIG_CPU_FREQ + struct notifier_block freq_transition; +#endif +}; + +static struct semaphore xfer_sem; + +/* ~~~~~~~~~~~ poll mode ~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +#ifdef AK39_I2C_POLL_MODE + +/* + * Poll the i2c status register until the specified bit is set. + * Return 1 if transfer finished. + */ +static short ak39_poll_status(void) +{ + do { + } while (!(__raw_readl(AK39_I2C_CTRL) & INT_PEND_FLAG)); + + return 1; +} + +static int xfer_read(struct ak39_i2c *i2c, unsigned char *buf, int length) +{ + int i,j, ctrl_value; + unsigned long ret, reg_value; + unsigned char *p = buf; + int idx = 0; + + if((length - 1) > 15) + ctrl_value = 16; + else + ctrl_value = length; + + ret = __raw_readl(AK39_I2C_CTRL); + ret |= (AK39_I2C_START | AK39_I2C_ACKEN | AK39_I2C_TXRXSEL | AK39_I2C_CLR_DELAY); + ret &= ~((0xf) << AK39_I2C_TRX_BYTE); + ret |= ((ctrl_value - 1) << AK39_I2C_TRX_BYTE); + ret &= ~(INT_PEND_FLAG); + __raw_writel(ret, AK39_I2C_CTRL); + + + for (i = 0; i < length / 16; i++) { + if (!ak39_poll_status()) { + dev_dbg(&i2c->adap.dev, "RXRDY timeout\n"); + return -ETIMEDOUT; + } + + for (j = 0; j < 4; j++) { + reg_value = __raw_readl(AK39_I2C_DATA0 + j * 4); + *(unsigned long *)p = reg_value; + p +=4; + } + } + + if (!ak39_poll_status()) { + dev_dbg(&i2c->adap.dev, "RXRDY timeout\n"); + return -ETIMEDOUT; + } + + idx = 0; + for (; idx < (length % 16) / 4; idx++) { + unsigned long regval = __raw_readl(AK39_I2C_DATA0 + idx * 4); + *(unsigned long *)p = regval; + p +=4; + } + + if (length % 4) { + unsigned long regval; + if (!ak39_poll_status()) { + dev_dbg(&i2c->adap.dev, "RXRDY timeout\n"); + return -ETIMEDOUT; + } + + regval = __raw_readl(AK39_I2C_DATA0 + idx / 4); + for (i = 0; i < length % 4; i++) { + *p++ = (regval >> (i * 8)) & 0xFF; + } + } + + return 0; +} + +static void wait_xfer_write(struct ak39_i2c *i2c, int length) +{ + unsigned long ret; + + ret = __raw_readl(AK39_I2C_CTRL); + ret |= (AK39_I2C_START); + ret &= ~(AK39_I2C_TXRXSEL | INT_PEND_FLAG); + ret &= ~((0xf) << AK39_I2C_TRX_BYTE); + ret |= ((length - 1)<< AK39_I2C_TRX_BYTE); + __raw_writel(ret, AK39_I2C_CTRL); + + if (!ak39_poll_status()) { + dev_info(&i2c->adap.dev, "TXRDY timeout\n"); + return ; + } +} + +static int xfer_write(struct ak39_i2c *i2c, unsigned char *buf, int length) +{ + unsigned int i; + unsigned long reg_value; + unsigned int num_count, num_char; + int ctrl_value, num_bck = length; + unsigned char *p = buf; + + if((length - 1) > 15) + ctrl_value = 16; + else + ctrl_value = length; + + num_count = num_bck / 16; + num_char = num_bck % 16; + + while(num_count--) { + for(i = 0; i < 4; i++) { + reg_value = *(unsigned long *)p; + p += 4; + __raw_writel(reg_value, AK39_I2C_DATA0 + i * 4); + } + + wait_xfer_write(i2c, 16); + } + if(num_char > 0) { + unsigned long value = 0; + for(i = 0; i < num_char; i+=4) { + value = *(unsigned long *)p; + p +=4; + __raw_writel(value, AK39_I2C_DATA0 + i); + } + wait_xfer_write(i2c, num_char); + } + + return 0; +} + +static int ak39_i2c_doxfer(struct ak39_i2c *i2c, struct i2c_msg *msgs, int num) +{ + unsigned int addr = (msgs->addr & 0x7f) << 1; + int i, ret, rw_flag; + + dev_dbg(&i2c->adap.dev, "ak39_i2c_doxfer: processing %d messages:\n", num); + + for (i = 0; i < num; i++) { + if (msgs->flags & I2C_M_RD) { + addr |= AK39_I2C_READ; + rw_flag = 1; + } else { + rw_flag = 0; + } + if (msgs->flags & I2C_M_REV_DIR_ADDR) + addr ^= 1; + + __raw_writel((AK39_I2C_CMD_EN | AK39_I2C_START_BIT)|addr, AK39_I2C_CMD1); + if(msgs->len && msgs->buf) { + if (rw_flag == 1) + ret = xfer_read(i2c, msgs->buf, msgs->len); + else + ret = xfer_write(i2c, msgs->buf, msgs->len); + + if (ret) + return ret; + } + dev_dbg(&i2c->adap.dev, "transfer complete\n"); + msgs++; /* next message */ + } + return i; +} +#endif + +/* ~~~~~~~~~~~ INTER MODE ~~~~~~~~~~~~~~~~~~~ */ + +#ifdef AK39_I2C_INTERRUPT_MODE + +static void clear_int_flag(void) +{ + unsigned long tmp; + + tmp = __raw_readl(AK39_I2C_CTRL); + tmp &= ~INT_PEND_FLAG; + __raw_writel(tmp, AK39_I2C_CTRL); +} + +/* irq disable functions */ +static void ak39_i2c_disable_irq(struct ak39_i2c *i2c) +{ + unsigned long tmp; + + tmp = __raw_readl(AK39_I2C_CTRL); + __raw_writel(tmp & ~AK39_I2C_INTEN, AK39_I2C_CTRL); +} + +static inline void ak39_i2c_master_complete(struct ak39_i2c *i2c, int ret) +{ + dev_dbg(i2c->dev, "master_complete %d\n", ret); + + i2c->msg_ptr = 0; + i2c->msg = NULL; + i2c->msg_num = 0; + if (ret) + i2c->msg_idx = ret; + + wake_up(&i2c->wait); +} + +static inline void ak39_i2c_stop(struct ak39_i2c *i2c, int ret) +{ + dev_dbg(i2c->dev, "STOP\n"); + + ak39_i2c_master_complete(i2c, ret); + ak39_i2c_disable_irq(i2c); +} + +static int ak39_i2c_irq_transfer(struct ak39_i2c *i2c) +{ + unsigned int addr = (i2c->msg->addr & 0x7f) << 1; + unsigned int num_char, i, length; + unsigned long stat, regval = 0; + unsigned char *p = i2c->msg->buf; + +read_next: + if(i2c->msg_idx < i2c->msg_num) { + if (i2c->msg->len == 0) { + ak39_i2c_stop(i2c, 0); + return 0; + } + + stat = __raw_readl(AK39_I2C_CTRL); + stat &= ~(0xf << 9); + if (i2c->msg->flags & I2C_M_RD) { + addr |= AK39_I2C_READ ; + i2c->state = STATE_READ ; + stat |= AK39_I2C_TXRXSEL ; + } + else { + i2c->state = STATE_WRITE ; + stat &= ~AK39_I2C_TXRXSEL ; + } + + if (i2c->msg->flags & I2C_M_REV_DIR_ADDR) + addr ^= 1; + + __raw_writel((AK39_I2C_CMD_EN | AK39_I2C_START_BIT)|addr, AK39_I2C_CMD1); + + switch(i2c->state) { + case STATE_WRITE: + + if (i2c->msg->len > 16) { + printk("Error, needed debug more data transmitted.\n"); + return 0; + } + if (i2c->msg_ptr < i2c->msg->len) { + + num_char = i2c->msg->len % 16; + + if (num_char > 0) { + __raw_writel(stat | ((num_char - 1) << 9), AK39_I2C_CTRL); + for(i = 0; i < num_char; i+=4) { + regval = *(unsigned long *)p; + p += 4; + __raw_writel(regval, AK39_I2C_DATA0 + i); + } + i2c->msg_ptr += num_char; + } + + if(i2c->msg_ptr >= i2c->msg->len){ + i2c->msg_ptr = 0; + i2c->msg_idx++; + i2c->msg++; + } + + stat = __raw_readl(AK39_I2C_CTRL); + __raw_writel(stat | AK39_I2C_START, AK39_I2C_CTRL); + } + break; + + case STATE_READ: + length = i2c->msg->len; + + if (i2c->msg->len > 16) { + printk("Error, needed debug more data transmitted.\n"); + return 0; + } + __raw_writel(stat | ((length - 1) << 9), AK39_I2C_CTRL); + + if(i2c->read_value == READ_ADDR) { + stat = __raw_readl(AK39_I2C_CTRL); + __raw_writel(stat | AK39_I2C_START, AK39_I2C_CTRL); + i2c->read_value = READ_DATA; + } else { + if (i2c->msg_ptr < i2c->msg->len) { + + num_char = i2c->msg->len % 16; + + if (num_char > 0) { + for (i = 0; i < num_char; i+=4) { + regval = __raw_readl(AK39_I2C_DATA0 + i); + *(unsigned long *)p = regval; + p += 4; + } + i2c->msg_ptr += num_char; + } + + if (i2c->msg_ptr >= i2c->msg->len) { + /* we need to go to the next i2c message */ + dev_dbg(i2c->dev, "READ: Next Message\n"); + + i2c->msg_ptr = 0; + i2c->msg_idx++; + i2c->msg++; + + if(i2c->msg_idx >= i2c->msg_num) { + ak39_i2c_stop(i2c, 0); + return 0; + } + } + i2c->read_value = READ_ADDR; + goto read_next; + } + } + break; + } + } + else { + ak39_i2c_stop(i2c, 0); + } + return 0; +} + +/* ak39_i2c_irq + * + * top level IRQ servicing routine +*/ +static irqreturn_t ak39_i2c_irq(int irqno, void *dev_id) +{ + struct ak39_i2c *i2c = dev_id; + + clear_int_flag(); + + /* pretty much this leaves us with the fact that we've + * transmitted or received whatever byte we last sent */ + + ak39_i2c_irq_transfer(i2c); + + return IRQ_HANDLED; +} + +static int ak39_i2c_doxfer(struct ak39_i2c *i2c, struct i2c_msg *msgs, int num) +{ + unsigned long timeout, stat; + int ret; + + if (i2c->suspended) + return -EIO; + + spin_lock_irq(&i2c->lock); + i2c->msg = msgs; + i2c->msg_num = num; + i2c->msg_ptr = 0; + i2c->msg_idx = 0; + i2c->read_value = READ_ADDR; + + ak39_i2c_irq_transfer(i2c); + stat = __raw_readl(AK39_I2C_CTRL); + stat |= AK39_I2C_INTEN | AK39_I2C_ACKEN; + __raw_writel(stat, AK39_I2C_CTRL); + + spin_unlock_irq(&i2c->lock); + timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); + + ret = i2c->msg_idx; + + /* having these next two as dev_err() makes life very + * noisy when doing an i2cdetect */ + + if (timeout == 0) + dev_info(i2c->dev, "timeout\n"); + else if (ret != num) + dev_info(i2c->dev, "incomplete xfer (%d)\n", ret); + + return ret; +} +#endif + + +/* ak39_i2c_xfer + * + * first port of call from the i2c bus code when an message needs + * transferring across the i2c bus. +*/ +static int ak39_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + struct ak39_i2c *i2c = (struct ak39_i2c *)adap->algo_data; + int retry; + int ret; + + down(&xfer_sem); + for (retry = 0; retry < adap->retries; retry++) { + + ret = ak39_i2c_doxfer(i2c, msgs, num); + + if (ret != -EAGAIN) + { + up(&xfer_sem); + return ret; + } + + dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry); + + udelay(100); + } + + up(&xfer_sem); + return -EREMOTEIO; +} + +/* declare our i2c functionality */ +static u32 ak39_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING; +} + +/* i2c bus registration info */ + +static const struct i2c_algorithm ak39_i2c_algorithm = { + .master_xfer = ak39_i2c_xfer, + .functionality = ak39_i2c_func, +}; + +/* ak39_i2c_calcdivisor + * + * return the divisor settings for a given frequency +*/ +static int ak39_i2c_calcdivisor(unsigned long clkin, unsigned int wanted, + unsigned int *div1, unsigned int *divs) +{ + unsigned int calc_divs = clkin / wanted / 2; + unsigned int calc_div1; + + if (calc_divs > (16*16)) + calc_div1 = 512; + else + calc_div1 = 16; + + calc_divs /= calc_div1; + + if (calc_divs == 0) + calc_divs = 1; + if (calc_divs > 16) + calc_divs = 16; + + *divs = calc_divs; + *div1 = calc_div1; + + return clkin / ((calc_divs * 2)* calc_div1); +} + +/* ak39_i2c_clockrate + * + * work out a divisor for the user requested frequency setting, + * either by the requested frequency, or scanning the acceptable + * range of frequencies until something is found +*/ +static int ak39_i2c_clockrate(struct ak39_i2c *i2c, unsigned int *got) +{ + struct ak39_platform_i2c *pdata = i2c->dev->platform_data; + unsigned long clkin = ak_get_asic_clk(); + unsigned int divs, div1; + unsigned long target_frequency; + u32 i2c_val, sda_delay; + int freq; + + i2c->clkrate = clkin; + clkin /= 1000; /* clkin now in KHz */ + + dev_dbg(i2c->dev, "pdata desired frequency %lu\n", pdata->frequency); + + target_frequency = pdata->frequency ? pdata->frequency : 100000; + //target_frequency *= 4; + target_frequency /= 1000; /* Target frequency now in KHz */ + + freq = ak39_i2c_calcdivisor(clkin, target_frequency, &div1, &divs); + if (freq > target_frequency) { + dev_err(i2c->dev, + "Unable to achieve desired frequency %luKHz." \ + " Lowest achievable %dKHz\n", target_frequency, freq); + // return -EINVAL; + } + + *got = freq; + + i2c_val = __raw_readl(AK39_I2C_CTRL); + i2c_val &= ~(AK39_TX_CLK_DIV | AK39_I2C_TXDIV_512); + i2c_val |= (divs-1); + + if (div1 == 512) + i2c_val |= AK39_I2C_TXDIV_512; + + __raw_writel(i2c_val, AK39_I2C_CTRL); + + if (pdata->sda_delay) { + sda_delay = (freq / 1000) * pdata->sda_delay; + sda_delay /= 1000000; + sda_delay = DIV_ROUND_UP(sda_delay, 5); + if (sda_delay > 3) + sda_delay = AK39_I2C_CLR_DELAY; + + sda_delay |= AK39_I2C_SDA_DELAY; + } else + sda_delay = ~AK39_I2C_CLR_DELAY; + + i2c_val |= sda_delay; + __raw_writel(i2c_val, AK39_I2C_CTRL); + + return 0; +} + +#ifdef CONFIG_CPU_FREQ + +#define freq_to_i2c(_n) container_of(_n, struct ak39_i2c, freq_transition) + +static int ak39_i2c_cpufreq_transition(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct ak39_i2c *i2c = freq_to_i2c(nb); + struct cpufreq_freqs *freqs = (struct cpufreq_freqs *)data; + unsigned int old_clk = freqs->old_cpufreq.asic_clk; + unsigned int new_clk = freqs->new_cpufreq.asic_clk; + unsigned long flags; + unsigned int got; + int ret; + + if (val == CPUFREQ_PRECHANGE) + { + down(&xfer_sem); + } + else if (val == CPUFREQ_POSTCHANGE) + { + if (old_clk != new_clk) + { + spin_lock_irqsave(&i2c->lock, flags); + ret = ak39_i2c_clockrate(i2c, &got); + //printk("setting freq %u\n", got); + spin_unlock_irqrestore(&i2c->lock, flags); + + if (ret < 0) + dev_err(i2c->dev, "cannot find frequency\n"); + else + dev_info(i2c->dev, "setting freq %d\n", got); + + } + up(&xfer_sem); + } + + return 0; +} + +static inline int ak39_i2c_register_cpufreq(struct ak39_i2c *i2c) +{ + i2c->freq_transition.notifier_call = ak39_i2c_cpufreq_transition; + return cpufreq_register_notifier(&i2c->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +static inline void ak39_i2c_deregister_cpufreq(struct ak39_i2c *i2c) +{ + cpufreq_unregister_notifier(&i2c->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} +#else +static inline int ak39_i2c_register_cpufreq(struct ak39_i2c *i2c) +{ + return 0; +} +static inline void ak39_i2c_deregister_cpufreq(struct ak39_i2c *i2c) +{ +} +#endif + +/* ak39_i2c_init + * + * initialise the controller, set the IO lines and frequency +*/ +static int ak39_i2c_init(struct ak39_i2c *i2c) +{ + struct ak39_platform_i2c *pdata; + unsigned int freq ; + + /* + * Reset I2C Controller + */ + ak39_soft_reset(AK39_SRESET_I2C); + + /* get the plafrom data */ + pdata = i2c->dev->platform_data; + + /* inititalise the gpio */ + ak_group_config(ePIN_AS_I2C); + + __raw_writel(AK39_I2C_INTEN | AK39_I2C_ACKEN, AK39_I2C_CTRL); + + /* we need to work out the divisors for the clock... */ + if (ak39_i2c_clockrate(i2c, &freq) != 0) { + __raw_writel(0, AK39_I2C_CTRL); + dev_err(i2c->dev, "cannot meet bus frequency required\n"); + return -EINVAL; + } + + /* todo - check that the i2c lines aren't being dragged anywhere */ + + dev_dbg(i2c->dev, "bus frequency set to %d KHz\n", freq); + return 0; +} + + +/* ak39_i2c_probe + * + * called by the bus driver when a suitable device is found +*/ +static int ak39_i2c_probe(struct platform_device *pdev) +{ + struct ak39_i2c *i2c; + struct ak39_platform_i2c *pdata; + struct resource *res; + int ret; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "no platform data\n"); + return -EINVAL; + } + + i2c = kzalloc(sizeof(struct ak39_i2c), GFP_KERNEL); + if (!i2c) { + dev_err(&pdev->dev, "no memory for state\n"); + return -ENOMEM; + } + + strlcpy(i2c->adap.name, "ak39-i2c", sizeof(i2c->adap.name)); + i2c->adap.owner = THIS_MODULE; + i2c->adap.algo = &ak39_i2c_algorithm; + i2c->adap.retries = 2; + i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + + spin_lock_init(&i2c->lock); + init_waitqueue_head(&i2c->wait); + + /* find the clock and enable it */ + + i2c->dev = &pdev->dev; + i2c->clk = clk_get(&pdev->dev, "i2c"); + if (IS_ERR(i2c->clk)) { + dev_err(&pdev->dev, "cannot get clock\n"); + ret = -ENOENT; + goto err_noclk; + } + + dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk); + + clk_enable(i2c->clk); + + /* map the registers */ + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "cannot find IO resource\n"); + ret = -ENOENT; + goto err_clk; + } + + i2c->ioarea = request_mem_region(res->start, resource_size(res), pdev->name); + if (i2c->ioarea == NULL) { + dev_err(&pdev->dev, "cannot request IO\n"); + ret = -ENXIO; + goto err_clk; + } + + i2c->regs = ioremap(res->start, resource_size(res)); + if (i2c->regs == NULL) { + dev_err(&pdev->dev, "cannot map IO\n"); + ret = -ENXIO; + goto err_ioarea; + } + + dev_dbg(&pdev->dev, "registers %p (%p, %p)\n", i2c->regs, i2c->ioarea, res); + + /* setup info block for the i2c core */ + + i2c->adap.algo_data = i2c; + i2c->adap.dev.parent = &pdev->dev; + + /* initialise the i2c controller */ + + ret = ak39_i2c_init(i2c); + if (ret != 0) + goto err_iomap; + + /* find the IRQ for this unit (note, this relies on the init call to + * ensure no current IRQs pending + */ + i2c->irq = ret = platform_get_irq(pdev, 0); + if (ret <= 0) { + dev_err(&pdev->dev, "cannot find IRQ\n"); + goto err_iomap; + } + +#ifdef AK39_I2C_INTERRUPT_MODE + ret = request_irq(i2c->irq, ak39_i2c_irq, IRQF_DISABLED, dev_name(&pdev->dev), i2c); + if (ret != 0) { + dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq); + goto err_iomap; + } +#endif + + sema_init(&xfer_sem, 1); + ret = ak39_i2c_register_cpufreq(i2c); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register cpufreq notifier\n"); + goto err_irq; + } + + i2c->adap.nr = pdata->bus_num; + ret = i2c_add_numbered_adapter(&i2c->adap); + + if (ret < 0) { + dev_err(&pdev->dev, "failed to add bus to i2c core\n"); + goto err_cpufreq; + } + + platform_set_drvdata(pdev, i2c); + + dev_info(&pdev->dev, "%s: AK39 I2C adapter\n", dev_name(&i2c->adap.dev)); + + return 0; + + err_cpufreq: + ak39_i2c_deregister_cpufreq(i2c); + + err_irq: + free_irq(i2c->irq, i2c); + + err_iomap: + iounmap(i2c->regs); + + err_ioarea: + release_resource(i2c->ioarea); + kfree(i2c->ioarea); + + err_clk: + clk_disable(i2c->clk); + clk_put(i2c->clk); + + err_noclk: + kfree(i2c); + return ret; +} + + +/* ak39_i2c_remove + * + * called when device is removed from the bus +*/ +static int ak39_i2c_remove(struct platform_device *pdev) +{ + struct ak39_i2c *i2c = platform_get_drvdata(pdev); + + ak39_i2c_deregister_cpufreq(i2c); + + i2c_del_adapter(&i2c->adap); + free_irq(i2c->irq, i2c); + + clk_disable(i2c->clk); + clk_put(i2c->clk); + + iounmap(i2c->regs); + + release_resource(i2c->ioarea); + + kfree(i2c->ioarea); + kfree(i2c); + + return 0; +} + +#ifdef CONFIG_PM +static int ak39_i2c_suspend_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ak39_i2c *i2c = platform_get_drvdata(pdev); + struct ak39_platform_i2c *pdata = pdev->dev.platform_data; + int i; + + down(&xfer_sem); + i2c->suspended = 1; + + clk_disable(i2c->clk); + clk_put(i2c->clk); + + for (i=0; inpins; i++) + ak_gpio_set(&(pdata->gpios[i])); + + return 0; +} + +static int ak39_i2c_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ak39_i2c *i2c = platform_get_drvdata(pdev); + + i2c->suspended = 0; + clk_enable(i2c->clk); + ak39_i2c_init(i2c); + up(&xfer_sem); + return 0; +} + +static struct dev_pm_ops ak39_i2c_dev_pm_ops = { + .suspend_noirq = ak39_i2c_suspend_noirq, + .resume = ak39_i2c_resume, +}; + +#define AK39_DEV_PM_OPS (&ak39_i2c_dev_pm_ops) +#else +#define AK39_DEV_PM_OPS NULL +#endif + +/* device driver for platform bus bits */ +static struct platform_driver ak39_i2c_driver = { + .probe = ak39_i2c_probe, + .remove = ak39_i2c_remove, + .driver = { + .owner = THIS_MODULE, + .name = "i2c-ak39", + .pm = AK39_DEV_PM_OPS, + }, +}; + +static int __init i2c_adap_ak39_init(void) +{ + return platform_driver_register(&ak39_i2c_driver); +} +subsys_initcall(i2c_adap_ak39_init); + +static void __exit i2c_adap_ak39_exit(void) +{ + platform_driver_unregister(&ak39_i2c_driver); +} +module_exit(i2c_adap_ak39_exit); + +MODULE_DESCRIPTION("AK39 I2C Bus driver"); +MODULE_AUTHOR("Anaka, "); +MODULE_LICENSE("GPL"); + diff --git a/drivers/i2c/busses/i2c-gpio-soft.c b/drivers/i2c/busses/i2c-gpio-soft.c new file mode 100644 index 00000000..37984874 --- /dev/null +++ b/drivers/i2c/busses/i2c-gpio-soft.c @@ -0,0 +1,287 @@ +/* + * Bitbanging I2C bus driver using the GPIO API + * + * Copyright (C) 2007 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +struct i2c_gpio_private_data { + struct i2c_adapter adap; + struct i2c_algo_bit_data bit_data; + struct i2c_gpio_platform_data pdata; +}; + +/* Toggle SDA by changing the direction of the pin */ +static void i2c_gpio_setsda_dir(void *data, int state) +{ + struct i2c_gpio_platform_data *pdata = data; + + if (state) + ak_gpio_dircfg(pdata->sda_pin, AK_GPIO_DIR_INPUT); + else { + ak_gpio_dircfg(pdata->sda_pin, AK_GPIO_DIR_OUTPUT); + ak_gpio_setpin(pdata->sda_pin, AK_GPIO_OUT_LOW); + } +} + +/* + * Toggle SDA by changing the output value of the pin. This is only + * valid for pins configured as open drain (i.e. setting the value + * high effectively turns off the output driver.) + */ +static void i2c_gpio_setsda_val(void *data, int state) +{ + struct i2c_gpio_platform_data *pdata = data; + + ak_gpio_setpin(pdata->sda_pin, state); +} + +/* Toggle SCL by changing the direction of the pin. */ +static void i2c_gpio_setscl_dir(void *data, int state) +{ + struct i2c_gpio_platform_data *pdata = data; + + if (state) + ak_gpio_dircfg(pdata->scl_pin, AK_GPIO_DIR_INPUT); + else { + ak_gpio_dircfg(pdata->scl_pin, AK_GPIO_DIR_OUTPUT); + ak_gpio_setpin(pdata->sda_pin, AK_GPIO_OUT_LOW); + } +} + +/* + * Toggle SCL by changing the output value of the pin. This is used + * for pins that are configured as open drain and for output-only + * pins. The latter case will break the i2c protocol, but it will + * often work in practice. + */ +static void i2c_gpio_setscl_val(void *data, int state) +{ + struct i2c_gpio_platform_data *pdata = data; + + ak_gpio_setpin(pdata->scl_pin, state); +} + +static int i2c_gpio_getsda(void *data) +{ + struct i2c_gpio_platform_data *pdata = data; + + return ak_gpio_getpin(pdata->sda_pin); +} + +static int i2c_gpio_getscl(void *data) +{ + struct i2c_gpio_platform_data *pdata = data; + + return ak_gpio_getpin(pdata->scl_pin); +} + +static int __devinit of_i2c_gpio_probe(struct device_node *np, + struct i2c_gpio_platform_data *pdata) +{ + u32 reg; + + if (of_gpio_count(np) < 2) + return -ENODEV; + + pdata->sda_pin = of_get_gpio(np, 0); + pdata->scl_pin = of_get_gpio(np, 1); + + if (!gpio_is_valid(pdata->sda_pin) || !gpio_is_valid(pdata->scl_pin)) { + pr_err("%s: invalid GPIO pins, sda=%d/scl=%d\n", + np->full_name, pdata->sda_pin, pdata->scl_pin); + return -ENODEV; + } + + of_property_read_u32(np, "i2c-gpio,delay-us", &pdata->udelay); + + if (!of_property_read_u32(np, "i2c-gpio,timeout-ms", ®)) + pdata->timeout = msecs_to_jiffies(reg); + + pdata->sda_is_open_drain = + of_property_read_bool(np, "i2c-gpio,sda-open-drain"); + pdata->scl_is_open_drain = + of_property_read_bool(np, "i2c-gpio,scl-open-drain"); + pdata->scl_is_output_only = + of_property_read_bool(np, "i2c-gpio,scl-output-only"); + + return 0; +} + +static int __devinit i2c_gpio_probe(struct platform_device *pdev) +{ + struct i2c_gpio_private_data *priv; + struct i2c_gpio_platform_data *pdata; + struct i2c_algo_bit_data *bit_data; + struct i2c_adapter *adap; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + adap = &priv->adap; + bit_data = &priv->bit_data; + pdata = &priv->pdata; + + if (pdev->dev.of_node) { + ret = of_i2c_gpio_probe(pdev->dev.of_node, pdata); + if (ret) + return ret; + } else { + if (!pdev->dev.platform_data) + return -ENXIO; + memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata)); + } + + ret = ak_gpio_request(pdata->sda_pin, "sda"); + if (ret) + goto err_request_sda; + ret = ak_gpio_request(pdata->scl_pin, "scl"); + if (ret) + goto err_request_scl; + + if (pdata->sda_is_open_drain) { + ak_gpio_dircfg(pdata->sda_pin, AK_GPIO_DIR_OUTPUT); + ak_gpio_setpin(pdata->sda_pin, AK_GPIO_OUT_HIGH); + bit_data->setsda = i2c_gpio_setsda_val; + } else { + ak_gpio_dircfg(pdata->sda_pin, AK_GPIO_DIR_INPUT); + bit_data->setsda = i2c_gpio_setsda_dir; + } + + if (pdata->scl_is_open_drain || pdata->scl_is_output_only) { + ak_gpio_dircfg(pdata->sda_pin, AK_GPIO_DIR_OUTPUT); + ak_gpio_setpin(pdata->sda_pin, AK_GPIO_OUT_HIGH); + bit_data->setscl = i2c_gpio_setscl_val; + } else { + ak_gpio_dircfg(pdata->sda_pin, AK_GPIO_DIR_INPUT); + bit_data->setscl = i2c_gpio_setscl_dir; + } + + if (!pdata->scl_is_output_only) + bit_data->getscl = i2c_gpio_getscl; + bit_data->getsda = i2c_gpio_getsda; + + if (pdata->udelay) + bit_data->udelay = pdata->udelay; + else if (pdata->scl_is_output_only) + bit_data->udelay = 50; /* 10 kHz */ + else + bit_data->udelay = 5; /* 100 kHz */ + + if (pdata->timeout) + bit_data->timeout = pdata->timeout; + else + bit_data->timeout = HZ / 10; /* 100 ms */ + + bit_data->data = pdata; + + adap->owner = THIS_MODULE; + snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id); + adap->algo_data = bit_data; + adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + adap->dev.parent = &pdev->dev; + adap->dev.of_node = pdev->dev.of_node; + + /* + * If "dev->id" is negative we consider it as zero. + * The reason to do so is to avoid sysfs names that only make + * sense when there are multiple adapters. + */ + adap->nr = (pdev->id != -1) ? pdev->id : 0; + ret = i2c_bit_add_numbered_bus(adap); + if (ret) + goto err_add_bus; + + of_i2c_register_devices(adap); + + platform_set_drvdata(pdev, priv); + + dev_info(&pdev->dev, "using pins %u (SDA) and %u (SCL%s)\n", + pdata->sda_pin, pdata->scl_pin, + pdata->scl_is_output_only + ? ", no clock stretching" : ""); + + return 0; + +err_add_bus: + ak_gpio_free(pdata->scl_pin); +err_request_scl: + ak_gpio_free(pdata->sda_pin); +err_request_sda: + return ret; +} + +static int __devexit i2c_gpio_remove(struct platform_device *pdev) +{ + struct i2c_gpio_private_data *priv; + struct i2c_gpio_platform_data *pdata; + struct i2c_adapter *adap; + + priv = platform_get_drvdata(pdev); + adap = &priv->adap; + pdata = &priv->pdata; + + i2c_del_adapter(adap); + ak_gpio_free(pdata->scl_pin); + ak_gpio_free(pdata->sda_pin); + + return 0; +} + +#if defined(CONFIG_OF) +static const struct of_device_id i2c_gpio_dt_ids[] = { + { .compatible = "i2c-gpio", }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, i2c_gpio_dt_ids); +#endif + +static struct platform_driver i2c_gpio_driver = { + .driver = { + .name = "i2c-gpio", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(i2c_gpio_dt_ids), + }, + .probe = i2c_gpio_probe, + .remove = __devexit_p(i2c_gpio_remove), +}; + +static int __init i2c_gpio_init(void) +{ + int ret; + + ret = platform_driver_register(&i2c_gpio_driver); + if (ret) + printk(KERN_ERR "i2c-gpio: probe failed: %d\n", ret); + + return ret; +} +subsys_initcall(i2c_gpio_init); + +static void __exit i2c_gpio_exit(void) +{ + platform_driver_unregister(&i2c_gpio_driver); +} +module_exit(i2c_gpio_exit); + +MODULE_AUTHOR("Anyka ltd company"); +MODULE_DESCRIPTION("Platform-independent bitbanging I2C driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:i2c-gpio"); diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 33259798..69d36863 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -165,6 +165,15 @@ config INPUT_APMPOWER To compile this driver as a module, choose M here: the module will be called apm-power. +config INPUT_KEYRESET + tristate "Reset key" + depends on INPUT + ---help--- + Say Y here if you want to reboot when some keys are pressed; + + To compile this driver as a module, choose M here: the + module will be called keyreset. + comment "Input Device Drivers" source "drivers/input/keyboard/Kconfig" diff --git a/drivers/input/Makefile b/drivers/input/Makefile index b173a13a..cf643bee 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -25,3 +25,4 @@ obj-$(CONFIG_INPUT_MISC) += misc/ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o obj-$(CONFIG_INPUT_OF_MATRIX_KEYMAP) += of_keymap.o +obj-$(CONFIG_INPUT_KEYRESET) += keyreset.o diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 4b2e10d5..a9374387 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "input-compat.h" struct evdev { @@ -43,6 +44,9 @@ struct evdev_client { unsigned int tail; unsigned int packet_head; /* [future] position of the first element of next packet */ spinlock_t buffer_lock; /* protects access to buffer, head and tail */ + struct wake_lock wake_lock; + bool use_wake_lock; + char name[28]; struct fasync_struct *fasync; struct evdev *evdev; struct list_head node; @@ -80,10 +84,14 @@ static void evdev_pass_event(struct evdev_client *client, client->buffer[client->tail].value = 0; client->packet_head = client->tail; + if (client->use_wake_lock) + wake_unlock(&client->wake_lock); } if (event->type == EV_SYN && event->code == SYN_REPORT) { client->packet_head = client->head; + if (client->use_wake_lock) + wake_lock(&client->wake_lock); kill_fasync(&client->fasync, SIGIO, POLL_IN); } @@ -264,6 +272,8 @@ static int evdev_release(struct inode *inode, struct file *file) mutex_unlock(&evdev->mutex); evdev_detach_client(evdev, client); + if (client->use_wake_lock) + wake_lock_destroy(&client->wake_lock); kfree(client); evdev_close_device(evdev); @@ -315,6 +325,8 @@ static int evdev_open(struct inode *inode, struct file *file) client->bufsize = bufsize; spin_lock_init(&client->buffer_lock); + snprintf(client->name, sizeof(client->name), "%s-%d", + dev_name(&evdev->dev), task_tgid_vnr(current)); client->evdev = evdev; evdev_attach_client(evdev, client); @@ -382,6 +394,9 @@ static int evdev_fetch_next_event(struct evdev_client *client, if (have_event) { *event = client->buffer[client->tail++]; client->tail &= client->bufsize - 1; + if (client->use_wake_lock && + client->packet_head == client->tail) + wake_unlock(&client->wake_lock); } spin_unlock_irq(&client->buffer_lock); @@ -654,6 +669,35 @@ static int evdev_handle_mt_request(struct input_dev *dev, return 0; } +static int evdev_enable_suspend_block(struct evdev *evdev, + struct evdev_client *client) +{ + if (client->use_wake_lock) + return 0; + + spin_lock_irq(&client->buffer_lock); + wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, client->name); + client->use_wake_lock = true; + if (client->packet_head != client->tail) + wake_lock(&client->wake_lock); + spin_unlock_irq(&client->buffer_lock); + return 0; +} + +static int evdev_disable_suspend_block(struct evdev *evdev, + struct evdev_client *client) +{ + if (!client->use_wake_lock) + return 0; + + spin_lock_irq(&client->buffer_lock); + client->use_wake_lock = false; + wake_lock_destroy(&client->wake_lock); + spin_unlock_irq(&client->buffer_lock); + + return 0; +} + static long evdev_do_ioctl(struct file *file, unsigned int cmd, void __user *p, int compat_mode) { @@ -735,6 +779,15 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, case EVIOCSKEYCODE_V2: return evdev_handle_set_keycode_v2(dev, p); + + case EVIOCGSUSPENDBLOCK: + return put_user(client->use_wake_lock, ip); + + case EVIOCSSUSPENDBLOCK: + if (p) + return evdev_enable_suspend_block(evdev, client); + else + return evdev_disable_suspend_block(evdev, client); } size = _IOC_SIZE(cmd); diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index f354813a..3e362083 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -580,4 +580,35 @@ config KEYBOARD_W90P910 To compile this driver as a module, choose M here: the module will be called w90p910_keypad. +config KEYBOARD_AKKEY + tristate "Anyka gpio-keys support" + depends on ARCH_AK39 + help + This driver implements support for buttons connected + to GPIO pins of various CPUs (and some other chips). + + To compile this driver as a module, choose M here: the + module will be called akgpio_keys. + +config KEYBOARD_AK_KEYPAD + tristate "Anyka matrix-keypad support" + depends on ARCH_AK39 + help + This driver implements support for buttons connected + to GPIO pins of various CPUs (and some other chips). + + To compile this driver as a module, choose M here: the + module will be called ak_matrix_keypad. + +config KEYBOARD_ADKEY + tristate "ADC simulate gpio keys support" + depends on ARCH_AK39 +# select INPUT_POLLDEV + help + This driver implements support for AD-KEY connected + to ADC pin. + + To compile this driver as a module, choose M here: the + module will be called akad-key. + endif diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index df7061f1..30cf1c52 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -52,3 +52,6 @@ obj-$(CONFIG_KEYBOARD_TNETV107X) += tnetv107x-keypad.o obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o +obj-$(CONFIG_KEYBOARD_AKKEY) += akgpio_keys.o +obj-$(CONFIG_KEYBOARD_AK_KEYPAD) += akmatrix_keypad.o +obj-$(CONFIG_KEYBOARD_ADKEY) += akad-keys.o diff --git a/drivers/input/keyboard/akad-keys.c b/drivers/input/keyboard/akad-keys.c new file mode 100644 index 00000000..7a168f11 --- /dev/null +++ b/drivers/input/keyboard/akad-keys.c @@ -0,0 +1,372 @@ +/* + * drivers/input/keyboard/akad-keys.c + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +//#define ADKEY_DEBUG + +#ifdef ADKEY_DEBUG +#define pr_dbg(fmt...) printk(fmt) +#else +#define pr_dbg(fmt...) +#endif + +#define DRV_NAME "ad-keys" + +struct adgpio_key_data { + struct input_dev *input; + struct adgpio_key *keys; + struct timer_list timer; + /* private: */ + struct delayed_work work; + + struct semaphore sem; + + struct multi_addetect *addet; + int naddet; + + int poll_interval; + int debounce_interval; + int keycode; + int nkey; + + unsigned char loop; + unsigned char press; + unsigned char last_press; + unsigned char sync_me; +}; + +static void adkeys_poll(struct adgpio_key_data *adkey); + +static void adkey_input_queue_work(struct adgpio_key_data *adkey) +{ + unsigned long delay; + + delay = msecs_to_jiffies(adkey->poll_interval); + if (delay >= HZ) + delay = round_jiffies_relative(delay); + + queue_delayed_work(system_freezable_wq, &adkey->work, delay); +} + +static void adkey_input_device_work(struct work_struct *work) +{ + struct adgpio_key_data *adkey = + container_of(work, struct adgpio_key_data, work.work); + + adkeys_poll(adkey); + adkey_input_queue_work(adkey); +} + +#if 0 +static void addetect_plugin_notify(T_ad_check state) +{ + static bool dev1_flag = false; + static bool dev2_flag = false; + static bool dev3_flag = false; + static bool dev4_flag = false; + + pr_dbg("adkey: plugin device state: 0x%0x\n", state); + + if ((state & PLUGIN_DEV1) && (!dev1_flag)) { + addetect_notifier_call_chain(ADDEDECT_DEV1_PLUGIN, NULL); + dev1_flag = true; + } else if ((!(state & PLUGIN_DEV1)) && dev1_flag){ + addetect_notifier_call_chain(ADDEDECT_DEV1_PLUGOUT, NULL); + dev1_flag = false; + } + + if ((state & PLUGIN_DEV2) && (!dev2_flag)) { + addetect_notifier_call_chain(ADDEDECT_DEV2_PLUGIN, NULL); + dev2_flag = true; + } else if ((!(state & PLUGIN_DEV2)) && dev2_flag){ + addetect_notifier_call_chain(ADDEDECT_DEV2_PLUGOUT, NULL); + dev2_flag = false; + } + + if ((state & PLUGIN_DEV3) && (!dev3_flag)) { + addetect_notifier_call_chain(ADDEDECT_DEV3_PLUGIN, NULL); + dev3_flag = true; + } else if ((!(state & PLUGIN_DEV3)) && dev3_flag) { + addetect_notifier_call_chain(ADDEDECT_DEV3_PLUGOUT, NULL); + dev3_flag = false; + } + + if ((state & PLUGIN_DEV4) && (!dev4_flag)) { + addetect_notifier_call_chain(ADDEDECT_DEV4_PLUGIN, NULL); + dev4_flag = true; + } else if ((!(state & PLUGIN_DEV4)) && dev4_flag) { + addetect_notifier_call_chain(ADDEDECT_DEV4_PLUGOUT, NULL); + dev4_flag = false; + } +} +#endif + +static void adkeys_timer(unsigned long _data) +{ + struct adgpio_key_data *adkey = (struct adgpio_key_data *)_data; + struct input_dev *input_dev = adkey->input; + int advol=0, i, code; + + if (!adkey->press) { + adkey->sync_me = 0; + } else { + advol = adc1_read_ad5(); + pr_dbg("timer: volatge: %d\n", advol); + + i = 0; + do { + if ((advol >= adkey->keys[i].min)&&(advol <= adkey->keys[i].max)) { + pr_dbg("timer: volatge: %d\n", advol); + code = adkey->keys[i].code; + if ((code == adkey->keycode)&&(i == adkey->loop)) + break; + } + i++; + } while(i < adkey->nkey); + } + + /* report the key */ + if(adkey->press != adkey->last_press){ + pr_dbg("input_event: %d press: %d\n", adkey->keycode, adkey->press); + adkey->last_press = adkey->press; + input_event(input_dev, EV_KEY, adkey->keycode, adkey->press); + input_sync(input_dev); + } +} + +static void adkeys_poll(struct adgpio_key_data *adkey) +{ + int advol, i; + static struct adgpio_key *prev_keys = NULL; + static T_ad_check state = PLUGIN_INVALID; + static int is_change_flag = 0; + + advol = adc1_read_ad5(); + pr_dbg("poll: volatge: %d\n", advol); + + for (i = 0; i < adkey->naddet; i++) { + if ((advol >= adkey->addet[i].unpress_min) + &&(advol <= adkey->addet[i].unpress_max)) { + + adkey->keys = adkey->addet[i].fixkeys; + if ((prev_keys != adkey->keys) || (state != adkey->addet[i].plugdev)) { + prev_keys = adkey->keys; + state = adkey->addet[i].plugdev; + is_change_flag = 1; + } else { + is_change_flag = 0; + } + break; + } + } + + /* + if (is_change_flag) { + addetect_plugin_notify(state); + } + */ + + i = 0; + do { + if ((adkey->keys != NULL) + && (advol >= adkey->keys[i].min) && (advol <= adkey->keys[i].max)) { + adkey->press = 1; + adkey->sync_me = 1; + + adkey->keycode = adkey->keys[i].code; + adkey->loop = i; + break; + } else + adkey->press = 0; + + i++; + } while(i < adkey->nkey); + + if (adkey->sync_me) { + mod_timer(&adkey->timer, + jiffies + msecs_to_jiffies(adkey->debounce_interval)); + } +} + +static int analog_gpio_probe(struct platform_device *pdev) +{ + struct adgpio_key_data *adkey; + struct input_dev *input_dev; + struct analog_gpio_key *pdata = NULL; + int i, err; + + pdata = pdev->dev.platform_data; + if (!pdata) + return -ENODEV; + + printk("###analog_gpio_probe\n"); + adkey = kzalloc(sizeof(struct adgpio_key_data)+ + pdata->naddet*sizeof(struct multi_addetect)+ + pdata->nkey*sizeof(struct adgpio_key), GFP_KERNEL); + if (!adkey) + return -ENOMEM; + + input_dev = input_allocate_device(); + if (!input_dev) { + err = -ENOMEM; + goto fail; + } + + platform_set_drvdata(pdev, adkey); + + adkey->input = input_dev; + if ((pdata->addet) && (pdata->naddet > 0)) { + adkey->addet = pdata->addet; + adkey->naddet = pdata->naddet; + adkey->keys = pdata->addet[0].fixkeys; + } + adkey->nkey = pdata->nkey; + + if (pdata->interval <= 0) + adkey->poll_interval = 500; + else + adkey->poll_interval = pdata->interval; + + if (pdata->debounce_interval <= 0) + adkey->debounce_interval = 20; + else + adkey->debounce_interval = pdata->debounce_interval; + + adkey->press = 0; + adkey->last_press = 0; + adkey->sync_me = 0; + + input_dev->name = pdev->name; + input_dev->phys = DRV_NAME"/input0"; + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x0010; + input_dev->id.version = 0x00100; + input_dev->dev.parent = &pdev->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY); + input_dev->keycode = adkey->keys; + input_dev->keycodesize = sizeof(struct adgpio_key); + input_dev->keycodemax = adkey->nkey; + + for (i = 0; i < adkey->nkey; i++) + set_bit(adkey->keys[i].code, input_dev->keybit); + clear_bit(KEY_RESERVED, input_dev->keybit); + + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + + INIT_DELAYED_WORK(&adkey->work, adkey_input_device_work); + + /* Only start polling if polling is enabled */ + adkey_input_queue_work(adkey); + + setup_timer(&adkey->timer, adkeys_timer, (unsigned long)adkey); + + //set gpio5 to ain1 + REG32(SAR_ADC_ACR_REG3) |= (1<<29); + + err = input_register_device(adkey->input); + if (err) + goto fail; + + return 0; + +fail: + printk(KERN_ERR "Adkey: failed to register driver, error: %d\n", err); + platform_set_drvdata(pdev, NULL); + input_free_device(input_dev); + kfree(adkey); + return err; +} + +static int analog_gpio_remove(struct platform_device *pdev) +{ + struct adgpio_key_data *adkey = platform_get_drvdata(pdev); + + cancel_delayed_work_sync(&adkey->work); + + input_unregister_device(adkey->input); + + input_free_device(adkey->input); + + del_timer_sync(&adkey->timer); + kfree(adkey); + + return 0; +} + + +#ifdef CONFIG_PM +static int analog_gpio_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct adgpio_key_data *adkey = platform_get_drvdata(pdev); + struct analog_gpio_key *pdata = pdev->dev.platform_data; + + if (pdata->wakeup > 0) + adkey_wakeup_enable(true); + + del_timer_sync(&adkey->timer); + + return 0; +} + +static int analog_gpio_resume(struct platform_device *pdev) +{ + //struct adgpio_key_data *adkey = platform_get_drvdata(pdev); + struct analog_gpio_key *pdata = pdev->dev.platform_data; + + if (pdata->wakeup > 0) + adkey_wakeup_enable(false); + + return 0; +} +#else +#define analog_gpio_suspend NULL +#define analog_gpio_resume NULL +#endif + +static struct platform_driver analog_ops = { + .probe = analog_gpio_probe, + .remove = __devexit_p(analog_gpio_remove), + .suspend = analog_gpio_suspend, + .resume = analog_gpio_resume, + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + }, +}; + +static int __init analog_gpio_key_init(void) +{ + printk("Init ADC simulate gpio keys driver.\n"); + return platform_driver_register(&analog_ops); +} + +static void __exit analog_gpio_key_exit(void) +{ + platform_driver_unregister(&analog_ops); +} + +late_initcall(analog_gpio_key_init); +module_exit(analog_gpio_key_exit); + +MODULE_AUTHOR("Anyka Microelectronic Ltd."); +MODULE_DESCRIPTION("Anyka ADC simulate gpio keys driver"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/keyboard/akgpio_keys.c b/drivers/input/keyboard/akgpio_keys.c new file mode 100644 index 00000000..923775a1 --- /dev/null +++ b/drivers/input/keyboard/akgpio_keys.c @@ -0,0 +1,1003 @@ +/* + * Driver for keys on GPIO lines capable of generating interrupts. + * + * Copyright 2005 Phil Blundell + * Copyright 2010, 2011 David Jander + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct gpio_button_data { + const struct gpio_keys_button *button; + struct input_dev *input; + struct timer_list timer; + struct work_struct work; + unsigned int timer_debounce; /* in msecs */ + unsigned int irq; + spinlock_t lock; + bool disabled; + bool key_pressed; +}; + +struct gpio_keys_drvdata { + struct input_dev *input; + struct mutex disable_lock; + unsigned int n_buttons; + int (*enable)(struct device *dev); + void (*disable)(struct device *dev); + struct gpio_button_data data[0]; +}; + +/* + * SYSFS interface for enabling/disabling keys and switches: + * + * There are 4 attributes under /sys/devices/platform/gpio-keys/ + * keys [ro] - bitmap of keys (EV_KEY) which can be + * disabled + * switches [ro] - bitmap of switches (EV_SW) which can be + * disabled + * disabled_keys [rw] - bitmap of keys currently disabled + * disabled_switches [rw] - bitmap of switches currently disabled + * + * Userland can change these values and hence disable event generation + * for each key (or switch). Disabling a key means its interrupt line + * is disabled. + * + * For example, if we have following switches set up as gpio-keys: + * SW_DOCK = 5 + * SW_CAMERA_LENS_COVER = 9 + * SW_KEYPAD_SLIDE = 10 + * SW_FRONT_PROXIMITY = 11 + * This is read from switches: + * 11-9,5 + * Next we want to disable proximity (11) and dock (5), we write: + * 11,5 + * to file disabled_switches. Now proximity and dock IRQs are disabled. + * This can be verified by reading the file disabled_switches: + * 11,5 + * If we now want to enable proximity (11) switch we write: + * 5 + * to disabled_switches. + * + * We can disable only those keys which don't allow sharing the irq. + */ + +/** + * get_n_events_by_type() - returns maximum number of events per @type + * @type: type of button (%EV_KEY, %EV_SW) + * + * Return value of this function can be used to allocate bitmap + * large enough to hold all bits for given type. + */ +static inline int get_n_events_by_type(int type) +{ + BUG_ON(type != EV_SW && type != EV_KEY); + + return (type == EV_KEY) ? KEY_CNT : SW_CNT; +} + +/** + * gpio_keys_disable_button() - disables given GPIO button + * @bdata: button data for button to be disabled + * + * Disables button pointed by @bdata. This is done by masking + * IRQ line. After this function is called, button won't generate + * input events anymore. Note that one can only disable buttons + * that don't share IRQs. + * + * Make sure that @bdata->disable_lock is locked when entering + * this function to avoid races when concurrent threads are + * disabling buttons at the same time. + */ +static void gpio_keys_disable_button(struct gpio_button_data *bdata) +{ + if (!bdata->disabled) { + /* + * Disable IRQ and possible debouncing timer. + */ + disable_irq(bdata->irq); + if (bdata->timer_debounce) + del_timer_sync(&bdata->timer); + + bdata->disabled = true; + } +} + +/** + * gpio_keys_enable_button() - enables given GPIO button + * @bdata: button data for button to be disabled + * + * Enables given button pointed by @bdata. + * + * Make sure that @bdata->disable_lock is locked when entering + * this function to avoid races with concurrent threads trying + * to enable the same button at the same time. + */ +static void gpio_keys_enable_button(struct gpio_button_data *bdata) +{ + if (bdata->disabled) { + enable_irq(bdata->irq); + bdata->disabled = false; + } +} + +/** + * gpio_keys_attr_show_helper() - fill in stringified bitmap of buttons + * @ddata: pointer to drvdata + * @buf: buffer where stringified bitmap is written + * @type: button type (%EV_KEY, %EV_SW) + * @only_disabled: does caller want only those buttons that are + * currently disabled or all buttons that can be + * disabled + * + * This function writes buttons that can be disabled to @buf. If + * @only_disabled is true, then @buf contains only those buttons + * that are currently disabled. Returns 0 on success or negative + * errno on failure. + */ +static ssize_t gpio_keys_attr_show_helper(struct gpio_keys_drvdata *ddata, + char *buf, unsigned int type, + bool only_disabled) +{ + int n_events = get_n_events_by_type(type); + unsigned long *bits; + ssize_t ret; + int i; + + bits = kcalloc(BITS_TO_LONGS(n_events), sizeof(*bits), GFP_KERNEL); + if (!bits) + return -ENOMEM; + + for (i = 0; i < ddata->n_buttons; i++) { + struct gpio_button_data *bdata = &ddata->data[i]; + + if (bdata->button->type != type) + continue; + + if (only_disabled && !bdata->disabled) + continue; + + __set_bit(bdata->button->code, bits); + } + + ret = bitmap_scnlistprintf(buf, PAGE_SIZE - 2, bits, n_events); + buf[ret++] = '\n'; + buf[ret] = '\0'; + + kfree(bits); + + return ret; +} + +/** + * gpio_keys_attr_store_helper() - enable/disable buttons based on given bitmap + * @ddata: pointer to drvdata + * @buf: buffer from userspace that contains stringified bitmap + * @type: button type (%EV_KEY, %EV_SW) + * + * This function parses stringified bitmap from @buf and disables/enables + * GPIO buttons accordingly. Returns 0 on success and negative error + * on failure. + */ +static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata, + const char *buf, unsigned int type) +{ + int n_events = get_n_events_by_type(type); + unsigned long *bits; + ssize_t error; + int i; + + bits = kcalloc(BITS_TO_LONGS(n_events), sizeof(*bits), GFP_KERNEL); + if (!bits) + return -ENOMEM; + + error = bitmap_parselist(buf, bits, n_events); + if (error) + goto out; + + /* First validate */ + for (i = 0; i < ddata->n_buttons; i++) { + struct gpio_button_data *bdata = &ddata->data[i]; + + if (bdata->button->type != type) + continue; + + if (test_bit(bdata->button->code, bits) && + !bdata->button->can_disable) { + error = -EINVAL; + goto out; + } + } + + mutex_lock(&ddata->disable_lock); + + for (i = 0; i < ddata->n_buttons; i++) { + struct gpio_button_data *bdata = &ddata->data[i]; + + if (bdata->button->type != type) + continue; + + if (test_bit(bdata->button->code, bits)) + gpio_keys_disable_button(bdata); + else + gpio_keys_enable_button(bdata); + } + + mutex_unlock(&ddata->disable_lock); + +out: + kfree(bits); + return error; +} + +#define ATTR_SHOW_FN(name, type, only_disabled) \ +static ssize_t gpio_keys_show_##name(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct platform_device *pdev = to_platform_device(dev); \ + struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); \ + \ + return gpio_keys_attr_show_helper(ddata, buf, \ + type, only_disabled); \ +} + +ATTR_SHOW_FN(keys, EV_KEY, false); +ATTR_SHOW_FN(switches, EV_SW, false); +ATTR_SHOW_FN(disabled_keys, EV_KEY, true); +ATTR_SHOW_FN(disabled_switches, EV_SW, true); + +/* + * ATTRIBUTES: + * + * /sys/devices/platform/gpio-keys/keys [ro] + * /sys/devices/platform/gpio-keys/switches [ro] + */ +static DEVICE_ATTR(keys, S_IRUGO, gpio_keys_show_keys, NULL); +static DEVICE_ATTR(switches, S_IRUGO, gpio_keys_show_switches, NULL); + +#define ATTR_STORE_FN(name, type) \ +static ssize_t gpio_keys_store_##name(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, \ + size_t count) \ +{ \ + struct platform_device *pdev = to_platform_device(dev); \ + struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); \ + ssize_t error; \ + \ + error = gpio_keys_attr_store_helper(ddata, buf, type); \ + if (error) \ + return error; \ + \ + return count; \ +} + +ATTR_STORE_FN(disabled_keys, EV_KEY); +ATTR_STORE_FN(disabled_switches, EV_SW); + +/* + * ATTRIBUTES: + * + * /sys/devices/platform/gpio-keys/disabled_keys [rw] + * /sys/devices/platform/gpio-keys/disables_switches [rw] + */ +static DEVICE_ATTR(disabled_keys, S_IWUSR | S_IRUGO, + gpio_keys_show_disabled_keys, + gpio_keys_store_disabled_keys); +static DEVICE_ATTR(disabled_switches, S_IWUSR | S_IRUGO, + gpio_keys_show_disabled_switches, + gpio_keys_store_disabled_switches); + +static struct attribute *gpio_keys_attrs[] = { + &dev_attr_keys.attr, + &dev_attr_switches.attr, + &dev_attr_disabled_keys.attr, + &dev_attr_disabled_switches.attr, + NULL, +}; + +static struct attribute_group gpio_keys_attr_group = { + .attrs = gpio_keys_attrs, +}; + +/** +* @Copyright (C) Anyka 2012 +* @brief init gpio +* @date 2013-5-14 +* @param[out] void +* @param[in] *button +* @return fail or not +*/ +static int gpio_init_data(const struct gpio_keys_button *button) +{ + ak_setpin_as_gpio(button->gpio); + ak_gpio_cfgpin(button->gpio, button->dir); + if (button->pullup == AK_PULLUP_ENABLE || button->pullup == AK_PULLUP_DISABLE) + ak_gpio_pullup(button->gpio, button->pullup); + if (button->pulldown == AK_PULLDOWN_ENABLE || button->pulldown == AK_PULLDOWN_DISABLE) + ak_gpio_pulldown(button->gpio, button->pulldown); + ak_gpio_intpol(button->gpio, button->int_pol); + + return 0; +} + +/** +* @Copyright (C) Anyka 2012 +* @brief revert irq polarity +* @date 2013-5-14 +* @param[out] void +* @param[in] irq, int_pol +* @return none +*/ +static void revert_irq_polarity(unsigned int irq, char int_pol) +{ + ak_gpio_intpol(ak_irq_to_gpio(irq), int_pol); +} + +/** +* @Copyright (C) Anyka 2012 +* @brief report event to user +* @date 2013-5-14 +* @param[out] void +* @param[in] *bdata +* @return none +*/ +static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata) +{ + const struct gpio_keys_button *button = bdata->button; + struct input_dev *input = bdata->input; + unsigned int type = button->type ?: EV_KEY; + int state = (ak_gpio_getpin(button->gpio) ? 1 : 0) ^ button->active_low; + + if (type == EV_ABS) { + if (state) + input_event(input, type, button->code, button->value); + } else { + input_event(input, type, button->code, !!state); + } + input_sync(input); +} + +/** +* @Copyright (C) Anyka 2012 +* @brief keys work function +* @date 2013-5-14 +* @param[out] void +* @param[in] *work +* @return none +*/ +static void gpio_keys_gpio_work_func(struct work_struct *work) +{ + struct gpio_button_data *bdata = + container_of(work, struct gpio_button_data, work); + + gpio_keys_gpio_report_event(bdata); +} + +/** +* @Copyright (C) Anyka 2012 +* @brief keys timer +* @date 2013-5-14 +* @param[out] void +* @param[in] _data +* @return none +*/ +static void gpio_keys_gpio_timer(unsigned long _data) +{ + struct gpio_button_data *bdata = (struct gpio_button_data *)_data; + + schedule_work(&bdata->work); +} + +/** +* @Copyright (C) Anyka 2012 +* @brief keys isr +* @date 2013-5-14 +* @param[out] void +* @param[in] int irq, void *dev_id +* @return IRQ_HANDLED +*/ +static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id) +{ + struct gpio_button_data *bdata = dev_id; + const struct gpio_keys_button *button = bdata->button; + int gpio_level; + + BUG_ON(irq != bdata->irq); + + gpio_level = ak_gpio_getpin(button->gpio); + //disalbe gpio_irq when the button down + if ((gpio_level && button->int_pol) || (!gpio_level && !button->int_pol)) + revert_irq_polarity(irq, !button->int_pol); + + //enable gpiot_irq when the button up + if ((!gpio_level && button->int_pol) || (gpio_level && !button->int_pol)) + revert_irq_polarity(irq, button->int_pol); + + if (bdata->timer_debounce) + mod_timer(&bdata->timer, + jiffies + msecs_to_jiffies(bdata->timer_debounce)); + else + schedule_work(&bdata->work); + + return IRQ_HANDLED; +} + +/** +* @Copyright (C) Anyka 2012 +* @brief keys timer isr +* @date 2013-5-14 +* @param[out] void +* @param[in] _data +* @return none +*/ +static void gpio_keys_irq_timer(unsigned long _data) +{ + struct gpio_button_data *bdata = (struct gpio_button_data *)_data; + struct input_dev *input = bdata->input; + unsigned long flags; + + spin_lock_irqsave(&bdata->lock, flags); + if (bdata->key_pressed) { + input_event(input, EV_KEY, bdata->button->code, 0); + input_sync(input); + bdata->key_pressed = false; + } + spin_unlock_irqrestore(&bdata->lock, flags); +} + +/** +* @Copyright (C) Anyka 2012 +* @brief keys irq isr +* @date 2013-5-14 +* @param[out] void +* @param[in] int irq, void *dev_id +* @return IRQ_HANDLED +*/ +static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id) +{ + struct gpio_button_data *bdata = dev_id; + const struct gpio_keys_button *button = bdata->button; + struct input_dev *input = bdata->input; + unsigned long flags; + + BUG_ON(irq != bdata->irq); + + spin_lock_irqsave(&bdata->lock, flags); + + if (!bdata->key_pressed) { + input_event(input, EV_KEY, button->code, 1); + input_sync(input); + + if (!bdata->timer_debounce) { + input_event(input, EV_KEY, button->code, 0); + input_sync(input); + goto out; + } + + bdata->key_pressed = true; + } + + if (bdata->timer_debounce) + mod_timer(&bdata->timer, + jiffies + msecs_to_jiffies(bdata->timer_debounce)); +out: + spin_unlock_irqrestore(&bdata->lock, flags); + return IRQ_HANDLED; +} + +/** +* @Copyright (C) Anyka 2012 +* @brief setup key +* @date 2013-5-14 +* @param[out] void +* @param[in] *pdev,... +* @return fail or not +*/ +static int __devinit gpio_keys_setup_key(struct platform_device *pdev, + struct input_dev *input, + struct gpio_button_data *bdata, + const struct gpio_keys_button *button) +{ + const char *desc = button->desc ? button->desc : "gpio_keys"; + struct device *dev = &pdev->dev; + irq_handler_t isr; + unsigned long irqflags; + int irq, error; + + bdata->input = input; + bdata->button = button; + spin_lock_init(&bdata->lock); + + if (gpio_is_valid(button->gpio)) { + + error = gpio_init_data(button); + if (error < 0) { + dev_err(dev, "gpio-keys: failed to configure input" + " direction and pull up/down for GPIO %d, error %d\n", + button->gpio, error); + gpio_free(button->gpio); + goto fail; + } + + if (button->debounce_interval) { + error = gpio_set_debounce(button->gpio, + button->debounce_interval * 1000); + /* use timer if gpiolib doesn't provide debounce */ + if (error < 0) + bdata->timer_debounce = button->debounce_interval; + } + + irq = ak_gpio_to_irq(button->gpio); + if (irq < 0) { + error = irq; + dev_err(dev, + "Unable to get irq number for GPIO %d, error %d\n", + button->gpio, error); + goto fail; + } + bdata->irq = irq; + + INIT_WORK(&bdata->work, gpio_keys_gpio_work_func); + setup_timer(&bdata->timer, + gpio_keys_gpio_timer, (unsigned long)bdata); + + isr = gpio_keys_gpio_isr; + irqflags = (button->active_low)?(IRQF_TRIGGER_LOW):(IRQF_TRIGGER_HIGH); + + } else { + if (!button->irq) { + dev_err(dev, "No IRQ specified\n"); + return -EINVAL; + } + bdata->irq = button->irq; + + if (button->type && button->type != EV_KEY) { + dev_err(dev, "Only EV_KEY allowed for IRQ buttons.\n"); + return -EINVAL; + } + + bdata->timer_debounce = button->debounce_interval; + setup_timer(&bdata->timer, + gpio_keys_irq_timer, (unsigned long)bdata); + + isr = gpio_keys_irq_isr; + irqflags = 0; + } + + input_set_capability(input, button->type ?: EV_KEY, button->code); + + /* + * If platform has specified that the button can be disabled, + * we don't want it to share the interrupt line. + */ + if (!button->can_disable) + irqflags |= IRQF_SHARED; + + error = request_any_context_irq(bdata->irq, isr, irqflags, desc, bdata); + if (error < 0) { + dev_err(dev, "Unable to claim irq %d; error %d\n", + bdata->irq, error); + goto fail; + } + + return 0; + +fail: + if (gpio_is_valid(button->gpio)) + gpio_free(button->gpio); + + return error; +} + +static int gpio_keys_open(struct input_dev *input) +{ + struct gpio_keys_drvdata *ddata = input_get_drvdata(input); + + return ddata->enable ? ddata->enable(input->dev.parent) : 0; +} + +static void gpio_keys_close(struct input_dev *input) +{ + struct gpio_keys_drvdata *ddata = input_get_drvdata(input); + + if (ddata->disable) + ddata->disable(input->dev.parent); +} + +/* + * Handlers for alternative sources of platform_data + */ +#ifdef CONFIG_OF +/* + * Translate OpenFirmware node properties into platform_data + */ +static int gpio_keys_get_devtree_pdata(struct device *dev, + struct akgpio_keys_platform_data *pdata) +{ + struct device_node *node, *pp; + int i; + struct gpio_keys_button *buttons; + u32 reg; + + node = dev->of_node; + if (node == NULL) + return -ENODEV; + + memset(pdata, 0, sizeof *pdata); + + pdata->rep = !!of_get_property(node, "autorepeat", NULL); + + /* First count the subnodes */ + pdata->nbuttons = 0; + pp = NULL; + while ((pp = of_get_next_child(node, pp))) + pdata->nbuttons++; + + if (pdata->nbuttons == 0) + return -ENODEV; + + buttons = kzalloc(pdata->nbuttons * (sizeof *buttons), GFP_KERNEL); + if (!buttons) + return -ENOMEM; + + pp = NULL; + i = 0; + while ((pp = of_get_next_child(node, pp))) { + enum of_gpio_flags flags; + + if (!of_find_property(pp, "gpios", NULL)) { + pdata->nbuttons--; + dev_warn(dev, "Found button without gpios\n"); + continue; + } + buttons[i].gpio = of_get_gpio_flags(pp, 0, &flags); + buttons[i].active_low = flags & OF_GPIO_ACTIVE_LOW; + + if (of_property_read_u32(pp, "linux,code", ®)) { + dev_err(dev, "Button without keycode: 0x%x\n", buttons[i].gpio); + goto out_fail; + } + buttons[i].code = reg; + + buttons[i].desc = of_get_property(pp, "label", NULL); + + if (of_property_read_u32(pp, "linux,input-type", ®) == 0) + buttons[i].type = reg; + else + buttons[i].type = EV_KEY; + + buttons[i].wakeup = !!of_get_property(pp, "gpio-key,wakeup", NULL); + + if (of_property_read_u32(pp, "debounce-interval", ®) == 0) + buttons[i].debounce_interval = reg; + else + buttons[i].debounce_interval = 5; + + i++; + } + + pdata->buttons = buttons; + + return 0; + +out_fail: + kfree(buttons); + return -ENODEV; +} + +static struct of_device_id gpio_keys_of_match[] = { + { .compatible = "gpio-keys", }, + { }, +}; +MODULE_DEVICE_TABLE(of, gpio_keys_of_match); + +#else + +static int gpio_keys_get_devtree_pdata(struct device *dev, + struct akgpio_keys_platform_data *altp) +{ + return -ENODEV; +} + +#define gpio_keys_of_match NULL + +#endif + + +/** +* @Copyright (C) Anyka 2012 +* @brief gpio_remove_key +* @date 2013-5-14 +* @param[out] void +* @param[in] *bdata +* @return none +*/ +static void gpio_remove_key(struct gpio_button_data *bdata) +{ + free_irq(bdata->irq, bdata); + if (bdata->timer_debounce) + del_timer_sync(&bdata->timer); + cancel_work_sync(&bdata->work); + if (gpio_is_valid(bdata->button->gpio)) + gpio_free(bdata->button->gpio); +} + +/** +* @Copyright (C) Anyka 2012 +* @brief gpio_keys_probe +* @date 2013-5-14 +* @param[out] void +* @param[in] *pdev +* @return fail or not +*/ +static int __devinit gpio_keys_probe(struct platform_device *pdev) +{ + const struct akgpio_keys_platform_data *pdata = pdev->dev.platform_data; + struct gpio_keys_drvdata *ddata; + struct device *dev = &pdev->dev; + struct akgpio_keys_platform_data alt_pdata; + struct input_dev *input; + int i, error; + int wakeup = 0; + + if (!pdata) { + error = gpio_keys_get_devtree_pdata(dev, &alt_pdata); + if (error) + return error; + pdata = &alt_pdata; + } + + ddata = kzalloc(sizeof(struct gpio_keys_drvdata) + + pdata->nbuttons * sizeof(struct gpio_button_data), + GFP_KERNEL); + input = input_allocate_device(); + if (!ddata || !input) { + dev_err(dev, "failed to allocate state\n"); + error = -ENOMEM; + goto fail1; + } + + ddata->input = input; + ddata->n_buttons = pdata->nbuttons; + ddata->enable = pdata->enable; + ddata->disable = pdata->disable; + mutex_init(&ddata->disable_lock); + + platform_set_drvdata(pdev, ddata); + input_set_drvdata(input, ddata); + + input->name = pdata->name ? : pdev->name; + input->phys = "gpio-keys/input0"; + input->dev.parent = &pdev->dev; + input->open = gpio_keys_open; + input->close = gpio_keys_close; + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; + + /* Enable auto repeat feature of Linux input subsystem */ + if (pdata->rep) + __set_bit(EV_REP, input->evbit); + + for (i = 0; i < pdata->nbuttons; i++) { + const struct gpio_keys_button *button = &pdata->buttons[i]; + struct gpio_button_data *bdata = &ddata->data[i]; + + error = gpio_keys_setup_key(pdev, input, bdata, button); + if (error) + goto fail2; + + if (button->wakeup) + wakeup = 1; + } + + error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group); + if (error) { + dev_err(dev, "Unable to export keys/switches, error: %d\n", + error); + goto fail2; + } + + error = input_register_device(input); + if (error) { + dev_err(dev, "Unable to register input device, error: %d\n", + error); + goto fail3; + } + + /* get current state of buttons that are connected to GPIOs */ + for (i = 0; i < pdata->nbuttons; i++) { + struct gpio_button_data *bdata = &ddata->data[i]; + if (gpio_is_valid(bdata->button->gpio)) + gpio_keys_gpio_report_event(bdata); + } + input_sync(input); + + device_init_wakeup(&pdev->dev, wakeup); + + return 0; + + fail3: + sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); + fail2: + while (--i >= 0) + gpio_remove_key(&ddata->data[i]); + + platform_set_drvdata(pdev, NULL); + fail1: + input_free_device(input); + kfree(ddata); + /* If we have no platform_data, we allocated buttons dynamically. */ + if (!pdev->dev.platform_data) + kfree(pdata->buttons); + + return error; +} + +/** +* @Copyright (C) Anyka 2012 +* @brief gpio keys remove +* @date 2013-5-14 +* @param[out] void +* @param[in] *pdev +* @return fail or not +*/ +static int __devexit gpio_keys_remove(struct platform_device *pdev) +{ + struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); + struct input_dev *input = ddata->input; + int i; + + sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); + + device_init_wakeup(&pdev->dev, 0); + + for (i = 0; i < ddata->n_buttons; i++) + gpio_remove_key(&ddata->data[i]); + + input_unregister_device(input); + + /* + * If we had no platform_data, we allocated buttons dynamically, and + * must free them here. ddata->data[0].button is the pointer to the + * beginning of the allocated array. + */ + if (!pdev->dev.platform_data) + kfree(ddata->data[0].button); + + kfree(ddata); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +/** +* @Copyright (C) Anyka 2012 +* @brief gpio_keys_suspend +* @date 2013-5-14 +* @param[out] void +* @param[in] *dev +* @return 0 +*/ +static int gpio_keys_suspend(struct device *dev) +{ + struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); + int i; + + if (device_may_wakeup(dev)) { + for (i = 0; i < ddata->n_buttons; i++) { + struct gpio_button_data *bdata = &ddata->data[i]; + if (bdata->button->wakeup) + enable_irq_wake(bdata->irq); + } + } + + return 0; +} + +/** +* @Copyright (C) Anyka 2012 +* @brief gpio_keys_resume +* @date 2013-5-14 +* @param[out] void +* @param[in] *dev +* @return 0 +*/ +static int gpio_keys_resume(struct device *dev) +{ + struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); + int i; + + for (i = 0; i < ddata->n_buttons; i++) { + struct gpio_button_data *bdata = &ddata->data[i]; + if (bdata->button->wakeup && device_may_wakeup(dev)) + disable_irq_wake(bdata->irq); + + if (gpio_is_valid(bdata->button->gpio)) + gpio_keys_gpio_report_event(bdata); + } + input_sync(ddata->input); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume); + +static struct platform_driver gpio_keys_device_driver = { + .probe = gpio_keys_probe, + .remove = __devexit_p(gpio_keys_remove), + .driver = { + .name = "akgpio-keys", + .owner = THIS_MODULE, + .pm = &gpio_keys_pm_ops, + .of_match_table = gpio_keys_of_match, + } +}; + +static int __init gpio_keys_init(void) +{ + return platform_driver_register(&gpio_keys_device_driver); +} + +static void __exit gpio_keys_exit(void) +{ + platform_driver_unregister(&gpio_keys_device_driver); +} + +module_init(gpio_keys_init); +module_exit(gpio_keys_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Phil Blundell "); +MODULE_DESCRIPTION("Keyboard driver for GPIOs"); +MODULE_ALIAS("platform:gpio-keys"); diff --git a/drivers/input/keyboard/akmatrix_keypad.c b/drivers/input/keyboard/akmatrix_keypad.c new file mode 100644 index 00000000..2978f8d7 --- /dev/null +++ b/drivers/input/keyboard/akmatrix_keypad.c @@ -0,0 +1,708 @@ +/** +* @file ak_matrix_keypad.c +* @brief GPIO driven matrix keyboard driver +* Copyright C 2011 Anyka CO.,LTD +* Based on matrix_keypad.c +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* @author zhou wenyong +* @date 2011-08-05 +* @note 2010-12-24 created by cao_lianming +* @note 2011-06-25 integrate and optimize matrix keypad driver -- +* only one copy code exists now. we use the same code whether +* or not keypad is connected aw9523, whether or not there is a +* grounding line. +* @note 2011-06-27 fix a bug imported from previous version, which is that +* driver can not work properly if there is grounding line in 3X3 +* or 4X4 matrix keypad +* @note 2011-07-08 fix the issue of incorrect responsing to long press +* of menu key +* +* Notes: if the comments look like inaesthetic, try to press +* Alt+F12(source insight) +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + structure used to present the keypad +*/ +struct matrix_keypad { + struct matrix_keypad_platform_data *pdata; + struct input_dev *input_dev; + unsigned short *keycodes; + unsigned int row_shift; + + uint32_t last_key_state[MATRIX_MAX_COLS]; + struct delayed_work work; + bool scan_pending; + bool stopped; + spinlock_t lock; + bool start_close_int; +}; + +/** +* @brief get the value of gpio (this function may sleep) +* @author zhou wenyong +* @date 2011-08-05 +* @param[in] gpio +* @return int +*/ +static inline int ak_gpio_get_value_cansleep(unsigned gpio) +{ + might_sleep(); + return ak_gpio_getpin(gpio); +} +/** +* @brief set the value of gpio (this function may sleep) +* @author zhou wenyong +* @date 2011-08-05 +* @param[in] gpio +* @param[in] value +* @return void +*/ +static inline void ak_gpio_set_value_cansleep(unsigned gpio, int value) +{ + might_sleep(); + ak_gpio_setpin(gpio, value); +} + +/** +* @brief called by activate_other_col (refer to it) +* @author zhou wenyong +* @date 2011-08-05 +* @param[in] *pdata +* @param[in] col +* @param[in] on +* @return void +*/ +static void __activate_col(const struct matrix_keypad_platform_data *pdata, + int col, bool on) +{ + bool level_on = pdata->active_low; + int i; + + for (i=0; inum_col_gpios;i++) + { + if (i == col) + continue; + if (on) + { + ak_gpio_setpin(pdata->col_gpios[i], level_on); + } + else + { + ak_gpio_setpin(pdata->col_gpios[i], !level_on); + } + } +} + +/** +* @brief activate other colums +* @author zhou wenyong +* @date 2011-08-05 +* @param[in] *pdata +* @param[in] col -- the column excluded +* @param[in] on +* @return void +*/ +static void activate_other_col(const struct matrix_keypad_platform_data *pdata, + int col, bool on) +{ + __activate_col(pdata, col, on); + + if (on && pdata->col_scan_delay_us) + udelay(pdata->col_scan_delay_us); +} + +/** +* @brief get the value of gpio (can sleep) +* @author zhou wenyong +* @date 2011-08-05 +* @param[in] *pdata +* @param[in] row +* @return bool +*/ +static bool row_asserted(const struct matrix_keypad_platform_data *pdata, + int row) +{ + return ak_gpio_get_value_cansleep(pdata->row_gpios[row]) ? + !pdata->active_low : pdata->active_low; +} + +/** +* @brief enable_row_irqs +* @author zhou wenyong +* @date 2011-08-05 +* @param[in] *keypad +* @return void +*/ +static void enable_row_irqs(struct matrix_keypad *keypad) +{ + const struct matrix_keypad_platform_data *pdata = keypad->pdata; + int i; + + + for (i = 0; i < pdata->num_row_gpios; i++) + enable_irq(ak_gpio_to_irq(pdata->row_gpios[i])); +} + +/** +* @brief disable_row_irqs +* @author zhou wenyong +* @date 2011-08-05 +* @param[in] *keypad +* @return void +*/ +static void disable_row_irqs(struct matrix_keypad *keypad) +{ + const struct matrix_keypad_platform_data *pdata = keypad->pdata; + int i; + + + for (i = 0; i < pdata->num_row_gpios; i++) + disable_irq_nosync(ak_gpio_to_irq(pdata->row_gpios[i])); +} + +/** +* @brief print the CODE of button pressed -- for debugging +* @author zhou wenyong +* @date 2011-08-05 +* @param[in] code +* @param[in] state +* @return void +*/ +static void print_code(int code, int state) +{ + switch(code) + { + case KEY_VOLUMEUP: + printk("KEY_VOLUMEUP "); break; + case KEY_RIGHT: + printk("KEY_RIGHT "); break; + case KEY_MENU: + printk("KEY_MENU "); break; + case KEY_UP: + printk("KEY_UP "); break; + case KEY_REPLY: + printk("KEY_CENTER "); break; + case KEY_DOWN: + printk("KEY_DOWN "); break; + case KEY_VOLUMEDOWN: + printk("KEY_VOLUMEDOWN "); break; + case KEY_LEFT: + printk("KEY_LEFT "); break; + case KEY_HOME: + printk("KEY_HOME "); break; + case KEY_BACK: + printk("KEY_BACK "); break; + } + printk("%s \n", state?"Down":"Up"); + + +} + +EXPORT_SYMBOL(print_code); + +/** +* @brief This gets the keys from keyboard and reports it to input +* subsystem +* @author zhou wenyong +* @date 2011-08-05 +* @param[out] *work +* @return void +*/ +static void matrix_keypad_scan(struct work_struct *work) +{ + struct matrix_keypad *keypad = + container_of(work, struct matrix_keypad, work.work); + struct input_dev *input_dev = keypad->input_dev; + const struct matrix_keypad_platform_data *pdata = keypad->pdata; + uint32_t new_state[MATRIX_MAX_COLS]; + int row, col, code, num_cols; + + memset(new_state, 0, sizeof(new_state)); + + + num_cols = pdata->num_col_gpios; + if (pdata->grounding) + num_cols++; + for (col = 0; col < num_cols; col++) + { + activate_other_col(pdata, col, true); + for (row = 0; row < pdata->num_row_gpios; row++) + { + new_state[col] |= + row_asserted(pdata, row) ? (1 << row) : 0; + + } + + activate_other_col(pdata, col, false); + } + + /* + * if the button pressed is connected to the grounding line, the state of + * the row input line connected to the button pressed will keep low level + * when we activate other lines + */ + if (pdata->grounding) + { + for (row=0; row < pdata->num_row_gpios; row++) + { + for (col=0; colrow_gpios[0]); + + num_cols = pdata->num_col_gpios; + if (pdata->grounding) + num_cols++; + for (col = 0; col < num_cols; col++) { + uint32_t bits_changed; + + bits_changed = (keypad->last_key_state[col] ^ new_state[col]); + + + if (bits_changed == 0) + continue; + + for (row = 0; row < pdata->num_row_gpios; row++) { + if ((bits_changed & (1 << row)) == 0) + continue; + + code = MATRIX_SCAN_CODE(row, col, keypad->row_shift); + input_event(input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(input_dev, + keypad->keycodes[code], + new_state[col] & (1 << row)); + //print_code(keypad->keycodes[code], new_state[col] & (1 << row)); + } + } + input_sync(input_dev); + + memcpy(keypad->last_key_state, new_state, sizeof(new_state)); + + + /* Enable IRQs again */ + spin_lock_irq(&keypad->lock); + keypad->scan_pending = false; + enable_row_irqs(keypad); + spin_unlock_irq(&keypad->lock); +} + +/** +* @brief matrix keypad interrupt routine +* @author zhou wenyong +* @date 2011-08-05 +* @param[in] irq +* @param[out] *id +* @return irqreturn_t +*/ +static irqreturn_t matrix_keypad_interrupt(int irq, void *id) +{ + struct matrix_keypad *keypad = id; + unsigned long flags; + + + spin_lock_irqsave(&keypad->lock, flags); + + /* + * See if another IRQ beaten us to it and scheduled the + * scan already. In that case we should not try to + * disable IRQs again. + */ + if (unlikely(keypad->scan_pending || keypad->stopped)) + goto out; + + disable_row_irqs(keypad); + keypad->scan_pending = true; + schedule_delayed_work(&keypad->work, + msecs_to_jiffies(keypad->pdata->debounce_ms)); + +out: + /* + * if disable_row_irqs(keypad); in init_matrix_gpio is not reached, then + * we need excuting it here. otherwise system can not start successfully if + * one key is pressed on OS starts. + */ + if (!keypad->start_close_int) + { + disable_row_irqs(keypad); + keypad->start_close_int = true; + } + spin_unlock_irqrestore(&keypad->lock, flags); + return IRQ_HANDLED; +} + +/** +* @brief start the keypad +* @author zhou wenyong +* @date 2011-08-05 +* @param[in] *dev +* @return int +*/ +static int matrix_keypad_start(struct input_dev *dev) +{ + struct matrix_keypad *keypad = input_get_drvdata(dev); + + keypad->stopped = false; + mb(); + + /* + * Schedule an immediate key scan to capture current key state; + * columns will be activated and IRQs be enabled after the scan. + */ + schedule_delayed_work(&keypad->work, 0); + + return 0; +} + +/** +* @brief stop the keypad +* @author zhou wenyong +* @date 2011-08-05 +* @param[out] *dev +* @return void +*/ +static void matrix_keypad_stop(struct input_dev *dev) +{ + struct matrix_keypad *keypad = input_get_drvdata(dev); + + keypad->stopped = true; + mb(); + flush_work(&keypad->work.work); + /* + * matrix_keypad_scan() will leave IRQs enabled; + * we should disable them now. + */ + disable_row_irqs(keypad); +} + +#ifdef CONFIG_PM +/** +* @brief matrix_keypad_suspend +* @author zhou wenyong +* @date 2011-08-05 +* @param[in] *pdev +* @param[in] state +* @return int +*/ +static int matrix_keypad_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct matrix_keypad *keypad = platform_get_drvdata(pdev); + const struct matrix_keypad_platform_data *pdata = keypad->pdata; + int i; + + matrix_keypad_stop(keypad->input_dev); + + if (device_may_wakeup(&pdev->dev)) + for (i = 0; i < pdata->num_row_gpios; i++) + enable_irq_wake(ak_gpio_to_irq(pdata->row_gpios[i])); + + return 0; +} + +/** +* @brief matrix_keypad_resume +* @author zhou wenyong +* @date 2011-08-05 +* @param[in] *pdev +* @return int +*/ +static int matrix_keypad_resume(struct platform_device *pdev) +{ + struct matrix_keypad *keypad = platform_get_drvdata(pdev); + const struct matrix_keypad_platform_data *pdata = keypad->pdata; + int i; + + if (device_may_wakeup(&pdev->dev)) + for (i = 0; i < pdata->num_row_gpios; i++) + disable_irq_wake(ak_gpio_to_irq(pdata->row_gpios[i])); + + matrix_keypad_start(keypad->input_dev); + + return 0; +} +#else +#define matrix_keypad_suspend NULL +#define matrix_keypad_resume NULL +#endif + +/** +* @brief init_matrix_gpio +* @author zhou wenyong +* @date 2011-08-05 +* @param[in] *pdev +* @param[in] *keypad +* @return int __devinit +*/ +static int __devinit init_matrix_gpio(struct platform_device *pdev, + struct matrix_keypad *keypad) +{ + struct matrix_keypad_platform_data *pdata = keypad->pdata; + int i, err = -(EINVAL); + unsigned long flags; + + /* to fix the bug: can not start OS if one key is pressed on OS starts */ + keypad->start_close_int = false; + /* initialized strobe lines as outputs, activated */ + for (i = 0; i < pdata->num_col_gpios; i++) { + + err = ak_gpio_request(pdata->col_gpios[i], "matrix_kbd_col"); + if (err) { + dev_err(&pdev->dev, + "failed to request GPIO%d for COL%d\n", + pdata->col_gpios[i], i); + goto err_free_cols; + } + pdata->col_gpios_cfginfo.pin = pdata->col_gpios[i]; + ak_gpio_set(&(pdata->col_gpios_cfginfo)); + + } + + for (i = 0; i < pdata->num_row_gpios; i++) { + + err = ak_gpio_request(pdata->row_gpios[i], "matrix_kbd_row"); + if (err) { + dev_err(&pdev->dev, + "failed to request GPIO%d for ROW%d\n", + pdata->row_gpios[i], i); + goto err_free_rows; + } + pdata->row_gpios_cfginfo.pin = pdata->row_gpios[i]; + ak_gpio_set(&(pdata->row_gpios_cfginfo)); + ak_gpio_intcfg(pdata->row_gpios[i], AK_GPIO_INT_ENABLE); + } + + + for (i = 0; i < pdata->num_row_gpios; i++) { + err = request_irq(ak_gpio_to_irq(pdata->row_gpios[i]), + matrix_keypad_interrupt, + IRQF_DISABLED, + "matrix-keypad", keypad); + if (err) { + dev_err(&pdev->dev, + "Unable to acquire interrupt for GPIO line %i\n", + pdata->row_gpios[i]); + goto err_free_irqs; + } + } + + /* update input status, needed if keypad is connected to AW9523 */ + ak_gpio_getpin(pdata->row_gpios[0]); + + spin_lock_irqsave(&keypad->lock, flags); + /* initialized as disabled - enabled by input->open */ + if (!keypad->start_close_int) + { + disable_row_irqs(keypad); + keypad->start_close_int = true; + } + spin_unlock_irqrestore(&keypad->lock, flags); + return 0; + +err_free_irqs: + while (--i >= 0) + free_irq(ak_gpio_to_irq(pdata->row_gpios[i]), keypad); + i = pdata->num_row_gpios; +err_free_rows: + while (--i >= 0) + ak_gpio_free(pdata->row_gpios[i]); + i = pdata->num_col_gpios; +err_free_cols: + while (--i >= 0) + ak_gpio_free(pdata->col_gpios[i]); + + return err; +} + +/** +* @brief matrix_keypad_probe +* @author zhou wenyong +* @date 2011-08-05 +* @param[in] *pdev +* @return int __devinit +*/ +static int __devinit matrix_keypad_probe(struct platform_device *pdev) +{ + struct matrix_keypad_platform_data *pdata; + const struct matrix_keymap_data *keymap_data; + struct matrix_keypad *keypad; + struct input_dev *input_dev; + unsigned short *keycodes; + unsigned int row_shift; + int err; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "no platform data defined\n"); + return -EINVAL; + } + + keymap_data = pdata->keymap_data; + if (!keymap_data) { + dev_err(&pdev->dev, "no keymap data defined\n"); + return -EINVAL; + } + + row_shift = get_count_order(pdata->num_col_gpios); + if (pdata->grounding) + row_shift++; + + keypad = kzalloc(sizeof(struct matrix_keypad), GFP_KERNEL); + keycodes = kzalloc((pdata->num_row_gpios << (row_shift)) * + sizeof(*keycodes), + GFP_KERNEL); + input_dev = input_allocate_device(); + if (!keypad || !keycodes || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + keypad->input_dev = input_dev; + keypad->pdata = pdata; + keypad->keycodes = keycodes; + + keypad->row_shift = row_shift; + keypad->stopped = true; + INIT_DELAYED_WORK(&keypad->work, matrix_keypad_scan); + spin_lock_init(&keypad->lock); + + input_dev->name = pdev->name; + input_dev->id.bustype = BUS_HOST; + input_dev->dev.parent = &pdev->dev; + input_dev->evbit[0] = BIT_MASK(EV_KEY);// | BIT_MASK(EV_REP); + input_dev->open = matrix_keypad_start; + input_dev->close = matrix_keypad_stop; + + input_dev->keycode = keycodes; + input_dev->keycodesize = sizeof(*keycodes); + input_dev->keycodemax = pdata->num_row_gpios << (row_shift); + + matrix_keypad_build_keymap(keymap_data, row_shift, + input_dev->keycode, input_dev->keybit); + + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + input_set_drvdata(input_dev, keypad); + + err = init_matrix_gpio(pdev, keypad); + if (err) + goto err_free_mem; + + err = input_register_device(keypad->input_dev); + if (err) + goto err_free_mem; + + device_init_wakeup(&pdev->dev, pdata->wakeup); + platform_set_drvdata(pdev, keypad); + + return 0; + +err_free_mem: + input_free_device(input_dev); + kfree(keycodes); + kfree(keypad); + return err; +} + +/** +* @brief called when the device is removed +* @author zhou wenyong +* @date 2011-08-05 +* @param[in] *pdev +* @return int __devexit +*/ +static int __devexit matrix_keypad_remove(struct platform_device *pdev) +{ + struct matrix_keypad *keypad = platform_get_drvdata(pdev); + const struct matrix_keypad_platform_data *pdata = keypad->pdata; + int i; + + device_init_wakeup(&pdev->dev, 0); + + for (i = 0; i < pdata->num_row_gpios; i++) { + free_irq(ak_gpio_to_irq(pdata->row_gpios[i]), keypad); + ak_gpio_free(pdata->row_gpios[i]); + } + + for (i = 0; i < pdata->num_col_gpios; i++) + ak_gpio_free(pdata->col_gpios[i]); + + input_unregister_device(keypad->input_dev); + platform_set_drvdata(pdev, NULL); + kfree(keypad->keycodes); + kfree(keypad); + + return 0; +} + +static struct platform_driver matrix_keypad_driver = { + .probe = matrix_keypad_probe, + .remove = __devexit_p(matrix_keypad_remove), + .suspend = matrix_keypad_suspend, + .resume = matrix_keypad_resume, + .driver = { + .name = "matrix-keypad", + .owner = THIS_MODULE, + }, +}; + +/** +* @brief matrix_keypad_init +* @author zhou wenyong +* @date 2011-08-05 +* @param[in] void +* @return int __init +*/ +static int __init matrix_keypad_init(void) +{ + return platform_driver_register(&matrix_keypad_driver); +} + +/** +* @brief matrix_keypad_exit +* @author zhou wenyong +* @date 2011-08-05 +* @param[in] void +* @return void __exit +*/ +static void __exit matrix_keypad_exit(void) +{ + platform_driver_unregister(&matrix_keypad_driver); +} + +module_init(matrix_keypad_init); +module_exit(matrix_keypad_exit); + +MODULE_AUTHOR("Anyka "); +MODULE_DESCRIPTION("GPIO Driven Matrix Keypad Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:matrix-keypad"); + diff --git a/drivers/input/keyreset.c b/drivers/input/keyreset.c new file mode 100644 index 00000000..36208fe0 --- /dev/null +++ b/drivers/input/keyreset.c @@ -0,0 +1,239 @@ +/* drivers/input/keyreset.c + * + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +struct keyreset_state { + struct input_handler input_handler; + unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; + unsigned long upbit[BITS_TO_LONGS(KEY_CNT)]; + unsigned long key[BITS_TO_LONGS(KEY_CNT)]; + spinlock_t lock; + int key_down_target; + int key_down; + int key_up; + int restart_disabled; + int (*reset_fn)(void); +}; + +int restart_requested; +static void deferred_restart(struct work_struct *dummy) +{ + restart_requested = 2; + sys_sync(); + restart_requested = 3; + kernel_restart(NULL); +} +static DECLARE_WORK(restart_work, deferred_restart); + +static void keyreset_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ + unsigned long flags; + struct keyreset_state *state = handle->private; + + if (type != EV_KEY) + return; + + if (code >= KEY_MAX) + return; + + if (!test_bit(code, state->keybit)) + return; + + spin_lock_irqsave(&state->lock, flags); + if (!test_bit(code, state->key) == !value) + goto done; + __change_bit(code, state->key); + if (test_bit(code, state->upbit)) { + if (value) { + state->restart_disabled = 1; + state->key_up++; + } else + state->key_up--; + } else { + if (value) + state->key_down++; + else + state->key_down--; + } + if (state->key_down == 0 && state->key_up == 0) + state->restart_disabled = 0; + + pr_debug("reset key changed %d %d new state %d-%d-%d\n", code, value, + state->key_down, state->key_up, state->restart_disabled); + + if (value && !state->restart_disabled && + state->key_down == state->key_down_target) { + state->restart_disabled = 1; + if (restart_requested) + panic("keyboard reset failed, %d", restart_requested); + if (state->reset_fn) { + restart_requested = state->reset_fn(); + } else { + pr_info("keyboard reset\n"); + schedule_work(&restart_work); + restart_requested = 1; + } + } +done: + spin_unlock_irqrestore(&state->lock, flags); +} + +static int keyreset_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + int i; + int ret; + struct input_handle *handle; + struct keyreset_state *state = + container_of(handler, struct keyreset_state, input_handler); + + for (i = 0; i < KEY_MAX; i++) { + if (test_bit(i, state->keybit) && test_bit(i, dev->keybit)) + break; + } + if (i == KEY_MAX) + return -ENODEV; + + handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "keyreset"; + handle->private = state; + + ret = input_register_handle(handle); + if (ret) + goto err_input_register_handle; + + ret = input_open_device(handle); + if (ret) + goto err_input_open_device; + + pr_info("using input dev %s for key reset\n", dev->name); + + return 0; + +err_input_open_device: + input_unregister_handle(handle); +err_input_register_handle: + kfree(handle); + return ret; +} + +static void keyreset_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static const struct input_device_id keyreset_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_KEY) }, + }, + { }, +}; +MODULE_DEVICE_TABLE(input, keyreset_ids); + +static int keyreset_probe(struct platform_device *pdev) +{ + int ret; + int key, *keyp; + struct keyreset_state *state; + struct keyreset_platform_data *pdata = pdev->dev.platform_data; + + if (!pdata) + return -EINVAL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + spin_lock_init(&state->lock); + keyp = pdata->keys_down; + while ((key = *keyp++)) { + if (key >= KEY_MAX) + continue; + state->key_down_target++; + __set_bit(key, state->keybit); + } + if (pdata->keys_up) { + keyp = pdata->keys_up; + while ((key = *keyp++)) { + if (key >= KEY_MAX) + continue; + __set_bit(key, state->keybit); + __set_bit(key, state->upbit); + } + } + + if (pdata->reset_fn) + state->reset_fn = pdata->reset_fn; + + state->input_handler.event = keyreset_event; + state->input_handler.connect = keyreset_connect; + state->input_handler.disconnect = keyreset_disconnect; + state->input_handler.name = KEYRESET_NAME; + state->input_handler.id_table = keyreset_ids; + ret = input_register_handler(&state->input_handler); + if (ret) { + kfree(state); + return ret; + } + platform_set_drvdata(pdev, state); + return 0; +} + +int keyreset_remove(struct platform_device *pdev) +{ + struct keyreset_state *state = platform_get_drvdata(pdev); + input_unregister_handler(&state->input_handler); + kfree(state); + return 0; +} + + +struct platform_driver keyreset_driver = { + .driver.name = KEYRESET_NAME, + .probe = keyreset_probe, + .remove = keyreset_remove, +}; + +static int __init keyreset_init(void) +{ + return platform_driver_register(&keyreset_driver); +} + +static void __exit keyreset_exit(void) +{ + return platform_driver_unregister(&keyreset_driver); +} + +module_init(keyreset_init); +module_exit(keyreset_exit); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 7faf4a7f..66550c2b 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -279,6 +279,17 @@ config INPUT_ATI_REMOTE2 To compile this driver as a module, choose M here: the module will be called ati_remote2. +config INPUT_KEYCHORD + tristate "Key chord input driver support" + help + Say Y here if you want to enable the key chord driver + accessible at /dev/keychord. This driver can be used + for receiving notifications when client specified key + combinations are pressed. + + To compile this driver as a module, choose M here: the + module will be called keychord. + config INPUT_KEYSPAN_REMOTE tristate "Keyspan DMR USB remote control (EXPERIMENTAL)" depends on EXPERIMENTAL @@ -407,6 +418,11 @@ config INPUT_SGI_BTNS To compile this driver as a module, choose M here: the module will be called sgi_btns. +config INPUT_GPIO + tristate "GPIO driver support" + help + Say Y here if you want to support gpio based keys, wheels etc... + config HP_SDC_RTC tristate "HP SDC Real Time Clock" depends on (GSC || HP300) && SERIO diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index f55cdf49..f8cb5221 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -25,8 +25,10 @@ obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o +obj-$(CONFIG_INPUT_GPIO) += gpio_event.o gpio_matrix.o gpio_input.o gpio_output.o gpio_axis.o obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o +obj-$(CONFIG_INPUT_KEYCHORD) += keychord.o obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o diff --git a/drivers/input/misc/gpio_axis.c b/drivers/input/misc/gpio_axis.c new file mode 100644 index 00000000..0acf4a57 --- /dev/null +++ b/drivers/input/misc/gpio_axis.c @@ -0,0 +1,192 @@ +/* drivers/input/misc/gpio_axis.c + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +struct gpio_axis_state { + struct gpio_event_input_devs *input_devs; + struct gpio_event_axis_info *info; + uint32_t pos; +}; + +uint16_t gpio_axis_4bit_gray_map_table[] = { + [0x0] = 0x0, [0x1] = 0x1, /* 0000 0001 */ + [0x3] = 0x2, [0x2] = 0x3, /* 0011 0010 */ + [0x6] = 0x4, [0x7] = 0x5, /* 0110 0111 */ + [0x5] = 0x6, [0x4] = 0x7, /* 0101 0100 */ + [0xc] = 0x8, [0xd] = 0x9, /* 1100 1101 */ + [0xf] = 0xa, [0xe] = 0xb, /* 1111 1110 */ + [0xa] = 0xc, [0xb] = 0xd, /* 1010 1011 */ + [0x9] = 0xe, [0x8] = 0xf, /* 1001 1000 */ +}; +uint16_t gpio_axis_4bit_gray_map(struct gpio_event_axis_info *info, uint16_t in) +{ + return gpio_axis_4bit_gray_map_table[in]; +} + +uint16_t gpio_axis_5bit_singletrack_map_table[] = { + [0x10] = 0x00, [0x14] = 0x01, [0x1c] = 0x02, /* 10000 10100 11100 */ + [0x1e] = 0x03, [0x1a] = 0x04, [0x18] = 0x05, /* 11110 11010 11000 */ + [0x08] = 0x06, [0x0a] = 0x07, [0x0e] = 0x08, /* 01000 01010 01110 */ + [0x0f] = 0x09, [0x0d] = 0x0a, [0x0c] = 0x0b, /* 01111 01101 01100 */ + [0x04] = 0x0c, [0x05] = 0x0d, [0x07] = 0x0e, /* 00100 00101 00111 */ + [0x17] = 0x0f, [0x16] = 0x10, [0x06] = 0x11, /* 10111 10110 00110 */ + [0x02] = 0x12, [0x12] = 0x13, [0x13] = 0x14, /* 00010 10010 10011 */ + [0x1b] = 0x15, [0x0b] = 0x16, [0x03] = 0x17, /* 11011 01011 00011 */ + [0x01] = 0x18, [0x09] = 0x19, [0x19] = 0x1a, /* 00001 01001 11001 */ + [0x1d] = 0x1b, [0x15] = 0x1c, [0x11] = 0x1d, /* 11101 10101 10001 */ +}; +uint16_t gpio_axis_5bit_singletrack_map( + struct gpio_event_axis_info *info, uint16_t in) +{ + return gpio_axis_5bit_singletrack_map_table[in]; +} + +static void gpio_event_update_axis(struct gpio_axis_state *as, int report) +{ + struct gpio_event_axis_info *ai = as->info; + int i; + int change; + uint16_t state = 0; + uint16_t pos; + uint16_t old_pos = as->pos; + for (i = ai->count - 1; i >= 0; i--) + state = (state << 1) | gpio_get_value(ai->gpio[i]); + pos = ai->map(ai, state); + if (ai->flags & GPIOEAF_PRINT_RAW) + pr_info("axis %d-%d raw %x, pos %d -> %d\n", + ai->type, ai->code, state, old_pos, pos); + if (report && pos != old_pos) { + if (ai->type == EV_REL) { + change = (ai->decoded_size + pos - old_pos) % + ai->decoded_size; + if (change > ai->decoded_size / 2) + change -= ai->decoded_size; + if (change == ai->decoded_size / 2) { + if (ai->flags & GPIOEAF_PRINT_EVENT) + pr_info("axis %d-%d unknown direction, " + "pos %d -> %d\n", ai->type, + ai->code, old_pos, pos); + change = 0; /* no closest direction */ + } + if (ai->flags & GPIOEAF_PRINT_EVENT) + pr_info("axis %d-%d change %d\n", + ai->type, ai->code, change); + input_report_rel(as->input_devs->dev[ai->dev], + ai->code, change); + } else { + if (ai->flags & GPIOEAF_PRINT_EVENT) + pr_info("axis %d-%d now %d\n", + ai->type, ai->code, pos); + input_event(as->input_devs->dev[ai->dev], + ai->type, ai->code, pos); + } + input_sync(as->input_devs->dev[ai->dev]); + } + as->pos = pos; +} + +static irqreturn_t gpio_axis_irq_handler(int irq, void *dev_id) +{ + struct gpio_axis_state *as = dev_id; + gpio_event_update_axis(as, 1); + return IRQ_HANDLED; +} + +int gpio_event_axis_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func) +{ + int ret; + int i; + int irq; + struct gpio_event_axis_info *ai; + struct gpio_axis_state *as; + + ai = container_of(info, struct gpio_event_axis_info, info); + if (func == GPIO_EVENT_FUNC_SUSPEND) { + for (i = 0; i < ai->count; i++) + disable_irq(gpio_to_irq(ai->gpio[i])); + return 0; + } + if (func == GPIO_EVENT_FUNC_RESUME) { + for (i = 0; i < ai->count; i++) + enable_irq(gpio_to_irq(ai->gpio[i])); + return 0; + } + + if (func == GPIO_EVENT_FUNC_INIT) { + *data = as = kmalloc(sizeof(*as), GFP_KERNEL); + if (as == NULL) { + ret = -ENOMEM; + goto err_alloc_axis_state_failed; + } + as->input_devs = input_devs; + as->info = ai; + if (ai->dev >= input_devs->count) { + pr_err("gpio_event_axis: bad device index %d >= %d " + "for %d:%d\n", ai->dev, input_devs->count, + ai->type, ai->code); + ret = -EINVAL; + goto err_bad_device_index; + } + + input_set_capability(input_devs->dev[ai->dev], + ai->type, ai->code); + if (ai->type == EV_ABS) { + input_set_abs_params(input_devs->dev[ai->dev], ai->code, + 0, ai->decoded_size - 1, 0, 0); + } + for (i = 0; i < ai->count; i++) { + ret = gpio_request(ai->gpio[i], "gpio_event_axis"); + if (ret < 0) + goto err_request_gpio_failed; + ret = gpio_direction_input(ai->gpio[i]); + if (ret < 0) + goto err_gpio_direction_input_failed; + ret = irq = gpio_to_irq(ai->gpio[i]); + if (ret < 0) + goto err_get_irq_num_failed; + ret = request_irq(irq, gpio_axis_irq_handler, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, + "gpio_event_axis", as); + if (ret < 0) + goto err_request_irq_failed; + } + gpio_event_update_axis(as, 0); + return 0; + } + + ret = 0; + as = *data; + for (i = ai->count - 1; i >= 0; i--) { + free_irq(gpio_to_irq(ai->gpio[i]), as); +err_request_irq_failed: +err_get_irq_num_failed: +err_gpio_direction_input_failed: + gpio_free(ai->gpio[i]); +err_request_gpio_failed: + ; + } +err_bad_device_index: + kfree(as); + *data = NULL; +err_alloc_axis_state_failed: + return ret; +} diff --git a/drivers/input/misc/gpio_event.c b/drivers/input/misc/gpio_event.c new file mode 100644 index 00000000..c7f396ab --- /dev/null +++ b/drivers/input/misc/gpio_event.c @@ -0,0 +1,239 @@ +/* drivers/input/misc/gpio_event.c + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include + +struct gpio_event { + struct gpio_event_input_devs *input_devs; + const struct gpio_event_platform_data *info; + void *state[0]; +}; + +static int gpio_input_event( + struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + int i; + int devnr; + int ret = 0; + int tmp_ret; + struct gpio_event_info **ii; + struct gpio_event *ip = input_get_drvdata(dev); + + for (devnr = 0; devnr < ip->input_devs->count; devnr++) + if (ip->input_devs->dev[devnr] == dev) + break; + if (devnr == ip->input_devs->count) { + pr_err("gpio_input_event: unknown device %p\n", dev); + return -EIO; + } + + for (i = 0, ii = ip->info->info; i < ip->info->info_count; i++, ii++) { + if ((*ii)->event) { + tmp_ret = (*ii)->event(ip->input_devs, *ii, + &ip->state[i], + devnr, type, code, value); + if (tmp_ret) + ret = tmp_ret; + } + } + return ret; +} + +static int gpio_event_call_all_func(struct gpio_event *ip, int func) +{ + int i; + int ret; + struct gpio_event_info **ii; + + if (func == GPIO_EVENT_FUNC_INIT || func == GPIO_EVENT_FUNC_RESUME) { + ii = ip->info->info; + for (i = 0; i < ip->info->info_count; i++, ii++) { + if ((*ii)->func == NULL) { + ret = -ENODEV; + pr_err("gpio_event_probe: Incomplete pdata, " + "no function\n"); + goto err_no_func; + } + if (func == GPIO_EVENT_FUNC_RESUME && (*ii)->no_suspend) + continue; + ret = (*ii)->func(ip->input_devs, *ii, &ip->state[i], + func); + if (ret) { + pr_err("gpio_event_probe: function failed\n"); + goto err_func_failed; + } + } + return 0; + } + + ret = 0; + i = ip->info->info_count; + ii = ip->info->info + i; + while (i > 0) { + i--; + ii--; + if ((func & ~1) == GPIO_EVENT_FUNC_SUSPEND && (*ii)->no_suspend) + continue; + (*ii)->func(ip->input_devs, *ii, &ip->state[i], func & ~1); +err_func_failed: +err_no_func: + ; + } + return ret; +} + +static void __maybe_unused gpio_event_suspend(struct gpio_event *ip) +{ + gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_SUSPEND); + if (ip->info->power) + ip->info->power(ip->info, 0); +} + +static void __maybe_unused gpio_event_resume(struct gpio_event *ip) +{ + if (ip->info->power) + ip->info->power(ip->info, 1); + gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_RESUME); +} + +static int gpio_event_probe(struct platform_device *pdev) +{ + int err; + struct gpio_event *ip; + struct gpio_event_platform_data *event_info; + int dev_count = 1; + int i; + int registered = 0; + + event_info = pdev->dev.platform_data; + if (event_info == NULL) { + pr_err("gpio_event_probe: No pdata\n"); + return -ENODEV; + } + if ((!event_info->name && !event_info->names[0]) || + !event_info->info || !event_info->info_count) { + pr_err("gpio_event_probe: Incomplete pdata\n"); + return -ENODEV; + } + if (!event_info->name) + while (event_info->names[dev_count]) + dev_count++; + ip = kzalloc(sizeof(*ip) + + sizeof(ip->state[0]) * event_info->info_count + + sizeof(*ip->input_devs) + + sizeof(ip->input_devs->dev[0]) * dev_count, GFP_KERNEL); + if (ip == NULL) { + err = -ENOMEM; + pr_err("gpio_event_probe: Failed to allocate private data\n"); + goto err_kp_alloc_failed; + } + ip->input_devs = (void*)&ip->state[event_info->info_count]; + platform_set_drvdata(pdev, ip); + + for (i = 0; i < dev_count; i++) { + struct input_dev *input_dev = input_allocate_device(); + if (input_dev == NULL) { + err = -ENOMEM; + pr_err("gpio_event_probe: " + "Failed to allocate input device\n"); + goto err_input_dev_alloc_failed; + } + input_set_drvdata(input_dev, ip); + input_dev->name = event_info->name ? + event_info->name : event_info->names[i]; + input_dev->event = gpio_input_event; + ip->input_devs->dev[i] = input_dev; + } + ip->input_devs->count = dev_count; + ip->info = event_info; + if (event_info->power) + ip->info->power(ip->info, 1); + + err = gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_INIT); + if (err) + goto err_call_all_func_failed; + + for (i = 0; i < dev_count; i++) { + err = input_register_device(ip->input_devs->dev[i]); + if (err) { + pr_err("gpio_event_probe: Unable to register %s " + "input device\n", ip->input_devs->dev[i]->name); + goto err_input_register_device_failed; + } + registered++; + } + + return 0; + +err_input_register_device_failed: + gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); +err_call_all_func_failed: + if (event_info->power) + ip->info->power(ip->info, 0); + for (i = 0; i < registered; i++) + input_unregister_device(ip->input_devs->dev[i]); + for (i = dev_count - 1; i >= registered; i--) { + input_free_device(ip->input_devs->dev[i]); +err_input_dev_alloc_failed: + ; + } + kfree(ip); +err_kp_alloc_failed: + return err; +} + +static int gpio_event_remove(struct platform_device *pdev) +{ + struct gpio_event *ip = platform_get_drvdata(pdev); + int i; + + gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); + if (ip->info->power) + ip->info->power(ip->info, 0); + for (i = 0; i < ip->input_devs->count; i++) + input_unregister_device(ip->input_devs->dev[i]); + kfree(ip); + return 0; +} + +static struct platform_driver gpio_event_driver = { + .probe = gpio_event_probe, + .remove = gpio_event_remove, + .driver = { + .name = GPIO_EVENT_DEV_NAME, + }, +}; + +static int __devinit gpio_event_init(void) +{ + return platform_driver_register(&gpio_event_driver); +} + +static void __exit gpio_event_exit(void) +{ + platform_driver_unregister(&gpio_event_driver); +} + +module_init(gpio_event_init); +module_exit(gpio_event_exit); + +MODULE_DESCRIPTION("GPIO Event Driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/misc/gpio_input.c b/drivers/input/misc/gpio_input.c new file mode 100644 index 00000000..eefd0272 --- /dev/null +++ b/drivers/input/misc/gpio_input.c @@ -0,0 +1,390 @@ +/* drivers/input/misc/gpio_input.c + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + DEBOUNCE_UNSTABLE = BIT(0), /* Got irq, while debouncing */ + DEBOUNCE_PRESSED = BIT(1), + DEBOUNCE_NOTPRESSED = BIT(2), + DEBOUNCE_WAIT_IRQ = BIT(3), /* Stable irq state */ + DEBOUNCE_POLL = BIT(4), /* Stable polling state */ + + DEBOUNCE_UNKNOWN = + DEBOUNCE_PRESSED | DEBOUNCE_NOTPRESSED, +}; + +struct gpio_key_state { + struct gpio_input_state *ds; + uint8_t debounce; +}; + +struct gpio_input_state { + struct gpio_event_input_devs *input_devs; + const struct gpio_event_input_info *info; + struct hrtimer timer; + int use_irq; + int debounce_count; + spinlock_t irq_lock; + struct wakeup_source *ws; + struct gpio_key_state key_state[0]; +}; + +static enum hrtimer_restart gpio_event_input_timer_func(struct hrtimer *timer) +{ + int i; + int pressed; + struct gpio_input_state *ds = + container_of(timer, struct gpio_input_state, timer); + unsigned gpio_flags = ds->info->flags; + unsigned npolarity; + int nkeys = ds->info->keymap_size; + const struct gpio_event_direct_entry *key_entry; + struct gpio_key_state *key_state; + unsigned long irqflags; + uint8_t debounce; + bool sync_needed; + +#if 0 + key_entry = kp->keys_info->keymap; + key_state = kp->key_state; + for (i = 0; i < nkeys; i++, key_entry++, key_state++) + pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio, + gpio_read_detect_status(key_entry->gpio)); +#endif + key_entry = ds->info->keymap; + key_state = ds->key_state; + sync_needed = false; + spin_lock_irqsave(&ds->irq_lock, irqflags); + for (i = 0; i < nkeys; i++, key_entry++, key_state++) { + debounce = key_state->debounce; + if (debounce & DEBOUNCE_WAIT_IRQ) + continue; + if (key_state->debounce & DEBOUNCE_UNSTABLE) { + debounce = key_state->debounce = DEBOUNCE_UNKNOWN; + enable_irq(gpio_to_irq(key_entry->gpio)); + if (gpio_flags & GPIOEDF_PRINT_KEY_UNSTABLE) + pr_info("gpio_keys_scan_keys: key %x-%x, %d " + "(%d) continue debounce\n", + ds->info->type, key_entry->code, + i, key_entry->gpio); + } + npolarity = !(gpio_flags & GPIOEDF_ACTIVE_HIGH); + pressed = gpio_get_value(key_entry->gpio) ^ npolarity; + if (debounce & DEBOUNCE_POLL) { + if (pressed == !(debounce & DEBOUNCE_PRESSED)) { + ds->debounce_count++; + key_state->debounce = DEBOUNCE_UNKNOWN; + if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) + pr_info("gpio_keys_scan_keys: key %x-" + "%x, %d (%d) start debounce\n", + ds->info->type, key_entry->code, + i, key_entry->gpio); + } + continue; + } + if (pressed && (debounce & DEBOUNCE_NOTPRESSED)) { + if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) + pr_info("gpio_keys_scan_keys: key %x-%x, %d " + "(%d) debounce pressed 1\n", + ds->info->type, key_entry->code, + i, key_entry->gpio); + key_state->debounce = DEBOUNCE_PRESSED; + continue; + } + if (!pressed && (debounce & DEBOUNCE_PRESSED)) { + if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) + pr_info("gpio_keys_scan_keys: key %x-%x, %d " + "(%d) debounce pressed 0\n", + ds->info->type, key_entry->code, + i, key_entry->gpio); + key_state->debounce = DEBOUNCE_NOTPRESSED; + continue; + } + /* key is stable */ + ds->debounce_count--; + if (ds->use_irq) + key_state->debounce |= DEBOUNCE_WAIT_IRQ; + else + key_state->debounce |= DEBOUNCE_POLL; + if (gpio_flags & GPIOEDF_PRINT_KEYS) + pr_info("gpio_keys_scan_keys: key %x-%x, %d (%d) " + "changed to %d\n", ds->info->type, + key_entry->code, i, key_entry->gpio, pressed); + input_event(ds->input_devs->dev[key_entry->dev], ds->info->type, + key_entry->code, pressed); + sync_needed = true; + } + if (sync_needed) { + for (i = 0; i < ds->input_devs->count; i++) + input_sync(ds->input_devs->dev[i]); + } + +#if 0 + key_entry = kp->keys_info->keymap; + key_state = kp->key_state; + for (i = 0; i < nkeys; i++, key_entry++, key_state++) { + pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio, + gpio_read_detect_status(key_entry->gpio)); + } +#endif + + if (ds->debounce_count) + hrtimer_start(timer, ds->info->debounce_time, HRTIMER_MODE_REL); + else if (!ds->use_irq) + hrtimer_start(timer, ds->info->poll_time, HRTIMER_MODE_REL); + else + __pm_relax(ds->ws); + + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + + return HRTIMER_NORESTART; +} + +static irqreturn_t gpio_event_input_irq_handler(int irq, void *dev_id) +{ + struct gpio_key_state *ks = dev_id; + struct gpio_input_state *ds = ks->ds; + int keymap_index = ks - ds->key_state; + const struct gpio_event_direct_entry *key_entry; + unsigned long irqflags; + int pressed; + + if (!ds->use_irq) + return IRQ_HANDLED; + + key_entry = &ds->info->keymap[keymap_index]; + + if (ds->info->debounce_time.tv64) { + spin_lock_irqsave(&ds->irq_lock, irqflags); + if (ks->debounce & DEBOUNCE_WAIT_IRQ) { + ks->debounce = DEBOUNCE_UNKNOWN; + if (ds->debounce_count++ == 0) { + __pm_stay_awake(ds->ws); + hrtimer_start( + &ds->timer, ds->info->debounce_time, + HRTIMER_MODE_REL); + } + if (ds->info->flags & GPIOEDF_PRINT_KEY_DEBOUNCE) + pr_info("gpio_event_input_irq_handler: " + "key %x-%x, %d (%d) start debounce\n", + ds->info->type, key_entry->code, + keymap_index, key_entry->gpio); + } else { + disable_irq_nosync(irq); + ks->debounce = DEBOUNCE_UNSTABLE; + } + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + } else { + pressed = gpio_get_value(key_entry->gpio) ^ + !(ds->info->flags & GPIOEDF_ACTIVE_HIGH); + if (ds->info->flags & GPIOEDF_PRINT_KEYS) + pr_info("gpio_event_input_irq_handler: key %x-%x, %d " + "(%d) changed to %d\n", + ds->info->type, key_entry->code, keymap_index, + key_entry->gpio, pressed); + input_event(ds->input_devs->dev[key_entry->dev], ds->info->type, + key_entry->code, pressed); + input_sync(ds->input_devs->dev[key_entry->dev]); + } + return IRQ_HANDLED; +} + +static int gpio_event_input_request_irqs(struct gpio_input_state *ds) +{ + int i; + int err; + unsigned int irq; + unsigned long req_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; + + for (i = 0; i < ds->info->keymap_size; i++) { + err = irq = gpio_to_irq(ds->info->keymap[i].gpio); + if (err < 0) + goto err_gpio_get_irq_num_failed; + err = request_irq(irq, gpio_event_input_irq_handler, + req_flags, "gpio_keys", &ds->key_state[i]); + if (err) { + pr_err("gpio_event_input_request_irqs: request_irq " + "failed for input %d, irq %d\n", + ds->info->keymap[i].gpio, irq); + goto err_request_irq_failed; + } + if (ds->info->info.no_suspend) { + err = enable_irq_wake(irq); + if (err) { + pr_err("gpio_event_input_request_irqs: " + "enable_irq_wake failed for input %d, " + "irq %d\n", + ds->info->keymap[i].gpio, irq); + goto err_enable_irq_wake_failed; + } + } + } + return 0; + + for (i = ds->info->keymap_size - 1; i >= 0; i--) { + irq = gpio_to_irq(ds->info->keymap[i].gpio); + if (ds->info->info.no_suspend) + disable_irq_wake(irq); +err_enable_irq_wake_failed: + free_irq(irq, &ds->key_state[i]); +err_request_irq_failed: +err_gpio_get_irq_num_failed: + ; + } + return err; +} + +int gpio_event_input_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func) +{ + int ret; + int i; + unsigned long irqflags; + struct gpio_event_input_info *di; + struct gpio_input_state *ds = *data; + char *wlname; + + di = container_of(info, struct gpio_event_input_info, info); + + if (func == GPIO_EVENT_FUNC_SUSPEND) { + if (ds->use_irq) + for (i = 0; i < di->keymap_size; i++) + disable_irq(gpio_to_irq(di->keymap[i].gpio)); + hrtimer_cancel(&ds->timer); + return 0; + } + if (func == GPIO_EVENT_FUNC_RESUME) { + spin_lock_irqsave(&ds->irq_lock, irqflags); + if (ds->use_irq) + for (i = 0; i < di->keymap_size; i++) + enable_irq(gpio_to_irq(di->keymap[i].gpio)); + hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + return 0; + } + + if (func == GPIO_EVENT_FUNC_INIT) { + if (ktime_to_ns(di->poll_time) <= 0) + di->poll_time = ktime_set(0, 20 * NSEC_PER_MSEC); + + *data = ds = kzalloc(sizeof(*ds) + sizeof(ds->key_state[0]) * + di->keymap_size, GFP_KERNEL); + if (ds == NULL) { + ret = -ENOMEM; + pr_err("gpio_event_input_func: " + "Failed to allocate private data\n"); + goto err_ds_alloc_failed; + } + ds->debounce_count = di->keymap_size; + ds->input_devs = input_devs; + ds->info = di; + wlname = kasprintf(GFP_KERNEL, "gpio_input:%s%s", + input_devs->dev[0]->name, + (input_devs->count > 1) ? "..." : ""); + + ds->ws = wakeup_source_register(wlname); + kfree(wlname); + if (!ds->ws) { + ret = -ENOMEM; + pr_err("gpio_event_input_func: " + "Failed to allocate wakeup source\n"); + goto err_ws_failed; + } + + spin_lock_init(&ds->irq_lock); + + for (i = 0; i < di->keymap_size; i++) { + int dev = di->keymap[i].dev; + if (dev >= input_devs->count) { + pr_err("gpio_event_input_func: bad device " + "index %d >= %d for key code %d\n", + dev, input_devs->count, + di->keymap[i].code); + ret = -EINVAL; + goto err_bad_keymap; + } + input_set_capability(input_devs->dev[dev], di->type, + di->keymap[i].code); + ds->key_state[i].ds = ds; + ds->key_state[i].debounce = DEBOUNCE_UNKNOWN; + } + + for (i = 0; i < di->keymap_size; i++) { + ret = gpio_request(di->keymap[i].gpio, "gpio_kp_in"); + if (ret) { + pr_err("gpio_event_input_func: gpio_request " + "failed for %d\n", di->keymap[i].gpio); + goto err_gpio_request_failed; + } + ret = gpio_direction_input(di->keymap[i].gpio); + if (ret) { + pr_err("gpio_event_input_func: " + "gpio_direction_input failed for %d\n", + di->keymap[i].gpio); + goto err_gpio_configure_failed; + } + } + + ret = gpio_event_input_request_irqs(ds); + + spin_lock_irqsave(&ds->irq_lock, irqflags); + ds->use_irq = ret == 0; + + pr_info("GPIO Input Driver: Start gpio inputs for %s%s in %s " + "mode\n", input_devs->dev[0]->name, + (input_devs->count > 1) ? "..." : "", + ret == 0 ? "interrupt" : "polling"); + + hrtimer_init(&ds->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ds->timer.function = gpio_event_input_timer_func; + hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + return 0; + } + + ret = 0; + spin_lock_irqsave(&ds->irq_lock, irqflags); + hrtimer_cancel(&ds->timer); + if (ds->use_irq) { + for (i = di->keymap_size - 1; i >= 0; i--) { + int irq = gpio_to_irq(di->keymap[i].gpio); + if (ds->info->info.no_suspend) + disable_irq_wake(irq); + free_irq(irq, &ds->key_state[i]); + } + } + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + + for (i = di->keymap_size - 1; i >= 0; i--) { +err_gpio_configure_failed: + gpio_free(di->keymap[i].gpio); +err_gpio_request_failed: + ; + } +err_bad_keymap: + wakeup_source_unregister(ds->ws); +err_ws_failed: + kfree(ds); +err_ds_alloc_failed: + return ret; +} diff --git a/drivers/input/misc/gpio_matrix.c b/drivers/input/misc/gpio_matrix.c new file mode 100644 index 00000000..eaa9e89d --- /dev/null +++ b/drivers/input/misc/gpio_matrix.c @@ -0,0 +1,441 @@ +/* drivers/input/misc/gpio_matrix.c + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +struct gpio_kp { + struct gpio_event_input_devs *input_devs; + struct gpio_event_matrix_info *keypad_info; + struct hrtimer timer; + struct wake_lock wake_lock; + int current_output; + unsigned int use_irq:1; + unsigned int key_state_changed:1; + unsigned int last_key_state_changed:1; + unsigned int some_keys_pressed:2; + unsigned int disabled_irq:1; + unsigned long keys_pressed[0]; +}; + +static void clear_phantom_key(struct gpio_kp *kp, int out, int in) +{ + struct gpio_event_matrix_info *mi = kp->keypad_info; + int key_index = out * mi->ninputs + in; + unsigned short keyentry = mi->keymap[key_index]; + unsigned short keycode = keyentry & MATRIX_KEY_MASK; + unsigned short dev = keyentry >> MATRIX_CODE_BITS; + + if (!test_bit(keycode, kp->input_devs->dev[dev]->key)) { + if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS) + pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) " + "cleared\n", keycode, out, in, + mi->output_gpios[out], mi->input_gpios[in]); + __clear_bit(key_index, kp->keys_pressed); + } else { + if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS) + pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) " + "not cleared\n", keycode, out, in, + mi->output_gpios[out], mi->input_gpios[in]); + } +} + +static int restore_keys_for_input(struct gpio_kp *kp, int out, int in) +{ + int rv = 0; + int key_index; + + key_index = out * kp->keypad_info->ninputs + in; + while (out < kp->keypad_info->noutputs) { + if (test_bit(key_index, kp->keys_pressed)) { + rv = 1; + clear_phantom_key(kp, out, in); + } + key_index += kp->keypad_info->ninputs; + out++; + } + return rv; +} + +static void remove_phantom_keys(struct gpio_kp *kp) +{ + int out, in, inp; + int key_index; + + if (kp->some_keys_pressed < 3) + return; + + for (out = 0; out < kp->keypad_info->noutputs; out++) { + inp = -1; + key_index = out * kp->keypad_info->ninputs; + for (in = 0; in < kp->keypad_info->ninputs; in++, key_index++) { + if (test_bit(key_index, kp->keys_pressed)) { + if (inp == -1) { + inp = in; + continue; + } + if (inp >= 0) { + if (!restore_keys_for_input(kp, out + 1, + inp)) + break; + clear_phantom_key(kp, out, inp); + inp = -2; + } + restore_keys_for_input(kp, out, in); + } + } + } +} + +static void report_key(struct gpio_kp *kp, int key_index, int out, int in) +{ + struct gpio_event_matrix_info *mi = kp->keypad_info; + int pressed = test_bit(key_index, kp->keys_pressed); + unsigned short keyentry = mi->keymap[key_index]; + unsigned short keycode = keyentry & MATRIX_KEY_MASK; + unsigned short dev = keyentry >> MATRIX_CODE_BITS; + + if (pressed != test_bit(keycode, kp->input_devs->dev[dev]->key)) { + if (keycode == KEY_RESERVED) { + if (mi->flags & GPIOKPF_PRINT_UNMAPPED_KEYS) + pr_info("gpiomatrix: unmapped key, %d-%d " + "(%d-%d) changed to %d\n", + out, in, mi->output_gpios[out], + mi->input_gpios[in], pressed); + } else { + if (mi->flags & GPIOKPF_PRINT_MAPPED_KEYS) + pr_info("gpiomatrix: key %x, %d-%d (%d-%d) " + "changed to %d\n", keycode, + out, in, mi->output_gpios[out], + mi->input_gpios[in], pressed); + input_report_key(kp->input_devs->dev[dev], keycode, pressed); + } + } +} + +static void report_sync(struct gpio_kp *kp) +{ + int i; + + for (i = 0; i < kp->input_devs->count; i++) + input_sync(kp->input_devs->dev[i]); +} + +static enum hrtimer_restart gpio_keypad_timer_func(struct hrtimer *timer) +{ + int out, in; + int key_index; + int gpio; + struct gpio_kp *kp = container_of(timer, struct gpio_kp, timer); + struct gpio_event_matrix_info *mi = kp->keypad_info; + unsigned gpio_keypad_flags = mi->flags; + unsigned polarity = !!(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH); + + out = kp->current_output; + if (out == mi->noutputs) { + out = 0; + kp->last_key_state_changed = kp->key_state_changed; + kp->key_state_changed = 0; + kp->some_keys_pressed = 0; + } else { + key_index = out * mi->ninputs; + for (in = 0; in < mi->ninputs; in++, key_index++) { + gpio = mi->input_gpios[in]; + if (gpio_get_value(gpio) ^ !polarity) { + if (kp->some_keys_pressed < 3) + kp->some_keys_pressed++; + kp->key_state_changed |= !__test_and_set_bit( + key_index, kp->keys_pressed); + } else + kp->key_state_changed |= __test_and_clear_bit( + key_index, kp->keys_pressed); + } + gpio = mi->output_gpios[out]; + if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) + gpio_set_value(gpio, !polarity); + else + gpio_direction_input(gpio); + out++; + } + kp->current_output = out; + if (out < mi->noutputs) { + gpio = mi->output_gpios[out]; + if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) + gpio_set_value(gpio, polarity); + else + gpio_direction_output(gpio, polarity); + hrtimer_start(timer, mi->settle_time, HRTIMER_MODE_REL); + return HRTIMER_NORESTART; + } + if (gpio_keypad_flags & GPIOKPF_DEBOUNCE) { + if (kp->key_state_changed) { + hrtimer_start(&kp->timer, mi->debounce_delay, + HRTIMER_MODE_REL); + return HRTIMER_NORESTART; + } + kp->key_state_changed = kp->last_key_state_changed; + } + if (kp->key_state_changed) { + if (gpio_keypad_flags & GPIOKPF_REMOVE_SOME_PHANTOM_KEYS) + remove_phantom_keys(kp); + key_index = 0; + for (out = 0; out < mi->noutputs; out++) + for (in = 0; in < mi->ninputs; in++, key_index++) + report_key(kp, key_index, out, in); + report_sync(kp); + } + if (!kp->use_irq || kp->some_keys_pressed) { + hrtimer_start(timer, mi->poll_time, HRTIMER_MODE_REL); + return HRTIMER_NORESTART; + } + + /* No keys are pressed, reenable interrupt */ + for (out = 0; out < mi->noutputs; out++) { + if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) + gpio_set_value(mi->output_gpios[out], polarity); + else + gpio_direction_output(mi->output_gpios[out], polarity); + } + for (in = 0; in < mi->ninputs; in++) + enable_irq(gpio_to_irq(mi->input_gpios[in])); + wake_unlock(&kp->wake_lock); + return HRTIMER_NORESTART; +} + +static irqreturn_t gpio_keypad_irq_handler(int irq_in, void *dev_id) +{ + int i; + struct gpio_kp *kp = dev_id; + struct gpio_event_matrix_info *mi = kp->keypad_info; + unsigned gpio_keypad_flags = mi->flags; + + if (!kp->use_irq) { + /* ignore interrupt while registering the handler */ + kp->disabled_irq = 1; + disable_irq_nosync(irq_in); + return IRQ_HANDLED; + } + + for (i = 0; i < mi->ninputs; i++) + disable_irq_nosync(gpio_to_irq(mi->input_gpios[i])); + for (i = 0; i < mi->noutputs; i++) { + if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) + gpio_set_value(mi->output_gpios[i], + !(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH)); + else + gpio_direction_input(mi->output_gpios[i]); + } + wake_lock(&kp->wake_lock); + hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + return IRQ_HANDLED; +} + +static int gpio_keypad_request_irqs(struct gpio_kp *kp) +{ + int i; + int err; + unsigned int irq; + unsigned long request_flags; + struct gpio_event_matrix_info *mi = kp->keypad_info; + + switch (mi->flags & (GPIOKPF_ACTIVE_HIGH|GPIOKPF_LEVEL_TRIGGERED_IRQ)) { + default: + request_flags = IRQF_TRIGGER_FALLING; + break; + case GPIOKPF_ACTIVE_HIGH: + request_flags = IRQF_TRIGGER_RISING; + break; + case GPIOKPF_LEVEL_TRIGGERED_IRQ: + request_flags = IRQF_TRIGGER_LOW; + break; + case GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_ACTIVE_HIGH: + request_flags = IRQF_TRIGGER_HIGH; + break; + } + + for (i = 0; i < mi->ninputs; i++) { + err = irq = gpio_to_irq(mi->input_gpios[i]); + if (err < 0) + goto err_gpio_get_irq_num_failed; + err = request_irq(irq, gpio_keypad_irq_handler, request_flags, + "gpio_kp", kp); + if (err) { + pr_err("gpiomatrix: request_irq failed for input %d, " + "irq %d\n", mi->input_gpios[i], irq); + goto err_request_irq_failed; + } + err = enable_irq_wake(irq); + if (err) { + pr_err("gpiomatrix: set_irq_wake failed for input %d, " + "irq %d\n", mi->input_gpios[i], irq); + } + disable_irq(irq); + if (kp->disabled_irq) { + kp->disabled_irq = 0; + enable_irq(irq); + } + } + return 0; + + for (i = mi->noutputs - 1; i >= 0; i--) { + free_irq(gpio_to_irq(mi->input_gpios[i]), kp); +err_request_irq_failed: +err_gpio_get_irq_num_failed: + ; + } + return err; +} + +int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func) +{ + int i; + int err; + int key_count; + struct gpio_kp *kp; + struct gpio_event_matrix_info *mi; + + mi = container_of(info, struct gpio_event_matrix_info, info); + if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME) { + /* TODO: disable scanning */ + return 0; + } + + if (func == GPIO_EVENT_FUNC_INIT) { + if (mi->keymap == NULL || + mi->input_gpios == NULL || + mi->output_gpios == NULL) { + err = -ENODEV; + pr_err("gpiomatrix: Incomplete pdata\n"); + goto err_invalid_platform_data; + } + key_count = mi->ninputs * mi->noutputs; + + *data = kp = kzalloc(sizeof(*kp) + sizeof(kp->keys_pressed[0]) * + BITS_TO_LONGS(key_count), GFP_KERNEL); + if (kp == NULL) { + err = -ENOMEM; + pr_err("gpiomatrix: Failed to allocate private data\n"); + goto err_kp_alloc_failed; + } + kp->input_devs = input_devs; + kp->keypad_info = mi; + for (i = 0; i < key_count; i++) { + unsigned short keyentry = mi->keymap[i]; + unsigned short keycode = keyentry & MATRIX_KEY_MASK; + unsigned short dev = keyentry >> MATRIX_CODE_BITS; + if (dev >= input_devs->count) { + pr_err("gpiomatrix: bad device index %d >= " + "%d for key code %d\n", + dev, input_devs->count, keycode); + err = -EINVAL; + goto err_bad_keymap; + } + if (keycode && keycode <= KEY_MAX) + input_set_capability(input_devs->dev[dev], + EV_KEY, keycode); + } + + for (i = 0; i < mi->noutputs; i++) { + err = gpio_request(mi->output_gpios[i], "gpio_kp_out"); + if (err) { + pr_err("gpiomatrix: gpio_request failed for " + "output %d\n", mi->output_gpios[i]); + goto err_request_output_gpio_failed; + } + if (gpio_cansleep(mi->output_gpios[i])) { + pr_err("gpiomatrix: unsupported output gpio %d," + " can sleep\n", mi->output_gpios[i]); + err = -EINVAL; + goto err_output_gpio_configure_failed; + } + if (mi->flags & GPIOKPF_DRIVE_INACTIVE) + err = gpio_direction_output(mi->output_gpios[i], + !(mi->flags & GPIOKPF_ACTIVE_HIGH)); + else + err = gpio_direction_input(mi->output_gpios[i]); + if (err) { + pr_err("gpiomatrix: gpio_configure failed for " + "output %d\n", mi->output_gpios[i]); + goto err_output_gpio_configure_failed; + } + } + for (i = 0; i < mi->ninputs; i++) { + err = gpio_request(mi->input_gpios[i], "gpio_kp_in"); + if (err) { + pr_err("gpiomatrix: gpio_request failed for " + "input %d\n", mi->input_gpios[i]); + goto err_request_input_gpio_failed; + } + err = gpio_direction_input(mi->input_gpios[i]); + if (err) { + pr_err("gpiomatrix: gpio_direction_input failed" + " for input %d\n", mi->input_gpios[i]); + goto err_gpio_direction_input_failed; + } + } + kp->current_output = mi->noutputs; + kp->key_state_changed = 1; + + hrtimer_init(&kp->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + kp->timer.function = gpio_keypad_timer_func; + wake_lock_init(&kp->wake_lock, WAKE_LOCK_SUSPEND, "gpio_kp"); + err = gpio_keypad_request_irqs(kp); + kp->use_irq = err == 0; + + pr_info("GPIO Matrix Keypad Driver: Start keypad matrix for " + "%s%s in %s mode\n", input_devs->dev[0]->name, + (input_devs->count > 1) ? "..." : "", + kp->use_irq ? "interrupt" : "polling"); + + if (kp->use_irq) + wake_lock(&kp->wake_lock); + hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + + return 0; + } + + err = 0; + kp = *data; + + if (kp->use_irq) + for (i = mi->noutputs - 1; i >= 0; i--) + free_irq(gpio_to_irq(mi->input_gpios[i]), kp); + + hrtimer_cancel(&kp->timer); + wake_lock_destroy(&kp->wake_lock); + for (i = mi->noutputs - 1; i >= 0; i--) { +err_gpio_direction_input_failed: + gpio_free(mi->input_gpios[i]); +err_request_input_gpio_failed: + ; + } + for (i = mi->noutputs - 1; i >= 0; i--) { +err_output_gpio_configure_failed: + gpio_free(mi->output_gpios[i]); +err_request_output_gpio_failed: + ; + } +err_bad_keymap: + kfree(kp); +err_kp_alloc_failed: +err_invalid_platform_data: + return err; +} diff --git a/drivers/input/misc/gpio_output.c b/drivers/input/misc/gpio_output.c new file mode 100644 index 00000000..2aac2fad --- /dev/null +++ b/drivers/input/misc/gpio_output.c @@ -0,0 +1,97 @@ +/* drivers/input/misc/gpio_output.c + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include + +int gpio_event_output_event( + struct gpio_event_input_devs *input_devs, struct gpio_event_info *info, + void **data, unsigned int dev, unsigned int type, + unsigned int code, int value) +{ + int i; + struct gpio_event_output_info *oi; + oi = container_of(info, struct gpio_event_output_info, info); + if (type != oi->type) + return 0; + if (!(oi->flags & GPIOEDF_ACTIVE_HIGH)) + value = !value; + for (i = 0; i < oi->keymap_size; i++) + if (dev == oi->keymap[i].dev && code == oi->keymap[i].code) + gpio_set_value(oi->keymap[i].gpio, value); + return 0; +} + +int gpio_event_output_func( + struct gpio_event_input_devs *input_devs, struct gpio_event_info *info, + void **data, int func) +{ + int ret; + int i; + struct gpio_event_output_info *oi; + oi = container_of(info, struct gpio_event_output_info, info); + + if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME) + return 0; + + if (func == GPIO_EVENT_FUNC_INIT) { + int output_level = !(oi->flags & GPIOEDF_ACTIVE_HIGH); + + for (i = 0; i < oi->keymap_size; i++) { + int dev = oi->keymap[i].dev; + if (dev >= input_devs->count) { + pr_err("gpio_event_output_func: bad device " + "index %d >= %d for key code %d\n", + dev, input_devs->count, + oi->keymap[i].code); + ret = -EINVAL; + goto err_bad_keymap; + } + input_set_capability(input_devs->dev[dev], oi->type, + oi->keymap[i].code); + } + + for (i = 0; i < oi->keymap_size; i++) { + ret = gpio_request(oi->keymap[i].gpio, + "gpio_event_output"); + if (ret) { + pr_err("gpio_event_output_func: gpio_request " + "failed for %d\n", oi->keymap[i].gpio); + goto err_gpio_request_failed; + } + ret = gpio_direction_output(oi->keymap[i].gpio, + output_level); + if (ret) { + pr_err("gpio_event_output_func: " + "gpio_direction_output failed for %d\n", + oi->keymap[i].gpio); + goto err_gpio_direction_output_failed; + } + } + return 0; + } + + ret = 0; + for (i = oi->keymap_size - 1; i >= 0; i--) { +err_gpio_direction_output_failed: + gpio_free(oi->keymap[i].gpio); +err_gpio_request_failed: + ; + } +err_bad_keymap: + return ret; +} + diff --git a/drivers/input/misc/keychord.c b/drivers/input/misc/keychord.c new file mode 100644 index 00000000..a5ea27ad --- /dev/null +++ b/drivers/input/misc/keychord.c @@ -0,0 +1,391 @@ +/* + * drivers/input/misc/keychord.c + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define KEYCHORD_NAME "keychord" +#define BUFFER_SIZE 16 + +MODULE_AUTHOR("Mike Lockwood "); +MODULE_DESCRIPTION("Key chord input driver"); +MODULE_SUPPORTED_DEVICE("keychord"); +MODULE_LICENSE("GPL"); + +#define NEXT_KEYCHORD(kc) ((struct input_keychord *) \ + ((char *)kc + sizeof(struct input_keychord) + \ + kc->count * sizeof(kc->keycodes[0]))) + +struct keychord_device { + struct input_handler input_handler; + int registered; + + /* list of keychords to monitor */ + struct input_keychord *keychords; + int keychord_count; + + /* bitmask of keys contained in our keychords */ + unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; + /* current state of the keys */ + unsigned long keystate[BITS_TO_LONGS(KEY_CNT)]; + /* number of keys that are currently pressed */ + int key_down; + + /* second input_device_id is needed for null termination */ + struct input_device_id device_ids[2]; + + spinlock_t lock; + wait_queue_head_t waitq; + unsigned char head; + unsigned char tail; + __u16 buff[BUFFER_SIZE]; +}; + +static int check_keychord(struct keychord_device *kdev, + struct input_keychord *keychord) +{ + int i; + + if (keychord->count != kdev->key_down) + return 0; + + for (i = 0; i < keychord->count; i++) { + if (!test_bit(keychord->keycodes[i], kdev->keystate)) + return 0; + } + + /* we have a match */ + return 1; +} + +static void keychord_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ + struct keychord_device *kdev = handle->private; + struct input_keychord *keychord; + unsigned long flags; + int i, got_chord = 0; + + if (type != EV_KEY || code >= KEY_MAX) + return; + + spin_lock_irqsave(&kdev->lock, flags); + /* do nothing if key state did not change */ + if (!test_bit(code, kdev->keystate) == !value) + goto done; + __change_bit(code, kdev->keystate); + if (value) + kdev->key_down++; + else + kdev->key_down--; + + /* don't notify on key up */ + if (!value) + goto done; + /* ignore this event if it is not one of the keys we are monitoring */ + if (!test_bit(code, kdev->keybit)) + goto done; + + keychord = kdev->keychords; + if (!keychord) + goto done; + + /* check to see if the keyboard state matches any keychords */ + for (i = 0; i < kdev->keychord_count; i++) { + if (check_keychord(kdev, keychord)) { + kdev->buff[kdev->head] = keychord->id; + kdev->head = (kdev->head + 1) % BUFFER_SIZE; + got_chord = 1; + break; + } + /* skip to next keychord */ + keychord = NEXT_KEYCHORD(keychord); + } + +done: + spin_unlock_irqrestore(&kdev->lock, flags); + + if (got_chord) { + pr_info("keychord: got keychord id %d. Any tasks: %d\n", + keychord->id, + !list_empty_careful(&kdev->waitq.task_list)); + wake_up_interruptible(&kdev->waitq); + } +} + +static int keychord_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + int i, ret; + struct input_handle *handle; + struct keychord_device *kdev = + container_of(handler, struct keychord_device, input_handler); + + /* + * ignore this input device if it does not contain any keycodes + * that we are monitoring + */ + for (i = 0; i < KEY_MAX; i++) { + if (test_bit(i, kdev->keybit) && test_bit(i, dev->keybit)) + break; + } + if (i == KEY_MAX) + return -ENODEV; + + handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = KEYCHORD_NAME; + handle->private = kdev; + + ret = input_register_handle(handle); + if (ret) + goto err_input_register_handle; + + ret = input_open_device(handle); + if (ret) + goto err_input_open_device; + + pr_info("keychord: using input dev %s for fevent\n", dev->name); + + return 0; + +err_input_open_device: + input_unregister_handle(handle); +err_input_register_handle: + kfree(handle); + return ret; +} + +static void keychord_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +/* + * keychord_read is used to read keychord events from the driver + */ +static ssize_t keychord_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct keychord_device *kdev = file->private_data; + __u16 id; + int retval; + unsigned long flags; + + if (count < sizeof(id)) + return -EINVAL; + count = sizeof(id); + + if (kdev->head == kdev->tail && (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + + retval = wait_event_interruptible(kdev->waitq, + kdev->head != kdev->tail); + if (retval) + return retval; + + spin_lock_irqsave(&kdev->lock, flags); + /* pop a keychord ID off the queue */ + id = kdev->buff[kdev->tail]; + kdev->tail = (kdev->tail + 1) % BUFFER_SIZE; + spin_unlock_irqrestore(&kdev->lock, flags); + + if (copy_to_user(buffer, &id, count)) + return -EFAULT; + + return count; +} + +/* + * keychord_write is used to configure the driver + */ +static ssize_t keychord_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct keychord_device *kdev = file->private_data; + struct input_keychord *keychords = 0; + struct input_keychord *keychord, *next, *end; + int ret, i, key; + unsigned long flags; + + if (count < sizeof(struct input_keychord)) + return -EINVAL; + keychords = kzalloc(count, GFP_KERNEL); + if (!keychords) + return -ENOMEM; + + /* read list of keychords from userspace */ + if (copy_from_user(keychords, buffer, count)) { + kfree(keychords); + return -EFAULT; + } + + /* unregister handler before changing configuration */ + if (kdev->registered) { + input_unregister_handler(&kdev->input_handler); + kdev->registered = 0; + } + + spin_lock_irqsave(&kdev->lock, flags); + /* clear any existing configuration */ + kfree(kdev->keychords); + kdev->keychords = 0; + kdev->keychord_count = 0; + kdev->key_down = 0; + memset(kdev->keybit, 0, sizeof(kdev->keybit)); + memset(kdev->keystate, 0, sizeof(kdev->keystate)); + kdev->head = kdev->tail = 0; + + keychord = keychords; + end = (struct input_keychord *)((char *)keychord + count); + + while (keychord < end) { + next = NEXT_KEYCHORD(keychord); + if (keychord->count <= 0 || next > end) { + pr_err("keychord: invalid keycode count %d\n", + keychord->count); + goto err_unlock_return; + } + if (keychord->version != KEYCHORD_VERSION) { + pr_err("keychord: unsupported version %d\n", + keychord->version); + goto err_unlock_return; + } + + /* keep track of the keys we are monitoring in keybit */ + for (i = 0; i < keychord->count; i++) { + key = keychord->keycodes[i]; + if (key < 0 || key >= KEY_CNT) { + pr_err("keychord: keycode %d out of range\n", + key); + goto err_unlock_return; + } + __set_bit(key, kdev->keybit); + } + + kdev->keychord_count++; + keychord = next; + } + + kdev->keychords = keychords; + spin_unlock_irqrestore(&kdev->lock, flags); + + ret = input_register_handler(&kdev->input_handler); + if (ret) { + kfree(keychords); + kdev->keychords = 0; + return ret; + } + kdev->registered = 1; + + return count; + +err_unlock_return: + spin_unlock_irqrestore(&kdev->lock, flags); + kfree(keychords); + return -EINVAL; +} + +static unsigned int keychord_poll(struct file *file, poll_table *wait) +{ + struct keychord_device *kdev = file->private_data; + + poll_wait(file, &kdev->waitq, wait); + + if (kdev->head != kdev->tail) + return POLLIN | POLLRDNORM; + + return 0; +} + +static int keychord_open(struct inode *inode, struct file *file) +{ + struct keychord_device *kdev; + + kdev = kzalloc(sizeof(struct keychord_device), GFP_KERNEL); + if (!kdev) + return -ENOMEM; + + spin_lock_init(&kdev->lock); + init_waitqueue_head(&kdev->waitq); + + kdev->input_handler.event = keychord_event; + kdev->input_handler.connect = keychord_connect; + kdev->input_handler.disconnect = keychord_disconnect; + kdev->input_handler.name = KEYCHORD_NAME; + kdev->input_handler.id_table = kdev->device_ids; + + kdev->device_ids[0].flags = INPUT_DEVICE_ID_MATCH_EVBIT; + __set_bit(EV_KEY, kdev->device_ids[0].evbit); + + file->private_data = kdev; + + return 0; +} + +static int keychord_release(struct inode *inode, struct file *file) +{ + struct keychord_device *kdev = file->private_data; + + if (kdev->registered) + input_unregister_handler(&kdev->input_handler); + kfree(kdev); + + return 0; +} + +static const struct file_operations keychord_fops = { + .owner = THIS_MODULE, + .open = keychord_open, + .release = keychord_release, + .read = keychord_read, + .write = keychord_write, + .poll = keychord_poll, +}; + +static struct miscdevice keychord_misc = { + .fops = &keychord_fops, + .name = KEYCHORD_NAME, + .minor = MISC_DYNAMIC_MINOR, +}; + +static int __init keychord_init(void) +{ + return misc_register(&keychord_misc); +} + +static void __exit keychord_exit(void) +{ + misc_deregister(&keychord_misc); +} + +module_init(keychord_init); +module_exit(keychord_exit); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 2a214191..f102a924 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -451,6 +451,12 @@ config TOUCHSCREEN_TNETV107X To compile this driver as a module, choose M here: the module will be called tnetv107x-ts. +config TOUCHSCREEN_SYNAPTICS_I2C_RMI + tristate "Synaptics i2c touchscreen" + depends on I2C + help + This enables support for Synaptics RMI over I2C based touchscreens. + config TOUCHSCREEN_TOUCHRIGHT tristate "Touchright serial touchscreen" select SERIO @@ -792,6 +798,35 @@ config TOUCHSCREEN_TSC2007 To compile this driver as a module, choose M here: the module will be called tsc2007. +config TOUCHSCREEN_ANYKA + bool "Anyka extra touchscreen" + depends on ARCH_AK39 + help + This item enable extra touchscreen interface for Anyka platform. + +config TOUCHSCREEN_CP2007 + tristate "cp2007 based touchscreen" + depends on TOUCHSCREEN_ANYKA + default y + help + Say Y here if you have a CP2007 based touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called tscp2007. + +config TOUCHSCREEN_CP2017 + tristate "cp2017 based touchscreen" + depends on TOUCHSCREEN_ANYKA + help + Say Y here if you have a CP2017 based touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called tscp2017. + config TOUCHSCREEN_W90X900 tristate "W90P910 touchscreen driver" depends on HAVE_CLK diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 3d5cf8cb..55706d47 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -51,12 +51,14 @@ obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o obj-$(CONFIG_TOUCHSCREEN_TI_TSCADC) += ti_tscadc.o obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o +obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI) += synaptics_i2c_rmi.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o obj-$(CONFIG_TOUCHSCREEN_TSC_SERIO) += tsc40.o obj-$(CONFIG_TOUCHSCREEN_TSC2005) += tsc2005.o obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o +obj-$(CONFIG_TOUCHSCREEN_CP2007) += tscp2007.o obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o obj-$(CONFIG_TOUCHSCREEN_WM831X) += wm831x-ts.o diff --git a/drivers/input/touchscreen/synaptics_i2c_rmi.c b/drivers/input/touchscreen/synaptics_i2c_rmi.c new file mode 100644 index 00000000..5729602c --- /dev/null +++ b/drivers/input/touchscreen/synaptics_i2c_rmi.c @@ -0,0 +1,675 @@ +/* drivers/input/keyboard/synaptics_i2c_rmi.c + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct workqueue_struct *synaptics_wq; + +struct synaptics_ts_data { + uint16_t addr; + struct i2c_client *client; + struct input_dev *input_dev; + int use_irq; + bool has_relative_report; + struct hrtimer timer; + struct work_struct work; + uint16_t max[2]; + int snap_state[2][2]; + int snap_down_on[2]; + int snap_down_off[2]; + int snap_up_on[2]; + int snap_up_off[2]; + int snap_down[2]; + int snap_up[2]; + uint32_t flags; + int reported_finger_count; + int8_t sensitivity_adjust; + int (*power)(int on); + struct early_suspend early_suspend; +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void synaptics_ts_early_suspend(struct early_suspend *h); +static void synaptics_ts_late_resume(struct early_suspend *h); +#endif + +static int synaptics_init_panel(struct synaptics_ts_data *ts) +{ + int ret; + + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x10); /* page select = 0x10 */ + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_write_byte_data failed for page select\n"); + goto err_page_select_failed; + } + ret = i2c_smbus_write_byte_data(ts->client, 0x41, 0x04); /* Set "No Clip Z" */ + if (ret < 0) + printk(KERN_ERR "i2c_smbus_write_byte_data failed for No Clip Z\n"); + + ret = i2c_smbus_write_byte_data(ts->client, 0x44, + ts->sensitivity_adjust); + if (ret < 0) + pr_err("synaptics_ts: failed to set Sensitivity Adjust\n"); + +err_page_select_failed: + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x04); /* page select = 0x04 */ + if (ret < 0) + printk(KERN_ERR "i2c_smbus_write_byte_data failed for page select\n"); + ret = i2c_smbus_write_byte_data(ts->client, 0xf0, 0x81); /* normal operation, 80 reports per second */ + if (ret < 0) + printk(KERN_ERR "synaptics_ts_resume: i2c_smbus_write_byte_data failed\n"); + return ret; +} + +static void synaptics_ts_work_func(struct work_struct *work) +{ + int i; + int ret; + int bad_data = 0; + struct i2c_msg msg[2]; + uint8_t start_reg; + uint8_t buf[15]; + struct synaptics_ts_data *ts = container_of(work, struct synaptics_ts_data, work); + int buf_len = ts->has_relative_report ? 15 : 13; + + msg[0].addr = ts->client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &start_reg; + start_reg = 0x00; + msg[1].addr = ts->client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = buf_len; + msg[1].buf = buf; + + /* printk("synaptics_ts_work_func\n"); */ + for (i = 0; i < ((ts->use_irq && !bad_data) ? 1 : 10); i++) { + ret = i2c_transfer(ts->client->adapter, msg, 2); + if (ret < 0) { + printk(KERN_ERR "synaptics_ts_work_func: i2c_transfer failed\n"); + bad_data = 1; + } else { + /* printk("synaptics_ts_work_func: %x %x %x %x %x %x" */ + /* " %x %x %x %x %x %x %x %x %x, ret %d\n", */ + /* buf[0], buf[1], buf[2], buf[3], */ + /* buf[4], buf[5], buf[6], buf[7], */ + /* buf[8], buf[9], buf[10], buf[11], */ + /* buf[12], buf[13], buf[14], ret); */ + if ((buf[buf_len - 1] & 0xc0) != 0x40) { + printk(KERN_WARNING "synaptics_ts_work_func:" + " bad read %x %x %x %x %x %x %x %x %x" + " %x %x %x %x %x %x, ret %d\n", + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7], + buf[8], buf[9], buf[10], buf[11], + buf[12], buf[13], buf[14], ret); + if (bad_data) + synaptics_init_panel(ts); + bad_data = 1; + continue; + } + bad_data = 0; + if ((buf[buf_len - 1] & 1) == 0) { + /* printk("read %d coordinates\n", i); */ + break; + } else { + int pos[2][2]; + int f, a; + int base; + /* int x = buf[3] | (uint16_t)(buf[2] & 0x1f) << 8; */ + /* int y = buf[5] | (uint16_t)(buf[4] & 0x1f) << 8; */ + int z = buf[1]; + int w = buf[0] >> 4; + int finger = buf[0] & 7; + + /* int x2 = buf[3+6] | (uint16_t)(buf[2+6] & 0x1f) << 8; */ + /* int y2 = buf[5+6] | (uint16_t)(buf[4+6] & 0x1f) << 8; */ + /* int z2 = buf[1+6]; */ + /* int w2 = buf[0+6] >> 4; */ + /* int finger2 = buf[0+6] & 7; */ + + /* int dx = (int8_t)buf[12]; */ + /* int dy = (int8_t)buf[13]; */ + int finger2_pressed; + + /* printk("x %4d, y %4d, z %3d, w %2d, F %d, 2nd: x %4d, y %4d, z %3d, w %2d, F %d, dx %4d, dy %4d\n", */ + /* x, y, z, w, finger, */ + /* x2, y2, z2, w2, finger2, */ + /* dx, dy); */ + + base = 2; + for (f = 0; f < 2; f++) { + uint32_t flip_flag = SYNAPTICS_FLIP_X; + for (a = 0; a < 2; a++) { + int p = buf[base + 1]; + p |= (uint16_t)(buf[base] & 0x1f) << 8; + if (ts->flags & flip_flag) + p = ts->max[a] - p; + if (ts->flags & SYNAPTICS_SNAP_TO_INACTIVE_EDGE) { + if (ts->snap_state[f][a]) { + if (p <= ts->snap_down_off[a]) + p = ts->snap_down[a]; + else if (p >= ts->snap_up_off[a]) + p = ts->snap_up[a]; + else + ts->snap_state[f][a] = 0; + } else { + if (p <= ts->snap_down_on[a]) { + p = ts->snap_down[a]; + ts->snap_state[f][a] = 1; + } else if (p >= ts->snap_up_on[a]) { + p = ts->snap_up[a]; + ts->snap_state[f][a] = 1; + } + } + } + pos[f][a] = p; + base += 2; + flip_flag <<= 1; + } + base += 2; + if (ts->flags & SYNAPTICS_SWAP_XY) + swap(pos[f][0], pos[f][1]); + } + if (z) { + input_report_abs(ts->input_dev, ABS_X, pos[0][0]); + input_report_abs(ts->input_dev, ABS_Y, pos[0][1]); + } + input_report_abs(ts->input_dev, ABS_PRESSURE, z); + input_report_abs(ts->input_dev, ABS_TOOL_WIDTH, w); + input_report_key(ts->input_dev, BTN_TOUCH, finger); + finger2_pressed = finger > 1 && finger != 7; + input_report_key(ts->input_dev, BTN_2, finger2_pressed); + if (finger2_pressed) { + input_report_abs(ts->input_dev, ABS_HAT0X, pos[1][0]); + input_report_abs(ts->input_dev, ABS_HAT0Y, pos[1][1]); + } + + if (!finger) + z = 0; + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, z); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, pos[0][0]); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, pos[0][1]); + input_mt_sync(ts->input_dev); + if (finger2_pressed) { + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, z); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, pos[1][0]); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, pos[1][1]); + input_mt_sync(ts->input_dev); + } else if (ts->reported_finger_count > 1) { + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0); + input_mt_sync(ts->input_dev); + } + ts->reported_finger_count = finger; + input_sync(ts->input_dev); + } + } + } + if (ts->use_irq) + enable_irq(ts->client->irq); +} + +static enum hrtimer_restart synaptics_ts_timer_func(struct hrtimer *timer) +{ + struct synaptics_ts_data *ts = container_of(timer, struct synaptics_ts_data, timer); + /* printk("synaptics_ts_timer_func\n"); */ + + queue_work(synaptics_wq, &ts->work); + + hrtimer_start(&ts->timer, ktime_set(0, 12500000), HRTIMER_MODE_REL); + return HRTIMER_NORESTART; +} + +static irqreturn_t synaptics_ts_irq_handler(int irq, void *dev_id) +{ + struct synaptics_ts_data *ts = dev_id; + + /* printk("synaptics_ts_irq_handler\n"); */ + disable_irq_nosync(ts->client->irq); + queue_work(synaptics_wq, &ts->work); + return IRQ_HANDLED; +} + +static int synaptics_ts_probe( + struct i2c_client *client, const struct i2c_device_id *id) +{ + struct synaptics_ts_data *ts; + uint8_t buf0[4]; + uint8_t buf1[8]; + struct i2c_msg msg[2]; + int ret = 0; + uint16_t max_x, max_y; + int fuzz_x, fuzz_y, fuzz_p, fuzz_w; + struct synaptics_i2c_rmi_platform_data *pdata; + unsigned long irqflags; + int inactive_area_left; + int inactive_area_right; + int inactive_area_top; + int inactive_area_bottom; + int snap_left_on; + int snap_left_off; + int snap_right_on; + int snap_right_off; + int snap_top_on; + int snap_top_off; + int snap_bottom_on; + int snap_bottom_off; + uint32_t panel_version; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + printk(KERN_ERR "synaptics_ts_probe: need I2C_FUNC_I2C\n"); + ret = -ENODEV; + goto err_check_functionality_failed; + } + + ts = kzalloc(sizeof(*ts), GFP_KERNEL); + if (ts == NULL) { + ret = -ENOMEM; + goto err_alloc_data_failed; + } + INIT_WORK(&ts->work, synaptics_ts_work_func); + ts->client = client; + i2c_set_clientdata(client, ts); + pdata = client->dev.platform_data; + if (pdata) + ts->power = pdata->power; + if (ts->power) { + ret = ts->power(1); + if (ret < 0) { + printk(KERN_ERR "synaptics_ts_probe power on failed\n"); + goto err_power_failed; + } + } + + ret = i2c_smbus_write_byte_data(ts->client, 0xf4, 0x01); /* device command = reset */ + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_write_byte_data failed\n"); + /* fail? */ + } + { + int retry = 10; + while (retry-- > 0) { + ret = i2c_smbus_read_byte_data(ts->client, 0xe4); + if (ret >= 0) + break; + msleep(100); + } + } + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: Product Major Version %x\n", ret); + panel_version = ret << 8; + ret = i2c_smbus_read_byte_data(ts->client, 0xe5); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: Product Minor Version %x\n", ret); + panel_version |= ret; + + ret = i2c_smbus_read_byte_data(ts->client, 0xe3); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: product property %x\n", ret); + + if (pdata) { + while (pdata->version > panel_version) + pdata++; + ts->flags = pdata->flags; + ts->sensitivity_adjust = pdata->sensitivity_adjust; + irqflags = pdata->irqflags; + inactive_area_left = pdata->inactive_left; + inactive_area_right = pdata->inactive_right; + inactive_area_top = pdata->inactive_top; + inactive_area_bottom = pdata->inactive_bottom; + snap_left_on = pdata->snap_left_on; + snap_left_off = pdata->snap_left_off; + snap_right_on = pdata->snap_right_on; + snap_right_off = pdata->snap_right_off; + snap_top_on = pdata->snap_top_on; + snap_top_off = pdata->snap_top_off; + snap_bottom_on = pdata->snap_bottom_on; + snap_bottom_off = pdata->snap_bottom_off; + fuzz_x = pdata->fuzz_x; + fuzz_y = pdata->fuzz_y; + fuzz_p = pdata->fuzz_p; + fuzz_w = pdata->fuzz_w; + } else { + irqflags = 0; + inactive_area_left = 0; + inactive_area_right = 0; + inactive_area_top = 0; + inactive_area_bottom = 0; + snap_left_on = 0; + snap_left_off = 0; + snap_right_on = 0; + snap_right_off = 0; + snap_top_on = 0; + snap_top_off = 0; + snap_bottom_on = 0; + snap_bottom_off = 0; + fuzz_x = 0; + fuzz_y = 0; + fuzz_p = 0; + fuzz_w = 0; + } + + ret = i2c_smbus_read_byte_data(ts->client, 0xf0); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: device control %x\n", ret); + + ret = i2c_smbus_read_byte_data(ts->client, 0xf1); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: interrupt enable %x\n", ret); + + ret = i2c_smbus_write_byte_data(ts->client, 0xf1, 0); /* disable interrupt */ + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_write_byte_data failed\n"); + goto err_detect_failed; + } + + msg[0].addr = ts->client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = buf0; + buf0[0] = 0xe0; + msg[1].addr = ts->client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 8; + msg[1].buf = buf1; + ret = i2c_transfer(ts->client->adapter, msg, 2); + if (ret < 0) { + printk(KERN_ERR "i2c_transfer failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: 0xe0: %x %x %x %x %x %x %x %x\n", + buf1[0], buf1[1], buf1[2], buf1[3], + buf1[4], buf1[5], buf1[6], buf1[7]); + + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x10); /* page select = 0x10 */ + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_write_byte_data failed for page select\n"); + goto err_detect_failed; + } + ret = i2c_smbus_read_word_data(ts->client, 0x02); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_word_data failed\n"); + goto err_detect_failed; + } + ts->has_relative_report = !(ret & 0x100); + printk(KERN_INFO "synaptics_ts_probe: Sensor properties %x\n", ret); + ret = i2c_smbus_read_word_data(ts->client, 0x04); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_word_data failed\n"); + goto err_detect_failed; + } + ts->max[0] = max_x = (ret >> 8 & 0xff) | ((ret & 0x1f) << 8); + ret = i2c_smbus_read_word_data(ts->client, 0x06); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_word_data failed\n"); + goto err_detect_failed; + } + ts->max[1] = max_y = (ret >> 8 & 0xff) | ((ret & 0x1f) << 8); + if (ts->flags & SYNAPTICS_SWAP_XY) + swap(max_x, max_y); + + ret = synaptics_init_panel(ts); /* will also switch back to page 0x04 */ + if (ret < 0) { + printk(KERN_ERR "synaptics_init_panel failed\n"); + goto err_detect_failed; + } + + ts->input_dev = input_allocate_device(); + if (ts->input_dev == NULL) { + ret = -ENOMEM; + printk(KERN_ERR "synaptics_ts_probe: Failed to allocate input device\n"); + goto err_input_dev_alloc_failed; + } + ts->input_dev->name = "synaptics-rmi-touchscreen"; + set_bit(EV_SYN, ts->input_dev->evbit); + set_bit(EV_KEY, ts->input_dev->evbit); + set_bit(BTN_TOUCH, ts->input_dev->keybit); + set_bit(BTN_2, ts->input_dev->keybit); + set_bit(EV_ABS, ts->input_dev->evbit); + inactive_area_left = inactive_area_left * max_x / 0x10000; + inactive_area_right = inactive_area_right * max_x / 0x10000; + inactive_area_top = inactive_area_top * max_y / 0x10000; + inactive_area_bottom = inactive_area_bottom * max_y / 0x10000; + snap_left_on = snap_left_on * max_x / 0x10000; + snap_left_off = snap_left_off * max_x / 0x10000; + snap_right_on = snap_right_on * max_x / 0x10000; + snap_right_off = snap_right_off * max_x / 0x10000; + snap_top_on = snap_top_on * max_y / 0x10000; + snap_top_off = snap_top_off * max_y / 0x10000; + snap_bottom_on = snap_bottom_on * max_y / 0x10000; + snap_bottom_off = snap_bottom_off * max_y / 0x10000; + fuzz_x = fuzz_x * max_x / 0x10000; + fuzz_y = fuzz_y * max_y / 0x10000; + ts->snap_down[!!(ts->flags & SYNAPTICS_SWAP_XY)] = -inactive_area_left; + ts->snap_up[!!(ts->flags & SYNAPTICS_SWAP_XY)] = max_x + inactive_area_right; + ts->snap_down[!(ts->flags & SYNAPTICS_SWAP_XY)] = -inactive_area_top; + ts->snap_up[!(ts->flags & SYNAPTICS_SWAP_XY)] = max_y + inactive_area_bottom; + ts->snap_down_on[!!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_left_on; + ts->snap_down_off[!!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_left_off; + ts->snap_up_on[!!(ts->flags & SYNAPTICS_SWAP_XY)] = max_x - snap_right_on; + ts->snap_up_off[!!(ts->flags & SYNAPTICS_SWAP_XY)] = max_x - snap_right_off; + ts->snap_down_on[!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_top_on; + ts->snap_down_off[!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_top_off; + ts->snap_up_on[!(ts->flags & SYNAPTICS_SWAP_XY)] = max_y - snap_bottom_on; + ts->snap_up_off[!(ts->flags & SYNAPTICS_SWAP_XY)] = max_y - snap_bottom_off; + printk(KERN_INFO "synaptics_ts_probe: max_x %d, max_y %d\n", max_x, max_y); + printk(KERN_INFO "synaptics_ts_probe: inactive_x %d %d, inactive_y %d %d\n", + inactive_area_left, inactive_area_right, + inactive_area_top, inactive_area_bottom); + printk(KERN_INFO "synaptics_ts_probe: snap_x %d-%d %d-%d, snap_y %d-%d %d-%d\n", + snap_left_on, snap_left_off, snap_right_on, snap_right_off, + snap_top_on, snap_top_off, snap_bottom_on, snap_bottom_off); + input_set_abs_params(ts->input_dev, ABS_X, -inactive_area_left, max_x + inactive_area_right, fuzz_x, 0); + input_set_abs_params(ts->input_dev, ABS_Y, -inactive_area_top, max_y + inactive_area_bottom, fuzz_y, 0); + input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 255, fuzz_p, 0); + input_set_abs_params(ts->input_dev, ABS_TOOL_WIDTH, 0, 15, fuzz_w, 0); + input_set_abs_params(ts->input_dev, ABS_HAT0X, -inactive_area_left, max_x + inactive_area_right, fuzz_x, 0); + input_set_abs_params(ts->input_dev, ABS_HAT0Y, -inactive_area_top, max_y + inactive_area_bottom, fuzz_y, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, -inactive_area_left, max_x + inactive_area_right, fuzz_x, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, -inactive_area_top, max_y + inactive_area_bottom, fuzz_y, 0); + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, fuzz_p, 0); + input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 15, fuzz_w, 0); + /* ts->input_dev->name = ts->keypad_info->name; */ + ret = input_register_device(ts->input_dev); + if (ret) { + printk(KERN_ERR "synaptics_ts_probe: Unable to register %s input device\n", ts->input_dev->name); + goto err_input_register_device_failed; + } + if (client->irq) { + ret = request_irq(client->irq, synaptics_ts_irq_handler, irqflags, client->name, ts); + if (ret == 0) { + ret = i2c_smbus_write_byte_data(ts->client, 0xf1, 0x01); /* enable abs int */ + if (ret) + free_irq(client->irq, ts); + } + if (ret == 0) + ts->use_irq = 1; + else + dev_err(&client->dev, "request_irq failed\n"); + } + if (!ts->use_irq) { + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = synaptics_ts_timer_func; + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + } +#ifdef CONFIG_HAS_EARLYSUSPEND + ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ts->early_suspend.suspend = synaptics_ts_early_suspend; + ts->early_suspend.resume = synaptics_ts_late_resume; + register_early_suspend(&ts->early_suspend); +#endif + + printk(KERN_INFO "synaptics_ts_probe: Start touchscreen %s in %s mode\n", ts->input_dev->name, ts->use_irq ? "interrupt" : "polling"); + + return 0; + +err_input_register_device_failed: + input_free_device(ts->input_dev); + +err_input_dev_alloc_failed: +err_detect_failed: +err_power_failed: + kfree(ts); +err_alloc_data_failed: +err_check_functionality_failed: + return ret; +} + +static int synaptics_ts_remove(struct i2c_client *client) +{ + struct synaptics_ts_data *ts = i2c_get_clientdata(client); + unregister_early_suspend(&ts->early_suspend); + if (ts->use_irq) + free_irq(client->irq, ts); + else + hrtimer_cancel(&ts->timer); + input_unregister_device(ts->input_dev); + kfree(ts); + return 0; +} + +static int synaptics_ts_suspend(struct i2c_client *client, pm_message_t mesg) +{ + int ret; + struct synaptics_ts_data *ts = i2c_get_clientdata(client); + + if (ts->use_irq) + disable_irq(client->irq); + else + hrtimer_cancel(&ts->timer); + ret = cancel_work_sync(&ts->work); + if (ret && ts->use_irq) /* if work was pending disable-count is now 2 */ + enable_irq(client->irq); + ret = i2c_smbus_write_byte_data(ts->client, 0xf1, 0); /* disable interrupt */ + if (ret < 0) + printk(KERN_ERR "synaptics_ts_suspend: i2c_smbus_write_byte_data failed\n"); + + ret = i2c_smbus_write_byte_data(client, 0xf0, 0x86); /* deep sleep */ + if (ret < 0) + printk(KERN_ERR "synaptics_ts_suspend: i2c_smbus_write_byte_data failed\n"); + if (ts->power) { + ret = ts->power(0); + if (ret < 0) + printk(KERN_ERR "synaptics_ts_resume power off failed\n"); + } + return 0; +} + +static int synaptics_ts_resume(struct i2c_client *client) +{ + int ret; + struct synaptics_ts_data *ts = i2c_get_clientdata(client); + + if (ts->power) { + ret = ts->power(1); + if (ret < 0) + printk(KERN_ERR "synaptics_ts_resume power on failed\n"); + } + + synaptics_init_panel(ts); + + if (ts->use_irq) + enable_irq(client->irq); + + if (!ts->use_irq) + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + else + i2c_smbus_write_byte_data(ts->client, 0xf1, 0x01); /* enable abs int */ + + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void synaptics_ts_early_suspend(struct early_suspend *h) +{ + struct synaptics_ts_data *ts; + ts = container_of(h, struct synaptics_ts_data, early_suspend); + synaptics_ts_suspend(ts->client, PMSG_SUSPEND); +} + +static void synaptics_ts_late_resume(struct early_suspend *h) +{ + struct synaptics_ts_data *ts; + ts = container_of(h, struct synaptics_ts_data, early_suspend); + synaptics_ts_resume(ts->client); +} +#endif + +static const struct i2c_device_id synaptics_ts_id[] = { + { SYNAPTICS_I2C_RMI_NAME, 0 }, + { } +}; + +static struct i2c_driver synaptics_ts_driver = { + .probe = synaptics_ts_probe, + .remove = synaptics_ts_remove, +#ifndef CONFIG_HAS_EARLYSUSPEND + .suspend = synaptics_ts_suspend, + .resume = synaptics_ts_resume, +#endif + .id_table = synaptics_ts_id, + .driver = { + .name = SYNAPTICS_I2C_RMI_NAME, + }, +}; + +static int __devinit synaptics_ts_init(void) +{ + synaptics_wq = create_singlethread_workqueue("synaptics_wq"); + if (!synaptics_wq) + return -ENOMEM; + return i2c_add_driver(&synaptics_ts_driver); +} + +static void __exit synaptics_ts_exit(void) +{ + i2c_del_driver(&synaptics_ts_driver); + if (synaptics_wq) + destroy_workqueue(synaptics_wq); +} + +module_init(synaptics_ts_init); +module_exit(synaptics_ts_exit); + +MODULE_DESCRIPTION("Synaptics Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/tscp2007.c b/drivers/input/touchscreen/tscp2007.c new file mode 100644 index 00000000..5fe2e0ba --- /dev/null +++ b/drivers/input/touchscreen/tscp2007.c @@ -0,0 +1,409 @@ +/* + * drivers/input/touchscreen/tscp2007.c + * + * Copyright (c) 2008 MtekVision Co., Ltd. + * Kwangwoo Lee + * + * Using code from: + * - ads7846.c + * Copyright (c) 2005 David Brownell + * Copyright (c) 2006 Nokia Corporation + * - corgi_ts.c + * Copyright (C) 2004-2005 Richard Purdie + * - omap_ts.[hc], ads7846.h, ts_osk.c + * Copyright (C) 2002 MontaVista Software + * Copyright (C) 2004 Texas Instruments + * Copyright (C) 2005 Dirk Behme + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + + +#define tscp2007_MEASURE_TEMP0 (0x0 << 4) +#define tscp2007_MEASURE_AUX (0x2 << 4) +#define tscp2007_MEASURE_TEMP1 (0x4 << 4) +#define tscp2007_ACTIVATE_XN (0x8 << 4) +#define tscp2007_ACTIVATE_YN (0x9 << 4) +#define tscp2007_ACTIVATE_YP_XN (0xa << 4) +#define tscp2007_SETUP (0xb << 4) +#define tscp2007_MEASURE_X (0xc << 4) +#define tscp2007_MEASURE_Y (0xd << 4) +#define tscp2007_MEASURE_Z1 (0xe << 4) +#define tscp2007_MEASURE_Z2 (0xf << 4) + +#define tscp2007_POWER_OFF_IRQ_EN (0x0 << 2) +#define tscp2007_ADC_ON_IRQ_DIS0 (0x1 << 2) +#define tscp2007_ADC_OFF_IRQ_EN (0x2 << 2) +#define tscp2007_ADC_ON_IRQ_DIS1 (0x3 << 2) + +#define tscp2007_12BIT (0x0 << 1) +#define tscp2007_8BIT (0x1 << 1) + +#define MAX_12BIT ((1 << 12) - 1) + +#define ADC_ON_12BIT (tscp2007_12BIT | tscp2007_ADC_ON_IRQ_DIS0) + +#define READ_Y (ADC_ON_12BIT | tscp2007_MEASURE_Y) +#define READ_Z1 (ADC_ON_12BIT | tscp2007_MEASURE_Z1) +#define READ_Z2 (ADC_ON_12BIT | tscp2007_MEASURE_Z2) +#define READ_X (ADC_ON_12BIT | tscp2007_MEASURE_X) +#define PWRDOWN (tscp2007_12BIT | tscp2007_POWER_OFF_IRQ_EN) + +struct ts_event { + u16 x; + u16 y; + u16 z1, z2; +}; + +struct tscp2007 { + struct input_dev *input; + char phys[32]; + + struct i2c_client *client; + + u16 model; + u16 x_plate_ohms; + u16 max_rt; + unsigned long poll_delay; + unsigned long poll_period; + + int irq; + + wait_queue_head_t wait; + bool stopped; + + unsigned int intpin; + int (*get_pendown_state)(unsigned int pin); + void (*clear_penirq)(void); +}; + +static inline int tscp2007_xfer(struct tscp2007 *tsc, u8 cmd) +{ + s32 data; + u16 val; + + data = i2c_smbus_read_word_data(tsc->client, cmd); + if (data < 0) { + dev_err(&tsc->client->dev, "i2c io error: %d\n", data); + return data; + } + + /* The protocol and raw data format from i2c interface: + * S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P + * Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit]. + */ + val = swab16(data) >> 4; + + dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val); + + return val; +} + +static void tscp2007_read_values(struct tscp2007 *tsc, struct ts_event *tc) +{ + /* y- still on; turn on only y+ (and ADC) */ + tc->y = tscp2007_xfer(tsc, READ_Y); + + /* turn y- off, x+ on, then leave in lowpower */ + tc->x = tscp2007_xfer(tsc, READ_X); + + /* turn y+ off, x- on; we'll use formula #1 */ + tc->z1 = tscp2007_xfer(tsc, READ_Z1); + tc->z2 = tscp2007_xfer(tsc, READ_Z2); + + /* Prepare for next touch reading - power down ADC, enable PENIRQ */ + tscp2007_xfer(tsc, PWRDOWN); +} + +static u32 tscp2007_calculate_pressure(struct tscp2007 *tsc, struct ts_event *tc) +{ + u32 rt = 0; + + /* range filtering */ + if (tc->x == MAX_12BIT) + tc->x = 0; + + if (likely(tc->x && tc->z1)) { + /* compute touch pressure resistance using equation #1 */ + rt = tc->z2 - tc->z1; + rt *= tc->x; + rt *= tsc->x_plate_ohms; + rt /= tc->z1; + rt = (rt + 2047) >> 12; + } + + return rt; +} + +static bool tscp2007_is_pen_down(struct tscp2007 *ts) +{ + /* + * NOTE: We can't rely on the pressure to determine the pen down + * state, even though this controller has a pressure sensor. + * The pressure value can fluctuate for quite a while after + * lifting the pen and in some cases may not even settle at the + * expected value. + * + * The only safe way to check for the pen up condition is in the + * work function by reading the pen signal state (it's a GPIO + * and IRQ). Unfortunately such callback is not always available, + * in that case we assume that the pen is down and expect caller + * to fall back on the pressure reading. + */ + + if (!ts->get_pendown_state) + return true; + + return ts->get_pendown_state(ts->intpin); +} + +static irqreturn_t tscp2007_soft_irq(int irq, void *handle) +{ + struct tscp2007 *ts = handle; + struct input_dev *input = ts->input; + struct ts_event tc; + u32 rt; + + while (!ts->stopped && tscp2007_is_pen_down(ts)) { + + /* pen is down, continue with the measurement */ + tscp2007_read_values(ts, &tc); + + rt = tscp2007_calculate_pressure(ts, &tc); + + if (rt == 0 && !ts->get_pendown_state) { + /* + * If pressure reported is 0 and we don't have + * callback to check pendown state, we have to + * assume that pen was lifted up. + */ + break; + } + + if (rt <= ts->max_rt) { + dev_dbg(&ts->client->dev, + "DOWN point(%4d,%4d), pressure (%4u)\n", + tc.x, tc.y, rt); + + input_report_key(input, BTN_TOUCH, 1); + input_report_abs(input, ABS_X, tc.x); + input_report_abs(input, ABS_Y, tc.y); + input_report_abs(input, ABS_PRESSURE, rt); + + input_sync(input); + + } else { + /* + * Sample found inconsistent by debouncing or pressure is + * beyond the maximum. Don't report it to user space, + * repeat at least once more the measurement. + */ + dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt); + } + + wait_event_timeout(ts->wait, ts->stopped, + msecs_to_jiffies(ts->poll_period)); + } + + dev_dbg(&ts->client->dev, "UP\n"); + + input_report_key(input, BTN_TOUCH, 0); + input_report_abs(input, ABS_PRESSURE, 0); + input_sync(input); + + if (ts->clear_penirq) + ts->clear_penirq(); + + return IRQ_HANDLED; +} + +static irqreturn_t tscp2007_hard_irq(int irq, void *handle) +{ + struct tscp2007 *ts = handle; + + if (!ts->get_pendown_state || likely(ts->get_pendown_state(ts->intpin))) + return IRQ_WAKE_THREAD; + + if (ts->clear_penirq) + ts->clear_penirq(); + + return IRQ_HANDLED; +} + +static void tscp2007_stop(struct tscp2007 *ts) +{ + ts->stopped = true; + mb(); + wake_up(&ts->wait); + + disable_irq(ts->irq); +} + +static int tscp2007_open(struct input_dev *input_dev) +{ + struct tscp2007 *ts = input_get_drvdata(input_dev); + int err; + + ts->stopped = false; + mb(); + + enable_irq(ts->irq); + + /* Prepare for touch readings - power down ADC and enable PENIRQ */ + err = tscp2007_xfer(ts, PWRDOWN); + if (err < 0) { + tscp2007_stop(ts); + return err; + } + + return 0; +} + +static void tscp2007_close(struct input_dev *input_dev) +{ + struct tscp2007 *ts = input_get_drvdata(input_dev); + + tscp2007_stop(ts); +} + +static int __devinit tscp2007_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tscp2007 *ts; + struct tscp2007_platform_data *pdata = client->dev.platform_data; + struct input_dev *input_dev; + int err; + + if (!pdata) { + dev_err(&client->dev, "platform data is required!\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -EIO; + + ts = kzalloc(sizeof(struct tscp2007), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + ts->client = client; + ts->irq = client->irq; + ts->input = input_dev; + init_waitqueue_head(&ts->wait); + + ts->model = pdata->model; + ts->x_plate_ohms = pdata->x_plate_ohms; + ts->max_rt = pdata->max_rt ? : MAX_12BIT; + ts->poll_delay = pdata->poll_delay ? : 1; + ts->poll_period = pdata->poll_period ? : 1; + ts->get_pendown_state = pdata->get_pendown_state; + ts->clear_penirq = pdata->clear_penirq; + ts->intpin = pdata->intpin_info.pin; + + if (pdata->x_plate_ohms == 0) { + dev_err(&client->dev, "x_plate_ohms is not set up in platform data"); + err = -EINVAL; + goto err_free_mem; + } + + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&client->dev)); + + input_dev->name = "tscp2007 Touchscreen"; + input_dev->phys = ts->phys; + input_dev->id.bustype = BUS_I2C; + + input_dev->open = tscp2007_open; + input_dev->close = tscp2007_close; + + input_set_drvdata(input_dev, ts); + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, pdata->fuzzx, 0); + input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, pdata->fuzzy, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, + pdata->fuzzz, 0); + + if (pdata->init_platform_hw) + pdata->init_platform_hw(&pdata->intpin_info); + + err = request_threaded_irq(ts->irq, tscp2007_hard_irq, tscp2007_soft_irq, + IRQF_ONESHOT, client->dev.driver->name, ts); + if (err < 0) { + dev_err(&client->dev, "irq %d busy?\n", ts->irq); + goto err_free_mem; + } + + tscp2007_stop(ts); + + err = input_register_device(input_dev); + if (err) + goto err_free_irq; + + i2c_set_clientdata(client, ts); + + return 0; + + err_free_irq: + free_irq(ts->irq, ts); + if (pdata->exit_platform_hw) + pdata->exit_platform_hw(); + err_free_mem: + input_free_device(input_dev); + kfree(ts); + return err; +} + +static int __devexit tscp2007_remove(struct i2c_client *client) +{ + struct tscp2007 *ts = i2c_get_clientdata(client); + struct tscp2007_platform_data *pdata = client->dev.platform_data; + + free_irq(ts->irq, ts); + + if (pdata->exit_platform_hw) + pdata->exit_platform_hw(); + + input_unregister_device(ts->input); + kfree(ts); + + return 0; +} + +static const struct i2c_device_id tscp2007_idtable[] = { + { "tscp2007", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, tscp2007_idtable); + +static struct i2c_driver tscp2007_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tscp2007" + }, + .id_table = tscp2007_idtable, + .probe = tscp2007_probe, + .remove = __devexit_p(tscp2007_remove), +}; + +module_i2c_driver(tscp2007_driver); + +MODULE_AUTHOR("Kwangwoo Lee "); +MODULE_DESCRIPTION("tscp2007 TouchScreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index ff4b8cfd..19820ee5 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -289,6 +289,12 @@ config LEDS_BD2802 This option enables support for BD2802GU RGB LED driver chips accessed via the I2C bus. +config LEDS_AK39 + tristate "LED Support for the AK39 LEDS" + depends on LEDS_CLASS && ARCH_AK39 + help + This option enables support for the AK39 LEDS + config LEDS_INTEL_SS4200 tristate "LED driver for Intel NAS SS4200 series" depends on LEDS_CLASS @@ -468,6 +474,12 @@ config LEDS_TRIGGER_DEFAULT_ON This allows LEDs to be initialised in the ON state. If unsure, say Y. +config LEDS_TRIGGER_SLEEP + tristate "LED Sleep Mode Trigger" + depends on LEDS_TRIGGERS && HAS_EARLYSUSPEND + help + This turns LEDs on when the screen is off but the cpu still running. + comment "iptables trigger is under Netfilter config (LED target)" depends on LEDS_TRIGGERS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 890481cb..b054e8a7 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o obj-$(CONFIG_LEDS_RENESAS_TPU) += leds-renesas-tpu.o obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o +obj-$(CONFIG_LEDS_AK39) += leds-ak39.o # LED SPI Drivers obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o @@ -56,3 +57,4 @@ obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledtrig-gpio.o obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o +obj-$(CONFIG_LEDS_TRIGGER_SLEEP) += ledtrig-sleep.o diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index 46b4c766..a85ce094 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -102,6 +102,12 @@ EXPORT_SYMBOL_GPL(led_trigger_show); void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger) { unsigned long flags; + char *event = NULL; + char *envp[2]; + const char *name; + + name = trigger ? trigger->name : "none"; + event = kasprintf(GFP_KERNEL, "TRIGGER=%s", name); /* Remove any existing trigger */ if (led_cdev->trigger) { @@ -122,6 +128,13 @@ void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger) if (trigger->activate) trigger->activate(led_cdev); } + + if (event) { + envp[0] = event; + envp[1] = NULL; + kobject_uevent_env(&led_cdev->dev->kobj, KOBJ_CHANGE, envp); + kfree(event); + } } EXPORT_SYMBOL_GPL(led_trigger_set); diff --git a/drivers/leds/leds-ak39.c b/drivers/leds/leds-ak39.c new file mode 100644 index 00000000..3539424b --- /dev/null +++ b/drivers/leds/leds-ak39.c @@ -0,0 +1,126 @@ +/* drivers/leds/leds-ak98-factory.c + * + * Copyright (c) 2012 anyka + * + * AK98- LEDs GPIO driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct ak_led_instance { + struct led_classdev cdev; + struct ak_led_data *led; +}; + +struct ak_led_class { + struct ak_led_instance *instance; + int nr_instance; +}; + +static void ak_led_brightness_set(struct led_classdev *led_cdev, enum led_brightness value) +{ + unsigned int active_level = 0; + + struct ak_led_instance *instance = container_of(led_cdev, struct ak_led_instance, cdev); + struct ak_led_data *led = instance->led; + + active_level = (value ? led->effective_level : + (led->effective_level == AK_GPIO_OUT_HIGH ? AK_GPIO_OUT_LOW : AK_GPIO_OUT_HIGH)); + ak_gpio_setpin(led->gpio.pin, active_level); +} + +static int ak_led_remove(struct platform_device *dev) +{ + int i; + struct ak_led_class *class = platform_get_drvdata(dev); + struct ak_led_instance *instance = class->instance; + + for (i = 0; i < class->nr_instance; i++) + led_classdev_unregister(&(instance + i)->cdev); + + kfree(instance); + kfree(class); + + return 0; +} + +static int ak_led_probe(struct platform_device *dev) +{ + int i, ret; + struct ak_led_pdata *pdata = dev->dev.platform_data; + struct ak_led_data *leds = pdata->leds; + struct ak_led_instance *instance; + struct ak_led_class *class; + + class = kzalloc(sizeof(struct ak_led_class), GFP_KERNEL); + instance = kzalloc(sizeof(struct ak_led_instance)*(pdata->nr_led), GFP_KERNEL); + if (!class || !instance) { + dev_err(&dev->dev, "No memory for device\n"); + return -ENOMEM; + } + + class->instance = instance; + class->nr_instance = pdata->nr_led; + platform_set_drvdata(dev, class); + + for (i = 0; i < class->nr_instance; i++) { + /* Default GPIO configure according to board defined */ + ak_gpio_set(&((leds + i)->gpio)); + (instance + i)->cdev.name = (leds + i)->name; + (instance + i)->cdev.default_trigger = (leds + i)->def_trigger; + (instance + i)->cdev.brightness_set = ak_led_brightness_set; + (instance + i)->cdev.flags |= LED_CORE_SUSPENDRESUME; + (instance + i)->led = (leds + i); + + ret = led_classdev_register(&dev->dev, &(instance + i)->cdev); + if (ret < 0) { + dev_err(&dev->dev, "led_classdev_register failed\n"); + kfree(instance); + kfree(class); + return ret; + } + } + + return 0; +} + +static struct platform_driver ak_led_driver = { + .probe = ak_led_probe, + .remove = ak_led_remove, + .driver = { + .name = "ak_led", + .owner = THIS_MODULE, + }, +}; + +static int __init ak_led_init(void) +{ + return platform_driver_register(&ak_led_driver); +} + +static void __exit ak_led_exit(void) +{ + platform_driver_unregister(&ak_led_driver); +} + +module_init(ak_led_init); +module_exit(ak_led_exit); + +MODULE_AUTHOR("Hongguang Du "); +MODULE_DESCRIPTION("AK LED driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ak_led"); diff --git a/drivers/leds/ledtrig-sleep.c b/drivers/leds/ledtrig-sleep.c new file mode 100644 index 00000000..f1640421 --- /dev/null +++ b/drivers/leds/ledtrig-sleep.c @@ -0,0 +1,80 @@ +/* drivers/leds/ledtrig-sleep.c + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include + +static int ledtrig_sleep_pm_callback(struct notifier_block *nfb, + unsigned long action, + void *ignored); + +DEFINE_LED_TRIGGER(ledtrig_sleep) +static struct notifier_block ledtrig_sleep_pm_notifier = { + .notifier_call = ledtrig_sleep_pm_callback, + .priority = 0, +}; + +static void ledtrig_sleep_early_suspend(struct early_suspend *h) +{ + led_trigger_event(ledtrig_sleep, LED_FULL); +} + +static void ledtrig_sleep_early_resume(struct early_suspend *h) +{ + led_trigger_event(ledtrig_sleep, LED_OFF); +} + +static struct early_suspend ledtrig_sleep_early_suspend_handler = { + .suspend = ledtrig_sleep_early_suspend, + .resume = ledtrig_sleep_early_resume, +}; + +static int ledtrig_sleep_pm_callback(struct notifier_block *nfb, + unsigned long action, + void *ignored) +{ + switch (action) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + led_trigger_event(ledtrig_sleep, LED_OFF); + return NOTIFY_OK; + case PM_POST_HIBERNATION: + case PM_POST_SUSPEND: + led_trigger_event(ledtrig_sleep, LED_FULL); + return NOTIFY_OK; + } + + return NOTIFY_DONE; +} + +static int __init ledtrig_sleep_init(void) +{ + led_trigger_register_simple("sleep", &ledtrig_sleep); + register_pm_notifier(&ledtrig_sleep_pm_notifier); + register_early_suspend(&ledtrig_sleep_early_suspend_handler); + return 0; +} + +static void __exit ledtrig_sleep_exit(void) +{ + unregister_early_suspend(&ledtrig_sleep_early_suspend_handler); + unregister_pm_notifier(&ledtrig_sleep_pm_notifier); + led_trigger_unregister_simple(ledtrig_sleep); +} + +module_init(ledtrig_sleep_init); +module_exit(ledtrig_sleep_exit); + diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index ce1e7ba9..2ce8bf66 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -1057,6 +1057,14 @@ config SOC_CAMERA_OV9740 help This is a ov9740 camera driver +menuconfig LINUX_AKSENSOR + tristate "aksensor support" + depends on SOC_CAMERA && I2C + help + This is a aksensor driver + +source "drivers/media/video/plat-anyka/Kconfig" + config MX1_VIDEO bool @@ -1115,6 +1123,26 @@ config VIDEO_OMAP2 ---help--- This is a v4l2 driver for the TI OMAP2 camera capture interface +config VIDEO_AK + tristate "AK Camera Interface driver" + depends on VIDEO_DEV && SOC_CAMERA + ---help--- + This is a v4l2 driver for the AK Platform Camera Interface + +config AK_SENSOR_I2C + depends on VIDEO_AK + bool "ak sensor i2c" + default y + ---help--- + Ak sensor i2c interface + +config AK_VIDEOBUF_DMA_CONTIG + bool "VIDEOBUF_DMA_CONTIG" + select VIDEOBUF_DMA_CONTIG + default y + ---help--- + VIDEOBUF_DMA_CONTIG for AK Camera + config VIDEO_MX2_HOSTSUPPORT bool diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index a6282a3a..572a2fc9 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -96,6 +96,7 @@ obj-$(CONFIG_SOC_CAMERA_OV9640) += ov9640.o obj-$(CONFIG_SOC_CAMERA_OV9740) += ov9740.o obj-$(CONFIG_SOC_CAMERA_RJ54N1) += rj54n1cb0c.o obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o +obj-y += plat-anyka/ # And now the v4l2 drivers: diff --git a/drivers/media/video/plat-anyka/Kconfig b/drivers/media/video/plat-anyka/Kconfig new file mode 100644 index 00000000..000d3c65 --- /dev/null +++ b/drivers/media/video/plat-anyka/Kconfig @@ -0,0 +1,107 @@ +config AK_SENSOR_I2C + bool "ak sensor i2c interface" + default y + help + This is a sensor i2c interface + +config AK_SENSOR_COMMON + bool "ak sensor common interface" + default y + help + This is sensor common interface + +config SENSOR_AR0130 + tristate "ar0130 sensor support" + depends on LINUX_AKSENSOR + help + This is a ar0130 sensor driver + +config SENSOR_BF3703 + tristate "bf3703 sensor support" + depends on LINUX_AKSENSOR + help + This is a bf3703 sensor driver + +config SENSOR_F22_F23_F28_F35_F37 + tristate "f22 f23 f35 f37 sensor support" + depends on LINUX_AKSENSOR + help + This is a sensor driver amongst model f22 f23 f28 f35 f37 + +config SENSOR_GC1054 + tristate "gc1054 sensor support" + depends on LINUX_AKSENSOR + help + This is a gc1054 sensor driver + +config SENSOR_GC2053 + tristate "gc2053 sensor support" + depends on LINUX_AKSENSOR + help + This is a gc2053 sensor driver + +config SENSOR_H63 + tristate "H63 sensor support" + depends on LINUX_AKSENSOR + help + This is a H63 sensor driver + +config SENSOR_H65 + tristate "h65 sensor support" + depends on LINUX_AKSENSOR + help + This is a h65 sensor driver + +config SENSOR_IMX307 + tristate "imx307 sensor support" + depends on LINUX_AKSENSOR + help + This is a imx307 sensor driver + +config SENSOR_PS5260 + tristate "ps5260 sensor support" + depends on LINUX_AKSENSOR + help + This is a ps5260 sensor driver + +config SENSOR_SC1235 + tristate "sc1235 sensor support" + depends on LINUX_AKSENSOR + help + This is a sc1235 sensor driver + +config SENSOR_SC1245 + tristate "sc1245 sensor support" + depends on LINUX_AKSENSOR + help + This is a sc1245 sensor driver + +config SENSOR_SC2232H_SC2135E + tristate "sc2232h & sc2135e sensor support" + depends on LINUX_AKSENSOR + help + This is a sc2232h & sc2135e sensor driver + +config SENSOR_SC2235 + tristate "sc2235 sensor support" + depends on LINUX_AKSENSOR + help + This is a sc2235 sensor driver + +config SENSOR_SC2239_SC2332_SC2335 + tristate "SC2239 SC2332 SC2335 SCsensor support" + depends on LINUX_AKSENSOR + help + This is a SC2239 SC2332 SC2335 sensor driver + +config SENSOR_SP2305 + tristate "sc2305 sensor support" + depends on LINUX_AKSENSOR + help + This is a sc2305 sensor driver + + + + + + diff --git a/drivers/media/video/plat-anyka/Makefile b/drivers/media/video/plat-anyka/Makefile new file mode 100644 index 00000000..65eac52a --- /dev/null +++ b/drivers/media/video/plat-anyka/Makefile @@ -0,0 +1,43 @@ +obj-$(CONFIG_VIDEO_AK) := akcamera.o +akcamera-objs += ak_camera.o ak_sensor.o + +obj-$(CONFIG_AK_SENSOR_I2C) += ak_sensor_i2c.o + +obj-$(CONFIG_AK_SENSOR_COMMON) += ak_sensor_common.o + +# sensor +obj-$(CONFIG_SENSOR_AR0130) += sensor_ar0130.o + +obj-$(CONFIG_SENSOR_BF3703) += sensor_bf3703.o + +obj-$(CONFIG_SENSOR_F22_F23_F28_F35_F37) += sensor_f22+f23+f28+f35+f37.o + +obj-$(CONFIG_SENSOR_GC1054) += sensor_gc1054.o + +obj-$(CONFIG_SENSOR_GC2053) += sensor_gc2053.o + +obj-$(CONFIG_SENSOR_H63) += sensor_H63.o + +obj-$(CONFIG_SENSOR_H65) += sensor_h65.o + +obj-$(CONFIG_SENSOR_IMX307) += sensor_imx307.o + +obj-$(CONFIG_SENSOR_PS5260) += sensor_ps5260.o + +obj-$(CONFIG_SENSOR_SC1235) += sensor_sc1235.o + +obj-$(CONFIG_SENSOR_SC1245) += sensor_sc1245.o + +obj-$(CONFIG_SENSOR_SC2232H_SC2135E) += sensor_sc2232h_sc2135e.o + +obj-$(CONFIG_SENSOR_SC2235) += sensor_sc2235.o + +obj-$(CONFIG_SENSOR_SC2239_SC2332_SC2335) += sensor_SC2239+SC2332+SC2335.o + +obj-$(CONFIG_SENSOR_SP2305) += sensor_sp2305.o + + + + + + diff --git a/drivers/media/video/plat-anyka/ak_camera.c b/drivers/media/video/plat-anyka/ak_camera.c new file mode 100644 index 00000000..8e6847c1 --- /dev/null +++ b/drivers/media/video/plat-anyka/ak_camera.c @@ -0,0 +1,2359 @@ +/* + * @file ak camera.c + * @camera host driver for ak + * @Copyright (C) 2010 Anyka (Guangzhou) Microelectronics Technology Co + * @author wu_daochao + * @date 2011-04 + * @version + * @for more information , please refer to AK980x Programmer's Guide Mannul + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +//#define CAMIF_DEBUG +#ifdef CAMIF_DEBUG +#define isp_dbg(fmt...) printk(KERN_INFO " ISP: " fmt) +#define CAMDBG(fmt...) printk(KERN_INFO " ISP: " fmt)//do{}while(0) +#else +#define CAMDBG(fmt...) do{}while(0) +#define isp_dbg(fmt, args...) do{}while(0) +#endif + +enum buffer_list_state { + LIST_ZERO = 1, + LIST_ONE, + LIST_TWO, + LIST_THREE, + LIST_FOUR, + LIST_FIVE, +}; + +struct ak_buffer { + struct videobuf_buffer vb; + enum v4l2_mbus_pixelcode code; + int inwork; +}; + +struct ak_camera_dev { + struct soc_camera_host soc_host; + struct soc_camera_device *icd; + struct ak_camera_pdata *pdata; + + struct clk *clk; // camera controller clk. it's parent is vclk defined in clock.c + struct clk *cis_sclk; // cis_sclk clock for sensor + struct clk *mipi_clk; + unsigned int irq; + struct list_head capture; + /* members to manage the dma and buffer*/ + spinlock_t lock; /* for videobuf_queue , passed in init_videobuf */ + + enum isp_working_mode def_mode; + enum isp_working_mode cur_mode; + enum v4l2_mbus_pixelcode cur_mode_class; + + struct videobuf_queue *vq; + enum buffer_list_state list_state; + enum buffer_list_state free_list; + + struct delayed_work awb_work; + struct delayed_work ae_work; + struct delayed_work af_work; + + int stream_ctrl_off; + int cur_buf_id; + + int crop_x; + int crop_y; + int crop_width; + int crop_height; + int tmp_raw; + int tmp_raw_buf_id; + int app_capture_a_rawdata; + + int td_reset_count; + int td_reset_frames; +}; + +struct ak_camera_cam { + /* Client output, as seen by the CEU */ + unsigned int width; + unsigned int height; +}; + +#define ISP_TIMEOUT (1) //unit: s +#define EMPTY_FRAME_NUM (2) + +#define RAW_HEADER_SIZE 128 + +static const char *ak_cam_driver_description = "AK_Camera"; + +static int _tdnr_flag = 0; +static int _tdnr_set = 0; +static int video_frame_interval; + +static unsigned long in_irq_jf = 0; + +AK_ISP_SENSOR_CB *ak_sensor_get_sensor_cb(void); + +/** + * @brief: for ak_videobuf_release, free buffer if camera stopped. + * + * @author: caolianming + * @date: 2014-01-06 + * @param [in] *vq: V4L2 buffer queue information structure + * @param [in] *buf: ak camera drivers structure, include struct videobuf_buffer + */ +static void free_buffer(struct videobuf_queue *vq, struct ak_buffer *buf) +{ + unsigned long flags; + struct soc_camera_device *icd = vq->priv_data; + struct videobuf_buffer *vb = &buf->vb; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct ak_camera_dev *pcdev = ici->priv; + + isp_dbg("%s (vb=0x%p) buf[%d] 0x%08lx %d\n", + __func__, vb, vb->i, vb->baddr, vb->bsize); + + BUG_ON(in_interrupt()); + + /* This waits until this buffer is out of danger, i.e., until it is no + * longer in STATE_QUEUED or STATE_ACTIVE */ + if (vb->state == VIDEOBUF_ACTIVE && pcdev->list_state == LIST_ZERO) { + printk("free_buffer: list_state=%d, doesn't neee to wait\n", pcdev->list_state); + //vb->state = VIDEOBUF_ERROR; + list_del(&vb->queue); + } else { + vb->state = VIDEOBUF_DONE; + videobuf_waiton(vq, vb, 0, 0); + } + + if (vq->streaming == 0 && vq->reading == 0) { + spin_lock_irqsave(&pcdev->lock, flags); + pcdev->list_state = LIST_ZERO; + pcdev->free_list = LIST_ZERO; + spin_unlock_irqrestore(&pcdev->lock, flags); + + if (pcdev->stream_ctrl_off == 0) + ispdrv_set_isp_pause(); + + if (!list_empty(&pcdev->capture)) { + list_del_init(&pcdev->capture); + } + printk("%s.\n",__func__); + } + + videobuf_dma_contig_free(vq, vb); + + vb->state = VIDEOBUF_NEEDS_INIT; +} + +/** + * @brief: Called when application apply buffers, camera buffer initial. + * + * @author: caolianming + * @date: 2014-01-06 + * @param [in] *vq: V4L2 buffer queue information structure + * @param [in] *count: buffer's number + * @param [in] *size: buffer total size + */ +static int ak_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct soc_camera_device *icd = vq->priv_data; + int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, + icd->current_fmt->host_fmt); + + bytes_per_line = icd->user_width * 3 /2; + if (bytes_per_line < 0) + return bytes_per_line; + + *size = bytes_per_line * icd->user_height; + //printk(KERN_ERR "%s size:%u, bytes_per_line:%d, icd->user_height:%d\n", __func__, *size, bytes_per_line, icd->user_height); + + if (*count < 3) { + printk("if use video mode, vbuf num isn't less than 3\n"); + *count = 3; + } + + if (*size * *count > CONFIG_VIDEO_RESERVED_MEM_SIZE) + *count = (CONFIG_VIDEO_RESERVED_MEM_SIZE) / *size; + + isp_dbg("%s count=%d, size=%d, bytes_per_line=%d\n", + __func__, *count, *size, bytes_per_line); + + return 0; +} + +/** + * @brief: Called when application apply buffers, camera buffer initial. + * + * @author: caolianming + * @date: 2014-01-06 + * @param [in] *vq: V4L2 buffer queue information structure + * @param [in] *vb: V4L2 buffer information structure + * @param [in] field: V4L2_FIELD_ANY + */ +static int ak_videobuf_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, enum v4l2_field field) +{ + struct soc_camera_device *icd = vq->priv_data; + struct ak_buffer *buf = container_of(vb, struct ak_buffer, vb); + int ret; + int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, + icd->current_fmt->host_fmt); + + isp_dbg("%s (vb=0x%p) buf[%d] vb->baddr=0x%08lx vb->bsize=%d bytes_per_line=%d\n", + __func__, vb, vb->i, vb->baddr, vb->bsize, bytes_per_line); + + bytes_per_line = icd->user_width * 3 /2; + + if (bytes_per_line < 0) + return bytes_per_line; + + /* Added list head initialization on alloc */ + WARN_ON(!list_empty(&vb->queue)); + +#if 0 +//#ifdef ISP_DEBUG + /* + * This can be useful if you want to see if we actually fill + * the buffer with something + */ + memset((void *)vb->baddr, 0xaa, vb->bsize); +#endif + + BUG_ON(NULL == icd->current_fmt); + + /* I think, in buf_prepare you only have to protect global data, + * the actual buffer is yours */ + buf->inwork = 1; + + if (buf->code != icd->current_fmt->code || + vb->width != icd->user_width || + vb->height != icd->user_height || + vb->field != field) { + buf->code = icd->current_fmt->code; + vb->width = icd->user_width; + vb->height = icd->user_height; + vb->field = field; + vb->state = VIDEOBUF_NEEDS_INIT; + } + + //vb->size = bytes_per_line * vb->height; + vb->size = bytes_per_line * icd->user_height; + if (0 != vb->baddr && vb->bsize < vb->size) { + ret = -EINVAL; + goto out; + } + + if (vb->state == VIDEOBUF_NEEDS_INIT) { + ret = videobuf_iolock(vq, vb, NULL); + if (ret) + goto fail; + + vb->state = VIDEOBUF_PREPARED; + } + + buf->inwork = 0; + + return 0; + +fail: + free_buffer(vq, buf); +out: + buf->inwork = 0; + return ret; +} + +static int wait_for_1st_buffer_in_driver(struct ak_camera_dev *pcdev, int timeout) +{ + struct ak_buffer *ak_buf; + struct videobuf_buffer *vb_1st; + int get_1st = 0; + + do { + list_for_each_entry(ak_buf, &pcdev->capture, vb.queue) { + vb_1st = &ak_buf->vb; + if (vb_1st->i == 0) { + pr_err("this is 1st vb\n"); + get_1st = 1; + break; + } + } + + if (get_1st) + break; + + msleep(1); + } while (timeout-- > 0); + + if (!get_1st) { + pr_err("wait for 1st in driver fail\n"); + return -EBUSY; + } + + pr_err("wait for 1st in driver success\n"); + return 0; +} + +static int add_rawdata_header(struct ak_camera_dev *pcdev, struct videobuf_buffer *vb_raw) +{ + unsigned long paddr; + void *vaddr; + unsigned char *p; + int raw_size; + int raw_bitsw; + int i; + + raw_bitsw = ispdrv_vo_get_inputdataw(); + raw_size = raw_bitsw * pcdev->crop_width * pcdev->crop_height / 8; + + paddr = videobuf_to_dma_contig(vb_raw); + vaddr = ioremap_nocache(paddr, raw_size); + if (!vaddr) { + pr_err("ioremap to add raw header fail\n"); + return -ENOMEM; + } + + p = vaddr; + for (i = raw_size; i > 0; i--) + *(p + i - 1 + RAW_HEADER_SIZE) = *(p + i - 1); + + memset(vaddr, 0, RAW_HEADER_SIZE); + + snprintf(vaddr, RAW_HEADER_SIZE, "AK-RAW,headsize:0x%04x,bitsw:0x%02x,size:0x%04x,w:0x%04x,h:0x%04x", + RAW_HEADER_SIZE, raw_bitsw, raw_size, pcdev->crop_width, pcdev->crop_height); + + pr_err("raw-header:%s\n", (char *)vaddr); + + iounmap(vaddr); + return 0; +} + +static int copy_raw_to_1st_vb(struct ak_camera_dev *pcdev) +{ + struct ak_buffer *ak_active; + struct ak_buffer *ak_buf; + struct videobuf_buffer *vb_active; + struct videobuf_buffer *vb_1st; + int get_1st = 0; + + ak_active = list_entry(pcdev->capture.next, + struct ak_buffer, vb.queue); + vb_active = &ak_active->vb; + + list_for_each_entry(ak_buf, &pcdev->capture, vb.queue) { + vb_1st = &ak_buf->vb; + if (vb_1st->i == 0) { + pr_err("this is 1st vb\n"); + get_1st = 1; + break; + } + } + + if (!get_1st) { + pr_err("can not 1st vb\n"); + return -ENOENT; + } + + if (vb_active->i != 0) { + unsigned long to_paddr; + unsigned long from_paddr; + int size; + void *to_vaddr; + void *from_vaddr; + + size = pcdev->crop_width * pcdev->crop_height * 3 / 2; + + to_paddr = videobuf_to_dma_contig(vb_active); + to_vaddr = ioremap_nocache(to_paddr, size); + if (!to_vaddr) { + pr_err("ioremap to copy raw to fail\n"); + return -ENOMEM; + } + + from_paddr = videobuf_to_dma_contig(vb_1st); + from_vaddr = ioremap_nocache(from_paddr, size); + if (!from_vaddr) { + pr_err("ioremap to from raw fail\n"); + iounmap(to_vaddr); + return -ENOMEM; + } + else + memcpy(to_vaddr, from_vaddr, size); + + iounmap(to_vaddr); + iounmap(from_vaddr); + + pr_err("copy raw success\n"); + } else { + pr_err("it 1st vb already, not need copy\n"); + } + + return add_rawdata_header(pcdev, vb_active); +} + +static void done_1st_vb(struct ak_camera_dev *pcdev) +{ + struct ak_buffer *ak_active; + struct videobuf_buffer *vb; + + ak_active = list_entry(pcdev->capture.next, + struct ak_buffer, vb.queue); + vb = &ak_active->vb; + + ispdrv_vo_disable_buffer(BUFFER_ONE + vb->i); + + list_del_init(&vb->queue); + vb->state = VIDEOBUF_DONE; + vb->field_count++; + + wake_up(&vb->done); +} + +static int continue_mode_capture_a_rawdata(struct ak_camera_dev *pcdev) +{ + int ret; + + /*1.pause isp*/ + //ispdev_set_isp_capturing(0); + ispdrv_set_isp_pause(); + + /*2.wait for 1st buffer in driver*/ + ret = wait_for_1st_buffer_in_driver(pcdev, 300); + + /*3.check if not 1st buffer in driver then show warning*/ + if (ret) { + pr_err("not all buffers in driver\n"); + } else { + int cnt = 100; + + /*4.set global flag*/ + pcdev->tmp_raw_buf_id = -1; + pcdev->tmp_raw = 1; + + /*5.start capturing raw*/ + //ispdrv_vo_enable_buffer(BUFFER_ONE); + ispdrv_vi_apply_mode(ISP_JPEG_MODE); + //ispdrv_vi_start_capturing(); + //ispdev_set_isp_capturing(1); + ispdrv_set_isp_resume(); + + /*6.wait for captuing raw finish*/ + while ((pcdev->tmp_raw_buf_id < 0) && + (cnt-->0)) + msleep(1); + + /*7.check if capture raw finish, if not then show warning*/ + if (pcdev->tmp_raw_buf_id >= 0) { + /*8.copy raw to 1st vb*/ + ret = copy_raw_to_1st_vb(pcdev); + + /*9.done 1st vb*/ + if (!ret) + done_1st_vb(pcdev); + } else { + pr_err("error: capture rawdata fail\n"); + } + + /*10.reset global flag*/ + pcdev->tmp_raw_buf_id = -1; + pcdev->tmp_raw = 0; + } + + /*11.resume isp continue mode*/ + ispdrv_vi_apply_mode(pcdev->cur_mode); + //ispdrv_vi_start_capturing(); + //ispdev_set_isp_capturing(1); + ispdrv_set_isp_resume(); + + return ret; +} + +static int queue_single_mode(struct videobuf_buffer *vb, struct ak_camera_dev *pcdev) +{ + unsigned long flags; + u32 yaddr_chl1, yaddr_chl2, size; + struct soc_camera_device *icd = pcdev->icd; + + //size = vb->width * vb->height; + size = icd->user_width * icd->user_height; + yaddr_chl1 = videobuf_to_dma_contig(vb); /* for mater channel */ + yaddr_chl2 = yaddr_chl1 + size * 3 / 2; /* for secondary channel */ + + spin_lock_irqsave(&pcdev->lock, flags); + vb->state = VIDEOBUF_ACTIVE; + list_add_tail(&vb->queue, &pcdev->capture); + spin_unlock_irqrestore(&pcdev->lock, flags); + + switch (pcdev->list_state) { + case LIST_ZERO: + ispdrv_vo_set_buffer_addr(BUFFER_ONE, yaddr_chl1, yaddr_chl2); + + ispdrv_vo_enable_buffer(BUFFER_ONE); + + ispdrv_vi_apply_mode(pcdev->cur_mode); + ispdrv_vi_start_capturing(); + + pcdev->list_state++; + break; + case LIST_ONE: + case LIST_TWO: + case LIST_THREE: + //case LIST_FOUR: + break; + default: + printk("Not defined list stat [single mode].\n"); + break; + } + + return 0; +} + +static int queue_continous_mode(struct videobuf_buffer *vb, struct ak_camera_dev *pcdev) +{ + int i; + unsigned long flags; + u32 yaddr_chl1, yaddr_chl2, size; + struct soc_camera_device *icd = pcdev->icd; + + //size = vb->width * vb->height; + size = icd->user_width * icd->user_height; + yaddr_chl1 = videobuf_to_dma_contig(vb); /* for mater channel */ + yaddr_chl2 = yaddr_chl1 + size * 3 / 2; /* for secondary channel */ + + isp_dbg("%s vb->i=%d, phyaddr=%x, user_width:%d, user_height:%d\n", __func__, vb->i, yaddr_chl1, icd->user_width, icd->user_height); +#if 1 + switch (pcdev->list_state) { + case LIST_FOUR: + break; + case LIST_ZERO: + for (i = 0; i < 4; i++) { + ispdrv_vo_disable_buffer(BUFFER_ONE + i); + } + + case LIST_ONE: + case LIST_TWO: + ispdrv_vo_set_buffer_addr(BUFFER_ONE + vb->i, yaddr_chl1, yaddr_chl2); + case LIST_THREE: + pcdev->list_state++; + break; + default: + printk("Not defined list stat [continous mode].\n"); + break; + } +#else + switch (pcdev->list_state) { + case LIST_FIVE: + break; + case LIST_ZERO: + for (i = 0; i < 4; i++) { + ispdrv_vo_disable_buffer(BUFFER_ONE + i); + } + + case LIST_ONE: + case LIST_TWO: + case LIST_THREE: + ispdrv_vo_set_buffer_addr(BUFFER_ONE + vb->i, yaddr_chl1, yaddr_chl2); + case LIST_FOUR: + pcdev->list_state++; + break; + default: + printk("Not defined list stat [continous mode].\n"); + break; + } +#endif + + spin_lock_irqsave(&pcdev->lock, flags); + vb->state = VIDEOBUF_ACTIVE; + list_add_tail(&vb->queue, &pcdev->capture); + + ispdrv_vo_enable_buffer(BUFFER_ONE + vb->i); + pcdev->free_list++; + spin_unlock_irqrestore(&pcdev->lock, flags); + +#if 1 + if (pcdev->list_state == LIST_THREE) { + ispdrv_vi_apply_mode(pcdev->cur_mode); + ispdrv_vo_enable_irq_status(0x1); + + if (pcdev->stream_ctrl_off == 0) { + //printk(KERN_ERR "%s start capture\n", __func__); + ispdrv_vi_start_capturing(); + } else { + //printk(KERN_ERR "%s resume capture\n", __func__); + ispdrv_set_isp_resume(); + } + } +#else + if (pcdev->list_state == LIST_FOUR) { + ispdrv_vi_apply_mode(pcdev->cur_mode); + ispdrv_vo_enable_irq_status(0x1); + + if (pcdev->stream_ctrl_off == 0) { + //printk(KERN_ERR "%s start capture\n", __func__); + ispdrv_vi_start_capturing(); + } else { + //printk(KERN_ERR "%s resume capture\n", __func__); + ispdrv_set_isp_resume(); + } + } +#endif + + + return 0; +} + +/** + * @brief: Called when application apply buffers, camera start data collection + * + * @author: caolianming + * @date: 2014-01-06 + * @param [in] *vq: V4L2 buffer queue information structure + * @param [in] *vb: V4L2 buffer information structure + */ +static void ak_videobuf_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct ak_camera_dev *pcdev = ici->priv; + + isp_dbg("%s (vb=0x%p) buf[%d] baddr = 0x%08lx, bsize = %d\n", + __func__, vb, vb->i, vb->baddr, vb->bsize); + + switch(pcdev->cur_mode) { + case ISP_YUV_OUT: + case ISP_RGB_OUT: + /* for single mode */ + queue_single_mode(vb, pcdev); + break; + + case ISP_YUV_VIDEO_OUT: + case ISP_RGB_VIDEO_OUT: + /* for continous mode */ + queue_continous_mode(vb, pcdev); + pcdev->vq = vq; + break; + + default: + printk("The working mode of ISP hasn't been initialized.\n"); + break; + } +} + +/** + * @brief: for ak_videobuf_release, free buffer if camera stopped. + * + * @author: caolianming + * @date: 2014-01-06 + * @param [in] *vq: V4L2 buffer queue information structure + * @param [in] *vb: V4L2 buffer information structure + */ +static void ak_videobuf_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct ak_buffer *buf = container_of(vb, struct ak_buffer, vb); + struct soc_camera_device *icd = vq->priv_data; +// struct device *dev = icd->parent; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct ak_camera_dev *pcdev = ici->priv; + unsigned long flags; + + isp_dbg("%s (vb=0x%p) buf[%d] 0x%08lx %d\n", + __func__, vb, vb->i, vb->baddr, vb->bsize); + + spin_lock_irqsave(&pcdev->lock, flags); + ispdrv_vo_clear_irq_status(0xffff); //?? 库还没有关闭irq函数 + spin_unlock_irqrestore(&pcdev->lock, flags); + + switch (vb->state) { + case VIDEOBUF_ACTIVE: + CAMDBG("vb status: ACTIVE\n"); + break; + case VIDEOBUF_QUEUED: + CAMDBG("vb status: QUEUED\n"); + break; + case VIDEOBUF_PREPARED: + CAMDBG("vb status: PREPARED\n"); + break; + default: + CAMDBG("vb status: unknown\n"); + break; + } + + free_buffer(vq, buf); + pcdev->stream_ctrl_off = 1; +} + +static struct videobuf_queue_ops ak_videobuf_ops = { + .buf_setup = ak_videobuf_setup, + .buf_prepare = ak_videobuf_prepare, + .buf_queue = ak_videobuf_queue, + .buf_release = ak_videobuf_release, +}; + +static inline int list_how_many_entries(struct list_head *head) +{ + int i; + struct list_head *list = head; + + for (i = 0; list->next != head; i++) + list = list->next; + + return i; +} + +static int delay_works(struct ak_camera_dev *pcdev) +{ + if (pcdev->cur_mode_class >= V4L2_MBUS_FMT_SBGGR8_1X8 && + pcdev->cur_mode_class <= V4L2_MBUS_FMT_SRGGB12_1X12) { + if (1){//(pcdev->rfled_ison == 0) { + schedule_delayed_work(&pcdev->awb_work, 0); + } + schedule_delayed_work(&pcdev->ae_work, 0); +// schedule_delayed_work(&pcdev->af_work, 0); + } + + return 0; +} + +static inline unsigned long get_timestamp(void) +{ + unsigned long ul; + + ul = jiffies; + if (ul >= INITIAL_JIFFIES) + ul -= INITIAL_JIFFIES; + else + ul = (~(unsigned long)0) - INITIAL_JIFFIES + ul; + ul = jiffies_to_msecs(ul); + + return ul; +} + +static int irq_handle_single_mode(struct videobuf_buffer *vb, struct ak_camera_dev *pcdev) +{ + int entries; + u32 yaddr_chl1, yaddr_chl2, size; + struct videobuf_buffer *vb_active; + struct ak_buffer *ak_active; + unsigned long timestamp_ms; + + if (pcdev->tmp_raw) { + /*process capuring raw in continue mode*/ + pcdev->tmp_raw_buf_id = vb->i; + pr_err("irq raw\n"); + return 0; + } + + timestamp_ms = get_timestamp(); + vb->ts.tv_sec = timestamp_ms / 1000; + vb->ts.tv_usec = (timestamp_ms % 1000) * 1000; + + list_del_init(&vb->queue); + vb->state = VIDEOBUF_DONE; + vb->field_count++; + + entries = list_how_many_entries(&pcdev->capture); + if (entries >= 1) { + ak_active = list_entry(pcdev->capture.next, + struct ak_buffer, vb.queue); + vb_active = &ak_active->vb; + + //size = vb_active->width * vb_active->height; + size = pcdev->icd->user_width * pcdev->icd->user_height; + yaddr_chl1 = videobuf_to_dma_contig(vb_active); /* for mater channel */ + yaddr_chl2 = yaddr_chl1 + size * 3 / 2; /* for secondary channel */ + + ispdrv_vo_set_buffer_addr(BUFFER_ONE, yaddr_chl1, yaddr_chl2); + ispdrv_vi_start_capturing(); + } else { + ispdrv_vo_disable_buffer(BUFFER_ONE); + ispdrv_vi_stop_capturing(); + pcdev->list_state = LIST_ZERO; + } + + return 0; +} + +static unsigned long sjf = 0; +static int irq_handle_continous_mode(struct ak_camera_dev *pcdev) +{ + int video_data_err = 0; + int id; + unsigned long timestamp_ms; + struct videobuf_buffer *vb; + struct ak_buffer *ak_buf; + struct list_head *next; + static struct list_head *save_list; + unsigned long ul , ul2; + int fps = 10; + AK_ISP_SENSOR_CB *sensor_cb; + + sensor_cb = ak_sensor_get_sensor_cb(); + if (NULL != sensor_cb) { + fps = sensor_cb->sensor_get_fps_func(); + } + //printk(KERN_ERR "fps:%d\n", fps); + video_frame_interval = 1000 / fps + 10; + + ul = ul2 = jiffies; + if (sjf == 0) + sjf = jiffies; + + if (ul >= sjf) + ul -= sjf; + else + ul = (~(unsigned long)0) - sjf+ ul; + if (jiffies_to_msecs(ul) > video_frame_interval) { + printk(KERN_ERR ">%d, sjf:%lu, ul:%lu\n", video_frame_interval, sjf, ul); + _tdnr_flag = 1; + } + sjf = ul2; + + id = ispdrv_vo_get_using_frame_buf_id(); + // printk("HWFrameId=%d\n", id); +// isp2_print_reg_table(); + + next = pcdev->capture.next; + if (next == &pcdev->capture) { + printk("Error, camera no buffer, but run to irq\n"); + ispdrv_vi_stop_capturing(); + } + + ak_buf = list_entry(next, struct ak_buffer, vb.queue); + vb = &ak_buf->vb; + if (id == -1) + { + if ((vb->field_count > 1) && (save_list != next )) { + printk("%s %d: vb->i=%d, but id %d\n", __func__,__LINE__, vb->i, id); + save_list = next; + } + return 0; + } else if ((id & 0x7F) != vb->i) { + printk("vb->i=%d, but id %d\n", vb->i, id); + return 0; + } else if (id >= 0x80) { + if ((id & 0x7F) != vb->i) { + printk("~~%s %d: vb->i=%d, but id %d\n", __func__,__LINE__, vb->i, id); + return 0; + } + + video_data_err = 1; + } + save_list = next; + next = next->next; + + if (next != &pcdev->capture) { + ispdrv_vo_disable_buffer(BUFFER_ONE + vb->i); + + if (!video_data_err) { + timestamp_ms = get_timestamp(); + vb->ts.tv_sec = timestamp_ms / 1000; + vb->ts.tv_usec = (timestamp_ms % 1000) * 1000; + } else { + vb->ts.tv_sec = 0; + vb->ts.tv_usec = 0; + } + + list_del_init(&vb->queue); + vb->state = VIDEOBUF_DONE; + vb->field_count++; +// printk("%s vb->i=%d DONE\n", __func__, vb->i); + + pcdev->cur_buf_id = vb->i; + // here, current frame commit to video_buffer layer + wake_up(&vb->done); + + + } else { + pcdev->cur_buf_id = -1; + printk("Warnning, lost frame at %ld\n", jiffies); + } + + return 0; +} + +static irqreturn_t ak_camera_dma_irq(int channel, void *data) +{ + unsigned long stat; + struct ak_camera_dev *pcdev = data; + struct ak_buffer *ak_active; + struct videobuf_buffer *vb; + unsigned long flags; + + in_irq_jf = jiffies; + //printk(KERN_ERR "%lu\n",in_irq_jf); + + spin_lock_irqsave(&pcdev->lock, flags); + + if (!((stat = ispdrv_vo_check_irq_status()) & 0x01)) { + ispdrv_vo_clear_irq_status(0xfffe); + spin_unlock_irqrestore(&pcdev->lock, flags); + printk("%s %d stat:0x%lx\n", __func__, __LINE__, stat); + return IRQ_HANDLED; +// goto out; + } + + if (!pcdev->tmp_raw) + ispdrv_irq_work(); + if (pcdev->list_state == LIST_ZERO) + { + printk("%s: state not handled\n", __func__); + goto out; + } + + if (!ispdrv_is_continuous()) { //?? 库没提供函数 + ak_active = list_entry(pcdev->capture.next, + struct ak_buffer, vb.queue); + vb = &ak_active->vb; + WARN_ON(ak_active->inwork || list_empty(&vb->queue)); + irq_handle_single_mode(vb, pcdev); + } else { + irq_handle_continous_mode(pcdev); + } + + if (!pcdev->tmp_raw) + delay_works(pcdev); + +out: + ispdrv_vo_clear_irq_status(0xffff); + spin_unlock_irqrestore(&pcdev->lock, flags); +// printk("%s stat:%x\n", __func__, stat); + return IRQ_HANDLED; +} + +/** + * @brief: delay work queue, update image effects entry. + * + * @author: caolianming + * @date: 2014-01-06 + * @param [in] *work: struct isp_struct + */ +static void isp_awb_work(struct work_struct *work) +{ + + ispdrv_awb_work(); +} + +static int ok_to_reload_td(struct ak_camera_dev *pcdev) +{ + if (pcdev->td_reset_frames > 0 ) { + pcdev->td_reset_frames--; + return 0; + } + return 1; +} + +/** + * @brief: delay work queue, update image effects entry. + * + * @author: caolianming + * @date: 2014-01-06 + * @param [in] *work: struct isp_struct + */ +static void isp_ae_work(struct work_struct *work) +{ + int aec_delay_ms = 10; + unsigned long flags; + struct ak_camera_dev *pcdev = container_of(work, struct ak_camera_dev, ae_work.work); + AK_ISP_SENSOR_CB *sensor_cb; + unsigned long cur_jf = jiffies; + + if (cur_jf >= in_irq_jf){ + int cur_fps = 25; + int active_ms = 36; + unsigned long diff_jf = cur_jf - in_irq_jf; + + sensor_cb = ak_sensor_get_sensor_cb(); + if (sensor_cb && sensor_cb->sensor_get_parameter_func) { + sensor_cb->sensor_get_parameter_func(GET_CUR_FPS, &cur_fps); + sensor_cb->sensor_get_parameter_func(GET_VSYNC_ACTIVE_MS, &active_ms); + aec_delay_ms = MSEC_PER_SEC / cur_fps - active_ms + 1; + if ((aec_delay_ms % 10) == 0) + aec_delay_ms -= 1; + } + + if (aec_delay_ms > diff_jf) + aec_delay_ms = aec_delay_ms - (int)diff_jf; + else + aec_delay_ms = 0; + + usleep_range(aec_delay_ms * USEC_PER_MSEC, (aec_delay_ms + 1) * USEC_PER_MSEC); + + //printk(KERN_ERR "D%d\n",aec_delay_ms); + } + + ispdrv_ae_work(); + + /* get current buf id */ + if (pcdev->cur_buf_id != -1) { + /* get tail addr of yuv and md info addr */ + void *yuv_paddr, *mdinfo, *yuv_vaddr = NULL; + int md_sz = 24*32*2; + int flip_en, mirror_en, height_block_num; + int i,j; + + ispdrv_get_yuvaddr_and_mdinfo(pcdev->cur_buf_id, &yuv_paddr, &mdinfo); + ispdrv_vo_get_flip_mirror(&flip_en, &mirror_en, &height_block_num); + + /* cat to yuv's tail */ + yuv_vaddr = ioremap_nocache((unsigned long )yuv_paddr, md_sz); + + if (yuv_vaddr) { + if (flip_en == 0 && mirror_en == 0) { + memcpy(yuv_vaddr, mdinfo, md_sz); + } else if (flip_en == 0 && mirror_en == 1) { + for (i = 0; i < height_block_num; i++) { + for (j = 0; j < 32; j++) { + memcpy(yuv_vaddr + 2*(32*i + j), mdinfo + 2*(32*i + 31 - j), 2); + } + } + } else if (flip_en == 1 && mirror_en == 0) { + for (i = 0; i < height_block_num; i++) { + memcpy(yuv_vaddr + 2*32*i, mdinfo + 2*32*(height_block_num - 1 - i), 2*32); + } + } else if (flip_en == 1 && mirror_en == 1) { + for (i = 0; i < height_block_num; i++) { + for (j = 0; j < 32; j++) { + memcpy(yuv_vaddr + 2*(32*i + j), mdinfo + 2*(32*(height_block_num - 1 - i) + 31 - j), 2); + } + } + } + iounmap(yuv_vaddr); + } + + pcdev->cur_buf_id = -1; + } + + if (_tdnr_flag && !_tdnr_set) { + ispdrv_set_td(); + spin_lock_irqsave(&pcdev->lock, flags); + _tdnr_set = 1; + _tdnr_flag = 0; + pr_info("td_reset_count:%d\n", pcdev->td_reset_count); + pcdev->td_reset_count++; + if (pcdev->td_reset_count < 2) + /*first td reset*/ + pcdev->td_reset_frames = 120; + else + pcdev->td_reset_frames = 5; + spin_unlock_irqrestore(&pcdev->lock, flags); + } else if (_tdnr_set && ok_to_reload_td(pcdev)) { + ispdrv_reload_td(); + spin_lock_irqsave(&pcdev->lock, flags); + _tdnr_set = 0; + _tdnr_flag = 0; + spin_unlock_irqrestore(&pcdev->lock, flags); + } + + if (pcdev->app_capture_a_rawdata) { + pcdev->app_capture_a_rawdata = 0; + continue_mode_capture_a_rawdata(pcdev); + } +} + +/** + * @brief: delay work queue, update image effects entry. + * + * @author: caolianming + * @date: 2014-01-06 + * @param [in] *work: struct isp_struct + */ +static void isp_af_work(struct work_struct *work) +{ + //ispdrv_af_work(isp); +} + +static struct soc_camera_device *ctrl_to_icd(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct soc_camera_device, + ctrl_handler); +} + +/** + * @brief: get function supported of camera, the function is image adjust, color effect... + * + * @author: caolianming + * @date: 2014-01-06 + * @param [in] *ctrl: V4L2 image effect control information structure + */ +static int ak_camera_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ +// struct soc_camera_device *icd = ctrl_to_icd(ctrl); +// struct v4l2_subdev *sd = soc_camera_to_subdev(icd); +// struct soc_camera_host *ici = to_soc_camera_host(icd->parent); +// struct ak_camera_dev *pcdev = ici->priv; + + isp_dbg("entry %s, ctrl->id=%x\n", __func__, ctrl->id); + return 0; +} + +/** + * @brief: the isp standard control should be implemented here. + * the function is image adjust, color effect... + * + * @author: caolianming + * @date: 2014-01-06 + * @param [in] *ctrl: V4L2 image effect control information structure + */ +static int ak_camera_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_control control; + struct soc_camera_device *icd = ctrl_to_icd(ctrl); + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + + control.id = ctrl->id; + control.value = ctrl->val; + v4l2_subdev_call(sd, core, s_ctrl, &control); + + return 0; +} + + +static const struct v4l2_ctrl_ops ak_camera_ctrl_ops = { + .g_volatile_ctrl = ak_camera_g_volatile_ctrl, + .s_ctrl = ak_camera_s_ctrl, +}; + +/** + * @brief: set sensor clock + * + * @author: caolianming + * @date: 2014-01-06 + * @param [in] cis_sclk: sensor work clock + */ +static void set_sensor_cis_sclk(unsigned int cis_sclk) +{ + unsigned long regval; + unsigned int cis_sclk_div; + + unsigned int peri_pll = ak_get_peri_pll_clk()/1000000; + + cis_sclk_div = peri_pll/cis_sclk - 1; + + regval = REG32(CLOCK_PERI_PLL_CTRL2); + regval &= ~(0x3f << 10); + regval |= (cis_sclk_div << 10); + REG32(CLOCK_PERI_PLL_CTRL2) = (1 << 19)|regval; + + isp_dbg("%s() cis_sclk=%dMHz peri_pll=%dMHz cis_sclk_div=%d\n", + __func__, cis_sclk, peri_pll, cis_sclk_div); +} + +static void set_dvp_initiation(int sensor_io_level) +{ + __raw_writeb(0x7d, AK_VA_MIPI + 0x00); + __raw_writeb(0x3f, AK_VA_MIPI + 0x20); + __raw_writeb(0x01, AK_VA_MIPI + 0xb3); + switch (sensor_io_level) { + case SENSOR_IO_LEVEL_1V8: + __raw_writeb(0x10, AK_VA_MIPI + 0xb8); + break; + + case SENSOR_IO_LEVEL_2V5: + __raw_writeb(0x08, AK_VA_MIPI + 0xb8); + break; + + case SENSOR_IO_LEVEL_3V3: + __raw_writeb(0x04, AK_VA_MIPI + 0xb8); + break; + + default: + printk(KERN_ERR "%s sensor_io_level:%d not defined\n", + __func__, sensor_io_level); + break; + } +} + +static void set_mipi_2lanes_initiation(int thssettle) +{ + unsigned char value; + + printk(KERN_ERR "2lane thssettle:%d\n", thssettle); + + //mipi dphy data 0~3 and clk lane enable + __raw_writeb(0x7d, AK_VA_MIPI + 0x00); + printk(KERN_ERR "reg0x00, read:0x%02x",__raw_readb(AK_VA_MIPI)); + + //ttl register bank disable + __raw_writeb(0x1f, AK_VA_MIPI + 0x20); + printk(KERN_ERR "reg0x20, read:0x%02x",__raw_readb(AK_VA_MIPI + 0x20)); + + //two lane mode + //REG8(MIPI_LANE_MODE_CFG_REG) = 0xf9;//0x204000e0 + __raw_writeb(0xf9, AK_VA_MIPI + 0xe0); + printk(KERN_ERR "reg0xe0, read:0x%02x",__raw_readb(AK_VA_MIPI + 0xe0)); + + //enable err + //REG8(MIPI_ERR_ENA_CFG_REG) = 0x1;//0x204000e1 + __raw_writeb(0x01, AK_VA_MIPI + 0xe1); + printk(KERN_ERR "reg0xe1, read:0x%02x",__raw_readb(AK_VA_MIPI + 0xe1)); + + + //THS - SETTLE + //REG8(MIPI_CLK_LANE_FREQ_CFG_REG) = 0x8b;//0x20400040 + //REG8(MIPI_DATA0_LANE_FREQ_CFG_REG) = 0x8b;//0x20400060 + //REG8(MIPI_DATA1_LANE_FREQ_CFG_REG) = 0x8b;//0x20400080 + value = 0x80 | (thssettle & 0x0f); + __raw_writeb(value, AK_VA_MIPI + 0x40); + printk(KERN_ERR "reg0x40, read:0x%02x",__raw_readb(AK_VA_MIPI + 0x40)); + __raw_writeb(value, AK_VA_MIPI + 0x60); + printk(KERN_ERR "reg0x60, read:0x%02x",__raw_readb(AK_VA_MIPI + 0x60)); + __raw_writeb(value, AK_VA_MIPI + 0x80); + printk(KERN_ERR "reg0x80, read:0x%02x",__raw_readb(AK_VA_MIPI + 0x80)); + + //enable mipi mode + //REG8(MIPI_IO_MODE_REG) = 0x2;//0x204000b3 + __raw_writeb(0x02, AK_VA_MIPI + 0xb3); + printk(KERN_ERR "reg0xb3, read:0x%02x",__raw_readb(AK_VA_MIPI + 0xb3)); +} + +static int mipi_mhz_to_thssettle(int mhz) +{ + int thssettle; + + if (mhz < 80){ + thssettle = -1; + printk(KERN_ERR"%s fail, not support this min mbps:%d\n", __func__, mhz); + return thssettle; + } + + if (mhz <= 110) + thssettle = 0; + else if (mhz <= 150) + thssettle = 1; + else if (mhz <= 200) + thssettle = 2; + else if (mhz <= 250) + thssettle = 3; + else if (mhz <= 300) + thssettle = 4; + else if (mhz <= 400) + thssettle = 5; + else if (mhz <= 500) + thssettle = 6; + else if (mhz <= 600) + thssettle = 7; + else if (mhz <= 700) + thssettle = 8; + else if (mhz <= 800) + thssettle = 9; + else if (mhz <= 1000) + thssettle = 0xa; + else{ + thssettle = -1; + printk(KERN_ERR"%s fail, not support this max mbps:%d\n", __func__, mhz); + } + + return thssettle; +} + +static void set_mipi_1lane_initiation(int thssettle) +{ + unsigned char value; + + printk(KERN_ERR "1lane thssettle:%d\n", thssettle); + + //THS - SETTLE + //REG8(MIPI_CLK_LANE_FREQ_CFG_REG) = 0x8b;//0x20400040 + //REG8(MIPI_DATA0_LANE_FREQ_CFG_REG) = 0x8b;//0x20400060 + value = 0x80 | (thssettle & 0x0f); + __raw_writeb(value, AK_VA_MIPI + 0x40); + printk(KERN_ERR "reg0x40, read:0x%02x",__raw_readb(AK_VA_MIPI + 0x40)); + __raw_writeb(value, AK_VA_MIPI + 0x60); + printk(KERN_ERR "reg0x60, read:0x%02x",__raw_readb(AK_VA_MIPI + 0x60)); + + __raw_writeb(0x7d, AK_VA_MIPI + 0x00); + __raw_writeb(0xf8, AK_VA_MIPI + 0xe0); +} + + +struct ths_settle_info +{ + int max_mbps; + int time_formula; + int settle_value; +}; + +struct ths_settle_dphy +{ + int min_ths_settle; + int max_ths_settle; +}; + +#define MIPI_IP_MAX_THS_SETTLE_NUM 11 + +/* this minst mbps must be more than 80MBPS */ +struct ths_settle_info ths_settle[MIPI_IP_MAX_THS_SETTLE_NUM] = +{ + {110, 4, 0x0}, + {150, 6, 0x1}, + {200, 9, 0x2}, + {250, 10, 0x3}, + {300, 13, 0x4}, + {400, 17, 0x5}, + {500, 23, 0x6}, + {600, 28, 0x7}, + {700, 33, 0x8}, + {800, 35, 0x9}, + {1000, 44, 0xa}, +}; + +static int mipi_mbps_to_100times_ui(int mbps) +{ + int ui_ns = 0; + + ui_ns = 1000*100/mbps; + + return ui_ns; +} + +static int mipi_thssettle_to_time(int thssettle, int mbps) +{ + int thssettle_time = 0; + int thssettle_ui = 0; + int i=0; + int thssettle_iptime = 0; + + thssettle_ui = mipi_mbps_to_100times_ui(mbps); + + for(i=0; imin_ths_settle = 85 + (6 * thssettle_ui /100); + thssettle_dphy->max_ths_settle = 145 + (10 * thssettle_ui /100); + + return 0; +} + +static int mipi_thssettle_init(int ths_settle, int mbps) +{ + struct ths_settle_dphy thssettle_dphy_arrange; + int thssettle_timebase = 0; + int thssettle_timelast = 0; + int i=0; + + /* first step:get dphy ths-settle arrange according mbps and ui */ + mipi_thssettle_to_dphy_time(&thssettle_dphy_arrange, mbps); + + /* second step:get mipi ip ths_settle base according from mbps + * three step:adjust mipi ip ths_settle comply with dphy arrange + */ + thssettle_timebase = mipi_thssettle_to_time(ths_settle, mbps); + if(thssettle_timebase <= thssettle_dphy_arrange.min_ths_settle){ /* ths-settle < dphy_min */ + for(i=ths_settle; i<=MIPI_IP_MAX_THS_SETTLE_NUM; i++){ + thssettle_timebase = mipi_thssettle_to_time(i, mbps); + if((thssettle_timebase>thssettle_dphy_arrange.min_ths_settle) && (thssettle_timebase<= thssettle_dphy_arrange.max_ths_settle)){ + /*get min suitable ths_settle */ + thssettle_timelast = i; + break; + } + } + }else if(thssettle_timebase >= thssettle_dphy_arrange.max_ths_settle){ /* ths-settle > dphy_max */ + for(i=ths_settle; i>=0; i--){ + thssettle_timebase = mipi_thssettle_to_time(i, mbps); + if((thssettle_timebase>thssettle_dphy_arrange.min_ths_settle) && (thssettle_timebase<= thssettle_dphy_arrange.max_ths_settle)){ + /*get max suitable ths_settle */ + thssettle_timelast = i; + break; + } + } + }else{ /* dphy_min < ths-settle < dphy_max */ + thssettle_timelast = ths_settle; + } + + if ((i > MIPI_IP_MAX_THS_SETTLE_NUM) || (i < 0)){ + pr_err("Err,no find suitable mipi ip ths-settle!\n"); + return -1; + } + + return thssettle_timelast; + +} + +static int mipi_ip_initiation(void) +{ + int thssettle = 2, mipi_mhz = 200, mipi_lane = 2; + AK_ISP_SENSOR_CB *sensor_cb; + int thssettle_base; + + sensor_cb = ak_sensor_get_sensor_cb(); + if (sensor_cb && sensor_cb->sensor_get_parameter_func) { + sensor_cb->sensor_get_parameter_func(GET_MIPI_MHZ, &mipi_mhz); + sensor_cb->sensor_get_parameter_func(GET_MIPI_LANE, &mipi_lane); + } else { + printk(KERN_ERR "%s get mipi_mhz failed\n", __func__); + return -1; + } + + thssettle_base = mipi_mhz_to_thssettle(mipi_mhz); + if(thssettle_base == -1){ + printk(KERN_ERR"%s fail, not support this mhz:%d\n", __func__, mipi_mhz); + return -1; + }else{ + printk(KERN_ERR"%s ok, thssettle_base:0x%x, support this mhz:%d\n", __func__, thssettle_base, mipi_mhz); + } + + thssettle = mipi_thssettle_init(thssettle_base, mipi_mhz); + if(thssettle == -1){ + printk(KERN_ERR"%s fail, not find suitable dphy mhz:%d\n", __func__, mipi_mhz); + return -1; + }else{ + printk(KERN_ERR"%s ok, thssettle:0x%x, find suitable dphy mhz:%d\n", __func__, thssettle, mipi_mhz); + } + + if (mipi_lane == 2) + set_mipi_2lanes_initiation(thssettle); + else if (mipi_lane == 1) + set_mipi_1lane_initiation(thssettle); + + __raw_writeb(0x04, AK_VA_MIPI + 0xb8); + __raw_writeb(0xc0, AK_VA_MIPI + 0x0d); + + return 0; +} + +/** + * @brief: Called when the /dev/videox is opened. initial ISP and sensor device. + * + * @author: caolianming + * @date: 2014-01-06 + * @param [in] *icd: soc_camera_device information structure, + * akcamera depends on the soc driver. + */ +static int ak_camera_add_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct ak_camera_dev *pcdev = ici->priv; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct ak_camera_cam *cam; + int cis_sclk; + int sensor_interface = DVP_INTERFACE, sensor_io_level = SENSOR_IO_LEVEL_1V8; + AK_ISP_SENSOR_CB *sensor_cb; + + CAMDBG("entry %s\n", __func__); + + /* The ak camera host driver only support one image sensor */ + if (pcdev->icd) + return -EBUSY; + + dev_info(icd->parent, "AK Camera driver attached to camera %d\n", + icd->devnum); + + /* for debugging. Capture list should be empty when the video opened. */ + if (!list_empty(&pcdev->capture)) { + printk("Bug: pcdev->capture is not empty\n"); + list_del_init(&pcdev->capture); + } + + /********** config sensor module **********/ + v4l2_subdev_call(sd, core, init, 0); + + sensor_cb = ak_sensor_get_sensor_cb(); + if (NULL == sensor_cb) { + cis_sclk = 24; + //printk(KERN_ERR "%s get sensor_cb faild!!!!!!!\n", __func__); + } else { + cis_sclk = sensor_cb->sensor_get_mclk_func(); + } + + //set cis_sclk, the sensor present working 24MHz + clk_enable(pcdev->cis_sclk); + set_sensor_cis_sclk(cis_sclk); + + // load the default setting for sensor +// v4l2_subdev_call(sd, core, load_fw); + + /********** config isp module **********/ + ak_soft_reset(AK_SRESET_CAMERA); + + // enable isp clock + clk_enable(pcdev->clk); +// printk("ISP CLOCK ENABLE \n"); + REG32(CLOCK_PERI_PLL_CTRL1) &=~(0x01<<25); + + if (NULL != sensor_cb) { + sensor_cb->sensor_get_parameter_func(GET_INTERFACE, &sensor_interface); + sensor_cb->sensor_get_parameter_func(GET_SENSOR_IO_LEVEL, &sensor_io_level); + } + + switch (sensor_interface) { + case MIPI_INTERFACE: + /* set inter CIS_PCLK_tmp */ + ak_set_cis_pclk(75); + + /* pclk -> cis2_sclk */ + REG32(AK_SHAREPIN_CON1) |= (0x1<<30) | (0x1<<4); + + /* asic clk reset mipi controller*/ + ak_soft_reset(AK39_SRESET_MIPI); + + //release mipi controller pin_byte_clk area resest + ak_set_mipi_byte_rst(0); + + //select pll to generate internal pclk for mipi + ak_set_cis_pclk_sel(1); + + //select mipi sensor + ak_set_cis_mipi_dvp_sel(1); + + //share pin config mipi + ak_group_config(ePIN_AS_MIPI); + + //open mipi controller clock gate + clk_enable(pcdev->mipi_clk); + + //release mipi controller reset + ak_soft_reset(AK39_SRESET_MIPI); + + mipi_ip_initiation(); + break; + + case DVP_INTERFACE: + set_dvp_initiation(sensor_io_level); + break; + + default: + printk(KERN_ERR "%s interface:%d not defined\n", + __func__, sensor_interface); + break; + } + + pcdev->icd = icd; + pcdev->td_reset_count = 0; + + /* FIXME Here, add out control */ + if (!icd->host_priv) { + /* FIXME: subwindow is lost between close / open */ + cam = kzalloc(sizeof(*cam), GFP_KERNEL); + if (!cam) + return -ENOMEM; + + /* We are called with current camera crop, initialise subrect with it */ + icd->host_priv = cam; + } else { + cam = icd->host_priv; + } + + + return 0; +} + +/** + * @brief: Called when the /dev/videox is close. close ISP and sensor device. + * + * @author: caolianming + * @date: 2014-01-06 + * @param [in] *icd: soc_camera_device information structure, + * akcamera depends on the soc driver. + */ +static void ak_camera_remove_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct ak_camera_dev *pcdev = ici->priv; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + + CAMDBG("entry %s\n", __func__); + + _tdnr_set = 0; + _tdnr_flag = 0; + + BUG_ON(icd != pcdev->icd); + + v4l2_subdev_call(sd, core, reset, 0); + + ispdrv_vo_clear_irq_status(0xffff); //?? 库还没有关闭irq函数 + ispdrv_vi_stop_capturing(); + + mdelay(500); + + /* disable the clock of isp module */ + clk_disable(pcdev->clk); + + /* disable sensor clk */ + clk_disable(pcdev->cis_sclk); + + clk_disable(pcdev->mipi_clk); + + //ak_soft_reset(AK_SRESET_CAMERA); + + dev_info(icd->parent, "AK Camera driver detached from camera %d\n", + icd->devnum); + + pcdev->list_state = LIST_ZERO; + pcdev->free_list = LIST_ZERO; + + pcdev->icd = NULL; + + pcdev->stream_ctrl_off = 0; + pcdev->td_reset_count = 0; + + CAMDBG("Leave %s\n", __func__); +} + +static void set_pclk_polar(int is_rising) +{ + unsigned long regval; + + regval = REG32(CLOCK_PERI_PLL_CTRL2); + if (is_rising) + regval |= (0x3 << 30); + else + regval &= ~(0x3 << 30); + REG32(CLOCK_PERI_PLL_CTRL2) = regval; +} + +static int ak_camera_cropcap(struct soc_camera_device *icd, + struct v4l2_cropcap *crop) +{ + int pclk_polar; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + + isp_dbg("enter %s\n", __func__); + + pclk_polar = ispdrv_get_pclk_polar(); + switch (pclk_polar) { + case POLAR_RISING: + set_pclk_polar(1); + break; + case POLAR_FALLING: + set_pclk_polar(0); + break; + default: + printk("pclk polar wrong: %d\n", pclk_polar); + break; + } + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + // isp support crop, need complete. + return v4l2_subdev_call(sd, video, cropcap, crop); +} + +static int ak_camera_get_crop(struct soc_camera_device *icd, + struct v4l2_crop *crop) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + + isp_dbg("entry %s\n", __func__); + + return v4l2_subdev_call(sd, video, g_crop, crop); +} + +static int ak_camera_set_crop(struct soc_camera_device *icd, + struct v4l2_crop *crop) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct ak_camera_dev *pcdev = ici->priv; + struct v4l2_cropcap cropcap; + struct v4l2_crop local_crop; + struct v4l2_rect off_rect; + int ret; + int total_left, total_top; + AK_ISP_SENSOR_CB *sensor_cb; + + isp_dbg("entry %s\n", __func__); +#if 0 + if (pcdev->dma_running) { + /* make sure streaming is not started */ + v4l2_err(&ici->v4l2_dev, + "Cannot change crop when streaming is ON\n"); + return -EBUSY; + } +#endif + + sensor_cb = ak_sensor_get_sensor_cb(); + if (NULL == sensor_cb) { + printk(KERN_ERR "%s get sensor_cb failed!!!!!\n", __func__); + return -ENODEV; + } else { + sensor_cb->sensor_get_valid_coordinate_func(&off_rect.left, &off_rect.top); + } + + if (v4l2_subdev_call(sd, video, cropcap, &cropcap)) { + v4l2_err(&ici->v4l2_dev, + "%s %d cropcap err\n", __func__, __LINE__); + return -EINVAL; + } + + if ((cropcap.bounds.width - crop->c.left < crop->c.width) || + (cropcap.bounds.height - crop->c.top < crop->c.height)) { + v4l2_err(&ici->v4l2_dev, + "%s %d error crop values\n", __func__, __LINE__); + return -EINVAL; + } + + total_left = off_rect.left + crop->c.left; + total_top = off_rect.top + crop->c.top; + + ret = ispdrv_vi_set_crop(total_left, total_top, crop->c.width, crop->c.height); + if (ret) { + v4l2_err(&ici->v4l2_dev, + "%s %d error set isp crop\n", __func__, __LINE__); + return -EINVAL; + } + + pcdev->crop_x = total_left; + pcdev->crop_y = total_top; + pcdev->crop_width = crop->c.width; + pcdev->crop_height = crop->c.height; + + local_crop.c.left = total_left; + local_crop.c.top = total_top; + local_crop.c.width = crop->c.width; + local_crop.c.height = crop->c.height; + return v4l2_subdev_call(sd, video, s_crop, &local_crop); +} + +static int ak_camera_set_livecrop(struct soc_camera_device *icd, struct v4l2_crop *crop) +{ + + isp_dbg("entry %s\n", __func__); + + return ak_camera_set_crop(icd, crop); +} + +/** + * @brief: setting image format information, Called before ak_camera_set_fmt. + * + * @author: caolianming + * @date: 2014-01-06 + * @param [in] *icd: soc_camera_device information structure, + * akcamera depends on the soc driver. + * @param [in] *f: image format + */ +static int ak_camera_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + const struct soc_camera_format_xlate *xlate; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_mbus_framefmt mf; + int ret; + /* TODO: limit to ak hardware capabilities */ + CAMDBG("entry %s\n", __func__); + + xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); + if (!xlate) { + dev_warn(icd->parent, "Format %x not found\n", + pix->pixelformat); + return -EINVAL; + } + + mf.width = pix->width; + mf.height = pix->height; + mf.field = pix->field; + mf.colorspace = pix->colorspace; + mf.code = xlate->code; + + /* limit to sensor capabilities */ + ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf); + if (ret < 0) { + return ret; + } + + pix->width = mf.width; + pix->height = mf.height; + pix->field = mf.field; + pix->colorspace = mf.colorspace; + + return 0; +} + +/** + * @brief: setting image format information + * + * @author: caolianming + * @date: 2014-01-06 + * @param [in] *icd: soc_camera_device information structure, + * akcamera depends on the soc driver. + * @param [in] *f: image format + */ +static int ak_camera_set_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); +// struct soc_camera_host *ici = to_soc_camera_host(icd->parent); +// struct ak_camera_dev *pcdev = ici->priv; + const struct soc_camera_format_xlate *xlate; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_mbus_framefmt mf; + struct v4l2_cropcap cropcap; + int ret = 0, buswidth; + + isp_dbg("entry %s\n", __func__); + + xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); + if (!xlate) { + dev_warn(icd->parent, "Format %x not found\n", + pix->pixelformat); + return -EINVAL; + } + + buswidth = xlate->host_fmt->bits_per_sample; + if (buswidth > 10) { + dev_warn(icd->parent, + "bits-per-sample %d for format %x unsupported\n", + buswidth, pix->pixelformat); + return -EINVAL; + } + + mf.width = pix->width; + mf.height = pix->height; + mf.field = pix->field; + mf.colorspace = pix->colorspace; + mf.code = xlate->code; + icd->current_fmt = xlate; + + v4l2_subdev_call(sd, video, cropcap, &cropcap); + if (mf.width > cropcap.bounds.width) + mf.width = cropcap.bounds.width; + + if (mf.height > cropcap.bounds.height) + mf.height = cropcap.bounds.height; + + isp_dbg("%s. mf.width = %d, mf.height = %d\n", + __func__, mf.width, mf.height); + + if (mf.code != xlate->code) + return -EINVAL; + + ispdrv_vo_set_main_channel_scale(pix->width, pix->height); + + return ret; +} + +/** + * @brief: getting image format information + * + * @author: caolianming + * @date: 2014-01-06 + * @param [in] *icd: soc_camera_device information structure, + * akcamera depends on the soc driver. + * @param [in] *f: image format + */ +static int ak_camera_get_formats(struct soc_camera_device *icd, unsigned int idx, + struct soc_camera_format_xlate *xlate) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct device *dev = icd->parent; + struct soc_camera_host *ici = to_soc_camera_host(dev); + struct ak_camera_dev *pcdev = ici->priv; + int ret, formats = 0; + enum v4l2_mbus_pixelcode code; + const struct soc_mbus_pixelfmt *fmt; + + CAMDBG("entry %s\n", __func__); + ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code); + if (ret < 0) + /* No more formats */ + return 0; + + /* + * @Note: ISP only support yuv420 output and jpeg out. + * FIXME1: We miss jpeg here. + * FIXME2: the output squence of YUV is actually UYVY. + */ + fmt = soc_mbus_get_fmtdesc(V4L2_MBUS_FMT_YUYV8_2X8); + if (!fmt) { + dev_warn(dev, "unsupported format code #%u: %d\n", idx, code); + return 0; + } + CAMDBG("get format %s code=%d from sensor\n", fmt->name, code); + + /* Generic pass-through */ + formats++; + if (xlate) { + xlate->host_fmt = fmt; + xlate->code = code; + xlate++; + + /* + * @decide the default working mode of isp + * @prefer RGB mode + */ + if (code >= V4L2_MBUS_FMT_SBGGR8_1X8 && code <= V4L2_MBUS_FMT_SRGGB12_1X12) { + pcdev->def_mode = ISP_RGB_VIDEO_OUT; + //pcdev->def_mode = ISP_RGB_OUT; + } else if (code >= V4L2_MBUS_FMT_Y8_1X8 && + code <= V4L2_MBUS_FMT_YVYU10_1X20) { + pcdev->def_mode = ISP_YUV_VIDEO_OUT; + //pcdev->def_mode = ISP_YUV_OUT; + printk(KERN_ERR "set yuv video out\n"); + } else { + pcdev->def_mode = ISP_RGB_VIDEO_OUT; + } + +#if 0 + if ((pcdev->def_mode != ISP_RGB_VIDEO_OUT) + && (pcdev->def_mode != ISP_RGB_OUT)) { + pcdev->def_mode = ISP_RGB_VIDEO_OUT; + //pcdev->def_mode = ISP_YUV_BYPASS; + } +#endif + pcdev->cur_mode = pcdev->def_mode; + pcdev->cur_mode_class = code; + + dev_dbg(dev, "Providing format %s in pass-through mode\n", + fmt->name); + } + + return formats; +} + +static void ak_camera_put_formats(struct soc_camera_device *icd) +{ + CAMDBG("entry %s\n", __func__); + kfree(icd->host_priv); + icd->host_priv = NULL; + CAMDBG("leave %s\n", __func__); +} + +/* Maybe belong platform code fix me */ +static int ak_camera_set_bus_param(struct soc_camera_device *icd) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct ak_camera_dev *pcdev = ici->priv; + struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; + unsigned long common_flags; + int ret; + + CAMDBG("entry %s\n", __func__); + + /* AK39 supports 8bit and 10bit buswidth */ + ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); + if (!ret) { + common_flags = soc_mbus_config_compatible(&cfg, CSI_BUS_FLAGS); + if (!common_flags) { + dev_warn(icd->parent, + "Flags incompatible: camera 0x%x, host 0x%x\n", + cfg.flags, CSI_BUS_FLAGS); + return -EINVAL; + } + } else if (ret != -ENOIOCTLCMD) { + return ret; + } else { + common_flags = CSI_BUS_FLAGS; + } + + /* Make choises, based on platform choice */ + if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) && + (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) { + if (!pcdev->pdata || + pcdev->pdata->flags & AK_CAMERA_VSYNC_HIGH) + common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW; + else + common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH; + } + + if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) && + (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) { + if (!pcdev->pdata || + pcdev->pdata->flags & AK_CAMERA_PCLK_RISING) + common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING; + else + common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING; + } + + if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) && + (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) { + if (!pcdev->pdata || + pcdev->pdata->flags & AK_CAMERA_DATA_HIGH) + common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW; + else + common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH; + } + + cfg.flags = common_flags; + ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); + if (ret < 0 && ret != -ENOIOCTLCMD) { + dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n", + common_flags, ret); + return ret; + } + + CAMDBG("leave %s\n", __func__); + + return 0; +} + +/** + * @brief: register video buffer by video sub-system + * + * @author: caolianming + * @date: 2014-01-06 + * @param [in] *icd: soc_camera_device information structure, + * akcamera depends on the soc driver. + * @param [in] *q: V4L2 buffer queue information structure + */ +static void ak_camera_init_videobuf(struct videobuf_queue *q, + struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct ak_camera_dev *pcdev = ici->priv; + + CAMDBG("entry %s\n", __func__); + + videobuf_queue_dma_contig_init(q, &ak_videobuf_ops, icd->parent, + &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_NONE, + sizeof(struct ak_buffer), icd, &icd->video_lock); + + CAMDBG("leave %s\n", __func__); +} + +/** + * @brief: request video buffer. + * + * @author: caolianming + * @date: 2014-01-06 + * @param [in] *icd: soc_camera_device information structure, + * akcamera depends on the soc driver. + * @param [in] *q: V4L2 buffer queue information structure + */ +static int ak_camera_reqbufs(struct soc_camera_device *icd, + struct v4l2_requestbuffers *p) +{ + int i; + //struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + + CAMDBG("entry %s\n", __func__); + + /* This is for locking debugging only. I removed spinlocks and now I + * check whether .prepare is ever called on a linked buffer, or whether + * a dma IRQ can occur for an in-work or unlinked buffer. Until now + * it hadn't triggered */ + for (i = 0; i < p->count; i++) { + struct ak_buffer *buf = container_of(icd->vb_vidq.bufs[i], + struct ak_buffer, vb); + buf->inwork = 0; + INIT_LIST_HEAD(&buf->vb.queue); + } + + CAMDBG("leave %s\n", __func__); + + return 0; +} + +/* platform independent */ +static unsigned int ak_camera_poll(struct file *file, poll_table *pt) +{ + struct soc_camera_device *icd = file->private_data; + struct ak_buffer *buf; + + buf = list_entry(icd->vb_vidq.stream.next, struct ak_buffer, + vb.stream); + + poll_wait(file, &buf->vb.done, pt); + + if (buf->vb.state == VIDEOBUF_DONE || + buf->vb.state == VIDEOBUF_ERROR) { + return POLLIN | POLLRDNORM; + } + + return 0; +} + +static int ak_camera_querycap(struct soc_camera_host *ici, + struct v4l2_capability *cap) +{ + isp_dbg("entry %s\n", __func__); + + /* cap->name is set by the friendly caller:-> */ + strlcpy(cap->card, ak_cam_driver_description, sizeof(cap->card)); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + + return 0; +} + +/** + * @brief: The private interface of ISP for application user. + * setting ISP controller for image information + * + * @author: + * @date: + * @param + * akcamera depends on the soc driver. + * @param + */ +static int ak_camera_set_parm(struct soc_camera_device *icd, + struct v4l2_streamparm *a) +{ + int ret = 0; + int *parm_type; + enum camera_pcid pcid; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct ak_camera_dev *pcdev = ici->priv; + + isp_dbg("entry %s\n", __func__); + + parm_type = (int *)a->parm.raw_data; + pcid = *parm_type; + printk(KERN_ERR "%s pcid:%d\n", __func__, pcid); + switch(pcid) { + case PCID_CH2_OUTPUT_FMT: + { + struct pcid_ch2_output_fmt_data *ch2_fmt = (void *)a->parm.raw_data; + + ret = ispdrv_vo_set_sub_channel_scale(ch2_fmt->width, ch2_fmt->height); + printk(KERN_ERR "%s pcid:%d, ch2w:%d, ch2h:%d\n", __func__, pcid, ch2_fmt->width, ch2_fmt->height); + } + break; + + case PCID_A_FRAME_RAW: + //ret = continue_mode_capture_a_rawdata(pcdev); + pcdev->app_capture_a_rawdata = 1; + ret = 0;//continue_mode_capture_a_rawdata(pcdev); + if (ret) { + pr_err("PCID_A_FRAME_RAW: fail\n"); + } + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static struct soc_camera_host_ops ak_soc_camera_host_ops = { + .owner = THIS_MODULE, + .add = ak_camera_add_device, + .remove = ak_camera_remove_device, + .get_formats = ak_camera_get_formats, + .put_formats = ak_camera_put_formats, + .set_bus_param = ak_camera_set_bus_param, + .cropcap = ak_camera_cropcap, + .get_crop = ak_camera_get_crop, + .set_crop = ak_camera_set_crop, + .set_livecrop = ak_camera_set_livecrop, + .set_fmt = ak_camera_set_fmt, + .try_fmt = ak_camera_try_fmt, + .init_videobuf = ak_camera_init_videobuf, + .reqbufs = ak_camera_reqbufs, + .poll = ak_camera_poll, + .querycap = ak_camera_querycap, + .set_parm = ak_camera_set_parm, +}; + +static int ak_camera_probe(struct platform_device *pdev) +{ + struct ak_camera_dev *pcdev; + struct clk *clk, *cis_sclk, *mipi_clk; + unsigned int irq; + int err = 0; + + CAMDBG("entry %s\n", __func__); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + printk("platform_get_irq | platform_get_resource\n"); + err = -ENODEV; + goto exit; + } + + mipi_clk = clk_get(&pdev->dev, "mipi"); + if (IS_ERR(mipi_clk)) { + printk(KERN_ERR "%s get mipi asic clk error\n", __func__); + err = PTR_ERR(mipi_clk); + goto exit; + } + + /* + * @get isp working clock + */ + clk = clk_get(&pdev->dev, "camera"); + if (IS_ERR(clk)) { + err = PTR_ERR(clk); + goto exit; + } + + /* + * @get cis_sclk for sensor + */ + cis_sclk = clk_get(&pdev->dev, "sensor"); + if (IS_ERR(cis_sclk)) { + err = PTR_ERR(cis_sclk); + goto exit_put_clk; + } + + /* + ** @allocate memory to struct ak_camera, including struct soc_camera_host + ** @and struct v4l2_device + */ + pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL); + if (!pcdev) { + err = -ENOMEM; + goto exit_put_cisclk; + } + + /* @initailization for struct pcdev */ + INIT_LIST_HEAD(&pcdev->capture); +// pcdev->res = res; + pcdev->clk = clk; + pcdev->cis_sclk = cis_sclk; + pcdev->mipi_clk = mipi_clk; + + pcdev->list_state = LIST_ZERO; + pcdev->free_list = LIST_ZERO; + + pcdev->pdata = pdev->dev.platform_data; + if (!pcdev->pdata) { + err = -ENODEV; + goto exit_isp_fini; + } + + /* + * request irq + */ + err = request_irq(irq, ak_camera_dma_irq, IRQF_DISABLED, "ak_camera", pcdev); + if (err) { + err = -EBUSY; + goto exit_isp_fini; + } + pcdev->irq = irq; + + INIT_DELAYED_WORK(&pcdev->awb_work, isp_awb_work); + INIT_DELAYED_WORK(&pcdev->ae_work, isp_ae_work); + INIT_DELAYED_WORK(&pcdev->af_work, isp_af_work); + + /* + ** @register soc_camera_host + */ + pcdev->soc_host.drv_name = AK_CAM_DRV_NAME; + pcdev->soc_host.ops = &ak_soc_camera_host_ops; + pcdev->soc_host.priv = pcdev; + pcdev->soc_host.v4l2_dev.dev = &pdev->dev; + pcdev->soc_host.nr = pdev->id; + + err = soc_camera_host_register(&pcdev->soc_host); + if (err) { + goto exit_freeirq; + } + pcdev->cur_buf_id = -1; + + dev_info(&pdev->dev, "AK Camera driver loaded\n"); + + return 0; + +exit_freeirq: + free_irq(irq, pcdev); +exit_isp_fini: + kfree(pcdev); +exit_put_cisclk: + clk_put(cis_sclk); +exit_put_clk: + clk_put(clk); +exit: + return err; +} + +static int ak_camera_remove(struct platform_device *pdev) +{ + struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); + struct ak_camera_dev *pcdev = container_of(soc_host, + struct ak_camera_dev, soc_host); + + CAMDBG("entry %s\n", __func__); + + soc_camera_host_unregister(soc_host); + + free_irq(pcdev->irq, pcdev); + + kfree(pcdev); + + /* free clk */ + clk_put(pcdev->cis_sclk); + clk_put(pcdev->clk); + + dev_info(&pdev->dev, "AK Camera driver unloaded\n"); + + return 0; +} + +static struct platform_driver ak_camera_driver = { + .probe = ak_camera_probe, + .remove = ak_camera_remove, + .driver = { + .name = AK_CAM_DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init ak_camera_init(void) +{ + int ret; + + CAMDBG("entry %s\n", __func__); + + ret = aksensor_module_init(); + if (ret) + return ret; + + return platform_driver_register(&ak_camera_driver); +} + +static void __exit ak_camera_exit(void) +{ + CAMDBG("entry %s\n", __func__); + + aksensor_module_exit(); + + platform_driver_unregister(&ak_camera_driver); +} + +module_init(ak_camera_init); +module_exit(ak_camera_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("wu_daochao "); +MODULE_DESCRIPTION("Driver for ak Camera Interface"); + diff --git a/drivers/media/video/plat-anyka/ak_sensor.c b/drivers/media/video/plat-anyka/ak_sensor.c new file mode 100644 index 00000000..8cdf5e75 --- /dev/null +++ b/drivers/media/video/plat-anyka/ak_sensor.c @@ -0,0 +1,610 @@ +/* + * ak sensor Driver + * + * Copyright (C) 2012 Anyka + * + * Based on anykaplatform driver, + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +//#define SENSOR_DEBUG +#ifdef SENSOR_DEBUG +#define sensor_dbg(fmt...) printk(KERN_INFO "Sensor: " fmt) +#define SENDBG(fmt, args...) printk(KERN_INFO "Sensor: " fmt, ## args) +#else +#define sensor_dbg(fmt, args...) do{}while(0) +#define SENDBG(fmt, args...) do{}while(0) +#endif + + +void ak_sensor_set_sensor_cb(AK_ISP_SENSOR_CB *cb); +static AK_ISP_SENSOR_CB *cur_sensor_info = NULL; +static struct aksensor_color_format cur_sensor_cfmts; + +struct aksensor_priv { + struct v4l2_subdev subdev; + struct v4l2_ctrl_handler hdl; + struct aksensor_camera_info *info; + const struct aksensor_color_format *cfmt; + struct aksensor_win_size win; + int model; +}; + +int aksensor_get_sensor_id(void) +{ + return (cur_sensor_info ? cur_sensor_info->sensor_read_id_func() : 0); +} +EXPORT_SYMBOL_GPL(aksensor_get_sensor_id); + +int aksensor_get_sensor_if(char *if_str) +{ + int ret = -1; + int sensor_if = -1; + int mipi_lane = 1; + + if (cur_sensor_info && cur_sensor_info->sensor_get_parameter_func) + ret = cur_sensor_info->sensor_get_parameter_func(GET_INTERFACE, &sensor_if); + + if (!ret) { + switch (sensor_if) { + case DVP_INTERFACE: + strcpy(if_str, "dvp"); + break; + + case MIPI_INTERFACE: + ret = cur_sensor_info->sensor_get_parameter_func(GET_MIPI_LANE, &mipi_lane); + sprintf(if_str, "mipi%d", mipi_lane); + + if (ret) { + printk(KERN_ERR "%s not defined lane\n", __func__); + } + break; + + default: + printk(KERN_ERR "%s not defined interface:%d\n", __func__, sensor_if); + strcpy(if_str, "unknowif"); + break; + } + } else { + strcpy(if_str, "unknowif"); + } + + return 0; +} +EXPORT_SYMBOL_GPL(aksensor_get_sensor_if); + +/*****************************************/ +static struct aksensor_priv *to_aksensor(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct aksensor_priv, subdev); +} + +/** + * @brief camera probe pointer + * @author dengzhou + * @date 2012-03-16 + * @param + * @return sensor_info * camera device pointer + * @retval + */ +static AK_ISP_SENSOR_CB *probe_sensors(struct i2c_client *client) +{ + struct aksensor_priv *priv = to_aksensor(client); + int read_id, probe_id; + int index = 0; + AK_ISP_SENSOR_CB *si; + + do { + si = (AK_ISP_SENSOR_CB *)ispdrv_get_sensor(&index); + if (si) { + si->sensor_set_power_on_func(priv->info->pin_pwdn, priv->info->pin_reset); + + read_id = si->sensor_read_id_func(); + probe_id = si->sensor_probe_id_func(); + + if (read_id == probe_id) { + dev_info(&client->dev, "%s Probing Sensor ID: 0x%x\n", + __func__, read_id); + return si; + } else { + si->sensor_set_power_off_func(priv->info->pin_pwdn, priv->info->pin_reset); + } + } + index++; + } while (si); + + return NULL; +} + +/** + * @brief: initial sensor device, open clock + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] *sd: v4l2_subdev struct, v4l2 sub-device info + * @param [in] val: none + */ +static int aksensor_init(struct v4l2_subdev *sd, u32 val) +{ + struct aksensor_priv *priv = container_of(sd, struct aksensor_priv, subdev); + + SENDBG("entry %s\n", __func__); + return cur_sensor_info->sensor_set_power_on_func(priv->info->pin_pwdn, priv->info->pin_reset); +} + +/** + * @brief: close sensor device, close clock + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] *sd: v4l2_subdev struct, v4l2 sub-device info + * @param [in] val: none + */ +static int aksensor_reset( struct v4l2_subdev *sd, u32 val ) +{ + struct aksensor_priv *priv = container_of(sd, struct aksensor_priv, subdev); + + SENDBG("entry %s\n", __func__); + return cur_sensor_info->sensor_set_power_off_func(priv->info->pin_pwdn, priv->info->pin_reset); +} + +static int aksensor_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) +{ + SENDBG("entry %s\n", __func__); + + id->ident = cur_sensor_info->sensor_read_id_func(); + id->revision = 0; + + return 0; +} + +static int aksensor_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + printk(KERN_ERR "%s no support any ctrl\n", __func__); + return -1; +} + +/** + * @brief: query v4l2 sub-device ctroller function + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] *sd: v4l2_subdev struct, v4l2 sub-device info + * @param [in] *ctrl: v4l2_control struct + */ +static int aksensor_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + printk(KERN_ERR "%s no support any ctrl\n", __func__); + return -1; +} + +static struct v4l2_subdev_core_ops aksensor_subdev_core_ops = { + .init = aksensor_init, + .reset = aksensor_reset, + .g_chip_ident = aksensor_g_chip_ident, + .g_ctrl = aksensor_g_ctrl, + .s_ctrl = aksensor_s_ctrl, +}; + +/* + * soc_camera_ops function + */ +static int aksensor_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct aksensor_priv *priv = container_of(sd, struct aksensor_priv, subdev); + + SENDBG("entry %s\n", __func__); + + switch (enable) { + case 0: + case -2: + /* standby */ + printk("aksensor_s_stream standby.\n"); + cur_sensor_info->sensor_set_standby_in_func(priv->info->pin_pwdn, priv->info->pin_reset); + break; + case 1: + case -1: + /* normal */ + printk("aksensor_s_stream resume.\n"); + cur_sensor_info->sensor_set_standby_out_func(priv->info->pin_pwdn, priv->info->pin_reset); + break; + default: + break; + } + + return 0; +} + +static int aksensor_get_params(struct i2c_client *client, + enum v4l2_mbus_pixelcode code) +{ + struct aksensor_priv *priv = to_aksensor(client); + int ret = -EINVAL; + + SENDBG("entry %s\n", __func__); + + /* + * select format + */ + priv->cfmt = NULL; + if (code == cur_sensor_cfmts.code) { + priv->cfmt = &cur_sensor_cfmts; + } + if (!priv->cfmt) + goto aksensor_set_fmt_error; + + return 0; + +aksensor_set_fmt_error: + priv->cfmt = NULL; + return ret; +} + +static int aksensor_get_num_params(struct i2c_client *client, int num) +{ + struct aksensor_priv *priv = to_aksensor(client); + int ret = -EINVAL; + + SENDBG("entry %s\n", __func__); + + /* + * select format + */ + priv->cfmt = NULL; + if (num <= 1) + priv->cfmt = &cur_sensor_cfmts; + + if (!priv->cfmt) + goto aksensor_set_fmt_error; + + return 0; + +aksensor_set_fmt_error: + priv->cfmt = NULL; + return ret; +} + +/* first called by soc_camera_prove to initialize icd->user_width... */ +static int aksensor_g_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct aksensor_priv *priv = container_of(sd, struct aksensor_priv, subdev); + int ret; + + SENDBG("entry %s\n", __func__); + + if (!priv->cfmt) { + SENDBG("select VGA for first time\n"); + ret = aksensor_get_params(client, V4L2_MBUS_FMT_YUYV8_2X8); + if (ret) + ret = aksensor_get_num_params(client, 0); + if (ret < 0) + return ret; + } + + mf->width = priv->win.width; + mf->height = priv->win.height; + mf->code = priv->cfmt->code; + mf->colorspace = priv->cfmt->colorspace; + mf->field = V4L2_FIELD_NONE; + + return 0; +} + + +static int aksensor_try_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + SENDBG("entry %s\n", __func__); + SENDBG("leave %s\n", __func__); + + return 0; +} + +static int aksensor_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct aksensor_priv *priv = to_aksensor(client); + int width, height; + int w, h; + + sensor_dbg("entry %s. priv=%p\n", __func__, priv); + + cur_sensor_info->sensor_get_resolution_func(&width, &height); + cur_sensor_info->sensor_get_valid_coordinate_func(&w, &h); + width -= w; + height -= h; + a->bounds.width = width; + a->bounds.height = height; + a->bounds.left = 0; + a->bounds.top = 0; + + a->defrect.width = priv->win.width; + a->defrect.height = priv->win.height; + a->defrect.left = 0; + a->defrect.top = 0; + + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + //printk(KERN_ERR "%s i:%d, w:%d, h:%d\n", __func__, i, cur_sensor_info->resolution[i].width, cur_sensor_info->resolution[i].height); + + sensor_dbg("%s.\n" + " a->bounds.width=%d, a->bounds.height=%d\n" + " a->defrect.width=%d, a->defrect.height=%d\n", + __func__, + a->bounds.width, a->bounds.height, + a->defrect.width, a->defrect.height); + return 0; +} + +static int aksensor_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct aksensor_priv *priv = to_aksensor(client); + + sensor_dbg("entry %s\n", __func__); + + a->c.left = priv->win.left; + a->c.top = priv->win.top; + a->c.width = priv->win.width; + a->c.height = priv->win.height; + + return 0; +} + +static int aksensor_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct aksensor_priv *priv = to_aksensor(client); + + sensor_dbg("entry %s\n", __func__); + + priv->win.left = a->c.left; + priv->win.top = a->c.top; + priv->win.width = a->c.width; + priv->win.height = a->c.height; + + return 0; +} + +static int aksensor_video_probe(struct i2c_client *client) +{ + struct aksensor_priv *priv = to_aksensor(client); + enum sensor_bus_type bus_type; + + SENDBG("entry %s\n", __func__); + + /* + * check and show product ID and manufacturer ID + */ + ak_sensor_i2c_set_client(client); + if (cur_sensor_info != NULL) { + dev_info(&client->dev, "Probing Sensor ID 0x%x\n", + cur_sensor_info->sensor_read_id_func()); + } else { + cur_sensor_info = probe_sensors(client); + if (cur_sensor_info == NULL) { + dev_err(&client->dev, "Sensor ID error\n"); + return -ENODEV; + } + } + + bus_type = cur_sensor_info->sensor_get_bus_type_func(); + switch (bus_type) { + case BUS_TYPE_RAW: + cur_sensor_cfmts.code = /*V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE;*/V4L2_MBUS_FMT_SBGGR8_1X8; + cur_sensor_cfmts.colorspace = V4L2_COLORSPACE_SRGB; + break; + + case BUS_TYPE_YUV: + cur_sensor_cfmts.code = V4L2_MBUS_FMT_YUYV8_2X8; + cur_sensor_cfmts.colorspace = V4L2_COLORSPACE_SMPTE170M; + break; + + default: + cur_sensor_cfmts.code = V4L2_MBUS_FMT_SBGGR8_1X8; + cur_sensor_cfmts.colorspace = V4L2_COLORSPACE_SRGB; + printk(KERN_ERR "%s bus type:%d not defined!!!!!!!!!!\n", __func__, bus_type); + break; + } + + priv->model = cur_sensor_info->sensor_read_id_func(); + + return 0; +} + +static int aksensor_enum_fmt(struct v4l2_subdev *sd, unsigned int index, + enum v4l2_mbus_pixelcode *code) +{ + if (index >= 1) + return -EINVAL; + + *code = cur_sensor_cfmts.code; + return 0; +} + +static int aksensor_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + + SENDBG("entry %s\n", __func__); + cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | + V4L2_MBUS_DATA_ACTIVE_HIGH; + cfg->type = V4L2_MBUS_PARALLEL; + cfg->flags = soc_camera_apply_board_flags(icl, cfg); + SENDBG("leave %s\n", __func__); + + return 0; +} + +static struct v4l2_subdev_video_ops aksensor_subdev_video_ops = { + .s_stream = aksensor_s_stream, + .cropcap = aksensor_cropcap, + .g_crop = aksensor_g_crop, + .s_crop = aksensor_s_crop, + .enum_mbus_fmt = aksensor_enum_fmt, + .try_mbus_fmt = aksensor_try_fmt, + .g_mbus_config = aksensor_g_mbus_config, + .g_mbus_fmt = aksensor_g_fmt, +}; + +static struct v4l2_subdev_ops aksensor_subdev_ops = { + .core = &aksensor_subdev_core_ops, + .video = &aksensor_subdev_video_ops, +}; + +/* + * i2c_driver function + */ +static int aksensor_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct aksensor_priv *priv; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + int ret; + int width, height; + int w, h; + + SENDBG("entry %s\n", __func__); + + if (!icl || !icl->priv) { + dev_err(&client->dev, "AKSENSOR: missing platform data!\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&adapter->dev, + "I2C-Adapter doesn't support " + "I2C_FUNC_SMBUS_BYTE_DATA\n"); + return -EIO; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + return -ENOMEM; + } + + priv->info = icl->priv; + v4l2_i2c_subdev_init(&priv->subdev, client, &aksensor_subdev_ops); + + ret = aksensor_video_probe(client); + if (ret) { + kfree(priv); + return ret; + } + + /* register sensor callback to ISPDRV */ + ak_sensor_set_sensor_cb(cur_sensor_info); + +#if 0 + v4l2_ctrl_handler_init(&priv->hdl, cur_sensor_info->nr_ctrls); + for (i = 0; i < cur_sensor_info->nr_ctrls; i++) + v4l2_ctrl_new_custom(&priv->hdl, &cur_sensor_info->ctrls[i], NULL); + priv->subdev.ctrl_handler = &priv->hdl; + if (priv->hdl.error) { + int err = priv->hdl.error; + v4l2_ctrl_handler_free(&priv->hdl); + kfree(priv); + return err; + } +#endif + + // init sensor resolution, default VGA + cur_sensor_info->sensor_get_resolution_func(&width, &height); + cur_sensor_info->sensor_get_valid_coordinate_func(&w, &h); + width -= w; + height -= h; + priv->win.width = width; + priv->win.height = height; + /*printk(KERN_ERR "%s: priv->win.width=%d priv->win.height=%d\n", + __func__, priv->win.width, priv->win.height);*/ + sensor_dbg("%s: priv->win.width=%d priv->win.height=%d\n", + __func__, priv->win.width, priv->win.height); + return ret; +} + +static int aksensor_remove(struct i2c_client *client) +{ + struct aksensor_priv *priv = to_aksensor(client); + + cur_sensor_info->sensor_set_power_off_func(priv->info->pin_pwdn, priv->info->pin_reset); + + v4l2_device_unregister_subdev(&priv->subdev); + //v4l2_ctrl_handler_free(&priv->hdl); + kfree(priv); + + ispdrv_remove_all_sensors(); + + return 0; +} + +static const struct i2c_device_id aksensor_id[] = { + { "aksensor", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aksensor_id); + +static struct i2c_driver aksensor_i2c_driver = { + .driver = { + .name = "aksensor", + }, + .probe = aksensor_probe, + .remove = aksensor_remove, + .id_table = aksensor_id, +}; + +/* + * module function + */ + +int aksensor_module_init(void) +{ + SENDBG("entry %s\n", __func__); + + printk(KERN_ERR "%s\n", __func__); + return i2c_add_driver(&aksensor_i2c_driver); +} + +void aksensor_module_exit(void) +{ + SENDBG("entry %s\n", __func__); + + i2c_del_driver(&aksensor_i2c_driver); +} + +/*module_init(aksensor_module_init); +module_exit(aksensor_module_exit); + +MODULE_DESCRIPTION("SoC Camera driver for aksensor"); +MODULE_AUTHOR("dengzhou"); +MODULE_LICENSE("GPL v2"); +*/ diff --git a/drivers/media/video/plat-anyka/ak_sensor_common.c b/drivers/media/video/plat-anyka/ak_sensor_common.c new file mode 100644 index 00000000..12b4396d --- /dev/null +++ b/drivers/media/video/plat-anyka/ak_sensor_common.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include "ak_sensor_common.h" + +int ak_sensor_read_register(struct ak_sensor_i2c_data *p_i2c_data) +{ + return sensor_read_register((T_SENSOR_I2C_DATA_S *)p_i2c_data); +} +EXPORT_SYMBOL_GPL(ak_sensor_read_register); + +int ak_sensor_write_register(const struct ak_sensor_i2c_data *p_i2c_data) +{ + return sensor_write_register((T_SENSOR_I2C_DATA_S *)p_i2c_data); +} +EXPORT_SYMBOL_GPL(ak_sensor_write_register); + +int ak_sensor_set_pin_as_gpio(const int pin) +{ + unsigned int tmp; + + tmp = (unsigned int)pin; + return ak_setpin_as_gpio(tmp); +} +EXPORT_SYMBOL_GPL(ak_sensor_set_pin_as_gpio); + +int ak_sensor_set_pin_dir(const int pin, const int is_output) +{ + unsigned int tmp; + + tmp = (unsigned int)pin; + if (is_output) + return ak_gpio_cfgpin(tmp, AK_GPIO_DIR_OUTPUT); + else + return ak_gpio_cfgpin(tmp, AK_GPIO_DIR_INPUT); +} +EXPORT_SYMBOL_GPL(ak_sensor_set_pin_dir); + +int ak_sensor_set_pin_level(const int pin, const int level) +{ + unsigned int tmp; + + tmp = (unsigned int)pin; + return ak_gpio_setpin(tmp, level ? 1 : 0); +} +EXPORT_SYMBOL_GPL(ak_sensor_set_pin_level); + +int ak_sensor_set_pin_pull(const int pin, const int is_pullup, const int is_enable) +{ + unsigned int tmp; + + tmp = (unsigned int)pin; + if (is_pullup) + return ak_gpio_pullup(tmp, is_enable); + else + return ak_gpio_pulldown(tmp, is_enable); +} +EXPORT_SYMBOL_GPL(ak_sensor_set_pin_pull); + +int ak_sensor_mdelay(const int msec) +{ + mdelay(msec); + return 0; +} +EXPORT_SYMBOL_GPL(ak_sensor_mdelay); diff --git a/drivers/media/video/plat-anyka/ak_sensor_common.h b/drivers/media/video/plat-anyka/ak_sensor_common.h new file mode 100644 index 00000000..9780ba13 --- /dev/null +++ b/drivers/media/video/plat-anyka/ak_sensor_common.h @@ -0,0 +1,53 @@ +#ifndef __ak_sensor_common_h__ +#define __ak_sensor_common_h__ + +#include +#include +#include + +#define ak_sensor_print printk + +struct ak_sensor_i2c_data { + unsigned char u8DevAddr; + unsigned int u32RegAddr; + unsigned int u32RegAddrByteNum; + unsigned int u32Data; + unsigned int u32DataByteNum; + unsigned int reserved[2]; +}; + +struct ak_sensor_parameter { + u32 sensor_ucode; + unsigned int sensor_id; + unsigned int sensor_mclk; + unsigned int sensor_io_interface; + unsigned int mipi_mbps; + unsigned int mipi_lane; + unsigned int pclk_hz; +}; + +int ak_sensor_read_register(struct ak_sensor_i2c_data *p_i2c_data); +int ak_sensor_write_register(const struct ak_sensor_i2c_data *p_i2c_data); + +int ak_sensor_set_pin_as_gpio(const int pin); +int ak_sensor_set_pin_dir(const int pin, const int is_output); +int ak_sensor_set_pin_level(const int pin, const int level); +int ak_sensor_set_pin_pull(const int pin, const int is_pullup, const int is_enable); + +int ak_sensor_mdelay(const int msec); + +#define AK_SENSOR_MODULE(callback, name) \ +static int name##_module_init(void) \ +{ \ + return ispdrv_register_sensor(&callback); \ +} \ +static void name##_module_exit(void) \ +{ \ +} \ +module_init(name##_module_init); \ +module_exit(name##_module_exit); \ +MODULE_LICENSE("GPL"); \ +MODULE_AUTHOR("ye_guohong "); \ +MODULE_DESCRIPTION("Driver for Sensor Interface"); + +#endif diff --git a/drivers/media/video/plat-anyka/ak_sensor_i2c.c b/drivers/media/video/plat-anyka/ak_sensor_i2c.c new file mode 100644 index 00000000..9743e6da --- /dev/null +++ b/drivers/media/video/plat-anyka/ak_sensor_i2c.c @@ -0,0 +1,212 @@ +/* + * ak sensor Driver + * + * Copyright (C) 2012 Anyka + * + * Based on anykaplatform driver, + * + */ + +#include +#include +#include + +static struct i2c_client *g_client = NULL; + +/** + * @brief: write sensor register by i2c bus + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] raddr: sensor device register(cmd) + * @param [in] *data: pointer to data, the data writed to sensor register + * @param [in] size: data number + */ +static s32 aksensor_i2c_write_byte_short(u16 raddr, u8 data) +{ + unsigned char msg[3]; + + msg[0] = raddr >> 8; + msg[1] = raddr & 0xff; + msg[2] = data; + +// printk("msg=0x%02x%02x, 0x%02x(write)\n", msg[0], msg[1], msg[2]); + return i2c_master_send(g_client, msg, 3); +} + +/** + * @brief: read sensor register value by i2c bus + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] raddr: sensor device register(cmd) + */ +static s32 aksensor_i2c_read_byte_short(u16 raddr) +{ + int ret; + unsigned char msg[2]; + unsigned char data; + + msg[0] = raddr >> 8; + msg[1] = raddr & 0xff; + + ret = i2c_master_send(g_client, msg, 2); + if (ret < 0) + return ret; + + ret = i2c_master_recv(g_client, &data, 1); + if (ret < 0) + return ret; + + // printk("msg=0x%02x%02x, 0x%02x(read)\n", msg[0], msg[1], data); + + return data; +} + +/** + * @brief: write sensor register by i2c bus + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] raddr: sensor device register(cmd) + * @param [in] *data: pointer to data, the data writed to sensor register + * @param [in] size: data number + */ +static s32 aksensor_i2c_write_word_data(u16 raddr, u16 data) +{ + unsigned char msg[4]; + + msg[0] = raddr >> 8; //high 8bit first send + msg[1] = raddr & 0xff; //low 8bit second send + msg[2] = data >> 8; //high 8bit firt send + msg[3] = data & 0xff; //low 8bit second send + + //printk("(cmd): 0x%02x %02x, (data): %02x %02x\n", msg[0], msg[1], msg[2], msg[3]); + return i2c_master_send(g_client, msg, 4); +} + +/** + * @brief: read sensor register value by i2c bus + * + * @author: caolianming + * @date: 2014-01-09 + * @param [in] raddr: sensor device register(cmd) + */ +static s32 aksensor_i2c_read_word_data(u16 raddr) +{ + int ret; + unsigned char msg[4]; + unsigned char buf[2]; + + msg[0] = raddr >> 8; //high 8bit first send + msg[1] = raddr & 0xff; //low 8bit second send + + ret = i2c_master_send(g_client, msg, 2); + if (ret < 0) + return ret; + + ret = i2c_master_recv(g_client, buf, 2); + if (ret < 0) + return ret; + + return (buf[0] << 8) | buf[1]; +} + +s32 ak_sensor_i2c_set_client(struct i2c_client *client) +{ + g_client = client; + return 0; +} +EXPORT_SYMBOL(ak_sensor_i2c_set_client); + +s32 sensor_read_register(T_SENSOR_I2C_DATA_S *pI2cData) +{ + int retry = 0, retry_max = 5; + s32 ret = 0; + + if (!g_client) { + printk(KERN_ERR "%s g_client is NULL\n", __func__); + return 0; + } + + g_client->addr = pI2cData->u8DevAddr >> 1; + +__retry: + switch (pI2cData->u32RegAddrByteNum) { + case 1: + if (pI2cData->u32DataByteNum == 1) + ret = i2c_smbus_read_byte_data(g_client, pI2cData->u32RegAddr); + else if (pI2cData->u32RegAddrByteNum == 2) + ret = i2c_smbus_read_word_data(g_client, pI2cData->u32RegAddr); + break; + case 2: + if (pI2cData->u32DataByteNum == 1) + ret = aksensor_i2c_read_byte_short(pI2cData->u32RegAddr); + else if (pI2cData->u32RegAddrByteNum == 2) + ret = aksensor_i2c_read_word_data(pI2cData->u32RegAddr); + break; + default: + break; + } + + if ((ret < 0) && (retry++ < retry_max)) { + goto __retry; + } + + if (retry > 0) { + printk(KERN_ERR "i2c read dev:0x%x reg[0x%x] retry:%d\n", (pI2cData->u8DevAddr >> 1) << 1, pI2cData->u32RegAddr, retry); + } + + if (ret < 0) { + printk(KERN_ERR "i2c read dev:0x%x reg[0x%x] fail\n", (pI2cData->u8DevAddr >> 1) << 1, pI2cData->u32RegAddr); + } + + return ret; +} +EXPORT_SYMBOL(sensor_read_register); + +s32 sensor_write_register(T_SENSOR_I2C_DATA_S *pI2cData) +{ + int retry = 0, retry_max = 5; + s32 ret = 0; + + if (!g_client) { + printk(KERN_ERR "%s g_client is NULL\n", __func__); + return -1; + } + + g_client->addr = pI2cData->u8DevAddr >> 1; + +__retry: + switch (pI2cData->u32RegAddrByteNum) { + case 1: + if (pI2cData->u32DataByteNum == 1) + ret = i2c_smbus_write_byte_data(g_client, pI2cData->u32RegAddr, pI2cData->u32Data); + else if (pI2cData->u32RegAddrByteNum == 2) + ret = i2c_smbus_write_word_data(g_client, pI2cData->u32RegAddr, pI2cData->u32Data); + break; + case 2: + if (pI2cData->u32DataByteNum == 1) + ret = aksensor_i2c_write_byte_short(pI2cData->u32RegAddr, pI2cData->u32Data); + else if (pI2cData->u32RegAddrByteNum == 2) + ret = aksensor_i2c_write_word_data(pI2cData->u32RegAddr, pI2cData->u32Data); + break; + default: + break; + } + + if ((ret < 0) && (retry++ < retry_max)) { + goto __retry; + } + + if (retry > 0) { + printk(KERN_ERR "i2c write dev:0x%x reg[0x%x] data[0x%x] retry:%d\n", (pI2cData->u8DevAddr >> 1) << 1, pI2cData->u32RegAddr, pI2cData->u32Data, retry); + } + + if (ret < 0) { + printk(KERN_ERR "i2c write dev:0x%x reg[0x%x] data[0x%x] fail\n", (pI2cData->u8DevAddr >> 1) << 1, pI2cData->u32RegAddr, pI2cData->u32Data); + } + + return ret; +} +EXPORT_SYMBOL(sensor_write_register); diff --git a/drivers/media/video/plat-anyka/sensor_H63.c b/drivers/media/video/plat-anyka/sensor_H63.c new file mode 100644 index 00000000..c80bb923 --- /dev/null +++ b/drivers/media/video/plat-anyka/sensor_H63.c @@ -0,0 +1,565 @@ +/** + * @file camera_h63.c + * @brief camera driver file + * Copyright (C) 2011 Anyka (Guangzhou) Microelectronics Technology Co., Ltd + * @author xia_wenting + * @date 2011-09-21 + * @version 1.0 + * @ref + */ +#include "ak_sensor_common.h" + +#define SENSOR_PWDN_LEVEL 1 +#define SENSOR_RESET_LEVEL 0 +#define SENSOR_I2C_ADDR 0x80 +#define SENSOR_ID 0x0a63 +#define SENSOR_MCLK 27 +#define SENSOR_REGADDR_BYTE 1 +#define SENSOR_DATA_BYTE 1 +#define MAX_FPS 30 +#define SENSOR_OUTPUT_WIDTH 1280 +#define SENSOR_OUTPUT_HEIGHT 720 +#define SENSOR_VALID_OFFSET_X 0 +#define SENSOR_VALID_OFFSET_Y 0 +#define SENSROR_BUS_TYPE BUS_TYPE_RAW + +#define SENSOR_IO_INTERFACE MIPI_INTERFACE +#define SENSOR_IO_LEVEL SENSOR_IO_LEVEL_1V8 + +#define SKIP_NUM 2 +#define EXP_EFFECT_FRAMES 1 +#define VSYNC_ACTIVE_MS 31 +#define MIPI_MHZ 216 +#define MIPI_LANE 1 +#define EFFECT_FRAMES 1 + + +#define PCLK_FREQ (43200000) + +static int _target_frame_height = 0; +static int _target_exp_ctrl = 0; +static int frame_width = 0; + +#define OV_MAX(a, b) (((a) < (b) ) ? (b) : (a)) +#define OV_MIN(a, b) (((a) > (b) ) ? (b) : (a)) +#define OV_CLIP3(low, high, x) (OV_MAX(OV_MIN((x), high), low)) + +static int h63_cmos_updata_d_gain(const unsigned int d_gain); +static int h63_cmos_updata_a_gain(const unsigned int a_gain); +static int h63_cmos_updata_exp_time(unsigned int exp_time); + + +static int __i2c_read (int reg) { + + struct ak_sensor_i2c_data i2cdata; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = 0; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_read_register(&i2cdata); +} + +static int __i2c_write (int reg, int data) { + + struct ak_sensor_i2c_data i2cdata; +// ak_sensor_print("%s~~~~~~~~~~reg:0x%x,data:0x%x\n",__func__,reg,data); + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = data; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_write_register(&i2cdata); +} + +/** + * 获取曝光控制的值。 + */ +static int get_exp_ctrl (void) +{ + + int const reg_exp_ctrl_15_8 = __i2c_read (0x02); + int const reg_exp_ctrl_7_0 = __i2c_read (0x01); + + return (reg_exp_ctrl_15_8 << 8) | reg_exp_ctrl_7_0; +} + +static void set_exp_ctrl (int v) { + + //printk(KERN_ERR "Active Exp Ctrl = %d\r\n",v); + __i2c_write (0x02, v >> 8); + __i2c_write (0x01, v & 0xff); +} + +/** + * 从寄存器读出是半帧的宽度。 + */ +static int __get_frame_half_width () { + + if (0 == frame_width) { + /// 这里重复读取 I2C 会导致 I2C 超时, + /// 通过缓冲见 + + int const reg_frame_w_15_8 = __i2c_read (0x21); + int const reg_frame_w_7_0 = __i2c_read (0x20); + frame_width = (reg_frame_w_15_8 << 8) | reg_frame_w_7_0; + } + +// printk(KERN_ERR "Get Frame Width = %d\r\n", frame_width); + return frame_width; + +} + +/** + * 适配运算 FPS 时需要转换成全帧的宽度。 + */ +#define __get_frame_width() (2 * __get_frame_half_width ()) + + +static int _cache_height = 0; +static int __get_frame_height () { + + if (0 == _cache_height){ + _cache_height = (__i2c_read (0x23) << 8) | __i2c_read (0x22); + } + + return _cache_height; +} + +static void __set_frame_height (int const frameh) { + + if (frameh != _cache_height) { + __i2c_write (0x23, frameh >> 8); + __i2c_write (0x22, frameh & 0xff); + _cache_height = frameh; + } +} + +/** + * 同时更新帧长与曝光 + */ +static void update_frame_height_and_exp_ctrl () { + + int const cur_frame_height = __get_frame_height (); + + /// 将要设置的帧长与曝光需要匹配,帧率优先,曝光需要满足帧长前提。 + int const target_frame_height = _target_frame_height; + int const target_exp_ctrl = _target_exp_ctrl <= target_frame_height - 1 ? _target_exp_ctrl : target_frame_height - 1; + + //printk(KERN_ERR "Active FrameHeight=%d ExpCtrl=%d\r\n", target_frame_height, target_exp_ctrl); + if (target_frame_height >= cur_frame_height) { + /// 帧长比原来变长了,这个时候需要先设帧长,腾出空间设置曝光。 + __set_frame_height (target_frame_height); + set_exp_ctrl (target_exp_ctrl); + } else { + /// 帧长比原来变短了,这个时候先设曝光,再设置帧率减小帧间隔。 + set_exp_ctrl (target_exp_ctrl); + __set_frame_height (target_frame_height); + } +} + + +static int h63_init(const AK_ISP_SENSOR_INIT_PARA *para) +{ + int i; + AK_ISP_SENSOR_REG_INFO *preg_info; +int value; + + preg_info = para->reg_info; + for (i = 0; i < para->num; i++) { +//value=h63_sensor_read_register(preg_info->reg_addr); +//ak_sensor_print("before w reg:0x%.x, val:0x%.x, to write:0x%.x\n", preg_info->reg_addr, value, preg_info->value); + //ak_sensor_print("before w reg:0x%.x,to write:0x%.x\n", preg_info->reg_addr, preg_info->value); + + if (preg_info->reg_addr == 0xffff) + ak_sensor_mdelay(preg_info->value); + else + __i2c_write(preg_info->reg_addr, preg_info->value); + //value=__i2c_read(preg_info->reg_addr); + //ak_sensor_print("after w reg:0x%.x, val:0x%.x\n", preg_info->reg_addr, value); + preg_info++; + } + + /// 初始化帧长与曝光的初始化值。 + _target_frame_height = __get_frame_height (); + _target_exp_ctrl = get_exp_ctrl (); + + return 0; +} + +static int h63_read_id(void) +{ + return SENSOR_ID; +} + +static int h63_probe_id(void) +{ + u8 value; + u32 id; + + value = __i2c_read(0x0a); + id = value << 8; + value = __i2c_read(0x0b); + id |= value; + + if (id == SENSOR_ID) + return SENSOR_ID; + + return 0; +} + +static int h63_get_resolution(int *width, int *height) +{ + *width = SENSOR_OUTPUT_WIDTH; + *height = SENSOR_OUTPUT_HEIGHT; + + return 0; +} + +static int h63_get_mclk(void) +{ + return SENSOR_MCLK; +} + +static int h63_get_fps(void) +{ + int fps = PCLK_FREQ; + + fps /= __get_frame_width (); + fps /= __get_frame_height (); + + return fps; +} + +static int h63_get_valid_coordinate(int *x, int *y) +{ + *x = SENSOR_VALID_OFFSET_X; + *y = SENSOR_VALID_OFFSET_Y; + + return 0; +} + +static enum sensor_bus_type h63_get_bus_type(void) +{ + return SENSROR_BUS_TYPE; +} + +static int h63_get_parameter(int param, void *value) +{ + int ret = 0; + enum sensor_get_param name = (enum sensor_get_param)param; + + switch (name) { + case GET_MIPI_MHZ: + *((int *)value) = MIPI_MHZ; + break; + + case GET_VSYNC_ACTIVE_MS: + *((int *)value) = VSYNC_ACTIVE_MS; + break; + + case GET_CUR_FPS: + *((int *)value) = h63_get_fps (); + break; + + case GET_MIPI_LANE: + *((int *)value) = MIPI_LANE; + break; + + case GET_INTERFACE: + *((int *)value) = SENSOR_IO_INTERFACE; + break; + + case GET_SENSOR_IO_LEVEL: + *((int *)value) = SENSOR_IO_LEVEL; + break; + + default: + ret = -1; + break; + } + + return ret; +} +static int h63_set_power_on(const int pwdn_pin, const int reset_pin) +{ + ak_sensor_set_pin_as_gpio(pwdn_pin); + ak_sensor_set_pin_dir(pwdn_pin, 1); + ak_sensor_set_pin_level(pwdn_pin, !SENSOR_PWDN_LEVEL); + ak_sensor_mdelay(10); + ak_sensor_set_pin_as_gpio(reset_pin); + ak_sensor_set_pin_dir(reset_pin, 1); + ak_sensor_set_pin_level(reset_pin, SENSOR_RESET_LEVEL); + ak_sensor_mdelay(10); + ak_sensor_set_pin_level(reset_pin, !SENSOR_RESET_LEVEL); + ak_sensor_mdelay(20); + + ak_sensor_print("%s\n",__func__); + return 0; +} + +static int h63_set_power_off(const int pwdn_pin, const int reset_pin) +{ +#if 0 + u8 Reg0x3d = 0x48; + u8 Reg0xc3 = 0x00; +#endif + +#if 0 + //sccb software standby mode + + __i2c_write(0x3d, Reg0x3d); + __i2c_write(0xc3, Reg0xc3); +#endif + + ak_sensor_set_pin_level(pwdn_pin, SENSOR_PWDN_LEVEL); + ak_sensor_set_pin_dir(reset_pin, 0); + + return 0; +} + +static int h63_set_fps(const int fps) +{ + int target_frame_height = 0; + + + /// FPS = PCLK / FRAMEW / FRAMEH + /// 计算出将要设置的帧长度。 + target_frame_height = PCLK_FREQ / __get_frame_width (); + target_frame_height /= fps; + + /// 这里只是简单登记当前需要设置的帧率, + /// 由于出现帧率与曝光不匹配会挂掉的问题, + /// 这里不能直接就去设置帧率生效,等到曝光设置时一起设置。 +// _setup_frame_ps = fps; + _target_frame_height = target_frame_height; + + printk(KERN_ERR "Set Frame Height = %d\r\n", _target_frame_height); + update_frame_height_and_exp_ctrl (); + + return 0; +} + +static int h63_set_standby_in(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static int h63_set_standby_out(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static int __on_timer(void) { + + int const reg_95 = __i2c_read(0x95); + int const reg_frame_w_15_8 = __i2c_read (0x21); + int const reg_frame_w_7_0 = __i2c_read (0x20); + frame_width = (reg_frame_w_15_8 << 8) | reg_frame_w_7_0; + + if (reg_95 > 0x80) { + __i2c_write(0x8e,0x01); + printk("write 0x8e 0x01\n"); + }else{ + __i2c_write(0x8e,0x00); + printk("write 0x8e 0x00\n"); + } +} + + +static AK_ISP_SENSOR_CB h63_callback = +{ + .sensor_init_func = h63_init, + .sensor_read_reg_func = __i2c_read, + .sensor_write_reg_func = __i2c_write, + .sensor_read_id_func = h63_read_id, + .sensor_update_a_gain_func = h63_cmos_updata_a_gain, + .sensor_update_d_gain_func = h63_cmos_updata_d_gain, + .sensor_updata_exp_time_func = h63_cmos_updata_exp_time, + .sensor_timer_func = __on_timer, + + .sensor_probe_id_func = h63_probe_id, //use IIC bus + .sensor_get_resolution_func = h63_get_resolution, + .sensor_get_mclk_func = h63_get_mclk, + .sensor_get_fps_func = h63_get_fps, + .sensor_get_valid_coordinate_func = h63_get_valid_coordinate, + .sensor_get_bus_type_func = h63_get_bus_type, + .sensor_get_parameter_func = h63_get_parameter, + + .sensor_set_power_on_func = h63_set_power_on, + .sensor_set_power_off_func = h63_set_power_off, + .sensor_set_fps_func = h63_set_fps, + .sensor_set_standby_in_func = h63_set_standby_in, + .sensor_set_standby_out_func = h63_set_standby_out +}; + +AK_SENSOR_MODULE(h63_callback, h63) + +/////////////////////////////////////////////////////////////////////////////////////////// + +static unsigned int h63_cmos_gains_convert(unsigned int a_gain, unsigned int d_gain, + unsigned int *a_gain_out ,unsigned int *d_gain_out) +{ + //ģ??????????ת???????????㷨??????????ת????sensor????????ʽ + unsigned int ag_interget_table[17] = {0, 1, 2, 2, 4, 4, 4, 4, 8, 8, 8, 8, + 8, 8, 8, 8, 16}; + unsigned int ag_integer, ag_fraction, tmp; + unsigned int ag_return; + + tmp = a_gain / 16; + if(tmp == 0) + { + tmp = 1; + ak_sensor_print("Div ERROR 000!\n"); + } + //ak_sensor_print("tmp = %d \n",tmp); + if(tmp<16) + { + tmp = OV_CLIP3(0, 16, tmp); + ag_integer = ag_interget_table[tmp]; + } + else if ((16<=tmp)&&(tmp<32)) + ag_integer = 16; + else if ((32<=tmp)&&(tmp<64)) + ag_integer = 32; + else// if((64<=tmp)&&(tmp<128)) + ag_integer = 64; + + + ag_fraction = (a_gain / ag_integer) - 16; + ag_fraction = OV_CLIP3(0, 15, ag_fraction); + + if (((ag_fraction + 16) * ag_integer) < a_gain) + { + if (ag_fraction < 15) + { + ag_fraction++; + } + // else if (ag_integer < 16) + else if (ag_integer <128) + { + tmp++; + /* + ag_integer = ag_interget_table[tmp]; + ag_fraction = 0; + */ + if(tmp<16) + { + tmp = OV_CLIP3(0, 16, tmp); + ag_integer = ag_interget_table[tmp]; + } + else if ((16<=tmp)&&(tmp<32)) + ag_integer = 16; + else if ((32<=tmp)&&(tmp<64)) + ag_integer = 32; + else if((64<=tmp)&&(tmp<128)) + ag_integer = 64; + + ag_fraction = 0; + } + else + { + } + } + + switch (ag_integer) + { + case 1 : + ag_integer = 0x00; + break; + case 2 : + ag_integer = 0x10; + break; + case 4 : + ag_integer = 0x20; + break; + case 8 : + ag_integer = 0x30; + break; + case 16 : + ag_integer = 0x40; + break; + case 32 : + ag_integer = 0x50; + break; + case 64 : + ag_integer = 0x60; + break; + default: + ag_integer = 0x00; + break; + } + + ag_return = ag_integer|ag_fraction; + *a_gain_out = ag_return ; + + return ag_return; +} + +static int h63_cmos_updata_d_gain(const unsigned int d_gain) +{ + return 0; + //?????????????Ļص????? + printk("================33================\n\n"); + printk("d_gain : %d\n",d_gain); + printk("==================================\n"); + +} + +static int h63_cmos_updata_a_gain(const unsigned int a_gain) +{ +// return 0; + //??????ģ???????Ļص????? + unsigned short ag_value; + unsigned int tmp_a_gain; + unsigned int tmp_d_gain; + unsigned int tmp_a_gain_out; + unsigned int tmp_d_gain_out; + + tmp_a_gain = a_gain>>4; + tmp_d_gain = 0; + + //ag_value = cmos_gains_update(isp,isp->aec_param.current_a_gain); + h63_cmos_gains_convert(tmp_a_gain, tmp_d_gain, &tmp_a_gain_out ,&tmp_d_gain_out); + ag_value = tmp_a_gain_out; + //ak_sensor_print("a_gain=%d\n",a_gain); + __i2c_write(0x00, tmp_a_gain_out); + + printk("================33================\n\n"); + printk("tmp_a_gain : %d\n",tmp_a_gain); + printk("tmp_a_gain_out : %d\n",tmp_a_gain_out); + printk("----------------------------------\n"); + printk("tmp_a_gain_out : 0x%x\n",tmp_a_gain_out); + printk("==================================\n"); + // updata_auto_exposure_num++; + //isp->aec_param.current_exposure_time = tmp_exposure_time; + return EFFECT_FRAMES; +} + + +static int h63_cmos_updata_exp_time(unsigned int exp_time) +{ +// return 0; + //?????ع?ʱ???Ļص????? +// unsigned char exposure_time_msb; +// unsigned char exposure_time_lsb; +// ak_sensor_print("exp_time=%d\n",exp_time); +// exposure_time_msb =(exp_time>>8)&0xff; +// exposure_time_lsb = exp_time&0xff; +// ak_sensor_print("msb = %d\n",exposure_time_msb); +// ak_sensor_print("lsb = %d\n",exposure_time_lsb); +// h63_sensor_write_register(0x02,exposure_time_msb); +// h63_sensor_write_register(0x01,exposure_time_lsb); + + _target_exp_ctrl = exp_time; + update_frame_height_and_exp_ctrl (); + + printk("================33================\n\n"); + printk("exp_time : 0x%x\n",exp_time); + printk("==================================\n"); +// ak_sensor_print("0x16=%d\n",h63_sensor_read_register(0x16)); + return 0; +} diff --git a/drivers/media/video/plat-anyka/sensor_SC2239+SC2332+SC2335.c b/drivers/media/video/plat-anyka/sensor_SC2239+SC2332+SC2335.c new file mode 100644 index 00000000..a38aa085 --- /dev/null +++ b/drivers/media/video/plat-anyka/sensor_SC2239+SC2332+SC2335.c @@ -0,0 +1,814 @@ +/** + * @file camera_sc2239.c + * @brief camera driver file + * Copyright (C) 2017 Anyka (Guangzhou) Microelectronics Technology Co., Ltd + * @author + * @date + * @version 1.0 + * @ref + * + * 命名规则: + * gain 标识 1X=256 单位的变量; + * gaain 标识 1X=1000 单位的变量; + * + */ + + +#include "ak_sensor_common.h" +#include + +#define SENSOR_PWDN_LEVEL 0 +#define SENSOR_RESET_LEVEL 0 +#define SENSOR_I2C_ADDR 0x60 + +#define SENSOR_SC2239_UCODE 0xcb10 +#define SENSOR_SC2332_UCODE 0xcb17 +#define SENSOR_SC2335_UCODE 0xcb14 +#define SENSOR_SC2239_ID 0x2239 ///< 序列号适配 +#define SENSOR_SC2335_ID 0x2335 +#define SENSOR_SC2332_ID 0x2332 +static u32 _SENSOR_ID = 0; ///< 数值为 0 表示未检测到,当检测到某一款 SENSOR 时此值将固定为一个非 0 的值。 + +#define SENSOR_MCLK 24 +#define SENSOR_REGADDR_BYTE 2 +#define SENSOR_DATA_BYTE 1 +#define MAX_FPS 30 +#define SENSOR_OUTPUT_WIDTH 1920 +#define SENSOR_OUTPUT_HEIGHT 1080 +#define SENSOR_VALID_OFFSET_X 0 +#define SENSOR_VALID_OFFSET_Y 0 +#define SENSOR_BUS_TYPE BUS_TYPE_RAW +#define SENSOR_IO_INTERFACE MIPI_INTERFACE +//#define SENSOR_IO_INTERFACE DVP_INTERFACE +#define SENSOR_IO_LEVEL SENSOR_IO_LEVEL_1V8 + +#define OV_MAX(a, b) (((a) < (b) ) ? (b) : (a)) +#define OV_MIN(a, b) (((a) > (b) ) ? (b) : (a)) +#define OV_CLIP3(low, high, x) (OV_MAX(OV_MIN((x), high), low)) + +#define SKIP_NUM 2 +#define EXP_EFFECT_FRAMES 1 +#define VSYNC_ACTIVE_MS 28 +#define MIPI_MHZ 780 +#define MIPI_1LANE 1 +#define MIPI_2LANE 2 + +#define PCLK_FREQ (74250000) + +static int _target_frame_vts = 0; ///< 缓存帧长设置,用于限制曝光与帧长大于曝光的逻辑。 +static int _target_exp_ctrl = 0; +static int _reg_frame_hts = 0; + +/** + * 通过 I2C 总线读取一个数据。 + */ +static int __i2c_read (int reg) { + + struct ak_sensor_i2c_data i2cdata; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = 0; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_read_register (&i2cdata); +} + +/** + * 通过 I2C 总线写入一个数据。 + */ +static int __i2c_write (int reg, int data) { + + struct ak_sensor_i2c_data i2cdata; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = data; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_write_register(&i2cdata); +} + + +/** + * 读取芯片唯一码。 + */ +static int __read_ucode (void) { + + int const msb = __i2c_read (0x3107); + int const lsb = __i2c_read (0x3108); + int ucode = (msb << 8) | lsb; + + if (0xffff == ucode) { + ucode = 0; ///< Force to Zero indicated Invalid. + } + + return ucode; +} + + +/** + * 获取曝光控制的值,注意,思特威读取是实际的 1/2。 + */ +static int _reg_exp_time = 0; +//static int __get_exp_ctrl (void) { +// +// if (0 == _reg_exp_time) { +// /// 通过缓冲变量避免 I2C 频繁读取造成额外性能开销。 +// int const exp_time_ext = __i2c_read (0x3e00) & 0xf; +// int const exp_time_msb = __i2c_read (0x3e01); +// int const exp_time_lsb = (__i2c_read (0x3e02) & 0xf0) >> 4; +// _reg_exp_time = exp_time_ext << 12 | exp_time_msb << 4 | exp_time_lsb; +// } +// +// return _reg_exp_time; +//} + +/** + * 设置半行曝光控制。 + * 如果上层传入曝光单位是行,传入前需要进行 X2 处理。 + */ +static void __set_half_exp_ctrl (int exp) { + + int const exp_time_ext = (exp >> 12) & 0xf; + int const exp_time_msb = (exp >> 4) & 0xff; + int const exp_time_lsb = ((exp) & 0xf) << 4 ; + + __i2c_write (0x3e00, exp_time_ext); + __i2c_write (0x3e01, exp_time_msb); + __i2c_write (0x3e02, exp_time_lsb); + + _reg_exp_time = exp; +} + +/** + * 获取半帧宽,注意:由于 SENSOR 前后有兼容性差异,\n + * 部分 SENSOR 该寄存器获取是半帧宽度,SC2239 读取是全帧宽度。\n + * 为了兼容性这里统一获取半帧宽度。 + */ +static int __get_frame_half_hts (void) { + + /// 该参数初始化导入配置以后不会再修改,因此可以通过变量缓存。 + + if (0 == _reg_frame_hts) { + _reg_frame_hts = __i2c_read (0x320c) << 8 | __i2c_read (0x320d); + if (SENSOR_SC2239_ID == _SENSOR_ID || SENSOR_SC2332_ID == _SENSOR_ID) { + /// SC2239 比较特别,从该寄存器读出来的值是一行的宽度,\n + /// 这里为了适配较多以往驱动接口,统一按半行返回。 + _reg_frame_hts /= 2; + } + } + + return _reg_frame_hts; +} + +/** + * 获取全帧宽度快速定义。 + */ +#define __get_frame_hts() (2 * __get_frame_half_hts()) + +/** + * 获取帧高。 + */ +static int _reg_frame_vts = 0; ///< 缓冲帧高参数,避免 I2C 频繁读取造成效率下降。 +static int __get_frame_vts (void) { + + if (0 == _reg_frame_vts) { + _reg_frame_vts = __i2c_read (0x320e) << 8 | __i2c_read (0x320f); + } + + return _reg_frame_vts; +} + +static void __set_frame_vts (int vts) { + + int const msb = vts >> 8; + int const lsb = vts & 0xff; + + if (vts == _reg_frame_vts) { + /// VTS 没有变化时不作更新处理。 + return; + } + + __i2c_write (0x320e, msb); + __i2c_write (0x320f, lsb); + + /// 回读设置。 + _reg_frame_vts = __i2c_read (0x320e) << 8 | __i2c_read (0x320f); +} + + +/** + * 设置 ana gain,对应 [0x3e08,0x3e09] 寄存器。 + */ +static int _reg_ana_gain = 0; ///< 寄存器缓冲。 +static void __set_reg_ana_gain (int const gain) { + + if (gain == _reg_ana_gain) { + /// gain 没有变化时不作更新处理。 + return; + } + + __i2c_write (0x3e08, (gain >> 8) & 0xff); + __i2c_write (0x3e09, gain & 0xff); + /// 回读缓存寄存器的值,优化多次写入的内存开销。 + _reg_ana_gain = (__i2c_read (0x3e08) << 8) | __i2c_read (0x3e09); + + printk (KERN_DEBUG "Set Reg{3e08,3e09}=%04x.\r\n", _reg_ana_gain); +} + + +/** + * 获取 ana gain,对应 [0x3e08,0x3e09] 寄存器。 + */ +static __get_reg_ana_gain (void) { + + if (0 == _reg_ana_gain) { + /// 初始化一次寄存器缓冲值。 + _reg_ana_gain = (__i2c_read (0x3e08) << 8) | __i2c_read (0x3e09); + } + + /// 返回缓冲寄存器的值。 + return _reg_ana_gain; +} + + + +/** + * 同时更新帧长与曝光 + */ +static void update_frame_vts_and_exp_ctrl (void) { + + int const cur_frame_vts = __get_frame_vts (); + + /// 将要设置的帧长与曝光需要匹配,帧率优先,曝光需要满足帧长前提。 + int const target_frame_vts = _target_frame_vts; + int const max_exp_ctrl = target_frame_vts - 8; ///< 最大曝光不超过 VTS - 8。 + + /// 注意,曝光控制单位是半行,因此设入的时候需要 X2。 + int const target_exp_ctrl = _target_exp_ctrl <= max_exp_ctrl ? _target_exp_ctrl : max_exp_ctrl; + + printk (KERN_DEBUG "VTS = %d->%d \r\n", cur_frame_vts, target_frame_vts); + printk (KERN_DEBUG "EXP = %d/%d \r\n", target_exp_ctrl, _target_exp_ctrl); + + //printk(KERN_ERR "Active FrameHeight=%d ExpCtrl=%d\r\n", target_frame_height, target_exp_ctrl); + if (target_frame_vts >= cur_frame_vts) { + /// 帧长比原来变长了,这个时候需要先设帧长,腾出空间设置曝光。 + __set_frame_vts (target_frame_vts); + __set_half_exp_ctrl (target_exp_ctrl * 2); + } else { + /// 帧长比原来变短了,这个时候先设曝光,再设置帧率减小帧间隔。 + __set_half_exp_ctrl (target_exp_ctrl * 2); + __set_frame_vts (target_frame_vts); + } +} + +static int sc2239_init(const AK_ISP_SENSOR_INIT_PARA *para) +{ + int i; +// int value; + AK_ISP_SENSOR_REG_INFO *preg_info; + + preg_info = para->reg_info; + for (i = 0; i < para->num; i++) { + __i2c_write (preg_info->reg_addr, preg_info->value); + preg_info++; + } + + return 0; +} + +/** + * 匹配 UCODE 并对应 ID 转换。 + */ +static int __probe_id (void) { + + int const ucode = __read_ucode (); + + /// 对应适配设备 ID。 + switch (ucode) { + case SENSOR_SC2239_UCODE: + _SENSOR_ID = SENSOR_SC2239_ID; + break; + + case SENSOR_SC2335_UCODE: + _SENSOR_ID = SENSOR_SC2335_ID; + break; + + case SENSOR_SC2332_UCODE: + _SENSOR_ID = SENSOR_SC2332_ID; + break; + + default: + break; + } + + return _SENSOR_ID; +} + + +static int __read_id (void) { + + if (0 == _SENSOR_ID) { + _SENSOR_ID = __probe_id (); + } + + if (0 == _SENSOR_ID) { + return 0xffff; ///< 返回一个非法 ID 使上层匹配错误。 + } + + return _SENSOR_ID; +} + + + +static int sc2239_get_resolution(int *width, int *height) +{ + *width = SENSOR_OUTPUT_WIDTH; + *height = SENSOR_OUTPUT_HEIGHT; + + return 0; +} + +static int sc2239_get_mclk(void) { + return SENSOR_MCLK; +} + +static int __get_fps (void) { + + int const fps = PCLK_FREQ / (__get_frame_hts () * __get_frame_vts ()); + /// 读取实时的帧率。 + //printk ("<0> FPS=%d\r\n", fps); + return fps; +} + +static int sc2239_get_valid_coordinate(int *x, int *y) +{ + *x = SENSOR_VALID_OFFSET_X; + *y = SENSOR_VALID_OFFSET_Y; + + return 0; +} + +static enum sensor_bus_type sc2239_get_bus_type(void) +{ + return SENSOR_BUS_TYPE; +} + +static int sc2239_get_parameter(int param, void *value) { + + int ret = 0; + enum sensor_get_param name = (enum sensor_get_param)param; + + switch (name) { + + case GET_VSYNC_ACTIVE_MS: + *((int *)value) = VSYNC_ACTIVE_MS; + break; + + case GET_CUR_FPS: + *((int *)value) = __get_fps (); + break; + + case GET_MIPI_MHZ: + *((int *)value) = MIPI_MHZ; + break; + + case GET_MIPI_LANE: + if(_SENSOR_ID == SENSOR_SC2332_ID){ + *((int *)value) = MIPI_2LANE; + }else{ + *((int *)value) = MIPI_1LANE; + } + break; + + case GET_INTERFACE: + *((int *)value) = SENSOR_IO_INTERFACE; + break; + + case GET_SENSOR_IO_LEVEL: + *((int *)value) = SENSOR_IO_LEVEL; + break; + + default: + ret = -1; + break; + } + + return ret; +} + +static int sc2239_set_power_on(const int pwdn_pin, const int reset_pin) +{ + ak_sensor_set_pin_as_gpio(pwdn_pin); + ak_sensor_set_pin_dir(pwdn_pin, 1); + ak_sensor_set_pin_level(pwdn_pin, !SENSOR_PWDN_LEVEL); + ak_sensor_mdelay(10); + ak_sensor_set_pin_as_gpio(reset_pin); + ak_sensor_set_pin_dir(reset_pin, 1); + ak_sensor_set_pin_level(reset_pin, SENSOR_RESET_LEVEL); + ak_sensor_mdelay(30); + ak_sensor_set_pin_level(reset_pin, !SENSOR_RESET_LEVEL); + ak_sensor_mdelay(20); + + return 0; +} + +static int sc2239_set_power_off(const int pwdn_pin, const int reset_pin) +{ + //sccb software standby mode + + ak_sensor_set_pin_level(pwdn_pin, SENSOR_PWDN_LEVEL); + ak_sensor_set_pin_dir(reset_pin, 0); + + return 0; +} + +static int __set_fps (const int fps) { + + int vts = 0; + /// FPS = PCLK / FRAMEW / FRAMEH + vts = PCLK_FREQ; + vts /= __get_frame_hts (); + vts /= fps; + + _target_frame_vts = vts; + update_frame_vts_and_exp_ctrl (); + + return 0; +} + +static int sc2239_set_standby_in(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static int sc2239_set_standby_out(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + + +/////////////////////////////////////////////////////////////////////////////////////////// + + +static int __sc2239_updata_d_gain(const unsigned int d_gain) { + + /// 这里传入的 dgain 精度位 1/256,统一转换成 1/1000 运算。 + + int const max_dgain = 31500; ///< SC2239 最大 DGAIN = 31.5X + + unsigned char dig_gain = 0x00; ///< x1 dgain + unsigned char dig_fine_gain = 0x80; ///< x1 dgain + int x = 0, frac = 0, range = 0; + int gain = d_gain * 1000 / 256; ///< 传进来1X=256,需要转换为1X=1000计量运算。 + + + if (gain > max_dgain) { + /// 不超过最大 AGAIN + gain = max_dgain; + } + + x = gain / 1000; ///< 倍数整数。 + frac = gain % 1000; ///< 小数部分。 + range = 0xfc - 0x80; + + + if (x < 1) { + /// 最小为 1X again + dig_gain = 0x00; + dig_fine_gain = 0x80; + + } else if (x < 2) { + /// 1 - 1.969 + dig_gain = 0x00; + dig_fine_gain = (gain - 1000) * range / (1969 - 1000) + 0x80; + + } else if (x < 4) { + /// 2 - 3.938 + dig_gain = 0x01; + dig_fine_gain = (gain - 2000) * range / (3938 - 2000) + 0x80; + + } else if (x < 8) { + /// 4 - 7.875 + dig_gain = 0x03; + dig_fine_gain = (gain - 4000) * range / (7875 - 4000) + 0x80; + + } else if (x < 16) { + /// 8 - 15.75 + dig_gain = 0x07; + dig_fine_gain = (gain - 8000) * range / (15750 - 8000) + 0x80; + + } else if (x < 32) { + /// 16 - 31.5 + dig_gain = 0x0f; + dig_fine_gain = (gain - 16000) * range / (31500 - 16000) + 0x80; + } + +// printk (KERN_ERR "Set SC2239 D-Gain %d.%03d {16'h3e06,16'h3e07} = {8'h%02x, 8'h%02x}.\r\n", +// x, frac, dig_gain, dig_fine_gain); + + + __i2c_write (0x3e06, dig_gain); + __i2c_write (0x3e07, dig_fine_gain); + + return 0; +} + + +/** + * 同步设置 SC2239、SC2332 的 A-Gain。 + * + * @param[IN] gaain + * 1X=1000 单位的增益值。 + * + * @param[IN] max_gaain + * 1X=1000 单位的最大增益值。 + * + */ +static void __sc2239_sc2332_set_a_gaain (int gaain, int const max_gaain) { + + //int const max_again = 15750; ///< SC2239 最大 AGAIN = 15.75X + int const min_gaain = 1000; ///< 最小 1X 增益。 + int ana_gain = 0x03; ///< x1 again + int ana_fine_gain = 0x20; ///< x1 again + int reg_ana_gain = 0; + int x = 0, frac = 0, range = 0; + + //printk(KERN_ERR "Set again = %d\r\n", a_gain); + + gaain = gaain > max_gaain ? max_gaain : gaain; ///< 不超过最大增益。 + gaain = gaain < min_gaain ? min_gaain : gaain; ///< 不低于最小增益。 + + x = gaain / 1000; ///< 倍数整数。 + frac = gaain % 1000; ///< 小数部分,用于打印。 + range = 0x3f - 0x20; + + if (x < 1) { + + /// 最小为 1X again + ana_gain = 0x03; + ana_fine_gain = 0x20; + + } else if (x < 2) { + /// 1 - 1.969 + ana_gain = 0x03; + ana_fine_gain = (gaain - 1000) * range / (1969 - 1000) + 0x20; + + } else if (x < 4) { + /// 2 - 3.938 + ana_gain = 0x07; + ana_fine_gain = (gaain - 2000) * range / (3938 - 2000) + 0x20; + + } else if (x < 8) { + /// 4 - 7.875 + ana_gain = 0x0f; + ana_fine_gain = (gaain - 4000) * range / (7875 - 4000) + 0x20; + + } else { + /// 8 - 15.75 + ana_gain = 0x1f; + ana_fine_gain = (gaain - 8000) * range / (15750 - 8000) + 0x20; + + } + + /// 换算成寄存器的值。 + reg_ana_gain = ana_gain << 8 | ana_fine_gain; + if (__get_reg_ana_gain () != reg_ana_gain) { + /// 出现增益变更时输出一下当前增益。 + printk (KERN_DEBUG "Set A-Gagin=%d.%03dX/%d.%03dX.\r\n", x, frac, max_gaain / 1000, max_gaain % 1000); + + } + + /// 设置寄存器。 + __set_reg_ana_gain (ana_gain << 8 | ana_fine_gain); +} + + +/** + * 缓存 SC2332 A-Gain。 + * 放大 1000 倍运算,如 15.75,该值为 15750 + * SC2332 最大增益会由于高温补偿变化,这里会缓存该数据用于作为下一次增益设置的范围参考。 + */ +static int _sc2332_max_a_gaain = 15750; ///< 缓存当前 SC2332 最大 A-Gain。 + + + +/** + * 设读取寄存器{0x3974}的值为 R;当前帧最大模拟增益用 MaxAgCur 表示,下一帧需要设置的最大模拟增益用 MaxAgNxt 表示。 + * 当 R>=0x1d时,MaxAgNxt = MaxAgCur – 16xfinegain(即最大模拟增益减 16 档模拟 finegain,finegain 为 1/32), + * 当 R <0x18时,MaxAgNxt = MaxAgCur + 16xfinegain(即最大模拟增益加 16 档模拟 finegain, finegain 为 1/32) + * 备注:MaxAgNxt 最小限制到 1x(即 0x3e08[4:0]=0x03,0x3e09[7:0]=0x20),最大就是默认 15.75x + * 即 0x3e08[4:0]=0x1f,0x3e09[7:0]=0x3f)。加减的 finegain step 可以适当调整。 + * 注意: MaxAgCur 只针对 sensor 的模拟增益,数字增益可以单独控制,无需限制。 + * + * @return MaxAgNxt + */ +static int __timer_sc2332_calc_max_gaain_ctrl () { + + static int MaxAgCur = 15750; ///< SC2332 最大 AGAIN = 15.75X + int MaxAgNxt = MaxAgCur; + int const reg3974 = __i2c_read (0x3974); ///< 读取 3974 寄存器 + + //printk(KERN_ERR "Set again = %d\r\n", a_gain); + /// 根据高温逻辑修正最大 A-Gain + /// 最大 Again 值得不大于 15.75X,不小于 2X。 + if (reg3974 >= 0x1c) { + MaxAgNxt = MaxAgCur - (1000 * 16 / 32); + MaxAgNxt = MaxAgNxt < 2000 ? 2000 : MaxAgNxt; ///< 最小增益 X2 限制。 + + } else if (reg3974 < 0x18){ + MaxAgNxt = MaxAgCur + (1000 * 16 / 32); + MaxAgNxt = MaxAgNxt > 15750 ? 15750 : MaxAgNxt; ///< 最大增益 X15.75 限制。 + + } else{ + /// 当处于 0x18 和 0x1d 中间时保持原来的最大增益。 + MaxAgNxt = MaxAgCur; + } + + if (MaxAgCur != MaxAgNxt) { + /// 增益最大值变更时输出一下结果。 + printk (KERN_DEBUG "Reg3974=%02x MaxAg=%d.%03dX->%d.%03dX.\r\n", + reg3974, MaxAgCur / 1000, MaxAgCur % 1000, MaxAgNxt / 1000, MaxAgNxt % 1000); + } + + /// 更新并返回当前最大增益。 + return (MaxAgCur = MaxAgNxt); +} + + + +static int __on_timer (void) { + + _reg_frame_hts = __i2c_read (0x320c) << 8 | __i2c_read (0x320d); + if (SENSOR_SC2239_ID == _SENSOR_ID || SENSOR_SC2332_ID == _SENSOR_ID) { + /// SC2239 比较特别,从该寄存器读出来的值是一行的宽度,\n + /// 这里为了适配较多以往驱动接口,统一按半行返回。 + _reg_frame_hts /= 2; + } + + if (SENSOR_SC2332_ID == _SENSOR_ID) { + + /// 在定时器过程中同步更新 SC2332 缓存的 A-Gain。 + /// 并在定时器中同步更新。 + _sc2332_max_a_gaain = __timer_sc2332_calc_max_gaain_ctrl (); + + } + + return 0; +} + + + +static int __sc2335_updata_a_gain (const unsigned int a_gain) { + + unsigned char dig_gain = 0x00; + unsigned char dig_fine_gain = 0x80; + unsigned char ana_gain = 0x03; + unsigned char ana_fine_gain = 0x40; + unsigned char reg_0x3040 = 0x00; + unsigned int gainShift = 0; + int gain = a_gain * 4;//1x=1024,a_gain 传进来的是 1X=256 + + if (gain <= 16256)//calc again, dgain=1x + { + gain = gain >> 4; + + for (gainShift = 0; (gain >> gainShift) > 0; gainShift++) + { + } + + ana_fine_gain = gain >> (gainShift - 7); + + for (gainShift;gainShift > 7; gainShift--) + { + ana_gain = (ana_gain << 1) | 0x01; + } + } + else + { + ana_gain = 0x1f; + ana_fine_gain = 0x7f; + + gain = gain / 127; //gain/15.75>>3 + for (gainShift = 0; (gain >> gainShift) > 0; gainShift++) + { + } + + dig_fine_gain = gain >> (gainShift - 8); + + for (gainShift; gainShift > 8; gainShift--) + { + dig_gain = (dig_gain << 1) | 0x01; + } + } + + //printf("0x%02x, 0x%02x, 0x%02x, 0x%02x\n", + // dig_gain, dig_fine_gain, ana_gain, ana_fine_gain); + + + __i2c_write (0x3e06, dig_gain); + __i2c_write (0x3e07, dig_fine_gain); + __i2c_write (0x3e08, ana_gain); + __i2c_write (0x3e09, ana_fine_gain); + + return 0; +} + + +static int __updata_a_gain (const unsigned int gain) { + + /// 这里传入的 again 精度位 1/256,统一转换成 1/1000 运算。 + int const gaain = gain * 1000 / 256; + + if (SENSOR_SC2239_ID == _SENSOR_ID) { + + u8 reg3634 = 0x24; ///< 对应 AGAIN 的整数关系对寄存器 3634 联动操作。 + + __sc2239_sc2332_set_a_gaain (gaain, 15750); ///< SC2239 最大 AGAIN = 15.75X + + /// 寄存器 3634 对应 2X,4X,8X 的值。 + if (gaain < 1000) { + reg3634 = 0x64; + } else if (gaain < 2000) { + reg3634 = 0x64; + } else if (gaain < 4000) { + reg3634 = 0x64; + } else if (gaain < 8000) { + reg3634 = 0x44; + } else { + reg3634 = 0x24; + } + + printk (KERN_DEBUG "Reg3634=%02x.\r\n", reg3634); + __i2c_write (0x3634, reg3634); + + } else if (SENSOR_SC2332_ID == _SENSOR_ID) { + + /// 由于 SC2332 需要一直观察寄存器 3974,需要通过异步设置, + __sc2239_sc2332_set_a_gaain (gaain, _sc2332_max_a_gaain); + + } else if (SENSOR_SC2335_ID == _SENSOR_ID) { + + return __sc2335_updata_a_gain (gain); + } + + return 0; +} + +static int __updata_d_gain(const unsigned int a_gain) { + + if (SENSOR_SC2239_ID == _SENSOR_ID || SENSOR_SC2332_ID == _SENSOR_ID) { + return __sc2239_updata_d_gain (a_gain); + } else if (SENSOR_SC2335_ID == _SENSOR_ID) { + + } + + return 0; +} + + + +static int __update_exp(unsigned int exp_time) { + + _target_exp_ctrl = exp_time; + update_frame_vts_and_exp_ctrl (); + + /// 思特威 SENSOR 特点 N+2 生效。 + return 2; +} + + + + +static AK_ISP_SENSOR_CB _callback = { + + .sensor_init_func = sc2239_init, + .sensor_read_reg_func = __i2c_read , + .sensor_write_reg_func = __i2c_write , + .sensor_read_id_func = __read_id, + .sensor_update_a_gain_func = __updata_a_gain, + .sensor_update_d_gain_func = __updata_d_gain, + .sensor_updata_exp_time_func = __update_exp, + .sensor_timer_func = __on_timer, + + .sensor_probe_id_func = __probe_id, //use IIC bus + .sensor_get_resolution_func = sc2239_get_resolution, + .sensor_get_mclk_func = sc2239_get_mclk, + .sensor_get_fps_func = __get_fps, + .sensor_get_valid_coordinate_func = sc2239_get_valid_coordinate, + .sensor_get_bus_type_func = sc2239_get_bus_type, + .sensor_get_parameter_func = sc2239_get_parameter, + + .sensor_set_power_on_func = sc2239_set_power_on, + .sensor_set_power_off_func = sc2239_set_power_off, + .sensor_set_fps_func = __set_fps, + .sensor_set_standby_in_func = sc2239_set_standby_in, + .sensor_set_standby_out_func = sc2239_set_standby_out +}; + +AK_SENSOR_MODULE(_callback, sc2xxx) + + diff --git a/drivers/media/video/plat-anyka/sensor_ar0130.c b/drivers/media/video/plat-anyka/sensor_ar0130.c new file mode 100644 index 00000000..b7231ac0 --- /dev/null +++ b/drivers/media/video/plat-anyka/sensor_ar0130.c @@ -0,0 +1,390 @@ +/** + * @file camera_ar0130.c + * @brief camera driver file + * Copyright (C) 2011 Anyka (Guangzhou) Microelectronics Technology Co., Ltd + * @author xia_wenting + * @date 2011-09-21 + * @version 1.0 + * @ref + */ +#include "ak_sensor_common.h" + +#define SENSOR_PWDN_LEVEL 1 +#define SENSOR_RESET_LEVEL 0 +#define SENSOR_I2C_ADDR 0x20 +#define SENSOR_ID 0x0130 +#define SENSOR_MCLK 27 +#define SENSOR_REGADDR_BYTE 2 +#define SENSOR_DATA_BYTE 2 +#define SENSOR_OUTPUT_WIDTH 1280 +#define SENSOR_OUTPUT_HEIGHT 960 +#define SENSOR_VALID_OFFSET_X 0 +#define SENSOR_VALID_OFFSET_Y 0 +#define SENSROR_BUS_TYPE BUS_TYPE_RAW + +#define EFFECT_FRAMES 1 +#define VSYNC_ACTIVE_MS 36 + +#define OV_MAX(a, b) (((a) < (b) ) ? (b) : (a)) +#define OV_MIN(a, b) (((a) > (b) ) ? (b) : (a)) +#define OV_CLIP3(low, high, x) (OV_MAX(OV_MIN((x), high), low)) + +static int ar0130_cmos_updata_d_gain(const unsigned int d_gain); +static int ar0130_cmos_updata_a_gain(const unsigned int a_gain); +static int ar0130_cmos_updata_exp_time(unsigned int exp_time); + +static int g_fps = 25; +static int to_fps = 25; +static int to_fps_value = 0; + +static int ar0130_sensor_read_register(int reg) +{ + struct ak_sensor_i2c_data i2cdata; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = 0; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_read_register(&i2cdata); +} + +static int ar0130_sensor_write_register(int reg, int data) +{ + struct ak_sensor_i2c_data i2cdata; +// ak_sensor_print("%s~~~~~~~~~~reg:0x%x,data:0x%x\n",__func__,reg,data); + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = data; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_write_register(&i2cdata); +} + +static int ar0130_init(const AK_ISP_SENSOR_INIT_PARA *para) +{ + int i; + AK_ISP_SENSOR_REG_INFO *preg_info; +//int value; + + preg_info = para->reg_info; + for (i = 0; i < para->num; i++) { +//value=ar0130_sensor_read_register(preg_info->reg_addr); +//ak_sensor_print("before w reg:0x%.x, val:0x%.x, to write:0x%.x\n", preg_info->reg_addr, value, preg_info->value); + if (preg_info->reg_addr == 0xfd) { + ak_sensor_mdelay(preg_info->value); + } + else + ar0130_sensor_write_register(preg_info->reg_addr, preg_info->value); +//value=ar0130_sensor_read_register(preg_info->reg_addr); +//ak_sensor_print("after w reg:0x%.x, val:0x%.x\n", preg_info->reg_addr, value); + preg_info++; + } + + g_fps = 25; + to_fps = 25; + return 0; +} + +static int ar0130_read_id(void) +{ + return SENSOR_ID; +} + +static int ar0130_probe_id(void) +{ + u16 value; + + value = ar0130_sensor_read_register(0x30a0); + if (value != 0x0001) + goto fail; + value = ar0130_sensor_read_register(0x319a); + if (value != 0x100b) + goto fail; + value = ar0130_sensor_read_register(0x319c); + if (value != 0x0014) + goto fail; + + return SENSOR_ID; +fail: + return 0; +} + +static int ar0130_get_resolution(int *width, int *height) +{ + *width = SENSOR_OUTPUT_WIDTH; + *height = SENSOR_OUTPUT_HEIGHT; + + return 0; +} + +static int ar0130_get_mclk(void) +{ + return SENSOR_MCLK; +} + +static int ar0130_get_fps(void) +{ + return g_fps; +} + +static int ar0130_get_valid_coordinate(int *x, int *y) +{ + *x = SENSOR_VALID_OFFSET_X; + *y = SENSOR_VALID_OFFSET_Y; + + return 0; +} + +static enum sensor_bus_type ar0130_get_bus_type(void) +{ + return SENSROR_BUS_TYPE; +} + +static int ar0130_get_parameter(int param, void *value) +{ + int ret = 0; + enum sensor_get_param name = (enum sensor_get_param)param; + + switch (name) { + case GET_VSYNC_ACTIVE_MS: + *((int *)value) = VSYNC_ACTIVE_MS; + break; + + case GET_CUR_FPS: + *((int *)value) = g_fps; + break; + + default: + ret = -1; + break; + } + + return ret; +} + +static int ar0130_set_power_on(const int pwdn_pin, const int reset_pin) +{ + ak_sensor_set_pin_as_gpio(pwdn_pin); + ak_sensor_set_pin_dir(pwdn_pin, 1); + ak_sensor_set_pin_level(pwdn_pin, !SENSOR_PWDN_LEVEL); + ak_sensor_mdelay(10); + ak_sensor_set_pin_as_gpio(reset_pin); + ak_sensor_set_pin_dir(reset_pin, 1); + ak_sensor_set_pin_level(reset_pin, SENSOR_RESET_LEVEL); + ak_sensor_mdelay(10); + ak_sensor_set_pin_level(reset_pin, !SENSOR_RESET_LEVEL); + ak_sensor_mdelay(20); + + return 0; +} + +static int ar0130_set_power_off(const int pwdn_pin, const int reset_pin) +{ +// u8 Reg0x3d = 0x48; +// u8 Reg0xc3 = 0x00; + + //sccb software standby mode + +// ar0130_sensor_write_register(0x3d, Reg0x3d); +// ar0130_sensor_write_register(0xc3, Reg0xc3); + + ak_sensor_set_pin_level(pwdn_pin, SENSOR_PWDN_LEVEL); + ak_sensor_set_pin_dir(reset_pin, 0); + + return 0; +} + +static int ar0130_set_fps(int fps) +{ + int ret = 0; + int tmp=0; + + switch (fps) { + case 30: + tmp = 0x3e6; + break; + + case 25: + tmp = 0x4ae; + break; + + case 13: //actual: 12.5fps + case 12: + tmp = 0x95c; + break; + + case 10: + tmp = 0xbb3; + break; + + case 8: + tmp = 0xea0; + //ar0130_sensor_write_register(0x300a, 0xe0a); //8.333 fps + break; + + case 5: + tmp = 0x1767; + + default: + ak_sensor_print("%s set fps fail\n", __func__); + ret = -EINVAL; + break; + } + + if (!ret) { + //ar0130_sensor_write_register(0x300a, 0x3e6); + //g_fps = fps; + to_fps = fps; + to_fps_value = tmp; + } + + return ret; +} + +static int ar0130_set_standby_in(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static int ar0130_set_standby_out(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static AK_ISP_SENSOR_CB ar0130_callback = +{ + .sensor_init_func = ar0130_init, + .sensor_read_reg_func = ar0130_sensor_read_register, + .sensor_write_reg_func = ar0130_sensor_write_register, + .sensor_read_id_func = ar0130_read_id, + .sensor_update_a_gain_func = ar0130_cmos_updata_a_gain, + .sensor_update_d_gain_func = ar0130_cmos_updata_d_gain, + .sensor_updata_exp_time_func = ar0130_cmos_updata_exp_time, + + .sensor_probe_id_func = ar0130_probe_id, //use IIC bus + .sensor_get_resolution_func = ar0130_get_resolution, + .sensor_get_mclk_func = ar0130_get_mclk, + .sensor_get_fps_func = ar0130_get_fps, + .sensor_get_valid_coordinate_func = ar0130_get_valid_coordinate, + .sensor_get_bus_type_func = ar0130_get_bus_type, + .sensor_get_parameter_func = ar0130_get_parameter, + + .sensor_set_power_on_func = ar0130_set_power_on, + .sensor_set_power_off_func = ar0130_set_power_off, + .sensor_set_fps_func = ar0130_set_fps, + .sensor_set_standby_in_func = ar0130_set_standby_in, + .sensor_set_standby_out_func = ar0130_set_standby_out +}; + +AK_SENSOR_MODULE(ar0130_callback, ar0130) + +/////////////////////////////////////////////////////////////////////////////////////////// + +static unsigned int ar0130_cmos_gains_convert(unsigned int a_gain, unsigned int d_gain, + unsigned int *a_gain_out ,unsigned int *d_gain_out) +{ + //ģ??????????ת???????????㷨??????????ת????sensor????????ʽ + unsigned int ag_interget_table[17] = {0, 1, 2, 2, 4, 4, 4, 4, 8, 8, 8, 8, + 8, 8, 8, 8, 8}; + unsigned int ag_integer, ag_fraction, tmp, reg_ag; + unsigned int ag_return; + + tmp = a_gain / 256; + if(tmp == 0) + { + tmp = 1; + ak_sensor_print("Div ERROR 000!\n"); + } + + tmp = OV_CLIP3(0, 16, tmp); + ag_integer = ag_interget_table[tmp]; + + ag_fraction = (a_gain / ag_integer) >>3; + + + /*if (((ag_fraction + 16) * ag_integer) < a_gain) + { + if (ag_fraction < 15) + { + ag_fraction++; + } + else if (ag_integer < 16) + { + tmp++; + ag_integer = ag_interget_table[tmp]; + ag_fraction = 0; + } + else + { + } + }*/ + + switch (ag_integer) + { + case 1 : + reg_ag = 0x1300; + break; + case 2 : + reg_ag = 0x1310; + break; + case 4 : + reg_ag = 0x1320; + break; + case 8 : + reg_ag = 0x1330; + break; + default: + reg_ag = 0x1300; + break; + } + + ag_return = ag_integer; + *a_gain_out = ag_return ; + + ar0130_sensor_write_register(0x30b0, reg_ag); + ar0130_sensor_write_register(0x305e, ag_fraction); + ak_sensor_print("gain=%d, again=%d, dgain=%d\n", a_gain, ag_integer, ag_fraction); + return ag_return; +} + +static void ar0130_set_fps_async(void) +{ + if (to_fps != g_fps) { + ar0130_sensor_write_register(0x300a, to_fps_value); + + g_fps = to_fps; + } +} + +static int ar0130_cmos_updata_d_gain(const unsigned int d_gain) +{ + //?????????????Ļص????? + return 0; +} + +static int ar0130_cmos_updata_a_gain(const unsigned int a_gain) +{ + unsigned int tmp_d_gain=0; + unsigned int tmp_a_gain_out; + unsigned int tmp_d_gain_out; + + ar0130_set_fps_async(); + + ar0130_cmos_gains_convert(a_gain, tmp_d_gain, &tmp_a_gain_out ,&tmp_d_gain_out); + return 0; +} + + +static int ar0130_cmos_updata_exp_time(unsigned int exp_time) +{ + + ar0130_set_fps_async(); + + //ak_sensor_print("exp_time=%d\n",exp_time); + ar0130_sensor_write_register(0x3012, exp_time); + //ak_sensor_print("exp_time=%d, 0x305e=%x\n", exp_time, ar0130_sensor_read_register(0x305e)); + return EFFECT_FRAMES; +} diff --git a/drivers/media/video/plat-anyka/sensor_bf3703.c b/drivers/media/video/plat-anyka/sensor_bf3703.c new file mode 100644 index 00000000..b341c3aa --- /dev/null +++ b/drivers/media/video/plat-anyka/sensor_bf3703.c @@ -0,0 +1,527 @@ +/** + * @file camera_bf3703.c + * @brief camera driver file + * Copyright (C) 2011 Anyka (Guangzhou) Microelectronics Technology Co., Ltd + * @author luweichun + * @date 2015-11-05 + * @version 1.0 + * @ref + */ +#include "ak_sensor_common.h" + +#define SENSOR_PWDN_LEVEL 1 +#define SENSOR_RESET_LEVEL 0 +#define SENSOR_I2C_ADDR 0xdd +#define SENSOR_ID 0x3703 +#define SENSOR_MCLK 24 +#define SENSOR_REGADDR_BYTE 1 +#define SENSOR_DATA_BYTE 1 +#define MAX_FPS 25 +#define SENSOR_OUTPUT_WIDTH 640 +#define SENSOR_OUTPUT_HEIGHT 480 +#define SENSOR_VALID_OFFSET_X 0 +#define SENSOR_VALID_OFFSET_Y 0 +#define SENSROR_BUS_TYPE BUS_TYPE_YUV + +#define OV_MAX(a, b) (((a) < (b) ) ? (b) : (a)) +#define OV_MIN(a, b) (((a) > (b) ) ? (b) : (a)) +#define OV_CLIP3(low, high, x) (OV_MAX(OV_MIN((x), high), low)) + +#define SKIP_NUM 2 +#define EFFECT_FRAMES 1 +#define VSYNC_ACTIVE_MS 36 + +/////////////////////////////////BF3703 INTERAL REGISTERS////////////////// +//register_rule +#define BF_Rup 1 +#define BF_Rdown 0 + +// bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 +//register bit +#define BF_Rbit0 0 +#define BF_Rbit1 1 +#define BF_Rbit2 2 +#define BF_Rbit3 3 +#define BF_Rbit4 4 +#define BF_Rbit5 5 +#define BF_Rbit6 6 +#define BF_Rbit7 7 + +/*---------register address--------*/ +//infomation +#define BF_R_ID_MSB 0xfc +#define BF_R_ID_LSB 0xfd + +#define BF_R_Device_addrss 0xfe + +//BF mod control +#define BF_R_reset 0x12 +#define BF_R_Standby 0x09 + +#define BF_R_data_Transfor 0xf1 //Enable the Data transfor + +//BF Setting +#define BF_R_ClkRC 0x11 + +//BF Common Control +#define BF_R_com2 0x09 +#define BF_R_com3 0xdc +#define BF_R_com4 0x0b +#define BF_R_com5 0x0a +#define BF_R_com6 0x10 +#define BF_R_com7 0x12 +#define BF_R_com8 0x13 +#define BF_R_com10 0x15 + +#define BF_R_TDreg 0x20 + +//BF test +#define BF_R_Test 0xbb +#define BF_R_light 0x32 + +/*----------------------------------BF3703 mod switch-----------------------------------------*/ +//Setting Resolution +#define BF_VGA_Resolution_YUV 0 // 30fps VGA YUV +#define BF_VGA_Resolution_Basic 1 // 30fps VGA RGB565/ RGB555/ RGB444 +#define BF_VGA_Resolution_RGB_R 2 // 30fps VGA Raw Bayer RGB mode +#define BF_VGA_Resolution_RGB_P 3 // 30fps VGA Processed RGB mode + +#define BF_QVGA_Resolution_YUV 4 +#define BF_QVGA_Resolution_Basic 5 +#define BF_QVGA_Resolution_RGB_R 6 +#define BF_QVGA_Resolution_RGB_P 7 +//????QVGA ?Ƚ????⻹֧?ֶ???֮һ?IJ???????????Ϊ?˲????Ȳ??????壬????Ԥ??4????λ??????8 9 10 11 +/*------------Array Control---------------*/ +//Setting Mirror +#define BF_Mirror_H 12 +#define BF_Mirror_V 13 +#define BF_Mirror_nor 14 + +#define BF_Subsample_all 15 +#define BF_Subsample_half 16 + +//Time Setting +#define BF_Mclk_normal 17 +#define BF_Vclk_normal 18 +#define BF_Vclk_half 19 + +//Exposure +#define BF_Exposure_AUTO 20 + + +/*------------Common Control---------------*/ +//com2 +#define BF_com2_DCHoutput 0 +#define BF_com2_Vclkoutput 1 +#define BF_com2_Hsycoutput 2 +#define BF_com2_Dataoutput 3 +#define BF_com2_standbymod 4 + +//com3 +#define BF_com3_Procress_RGB 5 +#define BF_com3_Procress_color 6 +#define BF_com3_Procress_L1GRGR 7 +#define BF_com3_Procress_L1RGRG 8 +#define BF_com3_Procress_L1BGBG 9 +#define BF_com3_Procress_L1GBGB 10 +#define BF_com3_HREF_swtich_on 11 +#define BF_com3_HREF_switch_off 12 +//com4 + +//com5 + +//com6 + +//com7 + +//com8 +#define BF_com8_INT_TIM 13 +#define BF_com8_AGC 14 +#define BF_com8_AWB 15 +#define BF_com8_AEC 16 + +//com10 + +static int bf3703_cmos_updata_d_gain(const unsigned int d_gain); +static int bf3703_cmos_updata_a_gain(const unsigned int a_gain); +static int bf3703_cmos_updata_exp_time(unsigned int exp_time); +static int bf3703_cmos_update_a_gain_timer(void); + +static int g_fps = MAX_FPS; +static int to_fps = MAX_FPS; + +static int bf3703_sensor_read_register(int reg) +{ + struct ak_sensor_i2c_data i2cdata; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = 0; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_read_register(&i2cdata); +} + +static int bf3703_sensor_write_register(int reg, int data) +{ + struct ak_sensor_i2c_data i2cdata; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = data; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_write_register(&i2cdata); +} + +static AK_ISP_SENSOR_REG_INFO BF3703_YUV_640X480[]= +{ +{0x12,0x80}, +{0x15,0x02}, +{0x09,0x01}, +{0x1e,0x00}, +{0x13,0x00}, +{0x01,0x14}, +{0x02,0x22}, +{0x8c,0x02}, +{0x8d,0x64}, +{0x87,0x18}, +{0x13,0x07}, +{0x05,0x1f}, +{0x06,0x60}, +{0x14,0x20}, +{0x27,0x03}, +{0x06,0xe0}, +{0x11,0x80}, +{0x2b,0x00}, +{0x92,0xea}, +{0x93,0x01}, +{0x9d,0x99}, +{0x9e,0x7f}, +{0xeb,0x30}, +{0xbb,0x20}, +{0xf5,0x21}, +{0xe1,0x3c}, +{0x16,0x03}, +{0x2f,0xf6}, +{0x33,0x20}, +{0x34,0x08}, +{0x35,0x58}, +{0x65,0x58}, +{0x66,0x52}, +{0x36,0x05}, +{0x37,0xf6}, +{0x38,0x46}, +{0x9b,0xf6}, +{0x9c,0x46}, +{0xbc,0x01}, +{0xbd,0xf6}, +{0xbe,0x46}, +{0x70,0x6f}, +{0x72,0x4f}, +{0x73,0x2f}, +{0x74,0x27}, +{0x77,0x90}, +{0x7a,0x4e}, +{0x7b,0x28}, +{0x82,0x14}, +{0x83,0x23}, +{0x9a,0x23}, +{0x84,0x1a}, +{0x85,0x20}, +{0x89,0x02}, +{0x8a,0x64}, +{0x86,0x28}, +{0x96,0xa6}, +{0x97,0x0c}, +{0x98,0x18}, +{0x80,0x55}, +{0x24,0x88}, +{0x25,0x78}, +{0x69,0x00}, +{0x94,0x0a}, +{0x1F,0x20}, +{0x22,0x20}, +{0x26,0x20}, +{0x56,0x40}, +{0x61,0xd3}, +{0x79,0x48}, +{0x3b,0x60}, +{0x3c,0x28}, +{0x39,0x80}, +{0x3f,0xA0}, +{0x39,0x80}, +{0x40,0x58}, +{0x41,0x54}, +{0x42,0x4E}, +{0x43,0x44}, +{0x44,0x3E}, +{0x45,0x39}, +{0x46,0x35}, +{0x47,0x31}, +{0x48,0x2E}, +{0x49,0x2B}, +{0x4b,0x29}, +{0x4c,0x27}, +{0x4e,0x23}, +{0x4f,0x20}, +{0x50,0x1E}, +{0x51,0x0d}, +{0x52,0x21}, +{0x53,0x14}, +{0x54,0x15}, +{0x57,0x8d}, +{0x58,0x78}, +{0x59,0x5f}, +{0x5a,0x84}, +{0x5b,0x25}, +{0x5c,0x0e}, +{0x5d,0x95}, +{0x60,0x20}, +{0xb0,0xd0}, +{0xb1,0xc0}, +{0xb2,0xa8}, +{0xb3,0x88}, +{0x6a,0x81}, +{0x23,0x66}, +{0xa0,0x03}, +{0x06,0xe0}, +{0xa1,0x31}, +{0xa2,0x0e}, +{0xa3,0x26}, +{0xa4,0x0d}, +{0xa5,0x26}, +{0xa6,0x06}, +{0xa7,0x80}, +{0xa8,0x7e}, +{0xa9,0x28}, +{0xaa,0x28}, +{0xab,0x28}, +{0xac,0x3c}, +{0xad,0xf0}, +{0xc8,0x18}, +{0xc9,0x20}, +{0xca,0x17}, +{0xcb,0x1f}, +{0xaf,0x00}, +{0xc5,0x18}, +{0xc6,0x00}, +{0xc7,0x20}, +{0xae,0x83}, +{0xcc,0x40}, +{0xcd,0x50}, +{0xee,0x4c}, +{0x8e,0x03}, +{0x8f,0x96}, +{0x3a,0x02}, +{0xff,0xff}, +}; + +static int bf3703_init(const AK_ISP_SENSOR_INIT_PARA *para) +{ + int i; +// int value; + AK_ISP_SENSOR_REG_INFO *preg_info; + + preg_info = BF3703_YUV_640X480;//para->reg_info; + for (i = 0; i < sizeof(BF3703_YUV_640X480) / sizeof(BF3703_YUV_640X480[0]); i++) { +//value=bf3703_sensor_read_register(preg_info->reg_addr); +//printk(KERN_ERR "before w reg:0x%.x, val:0x%.x, to write:0x%.x\n", preg_info->reg_addr, value, preg_info->value); + bf3703_sensor_write_register(preg_info->reg_addr, preg_info->value); +//value=bf3703_sensor_read_register(preg_info->reg_addr); +//printk(KERN_ERR "after w reg:0x%.x, val:0x%.x\n", preg_info->reg_addr, value); + preg_info++; + } + + g_fps = MAX_FPS; + to_fps = MAX_FPS; + return 0; +} + +static int bf3703_read_id(void) +{ + return SENSOR_ID; +} + +static int bf3703_probe_id(void) +{ + u8 value; + u32 id; + + value = bf3703_sensor_read_register(BF_R_ID_MSB); + id = value << 8; + + value = bf3703_sensor_read_register(BF_R_ID_LSB); + id |= value; + + printk(KERN_ERR "%s id:0x%x\n", __func__, id); + + //return SENSOR_ID; + if (id != SENSOR_ID) + goto fail; + + return SENSOR_ID; + +fail: + return 0; +} + +static int bf3703_get_resolution(int *width, int *height) +{ + *width = SENSOR_OUTPUT_WIDTH; + *height = SENSOR_OUTPUT_HEIGHT; + + return 0; +} + +static int bf3703_get_mclk(void) +{ + return SENSOR_MCLK; +} + +static int bf3703_get_fps(void) +{ + return g_fps; +} + +static int bf3703_get_valid_coordinate(int *x, int *y) +{ + *x = SENSOR_VALID_OFFSET_X; + *y = SENSOR_VALID_OFFSET_Y; + + return 0; +} + +static enum sensor_bus_type bf3703_get_bus_type(void) +{ + return SENSROR_BUS_TYPE; +} + +static int bf3703_get_parameter(int param, void *value) +{ + int ret = 0; + enum sensor_get_param name = (enum sensor_get_param)param; + + switch (name) { + case GET_VSYNC_ACTIVE_MS: + *((int *)value) = VSYNC_ACTIVE_MS; + break; + + case GET_CUR_FPS: + *((int *)value) = g_fps; + break; + + default: + ret = -1; + break; + } + + return ret; +} + +static int bf3703_set_power_on(const int pwdn_pin, const int reset_pin) +{ + ak_sensor_set_pin_as_gpio(pwdn_pin); + ak_sensor_set_pin_dir(pwdn_pin, 1); + ak_sensor_set_pin_level(pwdn_pin, !SENSOR_PWDN_LEVEL); + ak_sensor_mdelay(10); + ak_sensor_set_pin_as_gpio(reset_pin); + ak_sensor_set_pin_dir(reset_pin, 1); + ak_sensor_set_pin_level(reset_pin, SENSOR_RESET_LEVEL); + ak_sensor_mdelay(30); //modify reset time, used to solve no camera interrupt problem + ak_sensor_set_pin_level(reset_pin, !SENSOR_RESET_LEVEL); + ak_sensor_mdelay(20); + + g_fps = MAX_FPS; + + return 0; +} + +static int bf3703_set_power_off(const int pwdn_pin, const int reset_pin) +{ + //sccb software standby mode + + ak_sensor_set_pin_level(pwdn_pin, SENSOR_PWDN_LEVEL); + ak_sensor_set_pin_dir(reset_pin, 0); + + return 0; +} + +static int bf3703_set_fps(int fps) +{ + int ret = 0; + + switch (fps) { + case 25: + break; + default: + printk(KERN_ERR "%s set fps=%d fail\n", __func__, fps); + ret = -EINVAL; + break; + } + + return ret; +} + +static int bf3703_set_standby_in(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static int bf3703_set_standby_out(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static AK_ISP_SENSOR_CB bf3703_callback = +{ + .sensor_init_func = bf3703_init, + .sensor_read_reg_func = bf3703_sensor_read_register, + .sensor_write_reg_func = bf3703_sensor_write_register, + .sensor_read_id_func = bf3703_read_id, + .sensor_update_a_gain_func = bf3703_cmos_updata_a_gain, + .sensor_update_d_gain_func = bf3703_cmos_updata_d_gain, + .sensor_updata_exp_time_func = bf3703_cmos_updata_exp_time, + .sensor_timer_func = bf3703_cmos_update_a_gain_timer, + + .sensor_probe_id_func = bf3703_probe_id, //use IIC bus + .sensor_get_resolution_func = bf3703_get_resolution, + .sensor_get_mclk_func = bf3703_get_mclk, + .sensor_get_fps_func = bf3703_get_fps, + .sensor_get_valid_coordinate_func = bf3703_get_valid_coordinate, + .sensor_get_bus_type_func = bf3703_get_bus_type, + .sensor_get_parameter_func = bf3703_get_parameter, + + .sensor_set_power_on_func = bf3703_set_power_on, + .sensor_set_power_off_func = bf3703_set_power_off, + .sensor_set_fps_func = bf3703_set_fps, + .sensor_set_standby_in_func = bf3703_set_standby_in, + .sensor_set_standby_out_func = bf3703_set_standby_out +}; + +AK_SENSOR_MODULE(bf3703_callback, bf3703) +/////////////////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////////////////// +static int bf3703_cmos_updata_d_gain(const unsigned int d_gain) +{ + return 0; +} + +static int bf3703_cmos_updata_a_gain(const unsigned int a_gain) +{ + return 0; +} + +static int bf3703_cmos_updata_exp_time(unsigned int exp_time) +{ + return EFFECT_FRAMES; +} + +static int bf3703_cmos_update_a_gain_timer(void) +{ + return 0; +} diff --git a/drivers/media/video/plat-anyka/sensor_f22+f23+f28+f35+f37.c b/drivers/media/video/plat-anyka/sensor_f22+f23+f28+f35+f37.c new file mode 100644 index 00000000..1bb6479f --- /dev/null +++ b/drivers/media/video/plat-anyka/sensor_f22+f23+f28+f35+f37.c @@ -0,0 +1,669 @@ +/** + * @file sensor_f22_f23_f28_f35_f37.c + * @brief camera driver file + * Copyright (C) 2011 Anyka (Guangzhou) Microelectronics Technology Co., Ltd + * @author luo_zhanzhao + * @date + * @version 1.0 + * @ref + */ +#include "ak_sensor_common.h" + +#define SENSOR_PWDN_LEVEL 1 +#define SENSOR_RESET_LEVEL 0 +#define SENSOR_I2C_ADDR 0x80 +#define SENSOR_ID_F22 (0x0f22) +#define SENSOR_ID_F23 (0x0f23) +#define SENSOR_ID_F28 (0x0f28) +#define SENSOR_ID_F35 (0x0f35) +#define SENSOR_ID_F37 (0x0f37) + +/** + * 数值为 0 表示未检测到,当检测到某一款 SENSOR 时此值将固定为一个非 0 的值。 + */ +static u32 SENSOR_ID = 0; + +#define SENSOR_REGADDR_BYTE 1 +#define SENSOR_DATA_BYTE 1 +#define MAX_FPS 25 +#define SENSOR_OUTPUT_WIDTH 1920 +#define SENSOR_OUTPUT_HEIGHT 1080 +#define SENSOR_VALID_OFFSET_X 0 +#define SENSOR_VALID_OFFSET_Y 0 +#define SENSOR_BUS_TYPE BUS_TYPE_RAW +#define SENSOR_IO_INTERFACE DVP_INTERFACE +#define SENSOR_IO_LEVEL SENSOR_IO_LEVEL_3V3 + +#define OV_MAX(a, b) (((a) < (b) ) ? (b) : (a)) +#define OV_MIN(a, b) (((a) > (b) ) ? (b) : (a)) +#define OV_CLIP3(low, high, x) (OV_MAX(OV_MIN((x), high), low)) + +#define SKIP_NUM 2 +#define EXP_EFFECT_FRAMES 1 +#define VSYNC_ACTIVE_MS 31 + +//sensor_ucode,sensor_id,sensor_mclk,sensor_io_interface,mipi_mbps,mipi_lane,pclk +static struct ak_sensor_parameter sensor_parameter[] = +{ + {0x0f22, 0x0f22, 24, MIPI_INTERFACE, 192, 2, 43200000}, + {0x0f23, 0x0f23, 24, MIPI_INTERFACE, 192, 1, 43200000}, + {0X0f28, 0X0f28, 24, MIPI_INTERFACE, 192, 1, 43200000}, + {0x0f35, 0x0f35, 24, MIPI_INTERFACE, 192, 2, 43200000}, + {0X0f37, 0x0f37, 24, MIPI_INTERFACE, 720, 1, 43200000}, +}; + +static int f22_cmos_updata_d_gain(const unsigned int d_gain); +static int f22_cmos_updata_a_gain(const unsigned int a_gain); +static int f22_cmos_updata_exp_time(unsigned int exp_time); +static int f22_cmos_update_a_gain_timer(void); + +static int g_fps = MAX_FPS; +static int to_fps = MAX_FPS; +static int to_fps_value = 0; +static struct ak_sensor_parameter *current_sensor_parameter = NULL; + +static int f22_sensor_read_register(int reg) +{ + struct ak_sensor_i2c_data i2cdata; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = 0; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_read_register(&i2cdata); +} + +static int f22_sensor_write_register(int reg, int data) +{ + struct ak_sensor_i2c_data i2cdata; +// ak_sensor_print("%s~~~~~~~~~~reg:0x%x,data:0x%x\n",__func__,reg,data); + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = data; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_write_register(&i2cdata); +} + +static int f22_init(const AK_ISP_SENSOR_INIT_PARA *para) +{ + int i; + AK_ISP_SENSOR_REG_INFO *preg_info; +//int value; + + preg_info = para->reg_info; + for (i = 0; i < para->num; i++) { +//value=f22_sensor_read_register(preg_info->reg_addr); +//ak_sensor_print("before w reg:0x%.x, val:0x%.x, to write:0x%.x\n", preg_info->reg_addr, value, preg_info->value); + if (preg_info->reg_addr == 0xffff) + ak_sensor_mdelay(preg_info->value); + else + f22_sensor_write_register(preg_info->reg_addr, preg_info->value); +//value=f22_sensor_read_register(preg_info->reg_addr); +//ak_sensor_print("after w reg:0x%.x, val:0x%.x\n", preg_info->reg_addr, value); + preg_info++; + } + + g_fps = to_fps = MAX_FPS; + + return 0; +} + + + +static int f22_probe_id (void) +{ + u8 value = 0; + u32 id = 0; + int i=0; + /// ��ȡ 0x0a 0x0b ��ȡ��ǰ SENSOR ����š� + value = f22_sensor_read_register(0x0a); + id = value << 8; + value = f22_sensor_read_register(0x0b); + id |= value; + +// for(i=0; i<(sizeof(sensor_parameter)/sizeof(struct ak_sensor_parameter));i++) + for(i=0;i<5;i++) + { + if(id == sensor_parameter[i].sensor_ucode) + { + current_sensor_parameter = &sensor_parameter[i]; + break; + } + } + + SENSOR_ID = current_sensor_parameter->sensor_id; + return SENSOR_ID; + /* + switch (id) { + case SENSOR_ID_F22: + case SENSOR_ID_F23: + case SENSOR_ID_F28: + case SENSOR_ID_F35: + case SENSOR_ID_F37: + SENSOR_ID = id; ///< ��¼��ǰ SENSOR ��š� + break; + + default: + break; + } + + printk (KERN_ERR "%s id:0x%x\n", __func__, SENSOR_ID); + return SENSOR_ID;*/ +} + + +static int f22_read_id(void) +{ + if (0 == SENSOR_ID) { + SENSOR_ID = f22_probe_id (); + } + + if (0 == SENSOR_ID) { + return -1; + } + + return SENSOR_ID; +} + + +static int f22_get_resolution(int *width, int *height) +{ + *width = SENSOR_OUTPUT_WIDTH; + *height = SENSOR_OUTPUT_HEIGHT; + + return 0; +} + +static int f22_get_mclk(void) +{ + return current_sensor_parameter->sensor_mclk; +// return SENSOR_MCLK; +} + +static int f22_get_fps(void) +{ + return g_fps; +} + +static int f22_get_valid_coordinate(int *x, int *y) +{ + *x = SENSOR_VALID_OFFSET_X; + *y = SENSOR_VALID_OFFSET_Y; + + return 0; +} + +static enum sensor_bus_type f22_get_bus_type(void) +{ + return SENSOR_BUS_TYPE; +} + +static int f22_get_parameter(int param, void *value) +{ + int ret = 0; + enum sensor_get_param name = (enum sensor_get_param)param; + + switch (name) { + + case GET_MIPI_MHZ: + *((int *)value) = current_sensor_parameter->mipi_mbps; + //*((int *)value) = MIPI_MHZ; + break; + + case GET_VSYNC_ACTIVE_MS: + *((int *)value) = VSYNC_ACTIVE_MS; + break; + + case GET_CUR_FPS: + *((int *)value) = g_fps; + break; + + case GET_MIPI_LANE: + *((int *)value) = current_sensor_parameter->mipi_lane; + break; + + case GET_INTERFACE: + *((int *)value) = current_sensor_parameter->sensor_io_interface; + break; + + case GET_SENSOR_IO_LEVEL: + *((int *)value) = SENSOR_IO_LEVEL; + break; + + default: + ret = -1; + break; + } + + return ret; +} + +static int f22_set_power_on(const int pwdn_pin, const int reset_pin) +{ + ak_sensor_set_pin_as_gpio(pwdn_pin); + ak_sensor_set_pin_dir(pwdn_pin, 1); + ak_sensor_set_pin_level(pwdn_pin, !SENSOR_PWDN_LEVEL); + ak_sensor_mdelay(10); + ak_sensor_set_pin_as_gpio(reset_pin); + ak_sensor_set_pin_dir(reset_pin, 1); + ak_sensor_set_pin_level(reset_pin, SENSOR_RESET_LEVEL); + ak_sensor_mdelay(10); + ak_sensor_set_pin_level(reset_pin, !SENSOR_RESET_LEVEL); + ak_sensor_mdelay(20); + + printk(KERN_ERR"%s\n",__func__); + return 0; +} + +static int f22_set_power_off(const int pwdn_pin, const int reset_pin) +{ +#if 0 + u8 Reg0x3d = 0x48; + u8 Reg0xc3 = 0x00; +#endif + +#if 0 + //sccb software standby mode + + f22_sensor_write_register(0x3d, Reg0x3d); + f22_sensor_write_register(0xc3, Reg0xc3); +#endif + + ak_sensor_set_pin_level(pwdn_pin, SENSOR_PWDN_LEVEL); + ak_sensor_set_pin_dir(reset_pin, 0); + + return 0; +} + +static int f22_set_fps(const int fps) +{ + int ret = 0; + int tmp = 0; + int const sensor_if = current_sensor_parameter->sensor_io_interface; + + if (sensor_if == DVP_INTERFACE) { + /* actual mclk=24MHz pclk=76.8MHz, but should 30fps * (1280*2) * 1125 = 76.8MHz + * so all follow set fps is too higher than "FPS" + */ + switch (fps) { + case 25: + tmp = 0x04b0; + break; + + case 20: + tmp = 0x05dc; + break; + + case 15: + tmp = 0x07d0; + break; + + case 12: + tmp = 0x0960; + break; + + case 10: + tmp = 0x0bb8; + break; + + case 8: + tmp = 0x0ea6; + break; + + case 5: + tmp = 0x1770; + break; + + default: + ak_sensor_print("%s set fps fail\n", __func__); + ret = -EINVAL; + break; + } + }else if (sensor_if == MIPI_INTERFACE) { + /* actual mclk=24MHz pclk=76.8MHz, but should 30fps * (1381*2) * 1125 = 768MHz + * so all follow set fps is too higher than "FPS" + */ + switch (fps) { + case 25: + tmp = 0x0465; + break; + + case 20: + tmp = 0x056e; + break; + + case 15: + tmp = 0x073d; + break; + + case 12: + tmp = 0x08b0; + break; + + case 10: + tmp = 0x0adc; + break; + + case 8: + tmp = 0x0d93; + break; + + case 5: + tmp = 0x15b9; + break; + + default: + ak_sensor_print("%s set fps fail\n", __func__); + ret = -EINVAL; + break; + } + + } + + if (!ret) { +#if 0 + f22_sensor_write_register(0x23,tmp >> 8); + f22_sensor_write_register(0x22,tmp & 0xff); +#endif + //g_fps = fps; + to_fps = fps; + to_fps_value = tmp; + } + + return ret; +} + +static int f22_set_standby_in(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static int f22_set_standby_out(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + + +static AK_ISP_SENSOR_CB f22_callback = +{ + .sensor_init_func = f22_init, + .sensor_read_reg_func = f22_sensor_read_register, + .sensor_write_reg_func = f22_sensor_write_register, + .sensor_read_id_func = f22_read_id, + .sensor_update_a_gain_func = f22_cmos_updata_a_gain, + .sensor_update_d_gain_func = f22_cmos_updata_d_gain, + .sensor_updata_exp_time_func = f22_cmos_updata_exp_time, + .sensor_timer_func = f22_cmos_update_a_gain_timer, + + .sensor_probe_id_func = f22_probe_id, //use IIC bus + .sensor_get_resolution_func = f22_get_resolution, + .sensor_get_mclk_func = f22_get_mclk, + .sensor_get_fps_func = f22_get_fps, + .sensor_get_valid_coordinate_func = f22_get_valid_coordinate, + .sensor_get_bus_type_func = f22_get_bus_type, + .sensor_get_parameter_func = f22_get_parameter, + + .sensor_set_power_on_func = f22_set_power_on, + .sensor_set_power_off_func = f22_set_power_off, + .sensor_set_fps_func = f22_set_fps, + .sensor_set_standby_in_func = f22_set_standby_in, + .sensor_set_standby_out_func = f22_set_standby_out, +}; + +AK_SENSOR_MODULE(f22_callback, f22) + +/////////////////////////////////////////////////////////////////////////////////////////// + +static void f22_set_fps_async(void) +{ + if (to_fps != g_fps) { + f22_sensor_write_register(0x23, to_fps_value >> 8); + f22_sensor_write_register(0x22, to_fps_value & 0xff); + + g_fps = to_fps; + } +} + +static unsigned int f22_cmos_gains_convert(unsigned int a_gain, unsigned int d_gain, + unsigned int *a_gain_out ,unsigned int *d_gain_out) +{ + //ģ??????????ת???????????�??????????ת????sensor????????ʽ + unsigned int ag_interget_table[17] = {0, 1, 2, 2, 4, 4, 4, 4, 8, 8, 8, 8, + 8, 8, 8, 8, 16}; + unsigned int ag_integer, ag_fraction, tmp; + unsigned int ag_return; + + tmp = a_gain / 16; + if(tmp == 0) + { + tmp = 1; + ak_sensor_print("Div ERROR 000!\n"); + } + //ak_sensor_print("tmp = %d \n",tmp); + if(tmp<16) + { + tmp = OV_CLIP3(0, 16, tmp); + ag_integer = ag_interget_table[tmp]; + } + else if ((16<=tmp)&&(tmp<32)) + ag_integer = 16; + else if ((32<=tmp)&&(tmp<64)) + ag_integer = 32; + else// if((64<=tmp)&&(tmp<128)) + ag_integer = 64; + + + ag_fraction = (a_gain / ag_integer) - 16; + ag_fraction = OV_CLIP3(0, 15, ag_fraction); + + if (((ag_fraction + 16) * ag_integer) < a_gain) + { + if (ag_fraction < 15) + { + ag_fraction++; + } + // else if (ag_integer < 16) + else if (ag_integer <128) + { + tmp++; + /* + ag_integer = ag_interget_table[tmp]; + ag_fraction = 0; + */ + if(tmp<16) + { + tmp = OV_CLIP3(0, 16, tmp); + ag_integer = ag_interget_table[tmp]; + } + else if ((16<=tmp)&&(tmp<32)) + ag_integer = 16; + else if ((32<=tmp)&&(tmp<64)) + ag_integer = 32; + else if((64<=tmp)&&(tmp<128)) + ag_integer = 64; + + ag_fraction = 0; + } + else + { + } + } + + switch (ag_integer) + { + case 1 : + ag_integer = 0x00; + break; + case 2 : + ag_integer = 0x10; + break; + case 4 : + ag_integer = 0x20; + break; + case 8 : + ag_integer = 0x30; + break; + case 16 : + ag_integer = 0x40; + break; + case 32 : + ag_integer = 0x50; + break; + case 64 : + ag_integer = 0x60; + break; + default: + ag_integer = 0x00; + break; + } + + ag_return = ag_integer|ag_fraction; + *a_gain_out = ag_return ; + + return ag_return; +} + +static int f22_cmos_updata_d_gain(const unsigned int d_gain) +{ + //?????????????Ļص????? + return 0; +} + +/** + * 根据 SENSOR 4A/4C/4F 三个值获取 BLC Gb 的值。 + */ +static int get_blc_gb (int reg0x4a, int reg0x4c, int reg0x4f) { + + int blc_gb = 0; + + reg0x4a >>= 6; + reg0x4a &= 0x01; ///< 0X4A BIT6 + reg0x4f >>= 2; + reg0x4f &= 0x03; ///< 0X4F BIT[3:2] + reg0x4c &= 0xff; + + blc_gb = (reg0x4a << 10) | (reg0x4f << 8) | reg0x4c; + return blc_gb; + +} + + +static int f22_cmos_updata_a_gain(const unsigned int a_gain) +{ + //??????ģ???????Ļص????? + //unsigned short ag_value; + unsigned int tmp_a_gain; + unsigned int tmp_d_gain; + unsigned int tmp_a_gain_out; + unsigned int tmp_d_gain_out; + + tmp_a_gain = a_gain>>4; + tmp_d_gain = 0; + + f22_set_fps_async(); + + //ag_value = cmos_gains_update(isp,isp->aec_param.current_a_gain); + f22_cmos_gains_convert(tmp_a_gain, tmp_d_gain, &tmp_a_gain_out ,&tmp_d_gain_out); + + if (SENSOR_ID_F23 == SENSOR_ID) { + + /// 背光补偿调整策略。 + int const blc_gb = get_blc_gb ( + f22_sensor_read_register (0x4a), + f22_sensor_read_register (0x4c), + f22_sensor_read_register (0x4f)); + int const blc_gb_thrd = get_blc_gb (0x05, 0x93, 0); + + if (blc_gb > blc_gb_thrd) { + f22_sensor_write_register(0x8e, 0x04); + } else { + f22_sensor_write_register(0x8e, 0x00); + } + + /// F23 太阳黑子策略。 + if (tmp_a_gain_out >= 0x30) { + f22_sensor_write_register(0x0c, 0x40); + f22_sensor_write_register(0x66, 0x44); + } else { + f22_sensor_write_register(0x66, 0x04); + f22_sensor_write_register(0x0c, 0x00); + } + + } else if (SENSOR_ID_F37 == SENSOR_ID) { + /// F37 太阳黑子策略。 + + int reg0x2f = f22_sensor_read_register (0x2f); + int reg0x0c = f22_sensor_read_register (0x0c); + int reg0x82 = f22_sensor_read_register (0x82); + + if (tmp_a_gain_out < 0x10) { + reg0x2f |= (1<<5); + reg0x0c |= (1<<6); + reg0x82 |= (1<<1); + } else { + reg0x2f &= ~(1<<5); + reg0x0c &= ~(1<<6); + reg0x82 &= ~(1<<1); + } + + f22_sensor_write_register (0x2f, reg0x2f); + f22_sensor_write_register (0x0c, reg0x0c); + f22_sensor_write_register (0x82, reg0x82); + + } + + //ak_sensor_print("a_gain=%d\n",a_gain); + f22_sensor_write_register(0x00, tmp_a_gain_out); + + // updata_auto_exposure_num++; + //isp->aec_param.current_exposure_time = tmp_exposure_time; + return 0; +} + + +static int f22_cmos_updata_exp_time(unsigned int exp_time) +{ + //?????ع?ʱ???Ļص????? + unsigned char exposure_time_msb; + unsigned char exposure_time_lsb; +// ak_sensor_print("exp_time=%d\n",exp_time); + exposure_time_msb =(exp_time>>8)&0xff; + exposure_time_lsb = exp_time&0xff; + + f22_set_fps_async(); + +// ak_sensor_print("msb = %d\n",exposure_time_msb); +// ak_sensor_print("lsb = %d\n",exposure_time_lsb); + f22_sensor_write_register(0x02,exposure_time_msb); + f22_sensor_write_register(0x01,exposure_time_lsb); +// ak_sensor_print("0x16=%d\n",f22_sensor_read_register(0x16)); + return EXP_EFFECT_FRAMES; +} + +static int f22_cmos_update_a_gain_timer(void) +{ +#define UPDATE_FPS_TIMER_PERIOD (2) //s + static int start = 0; + static struct timeval stv = {.tv_sec = 0,}; + struct timeval tv; + + if ((to_fps != g_fps) && !start) { + start = 1; + do_gettimeofday(&stv); + } + + if (start == 1) { + do_gettimeofday(&tv); + if (tv.tv_sec >= stv.tv_sec + UPDATE_FPS_TIMER_PERIOD) { + f22_set_fps_async(); + stv.tv_sec = tv.tv_sec; + start = 0; + } + } + + return 0; +} diff --git a/drivers/media/video/plat-anyka/sensor_gc1054.c b/drivers/media/video/plat-anyka/sensor_gc1054.c new file mode 100644 index 00000000..3b5a1340 --- /dev/null +++ b/drivers/media/video/plat-anyka/sensor_gc1054.c @@ -0,0 +1,523 @@ +/** + * @file sensor_gc1054.c + * @brief camera driver file + * Copyright (C) 2011 Anyka (Guangzhou) Microelectronics Technology Co., Ltd + * @author ye_guohong + * @date + * @version 1.0 + * @ref + */ +#include "ak_sensor_common.h" + +#define SENSOR_PWDN_LEVEL 1 +#define SENSOR_RESET_LEVEL 0 +#define SENSOR_I2C_ADDR 0x42 +#define SENSOR_ID 0x1054 +#define SENSOR_MCLK 24 +#define SENSOR_REGADDR_BYTE 1 +#define SENSOR_DATA_BYTE 1 +#define MAX_FPS 30 +#define SENSOR_OUTPUT_WIDTH 1280 +#define SENSOR_OUTPUT_HEIGHT 720 +#define SENSOR_VALID_OFFSET_X 0 +#define SENSOR_VALID_OFFSET_Y 0 +#define SENSROR_BUS_TYPE BUS_TYPE_RAW + +#define EFFECT_FRAMES 1 +#define VSYNC_ACTIVE_MS 29 + +#define SENSOR_IO_INTERFACE MIPI_INTERFACE +#define SENSOR_IO_LEVEL SENSOR_IO_LEVEL_3V3 //SENSOR_IO_LEVEL_1V8 + +#define SKIP_NUM 2 +#define MIPI_MBPS 312 // 180 // 192 for 2lane +#define MIPI_LANE 1 +#define PCLK_FREQ (31200000) +#define EFFECT_FRAMES 1 + +static int gc1054_cmos_updata_d_gain(const unsigned int d_gain); +static int gc1054_cmos_updata_a_gain(const unsigned int a_gain); +static int gc1054_cmos_updata_exp_time(unsigned int exp_time); +static int gc1054_cmos_timer(void); + +static int g_fps = MAX_FPS; +static int to_fps = MAX_FPS; +static int to_fps_value = 0; + +static int gc1054_app_exp_time = 0; +static int gc1054_current_exp_time = 0; + +static int gc1054_sensor_read_register(int reg) +{ + struct ak_sensor_i2c_data i2cdata; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = 0; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_read_register(&i2cdata); +} + +static int gc1054_sensor_write_register(int reg, int data) +{ + struct ak_sensor_i2c_data i2cdata; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = data; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_write_register(&i2cdata); +} +static int gc1054_probe_id(void); + +static int gc1054_init(const AK_ISP_SENSOR_INIT_PARA *para) +{ + int i; + AK_ISP_SENSOR_REG_INFO *preg_info; + // int value; + gc1054_probe_id(); + + preg_info = para->reg_info; + for (i = 0; i < para->num; i++) { + gc1054_sensor_write_register(preg_info->reg_addr, preg_info->value); + preg_info++; + } + + // ygh gc1054_sensor_write_register(0x24,0xff); + + g_fps = MAX_FPS; + to_fps = MAX_FPS; + + return 0; +} + +static int gc1054_read_id(void) +{ + return SENSOR_ID; +} + +static int gc1054_probe_id(void) +{ + u8 value; + u32 id; + + value = gc1054_sensor_read_register(0xf0); + id = value << 8; + value = gc1054_sensor_read_register(0xf1); + id |= value; + pr_err("ID:%x\n",id); + + //if (id == SENSOR_ID) + //return SENSOR_ID; + if (id == 0x1054) + return SENSOR_ID; + + return 0; +} + +static int gc1054_get_resolution(int *width, int *height) +{ + *width = SENSOR_OUTPUT_WIDTH; + *height = SENSOR_OUTPUT_HEIGHT; + + return 0; +} + +static int gc1054_get_mclk(void) +{ + return SENSOR_MCLK; +} + +static int gc1054_get_fps(void) +{ + return g_fps; +} + +static int gc1054_get_valid_coordinate(int *x, int *y) +{ + *x = SENSOR_VALID_OFFSET_X; + *y = SENSOR_VALID_OFFSET_Y; + + return 0; +} + +static enum sensor_bus_type gc1054_get_bus_type(void) +{ + return SENSROR_BUS_TYPE; +} + +static int gc1054_get_parameter(int param, void *value) +{ + int ret = 0; + enum sensor_get_param name = (enum sensor_get_param)param; + + switch (name) { + case GET_MIPI_MHZ: + *((int *)value) = MIPI_MBPS; + break; + + case GET_MIPI_LANE: + *((int *)value) = MIPI_LANE; + break; + + case GET_INTERFACE: + *((int *)value) = SENSOR_IO_INTERFACE; + break; + + case GET_SENSOR_IO_LEVEL: + *((int *)value) = SENSOR_IO_LEVEL; + break; + + case GET_VSYNC_ACTIVE_MS: + *((int *)value) = VSYNC_ACTIVE_MS; + break; + + case GET_CUR_FPS: + *((int *)value) = g_fps; + break; + + default: + ret = -1; + break; + } + + return ret; +} + +static int gc1054_set_power_on(const int pwdn_pin, const int reset_pin) +{ + ak_sensor_set_pin_as_gpio(pwdn_pin); + ak_sensor_set_pin_dir(pwdn_pin, 1); + ak_sensor_set_pin_level(pwdn_pin, !SENSOR_PWDN_LEVEL); + ak_sensor_mdelay(10); + ak_sensor_set_pin_as_gpio(reset_pin); + ak_sensor_set_pin_dir(reset_pin, 1); + ak_sensor_set_pin_level(reset_pin, SENSOR_RESET_LEVEL); + ak_sensor_mdelay(10); + ak_sensor_set_pin_level(reset_pin, !SENSOR_RESET_LEVEL); + ak_sensor_mdelay(20); + + g_fps = MAX_FPS; + ak_sensor_print("%s\n",__func__); + return 0; +} + +static int gc1054_set_power_off(const int pwdn_pin, const int reset_pin) +{ + return 0; + ak_sensor_set_pin_level(pwdn_pin, SENSOR_PWDN_LEVEL); + ak_sensor_set_pin_dir(reset_pin, 0); + + return 0; +} + +static int get_vb(int fps100) +{ + int vb; + + vb = (724 + 16 + 10) * 3000 / fps100; + vb = vb - 724 - 16;//VB + + return vb; +} + +static int get_max_exp_current_fps(void) +{ + int max_exp; + + max_exp = (724 + 16 + 10) * 30 / g_fps; + + return max_exp; +} + +static int gc1054_set_fps(const int fps) +{ + /* + * gc1054: + * VTS = window_height + 16 +VB + * + * window_height: {0x0d,0x0e} = 724, @30fps + * VB: {0x07,0x08} = 10, @30fps + */ + + int tmp; + int tmp_fps; + + if (fps > MAX_FPS) + return -EINVAL; + + tmp_fps = fps * 100; + + if (fps == 12) + tmp_fps = 1250; + else if (fps == 14) + tmp_fps = 1428; + + tmp = get_vb(tmp_fps);//VB + + to_fps = fps; + to_fps_value = tmp; + + return 0; +} + +static int gc1054_set_standby_in(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static int gc1054_set_standby_out(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static AK_ISP_SENSOR_CB gc1054_callback = +{ + .sensor_init_func = gc1054_init, + .sensor_read_reg_func = gc1054_sensor_read_register, + .sensor_write_reg_func = gc1054_sensor_write_register, + .sensor_read_id_func = gc1054_read_id, + .sensor_update_a_gain_func = gc1054_cmos_updata_a_gain, + .sensor_update_d_gain_func = gc1054_cmos_updata_d_gain, + .sensor_updata_exp_time_func = gc1054_cmos_updata_exp_time, + .sensor_timer_func = gc1054_cmos_timer, + + .sensor_probe_id_func = gc1054_probe_id, //use IIC bus + .sensor_get_resolution_func = gc1054_get_resolution, + .sensor_get_mclk_func = gc1054_get_mclk, + .sensor_get_fps_func = gc1054_get_fps, + .sensor_get_valid_coordinate_func = gc1054_get_valid_coordinate, + .sensor_get_bus_type_func = gc1054_get_bus_type, + .sensor_get_parameter_func = gc1054_get_parameter, + + .sensor_set_power_on_func = gc1054_set_power_on, + .sensor_set_power_off_func = gc1054_set_power_off, + .sensor_set_fps_func = gc1054_set_fps, + .sensor_set_standby_in_func = gc1054_set_standby_in, + .sensor_set_standby_out_func = gc1054_set_standby_out +}; + +AK_SENSOR_MODULE(gc1054_callback, gc1054) + +/////////////////////////////////////////////////////////////////////////////////////////// + +#define ANALOG_GAIN_1 64 // 1.00x +#define ANALOG_GAIN_2 91 // 1.42x +#define ANALOG_GAIN_3 127 // 1.99x +#define ANALOG_GAIN_4 182 // 2.85x +#define ANALOG_GAIN_5 258 // 4.03x +#define ANALOG_GAIN_6 369 // 5.77x +#define ANALOG_GAIN_7 516 // 8.06x +#define ANALOG_GAIN_8 738 // 11.53x +#define ANALOG_GAIN_9 1051 // 16.42x +#define ANALOG_GAIN_10 1485 //23.3X +#define ANALOG_GAIN_11 2084 //32.57X + +u16 gc1054_setgain(u16 iGain) +{ + u16 iReg,temp; +// if(GC1054_Lock) +// return; +//#ifdef GC1054_DRIVER_TRACE +// SENSORDB("GC1054_SetGain iGain = %d \n",iGain); +//#endif + gc1054_sensor_write_register(0xfe, 0x01); + gc1054_sensor_write_register(0xb1, 0x01); + gc1054_sensor_write_register(0xb2, 0x00); + iReg = iGain; + if(iReg < 0x40) + iReg = 0x40; + else if((ANALOG_GAIN_1<= iReg)&&(iReg < ANALOG_GAIN_2)) + { + //analog gain + gc1054_sensor_write_register(0xb6, 0x00); + temp = iReg; + gc1054_sensor_write_register(0xb1, temp>>6); + gc1054_sensor_write_register(0xb2, (temp<<2)&0xfc); + //SENSORDB("GC1054 analogic gain 1x , GC1054 add pregain = %d\n",temp); + } + else if((ANALOG_GAIN_2<= iReg)&&(iReg < ANALOG_GAIN_3)) + { + gc1054_sensor_write_register(0xb6, 0x01); + temp = 64*iReg/ANALOG_GAIN_2; + gc1054_sensor_write_register(0xb1, temp>>6); + gc1054_sensor_write_register(0xb2, (temp<<2)&0xfc); + //SENSORDB("GC1054 analogic gain 1.4x , GC1054 add pregain = %d\n",temp); + } + else if((ANALOG_GAIN_3<= iReg)&&(iReg < ANALOG_GAIN_4)) + { + gc1054_sensor_write_register(0xb6, 0x02); + temp = 64*iReg/ANALOG_GAIN_3; + gc1054_sensor_write_register(0xb1, temp>>6); + gc1054_sensor_write_register(0xb2, (temp<<2)&0xfc); + //SENSORDB("GC1054 analogic gain 2.5x , GC1054 add pregain = %d\n",temp); + } + else if((ANALOG_GAIN_4<= iReg)&&(iReg < ANALOG_GAIN_5)) + { + gc1054_sensor_write_register(0xb6, 0x03); + temp = 64*iReg/ANALOG_GAIN_4; + gc1054_sensor_write_register(0xb1, temp>>6); + gc1054_sensor_write_register(0xb2, (temp<<2)&0xfc); + //SENSORDB("GC1054 analogic gain 3.54x , GC1054 add pregain = %d\n",temp); + } + else if((ANALOG_GAIN_5<= iReg)&&(iReg < ANALOG_GAIN_6)) + { + gc1054_sensor_write_register(0xb6, 0x04); + temp = 64*iReg/ANALOG_GAIN_5; + gc1054_sensor_write_register(0xb1, temp>>6); + gc1054_sensor_write_register(0xb2, (temp<<2)&0xfc); + //SENSORDB("GC1054 analogic gain 4.9x , GC1054 add pregain = %d\n",temp); + } + else if((ANALOG_GAIN_6<= iReg)&&(iReg < ANALOG_GAIN_7)) + { + gc1054_sensor_write_register(0xb6, 0x05);// + temp = 64*iReg/ANALOG_GAIN_6; + gc1054_sensor_write_register(0xb1, temp>>6); + gc1054_sensor_write_register(0xb2, (temp<<2)&0xfc); + //SENSORDB("GC1054 analogic gain6.9x, GC1054 add pregain = %d\n",temp); + } + else if((ANALOG_GAIN_7<= iReg)&&(iReg < ANALOG_GAIN_8)) + { + gc1054_sensor_write_register(0xb6, 0x06);// + temp = 64*iReg/ANALOG_GAIN_7; + gc1054_sensor_write_register(0xb1, temp>>6); + gc1054_sensor_write_register(0xb2, (temp<<2)&0xfc); + //SENSORDB("GC1054 analogic gain 9.7x, GC1054 add pregain = %d\n",temp); + } + else if((ANALOG_GAIN_8<= iReg)&&(iReg < ANALOG_GAIN_9)) + { + gc1054_sensor_write_register(0xb6, 0x07); + temp = 64*iReg/ANALOG_GAIN_8; + gc1054_sensor_write_register(0xb1, temp>>6); + gc1054_sensor_write_register(0xb2, (temp<<2)&0xfc); + //SENSORDB("GC1054 analogic gain 13.6x,GC1054 add pregain = %d\n",temp); + } + else if((ANALOG_GAIN_9<= iReg)&&(iReg < ANALOG_GAIN_10)) + { + gc1054_sensor_write_register(0xb6, 0x08); + temp = 64*iReg/ANALOG_GAIN_9; + gc1054_sensor_write_register(0xb1, temp>>6); + gc1054_sensor_write_register(0xb2, (temp<<2)&0xfc); + //SENSORDB("GC1054 analogic gain 19.5x ,GC1054 add pregain = %d\n",temp); + } + else if((ANALOG_GAIN_10<= iReg)&&(iReg < ANALOG_GAIN_11)) + { + gc1054_sensor_write_register(0xb6, 0x09); + temp = 64*iReg/ANALOG_GAIN_10; + gc1054_sensor_write_register(0xb1, temp>>6); + gc1054_sensor_write_register(0xb2, (temp<<2)&0xfc); + //SENSORDB("GC1054 analogic gain 27x ,GC1054 add pregain = %d\n",temp); + } + else if(ANALOG_GAIN_11<= iReg) + { + gc1054_sensor_write_register(0xb6, 0x0a); + temp = 64*iReg/ANALOG_GAIN_11; + gc1054_sensor_write_register(0xb1, temp>>6); + gc1054_sensor_write_register(0xb2, (temp<<2)&0xfc); + //SENSORDB("GC1054 analogic gain 39x ,GC1054 add pregain = %d\n",temp); + } + + gc1054_sensor_write_register(0xfe, 0x00); + + return 0; +} + +static void gc1054_set_fps_async(void) +{ + if (to_fps != g_fps) { + printk("%s g_fps:%d, to_fps:%d, value:0x%x\n", __func__, g_fps, to_fps, to_fps_value); + gc1054_sensor_write_register(0x07, to_fps_value>>8); + gc1054_sensor_write_register(0x08, to_fps_value & 0xff); + + g_fps = to_fps; + } +} + +static int gc1054_cmos_updata_d_gain(const unsigned int d_gain) +{ + //?????????????Ļص????? + return 0; +} + +static int gc1054_cmos_updata_a_gain(const unsigned int a_gain) +{ + //??????ģ???????Ļص????? + unsigned int tmp_a_gain; + unsigned int tmp_d_gain; + + gc1054_set_fps_async(); + + tmp_a_gain = a_gain>>2; + tmp_d_gain = 0; + + gc1054_setgain(tmp_a_gain); + return 0; +} + +static int gc1054_cmos_updata_exp_time(unsigned int exp_time) +{ + int max_exp_current_fps; + + //?????ع?ʱ???Ļص????? +// unsigned char exposure_time_msb; +// unsigned char exposure_time_lsb; + + gc1054_set_fps_async(); + +// printk("exp_time=%d\n",exp_time); +// exposure_time_msb =(exp_time>>8)&0xff; + // exposure_time_lsb = exp_time&0xff; +// printk("msb = %d\n",exposure_time_msb); +// printk("lsb = %d\n",exposure_time_lsb); + //gc1054_sensor_write_register(0x02,exposure_time_msb); + //gc1054_sensor_write_register(0x01,exposure_time_lsb); +// printk("0x16=%d\n",gc1054_sensor_read_register(0x16)); + + if(exp_time < 1) exp_time = 1; + if(exp_time > 8191) exp_time = 8191;//2^13 + gc1054_app_exp_time = exp_time; + max_exp_current_fps = get_max_exp_current_fps(); + if (exp_time > max_exp_current_fps) + exp_time = max_exp_current_fps; + gc1054_current_exp_time = exp_time; + + //Update Shutter + gc1054_sensor_write_register(0x04, (exp_time) & 0xff); + gc1054_sensor_write_register(0x03, (exp_time >> 8) & 0x1f); + + return EFFECT_FRAMES; +} + +static int gc1054_cmos_timer(void) +{ +#define UPDATE_FPS_TIMER_PERIOD (2) //s + static int start = 0; + static struct timeval stv = {.tv_sec = 0,}; + struct timeval tv; + + if (((to_fps != g_fps) || (gc1054_app_exp_time != gc1054_current_exp_time)) && + !start) { + start = 1; + do_gettimeofday(&stv); + } + + if (start == 1) { + do_gettimeofday(&tv); + if (tv.tv_sec >= stv.tv_sec + UPDATE_FPS_TIMER_PERIOD) { + /*设置帧率与曝光不同步会导致真实曝光可能没有打满*/ + if ((gc1054_app_exp_time != gc1054_current_exp_time) && + (gc1054_app_exp_time <= get_max_exp_current_fps())) + gc1054_cmos_updata_exp_time(gc1054_app_exp_time); + + gc1054_set_fps_async(); + stv.tv_sec = tv.tv_sec; + start = 0; + } + } + + return 0; +} diff --git a/drivers/media/video/plat-anyka/sensor_gc2053.c b/drivers/media/video/plat-anyka/sensor_gc2053.c new file mode 100644 index 00000000..3fea0096 --- /dev/null +++ b/drivers/media/video/plat-anyka/sensor_gc2053.c @@ -0,0 +1,519 @@ +/** + * @file sensor_gc2053.c + * @brief camera driver file + * Copyright (C) 2011 Anyka (Guangzhou) Microelectronics Technology Co., Ltd + * @author ye_guohong + * @date + * @version 1.0 + * @ref + */ +#include "ak_sensor_common.h" + +#define SENSOR_PWDN_LEVEL 1 +#define SENSOR_RESET_LEVEL 0 +#define SENSOR_I2C_ADDR 0x6e +#define SENSOR_ID 0x2053 +#define SENSOR_MCLK 27 //dvp 27 +#define SENSOR_REGADDR_BYTE 1 +#define SENSOR_DATA_BYTE 1 +#define MAX_FPS 30 +#define SENSOR_OUTPUT_WIDTH 1920 +#define SENSOR_OUTPUT_HEIGHT 1080 +#define SENSOR_VALID_OFFSET_X 0 +#define SENSOR_VALID_OFFSET_Y 0 +#define SENSOR_BUS_TYPE BUS_TYPE_RAW +#define SENSOR_IO_INTERFACE MIPI_INTERFACE +//#define SENSOR_IO_INTERFACE DVP_INTERFACE +#define SENSOR_IO_LEVEL SENSOR_IO_LEVEL_1V8 + +#define SKIP_NUM 2 +#define EXP_EFFECT_FRAMES 1 +#define VSYNC_ACTIVE_MS 31 +#define MIPI_MBPS 594 +#define MIPI_LANE 2 + +static int gc2053_cmos_updata_d_gain(const unsigned int d_gain); +static int gc2053_cmos_updata_a_gain(const unsigned int a_gain); +static int gc2053_cmos_updata_exp_time(unsigned int exp_time); +u16 gc2053_setgain(u16 iGain); +static int gc2053_cmos_update_a_gain_timer(void); + +static int g_fps = MAX_FPS; +static int to_fps = MAX_FPS; +static int to_fps_value = 0; + +static int gc2053_sensor_read_register(int reg) +{ + struct ak_sensor_i2c_data i2cdata; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = 0; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_read_register(&i2cdata); +} + +static int gc2053_sensor_write_register(int reg, int data) +{ + struct ak_sensor_i2c_data i2cdata; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = data; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_write_register(&i2cdata); +} + +static int gc2053_init(const AK_ISP_SENSOR_INIT_PARA *para) +{ + int i; + AK_ISP_SENSOR_REG_INFO *preg_info; + // int value; + + preg_info = para->reg_info; + for (i = 0; i < para->num; i++) { + //int value=gc2053_sensor_read_register(preg_info->reg_addr); + //printk(KERN_ERR "before w reg:0x%.x, val:0x%.x, to write:0x%.x\n", preg_info->reg_addr, value, preg_info->value); + gc2053_sensor_write_register(preg_info->reg_addr, preg_info->value); + //value=gc2053_sensor_read_register(preg_info->reg_addr); + //printk(KERN_ERR "after w reg:0x%.x, val:0x%.x\n", preg_info->reg_addr, value); + preg_info++; + } + //gc2053_sensor_write_register(0x24,0xff); + //gc2053_sensor_write_register(0x8c,0x00); + //gc2053_sensor_write_register(0xfc,0xc4); + //gc2053_sensor_write_register(0x23,0x32); + + g_fps = MAX_FPS; + + return 0; +} + +static int gc2053_read_id(void) +{ + return SENSOR_ID; +} + +static int gc2053_probe_id(void) +{ + u8 value; + u32 id; + + value = gc2053_sensor_read_register(0xf0); + id = value << 8; + value = gc2053_sensor_read_register(0xf1); + id |= value; + + printk(KERN_ERR "%s id:0x%x\n", __func__, id); + + if (id == SENSOR_ID) + return SENSOR_ID; + + return 0; +} + +static int gc2053_get_resolution(int *width, int *height) +{ + *width = SENSOR_OUTPUT_WIDTH; + *height = SENSOR_OUTPUT_HEIGHT; + + return 0; +} + +static int gc2053_get_mclk(void) +{ + return SENSOR_MCLK; +} + +static int gc2053_get_fps(void) +{ + return g_fps; +} + +static int gc2053_get_valid_coordinate(int *x, int *y) +{ + *x = SENSOR_VALID_OFFSET_X; + *y = SENSOR_VALID_OFFSET_Y; + + return 0; +} + +static enum sensor_bus_type gc2053_get_bus_type(void) +{ + return SENSOR_BUS_TYPE; +} + +static int gc2053_get_parameter(int param, void *value) +{ + int ret = 0; + enum sensor_get_param name = (enum sensor_get_param)param; + + switch (name) { + case GET_MIPI_MHZ: + *((int *)value) = MIPI_MBPS; + break; + + case GET_VSYNC_ACTIVE_MS: + *((int *)value) = VSYNC_ACTIVE_MS; + break; + + case GET_CUR_FPS: + *((int *)value) = g_fps; + break; + + case GET_MIPI_LANE: + *((int *)value) = MIPI_LANE; + break; + + case GET_INTERFACE: + *((int *)value) = SENSOR_IO_INTERFACE; + break; + + case GET_SENSOR_IO_LEVEL: + *((int *)value) = SENSOR_IO_LEVEL; + break; + + default: + ret = -1; + break; + } + + return ret; +} + +static int gc2053_set_power_on(const int pwdn_pin, const int reset_pin) +{ + ak_sensor_set_pin_as_gpio(pwdn_pin); + ak_sensor_set_pin_dir(pwdn_pin, 1); + ak_sensor_set_pin_level(pwdn_pin, !SENSOR_PWDN_LEVEL); + ak_sensor_mdelay(10); + ak_sensor_set_pin_as_gpio(reset_pin); + ak_sensor_set_pin_dir(reset_pin, 1); + ak_sensor_set_pin_level(reset_pin, SENSOR_RESET_LEVEL); + ak_sensor_mdelay(10); + ak_sensor_set_pin_level(reset_pin, !SENSOR_RESET_LEVEL); + ak_sensor_mdelay(20); + + ak_sensor_print("%s\n",__func__); + return 0; +} + +static int gc2053_set_power_off(const int pwdn_pin, const int reset_pin) +{ + ak_sensor_set_pin_level(pwdn_pin, SENSOR_PWDN_LEVEL); + ak_sensor_set_pin_dir(reset_pin, 0); + + return 0; +} + +static int gc2053_set_fps(const int fps) +{ +//#if 0 + int ret = 0; + int tmp = 0; + int sensor_interface = SENSOR_IO_INTERFACE; + + if (sensor_interface == DVP_INTERFACE) { + /*DVP tmp = (96M / 3200 / fps) - 1088 -16 */ + switch (fps) { + case 30: + tmp = 0x0010; + break; + + case 25: + tmp = 0x0060; + break; + + case 20: + tmp = 0x018c; + break; + + case 15: + tmp = 0x0380; + break; + + case 12: + tmp = 0x0510; + break; + + case 10: + tmp = 0x0768; + break; + + case 8: + tmp = 0x09c0; + break; + + case 5: + tmp = 0x1320; + break; + + default: + printk(KERN_ERR "%s set fps fail\n", __func__); + ret = -EINVAL; + break; + } + }else { + /*MIPI tmp = (96M / 3200 / fps) - 1088 -16 */ + switch (fps) { + case 30: + tmp = 0x0470; + break; + + case 25: + tmp = 0x0554; + break; + + case 20: + tmp = 0x0697; + break; + + case 15: + tmp = 0x08ca; + break; + + case 12: + tmp = 0x0afc; + break; + + case 10: + tmp = 0x0d20; + break; + + case 8: + tmp = 0x107b; + break; + + case 5: + tmp = 0x1a5e; + break; + + default: + printk(KERN_ERR "%s set fps fail\n", __func__); + ret = -EINVAL; + break; + } + + } + + + + if (!ret) { +#if 0 + gc2053_sensor_write_register(0x07, tmp >> 8); + gc2053_sensor_write_register(0x08, tmp & 0xff); +#endif + //g_fps = fps; + to_fps = fps; + to_fps_value = tmp; + } + + return ret; +//#endif +} + +static int gc2053_set_standby_in(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static int gc2053_set_standby_out(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static AK_ISP_SENSOR_CB gc2053_callback = +{ + .sensor_init_func = gc2053_init, + .sensor_read_reg_func = gc2053_sensor_read_register, + .sensor_write_reg_func = gc2053_sensor_write_register, + .sensor_read_id_func = gc2053_read_id, + .sensor_update_a_gain_func = gc2053_cmos_updata_a_gain, + .sensor_update_d_gain_func = gc2053_cmos_updata_d_gain, + .sensor_updata_exp_time_func = gc2053_cmos_updata_exp_time, + .sensor_timer_func = gc2053_cmos_update_a_gain_timer, + + .sensor_probe_id_func = gc2053_probe_id, //use IIC bus + .sensor_get_resolution_func = gc2053_get_resolution, + .sensor_get_mclk_func = gc2053_get_mclk, + .sensor_get_fps_func = gc2053_get_fps, + .sensor_get_valid_coordinate_func = gc2053_get_valid_coordinate, + .sensor_get_bus_type_func = gc2053_get_bus_type, + .sensor_get_parameter_func = gc2053_get_parameter, + + .sensor_set_power_on_func = gc2053_set_power_on, + .sensor_set_power_off_func = gc2053_set_power_off, + .sensor_set_fps_func = gc2053_set_fps, + .sensor_set_standby_in_func = gc2053_set_standby_in, + .sensor_set_standby_out_func = gc2053_set_standby_out +}; + +AK_SENSOR_MODULE(gc2053_callback, gc2053) + +u8 regValTable[29][4] = { + {0x00, 0x00,0x01,0x00}, + {0x00, 0x10,0x01,0x0c}, + {0x00, 0x20,0x01,0x1b}, + {0x00, 0x30,0x01,0x2c}, + {0x00, 0x40,0x01,0x3f}, + {0x00, 0x50,0x02,0x16}, + {0x00, 0x60,0x02,0x35}, + {0x00, 0x70,0x03,0x16}, + {0x00, 0x80,0x04,0x02}, + {0x00, 0x90,0x04,0x31}, + {0x00, 0xa0,0x05,0x32}, + {0x00, 0xb0,0x06,0x35}, + {0x00, 0xc0,0x08,0x04}, + {0x00, 0x5a,0x09,0x19}, + {0x00, 0x83,0x0b,0x0f}, + {0x00, 0x93,0x0d,0x12}, + {0x00, 0x84,0x10,0x00}, + {0x00, 0x94,0x12,0x3a}, + {0x01, 0x2c,0x1a,0x02}, + {0x01, 0x3c,0x1b,0x20}, + {0x00, 0x8c,0x20,0x0f}, + {0x00, 0x9c,0x26,0x07}, + {0x02, 0x64,0x36,0x21}, + {0x02, 0x74,0x37,0x3a}, + {0x00, 0xc6,0x3d,0x02}, + {0x00, 0xdc,0x3f,0x3f}, + {0x02, 0x85,0x3f,0x3f}, + {0x02, 0x95,0x3f,0x3f}, + {0x00, 0xce,0x3f,0x3f}, + }; + +unsigned int gainLevelTable[34] = { + 64 , + 74 , + 89 , + 102, + 127, + 147, + 177, + 203, + 260, + 300, + 361, + 415, + 504, + 581, + 722, + 832, + 1027, + 1182, + 1408, + 1621, + 1990, + 2291, + 2850, + 3282, + 4048, + 5180, + 5500, + 6744, + 7073, + 0xffffffff + }; + +static void gc2053_set_fps_async(void) +{ + if (to_fps != g_fps) { + gc2053_sensor_write_register(0x41, to_fps_value >> 8); + gc2053_sensor_write_register(0x42, to_fps_value & 0xff); + + g_fps = to_fps; + } +} + +u16 gc2053_setgain(u16 iGain) +{ + u16 i; + u16 total; + unsigned int tol_dig_gain = 0; //d_gain = 0; + u16 iReg; + + iReg = iGain; + + total = sizeof(gainLevelTable) / sizeof(unsigned int); + for(i = 0; i < total; i++) + { + if((gainLevelTable[i] <= iReg)&&(iReg < gainLevelTable[i+1])) + break; + } + gc2053_sensor_write_register(0xfe,0x00); + gc2053_sensor_write_register(0xb4,regValTable[i][0]); + gc2053_sensor_write_register(0xb3,regValTable[i][1]); + gc2053_sensor_write_register(0xb8,regValTable[i][2]); + gc2053_sensor_write_register(0xb9,regValTable[i][3]); + + tol_dig_gain = iReg*64/gainLevelTable[i]; + gc2053_sensor_write_register(0xb1,(tol_dig_gain>>6)); + gc2053_sensor_write_register(0xb2,((tol_dig_gain&0x3f)<<2)); + return 0; +} + +static int gc2053_cmos_updata_d_gain(const unsigned int d_gain) +{ + //?????????????Ļص????? + return 0; +} + +static int gc2053_cmos_updata_a_gain(const unsigned int a_gain) +{ + + //??????ģ???????Ļص????? + unsigned int tmp_a_gain; + unsigned int tmp_d_gain; + + tmp_a_gain = a_gain>>2; + tmp_d_gain = 0; + + gc2053_set_fps_async(); + + gc2053_setgain(tmp_a_gain); + return 0; +} + + +static int gc2053_cmos_updata_exp_time(unsigned int exp_time) +{ + + gc2053_set_fps_async(); + + if(exp_time < 1) exp_time = 1; + if(exp_time > 8191) exp_time = 8191;//2^13 + //Update Shutter + gc2053_sensor_write_register(0x04, (exp_time) & 0xff); + gc2053_sensor_write_register(0x03, (exp_time >> 8) & 0x1f); + + return EXP_EFFECT_FRAMES; +} + +static int gc2053_cmos_update_a_gain_timer(void) +{ + +#define UPDATE_FPS_TIMER_PERIOD (2) //s + static int start = 0; + static struct timeval stv = {.tv_sec = 0,}; + struct timeval tv; + + if ((to_fps != g_fps) && !start) { + start = 1; + do_gettimeofday(&stv); + } + + if (start == 1) { + do_gettimeofday(&tv); + if (tv.tv_sec >= stv.tv_sec + UPDATE_FPS_TIMER_PERIOD) { + gc2053_set_fps_async(); + stv.tv_sec = tv.tv_sec; + start = 0; + } + } + + return 0; +} diff --git a/drivers/media/video/plat-anyka/sensor_h65.c b/drivers/media/video/plat-anyka/sensor_h65.c new file mode 100644 index 00000000..8a1d1617 --- /dev/null +++ b/drivers/media/video/plat-anyka/sensor_h65.c @@ -0,0 +1,508 @@ +/** + * @file camera_h65.c + * @brief camera driver file + * Copyright (C) 2011 Anyka (Guangzhou) Microelectronics Technology Co., Ltd + * @author xia_wenting + * @date 2011-09-21 + * @version 1.0 + * @ref + */ +#include "ak_sensor_common.h" + +#define SENSOR_PWDN_LEVEL 1 +#define SENSOR_RESET_LEVEL 0 +#define SENSOR_I2C_ADDR 0x60 +#define SENSOR_ID 0xa065 +#define SENSOR_MCLK 24 +#define SENSOR_REGADDR_BYTE 1 +#define SENSOR_DATA_BYTE 1 +#define MAX_FPS 30 +#define SENSOR_OUTPUT_WIDTH 1280 +#define SENSOR_OUTPUT_HEIGHT 960 +#define SENSOR_VALID_OFFSET_X 0 +#define SENSOR_VALID_OFFSET_Y 0 +#define SENSROR_BUS_TYPE BUS_TYPE_RAW + +#define SENSOR_IO_INTERFACE MIPI_INTERFACE +#define SENSOR_IO_LEVEL SENSOR_IO_LEVEL_1V8 + +#define SKIP_NUM 2 +#define EXP_EFFECT_FRAMES 1 +#define MIPI_MHZ 432 +#define MIPI_LANE 1 +#define EFFECT_FRAMES 1 +#define VSYNC_ACTIVE_MS 36 + +#define PCLK_FREQ (43200000) + +static int _target_frame_height = 0; +static int _target_exp_ctrl = 0; + +#define OV_MAX(a, b) (((a) < (b) ) ? (b) : (a)) +#define OV_MIN(a, b) (((a) > (b) ) ? (b) : (a)) +#define OV_CLIP3(low, high, x) (OV_MAX(OV_MIN((x), high), low)) + +static int h65_cmos_updata_d_gain(const unsigned int d_gain); +static int h65_cmos_updata_a_gain(const unsigned int a_gain); +static int h65_cmos_updata_exp_time(unsigned int exp_time); + +static int g_fps = 25; +static int to_fps = MAX_FPS; +static int to_fps_value = 0; + +static int h65_sensor_read_register(int reg) +{ + struct ak_sensor_i2c_data i2cdata; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = 0; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_read_register(&i2cdata); +} + +static int h65_sensor_write_register(int reg, int data) +{ + struct ak_sensor_i2c_data i2cdata; +// ak_sensor_print("%s~~~~~~~~~~reg:0x%x,data:0x%x\n",__func__,reg,data); + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = data; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_write_register(&i2cdata); +} + +static int h65_init(const AK_ISP_SENSOR_INIT_PARA *para) +{ + int i; + AK_ISP_SENSOR_REG_INFO *preg_info; +//int value; + + preg_info = para->reg_info; + for (i = 0; i < para->num; i++) { +//value=h65_sensor_read_register(preg_info->reg_addr); +//ak_sensor_print("before w reg:0x%.x, val:0x%.x, to write:0x%.x\n", preg_info->reg_addr, value, preg_info->value); + if (preg_info->reg_addr == 0xffff) + ak_sensor_mdelay(preg_info->value); + else + h65_sensor_write_register(preg_info->reg_addr, preg_info->value); +//value=h65_sensor_read_register(preg_info->reg_addr); +//ak_sensor_print("after w reg:0x%.x, val:0x%.x\n", preg_info->reg_addr, value); + preg_info++; + } + g_fps = MAX_FPS; + to_fps = MAX_FPS; + + return 0; +} + +static int h65_read_id(void) +{ + return SENSOR_ID; +} + +static int h65_probe_id(void) +{ + u8 value; + u32 id; + + value = h65_sensor_read_register(0x0a); + id = value << 8; + value = h65_sensor_read_register(0x0b); + id |= value; + + if (id == 0x0a65) + return SENSOR_ID; + + return 0; +} + +static int h65_get_resolution(int *width, int *height) +{ + *width = SENSOR_OUTPUT_WIDTH; + *height = SENSOR_OUTPUT_HEIGHT; + + return 0; +} + +static int h65_get_mclk(void) +{ + return SENSOR_MCLK; +} + +static int h65_get_fps(void) +{ + return g_fps; +} + +static int h65_get_valid_coordinate(int *x, int *y) +{ + *x = SENSOR_VALID_OFFSET_X; + *y = SENSOR_VALID_OFFSET_Y; + + return 0; +} + +static enum sensor_bus_type h65_get_bus_type(void) +{ + return SENSROR_BUS_TYPE; +} + +static int h65_get_parameter(int param, void *value) +{ + int ret = 0; + enum sensor_get_param name = (enum sensor_get_param)param; + + switch (name) { + case GET_MIPI_MHZ: + *((int *)value) = MIPI_MHZ; + break; + + case GET_VSYNC_ACTIVE_MS: + *((int *)value) = VSYNC_ACTIVE_MS; + break; + + case GET_CUR_FPS: + *((int *)value) = g_fps; + break; + + case GET_MIPI_LANE: + *((int *)value) = MIPI_LANE; + break; + + case GET_INTERFACE: + *((int *)value) = SENSOR_IO_INTERFACE; + break; + + case GET_SENSOR_IO_LEVEL: + *((int *)value) = SENSOR_IO_LEVEL; + break; + + default: + ret = -1; + break; + } + + return ret; +} + +static int h65_set_power_on(const int pwdn_pin, const int reset_pin) +{ + ak_sensor_set_pin_as_gpio(pwdn_pin); + ak_sensor_set_pin_dir(pwdn_pin, 1); + ak_sensor_set_pin_level(pwdn_pin, !SENSOR_PWDN_LEVEL); + ak_sensor_mdelay(10); + ak_sensor_set_pin_as_gpio(reset_pin); + ak_sensor_set_pin_dir(reset_pin, 1); + ak_sensor_set_pin_level(reset_pin, SENSOR_RESET_LEVEL); + ak_sensor_mdelay(10); + ak_sensor_set_pin_level(reset_pin, !SENSOR_RESET_LEVEL); + ak_sensor_mdelay(20); + + g_fps = 25; + ak_sensor_print("%s\n",__func__); + return 0; +} + +static int h65_set_power_off(const int pwdn_pin, const int reset_pin) +{ +#if 0 + u8 Reg0x3d = 0x48; + u8 Reg0xc3 = 0x00; +#endif + +#if 0 + //sccb software standby mode + + h65_sensor_write_register(0x3d, Reg0x3d); + h65_sensor_write_register(0xc3, Reg0xc3); +#endif + + ak_sensor_set_pin_level(pwdn_pin, SENSOR_PWDN_LEVEL); + ak_sensor_set_pin_dir(reset_pin, 0); + + return 0; +} + +static int h65_set_fps(const int fps) +{ + int ret = 0; + int tmp = 0; + + switch (fps) { + case 30: + //h65_sensor_write_register(0x23,0x03); + //h65_sensor_write_register(0x22,0xe8); + tmp = 0x03e8; + break; + + case 25: + //h65_sensor_write_register(0x23,0x04); + //h65_sensor_write_register(0x22,0xb0); + tmp = 0x04b0; + break; + + case 15: + //h65_sensor_write_register(0x23,0x07); + //h65_sensor_write_register(0x22,0xd0); + tmp = 0x07d0; + break; + + case 13: //actual: 12.5fps + case 12: + //h65_sensor_write_register(0x23,0x09); + //h65_sensor_write_register(0x22,0x60); + tmp = 0x0960; + break; + + case 10: + //h65_sensor_write_register(0x23,0x0b); + //h65_sensor_write_register(0x22,0xb8); + tmp = 0x0bb8; + break; + case 8: + //h65_sensor_write_register(0x23,0x0e); + //h65_sensor_write_register(0x22,0xa6); + tmp = 0x0ea6; + break; + + default: + ak_sensor_print("%s set fps fail\n", __func__); + ret = -EINVAL; + break; + } + + if (!ret){ + //g_fps = fps; + to_fps = fps; + to_fps_value = tmp; + } + + return ret; +} + +static int h65_set_standby_in(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static int h65_set_standby_out(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + + +static int __on_timer(void) { + + /// 高温补偿逻辑。 + int const reg_4f = h65_sensor_read_register (0x4f); + int const reg_4b = h65_sensor_read_register (0x4b); + int const value = (reg_4f & 0x3) * 0x100 + reg_4b; + +// printk (KERN_ERR "4f=%02x 4b=%02x val=%02x\r\n", reg_4f, reg_4b, value); + + if (value > 0xc8) { + h65_sensor_write_register (0x7f,0x56); + h65_sensor_write_register (0x8e,0x03); + } else { + h65_sensor_write_register(0x7f,0x52); + h65_sensor_write_register(0x8e,0x00); + } + + return 0; +} + + +static AK_ISP_SENSOR_CB h65_callback = +{ + .sensor_init_func = h65_init, + .sensor_read_reg_func = h65_sensor_read_register, + .sensor_write_reg_func = h65_sensor_write_register, + .sensor_read_id_func = h65_read_id, + .sensor_update_a_gain_func = h65_cmos_updata_a_gain, + .sensor_update_d_gain_func = h65_cmos_updata_d_gain, + .sensor_updata_exp_time_func = h65_cmos_updata_exp_time, + .sensor_timer_func = __on_timer, + + .sensor_probe_id_func = h65_probe_id, //use IIC bus + .sensor_get_resolution_func = h65_get_resolution, + .sensor_get_mclk_func = h65_get_mclk, + .sensor_get_fps_func = h65_get_fps, + .sensor_get_valid_coordinate_func = h65_get_valid_coordinate, + .sensor_get_bus_type_func = h65_get_bus_type, + .sensor_get_parameter_func = h65_get_parameter, + + .sensor_set_power_on_func = h65_set_power_on, + .sensor_set_power_off_func = h65_set_power_off, + .sensor_set_fps_func = h65_set_fps, + .sensor_set_standby_in_func = h65_set_standby_in, + .sensor_set_standby_out_func = h65_set_standby_out +}; + +AK_SENSOR_MODULE(h65_callback, h65) + +/////////////////////////////////////////////////////////////////////////////////////////// + +static unsigned int h65_cmos_gains_convert(unsigned int a_gain, unsigned int d_gain, + unsigned int *a_gain_out ,unsigned int *d_gain_out) +{ + //ģ??????????ת???????????㷨??????????ת????sensor????????ʽ + unsigned int ag_interget_table[17] = {0, 1, 2, 2, 4, 4, 4, 4, 8, 8, 8, 8, + 8, 8, 8, 8, 16}; + unsigned int ag_integer, ag_fraction, tmp; + unsigned int ag_return; + + tmp = a_gain / 16; + if(tmp == 0) + { + tmp = 1; + ak_sensor_print("Div ERROR 000!\n"); + } + //ak_sensor_print("tmp = %d \n",tmp); + if(tmp<16) + { + tmp = OV_CLIP3(0, 16, tmp); + ag_integer = ag_interget_table[tmp]; + } + else if ((16<=tmp)&&(tmp<32)) + ag_integer = 16; + else if ((32<=tmp)&&(tmp<64)) + ag_integer = 32; + else// if((64<=tmp)&&(tmp<128)) + ag_integer = 64; + + + ag_fraction = (a_gain / ag_integer) - 16; + ag_fraction = OV_CLIP3(0, 15, ag_fraction); + + if (((ag_fraction + 16) * ag_integer) < a_gain) + { + if (ag_fraction < 15) + { + ag_fraction++; + } + // else if (ag_integer < 16) + else if (ag_integer <128) + { + tmp++; + /* + ag_integer = ag_interget_table[tmp]; + ag_fraction = 0; + */ + if(tmp<16) + { + tmp = OV_CLIP3(0, 16, tmp); + ag_integer = ag_interget_table[tmp]; + } + else if ((16<=tmp)&&(tmp<32)) + ag_integer = 16; + else if ((32<=tmp)&&(tmp<64)) + ag_integer = 32; + else if((64<=tmp)&&(tmp<128)) + ag_integer = 64; + + ag_fraction = 0; + } + else + { + } + } + + switch (ag_integer) + { + case 1 : + ag_integer = 0x00; + break; + case 2 : + ag_integer = 0x10; + break; + case 4 : + ag_integer = 0x20; + break; + case 8 : + ag_integer = 0x30; + break; + case 16 : + ag_integer = 0x40; + break; + case 32 : + ag_integer = 0x50; + break; + case 64 : + ag_integer = 0x60; + break; + default: + ag_integer = 0x00; + break; + } + + ag_return = ag_integer|ag_fraction; + *a_gain_out = ag_return ; + + return ag_return; +} + +static void h65_set_fps_async(void) +{ + if (to_fps != g_fps) { + h65_sensor_write_register(0x23, to_fps_value>>8); + h65_sensor_write_register(0x22, to_fps_value & 0xff); + + g_fps = to_fps; + } +} + +static int h65_cmos_updata_d_gain(const unsigned int d_gain) +{ + //?????????????Ļص????? + return 0; +} + +static int h65_cmos_updata_a_gain(const unsigned int a_gain) +{ + //??????ģ???????Ļص????? + unsigned short ag_value; + unsigned int tmp_a_gain; + unsigned int tmp_d_gain; + unsigned int tmp_a_gain_out; + unsigned int tmp_d_gain_out; + + tmp_a_gain = a_gain>>4; + tmp_d_gain = 0; + + h65_set_fps_async(); + + //ag_value = cmos_gains_update(isp,isp->aec_param.current_a_gain); + h65_cmos_gains_convert(tmp_a_gain, tmp_d_gain, &tmp_a_gain_out ,&tmp_d_gain_out); + ag_value = tmp_a_gain_out; + //ak_sensor_print("a_gain=%d\n",a_gain); + h65_sensor_write_register(0x00, tmp_a_gain_out); + // updata_auto_exposure_num++; + //isp->aec_param.current_exposure_time = tmp_exposure_time; + return 0; +} + + +static int h65_cmos_updata_exp_time(unsigned int exp_time) +{ + //?????ع?ʱ???Ļص????? + unsigned char exposure_time_msb; + unsigned char exposure_time_lsb; +// ak_sensor_print("exp_time=%d\n",exp_time); + exposure_time_msb =(exp_time>>8)&0xff; + exposure_time_lsb = exp_time&0xff; + + h65_set_fps_async(); + +// ak_sensor_print("msb = %d\n",exposure_time_msb); +// ak_sensor_print("lsb = %d\n",exposure_time_lsb); + h65_sensor_write_register(0x02,exposure_time_msb); + h65_sensor_write_register(0x01,exposure_time_lsb); +// ak_sensor_print("0x16=%d\n",h65_sensor_read_register(0x16)); + + return EFFECT_FRAMES; +} diff --git a/drivers/media/video/plat-anyka/sensor_imx307.c b/drivers/media/video/plat-anyka/sensor_imx307.c new file mode 100644 index 00000000..5177841b --- /dev/null +++ b/drivers/media/video/plat-anyka/sensor_imx307.c @@ -0,0 +1,417 @@ +/** + * @file camera_imx307.c + * @brief camera driver file + * Copyright (C) 2017 Anyka (Guangzhou) Microelectronics Technology Co., Ltd + * @author + * @date + * @version 1.0 + * @ref + */ +#include "ak_sensor_common.h" +#include + +#define SENSOR_PWDN_LEVEL 1 +#define SENSOR_RESET_LEVEL 0 +#define SENSOR_I2C_ADDR 0x34 +#define SENSOR_ID 0x0307 +#define SENSOR_MCLK 36 +#define SENSOR_REGADDR_BYTE 2 +#define SENSOR_DATA_BYTE 1 +#define MAX_FPS 30 +#define DETAULT_FPS 30 +#define SENSOR_OUTPUT_WIDTH (1920 + SENSOR_VALID_OFFSET_X) +#define SENSOR_OUTPUT_HEIGHT (1080 + SENSOR_VALID_OFFSET_Y) +#define SENSOR_VALID_OFFSET_X 0 +#define SENSOR_VALID_OFFSET_Y 0 +#define SENSOR_BUS_TYPE BUS_TYPE_RAW +#define SENSOR_IO_INTERFACE MIPI_INTERFACE +#define SENSOR_IO_LEVEL SENSOR_IO_LEVEL_1V8 + +#define OV_MAX(a, b) (((a) < (b) ) ? (b) : (a)) +#define OV_MIN(a, b) (((a) > (b) ) ? (b) : (a)) +#define OV_CLIP3(low, high, x) (OV_MAX(OV_MIN((x), high), low)) + +#define SKIP_NUM 2 +#define EXP_EFFECT_FRAMES 1 +#define VSYNC_ACTIVE_MS 30 +#define MIPI_MBPS 450 +#define MIPI_LANE 2 + +static int imx307_cmos_updata_d_gain(const unsigned int d_gain); +static int imx307_cmos_updata_a_gain(const unsigned int a_gain); +static int imx307_cmos_updata_exp_time(unsigned int exp_time); +static int imx307_cmos_update_a_gain_timer(void); + +static int g_fps = DETAULT_FPS; +static int to_fps = DETAULT_FPS; +static int to_fps_value = 0; + +static int imx307_sensor_read_register(int reg) +{ + struct ak_sensor_i2c_data i2cdata; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = 0; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_read_register(&i2cdata); +} + +static int imx307_sensor_write_register(int reg, int data) +{ + struct ak_sensor_i2c_data i2cdata; +#if 0 + int tmp; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = 0; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + tmp = ak_sensor_read_register(&i2cdata); + if (tmp == data) + return 0; +#endif + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = data; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_write_register(&i2cdata); +} + +static int imx307_init(const AK_ISP_SENSOR_INIT_PARA *para) +{ + int i; + int value; + AK_ISP_SENSOR_REG_INFO *preg_info; + + preg_info = para->reg_info; + for (i = 0; i < para->num; i++) + { + value=imx307_sensor_read_register(preg_info->reg_addr); + //printk(KERN_ERR "before w reg:0x%.x, val:0x%.x, to write:0x%.x\n", preg_info->reg_addr, value, preg_info->value); + imx307_sensor_write_register(preg_info->reg_addr, preg_info->value); + value=imx307_sensor_read_register(preg_info->reg_addr); + //printk(KERN_ERR "after w reg:0x%.x, val:0x%.x\n", preg_info->reg_addr, value); + preg_info++; + } + + g_fps = MAX_FPS; + return 0; +} + +static int imx307_read_id(void) +{ + return SENSOR_ID; +} + +static int imx307_probe_id(void) +{ + u8 value1,value2; + u32 id = 0; + + value1 = imx307_sensor_read_register(0x3004); + value2 = imx307_sensor_read_register(0x3008); + + if (0x10 == value1 && 0xA0 == value2) + { + id = 0x0307; + } + + printk(KERN_ERR "%s id:0x%x, 0x%x, 0x%x\n", __func__, id, value1, value2); + + if (id != SENSOR_ID) + goto fail; + + return SENSOR_ID; + +fail: + return 0; +} + +static int imx307_get_resolution(int *width, int *height) +{ + *width = SENSOR_OUTPUT_WIDTH; + *height = SENSOR_OUTPUT_HEIGHT; + + return 0; +} + +static int imx307_get_mclk(void) +{ + return SENSOR_MCLK; +} + +static int imx307_get_fps(void) +{ + return g_fps; +} + +static int imx307_get_valid_coordinate(int *x, int *y) +{ + *x = SENSOR_VALID_OFFSET_X; + *y = SENSOR_VALID_OFFSET_Y; + + return 0; +} + +static enum sensor_bus_type imx307_get_bus_type(void) +{ + return SENSOR_BUS_TYPE; +} + +static int imx307_get_parameter(int param, void *value) +{ + int ret = 0; + enum sensor_get_param name = (enum sensor_get_param)param; + + switch (name) { + case GET_MIPI_MHZ: + *((int *)value) = MIPI_MBPS; + break; + + case GET_VSYNC_ACTIVE_MS: + *((int *)value) = VSYNC_ACTIVE_MS; + break; + + case GET_CUR_FPS: + *((int *)value) = g_fps; + break; + + case GET_MIPI_LANE: + *((int *)value) = MIPI_LANE; + break; + + case GET_INTERFACE: + *((int *)value) = SENSOR_IO_INTERFACE; + break; + + case GET_SENSOR_IO_LEVEL: + *((int *)value) = SENSOR_IO_LEVEL; + break; + + default: + ret = -1; + break; + } + + return ret; +} + +static int imx307_set_power_on(const int pwdn_pin, const int reset_pin) +{ + ak_sensor_set_pin_as_gpio(reset_pin); + ak_sensor_set_pin_dir(reset_pin, 1); + ak_sensor_set_pin_level(reset_pin, SENSOR_RESET_LEVEL); + ak_sensor_mdelay(30); + ak_sensor_set_pin_level(reset_pin, !SENSOR_RESET_LEVEL); + ak_sensor_mdelay(20); //modify reset time, used to solve no camera interrupt problem + + return 0; +} + +static int imx307_set_power_off(const int pwdn_pin, const int reset_pin) +{ + ak_sensor_set_pin_level(pwdn_pin, SENSOR_PWDN_LEVEL); + ak_sensor_set_pin_dir(reset_pin, 0); + + return 0; +} + +static int imx307_set_fps(int fps) +{ + int ret = 0; + int tmp = 0; + + /* tmp = ? / (reg0x3021~2=4400) / fps */ + switch (fps) { + case 30: + tmp = 0x0465; + break; + case 25: + tmp = 0x0546; + break; + case 20: + tmp = 0x0697; + break; + case 15: + tmp = 0x08ca; + break; + case 12: + tmp = 0x0a8c; + break; + case 10: + tmp = 0x0d2f; + break; + case 8: + tmp = 0x107a; + break; + default: + printk(KERN_ERR "%s set fps=%d fail\n", __func__, fps); + ret = -EINVAL; + break; + } + + if (!ret) + { + //g_fps = fps; + to_fps = fps; + to_fps_value = tmp; + + } + + return ret; +} + +static int imx307_set_standby_in(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static int imx307_set_standby_out(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static AK_ISP_SENSOR_CB imx307_callback = +{ + .sensor_init_func = imx307_init, + .sensor_read_reg_func = imx307_sensor_read_register, + .sensor_write_reg_func = imx307_sensor_write_register, + .sensor_read_id_func = imx307_read_id, + .sensor_update_a_gain_func = imx307_cmos_updata_a_gain, + .sensor_update_d_gain_func = imx307_cmos_updata_d_gain, + .sensor_updata_exp_time_func = imx307_cmos_updata_exp_time, + .sensor_timer_func = imx307_cmos_update_a_gain_timer, + + .sensor_probe_id_func = imx307_probe_id, //use IIC bus + .sensor_get_resolution_func = imx307_get_resolution, + .sensor_get_mclk_func = imx307_get_mclk, + .sensor_get_fps_func = imx307_get_fps, + .sensor_get_valid_coordinate_func = imx307_get_valid_coordinate, + .sensor_get_bus_type_func = imx307_get_bus_type, + .sensor_get_parameter_func = imx307_get_parameter, + + .sensor_set_power_on_func = imx307_set_power_on, + .sensor_set_power_off_func = imx307_set_power_off, + .sensor_set_fps_func = imx307_set_fps, + .sensor_set_standby_in_func = imx307_set_standby_in, + .sensor_set_standby_out_func = imx307_set_standby_out +}; + +AK_SENSOR_MODULE(imx307_callback, imx307) + + +static int imx307_cmos_updata_d_gain(const unsigned int d_gain) +{ + return 0; +} + +#if 1 +static void imx307_set_fps_async(void) +{ + if (to_fps != g_fps) { + imx307_sensor_write_register(0x3019, to_fps_value>>8); + imx307_sensor_write_register(0x3018, to_fps_value & 0xff); + + g_fps = to_fps; + } +} +#endif + +static int gain_table[241]= // from hisi +{ + // INDEX again 0~71; dgain >71 + 1024,1059,1097,1135,1175,1217,1259,1304,1349,1397,1446,1497,1549,1604,1660,1719,1779,1842,1906, //5.4dB + 1973,2043,2114,2189,2266,2345,2428,2513,2601,2693,2788,2886,2987,3092,3201,3313,3430,3550, 3675, //11.1dB + 3804,3938,4076,4219,4368,4521,4680,4845,5015,5191,5374,5562,5758,5960,6170,6387,6611,6843, 7084, //16.8dB + 7333,7591,7857,8133,8419,8715,9021,9338,9667,10006,10358,10722,11099,11489,11893,12311,12743, //21.9dB + 13191,13655,14135,14631,15146,15678,16229,16799,17390,18001,18633,19288,19966,20668,21394,22146, //26.7dB + 22924,23730,24564,25427,26320,27245,28203,29194,30220,31282,32381,33519,34697,35917,37179,38485, //31.5dB + 39838,41238,42687,44187,45740,47347,49011,50734,52517,54362,56273,58250,60297,62416,64610,66880, //36.3dB + 69230,71663,74182,76789,79487,82281,85172,88165,91264,94471,97791,101228,104785,108468,112279, //40.8dB + 116225,120310,124537,128914,133444,138134,142988,148013,153215,158599,164172,169942,175914,182096, //45dB + 188495,195119,201976,209074,216421,224027,231900,240049,248485,257217,266256,275613,285299,295325, //49.2db + 305703,316446,327567,339078,350994,363329,376097,389314,402995,417157,431817,446992,462700,478961,//53.4db + 495793,513216,531251,549921,569246,589250,609958,631393,653582,676550,700326,724936,750412,776783,//57.6db + 804081,832338,861589,891867,923209,955652,989236,1024000,1059986,1097236,1135795,1175709,1217026,1259795,//61.8db + 1304067,1349895,1397333,1446438,1497269,1549887,1604353,1660734,1719095,1779509,1842044,1906777,1973786,2043149,//66db + 2119949,2189273,2266209,2345848,2428287,2513622,2601956,2693395,2788046,2886024,2987445,3092431,3201105,3313599,//70.2db + 3430046,3550585,3675361,3804521,3938220,4076617,//72db + +}; +static unsigned int _cmos_gains_convert(unsigned int a_gain, unsigned int d_gain, + unsigned int *a_gain_out ,unsigned int *d_gain_out) +{ + #define DEAD_INDEX 240 + int tmp = 0; + int i = 0; + + /* + * gain(reg) = (gain(dB) * 10 / 3) + * + * */ + +#if 0 + if (a_gain <= 18432) + tmp = ((a_gain * 10) >> 8) / 3; +#endif + imx307_set_fps_async(); + a_gain <<= 2; + if (a_gain >= gain_table[DEAD_INDEX]) { + a_gain = gain_table[DEAD_INDEX]; + tmp = DEAD_INDEX; + } + + for (i = 1; i <= DEAD_INDEX; i++) { + if (a_gain < gain_table[i]) { + tmp = i - 1; + break; + } + } + imx307_sensor_write_register(0x3014, tmp); + return 0; +} + +static int imx307_cmos_updata_a_gain(const unsigned int a_gain) +{ + unsigned int tmp_a_gain; + unsigned int tmp_d_gain; + unsigned int tmp_a_gain_out; + unsigned int tmp_d_gain_out; + + tmp_a_gain = a_gain ; + tmp_d_gain = 0; + + return _cmos_gains_convert(tmp_a_gain, tmp_d_gain, &tmp_a_gain_out ,&tmp_d_gain_out); +} + +static int imx307_cmos_updata_exp_time(unsigned int exp_time) +{ + unsigned int exposure_time_ext; + unsigned int exposure_time_msb; + imx307_set_fps_async(); + if((to_fps_value - 0x2) < exp_time) + { + exp_time = to_fps_value - 0x2; + } + + exp_time = to_fps_value -(1 + exp_time); + + exposure_time_ext =(exp_time>>8)&0xff; + exposure_time_msb =(exp_time)&0xff; + imx307_sensor_write_register(0x3021,exposure_time_ext); + imx307_sensor_write_register(0x3020,exposure_time_msb); + return EXP_EFFECT_FRAMES; +} + +static int imx307_cmos_update_a_gain_timer(void) +{ + return 0; +} diff --git a/drivers/media/video/plat-anyka/sensor_ps5260.c b/drivers/media/video/plat-anyka/sensor_ps5260.c new file mode 100644 index 00000000..1d4b0b7a --- /dev/null +++ b/drivers/media/video/plat-anyka/sensor_ps5260.c @@ -0,0 +1,465 @@ +/** + * @file camera_ps5260.c + * @brief camera driver file + * Copyright (C) 2011 Anyka (Guangzhou) Microelectronics Technology Co., Ltd + * @author dong_feilong + * @date 2015-11-05 + * @version 1.0 + * @ref + */ +#include "ak_sensor_common.h" +#include + + +#define SENSOR_PWDN_LEVEL 0 +#define SENSOR_RESET_LEVEL 0 +#define SENSOR_I2C_ADDR 0x90 +#define SENSOR_ID 0x5260 +#define SENSOR_MCLK 24 +#define SENSOR_REGADDR_BYTE 1 +#define SENSOR_DATA_BYTE 1 +#define MAX_FPS 25 +#define SENSOR_OUTPUT_WIDTH 1920 +#define SENSOR_OUTPUT_HEIGHT 1080 +#define SENSOR_VALID_OFFSET_X 0 +#define SENSOR_VALID_OFFSET_Y 0 +#define SENSOR_BUS_TYPE BUS_TYPE_RAW +#define SENSOR_IO_INTERFACE MIPI_INTERFACE +#define SENSOR_IO_LEVEL SENSOR_IO_LEVEL_1V8 + +#define OV_MAX(a, b) (((a) < (b) ) ? (b) : (a)) +#define OV_MIN(a, b) (((a) > (b) ) ? (b) : (a)) +#define OV_CLIP3(low, high, x) (OV_MAX(OV_MIN((x), high), low)) + +#define SKIP_NUM 2 +#define EXP_EFFECT_FRAMES 1 +#define VSYNC_ACTIVE_MS 36 +#define MIPI_MBPS 480 +#define MIPI_LANE 2 + +#define AG_BASE (4) // Ratio +#define AG_HS_NODE (4*1024) // 6.0x (U.9) +#define AG_LS_NODE (3*1024) // 5.0x (U.9) +#define NE_PATCH_LB (25) +#define NE_PATCH_UB (200) +#define NE_PATCH_SCALE (38) + +static int ps5260_cmos_updata_d_gain(const unsigned int d_gain); +static int ps5260_cmos_updata_a_gain(const unsigned int a_gain); +static int ps5260_cmos_updata_exp_time(unsigned int exp_time); +static int ps5260_cmos_update_a_gain_timer(void); + +static int g_fps = MAX_FPS; +static int to_fps = MAX_FPS; +static int to_fps_value = 0; + +static unsigned int g_total_lines = 1350; // Total exposure lines, xxx = Bank1 (0xA<<8|0xB), based on initial setting +static unsigned int g_lines = 4; // Exposure lines, xxx = Bank1 (0xA<<8|0xB) - (0xC<<8|0xD), based on initial setting +static unsigned int g_sghd_flg = 0; // HS/LS mode flag, xxx = Bank1 0x18, based on initial setting +static unsigned int g_sns_ver = 0; // Sensor version +static unsigned int g_sns_const1 = 0; // Exposure constant, xxx = Bank1 (0xE<<8|0xF) + (0x5F<<8|0x60) + +// PS5260 Gain Table for 1.0x ~ 128x (U.9) +static const unsigned int ps5260_gain_table[97] = { + 1024, 1088, 1152, 1216, 1280, 1344, 1408, 1472, 1536, 1600, 1664, 1728, + 1792, 1856, 1920, 1984, 2048, 2176, 2304, 2432, 2560, 2688, 2816, 2944, + 3072, 3200, 3328, 3456, 3584, 3712, 3840, 3968, 4096, 4352, 4608, 4864, + 5120, 5376, 5632, 5888, 6144, 6400, 6656, 6912, 7168, 7424, 7680, 7936, + 8192, 8704, 9216, 9728, 10240, 10752, 11264, 11776, 12288, 12800, 13312, + 13824, 14336, 14848, 15360, 15872, 16384, 17408, 18432, 19456, 20480, 21504, + 22528, 23552, 24576, 25600, 26624, 27648, 28672, 29696, 30720, 31744, 32768, + 34816, 36864, 38912, 40960, 43008, 45056, 47104, 49152, 51200, 53248, 55296, + 57344, 59392, 61440, 63488, 65536, + }; + + + + +static int ps5260_sensor_read_register(int reg) +{ + struct ak_sensor_i2c_data i2cdata; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = 0; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_read_register(&i2cdata); +} + +static int ps5260_sensor_write_register(int reg, int data) +{ + struct ak_sensor_i2c_data i2cdata; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = data; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_write_register(&i2cdata); +} + +static int ps5260_init(const AK_ISP_SENSOR_INIT_PARA *para) +{ + int i; +// int value; + AK_ISP_SENSOR_REG_INFO *preg_info; + + preg_info = para->reg_info; + + for (i = 0; i < para->num; i++) { +//value=ps5260_sensor_read_register(preg_info->reg_addr); +//printk(KERN_ERR "before w reg:0x%.x, val:0x%.x, to write:0x%.x\n", preg_info->reg_addr, value, preg_info->value); + ps5260_sensor_write_register(preg_info->reg_addr, preg_info->value); +//value=ps5260_sensor_read_register(preg_info->reg_addr); +//printk(KERN_ERR "after w reg:0x%.x, val:0x%.x\n", preg_info->reg_addr, value); + preg_info++; + } + + ps5260_sensor_write_register(0xEF, 0x01); // Bank1 + g_sns_ver = ps5260_sensor_read_register(0x01)&0x0F; // Get Sensor Version + g_sns_const1 = ((ps5260_sensor_read_register(0xE) & 0x1f)<<8|ps5260_sensor_read_register(0xF)) + ((ps5260_sensor_read_register(0x10)&0x04)<<6|ps5260_sensor_read_register(0x12)); + ps5260_sensor_write_register(0xEF, 0x00); // Bank0 + + ps5260_sensor_write_register(0xEF, 0x01); // Bank1 + + + + g_fps = MAX_FPS; + return 0; +} + +static int ps5260_read_id(void) +{ + return SENSOR_ID; +} + +static int ps5260_probe_id(void) +{ + u8 value; + u32 id; + + ps5260_sensor_write_register(0xef, 0x00); + + value = ps5260_sensor_read_register(0x00); + id = value << 8; + + value = ps5260_sensor_read_register(0x01); + id |= value; + + printk(KERN_ERR "%s id:0x%x,id:0x%x\n", __func__, id, SENSOR_ID); + + if (id != SENSOR_ID) + goto fail; + + return SENSOR_ID; + +fail: + return 0; +} + +static int ps5260_get_resolution(int *width, int *height) +{ + *width = SENSOR_OUTPUT_WIDTH; + *height = SENSOR_OUTPUT_HEIGHT; + + return 0; +} + +static int ps5260_get_mclk(void) +{ + return SENSOR_MCLK; +} + +static int ps5260_get_fps(void) +{ + return g_fps; +} + +static int ps5260_get_valid_coordinate(int *x, int *y) +{ + *x = SENSOR_VALID_OFFSET_X; + *y = SENSOR_VALID_OFFSET_Y; + + return 0; +} + +static enum sensor_bus_type ps5260_get_bus_type(void) +{ + return SENSOR_BUS_TYPE; +} + +static int ps5260_get_parameter(int param, void *value) +{ + int ret = 0; + enum sensor_get_param name = (enum sensor_get_param)param; + + switch (name) { + case GET_MIPI_MHZ: + *((int *)value) = MIPI_MBPS; + break; + + case GET_VSYNC_ACTIVE_MS: + *((int *)value) = VSYNC_ACTIVE_MS; + break; + + case GET_CUR_FPS: + *((int *)value) = g_fps; + break; + + case GET_MIPI_LANE: + *((int *)value) = MIPI_LANE; + break; + + case GET_INTERFACE: + *((int *)value) = SENSOR_IO_INTERFACE; + break; + + case GET_SENSOR_IO_LEVEL: + *((int *)value) = SENSOR_IO_LEVEL; + break; + + default: + ret = -1; + break; + } + + return ret; +} +static int ps5260_set_power_on(const int pwdn_pin, const int reset_pin) +{ + ak_sensor_set_pin_as_gpio(pwdn_pin); + ak_sensor_set_pin_dir(pwdn_pin, 1); + ak_sensor_set_pin_level(pwdn_pin, !SENSOR_PWDN_LEVEL); + ak_sensor_mdelay(10); + ak_sensor_set_pin_as_gpio(reset_pin); + ak_sensor_set_pin_dir(reset_pin, 1); + ak_sensor_set_pin_level(reset_pin, SENSOR_RESET_LEVEL); + ak_sensor_mdelay(30); //modify reset time, used to solve no camera interrupt problem + ak_sensor_set_pin_level(reset_pin, !SENSOR_RESET_LEVEL); + ak_sensor_mdelay(20); + + return 0; +} + +static int ps5260_set_power_off(const int pwdn_pin, const int reset_pin) +{ + //sccb software standby mode + + ak_sensor_set_pin_level(pwdn_pin, SENSOR_PWDN_LEVEL); + ak_sensor_set_pin_dir(reset_pin, 0); + + return 0; +} + +static int ps5260_set_fps(int fps) +{ + int ret = 0; + int tmp=0; + + /* tmp = 80M / (bank1,(reg0x27~28)/2=2110) / fps */ + switch (fps) { + /*case 30: + tmp = 0x0464; + break;*/ + case 25: + tmp = 0x0545; + break; + case 20: + tmp = 0x0696; + break; + case 15: + tmp = 0x08c9; + break; + case 12: + tmp = 0x0afb; + break; + case 10: + tmp = 0x0d2e; + break; + case 8: + tmp = 0x1079; + break; + default: + printk(KERN_ERR"%s set fps=%d fail\n", __func__, fps); + ret = -1; + break; + } + + if (!ret) { + to_fps = fps; + to_fps_value = tmp; + } + + return ret; +} + +static int ps5260_set_standby_in(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static int ps5260_set_standby_out(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static AK_ISP_SENSOR_CB ps5260_callback = +{ + .sensor_init_func = ps5260_init, + .sensor_read_reg_func = ps5260_sensor_read_register, + .sensor_write_reg_func = ps5260_sensor_write_register, + .sensor_read_id_func = ps5260_read_id, + .sensor_update_a_gain_func = ps5260_cmos_updata_a_gain, + .sensor_update_d_gain_func = ps5260_cmos_updata_d_gain, + .sensor_updata_exp_time_func = ps5260_cmos_updata_exp_time, + .sensor_timer_func = ps5260_cmos_update_a_gain_timer, + + .sensor_probe_id_func = ps5260_probe_id, //use IIC bus + .sensor_get_resolution_func = ps5260_get_resolution, + .sensor_get_mclk_func = ps5260_get_mclk, + .sensor_get_fps_func = ps5260_get_fps, + .sensor_get_valid_coordinate_func = ps5260_get_valid_coordinate, + .sensor_get_bus_type_func = ps5260_get_bus_type, + .sensor_get_parameter_func = ps5260_get_parameter, + + .sensor_set_power_on_func = ps5260_set_power_on, + .sensor_set_power_off_func = ps5260_set_power_off, + .sensor_set_fps_func = ps5260_set_fps, + .sensor_set_standby_in_func = ps5260_set_standby_in, + .sensor_set_standby_out_func = ps5260_set_standby_out +}; + +AK_SENSOR_MODULE(ps5260_callback, ps5260) +/////////////////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////////////////// + +static int ps5260_cmos_updata_d_gain(const unsigned int d_gain) +{ + return 0; +} + +static void ps5260_set_fps_async(void) +{ + if (to_fps != g_fps) { + ps5260_sensor_write_register(0xEF, 0x01); + ps5260_sensor_write_register(0x0a, to_fps_value>>8); + ps5260_sensor_write_register(0x0b, to_fps_value & 0xff); + + ps5260_sensor_write_register(0x09, 0x01); + g_fps = to_fps; + } +} + +static int ps5260_cmos_updata_a_gain(const unsigned int a_gain) +{ + unsigned int i; + unsigned int gain_idx, sghd_patch = 1; + int gain = a_gain<<2; + + + //printk(KERN_ERR"ps5260_cmos_updata_a_gain: a_gain = %d***************\n", a_gain); + ps5260_set_fps_async(); + + // Check gain + if (gain > ps5260_gain_table[96]) + gain = ps5260_gain_table[96]; + else if(gain < ps5260_gain_table[0]) + gain = ps5260_gain_table[0]; + + // search most suitable gain into gain table + for (i=0; i<96; i++) { + if (ps5260_gain_table[i]> gain) + break; + } + + // Check HS/LS mode + if(ps5260_gain_table[i] > AG_HS_NODE) + { + if(g_sghd_flg == 1) sghd_patch = 0; + g_sghd_flg = 0; // HS Mode + } + else if(ps5260_gain_table[i] < AG_LS_NODE) + { + if(g_sghd_flg == 0) sghd_patch = 0; + g_sghd_flg = 1; // LS Mode + } + + // Calculate gain index + gain_idx = (g_sghd_flg == 0)? (i-16):i; + if(gain_idx < 5) + { + gain_idx = 5; + + } + ps5260_sensor_write_register(0xEF, 0x01);//bank1 + ps5260_sensor_write_register(0x83, gain_idx); + ps5260_sensor_write_register(0x18, g_sghd_flg); + + ps5260_sensor_write_register(0x09, 0x01);//update flag + + + return 0; +} + +static int ps5260_cmos_updata_exp_time(unsigned int exp_time) +{ + unsigned int ny, ne, ne_patch; + int lines = exp_time; + + //printk(KERN_ERR"ps5260_cmos_updata_exp_time: exp_time = %d***************\n", exp_time); + + ps5260_set_fps_async(); + + g_total_lines = to_fps_value; // LPF + + g_lines = ((g_total_lines - 4) > lines)?((3 < lines)?lines:3):(g_total_lines - 4); // 4 <= Ny <= LPF-3 + ny = g_total_lines - g_lines; // ne_patch update. + ne_patch = NE_PATCH_LB + ((ny*NE_PATCH_SCALE)>>8); + if (ne_patch > NE_PATCH_UB) + ne_patch = NE_PATCH_UB; + ne = g_sns_const1 - ne_patch; + + //printk(KERN_ERR"ps5260_cmos_updata_exp_time: ny:%d,ne:%d ne_patch:%d ***************\n", ny,ne,ne_patch); + + ps5260_sensor_write_register(0xEF, 0x01);//bank1 + ps5260_sensor_write_register(0x0C, (ny>>8)); //Cmd_OffNy1[15:8] + ps5260_sensor_write_register(0x0D, ny&0xFF); //Cmd_OffNy1[7:0] + ps5260_sensor_write_register(0x0E, (ne>>8)); //Cmd_OffNe1[11:8] + ps5260_sensor_write_register(0x0F, ne&0xFF); //Cmd_OffNe1[7:0] + ps5260_sensor_write_register(0x10, ((ne_patch&0x0100)>>6)); // + ps5260_sensor_write_register(0x12, ne_patch&0xFF); // + + ps5260_sensor_write_register(0x09, 0x01);//update flag + + return EXP_EFFECT_FRAMES; +} + +static int ps5260_cmos_update_a_gain_timer(void) +{ +#define UPDATE_FPS_TIMER_PERIOD (2) //s + static int start = 0; + static struct timeval stv = {.tv_sec = 0,}; + struct timeval tv; + + if ((to_fps != g_fps) && !start) { + start = 1; + do_gettimeofday(&stv); + } + + if (start == 1) { + do_gettimeofday(&tv); + if (tv.tv_sec >= stv.tv_sec + UPDATE_FPS_TIMER_PERIOD) { + ps5260_set_fps_async(); + stv.tv_sec = tv.tv_sec; + start = 0; + } + } + + return 0; +} + diff --git a/drivers/media/video/plat-anyka/sensor_sc1235.c b/drivers/media/video/plat-anyka/sensor_sc1235.c new file mode 100644 index 00000000..a4eca677 --- /dev/null +++ b/drivers/media/video/plat-anyka/sensor_sc1235.c @@ -0,0 +1,435 @@ +/** + * @file camera_sc1235.c + * @brief camera driver file + * Copyright (C) 2011 Anyka (Guangzhou) Microelectronics Technology Co., Ltd + * @author luweichun + * @date 2015-11-05 + * @version 1.0 + * @ref + */ +#include "ak_sensor_common.h" + +#define SENSOR_PWDN_LEVEL 0 +#define SENSOR_RESET_LEVEL 0 +#define SENSOR_I2C_ADDR 0x60 +#define SENSOR_ID 0x1235 +#define SENSOR_MCLK 24 +#define SENSOR_REGADDR_BYTE 2 +#define SENSOR_DATA_BYTE 1 +#define MAX_FPS 25 +#define SENSOR_OUTPUT_WIDTH 1280 +#define SENSOR_OUTPUT_HEIGHT 960 +#define SENSOR_VALID_OFFSET_X 0 +#define SENSOR_VALID_OFFSET_Y 0 +#define SENSROR_BUS_TYPE BUS_TYPE_RAW + +#define OV_MAX(a, b) (((a) < (b) ) ? (b) : (a)) +#define OV_MIN(a, b) (((a) > (b) ) ? (b) : (a)) +#define OV_CLIP3(low, high, x) (OV_MAX(OV_MIN((x), high), low)) + +#define SKIP_NUM 2 +#define EFFECT_FRAMES 1 +#define VSYNC_ACTIVE_MS 36 + +static int sc1235_cmos_updata_d_gain(const unsigned int d_gain); +static int sc1235_cmos_updata_a_gain(const unsigned int a_gain); +static int sc1235_cmos_updata_exp_time(unsigned int exp_time); +static int sc1235_cmos_update_a_gain_timer(void); + +static int g_fps = MAX_FPS; +static int to_fps = MAX_FPS; +static int to_fps_value = 0; +static int g_a_gain = 0; + +static int sc1235_sensor_read_register(int reg) +{ + struct ak_sensor_i2c_data i2cdata; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = 0; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_read_register(&i2cdata); +} + +static int sc1235_sensor_write_register(int reg, int data) +{ + struct ak_sensor_i2c_data i2cdata; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = data; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_write_register(&i2cdata); +} + +static int sc1235_init(const AK_ISP_SENSOR_INIT_PARA *para) +{ + int i; +// int value; + AK_ISP_SENSOR_REG_INFO *preg_info; + + preg_info = para->reg_info; + for (i = 0; i < para->num; i++) { +//value=sc1235_sensor_read_register(preg_info->reg_addr); +//printk(KERN_ERR "before w reg:0x%.x, val:0x%.x, to write:0x%.x\n", preg_info->reg_addr, value, preg_info->value); + sc1235_sensor_write_register(preg_info->reg_addr, preg_info->value); +//value=sc1235_sensor_read_register(preg_info->reg_addr); +//printk(KERN_ERR "after w reg:0x%.x, val:0x%.x\n", preg_info->reg_addr, value); + preg_info++; + } + + g_fps = MAX_FPS; + to_fps = MAX_FPS; + return 0; +} + +static int sc1235_read_id(void) +{ + return SENSOR_ID; +} + +static int sc1235_probe_id(void) +{ + u8 value; + u32 id; + + value = sc1235_sensor_read_register(0x3107); + id = value << 8; + + value = sc1235_sensor_read_register(0x3108); + id |= value; + + printk(KERN_ERR "%s id:0x%x\n", __func__, id); + + //return SENSOR_ID; + if (id != SENSOR_ID) + goto fail; + + return SENSOR_ID; + +fail: + return 0; +} + +static int sc1235_get_resolution(int *width, int *height) +{ + *width = SENSOR_OUTPUT_WIDTH; + *height = SENSOR_OUTPUT_HEIGHT; + + return 0; +} + +static int sc1235_get_mclk(void) +{ + return SENSOR_MCLK; +} + +static int sc1235_get_fps(void) +{ + return g_fps; +} + +static int sc1235_get_valid_coordinate(int *x, int *y) +{ + *x = SENSOR_VALID_OFFSET_X; + *y = SENSOR_VALID_OFFSET_Y; + + return 0; +} + +static enum sensor_bus_type sc1235_get_bus_type(void) +{ + return SENSROR_BUS_TYPE; +} + +static int sc1235_get_parameter(int param, void *value) +{ + int ret = 0; + enum sensor_get_param name = (enum sensor_get_param)param; + + switch (name) { + case GET_VSYNC_ACTIVE_MS: + *((int *)value) = VSYNC_ACTIVE_MS; + break; + + case GET_CUR_FPS: + *((int *)value) = g_fps; + break; + + default: + ret = -1; + break; + } + + return ret; +} + +static int sc1235_set_power_on(const int pwdn_pin, const int reset_pin) +{ + ak_sensor_set_pin_as_gpio(pwdn_pin); + ak_sensor_set_pin_dir(pwdn_pin, 1); + ak_sensor_set_pin_level(pwdn_pin, !SENSOR_PWDN_LEVEL); + ak_sensor_mdelay(10); + ak_sensor_set_pin_as_gpio(reset_pin); + ak_sensor_set_pin_dir(reset_pin, 1); + ak_sensor_set_pin_level(reset_pin, SENSOR_RESET_LEVEL); + ak_sensor_mdelay(30); //modify reset time, used to solve no camera interrupt problem + ak_sensor_set_pin_level(reset_pin, !SENSOR_RESET_LEVEL); + ak_sensor_mdelay(20); + + g_fps = MAX_FPS; + + return 0; +} + +static int sc1235_set_power_off(const int pwdn_pin, const int reset_pin) +{ + //sccb software standby mode + + ak_sensor_set_pin_level(pwdn_pin, SENSOR_PWDN_LEVEL); + ak_sensor_set_pin_dir(reset_pin, 0); + + return 0; +} + +static int sc1235_set_fps(int fps) +{ + int ret = 0; + int tmp=0; + + /* tmp = 54M / (reg0x320c~d=2160) / fps */ + switch (fps) { + case 25: + tmp = 0x03e8; + break; + case 13: //actual: 12.5fps + case 12: + tmp = 0x07e0; + break; + case 10: + tmp = 0x09c4; + break; + case 8: + tmp = 0x0c35; + break; + case 5: + tmp = 0x1388; + break; + default: + printk(KERN_ERR "%s set fps=%d fail\n", __func__, fps); + ret = -EINVAL; + break; + } + + if (!ret) { +#if 0 + sc1235_sensor_write_register(0x320e, tmp>>8); + sc1235_sensor_write_register(0x320f, tmp & 0xff); +#endif +#if 0 + sc1235_sensor_write_register(0x3336, (tmp - 0x2e8) >> 8); + sc1235_sensor_write_register(0x3337, (tmp - 0x2e8) & 0xff); + sc1235_sensor_write_register(0x3338, tmp>>8); + sc1235_sensor_write_register(0x3339, tmp & 0xff); +#endif + + //g_fps = fps; + to_fps = fps; + to_fps_value = tmp; + } + + return ret; +} + +static int sc1235_set_standby_in(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static int sc1235_set_standby_out(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static AK_ISP_SENSOR_CB sc1235_callback = +{ + .sensor_init_func = sc1235_init, + .sensor_read_reg_func = sc1235_sensor_read_register, + .sensor_write_reg_func = sc1235_sensor_write_register, + .sensor_read_id_func = sc1235_read_id, + .sensor_update_a_gain_func = sc1235_cmos_updata_a_gain, + .sensor_update_d_gain_func = sc1235_cmos_updata_d_gain, + .sensor_updata_exp_time_func = sc1235_cmos_updata_exp_time, + .sensor_timer_func = sc1235_cmos_update_a_gain_timer, + + .sensor_probe_id_func = sc1235_probe_id, //use IIC bus + .sensor_get_resolution_func = sc1235_get_resolution, + .sensor_get_mclk_func = sc1235_get_mclk, + .sensor_get_fps_func = sc1235_get_fps, + .sensor_get_valid_coordinate_func = sc1235_get_valid_coordinate, + .sensor_get_bus_type_func = sc1235_get_bus_type, + .sensor_get_parameter_func = sc1235_get_parameter, + + .sensor_set_power_on_func = sc1235_set_power_on, + .sensor_set_power_off_func = sc1235_set_power_off, + .sensor_set_fps_func = sc1235_set_fps, + .sensor_set_standby_in_func = sc1235_set_standby_in, + .sensor_set_standby_out_func = sc1235_set_standby_out +}; + +AK_SENSOR_MODULE(sc1235_callback, sc1235) +/////////////////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////////////////// +static int sc1235_curr_again_level = 0; +static int sc1235_curr_again_10x = -1; + +/* + gain_level: + 0: <2x + 1: <4x + 2: >4x + */ +static void sc1235_ext(int gain_level) +{ + sc1235_sensor_write_register(0x3903, 0x84); + sc1235_sensor_write_register(0x3903, 0x04); + sc1235_sensor_write_register(0x3812, 0x00); + switch (gain_level) { + case 0: + sc1235_sensor_write_register(0x3631, 0x84); + sc1235_sensor_write_register(0x3301, 0x05); + sc1235_sensor_write_register(0x3633, 0x2f); + break; + case 1: + sc1235_sensor_write_register(0x3631, 0x88); + sc1235_sensor_write_register(0x3301, 0x1f); + sc1235_sensor_write_register(0x3633, 0x23); + break; + case 2: + sc1235_sensor_write_register(0x3631, 0x88); + sc1235_sensor_write_register(0x3301, 0xff); + sc1235_sensor_write_register(0x3633, 0x43); + break; + default: + printk("%s gain_level error\n", __func__); + break; + } + + sc1235_sensor_write_register(0x3812, 0x30); +} + + +/* + gain_level: + 0: <10x + 1: >=10x + */ +static void sc1235_dpc(int gain_10x) +{ + switch (gain_10x) { + case 0: + sc1235_sensor_write_register(0x5781, 0x04); + sc1235_sensor_write_register(0x5785, 0x18); + break; + case 1: + sc1235_sensor_write_register(0x5781, 0x02); + sc1235_sensor_write_register(0x5785, 0x08); + break; + default: + ak_sensor_print("%s err gain_10x:%d\n", __func__, gain_10x); + break; + } +} + +static void sc1235_set_fps_async(void) +{ + if (to_fps != g_fps) { + sc1235_sensor_write_register(0x320e, to_fps_value>>8); + sc1235_sensor_write_register(0x320f, to_fps_value & 0xff); + + g_fps = to_fps; + } +} + +static int sc1235_cmos_updata_d_gain(const unsigned int d_gain) +{ + return 0; +} + +static int sc1235_cmos_updata_a_gain(const unsigned int a_gain) +{ + //??????ģ???????Ļص????? + int tmp; + int old_10x = sc1235_curr_again_10x; + + sc1235_set_fps_async(); + + //printk(KERN_ERR "again=%d,%lu\n",a_gain,jiffies); + g_a_gain = a_gain; + + tmp = a_gain >> 4; + sc1235_sensor_write_register(0x3e08, tmp >> 8); + sc1235_sensor_write_register(0x3e09, tmp & 0xff); + if (tmp < (2 << 4)) { + sc1235_curr_again_level = 0; + sc1235_ext(0); + } else if (tmp < (4 << 4)) { + sc1235_curr_again_level = 1; + sc1235_ext(1); + } else { + sc1235_curr_again_level = 2; + sc1235_ext(2); + } + + if (tmp < (10 << 4)) + sc1235_curr_again_10x = 0; + else + sc1235_curr_again_10x = 1; + + if (old_10x != sc1235_curr_again_10x) + sc1235_dpc(sc1235_curr_again_10x); + + return 0; +} + +static int sc1235_cmos_updata_exp_time(unsigned int exp_time) +{ + //?????ع?ʱ???Ļص????? + unsigned char exposure_time_ext; + unsigned char exposure_time_msb; + unsigned char exposure_time_lsb; + unsigned char tem; + + sc1235_set_fps_async(); + + //printk("exp_time=%d ",exp_time); + //printk(KERN_ERR "exp_time=%d,%lu\n",exp_time,jiffies); + exposure_time_ext =(exp_time>>12)&0xf; + exposure_time_msb =(exp_time>>4)&0xff; + exposure_time_lsb =((exp_time)&0xf)<<4; + tem=sc1235_sensor_read_register(0x3e02)&0x0f; + exposure_time_lsb =exposure_time_lsb|tem; + //printk("msb = %d ",exposure_time_msb); + //printk("lsb = %d\n",exposure_time_lsb); + sc1235_sensor_write_register(0x3e01,exposure_time_msb); + sc1235_sensor_write_register(0x3e02,exposure_time_lsb); + + tem = sc1235_sensor_read_register(0x3e00) & 0xf0; + tem |= exposure_time_ext; + sc1235_sensor_write_register(0x3e00, tem); + sc1235_ext(sc1235_curr_again_level); + + return EFFECT_FRAMES; +} + +static int sc1235_cmos_update_a_gain_timer(void) +{ + return 0; +} diff --git a/drivers/media/video/plat-anyka/sensor_sc1245.c b/drivers/media/video/plat-anyka/sensor_sc1245.c new file mode 100644 index 00000000..0f3dd7bb --- /dev/null +++ b/drivers/media/video/plat-anyka/sensor_sc1245.c @@ -0,0 +1,620 @@ +/** + * @file camera_sc1245.c + * @brief camera driver file + * Copyright (C) 2011 Anyka (Guangzhou) Microelectronics Technology Co., Ltd + * @author luweichun + * @date 2015-11-05 + * @version 1.0 + * @ref + */ +#include "ak_sensor_common.h" +//#include + +#define SENSOR_PWDN_LEVEL 1 +#define SENSOR_RESET_LEVEL 0 +#define SENSOR_I2C_ADDR 0x60 +#define SENSOR_ID 0x1245 +#define SENSOR_MCLK 24 +#define SENSOR_REGADDR_BYTE 2 +#define SENSOR_DATA_BYTE 1 +#define MAX_FPS 25 +#define SENSOR_OUTPUT_WIDTH 1280 +#define SENSOR_OUTPUT_HEIGHT 720 +#define SENSOR_VALID_OFFSET_X 0 +#define SENSOR_VALID_OFFSET_Y 0 +#define SENSROR_BUS_TYPE BUS_TYPE_RAW + +#define SKIP_NUM 1 +#define EFFECT_FRAMES 1 +#define VSYNC_ACTIVE_MS 36 + +#define OV_MAX(a, b) (((a) < (b) ) ? (b) : (a)) +#define OV_MIN(a, b) (((a) > (b) ) ? (b) : (a)) +#define OV_CLIP3(low, high, x) (OV_MAX(OV_MIN((x), high), low)) + +static int sc1245_cmos_updata_d_gain(const unsigned int d_gain); +static int sc1245_cmos_updata_a_gain(const unsigned int a_gain); +static int sc1245_cmos_updata_exp_time(unsigned int exp_time); +static int sc1245_cmos_update_a_gain_timer(void); + +static int g_fps = MAX_FPS; +static int to_fps = MAX_FPS; +static int to_fps_value = 0; +//static int g_a_gain = 0; +static int g_r3020 = -1; +static int sc1245_curr_again_level = 0; + +static int sc1245_sensor_read_register(int reg) +{ + struct ak_sensor_i2c_data i2cdata; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = 0; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_read_register(&i2cdata); +} + +static int sc1245_sensor_write_register(int reg, int data) +{ + struct ak_sensor_i2c_data i2cdata; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = data; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_write_register(&i2cdata); +} + +static int sc1245_init_post(void) +{ + sc1245_sensor_write_register(0x3e00, 0x00); + sc1245_sensor_write_register(0x3e01, 0x5d); + sc1245_sensor_write_register(0x3e02, 0x80); + sc1245_sensor_write_register(0x3e03, 0x0b); + sc1245_sensor_write_register(0x3e08, 0x03); + sc1245_sensor_write_register(0x3e09, 0x10); + + g_r3020 = sc1245_sensor_read_register(0x3020); + printk(KERN_ERR "g_r3020:%d\n", g_r3020); + + if (g_r3020 == 0x03) { + sc1245_sensor_write_register(0x3303, 0x18); + sc1245_sensor_write_register(0x3309, 0x98); + sc1245_sensor_write_register(0x3635, 0x80); + sc1245_sensor_write_register(0x3633, 0x46); + sc1245_sensor_write_register(0x3301, 0x03); + sc1245_sensor_write_register(0x3622, 0xd6); + } else if (g_r3020 == 0x02) { + sc1245_sensor_write_register(0x3303, 0x20); + sc1245_sensor_write_register(0x3309, 0xa0); + sc1245_sensor_write_register(0x3635, 0x84); + sc1245_sensor_write_register(0x3633, 0x42); + sc1245_sensor_write_register(0x3301, 0x04); + sc1245_sensor_write_register(0x3622, 0xd6); + } + + sc1245_sensor_write_register(0x0100, 0x01); + + return 0; +} + +static int sc1245_init(const AK_ISP_SENSOR_INIT_PARA *para) +{ + int i; + AK_ISP_SENSOR_REG_INFO *preg_info; + + preg_info = para->reg_info; + for (i = 0; i < para->num; i++) { +//int value=sc1245_sensor_read_register(preg_info->reg_addr); +//printk(KERN_ERR "before w reg:0x%.x, val:0x%.x, to write:0x%.x\n", preg_info->reg_addr, value, preg_info->value); + sc1245_sensor_write_register(preg_info->reg_addr, preg_info->value); +//value=sc1245_sensor_read_register(preg_info->reg_addr); +//printk(KERN_ERR "after w reg:0x%.x, val:0x%.x\n", preg_info->reg_addr, value); + preg_info++; + } + + sc1245_init_post(); + g_fps = MAX_FPS; + to_fps = MAX_FPS; + + return 0; +} + +static int sc1245_read_id(void) +{ + return SENSOR_ID; +} + +static int sc1245_probe_id(void) +{ + u8 value; + u32 id; + + value = sc1245_sensor_read_register(0x3107); + id = value << 8; + + value = sc1245_sensor_read_register(0x3108); + id |= value; + + //printk("%s id:0x%x\n", __func__, id); + if (id != SENSOR_ID) + goto fail; + + value = sc1245_sensor_read_register(0x3109); + //printk("%s reg[0x3109]:0x%x\n", __func__, value); + if (value != 0x01) + goto fail; + + return SENSOR_ID; + +fail: + return 0; +} + +static int sc1245_get_resolution(int *width, int *height) +{ + *width = SENSOR_OUTPUT_WIDTH; + *height = SENSOR_OUTPUT_HEIGHT; + + return 0; +} + +static int sc1245_get_mclk(void) +{ + return SENSOR_MCLK; +} + +static int sc1245_get_fps(void) +{ + return g_fps; +} + +static int sc1245_get_valid_coordinate(int *x, int *y) +{ + *x = SENSOR_VALID_OFFSET_X; + *y = SENSOR_VALID_OFFSET_Y; + + return 0; +} + +static enum sensor_bus_type sc1245_get_bus_type(void) +{ + return SENSROR_BUS_TYPE; +} + +#if 0 +static int sc1245_get_aec_delay_ms(int fps) +{ + /* + * maxfps = 30, delay 5ms to do aec + * */ + + int base_frame_interval_ms = 33;//1000 / 30; + int base_fps_aec_delay_ms = 5; + int frame_interval_ms = 1000 / fps; + int aec_delay_ms = frame_interval_ms - base_frame_interval_ms + base_fps_aec_delay_ms; + return aec_delay_ms; +} +#endif + +static int sc1245_get_parameter(int param, void *value) +{ + int ret = 0; + enum sensor_get_param name = (enum sensor_get_param)param; + + switch (name) { + case GET_VSYNC_ACTIVE_MS: + *((int *)value) = VSYNC_ACTIVE_MS; + break; + + case GET_CUR_FPS: + *((int *)value) = g_fps; + break; + + default: + ret = -1; + break; + } + + return ret; +} + +static int sc1245_set_power_on(const int pwdn_pin, const int reset_pin) +{ + ak_sensor_set_pin_as_gpio(pwdn_pin); + ak_sensor_set_pin_dir(pwdn_pin, 1); + ak_sensor_set_pin_level(pwdn_pin, !SENSOR_PWDN_LEVEL); + ak_sensor_mdelay(10); + ak_sensor_set_pin_as_gpio(reset_pin); + ak_sensor_set_pin_dir(reset_pin, 1); + ak_sensor_set_pin_level(reset_pin, SENSOR_RESET_LEVEL); + ak_sensor_mdelay(30); //modify reset time, used to solve no camera interrupt problem + ak_sensor_set_pin_level(reset_pin, !SENSOR_RESET_LEVEL); + ak_sensor_mdelay(20); + + g_fps = MAX_FPS; + + printk(KERN_ERR "%s\n",__func__); + return 0; +} + +static int sc1245_set_power_off(const int pwdn_pin, const int reset_pin) +{ + ak_sensor_set_pin_level(pwdn_pin, SENSOR_PWDN_LEVEL); + ak_sensor_set_pin_dir(reset_pin, 0); + + + return 0; +} + +static int sc1245_set_fps(const int fps) +{ + + int ret = 0; + int tmp=0; + + switch (fps) { + case 25: + tmp = 0x0384; ///< 900 1796 + break; + + case 20: + tmp = 0x464; ///< 1124 2244 + break; + + case 15: + tmp = 0x5db; ///< 1499 2994 + break; + + case 13: //actual: 12.5fps + case 12: + tmp = 0x0708; + break; + case 10: + tmp = 0x08ca; + break; + case 8: + tmp = 0x0a8c; + break; + case 5: + tmp = 0x1194; + break; + default: + printk(KERN_ERR "%s set fps=%d fail\n", __func__, fps); + ret = -EINVAL; + break; + } + + if (!ret) { +#if 0 + sc1245_sensor_write_register(0x320e, tmp>>8); + sc1245_sensor_write_register(0x320f, tmp & 0xff); +#endif + + //g_fps = fps; + to_fps = fps; + to_fps_value = tmp; + } + + return ret; +} + +static int sc1245_set_standby_in(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static int sc1245_set_standby_out(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static AK_ISP_SENSOR_CB sc1245_callback = +{ + .sensor_init_func = sc1245_init, + .sensor_read_reg_func = sc1245_sensor_read_register, + .sensor_write_reg_func = sc1245_sensor_write_register, + .sensor_read_id_func = sc1245_read_id, + .sensor_update_a_gain_func = sc1245_cmos_updata_a_gain, + .sensor_update_d_gain_func = sc1245_cmos_updata_d_gain, + .sensor_updata_exp_time_func = sc1245_cmos_updata_exp_time, + .sensor_timer_func = sc1245_cmos_update_a_gain_timer, + .sensor_probe_id_func = sc1245_probe_id, //use IIC bus + .sensor_get_resolution_func = sc1245_get_resolution, + .sensor_get_mclk_func = sc1245_get_mclk, + .sensor_get_fps_func = sc1245_get_fps, + .sensor_get_valid_coordinate_func = sc1245_get_valid_coordinate, + .sensor_get_bus_type_func = sc1245_get_bus_type, + .sensor_get_parameter_func = sc1245_get_parameter, + + .sensor_set_power_on_func = sc1245_set_power_on, + .sensor_set_power_off_func = sc1245_set_power_off, + .sensor_set_fps_func = sc1245_set_fps, + .sensor_set_standby_in_func = sc1245_set_standby_in, + .sensor_set_standby_out_func = sc1245_set_standby_out +}; + +AK_SENSOR_MODULE(sc1245_callback, sc1135) + + + + +static int sc1245_cmos_updata_d_gain(const unsigned int d_gain) +{ + return 0; +} + +static void sc1245_ext(int gain_level) +{ + sc1245_sensor_write_register(0x3903, 0x84); + sc1245_sensor_write_register(0x3903, 0x04); + sc1245_sensor_write_register(0x3812, 0x00); + + if (g_r3020 == 0x03) { + switch (gain_level) { + case 0: + sc1245_sensor_write_register(0x3633, 0x46); + sc1245_sensor_write_register(0x3301, 0x03); + sc1245_sensor_write_register(0x3622, 0xd6); + sc1245_sensor_write_register(0x3635, 0x89); + //printk(KERN_ERR "sc1245_ext(0)\n"); + break; + case 1: + sc1245_sensor_write_register(0x3633, 0x42); + sc1245_sensor_write_register(0x3301, 0x09); + sc1245_sensor_write_register(0x3622, 0xd6); + sc1245_sensor_write_register(0x3635, 0x86); + //printk(KERN_ERR "sc1245_ext(1)\n"); + break; + case 2: + sc1245_sensor_write_register(0x3633, 0x42); + sc1245_sensor_write_register(0x3301, 0x0a); + sc1245_sensor_write_register(0x3622, 0xd6); + sc1245_sensor_write_register(0x3635, 0x84); + //printk(KERN_ERR "sc1245_ext(2)\n"); + break; + case 3: + sc1245_sensor_write_register(0x3633, 0x42); + sc1245_sensor_write_register(0x3301, 0x0c); + sc1245_sensor_write_register(0x3622, 0x16); + sc1245_sensor_write_register(0x3635, 0x82); + //printk(KERN_ERR "sc1245_ext(3)\n"); + break; + case 4: + sc1245_sensor_write_register(0x3633, 0x42); + sc1245_sensor_write_register(0x3301, 0x32); + sc1245_sensor_write_register(0x3622, 0x16); + sc1245_sensor_write_register(0x3635, 0x80); + //printk(KERN_ERR "sc1245_ext(3)\n"); + break; + default: + printk("%s gain_level error\n", __func__); + break; + } + } else if (g_r3020 == 0x02) { + switch (gain_level) { + case 0: + sc1245_sensor_write_register(0x3633, 0x42); + sc1245_sensor_write_register(0x3301, 0x04); + sc1245_sensor_write_register(0x3622, 0xd6); + //printk(KERN_ERR "sc1245_ext(0)\n"); + break; + case 1: + sc1245_sensor_write_register(0x3633, 0x42); + sc1245_sensor_write_register(0x3301, 0x05); + sc1245_sensor_write_register(0x3622, 0xd6); + //printk(KERN_ERR "sc1245_ext(1)\n"); + break; + case 2: + sc1245_sensor_write_register(0x3633, 0x42); + sc1245_sensor_write_register(0x3301, 0x05); + sc1245_sensor_write_register(0x3622, 0xd6); + //printk(KERN_ERR "sc1245_ext(2)\n"); + break; + case 3: + sc1245_sensor_write_register(0x3633, 0x42); + sc1245_sensor_write_register(0x3301, 0x06); + sc1245_sensor_write_register(0x3622, 0x16); + //printk(KERN_ERR "sc1245_ext(3)\n"); + break; + case 4: + sc1245_sensor_write_register(0x3633, 0x42); + sc1245_sensor_write_register(0x3301, 0x32); + sc1245_sensor_write_register(0x3622, 0x16); + //printk(KERN_ERR "sc1245_ext(3)\n"); + break; + default: + printk("%s gain_level error\n", __func__); + break; + } + } + + sc1245_sensor_write_register(0x3812, 0x30); +} + +static void sc1245_set_fps_async(void) +{ + if (to_fps != g_fps) { + sc1245_sensor_write_register(0x320e, to_fps_value>>8); + sc1245_sensor_write_register(0x320f, to_fps_value & 0xff); + + g_fps = to_fps; + } +} + +static int curr_2x_dgain = 0, curr_corse_gain = -1; +static unsigned int _cmos_gains_convert(unsigned int a_gain, unsigned int d_gain, + unsigned int *a_gain_out ,unsigned int *d_gain_out) +{ +#define _2XGAIN 2048 + unsigned int fine_gain= 0; + unsigned int corse_gain=0,/*sensor_2x_again=0,*/sensor_2x_dgain=0; + int tmp = 0; + //int is_10x = 0; + //int ret = 0; + + sc1245_set_fps_async(); + + if(a_gain<_2XGAIN) //1 - 1.9375 + { + corse_gain = 0; + tmp = a_gain-1024; + sc1245_curr_again_level = 0; + } + else if(a_gain<4096) // 2 - 3.875 + { + corse_gain = 1; + tmp = a_gain/2-1024; + sc1245_curr_again_level = 1; + } + else if(a_gain<8192) //4-7.75 + { + corse_gain = 3; + tmp = a_gain/4-1024; + sc1245_curr_again_level = 2; + } + else if(a_gain<16384) //8 - 15.5 + { + corse_gain = 7; + tmp = a_gain/8-1024; + sc1245_curr_again_level = 3; + } + else if(a_gain<32768) // 16 - 31 + { + corse_gain = 7; + //sensor_2x_again =1; + sensor_2x_dgain = 1; + a_gain=a_gain>>1; + tmp = a_gain/8-1024; + sc1245_curr_again_level = 4; + } + else if(a_gain<65536) // 32 - 62 + { + corse_gain = 7; + //sensor_2x_again =1; + sensor_2x_dgain = 3; + a_gain=a_gain>>2; + tmp = a_gain/8-1024; + sc1245_curr_again_level = 4; + } + else if(a_gain<131072) // 64 -124 + { + corse_gain = 7; + //sensor_2x_again =1; + sensor_2x_dgain = 7; + a_gain=a_gain>>3; + tmp = a_gain/8-1024; + sc1245_curr_again_level = 4; + } + else // 128 - 248 + { + corse_gain = 7; + //sensor_2x_again =1; + sensor_2x_dgain = 0xf; + a_gain=a_gain>>4; + tmp = a_gain/8-1024; + sc1245_curr_again_level = 4; + } + + if(tmp>=0){ + fine_gain = tmp/65; + } + else{ + fine_gain = 0; + } + + fine_gain |= 0x10; + + sc1245_sensor_write_register(0x3e09,fine_gain); + + if (curr_2x_dgain != sensor_2x_dgain || + curr_corse_gain != corse_gain) { + tmp = sc1245_sensor_read_register(0x3e08) & 0x3; + tmp |= ((sensor_2x_dgain & 0x7) << 5) | (corse_gain << 2); + sc1245_sensor_write_register(0x3e08, tmp); + //printk("2xgain:%d, corse_gain:%d\n",sensor_2x_dgain, corse_gain); + } + + if (curr_2x_dgain != sensor_2x_dgain) { + tmp = sc1245_sensor_read_register(0x3e07) & 0x0e; + tmp |= (sensor_2x_dgain >> 3) & 0x01; + sc1245_sensor_write_register(0x3e07, tmp); + //printk("2xgain:%d\n",sensor_2x_dgain); + } + + sc1245_ext(sc1245_curr_again_level); + + curr_2x_dgain = sensor_2x_dgain; + curr_corse_gain = corse_gain; + + return 0; +} + +static int sc1245_cmos_updata_a_gain(const unsigned int a_gain) +{ + unsigned int tmp_a_gain; + unsigned int tmp_d_gain; + unsigned int tmp_a_gain_out; + unsigned int tmp_d_gain_out; + + //msleep(10); + tmp_a_gain = a_gain<<2; + tmp_d_gain = 0; + + return _cmos_gains_convert(tmp_a_gain, tmp_d_gain, &tmp_a_gain_out ,&tmp_d_gain_out); +} + + +static int sc1245_cmos_updata_exp_time(unsigned int exp_time) +{ + //?????ع?ʱ???Ļص????? + unsigned char exposure_time_msb; + unsigned char exposure_time_lsb; + unsigned char tem; + + sc1245_set_fps_async(); + + //printk("exp_time=%d ",exp_time); + //msleep(10); + exposure_time_msb =(exp_time>>4)&0xff; + exposure_time_lsb =((exp_time)&0xf)<<4; + tem=sc1245_sensor_read_register(0x3e02)&0x0f; + exposure_time_lsb =exposure_time_lsb|tem; + //printk("msb = %d ",exposure_time_msb); + //printk("lsb = %d\n",exposure_time_lsb); + sc1245_sensor_write_register(0x3e01,exposure_time_msb); + sc1245_sensor_write_register(0x3e02,exposure_time_lsb); + + sc1245_ext(sc1245_curr_again_level); + + return EFFECT_FRAMES; +} + +static int sc1245_cmos_update_a_gain_timer(void) +{ +#define UPDATE_FPS_TIMER_PERIOD (2) //s + static int start = 0; + static struct timeval stv = {.tv_sec = 0,}; + struct timeval tv; + + if ((to_fps != g_fps) && !start) { + start = 1; + do_gettimeofday(&stv); + } + + if (start == 1) { + do_gettimeofday(&tv); + if (tv.tv_sec >= stv.tv_sec + UPDATE_FPS_TIMER_PERIOD) { + sc1245_set_fps_async(); + stv.tv_sec = tv.tv_sec; + start = 0; + } + } + + return 0; +} diff --git a/drivers/media/video/plat-anyka/sensor_sc2232h_sc2135e.c b/drivers/media/video/plat-anyka/sensor_sc2232h_sc2135e.c new file mode 100644 index 00000000..ffff0c1a --- /dev/null +++ b/drivers/media/video/plat-anyka/sensor_sc2232h_sc2135e.c @@ -0,0 +1,699 @@ +/** + * @file camera_sc2232h.c + * @brief camera driver file + * Copyright (C) 2017 Anyka (Guangzhou) Microelectronics Technology Co., Ltd + * @author + * @date + * @version 1.0 + * @ref + */ +#include "ak_sensor_common.h" +#include + +#define SENSOR_PWDN_LEVEL 0 +#define SENSOR_RESET_LEVEL 0 +#define SENSOR_I2C_ADDR 0x60 +#define SENSOR_ID 0xCB71 +#define SENSOR_MCLK 24 +#define SENSOR_REGADDR_BYTE 2 +#define SENSOR_DATA_BYTE 1 +#define MAX_FPS 30 +#define SENSOR_OUTPUT_WIDTH 1920 +#define SENSOR_OUTPUT_HEIGHT 1080 +#define SENSOR_VALID_OFFSET_X 0 +#define SENSOR_VALID_OFFSET_Y 0 +#define SENSOR_BUS_TYPE BUS_TYPE_RAW +#define SENSOR_IO_INTERFACE MIPI_INTERFACE +//#define SENSOR_IO_INTERFACE DVP_INTERFACE +#define SENSOR_IO_LEVEL SENSOR_IO_LEVEL_1V8 + +#define OV_MAX(a, b) (((a) < (b) ) ? (b) : (a)) +#define OV_MIN(a, b) (((a) > (b) ) ? (b) : (a)) +#define OV_CLIP3(low, high, x) (OV_MAX(OV_MIN((x), high), low)) + +#define SKIP_NUM 2 +#define EXP_EFFECT_FRAMES 1 +#define VSYNC_ACTIVE_MS 28 +#define MIPI_MBPS 390 +#define MIPI_LANE 2 + +#define SNS_DG_EN 0 + +static int sc2232h_cmos_updata_d_gain(const unsigned int d_gain); +static int sc2232h_cmos_updata_a_gain(const unsigned int a_gain); +static int sc2232h_cmos_updata_exp_time(unsigned int exp_time); +static int sc2232h_cmos_update_a_gain_timer(void); + +static int g_fps = MAX_FPS; +static int to_fps = MAX_FPS; +static int to_fps_value = 0; +//static int g_a_gain = 0; + +// flag the sensor between sc2232h and sc2315e +static int support_sc2315e = 0; + +static int sc2232h_sensor_read_register(int reg) +{ + struct ak_sensor_i2c_data i2cdata; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = 0; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_read_register(&i2cdata); +} + +static int sc2232h_sensor_write_register(int reg, int data) +{ + struct ak_sensor_i2c_data i2cdata; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = data; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_write_register(&i2cdata); +} + +static int sc2232h_init(const AK_ISP_SENSOR_INIT_PARA *para) +{ + int i; +// int value; + AK_ISP_SENSOR_REG_INFO *preg_info; + + preg_info = para->reg_info; + for (i = 0; i < para->num; i++) { + sc2232h_sensor_write_register(preg_info->reg_addr, preg_info->value); + preg_info++; + } + //close DPC for anti-false color + sc2232h_sensor_write_register(0x5000, 0x06); //for dpc + sc2232h_sensor_write_register(0x5780, 0xff); //for dpc + sc2232h_sensor_write_register(0x5781, 0x60); //for dpc + sc2232h_sensor_write_register(0x5785, 0x30); //for dpc + + g_fps = MAX_FPS; + + if (support_sc2315e) { + /// sc2315e + sc2232h_sensor_write_register (0x363b, 0x06); + } else { + /// sc2232 + sc2232h_sensor_write_register (0x363b, 0x26); + } + + return 0; +} + +static int sc2232h_read_id(void) +{ + return SENSOR_ID; +} + +static int sc2232h_probe_id(void) +{ + u8 value; + u32 id; + + u32 const id_sc2232h = 0xcb0701; + u32 const id_sc2315e = 0x223820; + + /// to sc2232h + /// [0x3107,0x3108,0x3109] = 0xcb0701 + /// to sc2315e + /// [0x3107,0x3108,0x3109] = 0x223820 + + value = sc2232h_sensor_read_register(0x3107); + id = value << 16; + value = sc2232h_sensor_read_register(0x3108); + id |= value << 8; + value = sc2232h_sensor_read_register(0x3109); + id |= value; + + + //printk(KERN_ERR "%s id:0x%x\n", __func__, id); + + if (id_sc2315e == id) { + support_sc2315e = 1; + return SENSOR_ID; + } else if (id_sc2232h == id) { + return SENSOR_ID; + } + + return 0; +} + +static int sc2232h_get_resolution(int *width, int *height) +{ + *width = SENSOR_OUTPUT_WIDTH; + *height = SENSOR_OUTPUT_HEIGHT; + + return 0; +} + +static int sc2232h_get_mclk(void) +{ + return SENSOR_MCLK; +} + +static int sc2232h_get_fps(void) +{ + return g_fps; +} + +static int sc2232h_get_valid_coordinate(int *x, int *y) +{ + *x = SENSOR_VALID_OFFSET_X; + *y = SENSOR_VALID_OFFSET_Y; + + return 0; +} + +static enum sensor_bus_type sc2232h_get_bus_type(void) +{ + return SENSOR_BUS_TYPE; +} + +#if 0 +static int sc2232h_get_aec_delay_ms(int fps) +{ + int aec_delay_ms; + + switch (fps) { + case 25: + aec_delay_ms = 5; + break; + case 20: + aec_delay_ms = 15; + break; + case 15: + aec_delay_ms = 32; + break; + case 12: + aec_delay_ms = 45; + break; + case 10: + aec_delay_ms = 65; + break; + case 8: + aec_delay_ms = 89; + break; + case 5: + aec_delay_ms = 165; + break; + default: + printk(KERN_ERR "%s fps=%d error\n", __func__, fps); + aec_delay_ms = 5; + break; + } + return aec_delay_ms; +} +#endif + +static int sc2232h_get_parameter(int param, void *value) +{ + int ret = 0; + enum sensor_get_param name = (enum sensor_get_param)param; + + switch (name) { + case GET_MIPI_MHZ: + *((int *)value) = MIPI_MBPS; + break; + + case GET_VSYNC_ACTIVE_MS: + *((int *)value) = VSYNC_ACTIVE_MS; + break; + + case GET_CUR_FPS: + *((int *)value) = g_fps; + break; + + case GET_MIPI_LANE: + *((int *)value) = MIPI_LANE; + break; + + case GET_INTERFACE: + *((int *)value) = SENSOR_IO_INTERFACE; + break; + + case GET_SENSOR_IO_LEVEL: + *((int *)value) = SENSOR_IO_LEVEL; + break; + + default: + ret = -1; + break; + } + + return ret; +} + +static int sc2232h_set_power_on(const int pwdn_pin, const int reset_pin) +{ + ak_sensor_set_pin_as_gpio(pwdn_pin); + ak_sensor_set_pin_dir(pwdn_pin, 1); + ak_sensor_set_pin_level(pwdn_pin, !SENSOR_PWDN_LEVEL); + ak_sensor_mdelay(10); + ak_sensor_set_pin_as_gpio(reset_pin); + ak_sensor_set_pin_dir(reset_pin, 1); + ak_sensor_set_pin_level(reset_pin, SENSOR_RESET_LEVEL); + ak_sensor_mdelay(30); + ak_sensor_set_pin_level(reset_pin, !SENSOR_RESET_LEVEL); + ak_sensor_mdelay(20); + + return 0; +} + +static int sc2232h_set_power_off(const int pwdn_pin, const int reset_pin) +{ + //sccb software standby mode + + ak_sensor_set_pin_level(pwdn_pin, SENSOR_PWDN_LEVEL); + ak_sensor_set_pin_dir(reset_pin, 0); + + return 0; +} + +static int sc2232h_set_fps(int fps) +{ + int ret = 0; + int tmp=0; + + /* tmp = 78M / (reg0x320c~d=2080) / fps */ + switch (fps) { + case 30: + tmp = 0x4e2;//0x049D; + break; + case 25: + tmp = 0x5dc;//0x058A; + break; + case 20: + tmp = 0x753;//0x06EC; + break; + case 15: + tmp = 0x9c4;//0x093B; + break; + case 12: + tmp = 0x0bb8;//0x0B8A; + break; + case 10: + tmp = 0x0ea6;//0x0DD9; + break; + case 8: + tmp = 0x124f;//0x114F; + break; + case 5: + tmp = 0x1d4c;//0x1BB2; + break; + default: + printk(KERN_ERR "%s set fps=%d fail\n", __func__, fps); + ret = -EINVAL; + break; + } + + if (!ret) { +#if 0 + sc2232h_sensor_write_register(0x320e, tmp>>8); + sc2232h_sensor_write_register(0x320f, tmp & 0xff); +#endif + + //g_fps = fps; + to_fps = fps; + to_fps_value = tmp; + } + + return ret; +} + +static int sc2232h_set_standby_in(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static int sc2232h_set_standby_out(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static AK_ISP_SENSOR_CB sc2232h_callback = +{ + .sensor_init_func = sc2232h_init, + .sensor_read_reg_func = sc2232h_sensor_read_register, + .sensor_write_reg_func = sc2232h_sensor_write_register, + .sensor_read_id_func = sc2232h_read_id, + .sensor_update_a_gain_func = sc2232h_cmos_updata_a_gain, + .sensor_update_d_gain_func = sc2232h_cmos_updata_d_gain, + .sensor_updata_exp_time_func = sc2232h_cmos_updata_exp_time, + .sensor_timer_func = sc2232h_cmos_update_a_gain_timer, + + .sensor_probe_id_func = sc2232h_probe_id, //use IIC bus + .sensor_get_resolution_func = sc2232h_get_resolution, + .sensor_get_mclk_func = sc2232h_get_mclk, + .sensor_get_fps_func = sc2232h_get_fps, + .sensor_get_valid_coordinate_func = sc2232h_get_valid_coordinate, + .sensor_get_bus_type_func = sc2232h_get_bus_type, + .sensor_get_parameter_func = sc2232h_get_parameter, + + .sensor_set_power_on_func = sc2232h_set_power_on, + .sensor_set_power_off_func = sc2232h_set_power_off, + .sensor_set_fps_func = sc2232h_set_fps, + .sensor_set_standby_in_func = sc2232h_set_standby_in, + .sensor_set_standby_out_func = sc2232h_set_standby_out +}; + +AK_SENSOR_MODULE(sc2232h_callback, sc2232h) +/////////////////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////////////////// +static int sc2232h_curr_again_level = 0; +static int sc2232h_curr_again_15x5 = -1; +static int sc2232h_r0x3e02_value = 0; +static int sc2232h_r0x3e01_value = 0; +static int sc2232h_r0x3e00_value =0; +//static int is_corse7x_againmax = 0; + +/* + gain_level: + 0: <2x + 1: <4x + 2: <8x + 3: <15.5x + 4: >=15.5x + */ +static void sc2232h_ext(int gain_level) +{ + sc2232h_sensor_write_register(0x3812, 0x00); + switch (gain_level) { + case 0: + sc2232h_sensor_write_register(0x3301, 0x12); + sc2232h_sensor_write_register(0x3632, 0x08); + break; + case 1: + sc2232h_sensor_write_register(0x3301, 0x20); + sc2232h_sensor_write_register(0x3632, 0x08); + break; + case 2: + sc2232h_sensor_write_register(0x3301, 0x28); + sc2232h_sensor_write_register(0x3632, 0x08); + break; + case 3: + sc2232h_sensor_write_register(0x3301, 0x64); + sc2232h_sensor_write_register(0x3632, 0x08); + break; + case 4: + sc2232h_sensor_write_register(0x3301, 0x64); + sc2232h_sensor_write_register(0x3632, 0x48); + break; + default: + sc2232h_sensor_write_register(0x3301, 0x64); + sc2232h_sensor_write_register(0x3632, 0x48); + printk("%s gain_level error\n", __func__); + break; + } + + if (sc2232h_r0x3e00_value == 0) + { + if (sc2232h_r0x3e01_value < 0x25) + sc2232h_sensor_write_register(0x3314, 0x14); + else if (sc2232h_r0x3e01_value > 0x45) + sc2232h_sensor_write_register(0x3314, 0x04); + } + + sc2232h_sensor_write_register(0x3812, 0x30); +} + + +/* + gain_level: + 0: <15x5 + 1: >=15x5 + */ +static void sc2232h_dpc(int gain_15x5) +{ + switch (gain_15x5) { + case 0: + sc2232h_sensor_write_register(0x5781, 0x60); + sc2232h_sensor_write_register(0x5785, 0x30); + break; + case 1: + sc2232h_sensor_write_register(0x5781, 0x01); + sc2232h_sensor_write_register(0x5785, 0x02); + break; + default: + ak_sensor_print("%s err gain_15x5:%d\n", __func__, gain_15x5); + break; + } +} + +static int sc2232h_cmos_updata_d_gain(const unsigned int d_gain) +{ + return 0; +} + +static void sc2232h_set_fps_async(void) +{ + if (to_fps != g_fps) { + sc2232h_sensor_write_register(0x320e, to_fps_value>>8); + sc2232h_sensor_write_register(0x320f, to_fps_value & 0xff); + + g_fps = to_fps; + } +} + +static int curr_corse_Dgain = 0, curr_corse_Again = -1; +static unsigned int _cmos_gains_convert(unsigned int a_gain, unsigned int d_gain, + unsigned int *a_gain_out ,unsigned int *d_gain_out) +{ +#define _2XGAIN 2048 + unsigned int fine_gain= 0; + unsigned int corse_Again=0,fine_Again=0x10,corse_Dgain=0,fine_Dgain=0x80; + + int tmp = 0; + int is_15x5 = 0; + //int ret = 0; + + //ak_sensor_print("a_gain =%d\n",a_gain); + + sc2232h_set_fps_async(); + + tmp = a_gain; + if (tmp < 15872) + is_15x5 = 0; + else + is_15x5 = 1; + + //g_a_gain = a_gain; + + if(a_gain<_2XGAIN) //1- 1.9375 + { + corse_Again = 0; + corse_Dgain = 0x0; + fine_Dgain = 0x80; + tmp = a_gain-1024; + sc2232h_curr_again_level = 0; + } + else if(a_gain<4096) // 2 - 3.875 + { + corse_Again = 1; + corse_Dgain = 0x0; + fine_Dgain = 0x80; + tmp = a_gain/2-1024; + sc2232h_curr_again_level = 1; + } + else if(a_gain<8192) //4-7.75 + { + corse_Again = 3; + corse_Dgain = 0x0; + fine_Dgain = 0x80; + tmp = a_gain/4-1024; + sc2232h_curr_again_level = 2; + } + else if(a_gain<15872) //8 - 15.5 + { + corse_Again = 7; + corse_Dgain = 0x0; + fine_Dgain = 0x80; + tmp = a_gain/8-1024; + sc2232h_curr_again_level = 3; + } +#if SNS_DG_EN + else if(a_gain<31744) // 15.5 - 31 + { + corse_Again = 7; + fine_Again = 0x1f; + corse_Dgain = 0x0; + tmp = a_gain*10/155-1024; + sc2232h_curr_again_level = 4; + } + else if(a_gain<63488) // 31 - 62 + { + corse_Again = 7; + fine_Again = 0x1f; + corse_Dgain = 0x1; + tmp = a_gain/31-1024; + sc2232h_curr_again_level = 4; + } + else if(a_gain<126976) // 62 -124 + { + corse_Again = 7; + fine_Again = 0x1f; + corse_Dgain = 0x3; + tmp = a_gain/62-1024; + sc2232h_curr_again_level = 4; + } + else // 124 - 248 + { + corse_Again = 7; + fine_Again = 0x1f; + corse_Dgain = 0x7; + tmp = a_gain/124-1024; + sc2232h_curr_again_level = 4; + } +#else + else //15.5 -- + { + corse_Again = 7; + corse_Dgain = 0x0; + fine_Dgain = 0x80; + tmp = 960; + sc2232h_curr_again_level = 4; + } +#endif + + sc2232h_ext(sc2232h_curr_again_level); + + if(tmp>=0){ + fine_gain = tmp/64; + } + else{ + fine_gain = 0; + } + +#if SNS_DG_EN + if(a_gain<15872) + { + fine_Again = 0x10 + fine_gain; + sc2232h_sensor_write_register(0x3e09, fine_Again); + + if (curr_corse_Again != corse_Again) + { + tmp = sc2232h_sensor_read_register(0x3e08) & 0xe3; + tmp |= corse_Again<<2 ; + sc2232h_sensor_write_register(0x3e08, tmp); + } + sc2232h_sensor_write_register(0x3e06, corse_Dgain); + sc2232h_sensor_write_register(0x3e07, fine_Dgain); + } + else + { + fine_Dgain = 0x80 +0x8*fine_gain; + sc2232h_sensor_write_register(0x3e07, fine_Dgain); + if (curr_corse_Dgain != corse_Dgain) + { + tmp = sc2232h_sensor_read_register(0x3e06); + tmp |= corse_Dgain; + sc2232h_sensor_write_register(0x3e06, tmp); + } + sc2232h_sensor_write_register(0x3e08, corse_Again); + sc2232h_sensor_write_register(0x3e09, fine_Again); + } +#else + fine_Again = 0x10 + fine_gain; + sc2232h_sensor_write_register(0x3e09, fine_Again); + + if (curr_corse_Again != corse_Again) + { + tmp = sc2232h_sensor_read_register(0x3e08) & 0xe3; + tmp |= corse_Again<<2 ; + sc2232h_sensor_write_register(0x3e08, tmp); + } + sc2232h_sensor_write_register(0x3e06, corse_Dgain); + sc2232h_sensor_write_register(0x3e07, fine_Dgain); +#endif + + if (is_15x5 != sc2232h_curr_again_15x5) { + //sc2232h_dpc(is_15x5); + sc2232h_curr_again_15x5 = is_15x5; + } + + curr_corse_Dgain = corse_Dgain; + curr_corse_Again = corse_Again; + return 0; +} + +static int sc2232h_cmos_updata_a_gain(const unsigned int a_gain) +{ + + unsigned int tmp_a_gain; + unsigned int tmp_d_gain; + unsigned int tmp_a_gain_out; + unsigned int tmp_d_gain_out; + + tmp_a_gain = a_gain<<2; + tmp_d_gain = 0; + + return _cmos_gains_convert(tmp_a_gain, tmp_d_gain, &tmp_a_gain_out ,&tmp_d_gain_out); +} + +static int sc2232h_cmos_updata_exp_time(unsigned int exp_time) +{ + //?????ع?ʱ???Ļص????? + unsigned char exposure_time_ext; + unsigned char exposure_time_msb; + unsigned char exposure_time_lsb; + unsigned char tem; + + sc2232h_set_fps_async(); + + sc2232h_ext(sc2232h_curr_again_level); + printk("exp_time=%d ",exp_time); + printk(KERN_ERR "exp_time=%d,%lu\n",exp_time,jiffies); + exposure_time_ext =(exp_time>>12)&0xf; + exposure_time_msb =(exp_time>>4)&0xff; + exposure_time_lsb =((exp_time)&0xf)<<4; + tem=sc2232h_sensor_read_register(0x3e02)&0x0f; + exposure_time_lsb =exposure_time_lsb|tem; + //printk("msb = %d ",exposure_time_msb); + //printk("lsb = %d\n",exposure_time_lsb); + sc2232h_sensor_write_register(0x3e01,exposure_time_msb); + sc2232h_sensor_write_register(0x3e02,exposure_time_lsb); + + tem = sc2232h_sensor_read_register(0x3e00) & 0xf0; + tem |= exposure_time_ext; + sc2232h_sensor_write_register(0x3e00, tem); + //sc2232h_ext(sc2232h_curr_again_level); + + sc2232h_r0x3e02_value = exposure_time_lsb; + sc2232h_r0x3e01_value = exposure_time_msb; + sc2232h_r0x3e00_value = exposure_time_ext; + + return EXP_EFFECT_FRAMES; +} + +static int sc2232h_cmos_update_a_gain_timer(void) +{ +#define UPDATE_FPS_TIMER_PERIOD (2) //s + static int start = 0; + static struct timeval stv = {.tv_sec = 0,}; + struct timeval tv; + + if ((to_fps != g_fps) && !start) { + start = 1; + do_gettimeofday(&stv); + } + + if (start == 1) { + do_gettimeofday(&tv); + if (tv.tv_sec >= stv.tv_sec + UPDATE_FPS_TIMER_PERIOD) { + sc2232h_set_fps_async(); + stv.tv_sec = tv.tv_sec; + start = 0; + } + } + + return 0; +} diff --git a/drivers/media/video/plat-anyka/sensor_sc2235.c b/drivers/media/video/plat-anyka/sensor_sc2235.c new file mode 100644 index 00000000..2d120a51 --- /dev/null +++ b/drivers/media/video/plat-anyka/sensor_sc2235.c @@ -0,0 +1,742 @@ +/** + * @file camera_sc2235.c + * @brief camera driver file + * Copyright (C) 2017 Anyka (Guangzhou) Microelectronics Technology Co., Ltd + * @author + * @date + * @version 1.0 + * @ref + */ +#include "ak_sensor_common.h" +#include + +#define SENSOR_PWDN_LEVEL 0 +#define SENSOR_RESET_LEVEL 0 +#define SENSOR_I2C_ADDR 0x60 +#define SENSOR_ID 0x2235 +#define SENSOR_MCLK 24 +#define SENSOR_REGADDR_BYTE 2 +#define SENSOR_DATA_BYTE 1 +#define MAX_FPS 25 +#define SENSOR_OUTPUT_WIDTH 1920 +#define SENSOR_OUTPUT_HEIGHT 1080 +#define SENSOR_VALID_OFFSET_X 0 +#define SENSOR_VALID_OFFSET_Y 0 +#define SENSOR_BUS_TYPE BUS_TYPE_RAW +#define SENSOR_IO_INTERFACE DVP_INTERFACE +#define SENSOR_IO_LEVEL SENSOR_IO_LEVEL_1V8 + +#define OV_MAX(a, b) (((a) < (b) ) ? (b) : (a)) +#define OV_MIN(a, b) (((a) > (b) ) ? (b) : (a)) +#define OV_CLIP3(low, high, x) (OV_MAX(OV_MIN((x), high), low)) + +#define SKIP_NUM 2 +#define EXP_EFFECT_FRAMES 1 +#define VSYNC_ACTIVE_MS 36 + +static int sc2235_cmos_updata_d_gain(const unsigned int d_gain); +static int sc2235_cmos_updata_a_gain(const unsigned int a_gain); +static int sc2235_cmos_updata_exp_time(unsigned int exp_time); +static int sc2235_cmos_update_a_gain_timer(void); + +static int g_fps = MAX_FPS; +static int to_fps = MAX_FPS; +static int to_fps_value = 0; +static int g_a_gain = 0; + +static int sc2235_sensor_read_register(int reg) +{ + struct ak_sensor_i2c_data i2cdata; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = 0; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_read_register(&i2cdata); +} + +static int sc2235_sensor_write_register(int reg, int data) +{ + struct ak_sensor_i2c_data i2cdata; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = data; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_write_register(&i2cdata); +} + +static int sc2235_init(const AK_ISP_SENSOR_INIT_PARA *para) +{ + int i; +// int value; + AK_ISP_SENSOR_REG_INFO *preg_info; + + preg_info = para->reg_info; + for (i = 0; i < para->num; i++) { +//value=sc2235_sensor_read_register(preg_info->reg_addr); +//printk(KERN_ERR "before w reg:0x%.x, val:0x%.x, to write:0x%.x\n", preg_info->reg_addr, value, preg_info->value); + sc2235_sensor_write_register(preg_info->reg_addr, preg_info->value); +//value=sc2235_sensor_read_register(preg_info->reg_addr); +//printk(KERN_ERR "after w reg:0x%.x, val:0x%.x\n", preg_info->reg_addr, value); + preg_info++; + } + + g_fps = MAX_FPS; + + return 0; +} + +static int sc2235_read_id(void) +{ + return SENSOR_ID; +} + +static int sc2235_probe_id(void) +{ + u8 value; + u32 id; + + value = sc2235_sensor_read_register(0x3107); + id = value << 8; + + value = sc2235_sensor_read_register(0x3108); + id |= value; + + printk(KERN_ERR "%s id:0x%x\n", __func__, id); + + //return SENSOR_ID; + if (id != SENSOR_ID) + goto fail; + + return SENSOR_ID; + +fail: + return 0; +} + +static int sc2235_get_resolution(int *width, int *height) +{ + *width = SENSOR_OUTPUT_WIDTH; + *height = SENSOR_OUTPUT_HEIGHT; + + return 0; +} + +static int sc2235_get_mclk(void) +{ + return SENSOR_MCLK; +} + +static int sc2235_get_fps(void) +{ + return g_fps; +} + +static int sc2235_get_valid_coordinate(int *x, int *y) +{ + *x = SENSOR_VALID_OFFSET_X; + *y = SENSOR_VALID_OFFSET_Y; + + return 0; +} + +static enum sensor_bus_type sc2235_get_bus_type(void) +{ + return SENSOR_BUS_TYPE; +} + +#if 0 +static int sc2235_get_aec_delay_ms(int fps) +{ + int aec_delay_ms; + + switch (fps) { + case 25: + aec_delay_ms = 5; + break; + case 20: + aec_delay_ms = 15; + break; + case 15: + aec_delay_ms = 32; + break; + case 12: + aec_delay_ms = 45; + break; + case 10: + aec_delay_ms = 65; + break; + case 8: + aec_delay_ms = 89; + break; + case 5: + aec_delay_ms = 165; + break; + default: + printk(KERN_ERR "%s fps=%d error\n", __func__, fps); + aec_delay_ms = 5; + break; + } + return aec_delay_ms; +} +#endif + +static int sc2235_get_parameter(int param, void *value) +{ + int ret = 0; + enum sensor_get_param name = (enum sensor_get_param)param; + + switch (name) { + case GET_VSYNC_ACTIVE_MS: + *((int *)value) = VSYNC_ACTIVE_MS; + break; + + case GET_CUR_FPS: + *((int *)value) = g_fps; + break; + + case GET_INTERFACE: + *((int *)value) = SENSOR_IO_INTERFACE; + break; + + case GET_SENSOR_IO_LEVEL: + *((int *)value) = SENSOR_IO_LEVEL; + break; + + default: + ret = -1; + break; + } + + return ret; +} + +static int sc2235_set_power_on(const int pwdn_pin, const int reset_pin) +{ + ak_sensor_set_pin_as_gpio(pwdn_pin); + ak_sensor_set_pin_dir(pwdn_pin, 1); + ak_sensor_set_pin_level(pwdn_pin, !SENSOR_PWDN_LEVEL); + ak_sensor_mdelay(10); + ak_sensor_set_pin_as_gpio(reset_pin); + ak_sensor_set_pin_dir(reset_pin, 1); + ak_sensor_set_pin_level(reset_pin, SENSOR_RESET_LEVEL); + ak_sensor_mdelay(30); //modify reset time, used to solve no camera interrupt problem + ak_sensor_set_pin_level(reset_pin, !SENSOR_RESET_LEVEL); + ak_sensor_mdelay(20); + + return 0; +} + +static int sc2235_set_power_off(const int pwdn_pin, const int reset_pin) +{ + //sccb software standby mode + + ak_sensor_set_pin_level(pwdn_pin, SENSOR_PWDN_LEVEL); + ak_sensor_set_pin_dir(reset_pin, 0); + + return 0; +} + +static int sc2235_set_fps(int fps) +{ + int ret = 0; + int tmp=0; + + /* tmp = 72M / (reg0x320c~d=2400) / fps */ + switch (fps) { + case 25: + tmp = 0x04b0; + break; + case 20: + tmp = 0x05dc; + break; + case 15: + tmp = 0x07d0; + break; + case 12: + tmp = 0x0960; + break; + case 10: + tmp = 0x0bb8; + break; + case 8: + tmp = 0x0ea6; + break; + case 5: + tmp = 0x1770; + break; + default: + printk(KERN_ERR "%s set fps=%d fail\n", __func__, fps); + ret = -EINVAL; + break; + } + + if (!ret) { +#if 0 + sc2235_sensor_write_register(0x320e, tmp>>8); + sc2235_sensor_write_register(0x320f, tmp & 0xff); +#endif +#if 0 + sc2235_sensor_write_register(0x3336, (tmp - 0x2e8) >> 8); + sc2235_sensor_write_register(0x3337, (tmp - 0x2e8) & 0xff); + sc2235_sensor_write_register(0x3338, tmp>>8); + sc2235_sensor_write_register(0x3339, tmp & 0xff); +#endif + + //g_fps = fps; + to_fps = fps; + to_fps_value = tmp; + } + + return ret; +} + +static int sc2235_set_standby_in(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static int sc2235_set_standby_out(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static AK_ISP_SENSOR_CB sc2235_callback = +{ + .sensor_init_func = sc2235_init, + .sensor_read_reg_func = sc2235_sensor_read_register, + .sensor_write_reg_func = sc2235_sensor_write_register, + .sensor_read_id_func = sc2235_read_id, + .sensor_update_a_gain_func = sc2235_cmos_updata_a_gain, + .sensor_update_d_gain_func = sc2235_cmos_updata_d_gain, + .sensor_updata_exp_time_func = sc2235_cmos_updata_exp_time, + .sensor_timer_func = sc2235_cmos_update_a_gain_timer, + + .sensor_probe_id_func = sc2235_probe_id, //use IIC bus + .sensor_get_resolution_func = sc2235_get_resolution, + .sensor_get_mclk_func = sc2235_get_mclk, + .sensor_get_fps_func = sc2235_get_fps, + .sensor_get_valid_coordinate_func = sc2235_get_valid_coordinate, + .sensor_get_bus_type_func = sc2235_get_bus_type, + .sensor_get_parameter_func = sc2235_get_parameter, + + .sensor_set_power_on_func = sc2235_set_power_on, + .sensor_set_power_off_func = sc2235_set_power_off, + .sensor_set_fps_func = sc2235_set_fps, + .sensor_set_standby_in_func = sc2235_set_standby_in, + .sensor_set_standby_out_func = sc2235_set_standby_out +}; + +AK_SENSOR_MODULE(sc2235_callback, sc2235) +/////////////////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////////////////// +static int sc2235_curr_again_level = 0; +static int sc2235_curr_again_10x = -1; +static int sc2235_r0x3e01_value = 0; +static int is_corse7x_againmax = 0; + +/* + gain_level: + 0: <2x + 1: <8x + 2: <15.5x + 3: >=15.5x + */ +static void sc2235_ext(int gain_level) +{ + sc2235_sensor_write_register(0x3903, 0x84); + sc2235_sensor_write_register(0x3903, 0x04); + sc2235_sensor_write_register(0x3812, 0x00); + switch (gain_level) { +#if 0 + case 0: + sc2235_sensor_write_register(0x3631, 0x84); + sc2235_sensor_write_register(0x3301, 0x05); + sc2235_sensor_write_register(0x3633, 0x2f); + break; + case 1: + sc2235_sensor_write_register(0x3631, 0x88); + sc2235_sensor_write_register(0x3301, 0x1f); + sc2235_sensor_write_register(0x3633, 0x23); + break; + case 2: + sc2235_sensor_write_register(0x3631, 0x88); + sc2235_sensor_write_register(0x3301, 0xff); + sc2235_sensor_write_register(0x3633, 0x43); + break; +#else + case 0: + sc2235_sensor_write_register(0x3301, 0x05); + sc2235_sensor_write_register(0x3631, 0x84); + sc2235_sensor_write_register(0x3622, 0xc6); + break; + case 1: + sc2235_sensor_write_register(0x3301, 0x13); + sc2235_sensor_write_register(0x3631, 0x88); + sc2235_sensor_write_register(0x3622, 0xc6); + break; + case 2: + sc2235_sensor_write_register(0x3301, 0x15); + sc2235_sensor_write_register(0x3631, 0x88); + sc2235_sensor_write_register(0x3622, 0xc6); + break; + case 3: + sc2235_sensor_write_register(0x3301, 0xff); + sc2235_sensor_write_register(0x3631, 0x88); + sc2235_sensor_write_register(0x3622, 0x06); + break; +#endif + default: + printk("%s gain_level error\n", __func__); + break; + } + + if (sc2235_r0x3e01_value < 0x05) + sc2235_sensor_write_register(0x3314, 0x12); + else if (sc2235_r0x3e01_value > 0x0a) + sc2235_sensor_write_register(0x3314, 0x02); + + if (is_corse7x_againmax) + sc2235_sensor_write_register(0x366f, 0x3a); + else + sc2235_sensor_write_register(0x366f, 0x2f); + + sc2235_sensor_write_register(0x3812, 0x30); +} + + +/* + gain_level: + 0: <10x + 1: >=10x + */ +static void sc2235_dpc(int gain_10x) +{ + switch (gain_10x) { + case 0: + sc2235_sensor_write_register(0x5781, 0x04); + sc2235_sensor_write_register(0x5785, 0x18); + break; + case 1: + sc2235_sensor_write_register(0x5781, 0x02); + sc2235_sensor_write_register(0x5785, 0x08); + break; + default: + ak_sensor_print("%s err gain_10x:%d\n", __func__, gain_10x); + break; + } +} + +static int sc2235_cmos_updata_d_gain(const unsigned int d_gain) +{ + return 0; +} + +static void sc2235_set_fps_async(void) +{ + if (to_fps != g_fps) { + sc2235_sensor_write_register(0x320e, to_fps_value>>8); + sc2235_sensor_write_register(0x320f, to_fps_value & 0xff); + + g_fps = to_fps; + } +} + +static int curr_2x_dgain = 0, curr_corse_gain = -1; +static unsigned int _cmos_gains_convert(unsigned int a_gain, unsigned int d_gain, + unsigned int *a_gain_out ,unsigned int *d_gain_out) +{ + //Ä£??????????ת???????????ã·??????????ת????sensor????????? +#define _2XGAIN 2048 + unsigned int fine_gain= 0; + unsigned int corse_gain=0,sensor_2x_again=0,sensor_2x_dgain=0; + int tmp = 0; + int is_10x = 0; + //int ret = 0; + + //ak_sensor_print("a_gain =%d\n",a_gain); + + sc2235_set_fps_async(); + + tmp = a_gain; + if (tmp < (10 << 10)) + is_10x = 0; + else + is_10x = 1; + +#if 0 + if (((g_a_gain < _2XGAIN) && (a_gain >= _2XGAIN)) || + ((g_a_gain >= _2XGAIN) && (a_gain < _2XGAIN))) + { + ret = SKIP_NUM; + } +#endif + + g_a_gain = a_gain; + + if(a_gain<_2XGAIN) //1 - 1.9375 + { + corse_gain = 0; + tmp = a_gain-1024; + sc2235_curr_again_level = 0; + } + else if(a_gain<4096) // 2 - 3.875 + { + corse_gain = 1; + tmp = a_gain/2-1024; + sc2235_curr_again_level = 1; + } + else if(a_gain<8192) //4-7.75 + { + corse_gain = 3; + tmp = a_gain/4-1024; + sc2235_curr_again_level = 1; + } + else if(a_gain<15872) //8 - 15.5 + { + corse_gain = 7; + tmp = a_gain/8-1024; + sc2235_curr_again_level = 2; + } + else if(a_gain<32768) // 15.5 - 31 + { + corse_gain = 7; + sensor_2x_again =1; + sensor_2x_dgain = 1; + a_gain=a_gain>>1; + tmp = a_gain/8-1024; + sc2235_curr_again_level = 3; + } + else if(a_gain<65536) // 32 - 62 + { + corse_gain = 7; + sensor_2x_again =1; + sensor_2x_dgain = 3; + a_gain=a_gain>>2; + tmp = a_gain/8-1024; + sc2235_curr_again_level = 3; + } + else if(a_gain<131072) // 64 -124 + { + corse_gain = 7; + sensor_2x_again =1; + sensor_2x_dgain = 7; + a_gain=a_gain>>3; + tmp = a_gain/8-1024; + sc2235_curr_again_level = 3; + } + else // 128 - 248 + { + corse_gain = 7; + sensor_2x_again =1; + sensor_2x_dgain = 0xf; + a_gain=a_gain>>4; + tmp = a_gain/8-1024; + sc2235_curr_again_level = 3; + } + + sc2235_ext(sc2235_curr_again_level); + + if(tmp>=0){ + fine_gain = tmp/65; + } + else{ + fine_gain = 0; + } + + fine_gain |= 0x10; + + //printk("again:%d, fine_gain:%d\n",a_gain>>2, fine_gain); + sc2235_sensor_write_register(0x3e09, fine_gain); + + if (curr_2x_dgain != sensor_2x_dgain || + curr_corse_gain != corse_gain) { + tmp = sc2235_sensor_read_register(0x3e08) & 0x3; + tmp |= ((sensor_2x_dgain & 0x7) << 5) | (corse_gain << 2); + sc2235_sensor_write_register(0x3e08, tmp); + //printk("2xgain:%d, corse_gain:%d\n",sensor_2x_dgain, corse_gain); + } + + if (curr_2x_dgain != sensor_2x_dgain) { + tmp = sc2235_sensor_read_register(0x3e07) & 0x0e; + tmp |= (sensor_2x_dgain >> 3) & 0x01; + sc2235_sensor_write_register(0x3e07, tmp); + //printk("2xgain:%d\n",sensor_2x_dgain); + } + + if (corse_gain >= 7 && fine_gain >= 0x1f) + is_corse7x_againmax = 1; + else + is_corse7x_againmax = 0; + + //sc2235_ext(sc2235_curr_again_level); + + if (is_10x != sc2235_curr_again_10x) { + sc2235_dpc(sc2235_curr_again_10x); + sc2235_curr_again_10x = is_10x; + } + + curr_2x_dgain = sensor_2x_dgain; + curr_corse_gain = corse_gain; + return 0; + +#if 0 + *a_gain_out = a_gain; + *d_gain_out = sensor_d_gain; + ag_value = corse_gain<<4|(fine_gain&0xf); + + //ak_sensor_print("corse=%d fine=%d\n",corse_gain,fine_gain); + sc1135_sensor_write_register(0x3e09, Again_limit(ag_value)); + //sc1135_sensor_write_register(0x3e08, sensor_d_gain); + if(sensor_2x_again==1) + sc1135_sensor_write_register(0x3635, 0x0c); //2x again + else + sc1135_sensor_write_register(0x3635, 0x0); + + if(corse_gain<1) //2 ±¶ÔöÒæ + { + sc1135_sensor_write_register(0x3630, 0xb8); + sc1135_sensor_write_register(0x3631, 0x82); + } + else if(sensor_2x_again<1) //2-16±¶ÔöÒæ + { + sc1135_sensor_write_register(0x3630, 0x70); + sc1135_sensor_write_register(0x3631, 0x8e); + } + else + { + sc1135_sensor_write_register(0x3630, 0x70); + sc1135_sensor_write_register(0x3631, 0x8c); + } + //ak_sensor_print(" 0x3e09 = %d\n", sc1135_sensor_read_register(0x3e09)); + //ak_sensor_print(" 0x3e0f = %d\n", sc1135_sensor_read_register(0x360f)); + + if(sc1135_curr_2x_again!=sensor_2x_again) + { + sc1135_curr_2x_again=sensor_2x_again; + return SKIP_FRAME_NUM; + } + else + return 0; +#endif +} + +static int sc2235_cmos_updata_a_gain(const unsigned int a_gain) +{ +#if 0 + //??????ģ???????Ļص????? + int ret = 0; + int tmp; + int old_10x = sc2235_curr_again_10x; + + //printk(KERN_ERR "again=%d,%lu\n",a_gain,jiffies); + + if (((g_a_gain < 512) && (a_gain >= 512)) || + ((g_a_gain >= 512) && (a_gain < 512))) + ret = 2; + + g_a_gain = a_gain; + + tmp = a_gain >> 4; + sc2235_sensor_write_register(0x3e08, tmp >> 8); + sc2235_sensor_write_register(0x3e09, tmp & 0xff); + if (tmp < (2 << 4)) { + sc2235_curr_again_level = 0; + sc2235_ext(0); + } else if (tmp < (8 << 4)) { + sc2235_curr_again_level = 1; + sc2235_ext(1); + } else if (tmp < 248) { //15.5x + sc2235_curr_again_level = 2; + sc2235_ext(2); + } else { + sc2235_curr_again_level = 3; + sc2235_ext(3); + } + + if (tmp < (10 << 4)) + sc2235_curr_again_10x = 0; + else + sc2235_curr_again_10x = 1; + + if (old_10x != sc2235_curr_again_10x) + sc2235_dpc(sc2235_curr_again_10x); + + return ret; +#else + unsigned int tmp_a_gain; + unsigned int tmp_d_gain; + unsigned int tmp_a_gain_out; + unsigned int tmp_d_gain_out; + + tmp_a_gain = a_gain<<2; + tmp_d_gain = 0; + + return _cmos_gains_convert(tmp_a_gain, tmp_d_gain, &tmp_a_gain_out ,&tmp_d_gain_out); + +#endif +} + +static int sc2235_cmos_updata_exp_time(unsigned int exp_time) +{ + //?????ع?ʱ???Ļص????? + unsigned char exposure_time_ext; + unsigned char exposure_time_msb; + unsigned char exposure_time_lsb; + unsigned char tem; + + sc2235_set_fps_async(); + + sc2235_ext(sc2235_curr_again_level); + //printk("exp_time=%d ",exp_time); + //printk(KERN_ERR "exp_time=%d,%lu\n",exp_time,jiffies); + exposure_time_ext =(exp_time>>12)&0xf; + exposure_time_msb =(exp_time>>4)&0xff; + exposure_time_lsb =((exp_time)&0xf)<<4; + tem=sc2235_sensor_read_register(0x3e02)&0x0f; + exposure_time_lsb =exposure_time_lsb|tem; + //printk("msb = %d ",exposure_time_msb); + //printk("lsb = %d\n",exposure_time_lsb); + sc2235_sensor_write_register(0x3e01,exposure_time_msb); + sc2235_sensor_write_register(0x3e02,exposure_time_lsb); + + tem = sc2235_sensor_read_register(0x3e00) & 0xf0; + tem |= exposure_time_ext; + sc2235_sensor_write_register(0x3e00, tem); + //sc2235_ext(sc2235_curr_again_level); + + sc2235_r0x3e01_value = exposure_time_msb; + + return EXP_EFFECT_FRAMES; +} + +static int sc2235_cmos_update_a_gain_timer(void) +{ +#define UPDATE_FPS_TIMER_PERIOD (2) //s + static int start = 0; + static struct timeval stv = {.tv_sec = 0,}; + struct timeval tv; + + if ((to_fps != g_fps) && !start) { + start = 1; + do_gettimeofday(&stv); + } + + if (start == 1) { + do_gettimeofday(&tv); + if (tv.tv_sec >= stv.tv_sec + UPDATE_FPS_TIMER_PERIOD) { + sc2235_set_fps_async(); + stv.tv_sec = tv.tv_sec; + start = 0; + } + } + + return 0; +} diff --git a/drivers/media/video/plat-anyka/sensor_sp2305.c b/drivers/media/video/plat-anyka/sensor_sp2305.c new file mode 100644 index 00000000..a468632f --- /dev/null +++ b/drivers/media/video/plat-anyka/sensor_sp2305.c @@ -0,0 +1,497 @@ +/** + * @file camera_sp2305.c + * @brief camera driver file + * Copyright (C) 2017 Anyka (Guangzhou) Microelectronics Technology Co., Ltd + * @author + * @date + * @version 1.0 + * @ref + */ +#include "ak_sensor_common.h" +#include + +#define SENSOR_PWDN_LEVEL 1 +#define SENSOR_RESET_LEVEL 0 +#define SENSOR_I2C_ADDR 0x7a ///< I2CADDR 上拉时 ADDR=0x7a,下拉时 ADDR=0x78。 +#define SENSOR_ID 0x2735 +#define SENSOR_MCLK 24 +#define SENSOR_REGADDR_BYTE 1 +#define SENSOR_DATA_BYTE 1 +#define MAX_FPS 25 +#define SENSOR_OUTPUT_WIDTH 1920 +#define SENSOR_OUTPUT_HEIGHT 1080 +#define SENSOR_VALID_OFFSET_X 0 +#define SENSOR_VALID_OFFSET_Y 0 +#define SENSOR_BUS_TYPE BUS_TYPE_RAW +#define SENSOR_IO_INTERFACE MIPI_INTERFACE +#define SENSOR_IO_LEVEL SENSOR_IO_LEVEL_1V8 + +#define OV_MAX(a, b) (((a) < (b) ) ? (b) : (a)) +#define OV_MIN(a, b) (((a) > (b) ) ? (b) : (a)) +#define OV_CLIP3(low, high, x) (OV_MAX(OV_MIN((x), high), low)) + +#define SKIP_NUM 2 +#define EXP_EFFECT_FRAMES 1 +#define VSYNC_ACTIVE_MS 31 +#define MIPI_MBPS 360 +#define MIPI_LANE 2 + +static int sp2305_cmos_updata_d_gain(const unsigned int d_gain); +static int sp2305_cmos_updata_a_gain(const unsigned int a_gain); +static int sp2305_cmos_updata_exp_time(unsigned int exp_time); +static int sp2305_cmos_update_a_gain_timer(void); + +static int g_fps = MAX_FPS; +static int to_fps = MAX_FPS; +static int to_fps_value = 0; + + +static int sp2305_sensor_read_register(int reg) +{ + struct ak_sensor_i2c_data i2cdata; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = 0; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_read_register(&i2cdata); +} + +static int sp2305_sensor_write_register(int reg, int data) +{ + struct ak_sensor_i2c_data i2cdata; + + i2cdata.u8DevAddr = SENSOR_I2C_ADDR; + i2cdata.u32RegAddr = reg; + i2cdata.u32RegAddrByteNum = SENSOR_REGADDR_BYTE; + i2cdata.u32Data = data; + i2cdata.u32DataByteNum = SENSOR_DATA_BYTE; + + return ak_sensor_write_register(&i2cdata); +} + +static int sp2305_init(const AK_ISP_SENSOR_INIT_PARA *para) +{ + int i; +// int value; + AK_ISP_SENSOR_REG_INFO *preg_info; + + preg_info = para->reg_info; + for (i = 0; i < para->num; i++) { +// value = sp2305_sensor_read_register(preg_info->reg_addr); +// printk(KERN_ERR "before w reg:0x%x, val:0x%x, to write:0x%x\n", preg_info->reg_addr, value, preg_info->value); + if(preg_info->reg_addr ==0x20) + { + sp2305_sensor_write_register(preg_info->reg_addr, preg_info->value); + ak_sensor_mdelay(5); + } + else + sp2305_sensor_write_register(preg_info->reg_addr, preg_info->value); + +// if(preg_info->reg_addr ==0x01) +// printk(KERN_ERR "after write reg:0x%.x, val:0x%.x\n", preg_info->reg_addr, preg_info->value); +// value=sp2305_sensor_read_register(preg_info->reg_addr); +// printk(KERN_ERR "after read reg:0x%.x, val:0x%.x\n", preg_info->reg_addr, value); + + + preg_info++; + } + + g_fps = MAX_FPS; + + return 0; +} + +static int sp2305_read_id(void) +{ + return SENSOR_ID; +} + +static int sp2305_probe_id(void) +{ + u8 value; + u32 id; + sp2305_sensor_write_register(0xfd, 0x00); + value = sp2305_sensor_read_register(0x02); + printk(KERN_ERR "ox02 %s value:0x%x\n", __func__, value); + id = value << 8; + + value = sp2305_sensor_read_register(0x03); + printk(KERN_ERR "ox03 %s value:0x%x\n", __func__, value); + value = value&0xff; + id |= value; + + + printk(KERN_ERR "%s id:0x%x\n", __func__, id); + + //return SENSOR_ID; + if (id != SENSOR_ID) + goto fail; + + return SENSOR_ID; + +fail: + return 0; +} + +static int sp2305_get_resolution(int *width, int *height) +{ + *width = SENSOR_OUTPUT_WIDTH; + *height = SENSOR_OUTPUT_HEIGHT; + + return 0; +} + +static int sp2305_get_mclk(void) +{ + return SENSOR_MCLK; +} + +static int sp2305_get_fps(void) +{ + return g_fps; +} + +static int sp2305_get_valid_coordinate(int *x, int *y) +{ + *x = SENSOR_VALID_OFFSET_X; + *y = SENSOR_VALID_OFFSET_Y; + + return 0; +} + +static enum sensor_bus_type sp2305_get_bus_type(void) +{ + return SENSOR_BUS_TYPE; +} + +#if 0 +static int sp2305_get_aec_delay_ms(int fps) +{ + int aec_delay_ms; + + switch (fps) { + case 25: + aec_delay_ms = 5; + break; + case 20: + aec_delay_ms = 15; + break; + case 15: + aec_delay_ms = 32; + break; + case 12: + aec_delay_ms = 45; + break; + case 10: + aec_delay_ms = 65; + break; + case 8: + aec_delay_ms = 89; + break; + case 5: + aec_delay_ms = 165; + break; + default: + printk(KERN_ERR "%s fps=%d error\n", __func__, fps); + aec_delay_ms = 5; + break; + } + return aec_delay_ms; +} +#endif + +static int sp2305_get_parameter(int param, void *value) +{ + int ret = 0; + enum sensor_get_param name = (enum sensor_get_param)param; + + switch (name) { + case GET_MIPI_MHZ: + *((int *)value) = MIPI_MBPS; + break; + + case GET_VSYNC_ACTIVE_MS: + *((int *)value) = VSYNC_ACTIVE_MS; + break; + + case GET_CUR_FPS: + *((int *)value) = g_fps; + break; + + case GET_MIPI_LANE: + *((int *)value) = MIPI_LANE; + break; + + case GET_INTERFACE: + *((int *)value) = SENSOR_IO_INTERFACE; + break; + + case GET_SENSOR_IO_LEVEL: + *((int *)value) = SENSOR_IO_LEVEL; + break; + + default: + ret = -1; + break; + } + + return ret; +} + +static int sp2305_set_power_on(const int pwdn_pin, const int reset_pin) +{ + ak_sensor_set_pin_as_gpio(pwdn_pin); + ak_sensor_set_pin_as_gpio(reset_pin); + ak_sensor_set_pin_dir(pwdn_pin, 1); + ak_sensor_set_pin_dir(reset_pin, 1); + + ak_sensor_set_pin_level(reset_pin, SENSOR_RESET_LEVEL); ///< Reset + ak_sensor_set_pin_level(pwdn_pin, SENSOR_PWDN_LEVEL); ///< Power Down + ak_sensor_mdelay(10); + ak_sensor_set_pin_level(pwdn_pin, !SENSOR_PWDN_LEVEL); ///< 正常工作。 + ak_sensor_mdelay(5); + ak_sensor_set_pin_level(reset_pin, !SENSOR_RESET_LEVEL); ///< 正常工作。 + ak_sensor_mdelay(20); + + return 0; +} + +static int sp2305_set_power_off(const int pwdn_pin, const int reset_pin) +{ + //sccb software standby mode + + ak_sensor_set_pin_level(pwdn_pin, SENSOR_PWDN_LEVEL); + ak_sensor_set_pin_dir(reset_pin, 0); + + return 0; +} + +static int sp2305_set_fps(int fps) +{ + //return 0; + int ret = 0; + int tmp=0; + + /* tmp = 78M / (reg0x320c~d=2080) / fps */ + switch (fps) { + case 25: + tmp = 0x0557; + break; + case 20: + tmp = 0x06ad; + break; + case 15: + tmp = 0x08e7; + break; + case 12: + tmp = 0x0b21; + break; + case 10: + tmp = 0x0d5a; + break; + case 8: + tmp = 0x10b1; + break; + case 5: + tmp = 0x1ab5; + break; + default: + printk(KERN_ERR "%s set fps=%d fail\n", __func__, fps); + ret = -EINVAL; + break; + } + + if (!ret) { +#if 0 + sp2305_sensor_write_register(0xfd,0x01); + sp2305_sensor_write_register(0x05, tmp>>8); + sp2305_sensor_write_register(0x06, tmp & 0xff); + sp2305_sensor_write_register(0x01,0x01); +#endif + + + //g_fps = fps; + to_fps = fps; + //tmp = tmp -0x0451; + to_fps_value = tmp; + } + + return ret; +} + +static int sp2305_set_standby_in(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static int sp2305_set_standby_out(const int pwdn_pin, const int reset_pin) +{ + return 0; +} + +static AK_ISP_SENSOR_CB sp2305_callback = +{ + .sensor_init_func = sp2305_init, + .sensor_read_reg_func = sp2305_sensor_read_register, + .sensor_write_reg_func = sp2305_sensor_write_register, + .sensor_read_id_func = sp2305_read_id, + .sensor_update_a_gain_func = sp2305_cmos_updata_a_gain, + .sensor_update_d_gain_func = sp2305_cmos_updata_d_gain, + .sensor_updata_exp_time_func = sp2305_cmos_updata_exp_time, + .sensor_timer_func = sp2305_cmos_update_a_gain_timer, + + .sensor_probe_id_func = sp2305_probe_id, //use IIC bus + .sensor_get_resolution_func = sp2305_get_resolution, + .sensor_get_mclk_func = sp2305_get_mclk, + .sensor_get_fps_func = sp2305_get_fps, + .sensor_get_valid_coordinate_func = sp2305_get_valid_coordinate, + .sensor_get_bus_type_func = sp2305_get_bus_type, + .sensor_get_parameter_func = sp2305_get_parameter, + + .sensor_set_power_on_func = sp2305_set_power_on, + .sensor_set_power_off_func = sp2305_set_power_off, + .sensor_set_fps_func = sp2305_set_fps, + .sensor_set_standby_in_func = sp2305_set_standby_in, + .sensor_set_standby_out_func = sp2305_set_standby_out +}; + +AK_SENSOR_MODULE(sp2305_callback, sp2305) +/////////////////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////////////////// +//static int is_corse7x_againmax = 0; + + + +static int sp2305_cmos_updata_d_gain(const unsigned int d_gain) +{ + return 0; +} + +static void sp2305_set_fps_async(void) +{ + if (to_fps != g_fps) { + sp2305_sensor_write_register(0xfd,0x01); + sp2305_sensor_write_register(0x0d,0x10); + sp2305_sensor_write_register(0x0e, to_fps_value>>8); + sp2305_sensor_write_register(0x0f, to_fps_value & 0xff); + sp2305_sensor_write_register(0x01,0x01); + + g_fps = to_fps; + } +} +#define _1XGAIN (256) +#define _2XGAIN (_1XGAIN*2) +#define _4XGAIN (_1XGAIN*4) +#define _8XGAIN (_1XGAIN*8) +#define _16XGAIN (_1XGAIN*16) + +static unsigned char curr_corse_gain = 0; +static unsigned int _cmos_gains_convert(unsigned int a_gain, unsigned int d_gain, + unsigned int *a_gain_out ,unsigned int *d_gain_out) +{ + + unsigned char fine_gain= 0; + unsigned int a_tmp_gain = 0; + sp2305_set_fps_async(); + + d_gain = 0; + + if(a_gain<_1XGAIN) + { + fine_gain = 0x10; + }else if(a_gain<_2XGAIN) + { + fine_gain = (a_gain >>4); + + }else if(a_gain<_4XGAIN) + { + fine_gain = ((a_gain>>4)&0xfe); + + }else if(a_gain<_8XGAIN) + { + fine_gain = ((a_gain>>4)&0xfc); + + }else if(a_gain<_16XGAIN) + { + fine_gain = ((a_gain>>4)&0xf8); + }else + { + fine_gain = 0xf8; + } + + printk("again:%d, fine_gain:0x%0.2x, curr_corse_gain:0x%0.2x,\n",a_gain, fine_gain,curr_corse_gain); + if (curr_corse_gain != fine_gain) + { + sp2305_sensor_write_register(0xfd, 0x01); + sp2305_sensor_write_register(0x24, fine_gain); + sp2305_sensor_write_register(0x01, 0x01); + curr_corse_gain = fine_gain; + } + return 0; +} + +static int sp2305_cmos_updata_a_gain(const unsigned int a_gain) +{ + + unsigned int tmp_a_gain; + unsigned int tmp_d_gain; + unsigned int tmp_a_gain_out; + unsigned int tmp_d_gain_out; + + tmp_a_gain = a_gain; + tmp_d_gain = 0; + + return _cmos_gains_convert(tmp_a_gain, tmp_d_gain, &tmp_a_gain_out ,&tmp_d_gain_out); +} + +static int sp2305_cmos_updata_exp_time(unsigned int exp_time) +{ + //?????????????????? + + unsigned char exposure_time_msb; + unsigned char exposure_time_lsb; + + + sp2305_set_fps_async(); + + //printk(KERN_ERR "exp_time=%d,%lu\n",exp_time,jiffies); + exposure_time_msb =(exp_time>>8)&0xff; + exposure_time_lsb =((exp_time)&0xff); + + sp2305_sensor_write_register(0xfd,0x01); + sp2305_sensor_write_register(0x03,exposure_time_msb); + sp2305_sensor_write_register(0x04,exposure_time_lsb); + sp2305_sensor_write_register(0x01,0x01); + + + return EXP_EFFECT_FRAMES; +} + +static int sp2305_cmos_update_a_gain_timer(void) +{ +#define UPDATE_FPS_TIMER_PERIOD (2) //s + static int start = 0; + static struct timeval stv = {.tv_sec = 0,}; + struct timeval tv; + + if ((to_fps != g_fps) && !start) { + start = 1; + do_gettimeofday(&stv); + } + + if (start == 1) { + do_gettimeofday(&tv); + if (tv.tv_sec >= stv.tv_sec + UPDATE_FPS_TIMER_PERIOD) { + sp2305_set_fps_async(); + stv.tv_sec = tv.tv_sec; + start = 0; + } + } + + return 0; +} diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index aedb970d..19e42463 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -1510,12 +1510,36 @@ static int __devexit soc_camera_pdrv_remove(struct platform_device *pdev) return 0; } +static int soc_camera_pdrv_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct soc_camera_device *icd = platform_get_drvdata(pdev); + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + + printk("soc camera suspend.\n"); + v4l2_subdev_call(sd, video, s_stream, -2); + + return 0; +} + +static int soc_camera_pdrv_resume(struct platform_device *pdev) +{ + struct soc_camera_device *icd = platform_get_drvdata(pdev); + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + + printk("soc camera resume.\n"); + v4l2_subdev_call(sd, video, s_stream, -1); + + return 0; +} + static struct platform_driver __refdata soc_camera_pdrv = { .remove = __devexit_p(soc_camera_pdrv_remove), .driver = { .name = "soc-camera-pdrv", .owner = THIS_MODULE, }, + .suspend = soc_camera_pdrv_suspend, + .resume = soc_camera_pdrv_resume, }; static int __init soc_camera_init(void) diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 5b2ec1fd..22c3e7af 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -2417,7 +2417,10 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, err = -EFAULT; if (_IOC_DIR(cmd) & _IOC_WRITE) { unsigned long n = cmd_input_size(cmd); - + + if (cmd == VIDIOC_G_PARM) + n+=4; + if (copy_from_user(parg, (void __user *)arg, n)) goto out; diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index de4fa4eb..e3efa314 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c @@ -469,8 +469,7 @@ int videobuf_reqbufs(struct videobuf_queue *q, } if (!list_empty(&q->stream)) { dprintk(1, "reqbufs: stream running\n"); - retval = -EBUSY; - goto done; + INIT_LIST_HEAD(&q->stream); } count = req->count; diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index c7795096..41c89805 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -382,6 +382,14 @@ config HMC6352 This driver provides support for the Honeywell HMC6352 compass, providing configuration and heading data via sysfs. +config SENSORS_AK8975 + tristate "AK8975 compass support" + default n + depends on I2C + help + If you say yes here you get support for Asahi Kasei's + orientation sensor AK8975. + config EP93XX_PWM tristate "EP93xx PWM support" depends on ARCH_EP93XX @@ -425,6 +433,10 @@ config TI_DAC7512 This driver can also be built as a module. If so, the module will be called ti_dac7512. +config UID_STAT + bool "UID based statistics tracking exported to /proc/uid_stat" + default n + config VMWARE_BALLOON tristate "VMware Balloon Driver" depends on X86 @@ -498,6 +510,54 @@ config MAX8997_MUIC Maxim MAX8997 PMIC. The MAX8997 MUIC is a USB port accessory detector and switch. +config WL127X_RFKILL + tristate "Bluetooth power control driver for TI wl127x" + depends on RFKILL + default n + ---help--- + Creates an rfkill entry in sysfs for power control of Bluetooth + TI wl127x chips. + +config AK_SERIAL_NUMBER + tristate "Anyka Serial Number Interface" + depends on ARCH_AK39 + ---help--- + If you say yes here you get an sysfs interface to read device serial number + on Anyka platform. + + +config AK_GETAIN + tristate "Anyka Get AIN value driver support" + default y + help + Say Y to enable support for the anyka ain0 from SOCLE. + + +config AK_MOTOR + tristate "Anyka Motor Support" + default n + help + If you say yes here you get support for the Anyka Motor device. + +config AK_DAC_CLOCK + tristate "Anyka Ak98 Dac Clock" + depends on ARCH_AK39 + help + If you say yes here you retrieve the elapsed time in dac driver + +comment "user space generic gpio controller" +config GPIOS_AKCUSTOM + tristate "Generic akgpio custom support" + depends on ARCH_AK39 + help + This enables anyka's generic GPIO for setting support through user space controller. + if you want need to enable this, you can control attribute of akgpio, + example dir[in/out], out[high/low], pull[up/down], get gpio level, + irq polarity[low/high], request gpio irq and delete irq etc. + + If unsure, say N. + + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" @@ -506,4 +566,31 @@ source "drivers/misc/ti-st/Kconfig" source "drivers/misc/lis3lv02d/Kconfig" source "drivers/misc/carma/Kconfig" source "drivers/misc/altera-stapl/Kconfig" +source "drivers/misc/akpcm/Kconfig" + +config H2_ISP_CHAR + tristate "H2 ISP CHAR device" + default y + help + H2 ISP CHAR device + +config USER_GPIO + tristate "gpios device that export to /sys" + default y + help + gpios device that export to /sys + +config AK_INFO_DUMP + tristate "dump anyka kernel informations that export to /sys" + default y + help + dump anyka kernel informations that export to /sys + +config AK_PWM_CHAR + tristate "AK PWM CHAR device" + default y + help + AK PCM CHAR device + endmenu + diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 3e1d8010..a70c69ff 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o obj-$(CONFIG_DS1682) += ds1682.o obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o +obj-$(CONFIG_UID_STAT) += uid_stat.o obj-$(CONFIG_C2PORT) += c2port/ obj-$(CONFIG_IWMC3200TOP) += iwmc3200top/ obj-$(CONFIG_HMC6352) += hmc6352.o @@ -49,3 +50,18 @@ obj-y += carma/ obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/ obj-$(CONFIG_MAX8997_MUIC) += max8997-muic.o +obj-$(CONFIG_WL127X_RFKILL) += wl127x-rfkill.o +obj-$(CONFIG_SENSORS_AK8975) += akm8975.o +obj-$(CONFIG_AK_MOTOR) += ak_motor.o +obj-$(CONFIG_AK_SERIAL_NUMBER) += ak_sn.o +obj-$(CONFIG_AK_GETAIN) += ain.o +obj-$(CONFIG_GPIOS_AKCUSTOM) += akgpios.o +obj-$(CONFIG_AK_DAC_CLOCK) += ak_dac_clock.o +obj-y += akpcm/ + +obj-$(CONFIG_H2_ISP_CHAR) += isp_char.o +isp_char-objs += ak_isp_char.o #../media/video/plat-anyka/ak39_isp2.o ../media/video/plat-anyka/ak39_isp2_3a.o ../media/video/plat-anyka/ar0130_3a.o ../media/video/plat-anyka/ov9712_3a.o + +obj-$(CONFIG_USER_GPIO) += user_gpio.o +obj-$(CONFIG_AK_INFO_DUMP) += ak_info_dump.o +obj-$(CONFIG_AK_PWM_CHAR) +=ak_pwm_char.o diff --git a/drivers/misc/ain.c b/drivers/misc/ain.c new file mode 100644 index 00000000..2eaad47a --- /dev/null +++ b/drivers/misc/ain.c @@ -0,0 +1,187 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DBG(fmt...) //printk(fmt) +#define EFUSE_CTRL_REG (AK_VA_SYSCTRL + 0x48) +#define ANALOG_CTRL_REG3 (AK_VA_SYSCTRL + 0x9C) +#define ANALOG_CTRL_REG4 (AK_VA_SYSCTRL + 0xA0) + + +static ssize_t ain0_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + /*int rTmp = 0x0;*/ + int ain; + + /*pull down VP */ + + //REG32(ANALOG_CTRL_REG3) |= (1 << 3); + //REG32(ANALOG_CTRL_REG4) |= ((1 << 0) | (1 << 25)); + #if 0 + msleep(200); //wait for VP pull down, (>150 ms) + REG32(EFUSE_CTRL_REG) = 0x00000002; //set read mode + REG32(EFUSE_CTRL_REG) |= 0x00000001; //start to read + msleep(15); //wait for read finish ( > 2us ) + REG32(EFUSE_CTRL_REG) &= (~0x00000001); //clear efuse_cfg_rdy bit for next operate + rTmp = REG32(EFUSE_CTRL_REG); + #endif +/* if(((rTmp >> 8)&0xff)< 8) + DBG("open get ain device failure.:%d\n",rTmp); + else + DBG("open get ain device success:%d.\n",rTmp); +*/ + ain = (int)adc1_read_ain0(); + + DBG("ad4_value = %d:\n",ain); + return sprintf(buf, "%d\n", ain); +} + +static ssize_t ain1_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + /*int rTmp = 0x0;*/ + int ain; + + /*pull down VP */ + + //REG32(ANALOG_CTRL_REG3) |= (1 << 3); + //REG32(ANALOG_CTRL_REG4) |= ((1 << 0) | (1 << 25)); + #if 0 + msleep(200); //wait for VP pull down, (>150 ms) + REG32(EFUSE_CTRL_REG) = 0x00000002; //set read mode + REG32(EFUSE_CTRL_REG) |= 0x00000001; //start to read + msleep(15); //wait for read finish ( > 2us ) + REG32(EFUSE_CTRL_REG) &= (~0x00000001); //clear efuse_cfg_rdy bit for next operate + rTmp = REG32(EFUSE_CTRL_REG); + #endif +/* if(((rTmp >> 8)&0xff)< 8) + DBG("open get ain device failure.:%d\n",rTmp); + else + DBG("open get ain device success:%d.\n",rTmp); +*/ + ain = (int)adc1_read_ain1(); + + DBG("ad4_value = %d:\n",ain); + return sprintf(buf, "%d\n", ain); +} + +static ssize_t bat_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + /*int rTmp = 0x0;*/ + int ain; + + /*pull down VP */ + + //REG32(ANALOG_CTRL_REG3) |= (1 << 3); + //REG32(ANALOG_CTRL_REG4) |= ((1 << 0) | (1 << 25)); + #if 0 + msleep(200); //wait for VP pull down, (>150 ms) + REG32(EFUSE_CTRL_REG) = 0x00000002; //set read mode + REG32(EFUSE_CTRL_REG) |= 0x00000001; //start to read + msleep(15); //wait for read finish ( > 2us ) + REG32(EFUSE_CTRL_REG) &= (~0x00000001); //clear efuse_cfg_rdy bit for next operate + rTmp = REG32(EFUSE_CTRL_REG); + #endif +/* if(((rTmp >> 8)&0xff)< 8) + DBG("open get ain device failure.:%d\n",rTmp); + else + DBG("open get ain device success:%d.\n",rTmp); +*/ + ain = (int)adc1_read_bat(); + + DBG("ad4_value = %d:\n",ain); + return sprintf(buf, "%d\n", ain); +} + + +static struct kobj_attribute ain0_attribute = + __ATTR(ain0, 0666, ain0_show, NULL); + +static struct kobj_attribute ain1_attribute = + __ATTR(ain1, 0666, ain1_show, NULL); + +static struct kobj_attribute bat_attribute = + __ATTR(bat, 0666, bat_show, NULL); + + +static struct attribute *attrain0[] = { + &ain0_attribute.attr, + NULL +}; +static struct attribute *attrain1[] = { + &ain1_attribute.attr, + NULL +}; +static struct attribute *attrbat[] = { + &bat_attribute.attr, + NULL +}; + + +static struct kobject *ain_kobj; + +static int __init ain_init(void) +{ + int ret; + + ain_kobj = kobject_create_and_add("ain", kernel_kobj); + if (!ain_kobj) { + printk("Create ain kobject failed\n"); + return -ENOMEM; + } + + + ret = sysfs_create_file(ain_kobj, *attrain0); + if (ret) { + printk("Create ain sysfs file failed\n"); + kobject_put(ain_kobj); + } + ret = sysfs_create_file(ain_kobj, *attrain1); + if (ret) { + printk("Create ain sysfs file failed\n"); + kobject_put(ain_kobj); + } + ret = sysfs_create_file(ain_kobj, *attrbat); + if (ret) { + printk("Create ain sysfs file failed\n"); + kobject_put(ain_kobj); + } + return ret; +} + +static void __exit ain_exit(void) +{ + kobject_put(ain_kobj); +} + +module_init(ain_init); +module_exit(ain_exit); + +MODULE_DESCRIPTION("Anyka Device Ain Interface"); +MODULE_AUTHOR("Anyka"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/ak_dac_clock.c b/drivers/misc/ak_dac_clock.c new file mode 100644 index 00000000..5197ad91 --- /dev/null +++ b/drivers/misc/ak_dac_clock.c @@ -0,0 +1,84 @@ +/* + * ak_dac_clock -- driver for reading the dac clock; + * Features + * AUTHOR Wudaochao + * 12-04-13 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +extern unsigned long long getPlaybackEclapseTime(void); + + +static struct miscdevice ak_dacclk_dev; + +static int ak_dacclk_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int ak_dacclk_release(struct inode *inode, struct file *file) +{ + return 0; +} + +long ak_dacclk_ioctl( struct file *filp, unsigned int cmd, unsigned long arg) +{ + unsigned long long *p; + + p = (unsigned long long *)arg; + + *p = getPlaybackEclapseTime(); + + return 0; +} + +struct file_operations ak_dacclk_fops = { + .release = ak_dacclk_release, + .open = ak_dacclk_open, + .unlocked_ioctl = ak_dacclk_ioctl +}; + +static int __init ak_dacclk_init(void) +{ + int err; + ak_dacclk_dev.name = "dac_clock"; + ak_dacclk_dev.minor = MISC_DYNAMIC_MINOR; + ak_dacclk_dev.fops = &ak_dacclk_fops; + printk(KERN_INFO "ak_dacclk_init==>%s\n", ak_dacclk_dev.name); + + err = misc_register(&ak_dacclk_dev); + if (err) { + printk(KERN_INFO "ak_dacclk_init==>misc_register failed\n"); + return -1; + } else { + printk(KERN_INFO "ak_dacclk_init==>misc_register succeedded\n"); + } + + return 0; +} + +static void __exit ak_dacclk_exit(void) +{ + misc_deregister(&ak_dacclk_dev); +} + +module_init(ak_dacclk_init); +module_exit(ak_dacclk_exit); + +MODULE_DESCRIPTION("Anyka Dac Clock Driver"); +MODULE_AUTHOR("Anyka"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/misc/ak_info_dump.c b/drivers/misc/ak_info_dump.c new file mode 100644 index 00000000..bfff7425 --- /dev/null +++ b/drivers/misc/ak_info_dump.c @@ -0,0 +1,102 @@ +#include +#include +#include + +int aksensor_get_sensor_id(void); +int aksensor_get_sensor_if(char *if_str); +int akmci_get_tf_err_count(void); +void akmci_set_tf_err_count(int count); + +static ssize_t reserved_mem_size_read(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "0x%x\n", CONFIG_VIDEO_RESERVED_MEM_SIZE); +} + +static ssize_t sensor_id_read(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "0x%x\n", aksensor_get_sensor_id()); +} + +static ssize_t sensor_if_read(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + int sensor_if; + char if_str[16]; + + if_str[0] = '\0'; + sensor_if = aksensor_get_sensor_if(if_str); + + return sprintf(buf, "%s\n", if_str); +} + +static ssize_t tf_err_count_read(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", akmci_get_tf_err_count()); +} + +static ssize_t tf_err_count_write(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + unsigned int tf_err_count = 0; + sscanf(buf, "%d", &tf_err_count); + akmci_set_tf_err_count(tf_err_count); + return count; +} + +static struct kobj_attribute reserved_mem_size_obj =__ATTR(reserved_mem_size, S_IRUGO, reserved_mem_size_read, NULL); +static struct kobj_attribute sensor_id_obj =__ATTR(sensor_id, S_IRUGO, sensor_id_read, NULL); +static struct kobj_attribute sensor_if_obj =__ATTR(sensor_if, S_IRUGO, sensor_if_read, NULL); +static struct kobj_attribute tf_err_count_obj =__ATTR(tf_err_count, 0666, tf_err_count_read, tf_err_count_write); + +static struct attribute *ak_info_dump_attrs[] = { + &reserved_mem_size_obj.attr, + &sensor_id_obj.attr, + &sensor_if_obj.attr, + &tf_err_count_obj.attr, + NULL, +}; + + +static struct attribute_group ak_info_dump_group = { + .attrs = ak_info_dump_attrs, +}; + +struct kobject *ak_info_dump_obj = NULL; +int ak_info_dump_init(void) +{ + int ret = 0; + printk("%s\n", __func__); + + ak_info_dump_obj = kobject_create_and_add("ak_info_dump", NULL); + if (!ak_info_dump_obj) + goto err_board_obj; + + ret = sysfs_create_group(ak_info_dump_obj, &ak_info_dump_group); + if (ret) + goto err_sysfs_create; + + return 0; + +err_sysfs_create: + //sysfs_remove_group(ak_info_dump_obj, &ak_info_dump_group); + kobject_put(ak_info_dump_obj); + printk("\nsysfs_create_group ERROR : %s\n",__func__); + return 0; +err_board_obj: + printk("\nobject_create_and_add ERROR : %s\n",__func__); + return 0; +} + +void ak_info_dump_exit(void) +{ + printk("%s\n", __func__); + sysfs_remove_group(ak_info_dump_obj, &ak_info_dump_group); + kobject_put(ak_info_dump_obj); +} + + +MODULE_AUTHOR("ye_guohong"); +MODULE_LICENSE("GPL"); +module_init(ak_info_dump_init); +module_exit(ak_info_dump_exit); + + diff --git a/drivers/misc/ak_isp_char.c b/drivers/misc/ak_isp_char.c new file mode 100644 index 00000000..77e05093 --- /dev/null +++ b/drivers/misc/ak_isp_char.c @@ -0,0 +1,3625 @@ +/* + * ak39 h2 isp driver interface. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*#include "../media/video/plat-anyka/ak39_isp2.h" +#include "../media/video/plat-anyka/ak39_isp2_3a.h" +#include "../media/video/plat-anyka/ak39_isp2_reg.h" +*/ +#include +#include +#include +#include "ak_isp_char.h" + +//#define ISP_DEBUG +#ifdef ISP_DEBUG +#define ISP_PRINTF(stuff...) printk(KERN_INFO " ISP: " stuff) +#else +#define ISP_PRINTF(fmt, args...) do{}while(0) +#endif +#define isp_info(stuff...) printk(KERN_INFO " ISP: " stuff) + +#define AKISP_REG_MEM_START 0x20000000 +#define AKISP_REG_MEM_END 0x2000004c +#define RESOURCE_SIZE (AKISP_REG_MEM_END - AKISP_REG_MEM_START + 1) + +#define AKISP_DEV_NAME "isp_char" + +//#define OSD_CHN_NUM 2 + +struct akisp_osd_info { + void *main_osd_vaddr; + void *main_osd_paddr; + int main_osd_byte_size; + + void *sub_osd_vaddr; + void *sub_osd_paddr; + int sub_osd_byte_size; + + AK_ISP_OSD_MEM_ATTR main_osd_irq_dma; + AK_ISP_OSD_MEM_ATTR sub_osd_irq_dma; +}; + +struct akisp_char_pirv { + void *base; + int used_cnt; + struct mutex lock; + AK_ISP_SENSOR_CB *sensor_cb; +// AK_ISP_OSD_MEM_ATTR osd_param[OSD_CHN_NUM]; + struct akisp_osd_info osd_info[ISP_OSD_CHN_NUM]; +}; + +static struct akisp_char_pirv *priv = NULL; + +void ak_sensor_set_sensor_cb(AK_ISP_SENSOR_CB *cb) +{ + if (priv) + priv->sensor_cb = cb; +} +EXPORT_SYMBOL(ak_sensor_set_sensor_cb); + +AK_ISP_SENSOR_CB *ak_sensor_get_sensor_cb(void) +{ + return priv ? priv->sensor_cb : NULL; +} +EXPORT_SYMBOL(ak_sensor_get_sensor_cb); + +static int printk_blc(AK_ISP_BLC_ATTR *blc_para) +{ + int i = 0; + + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("blc_para->blc_mode=%d\n", blc_para->blc_mode); + + ISP_PRINTF("blc_para->m_blc.black_level_enable=%d\n", blc_para->m_blc.black_level_enable); + ISP_PRINTF("blc_para->m_blc.bl_r_a=%d\n", blc_para->m_blc.bl_r_a); + ISP_PRINTF("blc_para->m_blc.bl_r_offset=%d\n", blc_para->m_blc.bl_r_offset); + ISP_PRINTF("blc_para->m_blc.bl_gr_a = %d\n", blc_para->m_blc.bl_gr_a); + ISP_PRINTF("blc_para->m_blc.bl_gr_offset =%d\n", blc_para->m_blc.bl_gr_offset); + ISP_PRINTF("blc_para->m_blc.bl_gb_a =%d\n", blc_para->m_blc.bl_gb_a); + ISP_PRINTF("blc_para->m_blc.bl_gb_offset=%d", blc_para->m_blc.bl_gb_offset); + ISP_PRINTF("blc_para->m_blc.bl_b_a=%d\n", blc_para->m_blc.bl_b_a); + ISP_PRINTF("blc_para->m_blc.bl_b_offset=%d\n", blc_para->m_blc.bl_b_offset); + + for (i=0; i<9; i++) + { + ISP_PRINTF("blc_para.linkage_blc[%d].black_level_enable=%d\n", i, blc_para->linkage_blc[i].black_level_enable); + ISP_PRINTF("blc_para.linkage_blc[%d].bl_r_a=%d\n", i, blc_para->linkage_blc[i].bl_r_a); + ISP_PRINTF("blc_para.linkage_blc[%d].bl_r_offset=%d\n", i, blc_para->linkage_blc[i].bl_r_offset); + ISP_PRINTF("blc_para.linkage_blc[%d].bl_gr_a=%d\n", i, blc_para->linkage_blc[i].bl_gr_a); + ISP_PRINTF("blc_para.linkage_blc[%d].bl_gr_offset=%d\n", i, blc_para->linkage_blc[i].bl_gr_offset); + ISP_PRINTF("blc_para.linkage_blc[%d].bl_gb_a=%d\n", i, blc_para->linkage_blc[i].bl_gb_a); + ISP_PRINTF("blc_para.linkage_blc[%d].bl_gb_offset=%d\n", i, blc_para->linkage_blc[i].bl_gb_offset); + ISP_PRINTF("blc_para.linkage_blc[%d].bl_b_a=%d\n", i, blc_para->linkage_blc[i].bl_b_a); + ISP_PRINTF("blc_para.linkage_blc[%d].bl_b_offset=%d\n", i, blc_para->linkage_blc[i].bl_b_offset); + } + + return 0; +} + +static int printk_gb(AK_ISP_GB_ATTR *gb_para) +{ + int i = 0; + + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("gb_para->gb_mode = %d\n", gb_para->gb_mode); + ISP_PRINTF("gb_para->manual_gb.gb_enable = %d\n", gb_para->manual_gb.gb_enable); + ISP_PRINTF("gb_para->manual_gb.gb_en_th = %d\n", gb_para->manual_gb.gb_en_th); + ISP_PRINTF("gb_para->manual_gb.gb_kstep=%d\n", gb_para->manual_gb.gb_kstep); + ISP_PRINTF("gb_para->manual_gb.gb_threshold=%d\n",gb_para->manual_gb.gb_threshold); + + for (i=0; i<9; i++) + { + ISP_PRINTF("gb_para.linkage_gb[%d].gb_enable=%d\n", i, gb_para->linkage_gb[i].gb_enable); + ISP_PRINTF("gb_para.linkage_gb[%d].gb_en_th=%d\n", i, gb_para->linkage_gb[i].gb_en_th); + ISP_PRINTF("gb_para.linkage_gb[%d].gb_kstep=%d\n", i, gb_para->linkage_gb[i].gb_kstep); + ISP_PRINTF("gb_para.linkage_gb[%d].gb_threshold=%d\n", i, gb_para->linkage_gb[i].gb_threshold); + } + + return 0; +} + +static int printk_raw_lut(AK_ISP_RAW_LUT_ATTR *lut_para) +{ + int i = 0; + + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("lut_para->raw_gamma_enable=%d\n", lut_para->raw_gamma_enable); + + for (i=0; i<129; i++) + { + ISP_PRINTF("lut_para->raw_r[%d]=%d\n", i, lut_para->raw_r[i]); + ISP_PRINTF("lut_para->raw_g[%d]=%d\n", i, lut_para->raw_g[i]); + ISP_PRINTF("lut_para->raw_b[%d]=%d\n", i, lut_para->raw_b[i]); + } + + return 0; +} + +static int printk_nr1_para(AK_ISP_NR1_ATTR *nr1) +{ + int i = 0; + int j = 0; + + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("nr1->nr1_mode=%d\n", nr1->nr1_mode); + ISP_PRINTF("nr1->manual_nr1.nr1_enable=%d\n", nr1->manual_nr1.nr1_enable); + ISP_PRINTF("nr1->manual_nr1.nr1_calc_r_k=%d\n", nr1->manual_nr1.nr1_calc_r_k); + ISP_PRINTF("nr1->manual_nr1.nr1_calc_g_k=%d\n", nr1->manual_nr1.nr1_calc_g_k); + ISP_PRINTF("nr1->manual_nr1.nr1_calc_b_k=%d\n", nr1->manual_nr1.nr1_calc_b_k); + + for (i=0; i<17; i++) + { + ISP_PRINTF("nr1->manual_nr1.nr1_weight_rtbl[%d]=%d\n", i, nr1->manual_nr1.nr1_weight_rtbl[i]); + } + + for (i=0; i<17; i++) + { + ISP_PRINTF("nr1->manual_nr1.nr1_weight_gtbl[%d]=%d\n", i, nr1->manual_nr1.nr1_weight_gtbl[i]); + } + + for (i=0; i<17; i++) + { + ISP_PRINTF("nr1->manual_nr1.nr1_weight_btbl[%d]=%d\n", i, nr1->manual_nr1.nr1_weight_btbl[i]); + } + + for (i=0; i<17; i++) + { + ISP_PRINTF("nr1->manual_nr1.nr1_lc_lut[%d]=%d\n", i, nr1->manual_nr1.nr1_lc_lut[i]); + } + + for (j=0; j<9; j++) + { + ISP_PRINTF("nr1->linkage_nr1[%d].nr1_enable=%d\n", j, nr1->manual_nr1.nr1_enable); + ISP_PRINTF("nr1->linkage_nr1[%d]1.nr1_calc_r_k=%d\n", j, nr1->linkage_nr1[j].nr1_calc_r_k); + ISP_PRINTF("nr1->linkage_nr1[%d].nr1_calc_g_k=%d\n", j, nr1->linkage_nr1[j].nr1_calc_g_k); + ISP_PRINTF("nr1->linkage_nr1[%d].nr1_calc_b_k=%d\n", j, nr1->linkage_nr1[j].nr1_calc_b_k); + + for (i=0; i<17; i++) + { + ISP_PRINTF("nr1->linkage_nr1[%d].nr1_weight_rtbl[%d]=%d\n", j, i, nr1->linkage_nr1[j].nr1_weight_rtbl[i]); + } + + //ISP_PRINTF("nr1->linkage_nr1[%d].nr1_calc_g_k=%d\n",j,nr1->linkage_nr1[j].nr1_calc_g_k); + for (i=0; i<17; i++) + { + ISP_PRINTF("nr1->linkage_nr1[%d].nr1_weight_gtbl[%d]=%d\n", j, i, nr1->linkage_nr1[j].nr1_weight_gtbl[i]); + } + + //ISP_PRINTF("nr1->linkage_nr1[%d].nr1_calc_b_k=%d\n",j,nr1->linkage_nr1[j].nr1_calc_b_k); + + for (i=0; i<17; i++) + { + ISP_PRINTF("nr1->linkage_nr1[%d].nr1_weight_btbl[%d]=%d\n", j, i, nr1->linkage_nr1[j].nr1_weight_btbl[i]); + } + + for (i=0; i<17; i++) + { + ISP_PRINTF("nr1->linkage_nr1[%d].nr1_lc_lut[%d]=%d\n", j, i, nr1->linkage_nr1[j].nr1_lc_lut[i]); + } + } + + return 0; +} + +static int printk_demo_para(AK_ISP_DEMO_ATTR *demo_para) +{ + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("demo_para->dm_bg_gain=%d\n", demo_para->dm_bg_gain); + ISP_PRINTF("demo_para->dm_bg_thre=%d\n", demo_para->dm_bg_thre); + ISP_PRINTF("demo_para->dm_gb_gain=%d\n", demo_para->dm_gb_gain); + ISP_PRINTF("demo_para->dm_gr_gain=%d\n", demo_para->dm_gr_gain); + ISP_PRINTF("demo_para->dm_hf_th1=%d\n", demo_para->dm_hf_th1); + ISP_PRINTF("demo_para->dm_hf_th2=%d\n", demo_para->dm_hf_th2); + ISP_PRINTF("demo_para->dm_HV_th=%d\n", demo_para->dm_HV_th); + ISP_PRINTF("demo_para->dm_rg_gain=%d\n", demo_para->dm_rg_gain); + ISP_PRINTF("demo_para->dm_rg_thre=%d\n", demo_para->dm_rg_thre); + + return 0; +} + +static int printk_ccm_para(AK_ISP_CCM_ATTR *ccm_para) +{ + int i,j,k; + + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("ccm_para->cc_mode=%d\n", ccm_para->cc_mode); + ISP_PRINTF("ccm_para->manual_ccm.cc_enable=%d\n", ccm_para->manual_ccm.cc_enable); + ISP_PRINTF("ccm_para->manual_ccm.cc_cnoise_yth=%d\n", ccm_para->manual_ccm.cc_cnoise_yth); + ISP_PRINTF("ccm_para->manual_ccm.cc_cnoise_gain=%d\n", ccm_para->manual_ccm.cc_cnoise_gain); + ISP_PRINTF("ccm_para->manual_ccm.cc_cnoise_slop=%d\n", ccm_para->manual_ccm.cc_cnoise_slop); + + for (i=0; i<3; i++) + { + for (j=0; j<3; j++) + { + ISP_PRINTF("ccm_para->manual_ccm.ccm[%d][%d]=%d\n", i, j, ccm_para->manual_ccm.ccm[i][j]); + } + } + + for (k=0; k<4; k++) + { + ISP_PRINTF("ccm_para->ccm[%d].cc_enable=%d\n", k, ccm_para->ccm[k].cc_enable); + ISP_PRINTF("ccm_para->ccm[%d].cc_cnoise_yth=%d\n", k, ccm_para->ccm[k].cc_cnoise_yth); + ISP_PRINTF("ccm_para->ccm[%d].cc_cnoise_gain=%d\n", k, ccm_para->ccm[k].cc_cnoise_gain); + ISP_PRINTF("ccm_para->ccm[%d].cc_cnoise_slop=%d\n", k, ccm_para->ccm[k].cc_cnoise_slop); + + for (i=0; i<3; i++) + { + for (j=0; j<3; j++) + { + ISP_PRINTF("ccm_para->ccm[%d].ccm[%d][%d]=%d\n", k, i, j, ccm_para->ccm[k].ccm[i][j]); + } + } + } + + return 0; +} + + +static int printk_wdr_para(AK_ISP_WDR_ATTR *wdr_para) +{ + int i = 0, j = 0; + + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("wdr_para->wdr_mode=%d\n", wdr_para->wdr_mode); + + ISP_PRINTF("wdr_para->manual_wdr.hdr_uv_adjust_level=%d\n", wdr_para->manual_wdr.hdr_uv_adjust_level); + ISP_PRINTF("wdr_para->manual_wdr.hdr_cnoise_suppress_slop=%d\n", wdr_para->manual_wdr.hdr_cnoise_suppress_slop); + ISP_PRINTF("wdr_para->manual_wdr.wdr_enable=%d\n", wdr_para->manual_wdr.wdr_enable); + ISP_PRINTF("wdr_para->manual_wdr.hdr_uv_adjust_enable=%d\n", wdr_para->manual_wdr.hdr_uv_adjust_enable); + ISP_PRINTF("wdr_para->manual_wdr.hdr_cnoise_suppress_yth1=%d\n", wdr_para->manual_wdr.hdr_cnoise_suppress_yth1); + ISP_PRINTF("wdr_para->manual_wdr.hdr_cnoise_suppress_yth2=%d\n", wdr_para->manual_wdr.hdr_cnoise_suppress_yth2); + ISP_PRINTF("wdr_para->manual_wdr.hdr_cnoise_suppress_gain=%d\n", wdr_para->manual_wdr.hdr_cnoise_suppress_gain); + + ISP_PRINTF("wdr_para->manual_wdr.wdr_th1=%d\n", wdr_para->manual_wdr.wdr_th1); + ISP_PRINTF("wdr_para->manual_wdr.wdr_th2=%d\n", wdr_para->manual_wdr.wdr_th2); + ISP_PRINTF("wdr_para->manual_wdr.wdr_th3=%d\n", wdr_para->manual_wdr.wdr_th3); + ISP_PRINTF("wdr_para->manual_wdr.wdr_th4=%d\n", wdr_para->manual_wdr.wdr_th4); + ISP_PRINTF("wdr_para->manual_wdr.wdr_th5=%d\n", wdr_para->manual_wdr.wdr_th5); + + for (i=0; i<65; i++) + { + ISP_PRINTF("wdr_para->manual_wdr.area_tb1[%d]=%d\n", i, wdr_para->manual_wdr.area_tb1[i]); + } + + for (i=0; i<65; i++) + { + ISP_PRINTF("wdr_para->manual_wdr.area_tb2[%d]=%d\n", i, wdr_para->manual_wdr.area_tb2[i]); + } + + for (i=0; i<65; i++) + { + ISP_PRINTF("wdr_para->manual_wdr.area_tb3[%d]=%d\n", i, wdr_para->manual_wdr.area_tb3[i]); + } + + for (i=0; i<65; i++) + { + ISP_PRINTF("wdr_para->manual_wdr.area_tb4[%d]=%d\n", i, wdr_para->manual_wdr.area_tb4[i]); + } + + for (i=0; i<65; i++) + { + ISP_PRINTF("wdr_para->manual_wdr.area_tb5[%d]=%d\n", i, wdr_para->manual_wdr.area_tb5[i]); + } + + for (j=0; j<9; j++) + { + ISP_PRINTF("wdr_para->linkage_wdr[%d].hdr_uv_adjust_level=%d\n", j, wdr_para->linkage_wdr[j].hdr_uv_adjust_level); + ISP_PRINTF("wdr_para->linkage_wdr[%d].hdr_cnoise_suppress_slop=%d\n", j, wdr_para->linkage_wdr[j].hdr_cnoise_suppress_slop); + ISP_PRINTF("wdr_para->linkage_wdr[%d].wdr_enable=%d\n", j, wdr_para->linkage_wdr[j].wdr_enable); + ISP_PRINTF("wdr_para->linkage_wdr[%d].hdr_uv_adjust_enable=%d\n", j, wdr_para->linkage_wdr[j].hdr_uv_adjust_enable); + ISP_PRINTF("wdr_para->linkage_wdr[%d].hdr_cnoise_suppress_yth1=%d\n", j, wdr_para->linkage_wdr[j].hdr_cnoise_suppress_yth1); + ISP_PRINTF("wdr_para->linkage_wdr[%d].hdr_cnoise_suppress_yth2=%d\n", j, wdr_para->linkage_wdr[j].hdr_cnoise_suppress_yth2); + ISP_PRINTF("wdr_para->linkage_wdr[%d].hdr_cnoise_suppress_gain=%d\n", j, wdr_para->linkage_wdr[j].hdr_cnoise_suppress_gain); + + ISP_PRINTF("wdr_para->linkage_wdr[%d].wdr_th1=%d\n", j, wdr_para->linkage_wdr[j].wdr_th1); + ISP_PRINTF("wdr_para->linkage_wdr[%d].wdr_th2=%d\n", j, wdr_para->linkage_wdr[j].wdr_th2); + ISP_PRINTF("wdr_para->linkage_wdr[%d].wdr_th3=%d\n", j, wdr_para->linkage_wdr[j].wdr_th3); + ISP_PRINTF("wdr_para->linkage_wdr[%d].wdr_th4=%d\n", j, wdr_para->linkage_wdr[j].wdr_th4); + ISP_PRINTF("wdr_para->linkage_wdr[%d].wdr_th5=%d\n", j, wdr_para->linkage_wdr[j].wdr_th5); + + for (i=0; i<65; i++) + { + ISP_PRINTF("wdr_para->linkage_wdr[%d].area_tb1[%d]=%d\n", j, i, wdr_para->linkage_wdr[j].area_tb1[i]); + } + + for (i=0; i<65; i++) + { + ISP_PRINTF("wdr_para->linkage_wdr[%d].area_tb2[%d]=%d\n", j, i, wdr_para->linkage_wdr[j].area_tb2[i]); + } + + for (i=0; i<65; i++) + { + ISP_PRINTF("wdr_para->linkage_wdr[%d].area_tb3[%d]=%d\n", j, i, wdr_para->linkage_wdr[j].area_tb3[i]); + } + + for (i=0; i<65; i++) + { + ISP_PRINTF("wdr_para->linkage_wdr[%d].area_tb4[%d]=%d\n", j, i, wdr_para->linkage_wdr[j].area_tb4[i]); + } + + for (i=0; i<65; i++) + { + ISP_PRINTF("wdr_para->linkage_wdr[%d].area_tb5[%d]=%d\n", j, i, wdr_para->linkage_wdr[j].area_tb5[i]); + } + } + + return 0; +} + +static int printk_nr2_para(AK_ISP_NR2_ATTR *nr2_para) +{ + int i = 0; + int j = 0; + + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("nr2_para->nr2_mode=%d\n", nr2_para->nr2_mode); + ISP_PRINTF("nr2_para->manual_nr2.nr2_enable=%d\n", nr2_para->manual_nr2.nr2_enable); + ISP_PRINTF("nr2_para->manual_nr2.nr2_calc_y_k=%d\n", nr2_para->manual_nr2.nr2_calc_y_k); + ISP_PRINTF("nr2_para->manual_nr2.nr2_k=%d\n", nr2_para->manual_nr2.nr2_k); + ISP_PRINTF("nr2_para->manual_nr2.y_dpc_enable=%d\n", nr2_para->manual_nr2.y_dpc_enable); + ISP_PRINTF("nr2_para->manual_nr2.y_dpc_th=%d\n", nr2_para->manual_nr2.y_dpc_th); + ISP_PRINTF("nr2_para->manual_nr2.y_black_dpc_enable=%d\n", nr2_para->manual_nr2.y_black_dpc_enable); + ISP_PRINTF("nr2_para->manual_nr2.y_white_dpc_enable=%d\n", nr2_para->manual_nr2.y_white_dpc_enable); + + for (i=0; i<17; i++) + { + ISP_PRINTF("nr2_para->manual_nr2.nr2_weight_tbl[%d]=%d\n", i, nr2_para->manual_nr2.nr2_weight_tbl[i]); + } + + for (i=0; i<9; i++) + { + ISP_PRINTF("nr2_para->linkage_nr2[%d].nr2_enable=%d\n", i, nr2_para->linkage_nr2[i].nr2_enable); + ISP_PRINTF("nr2_para->linkage_nr2[%d].nr2_calc_y_k=%d\n", i, nr2_para->linkage_nr2[i].nr2_calc_y_k); + ISP_PRINTF("nr2_para->linkage_nr2[%d].nr2_k=%d\n", i, nr2_para->linkage_nr2[i].nr2_k); + ISP_PRINTF("nr2_para->linkage_nr2[%d].y_dpc_enable=%d\n", i, nr2_para->linkage_nr2[i].y_dpc_enable); + ISP_PRINTF("nr2_para->linkage_nr2[%d].y_dpc_th=%d\n", i, nr2_para->linkage_nr2[i].y_dpc_th); + ISP_PRINTF("nr2_para->linkage_nr2[%d].y_black_dpc_enable=%d\n", i, nr2_para->linkage_nr2[i].y_black_dpc_enable); + ISP_PRINTF("nr2_para->linkage_nr2[%d].y_white_dpc_enable=%d\n", i, nr2_para->linkage_nr2[i].y_white_dpc_enable); + + for (j=0; j<17; j++) + { + ISP_PRINTF("nr2_para->linkage_nr2[%d].nr2_weight_tbl[%d]=%d\n", i, j, nr2_para->linkage_nr2[i].nr2_weight_tbl[j]); + } + } + + return 0; +} + +static int printk_rgb_gamma_para(AK_ISP_RGB_GAMMA_ATTR *gamma_para) +{ + int i = 0; + + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("gamma_para->rgb_gamma_enable=%d\n", gamma_para->rgb_gamma_enable); + + for (i=0; i<129; i++) + { + ISP_PRINTF("gamma_para->r_gamma[%d]=%d\n", i, gamma_para->r_gamma[i]); + } + + for (i=0; i<129; i++) + { + ISP_PRINTF("gamma_para->g_gamma[%d]=%d\n", i, gamma_para->g_gamma[i]); + } + + for (i=0; i<129; i++) + { + ISP_PRINTF("gamma_para->b_gamma[%d]=%d\n", i, gamma_para->b_gamma[i]); + } + + return 0; +} + +static int printk_fcs_para(AK_ISP_FCS_ATTR *fcs_para) +{ + int i = 0; + + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("fcs_para->fcs_mode=%d\n", fcs_para->fcs_mode); + ISP_PRINTF("fcs_para->manual_fcs.fcs_enable=%d\n", fcs_para->manual_fcs.fcs_enable); + ISP_PRINTF("fcs_para->manual_fcs.fcs_gain_slop=%d\n", fcs_para->manual_fcs.fcs_gain_slop); + ISP_PRINTF("fcs_para->manual_fcs.fcs_th=%d\n", fcs_para->manual_fcs.fcs_th); + ISP_PRINTF("fcs_para->manual_fcs.fcs_uv_nr_enable=%d\n", fcs_para->manual_fcs.fcs_uv_nr_enable); + ISP_PRINTF("fcs_para->manual_fcs.fcs_uv_nr_th=%d\n", fcs_para->manual_fcs.fcs_uv_nr_th); + + for (i=0; i<9; i++) + { + ISP_PRINTF("fcs_para->linkage_fcs[%d].fcs_enable=%d\n", i, fcs_para->linkage_fcs[i].fcs_enable); + ISP_PRINTF("fcs_para->linkage_fcs[%d].fcs_gain_slop=%d\n", i, fcs_para->linkage_fcs[i].fcs_gain_slop); + ISP_PRINTF("fcs_para->linkage_fcs[%d].fcs_th=%d\n", i, fcs_para->linkage_fcs[i].fcs_th); + ISP_PRINTF("fcs_para->linkage_fcs[%d].fcs_uv_nr_enable=%d\n", i, fcs_para->linkage_fcs[i].fcs_uv_nr_enable); + ISP_PRINTF("fcs_para->linkage_fcs[%d].fcs_uv_nr_th=%d\n", i, fcs_para->linkage_fcs[i].fcs_uv_nr_th); + } + + return 0; +} + +//static int printk_fcs_para(AK_ISP_FCS_ATTR *fcs_para) +static int printk_contrast_para(AK_ISP_CONTRAST_ATTR *contrast) +{ + int i = 0; + + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("contrast->cc_mode=%d\n", contrast->cc_mode); + ISP_PRINTF("contrast->manual_contrast.y_contrast=%d\n", contrast->manual_contrast.y_contrast); + ISP_PRINTF("contrast->manual_contrast.y_shift=%d\n", contrast->manual_contrast.y_shift); + + for (i=0; i<9; i++) + { + ISP_PRINTF("contrast->linkage_contrast[%d].dark_pixel_area=%d\n", i ,contrast->linkage_contrast[i].dark_pixel_area); + ISP_PRINTF("contrast->linkage_contrast[%d].dark_pixel_rate=%d\n", i ,contrast->linkage_contrast[i].dark_pixel_rate); + ISP_PRINTF("contrast->linkage_contrast[%d].shift_max=%d\n", i ,contrast->linkage_contrast[i].shift_max); + } + + return 0; +} + +static int printk_satu_para(AK_ISP_SATURATION_ATTR *satu_para) +{ + int i = 0; + + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("satu_para->SE_mode=%d\n", satu_para->SE_mode); + ISP_PRINTF("satu_para->manual_sat.SE_enable=%d\n", satu_para->manual_sat.SE_enable); + ISP_PRINTF("satu_para->manual_sat.SE_th1=%d\n", satu_para->manual_sat.SE_th1); + ISP_PRINTF("satu_para->manual_sat.SE_th2=%d\n", satu_para->manual_sat.SE_th2); + ISP_PRINTF("satu_para->manual_sat.SE_th3=%d\n", satu_para->manual_sat.SE_th3); + ISP_PRINTF("satu_para->manual_sat.SE_th4=%d\n", satu_para->manual_sat.SE_th4); + ISP_PRINTF("satu_para->manual_sat.SE_scale_slop1=%d\n", satu_para->manual_sat.SE_scale_slop1); + ISP_PRINTF("satu_para->manual_sat.SE_scale_slop2=%d\n", satu_para->manual_sat.SE_scale_slop2); + ISP_PRINTF("satu_para->manual_sat.SE_scale1=%d\n", satu_para->manual_sat.SE_scale1); + ISP_PRINTF("satu_para->manual_sat.SE_scale2=%d\n", satu_para->manual_sat.SE_scale2); + ISP_PRINTF("satu_para->manual_sat.SE_scale3=%d\n", satu_para->manual_sat.SE_scale3); + + for (i=0; i<9; i++) + { + ISP_PRINTF("satu_para->linkage_sat[%d].SE_enable=%d\n", i, satu_para->linkage_sat[i].SE_enable); + ISP_PRINTF("satu_para->linkage_sat[%d].SE_th1=%d\n", i, satu_para->linkage_sat[i].SE_th1); + ISP_PRINTF("satu_para->linkage_sat[%d].SE_th2=%d\n", i, satu_para->linkage_sat[i].SE_th2); + ISP_PRINTF("satu_para->linkage_sat[%d].SE_th3=%d\n", i, satu_para->linkage_sat[i].SE_th3); + ISP_PRINTF("satu_para->linkage_sat[%d].SE_th4=%d\n", i, satu_para->linkage_sat[i].SE_th4); + ISP_PRINTF("satu_para->linkage_sat[%d].SE_scale_slop1=%d\n", i, satu_para->linkage_sat[i].SE_scale_slop1); + ISP_PRINTF("satu_para->linkage_sat[%d].SE_scale_slop2=%d\n", i, satu_para->linkage_sat[i].SE_scale_slop2); + ISP_PRINTF("satu_para->linkage_sat[%d].SE_scale1=%d\n", i, satu_para->linkage_sat[i].SE_scale1); + ISP_PRINTF("satu_para->linkage_sat[%d].SE_scale2=%d\n", i, satu_para->linkage_sat[i].SE_scale2); + ISP_PRINTF("satu_para->linkage_sat[%d].SE_scale3=%d\n", i, satu_para->linkage_sat[i].SE_scale3); + } + + return 0; +} + +static int printk_lsc(AK_ISP_LSC_ATTR *lsc_para) +{ + int i = 0; + + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("lsc_para->enable=%d\n", lsc_para->enable); + ISP_PRINTF("lsc_para->xref=%d\n", lsc_para->xref); + ISP_PRINTF("lsc_para->yref=%d\n", lsc_para->yref); + ISP_PRINTF("lsc_para->lsc_shift=%d\n", lsc_para->lsc_shift); + + for (i=0; i<10; i++) + { + ISP_PRINTF("lsc_para->range[%d]=%d\n", i, lsc_para->range[i]); + } + + for (i=0; i<10; i++) + { + ISP_PRINTF("lsc_para->lsc_r_coef.coef_b[%d]=%d\n", i, lsc_para->lsc_r_coef.coef_b[i]); + ISP_PRINTF("lsc_para->lsc_r_coef.coef_c[%d]=%d\n", i, lsc_para->lsc_r_coef.coef_c[i]); + } + + for (i=0; i<10; i++) + { + ISP_PRINTF("lsc_para->lsc_gr_coef.coef_b[%d]=%d\n", i, lsc_para->lsc_gr_coef.coef_b[i]); + ISP_PRINTF("lsc_para->lsc_gr_coef.coef_c[%d]=%d\n", i, lsc_para->lsc_gr_coef.coef_c[i]); + } + + for (i=0; i<10; i++) + { + ISP_PRINTF("lsc_para->lsc_gb_coef.coef_b[%d]=%d\n", i, lsc_para->lsc_gb_coef.coef_b[i]); + ISP_PRINTF("lsc_para->lsc_gb_coef.coef_c[%d]=%d\n", i, lsc_para->lsc_gb_coef.coef_c[i]); + } + + for (i=0; i<10; i++) + { + ISP_PRINTF("lsc_para->lsc_b_coef.coef_b[%d]=%d\n", i, lsc_para->lsc_b_coef.coef_b[i]); + ISP_PRINTF("lsc_para->lsc_b_coef.coef_c[%d]=%d\n", i, lsc_para->lsc_b_coef.coef_c[i]); + } + + return 0; +} + +static int printk_dpc(AK_ISP_DDPC_ATTR *dpc_para) +{ + int i = 0; + + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("dpc_para->ddpc_mode=%d\n", dpc_para->ddpc_mode); + ISP_PRINTF("dpc_para->manual_ddpc.ddpc_enable=%d\n", dpc_para->manual_ddpc.ddpc_enable); + ISP_PRINTF("dpc_para->manual_ddpc.ddpc_th=%d\n", dpc_para->manual_ddpc.ddpc_th); + ISP_PRINTF("dpc_para->manual_ddpc.white_dpc_enable=%d\n", dpc_para->manual_ddpc.white_dpc_enable); + ISP_PRINTF("dpc_para->manual_ddpc.black_dpc_enable=%d\n", dpc_para->manual_ddpc.black_dpc_enable); + + for (i=0; i<9; i++) + { + ISP_PRINTF("dpc_para->linkage_ddpc[%d].ddpc_enable=%d\n", i, dpc_para->linkage_ddpc[i].ddpc_enable); + ISP_PRINTF("dpc_para->linkage_ddpc[%d].ddpc_th=%d\n", i, dpc_para->linkage_ddpc[i].ddpc_th); + ISP_PRINTF("dpc_para->linkage_ddpc[%d].white_dpc_enable=%d\n", i, dpc_para->linkage_ddpc[i].white_dpc_enable); + ISP_PRINTF("dpc_para->linkage_ddpc[%d].black_dpc_enable=%d\n", i, dpc_para->linkage_ddpc[i].black_dpc_enable); + } + + return 0; +} + +static int printk_sharp_para(AK_ISP_SHARP_ATTR *sharp_para) +{ + int i = 0; + int j = 0; + + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("sharp_para.ysharp_mode=%d\n", sharp_para->ysharp_mode); + ISP_PRINTF("sharp_para->manual_sharp_attr.ysharp_enable=%d\n", sharp_para->manual_sharp_attr.ysharp_enable); + ISP_PRINTF("sharp_para->manual_sharp_attr.hf_hpf_k=%d\n", sharp_para->manual_sharp_attr.hf_hpf_k); + ISP_PRINTF("sharp_para->manual_sharp_attr.hf_hpf_shift=%d\n", sharp_para->manual_sharp_attr.hf_hpf_shift); + ISP_PRINTF("sharp_para->manual_sharp_attr.mf_hpf_k=%d\n", sharp_para->manual_sharp_attr.mf_hpf_k); + ISP_PRINTF("sharp_para->manual_sharp_attr.mf_hpf_shift=%d\n", sharp_para->manual_sharp_attr.mf_hpf_shift); + ISP_PRINTF("sharp_para->manual_sharp_attr.sharp_method=%d\n", sharp_para->manual_sharp_attr.sharp_method); + ISP_PRINTF("sharp_para->manual_sharp_attr.sharp_skin_detect_enable=%d\n", sharp_para->manual_sharp_attr.sharp_skin_detect_enable); + ISP_PRINTF("sharp_para->manual_sharp_attr.sharp_skin_gain_th=%d\n", sharp_para->manual_sharp_attr.sharp_skin_gain_th); + ISP_PRINTF("sharp_para->manual_sharp_attr.sharp_skin_gain_weaken%d\n", sharp_para->manual_sharp_attr.sharp_skin_gain_weaken); + + for (i=0; i<256; i++) + { + ISP_PRINTF("sharp_para->manual_sharp_attr.MF_HPF_LUT[%d]=%d\n", i, sharp_para->manual_sharp_attr.MF_HPF_LUT[i]); + } + + for (i=0; i<256; i++) + { + ISP_PRINTF("sharp_para->manual_sharp_attr.HF_HPF_LUT[%d]=%d\n", i, sharp_para->manual_sharp_attr.HF_HPF_LUT[i]); + } + + for (j=0; j<9; j++) + { + ISP_PRINTF("sharp_para->linkage_sharp_attr[%d].ysharp_enable=%d\n", j, sharp_para->linkage_sharp_attr[j].ysharp_enable); + ISP_PRINTF("sharp_para->linkage_sharp_attr[%d].hf_hpf_k=%d\n", j, sharp_para->linkage_sharp_attr[j].hf_hpf_k); + ISP_PRINTF("sharp_para->linkage_sharp_attr[%d].hf_hpf_shift=%d\n", j, sharp_para->linkage_sharp_attr[j].hf_hpf_shift); + ISP_PRINTF("sharp_para->linkage_sharp_attr[%d].mf_hpf_k=%d\n", j, sharp_para->linkage_sharp_attr[j].mf_hpf_k); + ISP_PRINTF("sharp_para->linkage_sharp_attr[%d].mf_hpf_shift=%d\n", j, sharp_para->linkage_sharp_attr[j].mf_hpf_shift); + ISP_PRINTF("sharp_para->linkage_sharp_attr[%d].sharp_method=%d\n", j, sharp_para->linkage_sharp_attr[j].sharp_method); + ISP_PRINTF("sharp_para->linkage_sharp_attr[%d].sharp_skin_detect_enable=%d\n", j, sharp_para->linkage_sharp_attr[j].sharp_skin_detect_enable); + ISP_PRINTF("sharp_para->linkage_sharp_attr[%d].sharp_skin_gain_th=%d\n", j, sharp_para->linkage_sharp_attr[j].sharp_skin_gain_th); + ISP_PRINTF("sharp_para->linkage_sharp_attr[%d].sharp_skin_gain_weaken=%d\n", j, sharp_para->linkage_sharp_attr[j].sharp_skin_gain_weaken); + + for (i=0; i<256; i++) + { + ISP_PRINTF("sharp_para->linkage_sharp_attr[%d].MF_HPF_LUT[%d]=%d\n", j, i, sharp_para->linkage_sharp_attr[j].MF_HPF_LUT[i]); + } + + for (i=0; i<256; i++) + { + ISP_PRINTF("sharp_para->linkage_sharp_attr[%d].HF_HPF_LUT[%d]=%d\n", j, i, sharp_para->linkage_sharp_attr[j].HF_HPF_LUT[i]); + } + } + + return 0; +} + +static int printk_sharp_ex_para(AK_ISP_SHARP_EX_ATTR *sharp_ex_para) +{ + int i = 0; + + ISP_PRINTF("\n\n\n"); + + for (i=0; i<3; i++) + { + ISP_PRINTF("sharp_ex_para->hf_HPF[%d]=%d\n", i, sharp_ex_para->hf_HPF[i]); + } + + for (i=0; i<6; i++) + { + ISP_PRINTF("sharp_ex_para->mf_HPF[%d]=%d\n", i, sharp_ex_para->mf_HPF[i]); + } + + ISP_PRINTF("sharp_ex_para->sharp_skin_max_th=%d\n", sharp_ex_para->sharp_skin_max_th); + ISP_PRINTF("sharp_ex_para->sharp_skin_min_th=%d\n", sharp_ex_para->sharp_skin_min_th); + ISP_PRINTF("sharp_ex_para->sharp_skin_y_max_th=%d\n", sharp_ex_para->sharp_skin_y_max_th); + ISP_PRINTF("sharp_ex_para->sharp_skin_y_min_th=%d\n", sharp_ex_para->sharp_skin_y_min_th); + ISP_PRINTF("sharp_ex_para->sharp_skin_v_max_th=%d\n", sharp_ex_para->sharp_skin_v_max_th); + ISP_PRINTF("sharp_ex_para->sharp_skin_v_min_th=%d\n", sharp_ex_para->sharp_skin_v_min_th); + + return 0; +} + +static int printk_nr_3d_para(AK_ISP_3D_NR_ATTR *nr_3d_para) +{ + int i = 0, j = 0; + + ISP_PRINTF("\n\n\n"); + //printk("size =%d\n",sizeof(AK_ISP_3D_NR_ATTR)); + + ISP_PRINTF("nr_3d_para->_3d_nr_mode=%d\n", nr_3d_para->_3d_nr_mode); + ISP_PRINTF("nr_3d_para->manual_3d_nr.uv_min_enable=%d\n", nr_3d_para->manual_3d_nr.uv_min_enable); + ISP_PRINTF("nr_3d_para->manual_3d_nr.tnr_y_enable=%d\n", nr_3d_para->manual_3d_nr.tnr_y_enable); + ISP_PRINTF("nr_3d_para->manual_3d_nr.tnr_uv_enable=%d\n", nr_3d_para->manual_3d_nr.tnr_uv_enable); + ISP_PRINTF("nr_3d_para->manual_3d_nr.updata_ref_y=%d\n", nr_3d_para->manual_3d_nr.updata_ref_y); + ISP_PRINTF("nr_3d_para->manual_3d_nr.updata_ref_uv=%d\n", nr_3d_para->manual_3d_nr.updata_ref_uv); + ISP_PRINTF("nr_3d_para->manual_3d_nr.tnr_refFrame_format=%d\n", nr_3d_para->manual_3d_nr.tnr_refFrame_format); + ISP_PRINTF("nr_3d_para->manual_3d_nr.y_2dnr_enable=%d\n", nr_3d_para->manual_3d_nr.y_2dnr_enable); + ISP_PRINTF("nr_3d_para->manual_3d_nr.uv_2dnr_enable=%d\n", nr_3d_para->manual_3d_nr.uv_2dnr_enable); + + ISP_PRINTF("nr_3d_para->manual_3d_nr.uvnr_k=%d\n", nr_3d_para->manual_3d_nr.uvnr_k); + ISP_PRINTF("nr_3d_para->manual_3d_nr.uvlp_k=%d\n", nr_3d_para->manual_3d_nr.uvlp_k); + ISP_PRINTF("nr_3d_para->manual_3d_nr.t_uv_k=%d\n", nr_3d_para->manual_3d_nr.t_uv_k); + ISP_PRINTF("nr_3d_para->manual_3d_nr.t_uv_minstep=%d\n", nr_3d_para->manual_3d_nr.t_uv_minstep); + ISP_PRINTF("nr_3d_para->manual_3d_nr.t_uv_mf_th1=%d\n", nr_3d_para->manual_3d_nr.t_uv_mf_th1); + ISP_PRINTF("nr_3d_para->manual_3d_nr.t_uv_mf_th2=%d\n", nr_3d_para->manual_3d_nr.t_uv_mf_th2); + ISP_PRINTF("nr_3d_para->manual_3d_nr.t_uv_diffth_k1=%d\n", nr_3d_para->manual_3d_nr.t_uv_diffth_k1); + ISP_PRINTF("nr_3d_para->manual_3d_nr.t_uv_diffth_k2=%d\n", nr_3d_para->manual_3d_nr.t_uv_diffth_k2); + ISP_PRINTF("nr_3d_para->manual_3d_nr.t_uv_diffth_slop=%d\n", nr_3d_para->manual_3d_nr.t_uv_diffth_slop); + ISP_PRINTF("nr_3d_para->manual_3d_nr.t_uv_mc_k=%d\n", nr_3d_para->manual_3d_nr.t_uv_mc_k); + ISP_PRINTF("nr_3d_para->manual_3d_nr.t_uv_ac_th=%d\n", nr_3d_para->manual_3d_nr.t_uv_ac_th); + + ISP_PRINTF("nr_3d_para->manual_3d_nr.ynr_calc_k=%d\n", nr_3d_para->manual_3d_nr.ynr_calc_k); + ISP_PRINTF("nr_3d_para->manual_3d_nr.ynr_k=%d\n", nr_3d_para->manual_3d_nr.ynr_k); + ISP_PRINTF("nr_3d_para->manual_3d_nr.ynr_diff_shift=%d\n", nr_3d_para->manual_3d_nr.ynr_diff_shift); + ISP_PRINTF("nr_3d_para->manual_3d_nr.ylp_k=%d\n", nr_3d_para->manual_3d_nr.ylp_k); + ISP_PRINTF("nr_3d_para->manual_3d_nr.t_y_th1=%d\n", nr_3d_para->manual_3d_nr.t_y_th1); + ISP_PRINTF("nr_3d_para->manual_3d_nr.t_y_k1=%d\n", nr_3d_para->manual_3d_nr.t_y_k1); + ISP_PRINTF("nr_3d_para->manual_3d_nr.t_y_k2=%d\n", nr_3d_para->manual_3d_nr.t_y_k2); + ISP_PRINTF("nr_3d_para->manual_3d_nr.t_y_kslop=%d\n", nr_3d_para->manual_3d_nr.t_y_kslop); + ISP_PRINTF("nr_3d_para->manual_3d_nr.t_y_minstep=%d\n", nr_3d_para->manual_3d_nr.t_y_minstep); + ISP_PRINTF("nr_3d_para->manual_3d_nr.t_y_mf_th1=%d\n", nr_3d_para->manual_3d_nr.t_y_mf_th1); + ISP_PRINTF("nr_3d_para->manual_3d_nr.t_y_mf_th2=%d\n", nr_3d_para->manual_3d_nr.t_y_mf_th2); + ISP_PRINTF("nr_3d_para->manual_3d_nr.t_y_diffth_k1=%d\n", nr_3d_para->manual_3d_nr.t_y_diffth_k1); + ISP_PRINTF("nr_3d_para->manual_3d_nr.t_y_diffth_k2=%d\n", nr_3d_para->manual_3d_nr.t_y_diffth_k2); + ISP_PRINTF("nr_3d_para->manual_3d_nr.t_y_diffth_slop=%d\n", nr_3d_para->manual_3d_nr.t_y_diffth_slop); + ISP_PRINTF("nr_3d_para->manual_3d_nr.t_y_mc_k=%d\n", nr_3d_para->manual_3d_nr.t_y_mc_k); + ISP_PRINTF("nr_3d_para->manual_3d_nr.t_y_ac_th=%d\n", nr_3d_para->manual_3d_nr.t_y_ac_th); + ISP_PRINTF("nr_3d_para->manual_3d_nr.md_th=%d\n", nr_3d_para->manual_3d_nr.md_th); + ISP_PRINTF("nr_3d_para->manual_3d_nr.tnr_t_y_ex_k_cfg=%d\n", nr_3d_para->manual_3d_nr.tnr_t_y_ex_k_cfg); + + for (j=0; j<17; j++) + { + ISP_PRINTF("nr_3d_para->manual_3d_nr.ynr_weight_tbl[%d]=%d\n", j, nr_3d_para->manual_3d_nr.ynr_weight_tbl[j]); + } + + + for (i=0; i<9; i++) + { + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].uv_min_enable=%d\n", i, nr_3d_para->linkage_3d_nr[i].uv_min_enable); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].tnr_y_enable=%d\n", i, nr_3d_para->linkage_3d_nr[i].tnr_y_enable); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].tnr_uv_enable=%d\n", i, nr_3d_para->linkage_3d_nr[i].tnr_uv_enable); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].updata_ref_y=%d\n", i, nr_3d_para->linkage_3d_nr[i].updata_ref_y); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].updata_ref_uv=%d\n", i, nr_3d_para->linkage_3d_nr[i].updata_ref_uv); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].tnr_refFrame_format=%d\n", i, nr_3d_para->linkage_3d_nr[i].tnr_refFrame_format); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].y_2dnr_enable=%d\n", i, nr_3d_para->linkage_3d_nr[i].y_2dnr_enable); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].uv_2dnr_enable=%d\n", i, nr_3d_para->linkage_3d_nr[i].uv_2dnr_enable); + + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].uvnr_k=%d\n", i, nr_3d_para->linkage_3d_nr[i].uvnr_k); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].uvlp_k=%d\n", i, nr_3d_para->linkage_3d_nr[i].uvlp_k); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].t_uv_k=%d\n", i, nr_3d_para->linkage_3d_nr[i].t_uv_k); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].t_uv_minstep=%d\n", i, nr_3d_para->linkage_3d_nr[i].t_uv_minstep); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].t_uv_mf_th1=%d\n", i, nr_3d_para->linkage_3d_nr[i].t_uv_mf_th1); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].t_uv_mf_th2=%d\n", i, nr_3d_para->linkage_3d_nr[i].t_uv_mf_th2); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].t_uv_diffth_k1=%d\n", i, nr_3d_para->linkage_3d_nr[i].t_uv_diffth_k1); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].t_uv_diffth_k2=%d\n", i, nr_3d_para->linkage_3d_nr[i].t_uv_diffth_k2); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].t_uv_diffth_slop=%d\n", i, nr_3d_para->linkage_3d_nr[i].t_uv_diffth_slop); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].t_uv_mc_k=%d\n", i, nr_3d_para->linkage_3d_nr[i].t_uv_mc_k); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].t_uv_ac_th=%d\n", i, nr_3d_para->linkage_3d_nr[i].t_uv_ac_th); + + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].ynr_calc_k=%d\n", i, nr_3d_para->linkage_3d_nr[i].ynr_calc_k); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].ynr_k=%d\n", i, nr_3d_para->linkage_3d_nr[i].ynr_k); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].ynr_diff_shift=%d\n", i, nr_3d_para->linkage_3d_nr[i].ynr_diff_shift); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].ylp_k=%d\n", i, nr_3d_para->linkage_3d_nr[i].ylp_k); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].t_y_th1=%d\n", i, nr_3d_para->linkage_3d_nr[i].t_y_th1); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].t_y_k1=%d\n", i, nr_3d_para->linkage_3d_nr[i].t_y_k1); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].t_y_k2=%d\n", i, nr_3d_para->linkage_3d_nr[i].t_y_k2); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].t_y_kslop=%d\n", i, nr_3d_para->linkage_3d_nr[i].t_y_kslop); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].t_y_minstep=%d\n", i, nr_3d_para->linkage_3d_nr[i].t_y_minstep); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].t_y_mf_th1=%d\n", i, nr_3d_para->linkage_3d_nr[i].t_y_mf_th1); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].t_y_mf_th2=%d\n", i, nr_3d_para->linkage_3d_nr[i].t_y_mf_th2); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].t_y_diffth_k1=%d\n", i, nr_3d_para->linkage_3d_nr[i].t_y_diffth_k1); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].t_y_diffth_k2=%d\n", i, nr_3d_para->linkage_3d_nr[i].t_y_diffth_k2); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].t_y_diffth_slop=%d\n", i, nr_3d_para->linkage_3d_nr[i].t_y_diffth_slop); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].t_y_mc_k=%d\n", i, nr_3d_para->linkage_3d_nr[i].t_y_mc_k); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].t_y_ac_th=%d\n", i, nr_3d_para->linkage_3d_nr[i].t_y_ac_th); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].md_th=%d\n", i, nr_3d_para->linkage_3d_nr[i].md_th); + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].tnr_t_y_ex_k_cfg=%d\n", i, nr_3d_para->linkage_3d_nr[i].tnr_t_y_ex_k_cfg); + + for (j=0; j<17; j++) + { + ISP_PRINTF("nr_3d_para->linkage_3d_nr[%d].ynr_weight_tbl[%d]=%d\n", i, j, nr_3d_para->linkage_3d_nr[i].ynr_weight_tbl[j]); + } + } + + return 0; +} + +static int printk_rgb2yuv_para(AK_ISP_RGB2YUV_ATTR *rgb2yuv_para) +{ + ISP_PRINTF("rgb2yuv_para->mode=%d\n", rgb2yuv_para->mode); + return 0; +} + +static int printk_effect_para(AK_ISP_EFFECT_ATTR *effect_para) +{ + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("effect_para->dark_margin_en=%d\n", effect_para->dark_margin_en); + ISP_PRINTF("effect_para->y_a=%d\n", effect_para->y_a); + ISP_PRINTF("effect_para->y_b=%d\n", effect_para->y_b); + ISP_PRINTF("effect_para->uv_a=%d\n", effect_para->uv_a); + ISP_PRINTF("effect_para->uv_b=%d\n", effect_para->uv_b); + + return 0; +} + +static int printk_frame_rate_para(AK_ISP_FRAME_RATE_ATTR *frame_rate_para) +{ + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("frame_rate_para->hight_light_frame_rate=%d\n", frame_rate_para->hight_light_frame_rate); + ISP_PRINTF("frame_rate_para->hight_light_max_exp_time=%d\n", frame_rate_para->hight_light_max_exp_time); + ISP_PRINTF("frame_rate_para->hight_light_to_low_light_gain=%d\n", frame_rate_para->hight_light_to_low_light_gain); + ISP_PRINTF("frame_rate_para->low_light_frame_rate=%d\n", frame_rate_para->low_light_frame_rate); + ISP_PRINTF("frame_rate_para->low_light_max_exp_time=%d\n", frame_rate_para->low_light_max_exp_time); + ISP_PRINTF("frame_rate_para->low_light_to_hight_light_gain=%d\n", frame_rate_para->low_light_to_hight_light_gain); + + return 0; +} + +static int printk_ae_para(AK_ISP_AE_ATTR *ae_para) +{ + int i = 0, j = 0; + + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("ae_para->exp_time_max=%d\n", (int)ae_para->exp_time_max); + ISP_PRINTF("ae_para->exp_time_min=%d\n", (int)ae_para->exp_time_min); + ISP_PRINTF("ae_para->d_gain_max=%d\n", (int)ae_para->d_gain_max); + ISP_PRINTF("ae_para->d_gain_min=%d\n", (int)ae_para->d_gain_min); + ISP_PRINTF("ae_para->isp_d_gain_max=%d\n", (int)ae_para->isp_d_gain_max); + ISP_PRINTF("ae_para->isp_d_gain_min=%d\n", (int)ae_para->isp_d_gain_min); + ISP_PRINTF("ae_para->a_gain_max=%d\n", (int)ae_para->a_gain_max); + ISP_PRINTF("ae_para->a_gain_min=%d\n", (int)ae_para->a_gain_min); + ISP_PRINTF("ae_para->exp_step=%d\n", (int)ae_para->exp_step); + ISP_PRINTF("ae_para->exp_stable_range=%d\n", (int)ae_para->exp_stable_range); + ISP_PRINTF("ae_para->target_lumiance=%d\n", (int)ae_para->target_lumiance); + ISP_PRINTF("ae_para->OE_suppress_en=%d\n", (int)ae_para->OE_suppress_en); + ISP_PRINTF("ae_para->OE_detect_scope=%d\n", (int)ae_para->OE_detect_scope); + ISP_PRINTF("ae_para->OE_rate_max=%d\n", (int)ae_para->OE_rate_max); + ISP_PRINTF("ae_para->OE_rate_min=%d\n", (int)ae_para->OE_rate_min); + + for (i=0; i<10; i++) + { + for (j=0; j<2; j++) + { + ISP_PRINTF("ae_para->envi_gain_range[%d][%d]=%d\n", i, j, (int)ae_para->envi_gain_range[i][j]); + } + } + + for (i=0; i<16; i++) + { + ISP_PRINTF("ae_para->hist_weight[%d]=%d\n", i, (int)ae_para->hist_weight[i]); + } + + return 0; +} + +static int printk_mae_para(AK_ISP_MAE_ATTR *mae_para) +{ + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("mae_para->exp_time=%d\n", (int)mae_para->exp_time); + ISP_PRINTF("mae_para->a_gain=%d\n", (int)mae_para->a_gain); + ISP_PRINTF("mae_para->d_gain=%d\n", (int)mae_para->d_gain); + ISP_PRINTF("mae_para->isp_d_gain=%d\n", (int)mae_para->isp_d_gain); + + return 0; +} + + +static int printk_exp_type_para(AK_ISP_EXP_TYPE *exp_type_para) +{ + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("exp_type_para->exp_type=%d\n", exp_type_para->exp_type); + return 0; +} + +static int printk_awb_para(AK_ISP_AWB_ATTR *awb_para) +{ + int i = 0; + + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("awb_para->auto_wb_step=%d\n", awb_para->auto_wb_step); + ISP_PRINTF("awb_para->colortemp_stable_cnt_thresh=%d\n", awb_para->colortemp_stable_cnt_thresh); + ISP_PRINTF("awb_para->total_cnt_thresh=%d\n", awb_para->total_cnt_thresh); + ISP_PRINTF("awb_para->y_high=%d\n", awb_para->y_high); + ISP_PRINTF("awb_para->y_low=%d\n", awb_para->y_low); + ISP_PRINTF("awb_para->err_est=%d\n", awb_para->err_est); + + for(i=0; i<10; i++) + { + ISP_PRINTF("awb_para->gr_low[%d]=%d\n", i, awb_para->gr_low[i]); + ISP_PRINTF("awb_para->gr_high[%d]=%d\n", i, awb_para->gr_high[i]); + ISP_PRINTF("awb_para->gb_low[%d]=%d\n", i, awb_para->gb_low[i]); + ISP_PRINTF("awb_para->gb_high[%d]=%d\n", i, awb_para->gb_high[i]); + ISP_PRINTF("awb_para->rb_low[%d]=%d\n", i, awb_para->rb_low[i]); + ISP_PRINTF("awb_para->rb_high[%d]=%d\n", i, awb_para->rb_high[i]); + } + + for(i=0; i<16; i++) + { + ISP_PRINTF("awb_para->g_weight[%d]=%d\n", i, awb_para->g_weight[i]); + } + + for(i=0; i<10; i++) + { + ISP_PRINTF("awb_para->colortemp_envi[%d]=%d\n", i, awb_para->colortemp_envi[i]); + } + + return 0; +} + +static int printk_awb_ex_para(AK_ISP_AWB_EX_ATTR *awb_ex_para) +{ + int i = 0; + + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("awb_ex_para->awb_ex_ctrl_enable=%d\n", awb_ex_para->awb_ex_ctrl_enable); + + for(i=0; i<10; i++) + { + ISP_PRINTF("awb_ex_para->awb_ctrl[%d].rgain_max=%d\n", i, awb_ex_para->awb_ctrl[i].rgain_max); + ISP_PRINTF("awb_ex_para->awb_ctrl[%d].rgain_min=%d\n", i, awb_ex_para->awb_ctrl[i].rgain_min); + ISP_PRINTF("awb_ex_para->awb_ctrl[%d].ggain_max=%d\n", i, awb_ex_para->awb_ctrl[i].ggain_max); + ISP_PRINTF("awb_ex_para->awb_ctrl[%d].ggain_min=%d\n", i, awb_ex_para->awb_ctrl[i].ggain_min); + ISP_PRINTF("awb_ex_para->awb_ctrl[%d].bgain_max=%d\n", i, awb_ex_para->awb_ctrl[i].bgain_max); + ISP_PRINTF("awb_ex_para->awb_ctrl[%d].bgain_min=%d\n", i, awb_ex_para->awb_ctrl[i].bgain_min); + ISP_PRINTF("awb_ex_para->awb_ctrl[%d].rgain_ex=%d\n", i, awb_ex_para->awb_ctrl[i].rgain_ex); + ISP_PRINTF("awb_ex_para->awb_ctrl[%d].bgain_ex=%d\n", i, awb_ex_para->awb_ctrl[i].bgain_ex); + } + + return 0; +} + +static int printk_wb_type_para(AK_ISP_WB_TYPE_ATTR *wb_type_para) +{ + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("wb_type_para->wb_type=%d\n",wb_type_para->wb_type); + return 0; +} + +static int printk_af_para(AK_ISP_AF_ATTR *af_para) +{ + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("af_para->af_th=%d\n", af_para->af_th); + ISP_PRINTF("af_para->af_win0_left=%d\n", af_para->af_win0_left); + ISP_PRINTF("af_para->af_win0_right=%d\n", af_para->af_win0_right); + ISP_PRINTF("af_para->af_win0_top=%d\n", af_para->af_win0_top); + ISP_PRINTF("af_para->af_win0_bottom=%d\n", af_para->af_win0_bottom); + + ISP_PRINTF("af_para->af_win1_left=%d\n", af_para->af_win1_left); + ISP_PRINTF("af_para->af_win1_right=%d\n", af_para->af_win1_right); + ISP_PRINTF("af_para->af_win1_top=%d\n", af_para->af_win1_top); + ISP_PRINTF("af_para->af_win1_bottom=%d\n", af_para->af_win1_bottom); + + ISP_PRINTF("af_para->af_win2_left=%d\n", af_para->af_win2_left); + ISP_PRINTF("af_para->af_win2_right=%d\n", af_para->af_win2_right); + ISP_PRINTF("af_para->af_win2_top=%d\n", af_para->af_win2_top); + ISP_PRINTF("af_para->af_win2_bottom=%d\n", af_para->af_win2_bottom); + + ISP_PRINTF("af_para->af_win3_left=%d\n", af_para->af_win3_left); + ISP_PRINTF("af_para->af_win3_right=%d\n", af_para->af_win3_right); + ISP_PRINTF("af_para->af_win3_top=%d\n", af_para->af_win3_top); + ISP_PRINTF("af_para->af_win3_bottom=%d\n", af_para->af_win3_bottom); + + ISP_PRINTF("af_para->af_win4_left=%d\n", af_para->af_win4_left); + ISP_PRINTF("af_para->af_win4_right=%d\n", af_para->af_win4_right); + ISP_PRINTF("af_para->af_win4_top=%d\n", af_para->af_win4_top); + ISP_PRINTF("af_para->af_win4_bottom=%d\n", af_para->af_win4_bottom); + + return 0; +} + +static int printk_weight_para(AK_ISP_WEIGHT_ATTR *weight_para) +{ + int i = 0; + int j = 0; + + ISP_PRINTF("\n\n\n"); + + for (i=0; i<8; i++) + { + for (j=0; j<16; j++) + { + ISP_PRINTF("weight_para->zone_weight[%d][%d]=%d\n", i, j, weight_para->zone_weight[i][j]); + } + } + + return 0; +} + +static int printk_mwb_para(AK_ISP_MWB_ATTR *mwb_para) +{ + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("mwb_para->r_gain=%d\n", mwb_para->r_gain); + ISP_PRINTF("mwb_para->g_gain=%d\n", mwb_para->g_gain); + ISP_PRINTF("mwb_para->b_gain=%d\n", mwb_para->b_gain); + ISP_PRINTF("mwb_para->r_offset=%d\n", mwb_para->r_offset); + ISP_PRINTF("mwb_para->g_offset=%d\n", mwb_para->g_offset); + ISP_PRINTF("mwb_para->b_offset=%d\n", mwb_para->b_offset); + + return 0; +} + +static int printk_main_chan_mask_area_para(AK_ISP_MAIN_CHAN_MASK_AREA_ATTR *mask_area_para) +{ + int i = 0; + + ISP_PRINTF("\n\n\n"); + + for (i=0; i<4; i++) + { + ISP_PRINTF("main_chan_mask_area_para->mask_area[%d].enable=%d\n", i, mask_area_para->mask_area[i].enable); + ISP_PRINTF("main_chan_mask_area_para->mask_area[%d].start_xpos=%d\n", i, mask_area_para->mask_area[i].start_xpos); + ISP_PRINTF("main_chan_mask_area_para->mask_area[%d].start_ypos=%d\n", i, mask_area_para->mask_area[i].start_ypos); + ISP_PRINTF("main_chan_mask_area_para->mask_area[%d].end_xpos=%d\n", i, mask_area_para->mask_area[i].end_xpos); + ISP_PRINTF("main_chan_mask_area_para->mask_area[%d].end_ypos=%d\n", i, mask_area_para->mask_area[i].end_ypos); + } + + return 0; +} + +static int printk_sub_chan_mask_area_para(AK_ISP_SUB_CHAN_MASK_AREA_ATTR *mask_area_para) +{ + int i = 0; + + ISP_PRINTF("\n\n\n"); + + for (i=0; i<4; i++) + { + ISP_PRINTF("sub_chan_mask_area_para->mask_area[%d].enable=%d\n", i, mask_area_para->mask_area[i].enable); + ISP_PRINTF("sub_chan_mask_area_para->mask_area[%d].start_xpos=%d\n", i, mask_area_para->mask_area[i].start_xpos); + ISP_PRINTF("sub_chan_mask_area_para->mask_area[%d].start_ypos=%d\n", i, mask_area_para->mask_area[i].start_ypos); + ISP_PRINTF("sub_chan_mask_area_para->mask_area[%d].end_xpos=%d\n", i, mask_area_para->mask_area[i].end_xpos); + ISP_PRINTF("sub_chan_mask_area_para->mask_area[%d].end_ypos=%d\n", i, mask_area_para->mask_area[i].end_ypos); + } + + return 0; +} + +static int printk_mask_color_para(AK_ISP_MASK_COLOR_ATTR *mask_color_para) +{ + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("mask_color_para->color_type=%d\n", mask_color_para->color_type); + ISP_PRINTF("mask_color_para->mk_alpha=%d\n", mask_color_para->mk_alpha); + ISP_PRINTF("mask_color_para->y_mk_color=%d\n", mask_color_para->y_mk_color); + ISP_PRINTF("mask_color_para->u_mk_color=%d\n", mask_color_para->u_mk_color); + ISP_PRINTF("mask_color_para->v_mk_color=%d\n", mask_color_para->v_mk_color); + + return 0; +} + +static int printk_af_stat_para(AK_ISP_AF_STAT_INFO *af_stat_para) +{ + int i = 0; + + ISP_PRINTF("\n\n\n"); + + for (i=0; i<5; i++) + { + ISP_PRINTF("af_stat_para->af_statics[%d]=%d\n", i, (int)af_stat_para->af_statics[i]); + } + + return 0; +} + +static int printk_awb_stat_info_para(AK_ISP_AWB_STAT_INFO *awb_stat_info) +{ + int i = 0; + + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("awb_stat_info->r_gain=%d\n", awb_stat_info->r_gain); + ISP_PRINTF("awb_stat_info->r_offset=%d\n", awb_stat_info->r_offset); + ISP_PRINTF("awb_stat_info->g_gain=%d\n", awb_stat_info->g_gain); + ISP_PRINTF("awb_stat_info->g_offset=%d\n", awb_stat_info->g_offset); + ISP_PRINTF("awb_stat_info->b_gain=%d\n", awb_stat_info->b_gain); + ISP_PRINTF("awb_stat_info->b_offset=%d\n", awb_stat_info->b_offset); + + ISP_PRINTF("awb_stat_info->current_colortemp_index=%d\n", awb_stat_info->current_colortemp_index); + + for(i=0; i<10; i++) + { + ISP_PRINTF("awb_stat_info->total_R[%d]=%d\n", i, (int)awb_stat_info->total_R[i]); + ISP_PRINTF("awb_stat_info->total_G[%d]=%d\n", i, (int)awb_stat_info->total_G[i]); + ISP_PRINTF("awb_stat_info->total_B[%d]=%d\n", i, (int)awb_stat_info->total_B[i]); + ISP_PRINTF("awb_stat_info->total_cnt[%d]=%d\n", i, (int)awb_stat_info->total_cnt[i]); + ISP_PRINTF("awb_stat_info->colortemp_stable_cnt[%d]=%d\n", i,(int)awb_stat_info->colortemp_stable_cnt[i]); + } + + return 0; +} + +static int printk_ae_run_para(AK_ISP_AE_RUN_INFO *ae_run_para) +{ + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("ae_run_para->current_a_gain=%d\n", (int)ae_run_para->current_a_gain); + ISP_PRINTF("ae_run_para->current_a_gain_step=%d\n", (int)ae_run_para->current_a_gain_step); + ISP_PRINTF("ae_run_para->current_exp_time=%d\n", (int)ae_run_para->current_exp_time); + ISP_PRINTF("ae_run_para->current_exp_time_step=%d\n", (int)ae_run_para->current_exp_time_step); + ISP_PRINTF("ae_run_para->current_d_gain=%d\n", (int)ae_run_para->current_d_gain); + ISP_PRINTF("ae_run_para->current_d_gain_step=%d\n", (int)ae_run_para->current_d_gain_step); + ISP_PRINTF("ae_run_para->current_isp_d_gain=%d\n", (int)ae_run_para->current_isp_d_gain); + ISP_PRINTF("ae_run_para->current_isp_d_gain_step=%d\n", (int)ae_run_para->current_isp_d_gain_step); + ISP_PRINTF("ae_run_para->current_calc_avg_lumi=%d\n", ae_run_para->current_calc_avg_lumi); + ISP_PRINTF("ae_run_para->current_calc_avg_compensation_lumi=%d\n", ae_run_para->current_calc_avg_compensation_lumi); + ISP_PRINTF("ae_run_para->current_darked_flag=%d\n ", ae_run_para->current_darked_flag); + + return 0; +} + +static int printk_raw_hist_stat_info(AK_ISP_RAW_HIST_STAT_INFO *raw_hist_stat_info) +{ + int i = 0; + + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("raw_g_total=%d\n", (int)raw_hist_stat_info->raw_g_total); + + for (i=0; i<256; i++) + { + ISP_PRINTF("raw_g_hist[%d] = %d\n", i, (int)raw_hist_stat_info->raw_g_hist[i]); + } + + return 0; +} + +static int printk_rgb_hist_stat_info(AK_ISP_RGB_HIST_STAT_INFO *rgb_hist_stat_info) +{ + int i = 0; + + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("rgb_total=%d\n", (int)rgb_hist_stat_info->rgb_total); + + for (i=0; i<256; i++) + { + ISP_PRINTF("rgb_hist[%d] = %d\n", i, (int)rgb_hist_stat_info->rgb_hist[i]); + } + + return 0; +} + +static int printk_yuv_hist_stat_info(AK_ISP_YUV_HIST_STAT_INFO *yuv_hist_stat_info) +{ + int i = 0; + + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("y_total=%d\n", (int)yuv_hist_stat_info->y_total); + + for (i=0; i<256; i++) + { + ISP_PRINTF("y_hist[%d] = %d\n", i, (int)yuv_hist_stat_info->y_hist[i]); + } + + return 0; +} + +static int printk_raw_hist_para(AK_ISP_RAW_HIST_ATTR *raw_hist_para) +{ + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("raw_hist_para->enable=%d\n", raw_hist_para->enable); + return 0; +} + +static int printk_rgb_hist_para(AK_ISP_RGB_HIST_ATTR *rgb_hist_para) +{ + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("rgb_hist_para->enable=%d\n", rgb_hist_para->enable); + return 0; +} + +static int printk_yuv_hist_para(AK_ISP_YUV_HIST_ATTR *yuv_hist_para) +{ + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("yuv_hist_para->enable=%d\n", yuv_hist_para->enable); + return 0; +} + +static int printk_3d_nr_ref(AK_ISP_3D_NR_REF_ATTR *ref_para) +{ + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("ref_para->yaddr_3d=%d\n", (int)ref_para->yaddr_3d); + ISP_PRINTF("ref_para->ysize_3d=%d\n", (int)ref_para->ysize_3d); + ISP_PRINTF("ref_para->uaddr_3d=%d\n", (int)ref_para->uaddr_3d); + ISP_PRINTF("ref_para->usize_3d=%d\n", (int)ref_para->usize_3d); + ISP_PRINTF("ref_para->vaddr_3d=%d\n", (int)ref_para->vaddr_3d); + ISP_PRINTF("ref_para->vsize_3d=%d\n", (int)ref_para->vsize_3d); + + return 0; +} + +static int printk_3d_nr_stat_info(AK_ISP_3D_NR_STAT_INFO *nr_3d_stat_info) +{ + int i = 0, j = 0; + + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("nr_3d_stat_info->MD_stat_max=%d\n", nr_3d_stat_info->MD_stat_max); + ISP_PRINTF("nr_3d_stat_info->MD_level=%d\n", nr_3d_stat_info->MD_level); + + for(i=0; i<16; i++) + { + for(j=0; j<32; j++) + { + ISP_PRINTF("nr_3d_stat_info->MD_stat[%d][%d]=%d\n", i, j, nr_3d_stat_info->MD_stat[i][j]); + } + } + + return 0; +} + + +static int printk_misc_para(AK_ISP_MISC_ATTR *misc_para) +{ + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("misc_para->hsyn_pol=%d\n", misc_para->hsyn_pol); + ISP_PRINTF("misc_para->vsync_pol=%d\n", misc_para->vsync_pol); + ISP_PRINTF("misc_para->pclk_pol=%d\n", misc_para->pclk_pol); + ISP_PRINTF("misc_para->test_pattern_en=%d\n", misc_para->test_pattern_en); + ISP_PRINTF("misc_para->test_pattern_cfg=%d\n", misc_para->test_pattern_cfg); + ISP_PRINTF("misc_para->cfa_mode=%d\n", misc_para->cfa_mode); + ISP_PRINTF("misc_para->inputdataw=%d\n", misc_para->inputdataw); + ISP_PRINTF("misc_para->one_line_cycle=%d\n", misc_para->one_line_cycle); + ISP_PRINTF("misc_para->hblank_cycle=%d\n", misc_para->hblank_cycle); + ISP_PRINTF("misc_para->frame_start_delay_en=%d\n", misc_para->frame_start_delay_en); + ISP_PRINTF("misc_para->frame_start_delay_num=%d\n", misc_para->frame_start_delay_num); + ISP_PRINTF("misc_para->flip_en=%d\n", misc_para->flip_en); + ISP_PRINTF("misc_para->mirror_en=%d\n", misc_para->mirror_en); + ISP_PRINTF("misc_para->twoframe_merge_en=%d\n", misc_para->twoframe_merge_en); + ISP_PRINTF("misc_para->mipi_line_end_sel=%d\n", misc_para->mipi_line_end_sel); + ISP_PRINTF("misc_para->mipi_line_end_cnt_en_cfg=%d\n", misc_para->mipi_line_end_cnt_en_cfg); + ISP_PRINTF("misc_para->mipi_count_time=%d\n", misc_para->mipi_count_time); + + return 0; +} + + +static int printk_y_gamma_para(AK_ISP_Y_GAMMA_ATTR *y_gamma_para) +{ + int i = 0; + + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("y_gamma_para->ygamma_uv_adjust_enable=%d\n", y_gamma_para->ygamma_uv_adjust_enable); + ISP_PRINTF("y_gamma_para->ygamma_uv_adjust_level=%d\n", y_gamma_para->ygamma_uv_adjust_level); + ISP_PRINTF("y_gamma_para->ygamma_cnoise_yth1=%d\n", y_gamma_para->ygamma_cnoise_yth1); + ISP_PRINTF("y_gamma_para->ygamma_cnoise_yth2=%d\n", y_gamma_para->ygamma_cnoise_yth2); + ISP_PRINTF("y_gamma_para->ygamma_cnoise_slop=%d\n", y_gamma_para->ygamma_cnoise_slop); + ISP_PRINTF("y_gamma_para->ygamma_cnoise_gain=%d\n", y_gamma_para->ygamma_cnoise_gain); + + for (i=0; i<129; i++) + { + ISP_PRINTF("y_gamma_para->ygamma[%d]=%d\n", i, y_gamma_para->ygamma[i]); + } + + return 0; +} + + +static int printk_hue_para(AK_ISP_HUE_ATTR *hue_para) +{ + int i = 0, j = 0; + + ISP_PRINTF("\n\n\n"); + ISP_PRINTF("hue_para->hue_mode=%d\n", hue_para->hue_mode); + ISP_PRINTF("hue_para->manual_hue.hue_sat_en=%d\n", hue_para->manual_hue.hue_sat_en); + + for(i=0; i<65; i++) + { + ISP_PRINTF("hue_para->manual_hue.hue_lut_a[%d]=%d\n", i, hue_para->manual_hue.hue_lut_a[i]); + } + + for(i=0; i<65; i++) + { + ISP_PRINTF("hue_para->manual_hue.hue_lut_b[%d]=%d\n", i, hue_para->manual_hue.hue_lut_b[i]); + } + + for(i=0; i<65; i++) + { + ISP_PRINTF("hue_para->manual_hue.hue_lut_s[%d]=%d\n", i, hue_para->manual_hue.hue_lut_s[i]); + } + + for(j=0; j<4; j++) + { + ISP_PRINTF("hue_para->hue[%d].hue_sat_en=%d\n", j, hue_para->hue[j].hue_sat_en); + + for(i=0; i<65; i++) + { + ISP_PRINTF("hue_para->hue[%d].hue_lut_a[%d]=%d\n", j, i, hue_para->hue[j].hue_lut_a[i]); + } + + for(i=0; i<65; i++) + { + ISP_PRINTF("hue_para->hue[%d].hue_lut_b[%d]=%d\n", j, i, hue_para->hue[j].hue_lut_b[i]); + } + + for(i=0; i<65; i++) + { + ISP_PRINTF("hue_para->hue[%d].hue_lut_s[%d]=%d\n", j, i, hue_para->hue[j].hue_lut_s[i]); + } + } + + return 0; +} + + +static int ak_isp_set_user_params_do(AK_ISP_USER_PARAM *param) +{ + int ret = 0; + + switch (param->id) + { + case AK_ISP_USER_CID_SET_ZOOM: + break; + + case AK_ISP_USER_CID_SET_SUB_CHANNEL: + { + struct isp_channel2_info *sub = (void *)param->data; + ret = ak_isp_vo_set_sub_channel_scale(sub->width, sub->height); + } + break; + + case AK_ISP_USER_CID_SET_OCCLUSION: + break; + case AK_ISP_USER_CID_SET_OCCLUSION_COLOR: + break; + case AK_ISP_USER_CID_SET_GAMMA: + break; + case AK_ISP_USER_CID_SET_SATURATION: + break; + case AK_ISP_USER_CID_SET_BRIGHTNESS: + break; + case AK_ISP_USER_CID_SET_CONTRAST: + break; + case AK_ISP_USER_CID_SET_SHARPNESS: + break; + case AK_ISP_USER_CID_SET_POWER_LINE_FREQUENCY: + break; + case AK_ISP_USER_CID_SET_OSD_COLOR_TABLE_ATTR: + { + struct isp_osd_color_table_attr *color_table = (void *)param->data; + AK_ISP_OSD_COLOR_TABLE_ATTR *isp_color_table = kzalloc(sizeof(AK_ISP_OSD_COLOR_TABLE_ATTR), GFP_KERNEL); + if (!isp_color_table) { + printk("kzalloc for isp_color_table failed\n"); + ret = -ENOMEM; + goto out; + } + memcpy(isp_color_table, color_table, sizeof(AK_ISP_OSD_COLOR_TABLE_ATTR)); + ret = ak_isp_vpp_set_osd_color_table_attr(isp_color_table); + + kfree(isp_color_table); + } + break; + case AK_ISP_USER_CID_SET_MAIN_CHANNEL_OSD_CONTEXT_ATTR: + { + struct isp_osd_context_attr *user_context = (void *)param->data; + AK_ISP_OSD_CONTEXT_ATTR isp_osd_context; + int osd_byte_size = user_context->osd_width * user_context->osd_height / 2; + int chn = user_context->chn; + struct akisp_osd_info *p_osd_info = &priv->osd_info[chn]; + + if(user_context->enable){ + if (!p_osd_info->main_osd_vaddr || + p_osd_info->main_osd_paddr != user_context->osd_context_addr || + p_osd_info->main_osd_byte_size != osd_byte_size) { + if (p_osd_info->main_osd_vaddr) { + iounmap(p_osd_info->main_osd_vaddr); + printk(KERN_ERR "MAINCHN%d iounmap %p %p size:%d\n", + chn, p_osd_info->main_osd_vaddr, p_osd_info->main_osd_paddr, + p_osd_info->main_osd_byte_size); + p_osd_info->main_osd_vaddr = NULL; + p_osd_info->main_osd_paddr = NULL; + p_osd_info->main_osd_byte_size = 0; + } + + isp_osd_context.osd_context_addr = ioremap_nocache( + (unsigned long)user_context->osd_context_addr, osd_byte_size); + if (!isp_osd_context.osd_context_addr) { + printk(KERN_ERR "osd_context_addr fail\n"); + ret = -EINVAL; + goto out; + } + + p_osd_info->main_osd_vaddr = isp_osd_context.osd_context_addr; + p_osd_info->main_osd_paddr = user_context->osd_context_addr; + p_osd_info->main_osd_byte_size = osd_byte_size; + + printk(KERN_ERR "MAINCHN%d iomap %p %p size:%d\n", + chn, p_osd_info->main_osd_vaddr, p_osd_info->main_osd_paddr, + p_osd_info->main_osd_byte_size); + } else { + isp_osd_context.osd_context_addr = p_osd_info->main_osd_vaddr; + } + } else { + if (p_osd_info->main_osd_vaddr) { + iounmap(p_osd_info->main_osd_vaddr); + printk(KERN_ERR "MAINCHN%d iounmap %p %p size:%d\n", + chn, p_osd_info->main_osd_vaddr, p_osd_info->main_osd_paddr, + p_osd_info->main_osd_byte_size); + p_osd_info->main_osd_vaddr = NULL; + p_osd_info->main_osd_paddr = NULL; + p_osd_info->main_osd_byte_size = 0; + } + isp_osd_context.osd_context_addr = (T_U32 *)0; + } + isp_osd_context.chn = chn; + isp_osd_context.osd_width = user_context->osd_width; + isp_osd_context.osd_height = user_context->osd_height; + isp_osd_context.start_xpos = user_context->start_xpos; + isp_osd_context.start_ypos = user_context->start_ypos; + isp_osd_context.alpha = user_context->alpha; + isp_osd_context.enable = user_context->enable; + + /* + printk(KERN_ERR "%s %d osd width: %d, height: %d, start_xpos: %d," + "start_ypos: %d, alpha: %d\n", __func__, __LINE__, + user_context->osd_width, user_context->osd_height, + user_context->start_xpos, user_context->start_ypos, + user_context->alpha); + */ + ret = ak_isp_vpp_set_main_channel_osd_context_attr(&isp_osd_context); +#if 0 + if(0 == ret) + while (ak_isp_vpp_mainchn_osdmem_useok()) + msleep(50); + if (isp_osd_context.osd_context_addr) + iounmap(isp_osd_context.osd_context_addr); +#endif + + } + break; + case AK_ISP_USER_CID_SET_SUB_CHANNEL_OSD_CONTEXT_ATTR: + { + struct isp_osd_context_attr *user_context = (void *)param->data; + AK_ISP_OSD_CONTEXT_ATTR isp_osd_context ; + int osd_byte_size = user_context->osd_width * user_context->osd_height / 2; + int chn = user_context->chn; + struct akisp_osd_info *p_osd_info = &priv->osd_info[chn]; + + if(user_context->enable){ + if (!p_osd_info->sub_osd_vaddr || + p_osd_info->sub_osd_paddr != user_context->osd_context_addr || + p_osd_info->sub_osd_byte_size != osd_byte_size) { + + if (p_osd_info->sub_osd_vaddr) { + iounmap(p_osd_info->sub_osd_vaddr); + printk(KERN_ERR "SUBCHN%d iounmap %p %p size:%d\n", + chn, p_osd_info->sub_osd_vaddr, p_osd_info->sub_osd_paddr, + p_osd_info->sub_osd_byte_size); + p_osd_info->sub_osd_vaddr = NULL; + p_osd_info->sub_osd_paddr = NULL; + p_osd_info->sub_osd_byte_size = 0; + } + + isp_osd_context.osd_context_addr = ioremap_nocache( + (unsigned long) user_context->osd_context_addr, osd_byte_size); + if (!isp_osd_context.osd_context_addr) { + printk(KERN_ERR "osd_context_addr failed\n"); + ret = -EINVAL; + goto out; + } + + p_osd_info->sub_osd_vaddr = isp_osd_context.osd_context_addr; + p_osd_info->sub_osd_paddr = user_context->osd_context_addr; + p_osd_info->sub_osd_byte_size = osd_byte_size; + + printk(KERN_ERR "SUBCHN%d iomap %p %p size:%d\n", + chn, p_osd_info->sub_osd_vaddr, p_osd_info->sub_osd_paddr, + p_osd_info->sub_osd_byte_size); + } else { + isp_osd_context.osd_context_addr = p_osd_info->sub_osd_vaddr; + } + } else { + if (p_osd_info->sub_osd_vaddr) { + iounmap(p_osd_info->sub_osd_vaddr); + printk(KERN_ERR "SUBCHN%d iounmap %p %p size:%d\n", + chn, p_osd_info->sub_osd_vaddr, p_osd_info->sub_osd_paddr, + p_osd_info->sub_osd_byte_size); + p_osd_info->sub_osd_vaddr = NULL; + p_osd_info->sub_osd_paddr = NULL; + p_osd_info->sub_osd_byte_size = 0; + } + isp_osd_context.osd_context_addr = (T_U32 *)0; + } + isp_osd_context.chn = user_context->chn; + isp_osd_context.osd_width = user_context->osd_width; + isp_osd_context.osd_height = user_context->osd_height; + isp_osd_context.start_xpos = user_context->start_xpos; + isp_osd_context.start_ypos = user_context->start_ypos; + isp_osd_context.alpha = user_context->alpha; + isp_osd_context.enable = user_context->enable; + + ret = ak_isp_vpp_set_sub_channel_osd_context_attr(&isp_osd_context); +#if 0 + if(0 == ret) + while (ak_isp_vpp_subchn_osdmem_useok()) + msleep(50); + if (isp_osd_context.osd_context_addr) + iounmap(isp_osd_context.osd_context_addr); +#endif + + } + break; + case AK_ISP_USER_CID_SET_MAIN_CHANNEL_OSD_MEM_ATTR: + { + struct isp_osd_mem_attr *osd_mem = (void *)param->data; + int chn = osd_mem->chn; + AK_ISP_OSD_MEM_ATTR *isp_osd_mem = &priv->osd_info[chn].main_osd_irq_dma; + AK_ISP_OSD_MEM_ATTR tmp = {0}; + + printk("%s %d, set main osd mem attr\n", __func__, __LINE__); + /* save old param */ + memcpy(&tmp, isp_osd_mem, sizeof(AK_ISP_OSD_MEM_ATTR)); + + /* wait isp use old mem finish, than we can modify it */ + while (ak_isp_vpp_mainchn_osdmem_useok()) + msleep(5); + + isp_osd_mem->chn = osd_mem->chn; + isp_osd_mem->dma_paddr = osd_mem->dma_paddr; + isp_osd_mem->dma_vaddr = ioremap_nocache((unsigned long)osd_mem->dma_paddr, + osd_mem->size); + isp_osd_mem->size = osd_mem->size; + + printk("%s %d chn:%d, paddr:%p size:%d \n", + __func__, __LINE__, osd_mem->chn, osd_mem->dma_paddr, osd_mem->size); + + /* set mem addr to isp */ + ret = ak_isp_vpp_set_main_channel_osd_mem_attr(isp_osd_mem); + if (!ret && tmp.dma_vaddr) { + printk("%s %d, release old memory\n", __func__, __LINE__); + iounmap(tmp.dma_vaddr); + + } else if (ret) { + printk("%s %d, set main failed, just restore mem\n", __func__, __LINE__); + if (isp_osd_mem->dma_vaddr) + iounmap(isp_osd_mem->dma_vaddr); + memcpy(isp_osd_mem, &tmp, sizeof(AK_ISP_OSD_MEM_ATTR)); + } + } + break; + case AK_ISP_USER_CID_SET_SUB_CHANNEL_OSD_MEM_ATTR: + { + struct isp_osd_mem_attr *osd_mem = (void *)param->data; + int chn = osd_mem->chn; + AK_ISP_OSD_MEM_ATTR *isp_osd_mem = &priv->osd_info[chn].sub_osd_irq_dma; + AK_ISP_OSD_MEM_ATTR tmp = {0}; + + /* save old param */ + memcpy(&tmp, isp_osd_mem, sizeof(AK_ISP_OSD_MEM_ATTR)); + + /* wait isp use old mem finish, than we can modify it */ + while (ak_isp_vpp_subchn_osdmem_useok()) + msleep(5); + + isp_osd_mem->chn = osd_mem->chn; + isp_osd_mem->dma_paddr = osd_mem->dma_paddr; + isp_osd_mem->dma_vaddr = ioremap_nocache((unsigned long)osd_mem->dma_paddr, + osd_mem->size); + isp_osd_mem->size = osd_mem->size; + + printk("%s %d paddr:%p size:%d \n", __func__, __LINE__, + osd_mem->dma_paddr,osd_mem->size); + + ret = ak_isp_vpp_set_sub_channel_osd_mem_attr(isp_osd_mem); + if (!ret && tmp.dma_vaddr) { + iounmap(tmp.dma_vaddr); + } else if (ret) { + printk("%s %d, set sub failed, just restore mem\n", __func__, __LINE__); + if (isp_osd_mem->dma_vaddr) + iounmap(isp_osd_mem->dma_vaddr); + memcpy(isp_osd_mem, &tmp, sizeof(AK_ISP_OSD_MEM_ATTR)); + } + } + break; + + default: + ret = -EINVAL; + break; + } + +out: + return ret; +} + +static long akisp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + long ret = 0; + + mutex_lock(&priv->lock); + switch(cmd) + { + case AK_ISP_VP_SET_BLC: + { + AK_ISP_BLC_ATTR *blc_para; + blc_para = kmalloc(sizeof(AK_ISP_BLC_ATTR),GFP_KERNEL); + if (!blc_para){ + ret = -ENOMEM; + goto fini; + } + memset(blc_para,0,sizeof(AK_ISP_BLC_ATTR)); + if(copy_from_user(blc_para,(AK_ISP_BLC_ATTR *)arg, sizeof(AK_ISP_BLC_ATTR))) + { + ret = -EFAULT; + kfree(blc_para); + goto fini; + } + ak_isp_vp_set_blc_attr(blc_para); + printk_blc(blc_para); + kfree(blc_para); + } + break; + case AK_ISP_VP_GET_BLC: + { + AK_ISP_BLC_ATTR *blc_para; + blc_para = kmalloc(sizeof(AK_ISP_BLC_ATTR),GFP_KERNEL); + if (!blc_para){ + ret = -ENOMEM; + goto fini; + } + memset(blc_para,0,sizeof(AK_ISP_BLC_ATTR)); + ak_isp_vp_get_blc_attr(blc_para); + if(copy_to_user((AK_ISP_BLC_ATTR *)arg,blc_para, sizeof(AK_ISP_BLC_ATTR))){ + ret = -EFAULT; + kfree(blc_para); + goto fini; + } + printk_blc(blc_para); + kfree(blc_para); + } + break; + + case AK_ISP_VP_SET_LSC: + { + AK_ISP_LSC_ATTR *lsc_para; + lsc_para = kmalloc(sizeof(AK_ISP_LSC_ATTR),GFP_KERNEL); + if (!lsc_para){ + ret = -ENOMEM; + goto fini; + } + memset(lsc_para,0,sizeof(AK_ISP_LSC_ATTR)); + if(copy_from_user(lsc_para,(AK_ISP_LSC_ATTR *)arg, sizeof(AK_ISP_LSC_ATTR))){ + ret = -EFAULT; + kfree(lsc_para); + goto fini; + } + ak_isp_vp_set_lsc_attr(lsc_para); + printk_lsc(lsc_para); + kfree(lsc_para); + } + break; + case AK_ISP_VP_GET_LSC: + { + AK_ISP_LSC_ATTR *lsc_para; + lsc_para = kmalloc(sizeof(AK_ISP_LSC_ATTR),GFP_KERNEL); + if (!lsc_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_lsc_attr(lsc_para); + if(copy_to_user((AK_ISP_LSC_ATTR *)arg,lsc_para,sizeof(AK_ISP_LSC_ATTR))){ + ret = -EFAULT; + kfree(lsc_para); + goto fini; + } + printk_lsc(lsc_para); + kfree(lsc_para); + } + break; + + case AK_ISP_VP_SET_GB: + { + AK_ISP_GB_ATTR *gb_para; + gb_para = kmalloc(sizeof(AK_ISP_GB_ATTR),GFP_KERNEL); + if (!gb_para){ + ret = -ENOMEM; + goto fini; + } + memset(gb_para,0,sizeof(AK_ISP_GB_ATTR)); + if(copy_from_user(gb_para,(AK_ISP_GB_ATTR *)arg, sizeof(AK_ISP_GB_ATTR))){ + ret = -EFAULT; + kfree(gb_para); + goto fini; + } + ak_isp_vp_set_gb_attr(gb_para); + printk_gb(gb_para); + kfree(gb_para); + } + break; + case AK_ISP_VP_GET_GB: + { + AK_ISP_GB_ATTR *gb_para; + gb_para = kmalloc(sizeof(AK_ISP_GB_ATTR),GFP_KERNEL); + if (!gb_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_gb_attr(gb_para); + if(copy_to_user((AK_ISP_GB_ATTR *)arg,gb_para,sizeof(AK_ISP_GB_ATTR))){ + ret = -EFAULT; + kfree(gb_para); + goto fini; + } + printk_gb(gb_para); + kfree(gb_para); + } + break; + + case AK_ISP_VP_SET_RAW_LUT: + { + AK_ISP_RAW_LUT_ATTR *raw_lut_para; + raw_lut_para = kmalloc(sizeof(AK_ISP_RAW_LUT_ATTR),GFP_KERNEL); + if (!raw_lut_para){ + ret = -ENOMEM; + goto fini; + } + memset(raw_lut_para,0,sizeof(AK_ISP_RAW_LUT_ATTR)); + if(copy_from_user(raw_lut_para,(AK_ISP_RAW_LUT_ATTR *)arg, sizeof(AK_ISP_RAW_LUT_ATTR))){ + ret = -EFAULT; + kfree(raw_lut_para); + goto fini; + } + ak_isp_vp_set_raw_lut_attr(raw_lut_para); + printk_raw_lut(raw_lut_para); + kfree(raw_lut_para); + } + break; + case AK_ISP_VP_GET_RAW_LUT: + { + AK_ISP_RAW_LUT_ATTR *raw_lut_para; + raw_lut_para = kmalloc(sizeof(AK_ISP_RAW_LUT_ATTR),GFP_KERNEL); + if (!raw_lut_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_raw_lut_attr(raw_lut_para); + if(copy_to_user((AK_ISP_RAW_LUT_ATTR *)arg,raw_lut_para, sizeof(AK_ISP_RAW_LUT_ATTR))){ + ret = -EFAULT; + kfree(raw_lut_para); + goto fini; + } + printk_raw_lut(raw_lut_para); + kfree(raw_lut_para); + } + break; + + case AK_ISP_VP_SET_RAW_NR1: + { + AK_ISP_NR1_ATTR *nr1_para; + nr1_para = kmalloc(sizeof(AK_ISP_NR1_ATTR),GFP_KERNEL); + if (!nr1_para){ + ret = -ENOMEM; + goto fini; + } + memset(nr1_para,0,sizeof(AK_ISP_NR1_ATTR)); + if(copy_from_user(nr1_para,(AK_ISP_NR1_ATTR *)arg, sizeof(AK_ISP_NR1_ATTR))){ + ret = -EFAULT; + kfree(nr1_para); + goto fini; + } + ak_isp_vp_set_nr1_attr(nr1_para); + printk_nr1_para(nr1_para); + kfree(nr1_para); + } + break; + case AK_ISP_VP_GET_RAW_NR1: + { + AK_ISP_NR1_ATTR *nr1_para; + nr1_para = kmalloc(sizeof(AK_ISP_NR1_ATTR),GFP_KERNEL); + if (!nr1_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_nr1_attr(nr1_para); + if(copy_to_user((AK_ISP_NR1_ATTR *)arg, nr1_para, sizeof(AK_ISP_NR1_ATTR))){ + ret = -EFAULT; + kfree(nr1_para); + goto fini; + } + printk_nr1_para(nr1_para); + kfree(nr1_para); + } + break; + + case AK_ISP_VP_SET_DEMO: + { + AK_ISP_DEMO_ATTR *demo_para; + demo_para = kmalloc(sizeof(AK_ISP_DEMO_ATTR),GFP_KERNEL); + if (!demo_para){ + ret = -ENOMEM; + goto fini; + } + memset(demo_para,0,sizeof(AK_ISP_DEMO_ATTR)); + if(copy_from_user(demo_para,(AK_ISP_DEMO_ATTR *)arg, sizeof(AK_ISP_DEMO_ATTR))){ + ret = -EFAULT; + kfree(demo_para); + goto fini; + } + ak_isp_vp_set_demo_attr(demo_para); + printk_demo_para(demo_para); + kfree(demo_para); + } + break; + case AK_ISP_VP_GET_DEMO: + { + AK_ISP_DEMO_ATTR *demo_para; + demo_para = kmalloc(sizeof(AK_ISP_DEMO_ATTR),GFP_KERNEL); + if (!demo_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_demo_attr(demo_para); + if(copy_to_user((AK_ISP_DEMO_ATTR *)arg,demo_para, sizeof(AK_ISP_DEMO_ATTR))){ + ret = -EFAULT; + kfree(demo_para); + goto fini; + } + printk_demo_para(demo_para); + kfree(demo_para); + } + break; + + case AK_ISP_SET_DPC: + { + AK_ISP_DDPC_ATTR *dpc_para; + dpc_para = kmalloc(sizeof(AK_ISP_DDPC_ATTR),GFP_KERNEL); + if (!dpc_para){ + ret = -ENOMEM; + goto fini; + } + memset(dpc_para,0,sizeof(AK_ISP_DDPC_ATTR)); + if(copy_from_user(dpc_para,(AK_ISP_DDPC_ATTR *)arg, sizeof(AK_ISP_DDPC_ATTR))) + { + ret = -EFAULT; + kfree(dpc_para); + goto fini; + } + ak_isp_vp_set_dpc_attr(dpc_para); + printk_dpc(dpc_para); + kfree(dpc_para); + } + break; + case AK_ISP_GET_DPC: + { + AK_ISP_DDPC_ATTR *dpc_para; + dpc_para = kmalloc(sizeof(AK_ISP_DDPC_ATTR),GFP_KERNEL); + if (!dpc_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_dpc_attr(dpc_para); + if(copy_to_user((AK_ISP_DDPC_ATTR *)arg,dpc_para, sizeof(AK_ISP_DDPC_ATTR))){ + ret = -EFAULT; + kfree(dpc_para); + goto fini; + } + printk_dpc(dpc_para); + kfree(dpc_para); + } + break; + + case AK_ISP_SET_CCM: + { + AK_ISP_CCM_ATTR *ccm_para; + ccm_para = kmalloc(sizeof(AK_ISP_CCM_ATTR),GFP_KERNEL); + if (!ccm_para){ + ret = -ENOMEM; + goto fini; + } + memset(ccm_para,0,sizeof(AK_ISP_CCM_ATTR)); + if(copy_from_user(ccm_para,(AK_ISP_CCM_ATTR *)arg, sizeof(AK_ISP_CCM_ATTR))){ + ret = -EFAULT; + kfree(ccm_para); + goto fini; + } + ak_isp_vp_set_ccm_attr(ccm_para); + printk_ccm_para(ccm_para); + kfree(ccm_para); + } + break; + case AK_ISP_GET_CCM: + { + AK_ISP_CCM_ATTR *ccm_para; + ccm_para = kmalloc(sizeof(AK_ISP_CCM_ATTR),GFP_KERNEL); + if (!ccm_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_ccm_attr(ccm_para); + if(copy_to_user((AK_ISP_CCM_ATTR *)arg,ccm_para,sizeof(AK_ISP_CCM_ATTR))){ + ret = -EFAULT; + kfree(ccm_para); + goto fini; + } + printk_ccm_para(ccm_para); + kfree(ccm_para); + } + break; + + case AK_ISP_SET_RGB_GAMMA: + { + AK_ISP_RGB_GAMMA_ATTR *rgb_gamma_para; + rgb_gamma_para = kmalloc(sizeof(AK_ISP_RGB_GAMMA_ATTR),GFP_KERNEL); + if (!rgb_gamma_para){ + ret = -ENOMEM; + goto fini; + } + memset(rgb_gamma_para,0,sizeof(AK_ISP_RGB_GAMMA_ATTR)); + if(copy_from_user(rgb_gamma_para,(AK_ISP_RGB_GAMMA_ATTR *)arg, sizeof(AK_ISP_RGB_GAMMA_ATTR))) + { + ret = -EFAULT; + kfree(rgb_gamma_para); + goto fini; + } + ak_isp_vp_set_rgb_gamma_attr(rgb_gamma_para); + printk_rgb_gamma_para(rgb_gamma_para); + kfree(rgb_gamma_para); + } + break; + case AK_ISP_GET_RGB_GAMMA: + { + AK_ISP_RGB_GAMMA_ATTR *rgb_gamma_para; + rgb_gamma_para = kmalloc(sizeof(AK_ISP_RGB_GAMMA_ATTR),GFP_KERNEL); + if (!rgb_gamma_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_rgb_gamma_attr(rgb_gamma_para); + if(copy_to_user((AK_ISP_RGB_GAMMA_ATTR *)arg,rgb_gamma_para, sizeof(AK_ISP_RGB_GAMMA_ATTR))){ + ret = -EFAULT; + kfree(rgb_gamma_para); + goto fini; + } + printk_rgb_gamma_para(rgb_gamma_para); + kfree(rgb_gamma_para); + } + break; + + case AK_ISP_SET_WDR: + { + AK_ISP_WDR_ATTR *wdr_para; + wdr_para = kmalloc(sizeof(AK_ISP_WDR_ATTR),GFP_KERNEL); + if (!wdr_para){ + ret = -ENOMEM; + goto fini; + } + memset(wdr_para,0,sizeof(AK_ISP_WDR_ATTR)); + if(copy_from_user(wdr_para,(AK_ISP_WDR_ATTR *)arg, sizeof(AK_ISP_WDR_ATTR))){ + ret = -EFAULT; + kfree(wdr_para); + goto fini; + } + ak_isp_vp_set_wdr_attr(wdr_para); + printk_wdr_para(wdr_para); + kfree(wdr_para); + } + break; + case AK_ISP_GET_WDR: + { + AK_ISP_WDR_ATTR *wdr_para; + wdr_para = kmalloc(sizeof(AK_ISP_WDR_ATTR),GFP_KERNEL); + if (!wdr_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_wdr_attr(wdr_para); + if(copy_to_user((AK_ISP_WDR_ATTR *)arg,wdr_para, sizeof(AK_ISP_WDR_ATTR))){ + ret = -EFAULT; + kfree(wdr_para); + goto fini; + } + printk_wdr_para(wdr_para); + kfree(wdr_para); + } + break; + + case AK_ISP_SET_SHARP: + { + AK_ISP_SHARP_ATTR *sharp_para; + sharp_para = kmalloc(sizeof(AK_ISP_SHARP_ATTR),GFP_KERNEL); + if (!sharp_para){ + ret = -ENOMEM; + goto fini; + } + memset(sharp_para,0,sizeof(AK_ISP_SHARP_ATTR)); + if(copy_from_user(sharp_para,(AK_ISP_SHARP_ATTR *)arg, sizeof(AK_ISP_SHARP_ATTR))){ + ret = -EFAULT; + kfree(sharp_para); + goto fini; + } + ak_isp_vp_set_sharp_attr(sharp_para); + printk_sharp_para(sharp_para); + kfree(sharp_para); + } + break; + + case AK_ISP_GET_SHARP: + { + AK_ISP_SHARP_ATTR *sharp_para; + sharp_para = kmalloc(sizeof(AK_ISP_SHARP_ATTR),GFP_KERNEL); + if (!sharp_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_sharp_attr(sharp_para); + if(copy_to_user((AK_ISP_SHARP_ATTR *)arg,sharp_para, sizeof(AK_ISP_SHARP_ATTR))){ + ret = -EFAULT; + kfree(sharp_para); + goto fini; + } + printk_sharp_para(sharp_para); + kfree(sharp_para); + } + break; + + case AK_ISP_SET_SHARP_EX: + { + AK_ISP_SHARP_EX_ATTR *sharp_ex_para; + sharp_ex_para = kmalloc(sizeof(AK_ISP_SHARP_EX_ATTR),GFP_KERNEL); + if (!sharp_ex_para){ + ret = -ENOMEM; + goto fini; + } + if(copy_from_user(sharp_ex_para,(AK_ISP_SHARP_EX_ATTR *)arg, sizeof(AK_ISP_SHARP_EX_ATTR))) + { + ret = -EFAULT; + kfree(sharp_ex_para); + goto fini; + } + ak_isp_vp_set_sharp_ex_attr(sharp_ex_para); + printk_sharp_ex_para(sharp_ex_para); + kfree(sharp_ex_para); + } + break; + case AK_ISP_GET_SHARP_EX: + { + AK_ISP_SHARP_EX_ATTR *sharp_ex_para; + sharp_ex_para = kmalloc(sizeof(AK_ISP_SHARP_EX_ATTR),GFP_KERNEL); + if (!sharp_ex_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_sharp_ex_attr(sharp_ex_para); + if(copy_to_user((AK_ISP_SHARP_EX_ATTR *)arg,sharp_ex_para, sizeof(AK_ISP_SHARP_EX_ATTR))){ + ret = -EFAULT; + kfree(sharp_ex_para); + goto fini; + } + printk_sharp_ex_para(sharp_ex_para); + kfree(sharp_ex_para); + } + break; + + case AK_ISP_SET_Y_NR2: + { + AK_ISP_NR2_ATTR *nr2_para; + nr2_para = kmalloc(sizeof(AK_ISP_NR2_ATTR),GFP_KERNEL); + if (!nr2_para){ + ret = -ENOMEM; + goto fini; + } + memset(nr2_para,0,sizeof(AK_ISP_NR2_ATTR)); + if(copy_from_user(nr2_para,(AK_ISP_NR2_ATTR *)arg, sizeof(AK_ISP_NR2_ATTR))){ + ret = -EFAULT; + kfree(nr2_para); + goto fini; + } + ak_isp_vp_set_nr2_attr(nr2_para); + printk_nr2_para(nr2_para); + kfree(nr2_para); + } + break; + case AK_ISP_GET_Y_NR2: + { + AK_ISP_NR2_ATTR *nr2_para; + nr2_para = kmalloc(sizeof(AK_ISP_NR2_ATTR),GFP_KERNEL); + if (!nr2_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_nr2_attr(nr2_para); + if(copy_to_user((AK_ISP_NR2_ATTR *)arg,nr2_para, sizeof(AK_ISP_NR2_ATTR))){ + ret = -EFAULT; + kfree(nr2_para); + goto fini; + } + printk_nr2_para(nr2_para); + kfree(nr2_para); + } + break; + + case AK_ISP_SET_3D_NR: + { + AK_ISP_3D_NR_ATTR *nr_3d_para; + nr_3d_para = kmalloc(sizeof(AK_ISP_3D_NR_ATTR),GFP_KERNEL); + if (!nr_3d_para){ + ret = -ENOMEM; + goto fini; + } + memset(nr_3d_para,0,sizeof(AK_ISP_3D_NR_ATTR)); + if(copy_from_user(nr_3d_para,(AK_ISP_3D_NR_ATTR *)arg, sizeof(AK_ISP_3D_NR_ATTR))){ + ret = -EFAULT; + kfree(nr_3d_para); + goto fini; + } + ak_isp_vp_set_3d_nr_attr(nr_3d_para); + printk_nr_3d_para(nr_3d_para); + kfree(nr_3d_para); + } + break; + case AK_ISP_GET_3D_NR: + { + AK_ISP_3D_NR_ATTR *nr_3d_para; + nr_3d_para = kmalloc(sizeof(AK_ISP_3D_NR_ATTR),GFP_KERNEL); + if (!nr_3d_para){ + ret = -ENOMEM; + goto fini; + } + memset(nr_3d_para,0,sizeof(AK_ISP_3D_NR_ATTR)); + ak_isp_vp_get_3d_nr_attr(nr_3d_para); + if(copy_to_user((AK_ISP_3D_NR_ATTR *)arg,nr_3d_para, sizeof(AK_ISP_3D_NR_ATTR))){ + ret = -EFAULT; + kfree(nr_3d_para); + goto fini; + } + printk_nr_3d_para(nr_3d_para); + kfree(nr_3d_para); + } + break; + + + case AK_ISP_SET_3D_NR_REF: + { + AK_ISP_3D_NR_REF_ATTR *nr_3d_ref_para; + nr_3d_ref_para = kmalloc(sizeof(AK_ISP_3D_NR_REF_ATTR),GFP_KERNEL); + if (!nr_3d_ref_para){ + ret = -ENOMEM; + goto fini; + } + memset(nr_3d_ref_para,0,sizeof(AK_ISP_3D_NR_REF_ATTR)); + if(copy_from_user(nr_3d_ref_para,(AK_ISP_3D_NR_REF_ATTR *)arg, sizeof(AK_ISP_3D_NR_REF_ATTR))){ + ret = -EFAULT; + kfree(nr_3d_ref_para); + goto fini; + } + ak_isp_vp_set_3d_nr_ref_addr(nr_3d_ref_para); + printk_3d_nr_ref(nr_3d_ref_para); + kfree(nr_3d_ref_para); + } + break; + case AK_ISP_GET_3D_NR_REF: + { + AK_ISP_3D_NR_REF_ATTR *nr_3d_ref_para; + nr_3d_ref_para = kmalloc(sizeof(AK_ISP_3D_NR_REF_ATTR),GFP_KERNEL); + if (!nr_3d_ref_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_3d_nr_ref_addr(nr_3d_ref_para); + if(copy_to_user((AK_ISP_3D_NR_REF_ATTR *)arg,nr_3d_ref_para, sizeof(AK_ISP_3D_NR_REF_ATTR))){ + ret = -EFAULT; + kfree(nr_3d_ref_para); + goto fini; + } + printk_3d_nr_ref(nr_3d_ref_para); + kfree(nr_3d_ref_para); + } + break; + case AK_ISP_GET_3D_NR_STAT_INFO: + { + AK_ISP_3D_NR_STAT_INFO *nr_3d_stat_info; + void *yuv_paddr, *mdinfo, *MD_stat = NULL; + int md_sz = 24*32*2; + int flip_en, mirror_en, height_block_num; + int i,j; + nr_3d_stat_info= kmalloc(sizeof(AK_ISP_3D_NR_STAT_INFO),GFP_KERNEL); + if (!nr_3d_stat_info){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_3d_nr_stat_info(nr_3d_stat_info); + + ak_isp_get_yuvaddr_and_mdinfo(-1, &yuv_paddr, &mdinfo); + ak_isp_get_flip_mirror(&flip_en, &mirror_en, &height_block_num); + + /* cat to nr_3d_stat_info struct */ + MD_stat = (void *)nr_3d_stat_info->MD_stat; + + if (flip_en == 0 && mirror_en == 0) { + memcpy(MD_stat, mdinfo, md_sz); + } else if (flip_en == 0 && mirror_en == 1) { + for (i = 0; i < height_block_num; i++) { + for (j = 0; j < 32; j++) { + memcpy(MD_stat + 2*(32*i + j), mdinfo + 2*(32*i + 31 - j), 2); + } + } + } else if (flip_en == 1 && mirror_en == 0) { + for (i = 0; i < height_block_num; i++) { + memcpy(MD_stat + 2*32*i, mdinfo + 2*32*(height_block_num - 1 - i), 2*32); + } + } else if (flip_en == 1 && mirror_en == 1) { + for (i = 0; i < height_block_num; i++) { + for (j = 0; j < 32; j++) { + memcpy(MD_stat + 2*(32*i + j), mdinfo + 2*(32*(height_block_num - 1 - i) + 31 - j), 2); + } + } + } + + if(copy_to_user((AK_ISP_3D_NR_STAT_INFO *)arg,nr_3d_stat_info, sizeof(AK_ISP_3D_NR_STAT_INFO))){ + ret = -EFAULT; + kfree(nr_3d_stat_info); + goto fini; + } + printk_3d_nr_stat_info(nr_3d_stat_info); + kfree(nr_3d_stat_info); + } + break; + case AK_ISP_GET_FCS: + { + AK_ISP_FCS_ATTR *fcs_para; + fcs_para = kmalloc(sizeof(AK_ISP_FCS_ATTR),GFP_KERNEL); + if (!fcs_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_fcs_attr(fcs_para); + if(copy_to_user((AK_ISP_FCS_ATTR *)arg,fcs_para, sizeof(AK_ISP_FCS_ATTR))){ + ret = -EFAULT; + kfree(fcs_para); + goto fini; + } + printk_fcs_para(fcs_para); + kfree(fcs_para); + } + break; + case AK_ISP_SET_FCS: + { + AK_ISP_FCS_ATTR *fcs_para; + fcs_para = kmalloc(sizeof(AK_ISP_FCS_ATTR),GFP_KERNEL); + if (!fcs_para){ + ret = -ENOMEM; + goto fini; + } + memset(fcs_para,0,sizeof(AK_ISP_FCS_ATTR)); + if(copy_from_user(fcs_para,(AK_ISP_FCS_ATTR *)arg, sizeof(AK_ISP_FCS_ATTR))){ + ret = -EFAULT; + kfree(fcs_para); + goto fini; + } + ak_isp_vp_set_fcs_attr(fcs_para); + printk_fcs_para(fcs_para); + kfree(fcs_para); + } + break; + + case AK_ISP_SET_CONTRAST: + { + AK_ISP_CONTRAST_ATTR *contrast_para; + contrast_para = kmalloc(sizeof(AK_ISP_CONTRAST_ATTR),GFP_KERNEL); + if (!contrast_para){ + ret = -ENOMEM; + goto fini; + } + + memset(contrast_para,0,sizeof(AK_ISP_CONTRAST_ATTR)); + if(copy_from_user(contrast_para,(AK_ISP_CONTRAST_ATTR *)arg, sizeof(AK_ISP_CONTRAST_ATTR))){ + ret = -EFAULT; + kfree(contrast_para); + goto fini; + } + ak_isp_vp_set_contrast_attr(contrast_para); + printk_contrast_para(contrast_para); + kfree(contrast_para); + } + break; + case AK_ISP_GET_CONTRAST: + { + AK_ISP_CONTRAST_ATTR *contrast_para; + contrast_para = kmalloc(sizeof(AK_ISP_CONTRAST_ATTR),GFP_KERNEL); + if (!contrast_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_contrast_attr(contrast_para); + if(copy_to_user((AK_ISP_CONTRAST_ATTR *)arg,contrast_para, sizeof(AK_ISP_CONTRAST_ATTR))){ + ret = -EFAULT; + kfree(contrast_para); + goto fini; + } + printk_contrast_para(contrast_para); + kfree(contrast_para); + } + break; + + + case AK_ISP_SET_SAT: + { + AK_ISP_SATURATION_ATTR *satu_para; + satu_para = kmalloc(sizeof(AK_ISP_SATURATION_ATTR),GFP_KERNEL); + if (!satu_para){ + ret = -ENOMEM; + goto fini; + } + memset(satu_para,0,sizeof(AK_ISP_SATURATION_ATTR)); + if(copy_from_user(satu_para,(AK_ISP_SATURATION_ATTR *)arg, sizeof(AK_ISP_SATURATION_ATTR))){ + ret = -EFAULT; + kfree(satu_para); + goto fini; + } + ak_isp_vp_set_saturation_attr(satu_para); + printk_satu_para(satu_para); + kfree(satu_para); + } + break; + case AK_ISP_GET_SAT: + { + AK_ISP_SATURATION_ATTR *satu_para; + satu_para = kmalloc(sizeof(AK_ISP_SATURATION_ATTR),GFP_KERNEL); + if (!satu_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_saturation_attr(satu_para); + if(copy_to_user((AK_ISP_SATURATION_ATTR *)arg,satu_para, sizeof(AK_ISP_SATURATION_ATTR))){ + ret = -EFAULT; + kfree(satu_para); + goto fini; + } + printk_satu_para(satu_para); + kfree(satu_para); + } + break; + + case AK_ISP_SET_RGB2YUV: + { + AK_ISP_RGB2YUV_ATTR *rgb2yuv_para; + rgb2yuv_para = kmalloc(sizeof(AK_ISP_RGB2YUV_ATTR),GFP_KERNEL); + if (!rgb2yuv_para){ + ret = -ENOMEM; + goto fini; + } + memset(rgb2yuv_para,0,sizeof(AK_ISP_RGB2YUV_ATTR)); + + if(copy_from_user(rgb2yuv_para,(AK_ISP_RGB2YUV_ATTR *)arg, sizeof(AK_ISP_RGB2YUV_ATTR))){ + ret = -EFAULT; + kfree(rgb2yuv_para); + goto fini; + } + ak_isp_vp_set_rgb2yuv_attr(rgb2yuv_para); + printk_rgb2yuv_para(rgb2yuv_para); + kfree(rgb2yuv_para); + } + break; + case AK_ISP_GET_RGB2YUV: + { + AK_ISP_RGB2YUV_ATTR *rgb2yuv_para; + rgb2yuv_para = kmalloc(sizeof(AK_ISP_RGB2YUV_ATTR),GFP_KERNEL); + if (!rgb2yuv_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_rgb2yuv_attr(rgb2yuv_para); + if(copy_to_user((AK_ISP_RGB2YUV_ATTR *)arg,rgb2yuv_para, sizeof(AK_ISP_RGB2YUV_ATTR))){ + ret = -EFAULT; + kfree(rgb2yuv_para); + goto fini; + } + printk_rgb2yuv_para(rgb2yuv_para); + kfree(rgb2yuv_para); + } + break; + + case AK_ISP_SET_YUV_EFFECT: + { + AK_ISP_EFFECT_ATTR *effect_para; + effect_para = kmalloc(sizeof(AK_ISP_EFFECT_ATTR),GFP_KERNEL); + if (!effect_para){ + ret = -ENOMEM; + goto fini; + } + memset(effect_para,0,sizeof(AK_ISP_EFFECT_ATTR)); + if(copy_from_user(effect_para,(AK_ISP_EFFECT_ATTR *)arg, sizeof(AK_ISP_EFFECT_ATTR))){ + ret = -EFAULT; + kfree(effect_para); + goto fini; + } + ak_isp_vp_set_effect_attr(effect_para); + printk_effect_para(effect_para); + kfree(effect_para); + } + break; + case AK_ISP_GET_YUV_EFFECT: + { + AK_ISP_EFFECT_ATTR *effect_para; + effect_para = kmalloc(sizeof(AK_ISP_EFFECT_ATTR),GFP_KERNEL); + if (!effect_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_effect_attr(effect_para); + if(copy_to_user((AK_ISP_EFFECT_ATTR *)arg,effect_para, sizeof(AK_ISP_EFFECT_ATTR))){ + ret = -EFAULT; + kfree(effect_para); + goto fini; + } + printk_effect_para(effect_para); + kfree(effect_para); + } + break; + + case AK_ISP_SET_RAW_HIST: + { + AK_ISP_RAW_HIST_ATTR *raw_hist_para; + raw_hist_para = kmalloc(sizeof(AK_ISP_RAW_HIST_ATTR),GFP_KERNEL); + if (!raw_hist_para){ + ret = -ENOMEM; + goto fini; + } + if(copy_from_user(raw_hist_para,(AK_ISP_RAW_HIST_ATTR *)arg, sizeof(AK_ISP_RAW_HIST_ATTR))){ + ret = -EFAULT; + kfree(raw_hist_para); + goto fini; + } + ak_isp_vp_set_raw_hist_attr(raw_hist_para); + printk_raw_hist_para(raw_hist_para); + kfree(raw_hist_para); + } + break; + + case AK_ISP_GET_RAW_HIST: + { + AK_ISP_RAW_HIST_ATTR *raw_hist_para; + raw_hist_para = kmalloc(sizeof(AK_ISP_RAW_HIST_ATTR),GFP_KERNEL); + if (!raw_hist_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_raw_hist_attr(raw_hist_para); + if(copy_to_user((AK_ISP_RAW_HIST_ATTR *)arg,raw_hist_para, sizeof(AK_ISP_RAW_HIST_ATTR))){ + ret = -EFAULT; + kfree(raw_hist_para); + goto fini; + } + printk_raw_hist_para(raw_hist_para); + kfree(raw_hist_para); + } + + break; + case AK_ISP_GET_RAW_HIST_STAT: + { + AK_ISP_RAW_HIST_STAT_INFO *raw_hist_stat_info; + raw_hist_stat_info = kmalloc(sizeof(AK_ISP_RAW_HIST_STAT_INFO),GFP_KERNEL); + if (!raw_hist_stat_info){ + ret = -ENOMEM; + goto fini; + } + memset(raw_hist_stat_info,0,sizeof(AK_ISP_RAW_HIST_STAT_INFO)); + + ak_isp_vp_get_raw_hist_stat_info(raw_hist_stat_info); + //raw_hist_stat_info->raw_g_total = 1000; + if(copy_to_user((AK_ISP_RAW_HIST_STAT_INFO *)arg,raw_hist_stat_info, sizeof(AK_ISP_RAW_HIST_STAT_INFO))){ + ret = -EFAULT; + kfree(raw_hist_stat_info); + goto fini; + } + printk_raw_hist_stat_info(raw_hist_stat_info); + kfree(raw_hist_stat_info); + } + break; + + case AK_ISP_SET_RGB_HIST: + { + AK_ISP_RGB_HIST_ATTR *rgb_hist_para; + rgb_hist_para = kmalloc(sizeof(AK_ISP_RGB_HIST_ATTR),GFP_KERNEL); + if (!rgb_hist_para){ + ret = -ENOMEM; + goto fini; + } + if(copy_from_user(rgb_hist_para,(AK_ISP_RGB_HIST_ATTR *)arg, sizeof(AK_ISP_RGB_HIST_ATTR))){ + ret = -EFAULT; + kfree(rgb_hist_para); + goto fini; + } + ak_isp_vp_set_rgb_hist_attr(rgb_hist_para); + printk_rgb_hist_para(rgb_hist_para); + kfree(rgb_hist_para); + } + break; + + case AK_ISP_GET_RGB_HIST: + { + AK_ISP_RGB_HIST_ATTR *rgb_hist_para; + rgb_hist_para = kmalloc(sizeof(AK_ISP_RGB_HIST_ATTR),GFP_KERNEL); + if (!rgb_hist_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_rgb_hist_attr(rgb_hist_para); + if(copy_to_user((AK_ISP_RGB_HIST_ATTR *)arg,rgb_hist_para, sizeof(AK_ISP_RGB_HIST_ATTR))){ + ret = -EFAULT; + kfree(rgb_hist_para); + goto fini; + } + printk_rgb_hist_para(rgb_hist_para); + kfree(rgb_hist_para); + } + break; + + case AK_ISP_GET_RGB_HIST_STAT: + { + AK_ISP_RGB_HIST_STAT_INFO *rgb_hist_stat_info; + rgb_hist_stat_info = kmalloc(sizeof(AK_ISP_RGB_HIST_STAT_INFO),GFP_KERNEL); + if (!rgb_hist_stat_info){ + ret = -ENOMEM; + goto fini; + } + memset(rgb_hist_stat_info,0,sizeof(AK_ISP_RGB_HIST_STAT_INFO)); + + ak_isp_vp_get_rgb_hist_stat_info(rgb_hist_stat_info); + if(copy_to_user((AK_ISP_RGB_HIST_STAT_INFO *)arg,rgb_hist_stat_info, sizeof(AK_ISP_RGB_HIST_STAT_INFO))){ + ret = -EFAULT; + kfree(rgb_hist_stat_info); + goto fini; + } + printk_rgb_hist_stat_info(rgb_hist_stat_info); + kfree(rgb_hist_stat_info); + } + break; + case AK_ISP_SET_Y_HIST: + { + AK_ISP_YUV_HIST_ATTR *yuv_hist_para; + yuv_hist_para = kmalloc(sizeof(AK_ISP_YUV_HIST_ATTR),GFP_KERNEL); + if (!yuv_hist_para){ + ret = -ENOMEM; + goto fini; + } + if(copy_from_user(yuv_hist_para,(AK_ISP_YUV_HIST_ATTR *)arg, sizeof(AK_ISP_YUV_HIST_ATTR))){ + ret = -EFAULT; + kfree(yuv_hist_para); + goto fini; + } + ak_isp_vp_set_yuv_hist_attr(yuv_hist_para); + printk_yuv_hist_para(yuv_hist_para); + kfree(yuv_hist_para); + } + break; + case AK_ISP_GET_Y_HIST: + { + AK_ISP_YUV_HIST_ATTR *yuv_hist_para; + yuv_hist_para = kmalloc(sizeof(AK_ISP_YUV_HIST_ATTR),GFP_KERNEL); + if (!yuv_hist_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_yuv_hist_attr(yuv_hist_para); + if(copy_to_user((AK_ISP_YUV_HIST_ATTR *)arg,yuv_hist_para, sizeof(AK_ISP_YUV_HIST_ATTR))){ + ret = -EFAULT; + kfree(yuv_hist_para); + goto fini; + } + printk_yuv_hist_para(yuv_hist_para); + kfree(yuv_hist_para); + } + break; + case AK_ISP_GET_Y_HIST_STAT: + { + AK_ISP_YUV_HIST_STAT_INFO *yuv_hist_stat_info; + yuv_hist_stat_info = kmalloc(sizeof(AK_ISP_YUV_HIST_STAT_INFO),GFP_KERNEL); + if (!yuv_hist_stat_info){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_yuv_hist_stat_info(yuv_hist_stat_info); + if(copy_to_user((AK_ISP_YUV_HIST_STAT_INFO *)arg,yuv_hist_stat_info, sizeof(AK_ISP_YUV_HIST_STAT_INFO))){ + ret = -EFAULT; + kfree(yuv_hist_stat_info); + goto fini; + } + printk_yuv_hist_stat_info(yuv_hist_stat_info); + kfree(yuv_hist_stat_info); + } + break; + + case AK_ISP_SET_EXP_TYPE: + { + AK_ISP_EXP_TYPE *exp_type_para; + exp_type_para = kmalloc(sizeof(AK_ISP_EXP_TYPE),GFP_KERNEL); + if (!exp_type_para){ + ret = -ENOMEM; + goto fini; + } + memset(exp_type_para,0,sizeof(AK_ISP_EXP_TYPE)); + if(copy_from_user(exp_type_para,(AK_ISP_EXP_TYPE *)arg, sizeof(AK_ISP_EXP_TYPE))){ + ret = -EFAULT; + kfree(exp_type_para); + goto fini; + } + ak_isp_vp_set_exp_type(exp_type_para); + printk_exp_type_para(exp_type_para); + kfree(exp_type_para); + } + break; + case AK_ISP_GET_EXP_TYPE: + { + AK_ISP_EXP_TYPE *exp_type_para; + exp_type_para = kmalloc(sizeof(AK_ISP_EXP_TYPE),GFP_KERNEL); + if (!exp_type_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_exp_type(exp_type_para); + if(copy_to_user((AK_ISP_EXP_TYPE *)arg,exp_type_para, sizeof(AK_ISP_EXP_TYPE))){ + ret = -EFAULT; + kfree(exp_type_para); + goto fini; + } + printk_exp_type_para(exp_type_para); + kfree(exp_type_para); + } + break; + + case AK_ISP_SET_FRAME_RATE: + { + AK_ISP_FRAME_RATE_ATTR *frame_rate_para; + frame_rate_para = kmalloc(sizeof(AK_ISP_FRAME_RATE_ATTR),GFP_KERNEL); + if (!frame_rate_para){ + ret = -ENOMEM; + goto fini; + } + memset(frame_rate_para,0,sizeof(AK_ISP_FRAME_RATE_ATTR)); + if(copy_from_user(frame_rate_para,(AK_ISP_FRAME_RATE_ATTR *)arg, sizeof(AK_ISP_FRAME_RATE_ATTR))){ + ret = -EFAULT; + kfree(frame_rate_para); + goto fini; + } + ak_isp_vp_set_frame_rate(frame_rate_para); + printk_frame_rate_para(frame_rate_para); + kfree(frame_rate_para); + } + break; + case AK_ISP_GET_FRAME_RATE: + { + AK_ISP_FRAME_RATE_ATTR *frame_rate_para; + frame_rate_para = kmalloc(sizeof(AK_ISP_FRAME_RATE_ATTR),GFP_KERNEL); + if (!frame_rate_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_frame_rate(frame_rate_para); + if(copy_to_user((AK_ISP_FRAME_RATE_ATTR *)arg,frame_rate_para, sizeof(AK_ISP_FRAME_RATE_ATTR))){ + ret = -EFAULT; + kfree(frame_rate_para); + goto fini; + } + printk_frame_rate_para(frame_rate_para); + kfree(frame_rate_para); + } + break; + + case AK_ISP_SET_AE: + { + AK_ISP_AE_ATTR *ae_para; + ae_para = kmalloc(sizeof(AK_ISP_AE_ATTR),GFP_KERNEL); + if (!ae_para){ + ret = -ENOMEM; + goto fini; + } + memset(ae_para,0,sizeof(AK_ISP_AE_ATTR)); + if(copy_from_user(ae_para,(AK_ISP_AE_ATTR *)arg, sizeof(AK_ISP_AE_ATTR))){ + ret = -EFAULT; + kfree(ae_para); + goto fini; + } + printk_ae_para(ae_para); + ak_isp_vp_set_ae_attr(ae_para); + kfree(ae_para); + } + break; + case AK_ISP_GET_AE: + { + AK_ISP_AE_ATTR *ae_para; + ae_para = kmalloc(sizeof(AK_ISP_AE_ATTR),GFP_KERNEL); + if (!ae_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_ae_attr(ae_para); + if(copy_to_user((AK_ISP_AE_ATTR *)arg,ae_para, sizeof(AK_ISP_AE_ATTR))){ + ret = -EFAULT; + kfree(ae_para); + goto fini; + } + printk_ae_para(ae_para); + kfree(ae_para); + } + break; + + case AK_ISP_SET_MAE: + { + AK_ISP_MAE_ATTR *mae_para; + mae_para = kmalloc(sizeof(AK_ISP_MAE_ATTR),GFP_KERNEL); + if (!mae_para){ + ret = -ENOMEM; + goto fini; + } + memset(mae_para,0,sizeof(AK_ISP_MAE_ATTR)); + if(copy_from_user(mae_para,(AK_ISP_MAE_ATTR *)arg, sizeof(AK_ISP_MAE_ATTR))){ + ret = -EFAULT; + kfree(mae_para); + goto fini; + } + printk_mae_para(mae_para); + ak_isp_vp_set_mae_attr(mae_para); + kfree(mae_para); + } + break; + case AK_ISP_GET_MAE: + { + AK_ISP_MAE_ATTR *mae_para; + mae_para = kmalloc(sizeof(AK_ISP_MAE_ATTR),GFP_KERNEL); + if (!mae_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_mae_attr(mae_para); + if(copy_to_user((AK_ISP_MAE_ATTR *)arg,mae_para, sizeof(AK_ISP_MAE_ATTR))){ + ret = -EFAULT; + kfree(mae_para); + goto fini; + } + printk_mae_para(mae_para); + kfree(mae_para); + } + break; + + case AK_ISP_GET_AE_RUN_INFO: + { + AK_ISP_AE_RUN_INFO *ae_run_para; + ae_run_para = kmalloc(sizeof(AK_ISP_AE_RUN_INFO),GFP_KERNEL); + if (!ae_run_para){ + ret = -ENOMEM; + goto fini; + } + memset(ae_run_para,0,sizeof(AK_ISP_AE_RUN_INFO)); + + ak_isp_vp_get_ae_run_info(ae_run_para); + //ae_run_para->current_a_gain =100; + if(copy_to_user((AK_ISP_AE_RUN_INFO*)arg,ae_run_para, sizeof(AK_ISP_AE_RUN_INFO))){ + ret = -EFAULT; + kfree(ae_run_para); + goto fini; + } + printk_ae_run_para(ae_run_para); + kfree(ae_run_para); + } + break; + + case AK_ISP_SET_WB_TYPE: + { + AK_ISP_WB_TYPE_ATTR *wb_type_para; + wb_type_para = kmalloc(sizeof(AK_ISP_WB_TYPE_ATTR),GFP_KERNEL); + if (!wb_type_para){ + ret = -ENOMEM; + goto fini; + } + + memset(wb_type_para,0,sizeof(AK_ISP_WB_TYPE_ATTR)); + if(copy_from_user(wb_type_para,(AK_ISP_WB_TYPE_ATTR *)arg, sizeof(AK_ISP_WB_TYPE_ATTR))){ + ret = -EFAULT; + kfree(wb_type_para); + goto fini; + } + ak_isp_vp_set_wb_type(wb_type_para); + printk_wb_type_para(wb_type_para); + kfree(wb_type_para); + } + break; + case AK_ISP_GET_WB_TYPE: + { + AK_ISP_WB_TYPE_ATTR *wb_type_para; + wb_type_para = kmalloc(sizeof(AK_ISP_WB_TYPE_ATTR),GFP_KERNEL); + if (!wb_type_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_wb_type(wb_type_para); + if(copy_to_user((AK_ISP_WB_TYPE_ATTR *)arg,wb_type_para,sizeof(AK_ISP_WB_TYPE_ATTR))){ + ret = -EFAULT; + kfree(wb_type_para); + goto fini; + } + printk_wb_type_para(wb_type_para); + kfree(wb_type_para); + } + break; + + + case AK_ISP_SET_AWB: + { + AK_ISP_AWB_ATTR *awb_para; + awb_para = kmalloc(sizeof(AK_ISP_AWB_ATTR),GFP_KERNEL); + if (!awb_para){ + ret = -ENOMEM; + goto fini; + } + memset(awb_para,0,sizeof(AK_ISP_AWB_ATTR)); + if(copy_from_user(awb_para,(AK_ISP_AWB_ATTR *)arg, sizeof(AK_ISP_AWB_ATTR))){ + ret = -EFAULT; + kfree(awb_para); + goto fini; + } + ak_isp_vp_set_awb_attr(awb_para); + printk_awb_para(awb_para); + kfree(awb_para); + } + break; + case AK_ISP_GET_AWB: + { + AK_ISP_AWB_ATTR *awb_para; + awb_para = kmalloc(sizeof(AK_ISP_AWB_ATTR),GFP_KERNEL); + if (!awb_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_awb_attr(awb_para); + if(copy_to_user((AK_ISP_AWB_ATTR *)arg, awb_para,sizeof(AK_ISP_AWB_ATTR))){ + ret = -EFAULT; + kfree(awb_para); + goto fini; + } + printk_awb_para(awb_para); + kfree(awb_para); + } + break; + + case AK_ISP_SET_AWB_EX: + { + AK_ISP_AWB_EX_ATTR *awb_ex_para; + awb_ex_para = kmalloc(sizeof(AK_ISP_AWB_EX_ATTR),GFP_KERNEL); + if (!awb_ex_para){ + ret = -ENOMEM; + goto fini; + } + memset(awb_ex_para,0,sizeof(AK_ISP_AWB_EX_ATTR)); + if(copy_from_user(awb_ex_para,(AK_ISP_AWB_EX_ATTR *)arg, sizeof(AK_ISP_AWB_EX_ATTR))){ + ret = -EFAULT; + kfree(awb_ex_para); + goto fini; + } + ak_isp_vp_set_awb_ex_attr(awb_ex_para); + printk_awb_ex_para(awb_ex_para); + kfree(awb_ex_para); + } + break; + case AK_ISP_GET_AWB_EX: + { + AK_ISP_AWB_EX_ATTR *awb_ex_para; + awb_ex_para = kmalloc(sizeof(AK_ISP_AWB_EX_ATTR),GFP_KERNEL); + if (!awb_ex_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_awb_ex_attr(awb_ex_para); + if(copy_to_user((AK_ISP_AWB_EX_ATTR *)arg, awb_ex_para,sizeof(AK_ISP_AWB_EX_ATTR))){ + ret = -EFAULT; + kfree(awb_ex_para); + goto fini; + } + printk_awb_ex_para(awb_ex_para); + kfree(awb_ex_para); + } + break; + + case AK_ISP_GET_AWB_STAT_INFO: + { + AK_ISP_AWB_STAT_INFO *awb_stat_info; + awb_stat_info = kmalloc(sizeof(AK_ISP_AWB_STAT_INFO),GFP_KERNEL); + if (!awb_stat_info){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_awb_stat_info(awb_stat_info); + //awb_stat_info->r_gain = 100; + if(copy_to_user((AK_ISP_AWB_STAT_INFO*)arg,awb_stat_info,sizeof(AK_ISP_AWB_STAT_INFO))){ + ret = -EFAULT; + kfree(awb_stat_info); + goto fini; + } + printk_awb_stat_info_para(awb_stat_info); + kfree(awb_stat_info); + } + break; + + case AK_ISP_SET_AF: + { + AK_ISP_AF_ATTR *af_para; + af_para = kmalloc(sizeof(AK_ISP_AF_ATTR),GFP_KERNEL); + if (!af_para){ + ret = -ENOMEM; + goto fini; + } + memset(af_para,0,sizeof(AK_ISP_AF_ATTR)); + if(copy_from_user(af_para,(AK_ISP_AF_ATTR*)arg, sizeof(AK_ISP_AF_ATTR))){ + ret = -EFAULT; + kfree(af_para); + goto fini; + } + ak_isp_vp_set_af_attr(af_para); + printk_af_para(af_para); + kfree(af_para); + } + break; + case AK_ISP_SET_AF_WIN34: + { + AK_ISP_AF_ATTR *af_para; + af_para = kmalloc(sizeof(AK_ISP_AF_ATTR),GFP_KERNEL); + if (!af_para){ + ret = -ENOMEM; + goto fini; + } + memset(af_para,0,sizeof(AK_ISP_AF_ATTR)); + if(copy_from_user(af_para,(AK_ISP_AF_ATTR*)arg, sizeof(AK_ISP_AF_ATTR))){ + ret = -EFAULT; + kfree(af_para); + goto fini; + } + ak_isp_vp_set_af_win34_attr(af_para); + printk_af_para(af_para); + kfree(af_para); + } + break; + case AK_ISP_GET_AF: + { + AK_ISP_AF_ATTR *af_para; + af_para = kmalloc(sizeof(AK_ISP_AF_ATTR),GFP_KERNEL); + if (!af_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_af_attr(af_para); + if(copy_to_user((AK_ISP_AF_ATTR*)arg, af_para,sizeof(AK_ISP_AF_ATTR))){ + ret = -EFAULT; + kfree(af_para); + goto fini; + } + printk_af_para(af_para); + kfree(af_para); + } + break; + + case AK_ISP_GET_AF_STAT: + { + AK_ISP_AF_STAT_INFO *af_stat_para; + af_stat_para = kmalloc(sizeof(AK_ISP_AF_STAT_INFO),GFP_KERNEL); + if (!af_stat_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_af_stat_info(af_stat_para); + if(copy_to_user((AK_ISP_AF_STAT_INFO*)arg, af_stat_para,sizeof(AK_ISP_AF_STAT_INFO))) + { + ret = -EFAULT; + kfree(af_stat_para); + goto fini; + } + printk_af_stat_para(af_stat_para); + kfree(af_stat_para); + } + break; + + case AK_ISP_SET_MWB: + { + AK_ISP_MWB_ATTR *mwb_para; + mwb_para = kmalloc(sizeof(AK_ISP_MWB_ATTR),GFP_KERNEL); + if (!mwb_para){ + ret = -ENOMEM; + goto fini; + } + memset(mwb_para,0,sizeof(AK_ISP_MWB_ATTR)); + if(copy_from_user(mwb_para,(AK_ISP_MWB_ATTR*)arg, sizeof(AK_ISP_MWB_ATTR))){ + ret = -EFAULT; + kfree(mwb_para); + goto fini; + } + ak_isp_vp_set_mwb_attr(mwb_para); + printk_mwb_para(mwb_para); + kfree(mwb_para); + } + break; + case AK_ISP_GET_MWB: + { + AK_ISP_MWB_ATTR *mwb_para; + mwb_para = kmalloc(sizeof(AK_ISP_MWB_ATTR),GFP_KERNEL); + if (!mwb_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_mwb_attr(mwb_para); + if(copy_to_user((AK_ISP_MWB_ATTR*)arg,mwb_para, sizeof(AK_ISP_MWB_ATTR))){ + ret = -EFAULT; + kfree(mwb_para); + goto fini; + } + printk_mwb_para(mwb_para); + kfree(mwb_para); + } + break; + + case AK_ISP_SET_WEIGHT: + { + AK_ISP_WEIGHT_ATTR *weight_para; + weight_para = kmalloc(sizeof(AK_ISP_WEIGHT_ATTR),GFP_KERNEL); + if (!weight_para){ + ret = -ENOMEM; + goto fini; + } + memset(weight_para,0,sizeof(AK_ISP_WEIGHT_ATTR)); + if(copy_from_user(weight_para,(AK_ISP_WEIGHT_ATTR*)arg, sizeof(AK_ISP_WEIGHT_ATTR))){ + ret = -EFAULT; + kfree(weight_para); + goto fini; + } + ak_isp_vp_set_zone_weight(weight_para); + printk_weight_para(weight_para); + kfree(weight_para); + } + break; + + case AK_ISP_GET_WEIGHT: + { + AK_ISP_WEIGHT_ATTR *weight_para; + weight_para = kmalloc(sizeof(AK_ISP_WEIGHT_ATTR),GFP_KERNEL); + if (!weight_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vp_get_zone_weight(weight_para); + if(copy_to_user((AK_ISP_WEIGHT_ATTR*)arg,weight_para, sizeof(AK_ISP_WEIGHT_ATTR))) + { + ret = -EFAULT; + kfree(weight_para); + goto fini; + } + printk_weight_para(weight_para); + kfree(weight_para); + } + break; + + case AK_ISP_SET_MAIN_CHAN_MASK_AREA: + { + AK_ISP_MAIN_CHAN_MASK_AREA_ATTR *main_chan_mask_area_para; + main_chan_mask_area_para = kmalloc(sizeof(AK_ISP_MAIN_CHAN_MASK_AREA_ATTR),GFP_KERNEL); + if (!main_chan_mask_area_para){ + ret = -ENOMEM; + goto fini; + } + memset(main_chan_mask_area_para,0,sizeof(AK_ISP_MAIN_CHAN_MASK_AREA_ATTR)); + if(copy_from_user(main_chan_mask_area_para,(AK_ISP_MAIN_CHAN_MASK_AREA_ATTR*)arg, sizeof(AK_ISP_MAIN_CHAN_MASK_AREA_ATTR))) + { + ret = -EFAULT; + kfree(main_chan_mask_area_para); + goto fini; + } + ak_isp_vpp_set_main_chan_mask_area_attr(main_chan_mask_area_para); + printk_main_chan_mask_area_para(main_chan_mask_area_para); + kfree(main_chan_mask_area_para); + } + break; + case AK_ISP_GET_MAIN_CHAN_MASK_AREA: + { + AK_ISP_MAIN_CHAN_MASK_AREA_ATTR *main_chan_mask_area_para; + main_chan_mask_area_para = kmalloc(sizeof(AK_ISP_MAIN_CHAN_MASK_AREA_ATTR),GFP_KERNEL); + if (!main_chan_mask_area_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vpp_get_main_chan_mask_area_attr(main_chan_mask_area_para); + if(copy_to_user((AK_ISP_MASK_AREA_ATTR*)arg,main_chan_mask_area_para, sizeof(AK_ISP_MAIN_CHAN_MASK_AREA_ATTR))) + { + ret = -EFAULT; + kfree(main_chan_mask_area_para); + goto fini; + } + printk_main_chan_mask_area_para(main_chan_mask_area_para); + kfree(main_chan_mask_area_para); + } + break; + case AK_ISP_SET_SUB_CHAN_MASK_AREA: + { + AK_ISP_SUB_CHAN_MASK_AREA_ATTR *sub_chan_mask_area_para; + sub_chan_mask_area_para = kmalloc(sizeof(AK_ISP_SUB_CHAN_MASK_AREA_ATTR),GFP_KERNEL); + if (!sub_chan_mask_area_para){ + ret = -ENOMEM; + goto fini; + } + memset(sub_chan_mask_area_para,0,sizeof(AK_ISP_SUB_CHAN_MASK_AREA_ATTR)); + if(copy_from_user(sub_chan_mask_area_para,(AK_ISP_SUB_CHAN_MASK_AREA_ATTR*)arg, sizeof(AK_ISP_SUB_CHAN_MASK_AREA_ATTR))) + { + ret = -EFAULT; + kfree(sub_chan_mask_area_para); + goto fini; + } + ak_isp_vpp_set_sub_chan_mask_area_attr(sub_chan_mask_area_para); + printk_sub_chan_mask_area_para(sub_chan_mask_area_para); + kfree(sub_chan_mask_area_para); + } + break; + + case AK_ISP_GET_SUB_CHAN_MASK_AREA: + { + AK_ISP_SUB_CHAN_MASK_AREA_ATTR *sub_chan_mask_area_para; + sub_chan_mask_area_para = kmalloc(sizeof(AK_ISP_SUB_CHAN_MASK_AREA_ATTR),GFP_KERNEL); + if (!sub_chan_mask_area_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vpp_get_sub_chan_mask_area_attr(sub_chan_mask_area_para); + if(copy_to_user((AK_ISP_SUB_CHAN_MASK_AREA_ATTR*)arg,sub_chan_mask_area_para, sizeof(AK_ISP_SUB_CHAN_MASK_AREA_ATTR))) + { + ret = -EFAULT; + kfree(sub_chan_mask_area_para); + goto fini; + } + printk_sub_chan_mask_area_para(sub_chan_mask_area_para); + kfree(sub_chan_mask_area_para); + } + break; + + case AK_ISP_SET_MASK_COLOR: + { + AK_ISP_MASK_COLOR_ATTR *mask_color_para; + mask_color_para = kmalloc(sizeof(AK_ISP_MASK_COLOR_ATTR),GFP_KERNEL); + if (!mask_color_para){ + ret = -ENOMEM; + goto fini; + } + memset(mask_color_para,0,sizeof(AK_ISP_MASK_COLOR_ATTR)); + if(copy_from_user(mask_color_para,(AK_ISP_MASK_COLOR_ATTR*)arg, sizeof(AK_ISP_MASK_COLOR_ATTR))) + { + ret = -EFAULT; + kfree(mask_color_para); + goto fini; + } + ak_isp_vpp_set_mask_color(mask_color_para); + printk_mask_color_para(mask_color_para); + kfree(mask_color_para); + } + break; + case AK_ISP_GET_MASK_COLOR: + { + AK_ISP_MASK_COLOR_ATTR *mask_color_para; + mask_color_para = kmalloc(sizeof(AK_ISP_MASK_COLOR_ATTR),GFP_KERNEL); + if (!mask_color_para){ + ret = -ENOMEM; + goto fini; + } + ak_isp_vpp_get_mask_color(mask_color_para); + if(copy_to_user((AK_ISP_MASK_COLOR_ATTR*)arg,mask_color_para, sizeof(AK_ISP_MASK_COLOR_ATTR))) + { + ret = -EFAULT; + kfree(mask_color_para); + goto fini; + } + printk_mask_color_para(mask_color_para); + kfree(mask_color_para); + } + break; + + case AK_ISP_INIT_SENSOR_DEV: + { + void *p_reg_info; + AK_ISP_SENSOR_INIT_PARA sensor_para; + AK_ISP_SENSOR_CB *sensor_cb = ak_sensor_get_sensor_cb(); + + if (copy_from_user(&sensor_para, (void *)arg, sizeof(AK_ISP_SENSOR_INIT_PARA))) + { + ret = -EFAULT; + goto fini; + } + p_reg_info = sensor_para.reg_info; + sensor_para.reg_info = kmalloc(sizeof(AK_ISP_SENSOR_REG_INFO) * sensor_para.num, GFP_KERNEL); + if (!sensor_para.reg_info) { + ret = -ENOMEM; + goto fini; + } else { + if (copy_from_user(sensor_para.reg_info, p_reg_info, sizeof(AK_ISP_SENSOR_REG_INFO) * sensor_para.num)) + { + ret = -EFAULT; + kfree(sensor_para.reg_info); + goto fini; + } + sensor_cb->sensor_init_func(&sensor_para); + kfree(sensor_para.reg_info); + } + } + break; + + case AK_ISP_SET_SENSOR_REG: + { + AK_ISP_SENSOR_CB *sensor_cb = ak_sensor_get_sensor_cb(); + AK_ISP_SENSOR_REG_INFO reg_info; + + if (copy_from_user(®_info, (void *)arg, sizeof(AK_ISP_SENSOR_REG_INFO))) + { + ret = -EFAULT; + goto fini; + } + sensor_cb->sensor_write_reg_func(reg_info.reg_addr, reg_info.value); + } + break; + + case AK_ISP_GET_SENSOR_REG: + { + AK_ISP_SENSOR_CB *sensor_cb = ak_sensor_get_sensor_cb(); + AK_ISP_SENSOR_REG_INFO reg_info; + + if (copy_from_user(®_info, (void *)arg, sizeof(AK_ISP_SENSOR_REG_INFO))) + { + ret = -EFAULT; + goto fini; + } + reg_info.value = sensor_cb->sensor_read_reg_func(reg_info.reg_addr); + if (copy_to_user((void *)arg, ®_info, sizeof(AK_ISP_SENSOR_REG_INFO))) + { + ret = -EFAULT; + goto fini; + } + } + break; + + case AK_ISP_GET_SENSOR_ID: + { + int id; + AK_ISP_SENSOR_CB *sensor_cb = ak_sensor_get_sensor_cb(); + + id = sensor_cb->sensor_read_id_func(); + if (copy_to_user((void *)arg, &id, sizeof(int))) + { + ret = -EFAULT; + goto fini; + } + } + break; + + case AK_ISP_SET_ISP_CAPTURING: + { + int resume; + if (copy_from_user(&resume, (void *)arg, sizeof(int))) + { + ret = -EFAULT; + goto fini; + } + ret = ak_isp_set_isp_capturing(resume); + } + break; + + case AK_ISP_SET_USER_PARAMS: + { + AK_ISP_USER_PARAM param; + if (copy_from_user(¶m, (void *)arg, sizeof(AK_ISP_USER_PARAM))) + { + ret = -EFAULT; + goto fini; + } + ret = ak_isp_set_user_params_do(¶m); + } + break; + + case AK_ISP_SET_MISC_ATTR: + { + AK_ISP_MISC_ATTR misc = {0}; + if (copy_from_user(&misc, (void *)arg, sizeof(AK_ISP_MISC_ATTR))) + { + ret = -EFAULT; + goto fini; + } + ret = ak_isp_vo_set_misc_attr(&misc); + printk_misc_para(&misc); + } + break; + + case AK_ISP_GET_MISC_ATTR: + { + AK_ISP_MISC_ATTR misc = {0}; + ret = ak_isp_vo_get_misc_attr(&misc); + if (copy_to_user((void *)arg, &misc, sizeof(AK_ISP_MISC_ATTR))) + { + ret = -EFAULT; + goto fini; + } + printk_misc_para(&misc); + } + break; + + case AK_ISP_SET_3D_NR_PHYADDR: + { + AK_ISP_3D_NR_REF_ATTR p_ref = {0}; + if(copy_from_user(&p_ref, (void *)arg, sizeof(AK_ISP_3D_NR_REF_ATTR))) + { + ret = -EFAULT; + goto fini; + } + ak_isp_vp_set_3d_nr_ref_addr(&p_ref); + } + break; + + case AK_ISP_SET_Y_GAMMA: + { + AK_ISP_Y_GAMMA_ATTR *y_gamma; + y_gamma = kmalloc(sizeof(AK_ISP_Y_GAMMA_ATTR),GFP_KERNEL); + if (!y_gamma){ + ret = -ENOMEM; + goto fini; + } + memset(y_gamma,0,sizeof(AK_ISP_Y_GAMMA_ATTR)); + if(copy_from_user(y_gamma,(AK_ISP_Y_GAMMA_ATTR *)arg, sizeof(AK_ISP_Y_GAMMA_ATTR))) + { + ret = -EFAULT; + kfree(y_gamma); + goto fini; + } + ak_isp_vp_set_y_gamma_attr(y_gamma); + printk_y_gamma_para(y_gamma); + kfree(y_gamma); + } + break; + case AK_ISP_GET_Y_GAMMA: + { + AK_ISP_Y_GAMMA_ATTR *y_gamma; + y_gamma = kmalloc(sizeof(AK_ISP_Y_GAMMA_ATTR),GFP_KERNEL); + if (!y_gamma){ + ret = -ENOMEM; + goto fini; + } + memset(y_gamma,0,sizeof(AK_ISP_Y_GAMMA_ATTR)); + ak_isp_vp_get_y_gamma_attr(y_gamma); + if(copy_to_user((AK_ISP_Y_GAMMA_ATTR *)arg,y_gamma, sizeof(AK_ISP_Y_GAMMA_ATTR))){ + ret = -EFAULT; + kfree(y_gamma); + goto fini; + } + printk_y_gamma_para(y_gamma); + kfree(y_gamma); + } + break; + + + case AK_ISP_SET_HUE: + { + AK_ISP_HUE_ATTR *hue; + hue = kmalloc(sizeof(AK_ISP_HUE_ATTR),GFP_KERNEL); + if (!hue){ + ret = -ENOMEM; + goto fini; + } + memset(hue,0,sizeof(AK_ISP_HUE_ATTR)); + if(copy_from_user(hue,(AK_ISP_HUE_ATTR *)arg, sizeof(AK_ISP_HUE_ATTR))) + { + ret = -EFAULT; + kfree(hue); + goto fini; + } + ak_isp_vp_set_hue_attr(hue); + printk_hue_para(hue); + kfree(hue); + } + break; + case AK_ISP_GET_HUE: + { + AK_ISP_HUE_ATTR *hue; + hue = kmalloc(sizeof(AK_ISP_HUE_ATTR),GFP_KERNEL); + if (!hue){ + ret = -ENOMEM; + goto fini; + } + memset(hue,0,sizeof(AK_ISP_HUE_ATTR)); + ak_isp_vp_get_hue_attr(hue); + if(copy_to_user((AK_ISP_HUE_ATTR *)arg,hue, sizeof(AK_ISP_HUE_ATTR))){ + ret = -EFAULT; + kfree(hue); + goto fini; + } + printk_hue_para(hue); + kfree(hue); + } + break; + + case AK_ISP_SET_FLIP_MIRROR: + { + struct isp_flip_mirror_info info; + if (copy_from_user(&info, (struct isp_flip_mirror_info *)arg, sizeof(struct isp_flip_mirror_info))){ + ret = -EFAULT; + goto fini; + } + + ret = ak_isp_set_flip_mirror(info.flip_en, info.mirror_en); + } + break; + + case AK_ISP_SET_SENSOR_FPS: + { + int fps; + AK_ISP_SENSOR_CB *sensor_cb; + + if (copy_from_user(&fps,(int *)arg, sizeof(int))) { + printk("copy from user for fps failed\n"); + ret = -EFAULT; + } else { + sensor_cb = ak_sensor_get_sensor_cb(); + if (sensor_cb) { + ret = sensor_cb->sensor_set_fps_func(fps); + } else { + printk(KERN_ERR "get sensor_cb failed int set sensor fps\n"); + ret = -ENODEV; + } + } + } + break; + case AK_ISP_GET_SENSOR_FPS: + { + int fps; + AK_ISP_SENSOR_CB *sensor_cb; + + sensor_cb = ak_sensor_get_sensor_cb(); + if (sensor_cb) { + fps = sensor_cb->sensor_get_fps_func(); + if (copy_to_user((int *)arg, &fps, sizeof(int))) { + printk("copy to user for fps failed\n"); + ret = -EFAULT; + } + } else { + printk(KERN_ERR "get sensor_cb failed int set sensor fps\n"); + ret = -ENODEV; + } + } + break; + case AK_ISP_GET_WORK_SCENE: + { + int scene = ak_isp_get_scene(); + if (copy_to_user((int *)arg, &scene, sizeof(int))) { + printk("copy to user for scene failed\n"); + ret = -EFAULT; + } + } + break; + + case AK_ISP_GET_ISO: + { + int iso = ak_isp_get_iso(); + if (copy_to_user((int *)arg, &iso, sizeof(int))) { + printk("copy to user for iso failed\n"); + ret = -EFAULT; + } + } + break; + default: + printk(KERN_ERR "akisp: the ioctl is unknow. cmd=0x%X\n", cmd); + ret = -EINVAL; + break; + } + +fini: + mutex_unlock(&priv->lock); + return ret; +} + +void *dmamalloc(unsigned long bytes, void *handle) +{ + void *ptr; + + ptr = dma_alloc_coherent(NULL, bytes, handle, GFP_KERNEL); + printk("dma alloc vir:0x%p, phy:0x%x\n", ptr, *(dma_addr_t *)handle); + + return ptr; +} + +void dmafree(void *ptr, unsigned long bytes, unsigned long handle) +{ + printk("dma free vir:0x%p, phy:0x%lx, bytes=%ld\n", ptr, handle, bytes); + dma_free_coherent(NULL, bytes, ptr, handle); +} + +void *ispmalloc(unsigned long bytes) +{ + return kzalloc(bytes, GFP_KERNEL); +} + +static int akisp_open(struct inode *node, struct file *file) +{ + int err; + void *base; + AK_ISP_FUNC_CB cb; + AK_ISP_SENSOR_CB *sensor_cb; + + + mutex_lock(&priv->lock); + + priv->used_cnt++; + + if (priv->base) { + printk("isp char device had opend, no need to init more\n"); + goto fini; + } + + cb.cb_printk = (ISPDRV_CB_PRINTK)printk; + cb.cb_memcpy = (ISPDRV_CB_MEMCPY)memcpy; + cb.cb_memset = (ISPDRV_CB_MEMSET)memset; + cb.cb_malloc = (ISPDRV_CB_MALLOC)ispmalloc; + cb.cb_free = (ISPDRV_CB_FREE)kfree; + cb.cb_dmamalloc = dmamalloc; + cb.cb_dmafree = dmafree; + cb.cb_msleep = (ISPDRV_CB_MSLEEP)msleep; + + sensor_cb = ak_sensor_get_sensor_cb(); + + if (!request_mem_region(AKISP_REG_MEM_START, RESOURCE_SIZE, AKISP_DEV_NAME)) { + err = -EBUSY; + goto fail_req4reg; + } + + base = ioremap_nocache(AKISP_REG_MEM_START, RESOURCE_SIZE); + if (!base) { + err = -ENOMEM; + goto fail_ior4reg; + } + + err = isp2_module_init(&cb, sensor_cb, base); + if (err) { + goto fail_isp2_init; + } + priv->base = base; + +fini: + mutex_unlock(&priv->lock); + return 0; + +fail_isp2_init: + iounmap(base); +fail_ior4reg: + release_mem_region(AKISP_REG_MEM_START, RESOURCE_SIZE); +fail_req4reg: + mutex_unlock(&priv->lock); + return err; +} + +static int akisp_release(struct inode *node, struct file *file) +{ + int chn; + AK_ISP_OSD_MEM_ATTR *isp_osd_mem = NULL; + struct akisp_osd_info *p_osd_info; + + mutex_lock(&priv->lock); + + priv->used_cnt--; + if (priv->used_cnt) { + goto fini; + } + + isp2_module_fini(); + + for (chn = 0; chn < ISP_OSD_CHN_NUM; chn++) { + p_osd_info = &priv->osd_info[chn]; + if (p_osd_info->main_osd_vaddr) { + iounmap(p_osd_info->main_osd_vaddr); + p_osd_info->main_osd_vaddr = NULL; + p_osd_info->main_osd_paddr = NULL; + p_osd_info->main_osd_byte_size = 0; + } + + if (p_osd_info->sub_osd_vaddr) { + iounmap(p_osd_info->sub_osd_vaddr); + p_osd_info->sub_osd_vaddr = NULL; + p_osd_info->sub_osd_paddr = NULL; + p_osd_info->sub_osd_byte_size = 0; + } + + /* release osd irq dma memory */ + isp_osd_mem = &p_osd_info->main_osd_irq_dma; + printk("%s %d, release main osd addr: %p\n", + __func__, __LINE__, isp_osd_mem->dma_vaddr); + if (isp_osd_mem->dma_vaddr) { + printk("%s %d, unmap main osd\n", __func__, __LINE__); + iounmap(isp_osd_mem->dma_vaddr); + isp_osd_mem->dma_vaddr = NULL; + } + + printk("%s %d, release main osd res ok\n", __func__, __LINE__); + + isp_osd_mem = &p_osd_info->sub_osd_irq_dma; + printk("%s %d, release sub osd addr: %p\n", + __func__, __LINE__, isp_osd_mem->dma_vaddr); + if (isp_osd_mem->dma_vaddr) { + iounmap(isp_osd_mem->dma_vaddr); + isp_osd_mem->dma_vaddr = NULL; + } + } + + printk("%s %d, release sub osd res ok\n", __func__, __LINE__); + + iounmap(priv->base); + release_mem_region(AKISP_REG_MEM_START, RESOURCE_SIZE); + + priv->base = NULL; + + printk("%s %d, release akisp\n", __func__, __LINE__); +fini: + mutex_unlock(&priv->lock); + return 0; +} + +static const struct file_operations akisp_ops = { + .owner = THIS_MODULE, + .open = akisp_open, + .release = akisp_release, + .unlocked_ioctl = akisp_ioctl, +}; + +static struct miscdevice akisp_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = AKISP_DEV_NAME, + .fops = &akisp_ops, + .mode = S_IRWXO, +}; + +int __init akisp_init(void) +{ + int err = 0; + printk(KERN_ERR "%s\n", __func__); + if (misc_register(&akisp_dev)) { + printk(KERN_ERR "akisp: Unable register misc device.\n"); + err = -ENODEV; + goto fail_reg; + } + + priv = kzalloc(sizeof(struct akisp_char_pirv), GFP_KERNEL); + if (!priv) { + printk("%s no mem\n", __func__); + err = -ENOMEM; + goto fail_mem4isp; + } + + mutex_init(&priv->lock); + + return 0; + +fail_mem4isp: + misc_deregister(&akisp_dev); +fail_reg: + return err; +} + +void __exit akisp_exit(void) +{ + misc_deregister(&akisp_dev); + kfree(priv); + priv = NULL; +} + +module_init(akisp_init); +module_exit(akisp_exit); + +MODULE_AUTHOR("Anyka Microelectronic Ltd."); +MODULE_DESCRIPTION("Anyka isp apply for user space control"); +MODULE_ALIAS("Anyka ISP Apply"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/misc/ak_isp_char.h b/drivers/misc/ak_isp_char.h new file mode 100644 index 00000000..4b824121 --- /dev/null +++ b/drivers/misc/ak_isp_char.h @@ -0,0 +1,281 @@ +#ifndef __AK_ISP_CHAR_H__ +#define __AK_ISP_CHAR_H__ + +#define AKISP_MAGIC 'I' +#define AK_ISP_VP_GET_BLC _IOR(AKISP_MAGIC, 1, int) +#define AK_ISP_VP_SET_BLC _IOW(AKISP_MAGIC, 2, int) +#define AK_ISP_VP_GET_LSC _IOR(AKISP_MAGIC, 3, int) +#define AK_ISP_VP_SET_LSC _IOW(AKISP_MAGIC, 4, int) +#define AK_ISP_VP_GET_GB _IOR(AKISP_MAGIC, 5, int) +#define AK_ISP_VP_SET_GB _IOW(AKISP_MAGIC, 6, int) +#define AK_ISP_VP_GET_GB_LINKAGE _IOR(AKISP_MAGIC, 7, int) +#define AK_ISP_VP_SET_GB_LINKAGE _IOW(AKISP_MAGIC, 8, int) +#define AK_ISP_VP_GET_RAW_LUT _IOR(AKISP_MAGIC, 9, int) +#define AK_ISP_VP_SET_RAW_LUT _IOW(AKISP_MAGIC, 10, int) +#define AK_ISP_VP_GET_RAW_NR1 _IOR(AKISP_MAGIC, 11, int) +#define AK_ISP_VP_SET_RAW_NR1 _IOW(AKISP_MAGIC, 12, int) +#define AK_ISP_VP_GET_DEMO _IOR(AKISP_MAGIC, 13, int) +#define AK_ISP_VP_SET_DEMO _IOW(AKISP_MAGIC, 14, int) +#define AK_ISP_GET_DPC _IOR(AKISP_MAGIC, 15, int) +#define AK_ISP_SET_DPC _IOW(AKISP_MAGIC, 16, int) +#define AK_ISP_GET_CCM _IOR(AKISP_MAGIC, 17, int) +#define AK_ISP_SET_CCM _IOW(AKISP_MAGIC, 18, int) +#define AK_ISP_GET_CCM_EX _IOR(AKISP_MAGIC, 19, int) +#define AK_ISP_SET_CCM_EX _IOW(AKISP_MAGIC, 20, int) + +#define AK_ISP_SET_WHITE_COLOR_S _IOW(AKISP_MAGIC, 21, int) +#define AK_ISP_GET_WHITE_COLOR_S _IOR(AKISP_MAGIC, 22, int) +#define AK_ISP_SET_CCM_FINE _IOW(AKISP_MAGIC, 23, int) +#define AK_ISP_GET_CCM_FINE _IOR(AKISP_MAGIC, 24, int) + +#define AK_ISP_GET_RGB_GAMMA _IOR(AKISP_MAGIC, 25, int) +#define AK_ISP_SET_RGB_GAMMA _IOW(AKISP_MAGIC, 26, int) + +#define AK_ISP_SET_WDR _IOW(AKISP_MAGIC, 27, int) +#define AK_ISP_GET_WDR _IOR(AKISP_MAGIC, 28, int) +#define AK_ISP_SET_WDR_EX _IOW(AKISP_MAGIC, 29, int) +#define AK_ISP_GET_WDR_EX _IOR(AKISP_MAGIC, 30, int) +#define AK_ISP_SET_EDGE _IOW(AKISP_MAGIC, 31, int) +#define AK_ISP_GET_EDGE _IOR(AKISP_MAGIC, 32, int) +#define AK_ISP_SET_EDGE_EX _IOW(AKISP_MAGIC, 33, int) +#define AK_ISP_GET_EDGE_EX _IOR(AKISP_MAGIC, 34, int) +#define AK_ISP_SET_EDGE_LINKAGE _IOW(AKISP_MAGIC, 35, int) +#define AK_ISP_GET_EDGE_LINKAGE _IOR(AKISP_MAGIC, 36, int) +#define AK_ISP_SET_SHARP _IOW(AKISP_MAGIC, 37, int) +#define AK_ISP_GET_SHARP _IOR(AKISP_MAGIC, 38, int) +#define AK_ISP_SET_SHARP_EX _IOW(AKISP_MAGIC, 39, int) +#define AK_ISP_GET_SHARP_EX _IOR(AKISP_MAGIC, 40, int) +#define AK_ISP_SET_SHARP_LINKAGE _IOW(AKISP_MAGIC, 41, int) +#define AK_ISP_GET_SHARP_LINKAGE _IOR(AKISP_MAGIC, 42, int) +#define AK_ISP_SET_Y_NR2 _IOW(AKISP_MAGIC, 43, int) +#define AK_ISP_GET_Y_NR2 _IOR(AKISP_MAGIC, 44, int) +#define AK_ISP_SET_Y_NR2_LINKAGE _IOW(AKISP_MAGIC, 45, int) +#define AK_ISP_GET_Y_NR2_LINKAGE _IOR(AKISP_MAGIC, 46, int) +#define AK_ISP_SET_3D_NR _IOW(AKISP_MAGIC, 47, int) +#define AK_ISP_GET_3D_NR _IOR(AKISP_MAGIC, 48, int) +#define AK_ISP_SET_3D_NR_EX _IOW(AKISP_MAGIC, 49, int) +#define AK_ISP_GET_3D_NR_EX _IOR(AKISP_MAGIC, 50, int) +#define AK_ISP_SET_3D_NR_LINKAGE _IOW(AKISP_MAGIC, 51, int) +#define AK_ISP_GET_3D_NR_LINKAGE _IOR(AKISP_MAGIC, 52, int) + +#define AK_ISP_GET_FCS _IOR(AKISP_MAGIC, 53, int) +#define AK_ISP_SET_FCS _IOW(AKISP_MAGIC, 54, int) + +#define AK_ISP_SET_FCS_LINKAGE _IOW(AKISP_MAGIC, 55, int) +#define AK_ISP_GET_FCS_LINKAGE _IOR(AKISP_MAGIC, 56, int) +#define AK_ISP_SET_CONTRAST _IOW(AKISP_MAGIC, 57, int) +#define AK_ISP_GET_CONTRAST _IOR(AKISP_MAGIC, 58, int) +#define AK_ISP_SET_SAT _IOW(AKISP_MAGIC, 59, int) +#define AK_ISP_GET_SAT _IOR(AKISP_MAGIC, 60, int) +#define AK_ISP_SET_SAT_LINKAGE _IOW(AKISP_MAGIC, 61, int) +#define AK_ISP_GET_SAT_LINKAGE _IOR(AKISP_MAGIC, 62, int) +#define AK_ISP_SET_RGB2YUV _IOW(AKISP_MAGIC, 63, int) +#define AK_ISP_GET_RGB2YUV _IOR(AKISP_MAGIC, 64, int) +#define AK_ISP_SET_YUV_EFFECT _IOW(AKISP_MAGIC, 65, int) +#define AK_ISP_GET_YUV_EFFECT _IOR(AKISP_MAGIC, 66, int) +#define AK_ISP_SET_RAW_HIST _IOW(AKISP_MAGIC, 67, int) +#define AK_ISP_GET_RAW_HIST _IOR(AKISP_MAGIC, 68, int) + +#define AK_ISP_GET_RAW_HIST_STAT _IOR(AKISP_MAGIC, 69, int) +#define AK_ISP_SET_RGB_HIST _IOW(AKISP_MAGIC, 70, int) +#define AK_ISP_GET_RGB_HIST _IOR(AKISP_MAGIC, 71, int) +#define AK_ISP_GET_RGB_HIST_STAT _IOR(AKISP_MAGIC, 72, int) + +#define AK_ISP_SET_Y_HIST _IOW(AKISP_MAGIC, 73, int) +#define AK_ISP_GET_Y_HIST _IOR(AKISP_MAGIC, 74, int) +#define AK_ISP_GET_Y_HIST_STAT _IOR(AKISP_MAGIC, 75, int) +#define AK_ISP_SET_EXP_TYPE _IOW(AKISP_MAGIC, 76, int) +#define AK_ISP_GET_EXP_TYPE _IOR(AKISP_MAGIC, 77, int) +#define AK_ISP_SET_FRAME_RATE _IOW(AKISP_MAGIC, 78, int) +#define AK_ISP_GET_FRAME_RATE _IOR(AKISP_MAGIC, 79, int) +#define AK_ISP_SET_AE _IOW(AKISP_MAGIC, 80, int) +#define AK_ISP_GET_AE _IOR(AKISP_MAGIC, 81, int) +#define AK_ISP_GET_AE_RUN_INFO _IOR(AKISP_MAGIC, 82, int) +#define AK_ISP_SET_WB_TYPE _IOW(AKISP_MAGIC, 83, int) +#define AK_ISP_GET_WB_TYPE _IOR(AKISP_MAGIC, 84, int) +#define AK_ISP_SET_AWB _IOW(AKISP_MAGIC, 85, int) +#define AK_ISP_GET_AWB _IOR(AKISP_MAGIC, 86, int) +#define AK_ISP_SET_AWB_DEFAULT _IOW(AKISP_MAGIC, 87, int) +#define AK_ISP_GET_AWB_DEFAULT _IOR(AKISP_MAGIC, 88, int) +#define AK_ISP_GET_AWB_STAT_INFO _IOR(AKISP_MAGIC, 89, int) + +#define AK_ISP_SET_MASK_COLOR _IOW(AKISP_MAGIC, 92, int) +#define AK_ISP_GET_MASK_COLOR _IOR(AKISP_MAGIC, 93, int) +#define AK_ISP_SET_WEIGHT _IOW(AKISP_MAGIC, 94, int) +#define AK_ISP_GET_WEIGHT _IOR(AKISP_MAGIC, 95, int) +#define AK_ISP_SET_AF _IOW(AKISP_MAGIC, 96, int) +#define AK_ISP_GET_AF _IOR(AKISP_MAGIC, 97, int) +#define AK_ISP_GET_AF_STAT _IOR(AKISP_MAGIC, 98, int) +#define AK_ISP_SET_MWB _IOW(AKISP_MAGIC, 99, int) +#define AK_ISP_GET_MWB _IOR(AKISP_MAGIC, 100,int) + +#define AK_ISP_SET_MAIN_CHAN_MASK_AREA _IOW(AKISP_MAGIC, 90, int) +#define AK_ISP_GET_MAIN_CHAN_MASK_AREA _IOR(AKISP_MAGIC, 91, int) +#define AK_ISP_SET_SUB_CHAN_MASK_AREA _IOW(AKISP_MAGIC, 101, int) +#define AK_ISP_GET_SUB_CHAN_MASK_AREA _IOR(AKISP_MAGIC, 102, int) + +#define AK_ISP_SET_3D_NR_REF _IOW(AKISP_MAGIC, 103, int) +#define AK_ISP_GET_3D_NR_REF _IOR(AKISP_MAGIC, 104, int) + +#define AK_ISP_INIT_SENSOR_DEV _IOW(AKISP_MAGIC, 105, int) +#define AK_ISP_SET_3D_NR_PHYADDR _IOW(AKISP_MAGIC, 106, int) +#define AK_ISP_SET_SENSOR_REG _IOW(AKISP_MAGIC, 107, int) +#define AK_ISP_GET_SENSOR_REG _IOR(AKISP_MAGIC, 108, int) +#define AK_ISP_SET_USER_PARAMS _IOW(AKISP_MAGIC, 109, int) +#define AK_ISP_SET_MISC_ATTR _IOW(AKISP_MAGIC, 110, int) +#define AK_ISP_GET_MISC_ATTR _IOR(AKISP_MAGIC, 111, int) + +#define AK_ISP_GET_3D_NR_STAT_INFO _IOR(AKISP_MAGIC, 112, int) +#define AK_ISP_GET_SENSOR_ID _IOR(AKISP_MAGIC, 113, int) + +#define AK_ISP_SET_ISP_CAPTURING _IOW(AKISP_MAGIC, 114, int) + +#define AK_ISP_SET_AWB_EX _IOW(AKISP_MAGIC, 115, int) +#define AK_ISP_GET_AWB_EX _IOR(AKISP_MAGIC, 116, int) + +#define AK_ISP_SET_Y_GAMMA _IOW(AKISP_MAGIC, 117, int) +#define AK_ISP_GET_Y_GAMMA _IOR(AKISP_MAGIC, 118, int) + +#define AK_ISP_SET_HUE _IOW(AKISP_MAGIC, 119, int) +#define AK_ISP_GET_HUE _IOR(AKISP_MAGIC, 120, int) + +#define AK_ISP_SET_FLIP_MIRROR _IOW(AKISP_MAGIC, 121, int) +#define AK_ISP_SET_SENSOR_FPS _IOW(AKISP_MAGIC, 122, int) +#define AK_ISP_GET_SENSOR_FPS _IOR(AKISP_MAGIC, 123, int) + +#define AK_ISP_GET_WORK_SCENE _IOR(AKISP_MAGIC, 124, int) +#define AK_ISP_GET_ISO _IOR(AKISP_MAGIC, 125, int) + + +#define AK_ISP_SET_AF_WIN34 _IOW(AKISP_MAGIC, 126, int) + +#define AK_ISP_SET_MAE _IOW(AKISP_MAGIC, 127, int) +#define AK_ISP_GET_MAE _IOR(AKISP_MAGIC, 128, int) + +/* Blow is the params that user can adjust in real time */ +typedef struct { + int id; + unsigned char data[128]; +} AK_ISP_USER_PARAM; + +struct isp_zoom_info { + int channel; + int cut_xpos; + int cut_ypos; + int cut_width; + int cut_height; + int out_width; + int out_height; +}; + +struct isp_channel2_info { + int width; + int height; +}; + +struct isp_mask_area { + unsigned short start_xpos; + unsigned short end_xpos; + unsigned short start_ypos; + unsigned short end_ypos; + unsigned char enable; +}; + +struct isp_mask_area_info { + struct isp_mask_area mask[4]; +}; + +struct isp_mask_color_info { + unsigned char color_type; + unsigned char mk_alpha; + unsigned char y_mk_color; + unsigned char u_mk_color; + unsigned char v_mk_color; +}; + +struct isp_gamma_info { + int value; +}; + +struct isp_saturation_info { + int value; +}; + +struct isp_brightness_info { + int value; +}; + +struct isp_contrast_info { + int value; +}; + +struct isp_sharp_info { + int value; +}; + +struct isp_power_line_freq_info { + int value; +}; + +struct isp_flip_mirror_info { + int flip_en; + int mirror_en; +}; + +struct isp_osd_color_table_attr { + unsigned int color_table[16]; +}; + +typedef enum isp_osd_channel { + ISP_OSD_CHN0 = 0, + ISP_OSD_CHN1, + ISP_OSD_CHN2, + ISP_OSD_CHN_NUM +} ISP_OSD_CHANNEL; + +struct isp_osd_context_attr { + ISP_OSD_CHANNEL chn; + unsigned char *osd_context_addr; + unsigned int osd_width; + unsigned int osd_height; + unsigned short start_xpos; + unsigned short start_ypos; + unsigned short alpha; + unsigned short enable; +}; + +struct isp_osd_mem_attr { + ISP_OSD_CHANNEL chn; + unsigned char *dma_paddr; + unsigned int size; +}; + +#define AK_ISP_USER_CID_SET_ZOOM _IOW(AKISP_MAGIC, 0x00010000, int) +#define AK_ISP_USER_CID_SET_SUB_CHANNEL _IOW(AKISP_MAGIC, 0x00010001, int) +#define AK_ISP_USER_CID_SET_OCCLUSION _IOW(AKISP_MAGIC, 0x00010002, int) +#define AK_ISP_USER_CID_SET_OCCLUSION_COLOR _IOW(AKISP_MAGIC, 0x00010003, int) +#define AK_ISP_USER_CID_SET_GAMMA _IOW(AKISP_MAGIC, 0x00010004, int) +#define AK_ISP_USER_CID_SET_SATURATION _IOW(AKISP_MAGIC, 0x00010005, int) +#define AK_ISP_USER_CID_SET_BRIGHTNESS _IOW(AKISP_MAGIC, 0x00010006, int) +#define AK_ISP_USER_CID_SET_CONTRAST _IOW(AKISP_MAGIC, 0x00010007, int) +#define AK_ISP_USER_CID_SET_SHARPNESS _IOW(AKISP_MAGIC, 0x00010008, int) + +#define AK_ISP_USER_CID_SET_POWER_LINE_FREQUENCY \ + _IOW(AKISP_MAGIC, 0x00010009, int) + +#define AK_ISP_USER_CID_SET_OSD_COLOR_TABLE_ATTR \ + _IOW(AKISP_MAGIC, 0x0001000a, int) + +#define AK_ISP_USER_CID_SET_MAIN_CHANNEL_OSD_CONTEXT_ATTR \ + _IOW(AKISP_MAGIC, 0x0001000b, int) + +#define AK_ISP_USER_CID_SET_SUB_CHANNEL_OSD_CONTEXT_ATTR \ + _IOW(AKISP_MAGIC, 0x0001000c, int) + +#define AK_ISP_USER_CID_SET_MAIN_CHANNEL_OSD_MEM_ATTR \ + _IOW(AKISP_MAGIC, 0x0001000d, int) + +#define AK_ISP_USER_CID_SET_SUB_CHANNEL_OSD_MEM_ATTR \ + _IOW(AKISP_MAGIC, 0x0001000e, int) + +#endif diff --git a/drivers/misc/ak_motor.c b/drivers/misc/ak_motor.c new file mode 100644 index 00000000..a39728ad --- /dev/null +++ b/drivers/misc/ak_motor.c @@ -0,0 +1,856 @@ +/* + * @file /driver/misc/ak_motor.c + * @brief AK On-chip motor driver + * Copyright C 2013 Anyka CO.,LTD + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * @author lixinhai + * @date 2013-05-11 + * @note 2013-05-11 created + * @note 2013-05-14 add more comments + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define AK_MOTOR_DEVNAME "ak-motor" + +//#define MOTOR_DEBUG + +#ifdef MOTOR_DEBUG +#define PDEBUG(fmt, args...) pr_info("ak-motor:" fmt, ##args) +#else +#define PDEBUG(fmt, args...) +#endif + + +#define MOTOR_TURN_CLKWISE (0) /*turn by clock wise*/ +#define MOTOR_TURN_ANTICLKWISE (1) /*turn by anti clock wise*/ +#define MOTOR_STEP_PERIOD (64) +#define MOTOR_STEP_ANGLE (360/MOTOR_STEP_PERIOD) +#define MOTOR_STEP_REMAIN (360%MOTOR_STEP_PERIOD) +#define MOTOR_DEFAULT_DELAY_MS (2) +#define MOTOR_MIN_DELAY_MS (2) + +/*motor device list*/ +static LIST_HEAD(motor_list); +static DEFINE_MUTEX(list_lock); + +struct ak_motor_runtime +{ + int cw; + int steps; + int remain_steps; + int ctrl_index; //point to ctrl_tbl_cw + int running; +#define MOTOR_STATUS_RUNNING (1) +#define MOTOR_STATUS_STOPING (2) +#define MOTOR_STATUS_STOPED (3) +}; + +struct ak_motor { + struct miscdevice miscdev; + struct ak_motor_plat_data *pdata; + int irq_hit[AK_MOTOR_HIT_NUM]; /*hit feedback irq descriptor code*/ + int hit_pin[AK_MOTOR_HIT_NUM]; /*hit feedback irq pin.*/ + int trigger_level[AK_MOTOR_HIT_NUM]; /*irq trigger level*/ + + int irq_hit_type[AK_MOTOR_HIT_NUM]; /*current trigger level*/ + int trigger_irq; /*in course of trigger irq index.*/ + int phase_pin[AK_MOTOR_PHASE_NUM]; /*phase pin*/ + spinlock_t lock; + u32 angular_speed; + struct timer_list detect_timer; + + void *hw_timer; + + struct ak_motor_runtime runtime; + struct list_head list; + int index; + + struct ak_motor_dev *curr_dev; + + int no_limit_switch; +}; + +struct ak_motor_dev { + struct ak_motor *motor; + spinlock_t lock; + int rd_flags; + struct notify_data data; /*notify data to user.*/ + int is_open; + wait_queue_head_t event; + + /// Anycloud 接扩向下兼容。 + struct motor_parm Param; +}; + +/*clockwise*/ +static const unsigned char ctrl_tbl_cw[] = {0x03, 0x06, 0x0c, 0x09}; +static const int cycle_steps = 2048; +static const int ctrl_phrase = (sizeof (ctrl_tbl_cw) / sizeof (ctrl_tbl_cw[0])); + +/*anticlockwise*/ +//static unsigned char ctrl_tbl_acw[] = {0x03, 0x09, 0x0c, 0x06}; + +static void ak_motor_stop(struct ak_motor_dev *motor_dev); +static int ak_motor_hw_timer_handler(void *data); + +#define ms_unit (1000) +static inline int get_delay_by_speed(int speed) +{ + int time; + int steps; + + steps = (speed * 64 * 64) / (360 * 2); + time = ms_unit / steps; + + if (time < MOTOR_DEFAULT_DELAY_MS) + time = MOTOR_DEFAULT_DELAY_MS; + + return time; +} + + +/** + * * @brief ak motor open + * * + * * open. + * * @author lixinhai + * * @date 2013-03-20 + * * @param[in] inode pointer. + * * @param[in] file pointer. + * * @return int return exec success or failed + * * @retval returns zero on success + * * @retval return a non-zero error code if failed + * */ +static int ak_motor_open(struct inode *inode, struct file *file) +{ + int minor; + bool found = false; + struct ak_motor *motor; + struct ak_motor_dev *motor_dev; + + PDEBUG("open the motor device.\n"); + + minor = iminor(file->f_path.dentry->d_inode); + list_for_each_entry(motor, &motor_list, list) { + if(minor == motor->miscdev.minor) { + found = true; + break; + } + } + + if(found == false) { + printk("ak-motor no device.\n"); + return -ENODEV; + } + + if(motor->curr_dev != NULL) { + printk("warning ak-motor had opened.\n"); + motor_dev = motor->curr_dev; + } else { + motor_dev = kzalloc(sizeof *motor_dev, GFP_KERNEL); + if(!motor_dev) { + printk("ak-motor: alloc the motor dev err, open fail.\n"); + return -ENOMEM; + } + + init_waitqueue_head(&motor_dev->event); + motor_dev->motor = motor; + motor->curr_dev = motor_dev; + motor_dev->data.event = 0; + motor_dev->is_open = 1; + spin_lock_init(&motor_dev->lock); + + /// Init Parameter. + motor_dev->Param.pos = 0; + motor_dev->Param.steps_one_circle = cycle_steps; + motor_dev->Param.total_steps = cycle_steps; + motor_dev->Param.speed_step = 1000 / get_delay_by_speed (motor->angular_speed); + motor_dev->Param.boundary_steps = 0; + } + + file->private_data = motor_dev; + printk("open ak motor device success.\n"); + return 0; +} + +static int ak_motor_dev_close(struct ak_motor *motor) +{ + //struct ak_motor_dev *motor_dev = file->private_data; + //struct ak_motor *motor = motor_dev->motor; + struct ak_motor_dev *motor_dev; + + BUG_ON(motor==NULL); + + motor_dev = motor->curr_dev; + + motor_dev->is_open = 0; + ak_motor_stop(motor_dev); + + motor_dev->motor->curr_dev = NULL; + kfree(motor_dev); + printk("close ak motor device success.\n"); + return 0; +} + +/** + * @brief ak motor close + * + * close. + * @author lixinhai + * @date 2013-03-20 + * @param[in] inode pointer. + * @param[in] file pointer. + * @return int return exec success or failed + * @retval returns zero on success + * @retval return a non-zero error code if failed + */ +static int ak_motor_close(struct inode *inode, struct file *file) +{ + int minor; + bool found = false; + struct ak_motor *motor; + struct ak_motor_dev *motor_dev; + + PDEBUG("close the motor device.\n"); + + minor = iminor(file->f_path.dentry->d_inode); + list_for_each_entry(motor, &motor_list, list) { + if(minor == motor->miscdev.minor) { + found = true; + break; + } + } + + if(found == false) { + printk("ak-motor no device.\n"); + return -ENODEV; + } + + motor_dev = motor->curr_dev; + ak_motor_stop(motor_dev); + + return 0; +} + + +/** + * @brief ak motor read + * + * read. + * @author lixinhai + * @date 2013-03-20 + * @param[in] file pointer. + * @param[in] data buf. + * @param[in] data count. + * @param[in] data offset. + * @return int return read data count. + */ +static ssize_t ak_motor_read(struct file *file, char __user *data, size_t len, loff_t *ofs) +{ + unsigned long flags; + int ret = 0; + struct ak_motor_dev *motor_dev = file->private_data; + + PDEBUG("read the motor data, len:%d\n", len); + wait_event_interruptible(motor_dev->event, motor_dev->rd_flags != 0); + + spin_lock_irqsave(&motor_dev->lock, flags); + + ret = copy_to_user(data, &motor_dev->data, sizeof(struct notify_data)); + + memset(&motor_dev->data, 0, sizeof(struct notify_data)); + motor_dev->rd_flags = 0; + PDEBUG("copy to user data, ret:%d.\n", ret); + spin_unlock_irqrestore(&motor_dev->lock, flags); + + return ret; +} + + +static unsigned int ak_motor_poll(struct file *file, struct poll_table_struct *wait) +{ + unsigned int mask = 0; + struct ak_motor_dev *motor_dev = file->private_data; + + PDEBUG("motor poll.\n"); + + poll_wait(file, &motor_dev->event, wait); + + if(motor_dev->rd_flags != 0) + mask |= POLLIN; + + return mask; +} + +/** + * @brief control the motor turn. + * + * read. + * @author lixinhai + * @date 2013-03-20 + * @param[in] ak_motor_dev. + * @param[in] is clockwise. + * @param[in] turn angle.. + * @return int return exec success or failed + * @retval returns zero on success + * @retval return a non-zero error code if failed + */ +static int ak_motor_turn(struct ak_motor_dev *motor_dev, int cw, int steps) +{ + int ret; + struct ak_motor *motor = motor_dev->motor; + unsigned long flags; + + spin_lock_irqsave(&motor_dev->lock, flags); + motor->runtime.cw = cw; + motor->runtime.steps = steps; + motor->runtime.remain_steps = steps; + motor->runtime.running = MOTOR_STATUS_RUNNING; + + //printk(KERN_ERR "total motor run steps:%d\n\n",motor->runtime.steps); + + +// printk(KERN_ERR "delay=%d, angle=%d\n", delay, angle); + ret = ak39_timer_start(ak_motor_hw_timer_handler, motor, motor->hw_timer, motor_dev->Param.speed_step); + if (ret) { + printk(KERN_ERR "open timer for ptz failed! !\n"); + } + + spin_unlock_irqrestore(&motor_dev->lock, flags); + return ret; +} + +static void ak_motor_stop(struct ak_motor_dev *motor_dev) +{ + unsigned long flags; + struct ak_motor *motor = motor_dev->motor; + + spin_lock_irqsave(&motor_dev->lock, flags); + motor->runtime.running = MOTOR_STATUS_STOPING; + spin_unlock_irqrestore(&motor_dev->lock, flags); +} + +static long ak_motor_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int val; + int err = 0, ret = 0; + struct ak_motor_dev *motor_dev = file->private_data; + struct ak_motor *motor = motor_dev->motor; + + PDEBUG("exec the motor ioctl.\n"); + /* Check type and command number */ + if (_IOC_TYPE(cmd) != AK_MOTOR_IOC_MAGIC) + return -ENOTTY; + + /* Check access direction once here; don't repeat below. + * IOC_DIR is from the user perspective, while access_ok is + * from the kernel perspective; so they look reversed. + */ + if (_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, + (void __user *)arg, _IOC_SIZE(cmd)); + if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, + (void __user *)arg, _IOC_SIZE(cmd)); + if (err) + return -EFAULT; + + switch(cmd) { + case AK_MOTOR_SET_ANG_SPEED: { + + u32 delay = 1; + __get_user(val, (int __user*)arg); + PDEBUG("ak motor set angular speed %d.\n", val); + if((val AK_MOTOR_MAX_SPEED) + return -EINVAL; + + motor->angular_speed = val; + delay = get_delay_by_speed(motor->angular_speed); + delay = (delay >= MOTOR_MIN_DELAY_MS) ? (delay) : MOTOR_MIN_DELAY_MS; + motor_dev->Param.speed_step = 1000 / delay; + + break; + } + case AK_MOTOR_GET_ANG_SPEED: + PDEBUG("ak motor get angular speed.\n"); + __put_user(motor->angular_speed, (int __user*)arg); + break; + case AK_MOTOR_TURN_CLKWISE: + __get_user(val, (int __user*)arg); + PDEBUG("ak motor turn clk wise, angle:%d\n", val); + if(val < 0) + return -EINVAL; + + ak_motor_turn(motor_dev, MOTOR_TURN_CLKWISE, val); + break; + case AK_MOTOR_TURN_ANTICLKWISE: + __get_user(val, (int __user*)arg); + PDEBUG("ak motor turn anti clkwise, angle:%d\n", val); + if(val < 0) + return -EINVAL; + + ak_motor_turn(motor_dev, MOTOR_TURN_ANTICLKWISE, val); + break; + case AK_MOTOR_GET_HIT_STATUS: + val = 0; + ret = ak_gpio_getpin(motor->hit_pin[AK_MOTOR_HIT_LEFT]); + if(ret == (motor->trigger_level[AK_MOTOR_HIT_LEFT] == IRQ_TYPE_LEVEL_HIGH)) + val |= AK_MOTOR_HITTING_LEFT; + + ret = ak_gpio_getpin(motor->hit_pin[AK_MOTOR_HIT_RIGHT]); + if(ret == (motor->trigger_level[AK_MOTOR_HIT_RIGHT] == IRQ_TYPE_LEVEL_HIGH)) + val |= AK_MOTOR_HITTING_RIGHT; + + PDEBUG("ak motor get status, val:%d\n", val); + __put_user(val, (int __user*)arg); + case AK_MOTOR_TURN_STOP: + PDEBUG("ak motor stop request.\n"); + ak_motor_stop(motor_dev); + break; + + /// 以下命令引用自 AnycloudV500 平台。 + case MOTOR_GET_STATUS: { + struct motor_message Mesg; + Mesg.total_steps = motor_dev->Param.total_steps; + Mesg.speed_step = motor_dev->Param.speed_step; + Mesg.steps_one_circle = motor_dev->Param.steps_one_circle; + Mesg.status = MOTOR_STATUS_RUNNING == motor_dev->motor->runtime.running ? MOTOR_IS_RUNNING : MOTOR_IS_STOP; + Mesg.boundary_steps = 0; + Mesg.attach_timer = 0; + Mesg.speed_angle = 0; + Mesg.pos = motor_dev->Param.pos; + + if (!access_ok (VERIFY_WRITE, (void __user *)arg, sizeof(struct motor_message))) { + ret = -EFAULT; + break; + } + + ret = __copy_to_user(arg, (void *)(&Mesg), sizeof(Mesg)); + break; + } + + + case MOTOR_PARM: { + + struct motor_parm Param; + + if (__copy_from_user((void *)(&Param), arg, sizeof(Param)) < 0) { + ret = -EFAULT; + break; + } + + /// 参数合法性判定。 + if (Param.steps_one_circle <= 0 + || Param.total_steps <= 0 + || Param.total_steps > Param.steps_one_circle + || Param.pos < 0 + || Param.pos >= Param.total_steps + || Param.speed_step <= 0) { + return -EINVAL; + } + + /// 记录参数。 + memcpy (&motor_dev->Param, &Param, sizeof (Param)); + break; + } + + default: + break; + } + + return ret; +} + +static struct file_operations ak_motor_fops = { + .owner = THIS_MODULE, + .open = ak_motor_open, + .release = ak_motor_close, + .read = ak_motor_read, + .poll = ak_motor_poll, + .unlocked_ioctl = ak_motor_ioctl, +}; + +/*********************************************************************/ + +static void ak_motor_event_notify(struct ak_motor *motor, int num, int event) +{ + struct ak_motor_dev *motor_dev = motor->curr_dev; + + if(!motor_dev) + return; + + motor_dev->data.hit_num = num; + motor_dev->data.event |= event; + motor_dev->data.remain_steps = motor->runtime.remain_steps; + motor_dev->rd_flags = 1; + PDEBUG("notify: %s, hit number:%d, event:%d, remain steps:%d.\n", + motor->miscdev.name, num, event, motor->runtime.remain_steps); + + wake_up_interruptible(&motor_dev->event); +} + +/** + * @brief detect the motor hit the boundary or not. + * + * @author lixinhai + * @date 2013-03-20 + * @param[in] timer data pointer. + */ +static void ak_motor_hit_detect(unsigned long data) +{ + int i; + int hit_event; + unsigned long flags; + struct ak_motor *motor = (struct ak_motor*)data; + struct ak_motor_dev *motor_dev = motor->curr_dev; + + PDEBUG("ak motor hit detect.\n"); + spin_lock_irqsave(&motor_dev->lock, flags); + for(i=0; itrigger_irq == motor->irq_hit[i]) { + hit_event = (motor->trigger_level[i] == motor->irq_hit_type[i]) ? + AK_MOTOR_EVENT_HIT : AK_MOTOR_EVENT_UNHIT; + + if(motor->irq_hit_type[i] == IRQ_TYPE_LEVEL_LOW) { + motor->irq_hit_type[i] = IRQ_TYPE_LEVEL_HIGH; + }else { + motor->irq_hit_type[i] = IRQ_TYPE_LEVEL_LOW; + } + irq_set_irq_type(motor->irq_hit[i], motor->irq_hit_type[i]); + enable_irq(motor->irq_hit[i]); + if (hit_event == AK_MOTOR_EVENT_UNHIT) + ak_motor_event_notify(motor, i, hit_event); + break; + } + } + spin_unlock_irqrestore(&motor_dev->lock, flags); +} + +/** + * @brief hitting the boundary interrupt. + * + * @author lixinhai + * @date 2013-03-20 + * @param[in] irq description. + * @param[in] dev_id. + */ +static irqreturn_t ak_motor_hit_irq(int irq, void *dev_id) +{ + struct ak_motor *motor = dev_id; + + PDEBUG("receive the motor hit irq.\n"); + disable_irq_nosync(irq); + motor->trigger_irq = irq; + mod_timer(&motor->detect_timer, jiffies + msecs_to_jiffies(50)); + + return IRQ_HANDLED; +} + +int ak_motor_check_hit_in_runtime_dir(struct ak_motor *motor) +{ + int i; + int hit_level; + int ret = -1; + + if (motor->no_limit_switch) + return ret; + + if (motor->runtime.cw) + i = AK_MOTOR_HIT_RIGHT; + else + i = AK_MOTOR_HIT_LEFT; + + if (motor->trigger_level[i] == IRQ_TYPE_LEVEL_LOW) + hit_level = 0; + else + hit_level = 1; + + if (ak_gpio_getpin(motor->hit_pin[i]) == hit_level) + ret = i; + + if (ret >= 0) + printk("hit_level%d.\n",hit_level); + + return ret; +} + +static int ak_motor_hw_timer_handler(void *data) +{ + unsigned long flags; + int j; + struct ak_motor_dev *motor_dev; + struct ak_motor_runtime *runtime; + struct ak_motor *motor = (struct ak_motor*)data; + char *err_desc = ""; + int num; + int domore = 0; + int ctrl; + + motor_dev = motor->curr_dev; + if(!motor_dev) + return domore; + + spin_lock_irqsave(&motor_dev->lock, flags); + runtime = &motor->runtime; + //printk("[%d:%d]",i, runtime->steps); +/* + PDEBUG("ak motor turn: %s, steps:%d, delay:%d, steps:%d, runtime steps:%d\n", + runtime->cw ?"cw":"acw", runtime->steps, motor->delay, steps, runtime->steps); +*/ + if(unlikely(!motor_dev->is_open)) { + err_desc = "not open"; + goto out; + } + + if(runtime->steps <= 0 || + runtime->running != MOTOR_STATUS_RUNNING) { + //printk(KERN_ERR "ak motor will stop. steps=%d, running=%d\n", runtime->steps, runtime->running); + runtime->running = MOTOR_STATUS_STOPED; + for(j=0; jphase_pin[j], 0); + } + ak_motor_event_notify(motor, 0, AK_MOTOR_EVENT_STOP); + printk(KERN_INFO "finish !! remain_steps:%d\n", runtime->remain_steps); + goto out; + } + + //printk(KERN_ERR "hw:steps=%d\n",runtime->steps); + num = ak_motor_check_hit_in_runtime_dir(motor); + if (num >= 0) { + err_desc = "prev time had hit"; + for(j=0; jphase_pin[j], 0); + } + ak_motor_event_notify(motor, num, AK_MOTOR_EVENT_HIT | AK_MOTOR_EVENT_STOP); + goto out; + } + + ctrl = ctrl_tbl_cw[runtime->ctrl_index]; + if (runtime->cw) { + runtime->ctrl_index = (runtime->ctrl_index + 1) % ctrl_phrase; + } else { + runtime->ctrl_index = (runtime->ctrl_index - 1 + ctrl_phrase) % ctrl_phrase; + } + + for (j=0; jphase_pin[j], !!((1<steps--; + runtime->remain_steps = runtime->steps; + + /// 记录当前步数。 + if (runtime->cw) { + motor_dev->Param.pos -= 1; + motor_dev->Param.pos = (motor_dev->Param.pos < 0) ? 0 : motor_dev->Param.pos; + } else { + motor_dev->Param.pos += 1; + motor_dev->Param.pos = (motor_dev->Param.pos >= motor_dev->Param.total_steps) ? motor_dev->Param.total_steps - 1 : motor_dev->Param.pos; + } + + printk (KERN_DEBUG "Step %d / %d / %d\r\n", motor_dev->Param.pos, + motor_dev->Param.total_steps, motor_dev->Param.steps_one_circle); + spin_unlock_irqrestore(&motor_dev->lock, flags); + return domore; +out: + runtime->running = MOTOR_STATUS_STOPED; + spin_unlock_irqrestore(&motor_dev->lock, flags); + //printk(KERN_ERR "ak motor running stop, out reason:%s\n", err_desc); + ak39_timer_stop(motor->hw_timer); + return domore; +} + + +/** + * @brief initilize the motor gpio. + * + * @author lixinhai + * @date 2013-03-20 + * @param[in] ak_motor. + */ +static int ak_motor_init_cfg(struct ak_motor *motor) +{ + int i, ret = 0; + bool flags = false; + struct ak_motor_plat_data *plat = motor->pdata; + + for(i=0; igpio_phase[i]); + motor->phase_pin[i] = plat->gpio_phase[i].pin; + ak_setpin_as_gpio(motor->phase_pin[i]); + } + + for(i=0; igpio_hit[i].pin < 0) { + motor->no_limit_switch = 1; + flags = true; + goto out; + } + } + + for(i=0; iirq_hit[i] = 0; + if(plat->gpio_hit[i].pin >= 0) { + motor->hit_pin[i] = plat->gpio_hit[i].pin; + ak_gpio_set(&plat->gpio_hit[i]); + + if(!flags) { + setup_timer(&motor->detect_timer, ak_motor_hit_detect, + (unsigned long)motor); + flags = true; + } + motor->irq_hit[i] = ak_gpio_to_irq(motor->hit_pin[i]); + + /*request the hit boundary irq*/ + ret = request_irq(motor->irq_hit[i], ak_motor_hit_irq, + IRQF_DISABLED|IRQF_TRIGGER_LOW, "motor", motor); + if(ret) + goto out; + + printk("ak_motor:request gpio irq ret = %d, irq=%d\n", + ret, motor->irq_hit[i]); + + motor->trigger_level[i] = motor->irq_hit_type[i] = plat->irq_hit_type[i]; + } + } + +out: + return ret; +} + +static int __devinit ak_motor_probe(struct platform_device *pdev) +{ + int ret; + struct ak_motor *motor; + struct ak_motor_plat_data *pdata; + char *name = kzalloc(64, GFP_KERNEL); + + PDEBUG("ak motor driver probe enter.\n"); + if(!name) { + printk("alloc the motor name fail.\n"); + return -ENOMEM; + } + pdata = pdev->dev.platform_data; + if(!pdata) + return -ENODEV; + + motor = kzalloc(sizeof *motor, GFP_KERNEL); + if(!motor) { + printk("alloc the motor device fail.\n"); + return -ENOMEM; + } + + motor->pdata = pdata; + platform_set_drvdata(pdev, motor); + + sprintf(name, "%s%d", pdev->name, pdev->id); + + motor->angular_speed = pdata->angular_speed; + motor->miscdev.minor = MISC_DYNAMIC_MINOR; + motor->miscdev.name = name; + motor->miscdev.fops = &ak_motor_fops; + motor->miscdev.mode = S_IRWXO; + spin_lock_init(&motor->lock); + + ak_motor_init_cfg(motor); + list_add_tail(&motor->list, &motor_list); + + /*register to miscdevice subsystem*/ + ret = misc_register(&motor->miscdev); + if(ret) { + dev_err(&pdev->dev, "register misc device fail.\n"); + ret = -ENOENT; + goto err_misc_dev; + } + + motor->hw_timer = ak39_timer_probe(2 + pdev->id); + if (motor->hw_timer == NULL) { + dev_err(&pdev->dev, "get hw timer for ptz fail.\n"); + ret = -ENOENT; + goto err_hw_timer; + } + PDEBUG("minor:%d\n", motor->miscdev.minor); + printk("init the ak-motor device success.\n"); + return 0; + +err_hw_timer: + misc_deregister(&motor->miscdev); +err_misc_dev: + kfree(motor); + + return ret; +} + +static int __devexit ak_motor_remove(struct platform_device *pdev) +{ + struct ak_motor *motor = platform_get_drvdata(pdev); + + dev_info(&pdev->dev, "remove the ak motor driver.\n"); + ak39_timer_remove(motor->hw_timer); + kfree(motor->miscdev.name); + ak_motor_dev_close(motor); + misc_deregister(&motor->miscdev); + + kfree(motor); + return 0; +} + +static struct platform_driver ak_motor_driver = { + .driver = { + .name = "ak-motor", + .owner = THIS_MODULE, + }, + .probe = ak_motor_probe, + .remove = __devexit_p(ak_motor_remove), +}; + + +static int __init ak_motor_init(void) +{ + printk("AK Motor Driver (c) 2013 ANYKA\n"); + return platform_driver_register(&ak_motor_driver); +} + +static void __exit ak_motor_exit(void) +{ + platform_driver_unregister(&ak_motor_driver); +} + +module_init(ak_motor_init); +module_exit(ak_motor_exit); + +MODULE_AUTHOR("Anyka"); +MODULE_DESCRIPTION("Anyka Motor Device Driver"); +MODULE_ALIAS("platform:ak-motor"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/misc/ak_pwm_char.c b/drivers/misc/ak_pwm_char.c new file mode 100644 index 00000000..2b3f7d76 --- /dev/null +++ b/drivers/misc/ak_pwm_char.c @@ -0,0 +1,206 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AK_PWM_SELET_DEV 0x0600 +#define AK_PWM_SET 0x0601 + +struct ak_duty{ + int numerator; + int denominator; +}; + +struct ak_pwm_cycle { + int frq; + struct ak_duty duty; +}; + +struct ak_pwm_data_st { + struct ak_pwm_timer *pwm; + struct ak_pwm_cycle ak_pwm_value; +}; + + +static int ak_pwm_open(struct inode *inode, struct file *file) +{ + int ret=0; + struct ak_pwm_data_st *ak_pwm; + ak_pwm = kmalloc(sizeof(struct ak_pwm_data_st), GFP_KERNEL); + if(!ak_pwm) { + printk(KERN_ERR "%s alloc the priv device fail.\n", __func__); + ret = -ENOMEM; + } + memset(ak_pwm,0,sizeof(struct ak_pwm_data_st)); + ak_pwm->ak_pwm_value.duty.denominator = 100; + ak_pwm->ak_pwm_value.duty.numerator = 1; + ak_pwm->ak_pwm_value.frq = 92; + file->private_data = ak_pwm; + return ret; +} + +static int ak_pwm_close(struct inode *inode, struct file *file) +{ + struct ak_pwm_data_st *ak_pwm = (struct ak_pwm_data_st *)file->private_data; + + if(ak_pwm->pwm){ + ak_timer_disable(ak_pwm->pwm); + ak_pwm_disable(ak_pwm->pwm); + ak_pwm_release(ak_pwm->pwm); + } + + if(ak_pwm) + kfree(ak_pwm); + + return 0; +} + +static long ak_pwm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret=0; + struct ak_pwm_data_st *ak_pwm = (struct ak_pwm_data_st *)file->private_data; + struct ak_pwm_timer *pwm_st; + switch (cmd){ + case AK_PWM_SELET_DEV: + { + /* + int pwm_id; + ret = copy_from_user(&pwm_id, (void *)arg, sizeof(long)); + if (ret) { + printk(KERN_ERR "%s copy failed\n", __func__); + return -1; + } + */ + pwm_st = ak_pwm_request(arg); + if(!pwm_st){ + printk(KERN_ERR"pwm[%d] is working now. request fail.\n", (int)arg); + return -1; + } + ak_pwm->pwm = pwm_st; + } + break; + + case AK_PWM_SET: + { + int i = 0; + unsigned int tmp = 0; + unsigned int high=0; + unsigned int low = 0; + unsigned int hl = 0; + struct ak_pwm_cycle bb; + ret = copy_from_user(&bb, (void *)arg, sizeof(struct ak_pwm_cycle)); + if (ret) { + printk(KERN_ERR "%s copy failed\n", __func__); + return -1; + } + ak_pwm->ak_pwm_value.frq = bb.frq; + ak_pwm->ak_pwm_value.duty.numerator = bb.duty.numerator; + ak_pwm->ak_pwm_value.duty.denominator = bb.duty.denominator; + + tmp = REAL_CRYSTAL_FREQ / ak_pwm->ak_pwm_value.frq; + for (i = 1; i<=AK_PWM_TIMER_PRE_DIV_MAX; i++) { + hl = tmp / i; + //round off for high + high = hl * ak_pwm->ak_pwm_value.duty.numerator + ak_pwm->ak_pwm_value.duty.denominator / 2; + high /= ak_pwm->ak_pwm_value.duty.denominator; + if(high >= 1 && high < hl) + ; + else if(high >= hl) + high = hl - 1; + else + high = 1; + + low = hl - high; + if ((high <= 65535) && (low <= 65535)) + break; + } + + //printk(KERN_ERR "duty_num:%u,duty_den:%u, div:%u, high:%u, low:%u\n", ak_pwm->ak_pwm_value.duty.numerator,ak_pwm->ak_pwm_value.duty.denominator, i, high, low); + + ret = ak_pwm_config(ak_pwm->pwm, high - 1, low - 1, ak_pwm->ak_pwm_value.frq); + if(ret){ + printk("pwm set err! %d <= frq <= %d\n", PWM_MIN_FREQ, PWM_MAX_FREQ); + return -1; + } + ak_pwm_enable(ak_pwm->pwm); + ak_timer_enable(ak_pwm->pwm); + + } + break; + + default: + break; + + } + return ret; +} + +static struct file_operations ak_pwm_fops = { + .owner = THIS_MODULE, + .open = ak_pwm_open, + .release = ak_pwm_close, + .unlocked_ioctl = ak_pwm_ioctl, +}; + +static struct miscdevice ak_pwm_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "ak_pwm", + .fops = &ak_pwm_fops, + .mode = S_IRWXO, +}; + +static int ak_pwm_probe(struct platform_device *pdev) +{ + int ret = 0; + + printk(KERN_ERR "%s\n", __func__); + /*register to miscdevice subsystem*/ + ret = misc_register(&ak_pwm_dev); + if(ret) { + printk(KERN_ERR "%s reg miscdev failed.\n", __func__); + ret = -ENOENT; + } + + return ret; +} + +static int ak_pwm_remove(struct platform_device *pdev) +{ + misc_deregister(&ak_pwm_dev); + + return 0; +} + +static struct platform_driver ak_pwm_driver = { + .driver = { + .name = "ak-pwm", + .owner = THIS_MODULE, + }, + .probe = ak_pwm_probe, + .remove = ak_pwm_remove, +}; + +static int __init ak_pwm_init(void) +{ + return platform_driver_register(&ak_pwm_driver); +} +module_init(ak_pwm_init); + +static void __exit ak_pwm_exit(void) +{ + platform_driver_unregister(&ak_pwm_driver); +} + +module_exit(ak_pwm_exit); + +MODULE_DESCRIPTION("AK39 PWM based Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ak-pwm"); + diff --git a/drivers/misc/ak_sn.c b/drivers/misc/ak_sn.c new file mode 100644 index 00000000..dfa76994 --- /dev/null +++ b/drivers/misc/ak_sn.c @@ -0,0 +1,319 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BAR_CODE_FILE_NAME "BAR" +#define SN_FILE_NAME "SEQ" +#define SN_MAX_LEN 64 + + +#define EFUSE_CFG_REG (AK_VA_SYSCTRL + 0x0000008C) // EFUSE CFG +#define EFUSE_CFG_REG1 (AK_VA_SYSCTRL + 0x00000090) // CFG35 +#define EFUSE_CFG_REG2 (AK_VA_SYSCTRL + 0x00000094) // CFG36 +#define EFUSE_CFG_REG4 (AK_VA_SYSCTRL + 0x0000009C) // CODEC CFG1 +#define EFUSE_CFG_REG5 (AK_VA_SYSCTRL + 0x000000A0) // CODEC CFG2 + + + +#define DBG(fmt, args...) //printk(fmt, ##args) + + +/** + * @brief: read vendor id and serial number from efuse area + * @param: vid, pointer to 14bit vendor id + * @param: sid, pointer to 32bit serial number + * @retval: int + */ +static int efuse_read_id(unsigned long *vid, unsigned long *sid) +{ + unsigned long regval; + + //enable resistor dirve 1.5v + regval = __raw_readl(EFUSE_CFG_REG5); + regval |= (1<<27); + __raw_writel(regval, (EFUSE_CFG_REG5)); + + //power on Avcc25_sel and efuse_vp + regval = __raw_readl(EFUSE_CFG_REG4) ; + regval |= ((1<<2)|(1<<1)); + __raw_writel(regval, (EFUSE_CFG_REG4)); + + //power on bias generator and VCM3 + regval = __raw_readl(EFUSE_CFG_REG4) ; + regval &=~((1<<0)|(1<<3)); + __raw_writel(regval, (EFUSE_CFG_REG4)); + + // not used vcm3 pull down + regval = __raw_readl(EFUSE_CFG_REG5); + regval &=~(1<<0); + __raw_writel(regval, (EFUSE_CFG_REG5)); + + msleep(300); + + while(__raw_readl(EFUSE_CFG_REG) & (1<<31)); + + //enable efuse read + regval = __raw_readl(EFUSE_CFG_REG); + regval |= (1<<31); + __raw_writel(regval, (EFUSE_CFG_REG)); + + //wait read finished + while(__raw_readl(EFUSE_CFG_REG) & (1<<31)); + + //bit[31:0] + regval = __raw_readl(EFUSE_CFG_REG1); + *vid = regval&0xffff; + *sid = (regval>>16) & 0xffff; + + //bit[47:32] + regval = __raw_readl(EFUSE_CFG_REG2); + *sid |= ((regval<<16) & 0xffff0000); + + return 0; +} + +/** + * @brief: test efuse has been locked or not + * @retval: bool, locked return true + */ +static bool efuse_locked(void) +{ + unsigned long regval,sid=0; + + //enable resistor dirve 1.5v + regval = __raw_readl(EFUSE_CFG_REG5); + regval |= (1<<27); + __raw_writel(regval, (EFUSE_CFG_REG5)); + + //power on Avcc25_sel and efuse_vp + regval = __raw_readl(EFUSE_CFG_REG4) ; + regval |= ((1<<2)|(1<<1)); + __raw_writel(regval, (EFUSE_CFG_REG4)); + + //power on bias generator and VCM3 + regval = __raw_readl(EFUSE_CFG_REG4) ; + regval &=~((1<<0)|(1<<3)); + __raw_writel(regval, (EFUSE_CFG_REG4)); + + // not used vcm3 pull down + regval = __raw_readl(EFUSE_CFG_REG5); + regval &=~(1<<0); + __raw_writel(regval, (EFUSE_CFG_REG5)); + + msleep(300); + + //enable efuse read + regval = __raw_readl(EFUSE_CFG_REG); + regval |= (1<<31); + __raw_writel(regval, (EFUSE_CFG_REG)); + + //wait read finished + while(__raw_readl(EFUSE_CFG_REG) & (1<<31)); + +/* + //bit[47:32] + regval = __raw_readl(AK_VA_SYSCTRL+0x94); + + if ((regval & (1<<15)) == 1) + return true; //locked + else + return false; //unlocked +*/ + regval = __raw_readl(EFUSE_CFG_REG1); + sid = (regval>>16) & 0xffff; + //bit[47:32] + regval = __raw_readl(EFUSE_CFG_REG2); + sid |= ((regval<<16) & 0xffff0000); + DBG("lock sid %lx\n", sid); + if (sid > 0) + return true; //locked + else + return false; //unlocked + +} + +/* + * @brief: burn efuse bit, the last bit is lock bit + * @param: cell: 0 ~ 5 + * @param: bit : 0 ~ 7 + */ +static int efuse_burn_bit(unsigned char cell, unsigned char bit) +{ + + unsigned long regval = 0; + + DBG("cell:%d,bit:%x\n",cell,bit); + + DBG("\n===========efuse write operation============\n"); + + while(__raw_readl(EFUSE_CFG_REG) & (1<<30) ) //wait bit[30]=0 ,ready to configure write operation + {}; + DBG("****EFUSE_CFG_REG = %x \n", __raw_readl(EFUSE_CFG_REG)); + + regval = __raw_readl(EFUSE_CFG_REG); //clear macro value and macro cell select + regval &= ~(0x7ff <<0); + __raw_writel(regval, EFUSE_CFG_REG); + + regval = __raw_readl(EFUSE_CFG_REG); // set new macro value and select macro cell + regval |= ( bit |(cell <<8)); + __raw_writel(regval, EFUSE_CFG_REG); + + + regval = __raw_readl(EFUSE_CFG_REG); // start efuse write operation + regval |= (1<<30); + __raw_writel(regval, EFUSE_CFG_REG); + + DBG("****Configure efuse write finished , EFUSE_CFG_REG = %x \n", __raw_readl(EFUSE_CFG_REG)); + while(__raw_readl(EFUSE_CFG_REG) & (1<<30) ) //wait bit[30]=0 ,one cell write finished + {}; + DBG("****macro cell %d write finished , EFUSE_CFG_REG = %x \n",cell, __raw_readl(EFUSE_CFG_REG)); + + return 0; +} + +static ssize_t barcode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + unsigned long vid=0, sid=0, len=0; + + efuse_read_id(&vid, &sid); + + DBG("sid:%lx\n",sid); + len = sprintf(buf, "%lx:", vid); + len += sprintf(buf+len, "%lx", sid); + len += sprintf(buf+len, "\n"); + + return len; + +} + +ssize_t barcode_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + unsigned long vid=0, sid=0,sidbk=0; + unsigned char cell, bit, i; + + if (efuse_locked()) + { + printk(KERN_WARNING "barcode number has been locked!!\n"); + return -EACCES; + } + + sscanf(buf, "%lx", &sid); + DBG("sid:%lx\n",sid); + sidbk = sid; + for(i=0; i<4; i++) + { + + cell = i+2; + DBG("sidbk:%lx\n",sidbk); + bit = sidbk & 0xff; +// if(i >= 3) +// bit |= 0x8; //lock bit + + DBG("bit:%x\n",bit); + efuse_burn_bit(cell, bit); + sidbk = sidbk >> 8; + DBG("sidbk11:%lx\n",sidbk); + } + + //readback and show + efuse_read_id(&vid, &sid); + DBG("barcode: %lx\n", sid); + + return count; +} + + +static ssize_t sn_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + unsigned char sn[SN_MAX_LEN] = {0}; + void * handle ; + int ret = 0; + + /* search the same name partition */ + handle = partition_open(SN_FILE_NAME); + if (handle == NULL) { + printk(KERN_ERR "%s, open partition error!\n", __func__); + goto out; + } + + ret = partition_read(handle , sn, SN_MAX_LEN); + if (ret == -1) { + printk(KERN_ERR "%s, write partition error!\n", __func__); + memset(sn, 0, SN_MAX_LEN); + } + + /* search open the same name partition */ + ret = partition_close(handle); + if (ret == -1) { + printk(KERN_ERR "%s, close partition error!\n", __func__); + memset(sn, 0, SN_MAX_LEN); + goto out; + } +out: + return sprintf(buf, "%s\n", sn + 4); +} + + +static struct kobj_attribute sn_attribute = + __ATTR(sn, 0666, sn_show, NULL); + +static struct kobj_attribute barcode_attribute = + __ATTR(barcode, 0666, barcode_show, barcode_store); + +static struct attribute *attrs[] = { + &sn_attribute.attr, + NULL +}; + +static struct attribute *attrsbarcode[] = { + &barcode_attribute.attr, + NULL +}; + + +static struct kobject *sn_kobj; + +static int __init serial_number_init(void) +{ + int ret; + + sn_kobj = kobject_create_and_add("serial_number", kernel_kobj); + if (!sn_kobj) { + printk("Create serial number kobject failed\n"); + return -ENOMEM; + } + + ret = sysfs_create_file(sn_kobj, *attrs); + if (ret) { + printk("Create serial number sysfs file failed\n"); + kobject_put(sn_kobj); + } + + ret = sysfs_create_file(sn_kobj, *attrsbarcode); + if (ret) { + printk("Create serial number sysfs file failed\n"); + kobject_put(sn_kobj); + } + + return ret; +} + +static void __exit serial_number_exit(void) +{ + kobject_put(sn_kobj); +} + +module_init(serial_number_init); +module_exit(serial_number_exit); + +MODULE_DESCRIPTION("Anyka Device Serial Number Interface"); +MODULE_AUTHOR("Anyka"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/akgpios.c b/drivers/misc/akgpios.c new file mode 100644 index 00000000..f3985e65 --- /dev/null +++ b/drivers/misc/akgpios.c @@ -0,0 +1,361 @@ +/* + * drivers/gpio/akgpios.c + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +//#define AKGPIO_DEDUB +#define DBG(fmt...) //printk(fmt); + +#define AKGPIO_DEV_NAME "akgpio" + +#define init_MUTEX(sem) sema_init(sem, 1) + +struct akgpio_custom_device { + struct gpio_info gpioinfo; + int gpio; + int used; + int irq_flag; + struct semaphore sem; + int locked; +}; + +static struct custom_gpio_data *pdata = NULL; +static struct akgpio_custom_device *akgpios = NULL; +static int gpio_num; + + +#ifdef AKGPIO_DEDUB +static void print_gpio_set_info(struct gpio_info *gpio) +{ + printk("GPIO[%d], dir[%s] out[%s] pulldown[%s] pullup[%s] int_pol[%s]\n", + gpio->pin, + (gpio->dir == AK_GPIO_DIR_OUTPUT) ? "out" : "in", + (gpio->value == AK_GPIO_OUT_HIGH) ? "high" : + (gpio->value == AK_GPIO_OUT_LOW) ? "low" : "no set", + (gpio->pulldown == AK_PULLDOWN_ENABLE) ? "enable" : "disable", + (gpio->pullup == AK_PULLUP_ENABLE) ? "enable" : "disable", + (gpio->int_pol == AK_GPIO_INT_HIGHLEVEL) ? "high" : + (gpio->int_pol == AK_GPIO_INT_LOWLEVEL) ? "low" : "no set"); +} + +static void print_gpios_info(struct akgpio_custom_device *gpioirq, int ngpio) +{ + int i; + for (i = 0; i < ngpio; i++) { + printk("akgpios[%d].gpio=%d, akgpios[%d].used=%d\n," + " akgpios[%d].irq_flag=%d, akgpios[%d].locked=%d\n," + " akgpios[%d].gpioinfo=%p, akgpios[%d].sem=%p\n\n", + i, gpioirq[i].gpio, i, gpioirq[i].used, i, gpioirq[i].irq_flag, + i, gpioirq[i].locked, i, &gpioirq[i].gpioinfo, i, &gpioirq[i].sem); + } +} + +#else +static void print_gpio_set_info(struct gpio_info *gpio) {} +static void print_gpios_info(struct akgpio_custom_device *gpioirq, int ngpio){} +#endif + +static irqreturn_t akgpio_custom_irq(int irq, void *dev_id) +{ + struct akgpio_custom_device *gpioirq = dev_id; + struct gpio_info *gpio = &gpioirq->gpioinfo; + unsigned int gpio_level; + + DBG("akgpio irq: irq=%d, gpioirq->gpio=%d\n", irq, gpioirq->gpio); + + BUG_ON(irq != ak_gpio_to_irq(gpio->pin)); + + gpio_level = ak_gpio_getpin(gpio->pin); + //disalbe gpio_irq when the button down + if ((gpio_level && gpio->int_pol) || (!gpio_level && !gpio->int_pol)) + ak_gpio_intpol(gpio->pin, !gpio->int_pol); + + //enable gpiot_irq when the button up + if ((!gpio_level && gpio->int_pol) || (gpio_level && !gpio->int_pol)) { + ak_gpio_intpol(gpio->pin, gpio->int_pol); + + /* release semaphore lock */ + if (gpioirq->locked) { + up(&gpioirq->sem); + gpioirq->locked = 0; + } + } + + return IRQ_HANDLED; +} + +static long akgpio_custom_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + unsigned int *gpio_array = pdata->gpiopin; + unsigned int ngpio = pdata->ngpiopin; + struct gpio_info gpio; + struct gpio_group_info gpios_info; + unsigned int irq, gpiopin; + int i, ret; + + switch(cmd) { + case GET_GPIO_NUM: + /* get gpio num */ + if (copy_to_user((void __user *)arg, &ngpio, sizeof(unsigned int))) + return -EINVAL; + break; + case GET_AVAILABLE_GPIO: + /* get unused gpio num, and report user space */ + if (copy_to_user((void __user *)arg, gpio_array, ngpio*sizeof(unsigned int))) + return -EINVAL; + break; + + case SET_GPIO_FUNC: + /* set gpio attribute, include dir(in/out), out(low/high), pull(up/down) */ + if (copy_from_user(&gpio, (void __user *)arg, sizeof(struct gpio_info))) + return -EINVAL; + + ak_gpio_set(&gpio); + break; + + case SET_GPIO_LEVEL: + /* set gpio level when the pin as output */ + if (copy_from_user(&gpio, (void __user *)arg, sizeof(struct gpio_info))) + return -EINVAL; + + ak_gpio_setpin(gpio.pin, gpio.value); + break; + + case SET_GROUP_GPIO_LEVEL: + /* set gpio level when the pin as output */ + if (copy_from_user(&gpios_info, (void __user *)arg, sizeof(struct gpio_group_info))) + return -EINVAL; + + for(i = 0; i < gpios_info.gpio_num; i++) { + ak_gpio_setpin(gpios_info.gpio[i].pin, gpios_info.gpio[i].value); + } + break; + + case GET_GPIO_VALUE: + /* read gpio value when the pin is input */ + if (copy_from_user(&gpio, (void __user *)arg, sizeof(struct gpio_info))) + return -EINVAL; + + gpio.value = ak_gpio_getpin(gpio.pin); + if(copy_to_user((void __user *)arg, &gpio, sizeof(struct gpio_info))) + return -EINVAL; + break; + + case SET_GPIO_IRQ: + { + /* set gpio irq func */ + struct akgpio_custom_device *gpioirq = NULL; + if (copy_from_user(&gpio, (void __user *)arg, sizeof(struct gpio_info))) + return -EINVAL; + + for (i = 0; i < ngpio; i++) { + gpioirq = &akgpios[i]; + if ((gpioirq->used)&&(gpioirq->gpio == gpio.pin)) { + /* gpio irq had set, return for */ + break; + } else if (!gpioirq->used) { + gpioirq->used = 1; + gpioirq->gpio = gpio.pin; + down(&gpioirq->sem); + break; + } + } + + for (i = 0; i < ngpio; i++) { + gpioirq = &akgpios[i]; + if ((gpioirq->used)&&(gpioirq->gpio == gpio.pin)) { + memcpy(&gpioirq->gpioinfo, &gpio, sizeof(struct gpio_info)); + + print_gpio_set_info(&gpio); + ak_gpio_set(&gpio); + + if (!gpioirq->irq_flag) { + irq = ak_gpio_to_irq(gpio.pin); + DBG("akgpio: gpio[%d], irq[%d]\n", gpio.pin, irq); + ret = request_irq(irq, akgpio_custom_irq, + ((gpio.int_pol == AK_GPIO_INT_HIGHLEVEL)?(IRQF_TRIGGER_HIGH):(IRQF_TRIGGER_LOW)), + "akgpio", gpioirq); + if (ret) { + printk("Request irq failed. ret=%d\n", ret); + return -EINVAL; + } + gpioirq->irq_flag = 1; + } + break; + } + } + } + break; + + case LISTEN_GPIO_IRQ: + /* wait irq occurs */ + gpiopin = (unsigned int)arg; + for (i = 0; i < ngpio; i++) { + struct akgpio_custom_device *gpioirq = &akgpios[i]; + if ((gpioirq->used)&&(gpioirq->gpio == gpiopin)) { + gpioirq->locked = 1; + down(&gpioirq->sem); + /* blocking, wait irq occurs, then continue */ + break; + } + + if (i == ngpio) { + printk("listen gpio irq: first request irq, then do this.\n"); + return -EINVAL; + } + } + break; + + case DELETE_GPIO_IRQ: + /* delete gpio irq that specific */ + gpiopin = (unsigned int)arg; + for (i = 0; i < ngpio; i++) { + struct akgpio_custom_device *gpioirq = &akgpios[i]; + if ((gpioirq->used)&&(gpioirq->gpio == gpiopin)) { + irq = ak_gpio_to_irq(gpiopin); + free_irq(irq, gpioirq); + gpioirq->irq_flag = 0; + break; + } + + if (i == ngpio) { + printk("delete gpio irq: first request irq, then do this.\n"); + return -EINVAL; + } + } + break; + + default: + printk("akgpio: the ioctl is unknow.\n"); + break; + } + + //print_gpios_info(akgpios, ngpio); + return 0; +} + +static int akgpio_custom_open(struct inode *node, struct file *file) +{ + /* do nothing, return correct */ + printk(KERN_INFO "open akgpio device success.\n"); + return 0; +} + +static int akgpio_custom_release(struct inode *node, struct file *file) +{ + /* do nothing */ + return 0; +} + +static const struct file_operations akgpio_ops = { + .owner = THIS_MODULE, + .open = akgpio_custom_open, + .release = akgpio_custom_release, + .unlocked_ioctl = akgpio_custom_ioctl, +}; + +static struct miscdevice akgpio_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = AKGPIO_DEV_NAME, + .fops = &akgpio_ops, + .mode = S_IRWXO, +}; + +static int akgpio_custom_probe(struct platform_device *pdev) +{ + int i; + struct akgpio_custom_device *gpioirq = NULL; + + /* get platform data of akgpio */ + pdata = pdev->dev.platform_data; + if ((pdata == NULL)||((pdata)&&(pdata->ngpiopin == 0))) + return -ENODEV; + + /* register misc device */ + if (misc_register(&akgpio_dev)) { + printk(KERN_ERR "akgpio: Unable register misc device.\n"); + return -ENODEV; + } + + gpio_num = pdata->ngpiopin; + + akgpios = kzalloc(gpio_num * sizeof(struct akgpio_custom_device), GFP_KERNEL); + if (akgpios == NULL) + return -ENOMEM; + gpioirq = akgpios; + + for (i = 0; i < gpio_num; i++) { + gpioirq[i].gpio = -1; + gpioirq[i].used = 0; + gpioirq[i].irq_flag = 0; + init_MUTEX(&gpioirq[i].sem); + gpioirq[i].locked = 0; + } + + printk("akgpio driver initialize.\n"); + print_gpios_info(gpioirq, gpio_num); + return 0; +} + +static int akgpio_custom_remove(struct platform_device *pdev) +{ + /* release platform data for akgpio */ + int i; + struct akgpio_custom_device *gpioirq = akgpios; + + pdata = NULL; + misc_deregister(&akgpio_dev); + + for (i = 0; i < gpio_num; i++) { + gpioirq[i].gpio = -1; + gpioirq[i].used = 0; + gpioirq[i].irq_flag = 0; + gpioirq[i].locked = 0; + } + + return 0; +} + +static struct platform_driver akgpio_custom_driver = { + .probe = akgpio_custom_probe, + .remove = __devexit_p(akgpio_custom_remove), + .driver = { + .owner = THIS_MODULE, + .name = AKGPIO_DEV_NAME, + }, +}; + +static int __init akgpio_custom_init(void) +{ + return platform_driver_register(&akgpio_custom_driver); +} + +static void __exit akgpio_custom_exit(void) +{ + platform_driver_unregister(&akgpio_custom_driver); +} + +module_init(akgpio_custom_init); +module_exit(akgpio_custom_exit); + +MODULE_AUTHOR("Anyka Microelectronic Ltd."); +MODULE_DESCRIPTION("Anyka gpio apply for user space control"); +MODULE_ALIAS("Anyka GPIO Apply"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/misc/akm8975.c b/drivers/misc/akm8975.c new file mode 100644 index 00000000..830d2897 --- /dev/null +++ b/drivers/misc/akm8975.c @@ -0,0 +1,732 @@ +/* drivers/misc/akm8975.c - akm8975 compass driver + * + * Copyright (C) 2007-2008 HTC Corporation. + * Author: Hou-Kun Chen + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +/* + * Revised by AKM 2009/04/02 + * Revised by Motorola 2010/05/27 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AK8975DRV_CALL_DBG 0 +#if AK8975DRV_CALL_DBG +#define FUNCDBG(msg) pr_err("%s:%s\n", __func__, msg); +#else +#define FUNCDBG(msg) +#endif + +#define AK8975DRV_DATA_DBG 0 +#define MAX_FAILURE_COUNT 10 + +struct akm8975_data { + struct i2c_client *this_client; + struct akm8975_platform_data *pdata; + struct input_dev *input_dev; + struct work_struct work; + struct mutex flags_lock; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif +}; + +/* +* Because misc devices can not carry a pointer from driver register to +* open, we keep this global. This limits the driver to a single instance. +*/ +struct akm8975_data *akmd_data; + +static DECLARE_WAIT_QUEUE_HEAD(open_wq); + +static atomic_t open_flag; + +static short m_flag; +static short a_flag; +static short t_flag; +static short mv_flag; + +static short akmd_delay; + +static ssize_t akm8975_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + return sprintf(buf, "%u\n", i2c_smbus_read_byte_data(client, + AK8975_REG_CNTL)); +} +static ssize_t akm8975_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned long val; + strict_strtoul(buf, 10, &val); + if (val > 0xff) + return -EINVAL; + i2c_smbus_write_byte_data(client, AK8975_REG_CNTL, val); + return count; +} +static DEVICE_ATTR(akm_ms1, S_IWUSR | S_IRUGO, akm8975_show, akm8975_store); + +static int akm8975_i2c_rxdata(struct akm8975_data *akm, char *buf, int length) +{ + struct i2c_msg msgs[] = { + { + .addr = akm->this_client->addr, + .flags = 0, + .len = 1, + .buf = buf, + }, + { + .addr = akm->this_client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = buf, + }, + }; + + FUNCDBG("called"); + + if (i2c_transfer(akm->this_client->adapter, msgs, 2) < 0) { + pr_err("akm8975_i2c_rxdata: transfer error\n"); + return EIO; + } else + return 0; +} + +static int akm8975_i2c_txdata(struct akm8975_data *akm, char *buf, int length) +{ + struct i2c_msg msgs[] = { + { + .addr = akm->this_client->addr, + .flags = 0, + .len = length, + .buf = buf, + }, + }; + + FUNCDBG("called"); + + if (i2c_transfer(akm->this_client->adapter, msgs, 1) < 0) { + pr_err("akm8975_i2c_txdata: transfer error\n"); + return -EIO; + } else + return 0; +} + +static void akm8975_ecs_report_value(struct akm8975_data *akm, short *rbuf) +{ + struct akm8975_data *data = i2c_get_clientdata(akm->this_client); + + FUNCDBG("called"); + +#if AK8975DRV_DATA_DBG + pr_info("akm8975_ecs_report_value: yaw = %d, pitch = %d, roll = %d\n", + rbuf[0], rbuf[1], rbuf[2]); + pr_info("tmp = %d, m_stat= %d, g_stat=%d\n", rbuf[3], rbuf[4], rbuf[5]); + pr_info("Acceleration: x = %d LSB, y = %d LSB, z = %d LSB\n", + rbuf[6], rbuf[7], rbuf[8]); + pr_info("Magnetic: x = %d LSB, y = %d LSB, z = %d LSB\n\n", + rbuf[9], rbuf[10], rbuf[11]); +#endif + mutex_lock(&akm->flags_lock); + /* Report magnetic sensor information */ + if (m_flag) { + input_report_abs(data->input_dev, ABS_RX, rbuf[0]); + input_report_abs(data->input_dev, ABS_RY, rbuf[1]); + input_report_abs(data->input_dev, ABS_RZ, rbuf[2]); + input_report_abs(data->input_dev, ABS_RUDDER, rbuf[4]); + } + + /* Report acceleration sensor information */ + if (a_flag) { + input_report_abs(data->input_dev, ABS_X, rbuf[6]); + input_report_abs(data->input_dev, ABS_Y, rbuf[7]); + input_report_abs(data->input_dev, ABS_Z, rbuf[8]); + input_report_abs(data->input_dev, ABS_WHEEL, rbuf[5]); + } + + /* Report temperature information */ + if (t_flag) + input_report_abs(data->input_dev, ABS_THROTTLE, rbuf[3]); + + if (mv_flag) { + input_report_abs(data->input_dev, ABS_HAT0X, rbuf[9]); + input_report_abs(data->input_dev, ABS_HAT0Y, rbuf[10]); + input_report_abs(data->input_dev, ABS_BRAKE, rbuf[11]); + } + mutex_unlock(&akm->flags_lock); + + input_sync(data->input_dev); +} + +static void akm8975_ecs_close_done(struct akm8975_data *akm) +{ + FUNCDBG("called"); + mutex_lock(&akm->flags_lock); + m_flag = 1; + a_flag = 1; + t_flag = 1; + mv_flag = 1; + mutex_unlock(&akm->flags_lock); +} + +static int akm_aot_open(struct inode *inode, struct file *file) +{ + int ret = -1; + + FUNCDBG("called"); + if (atomic_cmpxchg(&open_flag, 0, 1) == 0) { + wake_up(&open_wq); + ret = 0; + } + + ret = nonseekable_open(inode, file); + if (ret) + return ret; + + file->private_data = akmd_data; + + return ret; +} + +static int akm_aot_release(struct inode *inode, struct file *file) +{ + FUNCDBG("called"); + atomic_set(&open_flag, 0); + wake_up(&open_wq); + return 0; +} + +static int akm_aot_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *) arg; + short flag; + struct akm8975_data *akm = file->private_data; + + FUNCDBG("called"); + + switch (cmd) { + case ECS_IOCTL_APP_SET_MFLAG: + case ECS_IOCTL_APP_SET_AFLAG: + case ECS_IOCTL_APP_SET_MVFLAG: + if (copy_from_user(&flag, argp, sizeof(flag))) + return -EFAULT; + if (flag < 0 || flag > 1) + return -EINVAL; + break; + case ECS_IOCTL_APP_SET_DELAY: + if (copy_from_user(&flag, argp, sizeof(flag))) + return -EFAULT; + break; + default: + break; + } + + mutex_lock(&akm->flags_lock); + switch (cmd) { + case ECS_IOCTL_APP_SET_MFLAG: + m_flag = flag; + break; + case ECS_IOCTL_APP_GET_MFLAG: + flag = m_flag; + break; + case ECS_IOCTL_APP_SET_AFLAG: + a_flag = flag; + break; + case ECS_IOCTL_APP_GET_AFLAG: + flag = a_flag; + break; + case ECS_IOCTL_APP_SET_MVFLAG: + mv_flag = flag; + break; + case ECS_IOCTL_APP_GET_MVFLAG: + flag = mv_flag; + break; + case ECS_IOCTL_APP_SET_DELAY: + akmd_delay = flag; + break; + case ECS_IOCTL_APP_GET_DELAY: + flag = akmd_delay; + break; + default: + return -ENOTTY; + } + mutex_unlock(&akm->flags_lock); + + switch (cmd) { + case ECS_IOCTL_APP_GET_MFLAG: + case ECS_IOCTL_APP_GET_AFLAG: + case ECS_IOCTL_APP_GET_MVFLAG: + case ECS_IOCTL_APP_GET_DELAY: + if (copy_to_user(argp, &flag, sizeof(flag))) + return -EFAULT; + break; + default: + break; + } + + return 0; +} + +static int akmd_open(struct inode *inode, struct file *file) +{ + int err = 0; + + FUNCDBG("called"); + err = nonseekable_open(inode, file); + if (err) + return err; + + file->private_data = akmd_data; + return 0; +} + +static int akmd_release(struct inode *inode, struct file *file) +{ + struct akm8975_data *akm = file->private_data; + + FUNCDBG("called"); + akm8975_ecs_close_done(akm); + return 0; +} + +static int akmd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *) arg; + + char rwbuf[16]; + int ret = -1; + int status; + short value[12]; + short delay; + struct akm8975_data *akm = file->private_data; + + FUNCDBG("called"); + + switch (cmd) { + case ECS_IOCTL_READ: + case ECS_IOCTL_WRITE: + if (copy_from_user(&rwbuf, argp, sizeof(rwbuf))) + return -EFAULT; + break; + + case ECS_IOCTL_SET_YPR: + if (copy_from_user(&value, argp, sizeof(value))) + return -EFAULT; + break; + + default: + break; + } + + switch (cmd) { + case ECS_IOCTL_READ: + if (rwbuf[0] < 1) + return -EINVAL; + + ret = akm8975_i2c_rxdata(akm, &rwbuf[1], rwbuf[0]); + if (ret < 0) + return ret; + break; + + case ECS_IOCTL_WRITE: + if (rwbuf[0] < 2) + return -EINVAL; + + ret = akm8975_i2c_txdata(akm, &rwbuf[1], rwbuf[0]); + if (ret < 0) + return ret; + break; + case ECS_IOCTL_SET_YPR: + akm8975_ecs_report_value(akm, value); + break; + + case ECS_IOCTL_GET_OPEN_STATUS: + wait_event_interruptible(open_wq, + (atomic_read(&open_flag) != 0)); + status = atomic_read(&open_flag); + break; + case ECS_IOCTL_GET_CLOSE_STATUS: + wait_event_interruptible(open_wq, + (atomic_read(&open_flag) == 0)); + status = atomic_read(&open_flag); + break; + + case ECS_IOCTL_GET_DELAY: + delay = akmd_delay; + break; + + default: + FUNCDBG("Unknown cmd\n"); + return -ENOTTY; + } + + switch (cmd) { + case ECS_IOCTL_READ: + if (copy_to_user(argp, &rwbuf, sizeof(rwbuf))) + return -EFAULT; + break; + case ECS_IOCTL_GET_OPEN_STATUS: + case ECS_IOCTL_GET_CLOSE_STATUS: + if (copy_to_user(argp, &status, sizeof(status))) + return -EFAULT; + break; + case ECS_IOCTL_GET_DELAY: + if (copy_to_user(argp, &delay, sizeof(delay))) + return -EFAULT; + break; + default: + break; + } + + return 0; +} + +/* needed to clear the int. pin */ +static void akm_work_func(struct work_struct *work) +{ + struct akm8975_data *akm = + container_of(work, struct akm8975_data, work); + + FUNCDBG("called"); + enable_irq(akm->this_client->irq); +} + +static irqreturn_t akm8975_interrupt(int irq, void *dev_id) +{ + struct akm8975_data *akm = dev_id; + FUNCDBG("called"); + + disable_irq_nosync(akm->this_client->irq); + schedule_work(&akm->work); + return IRQ_HANDLED; +} + +static int akm8975_power_off(struct akm8975_data *akm) +{ +#if AK8975DRV_CALL_DBG + pr_info("%s\n", __func__); +#endif + if (akm->pdata->power_off) + akm->pdata->power_off(); + + return 0; +} + +static int akm8975_power_on(struct akm8975_data *akm) +{ + int err; + +#if AK8975DRV_CALL_DBG + pr_info("%s\n", __func__); +#endif + if (akm->pdata->power_on) { + err = akm->pdata->power_on(); + if (err < 0) + return err; + } + return 0; +} + +static int akm8975_suspend(struct i2c_client *client, pm_message_t mesg) +{ + struct akm8975_data *akm = i2c_get_clientdata(client); + +#if AK8975DRV_CALL_DBG + pr_info("%s\n", __func__); +#endif + /* TO DO: might need more work after power mgmt + is enabled */ + return akm8975_power_off(akm); +} + +static int akm8975_resume(struct i2c_client *client) +{ + struct akm8975_data *akm = i2c_get_clientdata(client); + +#if AK8975DRV_CALL_DBG + pr_info("%s\n", __func__); +#endif + /* TO DO: might need more work after power mgmt + is enabled */ + return akm8975_power_on(akm); +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void akm8975_early_suspend(struct early_suspend *handler) +{ + struct akm8975_data *akm; + akm = container_of(handler, struct akm8975_data, early_suspend); + +#if AK8975DRV_CALL_DBG + pr_info("%s\n", __func__); +#endif + akm8975_suspend(akm->this_client, PMSG_SUSPEND); +} + +static void akm8975_early_resume(struct early_suspend *handler) +{ + struct akm8975_data *akm; + akm = container_of(handler, struct akm8975_data, early_suspend); + +#if AK8975DRV_CALL_DBG + pr_info("%s\n", __func__); +#endif + akm8975_resume(akm->this_client); +} +#endif + + +static int akm8975_init_client(struct i2c_client *client) +{ + struct akm8975_data *data; + int ret; + + data = i2c_get_clientdata(client); + + ret = request_irq(client->irq, akm8975_interrupt, IRQF_TRIGGER_RISING, + "akm8975", data); + + if (ret < 0) { + pr_err("akm8975_init_client: request irq failed\n"); + goto err; + } + + init_waitqueue_head(&open_wq); + + mutex_lock(&data->flags_lock); + m_flag = 1; + a_flag = 1; + t_flag = 1; + mv_flag = 1; + mutex_unlock(&data->flags_lock); + + return 0; +err: + return ret; +} + +static const struct file_operations akmd_fops = { + .owner = THIS_MODULE, + .open = akmd_open, + .release = akmd_release, + .ioctl = akmd_ioctl, +}; + +static const struct file_operations akm_aot_fops = { + .owner = THIS_MODULE, + .open = akm_aot_open, + .release = akm_aot_release, + .ioctl = akm_aot_ioctl, +}; + +static struct miscdevice akm_aot_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "akm8975_aot", + .fops = &akm_aot_fops, +}; + +static struct miscdevice akmd_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "akm8975_dev", + .fops = &akmd_fops, +}; + +int akm8975_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + struct akm8975_data *akm; + int err; + FUNCDBG("called"); + + if (client->dev.platform_data == NULL) { + dev_err(&client->dev, "platform data is NULL. exiting.\n"); + err = -ENODEV; + goto exit_platform_data_null; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "platform data is NULL. exiting.\n"); + err = -ENODEV; + goto exit_check_functionality_failed; + } + + akm = kzalloc(sizeof(struct akm8975_data), GFP_KERNEL); + if (!akm) { + dev_err(&client->dev, + "failed to allocate memory for module data\n"); + err = -ENOMEM; + goto exit_alloc_data_failed; + } + + akm->pdata = client->dev.platform_data; + + mutex_init(&akm->flags_lock); + INIT_WORK(&akm->work, akm_work_func); + i2c_set_clientdata(client, akm); + + err = akm8975_power_on(akm); + if (err < 0) + goto exit_power_on_failed; + + akm8975_init_client(client); + akm->this_client = client; + akmd_data = akm; + + akm->input_dev = input_allocate_device(); + if (!akm->input_dev) { + err = -ENOMEM; + dev_err(&akm->this_client->dev, + "input device allocate failed\n"); + goto exit_input_dev_alloc_failed; + } + + set_bit(EV_ABS, akm->input_dev->evbit); + + /* yaw */ + input_set_abs_params(akm->input_dev, ABS_RX, 0, 23040, 0, 0); + /* pitch */ + input_set_abs_params(akm->input_dev, ABS_RY, -11520, 11520, 0, 0); + /* roll */ + input_set_abs_params(akm->input_dev, ABS_RZ, -5760, 5760, 0, 0); + /* x-axis acceleration */ + input_set_abs_params(akm->input_dev, ABS_X, -5760, 5760, 0, 0); + /* y-axis acceleration */ + input_set_abs_params(akm->input_dev, ABS_Y, -5760, 5760, 0, 0); + /* z-axis acceleration */ + input_set_abs_params(akm->input_dev, ABS_Z, -5760, 5760, 0, 0); + /* temparature */ + input_set_abs_params(akm->input_dev, ABS_THROTTLE, -30, 85, 0, 0); + /* status of magnetic sensor */ + input_set_abs_params(akm->input_dev, ABS_RUDDER, 0, 3, 0, 0); + /* status of acceleration sensor */ + input_set_abs_params(akm->input_dev, ABS_WHEEL, 0, 3, 0, 0); + /* x-axis of raw magnetic vector */ + input_set_abs_params(akm->input_dev, ABS_HAT0X, -20480, 20479, 0, 0); + /* y-axis of raw magnetic vector */ + input_set_abs_params(akm->input_dev, ABS_HAT0Y, -20480, 20479, 0, 0); + /* z-axis of raw magnetic vector */ + input_set_abs_params(akm->input_dev, ABS_BRAKE, -20480, 20479, 0, 0); + + akm->input_dev->name = "compass"; + + err = input_register_device(akm->input_dev); + if (err) { + pr_err("akm8975_probe: Unable to register input device: %s\n", + akm->input_dev->name); + goto exit_input_register_device_failed; + } + + err = misc_register(&akmd_device); + if (err) { + pr_err("akm8975_probe: akmd_device register failed\n"); + goto exit_misc_device_register_failed; + } + + err = misc_register(&akm_aot_device); + if (err) { + pr_err("akm8975_probe: akm_aot_device register failed\n"); + goto exit_misc_device_register_failed; + } + + err = device_create_file(&client->dev, &dev_attr_akm_ms1); + +#ifdef CONFIG_HAS_EARLYSUSPEND + akm->early_suspend.suspend = akm8975_early_suspend; + akm->early_suspend.resume = akm8975_early_resume; + register_early_suspend(&akm->early_suspend); +#endif + return 0; + +exit_misc_device_register_failed: +exit_input_register_device_failed: + input_free_device(akm->input_dev); +exit_input_dev_alloc_failed: + akm8975_power_off(akm); +exit_power_on_failed: + kfree(akm); +exit_alloc_data_failed: +exit_check_functionality_failed: +exit_platform_data_null: + return err; +} + +static int __devexit akm8975_remove(struct i2c_client *client) +{ + struct akm8975_data *akm = i2c_get_clientdata(client); + FUNCDBG("called"); + free_irq(client->irq, NULL); + input_unregister_device(akm->input_dev); + misc_deregister(&akmd_device); + misc_deregister(&akm_aot_device); + akm8975_power_off(akm); + kfree(akm); + return 0; +} + +static const struct i2c_device_id akm8975_id[] = { + { "akm8975", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, akm8975_id); + +static struct i2c_driver akm8975_driver = { + .probe = akm8975_probe, + .remove = akm8975_remove, +#ifndef CONFIG_HAS_EARLYSUSPEND + .resume = akm8975_resume, + .suspend = akm8975_suspend, +#endif + .id_table = akm8975_id, + .driver = { + .name = "akm8975", + }, +}; + +static int __init akm8975_init(void) +{ + pr_info("AK8975 compass driver: init\n"); + FUNCDBG("AK8975 compass driver: init\n"); + return i2c_add_driver(&akm8975_driver); +} + +static void __exit akm8975_exit(void) +{ + FUNCDBG("AK8975 compass driver: exit\n"); + i2c_del_driver(&akm8975_driver); +} + +module_init(akm8975_init); +module_exit(akm8975_exit); + +MODULE_AUTHOR("Hou-Kun Chen "); +MODULE_DESCRIPTION("AK8975 compass driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/akpcm/Kconfig b/drivers/misc/akpcm/Kconfig new file mode 100644 index 00000000..c4e880ed --- /dev/null +++ b/drivers/misc/akpcm/Kconfig @@ -0,0 +1,5 @@ +config AK39_PCM + tristate "Anyka pcm cdev" + depends on ARCH_AK39 + ---help--- + akpcm cdev \ No newline at end of file diff --git a/drivers/misc/akpcm/Makefile b/drivers/misc/akpcm/Makefile new file mode 100644 index 00000000..9add26cb --- /dev/null +++ b/drivers/misc/akpcm/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_AK39_PCM) += akpcm_drv.o +akpcm_drv-objs := capture.o playback.o aec.o aec_dump.o +akpcm_drv-objs += akpcm.o akpcmL0.o diff --git a/drivers/misc/akpcm/aec.c b/drivers/misc/akpcm/aec.c new file mode 100644 index 00000000..f0fd5717 --- /dev/null +++ b/drivers/misc/akpcm/aec.c @@ -0,0 +1,1248 @@ +#include +#include +#include +#include +#include + +#include "aec.h" +#include "aec_dump.h" +#include "capture.h" +#include "playback.h" + +#define AEC_TASK_DEBUG (0) + + + +static u_int32_t aec_cost; // us +static int aec_frame_count; +static T_U32 far_threshold = AEC_DA_THRESH; // far threshold for aec +static T_U32 agc_level = AEC_AGC_LEVEL; // agc's target level, 0: use default. use AK32Q15 +static T_U16 max_gain = 4; // agc's max_gain, Q0 +static T_U16 min_gain = AK16Q10(0.1); // agc's min_gain, use AK16Q10 +static T_S16 far_digi_gain = AK16Q10(1); // 100% // digtal gain for far signal, Q10, 0: use default. use AK16Q10 +static T_S16 near_digi_gain = AK16Q10(0.5); // 50% // digtal gain for near signal, Q10, 0: use default. use AK16Q10 +static T_S16 noise_suppress_db = -40; // attenuation of noise in dB (negative number), 0: use default +static T_S16 near_sensitivity = 20; + +static T_S32 peqpara[EQ_ARRAY_NUMBER] = +{ + 0x1, //cIsEnable + 0x0, //preGain + 0x400, //preVol + 0x2, //bands + 0x0acae8a8, 0xea6a2eaf, 0x0acae8a8, 0x13de4aa7, 0xf8b2a804, //pab[0] + 0x0a32aa0a, 0x060fd5d9, 0x096b2e33, 0xf9f02a27, 0xfc6227c3, //pab[1] + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, //pab[2] + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, //pab[3] + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, //pab[4] + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, //pab[5] + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, //pab[6] + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, //pab[7] + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, //pab[8] + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, //pab[9] +}; + +static DEFINE_KFIFO(aec_event_buf, t_aec_event, 8); + +static T_S32 DA_factor = AK32Q15(0.75); + +static int open_filter_lib(struct akpcm *pcm) +{ + int ret = 0; + T_AUDIO_FILTER_INPUT s_ininfo; + memset(&s_ininfo, 0, sizeof(T_AUDIO_FILTER_INPUT)); + + // open eq handle + s_ininfo.cb_fun.Malloc = (MEDIALIB_CALLBACK_FUN_MALLOC)akpcm_malloc_cb; + s_ininfo.cb_fun.Free = (MEDIALIB_CALLBACK_FUN_FREE)akpcm_free_cb; + s_ininfo.cb_fun.printf = (MEDIALIB_CALLBACK_FUN_PRINTF)printk; + s_ininfo.cb_fun.delay = AK_NULL; + s_ininfo.m_info.m_BitsPerSample = 16; + s_ininfo.m_info.m_Channels = 1; + s_ininfo.m_info.m_SampleRate = 44100; + s_ininfo.m_info.m_Type = _SD_FILTER_EQ; + s_ininfo.chip = AUDIOLIB_CHIP_AK39XXEV3; //�ij���Ӧ��оƬ�ͺ� + s_ininfo.strVersion = AUDIO_FILTER_VERSION_STRING; + s_ininfo.m_info.m_Private.m_eq.eqmode = _SD_EQ_USER_DEFINE; + s_ininfo.m_info.m_Private.m_eq.preGain = (T_S16)(-1*(1<<10)); + s_ininfo.m_info.m_Private.m_eq.bands = 2; + s_ininfo.m_info.m_Private.m_eq.bandfreqs[0] = 80; + s_ininfo.m_info.m_Private.m_eq.bandTypes[0] = FILTER_TYPE_HPF; + s_ininfo.m_info.m_Private.m_eq.bandQ[0] = (T_U16)(0.707*(1<<10)); + s_ininfo.m_info.m_Private.m_eq.bandfreqs[1] = 120; + s_ininfo.m_info.m_Private.m_eq.bandTypes[1] = FILTER_TYPE_PF1; + s_ininfo.m_info.m_Private.m_eq.bandgains[1] = (T_U16)(3*(1<<10)); + s_ininfo.m_info.m_Private.m_eq.bandQ[1] = (T_U16)(0.707*(1<<10)); + + pcm->filter_handle = _SD_Filter_Open(&s_ininfo); + if (!pcm->filter_handle) { + ret = -EPERM; /* Operation not permitted */ + } + + return ret; +} + +static int set_filter_lib_param(struct akpcm *pcm, T_S32 *peqpara) +{ + + ak_pcm_info_ex("--- ***************pcm->filter_handle=%p\n", pcm->filter_handle); + + if (AK_TRUE != _SD_Filter_SetEqTimePara(pcm->filter_handle, peqpara)) { + ak_pcm_err_ex("Error! set_filter_lib_param error!"); + return -EFAULT; + } + + return 0; + +} + +static int filter_lib_control(struct akpcm *pcm, unsigned char *inbuf, + unsigned int inlen, unsigned char *outbuf, unsigned int outlen) +{ + T_AUDIO_FILTER_BUF_STRC fbuf_strc={0}; + int processlen = 0; + fbuf_strc.buf_in = inbuf; + fbuf_strc.buf_out = outbuf; + fbuf_strc.len_in = inlen; //������Ч���ݳ��� + fbuf_strc.len_out = outlen; //���buffer��С + if (!pcm->filter_handle) { + ak_pcm_err_ex("filter lib handle is NULL"); + return -EINVAL; + } + processlen = _SD_Filter_Control(pcm->filter_handle, &fbuf_strc); + return processlen; +} + +static int close_filter_lib(struct akpcm *pcm) +{ + int ret = 0; + if (!pcm->eq_enable) + return 0; + + if (NULL == pcm->filter_handle) { + ak_pcm_err_ex("filter lib handle is NULL"); + return -EFAULT; + } + + if (_SD_Filter_Close(pcm->filter_handle)) { + ret = 0; + } else { + ret = -EFAULT; + } + + pcm->filter_handle = NULL; + return ret; +} + +void print_event_string(t_aec_event event) +{ + switch (event) { + case AEC_EVENT_NONE: + ak_pcm_info_ex("event-%d: none, general trigger", event); + break; + case AEC_EVENT_OPEN: + ak_pcm_info_ex("event-%d: open", event); + break; + case AEC_EVENT_ADC_START: + ak_pcm_info_ex("event-%d: adc start", event); + break; + case AEC_EVENT_DAC_START: + ak_pcm_info_ex("event-%d: dac start", event); + break; + case AEC_EVENT_AEC_START: + ak_pcm_info_ex("event-%d: aec start", event); + break; + case AEC_EVENT_ADC_STOP: + ak_pcm_info_ex("event-%d: adc stop", event); + break; + case AEC_EVENT_DAC_STOP: + ak_pcm_info_ex("event-%d: dac stop", event); + break; + case AEC_EVENT_AEC_STOP: + ak_pcm_info_ex("event-%d: aec stop", event); + break; + default: + ak_pcm_info_ex("event-%d: unknow", event); + break; + } +} + +/* + * playback & capture helper routine + * get how many data to be processed by AEC in playback or capture buffer + */ +inline unsigned int get_aec_bytes(struct akpcm_runtime *rt) +{ + unsigned int aec_bytes; + unsigned int aec_pos = rt->aec_ptr; + unsigned int hw_pos = rt->hw_ptr; + unsigned int boundary = rt->boundary; + + /* calculate pend_data & free_space */ + if (hw_pos >= aec_pos) { + aec_bytes = hw_pos - aec_pos; + } else { + aec_bytes = boundary - aec_pos + hw_pos; + } + +#ifdef DEBUG_BYTES + if (aec_bytes > rt->buffer_bytes) { + struct akpcm *pcm = get_akpcm_ptr(); + ak_pcm_err_ex("%s err aec_bytes:%u", + (pcm->play_rt == rt) ? "PB":"CP", aec_bytes); + ak_pcm_err_ex("ptr app:%u, aec:%u, hw:%u", + rt->app_ptr, rt->aec_ptr, rt->hw_ptr); + } +#endif + + return aec_bytes; +} + +static inline void release_aec_resource(struct akpcm *pcm) +{ + /* close lib */ + if (pcm->aec_filter) { + AECLib_Close(pcm->aec_filter); + pcm->aec_filter = NULL; + } + /* release temp bufs */ + if (pcm->aec_out_buf) { + kfree(pcm->aec_out_buf); + pcm->aec_out_buf = NULL; + } +} + +static inline int update_capt_aec_ptr(struct akpcm_runtime *cptr_rt) +{ + unsigned int aec_ptr = cptr_rt->aec_ptr; + + if (aec_ptr >= cptr_rt->hw_ptr) { + printk(KERN_ERR "%s aec>=hw", __func__); + return 0; + } + + aec_ptr += AEC_FRAME_BYTES; + if(aec_ptr >= cptr_rt->boundary) + aec_ptr -= cptr_rt->boundary; + + cptr_rt->aec_ptr = aec_ptr; + + return 1; +} + +static inline int update_play_aec_ptr(struct akpcm_runtime *play_rt) +{ + unsigned int aec_ptr = play_rt->aec_ptr; + + if (aec_ptr >= play_rt->hw_ptr) { + printk(KERN_ERR "%s aec>=hw", __func__); + return 0; + } + + aec_ptr += FAR_FRAME_SIZE; + if(aec_ptr >= play_rt->boundary) { + aec_ptr -= play_rt->boundary; + } + + play_rt->aec_ptr = aec_ptr; + + return 1; +} + +static void handle_aec_task(struct akpcm *pcm) +{ + struct akpcm_runtime *play_rt = pcm->play_rt; + struct akpcm_runtime *cptr_rt = pcm->cptr_rt; + struct aec_dump_info info; + T_AEC_BUF *aec_bufs = &pcm->aec_bufs; + T_S16 *near_frame = AK_NULL; + T_S16 *far_frame = AK_NULL; + u_int64_t start_time = 0; + u_int64_t end_time = 0; + int spend = 0; + int spend_valid = 0; + int cptr_update_aec; + int play_done = 0; + + if (cptr_rt) { + info.sample_rate = cptr_rt->cfg.rate; + } + + print_isr(pcm->aec_dump_type); + +#if AEC_TASK_DEBUG + ak_pcm_func("T+"); +#endif + + start_time = sched_clock(); + while (1) { + spin_lock_irq(&cptr_rt->ptr_lock); + if (!(get_aec_bytes(cptr_rt) >= AEC_FRAME_BYTES)) { + spin_unlock_irq(&cptr_rt->ptr_lock); + break; + } + spin_unlock_irq(&cptr_rt->ptr_lock); + + /* get far end frame */ + if (pcm->enable_aec) { + /* playback may stop in isr */ + if (!is_playback_working(pcm)) { + break; + } + + spin_lock_irq(&play_rt->ptr_lock); + /* if there is not enouph far data, skip this round */ + if (get_aec_bytes(play_rt) < FAR_FRAME_SIZE) { + spin_unlock_irq(&play_rt->ptr_lock); + break; + } + + far_frame = (T_S16*)(play_rt->dma_area + + (play_rt->aec_ptr % play_rt->buffer_bytes)); + + spin_unlock_irq(&play_rt->ptr_lock); + + /* extrace single track from stereo */ + stereo_to_mono(far_frame, far_frame, FAR_FRAME_SIZE); + + info.id = AEC_DUMP_FAR; + info.data = (unsigned char *)far_frame; + info.size = AEC_FRAME_BYTES; + aec_dump(pcm->aec_dump_type, &info); + } + + /* when collected sufficient data, do AEC task */ + near_frame = (T_S16*)(cptr_rt->dma_area + + (cptr_rt->aec_ptr % cptr_rt->buffer_bytes)); + + if (pcm->aec_dump_type == AEC_DUMP_FILE || pcm->aec_dump_type == AEC_DUMP_DEBUG) { + info.id = AEC_DUMP_NEAR; + info.data = (unsigned char *)near_frame; + info.size = AEC_FRAME_BYTES; + aec_dump(pcm->aec_dump_type, &info); + } + + if (pcm->eq_enable) { + filter_lib_control(pcm, (unsigned char *)near_frame, + AEC_FRAME_BYTES, (unsigned char *)near_frame, AEC_FRAME_BYTES); + + if (pcm->aec_dump_type == AEC_DUMP_FILE || pcm->aec_dump_type == AEC_DUMP_DEBUG) { + /* dump after eq audio data */ + info.id = AEC_DUMP_AFTER_EQ; + info.data = (unsigned char *)near_frame; + info.size = AEC_FRAME_BYTES; + aec_dump(pcm->aec_dump_type, &info); + } + } + + /* put data to AEC process */ + aec_bufs->buf_near = near_frame; + aec_bufs->buf_far = far_frame;/*far_frame is NULL if dac closed, it is allowed*/ + /* near and out buf should not be the same */ + aec_bufs->buf_out = pcm->aec_out_buf; + + if (pcm->aec_filter) { + AECLib_Control(pcm->aec_filter, aec_bufs); + } + + /* copy to near buffer */ + memcpy(near_frame, aec_bufs->buf_out, AEC_FRAME_BYTES); + + if (pcm->aec_dump_type == AEC_DUMP_FILE || pcm->aec_dump_type == AEC_DUMP_DEBUG) { + info.id = AEC_DUMP_RES; + info.data = (unsigned char *)near_frame; + info.size = AEC_FRAME_BYTES; + + aec_dump(pcm->aec_dump_type, &info); + aec_dump_sync(pcm); + } + + aec_frame_count++; + spend_valid++; + + spin_lock_irq(&cptr_rt->ptr_lock); + + /* update capture aec pointer */ + cptr_update_aec = update_capt_aec_ptr(cptr_rt); + + if (pcm->enable_aec && cptr_update_aec) { + /* update playback aec pointer */ + update_play_aec_ptr(play_rt); + play_done = 1; + } + spin_unlock_irq(&cptr_rt->ptr_lock); + + /* wakeup capture read */ + wake_up_interruptible(&(pcm->capt_wq)); + + /* wakeup playback write */ + if (play_done) + wake_up_interruptible(&(pcm->play_wq)); + + if ((pcm->aec_high_prio == 2) + && ((get_aec_bytes(cptr_rt) < cptr_rt->cfg.threshold))) { + pcm->aec_high_prio = -1; + wake_up_interruptible(&pcm->aec_prio_wq); + } + } + + if (spend_valid) { + end_time = sched_clock(); + spend = ak39_spend_us(start_time, end_time); + aec_cost += spend; + } + +#if AEC_TASK_DEBUG + if (play_rt) { + ak_pcm_debug("[PB]: aec_bytes:%u, hw_pos: %u, aec_ptr:%u", + get_aec_bytes(play_rt), play_rt->hw_ptr, play_rt->aec_ptr); + } + if (cptr_rt) { + ak_pcm_debug("[CP]: aec_bytes:%u, hw_pos: %u, aec_ptr:%u", + get_aec_bytes(cptr_rt), cptr_rt->hw_ptr, cptr_rt->aec_ptr); + } + ak_pcm_debug("aec_cost:%u, spend:%d, af_cnt: %d", + aec_cost, spend, aec_frame_count); + ak_pcm_func_exx("T-"); +#endif +} + +static int aec_thread(void *data) +{ + struct akpcm *pcm = (struct akpcm *)data; + + ak_pcm_func("enter"); + + while (pcm->aec_thread_run) { + if (wait_event_interruptible(pcm->aec_wq, + (pcm->aec_data_triger || !(pcm->aec_thread_run))) < 0){ + ak_pcm_debug("aec thread wakeup by signal!"); + return -ERESTARTSYS; + } + + if (!pcm->aec_thread_run) + break; + + mutex_lock(&(pcm->aec_status_lock)); + if (pcm->aec_filter) { + handle_aec_task(pcm); + } + mutex_unlock(&(pcm->aec_status_lock)); + pcm->aec_data_triger = 0; + } + + ak_pcm_func("stop working"); + + /* Wait until we are told to stop */ + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (kthread_should_stop()) + break; + schedule(); + } + __set_current_state(TASK_RUNNING); + + ak_pcm_func("leave"); + + return 0; +} + +static int supervise_aec_priority(void *data) +{ + struct akpcm *pcm = (struct akpcm *)data; + struct akpcm_runtime *cptr_rt = pcm->cptr_rt; + struct sched_param param; + + ak_pcm_func("enter"); + + while (pcm->aec_supervisor_thread_run) { + if (wait_event_interruptible(pcm->aec_prio_wq, + ((pcm->aec_high_prio == -1) + || (pcm->aec_high_prio == 1) + || !(pcm->aec_supervisor_thread_run)))){ + ak_pcm_debug("aec priority supervisor thread wakeup by signal!"); + return -ERESTARTSYS; + } + + switch (pcm->aec_high_prio) { + case 1: //to raise + param.sched_priority = MAX_RT_PRIO-2; + + ak_pcm_info("raise aec task's priority, aec_bytes=%d", + get_aec_bytes(cptr_rt)); + sched_setscheduler_nocheck(pcm->aec_thread, SCHED_FIFO, ¶m); + pcm->aec_high_prio = 2; + break; + case -1: //to restore + param.sched_priority = MAX_RT_PRIO-1; + + ak_pcm_info("restore aec task's priority, aec_bytes=%d", + get_aec_bytes(cptr_rt)); + sched_setscheduler_nocheck(pcm->aec_thread, SCHED_FIFO, ¶m); + pcm->aec_high_prio = 0; + break; + default: + break; + } + } + + ak_pcm_func("stop working"); + + /* Wait until we are told to stop */ + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (kthread_should_stop()) + break; + schedule(); + } + __set_current_state(TASK_RUNNING); + + ak_pcm_func("leave"); + + return 0; +} + +static inline void exit_aec_thread(struct akpcm *pcm) +{ + /* let two aec thread exit */ + pcm->aec_thread_run = 0; + pcm->aec_supervisor_thread_run = 0; + if (pcm->aec_prio_supervisor_thread) { + wake_up_interruptible(&(pcm->aec_prio_wq)); + kthread_stop(pcm->aec_prio_supervisor_thread); + pcm->aec_prio_supervisor_thread = NULL; + } + + if (pcm->aec_thread) { + wake_up_interruptible(&(pcm->aec_wq)); + kthread_stop(pcm->aec_thread); + pcm->aec_thread = NULL; + } +} + +static int create_aec_thread(struct akpcm *pcm) +{ + int ret = 0; + struct sched_param param = {0}; + + init_waitqueue_head(&(pcm->aec_wq)); + + /* create aec thread */ + pcm->aec_thread = kthread_create(aec_thread, pcm, "aec_task"); + if (IS_ERR(pcm->aec_thread)) { + ak_pcm_info_ex("ERROR! unable to create aec thread: %ld", + PTR_ERR(pcm->aec_thread)); + ret = PTR_ERR(pcm->aec_thread); + goto create_thread_end; + } + + param.sched_priority = (MAX_RT_PRIO - 1); + /* similar to workqueues */ + sched_setscheduler_nocheck(pcm->aec_thread, SCHED_FIFO, ¶m); + + /* create aec priority supervisor thread */ + pcm->aec_high_prio = 0; + init_waitqueue_head(&(pcm->aec_prio_wq)); + pcm->aec_prio_supervisor_thread = kthread_create(supervise_aec_priority, + pcm, "aec_priority"); + if (IS_ERR(pcm->aec_prio_supervisor_thread)) { + ak_pcm_info_ex("ERROR! unable to create aec priority supervisor " + "thread: %ld", PTR_ERR(pcm->aec_prio_supervisor_thread)); + ret = PTR_ERR(pcm->aec_prio_supervisor_thread); + goto create_thread_end; + } + + /* give it a very high priority */ + param.sched_priority = (MAX_RT_PRIO - 3); + /* similar to workqueues */ + sched_setscheduler_nocheck(pcm->aec_prio_supervisor_thread, + SCHED_FIFO, ¶m); + +create_thread_end: + if (ret) { + exit_aec_thread(pcm); + } + + return ret; +} + +static int open_aec_lib(struct akpcm *pcm) +{ + int ret = 0; + T_AEC_INPUT p_aecin; + + memset(&pcm->aec_bufs, 0, sizeof(pcm->aec_bufs)); + pcm->aec_bufs.len_far = NN; + pcm->aec_bufs.len_near = NN; + pcm->aec_bufs.len_out = NN; + + memset(&p_aecin, 0, sizeof(p_aecin)); + p_aecin.cb_fun.Malloc = akpcm_malloc_cb; + p_aecin.cb_fun.Free = akpcm_free_cb; + p_aecin.cb_fun.printf = (AEC_CALLBACK_FUN_PRINTF)printk; + p_aecin.cb_fun.notify = akpcm_notify_cb; + p_aecin.m_info.strVersion = ECHO_LIB_VERSION_STRING; + p_aecin.m_info.chip = ECHO_CHIP_AK39XXEV3; + p_aecin.m_info.m_Type = AEC_TYPE_1; + p_aecin.m_info.m_BitsPerSample = 16; + p_aecin.m_info.m_Channels = 1; + + if (pcm->cptr_rt) + p_aecin.m_info.m_SampleRate = pcm->cptr_rt->cfg.rate; + else + p_aecin.m_info.m_SampleRate = 8000; + + p_aecin.m_info.m_Private.m_aec.m_framelen = NN; + p_aecin.m_info.m_Private.m_aec.m_tail = TAIL; + p_aecin.m_info.m_Private.m_aec.m_aecEna = pcm->app_enable_aec; + p_aecin.m_info.m_Private.m_aec.m_PreprocessEna = pcm->app_enable_nr; + p_aecin.m_info.m_Private.m_aec.m_agcEna = pcm->app_enable_agc; + p_aecin.m_info.m_Private.m_aec.m_agcLevel = agc_level; + p_aecin.m_info.m_Private.m_aec.m_maxGain = max_gain; + p_aecin.m_info.m_Private.m_aec.m_farThreshold = far_threshold; + p_aecin.m_info.m_Private.m_aec.m_farDigiGain = far_digi_gain; + p_aecin.m_info.m_Private.m_aec.m_nearDigiGain = near_digi_gain; + + p_aecin.m_info.m_Private.m_aec.m_minGain = min_gain; + p_aecin.m_info.m_Private.m_aec.m_noiseSuppressDb = noise_suppress_db; + p_aecin.m_info.m_Private.m_aec.m_nearSensitivity = near_sensitivity; + + pcm->aec_filter = AECLib_Open(&p_aecin); + if (!pcm->aec_filter) { + ak_pcm_err_ex("AEC open failed!"); + ret = -EPERM; /* Operation not permitted */ + } else { + ak_pcm_info_ex("AEC open success, version:%s", ECHO_LIB_VERSION_STRING); + } + + return ret; +} + +static int open_aec(void) +{ + int ret = 0; + struct akpcm *pcm = get_akpcm_ptr(); + T_S8 * version = NULL; + T_AUDIO_FILTER_CB_FUNS cb_filter={0}; + + int enable_aec = pcm->app_enable_aec; + int enable_nr = pcm->app_enable_nr; + int enable_agc = pcm->app_enable_agc; + + if (pcm->aec_filter) { + ak_pcm_info_ex("skip, because aec is already opened."); + return 0; + } + + if (pcm->eq_enable && !pcm->filter_handle) { + if (open_filter_lib(pcm)) { + ak_pcm_err_ex("Error! open_filter_libis NULL!"); + return -EPERM; + } else { + + cb_filter.printf = (MEDIALIB_CALLBACK_FUN_PRINTF)printk; + version = _SD_GetAudioFilterVersions(&cb_filter); + ak_pcm_info_ex("filter lib open success, version:%s", version); + + if (set_filter_lib_param(pcm, peqpara)) { + ak_pcm_err_ex("Error! set_filter_lib_param error!"); + return -EPERM; + } + } + } + + ak_pcm_info_ex("aec=%d, nr=%d, agc=%d", enable_aec, enable_nr, enable_agc); + if (!enable_aec && !enable_nr) + return 0; + + ak_pcm_assert(pcm->aec_status == AEC_STATUS_IDLE); + if (open_aec_lib(pcm)) { + return -EPERM; + } + + /* prepare aec result buf */ + pcm->aec_out_buf = (T_S16 *)kmalloc(AEC_FRAME_BYTES, GFP_KERNEL); + if (!pcm->aec_out_buf) { + ak_pcm_err_ex("alloc AEC out buf failed!"); + ret = -ENOMEM; + goto open_aec_end; + } + + pcm->enable_aec = enable_aec; + pcm->enable_nr = enable_nr; + pcm->enable_agc = enable_agc; + + /* aec thread working */ + pcm->aec_thread_run = 1; + pcm->aec_data_triger = 0; + aec_cost=0; + aec_frame_count=0; + wake_up_process(pcm->aec_thread); + + /* aec priority supervisor thread working */ + pcm->aec_supervisor_thread_run = 1; + wake_up_process(pcm->aec_prio_supervisor_thread); + +open_aec_end: + if (ret) { + release_aec_resource(pcm); + } + + return ret; +} + +static int close_aec(struct akpcm *pcm) +{ + if (!pcm->aec_filter) { + ak_pcm_info_ex("skip, because aec is not opened."); + return 0; + } + + ak_pcm_func("+"); + + update_aec_status(pcm, AEC_STATUS_IDLE); + print_isr(pcm->aec_dump_type); + aec_dump_complete(pcm->aec_dump_type); + + /* restore hp and mic gain */ + akpcm_set_hp_gain(pcm->mixer_volume[MIXER_VOL_HP]); + akpcm_set_mic_gain(pcm->mixer_volume[MIXER_VOL_MIC]); + + pcm->enable_aec = 0; + pcm->enable_nr = 0; + pcm->enable_agc = 0; + release_aec_resource(pcm); + + ak_pcm_info_ex("AEC: %d frames, total: %d(ms), %d(us) per frame", + aec_frame_count, (aec_cost / 1000), + aec_frame_count ? (aec_cost / aec_frame_count) : 0); + + ak_pcm_func("-"); + + return 0; +} + +/* check aec prerequisite, and try to open aec or just nr */ +static int try_open_aec(struct akpcm *pcm) +{ + int adc_ready = is_capture_ready(pcm); + int dac_ready = is_playback_ready(pcm); + int dac_working = is_playback_working(pcm); + int adc_working = is_capture_working(pcm); + int ret = -1; + + ak_pcm_func("+ app_enable_aec=%d, app_enable_nr=%d", + pcm->app_enable_aec, pcm->app_enable_nr); + ak_pcm_debug("adc_ready=%d, dac_ready=%d, adc_working=%d, dac_working=%d", + adc_ready, dac_ready, adc_working, dac_working); + + ak_pcm_assert(pcm->aec_status == AEC_STATUS_IDLE); + + if (adc_ready) { + /* user want to enable AEC */ + if (pcm->app_enable_aec) { + /* adc and dac must all be ready. */ + if (dac_ready) { + /* adc and dac must be the same sample rate. */ + if((pcm->play_rt->cfg.rate == pcm->cptr_rt->cfg.rate)) { + //&& !(adc_working && dac_working)) { + ret = open_aec(); + } + } else if (pcm->app_enable_nr) { /* user also enable nr */ + /* to open aec with only nr enabled */ + ak_pcm_info_ex("dac not ready, open with nr only"); + pcm->app_enable_aec = 0; + ret = open_aec(); + pcm->app_enable_aec = 1; + } + } else if (pcm->app_enable_nr) { /* user just want to enable NR */ + ret = open_aec(); + } + } + + ak_pcm_func("- %s", ret ? "not opened" : "opened"); + + return ret; +} + +static e_aec_status switch_aec_status(struct akpcm *pcm, t_aec_event event, + e_aec_status old_status) +{ + e_aec_status new_status = old_status; + + switch (pcm->aec_status) { + case AEC_STATUS_IDLE: + if ((event == AEC_EVENT_OPEN) + && !pcm->aec_filter /* make sure aec not opened */ + && (try_open_aec(pcm) >= 0)){ + if (pcm->enable_aec) { + int dac_working = is_playback_working(pcm); + int adc_working = is_capture_working(pcm); + ak_pcm_assert(!(dac_working&&adc_working)); + + new_status = AEC_STATUS_AEC_OPENED; + if (dac_working && !adc_working) { + new_status = AEC_STATUS_WAIT_ADC; + } else if (!dac_working && adc_working) { + new_status = AEC_STATUS_SYNC_DAC; + } + } else if (pcm->enable_nr) { + /* only enable NR/AGC */ + new_status = AEC_STATUS_NR_OPENED; + update_aec_status(pcm, new_status); + + new_status = AEC_STATUS_NR_WORKING; + } + } + break; + case AEC_STATUS_AEC_OPENED: + if (event == AEC_EVENT_DAC_START) { + new_status = AEC_STATUS_WAIT_ADC; + } else if (event == AEC_EVENT_ADC_START) { + ak_pcm_assert(!is_playback_working(pcm)); + + new_status = AEC_STATUS_SYNC_DAC; + } + break; + case AEC_STATUS_WAIT_ADC: + if (event == AEC_EVENT_ADC_START) { + ak_pcm_assert(!is_capture_working(pcm)); + + new_status = AEC_STATUS_SYNC_ADC; + } + break; + case AEC_STATUS_SYNC_ADC: + if (event == AEC_EVENT_ADC_START) { + /* shall be in isr context, so do least thing */ + new_status = AEC_STATUS_AEC_WORKING; + } + break; + case AEC_STATUS_SYNC_DAC: + if (event == AEC_EVENT_DAC_START) { + /* shall be in isr context, so do least thing */ + new_status = AEC_STATUS_AEC_WORKING; + } + break; + default: + break; + } + + return new_status; +} + +static e_aec_status start_procedure(struct akpcm *pcm, e_aec_status new_status) +{ + switch (new_status) { + case AEC_STATUS_SYNC_ADC: + /* wait until adc dma started */ + ak_pcm_debug("adc sync wait"); + update_aec_status(pcm, new_status); + if (wait_event_interruptible(pcm->aec_sync_wq, + (is_capture_working(pcm))) < 0) { + return -ERESTARTSYS; + } + + ak_pcm_debug("adc sync started"); + /* aec_status have been altered in isr */ + new_status = pcm->aec_status; + break; + case AEC_STATUS_SYNC_DAC: + playback_start_force(pcm); + + /* wait until dac dma started */ + ak_pcm_debug("dac sync wait"); + update_aec_status(pcm, new_status); + if (wait_event_interruptible(pcm->aec_sync_wq, + (is_playback_working(pcm))) < 0) { + return -ERESTARTSYS; + } + + ak_pcm_debug("dac sync started"); + /* aec_status have been altered in isr */ + new_status = pcm->aec_status; + break; + default: + break; + } + + return new_status; +} + +static t_aec_event exit_procedure(struct akpcm *pcm, t_aec_event event, + e_aec_status old_status, e_aec_status new_status) +{ + t_aec_event next_event = AEC_EVENT_NONE; + + if (pcm->enable_aec) { + switch (event) { + case AEC_EVENT_DAC_STOP: + case AEC_EVENT_ADC_STOP: + next_event = AEC_EVENT_OPEN; // reopen + /* intentionally fall down */ + case AEC_EVENT_AEC_STOP: + close_filter_lib(pcm); + + close_aec(pcm); + new_status = AEC_STATUS_IDLE; + break; + default: + break; + } + } else if (pcm->enable_nr) { + /* only nr is working, stopping of adc can cause nr to stop */ + switch (event) { + case AEC_EVENT_ADC_STOP: + next_event = AEC_EVENT_OPEN; // reopen + /* intentionally fall down */ + case AEC_EVENT_AEC_STOP: + close_filter_lib(pcm); + close_aec(pcm); + new_status = AEC_STATUS_IDLE; + break; + default: + break; + } + } + + if (new_status != old_status) { + ak_pcm_debug_ex("event:%d, status: %d->%d", + event, old_status, new_status); + update_aec_status(pcm, new_status); + } + + return next_event; +} + +static int handle_aec_event(struct akpcm *pcm, t_aec_event event) +{ + e_aec_status old_status = pcm->aec_status; + e_aec_status new_status = old_status; + t_aec_event next_event = AEC_EVENT_NONE; + + ak_pcm_func("+ aec_status=%d, event=%d", pcm->aec_status, event); + + new_status = switch_aec_status(pcm, event, old_status); + + /* start procedure */ + if (new_status != old_status) { + ak_pcm_debug_ex("aec status switch: %d->%d", old_status, new_status); + new_status = start_procedure(pcm, new_status); + } + + /* + * exit procedure + * aec is working, stopping of adc or dac can cause aec to stop. + */ + next_event = exit_procedure(pcm, event, old_status, new_status); + + ak_pcm_func_exx("-"); + + return (next_event == AEC_EVENT_NONE)? 0 : handle_aec_event(pcm, next_event); +} + +static void handle_event_wq(unsigned long data) +{ + struct akpcm *pcm = get_akpcm_ptr(); + t_aec_event buf_event; + int ret = 0; + + ak_pcm_func("enter"); + + mutex_lock(&(pcm->aec_status_lock)); + while (!kfifo_is_empty(&aec_event_buf)) { + ret = kfifo_get(&aec_event_buf, &buf_event); + ak_pcm_debug("process buffered aec event %d", buf_event); + handle_aec_event(pcm, buf_event); + } + mutex_unlock(&(pcm->aec_status_lock)); +} + +int aec_set_param(struct akpcm *pcm, int value) +{ + ak_pcm_func("+ value=%d", value); + + pcm->app_flag = value; + pcm->app_enable_aec = test_bit(ECHO_BIT_AEC, &(pcm->app_flag)); + pcm->app_enable_nr = test_bit(ECHO_BIT_NR, &(pcm->app_flag)); + pcm->app_enable_agc = test_bit(ECHO_BIT_AGC, &(pcm->app_flag)); + + ak_pcm_debug_ex("aec:%d->%d, nr:%d->%d, agc=%d->%d", + pcm->enable_aec, pcm->app_enable_aec, + pcm->enable_nr, pcm->app_enable_nr, + pcm->enable_agc, pcm->app_enable_agc); + + /* params not changed */ + if ((pcm->enable_aec == pcm->app_enable_aec) + && (pcm->enable_nr == pcm->app_enable_nr) + && (pcm->enable_agc == pcm->app_enable_agc)) { + return 0; + } + + /* params has been changed, then open / re-open / close aec */ + if (pcm->enable_aec || pcm->enable_nr) { + /* aec is already opened, stop aec */ + aec_event_handler(pcm, AEC_EVENT_AEC_STOP); + } + + if (pcm->app_enable_aec || pcm->app_enable_nr) { + /* user needs aec */ + aec_event_handler(pcm, AEC_EVENT_OPEN); + } + + ak_pcm_func_exx("-"); + + return 0; +} + +int aec_event_handler(struct akpcm *pcm, t_aec_event event) +{ + print_event_string(event); + + if (in_interrupt()) { + ak_pcm_debug("put aec event %d into buffer", event); + kfifo_put(&aec_event_buf, &event); + queue_work(pcm->aec_event_wq, &(pcm->aec_event_work)); + return 0; + } + + /*handle event from isr*/ + handle_event_wq(0); + + mutex_lock(&(pcm->aec_status_lock)); + if (event > AEC_EVENT_NONE) + handle_aec_event(pcm, event); + + mutex_unlock(&(pcm->aec_status_lock)); + + return 0; +} + +int aec_init(struct akpcm *pcm) +{ + pcm->enable_aec = 0; + pcm->enable_nr = 0; + pcm->enable_agc = 0; + pcm->app_flag = 0; + pcm->app_enable_aec = 0; + pcm->app_enable_nr = 0; + pcm->app_enable_agc = 0; + pcm->aec_dump_type = 0; + pcm->eq_enable = 0; + pcm->aec_status = AEC_STATUS_IDLE; + mutex_init(&(pcm->aec_status_lock)); + init_waitqueue_head(&(pcm->aec_sync_wq)); + pcm->aec_filter = NULL; + pcm->aec_event_wq = create_singlethread_workqueue("aec_event"); + INIT_WORK(&(pcm->aec_event_work), (work_func_t)handle_event_wq); + + pcm->aec_thread_run = 0; + pcm->aec_data_triger = 0; + + return create_aec_thread(pcm); +} + +void aec_exit(struct akpcm *pcm) +{ + exit_aec_thread(pcm); +} + +T_VOID aec_set_da_volume(struct akpcm *pcm, T_S16 *data, T_U16 data_len) +{ + T_S16 da_thresh = far_threshold; + T_S16 max_sample = 0; + T_S32 factor; + T_U32 i; + T_S32 tmp; + + for (i = 0; i < data_len; i++) { + tmp = data[i]; + + data[i] = (T_S16)tmp; + + tmp = (data[i] >= 0)? data[i] : -data[i]; + if (tmp > max_sample) + max_sample = (T_S16)tmp; + } + + if (max_sample > da_thresh) { + factor = (((T_S32)da_thresh) << 15) / max_sample; + if (factor < DA_factor) + DA_factor = factor; + else + DA_factor = (DA_factor * AK32Q15(0.99) + factor * AK32Q15(0.01)) >> 15; + } else { + DA_factor = (DA_factor * AK32Q15(.505)) >> 14; // *1.01 + if (DA_factor > 32767) + DA_factor = 32767; + } + + for(i = 0; i < data_len; i++) { + tmp = data[i]; + tmp = (tmp * DA_factor) >> 15; + data[i] = (T_S16)tmp; + } +} + +void aec_set_nr_max(struct akpcm *pcm, int enable_max) +{ +#if 0 + T_AEC_REQUEST aec_req = {0}; + T_U32 gain = 0; + + if (enable_max) { + gain = AK16Q10(0); + } else { + gain = AK16Q10(0.1); + } + //aec_req.m_req_type = AEC_REQ_SET_AGC_MINGAIN2; + aec_req.addrLen = 4; + aec_req.addr = (T_U8*)(&gain); + + AECLib_Request(pcm->aec_filter, &aec_req); +#endif + ak_pcm_info_ex("can not ste aec_set_nr_max"); +} + +int aec_get_echo_params(struct akpcm *pcm, T_U8 *aec_param) +{ + T_AEC_PARAMS *aec_param_info; + aec_param_info = (T_AEC_PARAMS *)aec_param; + if (!pcm->aec_filter) { + ak_pcm_info_ex("pcm->aec_filter=%p", pcm->aec_filter); + aec_param_info->m_farThreshold = far_threshold; + aec_param_info->m_agcLevel = agc_level; + aec_param_info->m_maxGain = max_gain; + aec_param_info->m_minGain = min_gain; + aec_param_info->m_farDigiGain = far_digi_gain; + aec_param_info->m_nearDigiGain = near_digi_gain; + aec_param_info->m_noiseSuppressDb = noise_suppress_db; + aec_param_info->m_nearSensitivity = near_sensitivity; + aec_param_info->m_aecEna = 0; + aec_param_info->m_agcEna = 0; + aec_param_info->m_PreprocessEna = 0; + return -EFAULT; + } else { + T_AEC_REQUEST aec_request = {0}; + ak_pcm_info_ex("aec_get_echo_params"); + + aec_request.m_req_type = AEC_REQ_GET_PARAMS; + aec_request.addr = aec_param; + aec_request.addrLen = sizeof(T_AEC_PARAMS); + AECLib_Request(pcm->aec_filter, &aec_request); + } + return 0; +} + +int aec_set_echo_params(struct akpcm *pcm, T_U8 *aec_param) +{ + T_AEC_REQUEST aec_request = {0}; + T_AEC_PARAMS *aec_param_info; + int ret = 0; + if (!pcm->aec_filter) { + ak_pcm_info_ex("pcm->aec_filter=%p", pcm->aec_filter); + ret = -EFAULT; + } else { + ak_pcm_info_ex("aec_set_echo_params"); + + aec_request.m_req_type = AEC_REQ_SET_PARAMS; + aec_request.addr = (T_U8 *)aec_param; + aec_request.addrLen = sizeof(T_AEC_PARAMS); + AECLib_Request(pcm->aec_filter, &aec_request); + } + + /* notice the playback the far threshold */ + aec_param_info = (T_AEC_PARAMS *)aec_param; + far_threshold = aec_param_info->m_farThreshold; + agc_level = aec_param_info->m_agcLevel; + max_gain = aec_param_info->m_maxGain; + min_gain = aec_param_info->m_minGain; + far_digi_gain = aec_param_info->m_farDigiGain; + near_digi_gain = aec_param_info->m_nearDigiGain; + noise_suppress_db = aec_param_info->m_noiseSuppressDb; + near_sensitivity = aec_param_info->m_nearSensitivity; + + if (aec_param_info->m_PreprocessEna) { + set_bit(ECHO_BIT_NR, &(pcm->app_flag)); + } else { + clear_bit(ECHO_BIT_NR, &(pcm->app_flag)); + } + + if (aec_param_info->m_agcEna) { + set_bit(ECHO_BIT_AGC, &(pcm->app_flag)); + } else { + clear_bit(ECHO_BIT_AGC, &(pcm->app_flag)); + } + return ret; +} + +int aec_set_eq_params(struct akpcm *pcm, T_S32 *eq_param) +{ + T_AUDIO_FILTER_CB_FUNS cb_filter={0}; + T_S8 * version = NULL; + + ak_pcm_info_ex("aec_set_eq_params, eq_param=%p, pcm =%p", eq_param, pcm); + + if (!(pcm->filter_handle)) { + if (open_filter_lib(pcm)) { + ak_pcm_err_ex("Error! open_filter_libis NULL!"); + return -EFAULT; + } else { + cb_filter.printf = (MEDIALIB_CALLBACK_FUN_PRINTF)printk; + version = _SD_GetAudioFilterVersions(&cb_filter); + ak_pcm_info_ex("filter lib open success, version:%s", version); + } + } + + if (set_filter_lib_param(pcm, eq_param)) { + ak_pcm_err_ex("Error! set_filter_lib_param error!"); + return -EFAULT; + } + + memcpy(peqpara, eq_param, sizeof(T_S32) * EQ_ARRAY_NUMBER); + + if (!pcm->eq_enable) + pcm->eq_enable = 1; + + return 0; +} + +void aec_enable_eq(struct akpcm *pcm, int eq_enable) +{ + T_AUDIO_FILTER_CB_FUNS cb_filter={0}; + T_S8 * version = NULL; + + if (pcm->eq_enable == eq_enable) + return; + + if (eq_enable) { + if (!pcm->filter_handle) { + if (open_filter_lib(pcm)) { + ak_pcm_err_ex("Error! open_filter_libis NULL!"); + return; + } else { + cb_filter.printf = (MEDIALIB_CALLBACK_FUN_PRINTF)printk; + version = _SD_GetAudioFilterVersions(&cb_filter); + ak_pcm_info_ex("filter lib open success, version:%s", version); + + if (set_filter_lib_param(pcm, peqpara)) { + ak_pcm_err_ex("Error! set_filter_lib_param error!"); + return; + } + } + } + pcm->eq_enable = 1; + } else { + pcm->eq_enable = 0; + close_filter_lib(pcm); + } + + ak_pcm_info_ex("eq enable = %d", pcm->eq_enable); +} + +void aec_set_dump_type(struct akpcm *pcm, int dump_type){ + if (dump_type < 0 || dump_type > AEC_DUMP_DEBUG) + return; + pcm->aec_dump_type = dump_type; + ak_pcm_debug("dump type= %d", pcm->aec_dump_type); +} + diff --git a/drivers/misc/akpcm/aec.h b/drivers/misc/akpcm/aec.h new file mode 100644 index 00000000..998ec6d9 --- /dev/null +++ b/drivers/misc/akpcm/aec.h @@ -0,0 +1,75 @@ +#ifndef __AKPCM_AEC_H__ +#define __AKPCM_AEC_H__ + +#include + +#include "akpcm_defs.h" + +///////////////////////////////////////////////////// +// AEC params +#define NN 128 // do not change +#define AEC_FRAME_BYTES (NN << 1) +#define TAIL (NN*4) // aec filter length +#define AEC_DA_THRESH AK32Q15(0.3) // DA޷<1 +#define AEC_DA_HW_GAIN 5 // 0 ~ HP_GAIN_MAX +#define AEC_AD_HW_GAIN 4 // 0 ~ MAX_MIC_GAIN +#define AEC_AGC_LEVEL AK32Q15(0.75) // ԶƵĿȣ<1 +#define FAR_FRAME_SIZE (AEC_FRAME_BYTES << 1) + +/* aec frame is per 16 ms, schedule to wait 10ms. unit: jiffies. */ +#define AEC_TASK_TIMER (10*HZ/1000) +#define EQ_ARRAY_NUMBER 54 + +static const T_S16 aec_volume[6] = { // Q10 format + AK16Q10(0.178), // 0, -15dB + AK16Q10(0.251), // 1, -12dB + AK16Q10(0.355), // 2, -9dB + AK16Q10(0.501), // 3, -6dB + AK16Q10(0.708), // 4, -3dB + AK16Q10(0.999), // 5, 0dB +}; + +typedef enum { + AEC_EVENT_NONE = 0, /* 0 none, general trigger */ + AEC_EVENT_OPEN, /* 1 */ + AEC_EVENT_ADC_START, /* 2 */ + AEC_EVENT_DAC_START, /* 3 */ + AEC_EVENT_AEC_START, /* 4 */ + AEC_EVENT_ADC_STOP, /* 5 */ + AEC_EVENT_DAC_STOP, /* 6 */ + AEC_EVENT_AEC_STOP, /* 7 */ +}t_aec_event; + +#define aec_need_playback(pcm) \ + (((pcm)->aec_status >= AEC_STATUS_SYNC_ADC) \ + && ((pcm)->aec_status <= AEC_STATUS_AEC_WORKING)) + +/* + * playback & capture helper routine + * get how many data to be processed by AEC in playback or capture buffer + */ +unsigned int get_aec_bytes(struct akpcm_runtime *rt); + +static inline void update_aec_status(struct akpcm *pcm, e_aec_status status) +{ + pcm->aec_status = status; +} + +int aec_init(struct akpcm *pcm); +void aec_exit(struct akpcm *pcm); + +int aec_set_param(struct akpcm *pcm, int value); +int aec_event_handler(struct akpcm *pcm, t_aec_event event); + +T_VOID aec_set_da_volume(struct akpcm *pcm, T_S16 *data, T_U16 data_len); + +void aec_set_nr_max(struct akpcm *pcm, int enable_max); +int aec_get_echo_params(struct akpcm *pcm, T_U8 *aec_param); +int aec_set_echo_params(struct akpcm *pcm, T_U8 *aec_param); + +int aec_set_eq_params(struct akpcm *pcm, T_S32 *eq_param); +void aec_enable_eq(struct akpcm *pcm, int eq_enable); + +void aec_set_dump_type(struct akpcm *pcm, int dump_type); + +#endif diff --git a/drivers/misc/akpcm/aec_dump.c b/drivers/misc/akpcm/aec_dump.c new file mode 100644 index 00000000..5548e5d1 --- /dev/null +++ b/drivers/misc/akpcm/aec_dump.c @@ -0,0 +1,349 @@ +#include +#include +#include +#include +#include +#include + +#include "akpcm_defs.h" +#include "aec_dump.h" + +#define CONFIG_AEC_DUMP_FILE_WAV + + +///////////////////////////////////////////////////// +/* aec debug info */ +static char isr_print_buf[1000]; +static long in_isr_print_buf = 0; + +unsigned char dump_head[20] = "\0AECdump"; // 8 char + +/* aec dump file info */ +typedef struct S_WAVE_HEADER { + char riff[4]; // "RIFF" 0 + long file_size; // in bytes 4 + char wavefmt[8]; // "WAVE" 8 + long chunk_size; // in bytes (16 for PCM) 16 + short format_tag; // 1=PCM, 2=ADPCM, 3=IEEE float, 6=A-Law, 7=Mu-Law 20 + short num_chans; // 1=mono, 2=stereo 22 + long sample_rate; // 24 + long bytes_per_sec; // 28 + short bytes_per_samp; // 2=16-bit mono, 4=16-bit stereo 32 + short bits_per_samp; // 34 + char data[4]; // "data" 36 + long data_length; // in bytes 40 +} WAVE_HEADER; //size 44 + +typedef struct _T_AEC_DUMP_HANDLE_ +{ + char *file_name; + struct file *file; + int cur; // current ptr + int data_len; +}T_AEC_DUMP_HANDLE; + +static T_AEC_DUMP_HANDLE aec_dump_handle[4] = {{"near"},{"far"},{"res"},{"after_eq"}}; +static int aec_dump_sn = 0; // serial number + +///////////////////////////////////////////////////// +/* aec debug function */ +void print_isr_debug(void) +{ + int i = 0; + + if (in_isr_print_buf == 0) + return; + + for (i=0; iid]; + char file_path[100]; +#ifdef CONFIG_AEC_DUMP_FILE_WAV + WAVE_HEADER WaveHead; +#endif + + sprintf(file_path, ("/tmp/%s_aec_%s" +#ifdef CONFIG_AEC_DUMP_FILE_WAV + ".wav"), +#else + ".pcm"), +#endif + time_str, + hDump->file_name); + + /* create file */ + hDump->file = file_open(file_path, O_RDWR|O_CREAT, 0644); + hDump->cur = 0; + hDump->data_len = 0; + +#ifdef CONFIG_AEC_DUMP_FILE_WAV + /* write wav head */ + memcpy(WaveHead.riff, "RIFF", 4); + WaveHead.file_size = sizeof(WAVE_HEADER) - 8; + memcpy(WaveHead.wavefmt, "WAVEfmt ", 8); + WaveHead.chunk_size = 16; + WaveHead.format_tag = 1; + WaveHead.num_chans = 1; + WaveHead.sample_rate = info->sample_rate; + WaveHead.bytes_per_sec = info->sample_rate * 2; + WaveHead.bytes_per_samp = 2; + WaveHead.bits_per_samp = 16; + memcpy(WaveHead.data, "data", 4); + WaveHead.data_length = 0; + + file_write(hDump->file, 0, (unsigned char *)&WaveHead, sizeof(WaveHead)); + hDump->cur += sizeof(WaveHead); +#endif + + //printk(KERN_ERR "[%s] open id %d, handle %p\n", __FUNCTION__, id, hDump->file); +} + +static void start_dump(struct aec_dump_info *info) +{ + struct timeval time; + unsigned long local_time; + struct tm tm; + char time_str[100]; + + do_gettimeofday(&time); + local_time = (u32)(time.tv_sec); + time_to_tm(local_time, 0, &tm); + + sprintf(time_str, "%04ld-%02d-%02d_%02d-%02d-%02d_%d", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, aec_dump_sn); + ak_pcm_info("aec dump start: %s", time_str); + + if (info->id >= AEC_DUMP_RES) { + ++aec_dump_sn; + } + open_dump_file(time_str, info); +} + +static void dump_complete_file(unsigned char id) +{ + T_AEC_DUMP_HANDLE *hDump = &aec_dump_handle[id]; +#ifdef CONFIG_AEC_DUMP_FILE_WAV + WAVE_HEADER WaveHead; +#endif + + if (hDump->file == NULL) + return; + +#ifdef CONFIG_AEC_DUMP_FILE_WAV + /* read back wav head */ + file_read(hDump->file, 0, (unsigned char *)&WaveHead, sizeof(WaveHead)); + + /* write wav length */ + WaveHead.data_length = hDump->data_len; + WaveHead.file_size = WaveHead.data_length + (sizeof(WAVE_HEADER) - 8); + file_write(hDump->file, 0, (unsigned char *)&WaveHead, sizeof(WaveHead)); +#endif + + /* close file */ + file_sync(hDump->file); + file_close(hDump->file); + hDump->file = NULL; + hDump->cur = 0; + hDump->data_len = 0; +} + +void aec_dump_complete_all_file(void) +{ + int i = 0; + ak_pcm_debug("aec dump complete"); + + for (i = 0; i < AEC_DUMP_MAX; i ++) { + dump_complete_file(i); + } +} + +void aec_dump_file(struct aec_dump_info *info) +{ + T_AEC_DUMP_HANDLE *hDump = &aec_dump_handle[info->id]; + + if (NULL == hDump->file) { + start_dump(info); + if (NULL == hDump->file) { + return; + } + } + + //printk(KERN_ERR "[%s] dump id %d\n", __FUNCTION__, id); + file_write(hDump->file, hDump->cur, info->data, info->size); + hDump->cur += info->size; + hDump->data_len += info->size; +} + +void aec_dump_sync_file(struct akpcm *pcm) +{ + T_AEC_DUMP_HANDLE *hDump = NULL; + int id; + int b_switch = 0; + int aec_dump_max_size = (pcm->cptr_rt == NULL) ? 480000 : pcm->cptr_rt->cfg.rate * 60;// 1 second = samplerate * 2 bytes, save 30 second + + for (id=0; iddata_len >= aec_dump_max_size) { + b_switch = 1; + } + } + + if (b_switch) { + aec_dump_complete_all_file(); + } else if (hDump->file && AEC_DUMP_FILE != pcm->aec_dump_type) { + aec_dump_complete_all_file(); + } +} + +void putstr_isr_null(char *str) +{ + printk(str); +} + +void print_isr(int dump_type) +{ + switch (dump_type) { + case AEC_DUMP_DEBUG : + print_isr_debug(); + break; + default : + break; + } +} + +void putstr_isr(int dump_type, char *str) +{ + switch (dump_type) { + case AEC_DUMP_FILE : + putstr_isr_null(str); + break; + case AEC_DUMP_DEBUG : + putstr_isr_debug(str); + break; + default : + putstr_isr_null(str); + break; + } +} + +void aec_dump_complete(int dump_type) +{ + switch (dump_type) { + case AEC_DUMP_FILE : + aec_dump_complete_all_file(); + break; + default : + break; + } +} + +void aec_dump(int dump_type, struct aec_dump_info *info) +{ + switch (dump_type) { + case AEC_DUMP_FILE : + aec_dump_file(info); + break; + case AEC_DUMP_DEBUG : + break; + default : + break; + } +} + +void aec_dump_sync(struct akpcm *pcm) +{ + aec_dump_sync_file(pcm); +} diff --git a/drivers/misc/akpcm/aec_dump.h b/drivers/misc/akpcm/aec_dump.h new file mode 100644 index 00000000..a821404f --- /dev/null +++ b/drivers/misc/akpcm/aec_dump.h @@ -0,0 +1,35 @@ +#ifndef _AKPCM_AEC_DUMP_H_ +#define _AKPCM_AEC_DUMP_H_ + +enum aec_dump_id { + AEC_DUMP_NEAR, + AEC_DUMP_FAR, + AEC_DUMP_RES, + AEC_DUMP_AFTER_EQ, + AEC_DUMP_MAX +}; + +enum aec_dump_type { + AEC_DUMP_NULL = 0, + AEC_DUMP_FILE, + AEC_DUMP_DEBUG +}; + +struct aec_dump_info { + unsigned char id; + unsigned char *data; + unsigned long size; + int sample_rate; +}; + +void print_isr(int dump_type); + +void putstr_isr(int dump_type, char *str); + +void aec_dump_complete(int dump_type); + +void aec_dump(int dump_type, struct aec_dump_info *info); + +void aec_dump_sync(struct akpcm *pcm); + +#endif diff --git a/drivers/misc/akpcm/akpcm.c b/drivers/misc/akpcm/akpcm.c new file mode 100644 index 00000000..b1147922 --- /dev/null +++ b/drivers/misc/akpcm/akpcm.c @@ -0,0 +1,745 @@ +/* + * pcm for anyka chip + * Copyright (c) by Anyka, Inc. + * Create by panqihe 2014-06-09 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + +#include "aec.h" +#include "capture.h" +#include "playback.h" + +MODULE_AUTHOR("Anyka, Inc."); +MODULE_DESCRIPTION("akpcm device"); +MODULE_LICENSE("GPL"); + +#define MALLOC_CB_DEBUG (0) + +static struct akpcm akxpcm; + +static inline unsigned int bytes_to_samples(struct akpcm_runtime *rt, + unsigned int size) +{ + return (size * 8 / rt->cfg.sample_bits); +} + +static inline unsigned int samples_to_bytes(struct akpcm_runtime *rt, + unsigned int size) +{ + return (size * rt->cfg.sample_bits / 8); +} + +static inline unsigned int bytes_to_frames(struct akpcm_runtime *rt, + unsigned int size) +{ + unsigned int frame_bits = rt->cfg.channels * rt->cfg.sample_bits; + return (size * 8 / frame_bits); +} + +inline void akpcm_set_mic_gain(unsigned long gain) +{ + ak39_set_mic_gain(gain); +} + +inline void akpcm_set_hp_gain(unsigned long gain) +{ + ak39_set_hp_gain(gain); +} + +static int pcm_major = 0; +static struct class *pcm_class[2]; + +/* AK39 ALSA interfaces*/ +unsigned long *ppb_statu; + +static inline void lock_playback_io(struct akpcm *pcm) +{ + mutex_lock(&pcm->io_lock); +} + +static inline void unlock_playback_io(struct akpcm *pcm) +{ + mutex_unlock(&pcm->io_lock); +} + +static inline void lock_capture_io(struct akpcm *pcm) +{ + mutex_lock(&pcm->io_lock); +} + +static inline void unlock_capture_io(struct akpcm *pcm) +{ + mutex_unlock(&pcm->io_lock); +} + +/** + * set source for OUTPUT(HP or ADC2) + * author: panqihe 2014-06-25 + * @addr: MIXER_SRC_HP or MIXER_SRC_ADC2 + * @src: source + */ +void set_channel_source(struct akpcm *pcm, int addr, int src) +{ + int pa_ctrl; + int s_dac, s_linein, s_mic; + ak_pcm_debug_ex("addr=%d, src=%d", addr, src); + + switch (addr) { + case MIXER_SRC_HP: + if (pcm->bIsHPmuteUsed) { + ak_gpio_setpin(pcm->hpmute_gpio.pin, pcm->hpmute_EnValue); + } + pa_ctrl = ak_gpio_getpin(pcm->SPdown_gpio.pin); + ak_gpio_setpin(pcm->SPdown_gpio.pin, pcm->SPdown_gpio.value); + ak39_set_hp_power((bool)src, pcm->bIsHPmuteUsed == 0); + ak_gpio_setpin(pcm->SPdown_gpio.pin, pa_ctrl); + if(pcm->bIsHPmuteUsed){ + ak_gpio_setpin(pcm->hpmute_gpio.pin, pcm->hpmute_DisValue); + } + ak39_set_hp_in(src); + /*AK37_power_source(src, addr, pcm->mixer_source);*/ + //ak39_set_src_power(0, src); + break; + case MIXER_SRC_LO: + ak_pcm_debug("No MIXER SOURECE FOR LO!"); //while(1); + //ak39_set_src_power(addr, src); + //ak39_set_adc2_in(src); + /* set ADC2 poweron in PCM interface function: capture_prepare() */ + break; + case MIXER_SRC_ADC2: + /*AK37_power_source(src, addr, pcm->mixer_source);*/ + //ak39_set_src_power(1, src); + ak39_set_adc2_in(src); + /* ADC2 is poweron in capture_prepare() */ + break; + default: + ak_pcm_debug("Invalid mixer addr!"); + return; + } + + pcm->mixer_source[addr] = src; + + /* get the route status */ + s_dac = (!!(pcm->mixer_source[MIXER_SRC_HP] & SOURCE_DAC)) | + (!!(pcm->mixer_source[MIXER_SRC_ADC2] & SOURCE_ADC_DAC)); + + s_linein = (!!(pcm->mixer_source[MIXER_SRC_HP] & SOURCE_LINEIN)) | + (!!(pcm->mixer_source[MIXER_SRC_ADC2] & SOURCE_ADC_LINEIN)); + + s_mic = (!!(pcm->mixer_source[MIXER_SRC_HP] & SOURCE_MIC)) | + (!!(pcm->mixer_source[MIXER_SRC_ADC2] & SOURCE_ADC_MIC)); + + ak_pcm_debug_ex("src=%d, s_dac=%d, s_linein=%d, s_mic=%d", + src, s_dac, s_linein, s_mic); + + /* set the power according to the route status */ + ak39_set_linein_power(s_linein); + ak39_set_mic_power(s_mic); +} + +/** + * change source for OUTPUT(HP or ADC2) + * author panqihe 2014-06-25 + * @addr: MIXER_SRC_HP or MIXER_SRC_ADC2 + * @src: source + */ +int change_channel_source(struct akpcm *pcm, int addr, int src) +{ + int change = 0; + if (pcm->mixer_source[addr] != src) { + ak_pcm_debug_ex("source=%d, src=%x", pcm->mixer_source[addr], src); + change = 1; + set_channel_source(pcm, addr, src); + } + + return change; +} + +inline void akpcm_notify_cb(T_U32 code) +{ + //printf("~~~~notify: 0x%x\n", code); +} + +#if MALLOC_CB_DEBUG +/* for test mallic size and time cnt */ +#define ONCE_MALLOC_SIZE 4096 +static int realTotalMallocSize = 0; +static int needTotalMallocSize = 0; +static int maxMallocSize = 0; +static int malloc_time_cnt = 0; +static int free_time_cnt = 0; + +inline void *akpcm_malloc_cb(unsigned long size) +{ + int realSize = size; + void *pbuf = AK_NULL; + + if (realSize%ONCE_MALLOC_SIZE) + { + realSize = (realSize/ONCE_MALLOC_SIZE*ONCE_MALLOC_SIZE) + ONCE_MALLOC_SIZE; + } + needTotalMallocSize += size; + realTotalMallocSize += realSize; + if (realTotalMallocSize > maxMallocSize) + { + maxMallocSize = realTotalMallocSize; + } + + pbuf = (void *)kmalloc(size, GFP_KERNEL); + printk("~~~~malloc: %d, %d, %d, %d, addr=0x%x\n", size, realSize, needTotalMallocSize,realTotalMallocSize, pbuf); + + malloc_time_cnt++; + printk(" malloc_time_cnt = %d\n", malloc_time_cnt); + + //memset(pbuf, 0, size); + return pbuf; +} + +inline void akpcm_free_cb(void * mem) +{ + free_time_cnt++; + printk(" free_time_cnt = %d\n", free_time_cnt); + printk("----free, addr=0x%x\n", mem); + + kfree(mem); + + return; +} +#else +inline void* akpcm_malloc_cb(unsigned long size) +{ + return (void *)kmalloc(size, GFP_KERNEL); +} + +inline void akpcm_free_cb(void * mem) +{ + kfree(mem); +} +#endif + +struct akpcm* get_akpcm_ptr(void) +{ + return &akxpcm; +} + +static const struct file_operations pcm_fops[2] = +{ + [0] = { + .owner = THIS_MODULE, + .open = playback_open, + .release = playback_close, + .unlocked_ioctl = playback_ioctl, + .write = playback_write, + //.read = capture_read, + }, + [1] = { + .owner = THIS_MODULE, + .open = capture_open, + .release = capture_close, + .unlocked_ioctl = capture_ioctl, + //.write = playback_write, + .read = capture_read, + }, +}; + +static void akpcm_cdev_setup(int i) +{ + int err = 0; + dev_t devno = MKDEV(pcm_major, i); + struct device *dev; + char name[2][13] = {"akpcm_class0","akpcm_class1"}; + + + cdev_init(&(akxpcm.cdevice[i]), &pcm_fops[i]); + akxpcm.cdevice[i].owner = THIS_MODULE; + akxpcm.cdevice[i].ops = &pcm_fops[i]; + err = cdev_add(&(akxpcm.cdevice[i]), devno, 1); + if (err) { + ak_pcm_info_ex("Error %d adding anyka akpcm[%d] cdevice", err, i); + while(1); + } + /* auto mknod device node */ + pcm_class[i] = class_create(THIS_MODULE, name[i]); + if (IS_ERR(pcm_class[i])) { + err = PTR_ERR(pcm_class[i]); + ak_pcm_info_ex("akpcm: can't register class[%d]", i); + while(1); + } + dev = device_create(pcm_class[i], NULL, devno, &akxpcm, "akpcm_cdev" "%d", i); + if (IS_ERR(dev)) { + ak_pcm_info_ex("akpcm: can't create akpcm[%d] files", i); + while(1); + } +} + +static void akpcm_cdev_release(int i) +{ + dev_t devno = MKDEV(pcm_major, i); + device_destroy(pcm_class[i], devno); + class_destroy(pcm_class[i]); + cdev_del(&(akxpcm.cdevice[i])); +} + +/* return: successful(=0) or failed(<0) */ +static int __devinit akpcm_probe(struct platform_device *devptr) +{ + struct akpcm *pcm; + /* + struct resource *Analog_Regs; + struct resource *I2S_Regs; + struct resource *ADC2_Regs; + */ + struct ak39_codec_platform_data *platfm_data; + struct device *dev; + dev_t devno; + int i; + int err; + + ak_pcm_func("enter"); + ppb_statu = &akxpcm.outflag; + ak39_codec_probe(devptr); + + dev = &devptr->dev; + platfm_data = (struct ak39_codec_platform_data *)devptr->dev.platform_data; + + /* + Analog_Regs = platform_get_resource_byname(devptr, IORESOURCE_MEM, "ak37pcm_AnalogCtrlRegs"); + if (!Analog_Regs) { + printk("no memory resource for Analog_Regs\n"); + return -ENXIO; + } + I2S_Regs = platform_get_resource_byname(devptr, IORESOURCE_MEM, "ak37pcm_I2SCtrlRegs"); + if (!I2S_Regs) { + printk("no memory resource for I2S_Regs\n"); + return -ENXIO; + } + ADC2_Regs = platform_get_resource_byname(devptr, IORESOURCE_MEM, "ak37pcm_ADC2ModeCfgRegs"); + if (!ADC2_Regs) { + printk("no memory resource for ADC2_Regs\n"); + return -ENXIO; + } + */ + + /* M by panqihe 2014-06-13 */ + if(pcm_major){ + devno = MKDEV(pcm_major, 0); + err = register_chrdev_region(devno, 2, "pcmchar"); + if (err < 0) return err; + }else{ + err = alloc_chrdev_region(&devno, 0, 2, "pcmchar"); + if (err < 0) return err; + pcm_major = MAJOR(devno); + } + for (i=0; i<2; i++){ + akpcm_cdev_setup(i); + } + pcm = &akxpcm; + ak_pcm_debug_ex("akpcm initialize OK!"); + + /* + pcm->Analog_Regs = ioremap(Analog_Regs->start, Analog_Regs->end - Analog_Regs->start + 1); + if (!pcm->Analog_Regs) { + printk("could not remap Analog_Regs memory"); + goto __out_free_cdev; + } + + pcm->I2S_Regs = ioremap(I2S_Regs->start, I2S_Regs->end - I2S_Regs->start + 1); + if (!pcm->I2S_Regs) { + printk("could not remap I2S_Regs memory"); + goto __out_unmap_AnalogCtrlRegs; + } + + pcm->ADC2_Regs = ioremap(ADC2_Regs->start, ADC2_Regs->end - ADC2_Regs->start + 1); + if (!pcm->ADC2_Regs) { + printk("could not remap ADC2_Regs memory"); + goto __out_unmap_I2SCtrlRegs; + } + + reg_audio.base0800 = pcm->Analog_Regs; + reg_audio.base2002E = pcm->I2S_Regs; + reg_audio.base20072 = pcm->ADC2_Regs; + */ + + /* init L2 buffer */ + pcm->L2BufID_DAC = BUF_NULL; + pcm->L2BufID_ADC2 = BUF_NULL; + + /* init default value for mixers */ + pcm->mixer_volume[MIXER_VOL_HP] = DEFAULT_HPVOL; + akpcm_set_hp_gain(DEFAULT_HPVOL); + pcm->mixer_volume[MIXER_VOL_LI] = DEFAULT_LINEINVOL; + ak39_set_linein_gain(DEFAULT_LINEINVOL); + pcm->mixer_volume[MIXER_VOL_MIC] = DEFAULT_MICVOL; + akpcm_set_mic_gain(DEFAULT_MICVOL); + +#if defined CONFIG_SPKHP_SWITCH_AUTO || defined CONFIG_SPKHP_SWITCH_MIXER +#ifdef CONFIG_SPKHP_SWITCH_AUTO + INIT_DELAYED_WORK(&pcm->DelayWork, hpDet_wq_work); +#endif + + /* config hp det gpio */ + pcm->HPDet_gpio = platfm_data->hpdet_gpio; + ak_gpio_set(&(pcm->HPDet_gpio)); + + /* set hp det pin irq */ + pcm->HPDet_irq = platfm_data->hpdet_irq; + pcm->HPDet_on_value = platfm_data->hp_on_value; + pcm->bfExistPlayDev = platfm_data->boutput_only; + if (AK_GPIO_OUT_LOW == pcm->HPDet_on_value) { + pcm->HPon_irqType = IRQ_TYPE_LEVEL_LOW; + pcm->HPoff_irqType = IRQ_TYPE_LEVEL_HIGH; + } else if (AK_GPIO_OUT_HIGH == pcm->HPDet_on_value) { + pcm->HPon_irqType = IRQ_TYPE_LEVEL_HIGH; + pcm->HPoff_irqType = IRQ_TYPE_LEVEL_LOW; + } else if (-1 == pcm->HPDet_on_value){ + goto NextSet; + } + + if (ak_gpio_getpin(pcm->HPDet_gpio.pin) == pcm->HPDet_on_value) { + /* hp is plugged in */ + if(!(EXIST_OUT_DEV_HP & pcm->bfExistPlayDev)) { + /* Use SP, if no HP device exist */ + pcm->play_dectect |= SWITCH_DET_SPEAKER; + } else { + pcm->play_dectect &= ~SWITCH_DET_SPEAKER; + pcm->play_dectect |= SWITCH_DET_HEADPHONE; + } + irq_set_irq_type(pcm->HPDet_irq, pcm->HPoff_irqType); + ak_pcm_debug_ex("akpcm probe: hp on"); + } else { + /* hp is pulled out */ + if(!(EXIST_OUT_DEV_SP & pcm->bfExistPlayDev)) { + /* Use HP, if no SP device exist */ + pcm->play_dectect |= SWITCH_DET_HEADPHONE; + } else { + pcm->play_dectect &= ~SWITCH_DET_HEADPHONE; + pcm->play_dectect |= SWITCH_DET_SPEAKER; + } + irq_set_irq_type(pcm->HPDet_irq, pcm->HPon_irqType); + ak_pcm_debug_ex("akpcm probe: hp off"); + } + + err = request_irq(pcm->HPDet_irq, HPDet_interrupt, IRQF_DISABLED, devptr->name, pcm); + if (err) { + ak_pcm_info_ex("request irq error!"); + goto __out_unmap_ADC2ModeCfgRegs; + } +#else + /* No HPDet pin */ + memset(&(pcm->HPDet_gpio), 0, sizeof(struct gpio_info)); +#endif + +#if defined CONFIG_SPKHP_SWITCH_AUTO || defined CONFIG_SPKHP_SWITCH_MIXER +NextSet: +#endif + /* config speaker shutdown gpio */ + pcm->SPdown_gpio = platfm_data->spk_down_gpio; + ak_gpio_set(&(pcm->SPdown_gpio)); + /* HPMute */ + pcm->bIsHPmuteUsed = platfm_data->bIsHPmuteUsed; + pcm->hpmute_EnValue = platfm_data->hp_mute_enable_value; + if (AK_GPIO_OUT_HIGH == pcm->hpmute_EnValue) { + pcm->hpmute_DisValue = AK_GPIO_OUT_LOW; + } else if (AK_GPIO_OUT_LOW == pcm->hpmute_EnValue) { + pcm->hpmute_DisValue = AK_GPIO_OUT_HIGH; + } + if(pcm->bIsHPmuteUsed){ + /* config hp mute gpio */ + pcm->hpmute_gpio = platfm_data->hpmute_gpio; + ak_gpio_set(&(pcm->hpmute_gpio)); + } + + /* config linein detecting gpio */ + pcm->linindet_gpio = platfm_data->linindet_gpio; + if (platfm_data->detect_flag == AKPCM_LINEIN_ALWAY) { + pcm->capture_dectect |= CAPTURE_DET_LINEIN; + set_channel_source(pcm, MIXER_SRC_ADC2, SOURCE_ADC_LINEIN); + pcm->cptrdev = CPTRDEV_LI; + pr_info("akpcm probe: linein alway on\n"); + } else if ((platfm_data->detect_flag != AKPCM_MIC_ALWAY) && (pcm->linindet_gpio.pin >= 0)) { + ak_gpio_set(&(pcm->linindet_gpio)); + pcm->linindet_irq = platfm_data->linindet_irq; + if (AK_GPIO_OUT_LOW == pcm->linindet_gpio.value) { + pcm->lininon_irqType = IRQ_TYPE_LEVEL_LOW; + pcm->lininoff_irqType = IRQ_TYPE_LEVEL_HIGH; + } else { + pcm->lininon_irqType = IRQ_TYPE_LEVEL_HIGH; + pcm->lininoff_irqType = IRQ_TYPE_LEVEL_LOW; + } + + if (ak_gpio_getpin(pcm->linindet_gpio.pin) == + pcm->linindet_gpio.value) { + /* linein is plugged in */ + pcm->capture_dectect |= CAPTURE_DET_LINEIN; + irq_set_irq_type(pcm->linindet_irq, pcm->lininoff_irqType); + set_channel_source(pcm, MIXER_SRC_ADC2, SOURCE_ADC_LINEIN); + pcm->cptrdev = CPTRDEV_LI; + pr_info("akpcm probe: linein on\n"); + } else { + pcm->capture_dectect &= ~CAPTURE_DET_LINEIN; + irq_set_irq_type(pcm->linindet_irq, pcm->lininon_irqType); + set_channel_source(pcm, MIXER_SRC_ADC2, SOURCE_ADC_MIC); + pcm->cptrdev = CPTRDEV_MIC; + pr_info("akpcm probe: linein off\n"); + } + err = request_irq(pcm->linindet_irq, linindet_interrupt, + IRQF_DISABLED, "linin-det", pcm); + if (err) { + pr_err("request linein irq error!\n"); + goto __out_failed_linin_config; + } + + INIT_DELAYED_WORK(&pcm->LininWork, LininDet_wq_work); + } else { + pcm->capture_dectect &= CAPTURE_DET_LINEIN; + set_channel_source(pcm, MIXER_SRC_ADC2, SOURCE_ADC_MIC); + pcm->cptrdev = CPTRDEV_MIC; + pr_info("akpcm probe: mic on\n"); + } + + /* init timer to stop output channel */ + init_timer(&(pcm->timer_stop_output)); + pcm->timer_stop_output.function = playback_stop_hw; + pcm->timer_stop_output.data = (unsigned long)pcm; + /* wait queue */ + init_waitqueue_head(&(pcm->play_wq)); + init_waitqueue_head(&(pcm->capt_wq)); + /* running flag */ + clear_bit(IO_BIT_STREAM, &(pcm->outflag)); + clear_bit(IO_BIT_DMA, &(pcm->outflag)); + clear_bit(IO_BIT_STREAM, &(pcm->inflag)); + clear_bit(IO_BIT_DMA, &(pcm->inflag)); + /* dev sm flag */ + clear_bit(STATUS_BIT_OPENED, &(pcm->playback_sm_flag)); + clear_bit(STATUS_BIT_PREPARED, &(pcm->playback_sm_flag)); + clear_bit(STATUS_BIT_OPENED, &(pcm->capture_sm_flag)); + clear_bit(STATUS_BIT_PREPARED, &(pcm->capture_sm_flag)); + pcm->play_rt = NULL; + pcm->cptr_rt = NULL; + pcm->dac_opened_count = 0; + mutex_init(&pcm->io_lock); + /* aec */ + aec_init(pcm); + + pr_info("akpcm_probe: OK\n\n"); + return 0; + +__out_failed_linin_config: +#if defined CONFIG_SPKHP_SWITCH_AUTO || defined CONFIG_SPKHP_SWITCH_MIXER + free_irq(pcm->HPDet_irq, pcm); +__out_unmap_ADC2ModeCfgRegs: +#endif + /*iounmap(pcm->ADC2_Regs);*/ + /*__out_unmap_I2SCtrlRegs:*/ + /*iounmap(pcm->I2S_Regs);*/ + /*__out_unmap_AnalogCtrlRegs:*/ + /*iounmap(pcm->Analog_Regs);*/ + /*__out_free_cdev:*/ + for (i=0; i<2; i++){ + akpcm_cdev_release(i); + } + unregister_chrdev_region(devno, 2); + + ak_pcm_debug_ex("failed"); + return err; +} + +static inline void akpcm_close(struct akpcm *pcm) +{ + /* stop dma opration */ + playback_pause(pcm); + capture_pause(pcm); + + aec_exit(pcm); + + /* close analog module */ + ak39_codec_dac_close(); + if (pcm->bIsHPmuteUsed) { + ak_gpio_setpin(pcm->hpmute_gpio.pin, pcm->hpmute_EnValue); + } + ak39_set_hp_power(0, pcm->bIsHPmuteUsed == 0); + if (pcm->bIsHPmuteUsed) { + ak_gpio_setpin(pcm->hpmute_gpio.pin, pcm->hpmute_DisValue); + } + + ak39_set_hp_in(0); + ak39_set_sp_power(pcm->SPdown_gpio.pin, 0); + ak39_set_adc2_in(0); + //ak39_set_vcm_ref_power(0); +} + +/* return: successful(=0) or failed(<0) */ +static int __devexit akpcm_remove(struct platform_device *devptr) +{ + int i = 0; + struct akpcm *pcm = &akxpcm; + + ak_pcm_func("enter"); + akpcm_close(pcm); +#ifdef CONFIG_SPKHP_SWITCH_AUTO + cancel_delayed_work_sync(&pcm->DelayWork); +#endif + del_timer(&(pcm->timer_stop_output)); + free_irq(pcm->HPDet_irq, pcm); + + if (pcm->linindet_gpio.pin >= 0) { + free_irq(pcm->linindet_irq, pcm); + cancel_delayed_work_sync(&pcm->LininWork); + } + + /*iounmap(pcm->Analog_Regs); + iounmap(pcm->I2S_Regs); + iounmap(pcm->ADC2_Regs);*/ + + /* by panqihe */ + for (i=0; i<2; i++) { + dev_t devno = MKDEV(pcm_major, i); + //device_destroy(pcm_class[i], devno); + //class_destroy(pcm_class[i]); + cdev_del(&(pcm->cdevice[i])); + unregister_chrdev_region(devno, 1); + } + + ak39_codec_remove(devptr); + + return 0; +} + +#ifdef CONFIG_PM +static void ADC_CfgVoiceW(unsigned short freq, unsigned short ref, + unsigned short time) +{ + REG32(AK_VA_SYSCTRL + 0x120) &= ~(0x3 << 30); + REG32(AK_VA_SYSCTRL + 0x120) &= ~(0x7 << 27); + REG32(AK_VA_SYSCTRL + 0x120) &= ~(0x3 << 25); + REG32(AK_VA_SYSCTRL + 0x120) |= ((freq & 0x3) << 30); + REG32(AK_VA_SYSCTRL + 0x120) |= ((ref & 0x7) << 27); + REG32(AK_VA_SYSCTRL + 0x120) |= ((time & 0x3) << 25); +} + +static void pcm_voice_wakeup(unsigned short enable) +{ + if (!enable) { + REG32(AK_VA_SYSCTRL + 0x120) |= (1 << 15); //power down voice wake up + REG32(AK_VA_SYSCTRL + 0xd8) &= ~(1 << 5); //disable voice wakeup interrupt + return; + } + + REG32(AK_VA_SYSCTRL + 0x120) |= (1 << 24); //poweroff mic_n + REG32(AK_VA_SYSCTRL + 0x120) |= (1 << 23); //poweroff mic_p + REG32(AK_VA_SYSCTRL + 0x120) &= ~(0x7 << 2); // set no input to adc2 + REG32(AK_VA_SYSCTRL + 0x11c) &= ~(0x7 << 12); // set hp mute + + //power off voice wakeup before + REG32(AK_VA_SYSCTRL + 0x120) |= (1 << 15); //power down voice wake up + REG32(AK_VA_SYSCTRL + 0x11c) |= (0x4 << 12); // set hp from mic + REG32(AK_VA_SYSCTRL + 0x11c) |= (0x1 << 17); // HP with 3k to gnd + + mdelay(5); + + REG32(AK_VA_SYSCTRL + 0x11c) &= ~(0x1 << 2); // no pl_vcm2 with 2k to gnd + REG32(AK_VA_SYSCTRL + 0x120) &= ~(0x1<<0); // no Pl_vcm3 with 2k to gnd + REG32(AK_VA_SYSCTRL + 0x11c) &= ~(0x1F << 4); //disable descharge for VCM2 + REG32(AK_VA_SYSCTRL + 0x11c) &= ~(1 << 0); + REG32(AK_VA_SYSCTRL + 0x11c) &= ~(1 << 1); // power on vcm2 + REG32(AK_VA_SYSCTRL + 0x11c) &= ~(1 << 23); // vcm3 from refrec1.5 + REG32(AK_VA_SYSCTRL + 0x11c) &= ~(1 << 3); // power on vcm3 + REG32(AK_VA_SYSCTRL + 0xd8) &= ~(1 << 5); //disable voice wakeup interrupt + REG32(AK_VA_SYSCTRL + 0xd8) |= (1 << 3); //cle ar voice wakeup interrupt status + REG32(AK_VA_SYSCTRL + 0xd8) &= ~(1 << 3); + + mdelay(500); + + REG32(AK_VA_SYSCTRL + 0x11c) |= (1 << 1); // power off vcm2 + REG32(AK_VA_SYSCTRL + 0x11c) |= (1 << 3); // power off vcm3 + REG32(AK_VA_SYSCTRL + 0x11c) |= (1 << 0); + REG32(AK_VA_SYSCTRL + 0x11c) &= ~(0x7 << 12); // set hp mute + REG32(AK_VA_SYSCTRL + 0x11c) &= ~(0x1 << 17); //no HP with 3k to gnd + REG32(AK_VA_SYSCTRL + 0xd8) &= ~(1 << 1); //rising + + ADC_CfgVoiceW(0x0, 0x0, 0x0); + + REG32(AK_VA_SYSCTRL + 0xd8) |= (1 << 5); //enable voice wakeup interrupt + REG32(AK_VA_SYSCTRL + 0x120) &= ~(1 << 26); //power on pd_vw, vcm3 from avcc + + mdelay(10); +} + +static int akpcm_suspend(struct platform_device *pdev, pm_message_t msg) +{ + struct akpcm *pcm = &akxpcm; + + //ak_pcm_debug_ex("enter"); + + akpcm_close(pcm); + + /* set voice wake up function */ + pcm_voice_wakeup(true); + + return 0; +} + +static int akpcm_resume(struct platform_device *pdev) +{ + //ak_pcm_debug_ex("enter"); + + /* disable voice wakeup */ + pcm_voice_wakeup(false); + + return 0; +} +#else +#define akpcm_suspend NULL +#define akpcm_resume NULL +#endif + +static struct platform_driver akpcm_driver = { + .probe = akpcm_probe, + .remove = __devexit_p(akpcm_remove), + .suspend = akpcm_suspend, + .resume = akpcm_resume, + .driver = { + .name = "ak39-codec" + }, +}; + +static int __init akpcm_init(void) +{ + int err = 0; + + ak_pcm_func("enter"); + err = platform_driver_register(&akpcm_driver); + if (err < 0){ + return err; + }else{ + return 0; + } +} + +static void __exit akpcm_exit(void) +{ + platform_driver_unregister(&akpcm_driver); +} + +module_init(akpcm_init) +module_exit(akpcm_exit) diff --git a/drivers/misc/akpcm/akpcm.h b/drivers/misc/akpcm/akpcm.h new file mode 100644 index 00000000..94633266 --- /dev/null +++ b/drivers/misc/akpcm/akpcm.h @@ -0,0 +1,168 @@ +#ifndef __AKPCM_H__ +#define __AKPCM_H__ + +/********************** IOCTL *********************************************/ +#define PCM_IOC_MAGIC 'P' +#define AKPCM_IO(nr) _IOC(_IOC_NONE, PCM_IOC_MAGIC, nr, 0) +#define AKPCM_IOR(nr) _IOR(PCM_IOC_MAGIC, nr, int) +#define AKPCM_IORn(nr, size) _IOR(PCM_IOC_MAGIC, nr, size) +#define AKPCM_IOW(nr) _IOW(PCM_IOC_MAGIC, nr, int) +#define AKPCM_IOWn(nr, size) _IOW(PCM_IOC_MAGIC, nr, size) +/* ------------- command ------------------------------------------------ */ +#define IOC_NR_PREPARE (0xE0) +#define IOC_NR_RESUME (0xE1) +#define IOC_NR_PAUSE (0xE2) +#define IOC_NR_RSTBUF (0xE3) +#define IOC_NR_RSTALL (0xE4) +#define IOC_NR_GETELAPSE (0xE5) +#define IOC_NR_GETTIMER (0xE6) +#define IOC_NR_GETSTATUS (0xE7) +#define IOC_NR_GETDATALEN (0xE8) +#define IOC_NR_NOTICE_END (0xE9) +#define IOC_PREPARE AKPCM_IO(IOC_NR_PREPARE) +#define IOC_RESUME AKPCM_IO(IOC_NR_RESUME) +#define IOC_PAUSE AKPCM_IO(IOC_NR_PAUSE) +#define IOC_RSTBUF AKPCM_IO(IOC_NR_RSTBUF) +#define IOC_RSTALL AKPCM_IO(IOC_NR_RSTALL) +#define IOC_GETELAPSE AKPCM_IORn(IOC_NR_GETELAPSE, unsigned long long) +#define IOC_GETTIMER AKPCM_IORn(IOC_NR_GETTIMER, unsigned long) +#define IOC_GET_STATUS AKPCM_IORn(IOC_NR_GETSTATUS, unsigned long) +#define IOC_GET_DATA_LENGTH AKPCM_IORn(IOC_NR_GETDATALEN, unsigned long) +#define IOC_NOTICE_END AKPCM_IO(IOC_NR_NOTICE_END) + +/* ------------- HW parameters ------------------------------------------ */ +/* ------------- HW configures ------------------------------------------ */ +/* ------------- SW configures ------------------------------------------ */ +#define IOC_NR_FEATS (0xF0) /* refer to akpcm_features */ +#define IOC_NR_PARS (0xF2) /* refer to akpcm_pars */ +#define IOC_GET_FEATS AKPCM_IORn(IOC_NR_FEATS, struct akpcm_features) +#define IOC_GET_PARS AKPCM_IORn(IOC_NR_PARS, struct akpcm_pars) +#define IOC_SET_PARS AKPCM_IOWn(IOC_NR_PARS, struct akpcm_pars) +/* ------------- SOURCEs -------------------------------------------------- */ +#define IOC_NR_SOURCES (0x10) +#define IOC_GET_SOURCES AKPCM_IOR(IOC_NR_SOURCES) +/* If set to SIGNAL_SRC_MUTE, devices will be power down */ +#define IOC_SET_SOURCES AKPCM_IOW(IOC_NR_SOURCES) +/* ------------- GAINs ------------------------------------------------- */ +#define IOC_NR_GAIN (0x30) /* HP */ +#define IOC_GET_GAIN AKPCM_IOR(IOC_NR_GAIN) +#define IOC_SET_GAIN AKPCM_IOW(IOC_NR_GAIN) +/* ------------- DEVICEs ---------------------------------------- */ +#define IOC_NR_DEV (0x70) +#define IOC_GET_DEV AKPCM_IOR(IOC_NR_DEV) +#define IOC_SET_DEV AKPCM_IOW(IOC_NR_DEV) +/* ------------- NR & AGC ---------------------------------------- */ +#define IOC_NR_AGC (0x80) +#define IOC_SET_NR_AGC AKPCM_IOW(IOC_NR_AGC) +#define IOC_NR_MAX (0x82) +#define IOC_SET_NR_MAX AKPCM_IOW(IOC_NR_MAX) +#define IOC_NR_AEC (0x83) +#define IOC_SET_AEC AKPCM_IOWn(IOC_NR_AEC, unsigned int) +#define IOC_AEC_DUMP (0x84) +#define IOC_SET_AEC_DUMP AKPCM_IOW(IOC_AEC_DUMP) +#define IOC_AEC_READ_PARAM (0x85) +#define IOC_GET_ECHO_PARAM AKPCM_IOR(IOC_AEC_READ_PARAM) +#define IOC_AEC_WRITE_PARAM (0x86) +#define IOC_SET_ECHO_PARAM AKPCM_IOW(IOC_AEC_WRITE_PARAM) +#define IOC_AD_EQ (0x87) +#define IOC_SET_AD_EQ AKPCM_IOW(IOC_AD_EQ) +#define IOC_AD_EQ_ATTR (0x88) +#define IOC_SET_AD_EQ_ATTR AKPCM_IOW(IOC_AD_EQ_ATTR) + +/* ------------- SPEAKER ---------------------------------------- */ +#define IOC_SPEAKER (0x81) +#define IOC_SET_SPEAKER AKPCM_IOW(IOC_SPEAKER) + +/* play_dev */ +#define AKPCM_PLAYDEV_HP (1UL<<0) +#define AKPCM_PLAYDEV_LO (1UL<<1) +#define AKPCM_PLAYDEV_AUTO (0UL<<0) + +/* cptr_dev */ +#define AKPCM_CPTRDEV_MIC (1UL<<0) +#define AKPCM_CPTRDEV_LI (1UL<<1) +#define AKPCM_CPTRDEV_AUTO (0UL<<0) + +/* sample_bits */ +#define AKPCM_SMPL_BIT_U8 (1UL<<0) +#define AKPCM_SMPL_BIT_U16 (1UL<<2) + +/* rates */ +#define AKPCM_RATE_8000 (1<<1) /* 8000Hz */ +#define AKPCM_RATE_11025 (1<<2) /* 11025Hz */ +#define AKPCM_RATE_16000 (1<<3) /* 16000Hz */ +#define AKPCM_RATE_22050 (1<<4) /* 22050Hz */ +#define AKPCM_RATE_32000 (1<<5) /* 32000Hz */ +#define AKPCM_RATE_44100 (1<<6) /* 44100Hz */ +#define AKPCM_RATE_48000 (1<<7) /* 48000Hz */ +#define AKPCM_RATE_CONTINUOUS (1<<30) /* continuous range */ +#define AKPCM_RATE_KNOT (1<<31) /* more non-continuos rates */ +#define AKPCM_RATE_ALL (AKPCM_RATE_8000 | AKPCM_RATE_11025 | \ + AKPCM_RATE_16000 | AKPCM_RATE_22050 | AKPCM_RATE_32000 | \ + AKPCM_RATE_44100 | AKPCM_RATE_48000) + +/* HP_IN ADC23_IN */ +#define SOURCE_DAC (0b100) +#define SOURCE_LINEIN (0b010) +#define SOURCE_MIC (0b001) +#define SIGNAL_SRC_MUTE 0 +#define SIGNAL_SRC_MAX (SOURCE_DAC|SOURCE_LINEIN|SOURCE_MIC) + +#define SOURCE_DAC_MASK (0b100) +#define SOURCE_LINEIN_MASK (0b010) +#define SOURCE_MIC_MASK (0b001) +#define SOURCE_MIXED_ALL_MASK (SOURCE_DAC_MASK|SOURCE_LINEIN_MASK|SOURCE_MIC_MASK) + +/* H240 */ +#define SOURCE_ADC_DAC (0b001) +#define SOURCE_ADC_LINEIN (0b100) +#define SOURCE_ADC_MIC (0b010) +#define SIGNAL_ADC_SRC_MUTE 0 +#define SIGNAL_ADC_SRC_MAX (SOURCE_ADC_DAC|SOURCE_ADC_LINEIN|SOURCE_ADC_MIC) + +#define HEADPHONE_GAIN_MIN 0 +#define HEADPHONE_GAIN_MAX 5 +#define LINEIN_GAIN_MIN 0 +#define LINEIN_GAIN_MAX 15 +#define MIC_GAIN_MIN 0 +#define MIC_GAIN_MAX 7 + +/* bit[0]-AEC; bit[1]-NR; bit[2]-AGC */ +enum echo_bit { + ECHO_BIT_AEC = 0x00, + ECHO_BIT_NR, + ECHO_BIT_AGC +}; + +struct akpcm_features { + unsigned int rates; /* AKPCM_RATE_* */ + unsigned int actual_rate; /* actual rate in Hz */ + unsigned int rate_min; /* min rate */ + unsigned int rate_max; /* max rate */ + unsigned int sample_bits; /* AKPCM_FMTBIT_XX */ + unsigned int channels_min; /* min channels */ + unsigned int channels_max; /* max channels */ + unsigned int period_bytes_min; /* min period size */ + unsigned int period_bytes_max; /* max period size */ + unsigned int periods_min; /* min # of periods */ + unsigned int periods_max; /* max # of periods */ + unsigned int hp_gain_max; /* max value for headphone gain */ + unsigned int li_gain_max; /* max value for linein gain */ + unsigned int mic_gain_max; /* max value for mic gain */ + unsigned int play_dev; /* HP or(and) LO */ + unsigned int cptr_dev; /* MIC or(and) LI */ +}; + +struct akpcm_pars { + /* -- HW parameters -- */ + unsigned int rate; /* rate in Hz */ + unsigned int channels; + unsigned int sample_bits; + unsigned int period_bytes; /* DMA size */ + unsigned int periods; /* buffer size = (periods * period_bytes) */ + /* -- SW parameters -- */ + unsigned int threshold; + unsigned int reserved; //stop_threshold; +}; + +#endif diff --git a/drivers/misc/akpcm/akpcmL0.c b/drivers/misc/akpcm/akpcmL0.c new file mode 100644 index 00000000..cec4b5ac --- /dev/null +++ b/drivers/misc/akpcm/akpcmL0.c @@ -0,0 +1,2417 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include +#include + +#define DOUBLE_MIC_GAIN 1 +//#define SND_CODEC_DBG + +#ifdef SND_CODEC_DBG +#ifdef __KERNEL__ +#define PDEBUG(fmt, args...) printk(KERN_INFO "ak39 codec:" fmt, ##args) +#else +#define PDEBUG(fmt, args...) fprintf(stderr, "%s %d:" fmt,__FILE__, __LINE__, ## args) +#endif +#else +#define PDEBUG(fmt, args...) +#endif + + +enum mixer_vol_port { + MIXER_PORT_HP_VOL = 0, + MIXER_PORT_LINEIN_VOL, + MIXER_PORT_MIC_VOL, + MIXER_PORT_VOL_COUNT, +}; + + +enum mixer_dst_port { + MIXER_ADDR_DST_HP = 0, + MIXER_ADDR_DST_ADC2, + MIXER_DST_COUNT, +}; + +enum mixer_detect_port { + MIXER_ADDR_HPDET = 0, + MIXER_ADDR_MICDET, + MIXER_ADDR_LINEINDET, + MIXER_SWITCH_COUNT, +}; + + +enum mixer_outmode { + MIXER_ADDR_OUTMODE_DAC = 0, + MIXER_OUTMODE_COUNT, +}; + +enum mixer_chnl_duration { + MIXER_ADDR_PLAY_DURATION = 0, + MIXER_DURATION_COUNT, +}; + +#define MAX_DACCLK 24000000 + +#define HEADPHONE_DEFAULT_VOL (HEADPHONE_GAIN_MAX - 2) +#define LINEIN_DEFAULT_VOL 10 +#define MIC_DEFAULT_VOL 6 + +#define PLAYMODE_STATUS_HP (1) +#define PLAYMODE_STATUS_SPEAKER (2) + +#define OUTMODE_AUTO (0) +#define OUTMODE_HP (1) +#define OUTMODE_LINEOUT (2) +#define OUTMODE_MIN OUTMODE_AUTO +#define OUTMODE_MAX OUTMODE_HP + +#define CHNLDURATION_CONSTANT 0 +#define CHNLDURATION_EVEROPEN 1 +#define CHNLDURATION_MIN CHNLDURATION_CONSTANT +#define CHNLDURATION_MAX CHNLDURATION_EVEROPEN + +struct ak39_codec { + struct ak_codec_dai dai; + void __iomem *analog_ctrl_base; + void __iomem *adda_cfg_base; + struct delayed_work d_work; + + int mixer_volume[MIXER_PORT_VOL_COUNT]; + int mixer_route[MIXER_DST_COUNT]; + int mixer_switch[MIXER_SWITCH_COUNT]; + int mixer_outmode[MIXER_OUTMODE_COUNT]; + int mixer_ch_duration[MIXER_DURATION_COUNT]; + + struct gpio_info hpdet_gpio; + struct gpio_info spkrshdn_gpio; + struct gpio_info hpmute_gpio; + + int hp_det_irq; + int irq_hp_on_type; + int hp_on_value; + int playmode; + int hpmute_en_val; + + unsigned used_hp_mute:1; // whether to use hardware de-pipa or not + unsigned outputing:1; + unsigned dac_state:1; + unsigned adc2_state:1; + unsigned share_is_opened:1; + + struct mutex share_lock; +}; + +struct ak39_volume_info +{ + int vol_default; + int vol_min; + int vol_max; +}; + + +#define VOL_INFO_DEFAULT 0 +#define VOL_INFO_MIN 1 +#define VOL_INFO_MAX 2 + +static struct ak39_volume_info vol_info[MIXER_PORT_VOL_COUNT] = { + [MIXER_PORT_HP_VOL] = { + .vol_default = HEADPHONE_DEFAULT_VOL, + .vol_min = HEADPHONE_GAIN_MIN, + .vol_max = HEADPHONE_GAIN_MAX, + }, + [MIXER_PORT_LINEIN_VOL] = { + .vol_default = LINEIN_DEFAULT_VOL, + .vol_min = LINEIN_GAIN_MIN, + .vol_max = LINEIN_GAIN_MAX, + }, + [MIXER_PORT_MIC_VOL] = { + .vol_default = MIC_DEFAULT_VOL, + .vol_min = MIC_GAIN_MIN, + .vol_max = MIC_GAIN_MAX, + } + +}; + +static int default_route[MIXER_DST_COUNT] = { + [MIXER_ADDR_DST_HP] = SOURCE_DAC, + [MIXER_ADDR_DST_ADC2] = SOURCE_ADC_MIC, +}; + +static struct ak39_codec *g_pcodec; +static struct ak_codec_dai *g_pdai; + +extern unsigned long *ppb_statu; + +#if 0 +static void dump_codec_reg(struct ak39_codec *codec) +{ + u32 reg_val; + + reg_val = REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG); + printk("\nCLOCK_CTRL_REG(0x0800%04x):%08x.\n", CLOCK_CTRL_REG, reg_val); + + reg_val = REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG); + printk("HIGHSPEED_CLOCK_CTRL_REG(0x0800%04x):%08x.\n", HIGHSPEED_CLOCK_CTRL_REG, reg_val); + + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + printk("\nANALOG_CTRL_REG1(0x0800%04x):%08x.\n", ANALOG_CTRL_REG1, reg_val); + + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); + printk("ANALOG_CTRL_REG2(0x0800%04x):%08x.\n", ANALOG_CTRL_REG2, reg_val); + + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG3); + printk("ANALOG_CTRL_REG3(0x0800%04x):%08x.\n", ANALOG_CTRL_REG3, reg_val); + + reg_val = REG32(codec->adda_cfg_base + ADC2_CONFIG_REG); + printk("ADC2_CONFIG_REG(0x2011%04x):%08x.\n", ADC2_CONFIG_REG, reg_val); + + reg_val = REG32(codec->adda_cfg_base + ADC2_DATA_REG); + printk("ADC2_DATA_REG(0x2011%04x):%08x.\n", ADC2_DATA_REG, reg_val); + + reg_val = REG32(codec->adda_cfg_base + DAC_CONFIG_REG); + printk("DAC_CONFIG_REG(0x2011%04x):%08x.\n", DAC_CONFIG_REG, reg_val); + + reg_val = REG32(codec->analog_ctrl_base + ADC1_CONF_REG1); + printk("ADC1_CONF_REG1:(0x0800%04x):%08x.\n", ADC1_CONF_REG1, reg_val); +} +#endif +static inline struct ak39_codec *to_ak39_codec(struct ak_codec_dai *dai) +{ + return dai ? container_of(dai, struct ak39_codec, dai): NULL; +} + + +static inline void pd_ref_enable(struct ak39_codec *codec) +{ + u32 reg_val; + + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_val &= ~PD_BIAS; //power on codec + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; +} + +static inline void pd_ref_disable(struct ak39_codec *codec) +{ + u32 reg_val; + + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_val |= PD_BIAS; //power off codec + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; +} + +#if 0 +static void SetHpDisChgCur(unsigned long value) +{ + int reg_val; + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_val &= ~(0x7 << 29); + reg_val |= (value << 29); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; +} + + +static void set_cur_pmos(struct ak39_codec *codec, unsigned long value) +{ + int reg_val; + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_val &= ~(0xf << 25); + reg_val |= (value << 25); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; +} +#endif + +#if 0 +static void set_cur_vcm2_dischg(struct ak39_codec *codec, unsigned long value) +{ + int reg_val; + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_val &= ~(0x1f << 6); + reg_val |= (value << 6); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; +} + +static void set_cur_vcm2(struct ak39_codec *codec, unsigned long value) +{ + //NOTE: no need for AK39E + +} +#endif + +/** + * @brief select HP in signal + * @author + * @date + * @param[in] (HP_In_Signal)signal: signal desired + * @return void + */ +void ak39_set_hp_in(unsigned long signal) +{ + unsigned long reg_val; + struct ak39_codec *codec = g_pcodec; + + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_val &= ~(0x7 << 14); + reg_val |= ((signal&0x7) << 14); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; +} + + +/** + * @brief set ADC2 source + * @author + * @date + * @param[in] signal: DAC|LINEIN|MIC + * @return void + */ +void ak39_set_adc2_in(unsigned long signal) +{ + unsigned long reg_val; + struct ak39_codec *codec = g_pcodec; + + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); + reg_val &= ~(0x7 << 29); + reg_val |= ((signal&0x7) << 29); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) = reg_val; +} + + +/** + * @brief set HP gain + * @author + * @date + * @param[in] x: (0.x)times + * @return void + */ +void ak39_set_hp_gain(unsigned long gain) +{ + unsigned long reg_value; + //unsigned long gain_table[6] = {0x0, 0x18, 0x14, 0x12, 0x11, 0x10}; + //for H240 + unsigned long gain_table[6] = {0x10, 0x08, 0x04, 0x02, 0x01, 0x00}; + + struct ak39_codec *codec = g_pcodec; + + reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_value &= ~MASK_HP_GAIN; + reg_value |= HP_GAIN(gain_table[gain]); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; +} + +/** + * @brief set mic gain + * @author + * @date + * @param[in] gain: 0~7 + * @return void + */ +void ak39_set_mic_gain(unsigned long gain) +{ + unsigned long reg_val = 0; + struct ak39_codec *codec= g_pcodec; + + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); + reg_val &= ~(0x7<<15); + +#if DOUBLE_MIC_GAIN + reg_val |= (1 << 18); //use double mic gain +#else + reg_val &= ~(1 << 18); //not use double Mic gain +#endif + reg_val |= ((gain & 0x7) << 15); + + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) = reg_val; +} + +/** + * @brief set linein gain + * @author + * @date + * @param[in] gain: 0~15 + * @return void + */ +void ak39_set_linein_gain(unsigned long gain) +{ + unsigned long reg_val = 0; + struct ak39_codec *codec = g_pcodec; + + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); + reg_val &= ~(0xF<<22); + reg_val |= ((gain&0xF)<<22); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) = reg_val; +} + +enum depipa_noise_ctrl { + DEPIPA_NOISE_NOT_USE, + DEPIPA_NOISE_USE, +}; + +#if 0 +static void set_bit_depipa_noise_ctrl(struct ak39_codec *codec, int enable) +{ + unsigned long reg_val = 0; + + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + + if(enable == DEPIPA_NOISE_USE) { + reg_val |= (PRE_EN); + }else { + reg_val &= ~(PRE_EN); + } + + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; +} +#endif + +void ak39_set_vcm_ref_power(bool bOn) +{ + unsigned long reg_val; + struct ak39_codec *codec = g_pcodec; + + if(bOn) + { + //add power control here for MIC-to-Lineout channel + //power on REF + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_val &= ~(PD_BIAS); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; + + //power on vcm3 + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_val &= ~(PD_VCM3); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; + } + else + { + //power off vcm3 + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_val |= (PD_VCM3); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; + + //power off codec + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_val |= (PD_BIAS); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; + } +} + +static void ak39_adc2_dac_share_open(void) +{ + unsigned int reg_value; + struct ak_codec_dai *dai = g_pdai; + struct ak39_codec *codec = to_ak39_codec(dai); + + mutex_lock(&g_pcodec->share_lock); + + if (codec->share_is_opened) { + //printk("share open had done\n"); + mutex_unlock(&g_pcodec->share_lock); + return; + } + //printk(KERN_ERR "share open will do\n"); + + codec->share_is_opened = 1; + + //if had opened then return + reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + if ((reg_value & ((1<<23) | (1<<24))) == 0) { + printk(KERN_ERR "Headphone driver already power on.\n"); + return; + } else { + printk("Headphone driver will power on.\n"); + } + + ////////////////////////////efuse charging vcm2///////////////////////////////////////// + //BGR->VCM2 + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) &= ~(PD_SARADCREF | SARADC_IBCTRL);//~(0x3<<2); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) &= ~MASK_ANTIPOP_EN;//~(0xf<<10); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG3) |= VREF_TEST_EN; + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~MASK_DIS_CHG_VCM2;//~(0x1f<<6); + ////////////////////////////efuse charging vcm2///////////////////////////////////////// + + //open VREF1.5V + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) &= ~MASK_ANTIPOP_EN;//~(0xf << 10); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) |= ANTIPOP_EN(1);//1 << 10; + + //power on bias generator + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~PD_BIAS;//(~(0x1<<0)); + + //Disable the pull-down 2Kohm resistor to VCM3 + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~PL_VCM3; + + //Power on VCM3 + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~PD_VCM3; + + //power on nmos part of head phone driver + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~PD1_HP;//~(0x1 << 23); + + //power on pmos part of head phone driver + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~PD2_HP;//~(0x1 << 24); + + //open vcm2 + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~MASK_DIS_CHG_VCM2;//~(0x1f << 6); + + //power on the integrator in DAC + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~PD_OP; + + //reset DAC output to middle volatge + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) |= RST_DAC;//(1<<13); + + //wait vcm2 charging fully + msleep(100); + + //disable BGR output VREF, close fast charge + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG3) &= ~VREF_TEST_EN; + + //to normal + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~RST_DAC;//~(1<<13); + + //power on the DAC clk + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~PD_CK; + + mutex_unlock(&g_pcodec->share_lock); +} + +static void ak39_adc2_dac_share_close(void) +{ + struct ak_codec_dai *dai = g_pdai; + struct ak39_codec *codec = to_ak39_codec(dai); + + mutex_lock(&g_pcodec->share_lock); + + if (codec->share_is_opened == 0) { + printk(KERN_ERR "share close had done\n"); + mutex_unlock(&g_pcodec->share_lock); + return; + } + printk(KERN_ERR "share close will do\n"); + + codec->share_is_opened = 0; + + //enable vcm2 discharge + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = (0x1f << 6); + + //PowerOn Vcm2/3 + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) |= PD_VCM3; + + //SetVcm3powdown + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) |= PL_VCM3; + + //power off bias generator + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) |= (0x1<<0); + + mutex_unlock(&g_pcodec->share_lock); +} + +/** + * @brief power on HP + * @author + * @date + * @param[in] bool poweron or poweroff + * @ [in] bool use delay to de-pipa or not + * @return void + */ +void ak39_set_hp_power(bool bOn, bool soft_de_pipa) +{ + printk("ak39_set_hp_power %d,soft_de_pipa=%d\n",bOn,soft_de_pipa); + if(bOn) + { + ak39_adc2_dac_share_open(); + } + else + { +#if 0 + set_bit_depipa_noise_ctrl(codec, DEPIPA_NOISE_USE); + + if (!codec->adc2_state) { + //off mute + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~(0x7 << 14); + // REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) |= (0x1f << 4); + + ak39_set_vcm_ref_power(0); + set_cur_vcm2_dischg(codec, 0x1f); + mdelay(400); + } + + //power off hp + reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_value |= ((PD2_HP)); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; + + //power off hp + reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_value |= (PD1_HP); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; +#else +#if 0 + printk("ygh hp power off\n"); + reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_value |= (0x1 << 6); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; + + msleep(5); + reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_value |= (0x1 << 7); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; + + msleep(5); + reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_value |= (0x1 << 8); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; + + msleep(10); + reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_value |= (0x1 << 9); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; + + msleep(10); + reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_value |= (0x1 << 10); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; + + msleep(10); + reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_value |= (0x1 << 0); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; + + msleep(10); + reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_value |= (0x1 << 24); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; + + msleep(20); + reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_value |= (0x1 << 23); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; + + msleep(20); + reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_value |= (0x1 << 2); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; + + msleep(10); + reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_value |= (0x1 << 1); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; + + + /* ָadcò */ + reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_value &= ~(0x1f << 6); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; + + reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_value &= ~(0x1 << 0); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; + + reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_value &= ~(0x1 << 2); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; + + reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_value &= ~(0x1 << 1); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; +#endif +#endif + } +} + + +/** + * @brief set linein interface power + * @author + * @date + * @param[in] bTrue: 1-power on; 0-power off + * @return void + */ +void ak39_set_linein_power(bool bOn) +{ + unsigned long reg_val = 0; + struct ak39_codec *codec = g_pcodec; + + if(bOn) + { + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); + reg_val &= ~(1 << 21); //power on the channel + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) = reg_val; + } + else + { + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); + reg_val |= (1 << 21); //power off the channel + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) = reg_val; + } +} + +/** + * @brief set speaker interface power + * @author + * @date + * @param[in] bTrue: 1-power on; 0-power off + * @return void + */ +void ak39_set_sp_power(unsigned int pin, bool bOn) +{ + +} + +/** + * @brief set DAC to lineout + * @author + * @date + * @param[in] bTrue: 1-connet DACto lineout; 0-disconnect + * @return void + */ +void ak39_set_mic_power(bool bOn) +{ + unsigned long reg_val = 0; + struct ak39_codec *codec = g_pcodec; + + //NOTE: + //mono: bit20 on/off + //diff: bit19 & bit19 on/off + if(bOn) { + //power on mic interface + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); +#if DOUBLE_MIC_GAIN + reg_val &= (~(0x1 << 19)); + reg_val |= (0x1 << 20); //power on diff mic +#else + reg_val &= (~((0x1 << 19) | (0x1 << 20))); //power on mono mic +#endif + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) = reg_val; + } else { + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); + reg_val |= (0x1 << 19) | (0x1 << 20); //power off mono mic + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) = reg_val; + } +} + +static void ak39_set_sel_vref(struct ak39_codec *codec, bool bOn) +{ + unsigned long reg_val; + reg_val = REG32(codec->analog_ctrl_base + ADC1_CONF_REG1); + if(bOn) + reg_val |= SEL_VREF; + else + reg_val &= ~SEL_VREF; + + REG32(codec->analog_ctrl_base + ADC1_CONF_REG1) = reg_val; +} + + +static void set_dac_highspeed(struct ak39_codec *codec, + unsigned long sample_rate) +{ + const unsigned long rate_list[] = + {8000, 16000, 24000, 48000, 96000}; + const unsigned long dac_hclk[sizeof(rate_list)/sizeof(rate_list[0])] = + {6, 12, 18, 36, 72}; + + unsigned long reg_value; + unsigned long pllclk; + unsigned long hclk_div; + unsigned long hclk; + unsigned long i; + + pllclk = ak_get_asic_pll_clk(); + pllclk /= 1000000; + + printk("seths sr %lu,pll %lu\n",sample_rate,pllclk); + for(i=0; i < sizeof(rate_list)/sizeof(rate_list[0]); ++i) + { + if(sample_rate <= rate_list[i]) + { + break; + } + } + + if(sizeof(rate_list)/sizeof(rate_list[0]) == i) + { + hclk = dac_hclk[i-1]; + } + else + { + hclk = dac_hclk[i]; + } + + hclk_div = pllclk / hclk; + //04V 3771 chip: when the divider of the DAC CLK is even number, + //the DAC MODULE is more steady + hclk_div &= ~1; + + if(0 != hclk_div) + { + hclk_div = hclk_div - 1; + } + + reg_value = REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG); + reg_value &= ~(MASK_DAC_HCLK_DIV); + reg_value |= DAC_HSDIV_VLD | DAC_HCLK_DIV(hclk_div); + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) = reg_value; + + i = 0; + while(REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) & (DAC_HSDIV_VLD)) + { + ++i; + if(i > 100000) + { + printk("set high speed clk reg fail\n"); + return ; + } + } +} + +static void set_adc_highspeed(struct ak39_codec *codec, + unsigned long sample_rate) +{ + unsigned long reg_value; + unsigned long pllclk; + unsigned long hclk_div; + unsigned long hclk; + unsigned long i; + + pllclk = ak_get_asic_pll_clk(); + for (i=1; i<=0x40; i++) + { + hclk = pllclk/i; + if ((hclk<=62000000)&&(hclk>=28000000)) + { + break; + } + } + hclk_div = i-1; + + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= ~(1<<29); + + i = 0; + while(REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) & (ADC2_HSDIV_VLD)) + { + ++i; + if(i > 100000) + { + printk("adc set high speed clk reg fail\n"); + return ; + } + } + + reg_value = REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG); + reg_value &= ~(MASK_ADC2_HCLK_DIV); + reg_value |= ADC2_HSDIV_VLD | ADC2_HCLK_DIV(hclk_div); + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) = reg_value; + + i = 0; + while(REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) & (ADC2_HSDIV_VLD)) + { + ++i; + if(i > 100000) + { + printk("adc set high speed clk reg fail\n"); + return ; + } + } + + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) |= (1<<29); +} + +/** + * @brief set ADC2 mode and clk_div + * @author + * @date + * @param[in] des_sr: desired rate to be set + * @param[out] mode_sel: reg(0x08000064) bit[12] + * @param[out] mclkdiv: reg(0x08000008) bit[11:4] + * @return void + */ +static unsigned long get_adc2_osr_div(struct ak39_codec *codec, unsigned char *mode_sel, + unsigned char *mclkdiv, unsigned long des_sr) +{ + unsigned short k, max_div; + unsigned short OSR_value=256; + unsigned long SR_save, out_sr=0; + signed long a, b; + unsigned long asic_pll; + unsigned long perfect_pll = 0; + + // sr = asic_pll/(*mclkdiv+1)/OSR_value; + + asic_pll = ak_get_asic_pll_clk(); + max_div = 0x100; + SR_save = 0; + *mode_sel = 0; + *mclkdiv = 0; + + if (des_sr > 24000 || des_sr == 8000 || des_sr==16000) + { + OSR_value = 256; + *mode_sel = 1; //48k mode + } + else + { + OSR_value = 512; + *mode_sel = 0; //16k mode + } + + for(k=0; k0)? a : (-a); + b = SR_save - des_sr; + b = (b>0)? b : (-b); + if (a=0; k--) //DAC_DIV value + { + out_sr = asic_pll/(k+1); + if (out_sr > MAX_DACCLK) + break; + out_sr = out_sr/OSR_table[j]; + a = out_sr-des_sr; + a = (a>0)? a : (-a); + b = SR_save-des_sr; + b = (b>0)? b : (-b); + if (a vol_info[port].vol_max) + vol = vol_info[port].vol_max; + + return vol; +} + +static inline int get_volume_info(int port, int type) +{ + int vol; + + switch(type){ + case VOL_INFO_DEFAULT: + vol = vol_info[port].vol_default; + break; + case VOL_INFO_MIN: + vol = vol_info[port].vol_min; + break; + case VOL_INFO_MAX: + vol = vol_info[port].vol_max; + break; + default: + vol = 0; + break; + } + return vol; +} + + +/* + *set mixer volume by port, + *return old volume; + * + * */ +static inline int set_mixer_volume(struct ak39_codec *codec, + int port, int volume) +{ + int old_vol; + + old_vol = codec->mixer_volume[port]; + codec->mixer_volume[port] = volume; + + return old_vol; +} + +static inline int get_mixer_volume(struct ak39_codec *codec, + int port) +{ + return codec->mixer_volume[port]; +} + +/** + * @brief set hp/linein/mic gain + * @author lixinhai + * @date 2013-08-02 + * @input codec: handle. + * @input port: control port:hp, linein or mic. + * @input gain: gain value. + * @return void + */ +static void ak39_set_gain(struct ak39_codec *codec, int port, int gain) +{ + switch(port) { + case MIXER_PORT_HP_VOL: + ak39_set_hp_gain(gain); + break; + case MIXER_PORT_LINEIN_VOL: + ak39_set_linein_gain(gain); + break; + case MIXER_PORT_MIC_VOL: + ak39_set_mic_gain(gain); + break; + } +} + +/** + * @brief set playback mode: headphone or speaker + * @author lixinhai + * @date 2013-08-02 + * @input codec: handle. + * @input plug_in: headphone is plug in. + * @return void + */ +static inline void set_playmode_switch(struct ak39_codec *codec, + int plug_in) +{ + if(codec->playmode != PLAYMODE_AUTO_SWITCH) + return; + + if(plug_in) + codec->mixer_switch[MIXER_ADDR_HPDET] = PLAYMODE_STATUS_HP; + else + codec->mixer_switch[MIXER_ADDR_HPDET] = PLAYMODE_STATUS_SPEAKER; +} + +/** + * @brief get playback mode: headphone or speaker + * @author lixinhai + * @date 2013-08-02 + * @input codec: handle. + * @return void + */ +static int ak39_codec_get_playmode(struct ak39_codec *codec) +{ + if(codec->playmode == PLAYMODE_HP) { + return PLAYMODE_STATUS_HP; + } else if(codec->playmode == PLAYMODE_SPEAKER) + return PLAYMODE_STATUS_SPEAKER; + else + return codec->mixer_switch[MIXER_ADDR_HPDET]; +} + +/** + * @brief set route source on destation. + * @author lixinhai + * @date 2013-08-02 + * @input codec: handle. + * @input dst: destation + * @input src: source + * @input src_mask: source mask. + * @return new destation of route. + */ +static inline int set_route_src_on_dst(struct ak39_codec *codec, + int dst, int src, int src_mask) +{ + codec->mixer_route[dst] &= ~src_mask; + codec->mixer_route[dst] |= src; + + PDEBUG("%s: destion:%d, src:%d, src_mask:%d, mixer source:%d.\n", + __func__, dst, src, src_mask, codec->mixer_route[dst]); + return codec->mixer_route[dst]; +} + +static inline int get_route_src_on_dst(struct ak39_codec *codec, + int dst, int src_mask) +{ + return codec->mixer_route[dst] & src_mask; +} + + +static inline int check_route_src_on_dst(struct ak39_codec *codec, + int dst, int src) +{ + return !!(codec->mixer_route[dst] & src); +} + +static int calc_route_src_status(struct ak39_codec *codec, int dst, int src, int src_mask) +{ + int i; + int status = 0; + + for(i=0; imixer_route[i] & (src & src_mask); + else + status |= codec->mixer_route[i] & src_mask; + } + + return !!status; +} + + +/** + * @brief set input device power + * @author + * @date + * @param[in] src: DAC|LINEIN|MIC + * addr:route No. + * CurSrc:all three route's current src + * @return void + */ +void ak39_set_src_power(int dst, int src) +{ + int s_dac, s_linein, s_mic, s_src; + struct ak39_codec *codec = g_pcodec; + + if(dst == MIXER_ADDR_DST_HP) { + s_dac = calc_route_src_status(codec, dst, src, SOURCE_DAC); + s_linein = calc_route_src_status(codec, dst, src, SOURCE_LINEIN); + s_mic = calc_route_src_status(codec, dst, src, SOURCE_MIC); + } else { + s_dac = calc_route_src_status(codec, dst, src, SOURCE_ADC_DAC); + s_linein = calc_route_src_status(codec, dst, src, SOURCE_ADC_LINEIN); + s_mic = calc_route_src_status(codec, dst, src, SOURCE_ADC_MIC); + } + + s_src = s_dac|s_linein|s_mic; + + PDEBUG("%s: src:%d, s_dac:%d, s_linein:%d, s_mic:%d.\n", + __func__, src, s_dac, s_linein, s_mic); + + ak39_set_sel_vref(codec, s_src); + ak39_set_vcm_ref_power(s_src); + + //set DAC power in PCM interface function: codec_playback_prepare() + ak39_set_linein_power(s_linein); + ak39_set_mic_power(s_mic); +} + + +static int ak39_codec_stream_control(struct ak39_codec *codec, + int dst, int running) +{ + int src; + + src = get_route_src_on_dst(codec, dst, SOURCE_MIXED_ALL_MASK); + PDEBUG("%s: dst:%d, src:%d, status:%s.\n", + __func__, dst, src, running ? "running":"stop"); + + if(!running) + src = 0; + + switch(dst) { + case MIXER_ADDR_DST_HP: + if(codec->used_hp_mute) + ak_gpio_setpin(codec->hpmute_gpio.pin, codec->hpmute_en_val); + + ak39_set_hp_power(!!src, !codec->used_hp_mute); + + if(codec->used_hp_mute) { + int dis_val; + + dis_val = (codec->hpmute_en_val == AK_GPIO_OUT_HIGH)? + AK_GPIO_OUT_LOW:AK_GPIO_OUT_HIGH; + ak_gpio_setpin(codec->hpmute_gpio.pin, dis_val); + } + ak39_set_hp_in(src); + ak39_set_src_power(dst, src); + + break; + case MIXER_ADDR_DST_ADC2: + ak39_set_adc2_in(src); + ak39_set_src_power(dst, src); + //dump_codec_reg(codec); + break; + default: + break; + } + + return 0; +} + + +/** + * @brief open a dac device + * @author + * @date + * @return void + */ +/*void ak39_codec_dac_open(struct ak_codec_dai *dai)*/ +void ak39_codec_dac_open(void) +{ + int i; + struct ak_codec_dai *dai = g_pdai; + struct ak39_codec *codec = to_ak39_codec(dai); + + if(codec->dac_state) + ;//return; + + codec->dac_state = 1; + + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) |= (1<<4); + REG32(codec->analog_ctrl_base + CLOCK_GATE_CTRL_REG) |= (1<<4); + + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= ~(1<<4); + REG32(codec->analog_ctrl_base + CLOCK_GATE_CTRL_REG) &= ~(1<<4); + + REG32(codec->adda_cfg_base + DAC_CONFIG_REG) |= MUTE; + + REG32(codec->adda_cfg_base + DAC_CONFIG_REG) &= ~DAC_CTRL_EN; + REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) &= ~DAC_CLK_EN; + + REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) |= DAC_DIV_VLD; + i = 0; + while(REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) & (DAC_DIV_VLD)) + { + ++i; + if(i > 100000) + { + printk("set da clk reg fail\n"); + return ; + } + } + + // to enable DAC CLK + REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) |= DAC_CLK_EN; + + //soft reset DAC + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= ~(DAC_SOFT_RST); + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) |= DAC_SOFT_RST; + + //to enable internal dac/adc via i2s + REG32(codec->analog_ctrl_base + MULTIPLE_FUN_CTRL_REG1) |= IN_DAAD_EN; + + //set default dac div and osr, for de pipa noise + + //I2S config reg + REG32(codec->adda_cfg_base + I2S_CONFIG_REG) &= (~I2S_CONFIG_WORDLENGTH_MASK); + REG32(codec->adda_cfg_base + I2S_CONFIG_REG) |= (16<<0); + + //config I2S interface DAC + REG32(codec->adda_cfg_base + DAC_CONFIG_REG) |= DAC_CTRL_EN; + + //enable accept data from l2 + REG32(codec->adda_cfg_base + DAC_CONFIG_REG) |= L2_EN; + REG32(codec->adda_cfg_base + DAC_CONFIG_REG) |= FORMAT; + REG32(codec->adda_cfg_base + DAC_CONFIG_REG) &= ~ARM_INT; + REG32(codec->adda_cfg_base + I2S_CONFIG_REG) &= ~POLARITY_SEL; + + REG32(codec->analog_ctrl_base + FADEOUT_CTRL_REG) |= DAC_FILTER_EN; + + REG32(codec->adda_cfg_base + DAC_CONFIG_REG) &= ~MUTE; +} + +void ak39_dac_mute(void) +{ + //REG32(REG_2002E + DAC_CONFIG_REG) |= MUTE; +} + +/** + * @brief Close a dac device + * @author + * @date + * @return void + */ +/*void ak39_codec_dac_close(struct ak_codec_dai *dai)*/ +void ak39_codec_dac_close(void) +{ + struct ak_codec_dai *dai = g_pdai; + struct ak39_codec *codec = to_ak39_codec(dai); + + if(!codec->dac_state) + return; + + codec->dac_state = 0; + REG32(codec->adda_cfg_base + DAC_CONFIG_REG) &= (~L2_EN); + + REG32(codec->analog_ctrl_base + MULTIPLE_FUN_CTRL_REG1) &= (~(IN_DAAD_EN));// tdisable internal DAC/ADC + REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) &= (~(DAC_CLK_EN|DAC_DIV_VLD));// disable DAC CLK + REG32(codec->analog_ctrl_base + DAC_CONFIG_REG) &= (~DAC_CTRL_EN);// to disable DACs +} + +/** + * @brief Set sample rate + * @author + * @date + * @param[in] samplerate: desired sample rate + * @return unsigned long + * @retval actual sample rate + */ +unsigned long ak39_codec_set_dac_samplerate(unsigned int samplerate) +{ + unsigned char osr, mclkdiv; + unsigned long act_samplerate; + struct ak_codec_dai *dai = g_pdai; + struct ak39_codec *codec = to_ak39_codec(dai); + + act_samplerate = get_dac_osv_div(codec, &osr, &mclkdiv, samplerate); + + //disable HCLK, and disable DAC interface + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) &= ~(DAC_HCLK_EN); + //REG32(codec->analog_ctrl_base + DAC_CONFIG_REG) &= ~(DAC_CTRL_EN); + REG32(codec->adda_cfg_base + DAC_CONFIG_REG) &= ~(DAC_CTRL_EN); + + //disable CLK + REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) &= ~(DAC_CLK_EN); + + set_dac_highspeed(codec, act_samplerate); + + // NOTE: should reset DAC!!! + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= (~DAC_SOFT_RST); + // mdelay(5); + + //set OSR + REG32(codec->analog_ctrl_base + FADEOUT_CTRL_REG) &= (~OSR_MASK); + REG32(codec->analog_ctrl_base + FADEOUT_CTRL_REG) |= (OSR(osr)); + //mdelay(2); + + //set DIV + REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) &= (~(MASK_DAC_DIV_INT)); + REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) |= (DAC_DIV_INT(mclkdiv)); + + REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) &= (~(MASK_DAC_DIV_FRAC)); + REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) |= (DAC_DIV_VLD); + //mdelay(2); + + //to reset dac + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) |= DAC_SOFT_RST; + + //mdelay(5); + //enable HCLK, and enable DAC interface + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) |= (DAC_HCLK_EN); + //REG32(codec->analog_ctrl_base + DAC_CONFIG_REG) |= (DAC_CTRL_EN); + REG32(codec->adda_cfg_base + DAC_CONFIG_REG) |= (DAC_CTRL_EN); + + //enable CLK + REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) |= (DAC_CLK_EN); + //mdelay(5); + + return act_samplerate; +} + +/** + * @brief Set DAC channels: mono,stereo + * @author + * @date + * @param[in] bool mono: true-mono, false-stereo + * @return void + */ +/*void ak39_codec_set_dac_channels(struct ak_codec_dai *dai, unsigned int chnl)*/ +void ak39_codec_set_dac_channels(unsigned int chnl) +{ + // struct ak_codec_dai *dai = g_pdai; +} + +/** + * @brief open ADC2 + * @author + * @date + * @param[in] void + * @return void + */ +void ak39_codec_adc2_open(void) +{ + struct ak_codec_dai *dai = g_pdai; + struct ak39_codec *codec = to_ak39_codec(dai); + + ak39_adc2_dac_share_open(); + + //unsigned long i; + if(codec->adc2_state) + return; + + codec->adc2_state = 1; + + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= ~(1<<3); + REG32(codec->analog_ctrl_base + CLOCK_GATE_CTRL_REG) &= ~(1<<3); + + // soft reset ADC2 controller + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) |= (ADC2_SOFT_RST); + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= ~(ADC2_SOFT_RST); + + // set_cur_pmos(codec, 0x0); //must be set to 0 when ADC2 is working + + REG32(codec->analog_ctrl_base + MULTIPLE_FUN_CTRL_REG1) |= IN_DAAD_EN; //enable internal + +#if 0 + //disable vcm2 discharge + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~(0x1f << 6); + + //PowerOn Vcm2/3 + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~PD_VCM3; + + //SetVcmNormal + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~PL_VCM3; + + //power on bias generator + REG32( codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= (~(0x1<<0)); +#endif + + //EnableAdc2Limit + //h240 no need this + //REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) |= ADC_LIM; + //enable limit ADC input between vcm3 to 0 + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) |= (1<<28); + + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) &= ~HOST_RD_INT_EN; + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) |= CH_POLARITY_SEL;//Receive the left channel data when the lrclk is high + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) &= ~I2S_EN; //Internal ADC MODE + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) &= ~WORD_LENGTH_MASK; + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) |= (0xF << 8); //WORD LENGTH IS 16 BIT + + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) |= (ADC2_SOFT_RST); + + //Enable adc controller + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) |= ADC2_CTRL_EN; + + //Enable l2 + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) |= ADC2MODE_L2_EN; + + //Power on adc2 conversion + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) &= ~PD_S2D; + + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) &= ~PD_ADC2; + +#if 0 + mdelay(1); + pd_ref_enable(codec); +#endif +} + +/** + * @brief close ADC2 + * @author + * @date + * @param[in] void + * @return void + */ +void ak39_codec_adc2_close(void) +{ + struct ak_codec_dai *dai = g_pdai; + struct ak39_codec *codec = to_ak39_codec(dai); + + if(!codec->adc2_state) + return; + + codec->adc2_state = 0; + + //disable l2 + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) &= ~ADC2MODE_L2_EN; + + //disable adc2 clk + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) &= ~ADC2_CLK_EN; + + //disable ADC2 interface + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) &= ~ADC2_CTRL_EN; + + //Power off adc2 + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) |= PD_ADC2|PD_S2D; + +#if 0 + if((1 << 17) != (REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) & (1 << 17))) + { + if(!(REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) & DAC_CLK_EN)) + { + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) |= PD_VCM3; + //h240 no need this + //REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) &= ~(PD_VCM2); + + pd_ref_disable(codec); + + } + } +#endif + + REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) |= (DAC_DIV_VLD); //inhabit adc2 clock + +} + +/** + * @brief set ADC2 sample rate + * @author + * @date + * @param[in] samplerate: desired rate to be set + * @return void + */ +unsigned long ak39_codec_set_adc2_samplerate(unsigned int samplerate) +{ + unsigned char mode_sel = 0; + unsigned char save_div = 0; + unsigned long out_sr = 0; + unsigned long i = 0; + struct ak_codec_dai *dai = g_pdai; + struct ak39_codec *codec = to_ak39_codec(dai); + + //disable HCLK, and disable ADC interface + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) &= ~(ADC2_CTRL_EN); + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) &= ~(ADC2_HCLK_EN); + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) &= ~(ADC2_CLK_EN); + + out_sr = get_adc2_osr_div(codec, &mode_sel, &save_div, samplerate); + + set_adc_highspeed(codec, out_sr); + + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= ~(1<<27); //reset adc from adc clk + i = 0; + while(REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) & (ADC2_DIV_VLD)) + { + ++i; + if(i > 100000) + { + printk("adc set sr fail\n"); + return 0; + } + } + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) &= ~(MASK_ADC2_DIV); + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) |= ADC2_DIV(save_div); + + REG32(codec->analog_ctrl_base + FADEOUT_CTRL_REG) &= ~(1<analog_ctrl_base + FADEOUT_CTRL_REG) |= (mode_sel << ADC2_OSR_BIT); + + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) |= (ADC2_DIV_VLD); + i = 0; + while(REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) & (ADC2_DIV_VLD)) + { + ++i; + if(i > 100000) + { + printk("adc set clk reg fail\n"); + return 0; + } + } + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) |= (1<<27); //release the reset adc from adc clk + + + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) |= (ADC2_CLK_EN);//enable ADC2 Clock + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) |= (ADC2_HCLK_EN);//enable ADC2 Clock + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) |= (ADC2_CTRL_EN); + return out_sr; +} + + +/** + * @brief set ADC2 channel + * @author + * @date + * @param[in] chnl: 1-mono; 2-stereo + * @return void + */ +void ak39_set_adc2_channels(unsigned int chnl) +{ + // struct ak_codec_dai *dai = g_pdai; +} + + +/** + * @brief cfg shutdown speaker GPIO + * @author + * @date + * @param[in] bOn: 1-power on; 0-power off + * @return void + */ +void ak39_codec_speak_on(struct ak39_codec *codec, bool bOn) +{ + unsigned int pin = codec->spkrshdn_gpio.pin; + ak_setpin_as_gpio(pin); + ak_gpio_cfgpin(pin, AK_GPIO_DIR_OUTPUT); + ak_gpio_setpin(pin, bOn); +} + + +/** + * @brief when playback start, ak39_codec_playback_start must be called to start output channel + * @author Cheng JunYi + * @revisor Wu Daochao(2012-08-23) + * @date + * @return void + */ +static void ak39_codec_playback_start(struct ak_codec_dai *dai) +{ + struct ak39_codec *codec = to_ak39_codec(dai); + + if(codec->mixer_ch_duration[MIXER_ADDR_PLAY_DURATION] == CHNLDURATION_EVEROPEN) + return; + + ak39_codec_get_playmode(codec); + ak39_codec_stream_control(codec, MIXER_ADDR_DST_HP, 1); + + codec->outputing = true; + +} + +void ak39_start_to_play(struct ak_codec_dai *dai, + unsigned int channels, unsigned int samplerate) +{ + ak39_codec_set_dac_samplerate(samplerate); + ak39_codec_set_dac_channels(channels); + ak39_codec_dac_open(); + ak39_codec_playback_start(dai); +} + +static void ak39_codec_playback_stop(struct ak_codec_dai *dai) +{ + struct ak39_codec *codec = to_ak39_codec(dai); + + //if we want to open some channel for ever, return + if(codec->mixer_ch_duration[MIXER_ADDR_PLAY_DURATION] == CHNLDURATION_EVEROPEN) + return; + + /*close the dac output*/ + ak39_codec_stream_control(codec, MIXER_ADDR_DST_HP, 0); + codec->outputing = false; +} + +#if 0 +static void ak39_codec_capture_start(struct ak_codec_dai *dai) +{ + struct ak39_codec *codec = to_ak39_codec(dai); + + ak39_codec_stream_control(codec, MIXER_ADDR_DST_ADC2, 1); +} + +static void ak39_codec_capture_stop(struct ak_codec_dai *dai) +{ + struct ak39_codec *codec = to_ak39_codec(dai); + + ak39_codec_stream_control(codec, MIXER_ADDR_DST_ADC2, 0); +} +#endif + +/**********************HPDet switch**************************/ + +static int set_hpdet_status(struct ak39_codec *codec) +{ + int plug_in = 0; + int irq_type; + + if(ak_gpio_getpin(codec->hpdet_gpio.pin) == codec->hp_on_value) + { + //hp is plugged in + plug_in = 1; + irq_type = (codec->irq_hp_on_type == IRQ_TYPE_LEVEL_LOW) ? + IRQ_TYPE_LEVEL_HIGH:IRQ_TYPE_LEVEL_LOW; + } + else + { + //hp is pulled out + plug_in = 0; + irq_type = codec->irq_hp_on_type; + } + + printk("detect the headphone plug %s.\n", plug_in ? "on":"out"); + irq_set_irq_type(codec->hp_det_irq, irq_type); + + set_playmode_switch(codec, plug_in); + + ak39_codec_speak_on(codec, !plug_in); + return 0; +} + + +/** + * @brief when hp state is changed,schedule hp_det_worker to config output \ + channel to speaker or not + * @author Cheng Mingjuan + * @date + * @return void + */ +#if 0 +static void hp_det_worker(struct work_struct *work) +{ + struct ak39_codec *codec = container_of(work, struct ak39_codec,d_work.work); + + set_hpdet_status(codec); + + ak_codec_ctl_event(SNDRV_CTL_ELEM_IFACE_HWDEP, + SNDRV_CTL_EVENT_MASK_VALUE, "HPDet switch"); + + enable_irq(codec->hp_det_irq); +} +#endif + +/** + * @brief hp det + * @author Cheng Mingjuan + * @date + * @return void + */ +#if 0 +static irqreturn_t ak39_codec_hpdet_irq(int irq, void *dev_id) +{ + struct ak39_codec *codec = dev_id; + + disable_irq_nosync(irq); + + schedule_delayed_work(&codec->d_work, msecs_to_jiffies(100)); + return IRQ_HANDLED; +} +#endif + +/************** + * mixer interface + **************/ + + +/***********************config channel duration*******************************/ +#define AK39PCM_OUTPUTCHNL_DURATION(xname, xindex, addr) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .name = xname, .index = xindex, \ + .info = codec_ChnlDuration_info, \ + .get = codec_ChnlDuration_get, \ + .put = codec_ChnlDuration_put, \ + .private_value = addr \ +} + +/** + * @brief info callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_ChnlDuration_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int addr = kcontrol->private_value; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + if(MIXER_ADDR_PLAY_DURATION == addr) + { + uinfo->value.integer.min = CHNLDURATION_MIN; + uinfo->value.integer.max = CHNLDURATION_MAX; + } + else + { + return -EINVAL; + } + + return 0; +} + +/** + * @brief get callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_ChnlDuration_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); + struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); + + int addr = kcontrol->private_value; + if(addr != MIXER_ADDR_PLAY_DURATION) + { + return -EINVAL; + } + + ucontrol->value.integer.value[0] = codec->mixer_ch_duration[addr]; + return 0; +} + +/** + * @brief put callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_ChnlDuration_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); + struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); + + int addr = kcontrol->private_value; + int duration = ucontrol->value.integer.value[0]; + + printk("ak39 codec: ChnlDuration_put duration=%d\n",duration); + if(duration == codec->mixer_ch_duration[MIXER_ADDR_PLAY_DURATION]){ + return 0; + } + if(addr != MIXER_ADDR_PLAY_DURATION) { + return -EINVAL; + } + if((duration > CHNLDURATION_MAX)||(duration < CHNLDURATION_MIN)) { + return -EINVAL; + } + + codec->mixer_ch_duration[MIXER_ADDR_PLAY_DURATION] = duration; + if((duration == CHNLDURATION_CONSTANT) && + (!test_bit(0, ppb_statu))) { + ak39_codec_playback_stop(dai); + } + + return 0; +} + +/*********************select fixed output channel**********************************/ +#define AK39PCM_DAC_OUT_MODE(xname, xindex, addr) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .name = xname, .index = xindex, \ + .info = codec_dac_outmode_info, \ + .get = codec_dac_outmode_get, \ + .put = codec_dac_outmode_put, \ + .private_value = addr \ +} + +/** + * @brief info callback. + * becsuse of arch-ak39 has dac output only, + * this interface is not used. + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_dac_outmode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int addr = kcontrol->private_value; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + if(MIXER_ADDR_OUTMODE_DAC == addr) + { + uinfo->value.integer.min = OUTMODE_MIN; + uinfo->value.integer.max = OUTMODE_MAX; + } + else + { + return -EINVAL; + } + + return 0; +} + +/** + * @brief get callback + * becsuse of arch-ak39 has dac output only, + * this interface is not used. + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_dac_outmode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int addr = kcontrol->private_value; + + if(addr != MIXER_ADDR_OUTMODE_DAC) + { + return -EINVAL; + } + + ucontrol->value.integer.value[0] = OUTMODE_HP; + return 0; +} + +/** + * @brief put callback + * becsuse of arch-ak39 has dac output only, + * this interface is not used. + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_dac_outmode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + printk("ak39 platform not support out mode setting.\n"); + return 0; +} + +/**********************hp detect (read-only)****************************/ +#define AK39PCM_SWITCH(xname, xindex, addr) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ, \ + .name = xname, .index = xindex, \ + .info = codec_switch_info, \ + .get = codec_switch_get, \ + .private_value = addr \ +} + +/** + * @brief info callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int addr = kcontrol->private_value; + if(MIXER_ADDR_HPDET != addr) + { + return -EINVAL; + } + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +/** + * @brief get callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); + struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); + + int addr = kcontrol->private_value; + if(MIXER_ADDR_HPDET != addr) + { + return -EINVAL; + } + + ucontrol->value.integer.value[0] = codec->mixer_switch[addr]; + PDEBUG("%s enter, value:%lu.\n", __func__, ucontrol->value.integer.value[0]); + return 0; +} + +/***************************VOLUME*********************/ +#define AK39PCM_VOLUME(xname, xindex, addr) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .name = xname, .index = xindex, \ + .info = codec_volume_info, \ + .get = codec_volume_get, \ + .put = codec_volume_put, \ + .private_value = addr \ +} + +/** + * @brief info callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int port = kcontrol->private_value; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + + + if(port < 0 || port >= MIXER_PORT_VOL_COUNT) { + printk(KERN_ERR "%s port %d is invaild.\n", __func__, port); + return -EINVAL; + } + uinfo->value.integer.min = get_volume_info(port, VOL_INFO_MIN); + uinfo->value.integer.max = get_volume_info(port, VOL_INFO_MAX); + + return 0; +} + +/** + * @brief get callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); + struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); + + int port = kcontrol->private_value; + if(port < 0 || port >= MIXER_PORT_VOL_COUNT) { + printk(KERN_ERR "%s port %d is invaild.\n", __func__, port); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = get_mixer_volume(codec, port); + PDEBUG("%s enter, port:%d, volume:%lu.\n", + __func__, port, ucontrol->value.integer.value[0]); + return 0; +} + +/** + * @brief put callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); + struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); + + int port = kcontrol->private_value; + int vol, old_vol; + int change = 0; + + vol = ucontrol->value.integer.value[0]; + + PDEBUG("%s enter, port:%d, volume:%d.\n", __func__, port, vol); + vol = volume_repair_range(port, vol); + + old_vol = set_mixer_volume(codec, port, vol); + if(vol != old_vol) { + change = 1; + ak39_set_gain(codec, port, vol); + } + + return change; +} + +/***************************ROUTE**************************/ +#define AK39PCM_ROUTE(xname, xindex, addr) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .name = xname, \ + .index = xindex, \ + .info = codec_route_info, \ + .get = codec_route_get, \ + .put = codec_route_put, \ + .private_value = addr \ +} + +/** + * @brief info callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_route_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int addr = kcontrol->private_value; + + if(addr >= MIXER_DST_COUNT) + { + return -EINVAL; + } + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = SIGNAL_SRC_MAX; + + PDEBUG("%s enter, min:0, max:%d.\n", __func__, SIGNAL_SRC_MAX); + return 0; +} + +/** + * @brief get callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_route_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); + struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); + int dst = kcontrol->private_value; + + if(dst >= MIXER_DST_COUNT) + { + return -EINVAL; + } + + ucontrol->value.integer.value[0] = get_route_src_on_dst(codec, dst, SOURCE_MIXED_ALL_MASK); + PDEBUG("%s enter, value:%lu.\n", __func__, ucontrol->value.integer.value[0]); + return 0; +} + +/** + * @brief put callback + * @author Cheng Mingjuan + * @date + * @return void + */ +/*application has to call this callback with params value=0 to power down input&output devices*/ +static int codec_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); + struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); + int change = 1; + unsigned long dst = kcontrol->private_value; + int src = 0; + + src = ucontrol->value.integer.value[0]; + PDEBUG("%s:set route: dst:%lu, src:%d.\n", __func__, dst, src); + + if((src < SIGNAL_SRC_MUTE) || (src > SIGNAL_SRC_MAX)) + return -EINVAL; + + if(dst == MIXER_ADDR_DST_HP) { + int plug_in = 0; + + if(codec->playmode == PLAYMODE_HP) + plug_in = 1; + else if(codec->playmode == PLAYMODE_SPEAKER) + plug_in = 0; + + set_playmode_switch(codec, plug_in); + + /*power on the speaker when headphone plug out.*/ + ak39_codec_speak_on(codec, !plug_in); + + }else if(dst == MIXER_ADDR_DST_ADC2) { + } + + if(get_route_src_on_dst(codec, dst, SOURCE_MIXED_ALL_MASK) == src) { + change = 0; + } + set_route_src_on_dst(codec, dst, src, SOURCE_MIXED_ALL_MASK); + + return change; +} + +/***************************ROUTE**************************/ +#define AK39PCM_AEC(xname, xindex, addr) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .name = xname, \ + .index = xindex, \ + .info = codec_aec_info, \ + .get = codec_aec_get, \ + .put = codec_aec_put, \ + .private_value = addr \ +} + +/** + * @brief info callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_aec_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + //int port = kcontrol->private_value; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + + printk( "get the aec info \n" ); + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 10; + + return 0; +} + +/** + * @brief get callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_aec_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); + //struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); + + //int port = kcontrol->private_value; + ucontrol->value.integer.value[0] = dai->aec_flag; + //printk("%s enter, %d\n", __func__, ucontrol->value.integer.value[0]); + return 0; +} + +/** + * @brief put callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_aec_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); + //struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); + + int src = ucontrol->value.integer.value[0]; + int port = kcontrol->private_value; + + + printk("%s enter, %d %d \n", __func__, port, src); + dai->aec_flag = src; + + return 1; +} + +static struct ak_proc_entry codec_pentries[] = { +}; + +static struct snd_kcontrol_new codec_controls[] = { + AK39PCM_VOLUME("Headphone Playback Volume", 0, MIXER_PORT_HP_VOL), + AK39PCM_VOLUME("LineIn Capture Volume", 0, MIXER_PORT_LINEIN_VOL), + AK39PCM_VOLUME("Mic Capture Volume", 0, MIXER_PORT_MIC_VOL), + AK39PCM_ROUTE("Headphone Playback Route", 0, MIXER_ADDR_DST_HP), + AK39PCM_ROUTE("ADC Capture Route", 0, MIXER_ADDR_DST_ADC2), + /*AK39PCM_ROUTE("LineIn Capture Route", 0, MIXER_ADDR_LINEINSRC),*/ + AK39PCM_SWITCH("HPDet switch", 0, MIXER_ADDR_HPDET), + AK39PCM_DAC_OUT_MODE("DAC out mode", 0, MIXER_ADDR_OUTMODE_DAC), + AK39PCM_OUTPUTCHNL_DURATION("duration of output channel", 0, MIXER_ADDR_PLAY_DURATION), + AK39PCM_AEC("Set the aec", 0, 0), +}; + +int ak39_codec_probe(struct platform_device *pdev) +{ + int err = 0; + int i; + struct ak39_codec_platform_data *pdata = pdev->dev.platform_data; + struct ak39_codec *codec; + struct resource *res; + + printk("akpcmL0->ak39_codec_probe enter...\n"); + + codec = kzalloc(sizeof(*codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + g_pcodec = codec; + g_pdai = &codec->dai; + + mutex_init(&g_pcodec->share_lock); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "akpcm_AnalogCtrlRegs"); + if(!res) + { + printk(KERN_ERR "no memory resource for analog_ctrl_res\n"); + err = -ENXIO; + goto out_free_codec; + } + + codec->analog_ctrl_base = ioremap(res->start, res->end - res->start + 1); + if (!codec->analog_ctrl_base) { + printk(KERN_ERR "could not remap analog_ctrl_res memory"); + err = -ENXIO; + goto out_free_codec; + } + + //get ADC2 mode registers + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "akpcm_ADC2ModeCfgRegs"); + if(!res) + { + printk(KERN_ERR "no memory resource for adda_cfg_res\n"); + err = -ENXIO; + goto out_unremap_analog; + } + + codec->adda_cfg_base = ioremap(res->start, res->end - res->start + 1); + if (!codec->adda_cfg_base) { + printk(KERN_ERR "could not remap adda_cfg_res memory"); + err = -ENXIO; + goto out_unremap_analog; + } + + for(i=0; imixer_outmode[MIXER_ADDR_OUTMODE_DAC] = OUTMODE_HP; + codec->mixer_ch_duration[MIXER_ADDR_PLAY_DURATION] = CHNLDURATION_CONSTANT; + + /*FIXME:can add the code for worker vaild when switch auto mode*/ + /*INIT_DELAYED_WORK(&codec->d_work, hp_det_worker);*/ + + codec->playmode = pdata->boutput_only; + + //config hp det gpio + codec->hpdet_gpio = pdata->hpdet_gpio; + + //config speaker shutdown gpio + codec->spkrshdn_gpio = pdata->spk_down_gpio; + ak_gpio_set(&(codec->spkrshdn_gpio)); + + //config speaker shutdown gpio + codec->spkrshdn_gpio = pdata->spk_down_gpio; + ak_gpio_set(&(codec->spkrshdn_gpio)); + + //set hp det pin irq + if(codec->hpdet_gpio.pin >= 0) { + ak_gpio_set(&(codec->hpdet_gpio)); + + if(AK_GPIO_OUT_LOW == codec->hp_on_value) + codec->irq_hp_on_type = IRQ_TYPE_LEVEL_LOW; + else if(AK_GPIO_OUT_HIGH == codec->hp_on_value) + codec->irq_hp_on_type = IRQ_TYPE_LEVEL_HIGH; + + codec->hp_det_irq = pdata->hpdet_irq; + codec->hp_on_value = pdata->hp_on_value; + + set_hpdet_status(codec); + /*err = request_irq(codec->hp_det_irq, ak39_codec_hpdet_irq, + IRQF_DISABLED, pdev->name, codec); + if(err) + { + printk(KERN_ERR "request irq error!"); + goto err_out; + } + */ + } + + /*codec->dai.ops = &ak39_codec_ops;*/ + codec->dai.num_kcontrols = ARRAY_SIZE(codec_controls); + codec->dai.kcontrols = codec_controls; + codec->dai.num_pentries = ARRAY_SIZE(codec_pentries); + codec->dai.pentries = codec_pentries; + codec->dai.entries_private = codec; + + /*if(ak_codec_register(&codec->dai)) + goto err_out; + */ + + codec->used_hp_mute = pdata->bIsHPmuteUsed; + codec->hpmute_en_val = pdata->hp_mute_enable_value; + + if(codec->used_hp_mute){ + //config hp mute gpio + codec->hpmute_gpio = pdata->hpmute_gpio; + ak_gpio_set(&(codec->hpmute_gpio)); + } + + codec->outputing = false; + + /*platform_set_drvdata(pdev, codec);*/ + return 0; + +//err_out: +//out_unremap_adda: + iounmap(codec->adda_cfg_base); +out_unremap_analog: + iounmap(codec->analog_ctrl_base); +out_free_codec: + kfree(codec); + return err; +} + +int ak39_codec_remove(struct platform_device *pdev) +{ + struct ak39_codec *codec = platform_get_drvdata(pdev); + + /*free_irq(codec->hp_det_irq, codec);*/ + + /*FIXME:can add the code for worker vaild when switch auto mode*/ + /*cancel_delayed_work_sync(&codec->d_work);*/ + + ak39_adc2_dac_share_close(); + + iounmap(codec->analog_ctrl_base); + iounmap(codec->adda_cfg_base); + + /*platform_set_drvdata(pdev, NULL);*/ + kfree(codec); + + return 0; +} diff --git a/drivers/misc/akpcm/akpcm_defs.h b/drivers/misc/akpcm/akpcm_defs.h new file mode 100644 index 00000000..c194f164 --- /dev/null +++ b/drivers/misc/akpcm/akpcm_defs.h @@ -0,0 +1,340 @@ +#ifndef __AKPCM_DEFS_H__ +#define __AKPCM_DEFS_H__ + +#include +#include +#include +#include +#include + +#include "akpcm.h" + +#define DEBUG_BYTES + +#if 1 +#define ak_pcm_debug(fmt, arg...) \ + printk(KERN_DEBUG "@@@ "fmt" @@@\n", ##arg) +#define ak_pcm_debugx(fmt, arg...) \ + printk(KERN_DEBUG "@@@ "fmt" @@@\n\n", ##arg) +#define ak_pcm_debug_ex(fmt, arg...) \ + printk(KERN_DEBUG "@@@ [%s:%d]: "fmt" @@@\n", __func__, __LINE__, ##arg) +#define ak_pcm_debug_exx(fmt, arg...) \ + printk(KERN_DEBUG "@@@ [%s:%d]: "fmt" @@@\n\n", __func__, __LINE__, ##arg) +#else +#define ak_pcm_debug(fmt, arg...) +#define ak_pcm_debug_ex(fmt, arg...) +#define ak_pcm_debug_exx(fmt, arg...) +#endif + +#define ak_pcm_func(fmt, arg...) \ + printk(KERN_INFO "@@@ [%s]: "fmt" @@@\n", __func__, ##arg) +#define ak_pcm_func_exx(fmt, arg...) \ + printk(KERN_INFO "@@@ [%s]: "fmt" @@@\n\n", __func__, ##arg) + +#define ak_pcm_info(fmt, arg...) \ + printk(KERN_ERR "@@@ "fmt" @@@\n", ##arg) +#define ak_pcm_info_ex(fmt, arg...) \ + printk(KERN_ERR "@@@ [%s:%d]: "fmt" @@@\n", __func__, __LINE__, ##arg) +#define ak_pcm_err(fmt, arg...) \ + printk(KERN_ERR "@@@ error! "fmt" @@@\n", ##arg) +#define ak_pcm_err_ex(fmt, arg...) \ + printk(KERN_ERR "@@@ [%s:%d] error! "fmt" @@@\n", __func__, __LINE__, ##arg) +#define ak_pcm_warn(fmt, arg...) \ + printk(KERN_ERR "@@@ warn! "fmt" @@@\n", ##arg) +#define ak_pcm_warn_ex(fmt, arg...) \ + printk(KERN_ERR "@@@ [%s:%d] warn! "fmt" @@@\n\n", __func__, __LINE__, ##arg) +#define ak_pcm_assert(exp) \ + ((exp)? 1 : ak_pcm_info_ex("assert")) + +#define USE_FORMATS (AKPCM_FMTBIT_U8 | AKPCM_SMPL_BIT_U16) +#define USE_RATE (AKPCM_RATE_CONTINUOUS | AKPCM_RATE_ALL) +#define USE_RATE_MIN 8000 +#define USE_RATE_MAX 48000 +#define PLAY_PERIOD_BYTES_MIN 512 +#define PLAY_PERIOD_BYTES_MAX 65536 +#define PLAY_PERIODS_MIN 4 +#define PLAY_PERIODS_MAX 4096 +#define DELAYS_FOR_CLOSE_DAC (HZ*30) +#define CAPT_PERIOD_BYTES_MIN 512 +#define CAPT_PERIOD_BYTES_MAX 32768 +#define CAPT_PERIODS_MIN 4 +#define CAPT_PERIODS_MAX 2048 +#define MAX_TIMESTAMP_CNT 80 + +/* GAIN chennels(ANALOG) */ +#define MIXER_VOL_HP 0 +#define MIXER_VOL_LI 1 +#define MIXER_VOL_MIC 2 +#define MIXER_VOL_END 2 +/* GAIN chennels's default value */ +#define DEFAULT_HPVOL 1 +#define DEFAULT_LINEINVOL 0xF +#define DEFAULT_MICVOL 7 +/* sources(OUTPUTS) */ +#define MIXER_SRC_HP 0 +#define MIXER_SRC_LO 1 +#define MIXER_SRC_ADC2 2 +#define MIXER_SRC_END 2 +/* devices dectect switch(OUTPUTS) */ +#define MIXER_SWITCH_HPDET 0 +#define MIXER_SWITCH_END 0 +/* devices for playback */ +#define PLAYDEV_HP (AKPCM_PLAYDEV_HP) +#define PLAYDEV_LO (AKPCM_PLAYDEV_LO) +#define PLAYDEV_AUTO (AKPCM_PLAYDEV_AUTO) +#define PLAYDEV_MSK (AKPCM_PLAYDEV_HP) +/* devices for capture */ +#define CPTRDEV_MIC (AKPCM_CPTRDEV_MIC) +#define CPTRDEV_LI (AKPCM_CPTRDEV_LI) +#define CPTRDEV_AUTO (AKPCM_CPTRDEV_AUTO) +#define CPTRDEV_MSK (AKPCM_CPTRDEV_MIC | AKPCM_CPTRDEV_LI) +/* supported devices for playback */ +#define EXIST_OUT_DEV_SP (1<<1) +#define EXIST_OUT_DEV_HP (1<<0) +#define EXIST_OUT_DEV_ALL (EXIST_OUT_DEV_SP|EXIST_OUT_DEV_HP) + +enum { + SWITCH_DET_NULL = 0, + SWITCH_DET_SPEAKER = 1, + SWITCH_DET_HEADPHONE = 2 +}; + +enum { + CAPTURE_DET_NULL = 0, + CAPTURE_DET_LINEIN = 1, +}; + +/* AEC utils interface */ +typedef enum { + AEC_STATUS_IDLE = 0, /* not started */ + AEC_STATUS_AEC_OPENED = 1, /* aec opened */ + AEC_STATUS_WAIT_ADC = 2, /* dac is working, wait for adc */ + AEC_STATUS_SYNC_ADC = 3, /* to start adc with sync condition */ + AEC_STATUS_SYNC_DAC = 4, /* to start dac with sync condition */ + AEC_STATUS_AEC_WORKING = 5, /* aec is working */ + AEC_STATUS_NR_OPENED = 6, /* only nr is opened */ + AEC_STATUS_NR_WORKING = 7, /* only nr is working */ +}e_aec_status; + +/* bit[0]-opened; bit[1]-prepared */ +enum status_bit { + STATUS_BIT_OPENED = 0x00, + STATUS_BIT_PREPARED, +}; + +/* bit[0]-strm; bit[1]-DMA; bit[2] suspend */ +enum pcm_io_bit { + IO_BIT_STREAM = 0x00, + IO_BIT_DMA, + IO_BIT_SUSPEND +}; + +typedef l2_dma_transfer_direction_t tL2DMA_DIR; + +struct akpcm_runtime { + struct akpcm_pars cfg; + unsigned int hw_ptr; /* hardware ptr (volatile ???) */ + unsigned int app_ptr; /* application ptr (volatile ???) */ + unsigned int aec_ptr; /* AEC ptr */ + spinlock_t ptr_lock; /* lock to protect ptr */ + struct mutex lock; + + /* -- SW parameters -- */ + unsigned int buffer_bytes; + unsigned int boundary; + + /* -- DMA -- */ + unsigned char *dma_area; /* DMA area(vaddr) */ + dma_addr_t dma_addr; /* physical bus address */ + + /* -- AEC/NR/AGC -- */ + int enable_nr; // noise reduction + int enable_agc; // automatic gain control + unsigned long ts; + + /* start data check time */ + u_int64_t start_time; + u_int64_t end_time; +}; + +struct akpcm { + struct cdev cdevice[2]; + struct device *dev; + struct akpcm_features *play_hw; + struct akpcm_features *capt_hw; + struct akpcm_runtime *play_rt; + struct akpcm_runtime *cptr_rt; + struct mutex io_lock; + + /* AEC */ + int enable_aec; // accoustic echo cancellation + int enable_nr; + int enable_agc; + unsigned long app_flag; + int app_enable_aec; + int app_enable_nr; + int app_enable_agc; + int aec_dump_type; + int eq_enable; + e_aec_status aec_status; + struct mutex aec_status_lock; + wait_queue_head_t aec_sync_wq; + struct workqueue_struct *aec_event_wq; + struct work_struct aec_event_work; + T_VOID *aec_filter; + T_VOID *filter_handle; + T_AEC_BUF aec_bufs; + T_S16 *aec_out_buf; // temp buf for aec result + struct task_struct *aec_thread; + wait_queue_head_t aec_wq; + bool aec_thread_run; + bool aec_data_triger; + struct task_struct *aec_prio_supervisor_thread; + wait_queue_head_t aec_prio_wq; + bool aec_supervisor_thread_run; + /* -1: to lower down, 0: normal, 1: to raise high, 2: raised */ + int aec_high_prio; + /* far NR */ + T_VOID *far_filter; + T_AEC_BUF far_bufs; + T_S8 *far_frame_buf; // temp buf for dac frame process + int far_frame_off; // write offset of far_frame_buf + + struct completion play_completion; /* for L2DMA */ + struct completion capt_completion; /* for L2DMA */ + wait_queue_head_t play_wq; + wait_queue_head_t capt_wq; + int playdev; + int cptrdev; + int dev_manual; + + void __iomem *Analog_Regs; + void __iomem *I2S_Regs; + void __iomem *ADC2_Regs; + + u8 L2BufID_DAC; + u8 L2BufID_ADC2; + + unsigned long playback_sm_flag; //bit[0]-opened; bit[1]-prepared + unsigned long capture_sm_flag; //bit[0]-opened; bit[1]-prepared + unsigned long outflag; //bit[0]-strm; bit[1]-DMA; bit[2] suspend + unsigned long inflag; //bit[0]-strm; bit[1]-DMA; bit[2] suspend + + int mixer_volume[MIXER_VOL_END+1]; + int mixer_source[MIXER_SRC_END+1]; + + /* EXIST_OUT_DEV_SP(b[1]), EXIST_OUT_DEV_HP(b[0]) */ + int bfExistPlayDev; + + int play_dectect; + struct gpio_info HPDet_gpio; // headphone detect GPIO + int HPDet_irq; + int HPDet_on_value; + int HPon_irqType; + int HPoff_irqType; + + struct gpio_info SPdown_gpio;// speaker shutdown GPIO + + struct gpio_info hpmute_gpio; + int hpmute_EnValue; // value for hpmute enable + int hpmute_DisValue; // value for hpmute disable + + int bIsHPmuteUsed; // whether to use hardware de-pipa or not + + struct delayed_work DelayWork; + struct timer_list timer_stop_output; + + bool IsOutputing; + + struct gpio_info linindet_gpio;// lineint detecting GPIO + int lininon_irqType; + int lininoff_irqType; + int capture_dectect; + struct delayed_work LininWork; + int linindet_irq; + unsigned long timestamp[MAX_TIMESTAMP_CNT]; + + u8 aec_has_pend_data; + u8 dac_endwith_zero; + + int dac_opened_count; +}; + +//extern u_int64_t ak39_gethrtick(void); + +/* + * convert a stereo buffer to mono format + * mono samples will be put into first half of buf + */ +static inline void stereo_to_mono(void *dest, void *src, int buf_size) +{ + int i; + + for (i=0; i0; i--) { + ((T_S16*)dest)[2*i-1] = ((T_S16*)src)[i-1]; + ((T_S16*)dest)[2*i-2] = ((T_S16*)src)[i-1]; + } +} + +static inline unsigned long get_timestamp(void) +{ + unsigned long ul; + + ul = jiffies; + if (ul >= INITIAL_JIFFIES) + ul -= INITIAL_JIFFIES; + else + ul = (~(unsigned long)0) - INITIAL_JIFFIES + ul; + ul = jiffies_to_msecs(ul); + + return ul; +} + +void akpcm_set_mic_gain(unsigned long gain); +void akpcm_set_hp_gain(unsigned long gain); + +struct akpcm* get_akpcm_ptr(void); + +void akpcm_notify_cb(T_U32 code); +void* akpcm_malloc_cb(unsigned long size); +void akpcm_free_cb(void * mem); + +/** + * set source for OUTPUT(HP or ADC2) + * author: panqihe 2014-06-25 + * @addr: MIXER_SRC_HP or MIXER_SRC_ADC2 + * @src: source + */ +void set_channel_source(struct akpcm *pcm, int addr, int src); + +/** + * change source for OUTPUT(HP or ADC2) + * author panqihe 2014-06-25 + * @addr: MIXER_SRC_HP or MIXER_SRC_ADC2 + * @src: source + * return int + */ +int change_channel_source(struct akpcm *pcm, int addr, int src); + +static inline int ak39_spend_us(u_int64_t start, u_int64_t end) +{ + int spend = (int)(end - start) / 1000; // 12M ticks/s + ak_pcm_assert(end >= start); + + return spend; +} + +#endif diff --git a/drivers/misc/akpcm/capture.c b/drivers/misc/akpcm/capture.c new file mode 100644 index 00000000..3bfe04dd --- /dev/null +++ b/drivers/misc/akpcm/capture.c @@ -0,0 +1,1420 @@ +/* + * pcm capture for anyka chip + * Copyright (c) by Anyka, Inc. + * Create by huang_haitao 2018-01-31 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aec.h" +#include "playback.h" +#include "capture.h" + +#define CAPTURE_READ_DEBUG (0) +#define CAPTURE_ISR_DEBUG (0) + +/* + * FULL_THRESTHOLD - + * is full state for the buffer + */ +#define FULL_THRESTHOLD(rt) (rt->cfg.period_bytes) + +/*default arguments*/ +static struct akpcm_features akpcm_capture_hardware = { + .sample_bits = AKPCM_SMPL_BIT_U8 | AKPCM_SMPL_BIT_U16, + .rates = USE_RATE, + .rate_min = USE_RATE_MIN, + .rate_max = USE_RATE_MAX, + .channels_min = 1, + .channels_max = 1, + .period_bytes_min = CAPT_PERIOD_BYTES_MIN, + .period_bytes_max = CAPT_PERIOD_BYTES_MAX, + .periods_min = CAPT_PERIODS_MIN, + .periods_max = CAPT_PERIODS_MAX, + .hp_gain_max = HEADPHONE_GAIN_MAX, + .li_gain_max = LINEIN_GAIN_MAX, + .mic_gain_max = MIC_GAIN_MAX, + .play_dev = PLAYDEV_MSK, /* HP or(and) LO */ + .cptr_dev = CPTRDEV_MSK, /* MIC or(and) LI */ +}; + +/* + * lock_capture_io - + * lock capture device + * + * @pcm: pointer to pcm device + */ +static inline void lock_capture_io(struct akpcm *pcm) +{ + mutex_lock(&pcm->io_lock); +} + +/* + * unlock_capture_io - + * unlock capture device + * + * @pcm: pointer to pcm device + */ +static inline void unlock_capture_io(struct akpcm *pcm) +{ + mutex_unlock(&pcm->io_lock); +} + +/* + * clear_capture_ptr - + * clear all cache for capturer + * + * @rt: pointer to runtime + */ +static inline void clear_capture_ptr(struct akpcm_runtime *rt) +{ + if (!in_interrupt()) + spin_lock_irq(&rt->ptr_lock); + /*all ptr is NULL means cache for capture is empty*/ + rt->hw_ptr = 0; + rt->app_ptr = 0; + rt->aec_ptr = 0; + if (!in_interrupt()) + spin_unlock_irq(&rt->ptr_lock); +} + +/* + * check_capture_status - + * check capture current status + * + * @pcm: pointer to pcm device + */ +static inline int check_capture_status(struct akpcm *pcm) +{ + int ret = 0; + unsigned long flag = (pcm->capture_sm_flag & 3); + + switch (flag) { + case 0: + ak_pcm_info_ex("ERR! capture device isn't open!"); + ret = -EPERM;/* Operation not permitted */ + break; + case 1: + ak_pcm_info_ex("ERR! capture close is operating!"); + ret = -EPERM;/* Operation not permitted */ + break; + case 2: + ak_pcm_info_ex("ERR! in capture device read!"); + ret = -EPERM;/* Operation not permitted */ + break; + case 3: + break; + default: + ak_pcm_info_ex("ERR! in capture device read!"); + ret = -EPERM;/* Operation not permitted */ + break; + } + + return ret; +} + +/* + * put_user_data - + * put capture data from here to user space + * + * @rt: pointer to runtime + * @buf: pointer to user space buffer + * @count: bytes of buffer + */ +static inline int put_user_data(struct akpcm_runtime *rt, + char __user *buf, size_t count) +{ + unsigned int app_pos = rt->app_ptr; + unsigned int buf_bytes = rt->buffer_bytes; + unsigned int app_off = (app_pos % buf_bytes); + unsigned char *vaddr = rt->dma_area; + + if (buf_bytes >= (app_off + count)) { + if (copy_to_user(buf, vaddr+app_off, count)) { + return -EFAULT; + } + } else { + unsigned int fist_bytes = buf_bytes - app_off; + + if (copy_to_user(buf, vaddr+app_off, fist_bytes)) { + return -EFAULT; + } + if (copy_to_user(buf+fist_bytes, vaddr, count-fist_bytes)) { + return -EFAULT; + } + } + + return 0; +} + +/* + * udpate_capture_app_ptr - + * update capture application ptr when application had read out. + * + * @rt: pointer to runtime + * @pre_app_pos: old app ptr + * @cur_app_pos: new app ptr + */ +static inline void update_capture_app_ptr(struct akpcm_runtime *rt, + unsigned int pre_app_pos, unsigned int cur_app_pos) +{ + spin_lock_irq(&rt->ptr_lock); +#if 0 + if (pre_app_pos != rt->app_ptr) { + /* app_ptr was updated by capture_isr */ + printk("U"); + rt->app_ptr = max(rt->app_ptr, cur_app_pos); + } else { + rt->app_ptr = cur_app_pos; + } +#else + if (cur_app_pos <= rt->aec_ptr) + rt->app_ptr = cur_app_pos; + else + rt->app_ptr = rt->aec_ptr; +#endif + + if(rt->app_ptr >= rt->boundary) + rt->app_ptr -= rt->boundary; + + spin_unlock_irq(&rt->ptr_lock); +} + +/* + * get_valid_bytes - + * capture helper routine + * get how many data ready to upper layer in capture buffer + * + * @rt: pointer to runtime + */ +static inline unsigned int get_valid_bytes(struct akpcm_runtime *rt) +{ + unsigned int valid_bytes; + unsigned int aec_pos = rt->aec_ptr; + unsigned int app_pos = rt->app_ptr; + unsigned int boundary = rt->boundary; + + /* calculate pend_data & free_space */ + if (aec_pos >= app_pos) { + valid_bytes = aec_pos - app_pos; + } else { + valid_bytes = boundary - app_pos + aec_pos; + } + +#ifdef DEBUG_BYTES + if (valid_bytes > rt->buffer_bytes) { + ak_pcm_err("valid_bytes:%u; aec_pos:%u, app_pos:%u, hw_ptr:%u", + valid_bytes, aec_pos, app_pos, rt->hw_ptr); + } +#endif + + return valid_bytes; +} + +/* + * get_capture_free_bytes - + * capture helper routine + * get how many free room in capture buffer + * + * @rt: pointer to runtime + */ +static inline unsigned int get_capture_free_bytes(struct akpcm_runtime *rt) +{ + unsigned int used_bytes; + unsigned int hw_pos = rt->hw_ptr; + unsigned int app_pos = rt->app_ptr; + unsigned int boundary = rt->boundary; + + /* calculate pend_data & free_space */ + if (hw_pos >= app_pos) { + used_bytes = hw_pos - app_pos; + } else { + used_bytes = boundary - app_pos + hw_pos; + } + +#ifdef DEBUG_BYTES + if (rt->buffer_bytes < used_bytes) { + ak_pcm_err_ex("buffer_bytes:%u, used_bytes:%u", + rt->buffer_bytes, used_bytes); + } +#endif + + return (rt->buffer_bytes - used_bytes); +} + +/* + * clean_abnormal_aec_ptr - + * correct wrong aec pointer + * called in adc interrupt + */ +static inline void clean_abnormal_aec_ptr(void) +{ + /* ADC: app->aec->hw */ + /* DAC: aec->hw->app */ + + struct akpcm *pcm = get_akpcm_ptr(); + struct akpcm_runtime *cptr_rt = pcm->cptr_rt; + struct akpcm_runtime *play_rt = pcm->play_rt; + unsigned int cptr_free_bytes = get_capture_free_bytes(cptr_rt); + unsigned int cptr_prd_bytes = cptr_rt->cfg.period_bytes; + + int dac_working = has_playback_stream(pcm); + int dac_aec_abn = dac_working ? check_playback_aec_ptr() : 0; + int adc_aec_abn = cptr_rt->app_ptr > cptr_rt->aec_ptr; + int adc_notfree_space = + cptr_free_bytes <= (FULL_THRESTHOLD(cptr_rt) + cptr_prd_bytes); + int adc_aec_slow = (adc_notfree_space && !adc_aec_abn) + ? (cptr_rt->aec_ptr - cptr_rt->app_ptr <= cptr_prd_bytes) : 0; + + if ((!pcm->enable_aec)) { + if (adc_aec_abn || adc_aec_slow) { + cptr_rt->aec_ptr = cptr_rt->hw_ptr; + pr_debug("cptr aec_ptr abn"); + } + } else if (dac_aec_abn || adc_aec_abn || adc_aec_slow) { + if (cptr_rt->hw_ptr < cptr_rt->aec_ptr + || play_rt->hw_ptr < play_rt->aec_ptr) { + cptr_rt->aec_ptr = cptr_rt->hw_ptr;// - AEC_FRAME_BYTES; + play_rt->aec_ptr = play_rt->hw_ptr;// - FAR_FRAME_SIZE; + + pr_debug("dac:%d, cpapp:%d, cpaecslow:%d, cphw:%d,"\ + " cpae:%d, pyhw:%d, pyae:%d\n", + dac_aec_abn, adc_aec_abn, adc_aec_slow, + cptr_rt->hw_ptr, cptr_rt->aec_ptr, play_rt->hw_ptr, play_rt->aec_ptr); + } else { + unsigned int cptr_aec_bytes = cptr_rt->hw_ptr - cptr_rt->aec_ptr; + unsigned int play_aec_bytes = play_rt->hw_ptr - play_rt->aec_ptr; + unsigned int off; + + if (cptr_aec_bytes >= (play_aec_bytes >> 1)) { + off = play_aec_bytes >> 1; + } else { + off = cptr_aec_bytes; + } + + if (off > 0) { + cptr_rt->aec_ptr += off; + play_rt->aec_ptr += off << 1; + } else { + cptr_rt->aec_ptr = cptr_rt->hw_ptr;// - AEC_FRAME_BYTES; + play_rt->aec_ptr = play_rt->hw_ptr;// - FAR_FRAME_SIZE; + } + +#if 0 + printk(KERN_ERR "dac:%d, cpapp:%d, cpaecslow:%d, off:%d\n", + dac_aec_abn, adc_aec_abn, adc_aec_slow, off); +#endif + } + } +} + +/* + * LininDet_wq_work - + * worker for linein in/out detection + * + * @work: the wait work + */ +void LininDet_wq_work(struct work_struct *work) +{ + struct akpcm *pcm = container_of(work, struct akpcm, LininWork.work); + + if (ak_gpio_getpin(pcm->linindet_gpio.pin) == + pcm->linindet_gpio.value) { + /* linein is plugged in */ + pcm->capture_dectect |= CAPTURE_DET_LINEIN; + if (pcm->dev_manual) + return; + pcm->cptrdev = CPTRDEV_LI; + set_channel_source(pcm, MIXER_SRC_ADC2, SOURCE_ADC_LINEIN); + pr_info("akpcm %s: linin on.\n", __func__); + } else { + pcm->capture_dectect &= ~CAPTURE_DET_LINEIN; + if (pcm->dev_manual) + return; + pcm->cptrdev = CPTRDEV_MIC; + set_channel_source(pcm, MIXER_SRC_ADC2, SOURCE_ADC_MIC); + pr_info("akpcm %s: linin off.\n", __func__); + } +} + +/* + * linindet_interrupt - + * linein in/out irq handler + * + * @irq: irq number of handler + * @dev_id: private data + */ +irqreturn_t linindet_interrupt(int irq, void *dev_id) +{ + struct akpcm *pcm = dev_id; + + if (ak_gpio_getpin(pcm->linindet_gpio.pin) == + pcm->linindet_gpio.value) { + /* linein is plugged in */ + irq_set_irq_type(pcm->linindet_irq, pcm->lininoff_irqType); + pr_info("akpcm %s: linin on.\n", __func__); + } else { + irq_set_irq_type(pcm->linindet_irq, pcm->lininon_irqType); + pr_info("akpcm %s: linin off.\n", __func__); + } + + schedule_delayed_work(&pcm->LininWork, msecs_to_jiffies(100)); + + return IRQ_HANDLED; +} + +/* + * start_adc - + * start the ADC for capture + * + * @pcm: pointer to pcm device + */ +static void start_adc(struct akpcm *pcm) +{ + struct akpcm_runtime *rt = pcm->cptr_rt; + unsigned int rt_channels = rt->cfg.channels; + /* prerare for HW */ + int ADC2src = pcm->mixer_source[MIXER_SRC_ADC2]; + + ak39_set_adc2_channels(rt_channels); + ak39_codec_adc2_open(); + + switch (ADC2src) { + case SOURCE_ADC_MIC: + ak39_set_mic_power(1); + ak39_set_adc2_in(SOURCE_ADC_MIC); + break; + case SOURCE_ADC_LINEIN: + ak39_set_linein_power(1); + ak39_set_adc2_in(SOURCE_ADC_LINEIN); + break; + default: + break; + } +} + +/* + * set_capture_timestamp - + * set timestamp for capture data + * + * @timestamp: to store the timestamp + * @prd_bytes: bytes of one transfer dma + * @hw_off: hardware pointer to the cache + */ +static void set_capture_timestamp(unsigned long *timestamp, + unsigned int prd_bytes, unsigned int hw_off) +{ + int index = hw_off / prd_bytes; + *(timestamp + index) = get_timestamp(); +} + +/* + * get_hw_off - + * get current hardware pointer + * + * @rt: pointer to runtime + */ +static inline unsigned int get_hw_off(struct akpcm_runtime *rt) +{ + return (rt->hw_ptr % rt->buffer_bytes); +} + +/* + * check_rubbish_data - + * check if have rubbish capture data + * + * @rt: pointer to runtime + */ +static inline int check_rubbish_data(struct akpcm_runtime *rt) +{ + int first_rubbish = 0; + + // TODO: need fix + // adc interrupt is not expected to raise too soon. + // when this happen, the first L2 size of data is rubbish + if (0 == rt->end_time) { + int spend_us = 0; + + rt->end_time = sched_clock(); + spend_us = ak39_spend_us(rt->start_time, rt->end_time); + + /* less than 15ms is considered abnormal */ + if (spend_us < (15 * 1000)) { + ak_pcm_info("adc first int too soon: %dus", spend_us); + first_rubbish = 1; + } + } + + return first_rubbish; +} + +/* + * update_hw_ptr - + * update hardware pointer + * + * @rt: pointer to runtime + */ +static inline void update_hw_ptr(struct akpcm_runtime *rt) +{ + unsigned int prd_bytes = rt->cfg.period_bytes; + + rt->hw_ptr += prd_bytes; + if(rt->hw_ptr >= rt->boundary) + rt->hw_ptr -= rt->boundary; +} + +/* + * sync_dac - + * sync dac startup. + * if adc startup before dac, it is called + * + * @pcm: pointer to pcm devic3 + */ +static inline void sync_dac(struct akpcm *pcm) +{ + struct akpcm_runtime *rt = pcm->cptr_rt; + u_int64_t end = 0; + u_int64_t start = sched_clock(); + + /* to start dac */ + ak_pcm_debug_ex("sync"); + playback_start_dma(pcm); + end = sched_clock(); + /*do not show the message because it is in irq*/ + ak_pcm_debug_ex("adc ~ dac: %d(us)", ak39_spend_us(start, end)); + + update_aec_status(pcm, AEC_STATUS_AEC_WORKING); + wake_up_interruptible(&(pcm->aec_sync_wq)); + + /* aec data starts from next period */ + rt->aec_ptr = rt->hw_ptr; +} + +/* + * capture_to_aec - + * capture data is ready to process aec + * + * @pcm: pointer to pcm devic3 + * @first_intr_rubbish: rubbish data + */ +static void capture_to_aec(struct akpcm *pcm, int first_intr_rubbish) +{ + struct akpcm_runtime *rt = pcm->cptr_rt; + unsigned int prd_bytes = rt->cfg.period_bytes; + + /* skip first intr rubbish */ + if (first_intr_rubbish) { + rt->aec_ptr += prd_bytes; + if(rt->aec_ptr >= rt->boundary) + rt->aec_ptr -= rt->boundary; + } + +#if 0 + /* check if has deposited too many aec data */ + if ((get_aec_bytes(rt) > pcm->cptr_rt->cfg.threshold) + && (0 == pcm->aec_high_prio)) { + /* if so, call on tasklet to handle */ + pcm->aec_high_prio = 1; + wake_up_interruptible(&(pcm->aec_prio_wq)); + } +#endif + + pcm->aec_data_triger = 1; + wake_up_interruptible(&(pcm->aec_wq)); +} + +/* + * handle_adc_buffer_full - + * process the capture buffer is full + * + * @pcm: pointer to pcm devic3 + * @free_bytes: current free capture bytes + */ +static void handle_adc_buffer_full(struct akpcm *pcm, unsigned int free_bytes) +{ + struct akpcm_runtime *rt = pcm->cptr_rt; + unsigned int prd_bytes = rt->cfg.period_bytes; + + /* aec data should not fill the adc buf */ + ak_pcm_assert(get_aec_bytes(rt) <= (rt->buffer_bytes - FULL_THRESTHOLD(rt)));//prd_bytes)); + if (pcm->enable_aec && free_bytes < FULL_THRESTHOLD(rt)) { + ak_pcm_info("warn: adc buffer full, ruin aec potentially"); + ak_pcm_info_ex("free_bytes=%u, prd_bytes=%u, valid_bytes=%u", + free_bytes, prd_bytes, get_valid_bytes(rt)); + } + + /* causion: take care not to conflict with capture_read on app_ptr */ + spin_lock(&rt->ptr_lock); // unnecessary because it is in irq + rt->app_ptr = rt->hw_ptr - (rt->buffer_bytes - FULL_THRESTHOLD(rt) - prd_bytes); + if(rt->app_ptr >= rt->boundary) + rt->app_ptr -= rt->boundary; + + spin_unlock(&rt->ptr_lock); +} + +/* + * capture_isr - + * DMA transfer for capture + * + * @data: private data + */ +void capture_isr(unsigned long data) +{ + struct akpcm *pcm = (struct akpcm *)data; + struct akpcm_runtime *rt = pcm->cptr_rt; + unsigned int prd_bytes = rt->cfg.period_bytes; + unsigned int hw_off = 0; + u8 id = pcm->L2BufID_ADC2; + dma_addr_t phyaddr = rt->dma_addr; + int valid_bytes_changed = 0; + int first_intr_rubbish = 0; + unsigned int free_bytes = 0; + +#if CAPTURE_ISR_DEBUG + ak_pcm_debug("CP+ ptr aec:%u, hw:%u, app:%u", + rt->aec_ptr, rt->hw_ptr, rt->app_ptr); +#endif + + hw_off = get_hw_off(rt); + set_capture_timestamp(pcm->timestamp, prd_bytes, hw_off); + dma_sync_single_for_cpu(pcm->dev, phyaddr+hw_off, prd_bytes, DMA_FROM_DEVICE); + + update_hw_ptr(rt); + hw_off = get_hw_off(rt); + + first_intr_rubbish = check_rubbish_data(rt); + + /* to start dac in case of aec */ + if (AEC_STATUS_SYNC_DAC == pcm->aec_status) { + sync_dac(pcm); + valid_bytes_changed = 1; + } + + if ((AEC_STATUS_AEC_WORKING == pcm->aec_status) + || (AEC_STATUS_NR_WORKING == pcm->aec_status)) { + /* when AEC is working, aec pointer is managed by handle_aec_task. */ + capture_to_aec(pcm, first_intr_rubbish); + } else { + /* AEC is not working */ + rt->aec_ptr = rt->hw_ptr; + valid_bytes_changed = 1; + } + + if (has_capture_stream(pcm)) { + /* capture stream is running */ + free_bytes = get_capture_free_bytes(rt); + /* adc buf is full */ + if (free_bytes <= FULL_THRESTHOLD(rt)) { + handle_adc_buffer_full(pcm, free_bytes); + valid_bytes_changed = 1; + } + + /* next DMA */ + l2_combuf_dma(phyaddr+hw_off, id, prd_bytes, (tL2DMA_DIR)BUF2MEM, 1); + } else { + /* capture stream is not running, stop it */ + ak_pcm_debug_ex("pcm capture stream stopped"); + /* stop DMA */ + clear_bit(IO_BIT_DMA, &(pcm->inflag)); + complete(&(pcm->capt_completion)); + ak_pcm_debug_ex("completion capture"); + } + + clean_abnormal_aec_ptr(); + + if (valid_bytes_changed) + wake_up_interruptible(&(pcm->capt_wq)); + +#if CAPTURE_ISR_DEBUG + ak_pcm_debugx("CP- ptr aec:%u, hw:%u, app:%u", + rt->aec_ptr, rt->hw_ptr, rt->app_ptr); +#endif +} + +/* + * capture_prepare - + * prepare for capture(open ADC2, power on MIC). + * + * @pcm: pointer to pcm device + */ +static int capture_prepare(struct akpcm *pcm) +{ + struct akpcm_runtime *rt = pcm->cptr_rt; + unsigned char *ptr = rt->dma_area; + unsigned int new_size = (rt->cfg.periods * rt->cfg.period_bytes); + int i; + + ak_pcm_func("enter"); + + /* allocate memory for loop-buffer */ + if((ptr) && (new_size != rt->buffer_bytes)){ + capture_pause(pcm); + dma_unmap_single(pcm->dev, rt->dma_addr, rt->buffer_bytes, DMA_FROM_DEVICE); + kfree(ptr); + rt->dma_area = ptr = NULL; + } + + if (!ptr) { + ptr = kmalloc(new_size, GFP_KERNEL); + if(!ptr){ + ak_pcm_debug("capture buffer alloc failed: 0x%x", new_size); + return -ENOMEM; + } else { + dma_addr_t phyaddr; + phyaddr = dma_map_single(pcm->dev, ptr, new_size, DMA_FROM_DEVICE); + if (dma_mapping_error(pcm->dev, phyaddr)) + ak_pcm_err_ex("dma_map_single failed."); + rt->dma_addr = phyaddr; + rt->dma_area = ptr; + ak_pcm_debug("capture buffer alloc: ptr=0x%p, new_size=0x%x", + ptr, new_size); + } + + /* reset some parameters */ + clear_capture_ptr(rt); + rt->buffer_bytes = new_size; + for(i=0; i<64; i++){ + if((new_size>>i) == 0){ + break; + } + } + rt->boundary = new_size << (32-i); + ak_pcm_debug_ex("capture boundary: 0x%x", rt->boundary); + } + + set_bit(STATUS_BIT_PREPARED, &(pcm->capture_sm_flag)); + aec_event_handler(pcm, AEC_EVENT_OPEN); + + ak_pcm_func_exx("OK"); + + return 0; +} + +/* + * capture_start - + * start operation for capture + * + * @pcm: pointer to pcm device + */ +void capture_start(struct akpcm *pcm) +{ + struct akpcm_runtime *rt = pcm->cptr_rt; + unsigned int prd_bytes = rt->cfg.period_bytes; + dma_addr_t phyaddr = rt->dma_addr; + u8 id = pcm->L2BufID_ADC2; + + ak_pcm_func("enter"); + + clear_capture_ptr(rt); + l2_clr_status(id); + /* start tranferring from offset 0 */ + l2_combuf_dma(phyaddr, id, prd_bytes, (tL2DMA_DIR)BUF2MEM, 1); + + start_adc(pcm); + rt->start_time = sched_clock(); + rt->end_time = 0; + + set_bit(IO_BIT_STREAM, &(pcm->inflag)); //set stream running flag + set_bit(IO_BIT_DMA, &(pcm->inflag)); //set DMA running flag +} + +/* + * capture_start - + * resume operation for capture + * + * @pcm: pointer to pcm device + */ +static void capture_resume(struct akpcm *pcm) +{ + if(is_capture_working(pcm)) + return; + + ak_pcm_func("enter, aec_status=%d", pcm->aec_status); + + if (pcm->aec_status == AEC_STATUS_WAIT_ADC) { + /* capture_start is called in playback isr */ + aec_event_handler(pcm, AEC_EVENT_ADC_START); + } else { + /* sync_adc should be in protect of io lock */ + ak_pcm_assert(pcm->aec_status != AEC_STATUS_SYNC_ADC); + + capture_start(pcm); + aec_event_handler(pcm, AEC_EVENT_ADC_START); + } +} + +/* + * capture_start - + * pause operation for capture + * + * @pcm: pointer to pcm device + */ +void capture_pause(struct akpcm *pcm) +{ + /* working until start capture DMA */ + if (!is_capture_working(pcm)) { + ak_pcm_debug_ex("capture is not working now"); + return; + } + + ak_pcm_func("enter"); + + /* clear capture stream running flag. STOP when current DMA finish */ + clear_bit(IO_BIT_STREAM, &(pcm->inflag)); + if (is_capture_working(pcm)) { + wait_for_completion(&(pcm->capt_completion)); + ak_pcm_info_ex("wait capture completion ok"); + } + + ak39_codec_adc2_close(); + + /* nowadays, aec is disabled when adc is stopped */ + aec_event_handler(pcm, AEC_EVENT_AEC_STOP); + aec_set_param(pcm, pcm->app_flag); + aec_event_handler(pcm, AEC_EVENT_ADC_STOP); +} + +/* + * set_capture_dev - + * set device + * + * @pcm: pointer to pcm device + * @arg: argument for device + */ +static int set_capture_dev(struct akpcm *pcm, unsigned long arg) +{ + unsigned int value; + int ret = get_user(value, (int *)arg); + if (ret) { + ret = -ENOMEM; + goto set_dev_end; + } + if((pcm->cptrdev & (~CPTRDEV_MSK)) != 0){ + ret = -EINVAL; + goto set_dev_end; + } + + + switch (value) { + case CPTRDEV_AUTO: + ak_pcm_debug_ex("IOC_SET_DEV, value=%d, auto", value); + pcm->dev_manual = 0; + if (pcm->capture_dectect & CAPTURE_DET_LINEIN) { + pcm->cptrdev = CPTRDEV_LI; + change_channel_source(pcm, MIXER_SRC_ADC2, SOURCE_ADC_LINEIN); + } else { + pcm->cptrdev = CPTRDEV_MIC; + change_channel_source(pcm, MIXER_SRC_ADC2, SOURCE_ADC_MIC); + } + break; + case CPTRDEV_MIC: + case CPTRDEV_LI: + pcm->dev_manual = 1; + if (value == pcm->cptrdev){ + break; + } + pcm->cptrdev = value; + if (value == CPTRDEV_LI) { + ak_pcm_debug_ex("IOC_SET_DEV, value=%d, linein", value); + change_channel_source(pcm, MIXER_SRC_ADC2, SOURCE_ADC_LINEIN); + } else { + ak_pcm_debug_ex("IOC_SET_DEV, value=%d, mic", value); + change_channel_source(pcm, MIXER_SRC_ADC2, SOURCE_ADC_MIC); + } + break; + default: + ak_pcm_debug("akpcm set capture device(s) no defined, value=%d", value); + ret = -EINVAL; + break; + } + +set_dev_end: + return ret; +} + +/* + * set_capture_gain - + * set analog gain + * + * @pcm: pointer to pcm device + * @arg: argument for gain + */ +static int set_capture_gain(struct akpcm *pcm, unsigned long arg) +{ + unsigned int addr; + unsigned int value; + int ret = 0; + + if (pcm->cptrdev == CPTRDEV_MIC) { + addr = MIXER_VOL_MIC; + ret = get_user(value, (int *)arg); + if (ret) + goto set_gain_end; + + if (value < MIC_GAIN_MIN) { + value = MIC_GAIN_MIN; + } else if (value > MIC_GAIN_MAX) { + value = MIC_GAIN_MAX; + } + ak_pcm_debug_ex("IOC_SET_GAIN, set MIC gain: %d", value); + + if (pcm->mixer_volume[addr] != value) { + akpcm_set_mic_gain(value); + } + pcm->mixer_volume[addr] = value; + }else{ + addr = MIXER_VOL_LI; + ret = get_user(value, (int *)arg); + if (ret) + goto set_gain_end; + + if (value < LINEIN_GAIN_MIN) { + value = LINEIN_GAIN_MIN; + } else if (value > LINEIN_GAIN_MAX) { + value = LINEIN_GAIN_MAX; + } + ak_pcm_debug_ex("IOC_SET_GAIN, set LI gain: %d", value); + + if (pcm->mixer_volume[addr] != value) { + ak39_set_linein_gain(value); + } + pcm->mixer_volume[addr] = value; + } + +set_gain_end: + return ret; +} + +/* + * set_capture_param - + * set parameters + * + * @pcm: pointer to pcm device + * @arg: argument for parameters + */ +static int set_capture_param(struct akpcm *pcm, unsigned long arg) +{ + int ret = 0; + struct akpcm_runtime *rt = pcm->cptr_rt; + struct akpcm_pars *rt_cfg = &rt->cfg; + + if (copy_from_user(rt_cfg, (void __user *)arg, sizeof(struct akpcm_pars))) { + ret = -EFAULT; + goto set_param_end; + } + if (rt_cfg->periods > MAX_TIMESTAMP_CNT) { + ak_pcm_info("periods too large"); + ret = -EINVAL; + goto set_param_end; + } + + if (rt_cfg->threshold <= rt_cfg->period_bytes) + rt_cfg->threshold = rt_cfg->period_bytes * 3; + + pcm->capt_hw->actual_rate = ak39_codec_set_adc2_samplerate(rt->cfg.rate); + +set_param_end: + return ret; +} + +/* + * set_capture_source - + * set source + * + * @pcm: pointer to pcm device + * @arg: argument for source + */ +static int set_capture_source(struct akpcm *pcm, unsigned long arg) +{ + unsigned int value = 0; + unsigned int addr = MIXER_SRC_ADC2; + int ret = get_user(value, (int *)arg); + if (ret) + return ret; + + ak_pcm_debug_ex("IOC_SET_SOURCES value=%d", value); + if (value < SIGNAL_ADC_SRC_MUTE) { + value = SIGNAL_ADC_SRC_MUTE; + } else if (value > SIGNAL_ADC_SRC_MAX){ + value = SIGNAL_ADC_SRC_MAX; + } + change_channel_source(pcm, addr, value); + + return 0; +} + +/* + * set_nr_max - + * set max nr + * + * @pcm: pointer to pcm device + * @arg: argument for max nr + */ +static int set_nr_max(struct akpcm *pcm, unsigned long arg) +{ + unsigned int enable = 0; + int ret = get_user(enable, (int *)arg); + + if (!ret) { + ak_pcm_debug("IOC_SET_NR_MAX enable=%d", enable); + if (pcm->aec_filter && pcm->cptr_rt->enable_agc) { + aec_set_nr_max(pcm, enable); + } + } + + return ret; +} + +/* + * set_ad_eq_attr - + * set eq of adc + * + * @pcm: pointer to pcm device + * @arg: argument for eq + */ +static int set_ad_eq_attr(struct akpcm *pcm, unsigned long arg) +{ + T_S32 temp[EQ_ARRAY_NUMBER]; + if (copy_from_user(temp, (void __user *)arg, sizeof(T_S32) * EQ_ARRAY_NUMBER)) { + ak_pcm_info_ex("copy from user failed"); + return -EFAULT; + } + + return aec_set_eq_params(pcm, temp); +} + +/* + * get_echo_attr - + * get echo attr + * + * @pcm: pointer to pcm device + * @aec_param: the buffer for read to + */ +static int get_echo_attr(struct akpcm *pcm, T_AEC_PARAMS *aec_param) +{ + return aec_get_echo_params(pcm, (T_U8 *)aec_param); +} + +/* + * set_echo_attr - + * set echo attr + * + * @pcm: pointer to pcm device + * @aec_param: the buffer to set + */ +static int set_echo_attr(struct akpcm *pcm, T_AEC_PARAMS *aec_param) +{ + return aec_set_echo_params(pcm, (T_U8 *)aec_param); +} + +/* + * capture_ioctl - + * capture device file ops: ioctl + * + * @flip: capture device file + * @cmd: command + * @arg; argument for the command + */ +long capture_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct akpcm *pcm = get_akpcm_ptr(); + struct akpcm_runtime *rt = pcm->cptr_rt; + struct akpcm_features *feats; + struct akpcm_pars *rt_cfg; + unsigned int addr; + unsigned int value; + int ret = 0; + unsigned long ul; + T_AEC_PARAMS aec_param; + + lock_capture_io(pcm); + + /* commands */ + switch (cmd) { + case IOC_PREPARE: + ak_pcm_debug_ex("IOC_PREPARE"); + ret = capture_prepare(pcm); + break; + case IOC_SET_AEC: + if (copy_from_user(&value, (void __user *)arg, sizeof(value))) { + ret = -EFAULT; + goto cap_ioc_end; + } + + ak_pcm_debug_ex("IOC_SET_AEC value=%d", value); + if (value) { + set_bit(ECHO_BIT_AEC, &(pcm->app_flag)); + } else { + clear_bit(ECHO_BIT_AEC, &(pcm->app_flag)); + } + + if (is_playback_working(pcm)) { + ret = playback_pause(pcm); + } else { + ret = aec_set_param(pcm, pcm->app_flag); + } + break; + case IOC_RESUME: + ak_pcm_debug_ex("IOC_RESUME"); + if (copy_from_user(&value, (void __user *)arg, sizeof(value))) { + ret = -EFAULT; + goto cap_ioc_end; + } + + ret = aec_set_param(pcm, value); + capture_resume(pcm); + break; + case IOC_PAUSE: + ak_pcm_debug_ex("IOC_PAUSE"); + capture_pause(pcm); + break; + case IOC_RSTBUF: + ak_pcm_debug_ex("IOC_RSTBUF, reset buf"); + /* reset buffer */ + clear_capture_ptr(rt); + break; + /* features */ + case IOC_GET_FEATS: + ak_pcm_debug_ex("IOC_GET_FEATS"); + feats = pcm->capt_hw; + if (copy_to_user((void __user *)arg, feats, + sizeof(struct akpcm_features))) { + ret = -EFAULT; + } + break; + /* configures */ + case IOC_GET_PARS: + ak_pcm_debug_ex("IOC_GET_PARS"); + rt_cfg = &rt->cfg; + if (copy_to_user((void __user *)arg, rt_cfg, + sizeof(struct akpcm_pars))) { + ret = -EFAULT; + } + break; + case IOC_SET_PARS: + ak_pcm_debug_ex("IOC_SET_PARS"); + set_capture_param(pcm, arg); + break; + /* ---------- capture devices ------------------------------------ */ + case IOC_GET_DEV: + value = pcm->cptrdev; + ret = put_user(value, (int *)arg); + break; + case IOC_SET_DEV: + ret = set_capture_dev(pcm, arg); + break; + /* ---------- sources ------------------------------------ */ + case IOC_GET_SOURCES: + addr = MIXER_SRC_ADC2; + value = pcm->mixer_source[addr]; + ret = put_user(value, (int *)arg); + break; + case IOC_SET_SOURCES: + set_capture_source(pcm, arg); + break; + /* ---------- GAIN ------------------------------------ */ + case IOC_GET_GAIN: + if (pcm->cptrdev == CPTRDEV_MIC) { + addr = MIXER_VOL_MIC; + }else{ + addr = MIXER_VOL_LI; + } + value = pcm->mixer_volume[addr]; + ret = put_user(value, (int *)arg); + break; + case IOC_SET_GAIN: + set_capture_gain(pcm, arg); + break; + case IOC_GETTIMER: + ul = rt->ts; + if (copy_to_user((void __user *)arg, &ul, sizeof(unsigned long))) { + ret = -EFAULT; + } + break; + case IOC_GET_STATUS: + value = is_capture_working(pcm); + ret = put_user(value, (unsigned int *)arg); + break; + case IOC_GET_DATA_LENGTH: + value = get_valid_bytes(rt); + if (copy_to_user((void __user *)arg, &value, sizeof(unsigned int))) { + ret = -EFAULT; + } + break; + case IOC_SET_NR_MAX: + set_nr_max(pcm, arg); + break; + case IOC_SET_AEC_DUMP: + get_user(value, (int *)arg); + aec_set_dump_type(pcm, value); + break; + case IOC_SET_AD_EQ: + ak_pcm_debug_ex("IOC_SET_AD_EQ"); + get_user(value, (int *)arg); + aec_enable_eq(pcm, value); + break; + case IOC_SET_AD_EQ_ATTR: + ak_pcm_debug_ex("IOC_SET_AD_EQ_ATTR"); + ret = set_ad_eq_attr(pcm, arg); + break; + case IOC_GET_ECHO_PARAM: + ret = get_echo_attr(pcm, &aec_param); + if (copy_to_user((void __user *)arg, &aec_param, sizeof(T_AEC_PARAMS))) { + ret = -EFAULT; + } + break; + case IOC_SET_ECHO_PARAM: + if (copy_from_user(&aec_param, (void __user *)arg, sizeof(T_AEC_PARAMS))) { + ret = -EFAULT; + goto cap_ioc_end; + } + ret = set_echo_attr(pcm, &aec_param); + break; + default: + ret = -ENOTTY; + break; + } + +cap_ioc_end: + unlock_capture_io(pcm); + + return ret; +} + +/* + * capture_read - + * capture device file ops: read + * + * @flip: capture device file + * @buf: buffer to store to + * @count: bytes of read + * @f_pos: current position of read + */ +ssize_t capture_read(struct file *filp, char __user *buf, size_t count, + loff_t *f_pos) +{ + struct akpcm *pcm = get_akpcm_ptr(); + struct akpcm_runtime *rt = pcm->cptr_rt; + unsigned int buf_bytes = rt->buffer_bytes; + unsigned int prd_bytes = rt->cfg.period_bytes; + unsigned int app_pos = rt->app_ptr; + unsigned int pre_app_pos = rt->app_ptr; + unsigned int app_off = (app_pos % buf_bytes); + unsigned long ts; + + if (check_capture_status(pcm)) { + return -EPERM; + } + +#if CAPTURE_READ_DEBUG + ak_pcm_func("R+"); + ak_pcm_debug("ptr hw:%u, app:%u, aec: %u", + rt->hw_ptr, rt->app_ptr, rt->aec_ptr); +#endif + + /* calculate valid_data & free_space */ + if (wait_event_interruptible(pcm->capt_wq, + (get_valid_bytes(rt) >= count)) < 0) { + return -ERESTARTSYS; + } + + /* modify under protection */ + app_pos = rt->app_ptr; + pre_app_pos = rt->app_ptr; + app_off = (app_pos % buf_bytes); + + if (put_user_data(rt, buf, count)) { + return -EFAULT; + } + + app_pos += count; + if(app_pos >= rt->boundary) + app_pos -= rt->boundary; + + update_capture_app_ptr(rt, pre_app_pos, app_pos); + + ts = pcm->timestamp[app_off / prd_bytes]; + ts += 1000 * (app_off % prd_bytes) / (pcm->capt_hw->actual_rate * 2); + pcm->cptr_rt->ts = ts; + +#if CAPTURE_READ_DEBUG + ak_pcm_debug("app_pos: %u, valid_bytes: %u, count: %u", + app_pos, get_valid_bytes(rt), count); + ak_pcm_func_exx("R-"); +#endif + + return count; +} + +/* + * capture_open - + * open capture + * + * @inode: pointer to capturer device node + * @filp: capturer device file + */ +int capture_open(struct inode *inode, struct file *filp) +{ + struct akpcm *pcm = get_akpcm_ptr(); + struct akpcm_runtime *rt = pcm->cptr_rt; + + ak_pcm_debug_ex("rt=0x%x", (int)rt); + if (is_capture_opened(pcm)) { + ak_pcm_debug_ex("ERR! capture device is already open!"); + return -EPERM; /* Operation not permitted */ + } + + /* allocate an L2 buffer */ + if (BUF_NULL == pcm->L2BufID_ADC2) { + pcm->L2BufID_ADC2 = l2_alloc((l2_device_t)ADDR_ADC); + if (BUF_NULL == pcm->L2BufID_ADC2){ + ak_pcm_debug_ex("Error! alloc L2 buffer for capture!"); + return -ENOMEM; + } + + ak_pcm_debug_ex("L2 buffer ID for capture: %d", pcm->L2BufID_ADC2); + } + + /* allocate memory for akpcm_runtime */ + rt = kmalloc(sizeof(struct akpcm_runtime), GFP_KERNEL); + if (!rt) { + l2_free((l2_device_t)ADDR_ADC); + pcm->L2BufID_ADC2 = BUF_NULL; + return -ENOMEM; + } + + pcm->cptr_rt = rt; + ak_pcm_debug("capture rt allocate: 0x%x", (int)(pcm->cptr_rt)); + + /* others */ + rt->dma_area = NULL; + rt->dma_addr = 0; + clear_capture_ptr(rt); + spin_lock_init(&rt->ptr_lock); + mutex_init(&rt->lock); + rt->cfg.rate = 16000; + rt->cfg.channels = 2; + rt->cfg.sample_bits = 16; + rt->cfg.period_bytes = 16*1024; + rt->cfg.periods = 16; + rt->buffer_bytes = (rt->cfg.periods * rt->cfg.period_bytes); + rt->enable_nr = 0; + rt->enable_agc = 0; + /* + pcm->mixer_source[MIXER_SRC_ADC2] = SOURCE_MIC; + pcm->cptrdev = CPTRDEV_MIC; + */ + + pcm->capt_hw = &akpcm_capture_hardware; + init_completion(&(pcm->capt_completion)); + + l2_set_dma_callback(pcm->L2BufID_ADC2, capture_isr, (unsigned long)pcm); + set_bit(STATUS_BIT_OPENED, &(pcm->capture_sm_flag)); + ak_pcm_debug_exx("OK"); + + return 0; +} + +/* + * capture_close - + * close capture + * + * @inode: pointer to capturer device node + * @filp: capturer device file + */ +int capture_close(struct inode *inode, struct file *filp) +{ + struct akpcm *pcm = get_akpcm_ptr(); + struct akpcm_runtime *rt = pcm->cptr_rt; + int hpsrc=0, losrc=0, ADC2src=0; + + ak_pcm_func("enter"); + + if (!is_capture_opened(pcm)) { + ak_pcm_debug_ex("ERR! capture device is not open!"); + return -EPERM; /* Operation not permitted */ + } + + lock_capture_io(pcm); + + capture_pause(pcm); + + hpsrc = pcm->mixer_source[MIXER_SRC_HP]; + losrc = pcm->mixer_source[MIXER_SRC_LO]; + ADC2src = pcm->mixer_source[MIXER_SRC_ADC2]; + if (SOURCE_ADC_MIC & ADC2src) { + /* stop process when MIC is use as a capture device */ + /* ??? only MIC will be use as a capture device ??? */ + /* ??? here ALL devices is checked. NEED TO BE FIXED ??? */ + /* ??? only make sure WORKING devices is not using it ??? */ + /* ??? don't check NOT-WORKING devices ??? */ + if ((0==(hpsrc&SOURCE_MIC)) && (0==(losrc&SOURCE_MIC))) { + /* power off MIC, if no other device use it */ + ak39_set_mic_power(0); + } + ak39_set_adc2_in(0); + /* ??? here ALL devices is checked. NEED TO BE FIXED ??? */ + /* ??? only make sure WORKING devices is not using it ??? */ + /* ??? don't check NOT-WORKING devices ??? */ + if ((0==hpsrc) && (!((SOURCE_DAC|SOURCE_MIC) & losrc))) { + /* power off VCM_REF, if no other device use it */ + /* ??? onlye make sure WORKING devices is not using it ??? */ + /* ??? don't check NOT-WORKING devices ??? */ + //ak39_set_vcm_ref_power(0); + } + } + + /* The following codes is added by panqihe 2014-06-12 */ + if (SOURCE_ADC_LINEIN & ADC2src) { + /* stop process when LINE_IN is use as a capture device */ + // TODO ... + } + /* free loop-buffer memory */ + clear_bit(STATUS_BIT_PREPARED, &(pcm->capture_sm_flag)); + + if (rt && rt->dma_area) { + dma_unmap_single(pcm->dev, rt->dma_addr, rt->buffer_bytes, DMA_FROM_DEVICE); + kfree(rt->dma_area); + rt->dma_area = NULL; + } + + /* free L2 buffers */ + if (BUF_NULL != pcm->L2BufID_ADC2) { + l2_free((l2_device_t)ADDR_ADC); + pcm->L2BufID_ADC2 = BUF_NULL; + } + + unlock_capture_io(pcm); + + if (rt) { + kfree(rt); + pcm->cptr_rt = NULL; + } + clear_bit(STATUS_BIT_OPENED, &(pcm->capture_sm_flag)); + pcm->app_flag = 0; + + ak_pcm_debug_exx("OK"); + + return 0; +} diff --git a/drivers/misc/akpcm/capture.h b/drivers/misc/akpcm/capture.h new file mode 100644 index 00000000..8d927f8e --- /dev/null +++ b/drivers/misc/akpcm/capture.h @@ -0,0 +1,44 @@ +#ifndef _AKPCM_CAPTURE_H_ +#define _AKPCM_CAPTURE_H_ + +#include + +static inline int is_capture_opened(struct akpcm *pcm) +{ + return test_bit(STATUS_BIT_OPENED, &(pcm->capture_sm_flag)); +} + +static inline int is_capture_ready(struct akpcm *pcm) +{ + return test_bit(STATUS_BIT_PREPARED, &(pcm->capture_sm_flag)); +} + +static inline int has_capture_stream(struct akpcm *pcm) +{ + return test_bit(IO_BIT_STREAM, &(pcm->inflag)); +} + +static inline int is_capture_working(struct akpcm *pcm) +{ + return test_bit(IO_BIT_DMA, &(pcm->inflag)); +} + +void LininDet_wq_work(struct work_struct *work); +irqreturn_t linindet_interrupt(int irq, void *dev_id); + +/** + * @brief resume operation for capture + * @author panqihe 2014-06-12 + * @modify + * @return void + */ +void capture_start(struct akpcm *pcm); + +void capture_pause(struct akpcm *pcm); +long capture_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +ssize_t capture_read(struct file *filp, char __user *buf, size_t count, + loff_t*f_pos); +int capture_open(struct inode *inode, struct file *filp); +int capture_close(struct inode *inode, struct file *filp); + +#endif diff --git a/drivers/misc/akpcm/playback.c b/drivers/misc/akpcm/playback.c new file mode 100644 index 00000000..f3afaf9c --- /dev/null +++ b/drivers/misc/akpcm/playback.c @@ -0,0 +1,1816 @@ +/* + * pcm for anyka chip + * Copyright (c) by Anyka, Inc. + * Create by panqihe 2014-06-09 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aec.h" +#include "playback.h" +#include "capture.h" + +#define PLAYBACK_WRITE_DEBUG (0) +#define PLAYBACK_ISR_DEBUG (0) + +/* + * FILL_PTHRESHOLD: minimum bytes in buffer + * WRITE_PTHRESHOLD: minimum empty bytes space to write + */ +#define FILL_PTHRESHOLD(rt) (rt->cfg.period_bytes) +#define WRITE_PTHRESHOLD(rt) (FILL_PTHRESHOLD(rt) + rt->cfg.period_bytes) + +static unsigned int dac_app_data_end = 0; +static unsigned int dac_fill_times = 0; +static unsigned int dac_user_data_playing = 0; +static unsigned long long dac_elapse = 0; + +/*default playback parameters*/ +static struct akpcm_features akpcm_play_hardware = { + .sample_bits = AKPCM_SMPL_BIT_U16, + .rates = USE_RATE, + .rate_min = USE_RATE_MIN, + .rate_max = USE_RATE_MAX, + .channels_min = 2, + .channels_max = 2, + .period_bytes_min = PLAY_PERIOD_BYTES_MIN, + .period_bytes_max = PLAY_PERIOD_BYTES_MAX, + .periods_min = PLAY_PERIODS_MIN, + .periods_max = PLAY_PERIODS_MAX, + .hp_gain_max = HEADPHONE_GAIN_MAX, + .li_gain_max = LINEIN_GAIN_MAX, + .mic_gain_max = MIC_GAIN_MAX, + .play_dev = PLAYDEV_MSK, /* HP or(and) LO */ + .cptr_dev = CPTRDEV_MSK, /* MIC or(and) LI */ +}; + +/* + * lock_playback_io - + * lock playback device + * + * @pcm: pointer to pcm device + */ +static inline void lock_playback_io(struct akpcm *pcm) +{ + mutex_lock(&pcm->io_lock); +} + +/* + * unlock_playback_io - + * unlock playback device + * + * @pcm: pointer to pcm device + */ +static inline void unlock_playback_io(struct akpcm *pcm) +{ + mutex_unlock(&pcm->io_lock); +} + +/* + * clear_playback_ptr - + * to empty playbakc buffer + * + * @rt: pointer to runtime + */ +static inline void clear_playback_ptr(struct akpcm_runtime *rt) +{ + /* ADC: app->aec->hw */ + /* DAC: aec->hw->app */ + + unsigned int app_pos = rt->app_ptr; + unsigned int aec_pos = rt->aec_ptr; + unsigned int hw_pos = rt->hw_ptr; + unsigned int prd_bytes = rt->cfg.period_bytes; + unsigned int rsv_bytes = prd_bytes; + struct akpcm *pcm = get_akpcm_ptr(); + + if (!in_interrupt()) { + //printk(KERN_ERR "CPP1\n"); + spin_lock_irq(&rt->ptr_lock); + } + + if (!has_playback_stream(pcm)) { + rt->hw_ptr = 0; + rt->app_ptr = 0; + rt->aec_ptr = 0; + } else if (app_pos != aec_pos) { + app_pos = hw_pos + rsv_bytes; + if(app_pos >= rt->boundary) + app_pos -= rt->boundary; + + rt->app_ptr = app_pos; + } + + + if (!in_interrupt()) { + //printk(KERN_ERR "CPP2\n"); + spin_unlock_irq(&rt->ptr_lock); + } + + //printk(KERN_ERR "bd:%u,aec:%u,hw:%u,app:%u\n",rt->boundary,rt->aec_ptr,rt->hw_ptr,rt->app_ptr); +} + +/* + * frames_to_bytes - + * one frames data transfer to bytes size + * + * @rt: pointer to runtime + * @size: bytes size of one frame + */ +static inline unsigned int frames_to_bytes(struct akpcm_runtime *rt, + unsigned int size) +{ + unsigned int frame_bits = rt->cfg.channels * rt->cfg.sample_bits; + return (size * frame_bits / 8); +} + +/* + * check_playback_status - + * check playback current status + * + * @pcm: pointer to pcm device + */ +static inline int check_playback_status(struct akpcm *pcm) +{ + int ret = 0; + unsigned long flag = (pcm->playback_sm_flag & 3); + + switch (flag) { + case 0: + ak_pcm_err("plackback device isn't open!"); + ret = -EPERM;/* Operation not permitted */ + break; + case 1: + ak_pcm_err("plackback opened but not prepare!"); + ret = -EPERM;/* Operation not permitted */ + break; + case 2: + ak_pcm_err("plackback just prepared!"); + ret = -EPERM;/* Operation not permitted */ + break; + case 3: + break; + default: + ak_pcm_err("plackback unknow status!"); + ret = -EPERM;/* Operation not permitted */ + break; + } + + return ret; +} + +/* + * get_user_data - + * get playback data to here from user space + * + * @rt: pointer to runtime + * @buf: pointer to user space buffer + * @count: bytes of buffer + */ +static int get_user_data(struct akpcm *pcm, const char __user *buf, + size_t count, unsigned int *handle_pos) +{ + struct akpcm_runtime *rt = pcm->play_rt; + unsigned int buf_bytes = rt->buffer_bytes; + unsigned int app_pos = *handle_pos; + unsigned int app_off = (app_pos % buf_bytes); + unsigned char *vaddr = rt->dma_area; + int frame_size = FAR_FRAME_SIZE; + size_t left = count; + + while (left > 0) { + size_t copy_size = frame_size - pcm->far_frame_off; + copy_size = min(copy_size, left); + + if (copy_from_user(pcm->far_frame_buf + pcm->far_frame_off, + buf, copy_size)){ + ak_pcm_err_ex("akpcm write all error"); + return -EFAULT; + } + + left -= copy_size; + buf += copy_size; + pcm->far_frame_off += copy_size; + + if (frame_size == pcm->far_frame_off) { + void *playback_buf = (void *)pcm->far_frame_buf; + + /* + * NR/AGC far frame + * a frame is ready, then send data to noise reduction + */ + if (aec_need_playback(pcm) || rt->enable_nr) { + /* convert to mono format */ + stereo_to_mono(playback_buf, playback_buf, frame_size); + + if (rt->enable_nr) { + T_AEC_BUF *aec_bufs = &pcm->far_bufs; + T_S32 ret_size; + + /* do NR and AGC */ + aec_bufs->buf_near = playback_buf; + /* same addr to avoid copy in aeclib */ + aec_bufs->buf_out = playback_buf; + aec_bufs->buf_far = AK_NULL; + ret_size = AECLib_Control(pcm->far_filter, aec_bufs); + if (ret_size != AEC_FRAME_BYTES) + ak_pcm_debug("far processing failed: %ld", ret_size); + } + + /* far's power should not too big to ruin aec */ + if (aec_need_playback(pcm)) + aec_set_da_volume(pcm, (T_S16*)playback_buf, AEC_FRAME_BYTES/2); + + /* covert back to stereo format */ + mono_to_stereo(vaddr+app_off, playback_buf, frame_size); + } else { + memcpy(vaddr+app_off, playback_buf, frame_size); + } + + app_pos += frame_size; + if(app_pos >= rt->boundary) + app_pos -= rt->boundary; + + app_off = (app_pos % buf_bytes); + pcm->far_frame_off = 0; + } + } + + *handle_pos = app_pos; + return 0; +} + +/* + * check_playback_aec_ptr - + * check pointers of playback buffer + */ +int check_playback_aec_ptr(void) +{ + struct akpcm *pcm = get_akpcm_ptr(); + struct akpcm_runtime *rt = pcm->play_rt; + + if (!rt) + return 0; + + if ((rt->app_ptr - rt->aec_ptr) > (rt->buffer_bytes - WRITE_PTHRESHOLD(rt))) { + //printk(KERN_ERR "%u %u, buffer:%u,prd:%u",rt->app_ptr - rt->aec_ptr, rt->app_ptr - rt->hw_ptr, rt->buffer_bytes, rt->cfg.period_bytes); + return 1; + } + return 0; +} + +/* + * udpate_playback_app_ptr - + * update playback application ptr when application had write in. + * + * @pcm: pointer to pcm device + * @app_pos_save: old app ptr + * @app_pos: new app ptr + */ +static inline void update_playback_app_ptr(struct akpcm *pcm, + unsigned int app_pos_save, unsigned int app_pos) +{ + struct akpcm_runtime *rt = pcm->play_rt; + + spin_lock_irq(&rt->ptr_lock); + if (aec_need_playback(pcm)) { + if (app_pos_save == rt->app_ptr) { + rt->app_ptr = app_pos; + } else { + /* app_ptr was updated by playback_isr */ + ak_pcm_info("V"); + rt->app_ptr = max(rt->app_ptr, app_pos); + } + } else { + rt->app_ptr = app_pos; + } + + if (!is_capture_working(pcm) + && check_playback_aec_ptr()) + rt->aec_ptr = rt->hw_ptr;// - FAR_FRAME_SIZE; + + spin_unlock_irq(&rt->ptr_lock); +} + +/* + * get_pend_bytes - + * playback helper routine + * get pend bytes to play + * get how many data sent to DAC in playback buffer + * + * @rt: pointer to runtime + */ +static inline unsigned int get_pend_bytes(struct akpcm_runtime *rt) +{ + unsigned int pend_bytes; + unsigned int hw_pos = rt->hw_ptr; + unsigned int app_pos = rt->app_ptr; + unsigned int boundary = rt->boundary; + + /* calculate pend_data & free_space */ + if (app_pos >= hw_pos) { + pend_bytes = app_pos - hw_pos; + } else { + pend_bytes = boundary - hw_pos + app_pos; + } + + if (pend_bytes > rt->buffer_bytes) { + ak_pcm_info_ex("pend_bytes:0x%x, buffer_bytes: 0x%x", + pend_bytes, rt->buffer_bytes); + ak_pcm_debug_ex("pos hw=%u, app=%u", hw_pos, app_pos); + } + + return pend_bytes; +} + +/* + * get_playback_free_bytes - + * playback helper routine + * get how many free room in playback buffer + * + * @rt: pointer to runtime + */ +static inline unsigned int get_playback_free_bytes(struct akpcm_runtime *rt) +{ + unsigned int used_bytes; + unsigned int aec_pos = rt->aec_ptr; + unsigned int app_pos = rt->app_ptr; + unsigned int boundary = rt->boundary;//0; + + /* calculate pend_data & free_space */ + if (app_pos >= aec_pos) { + used_bytes = app_pos - aec_pos; + } else { + used_bytes = boundary - aec_pos + app_pos; + } + +#ifdef DEBUG_BYTES + if (rt->buffer_bytes < used_bytes) { + ak_pcm_err_ex("buffer_bytes:%u, used_bytes:%u", + rt->buffer_bytes, used_bytes); + ak_pcm_err_ex("aec_pos:%u, app_pos:%u, hw_ptr:%u", + aec_pos, app_pos, rt->hw_ptr); + } +#endif + + return (rt->buffer_bytes - used_bytes); +} + +/* + * soft_fadeout - + * software fadeout + * + * @ptr: buffer + * @num: byte of buffer + */ +static int soft_fadeout(unsigned short *ptr, int num) +{ +#define MAX_LEVEL (0x7FFF) + unsigned short data = *(ptr - 1); //TODO: sample is 16 bits + int step; + int i; + + if (num <= 0) + return 0; + + if (data > MAX_LEVEL) { + step = (0xFFFF - data) / num; + step = -step; + + if (!step) + step = -1; + for (i = 0; i < num; i++) { + int tmp = *(ptr - 1); + if (tmp - step <= 0xFFFF) { + *ptr = tmp - step; + *(ptr + 1) = tmp - step; + } else { + *ptr = 0xFFFF; + *(ptr + 1) = 0xFFFF; + } + ptr += 2; + } + } else { + step = data / num; + if (!step) + step = 1; + for (i = 0; i < num; i++) { + int tmp = *(ptr - 1); + if (tmp - step >= 0) { + *ptr = tmp - step; + *(ptr + 1) = tmp - step; + } else { + *ptr = 0; + *(ptr + 1) = 0; + } + ptr += 2; + } + } + + return num; +} + +/* + * check_rubbish_data - + * check if have rubbish playback data + * + * @rt: pointer to runtime + */ +static int check_rubbish_data(struct akpcm_runtime *rt) +{ + int first_rubbish = 0; + + // TODO: need fix + // dac interrupt is not expected to raise too soon. + // when this happen, the first L2 size of data is rubbish + if (0 == rt->end_time) { + int spend_us = 0; + + rt->end_time = sched_clock(); + spend_us = ak39_spend_us(rt->start_time, rt->end_time); + + /* less than 10ms is considered abnormal */ + if (spend_us < (10*1000)) { + ak_pcm_info("dac first int too soon: %dus", spend_us); + first_rubbish = 1; + } + } + + return first_rubbish; +} + +/* + * update_hw_ptr - + * update hardware pointer + * + * @rt: pointer to runtime + */ +static inline void update_hw_ptr(struct akpcm_runtime *rt) +{ + unsigned int prd_bytes = rt->cfg.period_bytes; + + rt->hw_ptr += prd_bytes; + if(rt->hw_ptr >= rt->boundary) + rt->hw_ptr -= rt->boundary; +} + +/* + * calc_dac_elapse - + * calc elapse time to play + * + * @pcm: pointer to pcm device + */ +static inline void calc_dac_elapse(struct akpcm *pcm) +{ + struct akpcm_runtime *rt = pcm->play_rt; + unsigned int prd_bytes = rt->cfg.period_bytes; + unsigned int actual_rate = pcm->play_hw->actual_rate; + + dac_elapse += (prd_bytes / frames_to_bytes(rt, 1) * 1000000 / actual_rate); +} + +/* + * update_playback_stream - + * start next dma transfer to play + * + * @pcm: pointer to pcm device + */ +static inline void update_playback_stream(struct akpcm *pcm) +{ + struct akpcm_runtime *rt = pcm->play_rt; + unsigned char *vaddr = rt->dma_area; + dma_addr_t phyaddr = rt->dma_addr; + unsigned int prd_bytes = rt->cfg.period_bytes; + unsigned int buf_bytes = rt->buffer_bytes; + u8 id = pcm->L2BufID_DAC; + unsigned int hw_off = (rt->hw_ptr % buf_bytes); + + unsigned int pend_bytes = get_pend_bytes(rt); + if (pend_bytes < prd_bytes) { + unsigned short *ptr = (unsigned short *)(vaddr + hw_off + pend_bytes); + int num = (prd_bytes - pend_bytes) / 2 / 2; + soft_fadeout(ptr, num); + } + + dma_sync_single_for_device(pcm->dev, phyaddr+hw_off, + prd_bytes, DMA_TO_DEVICE); + /* next DMA */ + l2_combuf_dma(phyaddr+hw_off, id, prd_bytes, (tL2DMA_DIR)MEM2BUF, 1); +} + +/* + * stop_playback - + * stop play + * + * @pcm: pointer to pcm device + */ +static inline void stop_playback(struct akpcm *pcm) +{ + struct akpcm_runtime *rt = pcm->play_rt; + + while(l2_get_status(pcm->L2BufID_DAC)); + + ak39_dac_mute(); //by panqihe 2014-07-21 + complete(&(pcm->play_completion)); + + //aec_event_handler(pcm, AEC_EVENT_DAC_STOP); + + pcm->far_frame_off = 0; + clear_playback_ptr(rt); + clear_bit(IO_BIT_DMA, &(pcm->outflag)); + dac_user_data_playing = 0; +} + +/* + * fill_data_to_da - + * fill empty data to da buffer. + * then the playback runnging always. + * + * @pcm: pointer to pcm device + * @pend_bytes: pend bytes size to play + */ +static inline unsigned int fill_data_to_da(struct akpcm *pcm, + unsigned int pend_bytes) +{ + struct akpcm_runtime *rt = pcm->play_rt; + unsigned char *vaddr = rt->dma_area; + unsigned int prd_bytes = rt->cfg.period_bytes; + unsigned int buf_bytes = rt->buffer_bytes; + unsigned int hw_off = (rt->hw_ptr % buf_bytes); + + /* aec data should not fill the dac buf */ + ak_pcm_assert(get_aec_bytes(rt) <= (rt->buffer_bytes - FILL_PTHRESHOLD(rt))); + + /* fill a period of user data with 0s */ + memset(vaddr + hw_off + pend_bytes, 0, prd_bytes);//(prd_bytes - pend_bytes)); + pend_bytes = prd_bytes; + + /* + * forword app ptr + * causion: take care not to conflict with playback_write + */ + spin_lock(&rt->ptr_lock); //unnecessary because it is in irq + rt->app_ptr = rt->hw_ptr + prd_bytes; + if (rt->app_ptr >= rt->boundary) + rt->app_ptr -= rt->boundary; + + if (!is_capture_working(pcm) + && check_playback_aec_ptr()) + rt->aec_ptr = rt->hw_ptr;// - FAR_FRAME_SIZE; + + spin_unlock(&rt->ptr_lock); + + pcm->aec_has_pend_data = 0; + + return pend_bytes; +} + +/* + * sync_adc - + * sync adc after dac already startup + * + * @pcm: pointer to pcm device + */ +static inline void sync_adc(struct akpcm *pcm) +{ + struct akpcm_runtime *rt = pcm->play_rt; + unsigned int prd_bytes = rt->cfg.period_bytes; + u_int64_t end = 0; + u_int64_t start = sched_clock(); + + // time sequence critical segment + // DO NOT add printing msg here until adc dma is actually working + + // This period of data will be fetched into dac approximately + // 512 bytes (an L2 block) later, plus an unknown conversion latency. + // Adc will be kicked off much sooner than the conversion latency. + // I arrange the dac aec_ptr 512 bytes ahead of hw_ptr, so that + // adc aec_ptr will be align with dac with a moderate leading tolerance. + rt->aec_ptr += prd_bytes-512; + if(rt->aec_ptr >= rt->boundary) + rt->aec_ptr = 0; + + /* to start ADC */ + ak_pcm_debug_ex("sync"); + capture_start(pcm); + end = sched_clock(); + /*do not show the message because it is in irq*/ + ak_pcm_debug_ex("dac ~ adc: %dus", ak39_spend_us(start, end)); + + update_aec_status(pcm, AEC_STATUS_AEC_WORKING); + wake_up_interruptible(&(pcm->aec_sync_wq)); +} + +/* + * playback_start_dac - + * start the DAC for playback + * + * @pcm: pointer to pcm device + */ +static void playback_start_dac(struct akpcm *pcm) +{ + int dstsrc = 0; + + ak39_codec_dac_open(); + + if (PLAYDEV_HP == pcm->playdev) { + /* output to HP */ + dstsrc = pcm->mixer_source[MIXER_SRC_HP]; + //ak_pcm_debug("hpsrc=%d", dstsrc); + set_channel_source(pcm, MIXER_SRC_HP, dstsrc); + } else { + /* auto detect an outout device */ + if (pcm->play_dectect & SWITCH_DET_HEADPHONE) { + dstsrc = pcm->mixer_source[MIXER_SRC_HP]; + set_channel_source(pcm, MIXER_SRC_HP, dstsrc); + } else { + set_channel_source(pcm, MIXER_SRC_HP, 0); + } + } + + pcm->IsOutputing = true; +} + +/* + * playback_stop_hw + * stop output process for timer_stop_output + * + * @data: private data + */ +void playback_stop_hw(unsigned long data) +{ + struct akpcm *pcm = (struct akpcm *)data; + + if (!has_playback_stream(pcm)) { + ak39_codec_dac_close(); + if (PLAYDEV_HP == pcm->playdev) { + /* output to HP */ + //ak_pcm_debug("close hp channel"); + set_channel_source(pcm, MIXER_SRC_HP, 0); + } else { + /* auto detect an outout device */ +#if 0 + /* SP */ + if (pcm->play_dectect & SWITCH_DET_SPEAKER) { + //ak_pcm_debug("close sp channel"); + set_channel_source(pcm, MIXER_SRC_LO, 0); + } +#endif + /* HP */ + if (pcm->play_dectect & SWITCH_DET_HEADPHONE) { + //ak_pcm_debug("close hp channel"); + set_channel_source(pcm, MIXER_SRC_HP, 0); + } + } + + /* close other channels if needed */ + //set_channel_source(pcm, MIXER_SRC_ADC2, 0); + pcm->IsOutputing = false; + } +} + +///////////////////////////////////////////////////// +/* + * playback_isr - + * playback L2DMA ISR post-process + * + * @data: private data + */ +void playback_isr(unsigned long data) +{ + struct akpcm *pcm = (struct akpcm *)data; + struct akpcm_runtime *rt = pcm->play_rt; + unsigned int prd_bytes = rt->cfg.period_bytes; + unsigned int buf_bytes = rt->buffer_bytes; + unsigned int pend_bytes = 0; + int free_bytes_changed = 0; + int first_intr_rubbish = check_rubbish_data(rt); + +#if PLAYBACK_ISR_DEBUG + ak_pcm_debug("PB+ aec:%u, hw:%u, app:%u", + rt->aec_ptr, rt->hw_ptr, rt->app_ptr); +#endif + + update_hw_ptr(rt); + + /* calculate pend_data & elapse */ + pend_bytes = get_pend_bytes(rt); + calc_dac_elapse(pcm); + + /* user data in da buf is not enough */ + if (pend_bytes <= FILL_PTHRESHOLD(rt)) { + pend_bytes = fill_data_to_da(pcm, pend_bytes); + ++dac_fill_times; + if (dac_app_data_end && (dac_fill_times >= 3)) { + dac_user_data_playing = 0; + } + } else { + dac_fill_times = 0; + } + + if (aec_need_playback(pcm)) { + /* pend data is enough */ + if (pend_bytes >= prd_bytes) { + pcm->aec_has_pend_data = 1; + } + + /* skip first intr rubbish */ + if (first_intr_rubbish) { + rt->aec_ptr += prd_bytes-512; + if(rt->aec_ptr >= rt->boundary) + rt->aec_ptr = 0; + } + + /* to start adc in case of aec */ + if (AEC_STATUS_SYNC_ADC == pcm->aec_status) { + sync_adc(pcm); + free_bytes_changed = 1; + } + } else { + /* AEC is disabled */ + rt->aec_ptr = rt->hw_ptr; + free_bytes_changed = 1; + } + + if(has_playback_stream(pcm) && (pend_bytes) && (pend_bytes <= buf_bytes)){ + /* playback stream is running */ + update_playback_stream(pcm); + } else if (!has_playback_stream(pcm) && pcm->dac_endwith_zero){ + struct akpcm_runtime *rt = pcm->play_rt; + unsigned char *vaddr = rt->dma_area; + unsigned int hw_off = (rt->hw_ptr % buf_bytes); + + /* fill a period of user data with 0s */ + memset(vaddr + hw_off, 0, prd_bytes); + + pcm->dac_endwith_zero = 0; + update_playback_stream(pcm); + } else { + /* playback stream is not running, stop it */ + stop_playback(pcm); + ak_pcm_debug_exx("playback stopped"); + } + + if (free_bytes_changed) + wake_up_interruptible(&(pcm->play_wq)); + +#if PLAYBACK_ISR_DEBUG + ak_pcm_debugx("PB- aec:%u, hw:%u, app:%u", + rt->aec_ptr, rt->hw_ptr, rt->app_ptr); +#endif +} + +/* + * playback_start_nr - + * to start noise reduction for playback + * + * @value: value of nr + */ +static int playback_start_nr(unsigned long value) +{ + struct akpcm *pcm = get_akpcm_ptr(); + struct akpcm_runtime *rt = pcm->play_rt; + T_AEC_INPUT p_aecin; + int enable_nr, enable_agc; + + if (rt->enable_nr) + return 0; + + enable_nr = test_bit(ECHO_BIT_NR, &value); + enable_agc = test_bit(ECHO_BIT_AGC, &value); + + /* open lib */ + memset(&pcm->far_bufs, 0, sizeof(pcm->far_bufs)); + pcm->far_bufs.len_far = 0; + pcm->far_bufs.len_near = NN; + pcm->far_bufs.len_out = NN; + memset(&p_aecin, 0, sizeof(p_aecin)); + p_aecin.cb_fun.Malloc = akpcm_malloc_cb; + p_aecin.cb_fun.Free = akpcm_free_cb; + p_aecin.cb_fun.printf = (AEC_CALLBACK_FUN_PRINTF)printk; + p_aecin.cb_fun.notify = akpcm_notify_cb; + p_aecin.m_info.strVersion = ECHO_LIB_VERSION_STRING; + p_aecin.m_info.chip = ECHO_CHIP_AK39XXEV3; + p_aecin.m_info.m_Type = AEC_TYPE_1; + p_aecin.m_info.m_BitsPerSample = 16; + p_aecin.m_info.m_Channels = 1; + p_aecin.m_info.m_SampleRate = rt->cfg.rate; + p_aecin.m_info.m_Private.m_aec.m_framelen = NN; + p_aecin.m_info.m_Private.m_aec.m_tail = TAIL; + p_aecin.m_info.m_Private.m_aec.m_aecEna = 0; + p_aecin.m_info.m_Private.m_aec.m_PreprocessEna = enable_nr; + p_aecin.m_info.m_Private.m_aec.m_agcEna = enable_agc; + p_aecin.m_info.m_Private.m_aec.m_agcLevel = AEC_DA_THRESH; + pcm->far_filter = AECLib_Open(&p_aecin); + if (AK_NULL == pcm->far_filter) { + ak_pcm_info_ex("Error! far filter is not opened!"); + return -EPERM; /* Operation not permitted */ + } + + rt->enable_nr = enable_nr; + rt->enable_agc = enable_agc; + + return 0; +} + +/* + * playbakc_stop_nr - + * stop nr of player + */ +static int playback_stop_nr(void) +{ + struct akpcm *pcm = get_akpcm_ptr(); + struct akpcm_runtime *rt = pcm->play_rt; + + rt->enable_nr = 0; + rt->enable_agc = 0; + + // close lib + if (pcm->far_filter) { + AECLib_Close(pcm->far_filter); + pcm->far_filter = NULL; + } + + return 0; +} + +#if defined CONFIG_SPKHP_SWITCH_AUTO || defined CONFIG_SPKHP_SWITCH_MIXER +/* + * hpDet_wq_work - + * delay work process when HPDet is changed + * + * @work: worker for hp detection + */ +static void hpDet_wq_work(struct work_struct *work) +{ + struct akpcm *pcm = container_of(work, struct akpcm, DelayWork.work); + int hpsrc = pcm->mixer_source[MIXER_SRC_HP]; + int spksrc = pcm->mixer_source[MIXER_SRC_LO]; + int dstsrc = hpsrc | spksrc; + + if (ak_gpio_getpin(pcm->HPDet_gpio.pin) == pcm->HPDet_on_value){ + /* hp is plugged in */ + if(!(EXIST_OUT_DEV_HP & pcm->bfExistPlayDev)){ + /* Use SP, if no HP device exist */ + pcm->play_dectect |= SWITCH_DET_SPEAKER; + pcm->playdev = PLAYDEV_LO; + } else { + pcm->play_dectect &= ~SWITCH_DET_SPEAKER; + pcm->play_dectect |= SWITCH_DET_HEADPHONE; + pcm->playdev = PLAYDEV_HP; + } + } else { + /* hp is pulled out */ + if(!(EXIST_OUT_DEV_SP & pcm->bfExistPlayDev)){ + /* Use HP, if no SP device exist */ + pcm->play_dectect |= SWITCH_DET_HEADPHONE; + pcm->playdev = PLAYDEV_HP; + } else { + pcm->play_dectect &= ~SWITCH_DET_HEADPHONE; + pcm->play_dectect |= SWITCH_DET_SPEAKER; + pcm->playdev = PLAYDEV_LO; + } + } + + if (pcm->IsOutputing) { + /* HP */ + if (pcm->play_dectect & SWITCH_DET_HEADPHONE) { + /* if HP plug in, set HP source */ + if(dstsrc == 0) { dstsrc = SOURCE_DAC; } + set_channel_source(pcm, MIXER_SRC_HP, dstsrc); + } else { + /* if HP pull out, clear HP source */ + set_channel_source(pcm, MIXER_SRC_HP, 0); + } + + } +} + +/* + * HPDet_interrupt - + * irq handler of hp dectetion + * + * @irq: irq number of hp irq + * @dev_id: private data + */ +static irqreturn_t HPDet_interrupt(int irq, void *dev_id) +{ + struct akpcm *pcm = dev_id; + + if(ak_gpio_getpin(pcm->HPDet_gpio.pin) == pcm->HPDet_on_value) { + /* HP is plugged in */ + irq_set_irq_type(pcm->HPDet_irq, pcm->HPoff_irqType); + ak_pcm_debug("INT HPDet plug in"); + } else { + /* HP is pulled out */ + irq_set_irq_type(pcm->HPDet_irq, pcm->HPon_irqType); + ak_pcm_debug("INT HPDet pull out"); + } + + if (pcm->playdev != PLAYDEV_AUTO) { + return IRQ_HANDLED; + } + +#ifdef CONFIG_SPKHP_SWITCH_AUTO + schedule_delayed_work(&pcm->DelayWork, msecs_to_jiffies(100)); +#endif + return IRQ_HANDLED; +} +#endif + +/* + * playback_prepare - + * prepare to playback + * + * @pcm: pointer to pcm device + */ +static int playback_prepare(struct akpcm *pcm) +{ + struct akpcm_runtime *rt = pcm->play_rt; + unsigned char *ptr = rt->dma_area; + unsigned int rt_rate = rt->cfg.rate; + unsigned int new_size = (rt->cfg.periods * rt->cfg.period_bytes); + unsigned int i; + dma_addr_t phyaddr; + + /* allocate memory for loop-buffer */ + if((ptr) && (new_size != rt->buffer_bytes)){ + dma_unmap_single(pcm->dev, rt->dma_addr, rt->buffer_bytes, DMA_TO_DEVICE); + kfree(ptr); + rt->dma_area = ptr = NULL; + } + + if (!ptr) { + ptr = kmalloc(new_size, GFP_KERNEL|GFP_DMA); + if(!ptr){ + ak_pcm_debug("playback buffer alloc failed: 0x%x", new_size); + return -ENOMEM; + } + + phyaddr = dma_map_single(pcm->dev, ptr, new_size, DMA_TO_DEVICE); + if (dma_mapping_error(pcm->dev, phyaddr)) + printk(KERN_ERR "error: %s dma_map_single failed.", __FUNCTION__); + rt->dma_addr = phyaddr; + rt->dma_area = ptr; + ak_pcm_debug("playback buffer alloc: 0x%x, 0x%x", + (int)ptr, new_size); + + /* reset some parameters */ + clear_playback_ptr(rt); + rt->buffer_bytes = new_size; + for(i=0; i<64; i++){ + if((new_size>>i) == 0){ + break; + } + } + rt->boundary = new_size << (32-i); + ak_pcm_debug_ex("playback boundary: 0x%x", rt->boundary); + } + + /* set sample rate */ + ak_pcm_debug_ex("playback rate: 0x%x, %d", (int)rt_rate, rt_rate); + pcm->play_hw->actual_rate = ak39_codec_set_dac_samplerate(rt_rate); + + set_bit(STATUS_BIT_PREPARED, &(pcm->playback_sm_flag)); + del_timer(&(pcm->timer_stop_output)); + + aec_event_handler(pcm, AEC_EVENT_OPEN); + + return 0; +} + +/* + * playback_start_dma - + * start to playback + * + * @pcm: pointer to pcm device + */ +void playback_start_dma(struct akpcm *pcm) +{ + struct akpcm_runtime *rt = pcm->play_rt; + unsigned int prd_bytes = rt->cfg.period_bytes; + unsigned int buf_bytes = rt->buffer_bytes; + unsigned int hw_off = (rt->hw_ptr % buf_bytes); + dma_addr_t phyaddr = rt->dma_addr; + u8 id = pcm->L2BufID_DAC; + + /* debug only */ + //rt->cfg.period_bytes = 512; + //prd_bytes = rt->cfg.period_bytes; + /*no enough data then fill zero*/ + if (get_pend_bytes(rt) < prd_bytes) { + memset(rt->dma_area, 0, prd_bytes); + rt->app_ptr += prd_bytes; + } + + del_timer(&(pcm->timer_stop_output)); + init_completion(&(pcm->play_completion)); + set_bit(IO_BIT_STREAM, &(pcm->outflag)); + set_bit(IO_BIT_DMA, &(pcm->outflag)); + dma_sync_single_for_device(pcm->dev, phyaddr+hw_off, + prd_bytes, DMA_TO_DEVICE); + + l2_clr_status(id); + l2_combuf_dma(phyaddr+hw_off, id, prd_bytes, (tL2DMA_DIR)MEM2BUF, 1); + rt->start_time = sched_clock(); + rt->end_time = 0; + pcm->aec_has_pend_data = 1; + pcm->dac_endwith_zero = 1; + ak_pcm_debug_exx("hw_off=%u, prd_bytes=%d", hw_off, prd_bytes); +} + +/* + * playback_resume - + * resume playback + * + * @pcm: pointer to pcm device + */ +static void playback_resume(struct akpcm *pcm) +{ + if(is_playback_working(pcm)) { + return; + } + + ak_pcm_func("enter"); + /* sync_dac should be in protect of io lock */ + ak_pcm_assert(pcm->aec_status != AEC_STATUS_SYNC_DAC); + + aec_event_handler(pcm, AEC_EVENT_DAC_START); + playback_start_dac(pcm); + playback_start_dma(pcm); +} + +/* + * playback_start_force - + * force playback start + * + * @pcm: pointer to pcm device + */ +int playback_start_force(struct akpcm *pcm) +{ + struct akpcm_runtime *rt = pcm->play_rt; + unsigned int pnd_bytes; + unsigned int prd_bytes; + + if (!is_playback_ready(pcm)) { + ak_pcm_debug("warn: firing up playback when it is not ready"); + return -EPERM;/* Operation not permitted */ + } + + if (is_playback_working(pcm)) { + ak_pcm_debug("playback dma is already running"); + return 0; + } + + ak_pcm_func("enter"); + + /* ensure there's at least a period of data in buf, padding 0 if short */ + pnd_bytes = get_pend_bytes(rt); + prd_bytes = rt->cfg.period_bytes; + if (pnd_bytes < prd_bytes) { + unsigned int app_pos = rt->app_ptr; + unsigned char *vaddr = rt->dma_area; + unsigned int buf_bytes = rt->buffer_bytes; + unsigned int app_off = app_pos%buf_bytes; + + memset(vaddr+app_off, 0, prd_bytes); + app_pos += prd_bytes; + if(app_pos >= rt->boundary) + app_pos -= rt->boundary; + rt->app_ptr = app_pos; + } + + /* kick start playback */ + playback_start_dac(pcm); + + return 0; +} + +/* + * playback_pause - + * pause operation for playback + * + * @pcm: pointer to pcm device + */ +int playback_pause(struct akpcm *pcm) +{ + struct akpcm_runtime *rt = pcm->play_rt; + + /* working until start playback DMA */ + if (!is_playback_working(pcm)) { + ak_pcm_debug_ex("playback is not working now"); + return 0; + } + + ak_pcm_func("enter"); + + /* here just clear playback stream running flag. */ + clear_bit(IO_BIT_STREAM, &(pcm->outflag)); + if(is_playback_working(pcm)){ + wait_for_completion(&(pcm->play_completion)); + ak_pcm_info_ex("wait playback completion ok"); + } + + // playback_isr should have pushed AEC_EVENT_DAC_STOP event into fifo, + // so here just call handler to handle it + aec_event_handler(pcm, AEC_EVENT_NONE); + + // clear playback buffer + clear_playback_ptr(rt); + + return aec_set_param(pcm, pcm->app_flag); +} + +/* + * set_playback_param - + * set player parameters + * + * @pcm: pointer to pcm device + * @arg: arguments of player + */ +static int set_playback_param(struct akpcm *pcm, unsigned long arg) +{ + int ret = 0; + struct akpcm_runtime *rt = pcm->play_rt; + struct akpcm_pars *rt_cfg = &rt->cfg; + + if (copy_from_user(rt_cfg, (void __user *)arg, sizeof(struct akpcm_pars))) { + ret = -EFAULT; + } + + return ret; +} + +/* + * set_playback_dev - + * set playback device + * + * @pcm: pointer to pcm device + * @arg: arguments of player + */ +static int set_playback_dev(struct akpcm *pcm, unsigned long arg) +{ + int ret = 0; + unsigned int value = 0; + int sources = 0; + + ret = get_user(value, (int *)arg); + if (ret) + goto set_pb_dev_end; + + if((pcm->playdev & (~PLAYDEV_MSK)) != 0){ + ret = -EINVAL; + } + ak_pcm_debug_ex("IOC_SET_DEV %d", value); + + if(value == pcm->playdev){ + goto set_pb_dev_end; + } + if(value == PLAYDEV_AUTO){ + schedule_delayed_work(&pcm->DelayWork, msecs_to_jiffies(100)); + goto set_pb_dev_end; + } + + /* device auto-dectect */ + /* TODO, but we simply update 'pcm->playdev' in dectect isr */ + + /* device not auto-dectect */ + if((value & PLAYDEV_HP) && !(pcm->playdev & PLAYDEV_HP)){ + if(pcm->playdev & PLAYDEV_LO){ + /* we copy LO src to HP src, and clear LO src */ + sources = pcm->mixer_source[MIXER_SRC_LO]; + set_channel_source(pcm, MIXER_SRC_LO, 0); + if (sources) { + set_channel_source(pcm, MIXER_SRC_HP, sources); + } + }else{ + sources = pcm->mixer_source[MIXER_SRC_HP]; + set_channel_source(pcm, MIXER_SRC_HP, 0); + } + } + + if((value & PLAYDEV_LO) && !(pcm->playdev & PLAYDEV_LO)){ + if(pcm->playdev & PLAYDEV_HP){ + /* we copy HP src to LO src, and clear HP src */ + sources = pcm->mixer_source[MIXER_SRC_HP]; + set_channel_source(pcm, MIXER_SRC_HP, 0); + if (sources) { + set_channel_source(pcm, MIXER_SRC_LO, sources); + } + }else{ + sources = pcm->mixer_source[MIXER_SRC_LO]; + set_channel_source(pcm, MIXER_SRC_LO, 0); + } + } + + pcm->playdev = value; + +set_pb_dev_end: + return ret; +} + +/* + * get_playback_source - + * get playback source + * + * @pcm: pointer to pcm device + * @arg: arguments of player + */ +static int get_playback_source(struct akpcm *pcm, unsigned long arg) +{ + unsigned int value = 0; + unsigned int addr; + + switch (pcm->playdev) { + case PLAYDEV_HP: + addr = MIXER_SRC_HP; + break; + case PLAYDEV_LO: + addr = MIXER_SRC_LO; + break; + default: + return -EINVAL; + } + + value = pcm->mixer_source[addr]; + return put_user(value, (int *)arg); +} + +/* + * set_playback_source - + * set playback source + * + * @pcm: pointer to pcm device + * @arg: arguments of player + */ +static int set_playback_source(struct akpcm *pcm, unsigned long arg) +{ + int ret = 0; + unsigned int value = 0; + unsigned int addr; + + ret = get_user(value, (int *)arg); + if (ret) + return ret; + + ak_pcm_debug_ex("IOC_SET_SOURCES %d", value); + + switch (pcm->playdev) { + case PLAYDEV_HP: + addr = MIXER_SRC_HP; + if (value < SIGNAL_SRC_MUTE) { + value = SIGNAL_SRC_MUTE; + } else if (value > SIGNAL_SRC_MAX){ + value = SIGNAL_SRC_MAX; + } +#ifndef CONFIG_SPKHP_SWITCH_AUTO + if (value != SIGNAL_SRC_MUTE){ + pcm->play_dectect |= SWITCH_DET_HEADPHONE; + } else { + pcm->play_dectect &= ~SWITCH_DET_HEADPHONE; + } +#endif + + change_channel_source(pcm, addr, value); + break; + case PLAYDEV_LO: + addr = MIXER_SRC_LO; + if (value < SIGNAL_SRC_MUTE) { + value = SIGNAL_SRC_MUTE; + } else if (value > SIGNAL_SRC_MAX){ + value = SIGNAL_SRC_MAX; + } + if (value != SIGNAL_SRC_MUTE) { + value = SOURCE_LINEIN; + } + + change_channel_source(pcm, addr, value); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/* + * get_playback_gain - + * get playback analog gain + * + * @pcm: pointer to pcm device + * @arg: arguments of player + */ +static int get_playback_gain(struct akpcm *pcm, unsigned long arg) +{ + int ret = 0; + unsigned int value = 0; + unsigned int addr; + + switch (pcm->playdev) { + case PLAYDEV_HP: + addr = MIXER_VOL_HP; + value = pcm->mixer_volume[addr]; + ret = put_user(value, (int *)arg); + break; + case PLAYDEV_LO: + //addr = MIXER_VOL_LO; + //value = pcm->mixer_volume[addr]; + //ret = put_user(value, (int *)arg); + ret = -EINVAL; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/* + * set_playback_gain - + * set playback analog gain + * + * @pcm: pointer to pcm device + * @arg: arguments of player + */ +static int set_playback_gain(struct akpcm *pcm, unsigned long arg) +{ + int ret = 0; + unsigned int addr; + unsigned int value; + + if(pcm->playdev == PLAYDEV_HP) { + addr = MIXER_VOL_HP; + } else if(pcm->playdev == PLAYDEV_LO) { + ret = -EINVAL; + goto set_pb_gain_end; + } else { + ret = -EINVAL; + goto set_pb_gain_end; + } + + ret = get_user(value, (int *)arg); + if (ret) + goto set_pb_gain_end; + + if (value < HEADPHONE_GAIN_MIN) { + value = HEADPHONE_GAIN_MIN; + } else if (value > HEADPHONE_GAIN_MAX) { + value = HEADPHONE_GAIN_MAX; + } + ak_pcm_debug_ex("IOC_SET_GAIN, HP gain=%d", value); + + if (pcm->mixer_volume[addr] != value) { + pcm->mixer_volume[addr] = value; + + /* + * normally, volume is map to DAC gain. + * in case aec is enabled, volume is changed by software + */ + if (aec_need_playback(pcm)) { + /* do nothing here. volume is applied by aec_set_da_volume() */ + } else { + akpcm_set_hp_gain(value); + } + } + +set_pb_gain_end: + return ret; +} + +/* + * playback_ioctl - + * device file ops: ioctl + * + * @filp: pointer to device file + * @cmd: command + * @arg: argument of command + */ +long playback_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct akpcm *pcm = get_akpcm_ptr(); + struct akpcm_runtime *rt;// = pcm->play_rt; + unsigned long value = 0; + struct akpcm_features *feats; + struct akpcm_pars *rt_cfg; + unsigned long ul; + + lock_playback_io(pcm); + + rt = pcm->play_rt; + + if (!rt) { + unlock_playback_io(pcm); + ak_pcm_err_ex("play_rt is NULL"); + return -EPERM; + } + + /* commands */ + switch(cmd){ + case IOC_PREPARE: + ak_pcm_debug_ex("IOC_PREPARE"); + ret = playback_prepare(pcm); + aec_set_param(pcm, pcm->app_flag); + break; + case IOC_RESUME: + ak_pcm_debug_ex("IOC_RESUME"); + playback_resume(pcm); + break; + case IOC_PAUSE: + ak_pcm_debug_ex("IOC_PAUSE"); + playback_pause(pcm); + break; + case IOC_GETELAPSE: + ak_pcm_debug_ex("IOC_GETELAPSE, dac_elapse=%llu", dac_elapse); + if (copy_to_user((void __user *)arg, &dac_elapse, + sizeof(unsigned long long))) { + ret = -EFAULT; + } + break; + case IOC_RSTBUF: + ak_pcm_debug_ex("IOC_RSTBUF"); + clear_playback_ptr(rt); + break; + case IOC_RSTALL: + ak_pcm_debug_ex("IOC_RSTALL"); + clear_playback_ptr(rt); + /* reset dac elapse time */ + dac_elapse = 0; + break; + /* features */ + case IOC_GET_FEATS: + ak_pcm_debug_ex("IOC_GET_FEATS"); + feats = pcm->play_hw; + if (copy_to_user((void __user *)arg, feats, + sizeof(struct akpcm_features))) { + ret = -EFAULT; + } + break; + /* configures */ + case IOC_GET_PARS: + ak_pcm_debug_ex("IOC_GET_PARS"); + rt_cfg = &rt->cfg; + if (copy_to_user((void __user *)arg, rt_cfg, sizeof(struct akpcm_pars))) { + ret = -EFAULT; + } + break; + case IOC_SET_PARS: + ak_pcm_debug_ex("IOC_SET_PARS"); + ret = set_playback_param(pcm, arg); + break; + /* ---------- PLAY DEVICE ------------------------------------ */ + case IOC_GET_DEV: + ak_pcm_debug_ex("IOC_GET_DEV"); + value = pcm->playdev; + ret = put_user(value, (int *)arg); + break; + case IOC_SET_DEV: + ret = set_playback_dev(pcm, arg); + break; + /* ---------- sources ------------------------------------ */ + case IOC_GET_SOURCES: + ak_pcm_debug_ex("IOC_GET_SOURCES"); + ret = get_playback_source(pcm, arg); + break; + case IOC_SET_SOURCES: + ak_pcm_debug_ex("IOC_SET_SOURCES"); + ret = set_playback_source(pcm, arg); + break; + /* ---------- GAIN ------------------------------------ */ + case IOC_GET_GAIN: + ak_pcm_debug_ex("IOC_GET_GAIN"); + ret = get_playback_gain(pcm, arg); + break; + case IOC_SET_GAIN: + ret = set_playback_gain(pcm, arg); + break; + case IOC_SET_NR_AGC: + if (copy_from_user(&value, (void __user *)arg, sizeof(value))) { + ret = -EFAULT; + goto pb_ioc_end; + } + + ak_pcm_debug_ex("IOC_SET_NR_AGC, value=%lu", value); + if (test_bit(ECHO_BIT_NR, &value) && test_bit(ECHO_BIT_AGC, &value)) + playback_start_nr(value); + else + playback_stop_nr(); + break; + case IOC_SET_SPEAKER: + if (copy_from_user(&value, (void __user *)arg, sizeof(value))) { + ret = -EFAULT; + goto pb_ioc_end; + } + + ak_pcm_debug_ex("IOC_SET_SPEAKER, value=%lu", value); + if (value) { + ak_gpio_setpin(pcm->SPdown_gpio.pin, 1 - pcm->SPdown_gpio.value); + } else { + ak_gpio_setpin(pcm->SPdown_gpio.pin, pcm->SPdown_gpio.value); + } + break; + case IOC_GETTIMER: + ul = get_timestamp(); + if (copy_to_user((void __user *)arg, &ul, sizeof(unsigned long))) { + ret = -EFAULT; + } + break; + case IOC_GET_STATUS: + value = dac_user_data_playing; + if (aec_need_playback(pcm)) + value &= pcm->aec_has_pend_data; + + ret = put_user(value, (unsigned int *)arg); + break; + case IOC_NOTICE_END: + ak_pcm_debug_ex("IOC_NOTICE_END"); + dac_app_data_end = 1; + break; + default: + ret = -EINVAL; + break; + } + +pb_ioc_end: + unlock_playback_io(pcm); + + return ret; +} + +/* + * playback_write - + * device file ops: write + * + * @filp: pointer to device file + * @cmd: command + * @arg: argument of command + */ +ssize_t playback_write(struct file *filp, const char __user *buf, size_t count, + loff_t *f_pos) +{ + struct akpcm *pcm = get_akpcm_ptr(); + struct akpcm_runtime *rt = pcm->play_rt; + unsigned int pnd_bytes = 0; + unsigned int app_pos = rt->app_ptr; + unsigned int app_pos_save = 0; + + if (check_playback_status(pcm)) { + return -EPERM; + } + +#if PLAYBACK_WRITE_DEBUG + ak_pcm_func("W+"); + ak_pcm_debug("ptr hw:%u, app:%u, aec:%u", + rt->hw_ptr, rt->app_ptr, rt->aec_ptr); +#endif + + /* calculate pend_data & free_space */ + if (wait_event_interruptible(pcm->play_wq, + (get_playback_free_bytes(rt) >= (count + WRITE_PTHRESHOLD(rt)))) < 0){ + printk("-"); + ak_pcm_debug("playback write wakeup by signal!"); + return -ERESTARTSYS; + } + + app_pos_save = rt->app_ptr; + app_pos = rt->app_ptr; + /* app_pos should be aligned in frame */ + ak_pcm_assert((app_pos & (FAR_FRAME_SIZE-1)) == 0); + + if (get_user_data(pcm, buf, count, &app_pos)) { + return -EFAULT; + } + + update_playback_app_ptr(pcm, app_pos_save, app_pos); + pnd_bytes = get_pend_bytes(rt); + if (!dac_user_data_playing && (pnd_bytes >= rt->cfg.threshold)) { + dac_user_data_playing = 1; + } + +#if PLAYBACK_WRITE_DEBUG + ak_pcm_debug("app_pos: %u, pnd_bytes: %u, count: %u", + app_pos, pnd_bytes, count); + ak_pcm_func_exx("W-"); +#endif + + return count; +} + +/* + * playback_open - + * device file ops: open file + * + * @inode: device node + * @filp: pointer to device file + */ +int playback_open(struct inode *inode, struct file *filp) +{ + int ret = 0; + struct akpcm *pcm = get_akpcm_ptr(); + struct akpcm_runtime *rt = pcm->play_rt; + + ak_pcm_debug_ex("rt=0x%x", (int)rt); + + lock_playback_io(pcm); + + //if (is_playback_opened(pcm)) { + if (pcm->dac_opened_count > 0) { + ak_pcm_err_ex("plackback device is already open"); + //ret = -EPERM; /* Operation not permitted */ + pcm->dac_opened_count++; + ak_pcm_err_ex("dac_opened_count:%d",pcm->dac_opened_count); + unlock_playback_io(pcm); + return ret; + //goto pb_open_end; + } + + /* allocate an L2 buffer */ + if (BUF_NULL == pcm->L2BufID_DAC) { + pcm->L2BufID_DAC = l2_alloc((l2_device_t)ADDR_DAC); + if (BUF_NULL == pcm->L2BufID_DAC) { + ak_pcm_err_ex("ERR! no L2 buffer for playback"); + ret = -ENOMEM; + goto pb_open_end; + } + + ak_pcm_debug_ex("L2 buffer ID for playback: %d", pcm->L2BufID_DAC); + } + + /* allocate memory for akpcm_runtime */ + rt = kmalloc(sizeof(struct akpcm_runtime), GFP_KERNEL); + if (!rt) { + ak_pcm_err_ex("playback rt allocate failed"); + ret = -ENOMEM; + goto pb_open_end; + } + + pcm->play_rt = rt; + ak_pcm_debug("playback rt allocate: 0x%x", (int)(pcm->play_rt)); + + /* + * allocate memory for far frame buf + * 2 times of aec frame size because it is stereo + */ + pcm->far_frame_buf = (T_S8 *)kmalloc(FAR_FRAME_SIZE, GFP_KERNEL); + if (!pcm->far_frame_buf) { + ak_pcm_err_ex("playback frame buf allocate failed"); + ret = -ENOMEM; + goto pb_open_end; + } + pcm->far_frame_off = 0; + + /* others */ + rt->dma_area = NULL; + rt->dma_addr = 0; + clear_playback_ptr(rt); + spin_lock_init(&rt->ptr_lock); + mutex_init(&rt->lock); + rt->cfg.rate = 8000; + rt->cfg.channels = 2; + rt->cfg.sample_bits = 16; + rt->cfg.period_bytes = 16384; + rt->cfg.periods = 32; + rt->cfg.threshold = 16384*30; + rt->buffer_bytes = (rt->cfg.periods * rt->cfg.period_bytes); + rt->enable_nr = 0; + rt->enable_agc = 0; + pcm->mixer_source[MIXER_SRC_HP] = SOURCE_DAC; + pcm->mixer_source[MIXER_SRC_LO] = 0;//SOURCE_DAC; + pcm->playdev = PLAYDEV_HP; + pcm->IsOutputing = false; + pcm->play_hw = &akpcm_play_hardware; + dac_elapse = 0; + + pcm->far_filter = NULL; + pcm->aec_has_pend_data = 0; + + l2_set_dma_callback(pcm->L2BufID_DAC, playback_isr, (unsigned long)pcm); + set_bit(STATUS_BIT_OPENED, &(pcm->playback_sm_flag)); + ak_pcm_debug_exx("OK"); + +pb_open_end: + if (ret) { + if (rt) { + kfree(rt); + pcm->play_rt = NULL; + } + + if (BUF_NULL != pcm->L2BufID_DAC) { + l2_free((l2_device_t)ADDR_DAC); + pcm->L2BufID_DAC = BUF_NULL; + } + } else { + pcm->dac_opened_count++; + } + unlock_playback_io(pcm); + + return ret; +} + +/* + * playback_close - + * device file ops: close file + * + * @inode: device node + * @filp: pointer to device file + */ +int playback_close(struct inode *inode, struct file *filp) +{ + struct akpcm *pcm = get_akpcm_ptr(); + struct akpcm_runtime *rt;// = pcm->play_rt; + + lock_playback_io(pcm); + + //if (!is_playback_opened(pcm)) { + if (!pcm->dac_opened_count) { + ak_pcm_err_ex("ERR! plackback device is not open"); + unlock_playback_io(pcm); + /* Operation not permitted */ + return 0; + } + + ak_pcm_func_exx("enter"); + + rt = pcm->play_rt; + + if (!rt) { + ak_pcm_err_ex("rt is NULL"); + pcm->dac_opened_count = 0; + unlock_playback_io(pcm); + return -EPERM; + } + + pcm->dac_opened_count--; + if (pcm->dac_opened_count > 0) { + ak_pcm_err_ex("playback is used by others, dac_opened_count:%d",pcm->dac_opened_count); + unlock_playback_io(pcm); + /* Operation not permitted */ + return 0; + } + + playback_pause(pcm); + + /* delay to close output channel */ + //pcm->timer_stop_output.expires = jiffies + DELAYS_FOR_CLOSE_DAC; + //add_timer(&pcm->timer_stop_output); + playback_stop_hw((unsigned long)pcm); + /* free loop-buffer memory */ + clear_bit(STATUS_BIT_PREPARED, &(pcm->playback_sm_flag)); + + /* may be redundant but shall not miss */ + aec_event_handler(pcm, AEC_EVENT_DAC_STOP); + + if (pcm->far_frame_buf) { + kfree(pcm->far_frame_buf); + pcm->far_frame_buf = NULL; + } + + if (rt) { + if (rt->dma_area) { + dma_unmap_single(pcm->dev, rt->dma_addr, rt->buffer_bytes, + DMA_TO_DEVICE); + kfree(rt->dma_area); + rt->dma_area = NULL; + } + + kfree(rt); + pcm->play_rt = NULL; + } + + /* free L2 buffers */ + if (BUF_NULL != pcm->L2BufID_DAC) { + l2_free((l2_device_t)ADDR_DAC); + pcm->L2BufID_DAC = BUF_NULL; + } + + clear_bit(STATUS_BIT_OPENED, &(pcm->playback_sm_flag)); + pcm->dac_opened_count = 0; + unlock_playback_io(pcm); + + ak_pcm_debug_exx("OK"); + + return 0; +} diff --git a/drivers/misc/akpcm/playback.h b/drivers/misc/akpcm/playback.h new file mode 100644 index 00000000..61564138 --- /dev/null +++ b/drivers/misc/akpcm/playback.h @@ -0,0 +1,57 @@ +#ifndef _AKPCM_PLAYBACK_H_ +#define _AKPCM_PLAYBACK_H_ + +static inline int is_playback_opened(struct akpcm *pcm) +{ + return test_bit(STATUS_BIT_OPENED, &(pcm->playback_sm_flag)); +} + +static inline int is_playback_ready(struct akpcm *pcm) +{ + return test_bit(STATUS_BIT_PREPARED, &(pcm->playback_sm_flag)); +} + +static inline int has_playback_stream(struct akpcm *pcm) +{ + return test_bit(IO_BIT_STREAM, &(pcm->outflag)); +} + +static inline int is_playback_working(struct akpcm *pcm) +{ + return test_bit(IO_BIT_DMA, &(pcm->outflag)); +} + +/** + * @brief start to playback + * @author panqihe 2014-06-26 + * @modify + * @return void + */ +void playback_start_dma(struct akpcm *pcm); + +/** + * @brief stop output process for timer_stop_output + * @author Cheng Mingjuan + * @modify panqihe 2014-06-11 + * @return void + */ +void playback_stop_hw(unsigned long data); + +int playback_start_force(struct akpcm *pcm); + +/** + * @brief pause operation for playback + * @author panqihe 2014-06-12 + * @modify + * @return void + */ +int playback_pause(struct akpcm *pcm); + +long playback_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +ssize_t playback_write(struct file *filp, const char __user *buf, size_t count, + loff_t *f_pos); +int playback_open(struct inode *inode, struct file *filp); +int playback_close(struct inode *inode, struct file *filp); +int check_playback_aec_ptr(void); + +#endif diff --git a/drivers/misc/uid_stat.c b/drivers/misc/uid_stat.c new file mode 100644 index 00000000..2141124a --- /dev/null +++ b/drivers/misc/uid_stat.c @@ -0,0 +1,156 @@ +/* drivers/misc/uid_stat.c + * + * Copyright (C) 2008 - 2009 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_SPINLOCK(uid_lock); +static LIST_HEAD(uid_list); +static struct proc_dir_entry *parent; + +struct uid_stat { + struct list_head link; + uid_t uid; + atomic_t tcp_rcv; + atomic_t tcp_snd; +}; + +static struct uid_stat *find_uid_stat(uid_t uid) { + unsigned long flags; + struct uid_stat *entry; + + spin_lock_irqsave(&uid_lock, flags); + list_for_each_entry(entry, &uid_list, link) { + if (entry->uid == uid) { + spin_unlock_irqrestore(&uid_lock, flags); + return entry; + } + } + spin_unlock_irqrestore(&uid_lock, flags); + return NULL; +} + +static int tcp_snd_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + unsigned int bytes; + char *p = page; + struct uid_stat *uid_entry = (struct uid_stat *) data; + if (!data) + return 0; + + bytes = (unsigned int) (atomic_read(&uid_entry->tcp_snd) + INT_MIN); + p += sprintf(p, "%u\n", bytes); + len = (p - page) - off; + *eof = (len <= count) ? 1 : 0; + *start = page + off; + return len; +} + +static int tcp_rcv_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + unsigned int bytes; + char *p = page; + struct uid_stat *uid_entry = (struct uid_stat *) data; + if (!data) + return 0; + + bytes = (unsigned int) (atomic_read(&uid_entry->tcp_rcv) + INT_MIN); + p += sprintf(p, "%u\n", bytes); + len = (p - page) - off; + *eof = (len <= count) ? 1 : 0; + *start = page + off; + return len; +} + +/* Create a new entry for tracking the specified uid. */ +static struct uid_stat *create_stat(uid_t uid) { + unsigned long flags; + char uid_s[32]; + struct uid_stat *new_uid; + struct proc_dir_entry *entry; + + /* Create the uid stat struct and append it to the list. */ + if ((new_uid = kmalloc(sizeof(struct uid_stat), GFP_KERNEL)) == NULL) + return NULL; + + new_uid->uid = uid; + /* Counters start at INT_MIN, so we can track 4GB of network traffic. */ + atomic_set(&new_uid->tcp_rcv, INT_MIN); + atomic_set(&new_uid->tcp_snd, INT_MIN); + + spin_lock_irqsave(&uid_lock, flags); + list_add_tail(&new_uid->link, &uid_list); + spin_unlock_irqrestore(&uid_lock, flags); + + sprintf(uid_s, "%d", uid); + entry = proc_mkdir(uid_s, parent); + + /* Keep reference to uid_stat so we know what uid to read stats from. */ + create_proc_read_entry("tcp_snd", S_IRUGO, entry , tcp_snd_read_proc, + (void *) new_uid); + + create_proc_read_entry("tcp_rcv", S_IRUGO, entry, tcp_rcv_read_proc, + (void *) new_uid); + + return new_uid; +} + +int uid_stat_tcp_snd(uid_t uid, int size) { + struct uid_stat *entry; + activity_stats_update(); + if ((entry = find_uid_stat(uid)) == NULL && + ((entry = create_stat(uid)) == NULL)) { + return -1; + } + atomic_add(size, &entry->tcp_snd); + return 0; +} + +int uid_stat_tcp_rcv(uid_t uid, int size) { + struct uid_stat *entry; + activity_stats_update(); + if ((entry = find_uid_stat(uid)) == NULL && + ((entry = create_stat(uid)) == NULL)) { + return -1; + } + atomic_add(size, &entry->tcp_rcv); + return 0; +} + +static int __init uid_stat_init(void) +{ + parent = proc_mkdir("uid_stat", NULL); + if (!parent) { + pr_err("uid_stat: failed to create proc entry\n"); + return -1; + } + return 0; +} + +__initcall(uid_stat_init); diff --git a/drivers/misc/user_gpio.c b/drivers/misc/user_gpio.c new file mode 100644 index 00000000..f1e5ff84 --- /dev/null +++ b/drivers/misc/user_gpio.c @@ -0,0 +1,184 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct user_gpio_attribute { + struct attribute attr; + struct user_gpio_info *gpio; +}; + +struct user_gpio_driver_info { + struct kobject kobj; + struct attribute **attrs; +}; + +static struct user_gpio_driver_info driver_info; + +static void user_gpio_release(struct kobject *kobj) +{ + printk(KERN_ERR "%s\n", __func__); +} + +static ssize_t user_gpio_show(struct kobject *kobj, struct attribute *attr, char *buf) +{ + int level; + struct user_gpio_attribute *puser_gpio_attr_arrays; + + puser_gpio_attr_arrays = container_of(attr, struct user_gpio_attribute, attr); + level = ak_gpio_getpin(puser_gpio_attr_arrays->gpio->info.pin); + sprintf(buf, "%d\n", level); + return (strlen(buf) + 1); +} + +static ssize_t user_gpio_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + int ret; + long int level; + struct gpio_info info; + struct user_gpio_attribute *puser_gpio_attr_arrays; + + puser_gpio_attr_arrays = container_of(attr, struct user_gpio_attribute, attr); + ret = strict_strtol(buf, 10, &level); + if (ret) { + printk(KERN_ERR "string can not trans to long int\n"); + return 0; + } + + //printk(KERN_ERR "%s store:%ld\n", puser_gpio_attr_arrays->gpio->name, level); + + memcpy(&info, &puser_gpio_attr_arrays->gpio->info, sizeof(info)); + info.value = level; + ak_gpio_set(&info); + + return count; +} + +static const struct sysfs_ops user_gpio_sysfs_ops = { + .show = user_gpio_show, + .store = user_gpio_store, +}; + +static struct kobj_type user_gpio_ktype = { + .release = user_gpio_release, + .sysfs_ops = &user_gpio_sysfs_ops, +}; + +static int user_gpio_probe(struct platform_device *pd) +{ + int i; + int n; + int retval; + struct ak_user_gpio_pdata *pdata = pd->dev.platform_data; + struct user_gpio_info *pgpios = pdata->user_gpios; + int nr_user_gpios = pdata->nr_user_gpios; + struct user_gpio_attribute *puser_gpio_attr_arrays; + + driver_info.attrs = kzalloc(sizeof(struct attribute *) * (nr_user_gpios + 1) + , GFP_KERNEL); + if (!driver_info.attrs) { + printk(KERN_ERR "can not zalloc for attrs address\n"); + retval = -ENOMEM; + goto fail_alloc_attrs_address; + } + + puser_gpio_attr_arrays = kzalloc(sizeof(struct user_gpio_attribute) * + nr_user_gpios, GFP_KERNEL); + if (!puser_gpio_attr_arrays) { + printk(KERN_ERR "can not zalloc for all attrs\n"); + retval = -ENOMEM; + goto fail_alloc_attr_arrays; + } + + for (i = 0, n = 0; i < nr_user_gpios; i++) { + struct user_gpio_attribute *pgpio_attr = puser_gpio_attr_arrays + i; + struct user_gpio_info *pgpio = pgpios + i; + struct gpio_info *info; + + pgpio_attr->attr.name = pgpio->name; + pgpio_attr->attr.mode = 0666; + pgpio_attr->gpio = pgpio; + + if (pgpio_attr->gpio->info.pin < 0) + continue; + + *(driver_info.attrs + n) = &(pgpio_attr->attr); + n++; + + info = &pgpio_attr->gpio->info; + ak_gpio_set(info); + } + + retval = kobject_init_and_add(&driver_info.kobj, &user_gpio_ktype, NULL, "user-gpio"); + if (retval) { + printk(KERN_ERR "can not init kobject\n"); + goto fail_kobject_add; + } + + retval = sysfs_create_files(&driver_info.kobj, (const struct attribute **)driver_info.attrs); + if (retval) + goto fail_create_files; + + return 0; + +fail_create_files: + kobject_put(&driver_info.kobj); +fail_kobject_add: + kfree(puser_gpio_attr_arrays); +fail_alloc_attr_arrays: + kfree(driver_info.attrs); +fail_alloc_attrs_address: + return retval; +} + +static int user_gpio_remove(struct platform_device *pd) +{ + struct user_gpio_attribute *puser_gpio_attr_arrays; + struct attribute **pattrs; + + sysfs_remove_files(&driver_info.kobj, (const struct attribute **)driver_info.attrs); + kobject_put(&driver_info.kobj); + + pattrs = driver_info.attrs; + if (*pattrs) { + puser_gpio_attr_arrays = container_of(*pattrs, struct user_gpio_attribute, attr); + kfree(puser_gpio_attr_arrays); + } + + kfree(driver_info.attrs); + + return 0; +} + +static struct platform_driver user_gpio_device_driver = { + .probe = user_gpio_probe, + .remove = __devexit_p(user_gpio_remove), + .driver = { + .name = "user_gpio", + .owner = THIS_MODULE, + } +}; + +static int __init user_gpio_init(void) +{ + return platform_driver_register(&user_gpio_device_driver); +} + +static void __exit user_gpio_exit(void) +{ + platform_driver_unregister(&user_gpio_device_driver); +} + +module_init(user_gpio_init); +module_exit(user_gpio_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("ygh"); +MODULE_DESCRIPTION("Export GPIOs"); +MODULE_ALIAS("platform:user-keys"); diff --git a/drivers/misc/wl127x-rfkill.c b/drivers/misc/wl127x-rfkill.c new file mode 100644 index 00000000..f5b95152 --- /dev/null +++ b/drivers/misc/wl127x-rfkill.c @@ -0,0 +1,121 @@ +/* + * Bluetooth TI wl127x rfkill power control via GPIO + * + * Copyright (C) 2009 Motorola, Inc. + * Copyright (C) 2008 Texas Instruments + * Initial code: Pavan Savoy (wl127x_power.c) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + +static int wl127x_rfkill_set_power(void *data, enum rfkill_state state) +{ + int nshutdown_gpio = (int) data; + + switch (state) { + case RFKILL_STATE_UNBLOCKED: + gpio_set_value(nshutdown_gpio, 1); + break; + case RFKILL_STATE_SOFT_BLOCKED: + gpio_set_value(nshutdown_gpio, 0); + break; + default: + printk(KERN_ERR "invalid bluetooth rfkill state %d\n", state); + } + return 0; +} + +static int wl127x_rfkill_probe(struct platform_device *pdev) +{ + int rc = 0; + struct wl127x_rfkill_platform_data *pdata = pdev->dev.platform_data; + enum rfkill_state default_state = RFKILL_STATE_SOFT_BLOCKED; /* off */ + + rc = gpio_request(pdata->nshutdown_gpio, "wl127x_nshutdown_gpio"); + if (unlikely(rc)) + return rc; + + rc = gpio_direction_output(pdata->nshutdown_gpio, 0); + if (unlikely(rc)) + return rc; + + rfkill_set_default(RFKILL_TYPE_BLUETOOTH, default_state); + wl127x_rfkill_set_power(NULL, default_state); + + pdata->rfkill = rfkill_allocate(&pdev->dev, RFKILL_TYPE_BLUETOOTH); + if (unlikely(!pdata->rfkill)) + return -ENOMEM; + + pdata->rfkill->name = "wl127x"; + pdata->rfkill->state = default_state; + /* userspace cannot take exclusive control */ + pdata->rfkill->user_claim_unsupported = 1; + pdata->rfkill->user_claim = 0; + pdata->rfkill->data = (void *) pdata->nshutdown_gpio; + pdata->rfkill->toggle_radio = wl127x_rfkill_set_power; + + rc = rfkill_register(pdata->rfkill); + + if (unlikely(rc)) + rfkill_free(pdata->rfkill); + + return 0; +} + +static int wl127x_rfkill_remove(struct platform_device *pdev) +{ + struct wl127x_rfkill_platform_data *pdata = pdev->dev.platform_data; + + rfkill_unregister(pdata->rfkill); + rfkill_free(pdata->rfkill); + gpio_free(pdata->nshutdown_gpio); + + return 0; +} + +static struct platform_driver wl127x_rfkill_platform_driver = { + .probe = wl127x_rfkill_probe, + .remove = wl127x_rfkill_remove, + .driver = { + .name = "wl127x-rfkill", + .owner = THIS_MODULE, + }, +}; + +static int __init wl127x_rfkill_init(void) +{ + return platform_driver_register(&wl127x_rfkill_platform_driver); +} + +static void __exit wl127x_rfkill_exit(void) +{ + platform_driver_unregister(&wl127x_rfkill_platform_driver); +} + +module_init(wl127x_rfkill_init); +module_exit(wl127x_rfkill_exit); + +MODULE_ALIAS("platform:wl127x"); +MODULE_DESCRIPTION("wl127x-rfkill"); +MODULE_AUTHOR("Motorola"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig index 3b1f783b..c9905355 100644 --- a/drivers/mmc/card/Kconfig +++ b/drivers/mmc/card/Kconfig @@ -50,6 +50,15 @@ config MMC_BLOCK_BOUNCE If unsure, say Y here. +config MMC_BLOCK_DEFERRED_RESUME + bool "Deferr MMC layer resume until I/O is requested" + depends on MMC_BLOCK + default n + help + Say Y here to enable deferred MMC resume until I/O + is requested. This will reduce overall resume latency and + save power when theres an SD card inserted but not being used. + config SDIO_UART tristate "SDIO UART/GPS class support" help @@ -67,3 +76,11 @@ config MMC_TEST This driver is only of interest to those developing or testing a host driver. Most people should say N here. + +config SDIO_WIFI + tristate "SDIO WIFI support" + depends on MMC_ANYKA + default n + help + SDIO function driver for SDIO cards that implements the WIFI + module. diff --git a/drivers/mmc/card/Makefile b/drivers/mmc/card/Makefile index c73b406a..c6c2f22f 100644 --- a/drivers/mmc/card/Makefile +++ b/drivers/mmc/card/Makefile @@ -7,4 +7,5 @@ mmc_block-objs := block.o queue.o obj-$(CONFIG_MMC_TEST) += mmc_test.o obj-$(CONFIG_SDIO_UART) += sdio_uart.o +obj-$(CONFIG_SDIO_WIFI) += sdio_wifi.o diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 833ff16f..8865cd6b 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -42,6 +42,7 @@ #include #include +#include #include "queue.h" @@ -76,6 +77,10 @@ static int max_devices; static DECLARE_BITMAP(dev_use, 256); static DECLARE_BITMAP(name_use, 256); +/*mci0mci1ӿsdiosdmmcжspiõmciӿ˶ԻĿǰsdmmcspi*/ +#define mmc_host_is_sdmmc(host) (!((host)->caps & MMC_CAP_SDIO_IRQ)) + + /* * There is one mmc_blk_data per slot. */ @@ -143,11 +148,7 @@ static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) static inline int mmc_get_devidx(struct gendisk *disk) { - int devmaj = MAJOR(disk_devt(disk)); - int devidx = MINOR(disk_devt(disk)) / perdev_minors; - - if (!devmaj) - devidx = disk->first_minor / perdev_minors; + int devidx = disk->first_minor / perdev_minors; return devidx; } @@ -660,18 +661,22 @@ static int mmc_blk_cmd_error(struct request *req, const char *name, int error, req->rq_disk->disk_name, "timed out", name, status); /* If the status cmd initially failed, retry the r/w cmd */ - if (!status_valid) + if (!status_valid) { + pr_err("%s: status not valid, retrying timeout\n", req->rq_disk->disk_name); return ERR_RETRY; - + } /* * If it was a r/w cmd crc error, or illegal command * (eg, issued in wrong state) then retry - we should * have corrected the state problem above. */ - if (status & (R1_COM_CRC_ERROR | R1_ILLEGAL_COMMAND)) + if (status & (R1_COM_CRC_ERROR | R1_ILLEGAL_COMMAND)) { + pr_err("%s: command error, retrying timeout\n", req->rq_disk->disk_name); return ERR_RETRY; + } /* Otherwise abort the command */ + pr_err("%s: not retrying timeout\n", req->rq_disk->disk_name); return ERR_ABORT; default: @@ -1051,6 +1056,9 @@ static int mmc_blk_err_check(struct mmc_card *card, */ if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) { u32 status; + unsigned long before_jiffs = jiffies; + unsigned int status_ms = 0; + unsigned int count = 0; do { int err = get_card_status(card, &status, 5); if (err) { @@ -1058,13 +1066,22 @@ static int mmc_blk_err_check(struct mmc_card *card, req->rq_disk->disk_name, err); return MMC_BLK_CMD_ERR; } + count++; + if(!(count%5000)){ + printk(KERN_ERR"##get_card_status wait too long count : %u\n", count); + msleep(2); + } /* * Some cards mishandle the status bits, * so make sure to check both the busy * indication and the card state. */ + //msleep(2); + cond_resched(); } while (!(status & R1_READY_FOR_DATA) || (R1_CURRENT_STATE(status) == R1_STATE_PRG)); + status_ms = jiffies_to_msecs(jiffies-before_jiffs); + //printk("get_card_status ms:%u count:%u\n",status_ms,count); } if (brq->data.error) { @@ -1297,8 +1314,10 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) areq = NULL; areq = mmc_start_req(card->host, areq, (int *) &status); if (!areq) + { return 0; - + } + mq_rq = container_of(areq, struct mmc_queue_req, mmc_active); brq = &mq_rq->brq; req = mq_rq->req; @@ -1405,12 +1424,26 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) return 0; } +#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME +static int mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card) +{ + return 0; +} +#endif + static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) { int ret; struct mmc_blk_data *md = mq->data; struct mmc_card *card = md->queue.card; +#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME + if (mmc_bus_needs_resume(card->host)) { + mmc_resume_bus(card->host); + mmc_blk_set_blksize(md, card); + } +#endif + if (req && !mq->mqrq_prev->req) /* claim host only for the first request */ mmc_claim_host(card->host); @@ -1523,6 +1556,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, md->disk->queue = md->queue.queue; md->disk->driverfs_dev = parent; set_disk_ro(md->disk, md->read_only || default_ro); + md->disk->flags = GENHD_FL_EXT_DEVT; /* * As discussed on lkml, GENHD_FL_REMOVABLE should: @@ -1820,6 +1854,9 @@ static int mmc_blk_probe(struct mmc_card *card) mmc_set_drvdata(card, md); mmc_fixup_device(card, blk_fixups); +#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME + mmc_set_bus_resume_policy(card->host, 1); +#endif if (mmc_add_disk(md)) goto out; @@ -1845,6 +1882,9 @@ static void mmc_blk_remove(struct mmc_card *card) mmc_release_host(card->host); mmc_blk_remove_req(md); mmc_set_drvdata(card, NULL); +#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME + mmc_set_bus_resume_policy(card->host, 0); +#endif } #ifdef CONFIG_PM diff --git a/drivers/mmc/card/sdio_wifi.c b/drivers/mmc/card/sdio_wifi.c new file mode 100644 index 00000000..f7e82227 --- /dev/null +++ b/drivers/mmc/card/sdio_wifi.c @@ -0,0 +1,127 @@ +#include +#include +#include +#include +#include +#include + +#define WIFI_DEV_ENABLE 1 + +struct akwifi_platform_data pgpio; + +struct gpio_info * Akwifi_gpio_cs(void) +{ + printk("%s\n",__func__); + + if(pgpio.gpio_cs.pin > 0) + return &(pgpio.gpio_cs); + else + return NULL; +} +EXPORT_SYMBOL(Akwifi_gpio_cs); + + + +static int __devinit akplat_wifi_probe(struct platform_device *pdev) +{ + int ret; + struct akwifi_platform_data *pdata = pdev->dev.platform_data; + + printk("%s entered.\n", __func__); + if(!pdata) + { + ret = -EINVAL; + return ret; + } + if (pdata->gpio_cs.pin > 0) { + pgpio.gpio_cs = pdata->gpio_cs; + } + /* wifi reset */ + if (pdata->gpio_off.pin > 0) { + pdata->gpio_init(&pdata->gpio_off); + + msleep(pdata->power_off_delay); + printk("#### wifi power reset, %d(delay times)\n", + pdata->power_off_delay); + } + + /* akwifi power on */ + if (pdata->gpio_on.pin > 0) { + pdata->gpio_init(&pdata->gpio_on); + msleep(pdata->power_on_delay); + printk("wifi power on\n"); + } +// data->power_off(); +// data->power_on(); + return 0; +} + +static int __devexit akplat_wifi_remove(struct platform_device *pdev) +{ + struct akwifi_platform_data *pdata = pdev->dev.platform_data; + + printk("%s entered.\n", __func__); + if (pdata->gpio_off.pin > 0) { + pdata->gpio_init(&pdata->gpio_off); + + msleep(pdata->power_off_delay); + printk("wifi power off\n"); + } + +// data->power_off(); + return 0; +} + +static int akplat_wifi_suspend(struct platform_device *pdev, pm_message_t state) +{ +// struct wifi_power_platform_data *data = pdev->dev.platform_data; + + printk("%s entered.\n", __func__); + + //data->power_off(); + + return 0; +} + +static int akplat_wifi_resume(struct platform_device *pdev) +{ +// struct wifi_power_platform_data *data = pdev->dev.platform_data; + + printk("%s entered.\n", __func__); + + //data->power_on(); + + return 0; +} + +struct platform_device_id sdio_wifi_ids[] ={ + { + .name = "sdio_wifi_ar6302", + .driver_data = 0, + }, { }, +}; + +static struct platform_driver akplat_wifi_driver = { + .driver = { + .name = "anyka-wifi", + }, +// .id_table = sdio_wifi_ids, + .probe = akplat_wifi_probe, + .remove = akplat_wifi_remove, + .suspend = akplat_wifi_suspend, + .resume = akplat_wifi_resume, +}; + +static int __init sdio_wifi_init(void) +{ + return platform_driver_register(&akplat_wifi_driver); +} + +static void __exit sdio_wifi_exit(void) +{ + platform_driver_unregister(&akplat_wifi_driver); +} + +module_init(sdio_wifi_init); +module_exit(sdio_wifi_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig index ef103871..85c2e1ac 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig @@ -27,3 +27,20 @@ config MMC_CLKGATE support handling this in order for it to be of any use. If unsure, say N. + +config MMC_EMBEDDED_SDIO + boolean "MMC embedded SDIO device support (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + If you say Y here, support will be added for embedded SDIO + devices which do not contain the necessary enumeration + support in hardware to be properly detected. + +config MMC_PARANOID_SD_INIT + bool "Enable paranoid SD card initialization (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + If you say Y here, the MMC layer will be extra paranoid + about re-trying SD init requests. This can be a useful + work-around for buggy controllers and hardware. Enable + if you are experiencing issues with SD detection. diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index ba821fe7..9de87b7e 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -140,13 +141,25 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) cmd->retries = 0; } - if (err && cmd->retries && !mmc_card_removed(host->card)) { - /* - * Request starter must handle retries - see - * mmc_wait_for_req_done(). - */ - if (mrq->done) - mrq->done(mrq); + if ((err && cmd->retries && !mmc_card_removed(host->card)) + ||(mrq->data && mrq->data->error && mrq->data->retries)) { + if (err && cmd->retries) { + pr_debug("%s: req failed (CMD%u): %d, retrying...\n", + mmc_hostname(host), cmd->opcode, err); + + cmd->retries--; + cmd->error = 0; + host->ops->request(host, mrq); + } else { + printk("%s: data(%s) req failed (CMD%u): %d, retrying(%d)...\n", + mmc_hostname(host), + (mrq->data->flags & MMC_DATA_READ) ? "read" : "write", + cmd->opcode, mrq->data->error, mrq->data->retries); + + mrq->data->retries--; + mrq->data->error = 0; + host->ops->request(host, mrq); + } } else { mmc_should_fail_request(host, mrq); @@ -232,6 +245,7 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) mrq->cmd->data = mrq->data; mrq->data->error = 0; + mrq->data->retries = 3; mrq->data->mrq = mrq; if (mrq->stop) { mrq->data->stop = mrq->stop; @@ -1285,6 +1299,36 @@ static inline void mmc_bus_put(struct mmc_host *host) spin_unlock_irqrestore(&host->lock, flags); } +int mmc_resume_bus(struct mmc_host *host) +{ + unsigned long flags; + + if (!mmc_bus_needs_resume(host)) + return -EINVAL; + + printk("%s: Starting deferred resume\n", mmc_hostname(host)); + spin_lock_irqsave(&host->lock, flags); + host->bus_resume_flags &= ~MMC_BUSRESUME_NEEDS_RESUME; + host->rescan_disable = 0; + spin_unlock_irqrestore(&host->lock, flags); + + mmc_bus_get(host); + if (host->bus_ops && !host->bus_dead) { + mmc_power_up(host); + BUG_ON(!host->bus_ops->resume); + host->bus_ops->resume(host); + } + + if (host->bus_ops->detect && !host->bus_dead) + host->bus_ops->detect(host); + + mmc_bus_put(host); + printk("%s: Deferred resume completed\n", mmc_hostname(host)); + return 0; +} + +EXPORT_SYMBOL(mmc_resume_bus); + /* * Assign a mmc bus handler to a host. Only one bus handler may control a * host at any given time. @@ -1350,6 +1394,8 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay) spin_unlock_irqrestore(&host->lock, flags); #endif host->detect_change = 1; + + wake_lock(&host->detect_wake_lock); mmc_schedule_delayed_work(&host->detect, delay); } @@ -1478,6 +1524,10 @@ static unsigned int mmc_sd_erase_timeout(struct mmc_card *card, /* Erase timeout specified in SD Status Register (SSR) */ erase_timeout = card->ssr.erase_timeout * qty + card->ssr.erase_offset; + //printk("%s erase_timeout : %u\n", __FUNCTION__, erase_timeout); + //printk("%s card->ssr.erase_timeout : %u\n", __FUNCTION__, card->ssr.erase_timeout); + //printk("%s card->ssr.erase_offset : %u\n", __FUNCTION__, card->ssr.erase_offset); + } else { /* * Erase timeout not specified in SD Status Register (SSR) so @@ -1509,7 +1559,9 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, struct mmc_command cmd = {0}; unsigned int qty = 0; int err; - + int i = 0; + uint32_t start_jiffies = 0; + uint32_t timeout_jiffies = 0; /* * qty is used to calculate the erase timeout which depends on how many * erase groups (or allocation units in SD terminology) are affected. @@ -1535,11 +1587,13 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, qty += ((to / card->erase_size) - (from / card->erase_size)) + 1; +#if 0 if (!mmc_card_blockaddr(card)) { from <<= 9; to <<= 9; } - +#endif + if (mmc_card_sd(card)) cmd.opcode = SD_ERASE_WR_BLK_START; else @@ -1585,21 +1639,74 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, if (mmc_host_is_spi(card->host)) goto out; +#if 1 + start_jiffies = jiffies; + timeout_jiffies = msecs_to_jiffies(cmd.cmd_timeout_ms); + do { memset(&cmd, 0, sizeof(struct mmc_command)); cmd.opcode = MMC_SEND_STATUS; cmd.arg = card->rca << 16; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; /* Do not retry else we can't see errors */ - err = mmc_wait_for_cmd(card->host, &cmd, 0); + for(i = 0; i < 3; i++){ //adh:caohao 2018.11.23 + err = mmc_wait_for_cmd(card->host, &cmd, 0); + if(!err && !(cmd.resp[0] & 0xFDF92000)) + break; + } + if (err || (cmd.resp[0] & 0xFDF92000)) { + pr_err("error %d requesting status %#x\n", err, cmd.resp[0]); err = -EIO; goto out; } + + if ((jiffies - start_jiffies) > timeout_jiffies) { + pr_err("error timeout_jiffies=%u, start_jiffies=%u, now_jiffies=%lu\n", + timeout_jiffies, start_jiffies, jiffies); + err = -EBUSY; + goto out; + } } while (!(cmd.resp[0] & R1_READY_FOR_DATA) || R1_CURRENT_STATE(cmd.resp[0]) == R1_STATE_PRG); +#endif + +#if 0 +#define BLOCK_1M 2048 + + times = (to-from)/BLOCK_1M/10 + 2; + for(i=0;irca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + /* Do not retry else we can't see errors */ + err = mmc_wait_for_cmd(card->host, &cmd, 0); + + if (err || (cmd.resp[0] & 0xFDF92000)) { + pr_err("error %d requesting status %#x\n", + err, cmd.resp[0]); + err = -EIO; + goto out; + } + + if(!(!(cmd.resp[0] & R1_READY_FOR_DATA) || + R1_CURRENT_STATE(cmd.resp[0]) == R1_STATE_PRG)) + break; + + set_current_state(TASK_INTERRUPTIBLE); + msleep(50); + } + + if(i==times) + { + //printk("### return EBUSY\n"); + err = -EBUSY; + } +#endif out: return err; } @@ -2005,10 +2112,11 @@ EXPORT_SYMBOL(mmc_detect_card_removed); void mmc_rescan(struct work_struct *work) { - static const unsigned freqs[] = { 400000, 300000, 200000, 100000 }; + static const unsigned freqs[] = {300000, 200000, 100000 }; struct mmc_host *host = container_of(work, struct mmc_host, detect.work); int i; + bool extend_wakelock = false; if (host->rescan_disable) return; @@ -2025,6 +2133,12 @@ void mmc_rescan(struct work_struct *work) host->detect_change = 0; + /* If the card was removed the bus will be marked + * as dead - extend the wakelock so userspace + * can respond */ + if (host->bus_dead) + extend_wakelock = 1; + /* * Let mmc_bus_put() free the bus/bus_ops if we've found that * the card is no longer present. @@ -2049,16 +2163,24 @@ void mmc_rescan(struct work_struct *work) mmc_claim_host(host); for (i = 0; i < ARRAY_SIZE(freqs); i++) { - if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) + if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) { + extend_wakelock = true; break; + } if (freqs[i] <= host->f_min) break; } mmc_release_host(host); out: - if (host->caps & MMC_CAP_NEEDS_POLL) + if (extend_wakelock) + wake_lock_timeout(&host->detect_wake_lock, HZ / 2); + else + wake_unlock(&host->detect_wake_lock); + if (host->caps & MMC_CAP_NEEDS_POLL) { + wake_lock(&host->detect_wake_lock); mmc_schedule_delayed_work(&host->detect, HZ); + } } void mmc_start_host(struct mmc_host *host) @@ -2076,7 +2198,8 @@ void mmc_stop_host(struct mmc_host *host) spin_unlock_irqrestore(&host->lock, flags); #endif - cancel_delayed_work_sync(&host->detect); + if (cancel_delayed_work_sync(&host->detect)) + wake_unlock(&host->detect_wake_lock); mmc_flush_scheduled_work(); /* clear pm flags now and let card drivers set them as needed */ @@ -2272,7 +2395,11 @@ int mmc_suspend_host(struct mmc_host *host) { int err = 0; - cancel_delayed_work(&host->detect); + if (mmc_bus_needs_resume(host)) + return 0; + + if (cancel_delayed_work(&host->detect)) + wake_unlock(&host->detect_wake_lock); mmc_flush_scheduled_work(); err = mmc_cache_ctrl(host, 0); @@ -2322,6 +2449,12 @@ int mmc_resume_host(struct mmc_host *host) int err = 0; mmc_bus_get(host); + if (mmc_bus_manual_resume(host)) { + host->bus_resume_flags |= MMC_BUSRESUME_NEEDS_RESUME; + mmc_bus_put(host); + return 0; + } + if (host->bus_ops && !host->bus_dead) { if (!mmc_card_keep_power(host)) { mmc_power_up(host); @@ -2372,10 +2505,15 @@ int mmc_pm_notify(struct notifier_block *notify_block, case PM_SUSPEND_PREPARE: spin_lock_irqsave(&host->lock, flags); + if (mmc_bus_needs_resume(host)) { + spin_unlock_irqrestore(&host->lock, flags); + break; + } host->rescan_disable = 1; host->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT; spin_unlock_irqrestore(&host->lock, flags); - cancel_delayed_work_sync(&host->detect); + if (cancel_delayed_work_sync(&host->detect)) + wake_unlock(&host->detect_wake_lock); if (!host->bus_ops || host->bus_ops->suspend) break; @@ -2396,6 +2534,10 @@ int mmc_pm_notify(struct notifier_block *notify_block, case PM_POST_RESTORE: spin_lock_irqsave(&host->lock, flags); + if (mmc_bus_manual_resume(host)) { + spin_unlock_irqrestore(&host->lock, flags); + break; + } host->rescan_disable = 0; host->power_notify_type = MMC_HOST_PW_NOTIFY_LONG; spin_unlock_irqrestore(&host->lock, flags); @@ -2407,6 +2549,22 @@ int mmc_pm_notify(struct notifier_block *notify_block, } #endif +#ifdef CONFIG_MMC_EMBEDDED_SDIO +void mmc_set_embedded_sdio_data(struct mmc_host *host, + struct sdio_cis *cis, + struct sdio_cccr *cccr, + struct sdio_embedded_func *funcs, + int num_funcs) +{ + host->embedded_sdio_data.cis = cis; + host->embedded_sdio_data.cccr = cccr; + host->embedded_sdio_data.funcs = funcs; + host->embedded_sdio_data.num_funcs = num_funcs; +} + +EXPORT_SYMBOL(mmc_set_embedded_sdio_data); +#endif + static int __init mmc_init(void) { int ret; diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 91c84c7a..dd7b1203 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -329,6 +329,8 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) spin_lock_init(&host->lock); init_waitqueue_head(&host->wq); + wake_lock_init(&host->detect_wake_lock, WAKE_LOCK_SUSPEND, + kasprintf(GFP_KERNEL, "%s_detect", mmc_hostname(host))); INIT_DELAYED_WORK(&host->detect, mmc_rescan); #ifdef CONFIG_PM host->pm_notify.notifier_call = mmc_pm_notify; @@ -381,7 +383,8 @@ int mmc_add_host(struct mmc_host *host) mmc_host_clk_sysfs_init(host); mmc_start_host(host); - register_pm_notifier(&host->pm_notify); + if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY)) + register_pm_notifier(&host->pm_notify); return 0; } @@ -398,7 +401,9 @@ EXPORT_SYMBOL(mmc_add_host); */ void mmc_remove_host(struct mmc_host *host) { - unregister_pm_notifier(&host->pm_notify); + if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY)) + unregister_pm_notifier(&host->pm_notify); + mmc_stop_host(host); #ifdef CONFIG_DEBUG_FS @@ -425,6 +430,7 @@ void mmc_free_host(struct mmc_host *host) spin_lock(&mmc_host_lock); idr_remove(&mmc_host_idr, host->index); spin_unlock(&mmc_host_lock); + wake_lock_destroy(&host->detect_wake_lock); put_device(&host->class_dev); } diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index c272c686..f68e9e31 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -250,7 +250,7 @@ static int mmc_read_ssr(struct mmc_card *card) et = UNSTUFF_BITS(ssr, 402 - 384, 6); eo = UNSTUFF_BITS(ssr, 400 - 384, 2); if (es && et) { - card->ssr.erase_timeout = (et * 1000) / es; + card->ssr.erase_timeout = (et * 1000) / es + 500;//adh:caohao,2018.11.23,lager card tiemout 500ms card->ssr.erase_offset = eo * 1000; } } else { @@ -367,6 +367,7 @@ int mmc_sd_switch_hs(struct mmc_card *card) { int err; u8 *status; + int i = 0; if (card->scr.sda_vsn < SCR_SPEC_VER_1) return 0; @@ -389,7 +390,12 @@ int mmc_sd_switch_hs(struct mmc_card *card) return -ENOMEM; } - err = mmc_sd_switch(card, 1, 0, 1, status); + for(i = 0; i < 3; i++){ + err = mmc_sd_switch(card, 1, 0, 1, status); + if(!err) + break; + } + if (err) goto out; @@ -806,6 +812,9 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, bool reinit) { int err; +#ifdef CONFIG_MMC_PARANOID_SD_INIT + int retries; +#endif if (!reinit) { /* @@ -832,7 +841,26 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, /* * Fetch switch information from card. */ +#ifdef CONFIG_MMC_PARANOID_SD_INIT + for (retries = 1; retries <= 3; retries++) { + err = mmc_read_switch(card); + if (!err) { + if (retries > 1) { + printk(KERN_WARNING + "%s: recovered\n", + mmc_hostname(host)); + } + break; + } else { + printk(KERN_WARNING + "%s: read switch failed (attempt %d)\n", + mmc_hostname(host), retries); + } + } +#else err = mmc_read_switch(card); +#endif + if (err) return err; } @@ -1046,18 +1074,36 @@ static int mmc_sd_alive(struct mmc_host *host) */ static void mmc_sd_detect(struct mmc_host *host) { - int err; + int err = 0; +#ifdef CONFIG_MMC_PARANOID_SD_INIT + int retries = 5; +#endif BUG_ON(!host); BUG_ON(!host->card); - + mmc_claim_host(host); /* * Just check if our card has been removed. */ +#ifdef CONFIG_MMC_PARANOID_SD_INIT + while(retries) { + err = mmc_send_status(host->card, NULL); + if (err) { + retries--; + udelay(5); + continue; + } + break; + } + if (!retries) { + printk(KERN_ERR "%s(%s): Unable to re-detect card (%d)\n", + __func__, mmc_hostname(host), err); + } +#else err = _mmc_detect_card_removed(host); - +#endif mmc_release_host(host); if (err) { @@ -1096,12 +1142,31 @@ static int mmc_sd_suspend(struct mmc_host *host) static int mmc_sd_resume(struct mmc_host *host) { int err; +#ifdef CONFIG_MMC_PARANOID_SD_INIT + int retries; +#endif BUG_ON(!host); BUG_ON(!host->card); mmc_claim_host(host); +#ifdef CONFIG_MMC_PARANOID_SD_INIT + retries = 5; + while (retries) { + err = mmc_sd_init_card(host, host->ocr, host->card); + + if (err) { + printk(KERN_ERR "%s: Re-init card rc = %d (retries = %d)\n", + mmc_hostname(host), err, retries); + mdelay(5); + retries--; + continue; + } + break; + } +#else err = mmc_sd_init_card(host, host->ocr, host->card); +#endif mmc_release_host(host); return err; @@ -1155,6 +1220,9 @@ int mmc_attach_sd(struct mmc_host *host) { int err; u32 ocr; +#ifdef CONFIG_MMC_PARANOID_SD_INIT + int retries; +#endif BUG_ON(!host); WARN_ON(!host->claimed); @@ -1217,9 +1285,27 @@ int mmc_attach_sd(struct mmc_host *host) /* * Detect and init the card. */ +#ifdef CONFIG_MMC_PARANOID_SD_INIT + retries = 5; + while (retries) { + err = mmc_sd_init_card(host, host->ocr, NULL); + if (err) { + retries--; + continue; + } + break; + } + + if (!retries) { + printk(KERN_ERR "%s: mmc_sd_init_card() failure (err = %d)\n", + mmc_hostname(host), err); + goto err; + } +#else err = mmc_sd_init_card(host, host->ocr, NULL); if (err) goto err; +#endif mmc_release_host(host); err = mmc_add_card(host->card); diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c index 274ef00b..8dff8c13 100644 --- a/drivers/mmc/core/sd_ops.c +++ b/drivers/mmc/core/sd_ops.c @@ -308,6 +308,7 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group, struct mmc_command cmd = {0}; struct mmc_data data = {0}; struct scatterlist sg; + int i = 0; BUG_ON(!card); BUG_ON(!card->host); @@ -335,8 +336,11 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group, sg_init_one(&sg, resp, 64); mmc_set_data_timeout(&data, card); - - mmc_wait_for_req(card->host, &mrq); + for(i = 0; i < 3; i++){ + mmc_wait_for_req(card->host, &mrq); + if(!cmd.error) + break; + } if (cmd.error) return cmd.error; diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 13d0e953..b62b4fde 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -10,6 +10,7 @@ */ #include +#include #include #include @@ -28,6 +29,10 @@ #include "sdio_ops.h" #include "sdio_cis.h" +#ifdef CONFIG_MMC_EMBEDDED_SDIO +#include +#endif + static int sdio_read_fbr(struct sdio_func *func) { int ret; @@ -713,19 +718,35 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, goto finish; } - /* - * Read the common registers. - */ - err = sdio_read_cccr(card, ocr); - if (err) - goto remove; +#ifdef CONFIG_MMC_EMBEDDED_SDIO + if (host->embedded_sdio_data.cccr) + memcpy(&card->cccr, host->embedded_sdio_data.cccr, sizeof(struct sdio_cccr)); + else { +#endif + /* + * Read the common registers. + */ + err = sdio_read_cccr(card, ocr); + if (err) + goto remove; +#ifdef CONFIG_MMC_EMBEDDED_SDIO + } +#endif - /* - * Read the common CIS tuples. - */ - err = sdio_read_common_cis(card); - if (err) - goto remove; +#ifdef CONFIG_MMC_EMBEDDED_SDIO + if (host->embedded_sdio_data.cis) + memcpy(&card->cis, host->embedded_sdio_data.cis, sizeof(struct sdio_cis)); + else { +#endif + /* + * Read the common CIS tuples. + */ + err = sdio_read_common_cis(card); + if (err) + goto remove; +#ifdef CONFIG_MMC_EMBEDDED_SDIO + } +#endif if (oldcard) { int same = (card->cis.vendor == oldcard->cis.vendor && @@ -1124,14 +1145,36 @@ int mmc_attach_sdio(struct mmc_host *host) funcs = (ocr & 0x70000000) >> 28; card->sdio_funcs = 0; +#ifdef CONFIG_MMC_EMBEDDED_SDIO + if (host->embedded_sdio_data.funcs) + card->sdio_funcs = funcs = host->embedded_sdio_data.num_funcs; +#endif + /* * Initialize (but don't add) all present functions. */ for (i = 0; i < funcs; i++, card->sdio_funcs++) { - err = sdio_init_func(host->card, i + 1); - if (err) - goto remove; - +#ifdef CONFIG_MMC_EMBEDDED_SDIO + if (host->embedded_sdio_data.funcs) { + struct sdio_func *tmp; + + tmp = sdio_alloc_func(host->card); + if (IS_ERR(tmp)) + goto remove; + tmp->num = (i + 1); + card->sdio_func[i] = tmp; + tmp->class = host->embedded_sdio_data.funcs[i].f_class; + tmp->max_blksize = host->embedded_sdio_data.funcs[i].f_maxblksize; + tmp->vendor = card->cis.vendor; + tmp->device = card->cis.device; + } else { +#endif + err = sdio_init_func(host->card, i + 1); + if (err) + goto remove; +#ifdef CONFIG_MMC_EMBEDDED_SDIO + } +#endif /* * Enable Runtime PM for this func (if supported) */ @@ -1179,3 +1222,40 @@ err: return err; } +int sdio_reset_comm(struct mmc_card *card) +{ + struct mmc_host *host = card->host; + u32 ocr; + int err; + + printk("%s():\n", __func__); + mmc_claim_host(host); + + sdio_reset(host); + mmc_go_idle(host); + + mmc_set_clock(host, host->f_min); + + err = mmc_send_io_op_cond(host, 0, &ocr); + if (err) + goto err; + + host->ocr = mmc_select_voltage(host, ocr); + if (!host->ocr) { + err = -EINVAL; + goto err; + } + + err = mmc_sdio_init_card(host, host->ocr, card, 0); + if (err) + goto err; + + mmc_release_host(host); + return 0; +err: + printk("%s: Error resetting SDIO communications (%d)\n", + mmc_hostname(host), err); + mmc_release_host(host); + return err; +} +EXPORT_SYMBOL(sdio_reset_comm); diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index 236842ec..56d020e2 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c @@ -24,6 +24,10 @@ #include "sdio_cis.h" #include "sdio_bus.h" +#ifdef CONFIG_MMC_EMBEDDED_SDIO +#include +#endif + /* show configuration fields */ #define sdio_config_attr(field, format_string) \ static ssize_t \ @@ -263,7 +267,14 @@ static void sdio_release_func(struct device *dev) { struct sdio_func *func = dev_to_sdio_func(dev); - sdio_free_func_cis(func); +#ifdef CONFIG_MMC_EMBEDDED_SDIO + /* + * If this device is embedded then we never allocated + * cis tables for this func + */ + if (!func->card->host->embedded_sdio_data.funcs) +#endif + sdio_free_func_cis(func); if (func->info) kfree(func->info); diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c index 8f6f5ac1..01fa6e86 100644 --- a/drivers/mmc/core/sdio_io.c +++ b/drivers/mmc/core/sdio_io.c @@ -387,6 +387,39 @@ u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret) } EXPORT_SYMBOL_GPL(sdio_readb); +/** + * sdio_readb_ext - read a single byte from a SDIO function + * @func: SDIO function to access + * @addr: address to read + * @err_ret: optional status value from transfer + * @in: value to add to argument + * + * Reads a single byte from the address space of a given SDIO + * function. If there is a problem reading the address, 0xff + * is returned and @err_ret will contain the error code. + */ +unsigned char sdio_readb_ext(struct sdio_func *func, unsigned int addr, + int *err_ret, unsigned in) +{ + int ret; + unsigned char val; + + BUG_ON(!func); + + if (err_ret) + *err_ret = 0; + + ret = mmc_io_rw_direct(func->card, 0, func->num, addr, (u8)in, &val); + if (ret) { + if (err_ret) + *err_ret = ret; + return 0xFF; + } + + return val; +} +EXPORT_SYMBOL_GPL(sdio_readb_ext); + /** * sdio_writeb - write a single byte to a SDIO function * @func: SDIO function to access diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 2bc06e73..26560205 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -619,3 +619,13 @@ config MMC_USHC Note: These controllers only support SDIO cards and do not support MMC or SD memory cards. + +config MMC_ANYKA + tristate "ANYKA MMC/SD/SDIO Card Interface support" + depends on ARCH_AK39 + help + This selects a driver for the MCI interface found in ANYKA's CPUs. + If you have a board based on one of those and a MMC/SD + slot, say Y or M here. If unsure, say N. + + diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 3e7e26d0..7160d24c 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o +obj-$(CONFIG_MMC_ANYKA) +=akmci.o ifeq ($(CONFIG_CB710_DEBUG),y) CFLAGS-cb710-mmc += -DDEBUG diff --git a/drivers/mmc/host/akmci.c b/drivers/mmc/host/akmci.c new file mode 100644 index 00000000..25902f29 --- /dev/null +++ b/drivers/mmc/host/akmci.c @@ -0,0 +1,1896 @@ +/* + * linux/drivers/mmc/host/plat-anyka/akmci.c - Anyka MMC/SD/SDIO driver + * + * Copyright (C) 2010 Anyka, Ltd, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define DRIVER_NAME "akmci" +//#define DRIVER_NAME "ak-mci" + +#undef PDEBUG + +#define ___hdbg___() //printk("akmci:----func:%s---line:%d----\n", __func__, __LINE__); +#define DDREGS(host) //dbg_dumpregs(host, __func__, __LINE__) +#define DDDATA(h, d, s) //dbg_dumpdata(h, d, s) +#define HDBG(fmt, args...) //printk(fmt, ##args) + +//#define MCI_DBG + +#ifdef MCI_DBG +#ifdef __KERNEL__ +#define PDEBUG(fmt, args...) printk(KERN_INFO "akmci:" fmt, ##args) +#else +#define PDEBUG(fmt, args...) fprintf(stderr, "%s %d:" fmt,__FILE__, __LINE__, ## args) +#endif +#else +#define PDEBUG(fmt, args...) +#endif + +//#define READ_ONLY_SD +#define DMA_SIZE 512 + +#define USED_DMA(x) (((x) >= DMA_SIZE) && (!((x) % DMA_SIZE))) +#define USED_DMA_CPU(x) (((x) > DMA_SIZE) && ((x) % DMA_SIZE)) +#define USED_CPU(x) (((x) > 0) && ((x) < DMA_SIZE)) + +static struct kobject *gpio_pwr_obj = NULL; + +#define CARD_UNPLUGED 1 +#define CARD_PLUGED 0 +#define MCI_DATA_IRQ (1<<1) +#define MCI_CMD_IRQ (1<<0) +static unsigned int tf_err_count; +static unsigned int detect_time = (HZ/2); +static unsigned int retry_count = 100; +static unsigned int request_timeout = (5 * HZ); + +unsigned int slot_index = 0; +struct mmc_host *mci_host[MCI_SLOT_NUM] = {NULL}; + +static void akmci_l2xfer(struct akmci_host *host); +static void akmci_init_host_cfg(struct akmci_host *host); + + +static int validate_gpio_info (struct gpio_info *gpio) { + + /// 所有参数为零代表无效,通过该方法兼容旧版本 GPIO 配置选项。 + if(0 == gpio->pin + && 0 == gpio->pulldown + && 0 == gpio->pullup + && 0 == gpio->value + && 0 == gpio->dir + && 0 == gpio->int_pol + && 0 == gpio->pwr_dn){ + + gpio->pin = -1; + gpio->pulldown = -1; + gpio->pullup = -1; + gpio->value = 1; + gpio->dir = 1; + gpio->int_pol = -1; + gpio->pwr_dn = 1; + + } + return 1; +} + +/** + * @brief dump mci module register, use for debug. + * + * @author Hanyang + * @modify Lixinhai + * @date 2011-05-10 + * @param [in] *host information of data transmitted, including data buf pointer, data len . + * @return void. +*/ +static inline void dbg_dumpregs(struct akmci_host *host, + const char *prefix, int eflags) +{ + u32 clkcon, cmdarg, cmd, cmdrsp, rsp1, rsp2, rsp3, rsp4; + u32 dtimer, datlen, datcon, datcnt, stat, imask, dmamode, cpumode; + + clkcon = readl(host->base + MCI_CLK_REG); + cmdarg = readl(host->base + MCI_ARGUMENT_REG); + cmd = readl(host->base + MCI_COMMAND_REG); + cmdrsp = readl(host->base + MCI_RESPCMD_REG); + + rsp1 = readl(host->base + MCI_RESPONSE0_REG); + rsp2 = readl(host->base + MCI_RESPONSE1_REG); + rsp3 = readl(host->base + MCI_RESPONSE2_REG); + rsp4 = readl(host->base + MCI_RESPONSE3_REG); + + dtimer = readl(host->base + MCI_DATATIMER_REG); + datlen = readl(host->base + MCI_DATALENGTH_REG); + datcon = readl(host->base + MCI_DATACTRL_REG); + datcnt = readl(host->base + MCI_DATACNT_REG); + + stat = 0; //readl(host->base + MCI_STATUS_REG); + imask = readl(host->base + MCI_MASK_REG); + dmamode = readl(host->base + MCI_DMACTRL_REG); + cpumode = readl(host->base + MCI_FIFO_REG); + + PDEBUG("current prefix: %s (%d)\n", prefix, eflags); + + PDEBUG("clkcon:[%08x], cmdarg:[%08x], cmd:[%08x], cmdrsp:[%08x].\n", + clkcon, cmdarg, cmd, cmdrsp); + PDEBUG("rsp1:[%08x], rsp2:[%08x], rsp3:[%08x], rsp4:[%08x]\n", + rsp1, rsp2, rsp3, rsp4); + PDEBUG("dtimer:[%08x], datlen:[%08x], datcon:[%08x], datcnt:[%08x]\n", + dtimer, datlen, datcon, datcnt); + PDEBUG("stat:[%08x], imask:[%08x], dmamode:[%08x], cpumode:[%08x]\n", + stat, imask, dmamode, cpumode); +} + +/** + * @brief dump mci module read/write data, use for debug. + * + * @author Hanyang + * @modify Lixinhai + * @date 2011-05-10 + * @param [in] *host information of data transmitted, including data buf pointer, data len . + * @return void. +*/ +static inline void dbg_dumpdata(struct akmci_host *host, + void *data, int size) +{ + int ii; + int dsize = (size +3)/4; + u32 *dptr = data; + + printk("xfer data (size:%d):", size); + + for(ii = 0; ii < dsize; ii++) { + if((ii%10) == 0) + printk("\n"); + + printk("%08x ", *(dptr + ii)); + } + printk("\n"); +} + +/** + * the data transfer mode description. +*/ +static char* xfer_mode_desc[] = { + "unknown", + "l2dma", + "l2pio", + "inner pio", + }; + +/** + * the sd/mmc/sdio card detect mode description. +*/ +static char* detect_mode_desc[] = { + "plugin alway", + "GPIO detect", + "AD detect", +}; + +/** + * @brief initilize the module share pin. + * + * @author Hanyang + * @modify Lixinhai + * @date 2011-05-10 + * @param [in] *host information of data transmitted, including data buf pointer, data len . + * @return void. +*/ +static void akmci_init_sharepin(struct akmci_host *host) +{ + if(host->mci_mode == MCI_MODE_MMC_SD) { + ak_group_config(ePIN_AS_MCI); + } else { + ak_group_config(ePIN_AS_SDIO); + } +} + +static struct gpio_info mci0_one_line_idle[]= +{ + { + .pin = AK_GPIO_31, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_DISABLE, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + { + .pin = AK_GPIO_32, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_DISABLE, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, + + { + .pin = AK_GPIO_33, + .pulldown = -1, + .pullup = AK_PULLUP_DISABLE, + .value = AK_GPIO_DISABLE, + .dir = AK_GPIO_DIR_INPUT, + .int_pol = -1, + }, +}; + +static void akmci_init_sharepin_idle(struct akmci_host *host) +{ + /* set sd pin to gpio, input, power up disable*/ + if (0 == host->pdev->id) { //mci0 +#if 0 + unsigned int val; + /* set sd pin function to gpio */ + val = __raw_readl(AK_SHAREPIN_CON4); + pr_info("0x080000DC=%x\n",val); + val &= ~(0xf << 6); + __raw_writel(val, AK_SHAREPIN_CON4); + pr_info("set 0x080000DC=%x\n",val); + + /* set pull up disable */ + val = __raw_readl(AK_PPU_PPD4); + pr_info("0x080000E0=%x\n",val); + val |= (0x7 << 4); + __raw_writel(val, AK_PPU_PPD4); + pr_info("set 0x080000E0=%x\n",val); + +#else + int index; + for(index = 0; index < sizeof(mci0_one_line_idle)/sizeof(struct gpio_info); index ++) + ak_gpio_set(&mci0_one_line_idle[index]); + +#endif + } else { // mci1 + /// TODO + } +} + + +#define MCI_L2_ADDR(host) \ + ((host->mci_mode == MCI_MODE_MMC_SD) ? ADDR_MMC_SD : ADDR_SDIO) + +/** +* akmci_xfer_mode - judgement the mci transfer mode. +* ret: AKMCI_XFER_L2DMA: use for l2 dma mode +* AKMCI_XFER_L2PIO: use for l2 fifo mode. +* AKMCI_XFER_INNERPIO: use for inner fifo mode. +*/ +static inline int akmci_xfer_mode(struct akmci_host *host) +{ + return host->xfer_mode; +} + +static inline int enable_imask(struct akmci_host *host, u32 imask) +{ + u32 newmask; + + newmask = readl(host->base + MCI_MASK_REG); + newmask |= imask; + writel(newmask, host->base + MCI_MASK_REG); + + return newmask; +} + +static inline int disable_imask(struct akmci_host *host, u32 imask) +{ + u32 newmask; + + newmask = readl(host->base + MCI_MASK_REG); + newmask &= ~imask; + writel(newmask, host->base + MCI_MASK_REG); + + return newmask; +} + +static inline void clear_imask(struct akmci_host *host) +{ + u32 mask = readl(host->base + MCI_MASK_REG); + + /* preserve the SDIO IRQ mask state */ + mask &= MCI_SDIOINTMASK; + writel(mask, host->base + MCI_MASK_REG); +} + +/** + * @brief reset mmc/sd module. + * + * @author Hanyang + * @modify Lixinhai + * @date 2011-05-10 + * @param [in] *host information of data transmitted, + * including data buf pointer, data len . + * @return void. +*/ +static void akmci_reset(struct akmci_host *host) +{ + if(host->mci_mode == MCI_MODE_MMC_SD) { + ak_soft_reset(AK_SRESET_MMCSD); + } else { + ak_soft_reset(AK_SRESET_SDIO); + } +} + +/** + * @brief L2DMA ISR post-process + * @author Luo YongChuang + * @modify 2015-02-5 + * @return void + */ +void L2DMA_isr(unsigned long data) +{ + struct akmci_host *host = (struct akmci_host *)data; + if(host->sg_ptr==NULL){ + printk(KERN_ERR "##L2DMA_isr host->sg_ptr==null\n"); + return; + } + host->sg_ptr = sg_next(host->sg_ptr); + host->sg_len --; + host->sg_off = 0; + if(!host->sg_len) + return; + PDEBUG("###L2DMA_isr host->sg_len: %u\n", host->sg_len); + akmci_l2xfer(host); +} + +/** + * @brief L2DMA ISR post-process + * @author Luo YongChuang + * @modify 2015-02-5 + * @return void + */ +void L2DMA_res_isr(unsigned long data) +{ + struct akmci_host *host = (struct akmci_host *)data; + if(host->data==NULL){ + printk(KERN_ERR "##L2DMA_res_isr host->data==null\n"); + return; + } + akmci_l2xfer(host); +} + + + +/** + * @brief transmitting data. + * + * @author Hanyang + * @modify Lixinhai + * @date 2011-05-10 + * @param [in] *host information of data transmitted, + * including data buf pointer, data len . + * @return void. +*/ +static void akmci_l2xfer(struct akmci_host *host) +{ + u32 xferlen; + u8 dir; + u32 *tempbuf = NULL; + dma_addr_t phyaddr = 0; + unsigned int sg_num = 0; + + if (host->data->flags & MMC_DATA_WRITE) { + dir = MEM2BUF; + } else { + dir = BUF2MEM; + } + + xferlen = host->sg_ptr->length - host->sg_off; + sg_num = host->sg_len; + + if(xferlen==0){ + PDEBUG("##akmci_l2xfer %s xferlen==0\n", (host->data->flags & MMC_DATA_WRITE)?"write":"read"); + return; + } + + PDEBUG("###%s xferlen : %u, sg_num : %u\n",(host->data->flags & MMC_DATA_WRITE)?"write":"read", + xferlen, host->sg_len); + + if ((akmci_xfer_mode(host) == AKMCI_XFER_L2DMA) && + USED_DMA(xferlen)) + { + phyaddr = sg_dma_address(host->sg_ptr) + host->sg_off; + + host->sg_off += xferlen; + host->data_xfered += xferlen; + + if(sg_num>1){ + PDEBUG("##sg_num>1, set dam callback\n"); + l2_set_dma_callback(host->l2buf_id, L2DMA_isr, (unsigned long)(host)); + l2_combuf_dma(phyaddr, host->l2buf_id, xferlen, dir, AK_TRUE); + }else { + PDEBUG("##sg_num==1\n"); + l2_combuf_dma(phyaddr, host->l2buf_id, xferlen, dir, AK_FALSE); + } + } + else if((akmci_xfer_mode(host) == AKMCI_XFER_L2DMA) && + USED_DMA_CPU(xferlen)) + { + unsigned int need_callback=0; + PDEBUG("##akmci transfer data: DMA AND CPU mode.\n"); + phyaddr = sg_dma_address(host->sg_ptr) + host->sg_off; + + //l2 limit + //1. xferlen > 8192 + //2.xferlen < 8192 && xferlen %64 != 0 + //3.xferlen < 8192 && xferlen %64 ==0 + + if(xferlen > 8192){ + xferlen = (xferlen / 512) * 512; + need_callback = 1; + }else if((xferlen < 8192) && (xferlen % 64)){ + xferlen = (xferlen / 64) * 64; + need_callback = 1; + } + + host->sg_off += xferlen; + host->data_xfered += xferlen; + + if(need_callback){ + l2_set_dma_callback(host->l2buf_id, L2DMA_res_isr, (unsigned long)(host)); + l2_combuf_dma(phyaddr, host->l2buf_id, xferlen, dir, AK_TRUE); + }else{ + l2_combuf_dma(phyaddr, host->l2buf_id, xferlen, dir, AK_FALSE); + } + } + else + { + /* + * use for cpu transfer mode. + * */ + #if 0 + if((xferlen >= DMA_ONE_SHOT_LEN) && (xferlen % DMA_ONE_SHOT_LEN)){ + printk(KERN_ERR "xferlen > 64 bytes and not div by 64, transfer by cpu!\n"); + } + #endif + PDEBUG("##akmci transfer data: CPU mode.\n"); + tempbuf = sg_virt(host->sg_ptr) + host->sg_off; + + host->sg_off += xferlen; + host->data_xfered += xferlen; + l2_combuf_cpu((unsigned long)tempbuf, host->l2buf_id, xferlen, dir); + } + + /* debug info if data transfer error */ + if(host->data_err_flag > 0) { + PDEBUG("mci_xfer transfered: xferptr = 0x%p, xfer_offset=%d, xfer_bytes=%d\n", + sg_virt(host->sg_ptr)+host->sg_off, host->sg_off, host->data_xfered); + } + +} + + +void akmci_init_sg(struct akmci_host *host, struct mmc_data *data) +{ + /* + * Ideally, we want the higher levels to pass us a scatter list. + */ + host->sg_len = data->sg_len; + host->sg_ptr = data->sg; + host->sg_off = 0; +} + +int akmci_next_sg(struct akmci_host *host) +{ + host->sg_ptr++; + host->sg_off = 0; + return --host->sg_len; +} + +/** + * @brief stop data, close interrupt. + * + * @author Hanyang + * @modify Lixinhai + * @date 2011-05-10 + * @param [in] *host get the base address of resgister. + * @return void. + */ +static void akmci_stop_data(struct akmci_host *host) +{ + writel(0, host->base + MCI_DMACTRL_REG); + writel(0, host->base + MCI_DATACTRL_REG); + + disable_imask(host, MCI_DATAIRQMASKS|MCI_FIFOFULLMASK|MCI_FIFOEMPTYMASK); + + clear_bit(MCI_DATA_IRQ, &host->pending_events); + + if(akmci_xfer_mode(host) ==AKMCI_XFER_L2DMA) { + if (host->data->flags & MMC_DATA_WRITE) { + dma_sync_sg_for_cpu(mmc_dev(host->mmc), host->data->sg, host->data->sg_len, DMA_TO_DEVICE); + dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->data->sg_len, DMA_TO_DEVICE); + } else { + dma_sync_sg_for_cpu(mmc_dev(host->mmc), host->data->sg, host->data->sg_len, DMA_FROM_DEVICE); + dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->data->sg_len, DMA_FROM_DEVICE); + } + } + + host->sg_ptr = NULL; + host->sg_len = 0; + host->sg_off = 0; + + host->data = NULL; +} + +/** + * @brief finish a request,release resource. + * + * @author Hanyang + * @modify Lixinhai + * @date 2011-05-10 + * @param [in] *host information of sd controller. + * @param [in] *mrq information of request. + * @return void. + */ +static void akmci_request_end(struct akmci_host *host, struct mmc_request *mrq) +{ + writel(0, host->base + MCI_COMMAND_REG); + + //BUG_ON(host->data); + host->mrq = NULL; + host->cmd = NULL; + + if(host->data_err_flag > 0) { + akmci_reset(host); + + writel(MCI_ENABLE|MCI_FAIL_TRIGGER, host->base + MCI_CLK_REG); + writel(readl(host->base + MCI_CLK_REG)|host->clkreg, host->base + MCI_CLK_REG); + mdelay(10); + } + + if(host->l2buf_id != BUF_NULL) { + l2_free(MCI_L2_ADDR(host)); + host->l2buf_id = BUF_NULL; + } + + if (mrq->data) + mrq->data->bytes_xfered = host->data_xfered; + + + /* + * Need to drop the host lock here; mmc_request_done may call + * back into the driver... + */ + + mmc_request_done(host->mmc, mrq); + PDEBUG("finalize the mci request.\n"); + +#ifdef CONFIG_CPU_FREQ + /*if request fail,then mmc_request_done send request again, ak_mci_send_request + * not down freq_lock in interrupt,so not to unlock freq_lock. + */ + if (not_retry) { + up(&host->freq_lock); + } +#endif + +} + +#define BOTH_DIR (MMC_DATA_WRITE | MMC_DATA_READ) + +/** + * @brief config sd controller, start sending command. + * + * @author Hanyang + * @modify Lixinhai + * @date 2011-05-10 + * @param [in] *host information of sd controller. + * @param [in] *cmd information of cmd sended. + * @return void. + */ +static void akmci_start_command(struct akmci_host *host, struct mmc_command *cmd) +{ + unsigned int ccon; + + PDEBUG("mci send cmd: op %i arg 0x%08x flags 0x%08x.%s data.\n", + cmd->opcode, cmd->arg, cmd->flags, cmd->data ? "contain":"no"); + + writel(cmd->arg, host->base + MCI_ARGUMENT_REG); + /* enable mci cmd irq */ + enable_imask(host, MCI_CMDIRQMASKS); + + + ccon = MCI_CPSM_CMD(cmd->opcode) | MCI_CPSM_ENABLE; + if (cmd->flags & MMC_RSP_PRESENT) { + ccon |= MCI_CPSM_RESPONSE; + if (cmd->flags & MMC_RSP_136) + ccon |= MCI_CPSM_LONGRSP; + } + + if (cmd->data) + ccon |= MCI_CPSM_WITHDATA; + + host->cmd = cmd; + + ccon |= 1<<12;//512 clock command timeout + /* configurate cmd controller register */ + writel(ccon, host->base + MCI_COMMAND_REG); +} + + +static void print_mci_data_err(struct mmc_data *data, + unsigned int status, const char *err) +{ + if (data->flags & MMC_DATA_READ) { + printk("akmci: data(read) status=0x%x data_blocks=%d data_blocksize=%d %s\n", status, data->blocks, data->blksz, err); + } else if (data->flags & MMC_DATA_WRITE) { + printk("akmci: data(write) status=0x%x data_blocks=%d data_blocksize=%d %s\n", status, data->blocks, data->blksz, err); + } +} + +/* + * Handle completion of command and data transfers. + */ +static irqreturn_t akmci_irq(int irq, void *dev_id) +{ + struct akmci_host *host = dev_id; + u32 stat_mask; + u32 status; + int ret = 1; + + spin_lock(&host->lock); + status = readl(host->base + MCI_STATUS_REG); + PDEBUG("###akmci_irq status : %x\n", status); + + if (status & MCI_SDIOINT) { + /* + * must disable sdio irq ,than read status to clear the sdio status, + * else sdio irq will come again. + */ + mmc_signal_sdio_irq(host->mmc); + //status |= readl(host->base + MCI_STATUS_REG); + host->irq_status |= status; + } + + stat_mask = MCI_RESPCRCFAIL|MCI_RESPTIMEOUT|MCI_CMDSENT|MCI_RESPEND; + if ((status & stat_mask) && host->cmd){ + host->irq_status = status; + set_bit(MCI_CMD_IRQ, &host->pending_events); + } + + stat_mask = MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_DATAEND; + + if ((status & stat_mask) && host->data){ + host->irq_status = status; + set_bit(MCI_DATA_IRQ, &host->pending_events); + wake_up(&host->intr_data_wait); + } + + ret = 1; + spin_unlock(&host->lock); + + return IRQ_RETVAL(ret); + +} + +int get_card_status(struct akmci_host *host) +{ + unsigned int i, status[3], detect_retry_count = 0; + while (1) { + for (i = 0; i < 3; i++) { + status[i] = ak_gpio_getpin(host->gpio_cd); + udelay(10); + } + if ((status[0] == status[1]) && (status[0] == status[2])) + break; + + detect_retry_count++; + if (detect_retry_count >= 5) { + pr_err("this is a dithering,card detect error!"); + return -1; + } + } + return status[0]; +} + +/** + * @brief detect sdio card's level type . + * + * @author Hanyang + * @date 2011-05-10 + * @param [in] data getting the information of sd host. + * @return void. + */ +static void akmci_detect_change(unsigned long data) +{ + struct akmci_host *host = (struct akmci_host *)data; + struct ak_mci_platform_data *const plat = host->pdev->dev.platform_data; + struct gpio_info *const gpio_pwr = &plat->gpio_pwr; + int curr_status = 0; + + curr_status = get_card_status(host); + pr_debug("akmci_detect_change, curr_status=%d\n", curr_status); + if(curr_status < 0) + goto err; + if (curr_status != host->card_status) { + host->card_status = curr_status; + if (curr_status != CARD_UNPLUGED) { + //host->irq_cd_type = IRQ_TYPE_LEVEL_HIGH; + printk(KERN_INFO "card connected!\n"); + //ak_gpio_setpin(host->power.gpio, !host->power.pwr_dn); + + ak_gpio_setpin(gpio_pwr->pin, !gpio_pwr->pwr_dn); + akmci_reset(host); + akmci_init_host_cfg(host); + + } else{ + //host->irq_cd_type = IRQ_TYPE_LEVEL_LOW; + printk(KERN_INFO "card disconnected!\n"); + //ak_gpio_setpin(host->power.gpio, host->power.pwr_dn); + + ak_gpio_setpin(gpio_pwr->pin, gpio_pwr->pwr_dn); + akmci_init_sharepin_idle(host); + } + + if(host->mci_mode == MCI_MODE_MMC_SD) + tf_err_count = 0; + mmc_detect_change(host->mmc, msecs_to_jiffies(300)); + } + +err: + mod_timer(&host->detect_timer, jiffies + detect_time); + +// irq_set_irq_type(host->irq_cd, host->irq_cd_type); +// enable_irq(host->irq_cd); + +} + + +/** + * @brief detect the sdio card whether or not is in. + * + * @author Hanyang + * @date 2011-05-10 + * @param [in] *mmc information of host ,getting the sdio detect gpio. + * @return int. + * @retal 0:success. + */ +static int set_mci_plugin(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct akmci_host *host = container_of(nb, struct akmci_host, detect_nb); + + if(host->mci_mode == MCI_MODE_MMC_SD) { + if (val == ADDETECT_MMC_PLUGIN) + host->plugin_flag = 1; + else if (val == ADDETECT_MMC_PLUGOUT) + host->plugin_flag = 0; + } else { + if (val == ADDETECT_SDIO_PLUGIN) + host->plugin_flag = 1; + else if (val == ADDETECT_SDIO_PLUGOUT) + host->plugin_flag = 0; + } + + mmc_detect_change(host->mmc, 50); + return 0; +} + +static int akmci_enable(struct mmc_host *mmc) +{ + PDEBUG("akmci_enable:host is claimed.\n"); + return 0; +} + +static int akmci_disable(struct mmc_host *mmc) +{ + PDEBUG("akmci_disable:host is released.\n"); + return 0; +} + +/** + * @brief check the card that status present or not. + * + * @author Hanyang + * @date 2011-05-10 + * @param [in] *mmc information of host ,getting the sdio detect gpio. + * @return int. + * @retal 1 sdio card is in ;0 sdio card is not in + */ +static int akmci_card_present(struct mmc_host *mmc) +{ + struct akmci_host *host = mmc_priv(mmc); + + if(host->detect_mode == AKMCI_DETECT_MODE_AD) + { + return host->plugin_flag; + } + else if(host->detect_mode == AKMCI_DETECT_MODE_GPIO) + { + int ret = 1; + + if (host->gpio_cd >= 0) { + ret &= (ak_gpio_getpin (host->gpio_cd) == 0); + } + + if (host->power.gpio >= 0) { + ret &= (host->power.value != 0); ///< 判断电源启用 value=1 表示启用,这里区别与电平高低。 + } + + return ret; + } + else + { + return 1; //plugin alway. + } +} + +static int akmci_setup_data(struct akmci_host *host, struct mmc_data *data) +{ + unsigned int datactrl, dmacon; + unsigned int dma_sg_num; + + PDEBUG("%s: blksz %04x blks %04x flags %08x\n", + __func__, data->blksz, data->blocks, data->flags); + BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR); + + host->l2buf_id = l2_alloc(MCI_L2_ADDR(host));//alloc l2 buffer + if (BUF_NULL == host->l2buf_id) { + printk("L2 buffer malloc fail!\n"); + return -1; + //BUG(); + }else{ + PDEBUG("###L2 buffer id : %d\n", host->l2buf_id); + } + host->data = data; + host->size = data->blksz * data->blocks; + host->data_xfered = 0; + + PDEBUG("###dir : %s, data->blksz : %u, data->blocks : %u, host->size : %u\n", + (data->flags&MMC_DATA_READ)? "read" : "write", data->blksz, data->blocks, host->size); + + if (host->size > 64 * 1024) + pr_err("Err: %s %d akmci %s to long: %d.\n", __func__, __LINE__, + (data->flags & MMC_DATA_WRITE) ? "write":"read", host->size); + + akmci_init_sg(host, data); + + if(akmci_xfer_mode(host) == AKMCI_XFER_L2DMA) { + /* set dma addr */ + if (data->flags & MMC_DATA_WRITE) + dma_sg_num = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, DMA_TO_DEVICE); + else + dma_sg_num = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, DMA_FROM_DEVICE); + //printk("###dma_map_sg dma_sg_num : %u, data->sg_len : %u\n", dma_sg_num, data->sg_len); + } + + writel(TRANS_DATA_TIMEOUT, host->base + MCI_DATATIMER_REG); + writel(host->size, host->base + MCI_DATALENGTH_REG); + + if(akmci_xfer_mode(host) != AKMCI_XFER_INNERPIO) { + /*dma mode register*/ + dmacon = MCI_DMA_BUFEN | MCI_DMA_SIZE(MCI_L2FIFO_SIZE/4); + + if(akmci_xfer_mode(host) == AKMCI_XFER_L2DMA) { + dmacon |= MCI_DMA_EN; + } + writel(dmacon, host->base + MCI_DMACTRL_REG); + } + + /* + * enable mci data irq + * */ + enable_imask(host, MCI_DATAIRQMASKS); + + datactrl = MCI_DPSM_ENABLE; + + switch (host->bus_width) { + case MMC_BUS_WIDTH_8: + datactrl |= MCI_DPSM_BUSMODE(2); + break; + case MMC_BUS_WIDTH_4: + datactrl |= MCI_DPSM_BUSMODE(1); + break; + case MMC_BUS_WIDTH_1: + default: + datactrl |= MCI_DPSM_BUSMODE(0); + break; + } + + if (data->flags & MMC_DATA_STREAM) { + PDEBUG("STREAM Data\n"); + datactrl |= MCI_DPSM_STREAM; + } else { + PDEBUG("BLOCK Data: %u x %u\n", data->blksz, data->blocks); + datactrl |= MCI_DPSM_BLOCKSIZE(data->blksz); + datactrl &= ~MCI_DPSM_STREAM; + } + + if (data->flags & MMC_DATA_READ) + datactrl |= MCI_DPSM_DIRECTION; + else if (data->flags & MMC_DATA_WRITE) + datactrl &= ~MCI_DPSM_DIRECTION; + + /* configurate data controller register */ + writel(datactrl, host->base + MCI_DATACTRL_REG); + + PDEBUG("ENABLE DATA IRQ, datactrl: 0x%08x, timeout: 0x%08x, len: %u\n", + datactrl, readl(host->base + MCI_DATATIMER_REG), host->size); + return 0; + +} + + +static int akmci_wait_cmd_complete(struct akmci_host *host) +{ + int ret = 0; + unsigned int status; + struct mmc_command *cmd = host->cmd; + unsigned int cmd_retry_count = 0; + unsigned long flags; + unsigned long cmd_jiffies_timeout; + + cmd_jiffies_timeout = jiffies + request_timeout; + while(1){ + if (!time_before(jiffies, cmd_jiffies_timeout)) { + host->cmd->error = -ETIMEDOUT; + printk(KERN_ERR "##wait cmd request complete is timeout!"); + return -1; + } + + do { + spin_lock_irqsave(&host->lock, flags); + if (test_bit(MCI_CMD_IRQ, &host->pending_events)) { + status = host->irq_status; + spin_unlock_irqrestore(&host->lock, flags); + + cmd->resp[0] = readl(host->base + MCI_RESPONSE0_REG); + cmd->resp[1] = readl(host->base + MCI_RESPONSE1_REG); + cmd->resp[2] = readl(host->base + MCI_RESPONSE2_REG); + cmd->resp[3] = readl(host->base + MCI_RESPONSE3_REG); + + PDEBUG("resp[0]=0x%x, [1]=0x%x, resp[2]=0x%x, [3]=0x%x\n", + cmd->resp[0],cmd->resp[1],cmd->resp[2],cmd->resp[3]); + + if (status & MCI_RESPTIMEOUT) { + host->cmd->error = -ETIMEDOUT; + PDEBUG("CMD: send timeout\n"); + ret = -1; + } else if ((status & MCI_RESPCRCFAIL) && (host->cmd->flags & MMC_RSP_CRC)) { + host->cmd->error = -EILSEQ; + printk("CMD: illegal byte sequence\n"); + ret= -1; + } + /* disable mci cmd irq */ + disable_imask(host, MCI_CMDIRQMASKS); + clear_bit(MCI_CMD_IRQ, &host->pending_events); + + host->cmd = NULL; + + PDEBUG("end akmci_wait_cmd_complete ret=%d\n", ret); + + return ret; + } + spin_unlock_irqrestore(&host->lock, flags); + cmd_retry_count++; + } while (cmd_retry_count < retry_count); + schedule(); + } + +} + + + +static int akmci_wait_data_complete(struct akmci_host *host) +{ + unsigned int status; + struct mmc_data *data = host->data; + long time = request_timeout; + + time = wait_event_timeout(host->intr_data_wait, test_bit(MCI_DATA_IRQ, &host->pending_events), time); + /* disable mci data irq */ + disable_imask(host, MCI_DATAIRQMASKS|MCI_FIFOFULLMASK|MCI_FIFOEMPTYMASK); + + if(time <= 0){ + status = MCI_DATATIMEOUT; + printk(KERN_ERR "##wait data %s complete is timeout host->size: %u, host->data_xfered: %u!\n", + (host->data->flags & MMC_DATA_WRITE)?"write":"read", host->size, host->data_xfered); + }else{ + status = host->irq_status; + } + + if (status & MCI_DATAEND){ + if((akmci_xfer_mode(host) == AKMCI_XFER_L2DMA) && + (AK_FALSE == l2_combuf_wait_dma_finish(host->l2buf_id))){ + clear_bit(MCI_DATA_IRQ, &host->pending_events); + return 0; + } + if (data->flags & MMC_DATA_WRITE) + l2_clr_status(host->l2buf_id); + + if((host->size != host->data_xfered)&& + (USED_DMA(host->size) || USED_DMA_CPU(host->size))){ + printk(KERN_ERR "## %s host->size : %u , host->data_xfered : %u wait...\n", + (host->data->flags & MMC_DATA_WRITE)?"write":"read", host->size, host->data_xfered); + while(host->size != host->data_xfered){ + schedule(); + } + } + + }else if(status &(MCI_DATACRCFAIL|MCI_DATATIMEOUT)){ + if (status & MCI_DATACRCFAIL) { + data->error = -EILSEQ; + if(host->mci_mode==MCI_MODE_MMC_SD) + tf_err_count++; + print_mci_data_err(data, status, "illeage byte sequence"); + } else if (status & MCI_DATATIMEOUT) { + data->error = -ETIMEDOUT; + if(host->mci_mode==MCI_MODE_MMC_SD) + tf_err_count++; + print_mci_data_err(data, status, "transfer timeout"); + } + + host->data_err_flag = 1; + clear_bit(MCI_DATA_IRQ, &host->pending_events); + return -1; + }else{ + print_mci_data_err(data, status, "transfer data err!"); + } + + clear_bit(MCI_DATA_IRQ, &host->pending_events); + return 0; +} + +int akmci_get_tf_err_count(void) +{ + return tf_err_count; +} +EXPORT_SYMBOL_GPL(akmci_get_tf_err_count); + +void akmci_set_tf_err_count(int count) +{ + tf_err_count = count; +} +EXPORT_SYMBOL_GPL(akmci_set_tf_err_count); + +static void akmci_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct akmci_host *host = mmc_priv(mmc); + int ret; + host->mrq = mrq; + host->data_err_flag = 0; + + PDEBUG("start the mci request.\n"); + + if(host->card_status == CARD_UNPLUGED){ + printk("%s: no medium present.\n", __func__); + mrq->cmd->error = -ENOMEDIUM; + goto akmci_req_end; + } + + if(mrq->data){ + ret = akmci_setup_data(host, mrq->data); + if (ret) { + mrq->data->error = ret; + printk("data setup is error!"); + goto akmci_req_end; + } + + if ((mrq->data->flags & MMC_DATA_READ) && + (USED_DMA(host->size) || USED_DMA_CPU(host->size))){ + akmci_l2xfer(host); + } + } + + akmci_start_command(host, mrq->cmd); + ret = akmci_wait_cmd_complete(host); + if (ret) { + if(mrq->data) + akmci_stop_data(host); + goto akmci_req_end; + } + + if (!(mrq->data && !mrq->cmd->error)){ + goto akmci_req_end; + } + + if(mrq->data){ + if(mrq->data->flags & MMC_DATA_READ){ + PDEBUG("###MMC_DATA_READ\n"); + ret = akmci_wait_data_complete(host); + if(ret) + goto akmci_data_err; + if(USED_CPU(host->size)){ //cpu mode + PDEBUG("###want cpu mode, host->size : %d \n", host->size); + akmci_l2xfer(host); + } + } + else if(mrq->data->flags & MMC_DATA_WRITE){ + PDEBUG("###MMC_DATA_WRITE\n"); + akmci_l2xfer(host); + ret = akmci_wait_data_complete(host); + if(ret) + goto akmci_data_err; + } + + if (mrq->stop){ + PDEBUG("###write stop cmd\n"); + akmci_start_command(host, mrq->stop); + ret = akmci_wait_cmd_complete(host); + if (ret) { + goto akmci_data_err; + } + } + } + +akmci_data_err: + akmci_stop_data(host); + +akmci_req_end: + akmci_request_end(host, mrq); +} + +/** + * @brief setting the mmc module working clock. + * + * @author Hanyang + * @date 2011-05-10 + * @param [in] *mmc information of host ,getting the sdio detect gpio. + * @return int. + * @retal 0:success. otherwise :err. + */ +static void akmci_set_clk(struct akmci_host *host, struct mmc_ios *ios) +{ + u32 clk, div; + u32 clk_div_h, clk_div_l; + + if (ios->clock == 0) { + clk = readl(host->base + MCI_CLK_REG); + clk &= ~MCI_CLK_ENABLE; + writel(clk, host->base + MCI_CLK_REG); + + host->bus_clock = 0; + } else { + clk = readl(host->base + MCI_CLK_REG); + clk |= MCI_CLK_ENABLE;//|MCI_CLK_PWRSAVE; + clk &= ~0xffff; /* clear clk div */ + + div = host->asic_clk/ios->clock; + + if (host->asic_clk % ios->clock) + div += 1; + + div -= 2; + clk_div_h = div/2; + clk_div_l = div - clk_div_h; + + clk |= MMC_CLK_DIVL(clk_div_l) | MMC_CLK_DIVH(clk_div_h); + writel(clk, host->base + MCI_CLK_REG); + + host->bus_clock = host->asic_clk / ((clk_div_h+1)*(clk_div_l + 1)); + + PDEBUG("mmc clock is %lu Mhz. asic_clock is %ld MHz(div:l=%d, h=%d).\n", + ios->clock/MHz, host->asic_clk/MHz, clk_div_l, clk_div_h); + } + + host->clkreg = clk; +} + + +static void akmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct akmci_host *host = mmc_priv(mmc); + + switch(ios->power_mode) { + case MMC_POWER_ON: + PDEBUG("mci power on.\n"); + break; + case MMC_POWER_UP: + PDEBUG("mci power up.\n"); + break; + case MMC_POWER_OFF: + PDEBUG("mci power off.\n"); + break; + default: + break; + } + + host->bus_mode = ios->bus_mode; + host->bus_width = ios->bus_width; + + if(ios->clock != host->bus_clock) { + akmci_set_clk(host, ios); + } +} + +/** + * @brief detect the sdio card writing protection. + * + * @author Hanyang + * @date 2011-05-10 + * @param [in] *mmc information of host ,getting the sdio detect gpio. + * @return int. + * @retal 1 sdio card writing protected ;0 sdio card writing is not protected + */ +static int akmci_get_ro(struct mmc_host *mmc) +{ +#ifdef READ_ONLY_SD //change lyc del wp + struct akmci_host *host = mmc_priv(mmc); + + if (host->gpio_wp == -ENOSYS) + return -ENOSYS; + + return (ak_gpio_getpin(host->gpio_wp) == 0); +#else + return -ENOSYS; +#endif +} + +/** + * @brief enable or disable sdio interrupt, mmc host not use.. + * + * @author Hanyang + * @date 2011-05-10 + * @param [in] *mmc information of sd controller. + * @param [in] enable 1: enable; 0: disable. + * @return void. + */ + +static void akmci_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ + unsigned reg1,reg2; + unsigned long flags; + struct akmci_host *host = mmc_priv(mmc); +//sdmmc���������ߵ������ˣ�bug_on����ȥ������sdmmc��bug_on +// BUG_ON(host->mci_mode == MCI_MODE_SDIO); + + PDEBUG("%s the sdio interrupt.\n", enable ? "enable" : "disable"); + spin_lock_irqsave(&host->lock, flags); + + reg1 = readl(host->base + MCI_MASK_REG); + reg2 = readl(host->base + SDIO_INTRCTR_REG); + + if (enable) { + reg1 |= SDIO_INTR_ENABLE; + reg2 |= SDIO_INTR_CTR_ENABLE; + } else { + reg1 &= ~SDIO_INTR_ENABLE; + reg2 &= ~SDIO_INTR_CTR_ENABLE; + } + + writel(reg2, host->base + SDIO_INTRCTR_REG); + writel(reg1, host->base + MCI_MASK_REG); + spin_unlock_irqrestore(&host->lock, flags); +} + +int ak_sdio_rescan(int slot) +{ + struct mmc_host *mmc = mci_host[slot]; + + if (!mmc) { + pr_err("invalid mmc, please check the argument\n"); + return -EINVAL; + } + + mmc_detect_change(mmc, 0); + return 0; +} +EXPORT_SYMBOL_GPL(ak_sdio_rescan); + +/** + * register the function of sd/sdio driver. + * + */ +static struct mmc_host_ops akmci_ops = { + .enable = akmci_enable, + .disable = akmci_disable, + .request = akmci_request, + .set_ios = akmci_set_ios, + .get_ro = akmci_get_ro, + .get_cd = akmci_card_present, + .enable_sdio_irq = akmci_enable_sdio_irq, +}; + +/** + * @brief initilize the mmc host. + * + * @author Hanyang + * @date 2011-05-10 + * @param [in] *mmc information of sd controller. + * @return 0:success.. + */ +static int akmci_init_mmc_host(struct akmci_host *host) +{ + struct mmc_host *mmc = host->mmc; + struct ak_mci_platform_data *plat = host->plat; + mmc->ops = &akmci_ops; + + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + + if(plat->bus_width == MCI_BUS_WIDTH_4){ + if(host->pdev->id==0){ + printk(KERN_ERR "can not set mci0 to 4 line, because share with spi flash! force to 1 line\n"); + }else { + printk("%s use 4 line mode!\n",(host->mci_mode==MCI_MODE_SDIO)?"sdio":"mci"); + mmc->caps = MMC_CAP_4_BIT_DATA; + } + } +/*��mach�ļ�sdio�豸�����MCI_MODE_MMC_SD����sdio�жϣ������жϷ�ʽ����host��sdio�ж�*/ + if(host->mci_mode == MCI_MODE_SDIO){ + mmc->caps |= MMC_CAP_SDIO_IRQ; + } + if(plat->cap_highspeed) + mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; + +// mmc->caps |= MMC_CAP_NEEDS_POLL; + mmc->f_min = host->asic_clk / (255+1 + 255+1); + mmc->f_max = host->asic_clk / (0+1 + 0+1); + mmc->f_max = (mmc->f_max < plat->max_speed_hz) ? + mmc->f_max : plat->max_speed_hz; + + //support mmc erase + mmc->caps |= MMC_CAP_ERASE; + +#ifdef CONFIG_MMC_BLOCK_BOUNCE + /* use block bounce buffer. */ + mmc->max_segs = 1; +#else + /* We can do SGIO 128 */ + mmc->max_segs = MAX_MCI_REQ_SIZE/MAX_MCI_BLOCK_SIZE; +#endif + + /* + * Since we only have a 16-bit data length register, we must + * ensure that we don't exceed 2^16-1 bytes in a single request. + */ + //65536 + mmc->max_req_size = MAX_MCI_REQ_SIZE; + + /* + * Set the maximum segment size. Since we aren't doing DMA + * (yet) we are only limited by the data length register. + */ + //65536 + mmc->max_seg_size = mmc->max_req_size; + + mmc->max_blk_size = MAX_MCI_BLOCK_SIZE; //512 + + /*No limit on the number of blocks transferred.*/ + //128 + mmc->max_blk_count = mmc->max_req_size / MAX_MCI_BLOCK_SIZE; + return 0; +} + +static void akmci_init_host_cfg(struct akmci_host *host) +{ + akmci_init_sharepin(host); + /*enable the mci clock*/ + writel(MCI_ENABLE|MCI_FAIL_TRIGGER, host->base + MCI_CLK_REG); + + clear_imask(host); +} + + +#if defined(CONFIG_CPU_FREQ) + +static int akmci_cpufreq_transition(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct akmci_host *host; + struct mmc_host *mmc; + unsigned long newclk; + unsigned long flags; + struct cpufreq_freqs *freqs = (struct cpufreq_freqs *)data; + host = container_of(nb, struct ak_mci_host, freq_transition); + + PDEBUG("%s(): in_interrupt()=%ld\n", __func__, in_interrupt()); + PDEBUG("ak_get_asic_clk = %ld\n",ak_get_asic_clk()); + PDEBUG("freqs->new_cpufreq.asic_clk = %d\n", + freqs->new_cpufreq.asic_clk); + + mmc = host->mmc; + newclk = freqs->new_cpufreq.asic_clk; + if ((val == CPUFREQ_PRECHANGE && newclk > host->asic_clock) + || (val == CPUFREQ_POSTCHANGE && newclk < host->asic_clock)) + { + + if (mmc->ios.power_mode != MMC_POWER_OFF && + mmc->ios.clock != 0) + { + PDEBUG("%s(): preempt_count()=%d\n", __func__, preempt_count()); + + down(&host->freq_lock); + + spin_lock_irqsave(&mmc->lock, flags); + + host->asic_clock = newclk; + PDEBUG("MCI_CLK_REG1 = %d\n",readl(host->base + MCI_CLK_REG)); + aksdio_set_clk(host, &mmc->ios); + PDEBUG("MCI_CLK_REG2 = %d\n",readl(host->base + MCI_CLK_REG)); + + spin_unlock_irqrestore(&mmc->lock, flags); + + up(&host->freq_lock); + } + } + + return NOTIFY_DONE; +} + + +static inline int akmci_cpufreq_register(struct akmci_host *host) +{ + // use for requst and cpufreq + sema_init(&host->freq_lock, 1); + + host->freq_transition.notifier_call = akmci_cpufreq_transition; + + return cpufreq_register_notifier(&host->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +static inline void akmci_cpufreq_deregister(struct akmci_host *host) +{ + cpufreq_unregister_notifier(&host->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +#else +static inline int akmci_cpufreq_register(struct akmci_host *host) +{ + return 0; +} + +static inline void akmci_cpufreq_deregister(struct akmci_host *host) +{ +} +#endif + + +static ssize_t mmc_power_gpio_read(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + struct power_gpio_attribute *pga = container_of(attr, struct power_gpio_attribute, k_attr); + pr_debug("###mmc_power_gpio_read, value=%x\n",pga->value); + return sprintf(buf, "%d\n",pga->value); +} + +static ssize_t mmc_power_gpio_write(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + unsigned int gpio_value = 0; + struct power_gpio_attribute *pga = container_of(attr, struct power_gpio_attribute, k_attr); + struct akmci_host *host = container_of(pga, struct akmci_host, power); + struct ak_mci_platform_data *const plat = host->pdev->dev.platform_data; + struct gpio_info *const gpio_pwr = &plat->gpio_pwr; + + + sscanf(buf, "%d", &gpio_value); + pga->value = gpio_value; + pr_info ("## MMC Power: GPIO%02d -> %d, host->card_status=%d(1=CARD_UNPLUGED)\n", pga->gpio, gpio_value,host->card_status); + + if(host->card_status == CARD_PLUGED) + { + if (0 == gpio_value) + { + /// Power OFF. + pr_info("mmc_power_gpio_write, power off\n"); + ak_gpio_setpin(pga->gpio, gpio_pwr->pwr_dn); + akmci_init_sharepin_idle(host); + + } else { + /// Power On. + pr_info("mmc_power_gpio_write, power on\n"); + ak_gpio_setpin(pga->gpio, !gpio_pwr->pwr_dn); + akmci_init_host_cfg(host); + + } + + } + mmc_detect_change(host->mmc, msecs_to_jiffies(500)); + + return count; +} + + + +/** + * @brief sdio driver probe and init. + * + * @author Hanyang + * @date 2011-05-10 + * @param [in] *pdev information of platform device ,getting the sd driver resource . + * @return int. + * @retval -EINVAL no platform data , fail; + * @retval -EBUSY requset mem fail; + * @retval -ENOMEM alloc mem fail; + */ +static int __devinit akmci_probe(struct platform_device *pdev) +{ + struct akmci_host *host; + struct mmc_host *mmc; + int ret; + struct ak_mci_platform_data *const plat = pdev->dev.platform_data; + struct gpio_info *gpio_pwr = NULL; + + if(!plat) { + printk("not found mci platform data."); + ret = -EINVAL; + goto probe_out; + } + printk("akmci_probe : %s\n", plat->mci_mode?"SDIO":"MCI"); + gpio_pwr = &plat->gpio_pwr; + + mmc = mmc_alloc_host(sizeof(struct akmci_host), &pdev->dev); + if (!mmc) { + ret = -ENOMEM; + goto probe_out; + } + + mmc->pm_caps |= MMC_PM_KEEP_POWER; + host = mmc_priv(mmc); + host->mmc = mmc; + host->pdev = pdev; + + spin_lock_init(&host->lock); + + host->plat = plat; + host->mci_mode = plat->mci_mode; + host->data_err_flag = 0; + host->l2buf_id = BUF_NULL; + + host->gpio_wp = -ENOSYS; + host->gpio_cd = -ENOSYS; + host->detect_mode = plat->detect_mode; + host->xfer_mode = plat->xfer_mode; + host->power.gpio = -1; ///< 缺省电源控制无效。 + + akmci_reset(host); + + host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if(!host->mem) { + ret = -ENOENT; + goto probe_free_host; + } + + host->mem = request_mem_region(host->mem->start, + resource_size(host->mem), pdev->name); + + if (!host->mem) { + dev_err(&pdev->dev, "failed to request io memory region.\n"); + ret = -ENOENT; + goto probe_free_host; + } + + host->base = ioremap(host->mem->start, resource_size(host->mem)); + if (!host->base) { + dev_err(&pdev->dev, "failed to ioremap() io memory region.\n"); + ret = -EINVAL; + goto probe_free_mem_region; + } + + host->irq_mci = platform_get_irq(pdev, 0); + if(host->irq_mci == 0) { + dev_err(&pdev->dev, "failed to get interrupt resouce.\n"); + ret = -EINVAL; + goto probe_iounmap; + } + + init_waitqueue_head(&host->intr_data_wait); + + if (request_irq(host->irq_mci, akmci_irq, IRQF_DISABLED, pdev->name, host)) { + dev_err(&pdev->dev, "failed to request mci interrupt.\n"); + ret = -ENOENT; + goto probe_iounmap; + } + + /* We get spurious interrupts even when we have set the IMSK + * register to ignore everything, so use disable_irq() to make + * ensure we don't lock the system with un-serviceable requests. */ + //disable_irq(host->irq_mci); + + host->clk = clk_get(&pdev->dev, (host->mci_mode == MCI_MODE_MMC_SD)? "mci" : "sdio"); + if (IS_ERR(host->clk)) { + dev_err(&pdev->dev, "failed to find clock source.\n"); + ret = PTR_ERR(host->clk); + host->clk = NULL; + goto probe_free_irq; + } + + ret = clk_enable(host->clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable clock source.\n"); + goto clk_free; + } + + host->asic_clk = clk_get_rate(host->clk); + + ret = akmci_init_mmc_host(host); + if(ret) { + dev_err(&pdev->dev, "failed to init mmc host.\n"); + goto clk_disable; + } + + /*init mmc/sd host.*/ + akmci_init_host_cfg(host); + + /*use for gpio detect.*/ + if(host->detect_mode == AKMCI_DETECT_MODE_GPIO) { + if(plat->gpio_cd.pin >= 0) { + host->gpio_cd = plat->gpio_cd.pin; + plat->gpio_init(&plat->gpio_cd); + + host->card_status = get_card_status(host); + init_timer(&host->detect_timer); + host->detect_timer.function = akmci_detect_change; + host->detect_timer.data = (unsigned long)host; + host->detect_timer.expires = jiffies + detect_time; + add_timer(&host->detect_timer); + } + } + else if(host->detect_mode == AKMCI_DETECT_MODE_AD) { + memset(&host->detect_nb, 0, sizeof(host->detect_nb)); + host->detect_nb.notifier_call = set_mci_plugin; + addetect_register_client(&host->detect_nb); + }else if (host->detect_mode == AKMCI_PLUGIN_ALWAY) + { + host->card_status = CARD_PLUGED; + } + + if (validate_gpio_info (gpio_pwr)) { + + pr_info("## MMC Power: GPIO%02d", gpio_pwr->pin); + plat->gpio_init(gpio_pwr); +#if 0 + ak_gpio_setpin(gpio_pwr->pin, gpio_pwr->pwr_dn); + msleep(500); + ak_gpio_setpin(gpio_pwr->pin, !gpio_pwr->pwr_dn); + msleep(500); +#else + if(host->card_status == CARD_UNPLUGED) + { + /* power off*/ + pr_info("power off\n"); + ak_gpio_setpin(gpio_pwr->pin, gpio_pwr->pwr_dn); + akmci_init_sharepin_idle(host); + msleep(500); + }else + { + /* reset and power on*/ + pr_info("power on\n"); + //ak_gpio_setpin(gpio_pwr->pin, gpio_pwr->pwr_dn); + //msleep(500); + ak_gpio_setpin(gpio_pwr->pin, !gpio_pwr->pwr_dn); + msleep(500); + } +#endif + switch (pdev->id) { + case 0: + host->power.k_attr.attr.name = __stringify (0); + break; + case 1: + host->power.k_attr.attr.name = __stringify (1); + break; + default: + break; + } + + host->power.k_attr.attr.mode = 0666; + host->power.k_attr.show = mmc_power_gpio_read; + host->power.k_attr.store = mmc_power_gpio_write; + + host->power.gpio = gpio_pwr->pin; + host->power.value = gpio_pwr->value; + + ret = sysfs_create_file(gpio_pwr_obj, &host->power.k_attr.attr); + if (ret) { + pr_err("Create mmc_gpio sysfs file failed\n"); + kobject_put(gpio_pwr_obj); + } + } + +//#ifdef READ_ONLY_SD //change lyc del wp //change lyc del wp + if (plat->gpio_wp.pin >= 0) { + host->gpio_wp = plat->gpio_wp.pin; + plat->gpio_init(&plat->gpio_wp); + } +//#endif + + ret = akmci_cpufreq_register(host); + if (ret) { + goto detect_irq_free; + } + + platform_set_drvdata(pdev, mmc); + + ret = mmc_add_host(mmc); + if (ret) { + goto probe_cpufreq_free; + } + + dev_info(&pdev->dev, "Mci Interface driver.%s." + " using %s, %s IRQ. detect mode:%s.\n", + mmc_hostname(mmc), xfer_mode_desc[akmci_xfer_mode(host)], + mmc->caps & MMC_CAP_SDIO_IRQ ? "hw" : "sw", + detect_mode_desc[host->detect_mode]); + + mci_host[slot_index++] = host->mmc; + pr_info("slot_index=%d\n", slot_index); + + return 0; + +probe_cpufreq_free: + +detect_irq_free: + if(host->detect_mode == AKMCI_DETECT_MODE_GPIO) { //cdh:add for akmci free irq_cd only usr gpio + free_irq(host->irq_cd, host); + } +clk_disable: + clk_disable(host->clk); + +clk_free: + clk_put(host->clk); + +probe_free_irq: + free_irq(host->irq_mci, host); + +probe_iounmap: + iounmap(host->base); + +probe_free_mem_region: + release_mem_region(host->mem->start, resource_size(host->mem)); + +probe_free_host: + mmc_free_host(host->mmc); + +probe_out: + return ret; +} + +static int __devexit akmci_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct akmci_host *host; + struct ak_mci_platform_data *plat = pdev->dev.platform_data; + + mmc = platform_get_drvdata(pdev); + host = mmc_priv(mmc); + + mmc_remove_host(mmc); + + akmci_cpufreq_deregister(host); + + if(host->detect_mode == AKMCI_DETECT_MODE_AD) + addetect_unregister_client(&host->detect_nb); + + clk_disable(host->clk); + clk_put(host->clk); + + /* + *cdh:add for akmci free irq_cd only usr gpio + */ + if(host->detect_mode == AKMCI_DETECT_MODE_GPIO) { + free_irq(host->irq_cd, host); + } + + free_irq(host->irq_mci, host); + + iounmap(host->base); + release_mem_region(host->mem->start, resource_size(host->mem)); + + if (plat->gpio_pwr.pin >= 0) { + sysfs_remove_file (gpio_pwr_obj, &host->power.k_attr.attr); + } + + mmc_free_host(host->mmc); + slot_index = 0; + + return 0; +} + +#ifdef CONFIG_PM +static int akmci_suspend(struct device *dev) +{ + struct mmc_host *mmc = platform_get_drvdata(to_platform_device(dev)); + + return mmc_suspend_host(mmc); +} + +static int akmci_resume(struct device *dev) +{ + struct mmc_host *mmc = platform_get_drvdata(to_platform_device(dev)); + + return mmc_resume_host(mmc); +} + +static struct dev_pm_ops akmci_pm = { + .suspend = akmci_suspend, + .resume = akmci_resume +}; + +#define akmci_pm_ops &akmci_pm +#else +#define akmci_pm_ops NULL +#endif + +struct platform_device_id ak_mci_ids[] ={ + {.name = "ak_mci", .driver_data = MCI_MODE_MMC_SD,}, + {.name = "ak_sdio", .driver_data = MCI_MODE_SDIO,}, +}; + +static struct platform_driver akmci_driver = { + .probe = akmci_probe, + .remove = __devexit_p(akmci_remove), + .id_table = ak_mci_ids, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .pm = akmci_pm_ops, + }, + +}; + +/** + * @brief register mmc/sd module driver to platform + * + * @author Hanyang + * @date 2011-05-10 + * @return register status.. + */ +static int __init akmci_init(void) +{ + printk("AK MCI Driver (c) 2010 ANYKA\n"); + gpio_pwr_obj = kobject_create_and_add ("sdio_pwr", NULL); + if (!gpio_pwr_obj) { + printk ("cant craete mmc_pwr.\n"); + return -1; + } + return platform_driver_register(&akmci_driver); +} + +/** + * @brief release mmc/sd module from platform + * + * @author Hanyang + * @date 2011-05-10 + * @return void. + */ +static void __exit akmci_exit(void) +{ + platform_driver_unregister(&akmci_driver); + kobject_put (gpio_pwr_obj); + gpio_pwr_obj = NULL; + return; +} + + +module_init(akmci_init); +module_exit(akmci_exit); + +MODULE_DESCRIPTION("Anyka MCI Interface driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Anyka"); + diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1fe0ca9c..4b970ade 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -680,11 +680,8 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd) break; } - if (count >= 0xF) { - pr_warning("%s: Too large timeout requested for CMD%d!\n", - mmc_hostname(host->mmc), cmd->opcode); + if (count >= 0xF) count = 0xE; - } return count; } diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index f9013542..c7cd5914 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -5,6 +5,7 @@ # Core functionality. obj-$(CONFIG_MTD) += mtd.o mtd-y := mtdcore.o mtdsuper.o mtdconcat.o mtdpart.o +# mtd-y += akfha_char.o obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 4cdb2af7..12f6da2a 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -334,4 +334,30 @@ config MTD_DOCPROBE_55AA LinuxBIOS or if you need to recover a DiskOnChip Millennium on which you have managed to wipe the first block. +config MTD_AK_SPIFLASH + tristate "Support Anyka SPI Flash chips(Most chips)" + depends on SPI_MASTER + help + Anyka driver enables access to most SPI flash chips, used for + program and data storage. + + Set up your spi devices with the right board-specific platform data, + if you want to specify device partitioning or to use a device which + doesn't support the JEDEC ID instruction. +config MTD_AK_SPINAND + tristate "Support Anyka SPI NandFlash chips(Most chips)" + depends on SPI_MASTER + help + Anyka driver enables access to most SPI Nandflash chips, used for + program and data storage. + + Set up your spi devices with the right board-specific platform data, + if you want to specify device partitioning or to use a device which + doesn't support the JEDEC ID instruction. +config MTD_SPINAND_PRODUCER + bool "spinand_producer" + depends on MTD_AK_SPINAND + help + This turns on low-level debugging for the entire MTD sub-system. + Normally, you should say 'N' endmenu diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index a4dd1d82..406f8d4b 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -19,5 +19,8 @@ obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o obj-$(CONFIG_MTD_M25P80) += m25p80.o obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o obj-$(CONFIG_MTD_SST25L) += sst25l.o +obj-$(CONFIG_MTD_AK_SPIFLASH) += ak_spiflash.o +obj-$(CONFIG_MTD_AK_SPINAND) += ak_spi_nandflash.o +obj-y += ak_partition_table.o +CFLAGS_docg3.o += -I$(src) -CFLAGS_docg3.o += -I$(src) \ No newline at end of file diff --git a/drivers/mtd/devices/ak_partition_table.c b/drivers/mtd/devices/ak_partition_table.c new file mode 100644 index 00000000..207e78f9 --- /dev/null +++ b/drivers/mtd/devices/ak_partition_table.c @@ -0,0 +1,435 @@ + /** + * @file /driver/mtd/devices/ak_SPIFlash.c + * @brief SPI Flash driver for Anyka AK37 platform. + * Copyright C 2012 Anyka CO.,LTD + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * @author She Shaohua + * @date 2012-03-23 + * @note 2011-03-20 created + * @note 2011-03-23 Debug OK. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct kobject *partition_table_kobj; +spinlock_t partition_lock; + + +/** +* @brief ak spi flash partition table sys kobject attr show +* +* ak spi flash partition table sys kobject attr show +* @author SheShaohua +* @date 2012-03-20 +* @param[in] kobj: sys kobject +* @param[in] attr: kobject show +* @param[in] buf: kobject attr parameter show +* @return ssize_t +* @retval return string len on success +* @retval return zero error code if failed +*/ +static ssize_t partition_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + unsigned long len = 0; + unsigned long flags; + int ret = 0; + void *handle ; + T_EX_PARTITION_CONFIG ex_arrt_buf; + T_PARTITION_TABLE_INFO *phandle = NULL; + T_BIN_CONFIG ex_binarrt_lenbuf = {0}; + T_FS_CONFIG ex_fsarrt_lenbuf = {0}; + + spin_lock_irqsave(&partition_lock, flags); + + /* search open the same name partition */ + handle = partition_open((unsigned char *)kobj->name); + if (handle == NULL) { + spin_unlock_irqrestore(&partition_lock, flags); + printk(KERN_ERR "%s, open partition error!\n", __func__); + return -ENOENT; + } + + /* force tansmit handle type for get file type */ + phandle = (T_PARTITION_TABLE_INFO *)handle; + + /* search the partition ext 16B params */ + ret = partition_get_attr(handle, &ex_arrt_buf); + if (ret) { + printk(KERN_ERR "%s, open partition error!\n", __func__); + goto show_exit; + } + + /* transfer the ext struct format */ + if (phandle->partition_info.type == PART_FS_TYPE) { + memcpy(&ex_fsarrt_lenbuf, &ex_arrt_buf, sizeof(T_EX_PARTITION_CONFIG)); + }else { + memcpy(&ex_binarrt_lenbuf, &ex_arrt_buf, sizeof(T_EX_PARTITION_CONFIG)); + } + + /* first step: show partition infor transfer file_length data to char string */ + if (strcmp(attr->attr.name, "type") == 0){ + len = sprintf(buf, "%x:", phandle->partition_info.type); + } + + if (strcmp(attr->attr.name, "r_w_flag") == 0){ + len = sprintf(buf, "%x:", phandle->partition_info.r_w_flag); + } + + if (strcmp(attr->attr.name, "hidden_flag") == 0){ + len = sprintf(buf, "%x:", phandle->partition_info.hidden_flag); + } + + if (strcmp(attr->attr.name, "name") == 0){ + len = sprintf(buf, "%s:", phandle->partition_info.name); + } + + if (strcmp(attr->attr.name, "ksize") == 0){ + len = sprintf(buf, "%lx:", phandle->partition_info.ksize); + } + + if (strcmp(attr->attr.name, "start_pos") == 0){ + len = sprintf(buf, "%lx:", phandle->partition_info.start_pos); + } + + /* second step: show ext partition infor transfer file_length data to char string */ + if (strcmp(attr->attr.name, "file_length") == 0){ + if (phandle->partition_info.type == PART_FS_TYPE) { + len = sprintf(buf, "%lx:", ex_fsarrt_lenbuf.file_length); + }else { + len = sprintf(buf, "%lx:", ex_binarrt_lenbuf.file_length); + } + } + + if (strcmp(attr->attr.name, "ld_addr") == 0){ + if (phandle->partition_info.type != PART_FS_TYPE) { + len = sprintf(buf, "%lx:", ex_binarrt_lenbuf.ld_addr); + } + } + + if (strcmp(attr->attr.name, "mtd_index") == 0) { + if (phandle->partition_info.type == PART_FS_TYPE) { + len = sprintf(buf, "%x:", ex_fsarrt_lenbuf.mtd_idex); + }else { + len = sprintf(buf, "%x:", ex_binarrt_lenbuf.mtd_idex); + } + } + +show_exit: + /* search open the same name partition */ + ret = partition_close(handle); + if (ret) { + spin_unlock_irqrestore(&partition_lock, flags); + printk(KERN_ERR "%s, close partition error!\n", __func__); + return -ENOENT; + } + + spin_unlock_irqrestore(&partition_lock, flags); + + + return len; +} + +/** +* @brief ak spi flash partition table sys kobject attr store +* +* ak spi flash partition table sys kobject attr store +* @author SheShaohua +* @date 2012-03-20 +* @param[in] kobj: sys kobject +* @param[in] attr: kobject store +* @param[in] buf: kobject attr parameter store +* @param[in] count: attr parameter store cnt +* @return ssize_t +* @retval return parameter store cnt on success +* @retval return zero error code if failed +*/ +static ssize_t partition_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) +{ + unsigned long file_len =0; + unsigned long flags; + int ret = 0; + void * handle ; + T_EX_PARTITION_CONFIG ex_arrt_buf; + T_PARTITION_TABLE_INFO *phandle = NULL; + T_BIN_CONFIG ex_binarrt_lenbuf; + T_FS_CONFIG ex_fsarrt_lenbuf; + + sscanf(buf, "%lx", &file_len); + + spin_lock_irqsave(&partition_lock, flags); + + /* search the same name partition */ + handle = partition_open((unsigned char *)kobj->name); + if (handle == NULL) { + spin_unlock_irqrestore(&partition_lock, flags); + printk(KERN_ERR "%s, open partition error!\n", __func__); + return -ENOENT; + } + + phandle = (T_PARTITION_TABLE_INFO *)handle; + + /* search the partition ext 16B params */ + ret = partition_get_attr(handle, &ex_arrt_buf); + if (ret) { + printk(KERN_ERR "%s, get attr error!\n", __func__); + goto store_exit; + } + + /* transfer the ext struct format */ + if (phandle->partition_info.type == PART_FS_TYPE) { + memcpy(&ex_fsarrt_lenbuf, &ex_arrt_buf, sizeof(T_EX_PARTITION_CONFIG)); + }else { + memcpy(&ex_binarrt_lenbuf, &ex_arrt_buf, sizeof(T_EX_PARTITION_CONFIG)); + } + + /* transfer file_length data to char string */ + if (strcmp(attr->attr.name, "file_length") == 0){ + printk("cdh:%s, store attr.name:%s!\n", __func__, attr->attr.name); + if (phandle->partition_info.type == PART_FS_TYPE) { + ex_fsarrt_lenbuf.file_length = file_len; + memcpy(&ex_arrt_buf, &ex_fsarrt_lenbuf, sizeof(T_EX_PARTITION_CONFIG)); + }else { + ex_binarrt_lenbuf.file_length = file_len; + memcpy(&ex_arrt_buf, &ex_binarrt_lenbuf, sizeof(T_EX_PARTITION_CONFIG)); + } + } + + + /* search the partition ext 16B params */ + ret = partition_set_attr(handle, &ex_arrt_buf); + if (ret) { + printk(KERN_ERR "%s, set attr error!\n", __func__); + goto store_exit; + } + +store_exit: + /* search open the same name partition */ + ret = partition_close(handle); + if (ret) { + spin_unlock_irqrestore(&partition_lock, flags); + printk(KERN_ERR "%s, close partition error!\n", __func__); + return -ENOENT; + } + + spin_unlock_irqrestore(&partition_lock, flags); + + return count; +} + + +static struct kobj_attribute type_attribute = + __ATTR(type, 0666, partition_show, NULL); + +static struct kobj_attribute r_w_flag_attribute = + __ATTR(r_w_flag, 0666, partition_show, NULL); + +static struct kobj_attribute hidden_flag_attribute = + __ATTR(hidden_flag, 0666, partition_show, NULL); + +static struct kobj_attribute name_attribute = + __ATTR(name, 0666, partition_show, NULL); + +static struct kobj_attribute ksize_attribute = + __ATTR(ksize, 0666, partition_show, NULL); + +static struct kobj_attribute start_pos_attribute = + __ATTR(start_pos, 0666, partition_show, NULL); + +static struct kobj_attribute file_length_attribute = + __ATTR(file_length, 0666, partition_show, partition_store); + +static struct kobj_attribute ld_addr_attribute = + __ATTR(ld_addr, 0666, partition_show, NULL); + +static struct kobj_attribute mtd_index_attribute = + __ATTR(mtd_index, 0666, partition_show, NULL); + + +static struct attribute *partition_attribute[] = { + &type_attribute.attr, + &r_w_flag_attribute.attr, + &hidden_flag_attribute.attr, + &name_attribute.attr, + &ksize_attribute.attr, + &start_pos_attribute.attr, + NULL +}; + +static struct attribute *binfile_attribute[] = { + &file_length_attribute.attr, + &ld_addr_attribute.attr, + &mtd_index_attribute.attr, + NULL +}; + +static struct attribute *filefs_attribute[] = { + &file_length_attribute.attr, + &mtd_index_attribute.attr, + NULL +}; + +static struct attribute_group partition_attr_group = { + .attrs = partition_attribute, +}; + +static struct attribute_group binfile_attr_group = { + .attrs = binfile_attribute, +}; + +static struct attribute_group filefs_attr_group = { + .attrs = filefs_attribute, +}; + + +/** +* @brief ak spi flash partition table sys kobject create +* +* create ak spi flash partition table sys kobject for supply interface to app aplication +* @author SheShaohua +* @date 2012-03-20 +* @param[in] part_tab: partition tab buffer pointer +* @return int return write success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +int ak_partition_table_sys_create(T_PARTITION_TABLE_CONFIG *part_tab) +{ + int i; + int ret = 0; + int error; + unsigned long nr_parts; + T_PARTITION_TABLE_INFO *parts = NULL; + struct kobject *tmp_kobj; + unsigned char part_name[PARTITION_NAME_LEN + 1]; + + nr_parts = *(unsigned long *)part_tab->table; + + /* + * if no partiton to mount, the buf will be all 0xFF but not constant. + * So, it is not safe here. + */ + // printk("nr_parts=0x%lx\n", nr_parts); + if (nr_parts <= 0 || nr_parts > 15) { + printk(KERN_ERR "partition count invalid\n"); + ret = -EINVAL; + goto err_out1; + } + + parts = (T_PARTITION_TABLE_INFO *)(&part_tab->table[sizeof(unsigned long)]); + for (i=0; i +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +//nclude +//nclude +#include +#include +#include +#include +#include +#include +#include + + +//#define SPINAND_DEBUG + +#ifdef SPINAND_DEBUG +#define PDEBUG(fmt, args...) printk( KERN_ALERT fmt,## args) +#define DEBUG(n, args...) printk(KERN_ALERT args) +#else +#define PDEBUG(fmt, args...) +#define DEBUG(n, args...) +#endif + + +#define UPDATE_NULL "" // NO UPDATA +#define UPDATE_ING "U_ING" //UPDATE ING +#define UPDATE_END "U_END" //UPDATE FINISH +#define UPDATE_ERR "U_ERR" //UPDATE ERROR, is not to update + + + + +#define FHA_SUCCESS 0 +#define FHA_FAIL -1 + + + +#define FEATURE_ECC_EN (1<<4) //(1<<5) + + + +//#define FLASH_BUF_SIZE (32*1024) +#define FLASH_BUF_SIZE (flash->info.page_size) + +#define SPI_FLASH_READ 1 +#define SPI_FLASH_WRITE 2 +#define BAD_BLOCK_MARK_OFFSET 4 +//#define CONFIG_SPINAND_USE_FAST_READ 1 + +/*mtd layer allocate memory use for 'vmalloc' interface, need to convert.*/ +//#define SPINAND_USE_MTD_BLOCK_LAYER + + +#define OPCODE_RESET 0xff +#define OPCODE_WREN 0x06 /* Write Enable */ +#define OPCODE_WRDI 0x04 /* Write Disable */ + +#define OPCODE_RDSR1 0x0f /* Read Status Register1 */ +#define OPCODE_RDSR2 0x35 /* Read Status Register2 */ +#define OPCODE_RDSR3 0x15 /* Read Status Register3 */ +#define OPCODE_WRSR1 0x1f /* Write Status Register */ +#define OPCODE_WRSR2 0x31 /* Write Status2 Register*/ +#define OPCODE_WRSR3 0x11 /* Write Status3 Register*/ + +#define OPCODE_READ_TO_CACHE 0x13 +#define OPCODE_NORM_READ 0x03 /* Read Data Bytes */ +#define OPCODE_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */ +#define OPCODE_FAST_D_READ 0x3b /* Read Data Bytes at Dual output */ +#define OPCODE_FAST_Q_READ 0x6b /* Read Data Bytes at Quad output */ +#define OPCODE_FAST_D_IO 0xbb /* Read Data Bytes at Dual i/o */ +#define OPCODE_FAST_Q_IO 0xeb /* Read Data Bytes at Quad i/o */ + +#define OPCODE_PP 0x02 /* Page Program */ +#define OPCODE_PP_DUAL 0x12 /* Dual Page Program*/ +#define OPCODE_PP_QUAD 0x32 /* Quad Page Program*/ +#define OPCODE_2IO_PP 0x18 /* 2I/O Page Program (tmp)*/ +#define OPCODE_4IO_PP 0x38 /* 4I/O Page Program*/ +#define OPCODE_PP_EXEC 0x10 + +#define OPCODE_BE_4K 0x20 /* Sector (4K) Erase */ +#define OPCODE_BE_32K 0x52 /* Block (32K) Erase */ +#define OPCODE_BE_64K 0xd8 /* Block (64K) Erase */ +#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ +#define OPCODE_ERASE_BLOCK 0xd8 /* block Erase */ +#define OPCODE_RDID 0x9f /* Read JEDEC ID */ +#define OPCODE_DP 0xb9 /* Deep Power-down */ +#define OPCODE_RES 0xab /* Release from DP, and Read Signature */ + + +#define SPI_STATUS_REG1 1 +#define SPI_STATUS_REG2 2 + + +/* Define max times to check status register before we give up. */ +#define MAX_READY_WAIT_JIFFIES (40 * HZ) /* 40s max chip erase */ + +#define CMD_SIZE (1) +#define ADDR_SIZE (2) +#define CMD_ADDR_SIZE (CMD_SIZE + ADDR_SIZE) +#define MAX_DUMMY_SIZE (4) + +#define MTD_PART_NAME_LEN (4) + +#ifdef CONFIG_SPINAND_USE_FAST_READ +#define OPCODE_READ OPCODE_FAST_READ +#define FAST_READ_DUMMY_BYTE 1 +#else +#define OPCODE_READ OPCODE_NORM_READ +#define FAST_READ_DUMMY_BYTE 0 +#endif + +#define SPINAND_OOB_LEN(info) \ + ((info.oob_up_skiplen + info.oob_seglen + info.oob_down_skiplen)*info.oob_seg_perpage) + +#define ALIGN_DOWN(a, b) (((a) / (b)) * (b)) + +#define SPINAND_BIN_PAGE_START (62) +/****************************************************************************/ +struct partitions +{ + char name[MTD_PART_NAME_LEN]; + unsigned long long size; + unsigned long long offset; + unsigned int mask_flags; +}__attribute__((packed)); + +typedef struct +{ + T_U32 BinPageStart; /*bin data start addr*/ + T_U32 PageSize; /*spi page size*/ + T_U32 PagesPerBlock;/*page per block*/ + T_U32 BinInfoStart; + T_U32 FSPartStart; +} +T_SPI_BURN_INIT_INFO; + + + +T_PARTITION_INFO g_partition_info = {0}; +T_PARTITION_TABLE_CONFIG g_part_tab; +static unsigned long kernel_start_page = 0; +static unsigned long kernel_end_page = 0; + +static unsigned long rtf_start_page = 0; +static unsigned long rtf_end_page = 0; + + +int ak_check_error_to_update_flag_and_reboot(unsigned long page); + + + + +/* + * SPI device driver setup and teardown + */ +struct flash_info { + char *name; + + /* JEDEC id zero means "no ID" (most older chips); otherwise it has + * a high byte of zero plus three data bytes: the manufacturer id, + * then a two byte device id. + */ + u32 jedec_id; + u16 ext_id; + + u32 page_size; + u32 page_per_block; + + /* The size listed here is what works with OPCODE_SE, which isn't + * necessarily called a "sector" by the vendor. + */ + unsigned block_size; + u16 n_blocks; + + /*|--------------------64bytes------------------------------|*/ + /*|---12---|-4--|---12----|-4--|---12---|-4--|---12----|-4--|*/ + /*|-seglen-|skip|-segllen-|skip|-seglen-|skip|-segllen-|skip|*/ + u32 oob_size; + u16 oob_up_skiplen; + u16 oob_seglen; + u16 oob_down_skiplen; + u16 oob_seg_perpage; + u16 oob_vail_data_offset; + + /** + * chip character bits: + * bit 0: under_protect flag, the serial flash under protection or not when power on + * bit 1: fast read flag, the serial flash support fast read or not(command 0Bh) + * bit 2: AAI flag, the serial flash support auto address increment word programming + * bit 3: support dual write or no + * bit 4: support dual read or no + * bit 5: support quad write or no + * bit 6: support quad read or no + * bit 7: the second status command (35h) flag,if use 4-wire(quad) mode,the bit must be is enable + */ + u16 flags; +#define SFLAG_UNDER_PROTECT (1<<0) +#define SFLAG_FAST_READ (1<<1) +#define SFLAG_AAAI (1<<2) +#define SFLAG_COM_STATUS2 (1<<3) + +#define SFLAG_DUAL_IO_READ (1<<4) +#define SFLAG_DUAL_READ (1<<5) +#define SFLAG_QUAD_IO_READ (1<<6) +#define SFLAG_QUAD_READ (1<<7) + +#define SFLAG_DUAL_IO_WRITE (1<<8) +#define SFLAG_DUAL_WRITE (1<<9) +#define SFLAG_QUAD_IO_WRITE (1<<10) +#define SFLAG_QUAD_WRITE (1<<11) + +#define SFLAG_SECT_4K (1<<12) + +}; + +/** + *because of some spi nand is difference of status register difinition. + *this structure use mapping the status reg function and corresponding. +*/ +struct flash_status_reg +{ + u32 jedec_id; + u16 ext_id; + unsigned b_wip:4; /*write in progress*/ + unsigned b_wel:4; /*wrute ebabke latch*/ + unsigned b_bp0:4; /*block protected 0*/ + unsigned b_bp1:4; /*block protected 1*/ + unsigned b_bp2:4; /*block protected 2*/ + unsigned b_bp3:4; /*block protected 3*/ + unsigned b_bp4:4; /*block protected 4*/ + unsigned b_srp0:4; /*status register protect 0*/ + + unsigned b_srp1:4; /*status register protect 1*/ + unsigned b_qe:4; /*quad enable*/ + unsigned b_lb:4; /*write protect control and status to the security reg.*/ +/* + unsigned b_reserved0:4; + unsigned b_reserved1:4; + unsigned b_reserved2:4; +*/ + unsigned b_cmp:4; /*conjunction bp0-bp4 bit*/ + unsigned b_sus:4; /*exec an erase/program suspend cmd_buf*/ + unsigned b_efail:4; /**/ + unsigned b_pfail:4; /**/ +}; + +struct ak_spinand { + struct spi_device *spi; + struct mutex lock; + struct flash_info info; + struct mtd_info mtd; + unsigned partitioned:1; + + u8 bus_width; + unsigned char *buf; + u8 cmd_buf[CMD_ADDR_SIZE + MAX_DUMMY_SIZE]; + u8 dummy_len; + + u8 erase_opcode; + u8 tx_opcode; + u8 rx_opcode; + u8 txd_bus_width; + u8 rxd_bus_width; + + u8 txa_bus_width; + u8 rxa_bus_width; + u32 page_shift; + struct flash_status_reg stat_reg; +}; + +static struct mtd_info *ak_mtd_info; + + +//extern char __initdata saved_root_name[64]; + +/* + * feature cmd list ,reference by spec. + * */ +static int feature_cmd[3] = {0xC0, 0xB0, 0xA0}; + +static inline struct ak_spinand *mtd_to_spiflash(struct mtd_info *mtd) +{ + return container_of(mtd, struct ak_spinand, mtd); +} + + +static struct flash_status_reg __devinitdata status_reg_list[] = { + /*normal status reg define*/ + { + .jedec_id = 0xc8f4c8f4, .ext_id = 0, + .b_wip = 0, .b_wel = 1, .b_efail= 2, .b_pfail= 3, + .b_bp2 = 4, .b_bp3 = 5, .b_bp4 = 6, .b_srp0 = 7, + + .b_qe = 8, .b_srp1 = 9,.b_lb = 10, .b_cmp = 14, + .b_sus = 15, + }, + /*normal status reg define*/ + { + .jedec_id = 0xc8f2c8f2, .ext_id = 0, + .b_wip = 0, .b_wel = 1, .b_efail= 2, .b_pfail= 3, + .b_bp2 = 4, .b_bp3 = 5, .b_bp4 = 6, .b_srp0 = 7, + + .b_qe = 8, .b_srp1 = 9,.b_lb = 10, .b_cmp = 14, + .b_sus = 15, + }, + /*normal status reg define*/ + { + .jedec_id = 0xc8f1c8f1, .ext_id = 0, + .b_wip = 0, .b_wel = 1, .b_efail= 2, .b_pfail= 3, + .b_bp2 = 4, .b_bp3 = 5, .b_bp4 = 6, .b_srp0 = 7, + + .b_qe = 8, .b_srp1 = 9,.b_lb = 10, .b_cmp = 14, + .b_sus = 15, + }, + /*normal status reg define*/ + { + .jedec_id = 0xc8d1c8d1, .ext_id = 0, + .b_wip = 0, .b_wel = 1, .b_efail= 2, .b_pfail= 3, + .b_bp2 = 4, .b_bp3 = 5, .b_bp4 = 6, .b_srp0 = 7, + + .b_qe = 8, .b_srp1 = 9,.b_lb = 10, .b_cmp = 14, + .b_sus = 15, + }, + /*normal status reg define*/ + { + .jedec_id = 0xc8d2c8d2, .ext_id = 0, + .b_wip = 0, .b_wel = 1, .b_efail= 2, .b_pfail= 3, + .b_bp2 = 4, .b_bp3 = 5, .b_bp4 = 6, .b_srp0 = 7, + + .b_qe = 8, .b_srp1 = 9,.b_lb = 10, .b_cmp = 14, + .b_sus = 15, + }, + /*normal status reg define*/ + { + .jedec_id = 0xb148c8b1, .ext_id = 0, + .b_wip = 0, .b_wel = 1, .b_efail= 2, .b_pfail= 3, + .b_bp2 = 4, .b_bp3 = 5, .b_bp4 = 6, .b_srp0 = 7, + + .b_qe = 8, .b_srp1 = 9,.b_lb = 10, .b_cmp = 14, + .b_sus = 15, + }, + /*normal status reg define*/ + { + .jedec_id = 0xc8217f7f, .ext_id = 0, + .b_wip = 0, .b_wel = 1, .b_efail= 2, .b_pfail= 3, + .b_bp2 = 4, .b_bp3 = 5, .b_bp4 = 6, .b_srp0 = 7, + + .b_qe = 8, .b_srp1 = 9,.b_lb = 10, .b_cmp = 14, + .b_sus = 15, + }, + /*normal status reg define*/ + { + .jedec_id = 0xc831C831, .ext_id = 0, + .b_wip = 0, .b_wel = 1, .b_efail= 2, .b_pfail= 3, + .b_bp2 = 4, .b_bp3 = 5, .b_bp4 = 6, .b_srp0 = 7, + + .b_qe = 8, .b_srp1 = 9,.b_lb = 10, .b_cmp = 14, + .b_sus = 15, + }, + /*normal status reg define*/ + { + .jedec_id = 0xa1e1a1e1, .ext_id = 0, + .b_wip = 0, .b_wel = 1, .b_efail= 2, .b_pfail= 3, + .b_bp2 = 4, .b_bp3 = 5, .b_bp4 = 6, .b_srp0 = 7, + + .b_qe = 8, .b_srp1 = 9,.b_lb = 10, .b_cmp = 14, + .b_sus = 15, + }, + { + .jedec_id = 0xc212c212, .ext_id = 0, + .b_wip = 0, .b_wel = 1, .b_efail= 2, .b_pfail= 3, + .b_bp2 = 4, .b_bp3 = 5, .b_bp4 = 6, .b_srp0 = 7, + + .b_qe = 8, .b_srp1 = 9,.b_lb = 10, .b_cmp = 14, + .b_sus = 15, + }, + { + .jedec_id = 0xefaa2100, .ext_id = 0, + .b_wip = 0, .b_wel = 1, .b_efail= 2, .b_pfail= 3, + .b_bp2 = 4, .b_bp3 = 5, .b_bp4 = 6, .b_srp0 = 7, + + .b_qe = 8, .b_srp1 = 9,.b_lb = 10, .b_cmp = 14, + .b_sus = 15, + }, + /*normal status reg define*/ + { + .jedec_id = 0, .ext_id = 0, + .b_wip = 0, .b_wel = 1, .b_bp0 = 2, .b_bp1 = 3, + .b_bp2 = 4, .b_bp3 = 5, .b_bp4 = 6, .b_srp0 = 7, + + .b_srp1 = 8,.b_qe = 9, .b_lb = 10, .b_cmp = 14, + .b_sus = 15, + }, +}; + + +/* NOTE: double check command sets and memory organization when you add + * more flash chips. This current list focusses on newer chips, which + * have been converging on command sets which including JEDEC ID. + */ +static struct flash_info __devinitdata ak_spinand_supportlist [] = { + { "GD5G1GQ4U", 0xc8f1c8f1, 0, 2048, 64, 128*1024, 1024, 64, 0, 12, 4, 4, 4, SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_QUAD_WRITE,}, + { "GD5F1GQ4UB", 0xc8d1c8d1, 0, 2048, 64, 128*1024, 1024, 64, 0, 12, 4, 4, 4, SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_QUAD_WRITE,}, + { "GD5G2GQ4U", 0xc8f2c8f2, 0, 2048, 64, 128*1024, 2048, 64, 0, 12, 4, 4, 4, SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_QUAD_WRITE,}, + { "GD5F2GQ4UB", 0xc8d2c8d2, 0, 2048, 64, 128*1024, 2048, 64, 0, 16, 0, 4, 4, SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_QUAD_WRITE,}, + { "GD5G4GQ4U", 0xc8f4c8f4, 0, 2048, 64, 128*1024, 4096, 64, 0, 12, 4, 4, 4, SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_QUAD_WRITE,}, + { "PSU1GQ4U", 0xc8217f7f, 0, 2048, 64, 128*1024, 1024, 64, 8, 8, 0, 4, 8, SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_QUAD_WRITE,}, + { "AFS1GQ4UAAWC2", 0xc831C831, 0, 2048, 128, 256*1024, 512, 64, 0, 6, 10, 4, 4, SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_QUAD_WRITE,}, + { "PN26G01AWSIUG", 0xa1e1a1e1, 0, 2048, 64, 128*1024, 1024, 64, 0, 8, 0, 4, 6, SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_QUAD_WRITE,}, + {"MX35LF1GE4AB", 0xc212c212, 0, 2048, 64, 128*1024, 1024, 64, 0, 16, 0, 4, 4, SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_QUAD_WRITE,}, + { "W25N01GV", 0xefaa2100, 0, 2048, 64, 128*1024, 1024, 64, 0, 4, 12, 4, 4, SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_QUAD_WRITE,}, +}; + + +#ifdef SPINAND_USE_MTD_BLOCK_LAYER +/** +* @brief: because of the _read() function call by mtd block layer, the buffer be +* allocate by vmalloc() in mtd layer, spi driver layer may use this buffer that +* intents of use for DMA transfer, so, add this function to transition buffer. +* call this function at before real read/write data. +* +* @author lixinhai +* @date 2013-04-10 +* @param[in] flash spiflash handle. +* @param[in] buffer. +* @param[in] buffer len +* @param[in] read/write +* @retval return the transition buffer +*/ +static void *flash_buf_bounce_pre(struct ak_spinand *flash, + void *buf, u32 len, int dir) +{ + if(!is_vmalloc_addr(buf)) { + return buf; + } + + if(dir == SPI_FLASH_WRITE) { + memcpy(flash->buf, buf, len); + } + return flash->buf; +} + +/** +* @brief: because of the _read() function call by mtd block layer, the buffer be +* allocate by vmalloc() in mtd layer, spi driver layer may use this buffer that +* intents of use for DMA transfer, so, add this function to transition buffer. +* call this function at after real read/write data +* +* @author lixinhai +* @date 2013-04-10 +* @param[in] flash spiflash handle. +* @param[in] buffer. +* @param[in] buffer len +* @param[in] read/write +* @retval return the transition buffer +*/ +static void flash_buf_bounce_post(struct ak_spinand *flash, + void *buf, u32 len, int dir) +{ + if(!is_vmalloc_addr(buf)) { + return; + } + + if(dir == SPI_FLASH_READ) { + memcpy(buf, flash->buf, len); + } +} +#else +static inline void *flash_buf_bounce_pre(struct ak_spinand *flash, + void *buf, u32 len, int dir) +{ + return buf; +} + +static inline void flash_buf_bounce_post(struct ak_spinand *flash, + void *buf, u32 len, int dir) +{ +} +#endif + +/* + * Internal helper functions + */ + +/** +* @brief Read the status register. +* +* returning its value in the location +* @author lixinhai +* @date 2014-03-20 +* @param[in] spiflash handle. +* @return int Return the status register value. +*/ +static int read_sr(struct ak_spinand *flash, u32 addr) +{ + + u8 st_tmp= 0; + int status; + struct spi_message message; + struct spi_transfer x[2]; + + spi_message_init(&message); + memset(x, 0, sizeof x); + + flash->cmd_buf[0]= OPCODE_RDSR1; + flash->cmd_buf[1]= addr; + + x[0].tx_buf = flash->cmd_buf; + x[0].len = 2; + spi_message_add_tail(&x[0], &message); + + + x[1].rx_buf = flash->cmd_buf + 2; + x[1].len = 1; + spi_message_add_tail(&x[1], &message); + + /* do the i/o */ + status = spi_sync(flash->spi, &message); + if (status == 0) + memcpy(&st_tmp, x[1].rx_buf, 1); + + return st_tmp; + + /* + ssize_t retval; + u32 status; + u8 st_tmp= 0; +//printk("%s\n",__func__); + flash->cmd_buf[0]= OPCODE_RDSR1; + flash->cmd_buf[1]= addr; + + if((retval = spi_write_then_read(flash->spi, flash->cmd_buf, 2, &st_tmp, 1))<0) + return retval; + + status = st_tmp; + return status; + */ +} + + +/** +* @brief Write status register +* +* Write status register 1 byte. +* @author lixinhai +* @date 2014-03-20 +* @param[in] flash spiflash handle. +* @param[in] val register value to be write. +* @return int return write success or failed +* @retval returns zero on success +* @retval return a negative error code if failed +*/ +static int write_sr(struct ak_spinand *flash, u32 addr, u16 val) +{ + struct spi_transfer t[1]; + struct spi_message m; + spi_message_init(&m); + memset(t, 0, (sizeof t)); + + flash->cmd_buf[0] = OPCODE_WRSR1; + flash->cmd_buf[1] = addr; + flash->cmd_buf[2] = val; + + t[0].tx_buf = flash->cmd_buf; + t[0].len = 3; + spi_message_add_tail(&t[0], &m); + + return spi_sync(flash->spi, &m); +} + +static inline int sflash_reset(struct ak_spinand *flash) +{ + u8 code = OPCODE_RESET; + int ret; + + ret = spi_write_then_read(flash->spi, &code, 1, NULL, 0); + ret |= write_sr(flash, 0xa0, 0x0); + return ret; +} + +/** +* @brief Set write enable latch. +* +* Set write enable latch with Write Enable command. +* @author lixinhai +* @date 2014-03-20 +* @param[in] flash spiflash handle. +* @return int return write success or failed +* @retval returns zero on success +* @retval return a negative error code if failed +*/ +static inline int write_enable(struct ak_spinand *flash) +{ + +// u8 st_tmp= 0; + int status; + struct spi_message message; + struct spi_transfer x[2]; + + spi_message_init(&message); + memset(x, 0, sizeof x); + + flash->cmd_buf[0]= OPCODE_WREN; + + + x[0].tx_buf = flash->cmd_buf; + x[0].len = 1; + spi_message_add_tail(&x[0], &message); + + /* do the i/o */ + status = spi_sync(flash->spi, &message); + + + return status; + +/* + u8 code = OPCODE_WREN; + +//printk("%s\n",__func__); + return spi_write_then_read(flash->spi, &code, 1, NULL, 0); +*/ +} + + +/** +* @brief Set write disble +* +* Set write disble instruction to the chip. +* @author lixinhai +* @date 2014-03-20 +* @param[in] flash spiflash handle. +* @return int return write success or failed +* @retval returns zero on success +* @retval return a negative error code if failed +*/ +static inline int write_disable(struct ak_spinand *flash) +{ + u8 code = OPCODE_WRDI; + + return spi_write_then_read(flash->spi, &code, 1, NULL, 0); +} + +/** +* @brief Wait for SPI flash ready. +* +* Service routine to read status register until ready, or timeout occurs. +* @author lixinhai +* @date 2014-03-20 +* @param[in] flash spiflash handle. +* @return int return write success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int wait_till_ready(struct ak_spinand *flash) +{ + unsigned long deadline; + int idx, shift; + u32 sr; + u8 addr; + struct flash_status_reg *fsr = &flash->stat_reg; + + deadline = jiffies + MAX_READY_WAIT_JIFFIES; + + shift = fsr->b_wip / 8; + idx = fsr->b_wip % 8; + addr = feature_cmd[shift]; + do { + if ((sr = read_sr(flash, addr)) < 0) + break; + else if (!(sr & (1<<(fsr->b_wip%8)))) + return 0; + + cond_resched(); + + } while (!time_after_eq(jiffies, deadline)); + + return 1; +} + + +static int check_ecc_status(struct ak_spinand *flash) +{ + unsigned long deadline; + int idx, shift; + u32 sr; + u8 addr; + struct flash_status_reg *fsr = &flash->stat_reg; + + deadline = jiffies + MAX_READY_WAIT_JIFFIES; + + shift = fsr->b_wip / 8; + idx = fsr->b_wip % 8; + addr = feature_cmd[shift]; + do { + if ((sr = read_sr(flash, addr)) < 0) + { + printk(KERN_ERR "read_sr fail\n" ); + break; + } + + if(((sr >> 4) & 0x3) == 2) + { + printk(KERN_ERR "ecc error sr:%d\n", sr ); + return 1; + } + else + { + return 0; + } + + cond_resched(); + + } while (!time_after_eq(jiffies, deadline)); + + return 1; + +} + + + +/** +* @brief: enable 4 wire transfer mode. +* +* @author lixinhai +* @date 2014-04-10 +* @param[in] flash spiflash handle. +*/ +static int quad_mode_enable(struct ak_spinand *flash) +{ + int ret, idx, shift; + u32 regval; + u8 addr; + struct flash_status_reg *fsr = &flash->stat_reg; + + shift = fsr->b_qe / 8; + idx = fsr->b_qe % 8; + + addr = feature_cmd[shift]; + ret = wait_till_ready(flash); + if (ret) + return -EBUSY; + + write_enable(flash); + + regval = read_sr(flash, addr); + regval |= 1<<(fsr->b_qe % 8); + write_sr(flash, addr, regval); + + + regval = read_sr(flash, addr); + + write_disable(flash); + return 0; +} + +/** +* @brief: disable 4 wire transfer mode. +* +* @author lixinhai +* @date 2014-04-10 +* @param[in] flash spiflash handle. +*/ +static int quad_mode_disable(struct ak_spinand *flash) +{ + int ret, idx, shift; + u32 regval; + u8 addr; + struct flash_status_reg *fsr = &flash->stat_reg; + + shift = fsr->b_qe / 8; + idx = fsr->b_qe % 8; + addr = feature_cmd[shift]; + ret = wait_till_ready(flash); + if (ret) + return -EBUSY; + + write_enable(flash); + + regval = read_sr(flash, addr); + regval &= ~(1<<(fsr->b_qe%8)); + write_sr(flash, addr, regval); + + + regval = read_sr(flash, addr); + write_disable(flash); + return 0; +} + + +/** +* @brief Erase sector +* +* Erase a sector specialed by user. +* @author lixinhai +* @date 2014-03-20 +* @param[in] flash spiflash handle. +* @param[in] offset which is any address within the sector which should be erased. +* @return int return write success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int erase_block(struct ak_spinand *flash, u32 offset) +{ + u32 row; + struct spi_transfer t[1]; + struct spi_message m; + + spi_message_init(&m); + memset(t, 0, (sizeof t)); + + DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB at 0x%08x\n", + dev_name(&flash->spi->dev), __func__, + flash->mtd.erasesize / 1024, offset); + + row = ((offset>>flash->page_shift) & 0xffffff); + + /* Wait until finished previous write command. */ + if (wait_till_ready(flash)) + { + printk(KERN_ERR "kernel: error erase_block previous write command fail\n" ); + return -EBUSY; + } + + /* Send write enable, then erase commands. */ + write_enable(flash); + + /* Set up command buffer. */ + flash->cmd_buf[0] = flash->erase_opcode; + flash->cmd_buf[1] = row >> 16; + flash->cmd_buf[2] = row >> 8; + flash->cmd_buf[3] = row; + + t[0].tx_buf = flash->cmd_buf; + t[0].len = 4; + spi_message_add_tail(&t[0], &m); + + spi_sync(flash->spi, &m); + + +// spi_write(flash->spi, flash->cmd_buf, 4); + + if (wait_till_ready(flash)) { + printk(KERN_ERR "kernel: error erase_block write command fail\n" ); + /* REVISIT status return?? */ + return -EBUSY; + } + + return 0; +} + + +/** +* @brief MTD Erase +* +* Erase an address range on the flash chip. +* @author luoyongchuang +* @date 2015-05-17 +* @param[in] mtd mtd info handle. +* @param[in] instr erase info. +* @return int return write success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int ak_spinand_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct ak_spinand *flash = mtd_to_spiflash(mtd); + u32 addr,len; + uint32_t rem; + + + DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%llx, len %lld\n", + dev_name(&flash->spi->dev), __func__, "at", + (long long)instr->addr, (long long)instr->len); + + /* sanity checks */ + if (instr->addr + instr->len > mtd->size) + { + printk(KERN_ERR "ak_spinand_erase:instr->addr[0x%llx] + instr->len[%lld] > mtd->size[%lld]\n", + instr->addr, instr->len, mtd->size ); + return -EINVAL; + } + div_u64_rem(instr->len, mtd->erasesize, &rem); + if (rem != 0) + { + printk(KERN_ERR "ak_spinand_erase:rem!=0 [%u]\n", rem ); + return -EINVAL; + } + + addr = instr->addr; + len = instr->len; + + mutex_lock(&flash->lock); + + //printk("ak_spinand_erase:%d:%d\n", addr, len); + while (len) { + if (erase_block(flash, addr)) { + instr->state = MTD_ERASE_FAILED; + mutex_unlock(&flash->lock); + return -EIO; + } + + addr += mtd->erasesize; + len -= mtd->erasesize; + } + + mutex_unlock(&flash->lock); + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return 0; +} + +/** +* @brief initilize spi nand flash read/write param. +* +* @author lixinhai +* @date 2014-04-20 +* @param[in] spiflash info handle. +* @return int return config success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int init_spiflash_rw_info(struct ak_spinand *flash) +{ + /**default param.*/ + flash->rx_opcode = OPCODE_READ; + flash->rxd_bus_width = XFER_1DATAWIRE; + flash->rxa_bus_width = XFER_1DATAWIRE; + flash->tx_opcode = OPCODE_PP; + flash->txd_bus_width = XFER_1DATAWIRE; + flash->txa_bus_width = XFER_1DATAWIRE; + flash->dummy_len = 1; + + if(flash->bus_width & FLASH_BUS_WIDTH_2WIRE){ + if(flash->info.flags & SFLAG_DUAL_READ) { + flash->rx_opcode = OPCODE_FAST_D_READ; + flash->rxd_bus_width = XFER_2DATAWIRE; + flash->rxa_bus_width = XFER_1DATAWIRE; + flash->dummy_len = 1; + } else if (flash->info.flags & SFLAG_DUAL_IO_READ) { + flash->rx_opcode = OPCODE_FAST_D_IO; + flash->rxd_bus_width = XFER_2DATAWIRE; + flash->rxa_bus_width = XFER_2DATAWIRE; + flash->dummy_len = 1; + } + + if(flash->info.flags & SFLAG_DUAL_WRITE) { + flash->tx_opcode = OPCODE_PP_DUAL; + flash->txd_bus_width = XFER_2DATAWIRE; + flash->txa_bus_width = XFER_1DATAWIRE; + } else if(flash->info.flags & SFLAG_DUAL_IO_WRITE) { + flash->tx_opcode = OPCODE_2IO_PP; + flash->txd_bus_width = XFER_2DATAWIRE; + flash->txa_bus_width = XFER_2DATAWIRE; + } + } + + if(flash->bus_width & FLASH_BUS_WIDTH_4WIRE){ + if(flash->info.flags & SFLAG_QUAD_READ) { + flash->rx_opcode = OPCODE_FAST_Q_READ; + flash->rxd_bus_width = XFER_4DATAWIRE; + flash->rxa_bus_width = XFER_1DATAWIRE; + flash->dummy_len = 1; + }else if(flash->info.flags & SFLAG_QUAD_IO_READ){ + flash->rx_opcode = OPCODE_FAST_Q_IO; + flash->rxd_bus_width = XFER_4DATAWIRE; + flash->rxa_bus_width = XFER_4DATAWIRE; + flash->dummy_len = 3; + } + + if(flash->info.flags & SFLAG_QUAD_WRITE) { + flash->tx_opcode = OPCODE_PP_QUAD; + flash->txd_bus_width = XFER_4DATAWIRE; + flash->txa_bus_width = XFER_1DATAWIRE; + }else if(flash->info.flags & SFLAG_QUAD_IO_WRITE) { + flash->tx_opcode = OPCODE_4IO_PP; + flash->txd_bus_width = XFER_4DATAWIRE; + flash->txa_bus_width = XFER_4DATAWIRE; + } + + } + return 0; +} + + +#if 0 + +bool ak_spinand_get_protect(void) +{ + struct ak_spinand *flash; + u32 regval; + + if(ak_mtd_info == NULL) + return false; + + flash = mtd_to_spiflash(ak_mtd_info); + if(flash == NULL) + return false; + + mutex_lock(&flash->lock); + regval = read_sr(flash, 0xA0); + mutex_unlock(&flash->lock); + + if((regval & (0x7<<3)) == 0) + return false; + else + return true; +} + + + +//0: none, 1:1/64, 2:1/32, 3:1/16, 4:1/8, 5:1/4, 6:1/2 +int ak_spinand_cfg_protect(bool protect) +{ + struct ak_spinand *flash; + const unsigned char protect_bit[7] = {0, 0x0C, 0x14, 0x1C, 0x24, 0x2C, 0x34}; + const unsigned char mode = 6; + u8 addr = 0xA0; + u32 regval; + + if(ak_mtd_info == NULL) + return -1; + + flash = mtd_to_spiflash(ak_mtd_info); + if(flash == NULL) + return -1; + + mutex_lock(&flash->lock); + + if(protect) + regval = protect_bit[mode]; + else + regval = protect_bit[0]; + + if(wait_till_ready(flash)) { + mutex_unlock(&flash->lock); + return -EBUSY; + } + + write_enable(flash); + write_sr(flash, addr, regval); + regval = read_sr(flash, addr); + printk(KERN_ERR "[protect regval: %x]\n", regval); + write_disable(flash); + + mutex_unlock(&flash->lock); + + return 0; +} +#endif + + +/** +* @brief configure spi nandflash transfer mode according to flags. +* +* @author lixinhai +* @date 2014-04-20 +* @param[in] spiflash info handle. +* @return int return config success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int ak_spinand_cfg_quad_mode(struct ak_spinand *flash) +{ + int ret = 0; + + if((flash->bus_width & FLASH_BUS_WIDTH_4WIRE) && + (flash->info.flags & (SFLAG_QUAD_WRITE|SFLAG_QUAD_IO_WRITE| + SFLAG_DUAL_READ|SFLAG_DUAL_IO_READ))) { + ret = quad_mode_enable(flash); + if(ret) { + flash->bus_width &= ~FLASH_BUS_WIDTH_4WIRE; + printk("config the spiflash quad enable fail. transfer use 1 wire.\n"); + } + } + else + quad_mode_disable(flash); + + return ret; +} + + +#define FILL_CMD(c, val) do{c[0] = (val);}while(0) +#define FILL_ADDR(c, val) do{ \ + c[CMD_SIZE] = ((val) >> 8) & 0xf; \ + c[CMD_SIZE+1] = ((val) & 0xff); \ + }while(0) + +#define FILL_DUMMY_DATA(c, val) do{ \ + c[CMD_ADDR_SIZE] = val >> 16; \ + c[CMD_ADDR_SIZE+1] = 0; \ + c[CMD_ADDR_SIZE+2] = 0; \ + c[CMD_ADDR_SIZE+3] = 0; \ + }while(0) + +/** +* @brief configure spi nandflash transfer mode according to flags. +* +* @author lixinhai +* @date 2014-04-20 +* @param[in] mtd info handle. +* @param[in] row address. +* @param[in] column address. +* @param[in] transfer len. +* @param[out] transfer result len. +* @param[out] result buffer. +* @return int return config success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int spinand_read(struct mtd_info *mtd, int row, int col, size_t len, + size_t *retlen, u_char *buf) +{ + struct ak_spinand *flash = mtd_to_spiflash(mtd); + struct spi_transfer t[4]; + struct spi_message m; + struct spi_message m1; + void *bounce_buf; + + + spi_message_init(&m); + spi_message_init(&m1); + memset(t, 0, (sizeof t)); + + mutex_lock(&flash->lock); + bounce_buf = flash_buf_bounce_pre(flash, buf, len, SPI_FLASH_READ); + + /*fill command*/ + flash->cmd_buf[0] = OPCODE_READ_TO_CACHE; + flash->cmd_buf[1] = (row >> 16) & 0xff; + flash->cmd_buf[2] = (row >> 8) & 0xff; + flash->cmd_buf[3] = row & 0xff; + t[3].tx_buf = flash->cmd_buf; + t[3].len = 4; + spi_message_add_tail(&t[3], &m1); + spi_sync(flash->spi, &m1); + +// spi_write(flash->spi, flash->cmd_buf, 4); + + t[0].tx_buf = flash->cmd_buf; + t[0].len = CMD_SIZE; + spi_message_add_tail(&t[0], &m); + + t[1].tx_buf = &flash->cmd_buf[CMD_SIZE]; + t[1].len = ADDR_SIZE + flash->dummy_len; + t[1].xfer_mode = flash->rxa_bus_width; + spi_message_add_tail(&t[1], &m); + + t[2].rx_buf = bounce_buf; + t[2].len = len; + t[2].cs_change = 1; + t[2].xfer_mode = flash->rxd_bus_width; + + spi_message_add_tail(&t[2], &m); + + /* Byte count starts at zero. */ + if (retlen) + *retlen = 0; + + /* Wait till previous write/erase is done. */ + if (wait_till_ready(flash)) { + /* REVISIT status return?? */ + mutex_unlock(&flash->lock); + printk(KERN_ERR"kernel: error spinand_read fail, row:%d\n", row); + return -EBUSY; + } + + /* Set up the write data buffer. */ + FILL_CMD(flash->cmd_buf, flash->rx_opcode); + FILL_ADDR(flash->cmd_buf, col); + FILL_DUMMY_DATA(flash->cmd_buf, 0x00); + + spi_sync(flash->spi, &m); + + *retlen = m.actual_length - CMD_ADDR_SIZE - flash->dummy_len; + if (check_ecc_status(flash)) { + /* REVISIT status return?? */ + mutex_unlock(&flash->lock); + printk(KERN_ERR"kernel: check_ecc_status, row:%d\n", row); + return -EBUSY; + } + + flash_buf_bounce_post(flash, buf, len, SPI_FLASH_READ); + + mutex_unlock(&flash->lock); + + return 0; +} + +#if 0 + +static int ecc_ctl(struct ak_spinand *flash, bool enable) +{ + u8 addr = 0xB0; + int regval; + + if(wait_till_ready(flash)) { + return -1; + } + + write_enable(flash); + + regval = read_sr(flash, addr); + + if(enable) + regval |= FEATURE_ECC_EN; + else + regval &= ~FEATURE_ECC_EN; + + write_sr(flash, addr, regval); + //regval = read_sr(flash, addr); + //printk(KERN_ERR "[ecc enable regval: %x]\n", regval); + write_disable(flash); + + return 0; +} + + +static int spinand_read_noecc(struct mtd_info *mtd, int row, int col, size_t len, + size_t *retlen, u_char *buf) +{ + struct ak_spinand *flash = mtd_to_spiflash(mtd); + struct spi_transfer t[4]; + struct spi_message m; + struct spi_message m1; + void *bounce_buf; + int ret = 0; + + + spi_message_init(&m); + spi_message_init(&m1); + memset(t, 0, (sizeof t)); + + mutex_lock(&flash->lock); + + //set to no ecc + if (-1 == ecc_ctl(flash, false)) + { + printk(KERN_ERR"ecc_ctl fail\r\n"); + mutex_unlock(&flash->lock); + return -EBUSY; + } + + + bounce_buf = flash_buf_bounce_pre(flash, buf, len, SPI_FLASH_READ); + + /*fill command*/ + flash->cmd_buf[0] = OPCODE_READ_TO_CACHE; + flash->cmd_buf[1] = (row >> 16) & 0xff; + flash->cmd_buf[2] = (row >> 8) & 0xff; + flash->cmd_buf[3] = row & 0xff; + t[3].tx_buf = flash->cmd_buf; + t[3].len = 4; + spi_message_add_tail(&t[3], &m1); + spi_sync(flash->spi, &m1); + +// spi_write(flash->spi, flash->cmd_buf, 4); + + t[0].tx_buf = flash->cmd_buf; + t[0].len = CMD_SIZE; + spi_message_add_tail(&t[0], &m); + + t[1].tx_buf = &flash->cmd_buf[CMD_SIZE]; + t[1].len = ADDR_SIZE + flash->dummy_len; + t[1].xfer_mode = flash->rxa_bus_width; + spi_message_add_tail(&t[1], &m); + + t[2].rx_buf = bounce_buf; + t[2].len = len; + t[2].cs_change = 1; + t[2].xfer_mode = flash->rxd_bus_width; + + spi_message_add_tail(&t[2], &m); + + /* Byte count starts at zero. */ + if (retlen) + *retlen = 0; + + /* Wait till previous write/erase is done. */ + if (wait_till_ready(flash)) { + /* REVISIT status return?? */ + printk(KERN_ERR"kernel: error spinand_read fail\n"); + ret = -EBUSY; + goto OUT; + } + + /* Set up the write data buffer. */ + FILL_CMD(flash->cmd_buf, flash->rx_opcode); + FILL_ADDR(flash->cmd_buf, col); + FILL_DUMMY_DATA(flash->cmd_buf, 0x00); + + spi_sync(flash->spi, &m); + + *retlen = m.actual_length - CMD_ADDR_SIZE - flash->dummy_len; + + flash_buf_bounce_post(flash, buf, len, SPI_FLASH_READ); + +OUT: + if (-1 == ecc_ctl(flash, true)) + { + mutex_unlock(&flash->lock); + printk(KERN_ERR"spi_nand_read_noecc ecc_ctl fail\r\n"); + return -EBUSY; + } + + mutex_unlock(&flash->lock); + + return ret; +}ak_spi_nand +#endif + + +/** +* @brief configure spi nandflash transfer mode according to flags. +* +* @author lixinhai +* @date 2014-04-20 +* @param[in] mtd info handle. +* @param[in] from: address. +* @param[in] transfer len. +* @param[out] transfer result len. +* @param[out] result buffer. +* @return int return config success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int ak_spinand_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + int ret = 0; + size_t rlen = 0; + u32 xfer_len; + u32 offset = 0; + u32 count = len; + int row, column; + struct ak_spinand *flash = mtd_to_spiflash(mtd); + + *retlen = 0; + + /*decode row/column in address param*/ + row = ((from>>flash->page_shift) & 0xffffff); + column = (from & 0x7ff); + + while(count > 0) { + xfer_len = (count > FLASH_BUF_SIZE) ? FLASH_BUF_SIZE : count; + + /*transfer len not greater than page size*/ + if(xfer_len > flash->info.page_size) + xfer_len = ALIGN_DOWN(xfer_len, flash->info.page_size); + if(xfer_len+column >= flash->info.page_size) + xfer_len = flash->info.page_size - column; + + ret = spinand_read(mtd, row, column, xfer_len, &rlen, buf + offset); + if(unlikely(ret)) { + ret = -EBUSY; + goto out; + } + row++; + column = 0; + *retlen += rlen; + count -= rlen; + offset += rlen; + } +out: + return ret; +} + + +//此函数暂不实? + +static int ak_spinand_read_noecc(struct mtd_info *mtd, loff_t from, size_t len,size_t *retlen, u_char *buf) +{ + printk("%s: no support\n",__func__); + return 0; +} + + +static int spinand_do_read_page(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) +{ + int ret = 0; + size_t rlen = 0; + u32 xfer_len,read_len,read_len_oob,oob_size,oob_xfer_len,oob_add_len,oob_seglen_len; + u32 offset = 0; + u32 oob_offset = 0; + u32 count = ops->len; + int row, column; + uint8_t buftmp[2112]={0}; + uint8_t *buf = NULL; + uint8_t *oobbuf = ops->oobbuf; + uint8_t *datbuf = ops->datbuf; + struct ak_spinand *flash = mtd_to_spiflash(mtd); + +//printk("%d\n",count); + /*decode row/column in address param*/ + row = ((from>>flash->page_shift) & 0xffffff); + column = (from & 0x7ff); + + read_len_oob = ops->ooblen; + oob_seglen_len = flash->info.oob_up_skiplen + flash->info.oob_seglen + flash->info.oob_down_skiplen; + oob_size = flash->info.oob_size; + oob_xfer_len = flash->info.oob_seglen; + + while(count > 0) { + xfer_len = (count > FLASH_BUF_SIZE) ? FLASH_BUF_SIZE : count; + + /*transfer len not greater than page size*/ + if(xfer_len > flash->info.page_size) + xfer_len = ALIGN_DOWN(xfer_len, flash->info.page_size); + if(xfer_len+column >= flash->info.page_size) + xfer_len = flash->info.page_size - column; + + read_len = xfer_len + oob_size; + + + ret = spinand_read(mtd, row, column, read_len, &rlen, buftmp/**/); + if(unlikely(ret)) { + ret = -EBUSY; + goto out; + } + memcpy(datbuf + offset, buftmp, xfer_len); + + //buf = buftmp + rlen - oob_size; + //skip bad block mark. + buf = buftmp + rlen - oob_size + flash->info.oob_vail_data_offset; + oob_add_len = flash->info.oob_up_skiplen; + + + if(read_len_oob > 0) + { + while(read_len_oob > 0) + { + + oob_xfer_len = (read_len_oob > flash->info.oob_seglen) ? flash->info.oob_seglen : read_len_oob; + + memcpy(oobbuf+oob_offset, buf+oob_add_len, oob_xfer_len); + + oob_add_len += oob_seglen_len; + read_len_oob -= oob_xfer_len; + oob_offset += oob_xfer_len; + ops->oobretlen += oob_xfer_len; + } + } + else + { + oob_size = 0 ; + } + row++; + column = 0; + ops->retlen += (rlen - oob_size); + count -= (rlen - oob_size); + offset += (rlen-oob_size); + } +out: + return ret;//ops->retlen; +} + +/** +* @brief MTD write +* +* Write an address range to the flash chip. +* @author lixinhai +* @date 2014-03-20 +* @param[in] mtd mtd info handle. +* @param[in] to write start address. +* @param[in] len write length. +* @param[out] retlen write length at actually. +* @param[out] buf the pointer to write data. +* @return int return write success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int spinand_write(struct mtd_info *mtd, int row, int col, size_t len, + size_t *retlen, const u_char *buf) +{ + struct ak_spinand *flash = mtd_to_spiflash(mtd); + struct spi_transfer t[4]; + struct spi_message m; + struct spi_message m1; + void *bounce_buf; + //u_char buf_read[128]={0}; + //size_t read_rlen = 0; + //int ret = 0, i = 0; + + DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n", + dev_name(&flash->spi->dev), __func__, "to(page_shift)", + (u32)row, len); + + if (retlen) + *retlen = 0; + + /* sanity checks */ + if (!len) + return(0); + + spi_message_init(&m); + spi_message_init(&m1); + memset(t, 0, (sizeof t)); + + mutex_lock(&flash->lock); + bounce_buf = flash_buf_bounce_pre(flash, (void*)buf, len, SPI_FLASH_WRITE); + + t[0].tx_buf = flash->cmd_buf; + t[0].len = CMD_SIZE; + spi_message_add_tail(&t[0], &m); + + t[1].tx_buf = &flash->cmd_buf[CMD_SIZE]; + t[1].len = ADDR_SIZE; + t[1].xfer_mode = flash->txa_bus_width; + spi_message_add_tail(&t[1], &m); + + t[2].tx_buf = bounce_buf; + t[2].cs_change = 1; + t[2].xfer_mode = flash->txd_bus_width; + + spi_message_add_tail(&t[2], &m); + + /* Wait until finished previous write command. */ + if (wait_till_ready(flash)) { + mutex_unlock(&flash->lock); + printk(KERN_ERR"kernel: error spinand_write write cmd fail\n"); + return -EBUSY; + } + + write_enable(flash); + /* Set up the opcode in the write buffer. */ + FILL_CMD(flash->cmd_buf, flash->tx_opcode); + FILL_ADDR(flash->cmd_buf, col); + + t[2].len = len; + + spi_sync(flash->spi, &m); + *retlen = m.actual_length - CMD_ADDR_SIZE; + + flash->cmd_buf[0] = OPCODE_PP_EXEC; + flash->cmd_buf[1] = (row >> 16) & 0xff; + flash->cmd_buf[2] = (row >> 8) & 0xff; + flash->cmd_buf[3] = row & 0xff; + + t[3].tx_buf = flash->cmd_buf; + t[3].len = 4; + spi_message_add_tail(&t[3], &m1); + spi_sync(flash->spi, &m1); + +// spi_write(flash->spi, flash->cmd_buf, 4); + + /* Wait until finished previous write command. */ + if (wait_till_ready(flash)) + { + printk(KERN_ERR"kernel: error spinand_write write data fail\n"); + return -EBUSY; + } + + PDEBUG("ak_spinand_write: retlen=%ld\n", *retlen); + flash_buf_bounce_post(flash, (void*)buf, len, SPI_FLASH_WRITE); + + mutex_unlock(&flash->lock); + + return 0; +} + + +#define FLASH_OOB_SIZE 8 +static int ak_spinand_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + int ret = 0; + size_t rlen = 0; + u32 xfer_len; + u32 offset = 0; + u32 count = len; + int row, column; + struct ak_spinand *flash = mtd_to_spiflash(mtd); + + *retlen = 0; + + /*decode row/column in address param*/ + row = ((to>>flash->page_shift) & 0xffffff); + column = (to & 0x7ff); + + + while(count > 0) { + xfer_len = (count > FLASH_BUF_SIZE) ? FLASH_BUF_SIZE : count; + + /*transfer len not greater than page size*/ + if(xfer_len > flash->info.page_size) + xfer_len = ALIGN_DOWN(xfer_len, flash->info.page_size); + if(xfer_len+column >= flash->info.page_size) + xfer_len = flash->info.page_size - column; + + ret = spinand_write(mtd, row, column, xfer_len, &rlen, buf + offset); + if(unlikely(ret)) { + ret = -EBUSY; + goto out; + } + + row++; + column = 0; + *retlen += rlen; + count -= rlen; + offset += rlen; + } +out: + return ret; +} + + +/** +* @brief adjust transfer len according to readlen and column. +* +* @author lixinhai +* @date 2014-04-20 +* @param[in] spiflash info handle. +* @param[in] column pos. +* @param[in] need read length. +* @retval return transfer len +*/ +static int adjust_xfer_len(struct ak_spinand *flash, int column, int readlen) +{ + int seg_oob; + int xfer_len; + int ofs = flash->mtd.writesize; + int start = column - ofs; + int end; + + /*|--------------------64bytes------------------------------|*/ + /*|---12---|-4--|---12----|-4--|---12---|-4--|---12----|-4--|*/ + /*|-seglen-|skip|-segllen-|skip|-seglen-|skip|-segllen-|skip|*/ + + xfer_len = (readlen > flash->info.oob_seglen) ? flash->info.oob_seglen : readlen; + end = start + xfer_len; + seg_oob = flash->info.oob_up_skiplen + flash->info.oob_seglen + flash->info.oob_down_skiplen; + if(start/seg_oob != end/seg_oob) + end = (start/seg_oob + 1)*seg_oob; + + xfer_len = end - start; + + return xfer_len; +} + +/** +* @brief convert oob offset and addr pos to row/column coord. +* +* @author lixinhai +* @date 2014-04-20 +* @param[in] spiflash info handle. +* @param[in] read from addr +* @param[in] offset to read from +* @return int return write success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int spinand_oobpos_to_coord(struct ak_spinand *flash, + loff_t addr, uint32_t ooboffs, int *row, int *column) +{ + *row = ((addr >> flash->page_shift) & 0xffffff); + *column = (addr & (flash->mtd.writesize - 1)); + + *row += ooboffs / flash->mtd.oobsize; + *column += ooboffs % flash->mtd.oobsize; + + *column += flash->mtd.writesize; + + if(*column > (flash->mtd.writesize + FLASH_OOB_SIZE)) + return -EINVAL; + + return 0; +} + + + +static int spinand_do_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + + struct ak_spinand *flash = mtd_to_spiflash(mtd); + int ret = 0; + size_t rlen = 0; + u32 xfer_len; + u32 offset = 0; + int row, column; + uint8_t *oobrw_buf; + int readlen ; + uint8_t *buf = NULL ; + ops->oobretlen = 0; + readlen = ops->ooblen; + buf = ops->oobbuf; + + DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08Lx, len = %i\n", + __func__, (unsigned long long)from, readlen); + + + if (unlikely(ops->ooboffs >= mtd->oobsize)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to start read " + "outside oob\n", __func__); + return -EINVAL; + } + + /* Do not allow reads past end of device */ + if (unlikely(from >= mtd->size || + ops->ooboffs + readlen > ((mtd->size >> flash->page_shift) - + (from >> flash->page_shift)) * mtd->oobsize)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read beyond end " + "of device\n", __func__); + return -EINVAL; + } + oobrw_buf =(uint8_t *)kmalloc(SPINAND_OOB_LEN(flash->info), GFP_KERNEL); + if (!oobrw_buf) { + oobrw_buf =(uint8_t *)kmalloc(SPINAND_OOB_LEN(flash->info), GFP_KERNEL); + if (!oobrw_buf) { + printk("allocate memory for pInit_info failed\n"); + return -ENOMEM; + } + } + spinand_oobpos_to_coord(flash, from, ops->ooboffs, &row, &column); + memset(oobrw_buf,0,SPINAND_OOB_LEN(flash->info)); + // printk("rd:xfer_len:%d,%d,%d,%d,%d", row, column, xfer_len, from, ops->ooboffs); + ret = spinand_read(mtd, row, column, SPINAND_OOB_LEN(flash->info), &rlen, oobrw_buf); + if(unlikely(ret)) { + ret = -EBUSY; + goto out; + } + column = 0; + //column += flash->info.oob_up_skiplen; + column += flash->info.oob_vail_data_offset; + + + while(readlen > 0) { + + xfer_len = (readlen > flash->info.oob_seglen) ? flash->info.oob_seglen : readlen; + memcpy(buf + offset, oobrw_buf + column, xfer_len); + + column += (flash->info.oob_up_skiplen + flash->info.oob_seglen + flash->info.oob_down_skiplen); + + readlen -= xfer_len; + offset += xfer_len; + ops->oobretlen += xfer_len; + } +out: + kfree(oobrw_buf); + return ret; +} + + +static int ak_spinand_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + int ret = -ENOTSUPP; + struct ak_spinand *flash = mtd_to_spiflash(mtd); + uint32_t page = 0; + + ops->retlen = 0; + + /* Do not allow reads past end of device */ + if (ops->datbuf && (from + ops->len) > mtd->size) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read " + "beyond end of device\n", __func__); + return -EINVAL; + } + + switch(ops->mode) { + case MTD_OOB_PLACE: + case MTD_OOB_AUTO: + case MTD_OOB_RAW: + break; + + default: + goto out; + } + + if (!ops->datbuf){ +// printk("spinand_do_read_oob\n"); + ret = spinand_do_read_oob(mtd, from, ops); + } else { +// printk("spinand_do_read_page\n"); + ret = spinand_do_read_page(mtd, from, ops); + } + + if(ret != 0) + { + page = ((from >> flash->page_shift) & 0xffffff); + //printk(KERN_ERR"ak_check_error_to_update_flag_and_reboot&&&&&&&&&&&&&&&&&&&:%d,%d,%d\n", (uint32_t)from, page, flash->page_shift); + ak_check_error_to_update_flag_and_reboot(page); + } + + out: +// printk("%s,%d\n",__func__,ret); + return ret;//ret; +} + + +static int spinand_do_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + int ret = 0; + size_t rlen = 0; + u32 xfer_len; + u32 offset = 0; + int row, column; + int ofs = mtd->writesize; + int writelen = ops->ooblen; + uint8_t *buf = ops->oobbuf; + struct ak_spinand *flash = mtd_to_spiflash(mtd); + + DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08Lx, len = %i\n", + __func__, (unsigned long long)to, writelen); + + if (unlikely(ops->ooboffs >= mtd->oobsize)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to start read " + "outside oob\n", __func__); + return -EINVAL; + } + + /* Do not allow reads past end of device */ + if (unlikely(to >= mtd->size || + ops->ooboffs + writelen > ((mtd->size >> flash->page_shift) - + (to >> flash->page_shift)) * mtd->oobsize)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond end " + "of device\n", __func__); + return -EINVAL; + } + ret = spinand_oobpos_to_coord(flash, to, ops->ooboffs, &row, &column); + //column += flash->info.oob_up_skiplen; + column += flash->info.oob_vail_data_offset; + while(writelen> 0) { + xfer_len = adjust_xfer_len(flash, column, writelen); + + //printk("wr:to(%d)ofs(%d):%d,%d,%d,%p", (u32)to, ops->ooboffs, row, column, xfer_len, buf); + ret = spinand_write(mtd, row, column, xfer_len, &rlen, buf + offset); + if(unlikely(ret)) { + ret = -EBUSY; + goto out; + } + + column += (flash->info.oob_up_skiplen + rlen + flash->info.oob_down_skiplen); + if(column >= ofs + SPINAND_OOB_LEN(flash->info)) { + column = ofs; + row++; + } + writelen -= rlen; + offset += rlen; + } +out: + return 0; + +} + +static int spinand_do_write_page(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) +{ + int ret = 0; + size_t rlen = 0; + u32 xfer_len,read_len_oob,oob_size,oob_xfer_len,oob_seglen_len;// read_len, + u16 spare_offset; + u32 offset = 0; + u32 oob_offset = 0; + u32 oob_add_len = 0; + u32 count = ops->len; + int row, column; + int row_oob, column_oob; + uint8_t buftmp_oob[64]={0}; + uint8_t *p_buftmp_oob; + uint8_t buftmp[2112]={0}; + u32 buftmp_len = 0; +// uint8_t *buf = NULL; + int i; + uint8_t *oobbuf = ops->oobbuf; + uint8_t *datbuf = ops->datbuf; + struct ak_spinand *flash = mtd_to_spiflash(mtd); + /*decode row/column in address param*/ + row = ((to>>flash->page_shift) & 0xffffff); + column = (to & 0x7ff); + + read_len_oob = ops->ooblen; + oob_seglen_len = flash->info.oob_up_skiplen + flash->info.oob_seglen + flash->info.oob_down_skiplen; + oob_size = flash->info.oob_size; + oob_xfer_len = flash->info.oob_seglen; + spare_offset = flash->info.oob_vail_data_offset; + + while(count > 0) { + xfer_len = (count > FLASH_BUF_SIZE) ? FLASH_BUF_SIZE : count; + + /*transfer len not greater than page size*/ + if(xfer_len > flash->info.page_size) + xfer_len = ALIGN_DOWN(xfer_len, flash->info.page_size); + if(xfer_len+column >= flash->info.page_size) + xfer_len = flash->info.page_size - column; + + memcpy(buftmp, datbuf+offset, xfer_len); + + + spinand_oobpos_to_coord(flash, to, ops->ooboffs, &row_oob, &column_oob); + ret = spinand_read(mtd, row_oob, column_oob, oob_size, &rlen, buftmp_oob/**/); + + PDEBUG("count=%d, xfer_data_len=%d, xfer_oob_len=%d\n", count, xfer_len, read_len_oob); + PDEBUG("row_oob=%d, column_oob=%d\n", row_oob, column_oob); + + for(i=0; i<4; i++) + PDEBUG("buftmp_oob[%d] = 0x%02x ", i, buftmp_oob[i]); + PDEBUG("\n"); + + p_buftmp_oob = buftmp_oob + spare_offset; //offset to spare data + + while(read_len_oob> 0){ + buftmp_len = (read_len_oob > oob_xfer_len) ? oob_xfer_len : read_len_oob; + memcpy(p_buftmp_oob + oob_add_len, oobbuf+oob_offset, buftmp_len); + read_len_oob -= buftmp_len; + oob_offset += buftmp_len; + oob_add_len +=oob_seglen_len; + } + memcpy(buftmp+xfer_len, buftmp_oob, oob_size); + + + ret = spinand_write(mtd, row, column, xfer_len+oob_size, &rlen, buftmp); + if(unlikely(ret)) { + ret = -EBUSY; + goto out; + } + + row++; + column = 0; +// *retlen += xfer_len; + count -= xfer_len; + offset += xfer_len; + } +out: + return 0;//ret; + +} + +#if 0 +static int spinand_do_write_ops(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + int ret; + + ret = spinand_do_write_oob(mtd, to, ops); + + ret |= ak_spinand_write(mtd, to, ops->len, &ops->retlen, ops->datbuf); + return ret; +} +#endif + + +static int ak_spinand_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + int ret = -ENOTSUPP; + + ops->retlen = 0; + + /* Do not allow writes past end of device */ + if (ops->datbuf && (to + ops->len) > mtd->size) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond " + "end of device\n", __func__); + return -EINVAL; + } + + switch(ops->mode) { + case MTD_OOB_PLACE: + case MTD_OOB_AUTO: + case MTD_OOB_RAW: + break; + + default: + goto out; + } + + if (!ops->datbuf) + ret = spinand_do_write_oob(mtd, to, ops); + else + ret = spinand_do_write_page(mtd, to, ops);// spinand_do_write_ops(mtd, to, ops); + + out: + return 0;//ret; +} + +static int _ak_spinand_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + ak_spinand_read(mtd,from,len,retlen,buf); + return 0; +} + +static int _ak_spinand_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + ak_spinand_write(mtd,to,len,retlen,buf); + return 0; +} + +/** + * nand_block_bad - [anyka] Read bad block marker in anyka bbt + * @mtd: MTD device structure + * @ofs: offset from device start + * + * Check, if the block is bad. + */ +int ak_spinand_block_isbad(struct mtd_info *mtd, loff_t offs) +{ + T_U32 blk_no = 0; + T_U32 ret = 0; + + if (offs > mtd->size) + { + printk("spinand %s %d offs=0x%llx erase:%08x Error!!!\n!!!\n", __func__, __LINE__, offs, mtd->erasesize); + return -EINVAL; + } + + blk_no = div_u64(offs, mtd->erasesize); + //t = FHA_check_bad_block(blk_no); + ret = spinand_is_badblock(blk_no); + + ret = !ret; + if(ret) + { + printk("blk_no:%ld is bad block\n!!!\n", blk_no); + } + + return ret; +} + +/** + * ak_nand_block_markbad - [anyka] mark a block bad in anyka bbt + * @mtd: MTD device structure + * @ofs: offset from device start + * +*/ +int ak_spinand_block_markbad(struct mtd_info *mtd, loff_t offs) +{ + T_U32 blk_no = 0; + T_U32 ret = 0; + //bool protect = false; + + if (offs > mtd->size) + { + printk("spinand %s %d offs=0x%llx erase:%08x Error!!!\n!!!\n", __func__, __LINE__, offs, mtd->erasesize); + return -EINVAL; + } + + //protect = ak_spinand_get_protect(); + + // if(protect) + //ak_spinand_cfg_protect(false); + + blk_no = div_u64(offs, mtd->erasesize); + //t = FHA_set_bad_block(blk_no); + ret = spinand_set_badblock(blk_no); + //printk("ak_spinand_block_markbad:%d\n", ret); + // if(protect) + //ak_spinand_cfg_protect(true); + + if(ret == 0) + { + return 0; //mark successed + } + else + { + return 1; //mark failed + } +} + + + +/** +* @brief MTD get device ID +* +* get the device ID of the spi nand chip. +* @author lixinhai +* @date 2014-03-20 +* @param[in] mtd mtd info handle. +* @return int return device ID of the spi nand chip. +*/ +static int ak_spinand_get_devid(struct mtd_info *mtd) +{ + struct ak_spinand *flash = mtd_to_spiflash(mtd); + int ret; + u8 id[5]; + u32 jedec; + + /* Wait until finished previous write command. */ + if (wait_till_ready(flash)) + return -EBUSY; + + flash->cmd_buf[0] = OPCODE_RDID; + flash->cmd_buf[1] = 0x0; + /* JEDEC also defines an optional "extended device information" + * string for after vendor-specific data, after the three bytes + * we use here. Supporting some chips might require using it. + */ + ret = spi_write_then_read(flash->spi, flash->cmd_buf, 2, id, 4); + if (ret < 0) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d ak_spinand_get_devid\n", + dev_name(&flash->spi->dev), ret); + return AK_FALSE; + } + + jedec = id[0] | (id[1]<<8) | (id[2]<<16) | (id[3]<<24) ; + printk("spi nand ID: 0x%08x\n", jedec); + + return jedec; +} + +static int ak_fha_erase_callback(T_U32 chip_num, T_U32 block) +{ + struct erase_info einfo; + struct ak_spinand *flash = mtd_to_spiflash(ak_mtd_info); + + memset(&einfo, 0, sizeof(struct erase_info)); + einfo.addr = block* flash->info.page_per_block * flash->info.page_size; + einfo.len = ak_mtd_info->erasesize; + einfo.mtd = ak_mtd_info; + + if(ak_spinand_erase(ak_mtd_info, &einfo) == 0) + { + return FHA_SUCCESS; + } + else + { + printk(KERN_ERR"***erase failed, block:%ld\n", block); + return FHA_FAIL; + } +} + +#if 0 +T_U32 ak_fha_write_callback(T_U32 chip_num, T_U32 page_num, const T_U8 *data, + T_U32 data_len, T_U8 *oob, T_U32 oob_len, T_U32 eDataType) +{ + int ret; + ssize_t retlen; + struct ak_spinand *flash = mtd_to_spiflash(ak_mtd_info); + loff_t to = page_num * flash->info.page_size; + + + ret = ak_spinand_write(ak_mtd_info, to, data_len, &retlen, (u_char *)data); + if(ret) + { + printk("%s:%d\n", __func__, __LINE__); + return FHA_FAIL; + } +#if 0 + struct mtd_oob_ops ops; + if(oob_len >0) + { + + ops.ooblen = oob_len; + ops.oobbuf = oob; + ops.ooboffs = 0; + ret = spinand_do_write_oob(ak_mtd_info, to, &ops); + if (ret) { + printk("%s:%d\n", __func__, __LINE__); + return FHA_FAIL; + } + } +#endif + return FHA_SUCCESS; +} + +#else +int ak_fha_write_callback(T_U32 chip_num, T_U32 page_num, const T_U8 *data,T_U32 data_len, T_U8 *oob, T_U32 oob_len, T_U32 eDataType) +{ + int row = 0, column = 0; + int row_oob = 0, column_oob = 0; + uint8_t buftmp_oob[64]={0}; + uint8_t *p_buftmp_oob; + uint8_t buftmp[2112]={0}; + u32 buftmp_len = 0; + u32 count = data_len; + u32 xfer_len,read_len_oob,oob_size,oob_xfer_len,oob_seglen_len;// read_len, + size_t rlen = 0; + u32 offset = 0; + u16 spare_offset = 0; + int i = 0; + u32 oob_offset = 0; + u32 oob_add_len = 0; + u32 ooboffs = 0; + + int ret; + struct ak_spinand *flash = mtd_to_spiflash(ak_mtd_info); + loff_t to = page_num * flash->info.page_size; + + + row = ((to>>flash->page_shift) & 0xffffff); + column = (to & 0x7ff); + + read_len_oob = oob_len; + oob_seglen_len = flash->info.oob_up_skiplen + flash->info.oob_seglen + flash->info.oob_down_skiplen; + oob_size = flash->info.oob_size; + oob_xfer_len = flash->info.oob_seglen; + spare_offset = flash->info.oob_vail_data_offset; + ooboffs = flash->info.oob_vail_data_offset; + + while(count > 0) { + xfer_len = (count > FLASH_BUF_SIZE) ? FLASH_BUF_SIZE : count; + + /*transfer len not greater than page size*/ + if(xfer_len > flash->info.page_size) + xfer_len = ALIGN_DOWN(xfer_len, flash->info.page_size); + if(xfer_len+column >= flash->info.page_size) + xfer_len = flash->info.page_size - column; + + memcpy(buftmp, data+offset, xfer_len); + + //printk("ooboffs:%d\n", ooboffs); + spinand_oobpos_to_coord(flash, to, ooboffs, &row_oob, &column_oob); + ret = spinand_read(ak_mtd_info, row_oob, column_oob, oob_size, &rlen, buftmp_oob/**/); + + PDEBUG("count=%d, xfer_data_len=%d, xfer_oob_len=%d\n", count, xfer_len, read_len_oob); + PDEBUG("row_oob=%d, column_oob=%d\n", row_oob, column_oob); + + for(i=0; i<4; i++) + PDEBUG("buftmp_oob[%d] = 0x%02x ", i, buftmp_oob[i]); + PDEBUG("\n"); + + p_buftmp_oob = buftmp_oob + spare_offset; //offset to spare data + + + while(read_len_oob> 0){ + buftmp_len = (read_len_oob > oob_xfer_len) ? oob_xfer_len : read_len_oob; + memcpy(p_buftmp_oob + oob_add_len, oob+oob_offset, buftmp_len); + read_len_oob -= buftmp_len; + oob_offset += buftmp_len; + oob_add_len +=oob_seglen_len; + } + memcpy(buftmp+xfer_len, buftmp_oob, oob_size); + + + ret = spinand_write(ak_mtd_info, row, column, xfer_len+oob_size, &rlen, buftmp); + if(unlikely(ret)) { + ret = -1; + goto out; + } + + row++; + column = 0; + // *retlen += xfer_len; + count -= xfer_len; + offset += xfer_len; + } + +out: + return ret; +} + + +#endif + +int ak_fha_read_callback(T_U32 chip_num, T_U32 page_num, T_U8 *data, + T_U32 data_len, T_U8 *oob, T_U32 oob_len, T_U32 eDataType) +{ + int ret; + ssize_t retlen; + struct mtd_oob_ops ops; + //printk("%s,data_len:%d,oob_len:%d\n",__func__,data_len,oob_len); + struct ak_spinand *flash = mtd_to_spiflash(ak_mtd_info); + loff_t from = page_num * flash->info.page_size; + + + //printk("%d, %d, %d, %d\n", from, page_num, flash->info.page_size, data_len); + if(data_len > 0) + { + ret = ak_spinand_read(ak_mtd_info, from, data_len, &retlen, data); + if (ret) { + printk("%s:%d\n", __func__, __LINE__); + return FHA_FAIL; + } + } +#if 1 + + if(oob_len >0) + { + + ops.ooblen = oob_len; + ops.oobbuf = oob; + ops.ooboffs = 0; + ret = spinand_do_read_oob(ak_mtd_info, from, &ops); + if (ret) { + printk("%s:%d\n", __func__, __LINE__); + return FHA_FAIL; + } + } +#endif + return FHA_SUCCESS; +} + +int ak_fha_readbyte_callback(T_U32 chip_num, T_U32 page_num, T_U8 *data, + T_U32 data_len, T_U8 *oob, T_U32 oob_len, T_U32 eDataType) +{ + int ret; + ssize_t retlen; + //printk("%s,data_len:%d,oob_len:%d\n",__func__,data_len,oob_len); + struct ak_spinand *flash = mtd_to_spiflash(ak_mtd_info); + loff_t from = page_num * flash->info.page_size; + + if(data_len > 0) + { + ret = ak_spinand_read_noecc(ak_mtd_info, from, data_len, &retlen, data); + if (ret) { + printk("%s:%d\n", __func__, __LINE__); + return FHA_FAIL; + } + } +#if 0 + struct mtd_oob_ops ops; + if(oob_len >0) + { + + ops.ooblen = oob_len; + ops.oobbuf = oob; + ops.ooboffs = 0; + ret = spinand_do_read_oob(ak_mtd_info, from, &ops); + if (ret) { + printk("%s:%d\n", __func__, __LINE__); + return FHA_FAIL; + } + } +#endif + return FHA_SUCCESS; +} + +static T_VOID *fha_ram_alloc(T_U32 size) +{ + return kmalloc(size, GFP_KERNEL); +} + +static T_VOID *fha_ram_free(void *point) +{ + kfree(point); + return NULL; +} + +static T_S32 fha_print(T_pCSTR fmt, ...) +{ + va_list args; + int r; + + va_start(args, fmt); + vprintk("FHA:",args); + r = vprintk(fmt, args); + va_end(args); + + return r; +} + +#if 0 +int ak_get_partition_bak_name_idex(T_U32 partition_idex) +{ + char name[8] = {0}; + T_U32 i = 0, nr_parts= 0; + T_PARTITION_TABLE_INFO *parts = NULL; + + nr_parts = *(unsigned long *)g_part_tab.table; + parts = (T_PARTITION_TABLE_INFO *)(&g_part_tab.table[sizeof(unsigned long)]); + + //printk(KERN_ERR "nr_parts:%d\n", nr_parts); + for(i = 0; i < nr_parts; i++) + { + memset(name, 0, 8); + memcpy(name, parts[i].partition_info.name, PARTITION_NAME_LEN); + + //printk(KERN_ERR "name:%s, %s\n",g_partition_info.partition_name_info[partition_idex].partition_name, name); + if(strlen(g_partition_info.partition_name_info[partition_idex].partition_name) == strlen(name)) + { + if(memcmp(g_partition_info.partition_name_info[partition_idex].partition_name, name, strlen(name)) == 0) + { + break; + } + } + } + + if(i == nr_parts || i == 0) + { + return -1; + } + + + return (int)i; +} + + + +int ak_ckeck_partition_A_bak_name(void) +{ + char buf[10] = {0}; + T_U32 str_len = 0; + int idex = 0; + char *dev_name_temp = "/dev/mtdblock"; + T_U32 partition_idex = A_PARTITION_IDEX; //1表示A分区 + + //判断ROOT盘应用哪个mtdblock + if(g_partition_info.partition_name_info[partition_idex].partition_name[0] != 0 && g_partition_info.partition_cnt == PARTITION_CNT) + { + str_len = strlen(dev_name_temp); + idex = ak_get_partition_bak_name_idex(partition_idex); + if(idex == -1) + { + return -1; + } + + printk(KERN_ERR "saved_root_name:%s\n", saved_root_name); + printk(KERN_ERR "idex:%d, str_len:%ld\n", idex, str_len); + memset(buf, 0, 10); + sprintf(buf, "%d", idex + 1); + memcpy(&saved_root_name[str_len], buf, strlen(buf)+1); + } + + printk(KERN_ERR "saved_root_name:%s\n", saved_root_name); + + return 0; +} +#endif + + + + +#define NAND_BOIS_NAME "KERNEL" +#define NAND_A_NAME "A" +#define NAND_B_NAME "B" + + +int ak_get_readonly_partition_pos(unsigned long page_size) +{ + unsigned long nr_parts = 0; + unsigned long i = 0; + T_PARTITION_TABLE_INFO *parts; + unsigned char partition_name[8] = {0}; + + + printk(KERN_ERR "ak_get_readonly_partition_pos :%ld\n", page_size); + + if (page_size == 0) { + printk(KERN_ERR "page_size error :%ld\n", page_size); + return -1; + } + + nr_parts = ((unsigned long *)g_part_tab.table)[0]; + + printk(KERN_ERR "nr_parts:%ld\n", nr_parts); + + parts = (T_PARTITION_TABLE_INFO *)(&g_part_tab.table[sizeof(unsigned long)]); + for (i = 0; i < nr_parts; i++) { + memset(partition_name, 0, 8); + memcpy(partition_name, parts[i].partition_info.name, PARTITION_NAME_LEN); + + //printk(KERN_ERR "partition_name:%s, %s\n",NAND_BOIS_NAME, partition_name); + + if(strlen(NAND_BOIS_NAME) == strlen(partition_name) && memcmp(NAND_BOIS_NAME, partition_name, strlen(partition_name)) == 0) + { + kernel_start_page = parts[i].partition_info.start_pos/page_size; + kernel_end_page = kernel_start_page + parts[i].partition_info.ksize*1024/page_size; + + printk(KERN_ERR "NAND_BOIS_NAME:%s, kernel_start_page:%ld, kernel_end_page:%ld\n", NAND_BOIS_NAME, kernel_start_page, kernel_end_page); + } + else if(strlen(NAND_A_NAME) == strlen(partition_name) && memcmp(NAND_A_NAME, partition_name, strlen(partition_name)) == 0) + { + rtf_start_page = parts[i].partition_info.start_pos/page_size; + rtf_end_page = rtf_start_page + parts[i].partition_info.ksize*1024/page_size; + + printk(KERN_ERR "NAND_A_NAME:%s, rtf_start_page:%ld, rtf_end_page:%ld\n", NAND_A_NAME, rtf_start_page, rtf_end_page); + } + + + } + return 0; + +} + +extern void ak39_restart(char str, const char *cmd); + +int ak_check_error_to_update_flag_and_reboot(unsigned long page) +{ + unsigned char buf[64] = {0}; + unsigned char knl_page_error_flag = 0; + unsigned char rtf_page_error_flag = 0; + T_PARTITION_INFO *partition_info = (T_PARTITION_INFO *)buf; + + struct ak_spinand *flash = mtd_to_spiflash(ak_mtd_info); + unsigned long blk_no = page/flash->info.page_per_block; + + if(spinand_is_badblock(blk_no) == 0) + { + return 0; + } + + //printk(KERN_ERR "ak_check_error_to_update_flag_and_reboot@@@@@@@@@@@@@@@@@@@@@@@@@@@:%d, %d, %ld\r\n", knl_page_error_flag, rtf_page_error_flag, page); + + if(kernel_start_page != 0 && kernel_end_page != 0) + { + if(page >= kernel_start_page && page < kernel_end_page) + { + knl_page_error_flag = 1; + + } + + } + + if(rtf_start_page != 0 && rtf_end_page != 0) + { + if(page >= rtf_start_page && page < rtf_end_page) + { + rtf_page_error_flag = 1; + + } + + } + + partition_info->partition_cnt = PARTITION_CNT; + + // + if(knl_page_error_flag == 1) + { + memcpy(partition_info->partition_name_info[0].update_flag, UPDATE_END, strlen(UPDATE_END)); + } + + if(rtf_page_error_flag == 1) + { + memcpy(partition_info->partition_name_info[1].update_flag, UPDATE_END, strlen(UPDATE_END)); + } + + //printk(KERN_ERR "ak_check_error_to_update_flag_and_reboot@@@@@@@@@@@@@@@@@@@@@@@@@@@:%d, %d, %ld\r\n", knl_page_error_flag, rtf_page_error_flag, page); + if(knl_page_error_flag == 1 || rtf_page_error_flag == 1) + { + // //printk(KERN_ERR "spinand_write_asa_data@@@@@@@@@@@@@@@@@@@@@@@@@@@:%d, %d, %ld\r\n", knl_page_error_flag, rtf_page_error_flag, page); + printk(KERN_ERR "page read data error, and reboot system to reconvert the parttition\r\n"); + if(spinand_write_asa_data(buf, sizeof(T_PARTITION_INFO)) == -1) + { + printk(KERN_ERR "spinand_write_asa_data fail\r\n"); + return -1; + } + //msleep(2000); + + // + ak39_restart(0, NULL); + } + + return 0; +} + + + + +static int ak_fha_init(void) +{ + int ret = FHA_FAIL; + //T_PFHA_INIT_INFO pInit_info = NULL; + T_PFHA_LIB_CALLBACK pCallback = NULL; + T_NAND_PHY_INFO pNandPhyInfo; + struct ak_spinand *flash = mtd_to_spiflash(ak_mtd_info); + unsigned long asa_start_block = 1; + //unsigned char pa_data[64] = {0}; + + pCallback = kmalloc(sizeof(T_FHA_LIB_CALLBACK), GFP_KERNEL); + if (!pCallback) { + printk("allocate memory for pCallback failed\n"); + ret = -ENOMEM; + goto err_out; + } + + pCallback->Erase = ak_fha_erase_callback; + pCallback->Write = (FHA_Write)ak_fha_write_callback; + pCallback->Read = (FHA_Read)ak_fha_read_callback; + pCallback->ReadNandBytes = (FHA_ReadNandBytes)ak_fha_readbyte_callback; + pCallback->RamAlloc = fha_ram_alloc; + pCallback->RamFree = fha_ram_free; + pCallback->MemCmp = (FHA_MemCmp)memcmp; + pCallback->MemSet = (FHA_MemSet)memset; + pCallback->MemCpy = (FHA_MemCpy)memcpy; + pCallback->Printf = (FHA_Printf)fha_print; + + + //ret = FHA_mount(pInit_info, pCallback, &pNandPhyInfo); + + pNandPhyInfo.chip_id = flash->info.jedec_id; + pNandPhyInfo.page_size = flash->info.page_size; + pNandPhyInfo.page_per_blk = flash->info.page_per_block; + pNandPhyInfo.blk_num = flash->info.n_blocks; + pNandPhyInfo.plane_blk_num = flash->info.n_blocks; + pNandPhyInfo.spare_size = flash->info.oob_size; + + ret = partition_init(pCallback, &pNandPhyInfo, 0); + if (ret == FHA_FAIL) { + printk(KERN_ERR "partition_init failed\n"); + ret = FHA_FAIL; + } else { + ret = 0; + } + + //获取第一个坏块表的开始块 + + + //badblock init + if(-1 == spinand_babblock_tbl_init(0, true, asa_start_block)) + { + printk(KERN_ERR "spinand_babblock_tbl_init failed\n"); + ret = FHA_FAIL; + } + else + { + ret = 0; + } + + //printk(KERN_ERR "saved_root_name:%s\n", saved_root_name); + //read the update partition name + #if 0 + memset(pa_data, 0, 64); + + if(spinand_read_asa_data(pa_data, sizeof(T_PARTITION_INFO)) == -1) + { + printk(KERN_ERR "not read the update partition name\n"); + memset(&g_partition_info, 0, sizeof(T_PARTITION_INFO)); + } + else + { + memcpy(&g_partition_info, pa_data, sizeof(T_PARTITION_INFO)); + printk(KERN_ERR "g_partition_info.partition_cnt:%ld\n", g_partition_info.partition_cnt); + printk(KERN_ERR "partition_name_info[0].partition_name:%s\n", g_partition_info.partition_name_info[0].partition_name); + printk(KERN_ERR "partition_name_info[1].partition_name:%s\n", g_partition_info.partition_name_info[1].partition_name); + printk(KERN_ERR "partition_name_info[2].partition_name:%s\n", g_partition_info.partition_name_info[2].partition_name); + + } + #endif + + + //FHA_asa_scan(AK_TRUE); + kfree(pCallback); +err_out: + //kfree(pInit_info); + return ret; +} + +int ak_fha_init_for_update(int n) +{ +#if 0 + int ret = 0,retval = 0; + T_PFHA_INIT_INFO pInit_info = NULL; + T_PFHA_LIB_CALLBACK pCallback = NULL; + T_NAND_PHY_INFO pNandPhyInfo; + + pInit_info = kmalloc(sizeof(T_FHA_INIT_INFO), GFP_KERNEL); + if (!pInit_info) { + printk("allocate memory for pInit_info failed\n"); + return -ENOMEM; + } + + pInit_info->nChipCnt = 1; + pInit_info->nBlockStep = 1; + pInit_info->eAKChip = FHA_CHIP_SET_TYPE; + pInit_info->ePlatform = PLAT_LINUX; + pInit_info->eMedium = MEDIUM_SPI_NAND; + pInit_info->eMode = MODE_UPDATE; + + pCallback = kmalloc(sizeof(T_FHA_LIB_CALLBACK), GFP_KERNEL); + if (!pCallback) { + printk("allocate memory for pCallback failed\n"); + ret = -ENOMEM; + goto err_out; + } + + pCallback->Erase = ak_fha_erase_callback; + pCallback->Write = (FHA_Write)ak_fha_write_callback; + pCallback->Read = (FHA_Read)ak_fha_read_callback; + pCallback->RamAlloc = fha_ram_alloc; + pCallback->RamFree = fha_ram_free; + pCallback->MemCmp = (FHA_MemCmp)memcmp; + pCallback->MemSet = (FHA_MemSet)memset; + pCallback->MemCpy = (FHA_MemCpy)memcpy; + pCallback->Printf = (FHA_Printf)fha_print; + + ret = FHA_mount(pInit_info, pCallback, &pNandPhyInfo); + if (ret == FHA_FAIL) { + printk("FHA_mount failed\n"); + ret = -EINVAL; + } else { + ret = 0; + } + retval = FHA_burn_init(pInit_info, pCallback, &pNandPhyInfo); + FHA_asa_scan(AK_TRUE); + if(retval == FHA_FAIL) + { + goto err_out; + } + + + + kfree(pCallback); +err_out: + kfree(pInit_info); + return ret; +#else + int ret = 0; + + return ret; +#endif + + +} + + + + + +#if 0 +unsigned long ak_check_sn_flag(unsigned char *data) +{ + void *handle = NULL; + unsigned char *name = "ENV";//"NVT"; //"ENV" + unsigned long data_len; + int ret = -1; + + printk(KERN_ERR "partition_open:%s\n", name); + handle = partition_open(name); + if(handle == NULL) + { + printk(KERN_ERR "partition_open fail:%s\n", name); + return 0; + } + + data_len = partition_get_data_size(handle); + printk(KERN_ERR "data_len:%ld\n", data_len); + if(data_len == 0 || data_len > 2048) + { + partition_close(handle); + printk(KERN_ERR "the partition no have anything, name:%s, data_len:%ld\n", name, data_len); + return 0; + } + + ret = partition_read(handle, data, data_len); + if(ret == -1) + { + partition_close(handle); + printk(KERN_ERR "partition_read fail, data_len:%ld\n", data_len); + return 0; + } + + partition_close(handle); + + //printk(KERN_ERR "data:%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x\n", data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8]); + printk(KERN_ERR "data:%s\n", data); + + + return data_len; + + +} + + +int check_sn_data(char *data) +{ + if(memcmp(data, "0601BOK184403N", strlen("0601BOK184403N")) == 0){ + return 0; + } + + return -1; +} +#endif + +#if 0 +static int ak_check_chang_flash_param(void) +{ + unsigned long data_len = 0; + unsigned char data[2048] = {0}; + + struct ak_spinand *flash = mtd_to_spiflash(ak_mtd_info); + + printk(KERN_ERR "flash->info.oob_up_skiplen:%u\n", flash->info.oob_up_skiplen); + printk(KERN_ERR "flash->info.oob_seglen:%u\n", flash->info.oob_seglen); + printk(KERN_ERR "flash->info.oob_down_skiplen:%u\n", flash->info.oob_down_skiplen); + printk(KERN_ERR "flash->info.oob_seg_perpage:%u\n", flash->info.oob_seg_perpage); + + data_len = ak_check_sn_flag(data); + if(0 == data_len) + { + return -1; + } + + //0601BOK184403N000145 + if(check_sn_data(data) == 0) + { + flash->info.oob_up_skiplen = 0; + flash->info.oob_seglen = 16; + flash->info.oob_down_skiplen = 0; + printk(KERN_ERR "need to chang the flash param\n"); + } + printk(KERN_ERR "flash->info.oob_up_skiplen:%u\n", flash->info.oob_up_skiplen); + printk(KERN_ERR "flash->info.oob_seglen:%u\n", flash->info.oob_seglen); + printk(KERN_ERR "flash->info.oob_down_skiplen:%u\n", flash->info.oob_down_skiplen); + printk(KERN_ERR "flash->info.oob_seg_perpage:%u\n", flash->info.oob_seg_perpage); + + return 0; +} +#endif + + +#ifdef CONFIG_MTD_SPINAND_PRODUCER +static int ak_mount_partitions(struct spi_device *spi) +{ + int i, ret; + unsigned long nr_parts; + //unsigned char *buf; + T_PARTITION_TABLE_INFO *parts = NULL; + struct mtd_partition *mtd_part; + struct mtd_part_parser_data ppdata; + struct ak_spinand *flash = mtd_to_spiflash(ak_mtd_info); + + ret = ak_fha_init(); + if (ret == FHA_FAIL) { + printk("Init FHA lib failed\n"); + goto err_out; + } + + +#if 1 + ret = partition_get_partition_table(&g_part_tab,PART_SPINAND); + if (ret == FHA_FAIL) { + printk(KERN_ERR "get src partition failed, and read back partition\n"); + ret = partition_get_partition_backup_table(&g_part_tab,PART_SPINAND); + if (ret == FHA_FAIL) { + printk(KERN_ERR "get back partition info failed\n"); + ret = !ret; + goto no_parts; + } + } + + /* + * if no partiton to mount, the buf will be all 0xFF but not constant. + * So, it is not safe here. + */ + nr_parts = *(unsigned long *)g_part_tab.table; + //15 --> partition_get_partition_table only copy 512 byte,so max cnt is 15 + if (nr_parts <= 0 || nr_parts > 15) { + printk(KERN_ERR "get src partition num:%ld error, and read back partition\n", nr_parts); + ret = partition_get_partition_backup_table(&g_part_tab,PART_SPINAND); + if (ret == FHA_FAIL) { + printk(KERN_ERR "get partition info failed\n"); + ret = !ret; + goto no_parts; + } + + nr_parts = *(unsigned long *)g_part_tab.table; + if (nr_parts <= 0 || nr_parts > 15) + { + printk(KERN_ERR "partition count invalid, nr_parts:%ld\n", nr_parts); + ret = -EINVAL; + goto no_parts; + } + } + + mtd_part = kzalloc(sizeof(struct mtd_partition) * nr_parts, GFP_KERNEL); + if (!mtd_part) { + printk(KERN_ERR "allocate memory for mtd_partition failed\n"); + ret = -ENOMEM; + goto no_parts; + } + + parts = (T_PARTITION_TABLE_INFO *)(&g_part_tab.table[sizeof(unsigned long)]); + for (i = 0; i < nr_parts; i++) { + mtd_part[i].name = kzalloc(PARTITION_NAME_LEN, GFP_KERNEL); + memcpy(mtd_part[i].name, parts[i].partition_info.name, PARTITION_NAME_LEN); + mtd_part[i].size = parts[i].partition_info.ksize*1024; + mtd_part[i].offset = parts[i].partition_info.start_pos; + mtd_part[i].mask_flags = parts[i].partition_info.r_w_flag; + printk("mtd_part[%d]:\nname = %s\nsize = 0x%llx\noffset = 0x%llx\nmask_flags = 0x%x\n\n", + i, + mtd_part[i].name, + mtd_part[i].size, + mtd_part[i].offset, + mtd_part[i].mask_flags); + + } + + ak_get_readonly_partition_pos(flash->info.page_size); + //ak_ckeck_partition_A_bak_name(); + + //ak_check_chang_flash_param(); + + + /* + * call ak_partition_table_sys_create function , create sys kobject for app aplication + */ + ret = ak_partition_table_sys_create(&g_part_tab); + if (ret){ + printk(KERN_ERR "create sys partition table kobject failed!\n"); + ret = -EINVAL; + kfree(mtd_part); + goto no_parts; + } + +#else + + buf = kzalloc(flash->info.page_size, GFP_KERNEL); + if (!buf) { + printk("allocate memory for page buffer failed\n"); + ret = -ENOMEM; + goto err_out; + } + + ret = FHA_get_fs_part(buf, flash->info.page_size); + if (ret == FHA_FAIL) { + printk("get partition info failed\n"); + ret = !ret; + goto no_parts; + } + + nr_parts = *(unsigned long *)buf; + /* if no partiton to mount, the buf will be all 0xFF but not constant. + * So, it is not safe here. */ + printk("nr_parts=0x%lx\n", nr_parts); + if (nr_parts <= 0 || nr_parts > 32) { + printk("partition count invalid\n"); + ret = -EINVAL; + goto no_parts; + } + + mtd_part = kzalloc(sizeof(struct mtd_partition) * nr_parts, GFP_KERNEL); + if (!mtd_part) { + printk("allocate memory for mtd_partition failed\n"); + ret = -ENOMEM; + goto no_parts; + } + + parts = (struct partitions *)(&buf[sizeof(unsigned long)]); + + + for (i = 0; i < nr_parts; i++) { + mtd_part[i].name = kzalloc(MTD_PART_NAME_LEN, GFP_KERNEL); + memcpy(mtd_part[i].name, parts[i].name, MTD_PART_NAME_LEN); + mtd_part[i].size = parts[i].size; + mtd_part[i].offset = parts[i].offset; + mtd_part[i].mask_flags = parts[i].mask_flags; + printk("mtd_part[%d]:\nname = %s\nsize = 0x%llx\noffset = 0x%llx\nmask_flags = 0x%x\n\n", + i, mtd_part[i].name, mtd_part[i].size, mtd_part[i].offset, mtd_part[i].mask_flags); + } +/* + ret = add_mtd_partitions(ak_mtd_info, (const struct mtd_partition *)mtd_part, nr_parts); + + if (ret) { + printk("add mtd partition failed\n"); + goto no_parts; + } +*/ +#endif + ppdata.of_node = spi->dev.of_node; + + ret = mtd_device_parse_register(ak_mtd_info, NULL, &ppdata, + (const struct mtd_partition *)mtd_part, nr_parts); + if (ret) { + printk("add mtd partition failed\n"); + goto no_parts; + } + + +no_parts: + //kfree(buf); +err_out: + return ret; +} +#endif + +/** +* @brief get_flash_info +* +* Read the device ID and identify that it was supported or not. +* @author luoyongchuang +* @date 2016-03-17 +* @param[in] mtd spi device handle. +* @return int return the device info. +*/ + +static struct flash_info *__devinit get_flash_info(struct spi_device *spi) +{ + int tmp; + u8 cmd[2]; + u8 id[5]; + u32 jedec; + u16 ext_jedec = 0; + struct flash_info *info; + /* JEDEC also defines an optional "extended device information" + * string for after vendor-specific data, after the three bytes + * we use here. Supporting some chips might require using it. + */ + cmd[0] = OPCODE_RDID; + cmd[1] = 0x0; + tmp = spi_write_then_read(spi, cmd, 2, id, 4); + if (tmp < 0) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n", + dev_name(&spi->dev), tmp); + return NULL; + } + jedec = id[0]; + jedec = jedec << 8; + jedec |= id[1]; + jedec = jedec << 8; + jedec |= id[2]; + jedec = jedec << 8; + jedec |= id[3]; + + printk("akspi nand ID: 0x%08x\n", jedec); + + //ext_jedec = id[3] << 8 | id[4]; + for (tmp = 0, info = ak_spinand_supportlist; + tmp < ARRAY_SIZE(ak_spinand_supportlist); + tmp++, info++) { + if (info->jedec_id == jedec) { + if (info->ext_id != 0 && info->ext_id != ext_jedec) + continue; + return info; + } + } + dev_err(&spi->dev, "get_flash_info() unrecognized flash id %06x\n", jedec); + return NULL; +} + +static int ak_spinand_init_stat_reg(struct ak_spinand *flash) +{ + int i; + struct flash_status_reg *sr; + struct flash_info *info = &flash->info; + + for(i=0, sr=status_reg_list; ijedec_id == info->jedec_id) { + if (info->ext_id != 0 && info->ext_id != sr->ext_id) + continue; + flash->stat_reg = *sr; + return 0; + } + } + + flash->stat_reg = status_reg_list[i-1]; + return 0; +} + + + +/** +* @brief spi nand probe +* +* Initial the spi nand device driver to kernel. +* @author luoyongchuang +* @date 2016-03-17 +* @param[in] mtd spi device handle. +* @return int return write success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int __devinit ak_spinand_probe(struct spi_device *spi) +{ + + unsigned i; + unsigned ret = 0; + struct flash_info *info; + struct ak_spinand *flash; + struct flash_platform_data *data; + + printk("ak spinandflash probe enter.\n"); + + /* Platform data helps sort out which chip type we have, as + * well as how this board partitions it. If we don't have + * a chip ID, try the JEDEC id commands; they'll work for most + * newer chips, even if we don't recognize the particular chip. + */ + + data = spi->dev.platform_data; + info = get_flash_info(spi); + if (!info) + return -ENODEV; + + flash = kzalloc(sizeof *flash, GFP_KERNEL); + if (!flash) + return -ENOMEM; + + memset(flash, 0, sizeof *flash); + + ak_mtd_info = &flash->mtd; + + flash->spi = spi; + flash->info = *info; + mutex_init(&flash->lock); + dev_set_drvdata(&spi->dev, flash); + + sflash_reset(flash); + + if (data && data->name) + flash->mtd.name = data->name; + else + flash->mtd.name = dev_name(&spi->dev); + + flash->mtd.type = MTD_NANDFLASH; + flash->mtd.writesize = flash->info.page_size; + flash->mtd.flags = MTD_WRITEABLE; + flash->mtd.size = info->block_size * info->n_blocks; + flash->mtd._erase = ak_spinand_erase; + flash->mtd._write = _ak_spinand_write; + flash->mtd._read = _ak_spinand_read; + flash->mtd._read_oob = ak_spinand_read_oob; + flash->mtd._write_oob = ak_spinand_write_oob; + flash->mtd._block_isbad = ak_spinand_block_isbad; + flash->mtd._block_markbad = ak_spinand_block_markbad; + flash->mtd.get_device_id = ak_spinand_get_devid; + printk("%s, info->block_size = %d, info->n_blocks = %d\n", info->name, info->block_size, info->n_blocks); + + flash->erase_opcode = OPCODE_ERASE_BLOCK; + flash->mtd.erasesize = info->block_size; + flash->mtd.oobsize = info->oob_size; + flash->mtd.oobavail = info->oob_seglen * info->oob_seg_perpage; + + flash->bus_width = data->bus_width; + + printk("flash->bus_width:%x\n", flash->bus_width); + flash->page_shift = ffs(flash->mtd.writesize)-1; + +#ifdef SPINAND_USE_MTD_BLOCK_LAYER + /*pre-allocation buffer use for spi nand data transfer.*/ + flash->buf = kzalloc(FLASH_BUF_SIZE, GFP_KERNEL); + if (!flash->buf) { + printk("Allocate buf for spi page failed\n"); + kfree(flash); + return -ENOMEM; + } +#endif + + ak_spinand_init_stat_reg(flash); + ak_spinand_cfg_quad_mode(flash); + init_spiflash_rw_info(flash); + + flash->mtd.dev.parent = &spi->dev; + + dev_info(&spi->dev, "%s (%lld Kbytes)\n", info->name, + (long long)flash->mtd.size >> 10); + + DEBUG(MTD_DEBUG_LEVEL0, + "mtd .name = %s, .size = 0x%llx (%lldMiB) " + ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n", + flash->mtd.name, + (long long)flash->mtd.size, (long long)(flash->mtd.size >> 20), + flash->mtd.erasesize, flash->mtd.erasesize / 1024, + flash->mtd.numeraseregions); + + if (flash->mtd.numeraseregions) + for (i = 0; i < flash->mtd.numeraseregions; i++) + DEBUG(MTD_DEBUG_LEVEL0, + "mtd.eraseregions[%d] = { .offset = 0x%llx, " + ".erasesize = 0x%.8x (%uKiB), " + ".numblocks = %d }\n", + i, (long long)flash->mtd.eraseregions[i].offset, + flash->mtd.eraseregions[i].erasesize, + flash->mtd.eraseregions[i].erasesize / 1024, + flash->mtd.eraseregions[i].numblocks); + + //ak_spinand_cfg_protect(true); + + + /* partitions should match sector boundaries; and it may be good to + * use readonly partitions for writeprotected sectors (BP2..BP0). + */ + ret = mtd_device_parse_register(&flash->mtd, NULL, NULL, NULL, 0); + if (ret) { + printk("Add root MTD device failed\n"); + kfree(flash->buf); + kfree(flash); + return -EINVAL; + } +#ifdef CONFIG_MTD_SPINAND_PRODUCER + ret = ak_mount_partitions(spi); + if (ret) + printk("Add MTD partitions failed\n"); +#endif + printk("Init AK SPI Flash finish.\n"); + + return 0; +} + +/** +* @brief spi nand remove +* +* Remove the spi nand device driver from kernel. +* @author lixinhai +* @date 2014-03-20 +* @param[in] mtd spi device handle. +* @return int return write success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int __devexit ak_spinand_remove(struct spi_device *spi) +{ + struct ak_spinand *flash = dev_get_drvdata(&spi->dev); + int status; + + status = mtd_device_unregister(&flash->mtd); +/* + if (mtd_has_partitions() && flash->partitioned) + status = del_mtd_partitions(&flash->mtd); + + status = del_mtd_device(&flash->mtd); +*/ + if (status == 0) { + kfree(flash->buf); + kfree(flash); + } + return 0; +} + + +static struct spi_driver ak_spinand_driver = { + .driver = { + .name = "ak-spiflash", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = ak_spinand_probe, + .remove = __devexit_p(ak_spinand_remove), + + /* REVISIT: many of these chips have deep power-down modes, which + * should clearly be entered on suspend() to minimize power use. + * And also when they're otherwise idle... + */ +}; + +/** +* @brief spi nand device init +* +* Moudle initial. +* @author luoyongchuang +* @date 2016-05-17 +* @return int return write success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int __init ak_spinand_init(void) +{ + printk("init Anyka SPI Nand Flash driver\n"); + return spi_register_driver(&ak_spinand_driver); +} + + +/** +* @brief spi nand device exit +* +* Moudle exit. +* @author luoyongchuang +* @date 2016-05-17 +* @return None +*/ +static void __exit ak_spinand_exit(void) +{ + printk("exit Anyka SPI Nand Flash driver\n"); + spi_unregister_driver(&ak_spinand_driver); +} + + +module_init(ak_spinand_init); +module_exit(ak_spinand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Luoyongchuang"); +MODULE_DESCRIPTION("MTD SPI Nand flash driver for Anyka chips"); + diff --git a/drivers/mtd/devices/ak_spiflash.c b/drivers/mtd/devices/ak_spiflash.c new file mode 100644 index 00000000..afc49a44 --- /dev/null +++ b/drivers/mtd/devices/ak_spiflash.c @@ -0,0 +1,2403 @@ + /** + * @file /driver/mtd/devices/ak_SPIFlash.c + * @brief SPI Flash driver for Anyka AK37 platform. + * Copyright C 2012 Anyka CO.,LTD + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * @author She Shaohua + * @date 2012-03-23 + * @note 2011-03-20 created + * @note 2011-03-23 Debug OK. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +//#define SPIFLASH_DEBUG +#undef PDEBUG +#ifdef SPIFLASH_DEBUG +#define PDEBUG(fmt, args...) printk( KERN_INFO fmt,## args) +#define DEBUG(n, args...) printk(KERN_INFO args) +#else +#define PDEBUG(fmt, args...) +#define DEBUG(n, args...) +#endif + +#define FHA_SUCCESS 0 +#define FHA_FAIL -1 +#define SPI_FLASH_BIN_PAGE_START 558 +#define FLASH_BUF_SIZE (32*1024) +#define FLASH_PAGESIZE 256 +#define SPI_FLASH_READ 1 +#define SPI_FLASH_WRITE 2 +#define CONFIG_SPIFLASH_USE_FAST_READ 1 + +#define SPANSION_FLS_BA24_SET + +/* +* mtd layer allocate memory use for 'vmalloc' interface, need to convert. +*/ +//#define SPIFLASH_USE_MTD_BLOCK_LAYER + +#define OPCODE_WREN 0x06 /* Write Enable */ +#define OPCODE_WRDI 0x04 /* Write Disable */ +#define OPCODE_RDSR1 0x05 /* Read Status Register1 */ +#define OPCODE_RDSR2 0x35 /* Read Status Register2 */ +#define OPCODE_RDSR3 0x15 /* Read Status Register3 */ +#define XMC_OPCODE_RDSR2 0x09 /* XMC Spi flash Read Status Register2 */ +#define XMC_OPCODE_RDSR3 0x95 /* XMC Spi flash Read Status Register3 */ + +#define OPCODE_WRSR1 0x01 /* Write Status Register */ +#define OPCODE_WRSR2 0x31 /* Write Status2 Register eg:gd25q128c*/ +#define OPCODE_WRSR3 0x11 /* Write Status3 Register eg:gd25q128c*/ +#define XMC_OPCODE_WRSR3 0xC0 /* XMC Spi flash Write Status3 Register eg:gd25q128c*/ + +#define OPCODE_NORM_READ 0x03 /* Read Data Bytes */ +#define OPCODE_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */ +#define OPCODE_FAST_D_READ 0x3b /* Read Data Bytes at Dual output */ +#define OPCODE_FAST_Q_READ 0x6b /* Read Data Bytes at Quad output */ +#define OPCODE_FAST_D_IO 0xbb /* Read Data Bytes at Dual i/o */ +#define OPCODE_FAST_Q_IO 0xeb /* Read Data Bytes at Quad i/o */ + +#define OPCODE_PP 0x02 /* Page Program */ +#define OPCODE_PP_DUAL 0x12 /* Dual Page Program*/ +#define OPCODE_PP_QUAD 0x32 /* Quad Page Program*/ +#define OPCODE_2IO_PP 0x18 /* 2I/O Page Program (tmp)*/ +#define OPCODE_4IO_PP 0x38 /* 4I/O Page Program*/ + +#define OPCODE_BE_4K 0x20 /* Sector (4K) Erase */ +#define OPCODE_BE_32K 0x52 /* Block (32K) Erase */ +#define OPCODE_BE_64K 0xd8 /* Block (64K) Erase */ +#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ +#define OPCODE_CHIP_ERASE 0xc7 /* Chip Erase */ +#define OPCODE_RDID 0x9f /* Read JEDEC ID */ +#define OPCODE_DP 0xb9 /* Deep Power-down */ +#define OPCODE_RES 0xab /* Release from DP, and Read Signature */ + +/* +* Used for SST flashes only. +*/ +#define OPCODE_BP 0x02 /* Byte program */ +#define OPCODE_WRDI 0x04 /* Write disable */ +#define OPCODE_AAI_WP 0xad /* Auto address increment word program */ + +#define SPI_STATUS_REG1 1 +#define SPI_STATUS_REG2 2 +#define SPI_STATUS_REG3 3 + +#ifdef SPANSION_FLS_BA24_SET +#define OPCODE_BRWR 0xc5//0x17 /* Bank register write */ +#endif + +/* +* Define max times to check status register before we give up. +*/ +#define MAX_READY_WAIT_JIFFIES (40 * HZ) /* 40s max chip erase */ + +#define CMD_SIZE (1) +#define ADDR_SIZE (3) +#define CMD_ADDR_SIZE (CMD_SIZE + ADDR_SIZE) +#define MAX_DUMMY_SIZE (4) +#define MTD_PART_NAME_LEN (4) + +#ifdef CONFIG_SPIFLASH_USE_FAST_READ +#define OPCODE_READ OPCODE_FAST_READ +#define FAST_READ_DUMMY_BYTE 1 +#else +#define OPCODE_READ OPCODE_NORM_READ +#define FAST_READ_DUMMY_BYTE 0 +#endif + +#define ALIGN_DOWN(a, b) (((a) / (b)) * (b)) + +/****************************************************************************/ +struct partitions +{ + char name[MTD_PART_NAME_LEN]; + unsigned long long size; + unsigned long long offset; + unsigned int mask_flags; +}__attribute__((packed)); + +typedef struct +{ + T_U32 BinPageStart; /*bin data start addr*/ + T_U32 PageSize; /*spi page size*/ + T_U32 PagesPerBlock;/*page per block*/ + T_U32 BinInfoStart; + T_U32 FSPartStart; +} +T_SPI_BURN_INIT_INFO; + + +/* + * SPI device driver setup and teardown + */ +struct flash_info { + char *name; + + /* JEDEC id zero means "no ID" (most older chips); otherwise it has + * a high byte of zero plus three data bytes: the manufacturer id, + * then a two byte device id. + */ + u32 jedec_id; + u16 ext_id; + + /* The size listed here is what works with OPCODE_SE, which isn't + * necessarily called a "sector" by the vendor. + */ + unsigned sector_size; + u16 n_sectors; + + /** + * chip character bits: + * bit 0: under_protect flag, the serial flash under protection or not when power on + * bit 1: fast read flag, the serial flash support fast read or not(command 0Bh) + * bit 2: AAI flag, the serial flash support auto address increment word programming + * bit 3: support dual write or no + * bit 4: support dual read or no + * bit 5: support quad write or no + * bit 6: support quad read or no + * bit 7: the second status command (35h) flag,if use 4-wire(quad) mode,the bit must be is enable + */ + u16 flags; + +#define SFLAG_UNDER_PROTECT (1<<0) +#define SFLAG_FAST_READ (1<<1) +#define SFLAG_AAAI (1<<2) +#define SFLAG_COM_STATUS2 (1<<3) +#define SFLAG_DUAL_IO_READ (1<<4) +#define SFLAG_DUAL_READ (1<<5) +#define SFLAG_QUAD_IO_READ (1<<6) +#define SFLAG_QUAD_READ (1<<7) +#define SFLAG_DUAL_IO_WRITE (1<<8) +#define SFLAG_DUAL_WRITE (1<<9) +#define SFLAG_QUAD_IO_WRITE (1<<10) +#define SFLAG_QUAD_WRITE (1<<11) +#define SFLAG_SECT_4K (1<<12) +#define SFLAG_COM_STATUS3 (1<<13) +#define SFLAG_QUAD_NO_QE (1<<14) +#define SFLAG_COM_BIT24 (1<<15) //bit24λ��ǡ� +}; + +struct ak_spiflash; + +/** +* because of some spi flash is difference of status register difinition. +* this structure use mapping the status reg function and corresponding. +*/ +struct flash_status_reg +{ + u32 jedec_id; + u16 ext_id; + unsigned b_wip:4; /*write in progress*/ + unsigned b_wel:4; /*wrute ebabke latch*/ + unsigned b_bp0:4; /*block protected 0*/ + unsigned b_bp1:4; /*block protected 1*/ + unsigned b_bp2:4; /*block protected 2*/ + unsigned b_bp3:4; /*block protected 3*/ + unsigned b_bp4:4; /*block protected 4*/ + unsigned b_srp0:4; /*status register protect 0*/ + + unsigned b_srp1:4; /*status register protect 1*/ + unsigned b_qe:4; /*quad enable*/ + unsigned b_lb:4; /*write protect control and status to the security reg.*/ +/* +* unsigned b_reserved0:4; +* unsigned b_reserved1:4; +* unsigned b_reserved2:4; +*/ + unsigned b_cmp:4; /*conjunction bp0-bp4 bit*/ + unsigned b_sus:4; /*exec an erase/program suspend command*/ + unsigned b_as:8; /*0:3-Byte Address Mode. 1:4-Byte Address Mode*/ + unsigned b_ap:8; /*0:3-Byte Address Mode. 1:4-Byte Address Mode*/ + + u32 (*read_sr)(struct ak_spiflash *); + int (*write_sr)(struct ak_spiflash *, u32); +}; + +struct ak_spiflash { + struct spi_device *spi; + struct mutex lock; + struct flash_info info; + struct mtd_info mtd; + unsigned partitioned:1; + + u8 bus_width; + unsigned char *buf; + u8 command[CMD_ADDR_SIZE + MAX_DUMMY_SIZE]; + u8 dummy_len; + + u8 erase_opcode; + u8 tx_opcode; + u8 rx_opcode; + u8 txd_bus_width; + u8 rxd_bus_width; + + u8 txa_bus_width; + u8 rxa_bus_width; + struct flash_status_reg stat_reg; +}; + +#define CONFIG_SECTION_SIZE 4096 // cdh:add for erase section size +#define CONFIG_PARATION_TAB_SIZE 512 // cdh:add for paration table section size +#define CONFIG_SPIP_START_PAGE 0 // cdh:add for searching SPIP flag start flash offset address +#define CONFIG_SPIP_PAGE_CNT 3 // cdh:add for searching SPIP flag +#define CONFIG_PARATION_PAGE_CNT 2 // cdh:add for searching paration table page cnt + +#define FILL_CMD(c, val) do{c[0] = (val);}while(0) +#define FILL_ADDR(c, val) do{ \ + c[CMD_SIZE] = (val) >> 16; \ + c[CMD_SIZE+1] = (val) >> 8; \ + c[CMD_SIZE+2] = (val); \ + }while(0) + +#define FILL_DUMMY_DATA(c, val) do{ \ + c[CMD_ADDR_SIZE] = val >> 16; \ + c[CMD_ADDR_SIZE+1] = 0; \ + c[CMD_ADDR_SIZE+2] = 0; \ + c[CMD_ADDR_SIZE+3] = 0; \ + }while(0) + + +static struct mtd_info *ak_mtd_info; + +static inline int write_enable(struct ak_spiflash *flash); +static int wait_till_ready(struct ak_spiflash *flash); + + +/** +* @brief: because of the _read() function call by mtd block layer, the buffer be +* allocate by vmalloc() in mtd layer, spi driver layer may use this buffer that +* intents of use for DMA transfer, so, add this function to transition buffer. +* call this function at before real read/write data. +* +* @author lixinhai +* @date 2013-04-10 +* @param[in] *mtd :mtd infor. +* @return:struct ak_spiflash: ak_spiflash struct infor +* @retval ak_spiflash struct infor +*/ +static inline struct ak_spiflash *mtd_to_spiflash(struct mtd_info *mtd) +{ + return container_of(mtd, struct ak_spiflash, mtd); +} + +#ifdef SPIFLASH_USE_MTD_BLOCK_LAYER + +/** +* @brief: because of the _read() function call by mtd block layer, the buffer be +* allocate by vmalloc() in mtd layer, spi driver layer may use this buffer that +* intents of use for DMA transfer, so, add this function to transition buffer. +* call this function at before real read/write data. +* +* @author lixinhai +* @date 2013-04-10 +* @param[in] flash spiflash handle. +* @param[in] buffer. +* @param[in] buffer len +* @param[in] dir:read/write +* @return void +* @retval none +*/ +static void *flash_buf_bounce_pre(struct ak_spiflash *flash, + void *buf, u32 len, int dir) +{ + if(!is_vmalloc_addr(buf)) { + return buf; + } + + if(dir == SPI_FLASH_WRITE) { + memcpy(flash->buf, buf, len); + } + + return flash->buf; +} + +/** +* @brief: because of the _read() function call by mtd block layer, the buffer be +* allocate by vmalloc() in mtd layer, spi driver layer may use this buffer that +* intents of use for DMA transfer, so, add this function to transition buffer. +* call this function at after real read/write data +* +* @author lixinhai +* @date 2013-04-10 +* @param[in] flash spiflash handle. +* @param[in] buffer. +* @param[in] buffer len +* @param[in] dir:read/write +* @return void +* @retval none +*/ +static void flash_buf_bounce_post(struct ak_spiflash *flash, + void *buf, u32 len, int dir) +{ + if(!is_vmalloc_addr(buf)) { + return; + } + + if(dir == SPI_FLASH_READ) { + memcpy(buf, flash->buf, len); + } +} + +#else + +/** +* @brief: because of the _read() function call by mtd block layer, the buffer be +* allocate by vmalloc() in mtd layer, spi driver layer may use this buffer that +* intents of use for DMA transfer, so, add this function to transition buffer. +* call this function at before real read/write data. +* +* @author lixinhai +* @date 2013-04-10 +* @param[in] flash spiflash handle. +* @param[in] buffer. +* @param[in] buffer len +* @param[in] dir:read/write +* @return void +* @retval none +*/ +static inline void *flash_buf_bounce_pre(struct ak_spiflash *flash, + void *buf, u32 len, int dir) +{ + return buf; +} + +/** +* @brief: because of the _read() function call by mtd block layer, the buffer be +* allocate by vmalloc() in mtd layer, spi driver layer may use this buffer that +* intents of use for DMA transfer, so, add this function to transition buffer. +* call this function at after real read/write data +* +* @author lixinhai +* @date 2013-04-10 +* @param[in] flash spiflash handle. +* @param[in] buffer. +* @param[in] buffer len +* @param[in] dir:read/write +* @return void +* @retval none +*/ +static inline void flash_buf_bounce_post(struct ak_spiflash *flash, + void *buf, u32 len, int dir) +{ + +} + +#endif + + + +// cdh:add +static u32 xm25qh64a_read_sr(struct ak_spiflash *flash) +{ + ssize_t retval; + u8 code; + u32 status; + u8 st_tmp= 0; + + code = OPCODE_RDSR1; + if((retval = spi_write_then_read(flash->spi, &code, 1, &st_tmp, 1))<0) + return retval; + + status = st_tmp; + if(flash->info.flags & SFLAG_COM_STATUS2){ + code = XMC_OPCODE_RDSR2; + if((retval = spi_write_then_read(flash->spi, &code, 1, &st_tmp, 1))<0) + return retval; + + status = (status | (st_tmp << 8)); + } + + if(flash->info.flags & SFLAG_COM_STATUS3){ + code = XMC_OPCODE_RDSR3; + if((retval = spi_write_then_read(flash->spi, &code, 1, &st_tmp, 1))<0) + return retval; + + status = (status | (st_tmp << 16)); + } + + return status; +} + +static int xm25qh64a_write_sr(struct ak_spiflash *flash, u32 val) +{ + int ret; + + wait_till_ready(flash); + write_enable(flash); + flash->command[0] = OPCODE_WRSR1; + flash->command[1] = val & 0xff; + ret = spi_write(flash->spi, flash->command, 2); + +#if 0 + wait_till_ready(flash); + write_enable(flash); + flash->command[0] = XMC_OPCODE_WRSR3; + flash->command[1] = (val>>8) &0xff; + ret |= spi_write(flash->spi, flash->command, 2); + wait_till_ready(flash); +#endif + return ret; +} + +static int gd25q64c_write_sr(struct ak_spiflash *flash, u32 val) +{ + int ret; + + wait_till_ready(flash); + write_enable(flash); + flash->command[0] = OPCODE_WRSR1; + flash->command[1] = val & 0xff; + ret = spi_write(flash->spi, flash->command, 2); + + wait_till_ready(flash); + write_enable(flash); + flash->command[0] = OPCODE_WRSR2; + flash->command[1] = (val>>8) &0xff; + ret |= spi_write(flash->spi, flash->command, 2); + wait_till_ready(flash); + + return ret; +} + +/** +* @brief: for gd25q128c spi norflash write status +* +* @author lixinhai +* @date 2013-04-10 +* @param[in] *flash: spiflash handle. +* @param[in] val: status value +* @return int +* @retval ret:return write cnt +*/ +static int gd25q128c_write_sr(struct ak_spiflash *flash, u32 val) +{ + int ret; + + wait_till_ready(flash); + write_enable(flash); + flash->command[0] = OPCODE_WRSR1; + flash->command[1] = val & 0xff; + ret = spi_write(flash->spi, flash->command, 2); + + wait_till_ready(flash); + write_enable(flash); + flash->command[0] = OPCODE_WRSR2; + flash->command[1] = (val>>8) &0xff; + ret |= spi_write(flash->spi, flash->command, 2); + wait_till_ready(flash); + + return ret; +} + +/** +* @brief: for normal spi norflash read status +* +* @author lixinhai +* @date 2013-04-10 +* @param[in] *flash: spiflash handle. +* @return u32 +* @retval ret:return read spi norflash status value +*/ +static u32 normal_read_sr(struct ak_spiflash *flash) +{ + ssize_t retval; + u8 code; + u32 status; + u8 st_tmp= 0; + + code = OPCODE_RDSR1; + + if((retval = spi_write_then_read(flash->spi, &code, 1, &st_tmp, 1))<0) + return retval; + + status = st_tmp; + if(flash->info.flags & SFLAG_COM_STATUS2){ + code = OPCODE_RDSR2; + if((retval = spi_write_then_read(flash->spi, &code, 1, &st_tmp, 1))<0) + return retval; + + status = (status | (st_tmp << 8)); + } + + if(flash->info.flags & SFLAG_COM_STATUS3){ + code = OPCODE_RDSR3; + if((retval = spi_write_then_read(flash->spi, &code, 1, &st_tmp, 1))<0) + return retval; + + status = (status | (st_tmp << 16)); + } + + return status; +} + +/** +* @brief: for normal spi norflash write status +* +* @author lixinhai +* @date 2013-04-10 +* @param[in] *flash: spiflash handle. +* @param[in] val: status value +* @return int +* @retval ret:return write cnt +*/ +static int normal_write_sr(struct ak_spiflash *flash, u32 val) +{ + int wr_cnt; + + flash->command[0] = OPCODE_WRSR1; + flash->command[1] = val & 0xff; + flash->command[2] = (val>>8) &0xff; + + if (flash->info.flags & SFLAG_COM_STATUS2) { + wr_cnt = 3; + } else { + wr_cnt = 2; + } + + return spi_write(flash->spi, flash->command, wr_cnt); +} + + + + +/** +* @brief Read the status register. +* +* returning its value in the location +* @author SheShaohua +* @date 2012-03-20 +* @param[in] spiflash handle. +* @return int +* @retval Return the status register value. +*/ +static u32 read_sr(struct ak_spiflash *flash) +{ + struct flash_status_reg *fsr = &flash->stat_reg; + + if(fsr && fsr->read_sr) + return fsr->read_sr(flash); + + return -EINVAL; +} + + +/** +* @brief Write status register +* +* Write status register 1 byte. +* @author SheShaohua +* @date 2012-03-20 +* @param[in] flash spiflash handle. +* @param[in] val register value to be write. +* @return int return write success or failed +* @retval returns zero on success +* @retval return a negative error code if failed +*/ +static int write_sr(struct ak_spiflash *flash, u32 val) +{ + struct flash_status_reg *fsr = &flash->stat_reg; + + if(fsr && fsr->write_sr) + return fsr->write_sr(flash, val); + + return -EINVAL; +} + + +/** +* @brief Set write enable latch. +* +* Set write enable latch with Write Enable command. +* @author SheShaohua +* @date 2012-03-20 +* @param[in] flash spiflash handle. +* @return int return write success or failed +* @retval returns zero on success +* @retval return a negative error code if failed +*/ +static inline int write_enable(struct ak_spiflash *flash) +{ + u8 code = OPCODE_WREN; + + return spi_write_then_read(flash->spi, &code, 1, NULL, 0); +} + + +/** +* @brief Set write disble +* +* Set write disble instruction to the chip. +* @author SheShaohua +* @date 2012-03-20 +* @param[in] flash spiflash handle. +* @return int return write success or failed +* @retval returns zero on success +* @retval return a negative error code if failed +*/ +static inline int write_disable(struct ak_spiflash *flash) +{ + u8 code = OPCODE_WRDI; + + return spi_write_then_read(flash->spi, &code, 1, NULL, 0); +} + +#ifdef SPANSION_FLS_BA24_SET +static int spansion_fls_ba24_set(struct ak_spiflash *flash, u8 BA24Value) +{ + int ret; + uint32_t regval; + +// return ; + ret = wait_till_ready(flash); + if (ret) + return -EBUSY; + write_enable(flash); + + flash->command[0] = OPCODE_BRWR; + flash->command[1] = BA24Value&0x01; + return spi_write(flash->spi, flash->command, 2); + write_disable(flash); +} +#endif + +/** +* @brief Wait for SPI flash ready. +* +* Service routine to read status register until ready, or timeout occurs. +* @author SheShaohua +* @date 2012-03-20 +* @param[in] flash spiflash handle. +* @return int return write success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int wait_till_ready(struct ak_spiflash *flash) +{ + unsigned long deadline; + u32 sr; + struct flash_status_reg *fsr = &flash->stat_reg; + + deadline = jiffies + MAX_READY_WAIT_JIFFIES; + + do { + if ((sr = read_sr(flash)) < 0) + break; + else if (!(sr & (1<b_wip))) + return 0; + + cond_resched(); + + } while (!time_after_eq(jiffies, deadline)); + + return 1; +} + + + +/** +* @brief enable spi norflash quad 4 wires mode. +* +* enable spi norflash quad 4 wires mode can set spi norflash spi_wp and spi_hold share pin as data io +* @author SheShaohua +* @date 2012-03-20 +* @param[in] flash spiflash handle. +* @return int return write success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int quad_mode_enable(struct ak_spiflash *flash) +{ + int ret; + u32 regval; + struct flash_status_reg *fsr = &flash->stat_reg; + + ret = wait_till_ready(flash); + if (ret) + return -EBUSY; + + write_enable(flash); + + regval = read_sr(flash); + regval |= 1<b_qe; + write_sr(flash, regval); + + write_disable(flash); + + return 0; +} + + +/** +* @brief disable spi norflash quad 4 wires mode. +* +* disable spi norflash quad 4 wires mode can set data io share pin as spi norflash spi_wp and spi_hold +* @author SheShaohua +* @date 2012-03-20 +* @param[in] flash spiflash handle. +* @return int return write success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +#if 0 +static int quad_mode_disable(struct ak_spiflash *flash) +{ + int ret; + u32 regval; + struct flash_status_reg *fsr = &flash->stat_reg; + + ret = wait_till_ready(flash); + if (ret) + return -EBUSY; + + write_enable(flash); + + regval = read_sr(flash); + regval &= ~(1<b_qe); + write_sr(flash, regval); + + write_disable(flash); + return 0; +} +#endif + +/** +* @brief Erase chip +* +* Erase the whole flash memory. +* @author SheShaohua +* @date 2012-03-20 +* @param[in] flash spiflash handle. +* @return int return write success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int erase_chip(struct ak_spiflash *flash) +{ + DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %lldKiB\n", + dev_name(&flash->spi->dev), __func__, + (long long)(flash->mtd.size >> 10)); + + /* Wait until finished previous write command. */ + if (wait_till_ready(flash)) + return -EBUSY; + + /* Send write enable, then erase commands. */ + write_enable(flash); + + /* Set up command buffer. */ + flash->command[0] = OPCODE_CHIP_ERASE; + + spi_write(flash->spi, flash->command, 1); + + return 0; +} + + + +/** +* @brief Erase sector +* +* Erase a sector specialed by user. +* @author SheShaohua +* @date 2012-03-20 +* @param[in] flash spiflash handle. +* @param[in] offset which is any address within the sector which should be erased. +* @return int return write success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int erase_sector(struct ak_spiflash *flash, u32 offset) +{ + DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB at 0x%08x\n", + dev_name(&flash->spi->dev), __func__, + flash->mtd.erasesize / 1024, offset); + +#ifdef SPANSION_FLS_BA24_SET + if ((offset >= 0x1000000)&&((flash->info.flags & SFLAG_COM_BIT24) == 0)) + { + flash->info.flags = flash->info.flags | SFLAG_COM_BIT24; + spansion_fls_ba24_set(flash, 1); + } + else if((offset < 0x1000000)&&((flash->info.flags & SFLAG_COM_BIT24) != 0)) + { + flash->info.flags = flash->info.flags & (~SFLAG_COM_BIT24) ; + spansion_fls_ba24_set(flash, 0); + } +#endif + + /* Wait until finished previous write command. */ + if (wait_till_ready(flash)) + return -EBUSY; + + /* Send write enable, then erase commands. */ + write_enable(flash); + + /* Set up command buffer. */ + flash->command[0] = flash->erase_opcode; + flash->command[1] = offset >> 16; + flash->command[2] = offset >> 8; + flash->command[3] = offset; + + spi_write(flash->spi, flash->command, CMD_ADDR_SIZE); + + return 0; +} + + + +/** +* @brief MTD Erase +* +* Erase an address range on the flash chip. +* @author SheShaohua +* @date 2012-03-20 +* @param[in] mtd mtd info handle. +* @param[in] instr erase info. +* @return int return write success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int ak_spiflash_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct ak_spiflash *flash = mtd_to_spiflash(mtd); + u32 addr,len; + uint32_t rem; + + //printk("%s: instr->len=%lld, mtd->erasesize=%ld, addr=%lld\n", __func__, instr->len,mtd->erasesize,(long long)instr->addr); + + DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%llx, len %lld\n", + dev_name(&flash->spi->dev), __func__, "at", + (long long)instr->addr, (long long)instr->len); + + /* sanity checks */ + if (instr->addr + instr->len > mtd->size) { + printk(KERN_ERR "ak_spiflash_erase:instr->addr[0x%llx] + instr->len[%lld] > mtd->size[%lld]\n", + instr->addr, instr->len, mtd->size ); + return -EINVAL; + } + + div_u64_rem(instr->len, mtd->erasesize, &rem); + if (rem != 0) { + printk(KERN_ERR "ak_spiflash_erase:rem!=0 [%u]\n", rem ); + return -EINVAL; + } + + addr = instr->addr; + len = instr->len; + + mutex_lock(&flash->lock); + + /* whole-chip erase? */ + if (len == mtd->size) { + if (erase_chip(flash)) { + instr->state = MTD_ERASE_FAILED; + mutex_unlock(&flash->lock); + return -EIO; + } + + /* REVISIT in some cases we could speed up erasing large regions + * by using OPCODE_SE instead of OPCODE_BE_4K. We may have set up + * to use "small sector erase", but that's not always optimal. + */ + + /* "sector"-at-a-time erase */ + } else { + while (len) { + if (erase_sector(flash, addr)) { + instr->state = MTD_ERASE_FAILED; + mutex_unlock(&flash->lock); + return -EIO; + } + + addr += mtd->erasesize; + len -= mtd->erasesize; + } + } + + mutex_unlock(&flash->lock); + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return 0; +} + +/** +* @brief init spiflash read and write cmd and data bus width info +* +* @author SheShaohua +* @date 2012-03-20 +* @param[in] flash spiflash handle. +* @return int return init success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int init_spiflash_rw_info(struct ak_spiflash *flash) +{ + /**default param.*/ + flash->rx_opcode = OPCODE_READ; + flash->rxd_bus_width = XFER_1DATAWIRE; + flash->rxa_bus_width = XFER_1DATAWIRE; + flash->tx_opcode = OPCODE_PP; + flash->txd_bus_width = XFER_1DATAWIRE; + flash->txa_bus_width = XFER_1DATAWIRE; + flash->dummy_len = 1; + + /* force bus_width to 1 wire mode */ + flash->bus_width = FLASH_BUS_WIDTH_1WIRE; + + if(flash->bus_width & FLASH_BUS_WIDTH_2WIRE){ + if(flash->info.flags & SFLAG_DUAL_READ) { + flash->rx_opcode = OPCODE_FAST_D_READ; + flash->rxd_bus_width = XFER_2DATAWIRE; + flash->rxa_bus_width = XFER_1DATAWIRE; + flash->dummy_len = 1; + } else if (flash->info.flags & SFLAG_DUAL_IO_READ) { + flash->rx_opcode = OPCODE_FAST_D_IO; + flash->rxd_bus_width = XFER_2DATAWIRE; + flash->rxa_bus_width = XFER_2DATAWIRE; + flash->dummy_len = 1; + } + + if(flash->info.flags & SFLAG_DUAL_WRITE) { + flash->tx_opcode = OPCODE_PP_DUAL; + flash->txd_bus_width = XFER_2DATAWIRE; + flash->txa_bus_width = XFER_1DATAWIRE; + } else if(flash->info.flags & SFLAG_DUAL_IO_WRITE) { + flash->tx_opcode = OPCODE_2IO_PP; + flash->txd_bus_width = XFER_2DATAWIRE; + flash->txa_bus_width = XFER_2DATAWIRE; + } + } + + if(flash->bus_width & FLASH_BUS_WIDTH_4WIRE){ + if(flash->info.flags & SFLAG_QUAD_READ) { + flash->rx_opcode = OPCODE_FAST_Q_READ; + flash->rxd_bus_width = XFER_4DATAWIRE; + flash->rxa_bus_width = XFER_1DATAWIRE; + flash->dummy_len = 1; + }else if(flash->info.flags & SFLAG_QUAD_IO_READ){ + flash->rx_opcode = OPCODE_FAST_Q_IO; + flash->rxd_bus_width = XFER_4DATAWIRE; + flash->rxa_bus_width = XFER_4DATAWIRE; + flash->dummy_len = 3; + } + + if(flash->info.flags & SFLAG_QUAD_WRITE) { + flash->tx_opcode = OPCODE_PP_QUAD; + flash->txd_bus_width = XFER_4DATAWIRE; + flash->txa_bus_width = XFER_1DATAWIRE; + }else if(flash->info.flags & SFLAG_QUAD_IO_WRITE) { + flash->tx_opcode = OPCODE_4IO_PP; + flash->txd_bus_width = XFER_4DATAWIRE; + flash->txa_bus_width = XFER_4DATAWIRE; + } + } + + return 0; +} + +/** +* @brief cfg spi norflash into quad mode +* +* @author SheShaohua +* @date 2012-03-20 +* @param[in] flash spiflash handle. +* @return int return init success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int ak_spiflash_cfg_quad_mode(struct ak_spiflash *flash) +{ + int ret = 0; + + if((flash->bus_width & FLASH_BUS_WIDTH_4WIRE) && + (flash->info.flags & (SFLAG_QUAD_WRITE|SFLAG_QUAD_IO_WRITE| + SFLAG_QUAD_READ|SFLAG_QUAD_IO_READ))) { + if((flash->info.flags&SFLAG_QUAD_NO_QE) != SFLAG_QUAD_NO_QE){ + ret = quad_mode_enable(flash); + if(ret) { + flash->bus_width &= ~FLASH_BUS_WIDTH_4WIRE; + printk("config the spiflash quad enable fail. transfer use 1 wire.\n"); + } + } + } + else { + if (flash->info.flags & (SFLAG_QUAD_WRITE|SFLAG_QUAD_IO_WRITE| + SFLAG_DUAL_READ|SFLAG_DUAL_IO_READ)){ + if((flash->info.flags&SFLAG_QUAD_NO_QE) != SFLAG_QUAD_NO_QE){ + quad_mode_enable(flash); // cdh: test KH 1 WIRE for pin7 is not spi_hold but reset + } + } + } + + return ret; +} + + +/** +* @brief spi norflash read +* +* spi norflash read operation +* @author SheShaohua +* @date 2012-03-20 +* @param[in] mtd mtd info handle. +* @param[in] from spi norflash read offset. +* @param[in] len read len +* @param[in] retlen return real read len +* @param[in] buf read data buffer +* @return int return write success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int spiflash_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct ak_spiflash *flash = mtd_to_spiflash(mtd); + struct spi_transfer t[3]; + struct spi_message m; + void *bounce_buf; + + spi_message_init(&m); + memset(t, 0, (sizeof t)); + + mutex_lock(&flash->lock); + + bounce_buf = flash_buf_bounce_pre(flash, buf, len, SPI_FLASH_READ); + +#ifdef SPANSION_FLS_BA24_SET + if ((from >= 0x1000000)&&((flash->info.flags & SFLAG_COM_BIT24) == 0)) + { + flash->info.flags = flash->info.flags | SFLAG_COM_BIT24; + spansion_fls_ba24_set(flash, 1); + } + else if((from < 0x1000000)&&((flash->info.flags & SFLAG_COM_BIT24) != 0)) + { + flash->info.flags = flash->info.flags & (~SFLAG_COM_BIT24) ; + spansion_fls_ba24_set(flash, 0); + } +#endif + + t[0].tx_buf = flash->command; + t[0].len = CMD_SIZE; + spi_message_add_tail(&t[0], &m); + + t[1].tx_buf = &flash->command[CMD_SIZE]; + t[1].len = ADDR_SIZE + flash->dummy_len; + t[1].xfer_mode = flash->rxa_bus_width; + spi_message_add_tail(&t[1], &m); + + t[2].rx_buf = bounce_buf; + t[2].len = len; + t[2].cs_change = 1; + t[2].xfer_mode = flash->rxd_bus_width; + + spi_message_add_tail(&t[2], &m); + + /* Byte count starts at zero. */ + if (retlen) + *retlen = 0; + + /* Wait till previous write/erase is done. */ + if (wait_till_ready(flash)) { + /* REVISIT status return?? */ + mutex_unlock(&flash->lock); + return -EBUSY; + } + + /* Set up the write data buffer. */ + FILL_CMD(flash->command, flash->rx_opcode); + FILL_ADDR(flash->command, from); + FILL_DUMMY_DATA(flash->command, 0x00); + + spi_sync(flash->spi, &m); + + *retlen = m.actual_length - CMD_ADDR_SIZE - flash->dummy_len; + + flash_buf_bounce_post(flash, buf, len, SPI_FLASH_READ); + + mutex_unlock(&flash->lock); + + return 0; +} + +/** +* @brief mtd every time spi norflash read +* +* mtd spi norflash read operation +* @author SheShaohua +* @date 2012-03-20 +* @param[in] mtd mtd info handle. +* @param[in] from spi norflash read offset. +* @param[in] len read len +* @param[out] retlen return real read len +* @param[out] buf read data buffer +* @return int return write success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int ak_spiflash_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + int ret = 0; + size_t rlen = 0; + u32 xfer_len; + u32 offset = 0; + u32 count = len; + + + while(count > 0) { + xfer_len = (count > FLASH_BUF_SIZE) ? FLASH_BUF_SIZE : count; + + if(xfer_len > FLASH_PAGESIZE) + xfer_len = ALIGN_DOWN(xfer_len, FLASH_PAGESIZE); + + ret = spiflash_read(mtd, from + offset, xfer_len, &rlen, buf + offset); + if(unlikely(ret)) { + ret = -EBUSY; + goto out; + } + + *retlen += rlen; + count -= rlen; + offset += rlen; + } +out: + return ret; +} + + +/** +* @brief MTD write +* +* Write an address range to the flash chip. +* @author SheShaohua +* @date 2012-03-20 +* @param[in] mtd mtd info handle. +* @param[in] to write start address. +* @param[in] len write length. +* @param[out] retlen write length at actually. +* @param[in] buf the pointer to write data. +* @return int return write success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int ak_spiflash_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + u32 page_offset; + u32 page_size; + struct ak_spiflash *flash = mtd_to_spiflash(mtd); + struct spi_transfer t[3]; + struct spi_message m; + void *bounce_buf; + + DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n", + dev_name(&flash->spi->dev), __func__, "to", + (u32)to, len); + + if (retlen) + *retlen = 0; + + /* sanity checks */ + if (!len) + return(0); + + if (to + len > mtd->size) + return -EINVAL; + + spi_message_init(&m); + memset(t, 0, (sizeof t)); + + mutex_lock(&flash->lock); + bounce_buf = flash_buf_bounce_pre(flash, (void*)buf, len, SPI_FLASH_WRITE); + +#ifdef SPANSION_FLS_BA24_SET + if ((to >= 0x1000000)&&((flash->info.flags & SFLAG_COM_BIT24) == 0)) + { + flash->info.flags = flash->info.flags | SFLAG_COM_BIT24; + spansion_fls_ba24_set(flash, 1); + } + else if((to < 0x1000000)&&((flash->info.flags & SFLAG_COM_BIT24) != 0)) + { + flash->info.flags = flash->info.flags & (~SFLAG_COM_BIT24) ; + spansion_fls_ba24_set(flash, 0); + } +#endif + + t[0].tx_buf = flash->command; + t[0].len = CMD_SIZE; + spi_message_add_tail(&t[0], &m); + + t[1].tx_buf = &flash->command[CMD_SIZE]; + t[1].len = ADDR_SIZE; + t[1].xfer_mode = flash->txa_bus_width; + spi_message_add_tail(&t[1], &m); + + t[2].tx_buf = bounce_buf; + t[2].cs_change = 1; + t[2].xfer_mode = flash->txd_bus_width; + + spi_message_add_tail(&t[2], &m); + + //memcpy(flash->buf, buf, len); + + /* Wait until finished previous write command. */ + if (wait_till_ready(flash)) { + mutex_unlock(&flash->lock); + return 1; + } + + write_enable(flash); + + /* Set up the opcode in the write buffer. */ + FILL_CMD(flash->command, flash->tx_opcode); + FILL_ADDR(flash->command, to); + + /* what page do we start with? */ + page_offset = to % FLASH_PAGESIZE; + + /* do all the bytes fit onto one page? */ + if (page_offset + len <= FLASH_PAGESIZE) { + t[2].len = len; + + spi_sync(flash->spi, &m); + + *retlen = m.actual_length - CMD_ADDR_SIZE; + } else { + u32 i; + + /* the size of data remaining on the first page */ + page_size = FLASH_PAGESIZE - page_offset; + + t[2].len = page_size; + spi_sync(flash->spi, &m); + + *retlen = m.actual_length - CMD_ADDR_SIZE; + + /* write everything in PAGESIZE chunks */ + for (i = page_size; i < len; i += page_size) { + page_size = len - i; + if (page_size > FLASH_PAGESIZE) + page_size = FLASH_PAGESIZE; + + /* write the next page to flash */ + FILL_ADDR(flash->command, to+i); + + t[2].tx_buf = buf + i; + t[2].len = page_size; + + wait_till_ready(flash); + + write_enable(flash); + + spi_sync(flash->spi, &m); + + if (retlen) + *retlen += m.actual_length - CMD_ADDR_SIZE; + } + } + + PDEBUG("ak_spiflash_write: retlen=%ld\n", *retlen); + flash_buf_bounce_post(flash, (void*)buf, len, SPI_FLASH_WRITE); + + mutex_unlock(&flash->lock); + + return 0; +} + +/** +* @brief MTD get device ID +* +* get the device ID of the spi flash chip. +* @author SheShaohua +* @date 2012-03-20 +* @param[in] mtd mtd info handle. +* @return int return device ID of the spi flash chip. +*/ +static int ak_spiflash_get_devid(struct mtd_info *mtd) +{ + struct ak_spiflash *flash = mtd_to_spiflash(mtd); + int ret; + u8 code = OPCODE_RDID; + u8 id[5]; + u32 jedec; + + /* Wait until finished previous write command. */ + if (wait_till_ready(flash)) + return -EBUSY; + + /* + * JEDEC also defines an optional "extended device information" + * string for after vendor-specific data, after the three bytes + * we use here. Supporting some chips might require using it. + */ + ret = spi_write_then_read(flash->spi, &code, 1, id, 3); + if (ret < 0) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d ak_spiflash_get_devid\n", + dev_name(&flash->spi->dev), ret); + return AK_FALSE; + } + + jedec = id[0] | (id[1]<<8) | (id[2]<<16); + printk("spi flash ID: 0x%08x\n", jedec); + + return jedec; +} + + + /** + * @brief MTD write only for SST spi flash. + * + * Write an address range to the flash chip. + * @author SheShaohua + * @date 2012-03-20 + * @param[in] mtd mtd info handle. + * @param[in] to write start address. + * @param[in] len write length. + * @param[out] retlen write length at actually. + * @param[out] buf the pointer to write data. + * @return int return write success or failed + * @retval returns zero on success + * @retval return a non-zero error code if failed + */ +static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct ak_spiflash *flash = mtd_to_spiflash(mtd); + struct spi_transfer t[2]; + struct spi_message m; + size_t actual; + int cmd_sz, ret; + + if (retlen) + *retlen = 0; + + /* sanity checks */ + if (!len) + return 0; + + if (to + len > flash->mtd.size) + return -EINVAL; + + spi_message_init(&m); + memset(t, 0, (sizeof t)); + + t[0].tx_buf = flash->command; + t[0].len = CMD_ADDR_SIZE; + spi_message_add_tail(&t[0], &m); + + t[1].tx_buf = buf; + spi_message_add_tail(&t[1], &m); + + mutex_lock(&flash->lock); + + /* Wait until finished previous write command. */ + ret = wait_till_ready(flash); + if (ret) + goto time_out; + + write_enable(flash); + + actual = to % 2; + + /* Start write from odd address. */ + if (actual) { + flash->command[0] = OPCODE_BP; + flash->command[1] = to >> 16; + flash->command[2] = to >> 8; + flash->command[3] = to; + + /* write one byte. */ + t[1].len = 1; + spi_sync(flash->spi, &m); + ret = wait_till_ready(flash); + if (ret) + goto time_out; + *retlen += m.actual_length - CMD_ADDR_SIZE; + } + to += actual; + + flash->command[0] = OPCODE_AAI_WP; + flash->command[1] = to >> 16; + flash->command[2] = to >> 8; + flash->command[3] = to; + + /* Write out most of the data here. */ + cmd_sz = CMD_ADDR_SIZE; + for (; actual < len - 1; actual += 2) { + t[0].len = cmd_sz; + /* write two bytes. */ + t[1].len = 2; + t[1].tx_buf = buf + actual; + + spi_sync(flash->spi, &m); + ret = wait_till_ready(flash); + if (ret) + goto time_out; + *retlen += m.actual_length - cmd_sz; + cmd_sz = 1; + to += 2; + } + write_disable(flash); + ret = wait_till_ready(flash); + if (ret) + goto time_out; + + /* Write out trailing byte if it exists. */ + if (actual != len) { + write_enable(flash); + flash->command[0] = OPCODE_BP; + flash->command[1] = to >> 16; + flash->command[2] = to >> 8; + flash->command[3] = to; + t[0].len = CMD_ADDR_SIZE; + t[1].len = 1; + t[1].tx_buf = buf + actual; + + spi_sync(flash->spi, &m); + ret = wait_till_ready(flash); + if (ret) + goto time_out; + *retlen += m.actual_length - CMD_ADDR_SIZE; + write_disable(flash); + } + +time_out: + mutex_unlock(&flash->lock); + return ret; +} + +/****************************************************************************/ + +static struct flash_status_reg __devinitdata status_reg_list[] = { + /*spiflash mx25l12805d*/ + { + .jedec_id = 0xc22018, .ext_id = 0, + .b_wip = 0, .b_wel = 1, .b_bp0 = 2, .b_bp1 = 3, + .b_bp2 = 4, .b_bp3 = 5, .b_qe = 6, .b_srp0 = 7, + .read_sr = normal_read_sr, + .write_sr = normal_write_sr, + }, + + /*spiflash xm25qh64a*/ + { + .jedec_id = 0x207017, .ext_id = 0, + .b_wip = 0, .b_wel = 1, .b_bp0 = 2, .b_bp1 = 3, + .b_bp2 = 4, .b_bp3 = 5, .b_qe = 6, .b_srp0 = 7, + .read_sr = xm25qh64a_read_sr, + .write_sr = xm25qh64a_write_sr, + }, + + /*spiflash xm25qh128a*/ + { + .jedec_id = 0x207018, .ext_id = 0, + .b_wip = 0, .b_wel = 1, .b_bp0 = 2, .b_bp1 = 3, + .b_bp2 = 4, .b_bp3 = 5, .b_qe = 6, .b_srp0 = 7, + .read_sr = xm25qh64a_read_sr, + .write_sr = xm25qh64a_write_sr, + }, + + /*spiflash PN26f64B*/ + { + .jedec_id = 0x1c7017, .ext_id = 0, + .b_wip = 0, .b_wel = 1, .b_bp0 = 2, .b_bp1 = 3, + .b_bp2 = 4, .b_bp3 = 5, .b_qe = 6, .b_srp0 = 7, + .read_sr = xm25qh64a_read_sr, + .write_sr = xm25qh64a_write_sr, + }, + + /*spiflash gd25q64c*/ + { + .jedec_id = 0xc8401753, .ext_id = 0, + .b_wip = 0, .b_wel = 1, .b_bp0 = 2, .b_bp1 = 3, + .b_bp2 = 4, .b_bp3 = 5, .b_bp4 = 6, .b_srp0 = 7, + + .b_srp1 = 8,.b_qe = 9, .b_lb = 10, .b_cmp = 14, + .b_sus = 15, + .read_sr = normal_read_sr, + .write_sr = gd25q64c_write_sr, + }, + /*spiflash gd25q128c*/ + { + .jedec_id = 0xc84018, .ext_id = 0, + .b_wip = 0, .b_wel = 1, .b_bp0 = 2, .b_bp1 = 3, + .b_bp2 = 4, .b_bp3 = 5, .b_bp4 = 6, .b_srp0 = 7, + + .b_srp1 = 8,.b_qe = 9, .b_lb = 10, .b_cmp = 14, + .b_sus = 15, + .read_sr = normal_read_sr, + .write_sr = gd25q128c_write_sr, + }, + /*spiflash FM25Q64*/ + { + .jedec_id = 0xa14017, .ext_id = 0, + .b_wip = 0, .b_wel = 1, .b_bp0 = 2, .b_bp1 = 3, + .b_bp2 = 4, .b_srp0 = 7, + + .b_srp1 = 8,.b_qe = 9, .b_lb = 10, .b_cmp = 14, + .b_sus = 15, + .read_sr = normal_read_sr, + .write_sr = gd25q64c_write_sr, + }, + /*spiflash FM25Q128*/ + { + .jedec_id = 0xa14018, .ext_id = 0, + .b_wip = 0, .b_wel = 1, .b_bp0 = 2, .b_bp1 = 3, + .b_bp2 = 4, .b_srp0 = 7, + + .b_srp1 = 8,.b_qe = 9, .b_lb = 10, .b_cmp = 14, + .b_sus = 15, + .read_sr = normal_read_sr, + .write_sr = gd25q128c_write_sr, + }, + { + .jedec_id = 0xef4019, .ext_id = 0, + .b_wip = 0, .b_wel = 1, .b_bp0 = 2, .b_bp1 = 3, + .b_bp2 = 4, .b_bp3 = 5, .b_bp4 = 6, .b_srp0 = 7, + .b_srp1 = 8 ,.b_qe = 9, .b_lb = 11, .b_cmp = 14, + .b_sus = 15,.b_as =16, .b_ap = 17, + .read_sr = normal_read_sr, + .write_sr = normal_write_sr, + }, + /*normal status reg define*/ + { + .jedec_id = 0, .ext_id = 0, + .b_wip = 0, .b_wel = 1, .b_bp0 = 2, .b_bp1 = 3, + .b_bp2 = 4, .b_bp3 = 5, .b_bp4 = 6, .b_srp0 = 7, + + .b_srp1 = 8,.b_qe = 9, .b_lb = 10, .b_cmp = 14, + .b_sus = 15, + .read_sr = normal_read_sr, + .write_sr = normal_write_sr, + }, +}; + + + +/* NOTE: double check command sets and memory organization when you add + * more flash chips. This current list focusses on newer chips, which + * have been converging on command sets which including JEDEC ID. + */ +static struct flash_info __devinitdata ak_spiflash_supportlist [] = { + + /* Atmel -- some are (confusingly) marketed as "DataFlash" */ + { "at25fs010", 0x1f6601, 0, 32 * 1024, 4, SFLAG_SECT_4K, }, + { "at25fs040", 0x1f6604, 0, 64 * 1024, 8, SFLAG_SECT_4K, }, + + { "at25df041a", 0x1f4401, 0, 64 * 1024, 8, SFLAG_SECT_4K, }, + { "at25df641", 0x1f4800, 0, 64 * 1024, 128, SFLAG_SECT_4K, }, + + { "at26f004", 0x1f0400, 0, 64 * 1024, 8, SFLAG_SECT_4K, }, + { "at26df081a", 0x1f4501, 0, 64 * 1024, 16, SFLAG_SECT_4K, }, + { "at26df161a", 0x1f4601, 0, 64 * 1024, 32, SFLAG_SECT_4K, }, + { "at26df321", 0x1f4701, 0, 64 * 1024, 64, SFLAG_SECT_4K, }, + + /* Macronix */ + { "mx25l3205d", 0xc22016, 0, 64 * 1024, 64, SFLAG_SECT_4K | SFLAG_DUAL_READ}, + { "mx25l6405d", 0xc22017, 0, 64 * 1024, 128, SFLAG_SECT_4K | SFLAG_DUAL_READ}, + { "mx25l12805d", 0xc22018, 0, 64 * 1024, 256, SFLAG_SECT_4K | SFLAG_DUAL_IO_READ | SFLAG_QUAD_IO_READ | SFLAG_QUAD_IO_WRITE}, + { "mx25l12855e", 0xc22618, 0, 64 * 1024, 256, SFLAG_SECT_4K | SFLAG_DUAL_READ}, + { "mx25l25645g", 0xc22019, 0, 64 * 1024, 512, SFLAG_COM_BIT24|SFLAG_SECT_4K | SFLAG_DUAL_READ | SFLAG_QUAD_READ | SFLAG_DUAL_IO_READ | SFLAG_QUAD_IO_WRITE}, + + /* Spansion -- single (large) sector size only, at least + * for the chips listed here (without boot sectors). + */ + { "s25sl004a", 0x010212, 0, 64 * 1024, 8, }, + { "s25sl008a", 0x010213, 0, 64 * 1024, 16, }, + { "s25sl016a", 0x010214, 0, 64 * 1024, 32, }, + { "s25sl032a", 0x010215, 0, 64 * 1024, 64, }, + { "s25sl064a", 0x010216, 0, 64 * 1024, 128, }, + { "s25sl12800", 0x012018, 0x0300, 256 * 1024, 64, }, + { "s25sl12801", 0x012018, 0x0301, 64 * 1024, 256, }, + { "s25fl129p0", 0x012018, 0x4d00, 256 * 1024, 64, }, + { "s25fl129p1", 0x012018, 0x4d01, 64 * 1024, 256, }, + + /* SST -- large erase sizes are "overlays", "sectors" are 4K */ + { "sst25vf040b", 0xbf258d, 0, 64 * 1024, 8, SFLAG_SECT_4K, }, + { "sst25vf080b", 0xbf258e, 0, 64 * 1024, 16, SFLAG_SECT_4K, }, + { "sst25vf016b", 0xbf2541, 0, 64 * 1024, 32, SFLAG_SECT_4K, }, + { "sst25vf032b", 0xbf254a, 0, 64 * 1024, 64, SFLAG_SECT_4K, }, + { "sst25wf512", 0xbf2501, 0, 64 * 1024, 1, SFLAG_SECT_4K, }, + { "sst25wf010", 0xbf2502, 0, 64 * 1024, 2, SFLAG_SECT_4K, }, + { "sst25wf020", 0xbf2503, 0, 64 * 1024, 4, SFLAG_SECT_4K, }, + { "sst25wf040", 0xbf2504, 0, 64 * 1024, 8, SFLAG_SECT_4K, }, + + /* ST Microelectronics -- newer production may have feature updates */ + { "m25p05", 0x202010, 0, 32 * 1024, 2, }, + { "m25p10", 0x202011, 0, 32 * 1024, 4, }, + { "m25p20", 0x202012, 0, 64 * 1024, 4, }, + { "m25p40", 0x202013, 0, 64 * 1024, 8, }, + { "m25p80", 0, 0, 64 * 1024, 16, }, + { "m25p16", 0x202015, 0, 64 * 1024, 32, }, + { "m25p32", 0x202016, 0, 64 * 1024, 64, }, + { "m25p64", 0x202017, 0, 64 * 1024, 128, }, + { "m25p128", 0x202018, 0, 256 * 1024, 64, }, + + { "m45pe10", 0x204011, 0, 64 * 1024, 2, }, + { "m45pe80", 0x204014, 0, 64 * 1024, 16, }, + { "m45pe16", 0x204015, 0, 64 * 1024, 32, }, + + { "m25pe80", 0x208014, 0, 64 * 1024, 16, }, + { "m25pe16", 0x208015, 0, 64 * 1024, 32, SFLAG_SECT_4K, }, + + /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ + { "w25x10", 0xef3011, 0, 64 * 1024, 2, SFLAG_SECT_4K, }, + { "w25x20", 0xef3012, 0, 64 * 1024, 4, SFLAG_SECT_4K, }, + { "w25x40", 0xef3013, 0, 64 * 1024, 8, SFLAG_SECT_4K, }, + { "w25x80", 0xef3014, 0, 64 * 1024, 16, SFLAG_SECT_4K, }, + { "w25x16", 0xef3015, 0, 64 * 1024, 32, SFLAG_SECT_4K, }, + { "w25x32", 0xef3016, 0, 64 * 1024, 64, SFLAG_SECT_4K, }, + { "w25x64", 0xef3017, 0, 64 * 1024, 128, SFLAG_SECT_4K, }, + + { "w25q32", 0xef4016, 0, 32 * 1024, 128, SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_QUAD_WRITE, }, + { "w25q64", 0xef4017, 0, 64 * 1024, 128, SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_QUAD_WRITE, }, + { "w25q128", 0xef4018, 0, 64 * 1024, 256, SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_QUAD_WRITE, }, + { "w25q256", 0xef4019, 0, 64 * 1024, 512, SFLAG_COM_BIT24|SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_COM_STATUS3|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_DUAL_IO_READ|SFLAG_QUAD_IO_READ|SFLAG_QUAD_WRITE ,}, + + /* GigaDevice -- w25x "blocks" are 64K, "sectors" are 4KiB */ + { "gd25q64", 0xc84017, 0, 64 * 1024, 128, SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_DUAL_IO_READ|SFLAG_QUAD_IO_READ|SFLAG_QUAD_WRITE , }, + { "gd25q128", 0xc84018, 0, 64 * 1024, 256, SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_DUAL_IO_READ|SFLAG_QUAD_IO_READ|SFLAG_QUAD_WRITE ,}, + { "gd25q256", 0xc84019, 0, 64 * 1024, 512, SFLAG_COM_BIT24|SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_DUAL_IO_READ|SFLAG_QUAD_IO_READ|SFLAG_QUAD_WRITE ,}, + + /* XMC -- xm25qh64a "blocks" are 64K, "sectors" are 4KiB */ + { "xm25qh64a", 0x207017, 0, 64 * 1024, 128, SFLAG_QUAD_NO_QE|SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_DUAL_IO_READ|SFLAG_QUAD_IO_READ|SFLAG_QUAD_WRITE , }, + { "xm25qh128a", 0x207018, 0, 64 * 1024, 256, SFLAG_QUAD_NO_QE|SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_DUAL_IO_READ|SFLAG_QUAD_IO_READ|SFLAG_QUAD_WRITE , }, + +// { "xt25f128a", 0x207018, 0, 64 * 1024, 256, SFLAG_QUAD_NO_QE|SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_DUAL_IO_READ|SFLAG_QUAD_IO_READ|SFLAG_QUAD_WRITE ,}, + { "PN26f64B", 0x1c7017, 0, 64 * 1024, 128, SFLAG_QUAD_NO_QE|SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_DUAL_IO_READ|SFLAG_QUAD_IO_READ|SFLAG_QUAD_WRITE ,}, + + /* BOYA BY25Q64ASSIG -- "blocks" are 64K, "sectors" are 4KiB */ + { "BY25Q64ASSIG", 0x684017, 0, 64 * 1024, 128, SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_DUAL_IO_READ|SFLAG_QUAD_IO_READ|SFLAG_QUAD_WRITE,}, + { "BY25Q128ASSIG", 0x684018, 0, 64 * 1024, 256, SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_DUAL_IO_READ|SFLAG_QUAD_IO_READ|SFLAG_QUAD_WRITE,}, + + /* FM25Q64 FM25Q128 "blocks" are 64K, "sectors" are 4KiB */ + { "FM25Q64", 0xa14017, 0, 64 * 1024, 128, SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_DUAL_IO_READ|SFLAG_QUAD_IO_READ|SFLAG_QUAD_WRITE,}, + { "FM25Q128", 0xa14018, 0, 64 * 1024, 256, SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_DUAL_IO_READ|SFLAG_QUAD_IO_READ|SFLAG_QUAD_WRITE,}, + + /* ZB25VQ32 "blocks" are 64K, "sectors" are 4KiB */ + { "ZB25VQ32", 0x5e4016, 0, 32 * 1024, 128, SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_DUAL_IO_READ|SFLAG_QUAD_IO_READ|SFLAG_QUAD_WRITE,}, + + /* ESMT EON SIZE:128M, "blocks" are 64K, "sectors" are 4KiB */ + { "EN25QH128A", 0x1c7018, 0, 64 * 1024, 256, SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_DUAL_IO_READ|SFLAG_QUAD_IO_READ|SFLAG_QUAD_WRITE,}, + /* ZD25Q64A SIZE:8M, "blocks" are 64K, "sectors" are 4KiB */ + { "ZD25Q64A", 0xba4017, 0, 64 * 1024, 128, SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_DUAL_IO_READ|SFLAG_QUAD_IO_READ|SFLAG_QUAD_WRITE,}, + + /* ZB25VQ64 "blocks" are 64K, "sectors" are 4KiB */ + { "ZB25VQ64", 0x5e4017, 0, 64 * 1024, 128, SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_DUAL_IO_READ|SFLAG_QUAD_IO_READ|SFLAG_QUAD_WRITE,}, + + /* ZB25VQ128 "blocks" are 64K, "sectors" are 4KiB */ + { "ZB25VQ128", 0x5e4018, 0, 64 * 1024, 256, SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_DUAL_IO_READ|SFLAG_QUAD_IO_READ|SFLAG_QUAD_WRITE,}, + + /* GM25Q */ + { "GM25Q128AIQ", 0x1c4018, 0, 64 * 1024, 256, SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_DUAL_IO_READ|SFLAG_QUAD_IO_READ|SFLAG_QUAD_WRITE,}, + { "GM25Q128AIM", 0x1c7018, 0, 64 * 1024, 256, SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_DUAL_IO_READ|SFLAG_QUAD_IO_READ|SFLAG_QUAD_WRITE,}, + + /*XT25F64B*/ + { "XT25F64B", 0x0b4017, 0, 64 * 1024, 128, SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_DUAL_IO_READ|SFLAG_QUAD_IO_READ|SFLAG_QUAD_WRITE,}, +}; + + +/** +* @brief partition lib erase callback +* +* partition lib spi norflash erase callback function. +* @author SheShaohua +* @date 2012-03-20 +* @param[in] chip_num: chip select index +* @param[in] startblock: erase block index +* @return int +* @retval: succes:FHA_SUCCESS; fail:FHA_FAIL +*/ +int ak_fha_erase_callback(T_U32 chip_num, T_U32 startblock) // startpage +{ + struct erase_info einfo; + + memset(&einfo, 0, sizeof(struct erase_info)); + einfo.addr = startblock * ak_mtd_info->erasesize; // startpage * FLASH_PAGESIZE; + einfo.len = ak_mtd_info->erasesize; + einfo.mtd = ak_mtd_info; + + if(ak_spiflash_erase(ak_mtd_info, &einfo) == 0){ + return FHA_SUCCESS; + }else { + printk(KERN_ERR "***erase failed\n"); + return FHA_FAIL; + } +} + +/** +* @brief partition lib write callback +* +* partition lib spi norflash write callback function. +* @author SheShaohua +* @date 2012-03-20 +* @param[in] chip_num: chip select index +* @param[in] page_num: write start page index +* @param[in] data: write data buffer +* @param[in] data_len: write data len +* @param[in] oob:write oob buffer +* @param[in] oob_len: write oob len +* @param[in] eDataType:write data type +* @return int +* @retval: succes:FHA_SUCCESS; fail:FHA_FAIL +*/ +int ak_fha_write_callback(T_U32 chip_num, T_U32 page_num, const T_U8 *data, + T_U32 data_len, T_U8 *oob, T_U32 oob_len, T_U32 eDataType) +{ + int ret; + ssize_t retlen; + loff_t to = page_num * FLASH_PAGESIZE; + + ret = ak_spiflash_write(ak_mtd_info, to, data_len * FLASH_PAGESIZE, &retlen, data); + if(ret){ + printk(KERN_ERR "%s:%d\n", __func__, __LINE__); + return FHA_FAIL; + } + + return FHA_SUCCESS; +} + +/** +* @brief partition lib read callback +* +* partition lib spi norflash read callback function. +* @author SheShaohua +* @date 2012-03-20 +* @param[in] chip_num: chip select index +* @param[in] page_num: read start page index +* @param[in] data: read data buffer +* @param[in] data_len: read data len +* @param[in] oob:read oob buffer +* @param[in] oob_len: read oob len +* @param[in] eDataType:read data type +* @return int +* @retval: succes:FHA_SUCCESS; fail:FHA_FAIL +*/ +int ak_fha_read_callback(T_U32 chip_num, T_U32 page_num, T_U8 *data, + T_U32 data_len, T_U8 *oob, T_U32 oob_len, T_U32 eDataType) +{ + int ret; + ssize_t retlen; + loff_t from = page_num * FLASH_PAGESIZE; + + ret = ak_spiflash_read(ak_mtd_info, from, data_len * FLASH_PAGESIZE, &retlen, data); + if (ret) { + printk(KERN_ERR "%s:%d\n", __func__, __LINE__); + return FHA_FAIL; + } + + return FHA_SUCCESS; +} + + +/** +* @brief partition lib malloc callback +* +* partition lib spi norflash malloc callback function. +* @author SheShaohua +* @date 2012-03-20 +* @param[in] size: malloc size +* @return void +* @retval: malloc address pointer +*/ +static T_VOID *fha_ram_alloc(T_U32 size) +{ + return kmalloc(size, GFP_KERNEL); +} + +/** +* @brief partition lib free callback +* +* partition lib spi norflash free callback function. +* @author SheShaohua +* @date 2012-03-20 +* @param[in] point: malloc address pointer +* @return void +* @retval: none +*/ +static T_VOID *fha_ram_free(void *point) +{ + kfree(point); + return NULL; +} + +/** +* @brief partition lib print callback +* +* partition lib spi norflash print callback function. +* @author SheShaohua +* @date 2012-03-20 +* @param[in] fmt: format string +* @return T_S32 +* @retval: r: +*/ +static T_S32 fha_print(T_pCSTR fmt, ...) +{ + va_list args; + int r; + + va_start(args, fmt); + vprintk("FHA:",args); + r = vprintk(fmt, args); + va_end(args); + + return r; +} + +/** +* @brief partition lib init callback +* +* partition lib spi norflash init callback function. +* @author SheShaohua +* @date 2012-03-20 +* @param[in] void: none +* @return int +* @retval: 0: success, othet :failed +*/ +int ak_fha_init(void) +{ + int ret = 0; + T_PFHA_LIB_CALLBACK pCallback = NULL; + T_SPI_INIT_INFO spi_info; + + pCallback = kmalloc(sizeof(T_FHA_LIB_CALLBACK), GFP_KERNEL); + if (!pCallback) { + printk(KERN_ERR "allocate memory for pCallback failed\n"); + ret = -ENOMEM; + goto err_out; + } + + pCallback->Erase = ak_fha_erase_callback; + pCallback->Write = (FHA_Write)ak_fha_write_callback; + pCallback->Read = (FHA_Read)ak_fha_read_callback; + pCallback->RamAlloc = fha_ram_alloc; + pCallback->RamFree = fha_ram_free; + pCallback->MemCmp = (FHA_MemCmp)memcmp; + pCallback->MemSet = (FHA_MemSet)memset; + pCallback->MemCpy = (FHA_MemCpy)memcpy; + pCallback->Printf = (FHA_Printf)fha_print; + pCallback->ReadNandBytes = NULL; + /* + * Yea, PagePerBlock=16 in producer_all, + * when SPI flash didn`t suport 4k sector erase, + * all will dead, Why can be forbear of this big BUG ? + */ + spi_info.page_size = 256; + spi_info.pages_per_block = ak_mtd_info->erasesize / 256; + spi_info.total_size = ak_mtd_info->size; + + ret = partition_init(pCallback, &spi_info, 0); + if (ret == FHA_FAIL) { + printk(KERN_ERR "FHA_mount failed\n"); + ret = -EINVAL; + } else { + ret = 0; + } + + kfree(pCallback); +err_out: + return ret; +} + +/** +* @brief partition lib update init callback +* +* partition lib spi norflash update init callback function. +* @author SheShaohua +* @date 2012-03-20 +* @param[in] n: none +* @return int +* @retval: 0: success, othet :failed +*/ +int ak_fha_init_for_update(int n) +{ + int ret = 0; + + return ret; +} + +/** +* @brief partition lib mount init +* +* partition lib spi norflash mount init function. +* @author SheShaohua +* @date 2012-03-20 +* @param[in] spi: spi device handle +* @return int +* @retval: 0: success, othet :failed +*/ +static int ak_mount_partitions(struct spi_device *spi) +{ + int i; + int ret; + unsigned long nr_parts; + T_PARTITION_TABLE_INFO *parts = NULL; + T_PARTITION_TABLE_CONFIG part_tab; + struct mtd_partition *mtd_part; + struct mtd_part_parser_data ppdata; + + ret = ak_fha_init(); + if (ret) { + printk(KERN_ERR "Init FHA lib failed\n"); + goto no_parts; + } + + ret = partition_get_partition_table(&part_tab, PART_SPIFLASH); + if (ret == FHA_FAIL) { + printk(KERN_ERR "get partition info failed\n"); + ret = !ret; + goto no_parts; + } + + /* + * if no partiton to mount, the buf will be all 0xFF but not constant. + * So, it is not safe here. + */ + nr_parts = *(unsigned long *)part_tab.table; + if (nr_parts <= 0 || nr_parts > 15) { + printk(KERN_ERR "partition count invalid\n"); + ret = -EINVAL; + goto no_parts; + } + + mtd_part = kzalloc(sizeof(struct mtd_partition) * nr_parts, GFP_KERNEL); + if (!mtd_part) { + printk(KERN_ERR "allocate memory for mtd_partition failed\n"); + ret = -ENOMEM; + goto no_parts; + } + + parts = (T_PARTITION_TABLE_INFO *)(&part_tab.table[sizeof(unsigned long)]); + for (i = 0; i < nr_parts; i++) { + mtd_part[i].name = kzalloc(PARTITION_NAME_LEN, GFP_KERNEL); + memcpy(mtd_part[i].name, parts[i].partition_info.name, PARTITION_NAME_LEN); + mtd_part[i].size = parts[i].partition_info.ksize*1024; + mtd_part[i].offset = parts[i].partition_info.start_pos; + mtd_part[i].mask_flags = parts[i].partition_info.r_w_flag; + printk("mtd_part[%d]:\nname = %s\nsize = 0x%llx\noffset = 0x%llx\nmask_flags = 0x%x\n\n", + i, + mtd_part[i].name, + mtd_part[i].size, + mtd_part[i].offset, + mtd_part[i].mask_flags); + + } + + /* + * call ak_partition_table_sys_create function , create sys kobject for app aplication + */ + ret = ak_partition_table_sys_create(&part_tab); + if (ret){ + printk(KERN_ERR "create sys partition table kobject failed!\n"); + ret = -EINVAL; + kfree(mtd_part); + goto no_parts; + } + + ppdata.of_node = spi->dev.of_node; + ret = mtd_device_parse_register(ak_mtd_info, NULL, &ppdata, + (const struct mtd_partition *)mtd_part, nr_parts); + if (ret) { + printk(KERN_ERR "add mtd partition failed\n"); + kfree(mtd_part); + goto no_parts; + } + +no_parts: + return ret; +} + +/** +* @brief spi norfalsh device jedec probe +* +* Read the spi norfalsh device ID and identify that it was supported or not. +* @author SheShaohua +* @date 2012-03-20 +* @param[in] spi: spi device handle. +* @return struct flash_info * +* @retval return the device info. +*/ +static struct flash_info *__devinit jedec_probe(struct spi_device *spi) +{ + int tmp; + u8 code = OPCODE_RDID; + u8 id[5]; + u32 jedec; + u16 ext_jedec = 0; + struct flash_info *info; + + /* JEDEC also defines an optional "extended device information" + * string for after vendor-specific data, after the three bytes + * we use here. Supporting some chips might require using it. + */ + tmp = spi_write_then_read(spi, &code, 1, id, 3); + if (tmp < 0) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n", + dev_name(&spi->dev), tmp); + return NULL; + } + + jedec = id[0]; + jedec = jedec << 8; + jedec |= id[1]; + jedec = jedec << 8; + jedec |= id[2]; + + printk("akspi flash ID: 0x%08x\n", jedec); + + //ext_jedec = id[3] << 8 | id[4]; + for (tmp = 0, info = ak_spiflash_supportlist; + tmp < ARRAY_SIZE(ak_spiflash_supportlist); + tmp++, info++) { + if (info->jedec_id == jedec) { + if (info->ext_id != 0 && info->ext_id != ext_jedec) + continue; + return info; + } + } + dev_err(&spi->dev, "jedec_probe() unrecognized JEDEC id %06x\n", jedec); + return NULL; +} + + + +/** +* @brief spi norfalsh device init status register +* +* spi norfalsh device init status register +* @author SheShaohua +* @date 2012-03-20 +* @param[in] flash: spi flash handle. +* @return int +* @retval: 0: success, othet :failed +*/ +static int ak_spiflash_init_stat_reg(struct ak_spiflash *flash) +{ + int i,ret; + u8 code[5] = {0x5a,0x00,0x00,0x00,0x00}; //OPCODE_VER; + u8 id[2]; + u8 id_ver_c; + u32 tmp_id; + struct flash_status_reg *sr; + struct flash_info *info = &flash->info; + + tmp_id = info->jedec_id; + /* + ����gd25q64 ��B�汾��C �汾��Ȼ�������汾��״̬�Ĵ�����д��һ����������˸Ķ��� + */ + if(0xc84017 == info->jedec_id) + { + ret = spi_write_then_read(flash->spi, code, 5, id, 7); + if (ret < 0) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC VERSION\n", + dev_name(&spi->dev), ret); + return 1; + } + + id_ver_c = id[0]; //0x53 + printk("akspi flash VERSION: 0x%x\n", id_ver_c); + + if (0x53==id_ver_c) + { + info->jedec_id = info->jedec_id << 8; + info->jedec_id |= id_ver_c; + } + } + + for(i=0, sr=status_reg_list; ijedec_id == info->jedec_id) { + if (info->ext_id != 0 && info->ext_id != sr->ext_id) + continue; + flash->stat_reg = *sr; + info->jedec_id = tmp_id ; + return 0; + } + } + flash->stat_reg = status_reg_list[i-1]; + info->jedec_id = tmp_id ; + + return 0; +} + +int ak_get_partition_bak_name_idex(T_U32 partition_idex) +{ + return 0; +} + + + +/** +* @brief spi norflash probe +* +* Initial the spi norflash device driver to kernel. +* @author SheShaohua +* @date 2012-03-20 +* @param[in] mtd spi device handle. +* @return int return write success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int __devinit ak_spiflash_probe(struct spi_device *spi) +{ + struct flash_platform_data *data; + struct ak_spiflash *flash; + struct flash_info *info; + unsigned i, ret = 0; + + printk("ak spiflash probe enter.\n"); + + /* Platform data helps sort out which chip type we have, as + * well as how this board partitions it. If we don't have + * a chip ID, try the JEDEC id commands; they'll work for most + * newer chips, even if we don't recognize the particular chip. + */ + data = spi->dev.platform_data; + if (data && data->type) { + for (i = 0, info = ak_spiflash_supportlist; + i < ARRAY_SIZE(ak_spiflash_supportlist); + i++, info++) { + if (strcmp(data->type, info->name) == 0) + break; + } + + /* unrecognized chip? */ + if (i == ARRAY_SIZE(ak_spiflash_supportlist)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: unrecognized id %s\n", + dev_name(&spi->dev), data->type); + info = NULL; + + /* recognized; is that chip really what's there? */ + } else if (info->jedec_id) { + struct flash_info *chip = jedec_probe(spi); + + if (!chip || chip != info) { + dev_warn(&spi->dev, "found %s, expected %s\n", + chip ? chip->name : "UNKNOWN", + info->name); + info = NULL; + } + } + } else + info = jedec_probe(spi); + + if (!info) + return -ENODEV; + + flash = kzalloc(sizeof *flash, GFP_KERNEL); + if (!flash) + return -ENOMEM; + +#ifdef SPIFLASH_USE_MTD_BLOCK_LAYER + /*pre-allocation buffer use for spi flash data transfer.*/ + flash->buf = kzalloc(FLASH_BUF_SIZE, GFP_KERNEL); + if (!flash->buf) { + printk("Allocate buf for spi page failed\n"); + kfree(flash); + return -ENOMEM; + } +#endif + + ak_mtd_info = &flash->mtd; + + flash->spi = spi; + flash->info = *info; + mutex_init(&flash->lock); + dev_set_drvdata(&spi->dev, flash); + + flash->bus_width = data->bus_width; + + /* + * Atmel serial flash tend to power up + * with the software protection bits set + */ + + if (info->jedec_id >> 16 == 0x1f) { + write_enable(flash); + write_sr(flash, 0); + } + + if (data && data->name) + flash->mtd.name = data->name; + else + flash->mtd.name = dev_name(&spi->dev); + + flash->mtd.type = MTD_NORFLASH; + flash->mtd.writesize = FLASH_PAGESIZE; + flash->mtd.flags = MTD_WRITEABLE; + flash->mtd.size = info->sector_size * info->n_sectors; + flash->mtd._erase = ak_spiflash_erase; + flash->mtd._read = ak_spiflash_read; + flash->mtd.get_device_id = ak_spiflash_get_devid; + //printk("%s, info->sector_size = %d, info->n_sectors = %d\n", info->name, info->sector_size, info->n_sectors); + //printk("flash->mtd.size = %x, %ld\n", flash->mtd.size, flash->mtd.size); + + /* sst flash chips use AAI word program */ + if (info->jedec_id >> 16 == 0xbf) + flash->mtd._write = sst_write; + else + flash->mtd._write = ak_spiflash_write; + + /* prefer "small sector" erase if possible */ + if (info->flags & SFLAG_SECT_4K) { + flash->erase_opcode = OPCODE_BE_4K; + flash->mtd.erasesize = 4096; + } else { + flash->erase_opcode = OPCODE_SE; + flash->mtd.erasesize = info->sector_size; + } + + + ak_spiflash_init_stat_reg(flash); + ak_spiflash_cfg_quad_mode(flash); + init_spiflash_rw_info(flash); + + flash->mtd.dev.parent = &spi->dev; + + dev_info(&spi->dev, "%s (%lld Kbytes)\n", info->name, + (long long)flash->mtd.size >> 10); + + DEBUG(MTD_DEBUG_LEVEL0, + "mtd .name = %s, .size = 0x%llx (%lldMiB) " + ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n", + flash->mtd.name, + (long long)flash->mtd.size, (long long)(flash->mtd.size >> 20), + flash->mtd.erasesize, flash->mtd.erasesize / 1024, + flash->mtd.numeraseregions); + + if (flash->mtd.numeraseregions) + for (i = 0; i < flash->mtd.numeraseregions; i++) + DEBUG(MTD_DEBUG_LEVEL0, + "mtd.eraseregions[%d] = { .offset = 0x%llx, " + ".erasesize = 0x%.8x (%uKiB), " + ".numblocks = %d }\n", + i, (long long)flash->mtd.eraseregions[i].offset, + flash->mtd.eraseregions[i].erasesize, + flash->mtd.eraseregions[i].erasesize / 1024, + flash->mtd.eraseregions[i].numblocks); + + + /* partitions should match sector boundaries; and it may be good to + * use readonly partitions for writeprotected sectors (BP2..BP0). + */ + ret = mtd_device_parse_register(ak_mtd_info, NULL, NULL, NULL, 0); + if (ret) { + printk("Add root MTD device failed\n"); + kfree(flash->buf); + kfree(flash); + return -EINVAL; + } + ret = ak_mount_partitions(spi); + if (ret) + printk(KERN_ERR "Add MTD partitions failed\n"); + + printk("Init AK SPI Flash finish.\n"); + + return 0; +} + +/** +* @brief spi flash remove +* +* Remove the spi flash device driver from kernel. +* @author SheShaohua +* @date 2012-03-20 +* @param[in] mtd spi device handle. +* @return int return write success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int __devexit ak_spiflash_remove(struct spi_device *spi) +{ + struct ak_spiflash *flash = dev_get_drvdata(&spi->dev); + int status; + + status = mtd_device_unregister(&flash->mtd); + + if (status == 0) { + kfree(flash->buf); + kfree(flash); + } + return 0; +} + + +static struct spi_driver ak_spiflash_driver = { + .driver = { + .name = "ak-spiflash", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = ak_spiflash_probe, + .remove = __devexit_p(ak_spiflash_remove), + + /* REVISIT: many of these chips have deep power-down modes, which + * should clearly be entered on suspend() to minimize power use. + * And also when they're otherwise idle... + */ +}; + + +/** +* @brief ak spi flash module init +* +* ak spi norflash module init +* @author SheShaohua +* @date 2012-03-20 +* @param[in] void +* @return int return write success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int __init ak_spiflash_init(void) +{ + printk("Start to init Anyka SPI Flash...\n"); + + return spi_register_driver(&ak_spiflash_driver); +} + +/** +* @brief ak spi flash module exit +* +* ak spi norflash module exit +* @author SheShaohua +* @date 2012-03-20 +* @param[in] void +* @return void +* @retval none +*/ +static void __exit ak_spiflash_exit(void) +{ + spi_unregister_driver(&ak_spiflash_driver); +} + + +module_init(ak_spiflash_init); +module_exit(ak_spiflash_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("She Shaohua"); +MODULE_DESCRIPTION("MTD SPI driver for Anyka spiflash chips"); diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index f2f482be..85334aab 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -35,11 +35,61 @@ #include #include #include - +#include +#include +#include +#include +#include #include static DEFINE_MUTEX(mtd_mutex); +/* Add ioctl data structure for spi flash burn tool. +*/ +#define FLASH_PAGESIZE 256 + +#define AK_SPIFLASH_PHY_ERASE 0x80 +#define AK_SPIFLASH_PHY_READ 0x81 +#define AK_SPIFLASH_PHY_WRITE 0x82 +#define AK_SPIFLASH_GET_CHIP_ID 0x83 +#define AK_SPIFLASH_READ_BYTES 0x84 +#define AK_UPDATE_PART_NAME 0x85 + +#define AK_GET_PART_NAME 0x86 + +#define AK_PROTECT_CTL 0x87 + + + +#define LEFT_ALIGN(a,b) ((a/b)*b) +#define RIGHT_ALIGN(a, b) (((a+b-1)/b)*b) + +struct rw_para +{ + T_U32 chip_num; + T_U32 page_num; + T_U8 *data; + T_U32 data_len; + T_U8 *oob; //not used in spi flash, only reserve for nand. + T_U32 oob_len; //not used in spi flash, only reserve for nand. + T_U32 eDataType; +}; + +/* erase block info */ +struct erase_para +{ + T_U32 chip_num; //which chip the block is in + T_U32 startpage; // the block's first page number + T_U32 erasesize; +}; + +struct get_id_para +{ + T_U32 chip_num; + T_U32 *spiflash_id; +}; + + /* * Data structure to hold the pointer to the mtd device as well * as mode information of various use cases. @@ -48,6 +98,7 @@ struct mtd_file_info { struct mtd_info *mtd; struct inode *ino; enum mtd_file_modes mode; + void *part_handle; }; static loff_t mtdchar_lseek(struct file *file, loff_t offset, int orig) @@ -137,6 +188,16 @@ static int mtdchar_open(struct inode *inode, struct file *file) mfi->ino = mtd_ino; mfi->mtd = mtd; file->private_data = mfi; + if(MTD_NANDFLASH == mtd->type) + { + //open partition + mfi->part_handle = partition_open(mtd->name); + if (mfi->part_handle == NULL) { + printk(KERN_ERR "%s, open partition error!\n", __func__); + goto out2; + } + } + mutex_unlock(&mtd_mutex); return 0; @@ -162,6 +223,11 @@ static int mtdchar_close(struct inode *inode, struct file *file) /* Only sync if opened RW */ if ((file->f_mode & FMODE_WRITE)) mtd_sync(mtd); + if(MTD_NANDFLASH == mtd->type) + { + //close partition + partition_close(mfi->part_handle); + } iput(mfi->ino); @@ -241,7 +307,20 @@ static ssize_t mtdchar_read(struct file *file, char __user *buf, size_t count, break; } default: - ret = mtd_read(mtd, *ppos, len, &retlen, kbuf); + if(MTD_NANDFLASH == mtd->type) + { + retlen = partition_read(mfi->part_handle, kbuf, len); + if(retlen < 0){ + kfree(kbuf); + return -EFAULT; + } + else + ret = 0; + } + else + { + ret = mtd_read(mtd, *ppos, len, &retlen, kbuf); + } } /* Nand returns -EBADMSG on ECC errors, but it returns * the data. For our userspace tools it is important @@ -288,7 +367,7 @@ static ssize_t mtdchar_write(struct file *file, const char __user *buf, size_t c size_t total_retlen=0; int ret=0; int len; - + T_PARTITION_TABLE_INFO *parttition_handle = NULL; pr_debug("MTD_write\n"); if (*ppos == mtd->size) @@ -304,9 +383,19 @@ static ssize_t mtdchar_write(struct file *file, const char __user *buf, size_t c if (!kbuf) return -ENOMEM; + if(MTD_NANDFLASH == mtd->type){ + parttition_handle = (T_PARTITION_TABLE_INFO *)mfi->part_handle; + } + while (count) { len = min_t(size_t, count, size); - + if(MTD_NANDFLASH == mtd->type && size != count){ + if(parttition_handle->partition_info.type == PART_FS_TYPE) + { + len = len/2112; + len = len*2112; + } + } if (copy_from_user(kbuf, buf, len)) { kfree(kbuf); return -EFAULT; @@ -337,7 +426,20 @@ static ssize_t mtdchar_write(struct file *file, const char __user *buf, size_t c } default: - ret = mtd_write(mtd, *ppos, len, &retlen, kbuf); + if(MTD_NANDFLASH == mtd->type) + { + retlen = partition_write(mfi->part_handle, kbuf, len); + if(retlen <= 0){ + kfree(kbuf); + return -EFAULT; + } + else + ret = 0; + } + else + { + ret = mtd_write(mtd, *ppos, len, &retlen, kbuf); + } } if (!ret) { *ppos += retlen; @@ -620,17 +722,250 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd, return ret; } + +/** +* @brief MTD char earse +* +* Erase a sector specified by input paramerter. +* @author SheShaohua +* @date 2012-03-20 +* @param[in] mtd mtd info handle. +* @param[in] arg erase info. +* @return int return write success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int erase_SPIFlash_page(struct mtd_info *mtd, unsigned long arg) +{ + struct erase_info instr_info; + struct erase_para e_para; + unsigned long len; + int ret; + + ret = copy_from_user(&e_para, (struct erase_para *)arg, sizeof(struct erase_para)); + if(ret) + return -EFAULT; + //printk("Erase sector: %ld \n", e_para.startpage/16); + + memset(&instr_info, 0, sizeof(struct erase_info)); + instr_info.addr = e_para.startpage * FLASH_PAGESIZE; + instr_info.len = len = e_para.erasesize; + instr_info.mtd = mtd; + + //printk("Addr: %lld, len: %lld, start to erase...\n", instr_info.addr, instr_info.len); + + mtd_erase(mtd, &instr_info); + + return 0; +} + + +static int read_spiflash_bytes(struct mtd_info *mtd, unsigned long arg) +{ + struct rw_para r_para; + unsigned long baseaddr, len, retlen; + unsigned char *buf; + int ret; + + ret = copy_from_user(&r_para, (struct rw_para *)arg, sizeof(struct rw_para)); + if(ret) + return -EFAULT; + + baseaddr = r_para.page_num; + len = r_para.data_len; + buf = (unsigned char *)kmalloc(len, GFP_KERNEL); + if( buf == NULL ) + { + printk(KERN_ERR "%s, kmalloc buf fail!\n", __func__); + return -1; + } + + mtd_read(mtd, baseaddr, len, (size_t*)&retlen, buf); + + ret = copy_to_user(((struct rw_para *)arg)->data, buf, retlen); + if(ret) { + ret = -EFAULT; + goto rd_fail; + } + +rd_fail: + kfree(buf); + + return 0; +} + + +/** +* @brief MTD char read +* +* Read data from spi flash. +* @author SheShaohua +* @date 2012-03-20 +* @param[in] mtd mtd info handle. +* @param[in] arg read address and length info. +* @return int return write success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int read_SPIFlash_page(struct mtd_info *mtd, unsigned long arg) +{ + struct rw_para r_para; + unsigned long baseaddr, addr, len, retlen, pageCount; + unsigned char *buf; + int ret; + + ret = copy_from_user(&r_para, (struct rw_para *)arg, sizeof(struct rw_para)); + if(ret) + return -EFAULT; + + //printk("Read page: %ld, len: %ld\n", r_para.page_num, r_para.data_len); + + baseaddr = r_para.page_num * FLASH_PAGESIZE; + pageCount = r_para.data_len; + len = r_para.data_len * FLASH_PAGESIZE; + buf = (unsigned char *)kmalloc(len, GFP_KERNEL); + if( buf == NULL ) + { + printk(KERN_ERR "%s, kmalloc buf fail!\n", __func__); + return -1; + } + + addr = baseaddr; + mtd_read(mtd, addr, len, (size_t*)&retlen, buf); + + ret = copy_to_user(((struct rw_para *)arg)->data, buf, retlen); + if(ret) { + ret = -EFAULT; + goto rd_fail; + } + +rd_fail: + kfree(buf); + + return 0; +} + + +/** +* @brief MTD char write +* +* write data to spi flash. +* @author SheShaohua +* @date 2012-03-20 +* @param[in] mtd mtd info handle. +* @param[in] arg write related info. +* @return int return write success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int write_SPIFlash_page(struct mtd_info *mtd, unsigned long arg) +{ + struct rw_para w_para; + unsigned long baseAddr, addr, total_len, len, remainCount, retlen, pageCount; + unsigned char *buff, *pbuf; + u32 i; + int ret; + + ret = copy_from_user(&w_para, (struct rw_para *)arg, sizeof(struct rw_para)); + if(ret) + return -EFAULT; + +// printk("Write page: %ld ~ %ld\n", w_para.page_num, (w_para.page_num + w_para.data_len - 1) ); + + baseAddr = w_para.page_num * FLASH_PAGESIZE; + pageCount = w_para.data_len; + total_len = w_para.data_len * FLASH_PAGESIZE; + buff = (unsigned char *)kmalloc(total_len, GFP_KERNEL); + if( buff == NULL ) + { + printk(KERN_ERR "%s, kmalloc buf fail!\n", __func__); + return -1; + } + + ret = copy_from_user(buff, w_para.data, total_len); + if(ret) { + ret = -EFAULT; + goto wr_fail; + } + + + remainCount = total_len; + addr = baseAddr; + pbuf = buff; + for(i=0; i FLASH_PAGESIZE ) + { + len = FLASH_PAGESIZE; + } + else + { + len = remainCount; + } + + addr = baseAddr + (i * FLASH_PAGESIZE); + pbuf = buff + (i * FLASH_PAGESIZE); + mtd_write(mtd, addr, len, (size_t*)&retlen, pbuf); + remainCount -= FLASH_PAGESIZE; + } + +wr_fail: + kfree(buff); + + return 0; +} + + +/** +* @brief Get Device ID +* +* Get Device ID of spi flash. +* @author SheShaohua +* @date 2012-03-20 +* @param[in] mtd mtd info handle. +* @param[out] arg return device ID. +* @return int return write success or failed +* @retval returns zero on success +* @retval return a non-zero error code if failed +*/ +static int get_SPIFlash_ID(struct mtd_info *mtd, unsigned long arg) +{ + int ret; + u32 spi_id = 0; + + if(mtd->get_device_id) + { + spi_id = mtd->get_device_id(mtd); + if( spi_id == AK_FALSE) + { + printk(KERN_ERR "%s, get_device_id fail!\n", __func__); + return -1; + } + } + + ret = copy_to_user(((struct get_id_para *)arg)->spiflash_id, &spi_id, sizeof(u32)); + if(ret) + return -EFAULT; + + printk("get_SPIFlash_ID: 0x%08x\n", spi_id); + return 0; +} + + static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) { struct mtd_file_info *mfi = file->private_data; struct mtd_info *mtd = mfi->mtd; void __user *argp = (void __user *)arg; - int ret = 0; + int ret = 0, i = 0; u_long size; struct mtd_info_user info; + T_PARTITION_INFO partition_info = {0}; + T_PARTITION_INFO read_partition_info = {0}; + char read_flag = 0; //0表示不需要读 1表示?要读 pr_debug("MTD_ioctl\n"); - + size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT; if (cmd & IOC_IN) { if (!access_ok(VERIFY_READ, argp, size)) @@ -642,6 +977,181 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) } switch (cmd) { + case AK_PROTECT_CTL: + { + + + printk(KERN_ERR "spinand set protect not support \n"); + + #if 0 + int bprotect; + if (copy_from_user(&bprotect, argp, sizeof(bprotect))) + ret = -EFAULT; + else { + printk(KERN_ERR "spinand set protect: %d\n", bprotect); + if(bprotect) + ak_spinand_cfg_protect(true); + else + ak_spinand_cfg_protect(false); + } + #endif + } + break; + + case AK_GET_PART_NAME: + //只有个别分区进行更新?以需要把原来数据读取出来,再进行写下去 + memset(&read_partition_info, 0, sizeof(T_PARTITION_INFO)); + + if(MTD_NANDFLASH != mtd->type) + { + printk(KERN_ERR "spi nor not support get part name\n"); + } + else + { + + //printk(KERN_ERR "sizeof(T_PARTITION_INFO):%d\n", sizeof(T_PARTITION_INFO)); + if(spinand_read_asa_data((unsigned char *)&read_partition_info, sizeof(T_PARTITION_INFO)) == -1) + { + printk(KERN_ERR"spinand_read_asa_data fail\r\n"); + return -EFAULT; + } + + printk(KERN_ERR"partition_cnt:%ld\r\n", read_partition_info.partition_cnt); + //printk(KERN_ERR"partition_name_info[0].partition_name:%s\r\n", read_partition_info.partition_name_info[0].partition_name); + //printk(KERN_ERR"partition_name_info[1].partition_name:%s\r\n", read_partition_info.partition_name_info[1].partition_name); + //printk(KERN_ERR"partition_name_info[20].partition_name:%s\r\n", read_partition_info.partition_name_info[2].partition_name); + + + if (copy_to_user(argp, &read_partition_info, sizeof(T_PARTITION_INFO))) + { + printk(KERN_ERR"copy_to_user fail\r\n"); + return -EFAULT; + } + } + + break; + + case AK_UPDATE_PART_NAME: + + //printk(KERN_ERR "mtdchar_ioctl@@@@@@@@@@@@@@@@@@, cmd:%d\n", cmd); + if(MTD_NANDFLASH != mtd->type) + { + printk(KERN_ERR "spi nor not support update part name\n"); + } + else + { + + #if 1 + //判断传下来的数据是否个分区都更新 + + //printk(KERN_ERR "len:%d\n", sizeof(T_PARTITION_INFO)); + + if(copy_from_user(&partition_info, argp,sizeof(T_PARTITION_INFO))) { + return -EFAULT; + } + + //printk(KERN_ERR "read_flag:%d\n", read_flag); + if(read_flag == 0) + { + printk(KERN_ERR"partition_cnt:%ld\r\n", partition_info.partition_cnt); + printk(KERN_ERR"partition_name_info[0].update_flag:%s\r\n", partition_info.partition_name_info[0].update_flag); + printk(KERN_ERR"partition_name_info[1].update_flag:%s\r\n", partition_info.partition_name_info[1].update_flag); + printk(KERN_ERR"partition_name_info[2].update_flag:%s\r\n", partition_info.partition_name_info[2].update_flag); + + if(spinand_write_asa_data((unsigned char *)&partition_info, sizeof(T_PARTITION_INFO)) == -1) + { + printk(KERN_ERR"spinand_write_asa_data fail\r\n"); + return -EFAULT; + } + } + else + { + //只有个别分区进行更新?以需要把原来数据读取出来,再进行写下去 + memset(&read_partition_info, 0, sizeof(T_PARTITION_INFO)); + + //printk(KERN_ERR "sizeof(T_PARTITION_INFO):%d\n", sizeof(T_PARTITION_INFO)); + if(spinand_read_asa_data((unsigned char *)&read_partition_info, sizeof(T_PARTITION_INFO)) == -1) + { + printk(KERN_ERR"spinand_read_asa_data fail\r\n"); + return -EFAULT; + } + + printk(KERN_ERR"partition_cnt:%ld\r\n", read_partition_info.partition_cnt); + printk(KERN_ERR"partition_name_info[0].update_flag:%s\r\n", read_partition_info.partition_name_info[0].update_flag); + printk(KERN_ERR"partition_name_info[1].update_flag:%s\r\n", read_partition_info.partition_name_info[1].update_flag); + printk(KERN_ERR"partition_name_info[20].update_flag:%s\r\n", read_partition_info.partition_name_info[2].update_flag); + + // + for(i = 0; i < partition_info.partition_cnt; i++) + { + if(partition_info.partition_name_info[i].update_flag[0] != 0) + { + memset(read_partition_info.partition_name_info[i].update_flag, 0, 6); + strncpy(read_partition_info.partition_name_info[i].update_flag, partition_info.partition_name_info[i].update_flag, 6); + } + } + + printk(KERN_ERR"partition_cnt:%ld\r\n", read_partition_info.partition_cnt); + printk(KERN_ERR"partition_name_info[0].update_flag:%s\r\n", read_partition_info.partition_name_info[0].update_flag); + printk(KERN_ERR"partition_name_info[1].update_flag:%s\r\n", read_partition_info.partition_name_info[1].update_flag); + printk(KERN_ERR"partition_name_info[20].update_flag:%s\r\n",read_partition_info.partition_name_info[2].update_flag); + + if(spinand_write_asa_data((unsigned char *)&read_partition_info, sizeof(T_PARTITION_INFO)) == -1) + { + printk(KERN_ERR"spinand_write_asa_data fail\r\n"); + return -EFAULT; + } + } + #endif + } + + break; + + + case AK_SPIFLASH_PHY_READ: + ret = read_SPIFlash_page( mtd, arg ); + if( ret != 0 ) + { + printk(KERN_ERR "AK_SPIFLASH_PHY_READ failed:ret=%d\n", ret); + return -EFAULT; + } + break; + + case AK_SPIFLASH_PHY_WRITE: + ret = write_SPIFlash_page( mtd, arg ); + if( ret != 0 ) + { + printk(KERN_ERR "AK_SPIFLASH_PHY_WRITE failed:ret=%d\n", ret); + return -EFAULT; + } + break; + + case AK_SPIFLASH_PHY_ERASE: + ret = erase_SPIFlash_page( mtd, arg ); + if( ret != 0 ) + { + printk(KERN_ERR "AK_SPIFLASH_PHY_ERASE failed:ret=%d\n", ret); + return -EFAULT; + } + break; + + case AK_SPIFLASH_GET_CHIP_ID: + ret = get_SPIFlash_ID( mtd, arg ); + if( ret != 0 ) + { + printk(KERN_ERR "AK_SPIFLASH_GET_CHIP_ID failed:ret=%d\n", ret); + return -EFAULT; + } + break; + case AK_SPIFLASH_READ_BYTES: + ret = read_spiflash_bytes(mtd, arg); + if( ret != 0 ) + { + printk(KERN_ERR "AK_SPIFLASH_READ_BYTES failed:ret=%d\n", ret); + return -EFAULT; + } + break; + case MEMGETREGIONCOUNT: if (copy_to_user(argp, &(mtd->numeraseregions), sizeof(int))) return -EFAULT; diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index bf24aa77..257b4699 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -646,6 +646,7 @@ int add_mtd_partitions(struct mtd_info *master, return 0; } +EXPORT_SYMBOL_GPL(add_mtd_partitions); static DEFINE_SPINLOCK(part_parser_lock); static LIST_HEAD(part_parsers); diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 7d17ceca..884f99ca 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -1,3 +1,10 @@ +config MTD_NAND_IDS + tristate "Include chip ids for known NAND devices." + depends on MTD + help + Useful for NAND drivers that do not use the NAND subsystem but + still like to take advantage of the known chip information. + config MTD_NAND_ECC tristate @@ -115,9 +122,6 @@ config MTD_NAND_OMAP2 Support for NAND flash on Texas Instruments OMAP2, OMAP3 and OMAP4 platforms. -config MTD_NAND_IDS - tristate - config MTD_NAND_RICOH tristate "Ricoh xD card reader" default n diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index c63a64cb..a8d24dde 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -176,5 +176,5 @@ source "drivers/net/ethernet/tundra/Kconfig" source "drivers/net/ethernet/via/Kconfig" source "drivers/net/ethernet/xilinx/Kconfig" source "drivers/net/ethernet/xircom/Kconfig" - +source "drivers/net/ethernet/ak-ethernet/Kconfig" endif # ETHERNET diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index 9676a510..2c46e882 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -75,3 +75,4 @@ obj-$(CONFIG_NET_VENDOR_TUNDRA) += tundra/ obj-$(CONFIG_NET_VENDOR_VIA) += via/ obj-$(CONFIG_NET_VENDOR_XILINX) += xilinx/ obj-$(CONFIG_NET_VENDOR_XIRCOM) += xircom/ +obj-$(CONFIG_AK_ETHERNET) += ak-ethernet/ diff --git a/drivers/net/ethernet/ak-ethernet/Ethernethw.h b/drivers/net/ethernet/ak-ethernet/Ethernethw.h new file mode 100644 index 00000000..c93944ca --- /dev/null +++ b/drivers/net/ethernet/ak-ethernet/Ethernethw.h @@ -0,0 +1,742 @@ +/* + * @file ak ethernethw.h + * @ethernet register definition + * @Copyright (C) 2010 Anyka (Guangzhou) Microelectronics Technology Co + * @author tang_anyang + * @date 2013-04 + * @version + * @for more information , please refer to AK390x Programmer's Guide Mannul + */ + +#ifndef _MAC_REG_DEFINE_H_ +#define _MAC_REG_DEFINE_H_ +#define MAC_REG_BASE 0x60000000 //MAC register base + +#define REG_MASTER_CTRL ( 0x1400) /* WORD reg */ + #define MASTER_CTRL_MAC_SOFT_RST_OFF 0 /* hw sc */ + #define MASTER_CTRL_MAC_SOFT_RST_BITS 1 + #define MASTER_CTRL_PCIE_SOFT_RST_OFF 1 /* hw sc */ + #define MASTER_CTRL_PCIE_SOFT_RST_BITS 1 + #define MASTER_CTRL_PCIE_TEST_MOD_OFF 2 + #define MASTER_CTRL_PCIE_TEST_MOD_BITS 2 + #define MASTER_CTRL_BERT_START_OFF 4 + #define MASTER_CTRL_BERT_START_BITS 1 + #define MASTER_CTRL_OOB_DIS_OFF 6 + #define MASTER_CTRL_OOB_DIS_BITS 1 + #define MASTER_CTRL_SA_TIMER_EN_OFF 7 + #define MASTER_CTRL_SA_TIMER_EN_BITS 1 + #define MASTER_CTRL_MANUAL_TIME_EN_OFF 8 + #define MASTER_CTRL_MANUAL_TIMER_EN_BITS 1 + #define MASTER_CTRL_MANUAL_INT_OFF 9 + #define MASTER_CTRL_MANUAL_INT_BITS 1 + #define MASTER_CTRL_IRQ_MODRT_EN_OFF 10 /* tx if 2 timer */ + #define MASTER_CTRL_IRQ_MODRT_EN_BITS 1 + #define MASTER_CTRL_IRQ_MODRT_RXEN_OFF 11 /* rx if 2 timer */ + #define MASTER_CTRL_IRQ_MODRT_RXEN_BITS 1 + #define MASTER_CTRL_PCLK_SEL_DIS_OFF 12 /* ps may set it */ + #define MASTER_CTRL_PCLK_SEL_DIS_BITS 1 + #define MASTER_CTRL_CLKSW_MODE_OFF 13 + #define MASTER_CTRL_CLKSW_MODE_BITS 1 + #define MASTER_CTRL_INT_RCLR_EN_OFF 14 + #define MASTER_CTRL_INT_CLCR_EN_BITS 1 + #define MASTER_CTRL_OTP_SEL_OFF 31 + #define MASTER_CTRL_OTP_SEL_BITS 1 + +#define REG_DEV_REV_NUM (0x1402) /* BYTE reg */ +#define REG_DEV_ID_NUM (0x1403) /* BYTE reg */ + +#define REG_MANUAL_TIMER_INIT (0x1404) /* DWORD reg */ + +#define REG_IRQ_MODRT_INIT (0x1408) /* WORD reg */ +#define REG_IRQ_MODRT_RX_INIT (0x140A) /* WORD reg */ + +#define REG_PHY_CTRL (0x140C) /* DWORD reg */ + #define PHY_CTRL_EXIT_RST_OFF 0 + #define PHY_CTRL_EXIT_RST_BITS 1 + #define PHY_CTRL_RTL_MOD_OFF 1 + #define PHY_CTRL_RTL_MOD_BITS 1 + #define PHY_CTRL_LED_MOD_OFF 2 + #define PHY_CTRL_LED_MOD_BITS 1 + #define PHY_CTRL_ANEG_NOW_OFF 3 + #define PHY_CTRL_ANEG_NOW_BITS 1 + #define PHY_CTRL_REV_ANEG_OFF 4 + #define PHY_CTRL_REV_ANEG_BITS 1 + #define PHY_CTRL_GATE25M_EN_OFF 5 + #define PHY_CTRL_GATE25M_EN_BITS 1 + #define PHY_CTRL_LPW_EXIT_OFF 6 + #define PHY_CTRL_LPW_EXIT_BITS 1 + #define PHY_CTRL_PHY_IDDQ_OFF 7 + #define PHY_CTRL_PHY_IDDQ_BITS 1 + #define PHY_CTRL_PHY_IDDQ_DIS_OFF 8 + #define PHY_CTRL_PHY_IDDQ_DIS_BITS 1 + #define PHY_CTRL_GIGA_DIS_OFF 9 + #define PHY_CTRL_GIGA_DIS_BITS 1 + #define PHY_CTRL_HIB_EN_HW_OFF 10 + #define PHY_CTRL_HIB_EN_HW_BITS 1 + #define PHY_CTRL_HIB_PULSE_HW_OFF 11 + #define PHY_CTRL_HIB_PULSE_HW_BITS 1 + #define PHY_CTRL_SEL_ANA_RST_OFF 12 + #define PHY_CTRL_SEL_ANA_RST_BITS 1 + #define PHY_CTRL_PHY_PLL_ON_OFF 13 + #define PHY_CTRL_PHY_PLL_ON_BITS 1 + #define PHY_CTRL_POWERDOWN_HW_OFF 14 + #define PHY_CTRL_POWERDOWN_HW_BITS 1 + #define PHY_CTRL_PHY_PLL_BYPASS_OFF 15 + #define PHY_CTRL_PHY_PLL_BYPASS_BITS 1 + +#ifdef EN_HIB + #define PHY_CTRL_DEFAULT (\ + FLAG(PHY_CTRL_SEL_ANA_RST_OFF) |\ + FLAG(PHY_CTRL_HIB_EN_HW_OFF) |\ + FLAG(PHY_CTRL_HIB_PULSE_HW_OFF) ) +#else + #define PHY_CTRL_DEFAULT (\ + FLAG(PHY_CTRL_SEL_ANA_RST_OFF) |\ + FLAG(PHY_CTRL_HIB_PULSE_HW_OFF) ) +#endif/*EN_HIB*/ + + #define PHY_CTRL_POWER_SAVING (\ + FLAG(PHY_CTRL_SEL_ANA_RST_OFF) |\ + FLAG(PHY_CTRL_HIB_EN_HW_OFF) |\ + FLAG(PHY_CTRL_HIB_PULSE_HW_OFF) |\ + FLAG(PHY_CTRL_POWERDOWN_HW_OFF) |\ + FLAG(PHY_CTRL_PHY_IDDQ_OFF) ) + + +#define REG_IDLE_STATUS (0x1410) /* BYTE reg */ + #define IDLE_STATUS_RXMAC_OFF 0 + #define IDLE_STATUS_RXMAC_BITS 1 + #define IDLE_STATUS_TXMAC_OFF 1 + #define IDLE_STATUS_TXMAC_BITS 1 + #define IDLE_STATUS_RXQ_OFF 2 + #define IDLE_STATUS_RXQ_BITS 1 + #define IDLE_STATUS_TXQ_OFF 3 + #define IDLE_STATUS_TXQ_BITS 1 + +#define REG_MDIO_CTRL (0x1414) /* DWORD reg */ + #define MDIO_CTRL_DATA(val) (val&0xFFFF) + #define MDIO_CTRL_REG_ADDR(val) ((val&0x1f) << 16) + #define MDIO_CTRL_REG_ADDR_BITS 5 + #define MDIO_CTRL_WRITE (0<<21) + #define MDIO_CTRL_READ (1<<21)/*read:0, write:1*/ + #define MDIO_CTRL_RW_OFF 21 + #define MDIO_CTRL_RW_BITS 1 + #define MDIO_CTRL_SUP_PREAMBLE (1<<22) + #define MDIO_CTRL_SUP_PREAMBLE_BITS 1 + #define MDIO_CTRL_START (1<<23) + #define MDIO_CTRL_START_BITS 1 /* sc */ + #define MDIO_CTRL_CLK_SEL_OFF 24 + #define MDIO_CTRL_CLK_SEL_BITS 3 + #define MDIO_CTRL_BUSY_OFF 27 + #define MDIO_CTRL_BUSY_BITS 1 + #define MDIO_CTRL_AP_EN_OFF 28 + #define MDIO_CTRL_AP_EN_BITS 1 + #define MDIO_CLK_25_4 0 + #define MDIO_CLK_25_6 2 + #define MDIO_CLK_25_8 3 + #define MDIO_CLK_25_10 4 + #define MDIO_CLK_25_14 5 + #define MDIO_CLK_25_20 6 + #define MDIO_CLK_25_28 7 + #define MDIO_MAX_AC_TIMER 100 /* 1 ms */ + #define MDIO_CTRL_POST_READ_OFF 29 + #define MDIO_CTRL_POST_READ_BITS 1 + #define MDIO_CTRL_MODE_OFF 30 + #define MDIO_CTRL_MODE_BITS 1 + #define MDIO_CTRL_MODE_OLD 0 + #define MDIO_CTRL_MODE_EXTENSION 1 + + +#define REG_PHY_STATUS (0x1418) /* DWORD reg */ + #define PHY_STATUS_PHY_STATUS_OFF 0 + #define PHY_STATUS_PHY_STATUS_BITS 16 + #define PHY_STATUS_OE_PWSTRP_OFF 16 + #define PHY_STATUS_OE_PWSTRP_BITS 11 + #define PHY_STATUS_LPW_STATUS_OFF 31 + #define PHY_STATUS_LPW_STATUS_BITS 1 + +#define REG_BIST0_CTRL (0x141C) /* DWORD reg */ + #define BIST0_CTRL_NOW_OFF 0 + #define BIST0_CTRL_NOW_BITS 1 + #define BIST0_CTRL_SRAM_FAIL_OFF 1 + #define BIST0_CTRL_SRAM_FAIL_BITS 1 + #define BIST0_CTRL_FUSE_FLAG_OFF 2 + #define BIST0_CTRL_FUSE_FLAG_BITS 1 + #define BIST0_CTRL_FUSE_PAT_OFF 4 + #define BIST0_CTRL_FUSE_PAT_BITS 3 + #define BIST0_CTRL_FUSE_STEP_OFF 8 + #define BIST0_CTRL_FUSE_SETP_BITS 4 + #define BIST0_CTRL_FUSE_ROW_OFF 12 + #define BIST0_CTRL_FUSE_ROW_BITS 12 + #define BIST0_CTRL_FUSE_COL_OFF 24 + #define BIST0_CTRL_FUSE_COL_BITS 6 + +#define REG_BIST1_CTRL (0x1420) /* DWORD reg */ + #define BIST1_CTRL_NOW_OFF 0 + #define BIST1_CTRL_NOW_BITS 1 + #define BIST1_CTRL_SRAM_FAIL_OFF 1 + #define BIST1_CTRL_SRAM_FAIL_BITS 1 + #define BIST1_CTRL_FUSE_FLAG_OFF 2 + #define BIST1_CTRL_FUSE_FLAG_BITS 1 + #define BIST1_CTRL_FUSE_PAT_OFF 4 + #define BIST1_CTRL_FUSE_PAT_BITS 3 + #define BIST1_CTRL_FUSE_STEP_OFF 8 + #define BIST1_CTRL_FUSE_SETP_BITS 4 + #define BIST1_CTRL_FUSE_ROW_OFF 12 + #define BIST1_CTRL_FUSE_ROW_BITS 7 + #define BIST1_CTRL_FUSE_COL_OFF 24 + #define BIST1_CTRL_FUSE_COL_BITS 5 + +#define REG_SERDES_CTRL_STS (0x1424) +#define SERDES_CTRL_STS_SELFB_PLL_SEL_OFF 14 +#define SERDES_CTRL_STS_SELFB_PLL_SEL_BITS 2 +#define SERDES_OVCLK_18_25 0 +#define SERDES_OVCLK_12_18 1 +#define SERDES_OVCLK_0_4 2 +#define SERDES_OVCLK_4_12 3 +#define SERDES_MAC_CLK_SLOWDOWN_OFF 17 +#define SERDES_MAC_CLK_SLOWDOWN_BITS 1 +#define SERDES_PHY_CLK_SLOWDOWN_OFF 18 +#define SERDES_PHY_CLK_SLOWDOWN_BITS 1 + + +#define REG_LED_CTRL (0x1428) /* DWORD reg */ + #define LED_CTRL_DC_CTRL_OFF 0 + #define LED_CTRL_DC_CTRL_BITS 2 + #define LED_CTRL_D3_MODE_CTRL_OFF 2 + #define LED_CTRL_D3_MODE_CTRL_BITS 2 + #define LED_CTRL_0_PAT_MAP_OFF 4 + #define LED_CTRL_0_PAT_MAP_BITS 2 + #define LED_CTRL_1_PAT_MAP_OFF 6 + #define LED_CTRL_1_PAT_MAP_BITS 2 + #define LED_CTRL_2_PAT_MAP_OFF 8 + #define LED_CTRL_2_PAT_MAP_BITS 2 + +#define REG_LED_PAT0 (0x142C) /* WORD reg */ +#define REG_LED_PAT1 (0x142E) /* WORD reg */ +#define REG_LED_PAT2 (0x1430) /* WORD reg */ + +#define REG_SYS_ALIVE (0x1434) + #define SYS_ALIVE_FLAG_OFF 0 + #define SYS_ALIVE_FLAG_BITS 1 + +#define REG_LPI_TD (0x143C) + +#define REG_LPI_CTRL (0x1440) + #define LPI_CTRL_EN_OFF 0 + #define LPI_CTRL_EN_BITS 1 + #define LPI_CTRL_CTRL_OFF 1 + #define LPI_CTRL_CTRL_BITS 1 + #define LPI_CTRL_GMII_OFF 2 + #define LPI_CTRL_GMII_BITS 1 + #define LPI_CTRL_CHK_STATE_OFF 3 + #define LPI_CTRL_CHK_STATE_BITS 1 + #define LPI_CTRL_CHK_RX_OFF 4 + #define LPI_CTRL_CHK_RX_BITS 1 + +#define REG_LPI_TW (0x1444) + + +#define REG_MDIO_EXT_CTRL (0x1448) + #define MDIO_EXT_CTRL_REG_ADDR_OFF 0 + #define MDIO_EXT_CTRL_REG_ADDR_BITS 16 + #define MDIO_EXT_CTRL_DEVADDR_OFF 16 + #define MDIO_EXT_CTRL_DEVADDR_BITS 5 + #define MDIO_EXT_CTRL_PTADDR_OFF 21 + #define MDIO_EXT_CTRL_PTADDR_BITS 5 +//MAC operation register + +#define REG_MAC_CTRL (0x1480) /* DWORD reg */ + #define MAC_CTRL_TXEN_OFF 0 //bit for Tx Enable + #define MAC_CTRL_TXEN_BITS 1 //bit for Tx Enable + #define MAC_CTRL_RXEN_OFF 1 //bit for Rx Enable + #define MAC_CTRL_RXEN_BITS 1 //bit for Rx Enable + #define MAC_CTRL_TXFC_OFF 2 + #define MAC_CTRL_TXFC_BITS 1 + #define MAC_CTRL_RXFC_OFF 3 + #define MAC_CTRL_RXFC_BITS 1 + #define MAC_CTRL_LOOPBACK_OFF 4 //MAC loopback + #define MAC_CTRL_LOOPBACK_BITS 1 + #define MAC_CTRL_FULLD_OFF 5 + #define MAC_CTRL_FULLD_BITS 1 + #define MAC_CTRL_CRCE_OFF 6 + #define MAC_CTRL_CRCE_BITS 1 + #define MAC_CTRL_PCRCE_OFF 7 + #define MAC_CTRL_PCRCE_BITS 1 + #define MAC_CTRL_FLCHK_OFF 8 + #define MAC_CTRL_FLCHK_BITS 1 + #define MAC_CTRL_HUGEN_OFF 9 + #define MAC_CTRL_HUGEN_BITS 1 + #define MAC_CTRL_PRLEN_OFF 10 + #define MAC_CTRL_PRLEN_BITS 4 + #define MAC_CTRL_PRLEN_DEF 7 + #define MAC_CTRL_VLAN_STRIP_OFF 14 + #define MAC_CTRL_VLAN_STRIP_BITS 1 + #define MAC_CTRL_PROM_MODE_OFF 15 + #define MAC_CTRL_PROM_MODE_BITS 1 + #define MAC_CTRL_TPAUSE_OFF 16 + #define MAC_CTRL_TPAUSE_BITS 1 + #define MAC_CTRL_SSTCT_OFF 17 + #define MAC_CTRL_SSTCT_BITS 1 + #define MAC_CTRL_SRTFN_OFF 18 + #define MAC_CTRL_SRTFN_BITS 1 + #define MAC_CTRL_SIMR_OFF 19 + #define MAC_CTRL_SIMR_BITS 1 + #define MAC_CTRL_SPEED_OFF 20 + #define MAC_CTRL_SPEED_BITS 2 + #define MAC_CTRL_SPEED_10_100 1 + #define MAC_CTRL_SPEED_1000 2 + #define MAC_CTRL_MBOF_OFF 22 + #define MAC_CTRL_MBOF_BITS 1 + #define MAC_CTRL_HUGE_OFF 23 + #define MAC_CTRL_HUGE_BITS 1 + #define MAC_CTRL_RX_XSUM_EN_OFF 24 + #define MAC_CTRL_RX_XSUM_EN_BITS 1 + #define MAC_CTRL_MUTI_ALL_OFF 25 //bit for mulicast + #define MAC_CTRL_MUTI_ALL_BITS 1 //bit for mulicast + #define MAC_CTRL_BROAD_EN_OFF 26 //bit for broadcast + #define MAC_CTRL_BROAD_EN_BITS 1 //bit for broadcast + #define MAC_CTRL_DEBUG_MODE_OFF 27 //bit for debug + #define MAC_CTRL_DEBUG_MODE_BITS 1 //bit for debug + #define MAC_CTRL_SINGLE_PAUSE_OFF 28 + #define MAC_CTRL_SINGLE_PAUSE_BITS 1 + #define MAC_CTRL_HASH_ALG_MODE_OFF 29 + #define MAC_CTRL_HASH_ALG_MODE_BITS 1 + #define MAC_CTRL_HASH_ALG_CRC32 1 + #define MAC_CTRL_HASH_ALG_CRC16 0 + #define MAC_CTRL_SPEED_MODE_OFF 30 + #define MAC_CTRL_SPEED_MODE_BITS 1 + #define MAC_CTRL_SPEED_MODE_PHY 0 + #define MAC_CTRL_SPEED_MODE_SW 1 + + +#define REG_MAC_STA_ADDR (0x1488) /* QWORD reg */ +/* [1488]=0x749dc320, [148c]=0x00000013, mac-addr:00-13-74-9d-c3-20 */ + +#define REG_RX_HASH_TABLE (0x1490) /* QWORD reg */ + +#define REG_MTU (0x149C) /* WORD reg */ + +#define REG_WOL_CTRL (0x14A0) /* DWORD reg */ + #define WOL_CTRL_PATTERN_EN_OFF 0 + #define WOL_CTRL_PATTERN_EN_BITS 1 + #define WOL_CTRL_PATTERN_PME_EN_OFF 1 + #define WOL_CTRL_PATTERN_PME_EN_BITS 1 + #define WOL_CTRL_MAGIC_EN_OFF 2 + #define WOL_CTRL_MAGIC_EN_BITS 1 + #define WOL_CTRL_MAGIC_PME_EN_OFF 3 + #define WOL_CTRL_MAGIC_PME_EN_BITS 1 + #define WOL_CTRL_LINKCHG_EN_OFF 4 + #define WOL_CTRL_LINKCHG_EN_BITS 1 + #define WOL_CTRL_LINKCHG_PME_EN_OFF 5 + #define WOL_CTRL_LINKCHG_PME_EN_BITS 1 + #define WOL_CTRL_PATTERN_ST_OFF 8 + #define WOL_CTRL_PATTERN_ST_BITS 1 + #define WOL_CTRL_MAGIC_ST_OFF 9 + #define WOL_CTRL_MAGIC_ST_BITS 1 + #define WOL_CTRL_LINKCHG_ST_OFF 10 + #define WOL_CTRL_LINKCHG_ST_BITS 1 + #define WOL_CTRL_CLK_SWH_EN_OFF 15 + #define WOL_CTRL_CLK_SWH_EN_BITS 1 + #define WOL_CTRL_PT0_EN_OFF 16 + #define WOL_CTRL_PT0_EN_BITS 1 + #define WOL_CTRL_PT1_EN_OFF 17 + #define WOL_CTRL_PT1_EN_BITS 1 + #define WOL_CTRL_PT2_EN_OFF 18 + #define WOL_CTRL_PT2_EN_BITS 1 + #define WOL_CTRL_PT3_EN_OFF 19 + #define WOL_CTRL_PT3_EN_BITS 1 + #define WOL_CTRL_PT4_EN_OFF 20 + #define WOL_CTRL_PT4_EN_BITS 1 + #define WOL_CTRL_PT5_EN_OFF 21 + #define WOL_CTRL_PT5_EN_BITS 1 + #define WOL_CTRL_PT6_EN_OFF 22 + #define WOL_CTRL_PT6_EN_BITS 1 + #define WOL_CTRL_PT0_MATCH_OFF 24 + #define WOL_CTRL_PT0_MATCH_BITS 1 + #define WOL_CTRL_PT1_MATCH_OFF 25 + #define WOL_CTRL_PT1_MATCH_BITS 1 + #define WOL_CTRL_PT2_MATCH_OFF 26 + #define WOL_CTRL_PT2_MATCH_BITS 1 + #define WOL_CTRL_PT3_MATCH_OFF 27 + #define WOL_CTRL_PT3_MATCH_BITS 1 + #define WOL_CTRL_PT4_MATCH_OFF 28 + #define WOL_CTRL_PT4_MATCH_BITS 1 + #define WOL_CTRL_PT5_MATCH_OFF 29 + #define WOL_CTRL_PT5_MATCH_BITS 1 + #define WOL_CTRL_PT6_MATCH_OFF 30 + #define WOL_CTRL_PT6_MATCH_BITS 1 + +#define REG_WOL_PT0_LEN (0x14A4) /* BYTE reg */ +#define REG_WOL_PT1_LEN 0x14A5 /* BYTE reg */ +#define REG_WOL_PT2_LEN 0x14A6 /* BYTE reg */ +#define REG_WOL_PT3_LEN 0x14A7 /* BYTE reg */ +#define REG_WOL_PT4_LEN 0x14A8 /* BYTE reg */ +#define REG_WOL_PT5_LEN 0x14A9 /* BYTE reg */ +#define REG_WOL_PT6_LEN 0x14AA /* BYTE reg */ + +#define REG_SRAM_RFD0 (0x1500) /* DWORD reg */ + #define SRAM_RFD0_HDRADDR_OFF 0 + #define SRAM_RFD0_HDRADDR_BITS 12 + #define SRAM_RFD0_TALADDR_OFF 16 + #define SRAM_RFD0_TALADDR_BITS 12 + +#define REG_SRAM_RFD1 (0x1504) /* DWORD reg */ + #define SRAM_RFD1_HDRADDR_OFF 0 + #define SRAM_RFD1_HDRADDR_BITS 12 + #define SRAM_RFD1_TALADDR_OFF 16 + #define SRAM_RFD1_TALADDR_BITS 12 + +#define REG_SRAM_RFD2 (0x1508) /* DWORD reg */ + #define SRAM_RFD2_HDRADDR_OFF 0 + #define SRAM_RFD2_HDRADDR_BITS 12 + #define SRAM_RFD2_TALADDR_OFF 16 + #define SRAM_RFD2_TALADDR_BITS 12 + +#define REG_SRAM_RFD3 (0x150C) /* DWORD reg */ + #define SRAM_RFD3_HDRADDR_OFF 0 + #define SRAM_RFD3_HDRADDR_BITS 12 + #define SRAM_RFD3_TALADDR_OFF 16 + #define SRAM_RFD3_TALADDR_BITS 12 + +#define REG_SRAM_RFD_NICLEN (0x1510) /* DWORD reg */ + +#define REG_SRAM_TRD 0x1518 /* DWORD reg */ + #define SRAM_TRD_HDRADDR_OFF 0 + #define SRAM_TRD_HDRADDR_BITS 12 + #define SRAM_TRD_TALADDR_OFF 16 + #define SRAM_TRD_TALADDR_BITS 12 + +#define REG_SRAM_TRD_NICLEN (0x151C) /* DWORD reg */ + +#define REG_SRAM_RXF (0x1520) /* DWORD reg */ + #define SRAM_RXF_HDRADDR_OFF 0 + #define SRAM_RXF_HDRADDR_BITS 12 + #define SRAM_RXF_TALADDR_OFF 16 + #define SRAM_RXF_TALADDR_BITS 12 + +#define REG_SRAM_RXF_NICLEN (0x1524) /* DWORD reg */ + +#define REG_SRAM_TXF (0x1528) /* DWORD reg */ + #define SRAM_TXF_HDRADDR_OFF 0 + #define SRAM_TXF_HDRADDR_BITS 12 + #define SRAM_TXF_TALADDR_OFF 16 + #define SRAM_TXF_TALADDR_BITS 12 + +#define REG_SRAM_TXF_NICLEN (0x152C) /* DWORD reg */ + +#define REG_SRAM_TCP_HDRADDR (0x1530) /* WORD reg */ + +#define REG_SRAM_PAT_HDRADDR (0x1532) /* WORD reg */ + +#define REG_SRAM_LOAD_PTR (0x1534) /* BYTE reg, sc */ + #define SRAM_LOAD_PTR_OFF 0 + #define SRAM_LOAD_PTR_BITS 1 + + +#define REG_RX_BASE_ADDR_HI (0x1540) /* DWORD reg */ + +#define REG_TX_BASE_ADDR_HI (0x1544) /* DWORD reg */ + +#define REG_SMB_BASE_ADDR_HI (0x1548) /* DWORD reg */ +#define REG_SMB_BASE_ADDR_LO (0x154C) /* DWORD reg */ + +#define REG_RFD0_HDRADDR_LO (0x1550) /* DWORD reg */ +#define REG_RFD1_HDRADDR_LO (0x1554) /* DWORD reg */ +#define REG_RFD2_HDRADDR_LO (0x1558) /* DWORD reg */ +#define REG_RFD3_HDRADDR_LO (0x155C) /* DWORD reg */ +#define REG_RFD_RING_SIZE (0x1560) /* DWORD reg */ +#define REG_RFD_BUFFER_SIZE (0x1564) /* DWORD reg */ + +#define REG_RRD0_HDRADDR_LO (0x1568) /* DWORD reg */ +#define REG_RRD1_HDRADDR_LO (0x156C) /* DWORD reg */ +#define REG_RRD2_HDRADDR_LO (0x1570) /* DWORD reg */ +#define REG_RRD3_HDRADDR_LO (0x1574) /* DWORD reg */ +#define REG_RRD_RING_SIZE (0x1578) /* DWORD reg */ + +#define REG_HTPD_HDRADDR_LO (0x157C) /* DWORD reg */ +#define REG_NTPD_HDRADDR_LO (0x1580) /* DWORD reg */ +#define REG_TPD_RING_SIZE (0x1584) /* DWORD reg */ + +#define REG_CMB_BASE_ADDR_LO (0x1588) /* DWORD reg */ + +#define REG_RSS_KEY0 (0x14B0) /* DWORD reg */ +#define REG_RSS_KEY1 (0x14B4) /* DWORD reg */ +#define REG_RSS_KEY2 (0x14B8) /* DWORD reg */ +#define REG_RSS_KEY3 (0x14BC) /* DWORD reg */ +#define REG_RSS_KEY4 (0x14C0) /* DWORD reg */ +#define REG_RSS_KEY5 (0x14C4) /* DWORD reg */ +#define REG_RSS_KEY6 (0x14C8) /* DWORD reg */ +#define REG_RSS_KEY7 (0x14CC) /* DWORD reg */ +#define REG_RSS_KEY8 (0x14D0) /* DWORD reg */ +#define REG_RSS_KEY9 (0x14D4) /* DWORD reg */ + +#define REG_RSS_IDT_TABLE0 (0x14E0) /* DWORD reg */ +#define REG_RSS_IDT_TABLE1 (0x14E4) /* DWORD reg */ +#define REG_RSS_IDT_TABLE2 (0x14E8) /* DWORD reg */ +#define REG_RSS_IDT_TABLE3 (0x14EC) /* DWORD reg */ +#define REG_RSS_IDT_TABLE4 (0x14F0) /* DWORD reg */ +#define REG_RSS_IDT_TABLE5 (0x14F4) /* DWORD reg */ +#define REG_RSS_IDT_TABLE6 (0x14F8) /* DWORD reg */ +#define REG_RSS_IDT_TABLE7 (0x14FC) /* DWORD reg */ + +#define REG_RSS_HASH_VAL (0x15B0) /* DWORD reg */ +#define REG_RSS_HASH_FLAG (0x15B4) /* DOWRD reg */ + +#define REG_RSS_BASE_CPU_NUMBER (0x15B8) /* DWORD reg */ + +#define REG_TXQ_CTRL (0x1590) /* DWORD reg */ + #define TXQ_CTRL_NUM_TPD_BURST_OFF 0 + #define TXQ_CTRL_NUM_TPD_BURST_BITS 4 + #define TXQ_CTRL_NUM_TPD_BURST_DEF 5 + #define TXQ_CTRL_IP_OPT_SP_OFF 4 + #define TXQ_CTRL_IP_OPT_SP_BITS 1 + #define TXQ_CTRL_EN_OFF 5 + #define TXQ_CTRL_EN_BITS 1 + #define TXQ_CTRL_MODE_OFF 6 + #define TXQ_CTRL_MODE_BITS 1 + #define TXQ_CTRL_MODE_ENH 1 + #define TXQ_CTRL_EN_SNAP_LSO_OFF 7 + #define TXQ_CTRL_EN_SNAP_LSO_BITS 1 + #define TXQ_CTRL_NUM_TXF_BURST_OFF 16 + #define TXQ_CTRL_NUM_TXF_BURST_BITS 16 + + +#define REG_TXQ_JUMBO_TSO_THRESHOLD (0x1594) /* DWORD reg */ + +#define REG_TXQ_TXF_BURST_L1 (0x1598) /* DWORD reg */ + #define TXQ_TXF_BURST_L1_LWM_OFF 0 + #define TXQ_TXF_BURST_L1_LWM_BITS 12 + #define TXQ_TXF_BURST_L1_HWM_OFF 16 + #define TXQ_TXF_BURST_L1_HWM_BITS 12 + #define TXQ_TXF_BURST_L1_EN_OFF 31 + #define TXQ_TXF_BURST_L1_EN_BITS 1 + + +#define REG_THRUPUT_MON_CTRL (0x159C) /* DWORD reg */ + #define THRUPUT_MON_CTRL_RATE_OFF 0 + #define THRUPUT_MON_CTRL_RATE_BITS 2 + + #define THRUPUT_MON_CTRL_EN_OFF 7 + #define THRUPUT_MON_CTRL_EN_BITS 1 + +#define REG_RXQ_CTRL (0x15A0) /* DWORD reg */ + #define RXQ_CTRL_ASPM_THRUPUT_LIM_OFF 0 + #define RXQ_CTRL_ASPM_THRUPUT_LIM_BITS 2 + #define RXQ_CTRL_ASPM_THRUPUT_LIM_NO 0 + #define RXQ_CTRL_ASPM_THRUPUT_LIM_1MB 1 + #define RXQ_CTRL_ASPM_THRUPUT_LIM_10MB 2 + #define RXQ_CTRL_ASPM_THRUPUT_LIM_100MB 3 + #define RXQ_CTRL_Q1_EN_OFF 4 + #define RXQ_CTRL_Q1_EN_BITS 1 + #define RXQ_CTRL_Q2_EN_OFF 5 + #define RXQ_CTRL_Q2_EN_BITS 1 + #define RXQ_CTRL_Q3_EN_OFF 6 + #define RXQ_CTRL_Q3_EN_BITS 1 + #define RXQ_CTRL_IPV6_XSUM_EN_OFF 7 + #define RXQ_CTRL_IPV6_XSUM_EN_BITS 1 + #define RXQ_CTRL_RSS_HASH_BITS_OFF 8 + #define RXQ_CTRL_RSS_HASH_BITS_BITS 8 + #define RXQ_CTRL_RSS_HASH_TYPE_IPV4_OFF 16 + #define RXQ_CTRL_RSS_HASH_TYPE_IPV4_TCP_OFF 17 + #define RXQ_CTRL_RSS_HASH_TYPE_IPV6_OFF 18 + #define RXQ_CTRL_RSS_HASH_TYPE_IPV6_TCP_OFF 19 + #define RXQ_CTRL_NUM_RFD_PREF_OFF 20 + #define RXQ_CTRL_NUM_RFD_PREF_BITS 6 + #define RXQ_CTRL_NUM_RFD_PREF_DEF 8 + #define RXQ_CTRL_RSS_MODE_OFF 26 + #define RXQ_CTRL_RSS_MODE_BITS 2 + #define RXQ_CTRL_RSS_MODE_DIS 0 + #define RXQ_CTRL_RSS_MODE_SQSI 1 + #define RXQ_CTRL_RSS_MODE_MQSI 2 + #define RXQ_CTRL_RSS_MODE_MQMI 3 + #define RXQ_CTRL_NIP_QUEUE_SEL_OFF 28 + #define RXQ_CTRL_NIP_QUEUE_SEL_BITS 1 + #define RXQ_CTRL_RSS_HASH_EN_OFF 29 + #define RXQ_CTRL_RSS_HASH_EN_BITS 1 + #define RXQ_CTRL_CUT_THRU_OFF 30 + #define RXQ_CTRL_CUT_THRU_BITS 1 + #define RXQ_CTRL_EN_OFF 31 + #define RXQ_CTRL_EN_BITS 1 + +#define REG_RFD_PREF_CTRL (0x15A4) + #define RFD_PREF_CTRL_UP_TH_OFF 0 + #define RFD_PREF_CTRL_UP_TH_BITS 6 + #define RFD_PREF_CTRL_UP_TH_DEF 16 + #define RFD_PREF_CTRL_LOW_TH_OFF 6 + #define RFD_PREF_CTRL_LOW_TH_BITS 6 + #define RFD_PREF_CTRL_LOW_TH_DEF 8 + + +#define REG_FC_RXF_HI (0x15A8) /* WORD reg */ +#define REG_FC_RXF_LO 0x15AA /* WORD reg */ + +#define REG_RXD_CTRL (0x15AC) /* DWORD reg */ + #define RXD_CTRL_THRESHOLD_OFF 0 + #define RXD_CTRL_THRESHOLD_BITS 12 + #define RXD_CTRL_TIMER_OFF 16 + #define RXD_CTRL_TIMER_BITS 16 + + +#define REG_DMA_CTRL (0x15C0) /* DWORD reg */ + #define DMA_CTRL_ORDER_MODE_OFF 0 + #define DMA_CTRL_ORDER_MODE_BITS 3 + #define DMA_CTRL_ORDER_MODE_IN 1 + #define DMA_CTRL_ORDER_MODE_ENH 2 + #define DMA_CTRL_ORDER_MODE_OUT 4 + #define DMA_CTRL_RCB_VAL_OFF 3 + #define DMA_CTRL_RCB_VAL_BITS 1 + #define DMA_CTRL_REGRDBLEN_OFF 4 + #define DMA_CTRL_REGRDBLEN_BITS 3 + #define DMA_CTRL_REGWRBLEN_OFF 7 + #define DMA_CTRL_REGWRBLEN_BITS 3 + #define DMA_CTRL_DMAR_REQ_PRI_OFF 10 + #define DMA_CTRL_DMAR_REQ_PRI_BITS 1 + #define DMA_CTRL_DMAR_DLY_CNT_OFF 11 + #define DMA_CTRL_DMAR_DLY_CNT_BITS 5 + #define DMA_CTRL_DMAR_DLY_CNT_DEF 15 + #define DMA_CTRL_DMAW_DLY_CNT_OFF 16 + #define DMA_CTRL_DMAW_DLY_CNT_BITS 4 + #define DMA_CTRL_DMAW_DLY_CNT_DEF 4 + #define DMA_CTRL_CMB_EN_OFF 20 + #define DMA_CTRL_CMB_EN_BITS 1 + #define DMA_CTRL_SMB_DMA_SP_OFF 21 /* enable SMB DMA */ + #define DMA_CTRL_SMB_DMA_SP_BITS 1 + #define DMA_CTRL_CMB_NOW_OFF 22 + #define DMA_CTRL_CMB_NOW_BITS 1 + #define DMA_CTRL_SMB_DIS_OFF 24 + #define DMA_CTRL_SMB_DIS_BITS 1 + #define DMA_CTRL_SMB_NOW_OFF 31 + #define DMA_CTRL_SMB_NOW_BITS 1 + +#define REG_SMB_DIS (0x15C3) /* BYTE reg */ + +#define REG_SMB_TIMER (0x15C4 ) /* DWORD reg */ + +#define REG_CMB_TPD_THRESHOLD (0x15C8) /* WORD reg */ +#define REG_CMB_TIMER (0x15CC) /* WORD reg */ + +#define REG_RFD0_PROD_INDEX (0x15E0) /* WORD reg */ +#define REG_RFD1_PROD_INDEX (0x15E4) /* WORD reg */ +#define REG_RFD2_PROD_INDEX (0x15E8) /* WORD reg */ +#define REG_RFD3_PROD_INDEX (0x15EC) /* WORD reg */ + +#define REG_HTPD_PROD_INDEX (0x15F0) /* WORD reg */ +#define REG_NTPD_PROD_INDEX (0x15F2) /* WORD reg */ +#define REG_HTPD_CONS_INDEX (0x15F4) /* WORD reg, ro */ +#define REG_NTPD_CONS_INDEX (0x15F6) /* WORD reg, ro */ + +#define REG_RFD0_CONS_INDEX (0x15F8) /* WORD reg, ro */ +#define REG_RFD1_CONS_INDEX (0x15FA) /* WORD reg, ro */ +#define REG_RFD2_CONS_INDEX (0x15FC) /* WORD reg, ro */ +#define REG_RFD3_CONS_INDEX (0x15FE) /* WORD reg, ro */ + +//register for ISR +#define REG_ISR (0x1600) /* DWORD reg */ + #define ISR_SMB_OFF 0 + + #define ISR_TIMER_OFF 1 + + #define ISR_SW_MANUAL_OFF 2 + #define ISR_SW_MANUAL_BITS 1 + #define ISR_RXF_OV_OFF (1<<3) + #define ISR_RXF_OV_BITS 1 + #define ISR_RFD0_UR_OFF (1<<4) + #define ISR_RFD0_UR_BITS 1 + #define ISR_RFD1_UR_OFF 5 + #define ISR_RFD1_UR_BITS 1 + #define ISR_RFD2_UR_OFF 6 + #define ISR_RFD2_UR_BITS 1 + #define ISR_RFD3_UR_OFF 7 + #define ISR_RFD3_UR_BITS 1 + #define ISR_TXF_UR_OFF 8 + #define ISR_TXF_UR_BITS 1 + #define ISR_DMAR_OFF (1<<9) + #define ISR_DMAR_BITS 1 + #define ISR_DMAW_OFF (1<<10) + #define ISR_DMAW_BITS 1 + #define ISR_TX_CREDIT_OFF 11 + #define ISR_TX_CREDIT_BITS 1 + #define ISR_GPHY_OFF (1<<12) //bit for PHY INT + #define ISR_GPHY_BITS 1 //bit for PHY INT + #define ISR_GPHY_LPW_OFF (1<<13) //bit for PHY INT + #define ISR_GPHY_LPW_BITS 1 + #define ISR_TXQ_OFF (1<<14) //bit for Tx INT + #define ISR_TXQ_BITS 1 + #define ISR_TX_PKT_OFF (1<<15) //bit for Tx INT + #define ISR_TX_PKT_BITS 1 + #define ISR_RX0_PKT_OFF (1<<16) //bit for Rx INT + #define ISR_RX0_PKT_BITS 1 //bit for Rx INT + #define ISR_RX1_PKT_OFF 17 //bit for Rx INT + #define ISR_RX1_PKT_BITS 1 //bit for Rx INT + #define ISR_RX2_PKT_OFF 18 //bit for Rx INT + #define ISR_RX2_PKT_BITS 1 //bit for Rx INT + #define ISR_RX3_PKT_OFF 19 //bit for Rx INT + #define ISR_RX3_PKT_BITS 1 //bit for Rx INT + #define ISR_MAC_RX_OFF 20 //bit for Rx INT + #define ISR_MAC_RX_BITS 1 //bit for Rx INT + #define ISR_MAC_TX_OFF 21 //bit for Tx INT + #define ISR_MAC_TX_BITS 1 + #define ISR_PCIE_UR_OFF 22 + #define ISR_PCIE_UR_BITS 1 + #define ISR_PCIE_FERR_OFF 23 + #define ISR_PCIE_FERR_BITS 1 + #define ISR_PCIE_NFERR_OFF 24 + #define ISR_PCIE_NFERR_BITS 1 + #define ISR_PCIE_CERR_OFF 25 + #define ISR_PCIE_CERR_BITS 1 + #define ISR_PCIE_LINKDOWN_OFF 26 + #define ISR_PCIE_LINKDOWN_BITS 1 + #define ISR_DIS_OFF 31 + #define ISR_DIS_BITS 1 + +#define REG_IMR (0x1604) /* DWORD reg */ + + +#define INT_FATAL_MASK (\ + FLAG(ISR_DMAR_OFF) |\ + FLAG(ISR_DMAW_OFF) |\ + FLAG(ISR_PCIE_FERR_OFF) |\ + FLAG(ISR_PCIE_LINKDOWN_OFF) ) + + +#define INT_TX_MASK (\ + FLAG(ISR_MAC_TX_OFF) |\ + FLAG(ISR_TX_PKT_OFF) |\ + FLAG(ISR_TXF_UR_OFF) ) + +#define INT_RX_MASK (\ + FLAG(ISR_RXF_OV_OFF) |\ + /* FLAG(ISR_RFD0_UR_OFF) |*/\ + FLAG(ISR_RFD1_UR_OFF) |\ + FLAG(ISR_RFD2_UR_OFF) |\ + FLAG(ISR_RFD3_UR_OFF) |\ + FLAG(ISR_RX0_PKT_OFF) |\ + FLAG(ISR_RX1_PKT_OFF) |\ + FLAG(ISR_RX2_PKT_OFF) |\ + FLAG(ISR_RX3_PKT_OFF) |\ + FLAG(ISR_MAC_RX_OFF) ) + +#define INT_MASK (\ + INT_RX_MASK |\ + INT_TX_MASK |\ + INT_FATAL_MASK |\ + FLAG(ISR_SMB_OFF) |\ + FLAG(ISR_SW_MANUAL_OFF) |\ + FLAG(ISR_GPHY_OFF) |\ + FLAG(ISR_GPHY_LPW_OFF) ) + + + + +#define REG_INT_RETRIG_TIMER (0x1608) /* WORD reg */ + +#define REG_HDS_CTRL (0x160C) /* DWORD reg */ + #define HDS_CTRL_EN_OFF 0 + #define HDS_CTRL_EN_BITS 1 + #define HDS_CTRL_BACKFILLSIZE_OFF 8 + #define HDS_CTRL_BACKFILLSIZE_BITS 12 + #define HDS_CTRL_MAX_HDRSIZE_OFF 20 + #define HDS_CTRL_MAX_HDRSIZE_BITS 12 + +#endif diff --git a/drivers/net/ethernet/ak-ethernet/Kconfig b/drivers/net/ethernet/ak-ethernet/Kconfig new file mode 100644 index 00000000..c014cfe9 --- /dev/null +++ b/drivers/net/ethernet/ak-ethernet/Kconfig @@ -0,0 +1,24 @@ +config AK_ETHERNET + tristate "Anyka Ethernet support" + depends on ARCH_AK39 + help + Anyka Ethernet device support + +choice + prompt "Anyka Ethernet support" + default ETHERNET_MAC_MII + depends on ARCH_AK39 + + config ETHERNET_MAC_MII + bool "MAC_MII" + depends on ARCH_AK39 + help + Then Ethernet mac mii interface. + + config ETHERNET_MAC_RMII + bool "MAC_RMII" + depends on ARCH_AK39 + help + Then Ethernet mac Rmii interface. + +endchoice diff --git a/drivers/net/ethernet/ak-ethernet/Makefile b/drivers/net/ethernet/ak-ethernet/Makefile new file mode 100644 index 00000000..f6c1f519 --- /dev/null +++ b/drivers/net/ethernet/ak-ethernet/Makefile @@ -0,0 +1,5 @@ + + +obj-$(CONFIG_AK_ETHERNET) += ak_ethernet.o + + diff --git a/drivers/net/ethernet/ak-ethernet/ak_ethernet.c b/drivers/net/ethernet/ak-ethernet/ak_ethernet.c new file mode 100644 index 00000000..aba013d0 --- /dev/null +++ b/drivers/net/ethernet/ak-ethernet/ak_ethernet.c @@ -0,0 +1,5404 @@ +/* + * Anyka MAC Fast Ethernet driver for Linux. + * Features + * Copyright (C) 2010 ANYKA + * AUTHOR Tang Anyang + * AUTHOR Zhang Jingyuan + * 10-11-01 09:08:08 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ak_ethernet.h" +#include "eth_ops.h" +#include "Ethernethw.h" +#include "phyhw.h" +#include + +#include // cdh:add for static const struct ethtool_ops +#define MACNAME "AK39E_MAC" +#define DRV_VERSION "1.1" +#define TPD_RING_SIZE 0x50 +#define RFD_RING_SIZE 0x50 +#define RRD_RING_SIZE 0x50 +#define MAC_FILE_NAME "MAC" +#define CTOI(c) (isdigit(c) ? (c - '0') : (c - 'A' + 10)) +#define MAC_ADDR_APSTRING_LEN 8 + +#define IPC_OFFLOAD 1 // cdh:for hardware checksum + +#if 0 +#define dbg(fmt, arg...) printk( "%s(%d): " fmt "\n", __func__, __LINE__, ##arg) +#define TR(fmt, arg...) printk( "%s(%d): " fmt "\n", __func__, __LINE__, ##arg) +#else +#define dbg(fmt, arg...) {} +#define TR(fmt, arg...) {} +#endif + + +// cdh:add +#define OTHER_END_PARTNER_AN_ABITY (0x1<<0) +#define SPEED_100M_FULL (0x8) +#define SPEED_100M_HALF (0x4) +#define SPEED_10M_FULL (0x2) +#define NAN_SPEED_10M_HALF ((0x0<<8)|(0x1<<6)) +#define NAN_SPEED_100M_HALF ((0x1<<8)|(0x0<<6)) + + +/*GMAC IP Base address and Size */ +u8 *iomap_base = NULL; +u32 iomap_size = 0; + +/*global adapter gmacdev pcidev and netdev pointers */ +nt_adapter *adapter_pt; +gmac_device *gmacdev_pt; +struct pci_dev *pcidev_pt; +struct net_device *netdev_pt; +void *BoardbufVa = NULL; /* virtual address for ring buf */ +dma_addr_t BoardbufPa; /* physical address for ring buf */ +void *DatabufVa = NULL; /* virtual address for ring buf */ +static struct timer_list timer_cable_unplug; +static u32 GMAC_Power_down; // This global variable is used to indicate the ISR whether the interrupts occured in the process of powering down the mac or not + +/* +The synopGMAC_wakeup_filter_config3[] is a sample configuration for wake up filter. +Filter1 is used here +Filter1 offset is programmed to 50 (0x32) +Filter1 mask is set to 0x000000FF, indicating First 8 bytes are used by the filter +Filter1 CRC= 0x7EED this is the CRC computed on data 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 +Refer accompanied software DWC_gmac_crc_example.c for CRC16 generation and how to use the same. +*/ +u32 synopGMAC_wakeup_filter_config3[] = +{ + 0x00000000, // For Filter0 CRC is not computed may be it is 0x0000 + 0x000000FF, // For Filter1 CRC is computed on 0,1,2,3,4,5,6,7 bytes from offset + 0x00000000, // For Filter2 CRC is not computed may be it is 0x0000 + 0x00000000, // For Filter3 CRC is not computed may be it is 0x0000 + 0x00000100, // Filter 0,2,3 are disabled, Filter 1 is enabled and filtering applies to only unicast packets + 0x00003200, // Filter 0,2,3 (no significance), filter 1 offset is 50 bytes from start of Destination MAC address + 0x7eED0000, // No significance of CRC for Filter0, Filter1 CRC is 0x7EED, + 0x00000000 // No significance of CRC for Filter2 and Filter3 +}; + +unsigned char *pMacBase = NULL; +unsigned char *psysbase; +void *RingbufVa = NULL; /* virtual address for ring buf */ +dma_addr_t RingbufPa; /* physical address for ring buf */ + +extern int eth_validate_addr(struct net_device *dev); +extern int ak_fha_init_for_update(int n); +static int save_ether_addr(unsigned char dev_addr[MAC_ADDR_LEN]); + +static void mac_release_sharepin_phyclk(void) +{ + // cdh:set chip system ctrl reg base address + psysbase = AK_VA_SYSCTRL; + if(psysbase == NULL) + { + printk("sysbase alloc error!"); + }else { + printk("sysbase alloc OK!"); + } + + /** cdh:release + *cdh:h2 bit [10:9]=01, set sharepin gpio47 share as opclk, old (3 << 2) + *cdh:restore enable gpio47 pull down, old (1 << 2) + */ + REG32(psysbase + 0x74) &= ~(3 << 10); + REG32(psysbase + 0x80) &= ~(0x1 << 8); // cdh:disable gpio47 pull down, old (1 << 2) + + /** cdh:release + *cdh:restore first mac interface select mii, mac_speed_cfg=1(100m) + *cdh:clear bit[15], prohibit 25m crystal + *cdh:clean set bit[16],enable div24, generate 25m, bit[18], select 25m clock of mac from pll div + *cdh:close mac ctrl clk + */ + REG32(psysbase + 0x14) &= ~((0x1 << 22)|(0x1 << 23)); + REG32(psysbase + 0x14) &= ~(0x1 << 15); + REG32(psysbase + 0x14) &= ~((0x1 << 16)|(0x1 << 18)); + REG32(psysbase + 0x1c) |= (1 << 13); +} + +static void mac_exit(struct net_device *ndev) +{ + nt_adapter *adapter; + int iosize; + adapter = (nt_adapter *)netdev_priv(ndev); + +#if defined(CONFIG_ETHERNET_MAC_RMII) + printk("Release MAC RMII interface!\n"); + ak_setpin_as_gpio(AK_GPIO_10); + ak_setpin_as_gpio(AK_GPIO_11); + ak_setpin_as_gpio(AK_GPIO_12); + ak_setpin_as_gpio(AK_GPIO_13); + ak_setpin_as_gpio(AK_GPIO_14); + ak_setpin_as_gpio(AK_GPIO_15); + ak_setpin_as_gpio(AK_GPIO_19); + ak_setpin_as_gpio(AK_GPIO_20); + ak_setpin_as_gpio(AK_GPIO_23); + ak_setpin_as_gpio(AK_GPIO_24); +#elif defined(CONFIG_ETHERNET_MAC_MII) + printk("Release MAC MII interface!\n"); + ak_setpin_as_gpio(AK_GPIO_10); + ak_setpin_as_gpio(AK_GPIO_11); + ak_setpin_as_gpio(AK_GPIO_12); + ak_setpin_as_gpio(AK_GPIO_13); + ak_setpin_as_gpio(AK_GPIO_14); + ak_setpin_as_gpio(AK_GPIO_15); + ak_setpin_as_gpio(AK_GPIO_16); + ak_setpin_as_gpio(AK_GPIO_17); + ak_setpin_as_gpio(AK_GPIO_18); + ak_setpin_as_gpio(AK_GPIO_19); + ak_setpin_as_gpio(AK_GPIO_20); + ak_setpin_as_gpio(AK_GPIO_21); + ak_setpin_as_gpio(AK_GPIO_22); + ak_setpin_as_gpio(AK_GPIO_23); + ak_setpin_as_gpio(AK_GPIO_24); + ak_setpin_as_gpio(AK_GPIO_76); + ak_setpin_as_gpio(AK_GPIO_77); + ak_setpin_as_gpio(AK_GPIO_78); +#else + printk("No Release MAC interface!\n"); +#endif + + mac_release_sharepin_phyclk(); + + // cdh:release platform device resource + if (adapter->db_pt->addr_req) { + iosize = resource_size(adapter->db_pt->addr_res); + release_mem_region(adapter->db_pt->addr_res->start, iosize); + } + + if (RingbufVa) { + dma_free_coherent(&ndev->dev, sizeof (gmac_device), RingbufVa, RingbufPa); + RingbufVa = NULL; + RingbufPa = 0; + } + + if (BoardbufVa) { + dma_free_coherent(&ndev->dev, sizeof (mac_info_t), BoardbufVa, BoardbufPa); + BoardbufVa = NULL; + BoardbufPa = 0; + } + +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +/* + *Used by netconsole + */ +static void ak_mac_poll_controller(struct net_device *ndev) +{ + +} + + +#endif + + + +/** + * This is a wrapper function for platform dependent delay + * Take care while passing the argument to this function + * note:cdh:check ok + */ +void plat_delay(u32 delay) +{ + while (delay--); + return; +} + +/** + * This is a wrapper function for consistent dma-able Memory allocation routine. + * In linux Kernel, it depends on pci dev structure + * @param[in] bytes in bytes to allocate + * note:cdh:check ok + */ +void *plat_alloc_consistent_mem(struct net_device *pmnetdev, u32 size, dma_addr_t *mac_dma_handle) +{ + void *pt = dma_alloc_coherent(NULL, size, mac_dma_handle, GFP_KERNEL); + return pt; +} + +/** + * This is a wrapper function for freeing consistent dma-able Memory. + * In linux Kernel, it depends on pci dev structure + * @param[in] bytes in bytes to allocate + * note:cdh:check ok + */ +void plat_free_consistent_mem(struct net_device *pmnetdev, u32 size, void *addr, dma_addr_t mac_dma_handle) +{ + dma_free_coherent(NULL, size, addr, mac_dma_handle); + return; +} + + + +/** + * The Low level function to read register contents from Hardware. + * + * @param[in] pointer to the base of register map + * @param[in] Offset from the base + * \return Returns the register contents + * note:cdh:check ok + */ +static u32 gmac_read_reg(u32 *RegBase, u32 RegOffset) +{ + + u32 addr = (u32)RegBase + RegOffset; + u32 data = readl((void *)addr); + //TR("%s RegBase = 0x%08x RegOffset = 0x%08x RegData = 0x%08x\n", __FUNCTION__, (u32)RegBase, RegOffset, data ); + return data; + +} + + +/** + * The Low level function to write to a register in Hardware. + * + * @param[in] pointer to the base of register map + * @param[in] Offset from the base + * @param[in] Data to be written + * \return void + * note:cdh:check ok + */ +static void gmac_write_reg(u32 *RegBase, u32 RegOffset, u32 RegData) +{ + + u32 addr = (u32)RegBase + RegOffset; + // TR("%s RegBase = 0x%08x RegOffset = 0x%08x RegData = 0x%08x\n", __FUNCTION__,(u32) RegBase, RegOffset, RegData ); + writel(RegData, (void *)addr); + return; +} + + + +/** + * The Low level function to clear bits of a register in Hardware. + * + * @param[in] pointer to the base of register map + * @param[in] Offset from the base + * @param[in] Bit mask to clear bits to logical 0 + * \return void + */ +static void gmac_clr_bits(u32 *RegBase, u32 RegOffset, u32 BitPos) +{ + u32 addr = (u32)RegBase + RegOffset; + u32 data = readl((void *)addr); + data &= (~BitPos); + // TR("%s !!!!!!!!!!!!!! RegOffset = 0x%08x RegData = 0x%08x\n", __FUNCTION__, RegOffset, data ); + writel(data, (void *)addr); + // TR("%s !!!!!!!!!!!!! RegOffset = 0x%08x RegData = 0x%08x\n", __FUNCTION__, RegOffset, data ); + return; +} + +/** + * The Low level function to set bits of a register in Hardware. + * + * @param[in] pointer to the base of register map + * @param[in] Offset from the base + * @param[in] Bit mask to set bits to logical 1 + * \return void + */ +static void gmac_set_bits(u32 *RegBase, u32 RegOffset, u32 BitPos) +{ + u32 addr = (u32)RegBase + RegOffset; + u32 data = readl((void *)addr); + data |= BitPos; + // TR("%s !!!!!!!!!!!!! RegOffset = 0x%08x RegData = 0x%08x\n", __FUNCTION__, RegOffset, data ); + writel(data, (void *)addr); + // TR("%s !!!!!!!!!!!!! RegOffset = 0x%08x RegData = 0x%08x\n", __FUNCTION__, RegOffset, data ); + return; +} + +/** + * Function to read the Phy register. The access to phy register + * is a slow process as the data is moved accross MDI/MDO interface + * @param[in] pointer to Register Base (It is the mac base in our case) . + * @param[in] PhyBase register is the index of one of supported 32 PHY devices. + * @param[in] Register offset is the index of one of the 32 phy register. + * @param[out] u16 data read from the respective phy register (only valid iff return value is 0). + * \return Returns 0 on success else return the error status. + * note:cdh:check ok + */ +static s32 gmac_read_phy_reg(u32 *RegBase, u32 PhyBase, u32 RegOffset, u16 *data) +{ + u32 addr; + u32 loop_variable; + + addr = ((PhyBase << GmiiDevShift) & GmiiDevMask) | ((RegOffset << GmiiRegShift) & GmiiRegMask); + addr = addr | GmiiCsrClk1 | GmiiBusy ; //Gmii busy bit + gmac_write_reg(RegBase, GmacGmiiAddr, addr); //write the address from where the data to be read in GmiiGmiiAddr register of synopGMAC ip + + for(loop_variable = 0; loop_variable < DEFAULT_LOOP_VARIABLE; loop_variable++) //Wait till the busy bit gets cleared with in a certain amount of time + { + if (!(gmac_read_reg(RegBase, GmacGmiiAddr) & GmiiBusy)) + { + //TR("mdio read:%d\n", RegOffset); + break; + } else { + ;//CDH:TR("mdio busy\n"); + } + plat_delay(DEFAULT_DELAY_VARIABLE); + } + + if(loop_variable < DEFAULT_LOOP_VARIABLE){ + *data = (u16)(gmac_read_reg(RegBase, GmacGmiiData) & 0xFFFF); + // printk("OK:read phy reg:%d, data:0x%x\n", RegOffset, *data); + }else{ + // TR("Error::: PHY not responding Busy bit didnot get cleared !!!!!!\n"); + return -ESYNOPGMACPHYERR; + } + + return -ESYNOPGMACNOERR; +} + +/** + * Function to write to the Phy register. The access to phy register + * is a slow process as the data is moved accross MDI/MDO interface + * @param[in] pointer to Register Base (It is the mac base in our case) . + * @param[in] PhyBase register is the index of one of supported 32 PHY devices. + * @param[in] Register offset is the index of one of the 32 phy register. + * @param[in] data to be written to the respective phy register. + * \return Returns 0 on success else return the error status. + * note:cdh:check ok + */ +static s32 gmac_write_phy_reg(u32 *RegBase, u32 PhyBase, u32 RegOffset, u16 data) +{ + u32 addr; + u32 loop_variable; + + gmac_write_reg(RegBase, GmacGmiiData, data); + + addr = ((PhyBase << GmiiDevShift) & GmiiDevMask) | ((RegOffset << GmiiRegShift) & GmiiRegMask) | GmiiWrite; + + addr = addr | GmiiCsrClk1 | GmiiBusy ; //set Gmii clk to 20-35 Mhz and Gmii busy bit + + gmac_write_reg(RegBase, GmacGmiiAddr, addr); + for(loop_variable = 0; loop_variable < DEFAULT_LOOP_VARIABLE; loop_variable++) + { + if (!(gmac_read_reg(RegBase, GmacGmiiAddr) & GmiiBusy)) + { + // TR("mdio write: %x,%x\n", RegOffset, data); + break; + } else { + ; // TR("mdio busy\n"); + } + plat_delay(DEFAULT_DELAY_VARIABLE); + } + + if(loop_variable < DEFAULT_LOOP_VARIABLE) + { + return -ESYNOPGMACNOERR; + } + else + { + //TR("Error::: PHY not responding Busy bit didnot get cleared !!!!!!\n"); + return -ESYNOPGMACPHYERR; + } +} + + +// cdh:Mac Management Counters (MMC), cdh:check ok +void gmac_mmc_counters_reset(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacMmcCntrl, GmacMmcCounterReset); + return; +} + +/** + * Configures the MMC to stop rollover. + * Programs MMC interface so that counters will not rollover after reaching maximum value. + * @param[in] pointer to gmac_device. + * return returns void. + * note:cdh:check ok + */ +void gmac_mmc_counters_disable_rollover(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacMmcCntrl, GmacMmcCounterStopRollover); + return; +} + +/** + * Read the MMC Rx interrupt status. + * @param[in] pointer to gmac_device. + * \return returns the Rx interrupt status. + */ +u32 gmac_read_mmc_rx_int_status(gmac_device *gmacdev) +{ + return (gmac_read_reg((u32 *)gmacdev->MacBase, GmacMmcIntrRx)); +} + +/** + * Read the MMC Tx interrupt status. + * @param[in] pointer to gmac_device. + * \return returns the Tx interrupt status. + */ +u32 gmac_read_mmc_tx_int_status(gmac_device *gmacdev) +{ + return (gmac_read_reg((u32 *)gmacdev->MacBase, GmacMmcIntrTx)); +} + + +/** + * Function to reset the GMAC core. + * This reests the DMA and GMAC core. After reset all the registers holds their respective reset value + * @param[in] pointer to gmac_device. + * \return 0 on success else return the error status. + * note:cdh:check ok + */ +s32 gmac_reset(gmac_device *gmacdev) +{ + u32 data = 0; + s32 reset_cnt = 0xFFFF; + + // cdh:software reset , the resets all of the GMAC internal registers and logic + gmac_write_reg((u32 *)gmacdev->DmaBase, DmaBusMode , DmaResetOn); // cdh:reset dma engine + plat_delay(DEFAULT_LOOP_VARIABLE); + while(reset_cnt>0){ + data = gmac_read_reg((u32 *)gmacdev->DmaBase, DmaBusMode); + // cdh:after finish become 0 + if((data & DmaResetOn) != DmaResetOn) { + break; + } + reset_cnt--; + } + + if (reset_cnt <= 0) { + printk("No find phy small board!\n"); + return -1; + } + + return 0; +} + +/** + * Sets the Mac address in to GMAC register. + * This function sets the MAC address to the MAC register in question. + * @param[in] pointer to gmac_device to populate mac dma and phy addresses. + * @param[in] Register offset for Mac address high + * @param[in] Register offset for Mac address low + * @param[in] buffer containing mac address to be programmed. + * \return 0 upon success. Error code upon failure. + * \note:cdh:check ok + */ +static s32 gmac_set_mac_addr(gmac_device *gmacdev, u32 MacHigh, u32 MacLow, u8 *MacAddr) +{ + u32 data; + + data = (MacAddr[5] << 8) | MacAddr[4]; + gmac_write_reg((u32 *)gmacdev->MacBase, MacHigh, data); + data = (MacAddr[3] << 24) | (MacAddr[2] << 16) | (MacAddr[1] << 8) | MacAddr[0] ; + gmac_write_reg((u32 *)gmacdev->MacBase, MacLow, data); + return 0; +} + +/** + * Get the Mac address in to the address specified. + * The mac register contents are read and written to buffer passed. + * @param[in] pointer to gmac_device to populate mac dma and phy addresses. + * @param[in] Register offset for Mac address high + * @param[in] Register offset for Mac address low + * @param[out] buffer containing the device mac address. + * \return 0 upon success. Error code upon failure. + * \note:cdh:check ok + */ +static s32 gmac_get_mac_addr(gmac_device *gmacdev, u32 MacHigh, u32 MacLow, u8 *MacAddr) +{ + u32 data; + + data = gmac_read_reg((u32 *)gmacdev->MacBase, MacHigh); + MacAddr[5] = (data >> 8) & 0xff; + MacAddr[4] = (data) & 0xff; + + data = gmac_read_reg((u32 *)gmacdev->MacBase, MacLow); + MacAddr[3] = (data >> 24) & 0xff; + MacAddr[2] = (data >> 16) & 0xff; + MacAddr[1] = (data >> 8 ) & 0xff; + MacAddr[0] = (data ) & 0xff; + + return 0; +} + + +/** + * Attaches the synopGMAC device structure to the hardware. + * Device structure is populated with MAC/DMA and PHY base addresses. + * @param[in] pointer to gmac_device to populate mac dma and phy addresses. + * @param[in] GMAC IP mac base address. + * @param[in] GMAC IP dma base address. + * @param[in] GMAC IP phy base address. + * \return 0 upon success. Error code upon failure. + * \note This is important function. No kernel api provided by Synopsys + * note:cdh:check ok + */ +static s32 gmac_attach(gmac_device *gmacdev, u32 macBase, u32 dmaBase, u32 phyBase) +{ +#if 0 + u8 mac_addr0[6] = DEFAULT_MAC_ADDRESS; + u8 testmac_addr0[6] = {0}; + u8 i = 0; +#endif + + TR(" into %s, %d, %s \n", __FILE__, __LINE__, __FUNCTION__); + TR("1"); + + /*Make sure the Device data strucure is cleared before we proceed further*/ + memset((void *) gmacdev, 0, sizeof(gmac_device)); + /*Populate the mac and dma base addresses*/ + gmacdev->MacBase = macBase; + gmacdev->DmaBase = dmaBase; + gmacdev->PhyBase = phyBase; + dbg("2"); + +#if 0 + /* cdh:Program/flash in the station/IP's Mac address */ + gmac_set_mac_addr(gmacdev, GmacAddr0High, GmacAddr0Low, mac_addr0); + + /* cdh:Lets set ipaddress in to device structure, cdh:check ok*/ + gmac_get_mac_addr(gmacdev, GmacAddr0High, GmacAddr0Low, testmac_addr0); + for (i=0; i<6; i++) { + TR("cdh:mac_addr:%d:0x%x\n", i, testmac_addr0[i]); + } +#endif + + TR(" out %s, %d, %s \n", __FILE__, __LINE__, __FUNCTION__); + + return 0; +} + +/** + * Function to read the GMAC IP Version and populates the same in device data structure. + * @param[in] pointer to gmac_device. + * \return Always return 0. + * \note:cdh:check ok + */ + +s32 gmac_read_version(gmac_device *gmacdev) +{ + u32 data = 0; + + printk("gmac version register %x\n", (gmacdev->MacBase + GmacVersion)); + // cdh:available the mac ip version + data = gmac_read_reg((u32 *)gmacdev->MacBase, GmacVersion ); + gmacdev->Version = data; + printk("The data read from %08x is %08x\n", (gmacdev->MacBase + GmacVersion), data); + return 0; +} + + + +/** + * Enable all the interrupts. + * Enables the DMA interrupt as specified by the bit mask. + * @param[in] pointer to gmac_device. + * @param[in] bit mask of interrupts to be enabled. + * \return returns void. + * note:cdh:check ok + */ +static void gmac_enable_interrupt(gmac_device *gmacdev, u32 interrupts) +{ + gmac_write_reg((u32 *)gmacdev->DmaBase, DmaInterrupt, interrupts); + return; +} + + +/** + * Disable all the interrupts. + * Disables all DMA interrupts. + * @param[in] pointer to gmac_device. + * \return returns void. + * \note This function disabled all the interrupts, if you want to disable a particular interrupt then + * use gmac_disable_interrupt(). + * note:cdh:check ok + */ +void gmac_disable_interrupt_all(gmac_device *gmacdev) +{ + gmac_write_reg((u32 *)gmacdev->DmaBase, DmaInterrupt, DmaIntDisable); + return; +} + + +/** + * Checks whether the packet received is a magic packet?. + * @param[in] pointer to gmac_device. + * \return returns True if magic packet received else returns false. + */ +bool gmac_is_magic_packet_received(gmac_device *gmacdev) +{ + u32 data; + data = gmac_read_reg((u32 *)gmacdev->MacBase, GmacPmtCtrlStatus); + return((data & GmacPmtMagicPktReceived) == GmacPmtMagicPktReceived); +} + +/** + * Checks whether the packet received is a wakeup frame?. + * @param[in] pointer to gmac_device. + * \return returns true if wakeup frame received else returns false. + */ +bool gmac_is_wakeup_frame_received(gmac_device *gmacdev) +{ + u32 data; + data = gmac_read_reg((u32 *)gmacdev->MacBase, GmacPmtCtrlStatus); + return((data & GmacPmtWakeupFrameReceived) == GmacPmtWakeupFrameReceived); +} + +/** + * Enables the assertion of PMT interrupt. + * This enables the assertion of PMT interrupt due to Magic Pkt or Wakeup frame + * reception. + * @param[in] pointer to gmac_device. + * \return returns void. + */ +void gmac_pmt_int_enable(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacInterruptMask, GmacPmtIntMask); + return; +} +/** + * Disables the assertion of PMT interrupt. + * This disables the assertion of PMT interrupt due to Magic Pkt or Wakeup frame + * reception. + * @param[in] pointer to gmac_device. + * \return returns void. + */ +void gmac_pmt_int_disable(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacInterruptMask, GmacPmtIntMask); + return; +} + +/** + * Enable the reception of frames on GMII/MII. + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ +static void gmac_rx_enable(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacRx); + return; +} +/** + * Disable the reception of frames on GMII/MII. + * GMAC receive state machine is disabled after completion of reception of current frame. + * @param[in] pointer to gmac_device. + * \return returns void. + */ +void gmac_rx_disable(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacRx); + return; +} + +/** + * Enable the DMA Reception. + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ +static void gmac_enable_dma_rx(gmac_device *gmacdev) +{ + // gmac_set_bits((u32 *)gmacdev->DmaBase, DmaControl, DmaRxStart); + u32 data; + data = gmac_read_reg((u32 *)gmacdev->DmaBase, DmaControl); + data |= DmaRxStart; + gmac_write_reg((u32 *)gmacdev->DmaBase, DmaControl , data); + +} + +/** + * Enable the DMA Transmission. + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ +static void gmac_enable_dma_tx(gmac_device *gmacdev) +{ + // gmac_set_bits((u32 *)gmacdev->DmaBase, DmaControl, DmaTxStart); + u32 data; + data = gmac_read_reg((u32 *)gmacdev->DmaBase, DmaControl); + data |= DmaTxStart; + gmac_write_reg((u32 *)gmacdev->DmaBase, DmaControl , data); + +} + +/** + * Enable the transmission of frames on GMII/MII. + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ +static void gmac_tx_enable(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacTx); + return; +} +/** + * Disable the transmission of frames on GMII/MII. + * GMAC transmit state machine is disabled after completion of transmission of current frame. + * @param[in] pointer to gmac_device. + * \return returns void. + */ +void gmac_tx_disable(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacTx); + return; +} + + + + + +static void powerup_mac(gmac_device *gmacdev) +{ + GMAC_Power_down = 0; // Let ISR know that MAC is out of power down now + if(gmac_is_magic_packet_received(gmacdev)) + TR("GMAC wokeup due to Magic Pkt Received\n"); + if(gmac_is_wakeup_frame_received(gmacdev)) + TR("GMAC wokeup due to Wakeup Frame Received\n"); + //Disable the assertion of PMT interrupt + gmac_pmt_int_disable(gmacdev); + //Enable the mac and Dma rx and tx paths + gmac_rx_enable(gmacdev); + gmac_enable_dma_rx(gmacdev); + + gmac_tx_enable(gmacdev); + gmac_enable_dma_tx(gmacdev); + return; +} + + +/** + * Returns the all unmasked interrupt status after reading the DmaStatus register. + * @param[in] pointer to gmac_device. + * \return 0 upon success. Error code upon failure. + */ +u32 gmac_get_interrupt_type(gmac_device *gmacdev) +{ + u32 data; + u32 interrupts = 0; + data = gmac_read_reg((u32 *)gmacdev->DmaBase, DmaStatus); + gmac_write_reg((u32 *)gmacdev->DmaBase, DmaStatus , data); //This is the appropriate location to clear the interrupts + TR("DMA status reg is %08x\n", data); + if(data & DmaIntErrorMask) interrupts |= synopGMACDmaError; + if(data & DmaIntRxNormMask) interrupts |= synopGMACDmaRxNormal; + if(data & DmaIntRxAbnMask) interrupts |= synopGMACDmaRxAbnormal; + if(data & DmaIntRxStoppedMask) interrupts |= synopGMACDmaRxStopped; + if(data & DmaIntTxNormMask) interrupts |= synopGMACDmaTxNormal; + if(data & DmaIntTxAbnMask) interrupts |= synopGMACDmaTxAbnormal; + if(data & DmaIntTxStoppedMask) interrupts |= synopGMACDmaTxStopped; + + return interrupts; +} + + +#ifdef IPC_OFFLOAD +/** + * Enables the ip checksum offloading in receive path. + * When set GMAC calculates 16 bit 1's complement of all received ethernet frame payload. + * It also checks IPv4 Header checksum is correct. GMAC core appends the 16 bit checksum calculated + * for payload of IP datagram and appends it to Ethernet frame transferred to the application. + * @param[in] pointer to synopGMACdevice. + * \return returns void. + */ +static void synopGMAC_enable_rx_chksum_offload(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase,GmacConfig,GmacRxIpcOffload); + return; +} + +/** + * Instruct the DMA to drop the packets fails tcp ip checksum. + * This is to instruct the receive DMA engine to drop the recevied packet if they + * fails the tcp/ip checksum in hardware. Valid only when full checksum offloading is enabled(type-2). + * @param[in] pointer to synopGMACdevice. + * \return returns void. + */ +static void synopGMAC_rx_tcpip_chksum_drop_enable(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->DmaBase,DmaControl,DmaDisableDropTcpCs); + return; +} + +#endif + +#ifdef IPC_OFFLOAD +/** + * Checks if any Ipv4 header checksum error in the frame just transmitted. + * This serves as indication that error occureed in the IPv4 header checksum insertion. + * The sent out frame doesnot carry any ipv4 header checksum inserted by the hardware. + * @param[in] pointer to synopGMACdevice. + * @param[in] u32 status field of the corresponding descriptor. + * \return returns true if error in ipv4 header checksum, else returns false. + */ +static bool synopGMAC_is_tx_ipv4header_checksum_error(gmac_device *gmacdev, u32 status) +{ + return((status & DescTxIpv4ChkError) == DescTxIpv4ChkError); +} + +/** + * Checks if any payload checksum error in the frame just transmitted. + * This serves as indication that error occureed in the payload checksum insertion. + * The sent out frame doesnot carry any payload checksum inserted by the hardware. + * @param[in] pointer to synopGMACdevice. + * @param[in] u32 status field of the corresponding descriptor. + * \return returns true if error in ipv4 header checksum, else returns false. + */ +static bool synopGMAC_is_tx_payload_checksum_error(gmac_device *gmacdev, u32 status) +{ + return((status & DescTxPayChkError) == DescTxPayChkError); +} +#endif + + +#ifdef IPC_OFFLOAD + +/** + * Decodes the Rx Descriptor status to various checksum error conditions. + * @param[in] pointer to synopGMACdevice. + * @param[in] u32 status field of the corresponding descriptor. + * \return returns decoded enum (u32) indicating the status. + */ +u32 synopGMAC_is_rx_checksum_error(gmac_device *gmacdev, u32 status) +{ + if (((status & DescRxChkBit5) == 0) && ((status & DescRxChkBit7) == 0) && ((status & DescRxChkBit0) == 0)) + return RxLenLT600; //// + else if(((status & DescRxChkBit5) == 0) && ((status & DescRxChkBit7) == 0) && ((status & DescRxChkBit0) != 0)) + return RxIpHdrPayLoadChkBypass;//// + else if(((status & DescRxChkBit5) == 0) && ((status & DescRxChkBit7) != 0) && ((status & DescRxChkBit0) != 0)) + return RxChkBypass; //// + else if(((status & DescRxChkBit5) != 0) && ((status & DescRxChkBit7) == 0) && ((status & DescRxChkBit0) == 0)) + return RxNoChkError;//4// + else if(((status & DescRxChkBit5) != 0) && ((status & DescRxChkBit7) == 0) && ((status & DescRxChkBit0) != 0)) + return RxPayLoadChkError;//// + else if(((status & DescRxChkBit5) != 0) && ((status & DescRxChkBit7) != 0) && ((status & DescRxChkBit0) == 0)) + return RxIpHdrChkError; //6 + else if(((status & DescRxChkBit5) != 0) && ((status & DescRxChkBit7) != 0) && ((status & DescRxChkBit0) != 0)) + return RxIpHdrPayLoadChkError; + else + return RxIpHdrPayLoadRes; +} +#endif + + +/** + * Disable the DMA for Transmission. + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ + +void gmac_disable_dma_tx(gmac_device *gmacdev) +{ + // gmac_clr_bits((u32 *)gmacdev->DmaBase, DmaControl, DmaTxStart); + u32 data; + data = gmac_read_reg((u32 *)gmacdev->DmaBase, DmaControl); + data &= (~DmaTxStart); + gmac_write_reg((u32 *)gmacdev->DmaBase, DmaControl , data); +} + +/** + * Disable the DMA for Reception. + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ +void gmac_disable_dma_rx(gmac_device *gmacdev) +{ + // gmac_clr_bits((u32 *)gmacdev->DmaBase, DmaControl, DmaRxStart); + u32 data; + data = gmac_read_reg((u32 *)gmacdev->DmaBase, DmaControl); + data &= (~DmaRxStart); + gmac_write_reg((u32 *)gmacdev->DmaBase, DmaControl , data); +} + + +/** + * Checks whether this rx descriptor is in chain mode. + * This returns true if it is this descriptor is in chain mode. + * @param[in] pointer to DmaDesc structure. + * \return returns true if chain mode is set, false if not. + * note:cdh:check ok + */ +bool gmac_is_rx_desc_chained(DmaDesc *desc) +{ + return((desc->length & RxDescChain) == RxDescChain); +} + +/** + * Checks whether this tx descriptor is in chain mode. + * This returns true if it is this descriptor is in chain mode. + * @param[in] pointer to DmaDesc structure. + * \return returns true if chain mode is set, false if not. + */ +bool gmac_is_tx_desc_chained(DmaDesc *desc) +{ +#ifdef ENH_DESC + return((desc->status & TxDescChain) == TxDescChain); +#else + return((desc->length & TxDescChain) == TxDescChain); +#endif +} + +/** + * Take ownership of this Descriptor. + * The function is same for both the ring mode and the chain mode DMA structures. + * @param[in] pointer to gmac_device. + * \return returns void. + */ +void gmac_take_desc_ownership(DmaDesc *desc) +{ + if(desc) + { + desc->status &= ~DescOwnByDma; //Clear the DMA own bit + //desc->status |= DescError; // Set the error to indicate this descriptor is bad + } +} + + + +/** + * Take ownership of all the rx Descriptors. + * This function is called when there is fatal error in DMA transmission. + * When called it takes the ownership of all the rx descriptor in rx descriptor pool/queue from DMA. + * The function is same for both the ring mode and the chain mode DMA structures. + * @param[in] pointer to gmac_device. + * \return returns void. + * \note Make sure to disable the transmission before calling this function, otherwise may result in racing situation. + * note:cdh:check ok + */ +void gmac_take_desc_ownership_rx(gmac_device *gmacdev) +{ + s32 i; + DmaDesc *desc; + desc = gmacdev->RxDesc; + for(i = 0; i < gmacdev->RxDescCount; i++) + { + if(gmac_is_rx_desc_chained(desc)) //This descriptor is in chain mode + { + gmac_take_desc_ownership(desc); + desc = (DmaDesc *)desc->data2; + } + else + { + gmac_take_desc_ownership(desc + i); + } + } +} + +/** + * Take ownership of all the rx Descriptors. + * This function is called when there is fatal error in DMA transmission. + * When called it takes the ownership of all the tx descriptor in tx descriptor pool/queue from DMA. + * The function is same for both the ring mode and the chain mode DMA structures. + * @param[in] pointer to gmac_device. + * \return returns void. + * \note Make sure to disable the transmission before calling this function, otherwise may result in racing situation. + * note:cdh:check ok + */ +void gmac_take_desc_ownership_tx(gmac_device *gmacdev) +{ + s32 i; + DmaDesc *desc; + desc = gmacdev->TxDesc; + for(i = 0; i < gmacdev->TxDescCount; i++) + { + if(gmac_is_tx_desc_chained(desc)) //This descriptor is in chain mode + { + gmac_take_desc_ownership(desc); + desc = (DmaDesc *)desc->data2; + } + else + { + gmac_take_desc_ownership(desc + i); + } + } + +} + +/** + * Initialize the rx descriptors for ring or chain mode operation. + * - Status field is initialized to 0. + * - EndOfRing set for the last descriptor. + * - buffer1 and buffer2 set to 0 for ring mode of operation. (note) + * - data1 and data2 set to 0. (note) + * @param[in] pointer to DmaDesc structure. + * @param[in] whether end of ring + * \return void. + * \note Initialization of the buffer1, buffer2, data1,data2 and status are not done here. This only initializes whether one wants to use this descriptor + * in chain mode or ring mode. For chain mode of operation the buffer2 and data2 are programmed before calling this function. + */ +void gmac_rx_desc_init_ring(DmaDesc *desc, bool last_ring_desc) +{ + desc->status = 0; + desc->length = last_ring_desc ? RxDescEndOfRing : 0; + desc->buffer1 = 0; + desc->buffer2 = 0; + desc->data1 = 0; + desc->data2 = 0; + return; +} +/** + * Initialize the tx descriptors for ring or chain mode operation. + * - Status field is initialized to 0. + * - EndOfRing set for the last descriptor. + * - buffer1 and buffer2 set to 0 for ring mode of operation. (note) + * - data1 and data2 set to 0. (note) + * @param[in] pointer to DmaDesc structure. + * @param[in] whether end of ring + * \return void. + * \note Initialization of the buffer1, buffer2, data1,data2 and status are not done here. This only initializes whether one wants to use this descriptor + * in chain mode or ring mode. For chain mode of operation the buffer2 and data2 are programmed before calling this function. + * note:cdh:check ok + */ +void gmac_tx_desc_init_ring(DmaDesc *desc, bool last_ring_desc) +{ +#ifdef ENH_DESC + desc->status = last_ring_desc ? TxDescEndOfRing : 0; + desc->length = 0; +#else + desc->status = 0; // added by panqihe 2014-10-30 + desc->length = last_ring_desc ? TxDescEndOfRing : 0; // cdh:TxDescEndOfRing bit25(TER) +#endif + desc->buffer1 = 0; + desc->buffer2 = 0; + desc->data1 = 0; + desc->data2 = 0; + return; +} + +s32 gmac_init_tx_rx_desc_queue(gmac_device *gmacdev) +{ + s32 i; + for(i = 0; i < gmacdev->TxDescCount; i++) + { + gmac_tx_desc_init_ring(gmacdev->TxDesc + i, i == gmacdev->TxDescCount - 1); + } + TR("At line %d\n", __LINE__); + for(i = 0; i < gmacdev->RxDescCount; i++) + { + gmac_rx_desc_init_ring(gmacdev->RxDesc + i, i == gmacdev->RxDescCount - 1); + } + + gmacdev->TxNext = 0; + gmacdev->TxBusy = 0; + gmacdev->RxNext = 0; + gmacdev->RxBusy = 0; + + return -ESYNOPGMACNOERR; +} + +/** + * Function to program DMA bus mode register. + * + * The Bus Mode register is programmed with the value given. The bits to be set are + * bit wise or'ed and sent as the second argument to this function. + * @param[in] pointer to gmac_device. + * @param[in] the data to be programmed. + * \return 0 on success else return the error status. + * note:cdh:check ok + */ +static s32 gmac_set_dma_bus_mode(gmac_device *gmacdev, u32 init_value ) +{ + gmac_write_reg((u32 *)gmacdev->DmaBase, DmaBusMode , init_value); + return 0; + +} + +/** + * Function to program DMA Control register. + * + * The Dma Control register is programmed with the value given. The bits to be set are + * bit wise or'ed and sent as the second argument to this function. + * @param[in] pointer to gmac_device. + * @param[in] the data to be programmed. + * \return 0 on success else return the error status. + * note:cdh:check ok + */ +static s32 gmac_set_dma_control(gmac_device *gmacdev, u32 init_value) +{ + gmac_write_reg((u32 *)gmacdev->DmaBase, DmaControl, init_value); + return 0; +} + +/** + * Programs the DmaRxBaseAddress with the Rx descriptor base address. + * Rx Descriptor's base address is available in the gmacdev structure. This function progrms the + * Dma Rx Base address with the starting address of the descriptor ring or chain. + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ +static void gmac_init_rx_desc_base(gmac_device *gmacdev) +{ + u32 rx_desc_addr; + + // cdh:write RxDescDma consistent physic address to mac dma ctrl reg + gmac_write_reg((u32 *)gmacdev->DmaBase, DmaRxBaseAddr, (u32)gmacdev->RxDescDma); + printk("rx descriptor addr[w]: %x\n", gmacdev->RxDescDma); + rx_desc_addr = gmac_read_reg((u32 *)gmacdev->DmaBase, DmaRxBaseAddr); + printk("rx descriptor addr[r]: %x\n", rx_desc_addr); + return; +} + +/** + * Programs the DmaTxBaseAddress with the Tx descriptor base address. + * Tx Descriptor's base address is available in the gmacdev structure. This function progrms the + * Dma Tx Base address with the starting address of the descriptor ring or chain. + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ +static void gmac_init_tx_desc_base(gmac_device *gmacdev) +{ + // cdh:write TxDescDma consistent physic address to mac dma ctrl reg + gmac_write_reg((u32 *)gmacdev->DmaBase, DmaTxBaseAddr, (u32)gmacdev->TxDescDma); + return; +} + +/*Gmac configuration functions*/ + +/** + * Enable the watchdog timer on the receiver. + * When enabled, Gmac enables Watchdog timer, and GMAC allows no more than + * 2048 bytes of data (10,240 if Jumbo frame enabled). + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ +static void gmac_wd_enable(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacWatchdog); + return; +} +/** + * Disable the watchdog timer on the receiver. + * When disabled, Gmac disabled watchdog timer, and can receive frames up to + * 16,384 bytes. + * @param[in] pointer to gmac_device. + * \return returns void. + */ +void gmac_wd_disable(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacWatchdog); + return; +} + +/** + * Enables the Jabber frame support. + * When enabled, GMAC disabled the jabber timer, and can transfer 16,384 byte frames. + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ +static void gmac_jab_enable(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacJabber); + return; +} +/** + * Disables the Jabber frame support. + * When disabled, GMAC enables jabber timer. It cuts of transmitter if application + * sends more than 2048 bytes of data (10240 if Jumbo frame enabled). + * @param[in] pointer to gmac_device. + * \return returns void. + */ +void gmac_jab_disable(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacJabber); + return; +} + +/** + * Enables Frame bursting (Only in Half Duplex Mode). + * When enabled, GMAC allows frame bursting in GMII Half Duplex mode. + * Reserved in 10/100 and Full-Duplex configurations. + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ +static void gmac_frame_burst_enable(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacFrameBurst); + return; +} +/** + * Disables Frame bursting. + * When Disabled, frame bursting is not supported. + * @param[in] pointer to gmac_device. + * \return returns void. + */ +void gmac_frame_burst_disable(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacFrameBurst); + return; +} + +/** + * Enable Jumbo frame support. + * When Enabled GMAC supports jumbo frames of 9018/9022(VLAN tagged). + * Giant frame error is not reported in receive frame status. + * @param[in] pointer to gmac_device. + * \return returns void. + */ +void gmac_jumbo_frame_enable(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacJumboFrame); + return; +} +/** + * Disable Jumbo frame support. + * When Disabled GMAC does not supports jumbo frames. + * Giant frame error is reported in receive frame status. + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ +static void gmac_jumbo_frame_disable(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacJumboFrame); + return; +} + +/** + * Enables Receive Own bit (Only in Half Duplex Mode). + * When enaled GMAC receives all the packets given by phy while transmitting. + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ +static void gmac_rx_own_enable(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacRxOwn); + return; +} +/** + * Disables Receive Own bit (Only in Half Duplex Mode). + * When enaled GMAC disables the reception of frames when gmii_txen_o is asserted. + * @param[in] pointer to gmac_device. + * \return returns void. + */ +void gmac_rx_own_disable(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacRxOwn); + return; +} + +/** + * Sets the GMAC in loopback mode. + * When on GMAC operates in loop-back mode at GMII/MII. + * @param[in] pointer to gmac_device. + * \return returns void. + * \note (G)MII Receive clock is required for loopback to work properly, as transmit clock is + * not looped back internally. + */ +void gmac_loopback_on(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacLoopback); + return; +} +/** + * Sets the GMAC in Normal mode. + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ +static void gmac_loopback_off(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacLoopback); + return; +} + +/** + * Sets the GMAC core in Full-Duplex mode. + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ +static void gmac_set_full_duplex(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacDuplex); + return; +} +/** + * Sets the GMAC core in Half-Duplex mode. + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ +static void gmac_set_half_duplex(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacDuplex); + return; +} + +/** + * GMAC tries retransmission (Only in Half Duplex mode). + * If collision occurs on the GMII/MII, GMAC attempt retries based on the + * back off limit configured. + * @param[in] pointer to gmac_device. + * \return returns void. + * \note This function is tightly coupled with gmac_back_off_limit(gmacdev_pt *, u32). + * note:cdh:check ok + */ +static void gmac_retry_enable(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacRetry); + return; +} +/** + * GMAC tries only one transmission (Only in Half Duplex mode). + * If collision occurs on the GMII/MII, GMAC will ignore the current frami + * transmission and report a frame abort with excessive collision in tranmit frame status. + * @param[in] pointer to gmac_device. + * \return returns void. + */ +void gmac_retry_disable(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacRetry); + return; +} + +/** + * GMAC strips the Pad/FCS field of incoming frames. + * This is true only if the length field value is less than or equal to + * 1500 bytes. All received frames with length field greater than or equal to + * 1501 bytes are passed to the application without stripping the Pad/FCS field. + * @param[in] pointer to gmac_device. + * \return returns void. + */ +void gmac_pad_crc_strip_enable(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacPadCrcStrip); + return; +} +/** + * GMAC doesnot strips the Pad/FCS field of incoming frames. + * GMAC will pass all the incoming frames to Host unmodified. + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ +static void gmac_pad_crc_strip_disable(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacPadCrcStrip); + return; +} + +/** + * GMAC programmed with the back off limit value. + * @param[in] pointer to gmac_device. + * \return returns void. + * \note This function is tightly coupled with gmac_retry_enable(gmac_device * gmacdev) + * note:cdh:check ok + */ +static void gmac_back_off_limit(gmac_device *gmacdev, u32 value) +{ + u32 data; + data = gmac_read_reg((u32 *)gmacdev->MacBase, GmacConfig); + data &= (~GmacBackoffLimit); + data |= value; + gmac_write_reg((u32 *)gmacdev->MacBase, GmacConfig, data); + return; +} + +/** + * Enables the Deferral check in GMAC (Only in Half Duplex mode) + * GMAC issues a Frame Abort Status, along with the excessive deferral error bit set in the + * transmit frame status when transmit state machine is deferred for more than + * - 24,288 bit times in 10/100Mbps mode + * - 155,680 bit times in 1000Mbps mode or Jumbo frame mode in 10/100Mbps operation. + * @param[in] pointer to gmac_device. + * \return returns void. + * \note Deferral begins when transmitter is ready to transmit, but is prevented because of + * an active CRS (carrier sense) + */ +void gmac_deferral_check_enable(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacDeferralCheck); + return; +} +/** + * Disables the Deferral check in GMAC (Only in Half Duplex mode). + * GMAC defers until the CRS signal goes inactive. + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ +static void gmac_deferral_check_disable(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacDeferralCheck); + return; +} + +/** + * Selects the GMII port. + * When called GMII (1000Mbps) port is selected (programmable only in 10/100/1000 Mbps configuration). + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ +static void gmac_select_gmii(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacMiiGmii); + return; +} +/** + * Selects the MII port. + * When called MII (10/100Mbps) port is selected (programmable only in 10/100/1000 Mbps configuration). + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ +static void gmac_select_mii(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacMiiGmii); + return; +} + +/*Receive frame filter configuration functions*/ + +/** + * Enables reception of all the frames to application. + * GMAC passes all the frames received to application irrespective of whether they + * pass SA/DA address filtering or not. + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ +static void gmac_frame_filter_enable(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacFrameFilter, GmacFilter); + return; +} +/** + * Disables reception of all the frames to application. + * GMAC passes only those received frames to application which + * pass SA/DA address filtering. + * @param[in] pointer to gmac_device. + * \return void. + * note:cdh:check ok + */ +void gmac_frame_filter_disable(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacFrameFilter, GmacFilter); + return; +} + +/** + * Enables forwarding of control frames. + * When set forwards all the control frames (incl. unicast and multicast PAUSE frames). + * @param[in] pointer to gmac_device. + * \return void. + * \note Depends on RFE of FlowControlRegister[2] + * note:cdh:check ok + */ +static void gmac_set_pass_control(gmac_device *gmacdev, u32 passcontrol) +{ + u32 data; + data = gmac_read_reg((u32 *)gmacdev->MacBase, GmacFrameFilter); + data &= (~GmacPassControl); + data |= passcontrol; + gmac_write_reg((u32 *)gmacdev->MacBase, GmacFrameFilter, data); + return; +} + +/** + * Enables Broadcast frames. + * When enabled Address filtering module passes all incoming broadcast frames. + * @param[in] pointer to gmac_device. + * \return void. + * note:cdh:check ok + */ +static void gmac_broadcast_enable(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacFrameFilter, GmacBroadcast); + return; +} +/** + * Disable Broadcast frames. + * When disabled Address filtering module filters all incoming broadcast frames. + * @param[in] pointer to gmac_device. + * \return void. + */ +void gmac_broadcast_disable(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacFrameFilter, GmacBroadcast); + return; +} + +/** + * Enables Source address filtering. + * When enabled source address filtering is performed. Only frames matching SA filtering are passed to application with + * SAMatch bit of RxStatus is set. GMAC drops failed frames. + * @param[in] pointer to gmac_device. + * \return void. + * \note This function is overriden by gmac_frame_filter_disable(gmac_device *) + */ +void gmac_src_addr_filter_enable(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacFrameFilter, GmacSrcAddrFilter); + return; +} +/** + * Disables Source address filtering. + * When disabled GMAC forwards the received frames with updated SAMatch bit in RxStatus. + * @param[in] pointer to gmac_device. + * \return void. + * note:cdh:check ok + */ +static void gmac_src_addr_filter_disable(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacFrameFilter, GmacSrcAddrFilter); + return; +} + +/** + * Enables Multicast frames. + * When enabled all multicast frames are passed. + * @param[in] pointer to gmac_device. + * \return void. + */ +static void gmac_multicast_enable(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacFrameFilter, GmacMulticastFilter); + return; +} +/** + * Disable Multicast frames. + * When disabled multicast frame filtering depends on HMC bit. + * @param[in] pointer to gmac_device. + * \return void. + * note:cdh:check ok + */ +static void gmac_multicast_disable(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacFrameFilter, GmacMulticastFilter); + return; +} + +/** + * Enables the normal Destination address filtering. + * @param[in] pointer to gmac_device. + * \return void. + * note:cdh:check ok + */ +static void gmac_dst_addr_filter_normal(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacFrameFilter, GmacDestAddrFilterNor); + return; +} + +/** + * Enables multicast hash filtering. + * When enabled GMAC performs teh destination address filtering according to the hash table. + * @param[in] pointer to gmac_device. + * \return void. + */ +static void gmac_multicast_hash_filter_enable(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacFrameFilter, GmacMcastHashFilter); + return; +} + +/** + * Disables multicast hash filtering. + * When disabled GMAC performs perfect destination address filtering for multicast frames, it compares + * DA field with the value programmed in DA register. + * @param[in] pointer to gmac_device. + * \return void. + * note:cdh:check ok + */ +static void gmac_multicast_hash_filter_disable(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacFrameFilter, GmacMcastHashFilter); + return; +} + + +/** + * Clears promiscous mode. + * When called the GMAC falls back to normal operation from promiscous mode. + * @param[in] pointer to gmac_device. + * \return void. + * note:cdh:check ok + */ +#if 0 +static void gmac_promisc_disable(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacFrameFilter, GmacPromiscuousMode); + return; +} +#else +static void gmac_promisc_enable(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacFrameFilter, GmacPromiscuousMode); + return; +} +#endif + +/** + * Enables unicast hash filtering. + * When enabled GMAC performs the destination address filtering of unicast frames according to the hash table. + * @param[in] pointer to gmac_device. + * \return void. + */ +void gmac_unicast_hash_filter_enable(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacFrameFilter, GmacUcastHashFilter); + return; +} +/** + * Disables multicast hash filtering. + * When disabled GMAC performs perfect destination address filtering for unicast frames, it compares + * DA field with the value programmed in DA register. + * @param[in] pointer to gmac_device. + * \return void. + * note:cdh:check ok + */ +static void gmac_unicast_hash_filter_disable(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacFrameFilter, GmacUcastHashFilter); + return; +} + +/*Flow control configuration functions*/ + +/** + * Enables detection of pause frames with stations unicast address. + * When enabled GMAC detects the pause frames with stations unicast address in addition to the + * detection of pause frames with unique multicast address. + * @param[in] pointer to gmac_device. + * \return void. + */ +void gmac_unicast_pause_frame_detect_enable(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacFlowControl, GmacUnicastPauseFrame); + return; +} +/** + * Disables detection of pause frames with stations unicast address. + * When disabled GMAC only detects with the unique multicast address (802.3x). + * @param[in] pointer to gmac_device. + * \return void. + * note:cdh:check ok + */ +static void gmac_unicast_pause_frame_detect_disable(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacFlowControl, GmacUnicastPauseFrame); + return; +} + +/** + * Rx flow control enable. + * When Enabled GMAC will decode the rx pause frame and disable the tx for a specified time. + * @param[in] pointer to gmac_device. + * \return void. + * note:cdh:check ok + */ +static void gmac_rx_flow_control_enable(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacFlowControl, GmacRxFlowControl); + return; +} +/** + * Rx flow control disable. + * When disabled GMAC will not decode pause frame. + * @param[in] pointer to gmac_device. + * \return void. + * note:cdh:check ok + */ +static void gmac_rx_flow_control_disable(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacFlowControl, GmacRxFlowControl); + return; +} + +/** + * Tx flow control enable. + * When Enabled + * - In full duplex GMAC enables flow control operation to transmit pause frames. + * - In Half duplex GMAC enables the back pressure operation + * @param[in] pointer to gmac_device. + * \return void. + * note:cdh:check ok + */ +static void gmac_tx_flow_control_enable(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacFlowControl, GmacTxFlowControl); + return; +} + +/** + * Tx flow control disable. + * When Disabled + * - In full duplex GMAC will not transmit any pause frames. + * - In Half duplex GMAC disables the back pressure feature. + * @param[in] pointer to gmac_device. + * \return void. + * note:cdh:check ok + */ +static void gmac_tx_flow_control_disable(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacFlowControl, GmacTxFlowControl); + return; +} + + + + + +/** + * Example mac initialization sequence. + * This function calls the initialization routines to initialize the GMAC register. + * One can change the functions invoked here to have different configuration as per the requirement + * @param[in] pointer to gmac_device. + * \return Returns 0 on success. + */ +static s32 gmac_mac_init(gmac_device *gmacdev) +{ + u32 PHYreg; + + if(gmacdev->DuplexMode == FULLDUPLEX) + { + gmac_wd_enable(gmacdev); + gmac_jab_enable(gmacdev); + gmac_frame_burst_enable(gmacdev); + gmac_jumbo_frame_disable(gmacdev); + gmac_rx_own_enable(gmacdev); + gmac_loopback_off(gmacdev); + gmac_set_full_duplex(gmacdev); + gmac_retry_enable(gmacdev); + gmac_pad_crc_strip_disable(gmacdev); + gmac_back_off_limit(gmacdev, GmacBackoffLimit0); + gmac_deferral_check_disable(gmacdev); + gmac_tx_enable(gmacdev); // CDH:TX Enable + gmac_rx_enable(gmacdev); // CDH:RX Enable + + if(gmacdev->Speed == SPEED1000) + gmac_select_gmii(gmacdev); + else + gmac_select_mii(gmacdev); // cdh:we use mii + + /*Frame Filter Configuration*/ + gmac_frame_filter_enable(gmacdev); + gmac_set_pass_control(gmacdev, GmacPassControl0); + gmac_broadcast_enable(gmacdev); + gmac_src_addr_filter_disable(gmacdev); + gmac_multicast_enable(gmacdev); + gmac_dst_addr_filter_normal(gmacdev); + gmac_multicast_hash_filter_enable(gmacdev); + gmac_promisc_enable(gmacdev); + gmac_unicast_hash_filter_disable(gmacdev); + + /*Flow Control Configuration*/ + gmac_unicast_pause_frame_detect_disable(gmacdev); + gmac_rx_flow_control_enable(gmacdev); + gmac_tx_flow_control_enable(gmacdev); + } + else //Half Duplex + { + gmac_wd_enable(gmacdev); + gmac_jab_enable(gmacdev); + gmac_frame_burst_enable(gmacdev); + gmac_jumbo_frame_disable(gmacdev); + gmac_rx_own_enable(gmacdev); + gmac_loopback_off(gmacdev); + gmac_set_half_duplex(gmacdev); + gmac_retry_enable(gmacdev); + gmac_pad_crc_strip_disable(gmacdev); + gmac_back_off_limit(gmacdev, GmacBackoffLimit0); + gmac_deferral_check_disable(gmacdev); + gmac_tx_enable(gmacdev); + gmac_rx_enable(gmacdev); + + if(gmacdev->Speed == SPEED1000) + gmac_select_gmii(gmacdev); + else + gmac_select_mii(gmacdev); + + /*Frame Filter Configuration*/ + gmac_frame_filter_enable(gmacdev); + gmac_set_pass_control(gmacdev, GmacPassControl0); + gmac_broadcast_enable(gmacdev); + gmac_src_addr_filter_disable(gmacdev); + gmac_multicast_disable(gmacdev); + gmac_dst_addr_filter_normal(gmacdev); + gmac_multicast_hash_filter_disable(gmacdev); + gmac_promisc_enable(gmacdev); + gmac_unicast_hash_filter_disable(gmacdev); + + /*Flow Control Configuration*/ + gmac_unicast_pause_frame_detect_disable(gmacdev); + gmac_rx_flow_control_disable(gmacdev); + gmac_tx_flow_control_disable(gmacdev); + + /*To set PHY register to enable CRS on Transmit*/ + gmac_write_reg((u32 *)gmacdev->MacBase, GmacGmiiAddr, GmiiBusy | 0x00000408); + PHYreg = gmac_read_reg((u32 *)gmacdev->MacBase, GmacGmiiData); + gmac_write_reg((u32 *)gmacdev->MacBase, GmacGmiiData, PHYreg | 0x00000800); + gmac_write_reg((u32 *)gmacdev->MacBase, GmacGmiiAddr, GmiiBusy | 0x0000040a); + } + return 0; +} + +/** + * Checks whether the descriptor is owned by DMA. + * If descriptor is owned by DMA then the OWN bit is set to 1. This API is same for both ring and chain mode. + * @param[in] pointer to DmaDesc structure. + * \return returns true if Dma owns descriptor and false if not. + */ +bool gmac_is_desc_owned_by_dma(DmaDesc *desc) +{ + return ((desc->status & DescOwnByDma) == DescOwnByDma ); +} + +/** + * Checks whether the descriptor is empty. + * If the buffer1 and buffer2 lengths are zero in ring mode descriptor is empty. + * In chain mode buffer2 length is 0 but buffer2 itself contains the next descriptor address. + * @param[in] pointer to DmaDesc structure. + * \return returns true if descriptor is empty, false if not empty. + * note:cdh:check ok + */ +bool gmac_is_desc_empty(DmaDesc *desc) +{ + //if both the buffer1 length and buffer2 length are zero desc is empty + return(((desc->length & DescSize1Mask) == 0) && ((desc->length & DescSize2Mask) == 0) ); +} + +/** + * Checks whether this rx descriptor is last rx descriptor. + * This returns true if it is last descriptor either in ring mode or in chain mode. + * @param[in] pointer to devic structure. + * @param[in] pointer to DmaDesc structure. + * \return returns true if it is last descriptor, false if not. + * \note This function should not be called before initializing the descriptor using synopGMAC_desc_init(). + * note:cdh:check ok + */ +bool gmac_is_last_rx_desc(gmac_device *gmacdev, DmaDesc *desc) +{ + //bool synopGMAC_is_last_desc(DmaDesc *desc) + return (((desc->length & RxDescEndOfRing) == RxDescEndOfRing) || ((u32)gmacdev->RxDesc == desc->data2)); +} + +/** + * Initialize the rx descriptors for chain mode of operation. + * - Status field is initialized to 0. + * - EndOfRing set for the last descriptor. + * - buffer1 and buffer2 set to 0. + * - data1 and data2 set to 0. + * @param[in] pointer to DmaDesc structure. + * @param[in] whether end of ring + * \return void. + */ + +void gmac_rx_desc_init_chain(DmaDesc *desc) +{ + desc->status = 0; + desc->length = RxDescChain; + desc->buffer1 = 0; + desc->data1 = 0; + return; +} + + + +/** + * Get back the descriptor from DMA after data has been received. + * When the DMA indicates that the data is received (interrupt is generated), this function should be + * called to get the descriptor and hence the data buffers received. With successful return from this + * function caller gets the descriptor fields for processing. check the parameters to understand the + * fields returned.` + * @param[in] pointer to gmac_device. + * @param[out] pointer to hold the status of DMA. + * @param[out] Dma-able buffer1 pointer. + * @param[out] pointer to hold length of buffer1 (Max is 2048). + * @param[out] virtual pointer for buffer1. + * @param[out] Dma-able buffer2 pointer. + * @param[out] pointer to hold length of buffer2 (Max is 2048). + * @param[out] virtual pointer for buffer2. + * \return returns present rx descriptor index on success. Negative value if error. + */ +static s32 gmac_get_rx_qptr(gmac_device *gmacdev, u32 *Status, u32 *Buffer1, u32 *Length1, u32 *Data1, u32 *Buffer2, u32 *Length2, u32 *Data2) +{ + u32 rxnext = gmacdev->RxBusy; // index of descriptor the DMA just completed. May be useful when data + //is spread over multiple buffers/descriptors + DmaDesc *rxdesc = gmacdev->RxBusyDesc; + if(gmac_is_desc_owned_by_dma(rxdesc)) + return -1; + if(gmac_is_desc_empty(rxdesc)) + return -1; + + + if(Status != 0) + *Status = rxdesc->status;// send the status of this descriptor + + if(Length1 != 0) + *Length1 = (rxdesc->length & DescSize1Mask) >> DescSize1Shift; + if(Buffer1 != 0) + *Buffer1 = rxdesc->buffer1; + if(Data1 != 0) + *Data1 = rxdesc->data1; + + if(Length2 != 0) + *Length2 = (rxdesc->length & DescSize2Mask) >> DescSize2Shift; + if(Buffer2 != 0) + *Buffer2 = rxdesc->buffer2; + if(Data1 != 0) + *Data2 = rxdesc->data2; + + gmacdev->RxBusy = gmac_is_last_rx_desc(gmacdev, rxdesc) ? 0 : rxnext + 1; + + if(gmac_is_rx_desc_chained(rxdesc)) + { + gmacdev->RxBusyDesc = (DmaDesc *)rxdesc->data2; + gmac_rx_desc_init_chain(rxdesc); + //synopGMAC_desc_init_chain(rxdesc, gmac_is_last_rx_desc(gmacdev,rxdesc),0,0); + } + else + { + gmacdev->RxBusyDesc = gmac_is_last_rx_desc(gmacdev, rxdesc) ? gmacdev->RxDesc : (rxdesc + 1); + gmac_rx_desc_init_ring(rxdesc, gmac_is_last_rx_desc(gmacdev, rxdesc)); + } + // TR("(get rx)%02d %08x %08x %08x %08x %08x %08x %08x\n", rxnext, (u32)rxdesc, rxdesc->status, rxdesc->length, rxdesc->buffer1, rxdesc->buffer2, rxdesc->data1, rxdesc->data2); + (gmacdev->BusyRxDesc)--; //This returns one descriptor to processor. So busy count will be decremented by one + + return(rxnext); + +} + + + +/** + * Reads the time stamp higher sec value to respective pointers + * @param[in] pointer to gmac_device + * @param[in] pointer to hold 16 higher bit second register contents + * \return returns void + */ +void gmac_TS_read_timestamp_higher_val(gmac_device *gmacdev, u16 *higher_sec_val) +{ + * higher_sec_val = (u16)(gmac_read_reg((u32 *)gmacdev->MacBase, GmacTSHighWord) & GmacTSHighWordMask); + return; +} + +/** + * Checks whether the rx descriptor is valid. + * if rx descripor is not in error and complete frame is available in the same descriptor + * @param[in] pointer to DmaDesc structure. + * \return returns true if no error and first and last desc bits are set, otherwise it returns false. + */ +bool gmac_is_rx_desc_valid(u32 status) +{ + return ((status & DescError) == 0) && ((status & DescRxFirst) == DescRxFirst) && ((status & DescRxLast) == DescRxLast); +} + +/** + * returns the byte length of received frame including CRC. + * This returns the no of bytes received in the received ethernet frame including CRC(FCS). + * @param[in] pointer to DmaDesc structure. + * \return returns the length of received frame lengths in bytes. + */ +u32 gmac_get_rx_desc_frame_length(u32 status) +{ + return ((status & DescFrameLengthMask) >> DescFrameLengthShift); +} + + +/** + * Decodes the Rx Descriptor status to various checksum error conditions. + * @param[in] pointer to gmac_device. + * @param[in] u32 status field of the corresponding descriptor. + * \return returns decoded enum (u32) indicating the status. + */ +u32 gmac_is_rx_checksum_error(gmac_device *gmacdev, u32 status) +{ + if (((status & DescRxChkBit5) == 0) && ((status & DescRxChkBit7) == 0) && ((status & DescRxChkBit0) == 0)) + return RxLenLT600; + else if(((status & DescRxChkBit5) == 0) && ((status & DescRxChkBit7) == 0) && ((status & DescRxChkBit0) != 0)) + return RxIpHdrPayLoadChkBypass; + else if(((status & DescRxChkBit5) == 0) && ((status & DescRxChkBit7) != 0) && ((status & DescRxChkBit0) != 0)) + return RxChkBypass; + else if(((status & DescRxChkBit5) != 0) && ((status & DescRxChkBit7) == 0) && ((status & DescRxChkBit0) == 0)) + return RxNoChkError; + else if(((status & DescRxChkBit5) != 0) && ((status & DescRxChkBit7) == 0) && ((status & DescRxChkBit0) != 0)) + return RxPayLoadChkError; + else if(((status & DescRxChkBit5) != 0) && ((status & DescRxChkBit7) != 0) && ((status & DescRxChkBit0) == 0)) + return RxIpHdrChkError; + else if(((status & DescRxChkBit5) != 0) && ((status & DescRxChkBit7) != 0) && ((status & DescRxChkBit0) != 0)) + return RxIpHdrPayLoadChkError; + else + return RxIpHdrPayLoadRes; +} + +/** + * Check for damaged frame due to collision. + * Retruns true if rx frame was damaged due to late collision in half duplex mode. + * @param[in] pointer to DmaDesc structure. + * \return returns true if error else returns false. + */ +bool gmac_is_rx_frame_collision(u32 status) +{ + //bool synopGMAC_dma_rx_collisions(u32 status) + return ((status & DescRxCollision) == DescRxCollision); +} + +/** + * Check for receive CRC error. + * Retruns true if rx frame CRC error occured. + * @param[in] pointer to DmaDesc structure. + * \return returns true if error else returns false. + */ +bool gmac_is_rx_crc(u32 status) +{ + //u32 synopGMAC_dma_rx_crc(u32 status) + return ((status & DescRxCrc) == DescRxCrc); +} + +/** + * Indicates rx frame has non integer multiple of bytes. (odd nibbles). + * Retruns true if dribbling error in rx frame. + * @param[in] pointer to DmaDesc structure. + * \return returns true if error else returns false. + */ +bool gmac_is_frame_dribbling_errors(u32 status) +{ + //u32 synopGMAC_dma_rx_frame_errors(u32 status) + return ((status & DescRxDribbling) == DescRxDribbling); +} + +/** + * Indicates error in rx frame length. + * Retruns true if received frame length doesnot match with the length field + * @param[in] pointer to DmaDesc structure. + * \return returns true if error else returns false. + */ +bool gmac_is_rx_frame_length_errors(u32 status) +{ + //u32 synopGMAC_dma_rx_length_errors(u32 status) + return((status & DescRxLengthError) == DescRxLengthError); +} + + +/** + * Prepares the descriptor to receive packets. + * The descriptor is allocated with the valid buffer addresses (sk_buff address) and the length fields + * and handed over to DMA by setting the ownership. After successful return from this function the + * descriptor is added to the receive descriptor pool/queue. + * This api is same for both ring mode and chain mode. + * @param[in] pointer to gmac_device. + * @param[in] Dma-able buffer1 pointer. + * @param[in] length of buffer1 (Max is 2048). + * @param[in] Dma-able buffer2 pointer. + * @param[in] length of buffer2 (Max is 2048). + * @param[in] u32 data indicating whether the descriptor is in ring mode or chain mode. + * \return returns present rx descriptor index on success. Negative value if error. + */ +static s32 gmac_set_rx_qptr(gmac_device *gmacdev, u32 Buffer1, u32 Length1, u32 Data1, u32 Buffer2, u32 Length2, u32 Data2) +{ + u32 rxnext = gmacdev->RxNext; + DmaDesc *rxdesc = gmacdev->RxNextDesc; + + //TR("cdh:%s, rxnext=0x%x, rxdesc=%d, Buffer1=0x%x\n", __func__, rxnext, rxdesc, Buffer1); + if(!gmac_is_desc_empty(rxdesc)) + return -1; + + if(gmac_is_rx_desc_chained(rxdesc)) + { + //TR("cdh:chain queue set!\n"); + rxdesc->length |= ((Length1 << DescSize1Shift) & DescSize1Mask); + + rxdesc->buffer1 = Buffer1; // cdh:rx data address + rxdesc->data1 = Data1; + + if((rxnext % MODULO_INTERRUPT) != 0) + rxdesc->length |= RxDisIntCompl; + + rxdesc->status = DescOwnByDma; + + gmacdev->RxNext = gmac_is_last_rx_desc(gmacdev, rxdesc) ? 0 : rxnext + 1; + gmacdev->RxNextDesc = (DmaDesc *)rxdesc->data2; + } + else + { + //TR("cdh:ring queue set!\n"); + rxdesc->length |= (((Length1 << DescSize1Shift) & DescSize1Mask) | ((Length2 << DescSize2Shift) & DescSize2Mask)); + + rxdesc->buffer1 = Buffer1; + rxdesc->data1 = Data1; + + rxdesc->buffer2 = Buffer2; + rxdesc->data2 = Data2; + + if((rxnext % MODULO_INTERRUPT) != 0) + rxdesc->length |= RxDisIntCompl; + + rxdesc->status = DescOwnByDma; + + gmacdev->RxNext = gmac_is_last_rx_desc(gmacdev, rxdesc) ? 0 : rxnext + 1; + gmacdev->RxNextDesc = gmac_is_last_rx_desc(gmacdev, rxdesc) ? gmacdev->RxDesc : (rxdesc + 1); + } + //TR("(set rx)%02d %08x %08x %08x %08x %08x %08x %08x\n", rxnext, (u32)rxdesc, rxdesc->status, rxdesc->length, rxdesc->buffer1, rxdesc->buffer2, rxdesc->data1, rxdesc->data2); + (gmacdev->BusyRxDesc)++; //One descriptor will be given to Hardware. So busy count incremented by one + return rxnext; +} + + + +/** + * Checks whether this tx descriptor is last tx descriptor. + * This returns true if it is last descriptor either in ring mode or in chain mode. + * @param[in] pointer to devic structure. + * @param[in] pointer to DmaDesc structure. + * \return returns true if it is last descriptor, false if not. + * \note This function should not be called before initializing the descriptor using synopGMAC_desc_init(). + */ +bool gmac_is_last_tx_desc(gmac_device *gmacdev, DmaDesc *desc) +{ + //bool synopGMAC_is_last_desc(DmaDesc *desc) +#ifdef ENH_DESC + return (((desc->status & TxDescEndOfRing) == TxDescEndOfRing) || ((u32)gmacdev->TxDesc == desc->data2)); +#else + return (((desc->length & TxDescEndOfRing) == TxDescEndOfRing) || ((u32)gmacdev->TxDesc == desc->data2)); +#endif +} + + +/** + * Initialize the rx descriptors for chain mode of operation. + * - Status field is initialized to 0. + * - EndOfRing set for the last descriptor. + * - buffer1 and buffer2 set to 0. + * - data1 and data2 set to 0. + * @param[in] pointer to DmaDesc structure. + * @param[in] whether end of ring + * \return void. + * note:cdh:check ok + */ +void gmac_tx_desc_init_chain(DmaDesc *desc) +{ +#ifdef ENH_DESC + desc->status = TxDescChain; + desc->length = 0; +#else + desc->length = TxDescChain; +#endif + desc->buffer1 = 0; + desc->data1 = 0; + return; +} + + +/** + * Get the index and address of Tx desc. + * This api is same for both ring mode and chain mode. + * This function tracks the tx descriptor the DMA just closed after the transmission of data from this descriptor is + * over. This returns the descriptor fields to the caller. + * @param[in] pointer to gmac_device. + * @param[out] status field of the descriptor. + * @param[out] Dma-able buffer1 pointer. + * @param[out] length of buffer1 (Max is 2048). + * @param[out] virtual pointer for buffer1. + * @param[out] Dma-able buffer2 pointer. + * @param[out] length of buffer2 (Max is 2048). + * @param[out] virtual pointer for buffer2. + * @param[out] u32 data indicating whether the descriptor is in ring mode or chain mode. + * \return returns present tx descriptor index on success. Negative value if error. + */ +s32 gmac_get_tx_qptr(gmac_device *gmacdev, u32 *Status, u32 *Buffer1, u32 *Length1, u32 *Data1, u32 *Buffer2, u32 *Length2, u32 *Data2 ) +{ + u32 txover = gmacdev->TxBusy; + DmaDesc *txdesc = gmacdev->TxBusyDesc; + + if(gmac_is_desc_owned_by_dma(txdesc)) + return -1; + if(gmac_is_desc_empty(txdesc)) + return -1; + + (gmacdev->BusyTxDesc)--; + + if(Status != 0) + *Status = txdesc->status; + + if(Buffer1 != 0) + *Buffer1 = txdesc->buffer1; + if(Length1 != 0) + *Length1 = (txdesc->length & DescSize1Mask) >> DescSize1Shift; + if(Data1 != 0) + *Data1 = txdesc->data1; + + if(Buffer2 != 0) + *Buffer2 = txdesc->buffer2; + if(Length2 != 0) + *Length2 = (txdesc->length & DescSize2Mask) >> DescSize2Shift; + if(Data1 != 0) + *Data2 = txdesc->data2; + + gmacdev->TxBusy = gmac_is_last_tx_desc(gmacdev, txdesc) ? 0 : txover + 1; + + if(gmac_is_tx_desc_chained(txdesc)) + { + gmacdev->TxBusyDesc = (DmaDesc *)txdesc->data2; + gmac_tx_desc_init_chain(txdesc); + } + else + { + gmacdev->TxBusyDesc = gmac_is_last_tx_desc(gmacdev, txdesc) ? gmacdev->TxDesc : (txdesc + 1); + gmac_tx_desc_init_ring(txdesc, gmac_is_last_tx_desc(gmacdev, txdesc)); + } + //TR("(get tx)%02d %08x %08x %08x %08x %08x %08x %08x\n", txover, (u32)txdesc, txdesc->status, txdesc->length, txdesc->buffer1, txdesc->buffer2, txdesc->data1, txdesc->data2); + + return txover; +} + +/** + * Function to Receive a packet from the interface. + * After Receiving a packet, DMA transfers the received packet to the system memory + * and generates corresponding interrupt (if it is enabled). This function prepares + * the sk_buff for received packet after removing the ethernet CRC, and hands it over + * to linux networking stack. + * - Updataes the networking interface statistics + * - Keeps track of the rx descriptors + * @param[in] pointer to net_device structure. + * \return void. + * \note This function runs in interrupt context. + */ + +void synop_handle_received_data(struct net_device *netdev) +{ + nt_adapter *adapter; + gmac_device *gmacdev; + // struct pci_dev *pcidev; + s32 desc_index; + + u32 data1; + u32 data2; + u32 len; + u32 status; + //u32 rxcsum_status = 0; // cdh:add + u32 dma_addr1; + u32 dma_addr2; + //u32 length; + + struct sk_buff *skb; //This is the pointer to hold the received data + + //TR("%s\n", __FUNCTION__); + + adapter = (nt_adapter *)netdev_priv(netdev); + if(adapter == NULL) + { + TR("Unknown Device\n"); + return; + } + + gmacdev = adapter->gmacdev_pt; + if(gmacdev == NULL) + { + TR("GMAC device structure is missing\n"); + return; + } + + // cdh:pcidev = (struct pci_dev *)adapter->pcidev_pt; + /*Handle the Receive Descriptors*/ + do + { + desc_index = gmac_get_rx_qptr(gmacdev, &status, &dma_addr1, NULL, &data1, &dma_addr2, NULL, &data2); + + //desc_index = gmac_get_rx_qptr(gmacdev, &status,&dma_addr,NULL, &data1); + if(desc_index >= 0 && data1 != 0) + { +// TR("Received Data at Rx Descriptor %d for skb 0x%08x whose status is %08x\n", desc_index, data1, status); + /*At first step unmapped the dma address*/ + dma_unmap_single(NULL, dma_addr1, 0, DMA_FROM_DEVICE); + + skb = (struct sk_buff *)data1; + if(gmac_is_rx_desc_valid(status)) + { + len = gmac_get_rx_desc_frame_length(status) - 4; + //printf("real len: %x\n", len); + //printf("1, skb, head, data, tail: %x, %x, %x, %x\n", skb->head, skb->data, skb->tail, skb->len); + skb_put(skb, len); + + #ifdef IPC_OFFLOAD + TR("Checksum Offloading will be done now\n"); + skb->ip_summed = CHECKSUM_UNNECESSARY; + + if(synopGMAC_is_rx_checksum_error(gmacdev, status) == RxNoChkError ){ + //printk(KERN_ERR "Ip header and TCP/UDP payload checksum Bypassed \n"); + skb->ip_summed = CHECKSUM_UNNECESSARY; //Let Kernel bypass computing the Checksum + } + + if(synopGMAC_is_rx_checksum_error(gmacdev, status) == RxIpHdrChkError ){ + //Linux Kernel doesnot care for ipv4 header checksum. So we will simply proceed by printing a warning .... + //printk(KERN_ERR " Error in 16bit IPV4 Header Checksum \n"); + skb->ip_summed = CHECKSUM_UNNECESSARY; //Let Kernel bypass the TCP/UDP checksum computation + } + + if(synopGMAC_is_rx_checksum_error(gmacdev, status) == RxLenLT600 ){ + //printk(KERN_ERR "IEEE 802.3 type frame with Length field Lesss than 0x0600 \n"); + skb->ip_summed = CHECKSUM_NONE; //Let Kernel compute the Checksum + } + + if(synopGMAC_is_rx_checksum_error(gmacdev, status) == RxIpHdrPayLoadChkBypass ){ + //printk(KERN_ERR "Ip header and TCP/UDP payload checksum Bypassed \n"); + skb->ip_summed = CHECKSUM_NONE; //Let Kernel compute the Checksum + } + + if(synopGMAC_is_rx_checksum_error(gmacdev, status) == RxChkBypass ){ + //printk(KERN_ERR "Ip header and TCP/UDP payload checksum Bypassed \n"); + skb->ip_summed = CHECKSUM_NONE; //Let Kernel compute the Checksum + } + + if(synopGMAC_is_rx_checksum_error(gmacdev, status) == RxPayLoadChkError ){ + //printk(KERN_ERR " TCP/UDP payload checksum Error \n"); + skb->ip_summed = CHECKSUM_NONE; //Let Kernel compute the Checksum + } + + if(synopGMAC_is_rx_checksum_error(gmacdev, status) == RxIpHdrChkError ){ + //Linux Kernel doesnot care for ipv4 header checksum. So we will simply proceed by printing a warning .... + //printk(KERN_ERR " Both IP header and Payload Checksum Error \n"); + skb->ip_summed = CHECKSUM_NONE; //Let Kernel compute the Checksum + } + + #endif + + + //printf("2, skb, head, data, tail: %x, %x, %x, %x\n", skb->head, skb->data, skb->tail, skb->len); + skb->dev = netdev; + skb->protocol = eth_type_trans(skb, netdev); + netif_rx(skb); //printk("netif_rx end!\n"); + + netdev->last_rx = jiffies; + adapter->net_dev_stats.rx_packets++; + adapter->net_dev_stats.rx_bytes += len; + //printk("max rx end\n"); + } + else + { + /*Now the present skb should be set free*/ + dev_kfree_skb_irq(skb); + TR("s: %08x\n", status); + adapter->net_dev_stats.rx_errors++; + adapter->net_dev_stats.collisions += gmac_is_rx_frame_collision(status); + adapter->net_dev_stats.rx_crc_errors += gmac_is_rx_crc(status); + adapter->net_dev_stats.rx_frame_errors += gmac_is_frame_dribbling_errors(status); + adapter->net_dev_stats.rx_length_errors += gmac_is_rx_frame_length_errors(status); + } + + //Now lets allocate the skb for the emptied descriptor + //TR("SKB memory allocation ... \n"); + skb = dev_alloc_skb(netdev->mtu + ETHERNET_PACKET_EXTRA); + if(skb == NULL) + { + TR("SKB memory allocation failed \n"); + adapter->net_dev_stats.rx_dropped++; + continue; + }else{ + //TR("SKB memory allocation ok \n"); + } + + // cdh:dma_addr = pci_map_single(pcidev, skb->data, skb_tailroom(skb), PCI_DMA_FROMDEVICE); + + dma_addr1 = dma_map_single(NULL, skb->data, skb_tailroom(skb), DMA_FROM_DEVICE); + //TR("SKB map ok \n"); + + desc_index = gmac_set_rx_qptr(gmacdev, dma_addr1, skb_tailroom(skb), (u32)skb, 0, 0, 0); + if(desc_index < 0) + { + //TR("Cannot set Rx Descriptor for skb %08x\n", (u32)skb); + dev_kfree_skb_irq(skb); + } + //TR("set rx qptr ok \n"); + + } + }while(desc_index >= 0); +} + +/** + * Resumes the DMA Transmission. + * the DmaTxPollDemand is written. (the data writeen could be anything). + * This forces the DMA to resume transmission. + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ +void gmac_resume_dma_tx(gmac_device *gmacdev) +{ + gmac_write_reg((u32 *)gmacdev->DmaBase, DmaTxPollDemand, 0); + +} +/** + * Resumes the DMA Reception. + * the DmaRxPollDemand is written. (the data writeen could be anything). + * This forces the DMA to resume reception. + * @param[in] pointer to gmac_device. + * \return returns void. + */ +void gmac_resume_dma_rx(gmac_device *gmacdev) +{ + gmac_write_reg((u32 *)gmacdev->DmaBase, DmaRxPollDemand, 0); + +} + +/** + * Checks if any Ipv4 header checksum error in the frame just transmitted. + * This serves as indication that error occureed in the IPv4 header checksum insertion. + * The sent out frame doesnot carry any ipv4 header checksum inserted by the hardware. + * @param[in] pointer to gmac_device. + * @param[in] u32 status field of the corresponding descriptor. + * \return returns true if error in ipv4 header checksum, else returns false. + */ +bool gmac_is_tx_ipv4header_checksum_error(gmac_device *gmacdev, u32 status) +{ + return((status & DescTxIpv4ChkError) == DescTxIpv4ChkError); +} + +/** + * Checks if any payload checksum error in the frame just transmitted. + * This serves as indication that error occureed in the payload checksum insertion. + * The sent out frame doesnot carry any payload checksum inserted by the hardware. + * @param[in] pointer to gmac_device. + * @param[in] u32 status field of the corresponding descriptor. + * \return returns true if error in ipv4 header checksum, else returns false. + */ +bool gmac_is_tx_payload_checksum_error(gmac_device *gmacdev, u32 status) +{ + return((status & DescTxPayChkError) == DescTxPayChkError); +} + +/** + * Checks whether the descriptor is valid + * if no errors such as CRC/Receive Error/Watchdog Timeout/Late collision/Giant Frame/Overflow/Descriptor + * error the descritpor is said to be a valid descriptor. + * @param[in] pointer to DmaDesc structure. + * \return True if desc valid. false if error. + */ +bool gmac_is_desc_valid(u32 status) +{ + return ((status & DescError) == 0); +} + +/** + * Checks whether the tx is aborted due to collisions. + * @param[in] pointer to DmaDesc structure. + * \return returns true if collisions, else returns false. + */ +bool gmac_is_tx_aborted(u32 status) +{ + return (((status & DescTxLateCollision) == DescTxLateCollision) | ((status & DescTxExcCollisions) == DescTxExcCollisions)); + +} + +/** + * Checks whether the tx carrier error. + * @param[in] pointer to DmaDesc structure. + * \return returns true if carrier error occured, else returns falser. + */ +bool gmac_is_tx_carrier_error(u32 status) +{ + return (((status & DescTxLostCarrier) == DescTxLostCarrier) | ((status & DescTxNoCarrier) == DescTxNoCarrier)); +} + +/** + * Gives the transmission collision count. + * returns the transmission collision count indicating number of collisions occured before the frame was transmitted. + * Make sure to check excessive collision didnot happen to ensure the count is valid. + * @param[in] pointer to DmaDesc structure. + * \return returns the count value of collision. + */ +u32 gmac_get_tx_collision_count(u32 status) +{ + return ((status & DescTxCollMask) >> DescTxCollShift); +} +u32 gmac_is_exc_tx_collisions(u32 status) +{ + return ((status & DescTxExcCollisions) == DescTxExcCollisions); +} + + + +/** + * Function to handle housekeeping after a packet is transmitted over the wire. + * After the transmission of a packet DMA generates corresponding interrupt + * (if it is enabled). It takes care of returning the sk_buff to the linux + * kernel, updating the networking statistics and tracking the descriptors. + * @param[in] pointer to net_device structure. + * \return void. + * \note This function runs in interrupt context + */ +void synop_handle_transmit_over(struct net_device *netdev) +{ + nt_adapter *adapter; + gmac_device *gmacdev; + // cdh:struct pci_dev *pcidev; + s32 desc_index; + u32 data1, data2; + u32 status; + u32 length1, length2; + u32 dma_addr1, dma_addr2; + + adapter = (nt_adapter *)netdev_priv(netdev); + if(adapter == NULL) + { + TR("Unknown Device\n"); + return; + } + + gmacdev = adapter->gmacdev_pt; + if(gmacdev == NULL) + { + TR("GMAC device structure is missing\n"); + return; + } + + // cdh: pcidev = (struct pci_dev *)adapter->pcidev_pt; + /*Handle the transmit Descriptors*/ + do { + + desc_index = gmac_get_tx_qptr(gmacdev, &status, &dma_addr1, &length1, &data1, &dma_addr2, &length2, &data2); + + //desc_index = gmac_get_tx_qptr(gmacdev, &status, &dma_addr, &length, &data1); + if(desc_index >= 0 && data1 != 0) + { + #ifdef IPC_OFFLOAD + if(synopGMAC_is_tx_ipv4header_checksum_error(gmacdev, status)){ + printk(KERN_ERR "Harware Failed to Insert IPV4 Header Checksum\n"); + } + + if(synopGMAC_is_tx_payload_checksum_error(gmacdev, status)){ + printk(KERN_ERR "Harware Failed to Insert Payload Checksum\n"); + } + #endif + + //TR("Tx over: %x\n", desc_index); + //TR("Finished Transmit at Tx Descriptor %d for skb 0x%08x and buffer = %08x whose status is %08x \n", desc_index, data1, dma_addr1, status); + dma_unmap_single(NULL, dma_addr1, length1, DMA_TO_DEVICE); + dev_kfree_skb_irq((struct sk_buff *)data1); + if(gmac_is_desc_valid(status)) + { + adapter->net_dev_stats.tx_bytes += length1; + adapter->net_dev_stats.tx_packets++; + } + else + { + TR("Error in Status %08x\n", status); + adapter->net_dev_stats.tx_errors++; + adapter->net_dev_stats.tx_aborted_errors += gmac_is_tx_aborted(status); + adapter->net_dev_stats.tx_carrier_errors += gmac_is_tx_carrier_error(status); + } + } + adapter->net_dev_stats.collisions += gmac_get_tx_collision_count(status); + } while(desc_index >= 0); + netif_wake_queue(netdev); +} + + + +/** + * Interrupt service routing bottom half. + * This is the function registered as ISR for device interrupts. + * @param[in] interrupt number. + * @param[in] void pointer to device unique structure (Required for shared interrupts in Linux). + * @param[in] pointer to pt_regs (not used). + * \return Returns IRQ_NONE if not device interrupts IRQ_HANDLED for device interrupts. + * \note This function runs in interrupt context + * \note cdh:check ok + */ +irqreturn_t gmac_intr_handler(s32 intr_num, void *dev_id) +{ + struct net_device *netdev; + nt_adapter *adapter; + gmac_device *gmacdev; + u32 interrupt, dma_status_reg; + s32 status; + u32 dma_addr; + + netdev = (struct net_device *)dev_id; + if(netdev == NULL) + { + TR("Unknown Device\n"); + return -1; + } + + adapter = (nt_adapter *)netdev_priv(netdev); + if(adapter == NULL) + { + TR("Adapter Structure Missing\n"); + return -1; + } + + gmacdev = adapter->gmacdev_pt; + if(gmacdev == NULL) + { + TR("GMAC device structure Missing\n"); + return -1; + } + + // cdh:pcidev = (struct pci_dev *)adapter->pcidev_pt; + + /*Read the Dma interrupt status to know whether the interrupt got generated by our device or not*/ + dma_status_reg = gmac_read_reg((u32 *)gmacdev->DmaBase, DmaStatus); + + if(dma_status_reg == 0) + return IRQ_NONE; + + gmac_disable_interrupt_all(gmacdev); + + TR("(bh)GMAC INTs: 0x%08x\n", dma_status_reg); + + // cdh:power managent module(PMT) + if(dma_status_reg & GmacPmtIntr) + { + TR("%s:: Interrupt due to PMT module\n", __FUNCTION__); + powerup_mac(gmacdev); + } + + // cdh:check mmc interrupt + if(dma_status_reg & GmacMmcIntr) + { + TR("%s:: Interrupt due to MMC module\n", __FUNCTION__); + TR("%s:: synopGMAC_rx_int_status = %08x\n", __FUNCTION__, gmac_read_mmc_rx_int_status(gmacdev)); + TR("%s:: synopGMAC_tx_int_status = %08x\n", __FUNCTION__, gmac_read_mmc_tx_int_status(gmacdev)); + } + + // cdh:check pcs(link change and an complete) + if(dma_status_reg & GmacLineIntfIntr) + { + TR("%s:: Interrupt due to GMAC LINE module\n", __FUNCTION__); + } + + /*Now lets handle the DMA interrupts*/ + interrupt = gmac_get_interrupt_type(gmacdev); + //TR("%s: INTs to be handled: 0x%08x\n", __FUNCTION__, interrupt); + + if(interrupt & synopGMACDmaError) + { + TR("%s::Fatal Bus Error Inetrrupt Seen\n", __FUNCTION__); + gmac_disable_dma_tx(gmacdev); + gmac_disable_dma_rx(gmacdev); + + gmac_take_desc_ownership_tx(gmacdev); + gmac_take_desc_ownership_rx(gmacdev); + + gmac_init_tx_rx_desc_queue(gmacdev); + + gmac_reset(gmacdev);//reset the DMA engine and the GMAC ip + + gmac_set_mac_addr(gmacdev, GmacAddr0High, GmacAddr0Low, netdev->dev_addr); + gmac_set_dma_bus_mode(gmacdev, DmaFixedBurstEnable | DmaBurstLength8 | DmaDescriptorSkip2 ); + gmac_set_dma_control(gmacdev, DmaStoreAndForward); + gmac_init_rx_desc_base(gmacdev); + gmac_init_tx_desc_base(gmacdev); + gmac_mac_init(gmacdev); + gmac_enable_dma_rx(gmacdev); + gmac_enable_dma_tx(gmacdev); + } + + if(interrupt & synopGMACDmaRxNormal) + { + //TR("%s:: Rx Normal\n", __FUNCTION__); + synop_handle_received_data(netdev); + } + + if(interrupt & synopGMACDmaRxAbnormal) + { + TR("%s::Abnormal Rx Interrupt Seen\n", __FUNCTION__); +#if 1 + if(GMAC_Power_down == 0) // If Mac is not in powerdown + { + adapter->net_dev_stats.rx_over_errors++; + /*Now Descriptors have been created in synop_handle_received_data(). Just issue a poll demand to resume DMA operation*/ + gmac_resume_dma_rx(gmacdev);//To handle GBPS with 12 descriptors + } +#endif + } + + if(interrupt & synopGMACDmaRxStopped) + { + TR("%s::Receiver stopped seeing Rx interrupts\n", __FUNCTION__); //Receiver gone in to stopped state +#if 1 + if(GMAC_Power_down == 0) // If Mac is not in powerdown + { + adapter->net_dev_stats.rx_over_errors++; + do { + struct sk_buff *skb = alloc_skb(netdev->mtu + ETHERNET_HEADER + ETHERNET_CRC, GFP_ATOMIC); + if(skb == NULL) + { + TR("%s::ERROR in skb buffer allocation Better Luck Next time\n", __FUNCTION__); + break; + // return -ESYNOPGMACNOMEM; + } + + dma_addr = dma_map_single(NULL, skb->data, skb_tailroom(skb), DMA_FROM_DEVICE); + status = gmac_set_rx_qptr(gmacdev, dma_addr, skb_tailroom(skb), (u32)skb, 0, 0, 0); + TR("%s::Set Rx Descriptor no %08x for skb %08x \n", __FUNCTION__, status, (u32)skb); + if(status < 0) + dev_kfree_skb_irq(skb);//changed from dev_free_skb. If problem check this again + + } while(status >= 0); + gmac_enable_dma_rx(gmacdev); + } +#endif + } + + if(interrupt & synopGMACDmaTxNormal) + { + //xmit function has done its job + //TR("%s::Finished Normal Transmission \n", __FUNCTION__); + synop_handle_transmit_over(netdev);//Do whatever you want after the transmission is over + } + + if(interrupt & synopGMACDmaTxAbnormal) + { + TR("%s::Abnormal Tx Interrupt Seen\n", __FUNCTION__); +#if 1 + if(GMAC_Power_down == 0) // If Mac is not in powerdown + { + synop_handle_transmit_over(netdev); + } +#endif + } + + if(interrupt & synopGMACDmaTxStopped) + { + TR("%s::Transmitter stopped sending the packets\n", __FUNCTION__); +#if 1 + if(GMAC_Power_down == 0)// If Mac is not in powerdown + { + gmac_disable_dma_tx(gmacdev); + gmac_take_desc_ownership_tx(gmacdev); + + gmac_enable_dma_tx(gmacdev); + netif_wake_queue(netdev); + TR("%s::Transmission Resumed\n", __FUNCTION__); + } +#endif + } + + /* Enable the interrrupt before returning from ISR*/ + gmac_enable_interrupt(gmacdev, DmaIntEnable); + return IRQ_HANDLED; +} + + +/** + * This sets up the transmit Descriptor queue in ring or chain mode. + * This function is tightly coupled to the platform and operating system + * Device is interested only after the descriptors are setup. Therefore this function + * is not included in the device driver API. This function should be treated as an + * example code to design the descriptor structures for ring mode or chain mode. + * This function depends on the pcidev structure for allocation consistent dma-able memory in case of linux. + * This limitation is due to the fact that linux uses pci structure to allocate a dmable memory + * - Allocates the memory for the descriptors. + * - Initialize the Busy and Next descriptors indices to 0(Indicating first descriptor). + * - Initialize the Busy and Next descriptors to first descriptor address. + * - Initialize the last descriptor with the endof ring in case of ring mode. + * - Initialize the descriptors in chain mode. + * @param[in] pointer to gmac_device. + * @param[in] pointer to pci_device structure. + * @param[in] number of descriptor expected in tx descriptor queue. + * @param[in] whether descriptors to be created in RING mode or CHAIN mode. + * \return 0 upon success. Error code upon failure. + * \note This function fails if allocation fails for required number of descriptors in Ring mode, but in chain mode + * function returns -ESYNOPGMACNOMEM in the process of descriptor chain creation. once returned from this function + * user should for gmacdev->TxDescCount to see how many descriptors are there in the chain. Should continue further + * only if the number of descriptors in the chain meets the requirements + * note:cdh:check ok + */ +static s32 setup_tx_desc_queue(gmac_device *gmacdev, struct net_device *pnetdev, u32 no_of_desc, u32 desc_mode) +{ + s32 i; + + DmaDesc *first_desc = NULL; + DmaDesc *second_desc = NULL; + dma_addr_t dma_addr; + gmacdev->TxDescCount = 0; + + if(desc_mode == RINGMODE) + { + //printk("Total size of memory required for Tx Descriptors in Ring Mode = 0x%08x\n", ((sizeof(DmaDesc) * no_of_desc))); + first_desc = (DmaDesc *)plat_alloc_consistent_mem(pnetdev, sizeof(DmaDesc) * no_of_desc, &dma_addr); + if(first_desc == NULL) + { + printk("Error in Tx Descriptors memory allocation\n"); + return -ESYNOPGMACNOMEM; + } + gmacdev->TxDescCount = no_of_desc; // cdh:ring descriptor count + gmacdev->TxDesc = first_desc; // cdh:ring descriptor base virtual address + gmacdev->TxDescDma = dma_addr; // cdh:ring descriptor base physic address + + for(i = 0; i < gmacdev->TxDescCount; i++) + { + // cdh:why not init dma fifo start addresss??? + gmac_tx_desc_init_ring(gmacdev->TxDesc + i, i == gmacdev->TxDescCount - 1); + //printk("%02d %08x \n", i, (unsigned int)(gmacdev->TxDesc + i) ); + } + + } + else + { + //Allocate the head descriptor + first_desc = (DmaDesc *)plat_alloc_consistent_mem(pnetdev, sizeof(DmaDesc), &dma_addr); + if(first_desc == NULL) + { + printk("Error in Tx Descriptor Memory allocation in Ring mode\n"); + return -ESYNOPGMACNOMEM; + } + gmacdev->TxDesc = first_desc; // cdh:ring descriptor base virtual address + gmacdev->TxDescDma = dma_addr; // cdh:ring descriptor base physic address + + printk("Tx===================================================================Tx\n"); + first_desc->buffer2 = gmacdev->TxDescDma; + first_desc->data2 = (u32)gmacdev->TxDesc; + + gmacdev->TxDescCount = 1; + + for(i = 0; i < (no_of_desc - 1); i++) + { + second_desc = plat_alloc_consistent_mem(pnetdev, sizeof(DmaDesc), &dma_addr); + if(second_desc == NULL) + { + printk("Error in Tx Descriptor Memory allocation in Chain mode\n"); + return -ESYNOPGMACNOMEM; + } + first_desc->buffer2 = dma_addr; + first_desc->data2 = (u32)second_desc; + + second_desc->buffer2 = gmacdev->TxDescDma; + second_desc->data2 = (u32)gmacdev->TxDesc; + + gmac_tx_desc_init_chain(first_desc); + printk("%02d %08x %08x %08x %08x %08x %08x %08x \n", gmacdev->TxDescCount, (u32)first_desc, first_desc->status, first_desc->length, first_desc->buffer1, first_desc->buffer2, first_desc->data1, first_desc->data2); + gmacdev->TxDescCount += 1; + first_desc = second_desc; + } + + gmac_tx_desc_init_chain(first_desc); + printk("%02d %08x %08x %08x %08x %08x %08x %08x \n", gmacdev->TxDescCount, (u32)first_desc, first_desc->status, first_desc->length, first_desc->buffer1, first_desc->buffer2, first_desc->data1, first_desc->data2); + printk("Tx===================================================================Tx\n"); + } + + gmacdev->TxNext = 0; + gmacdev->TxBusy = 0; + gmacdev->TxNextDesc = gmacdev->TxDesc; + gmacdev->TxBusyDesc = gmacdev->TxDesc; + gmacdev->BusyTxDesc = 0; + + return -ESYNOPGMACNOERR; +} + + +/** + * This sets up the receive Descriptor queue in ring or chain mode. + * This function is tightly coupled to the platform and operating system + * Device is interested only after the descriptors are setup. Therefore this function + * is not included in the device driver API. This function should be treated as an + * example code to design the descriptor structures in ring mode or chain mode. + * This function depends on the pcidev structure for allocation of consistent dma-able memory in case of linux. + * This limitation is due to the fact that linux uses pci structure to allocate a dmable memory + * - Allocates the memory for the descriptors. + * - Initialize the Busy and Next descriptors indices to 0(Indicating first descriptor). + * - Initialize the Busy and Next descriptors to first descriptor address. + * - Initialize the last descriptor with the endof ring in case of ring mode. + * - Initialize the descriptors in chain mode. + * @param[in] pointer to gmac_device. + * @param[in] pointer to pci_device structure. + * @param[in] number of descriptor expected in rx descriptor queue. + * @param[in] whether descriptors to be created in RING mode or CHAIN mode. + * \return 0 upon success. Error code upon failure. + * \note This function fails if allocation fails for required number of descriptors in Ring mode, but in chain mode + * function returns -ESYNOPGMACNOMEM in the process of descriptor chain creation. once returned from this function + * user should for gmacdev->RxDescCount to see how many descriptors are there in the chain. Should continue further + * only if the number of descriptors in the chain meets the requirements + * note:cdh:check ok + */ +static s32 setup_rx_desc_queue(gmac_device *gmacdev, struct net_device *pnetdev, u32 no_of_desc, u32 desc_mode) +{ + s32 i; + + DmaDesc *first_desc = NULL; + DmaDesc *second_desc = NULL; + dma_addr_t dma_addr; + gmacdev->RxDescCount = 0; + + if(desc_mode == RINGMODE) + { + //printk("total size of memory required for Rx Descriptors in Ring Mode = 0x%08x\n", ((sizeof(DmaDesc) * no_of_desc))); + first_desc = plat_alloc_consistent_mem (pnetdev, sizeof(DmaDesc) * no_of_desc, &dma_addr); + if(first_desc == NULL) + { + printk("Error in Rx Descriptor Memory allocation in Ring mode\n"); + return -ESYNOPGMACNOMEM; + } + gmacdev->RxDescCount = no_of_desc; + gmacdev->RxDesc = first_desc; // cdh:the first desc virtual addr + gmacdev->RxDescDma = dma_addr; // cdh:the first desc physic addr + + for(i = 0; i < gmacdev->RxDescCount; i++) + { + gmac_rx_desc_init_ring(gmacdev->RxDesc + i, i == gmacdev->RxDescCount - 1); + //printk("%02d %08x \n", i, (unsigned int)(gmacdev->RxDesc + i)); + + } + } + else + { + //Allocate the head descriptor + first_desc = plat_alloc_consistent_mem (pnetdev, sizeof(DmaDesc), &dma_addr); + if(first_desc == NULL) + { + printk("Error in Rx Descriptor Memory allocation in Ring mode\n"); + return -ESYNOPGMACNOMEM; + } + gmacdev->RxDesc = first_desc; + gmacdev->RxDescDma = dma_addr; + + printk("Rx===================================================================Rx\n"); + first_desc->buffer2 = gmacdev->RxDescDma; + first_desc->data2 = (u32) gmacdev->RxDesc; + + gmacdev->RxDescCount = 1; + + for(i = 0; i < (no_of_desc - 1); i++) + { + second_desc = plat_alloc_consistent_mem(pnetdev, sizeof(DmaDesc), &dma_addr); + if(second_desc == NULL) + { + printk("Error in Rx Descriptor Memory allocation in Chain mode\n"); + return -ESYNOPGMACNOMEM; + } + first_desc->buffer2 = dma_addr; + first_desc->data2 = (u32)second_desc; + + second_desc->buffer2 = gmacdev->RxDescDma; + second_desc->data2 = (u32)gmacdev->RxDesc; + + gmac_rx_desc_init_chain(first_desc); + printk("%02d %08x %08x %08x %08x %08x %08x %08x \n", gmacdev->RxDescCount, (u32)first_desc, first_desc->status, first_desc->length, first_desc->buffer1, first_desc->buffer2, first_desc->data1, first_desc->data2); + gmacdev->RxDescCount += 1; + first_desc = second_desc; + } + gmac_rx_desc_init_chain(first_desc); + printk("%02d %08x %08x %08x %08x %08x %08x %08x \n", gmacdev->RxDescCount, (u32)first_desc, first_desc->status, first_desc->length, first_desc->buffer1, first_desc->buffer2, first_desc->data1, first_desc->data2); + printk("Rx===================================================================Rx\n"); + + } + + gmacdev->RxNext = 0; + gmacdev->RxBusy = 0; + gmacdev->RxNextDesc = gmacdev->RxDesc; + gmacdev->RxBusyDesc = gmacdev->RxDesc; + + gmacdev->BusyRxDesc = 0; + + return -ESYNOPGMACNOERR; +} + +/** + * Driver Api to get the descriptor field information. + * This returns the status, dma-able address of buffer1, the length of buffer1, virtual address of buffer1 + * dma-able address of buffer2, length of buffer2, virtural adddress of buffer2. + * @param[in] pointer to DmaDesc structure. + * @param[out] pointer to status field fo descriptor. + * @param[out] dma-able address of buffer1. + * @param[out] length of buffer1. + * @param[out] virtual address of buffer1. + * @param[out] dma-able address of buffer2. + * @param[out] length of buffer2. + * @param[out] virtual address of buffer2. + * \return returns void. + */ +void gmac_get_desc_data(DmaDesc *desc, u32 *Status, u32 *Buffer1, u32 *Length1, u32 *Data1, u32 *Buffer2, u32 *Length2, u32 *Data2) +{ + + if(Status != 0) + *Status = desc->status; + + if(Buffer1 != 0) + *Buffer1 = desc->buffer1; + if(Length1 != 0) + *Length1 = (desc->length & DescSize1Mask) >> DescSize1Shift; + if(Data1 != 0) + *Data1 = desc->data1; + + if(Buffer2 != 0) + *Buffer2 = desc->buffer2; + if(Length2 != 0) + *Length2 = (desc->length & DescSize2Mask) >> DescSize2Shift; + if(Data2 != 0) + *Data2 = desc->data2; + + return; + +} + + + +/** + * This gives up the receive Descriptor queue in ring or chain mode. + * This function is tightly coupled to the platform and operating system + * Once device's Dma is stopped the memory descriptor memory and the buffer memory deallocation, + * is completely handled by the operating system, this call is kept outside the device driver Api. + * This function should be treated as an example code to de-allocate the descriptor structures in ring mode or chain mode + * and network buffer deallocation. + * This function depends on the pcidev structure for dma-able memory deallocation for both descriptor memory and the + * network buffer memory under linux. + * The responsibility of this function is to + * - Free the network buffer memory if any. + * - Fee the memory allocated for the descriptors. + * @param[in] pointer to gmac_device. + * @param[in] pointer to pci_device structure. + * @param[in] number of descriptor expected in rx descriptor queue. + * @param[in] whether descriptors to be created in RING mode or CHAIN mode. + * \return 0 upon success. Error code upon failure. + * \note No referece should be made to descriptors once this function is called. This function is invoked when the device is closed. + * note:cdh:check ok + */ +void giveup_rx_desc_queue(gmac_device *gmacdev, struct net_device *pnetdev, u32 desc_mode) +{ + s32 i; + + DmaDesc *first_desc = NULL; + dma_addr_t first_desc_dma_addr; + u32 status; + dma_addr_t dma_addr1; + dma_addr_t dma_addr2; + u32 length1; + u32 length2; + u32 data1; + u32 data2; + + if(desc_mode == RINGMODE) + { + for(i = 0; i < gmacdev->RxDescCount; i++) + { + gmac_get_desc_data(gmacdev->RxDesc + i, &status, &dma_addr1, &length1, &data1, &dma_addr2, &length2, &data2); + if((length1 != 0) && (data1 != 0)) + { + dma_unmap_single(NULL, dma_addr1, 0, DMA_FROM_DEVICE); + dev_kfree_skb((struct sk_buff *) data1); // free buffer1 + } + + if((length2 != 0) && (data2 != 0)) + { + dma_unmap_single(NULL, dma_addr2, 0, DMA_FROM_DEVICE); + dev_kfree_skb((struct sk_buff *) data2); //free buffer2 + } + } + + plat_free_consistent_mem(pnetdev, (sizeof(DmaDesc) * gmacdev->RxDescCount), gmacdev->RxDesc, gmacdev->RxDescDma); //free descriptors memory + TR("Memory allocated %08x for Rx Desriptors (ring) is given back\n", (u32)gmacdev->RxDesc); + } + else + { + TR("rx-------------------------------------------------------------------rx\n"); + first_desc = gmacdev->RxDesc; + first_desc_dma_addr = gmacdev->RxDescDma; + for(i = 0; i < gmacdev->RxDescCount; i++) + { + gmac_get_desc_data(first_desc, &status, &dma_addr1, &length1, &data1, &dma_addr2, &length2, &data2); + TR("%02d %08x %08x %08x %08x %08x %08x %08x\n", i, (u32)first_desc, first_desc->status, first_desc->length, first_desc->buffer1, first_desc->buffer2, first_desc->data1, first_desc->data2); + if((length1 != 0) && (data1 != 0)) + { + dma_unmap_single(NULL, dma_addr1, 0, DMA_FROM_DEVICE); + dev_kfree_skb((struct sk_buff *) data1); // free buffer1 + printk("(Chain mode) rx buffer1 %08x of size %d from %d rx descriptor is given back\n", data1, length1, i); + } + plat_free_consistent_mem(pnetdev, (sizeof(DmaDesc)), first_desc, first_desc_dma_addr); //free descriptors + TR("Memory allocated %08x for Rx Descriptor (chain) at %d is given back\n", data2, i); + + first_desc = (DmaDesc *)data2; + first_desc_dma_addr = dma_addr2; + } + + printk("rx-------------------------------------------------------------------rx\n"); + } + gmacdev->RxDesc = NULL; + gmacdev->RxDescDma = 0; + + return; +} + +/** + * This gives up the transmit Descriptor queue in ring or chain mode. + * This function is tightly coupled to the platform and operating system + * Once device's Dma is stopped the memory descriptor memory and the buffer memory deallocation, + * is completely handled by the operating system, this call is kept outside the device driver Api. + * This function should be treated as an example code to de-allocate the descriptor structures in ring mode or chain mode + * and network buffer deallocation. + * This function depends on the pcidev structure for dma-able memory deallocation for both descriptor memory and the + * network buffer memory under linux. + * The responsibility of this function is to + * - Free the network buffer memory if any. + * - Fee the memory allocated for the descriptors. + * @param[in] pointer to gmac_device. + * @param[in] pointer to pci_device structure. + * @param[in] number of descriptor expected in tx descriptor queue. + * @param[in] whether descriptors to be created in RING mode or CHAIN mode. + * \return 0 upon success. Error code upon failure. + * \note No reference should be made to descriptors once this function is called. This function is invoked when the device is closed. + * note:cdh:check ok + */ +void giveup_tx_desc_queue(gmac_device *gmacdev, struct net_device *pnetdev, u32 desc_mode) +{ + s32 i; + + DmaDesc *first_desc = NULL; + dma_addr_t first_desc_dma_addr; + u32 status; + dma_addr_t dma_addr1; + dma_addr_t dma_addr2; + u32 length1; + u32 length2; + u32 data1; + u32 data2; + + if(desc_mode == RINGMODE) + { + for(i = 0; i < gmacdev->TxDescCount; i++) + { + gmac_get_desc_data(gmacdev->TxDesc + i, &status, &dma_addr1, &length1, &data1, &dma_addr2, &length2, &data2); + if((length1 != 0) && (data1 != 0)) + { + dma_unmap_single(NULL, dma_addr1, 0, DMA_TO_DEVICE); + dev_kfree_skb((struct sk_buff *) data1); // free buffer1 + TR("(Ring mode) tx buffer1 %08x of size %d from %d rx descriptor is given back\n", data1, length1, i); + } + if((length2 != 0) && (data2 != 0)) + { + dma_unmap_single(NULL, dma_addr2, 0, DMA_TO_DEVICE); + dev_kfree_skb((struct sk_buff *) data2); //free buffer2 + TR("(Ring mode) tx buffer2 %08x of size %d from %d rx descriptor is given back\n", data2, length2, i); + } + } + plat_free_consistent_mem(pnetdev, (sizeof(DmaDesc) * gmacdev->TxDescCount), gmacdev->TxDesc, gmacdev->TxDescDma); //free descriptors + TR("Memory allocated %08x for Tx Desriptors (ring) is given back\n", (u32)gmacdev->TxDesc); + } + else + { + printk("tx-------------------------------------------------------------------tx\n"); + first_desc = gmacdev->TxDesc; + first_desc_dma_addr = gmacdev->TxDescDma; + for(i = 0; i < gmacdev->TxDescCount; i++) + { + gmac_get_desc_data(first_desc, &status, &dma_addr1, &length1, &data1, &dma_addr2, &length2, &data2); + TR("%02d %08x %08x %08x %08x %08x %08x %08x\n", i, (u32)first_desc, first_desc->status, first_desc->length, first_desc->buffer1, first_desc->buffer2, first_desc->data1, first_desc->data2); + if((length1 != 0) && (data1 != 0)) + { + dma_unmap_single(NULL, dma_addr1, 0, DMA_TO_DEVICE); + dev_kfree_skb((struct sk_buff *) data2); // free buffer1 + printk("(Chain mode) tx buffer1 %08x of size %d from %d rx descriptor is given back\n", data1, length1, i); + } + plat_free_consistent_mem(pnetdev, (sizeof(DmaDesc)), first_desc, first_desc_dma_addr); //free descriptors + TR("Memory allocated %08x for Tx Descriptor (chain) at %d is given back\n", data2, i); + + first_desc = (DmaDesc *)data2; + first_desc_dma_addr = dma_addr2; + } + TR("tx-------------------------------------------------------------------tx\n"); + + } + gmacdev->TxDesc = NULL; + gmacdev->TxDescDma = 0; + + return; +} + +/** + * This enables the pause frame generation after programming the appropriate registers. + * presently activation is set at 3k and deactivation set at 4k. These may have to tweaked + * if found any issues + * @param[in] pointer to gmac_device. + * \return void. + * note:cdh:check ok + */ +static void gmac_pause_control(gmac_device *gmacdev) +{ + u32 omr_reg; + u32 mac_flow_control_reg; + omr_reg = gmac_read_reg((u32 *)gmacdev->DmaBase, DmaControl); + omr_reg |= DmaRxFlowCtrlAct4K | DmaRxFlowCtrlDeact5K | DmaEnHwFlowCtrl; + gmac_write_reg((u32 *)gmacdev->DmaBase, DmaControl, omr_reg); + + mac_flow_control_reg = gmac_read_reg((u32 *)gmacdev->MacBase, GmacFlowControl); + mac_flow_control_reg |= GmacRxFlowControl | GmacTxFlowControl | 0xFFFF0000; + gmac_write_reg((u32 *)gmacdev->MacBase, GmacFlowControl, mac_flow_control_reg); + + return; + +} + + +/** + * Disable the ip checksum offloading in receive path. + * Ip checksum offloading is disabled in the receive path. + * @param[in] pointer to gmac_device. + * \return returns void. + */ +void gmac_disable_rx_Ipchecksum_offload(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacConfig, GmacRxIpcOffload); +} + + +/** + * Instruct the DMA not to drop the packets even if it fails tcp ip checksum. + * This is to instruct the receive DMA engine to allow the packets even if recevied packet + * fails the tcp/ip checksum in hardware. Valid only when full checksum offloading is enabled(type-2). + * @param[in] pointer to gmac_device. + * \return returns void. + */ +void gmac_rx_tcpip_chksum_drop_disable(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->DmaBase, DmaControl, DmaDisableDropTcpCs); + return; +} + +/** + * The check summ offload engine is enabled to do only IPV4 header checksum. + * IPV4 header Checksum is computed in the Hardware. + * @param[in] pointer to gmac_device. + * @param[in] Pointer to tx descriptor for which ointer to gmac_device. + * \return returns void. + */ +void gmac_tx_checksum_offload_ipv4hdr(gmac_device *gmacdev, DmaDesc *desc) +{ +#ifdef ENH_DESC + desc->status = ((desc->status & (~DescTxCisMask)) | DescTxCisIpv4HdrCs);//ENH_DESC +#else + desc->length = ((desc->length & (~DescTxCisMask)) | DescTxCisIpv4HdrCs); +#endif + +} + +/** + * The check summ offload engine is enabled to do TCPIP checsum assuming Pseudo header is available. + * Hardware computes the tcp ip checksum assuming pseudo header checksum is computed in software. + * Ipv4 header checksum is also inserted. + * @param[in] pointer to gmac_device. + * @param[in] Pointer to tx descriptor for which ointer to gmac_device. + * \return returns void. + */ +void gmac_tx_checksum_offload_tcponly(gmac_device *gmacdev, DmaDesc *desc) +{ +#ifdef ENH_DESC + desc->status = ((desc->status & (~DescTxCisMask)) | DescTxCisTcpOnlyCs);//ENH_DESC +#else + desc->length = ((desc->length & (~DescTxCisMask)) | DescTxCisTcpOnlyCs); +#endif + +} + +/** + * Populate the tx desc structure with the buffer address. + * Once the driver has a packet ready to be transmitted, this function is called with the + * valid dma-able buffer addresses and their lengths. This function populates the descriptor + * and make the DMA the owner for the descriptor. This function also controls whetther Checksum + * offloading to be done in hardware or not. + * This api is same for both ring mode and chain mode. + * @param[in] pointer to gmac_device. + * @param[in] Dma-able buffer1 pointer. + * @param[in] length of buffer1 (Max is 2048). + * @param[in] virtual pointer for buffer1. + * @param[in] Dma-able buffer2 pointer. + * @param[in] length of buffer2 (Max is 2048). + * @param[in] virtual pointer for buffer2. + * @param[in] u32 data indicating whether the descriptor is in ring mode or chain mode. + * @param[in] u32 indicating whether the checksum offloading in HW/SW. + * \return returns present tx descriptor index on success. Negative value if error. + * note:cdh:check ok + */ +static s32 gmac_set_tx_qptr(gmac_device *gmacdev, u32 Buffer1, u32 Length1, u32 Data1, u32 Buffer2, u32 Length2, u32 Data2, u32 offload_needed) +{ + u32 txnext = gmacdev->TxNext; + DmaDesc *txdesc = gmacdev->TxNextDesc; + + if(!gmac_is_desc_empty(txdesc)) + return -1; + + (gmacdev->BusyTxDesc)++; //busy tx descriptor is incremented by one as it will be handed over to DMA + + if(gmac_is_tx_desc_chained(txdesc)) + { + txdesc->length |= ((Length1 << DescSize1Shift) & DescSize1Mask); + txdesc->length |= (DescTxFirst | DescTxLast | DescTxIntEnable); //Its always assumed that complete data will fit in to one descriptor + txdesc->buffer1 = Buffer1; + txdesc->data1 = Data1; + + if(offload_needed) + { + /** + *Make sure that the OS you are running supports the IP and TCP checkusm offloaidng, + *before calling any of the functions given below. + */ + gmac_tx_checksum_offload_ipv4hdr(gmacdev, txdesc); + gmac_tx_checksum_offload_tcponly(gmacdev, txdesc); + //gmac_tx_checksum_offload_tcp_pseudo(gmacdev, txdesc); + } + + txdesc->status = DescOwnByDma; + + gmacdev->TxNext = gmac_is_last_tx_desc(gmacdev, txdesc) ? 0 : txnext + 1; + gmacdev->TxNextDesc = (DmaDesc *)txdesc->data2; + } + else + { + txdesc->length |= (((Length1 << DescSize1Shift) & DescSize1Mask) | ((Length2 << DescSize2Shift) & DescSize2Mask)); + txdesc->length |= (DescTxFirst | DescTxLast | DescTxIntEnable); //Its always assumed that complete data will fit in to one descriptor + txdesc->buffer1 = Buffer1; + txdesc->data1 = Data1; + + txdesc->buffer2 = Buffer2; + txdesc->data2 = Data2; + + if(offload_needed) + { + /** + *cdh:Make sure that the OS you are running supports the IP and TCP checkusm offloaidng, + *cdh:before calling any of the functions given below. + */ + gmac_tx_checksum_offload_ipv4hdr(gmacdev, txdesc); + gmac_tx_checksum_offload_tcponly(gmacdev, txdesc); + //gmac_tx_checksum_offload_tcp_pseudo(gmacdev, txdesc); + } + + txdesc->status = DescOwnByDma; + + gmacdev->TxNext = gmac_is_last_tx_desc(gmacdev, txdesc) ? 0 : txnext + 1; + gmacdev->TxNextDesc = gmac_is_last_tx_desc(gmacdev, txdesc) ? gmacdev->TxDesc : (txdesc + 1); + } + + //printk("(set tx)%02d %08x %08x %08x %08x %08x %08x %08x\n", txnext, (u32)txdesc, txdesc->status, txdesc->length, txdesc->buffer1, txdesc->buffer2, txdesc->data1, txdesc->data2); + + return txnext; +} + +/** + * Checks and initialze phy. + * This function checks whether the phy initialization is complete. + * @param[in] pointer to gmac_device. + * \return 0 if success else returns the error number. + */ +static s32 gmac_check_phy_init(gmac_device *gmacdev) +{ + u16 data; + u16 data2; + s32 status = -ESYNOPGMACNOERR; + s32 loop_count; + u16 tmp_speed = 0; + u16 data_id1; + u16 data_id2; + + /** cdh:instruction + *Auto-Negotiation + *The KSZ8041NL conforms to the auto-negotiation protocol, defined in Clause 28 of the IEEE 802.3u specification. Autonegotiation + *is enabled by either hardware pin strapping (pin 30) or software (register 0h bit 12). + */ + loop_count = 10; //cdh:DEFAULT_LOOP_VARIABLE; + while(loop_count-- > 0) + { + status = gmac_read_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, PHY_STATUS_REG, &data); + if(status) + return status; + + if((data & Mii_AutoNegCmplt) != 0){ + printk("Autonegotiation Complete\n"); // cdh:check ok + break; + }else{ + ; //TR("waiting AN finish ...\n"); + } + } + status = gmac_read_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, PHY_ID_HI_REG, &data_id1); + if(status) { + printk("phy read phy id1 waiting Error,data=0x%x\n", data_id1); + }else { + printk("phy read phy id1 waiting ok,data=0x%x\n", data_id1); + } + + status = gmac_read_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, PHY_ID_LOW_REG, &data_id2); + if(status) { + printk("phy read phy id2 waiting Error,data=0x%x\n", data_id2); + }else { + printk("phy read phy id2 waiting ok,data=0x%x\n", data_id2); + } + + if(data_id1 == 0x0 && data_id2 == 0x128) { + status = gmac_read_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, PHY_SPECIFIC_STATUS_REG, &data); + if(status) { + return status; + } + if((data & Mii_phy_status_link_up) == 0) // cdh:Mii_phy_status_link_up + { + printk("No Link\n"); + gmacdev->LinkState = LINKDOWN; + return -ESYNOPGMACPHYERR; + } + else + { + gmacdev->LinkState = LINKUP; + printk("Link UP\n"); + mdelay(500); + } + }else{ + // cdh:note may be for same special phy define register, not same 8201F phy,cdh: PHY_SPECIFIC_STATUS_REG + status = gmac_read_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, PHY_STATUS_REG, &data); + if(status) { + return status; + } + + if((data & Mii_Link) == 0) // cdh:Mii_phy_status_link_up + { + printk("No Link\n"); + gmacdev->LinkState = LINKDOWN; + return -ESYNOPGMACPHYERR; + } + else + { + gmacdev->LinkState = LINKUP; + printk("Link UP\n"); + } + } + + // cdh:get other end partner AN ability from Reg6.0 + status = gmac_read_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, PHY_AN_EXP_REG, &data); + if(status) { + return status; + } + + if((data&OTHER_END_PARTNER_AN_ABITY) == OTHER_END_PARTNER_AN_ABITY) { + //printk("Local&Partner Support AN Ability\n"); + + // cdh:read phy Reg4[8:5]&Reg5[8:5] for speed + status = gmac_read_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, PHY_AN_ADV_REG, &data); + if(status) { + return status; + } + + status = gmac_read_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, PHY_LNK_PART_ABl_REG, &data2); + if(status) { + return status; + } + + tmp_speed = ((data>>5)&(data2>>5))&0xF; // cdh:get Reg4[8:5] for loacl AN speed + + if(((tmp_speed&SPEED_100M_FULL) == SPEED_100M_FULL) || ((tmp_speed&SPEED_100M_HALF) == SPEED_100M_HALF)){ + // cdh:set chip system ctrl reg base address + psysbase = AK_VA_SYSCTRL; + REG32(psysbase + 0x14) |= (0x1 << 23); // cdh:first mac_speed_cfg=1(100m) + + gmacdev->Speed = SPEED100; + if(tmp_speed&SPEED_100M_FULL){ + gmacdev->DuplexMode = FULLDUPLEX; + printk("LPAN Link is with 100M Speed, FULLDUPLEX mode\n"); + }else{ + gmacdev->DuplexMode = HALFDUPLEX; + printk("LPAN Link is with 100M Speed, HALFDUPLEX mode\n"); + } + }else { + // cdh:set chip system ctrl reg base address + psysbase = AK_VA_SYSCTRL; + REG32(psysbase + 0x14) &= ~(0x1 << 23); // cdh:first mac_speed_cfg=0(10m) + + gmacdev->Speed = SPEED10; + if(tmp_speed&SPEED_10M_FULL){ + gmacdev->DuplexMode = FULLDUPLEX; + printk("LPAN Link is with 10M Speed, FULLDUPLEX mode\n"); + }else{ + gmacdev->DuplexMode = HALFDUPLEX; + printk("LPAN Link is with 10M Speed, HALFDUPLEX mode\n"); + } + } + + }else{ + //printk("Only Local Support AN Ability\n"); + + // cdh:phy Reg0.12 == 0 ? , AN disable + status = gmac_read_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, PHY_CONTROL_REG, &data); + if(status) { + return status; + } + + if ((data&Mii_AN_En) != Mii_AN_En){ + if(data&(0x1<<13)){ + // cdh:set chip system ctrl reg base address + psysbase = AK_VA_SYSCTRL; + REG32(psysbase + 0x14) |= (0x1 << 23); // cdh:first mac_speed_cfg=1(100m) + + gmacdev->Speed = SPEED100; + if(data&(0x1<<8)){ + gmacdev->DuplexMode = FULLDUPLEX; + printk("NOAN Link is with 100M Speed, FULLDUPLEX mode\n"); + }else{ + gmacdev->DuplexMode = HALFDUPLEX; + printk("NOAN Link is with 100M Speed, HALFDUPLEX mode\n"); + } + }else{ + // cdh:set chip system ctrl reg base address + psysbase = AK_VA_SYSCTRL; + REG32(psysbase + 0x14) &= ~(0x1 << 23); // cdh:first mac_speed_cfg=0(10m) + + gmacdev->Speed = SPEED10; + if(data&(0x1<<8)){ + gmacdev->DuplexMode = FULLDUPLEX; + printk("NOAN Link is with 10M Speed, FULLDUPLEX mode\n"); + }else{ + gmacdev->DuplexMode = HALFDUPLEX; + printk("NOAN Link is with 10M Speed, HALFDUPLEX mode\n"); + } + } + }else{ + + // cdh:read phy Reg5[8,6] for speed + status = gmac_read_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, PHY_LNK_PART_ABl_REG, &data); + if(status) { + return status; + } + + if((data&NAN_SPEED_100M_HALF) == NAN_SPEED_100M_HALF){ + // cdh:set chip system ctrl reg base address + psysbase = AK_VA_SYSCTRL; + REG32(psysbase + 0x14) |= (0x1 << 23); // cdh:first mac_speed_cfg=1(100m) + + gmacdev->Speed = SPEED100; + gmacdev->DuplexMode = HALFDUPLEX; + printk("LAN Link is with 100M Speed, HALFDUPLEX mode\n"); + }else{ + // cdh:set chip system ctrl reg base address + psysbase = AK_VA_SYSCTRL; + REG32(psysbase + 0x14) &= ~(0x1 << 23); // cdh:first mac_speed_cfg=0(10m) + + gmacdev->Speed = SPEED10; + gmacdev->DuplexMode = HALFDUPLEX; + printk("LAN Link is with 10M Speed, HALFDUPLEX mode\n"); + } + } + } + + return -ESYNOPGMACNOERR; +} + +static s32 phy_wait_idle(gmac_device *gmacdev) +{ + u32 i; + s32 status; + u16 data; + + // cdh:wait phy idle, reset cycle < 0.5s + for(i=0; i<200; i++){ + status = gmac_read_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, PHY_CONTROL_REG, &data); + if(status) { + continue; + } + + // cdh:Mii_reset=1(soft_reset), or=0(normal), self-clearing + if((data & Mii_reset) == 0){ + printk("phy reset OK\n"); + break; + } + } + + if(i>=200){ + printk("timeout in waiting phy to idle!\n"); + } + printk("phy is idle!\n"); + + return status; +} + +static void gmac_phy_gpio_reset(struct ak_mac_data *pdata) +{ + printk("gpio phy reset pin:%d, value:%d\n", pdata->phy_rst_gpio.pin, pdata->phy_rst_gpio.value); + ak_setpin_as_gpio(pdata->phy_rst_gpio.pin); + ak_gpio_cfgpin(pdata->phy_rst_gpio.pin, AK_GPIO_DIR_OUTPUT); + ak_gpio_setpin(pdata->phy_rst_gpio.pin, !pdata->phy_rst_gpio.value); + #if 1 + mdelay(5); + ak_gpio_setpin(pdata->phy_rst_gpio.pin, pdata->phy_rst_gpio.value); + mdelay(200); + ak_gpio_setpin(pdata->phy_rst_gpio.pin, !pdata->phy_rst_gpio.value); + mdelay(5); + #endif + #if 0 + mdelay(5); + ak_gpio_setpin(pdata->phy_rst_gpio.pin, pdata->phy_rst_gpio.value); + mdelay(25); + ak_gpio_setpin(pdata->phy_rst_gpio.pin, !pdata->phy_rst_gpio.value); + mdelay(5); + #endif +} + + +static s32 gmac_phy_init(gmac_device *gmacdev, struct ak_mac_data *pdata_phy) +{ + s32 status; + u16 data_ctl; + u16 data_id1; + u16 data_id2; + + /* reset */ + gmac_phy_gpio_reset(pdata_phy); + + // cdh:wait phy idle + if(phy_wait_idle(gmacdev)) { + printk("cdh:%s,phy_wait_idle err!\n", __func__); + }else { + printk("cdh:%s,phy_wait_idle ok!\n", __func__); + } + + mdelay(200); + status = gmac_read_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, PHY_CONTROL_REG, &data_ctl); + if(status) { + printk("phy read phy ctrl waiting Error\n"); + } + printk("BMCR:0x%x\n", data_ctl); + + status = gmac_read_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, PHY_ID_HI_REG, &data_id1); + if(status) { + printk("phy read phy id1 waiting Error,data=0x%x\n", data_id1); + }else { + printk("phy read phy id1 waiting ok,data=0x%x\n", data_id1); + } + + status = gmac_read_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, PHY_ID_LOW_REG, &data_id2); + if(status) { + printk("phy read phy id2 waiting Error,data=0x%x\n", data_id2); + }else { + printk("phy read phy id2 waiting ok,data=0x%x\n", data_id2); + } + + + if(data_id1 == 0x22) { + printk("cdh:id 0x22 set!\n"); + gmac_read_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, PHY_CONTROL_REG, &data_ctl); + data_ctl |= 0x1000; // cdh:enable auto-negotiation process + gmac_write_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, PHY_CONTROL_REG, data_ctl); + + // cdh:reg_0x1b, bit[8](enable link up interrupt)=1 , bit[10](enable link down interrupt)=1 + //gmac_write_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, PHY_EXT_PHY_SPC_STATUS, 0x0500); + //gmac_write_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, 0x1F, 0x8300); + } + else { + printk("cdh:id other set!\n"); + gmac_read_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, PHY_CONTROL_REG, &data_ctl); + data_ctl |= 0x1100; // cdh:bit12 enable auto-negotiation process, set full-duplex mode + gmac_write_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, PHY_CONTROL_REG, data_ctl); +#if 1 + printk("###anyka phy set write!\n"); + data_ctl = 0x01; + gmac_write_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, PHY_EXT_PHY_SPC_CTRL, data_ctl); + gmac_read_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, 23, &data_ctl); + + printk("read page 1 reg 23 : 0x%x\n", data_ctl); + //set 13bit = 0 normal mode + data_ctl &= 0xdfff; + + //set 13bit = 1 loopback mode + //data_ctl |= (1<<13); + gmac_write_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, 23, data_ctl); +#endif + + } + + if(data_id1 == 0x0 && data_id2 == 0x128) { + printk("cdh:id 0x128 set!\n"); + + gmac_write_phy_reg((u32 *)gmacdev->MacBase,gmacdev->PhyBase, 0x0000,0x9140); + gmac_read_phy_reg((u32 *)gmacdev->MacBase,gmacdev->PhyBase, 0x0000, &data_ctl); + printk("read reg 0x00 : 0x%x\n",data_ctl); + + gmac_write_phy_reg((u32 *)gmacdev->MacBase,gmacdev->PhyBase, 0x001e,0x50); + gmac_read_phy_reg((u32 *)gmacdev->MacBase,gmacdev->PhyBase, 0x001f, &data_ctl); + printk("read reg 0x50 : 0x%x\n",data_ctl); + data_ctl |= 0x0040; + gmac_write_phy_reg((u32 *)gmacdev->MacBase,gmacdev->PhyBase, 0x001e, 0x50); + gmac_write_phy_reg((u32 *)gmacdev->MacBase,gmacdev->PhyBase, 0x001f, data_ctl); + printk("write reg 0x50 : 0x%x\n",data_ctl); + gmac_write_phy_reg((u32 *)gmacdev->MacBase,gmacdev->PhyBase, 0x001e,0x00); + + } + + return 0; + +} + + + +/** + * Function used to detect the cable plugging and unplugging. + * This function gets scheduled once in every second and polls + * the PHY register for network cable plug/unplug. Once the + * connection is back the GMAC device is configured as per + * new Duplex mode and Speed of the connection. + * @param[in] u32 type but is not used currently. + * \return returns void. + * \note This function is tightly coupled with Linux 2.6.xx. + * \callgraph + */ + +static void timer_cable_unplug_proc(u32 notused) +{ + s32 status; + u16 data; + nt_adapter *adapter = (nt_adapter *)timer_cable_unplug.data; + gmac_device *gmacdev = adapter->gmacdev_pt; + + init_timer(&timer_cable_unplug); + timer_cable_unplug.expires = CHECK_TIME + jiffies; + status = gmac_read_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, PHY_STATUS_REG, &data); + if (status) { + printk("cdh:%s, read phy err,status:0x%x\n", __func__, status); + return; + } + + if((data & Mii_Link) == 0) + { + //printk("No Link: %08x\n", data); + gmacdev->LinkState = 0; + gmacdev->DuplexMode = 0; + gmacdev->Speed = 0; + gmacdev->LoopBackMode = 0; + add_timer(&timer_cable_unplug); + netif_carrier_off(adapter->netdev_pt); // cdh:add date 2015.8.29 + } + else + { + //printk("Link UP: %08x\n", data); + if(!gmacdev->LinkState) + { + // cdh:status = gmac_phy_init(gmacdev); + gmac_check_phy_init(gmacdev); + gmac_mac_init(gmacdev); + // cdh:gmac_promisc_enable(gmacdev); + } + add_timer(&timer_cable_unplug); + netif_carrier_on(adapter->netdev_pt); // cdh:add date 2015.8.29 + } + +} + +/** + * Clears all the pending interrupts. + * If the Dma status register is read then all the interrupts gets cleared + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ +static void gmac_clear_interrupt(gmac_device *gmacdev) +{ + u32 data; + data = gmac_read_reg((u32 *)gmacdev->DmaBase, DmaStatus); + gmac_write_reg((u32 *)gmacdev->DmaBase, DmaStatus , data); +} + +/** + * Disable the MMC Tx interrupt. + * The MMC tx interrupts are masked out as per the mask specified. + * @param[in] pointer to gmac_device. + * @param[in] tx interrupt bit mask for which interrupts needs to be disabled. + * \return returns void. + * note:cdh:check ok + */ +static void gmac_disable_mmc_tx_interrupt(gmac_device *gmacdev, u32 mask) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacMmcIntrMaskTx, mask); + return; +} + +/** + * Enable the MMC Tx interrupt. + * The MMC tx interrupts are enabled as per the mask specified. + * @param[in] pointer to gmac_device. + * @param[in] tx interrupt bit mask for which interrupts needs to be enabled. + * \return returns void. + */ +void gmac_enable_mmc_tx_interrupt(gmac_device *gmacdev, u32 mask) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacMmcIntrMaskTx, mask); +} +/** + * Disable the MMC Rx interrupt. + * The MMC rx interrupts are masked out as per the mask specified. + * @param[in] pointer to gmac_device. + * @param[in] rx interrupt bit mask for which interrupts needs to be disabled. + * \return returns void. + * note:cdh:check ok + */ +static void gmac_disable_mmc_rx_interrupt(gmac_device *gmacdev, u32 mask) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacMmcIntrMaskRx, mask); + return; +} +/** + * Enable the MMC Rx interrupt. + * The MMC rx interrupts are enabled as per the mask specified. + * @param[in] pointer to gmac_device. + * @param[in] rx interrupt bit mask for which interrupts needs to be enabled. + * \return returns void. + */ +void gmac_enable_mmc_rx_interrupt(gmac_device *gmacdev, u32 mask) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacMmcIntrMaskRx, mask); + return; +} + +/** + * Disable the MMC ipc rx checksum offload interrupt. + * The MMC ipc rx checksum offload interrupts are masked out as per the mask specified. + * @param[in] pointer to gmac_device. + * @param[in] rx interrupt bit mask for which interrupts needs to be disabled. + * \return returns void. + * note:cdh:check ok + */ +static void gmac_disable_mmc_ipc_rx_interrupt(gmac_device *gmacdev, u32 mask) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacMmcRxIpcIntrMask, mask); + return; +} +/** + * Enable the MMC ipc rx checksum offload interrupt. + * The MMC ipc rx checksum offload interrupts are enabled as per the mask specified. + * @param[in] pointer to gmac_device. + * @param[in] rx interrupt bit mask for which interrupts needs to be enabled. + * \return returns void. + */ +void gmac_enable_mmc_ipc_rx_interrupt(gmac_device *gmacdev, u32 mask) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacMmcRxIpcIntrMask, mask); + return; +} + + +/* +*cdh:init share pin , mac controller clock and 25M_phy clock +*/ +static void gmac_ctrl_init(struct ak_mac_data *pdata) +{ + printk("Reset MAC controller!\n"); + ak_soft_reset(AK39_SRESET_MAC); + + printk("Reset MAC phy!\n"); + printk("phy reset pin:%d, value:%d\n", pdata->phy_rst_gpio.pin, pdata->phy_rst_gpio.value); + ak_setpin_as_gpio(pdata->phy_rst_gpio.pin); + ak_gpio_cfgpin(pdata->phy_rst_gpio.pin, AK_GPIO_DIR_OUTPUT); + ak_gpio_setpin(pdata->phy_rst_gpio.pin, !pdata->phy_rst_gpio.value); + mdelay(5); + ak_gpio_setpin(pdata->phy_rst_gpio.pin, pdata->phy_rst_gpio.value); + mdelay(25); + ak_gpio_setpin(pdata->phy_rst_gpio.pin, !pdata->phy_rst_gpio.value); + mdelay(5); +} + + +/** + * Function to set the MDC clock for mdio transactiona + * + * @param[in] pointer to device structure. + * @param[in] clk divider value. + * \return Reuturns 0 on success else return the error value. + * note:cdh:check ok + */ +s32 gmac_set_mdc_clk_div(gmac_device *gmacdev, u32 clk_div_val) +{ + u32 orig_data; + + // cdh:set MDO_CLK for MDIO transmit, note GmacGmiiAddr bit5, and 802.3 limit 2.5MHz + orig_data = gmac_read_reg((u32 *)gmacdev->MacBase, GmacGmiiAddr); //set the mdc clock to the user defined value + orig_data &= (~ GmiiCsrClkMask); // cdh:csr clk bit[5:2], must 0x3C + orig_data |= (clk_div_val | GmiiWrite | GmiiBusy); + gmac_write_reg((u32 *)gmacdev->MacBase, GmacGmiiAddr , orig_data); + return 0; +} + +/** + * Returns the current MDC divider value programmed in the ip. + * + * @param[in] pointer to device structure. + * @param[in] clk divider value. + * \return Returns the MDC divider value read. + */ +u32 synopGMAC_get_mdc_clk_div(gmac_device *gmacdev) +{ + u32 data; + data = gmac_read_reg((u32 *)gmacdev->MacBase, GmacGmiiAddr); + data &= GmiiCsrClkMask; + return data; +} + + + +static void mac_phy_reset(struct ak_mac_data *pdata) +{ + /* cdh:check ok, first set phy level low */ + pdata->gpio_init(&pdata->phy_rst_gpio); + mdelay(10); + + /* cdh:check ok, second set gpio as input and be in powersave */ + ak_gpio_cfgpin(pdata->phy_rst_gpio.pin, !pdata->phy_rst_gpio.dir); + mdelay(1); +} + +static int gmac_sharepin_clock_init(void) +{ + int i = 0; + + // cdh:setep1, ========set all mii interface share pin,=============== + REG32(psysbase + 0x7C) &= ~(0xf << 0); + REG32(psysbase + 0x7C) |= (0x1 << 0); // cdh:gpio10,bit[1:0]=01, set gmii_mdc + REG32(psysbase + 0x7C) |= (0x1 << 2); // cdh:gpio11,bit[3:2]=01, set gmii_mdio + REG32(psysbase + 0x7C) &= ~(0x1 << 4); // cdh:gpio12, + REG32(AK_VA_GPIO + 0x00) &= ~(0x1 << 12); // cdh:gpio12, input + + REG32(psysbase + 0x7C) &= ~(0x3 << 5); + REG32(psysbase + 0x7C) |= (0x1 << 5); // cdh:gpio13,bit[6:5]=01, set gmii_txen + REG32(psysbase + 0x7C) |= (0x1 << 7); // cdh:gpio76,bit[7]=1, set gmii_txclk + + REG32(psysbase + 0x7C) &= ~(0xf << 8); + REG32(psysbase + 0x7C) |= (0x1 << 8); // cdh:gpio14,bit[9:8]=01, set gmii_txd0 + REG32(psysbase + 0x7C) |= (0x1 << 10); // cdh:gpio15,bit[11:10]=01, set gmii_txd1 + REG32(psysbase + 0x7C) |= (0x1 << 12); // cdh:gpio16,bit[12]=1,set gmii_txd2 + REG32(psysbase + 0x7C) |= (0x1 << 13); // cdh:gpio17,bit[13]=1,set gmii_txd3 + REG32(psysbase + 0x7C) |= (0x1 << 14); // cdh:gpio18,bit[14]=1,set gmii_crs + REG32(psysbase + 0x7C) |= (0x1 << 15); // cdh:gpio77,bit[15]=1,set gmii_rxclk + + REG32(psysbase + 0x7C) &= ~(0xf << 16); + REG32(psysbase + 0x7C) |= (0x1 << 16); // cdh:gpio19,bit[17:16]=01, set gmii_rxd0 + REG32(psysbase + 0x7C) |= (0x1 << 18); // cdh:gpio20,bit[19:18]=01, set gmii_rxd1 + REG32(psysbase + 0x7C) |= (0x1 << 20); // cdh:gpio21,bit[20]=1, set gmii_rxd2 + REG32(psysbase + 0x7C) |= (0x1 << 21); // cdh:gpio22,bit[21]=1, set gmii_rxd3 + + REG32(psysbase + 0x7C) &= ~(0xf << 22); + REG32(psysbase + 0x7C) |= (0x1 << 22); // cdh:gpio23,bit[23:22]=01, set gmii_rxer + REG32(psysbase + 0x7C) |= (0x1 << 24); // cdh:gpio24,bit[25:24]=01, set gmii_rxdv + REG32(psysbase + 0x7C) |= (0x1 << 26); // cdh:gpio78,bit[26]=1, set gmii_col + + // cdh:step2,===h2 check ok, to enable the 25MHz oscillator========= + REG32(psysbase + 0x74) &= ~(3 << 9); // cdh:h2 bit [10:9]=01, set sharepin gpio47 share as opclk, old (3 << 2) + REG32(psysbase + 0x74) |= (1 << 9); // cdh:old (1 << 2) + REG32(psysbase + 0x80) |= (1 << 8); // cdh:disable gpio47 pull down, old (1 << 2) + + // cdh:step3,===first set Host mac interface select mii, mac_speed_cfg=1(100m)==== + REG32(psysbase + 0x14) |= ((0x1 << 22)|(0x1 << 23)); + REG32(psysbase + 0x14) &= ~(0x1 << 15); // cdh:clear bit[15], prohibit 25m crystal + REG32(psysbase + 0x14) |= ((0x1 << 16)|(0x1 << 18)); // cdh:set bit[16],enable div24, generate 25m, bit[18], select 25m clock of mac from pll div + REG32(psysbase + 0x1c) &= ~(1 << 13); // cdh:mac clk ctrl + + // cdh:bit[20]:select mac 25M clock from 25M crystal or pll div,here do what's mean? ,pg said at least repeat twice + for(i=0;i<6;i++) + { + REG32(psysbase + 0x14) |= (1 << 20); // cdh:select 25m crystal pad, what's mean? + REG32(psysbase + 0x14) &= ~(1 << 20); // cdh:select 25m clock input, what's mean? + } + + return 0; +} + +static int mac_init_hw(struct ak_mac_data *pdata) +{ +#if 0 + ak_group_config(ePIN_AS_MAC); +#else + gmac_sharepin_clock_init(); +#endif + + if (pdata != NULL) { + /* init mac power on */ + if (pdata->pwr_gpio.pin > 0) + pdata->gpio_init(&pdata->pwr_gpio); + + /* init the gpio for phy */ + if (pdata->phy_rst_gpio.pin > 0) + mac_phy_reset(pdata); + } + + return 0; +} + +/* init the mac and the phy */ +static s32 init_hw(struct net_device *ndev) +{ + signed int ijk; + signed int status = 0; + unsigned int dma_addr; + u8 mac_addr0[6] = DEFAULT_MAC_ADDRESS; + struct sk_buff *skb; // cdh:#include , check ok + nt_adapter *adapter; + mac_info_t *db; + gmac_device *gmacdev; + struct ak_mac_data *pdata; + + // TR("%s called \n", __FUNCTION__); + adapter = (nt_adapter *)netdev_priv(ndev); + db = adapter->db_pt; + gmacdev = (gmac_device *)adapter->gmacdev_pt; + pdata = adapter->db_pt->dev->platform_data; + + // initial mac hw support + mac_init_hw(db->dev->platform_data); + + /*cdh:Now platform dependent initialization.cdh:check ok*/ + gmac_mmc_counters_reset(gmacdev); // cdh:Mac Management Counters (MMC), cdh:check ok + gmac_mmc_counters_disable_rollover(gmacdev); //cdh:check ok + + /*Lets reset the IP*/ + printk("adapter= %08x gmacdev = %08x netdev = %08x netdev= %08x\n", (u32)adapter, (u32)gmacdev, (u32)ndev, (u32)ndev); + + // cdh:software reset , the resets all of the GMAC internal registers and logic, cdh:check ok + gmac_reset(gmacdev); + + /* cdh:Program/flash in the station/IP's Mac address */ + gmac_set_mac_addr(gmacdev, GmacAddr0High, GmacAddr0Low, mac_addr0); + + /* cdh:Lets read the version of ip, cdh:check ok */ + gmac_read_version(gmacdev); + + /* cdh:Lets set ipaddress in to device structure, cdh:check ok*/ + gmac_get_mac_addr(gmacdev, GmacAddr0High, GmacAddr0Low, ndev->dev_addr); + + /* cdh:check ok,Now set the broadcast address*/ + for(ijk = 0; ijk < 6; ijk++) + { + ndev->broadcast[ijk] = 0xff; + } + + for(ijk = 0; ijk < 6; ijk++) + { + printk("netdev->dev_addr[%d] = %02x and netdev->broadcast[%d] = %02x\n", ijk, ndev->dev_addr[ijk], ijk, ndev->broadcast[ijk]); + } + + /*Check for Phy initialization*/ + // cdh:set MDO_CLK for MDIO transmit, note GmacGmiiAddr bit5, and 802.3 limit 2.5MHz + gmac_set_mdc_clk_div(gmacdev, GmiiCsrClk0); + + // cdh:get MDO_CLK div + gmacdev->ClockDivMdc = synopGMAC_get_mdc_clk_div(gmacdev); + + // cdh:initial phy and check phy link status,must add check + status = gmac_phy_init(gmacdev, pdata);//gmac_check_phy_init(gmacdev);// R by panqihe + if (status) { + printk("cdh:%s,gmac_phy_init err!\n", __func__); + //return -EAGAIN; + } + + /* cdh:check ok ok,Set up the 32 unit, tx descriptor queue/ring*/ + setup_tx_desc_queue(gmacdev, ndev, TRANSMIT_DESC_SIZE, RINGMODE); + + // cdh:check ok ok, setup_tx_desc_queue(gmacdev, pcidev, TRANSMIT_DESC_SIZE, CHAINMODE); + gmac_init_tx_desc_base(gmacdev); // cdh:Program the transmit descriptor base address in to DmaTxBase addr + + /* cdh:check ok ok,Set up the 32 unit, rx descriptor queue/ring*/ + setup_rx_desc_queue(gmacdev, ndev, RECEIVE_DESC_SIZE, RINGMODE); + + // cdh:check ok ok,setup_rx_desc_queue(gmacdev, pcidev, RECEIVE_DESC_SIZE, CHAINMODE); + gmac_init_rx_desc_base(gmacdev); // cdh:Program the transmit descriptor base address in to DmaTxBase addr + + // cdh:dma busrt=32words=128B, two dma_descriptor interval = 2Bytes + gmac_set_dma_bus_mode(gmacdev, DmaBurstLength32 | DmaDescriptorSkip2); //pbl32 incr with rxthreshold 128 + + // cdh:set dma transmit method + gmac_set_dma_control(gmacdev, DmaDisableFlush | DmaStoreAndForward | DmaTxSecondFrame | DmaRxThreshCtrl128); + + /*Initialize the mac interface*/ + // cdh:check ok, initial mac part ip + gmac_mac_init(gmacdev); + + + /** + *cdh:inital dma and mac flow control + */ + gmac_pause_control(gmacdev); // This enables the pause control in Full duplex mode of operation + + /** + *cdh:initial one rx buffer dma infor, why loop 32 ci + */ + do { + skb = alloc_skb(ndev->mtu + ETHERNET_HEADER + ETHERNET_CRC, GFP_KERNEL); + if(skb == NULL) + { + printk("ERROR in skb buffer allocation\n"); + break; + } + + /** cdh:note as follow + * pci_map_singleӳ䵥ڴ͵ĻֵǿԴݸ豸ߵַĻΪ NULL + * һɣӦʹúpci_unmap_single ɾӳ䡣УdirectionΪķȡֵ£ + * PCI_DMA_TODEVICE ݱ͵豸 + * PCI_DMA_FROMDEVICEݽ͵ CPU + * PCI_DMA_BIDIRECTIONALݽƶ + * PCI_DMA_NONE ֻΪԶṩ + */ + // cdh:malloc buffer to descriptor dma buffer, dma_addr as physic address + dma_addr = dma_map_single(NULL, skb->data, skb_tailroom(skb), DMA_FROM_DEVICE); +// printk("cdh:skb=0x%x, dma_addr=0x%x\n", (u32 *)skb, dma_addr); + status = gmac_set_rx_qptr(gmacdev, dma_addr, skb_tailroom(skb), (u32)skb, 0, 0, 0); + if(status < 0) + dev_kfree_skb(skb); + else + ; // cdh:printk("skb, head, data, tail: %x, %x, %x, %x\n", skb->head, (u32 *)skb->data, (u32 *)skb->tail, (u32 *)skb->len); + }while(status >= 0); + + /** + *cdh:Clear all the interrupts + */ + gmac_clear_interrupt(gmacdev); + + /** + Disable the interrupts generated by MMC and IPC counters. + If these are not disabled ISR should be modified accordingly to handle these interrupts. + */ + gmac_disable_mmc_tx_interrupt(gmacdev, 0xFFFFFFFF); // cdh:set 1for mask interrupt + gmac_disable_mmc_rx_interrupt(gmacdev, 0xFFFFFFFF); + gmac_disable_mmc_ipc_rx_interrupt(gmacdev, 0xFFFFFFFF); + + /** + *cdh:Enable Tx and Rx DMA + */ + gmac_enable_interrupt(gmacdev, DmaIntEnable); + gmac_enable_dma_rx(gmacdev); + gmac_enable_dma_tx(gmacdev); + + return status; +} + + + +/** * @brief Initialize Mac +* Initialize MAC and PHY +* @author Tang Anyang +* @date 2010-11-16 +* @param unsigned char * MacAddress: +*/ +static bool MacInit(struct net_device *ndev) +{ + volatile unsigned long count; + s32 status = 0; + + // cdh:reset MAC ctrl module + count = 10; + while(count--); + REG32(psysbase + 0x20) |= (1 << 13); + + // cdh:without reset MAC ctrl module + count = 10; + while(count--); + REG32(psysbase + 0x20) &= ~(1 << 13); + + count = 10; + while(count--); + + // cdh:init mac hardware infor + status = init_hw(ndev); + if (status) + return false; + + return true; +} + + +static s32 synop_open(struct net_device *netdev) +{ + signed int status = 0; + signed int ijk; + unsigned int dma_addr; + struct sk_buff *skb; + nt_adapter *adapter; + gmac_device *gmacdev; + struct ak_mac_data *pdata; + + adapter = (nt_adapter *)netdev_priv(netdev); + gmacdev = (gmac_device *)adapter->gmacdev_pt; + pdata = adapter->db_pt->dev->platform_data; + + // cdh:initial all mii share pin and clock,and complete phy reset from gpio62 + gmac_ctrl_init(pdata); + + /*cdh:Now platform dependent initialization.cdh:check ok*/ + gmac_mmc_counters_reset(gmacdev); // cdh:Mac Management Counters (MMC), cdh:check ok + gmac_mmc_counters_disable_rollover(gmacdev); //cdh:check ok + + /*Lets reset the IP*/ + printk("adapter= %08x gmacdev = %08x netdev = %08x netdev= %08x\n", (u32)adapter, (u32)gmacdev, (u32)netdev, (u32)netdev); + + // cdh:software reset , the resets all of the GMAC internal registers and logic, cdh:check ok + if (gmac_reset(gmacdev)) { + return -1; + } + + /* cdh:Program/flash in the station/IP's Mac address */ + gmac_set_mac_addr(gmacdev, GmacAddr0High, GmacAddr0Low, netdev->dev_addr); + + /* cdh:Lets read the version of ip, cdh:check ok */ + gmac_read_version(gmacdev); + + /* cdh:Lets set ipaddress in to device structure, cdh:check ok*/ + gmac_get_mac_addr(gmacdev, GmacAddr0High, GmacAddr0Low, netdev->dev_addr); + + /* cdh:check ok,Now set the broadcast address*/ + for(ijk = 0; ijk < 6; ijk++) { + netdev->broadcast[ijk] = 0xff; + } + + for(ijk = 0; ijk < 6; ijk++) { + printk("netdev->dev_addr[%d] = %02x and netdev->broadcast[%d] = %02x\n", ijk, netdev->dev_addr[ijk], ijk, netdev->broadcast[ijk]); + } + + // cdh:set MDO_CLK for MDIO transmit, note GmacGmiiAddr bit5, and 802.3 limit 2.5MHz + gmac_set_mdc_clk_div(gmacdev, GmiiCsrClk1); + + // cdh:get MDO_CLK div + gmacdev->ClockDivMdc = synopGMAC_get_mdc_clk_div(gmacdev); + + // cdh:initial phy and check phy link status + gmac_phy_init(gmacdev, pdata); + gmac_check_phy_init(gmacdev); + + /*Request for an shared interrupt. Instead of using netdev->irq lets use pcidev->irq*/ + if(request_irq (netdev->irq, gmac_intr_handler, SA_SHIRQ | SA_INTERRUPT, netdev->name, netdev)) { + printk("Error in request_irq\n"); + goto error_in_irq; + } + + printk("%s owns a shared interrupt on line %d\n", netdev->name, netdev->irq); + + /* cdh:check ok ok,Set up the 32 unit, tx descriptor queue/ring*/ + setup_tx_desc_queue(gmacdev, netdev, TRANSMIT_DESC_SIZE, RINGMODE); + + // cdh:check ok ok + gmac_init_tx_desc_base(gmacdev); // cdh:Program the transmit descriptor base address in to DmaTxBase addr + + /* cdh:check ok ok,Set up the 32 unit, rx descriptor queue/ring*/ + setup_rx_desc_queue(gmacdev, netdev, RECEIVE_DESC_SIZE, RINGMODE); + + // cdh:check ok ok + gmac_init_rx_desc_base(gmacdev); // cdh:Program the transmit descriptor base address in to DmaTxBase addr + + // cdh:dma busrt=32words=128B, two dma_descriptor interval = 2Bytes + gmac_set_dma_bus_mode(gmacdev, DmaBurstLength32 | DmaDescriptorSkip2); //pbl32 incr with rxthreshold 128 + + // cdh:set dma transmit method + gmac_set_dma_control(gmacdev, DmaDisableFlush | DmaStoreAndForward | DmaTxSecondFrame | DmaRxThreshCtrl128); + + // cdh:check ok, initial mac part ip + gmac_mac_init(gmacdev); + + // cdh:enable promisc mode for bridge + gmac_promisc_enable(gmacdev); + + /** + *cdh:inital dma and mac flow control + */ + gmac_pause_control(gmacdev); // This enables the pause control in Full duplex mode of operation + + #ifdef IPC_OFFLOAD + /*IPC Checksum offloading is enabled for this driver. Should only be used if Full Ip checksumm offload engine is configured in the hardware*/ + synopGMAC_enable_rx_chksum_offload(gmacdev); //Enable the offload engine in the receive path + synopGMAC_rx_tcpip_chksum_drop_enable(gmacdev); // This is default configuration, DMA drops the packets if error in encapsulated ethernet payload + // The FEF bit in DMA control register is configured to 0 indicating DMA to drop the errored frames. + /*Inform the Linux Networking stack about the hardware capability of checksum offloading*/ + netdev->features = NETIF_F_HW_CSUM; + #endif + + /** + *cdh:initial one rx buffer dma infor, why loop 32 ci + */ + do { + skb = alloc_skb(netdev->mtu + ETHERNET_HEADER + ETHERNET_CRC, GFP_KERNEL); + if(skb == NULL) + { + printk("ERROR in skb buffer allocation\n"); + break; + } + + /** cdh:note as follow + * pci_map_singleӳ䵥ڴ͵ĻֵǿԴݸ豸ߵַĻΪ NULL + * һɣӦʹúpci_unmap_single ɾӳ䡣УdirectionΪķȡֵ£ + * PCI_DMA_TODEVICE ݱ͵豸 + * PCI_DMA_FROMDEVICEݽ͵ CPU + * PCI_DMA_BIDIRECTIONALݽƶ + * PCI_DMA_NONE ֻΪԶṩ + */ + // cdh:malloc buffer to descriptor dma buffer, dma_addr as physic address + dma_addr = dma_map_single(NULL, skb->data, skb_tailroom(skb), DMA_FROM_DEVICE); + status = gmac_set_rx_qptr(gmacdev, dma_addr, skb_tailroom(skb), (u32)skb, 0, 0, 0); + if(status < 0) + dev_kfree_skb(skb); + else + ; // cdh:printk("skb, head, data, tail: %x, %x, %x, %x\n", skb->head, (u32 *)skb->data, (u32 *)skb->tail, (u32 *)skb->len); + }while(status >= 0); + + + /** + *cdh:inital one timer to detect the cable plugging and unplugging + */ + printk("Setting up the cable unplug timer\n"); + init_timer(&timer_cable_unplug); + timer_cable_unplug.function = (void *)timer_cable_unplug_proc; + timer_cable_unplug.data = (u32) adapter; + timer_cable_unplug.expires = CHECK_TIME + jiffies; + add_timer(&timer_cable_unplug); + + /** + *cdh:Clear all the interrupts + */ + gmac_clear_interrupt(gmacdev); + + /** + Disable the interrupts generated by MMC and IPC counters. + If these are not disabled ISR should be modified accordingly to handle these interrupts. + */ + gmac_disable_mmc_tx_interrupt(gmacdev, 0xFFFFFFFF); // cdh:set 1for mask interrupt + gmac_disable_mmc_rx_interrupt(gmacdev, 0xFFFFFFFF); + gmac_disable_mmc_ipc_rx_interrupt(gmacdev, 0xFFFFFFFF); + + /** + *cdh:Enable Tx and Rx DMA + */ + gmac_enable_interrupt(gmacdev, DmaIntEnable); + gmac_enable_dma_rx(gmacdev); + gmac_enable_dma_tx(gmacdev); + + /** + *cdh:start platform dependent network interface + */ + netif_start_queue(netdev); + + return 0; + +error_in_irq: + return -ESYNOPGMACBUSY; +} + +/** + * Function used when the interface is closed. + * + * This function is registered to linux stop() function. This function is + * called whenever ifconfig (in Linux) closes the device (for example "ifconfig eth0 down"). + * This releases all the system resources allocated during open call. + * system resources int needs + * - Disable the device interrupts + * - Stop the receiver and get back all the rx descriptors from the DMA + * - Stop the transmitter and get back all the tx descriptors from the DMA + * - Stop the Linux network queue interface + * - Free the irq (ISR registered is removed from the kernel) + * - Release the TX and RX descripor memory + * - De-initialize one second timer rgistered for cable plug/unplug tracking + * @param[in] pointer to net_device structure. + * \return Returns 0 on success and error status upon failure. + * \callgraph + */ +static s32 synop_close(struct net_device *netdev) +{ + nt_adapter *adapter; + gmac_device *gmacdev; + + adapter = (nt_adapter *) netdev_priv(netdev); + if(adapter == NULL) { + TR("OOPS adapter is null\n"); + return -ESYNOPGMACNOMEM; + } + + gmacdev = (gmac_device *) adapter->gmacdev_pt; + if(gmacdev == NULL) { + TR("OOPS gmacdev is null\n"); + return -ESYNOPGMACNOMEM; + } + + /* cdh:Disable all the interrupts*/ + gmac_disable_interrupt_all(gmacdev); + printk("the synopGMAC interrupt has been disabled\n"); + + /* cdh:Disable the reception*/ + gmac_disable_dma_rx(gmacdev); + gmac_take_desc_ownership_rx(gmacdev); + printk("the synopGMAC Reception has been disabled\n"); + + /* cdh:Disable the transmission*/ + gmac_disable_dma_tx(gmacdev); + gmac_take_desc_ownership_tx(gmacdev); + + printk("the synopGMAC Transmission has been disabled\n"); + netif_stop_queue(netdev); + + /* cdh:Now free the irq: This will detach the interrupt handler registered*/ + free_irq(netdev->irq, netdev); + printk("the synopGMAC interrupt handler has been removed\n"); + + /* cdh:Free the Rx Descriptor contents*/ + printk("Now calling giveup_rx_desc_queue \n"); + giveup_rx_desc_queue(gmacdev, netdev, RINGMODE); + + /* cdh:Free the Tx Descriptor contents*/ + printk("Now calling giveup_tx_desc_queue \n"); + giveup_tx_desc_queue(gmacdev, netdev, RINGMODE); + + /* cdh:Free the unplug check Timer*/ + printk("Freeing the cable unplug timer\n"); + del_timer(&timer_cable_unplug); + + return -ESYNOPGMACNOERR; +} + +/** + * Function to transmit a given packet on the wire. + * Whenever Linux Kernel has a packet ready to be transmitted, this function is called. + * The function prepares a packet and prepares the descriptor and + * enables/resumes the transmission. + * @param[in] pointer to sk_buff structure. + * @param[in] pointer to net_device structure. + * \return Returns 0 on success and Error code on failure. + * \note structure sk_buff is used to hold packet in Linux networking stacks. + */ +static s32 synop_xmit_frames(struct sk_buff *skb, struct net_device *netdev) +{ + s32 status = 0; + u32 offload_needed = 0; + u32 dma_addr; + nt_adapter *adapter; + gmac_device *gmacdev; + mac_info_t *db; + unsigned long flags; + + if(skb == NULL) { + printk("skb is NULL What happened to Linux Kernel? \n "); + return -ESYNOPGMACNOMEM; + } + + adapter = (nt_adapter *)netdev_priv(netdev); + if(adapter == NULL) + return -ESYNOPGMACNOMEM; + + gmacdev = (gmac_device *)adapter->gmacdev_pt; + if(gmacdev == NULL) + return -ESYNOPGMACNOMEM; + + db = adapter->db_pt; + + /* cdh:Stop the network queue*/ + netif_stop_queue(netdev); + + /* cdh:for ipc offload used */ + if(skb->ip_summed == CHECKSUM_HW) { + /** + *cdh:In Linux networking, if kernel indicates skb->ip_summed = CHECKSUM_HW, then only checksum offloading should be performed + *cdh:Make sure that the OS on which this code runs have proper support to enable offloading. + */ + offload_needed = 0x00000001; + } + + + /* cdh:because dma_map_single hase owned dma_spin lock ,so we need protect,Now we have skb ready and OS invoked this function. Lets make our DMA know about this*/ + dma_addr = dma_map_single(NULL, skb->data, skb->len, DMA_TO_DEVICE); + + /* cdh:protect share resources here,and we used only one data buffer */ + spin_lock_irqsave(&db->lock, flags); + status = gmac_set_tx_qptr(gmacdev, dma_addr, skb->len, (u32)skb, 0, 0, 0, offload_needed); + if(status < 0) + { + printk("%s No More Free Tx Descriptors\n", __FUNCTION__); + spin_unlock_irqrestore(&db->lock, flags); + dma_unmap_single(NULL, dma_addr, 0, DMA_TO_DEVICE); + return -EBUSY; + } + + /* cdh:Now force the DMA to start transmission*/ + gmac_resume_dma_tx(gmacdev); + netdev->trans_start = jiffies; + spin_unlock_irqrestore(&db->lock, flags); + + /*Now start the netdev queue*/ + netif_wake_queue(netdev); + + return -ESYNOPGMACNOERR; +} + +/** + * Function to handle a Tx Hang. + * This is a software hook (Linux) to handle transmitter hang if any. + * We get transmitter hang in the device interrupt status, and is handled + * in ISR. This function is here as a place holder. + * @param[in] pointer to net_device structure + * \return void. + */ +static void synop_tx_timeout(struct net_device *netdev) +{ + nt_adapter *adapter; + gmac_device *gmacdev; + + printk("%s\n", __FUNCTION__); + adapter = (nt_adapter *) netdev_priv(netdev); + if(adapter == NULL) + { + TR("OOPS adapter is null\n"); + return ; + } + + gmacdev = (gmac_device *) adapter->gmacdev_pt; + if(gmacdev == NULL) + { + TR("OOPS gmacdev is null\n"); + return ; + } + + /* cdh:==step1==Disable all the interrupts*/ + gmac_disable_interrupt_all(gmacdev); + printk("the synopGMAC interrupt has been disabled\n"); + + /* cdh:Disable the reception*/ + gmac_disable_dma_rx(gmacdev); + gmac_take_desc_ownership_rx(gmacdev); + printk("the synopGMAC Reception has been disabled\n"); + + /* cdh:Disable the transmission*/ + gmac_disable_dma_tx(gmacdev); + gmac_take_desc_ownership_tx(gmacdev); + + printk("the synopGMAC Transmission has been disabled\n"); + netif_stop_queue(netdev); + + /*Free the Rx Descriptor contents*/ + printk("Now calling giveup_rx_desc_queue \n"); + giveup_rx_desc_queue(gmacdev, netdev, RINGMODE); + + printk("Now calling giveup_tx_desc_queue \n"); + giveup_tx_desc_queue(gmacdev, netdev, RINGMODE); + + /* cdh:==step2==retry initial mac and phy, because request interrupt already save, so not re-init */ + MacInit(netdev); + + /** + *cdh:start platform dependent network interface + */ + netif_start_queue(netdev); + + return; +} + + +/** + * Function to set multicast and promiscous mode. + * @param[in] pointer to net_device structure. + * \return returns void. + */ +void synop_set_multicast_list(struct net_device *netdev) +{ + TR("%s called \n", __FUNCTION__); + //todo Function not yet implemented. + return; +} + + +/** + * Function to change the Maximum Transfer Unit. + * @param[in] pointer to net_device structure. + * @param[in] New value for maximum frame size. + * \return Returns 0 on success Errorcode on failure. + */ +s32 synop_change_mtu(struct net_device *netdev, s32 newmtu) +{ + if (newmtu < 68 || newmtu > ETH_DATA_LEN){ + printk(KERN_ERR"[%s][%d] not support mtu size %d\n", __func__, __LINE__, newmtu); + return -EINVAL; + } + + printk(KERN_ERR"[%s][%d] change mtu size from %d to %d\n", __func__, __LINE__, netdev->mtu, newmtu); + netdev->mtu = newmtu; + + return 0; + +} + +/** + * Function to set ethernet address of the NIC. + * @param[in] pointer to net_device structure. + * @param[in] pointer to an address structure. + * \return Returns 0 on success Errorcode on failure. + */ +static s32 synop_set_mac_address(struct net_device *netdev, void *macaddr) +{ + + nt_adapter *adapter = NULL; + gmac_device *gmacdev = NULL; + struct sockaddr *addr = macaddr; + + adapter = (nt_adapter *) netdev_priv(netdev); + if(adapter == NULL) + return -1; + + gmacdev = adapter->gmacdev_pt; + if(gmacdev == NULL) + return -1; + + if(!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + gmac_set_mac_addr(gmacdev, GmacAddr0High, GmacAddr0Low, addr->sa_data); + gmac_get_mac_addr(adapter_pt->gmacdev_pt, GmacAddr0High, GmacAddr0Low, netdev->dev_addr); + + TR("%s called \n", __FUNCTION__); + save_ether_addr(netdev->dev_addr); + return 0; +} + +/** + * Enables GMAC to look for Magic packet. + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ +void gmac_magic_packet_enable(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacPmtCtrlStatus, GmacPmtMagicPktEnable); + return; +} + +/** + * Populates the remote wakeup frame registers. + * Consecutive 8 writes to GmacWakeupAddr writes the wakeup frame filter registers. + * Before commensing a new write, frame filter pointer is reset to 0x0000. + * A small delay is introduced to allow frame filter pointer reset operation. + * @param[in] pointer to gmac_device. + * @param[in] pointer to frame filter contents array. + * \return returns void. + * note:cdh:check ok + */ +void gmac_write_wakeup_frame_register(gmac_device *gmacdev, u32 *filter_contents) +{ + s32 i; + gmac_set_bits((u32 *)gmacdev->MacBase, GmacPmtCtrlStatus, GmacPmtFrmFilterPtrReset); + plat_delay(10); + for(i = 0; i < WAKEUP_REG_LENGTH; i++) + gmac_write_reg((u32 *)gmacdev->MacBase, GmacWakeupAddr, *(filter_contents + i)); + return; + +} + +/** + * Enables GMAC to look for wake up frame. + * Wake up frame is defined by the user. + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ +void gmac_wakeup_frame_enable(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacPmtCtrlStatus, GmacPmtWakeupFrameEnable); + return; +} + +/** + * Disables the powerd down setting of GMAC. + * If the driver wants to bring up the GMAC from powerdown mode, even though the magic packet or the + * wake up frames received from the network, this function should be called. + * @param[in] pointer to gmac_device. + * \return returns void. + */ +void gmac_power_down_disable(gmac_device *gmacdev) +{ + gmac_clr_bits((u32 *)gmacdev->MacBase, GmacPmtCtrlStatus, GmacPmtPowerDown); + return; +} + + + + +/** + * Enables the power down mode of GMAC. + * This function puts the Gmac in power down mode. + * @param[in] pointer to gmac_device. + * \return returns void. + * note:cdh:check ok + */ +void gmac_power_down_enable(gmac_device *gmacdev) +{ + gmac_set_bits((u32 *)gmacdev->MacBase, GmacPmtCtrlStatus, GmacPmtPowerDown); + return; +} + + + +// cdh:check ok +static void powerdown_mac(gmac_device *gmacdev) +{ + TR("Put the GMAC to power down mode..\n"); + // Disable the Dma engines in tx path + GMAC_Power_down = 1; // Let ISR know that Mac is going to be in the power down mode + gmac_disable_dma_tx(gmacdev); + plat_delay(10000); //allow any pending transmission to complete + // Disable the Mac for both tx and rx + gmac_tx_disable(gmacdev); + gmac_rx_disable(gmacdev); + plat_delay(10000); //Allow any pending buffer to be read by host + //Disable the Dma in rx path + gmac_disable_dma_rx(gmacdev); + + //enable the power down mode + //gmac_pmt_unicast_enable(gmacdev); + + //prepare the gmac for magic packet reception and wake up frame reception + gmac_magic_packet_enable(gmacdev); + gmac_write_wakeup_frame_register(gmacdev, synopGMAC_wakeup_filter_config3); + + gmac_wakeup_frame_enable(gmacdev); + + //gate the application and transmit clock inputs to the code. This is not done in this driver :). + + //enable the Mac for reception + gmac_rx_enable(gmacdev); + + //Enable the assertion of PMT interrupt + gmac_pmt_int_enable(gmacdev); + //enter the power down mode + gmac_power_down_enable(gmacdev); + return; +} + + +/** + * IOCTL interface. + * This function is mainly for debugging purpose. + * This provides hooks for Register read write, Retrieve descriptor status + * and Retreiving Device structure information. + * @param[in] pointer to net_device structure. + * @param[in] pointer to ifreq structure. + * @param[in] ioctl command. + * \return Returns 0 on success Error code on failure. + */ +s32 synop_do_ioctl(struct net_device *netdev, struct ifreq *ifr, s32 cmd) +{ + + s32 retval = 0; + u16 temp_data = 0; + nt_adapter *adapter = NULL; + gmac_device *gmacdev = NULL; + + struct ifr_data_struct + { + u32 unit; + u32 addr; + u32 data; + } *req; + + + if(netdev == NULL) + return -1; + if(ifr == NULL) + return -1; + + req = (struct ifr_data_struct *)ifr->ifr_data; + + adapter = (nt_adapter *) netdev_priv(netdev); + if(adapter == NULL) + return -1; + + gmacdev = adapter->gmacdev_pt; + if(gmacdev == NULL) + return -1; + TR("%s :: on device %s req->unit = %08x req->addr = %08x req->data = %08x cmd = %08x \n",__FUNCTION__,netdev->name,req->unit,req->addr,req->data,cmd); + + switch(cmd) + { + case IOCTL_READ_REGISTER: //IOCTL for reading IP registers : Read Registers + if (req->unit == 0) // Read Mac Register + req->data = gmac_read_reg((u32 *)gmacdev->MacBase, req->addr); + else if (req->unit == 1) // Read DMA Register + req->data = gmac_read_reg((u32 *)gmacdev->DmaBase, req->addr); + else if (req->unit == 2) // Read Phy Register + { + retval = gmac_read_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, req->addr, &temp_data); + req->data = (u32)temp_data; + if(retval != -ESYNOPGMACNOERR) + TR("ERROR in Phy read\n"); + } + break; + + case IOCTL_WRITE_REGISTER: //IOCTL for reading IP registers : Read Registers + if (req->unit == 0) // Write Mac Register + gmac_write_reg((u32 *)gmacdev->MacBase, req->addr, req->data); + else if (req->unit == 1) // Write DMA Register + gmac_write_reg((u32 *)gmacdev->DmaBase, req->addr, req->data); + else if (req->unit == 2) // Write Phy Register + { + retval = gmac_write_phy_reg((u32 *)gmacdev->MacBase, gmacdev->PhyBase, req->addr, req->data); + if(retval != -ESYNOPGMACNOERR) + TR("ERROR in Phy read\n"); + } + break; + + case IOCTL_READ_IPSTRUCT: //IOCTL for reading GMAC DEVICE IP private structure + if (req->unit == 0) + { + gmacdev = adapter->gmacdev_pt; + if(gmacdev == NULL) + return -1; + memcpy((gmac_device *)req->addr, gmacdev, sizeof(gmac_device)); + } + else + return -1; + + // memcpy(ifr->ifr_data, gmacdev, sizeof(gmac_device)); + break; + /* Total Number of interrupts over which AvbBits are accumulated*/ + case IOCTL_READ_RXDESC: //IOCTL for Reading Rx DMA DESCRIPTOR + if (req->unit == 0) + { + gmacdev = adapter->gmacdev_pt; + if(gmacdev == NULL) + return -1; + memcpy((DmaDesc *)req->addr, gmacdev->RxDesc + ((DmaDesc *) (req->addr))->data1, sizeof(DmaDesc) ); + } + else + return -1; + + // memcpy(ifr->ifr_data, gmacdev->RxDesc + ((DmaDesc *) (ifr->ifr_data))->data1, sizeof(DmaDesc) ); + break; + + case IOCTL_READ_TXDESC: //IOCTL for Reading Tx DMA DESCRIPTOR + if (req->unit == 0) + { + gmacdev = adapter->gmacdev_pt; + if(gmacdev == NULL) + return -1; + memcpy((DmaDesc *)req->addr, gmacdev->TxDesc + ((DmaDesc *) (req->addr))->data1, sizeof(DmaDesc) ); + } + else + return -1; + + // memcpy(ifr->ifr_data, gmacdev->TxDesc + ((DmaDesc *) (ifr->ifr_data))->data1, sizeof(DmaDesc) ); + break; + case IOCTL_POWER_DOWN: // CDH:WAIT FOR ........... + if (req->unit == 1) //power down the mac + { + TR("============I will Power down the MAC now =============\n"); + // If it is already in power down don't power down again + retval = 0; + if(((gmac_read_reg((u32 *)gmacdev->MacBase, GmacPmtCtrlStatus)) & GmacPmtPowerDown) != GmacPmtPowerDown) + { + powerdown_mac(gmacdev); + retval = 0; + } + } + if (req->unit == 2) //Disable the power down and wake up the Mac locally + { + TR("============I will Power up the MAC now =============\n"); + //If already powered down then only try to wake up + retval = -1; + if(((gmac_read_reg((u32 *)gmacdev->MacBase, GmacPmtCtrlStatus)) & GmacPmtPowerDown) == GmacPmtPowerDown) + { + gmac_power_down_disable(gmacdev); + powerup_mac(gmacdev); + retval = 0; + } + } + break; + // cdh:case IOCTL_AVB_TEST: //IOCTL for AVB Testing + default: + retval = -1; + } + return retval; +} + +/** + * Function provides the network interface statistics. + * Function is registered to linux get_stats() function. This function is + * called whenever ifconfig (in Linux) asks for networkig statistics + * (for example "ifconfig eth0"). + * @param[in] pointer to net_device structure. + * \return Returns pointer to net_device_stats structure. + * \callgraph + */ +struct net_device_stats *synop_get_stats(struct net_device *netdev) +{ + TR("%s called \n", __FUNCTION__); + return( &(((nt_adapter *)(netdev_priv(netdev)))->net_dev_stats) ); +} + +static const struct net_device_ops synop_net_device_ops = { + .ndo_open = synop_open, + .ndo_stop = synop_close, + .ndo_start_xmit = synop_xmit_frames, + .ndo_tx_timeout = synop_tx_timeout, + .ndo_change_mtu = synop_change_mtu, + .ndo_set_mac_address = synop_set_mac_address, + .ndo_do_ioctl = synop_do_ioctl, + .ndo_get_stats = synop_get_stats, + .ndo_validate_addr = eth_validate_addr, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = ak_mac_poll_controller, +#endif + +}; + + +static const struct ethtool_ops akgmac100_ethtool_ops = { + .get_link = ethtool_op_get_link, +}; +static int get_mac_addr(struct ak_mac_data *pdata) +{ + int i; + unsigned char mac_addr[32] = {0}; + unsigned char prefix_macaddr_cnt = 0; + void * handle ; + int ret = 0; + int retvalue = 0; + + /* search the same name partition */ + handle = partition_open(MAC_FILE_NAME); + if (handle == NULL) { + printk(KERN_ERR "%s, open partition error!\n", __func__); + return -ENOENT; + } + + ret = partition_read(handle , mac_addr, 32); + if (ret == -1) { + printk(KERN_ERR "%s, write partition error!\n", __func__); + retvalue = -ENOENT; + } + + /* search open the same name partition */ + ret = partition_close(handle); + if (ret == -1) { + printk(KERN_ERR "%s, close partition error!\n", __func__); + return -ENOENT; + } + + // cdh:check prefix equal FF:FF:FF + for (i = 0; i < MAC_ADDR_APSTRING_LEN; i++) { + if ((i % 3 != 2)) { + mac_addr[i + 4] = toupper(mac_addr[i + 4]); + if (mac_addr[i + 4] == 'F') { + prefix_macaddr_cnt++; + } + } + } + + if (prefix_macaddr_cnt == (MAC_ADDR_APSTRING_LEN - 2)) { + goto out; + } + + for (i = 0; i < MAC_ADDR_STRING_LEN; i++) { + if ((i % 3 != 2)) { + mac_addr[i + 4] = toupper(mac_addr[i + 4]); + if (!(isdigit(mac_addr[i + 4]) || (mac_addr[i + 4] <= 'F' && mac_addr[i + 4] >= 'A'))) + goto out; + } + else if (mac_addr[i + 4] != ':') + goto out; + } + + for (i = 0; i < MAC_ADDR_LEN; i++) + pdata->dev_addr[i] = CTOI(mac_addr[i * 3 + 4]) * 16 + CTOI(mac_addr[i * 3 + 5]); + + return 0; +out: + + printk("Failed to read MAC addres in medium storage, use default mac\n"); + return -1; +} + +static int UpdateAsaFile(char *buf, char* filename) +{ + unsigned char ttbuf[64]; + void * handle ; + int ret = 0; + int retvalue = 0; + + memset(ttbuf, 0, 64); + ttbuf[0] = strlen(buf); + memcpy(ttbuf + 4, buf, strlen(buf)); + + /* search the same name partition */ + handle = partition_open(MAC_FILE_NAME); + if (handle == NULL) { + printk(KERN_ERR "%s, open partition error!\n", __func__); + return -ENOENT; + } + + ret = partition_write(handle , ttbuf, strlen(buf)+4); + if (ret == -1) { + printk(KERN_ERR "%s, write partition error!\n", __func__); + retvalue = -ENOENT; + } + + /* search open the same name partition */ + ret = partition_close(handle); + if (ret == -1) { + printk(KERN_ERR "%s, close partition error!\n", __func__); + return -ENOENT; + } + + return retvalue; +} + +static int save_ether_addr(unsigned char dev_addr[MAC_ADDR_LEN]) +{ + int i; + int ret; + char mac[64]; + + memset(mac, '\0', sizeof(mac)); + for (i=0; idev.platform_data; // cdh:from platform device data + struct net_device *netdev; + int ret = 0; + int iosize = 0; + + /* cdh:alloc and Init network device */ + netdev = alloc_etherdev(sizeof(struct synopGMACAdapterStruct)); + if (!netdev) { + dev_err(&pdev->dev, "could not allocate device.\n"); + return -ENOMEM; + } + + SET_NETDEV_DEV(netdev, &pdev->dev); + + /* setup board info structure */ + adapter_pt = (nt_adapter *)netdev_priv(netdev); + printk("netdev private = %x \n", (u32)adapter_pt); + + adapter_pt->netdev_pt = netdev; + adapter_pt->db_pt = NULL; // cdh:pci_if, probe() function + adapter_pt->gmacdev_pt = NULL; // cdh:gmac_device data + + /*Allocate Memory for the the GMACip structure*/ + RingbufVa = dma_alloc_coherent(NULL, sizeof (gmac_device), &RingbufPa, GFP_KERNEL); + adapter_pt->gmacdev_pt = (gmac_device *)RingbufVa; + if(!adapter_pt->gmacdev_pt) { + printk("Error in Memory Allocataion \n"); + }else { + printk("Allocataion gmacdev OK\n"); + } + + /*Allocate Memory for the the GMACip structure*/ + BoardbufVa = dma_alloc_coherent(NULL, sizeof (mac_info_t), &BoardbufPa, GFP_KERNEL); + adapter_pt->db_pt = (mac_info_t *)BoardbufVa; + if(!adapter_pt->db_pt) { + printk("Error in Memory Allocataion \n"); + }else{ + printk("Allocataion mac_info OK\n"); + } + + adapter_pt->db_pt->dev = &pdev->dev; + adapter_pt->db_pt->ndev = netdev; + + /* cdh:get the register and irq resource */ + adapter_pt->db_pt->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); // cdh:get ctrl reg physic addr + adapter_pt->db_pt->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (adapter_pt->db_pt->addr_res == NULL || adapter_pt->db_pt->irq_res == NULL) { + printk("insufficient resources\n"); + ret = -ENOENT; + goto out; + } + + iosize = resource_size(adapter_pt->db_pt->addr_res); + adapter_pt->db_pt->addr_req = request_mem_region(adapter_pt->db_pt->addr_res->start, iosize, pdev->name); + if (adapter_pt->db_pt->addr_req == NULL) { + printk("cannot claim address reg area\n"); + ret = -EIO; + goto out; + } + + // cdh:mac reg physic addr map to virtual addr + adapter_pt->db_pt->io_addr = ioremap(adapter_pt->db_pt->addr_res->start, iosize); + if (adapter_pt->db_pt->io_addr == NULL) { + printk("failed to ioremap address reg\n"); + ret = -EINVAL; + goto out; + } + + /* cdh:fill in parameters for net-dev structure */ + netdev->base_addr = (unsigned long)adapter_pt->db_pt->io_addr; // cdh:mac ctrl reg base address(virtual address) + netdev->irq = adapter_pt->db_pt->irq_res->start; // cdh:mac ctrl interrupt + + /* cdh:driver system function */ + ether_setup(netdev); + + SET_ETHTOOL_OPS(netdev, &akgmac100_ethtool_ops); + netdev->netdev_ops = &synop_net_device_ops; // cdh:Ethernet operation + netdev->watchdog_timeo = msecs_to_jiffies(5000); //cdh:Ethernet watchdog timeout + + // cdh:open mac ctrl and phy 25m clock, and set mii interface all share pin ok + gmac_clk_sharepin_init(); + + // cdh:set mac address + /* cdh:get Ethernet mac address from flash area &check valid ,notes first we can get fixed value , not read file*/ + if ((get_mac_addr(pdata) < 0)||(!is_valid_ether_addr(pdata->dev_addr))) { + + dev_warn(adapter_pt->db_pt->dev, "%s, Invalid Ethernet address. " + "Generate software assigned\n\trandom Ethernet address.\n", netdev->name); + + /* Generate software assigned random Ethernet address */ + random_ether_addr(pdata->dev_addr); + if (!is_valid_ether_addr(pdata->dev_addr)) + dev_warn(adapter_pt->db_pt->dev, "%s: Invalid Ethernet address. Please " + "set using ifconfig\n", netdev->name); + else + save_ether_addr(pdata->dev_addr); // cdh:save random generated mac address + } + + // cdh:save pdata->dev_addr mac address to ndev->dev_addr + memcpy(netdev->dev_addr, pdata->dev_addr, 6); + + /** + *Attach the device to MAC struct This will configure all the required base addresses + *such as Mac base, configuration base, phy base address(out of 32 possible phys ) + * cdh:here must be initialled inside of ak_mac_probe + */ + iomap_base = adapter_pt->db_pt->io_addr; + gmac_attach(adapter_pt->gmacdev_pt, (u32) iomap_base + MACBASE, (u32) iomap_base + DMABASE, DEFAULT_PHY_BASE); + + + // cdh:set mac base address + pMacBase = adapter_pt->db_pt->io_addr; + + // cdh:set netdev as pdev->dev->prive + platform_set_drvdata(pdev, netdev); + + // cdh:register net device + ret = register_netdev(netdev); + if (ret == 0) { + printk("CDH_Success:%s: ak39E_mac at %p IRQ %d MAC: %pM\n", + netdev->name, adapter_pt->db_pt->io_addr, netdev->irq, netdev->dev_addr); + } + + return 0; +out: + printk("not found (%d).\n", ret); + free_netdev(netdev); + + // cdh:release platform device resource + if (adapter_pt->db_pt->addr_req) { + release_mem_region(adapter_pt->db_pt->addr_res->start, iosize); + } + + // cdh:must dma_free_coherent + if (RingbufVa) { + dma_free_coherent(NULL, sizeof (gmac_device), RingbufVa, RingbufPa); + RingbufVa = NULL; + RingbufPa = 0; + } + + if (BoardbufVa) { + dma_free_coherent(NULL, sizeof (mac_info_t), BoardbufVa, BoardbufPa); + BoardbufVa = NULL; + BoardbufPa = 0; + } + + return ret; +} + + +static int ak_mac_drv_suspend(struct device *dev) +{ + + return 0; +} + +static int ak_mac_drv_resume(struct device *dev) +{ + + return 0; +} + +static struct dev_pm_ops ak_mac_drv_pm_ops = { + .suspend = ak_mac_drv_suspend, + .resume = ak_mac_drv_resume, +}; + +static int __devexit ak_mac_drv_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + nt_adapter *adapter; + int iosize = 0; + + adapter = (nt_adapter *)netdev_priv(ndev); + + // cdh:check ok + platform_set_drvdata(pdev, NULL); + + // cdh:check ok + unregister_netdev(ndev); + + // cdh:release mii interface sharepin and clock,other request mem and resource + mac_exit(ndev); + + // cdh:release ndev struct + free_netdev(ndev); + + // cdh:release platform device resource + iosize = resource_size(adapter->db_pt->addr_res); + if (adapter->db_pt->addr_req) { + release_mem_region(adapter->db_pt->addr_res->start, iosize); + } + + // cdh:must dma_free_coherent + if (RingbufVa) { + dma_free_coherent(NULL, sizeof (gmac_device), RingbufVa, RingbufPa); + RingbufVa = NULL; + RingbufPa = 0; + } + + if (BoardbufVa) { + dma_free_coherent(NULL, sizeof (mac_info_t), BoardbufVa, BoardbufPa); + BoardbufVa = NULL; + BoardbufPa = 0; + } + + // cdh:release all pointer + adapter->netdev_pt = NULL; + adapter->db_pt = NULL; + adapter->gmacdev_pt = NULL; + + printk("released and freed device\n"); + + return 0; +} + +static struct platform_driver ak_mac_driver = { + .driver = { + .name = "ak_ethernet", + .owner = THIS_MODULE, + .pm = &ak_mac_drv_pm_ops, + }, + .probe = ak_mac_probe, + .remove = __devexit_p(ak_mac_drv_remove), +}; + +static int __init ak_mac_init(void) +{ + printk(KERN_INFO "%s Ethernet Driver, V%s\n", MACNAME, DRV_VERSION); + + return platform_driver_register(&ak_mac_driver); +} + +static void __exit ak_mac_cleanup(void) +{ + platform_driver_unregister(&ak_mac_driver); +} + +module_init(ak_mac_init); +module_exit(ak_mac_cleanup); + +MODULE_AUTHOR("Tang Anyang, Zhang Jingyuan (C) ANYKA"); +MODULE_DESCRIPTION("Anyka MAC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ak-ethernet"); diff --git a/drivers/net/ethernet/ak-ethernet/ak_ethernet.h b/drivers/net/ethernet/ak-ethernet/ak_ethernet.h new file mode 100644 index 00000000..a05e683c --- /dev/null +++ b/drivers/net/ethernet/ak-ethernet/ak_ethernet.h @@ -0,0 +1,1871 @@ +#ifndef _AK_ETHERNET_H_ +#define _AK_ETHERNET_H_ + +#include + +/* +DMA Descriptor Structure +The structure is common for both receive and transmit descriptors +The descriptor is of 4 words, but our structrue contains 6 words where +last two words are to hold the virtual address of the network buffer pointers +for driver's use +From the GMAC core release 3.50a onwards, the Enhanced Descriptor structure got changed. +The descriptor (both transmit and receive) are of 8 words each rather the 4 words of normal +descriptor structure. +Whenever IEEE 1588 Timestamping is enabled TX/RX DESC6 provides the lower 32 bits of Timestamp value and + TX/RX DESC7 provides the upper 32 bits of Timestamp value +In addition to this whenever extended status bit is set (RX DESC0 bit 0), RX DESC4 contains the extended status information +*/ + +#define MODULO_INTERRUPT 1 // if it is set to 1, interrupt is available for all the descriptors or else interrupt is available only for +// descriptor whose index%MODULO_INTERRUPT is zero + +#define DEFAULT_DELAY_VARIABLE 1000 +#define DEFAULT_LOOP_VARIABLE 10000 + +/*SynopGMAC can support up to 32 phys*/ + +enum GMACPhyBase +{ + PHY0 = 0, //The device can support 32 phys, but we use first phy only + PHY1 = 1, + PHY31 = 31, +}; + +#define DEFAULT_PHY_BASE PHY1 //We use First Phy +#define MACBASE 0x0000 // The Mac Base address offset is 0x0000 +#define DMABASE 0x1000 // Dma base address starts with an offset 0x1000 + +#ifdef AVB_SUPPORT +#define DMABASE_CH0 DMABASE // DMA base address for Channel 0 +#define DMABASE_CH1 0x1100 // DMA base address for Channel 1 +#define DMABASE_CH2 0x1200 // DMA base address for Channel 2 + +#define ETHERNET_HEADER_AVB 18 //6 byte Dest addr, 6 byte Src addr, 2 byte length/type + +#endif + +#define NET_IF_TIMEOUT (10*HZ) +#define CHECK_TIME (HZ) +#define SA_SHIRQ IRQF_SHARED +#define SA_INTERRUPT IRQF_DISABLED +#define CHECKSUM_HW CHECKSUM_PARTIAL + +//#define TRANSMIT_DESC_SIZE 256 //Tx Descriptors needed in the Descriptor pool/queue +//#define RECEIVE_DESC_SIZE 256 //Rx Descriptors needed in the Descriptor pool/queue +#define TRANSMIT_DESC_SIZE 256 // 32 Tx Descriptors needed in the Descriptor pool/queue +#define RECEIVE_DESC_SIZE 256 // 32 Rx Descriptors needed in the Descriptor pool/queue + +#define ETHERNET_HEADER 14 //6 byte Dest addr, 6 byte Src addr, 2 byte length/type +#define ETHERNET_CRC 4 //Ethernet CRC +#define ETHERNET_EXTRA 2 //Only God knows about this????? +#define ETHERNET_PACKET_COPY 250 // Maximum length when received data is copied on to a new skb +#define ETHERNET_PACKET_EXTRA 18 // Preallocated length for the rx packets is MTU + ETHERNET_PACKET_EXTRA +#define VLAN_TAG 4 //optional 802.1q VLAN Tag +#define MIN_ETHERNET_PAYLOAD 46 //Minimum Ethernet payload size +#define MAX_ETHERNET_PAYLOAD 1500 //Maximum Ethernet payload size +#define JUMBO_FRAME_PAYLOAD 9000 //Jumbo frame payload size + +#define TX_BUF_SIZE ETHERNET_HEADER + ETHERNET_CRC + MAX_ETHERNET_PAYLOAD + VLAN_TAG + +#define IOCTL_READ_REGISTER SIOCDEVPRIVATE+1 +#define IOCTL_WRITE_REGISTER SIOCDEVPRIVATE+2 +#define IOCTL_READ_IPSTRUCT SIOCDEVPRIVATE+3 +#define IOCTL_READ_RXDESC SIOCDEVPRIVATE+4 +#define IOCTL_READ_TXDESC SIOCDEVPRIVATE+5 +#define IOCTL_POWER_DOWN SIOCDEVPRIVATE+6 + +#define IOCTL_AVB_TEST SIOCDEVPRIVATE+7 +#define AVB_SET_CONFIG 0x00000001 +#define AVB_CONFIG_HW 0x00000002 +#define AVB_RUN_TEST 0x00000003 +#define AVB_GET_RESULT 0x00000004 +#define AVB_DEBUG_ENABLE 0x00000005 +#define AVB_DEBUG_DISABLE 0x00000006 +#define AVB_TX_FRAMES 0x00000007 + +/* Error Codes */ +#define ESYNOPGMACNOERR 0 +#define ESYNOPGMACNOMEM 1 +#define ESYNOPGMACPHYERR 2 +#define ESYNOPGMACBUSY 3 + +// This is the IP's phy address. This is unique address for every MAC in the universe +#define DEFAULT_MAC_ADDRESS {0x00, 0x55, 0x7B, 0xB5, 0x7D, 0xF7} + + +#if 0 +/* + * We define u64 as unsigned long long for every architecture + * so that we can print it with %Lx without getting warnings. + */ +typedef signed char s8; +typedef unsigned char u8; +typedef signed short s16; +typedef unsigned short u16; +typedef signed int s32; +typedef unsigned int u32; +typedef signed long long s64; +typedef unsigned long long u64; + + +#define readl(a) (*(volatile u32 *)(a)) +#define writel(d, a) (*(volatile u32 *)(a) = (d)) +#endif + + +// cdh:add, check ok, ignore type +#ifdef ENH_DESC_8W +typedef struct DmaDescStruct +{ + u32 status; /* Status */ + u32 length; /* Buffer 1 and Buffer 2 length */ + u32 buffer1; /* Network Buffer 1 pointer (Dma-able) */ + u32 buffer2; /* Network Buffer 2 pointer or next descriptor pointer (Dma-able)in chain structure */ + /* This data below is used only by driver */ + u32 extstatus; /* Extended status of a Rx Descriptor */ + u32 reserved1; /* Reserved word */ + u32 timestamplow; /* Lower 32 bits of the 64 bit timestamp value */ + u32 timestamphigh; /* Higher 32 bits of the 64 bit timestamp value */ + u32 data1; /* This holds virtual address of buffer1, not used by DMA */ + u32 data2; /* This holds virtual address of buffer2, not used by DMA */ +} DmaDesc; +#else +typedef struct DmaDescStruct +{ + u32 status; /* Status */ + u32 length; /* Buffer 1 and Buffer 2 length */ + u32 buffer1; /* Network Buffer 1 pointer (Dma-able) */ + u32 buffer2; /* Network Buffer 2 pointer or next descriptor pointer (Dma-able)in chain structure */ + /* This data below is used only by driver */ + u32 data1; /* This holds virtual address of buffer1, not used by DMA */ + u32 data2; /* This holds virtual address of buffer2, not used by DMA */ +} DmaDesc; +#endif + +enum DescMode +{ + RINGMODE = 0x00000001, + CHAINMODE = 0x00000002, +}; + +enum BufferMode +{ + SINGLEBUF = 0x00000001, + DUALBUF = 0x00000002, +}; + +/* synopGMAC device data */ +// cdh:add, check ok, ignore type +typedef struct synopGMACDeviceStruct +{ + u32 MacBase; /* base address of MAC registers */ + u32 DmaBase; /* base address of DMA registers */ + u32 PhyBase; /* PHY device address on MII interface */ + u32 Version; /* Gmac Revision version */ + + dma_addr_t TxDescDma; /* Dma-able address of first tx descriptor either in ring or chain mode, this is used by the GMAC device*/ + dma_addr_t RxDescDma; /* Dma-albe address of first rx descriptor either in ring or chain mode, this is used by the GMAC device*/ + DmaDesc *TxDesc; /* start address of TX descriptors ring or chain, this is used by the driver */ + DmaDesc *RxDesc; /* start address of RX descriptors ring or chain, this is used by the driver */ + + u32 BusyTxDesc; /* Number of Tx Descriptors owned by DMA at any given time*/ + u32 BusyRxDesc; /* Number of Rx Descriptors owned by DMA at any given time*/ + + u32 RxDescCount; /* number of rx descriptors in the tx descriptor queue/pool */ + u32 TxDescCount; /* number of tx descriptors in the rx descriptor queue/pool */ + + u32 TxBusy; /* index of the tx descriptor owned by DMA, is obtained by gmac_get_tx_qptr() */ + u32 TxNext; /* index of the tx descriptor next available with driver, given to DMA by gmac_set_tx_qptr() */ + u32 RxBusy; /* index of the rx descriptor owned by DMA, obtained by gmac_get_rx_qptr() */ + u32 RxNext; /* index of the rx descriptor next available with driver, given to DMA by gmac_set_rx_qptr() */ + + DmaDesc *TxBusyDesc; /* Tx Descriptor address corresponding to the index TxBusy */ + DmaDesc *TxNextDesc; /* Tx Descriptor address corresponding to the index TxNext */ + DmaDesc *RxBusyDesc; /* Rx Descriptor address corresponding to the index TxBusy */ + DmaDesc *RxNextDesc; /* Rx Descriptor address corresponding to the index RxNext */ + + + /*Phy related stuff*/ + u32 ClockDivMdc; /* Clock divider value programmed in the hardware */ + /* The status of the link */ + u32 LinkState; /* Link status as reported by the Marvel Phy */ + u32 DuplexMode; /* Duplex mode of the Phy */ + u32 Speed; /* Speed of the Phy */ + u32 LoopBackMode; /* Loopback status of the Phy */ + // u32 irq; /* Gmac ctrl interrupt bit , cdh add */ +} gmac_device; + +// cdh:add, check ok, ignore type +#ifdef AVB_SUPPORT +typedef struct AVBStruct +{ + u8 ChSelMask; /* This gives which DMA channel is enabled and which is disabled + Bit0 for Ch0 + Bit1 for Ch1 + Bit2 for Ch2 + */ + u8 DurationOfExp; /* Duration for which experiment should be conducted in minutes - Default 2 Minutes */ + + u8 AvControlCh; /* channel on which AV control channel must be received (Not used)*/ + u8 PTPCh; /* Channel on which PTP packets must be received (Not Used)*/ + u8 PrioTagForAV; /* Used when more than One channel enabled in Rx path (Not Used) + for only CH1 Enabled: + Frames sith Priority > Value programmed, frames sent to CH1 + Frames with priority < Value programmed are sent to CH0 + For both CH1 and CH2 Enabled: + Frames sith Priority > Value programmed, frames sent to CH2 + Frames with priority < Value programmed are sent to CH1 + */ + + u16 AvType; /* AV Ethernet Type to be programmed for Core to identify AV type */ + + u8 Ch1PrioWts; + u8 Ch1Bw; + u32 Ch1_frame_size; + u8 Ch1_EnableSlotCheck; /* Enable checking of slot numbers programmed in the Tx Desc*/ + u8 Ch1_AdvSlotInterval; /* When Set Data fetched for current slot and for next 2 slots in advance + When reset data fetched for current slot and in advance for next slot*/ + + u8 Ch1CrSh; /* When set Enables the credit based traffic shaping. Now works with Strict priority style*/ + u8 Ch1SlotCount; /* Over which transmiteed bits per slot needs to be computed (Only for Credit based shaping) */ + u32 Ch1AvgBitsPerSlot; /* Average bits per slot reported by core once in Ch1SlotCount * 125 micro seconds */ + u32 Ch1AvgBitsPerSlotAccL; /* No of Avg Bits per slot on Channel1*/ + u32 Ch1AvgBitsPerSlotAccH; /* No of Avg Bits per slot on Channel1*/ + u32 Ch1AvgBitsNoOfInterrupts; /* Total Number of interrupts over which AvbBits are accumulated*/ + + u8 Ch1CreditControl; /* Will be zero (Not used) */ + + u8 Ch1_tx_rx_prio_policy; // Should Ch1 use Strict or RR policy + u8 Ch1_use_tx_high_prio; // Should Ch1 Tx have High priority over Rx + u8 Ch1_tx_rx_prio_ratio; // For Round Robin what is the ratio between tx-rx or rx-tx + + u8 Ch1_tx_desc_slot_no_start; + u8 Ch1_tx_desc_slot_no_skip; + + u32 Ch1SendSlope; + u32 Ch1IdleSlope; + u32 Ch1HiCredit; + u32 Ch1LoCredit; + + u32 Ch1FramecountTx; /* No of Frames Transmitted on Channel 1 */ + u32 Ch1FramecountRx; /* No of Frames Received on Channel 1 */ + + u8 Ch2PrioWts; + u8 Ch2Bw; + u32 Ch2_frame_size; + u8 Ch2_EnableSlotCheck; /* Enable checking of slot numbers programmed in the Tx Desc*/ + u8 Ch2_AdvSlotInterval; /* When Set Data fetched for current slot and for next 2 slots in advance + When reset data fetched for current slot and in advance for next slot*/ + u8 Ch2CrSh; /* When set Enables the credit based traffic shaping. Now works with Strict priority style*/ + u8 Ch2SlotCount; /* Over which transmiteed bits per slot needs to be computed (Only for Credit based shaping) */ + u32 Ch2AvgBitsPerSlot; /* Average bits per slot reported by core once in Ch2SlotCount * 125 micro seconds */ + u32 Ch2AvgBitsPerSlotAccL; /* No of Avg Bits per slot on Channel2*/ + u32 Ch2AvgBitsPerSlotAccH; /* No of Avg Bits per slot on Channel2*/ + u32 Ch2AvgBitsNoOfInterrupts; /* Total Number of interrupts over which AvbBits are accumulated*/ + + u8 Ch2CreditControl; /* Will be zero at present*/ + + u8 Ch2_tx_rx_prio_policy; // Should Ch1 use Strict or RR policy + u8 Ch2_use_tx_high_prio; // Should Ch1 Tx have High priority over Rx + u8 Ch2_tx_rx_prio_ratio; // For Round Robin what is the ratio between tx-rx or rx-tx + + + u8 Ch2_tx_desc_slot_no_start; + u8 Ch2_tx_desc_slot_no_skip; + + u32 Ch2SendSlope; + u32 Ch2IdleSlope; + u32 Ch2HiCredit; + u32 Ch2LoCredit; + + u32 Ch2FramecountTx; /* No of Frames Transmitted on Channel 2 */ + u32 Ch2FramecountRx; /* No of Frames Received on Channel 2 */ + + u8 Ch0PrioWts; + u8 Ch0_tx_rx_prio_policy; // Should Ch1 use Strict or RR policy + u8 Ch0_use_tx_high_prio; // Should Ch1 Tx have High priority over Rx + u8 Ch0_tx_rx_prio_ratio; // For Round Robin what is the ratio between tx-rx or rx-tx + + u32 Ch0_frame_size; + u32 Ch0FramecountTx; /* No of Frames Transmitted on Channel 0 */ + u32 Ch0FramecountRx; /* No of Frames Received on Channel 0 */ + +} gmac_avb; +#endif + + +#if 0 +/* + * Network device statistics. Akin to the 2.0 ether stats but + * with byte counters. + * note:cdh:add, check ok, ignore type + */ +struct net_device_stats +{ + unsigned long rx_packets; /* total packets received */ + unsigned long tx_packets; /* total packets transmitted */ + unsigned long rx_bytes; /* total bytes received */ + unsigned long tx_bytes; /* total bytes transmitted */ + unsigned long rx_errors; /* bad packets received */ + unsigned long tx_errors; /* packet transmit problems */ + unsigned long rx_dropped; /* no space in linux buffers */ + unsigned long tx_dropped; /* no space available in linux */ + unsigned long multicast; /* multicast packets received */ + unsigned long collisions; + + /* detailed rx_errors: */ + unsigned long rx_length_errors; + unsigned long rx_over_errors; /* receiver ring buff overflow */ + unsigned long rx_crc_errors; /* recved pkt with crc error */ + unsigned long rx_frame_errors; /* recv'd frame alignment error */ + unsigned long rx_fifo_errors; /* recv'r fifo overrun */ + unsigned long rx_missed_errors; /* receiver missed packet */ + + /* detailed tx_errors */ + unsigned long tx_aborted_errors; + unsigned long tx_carrier_errors; + unsigned long tx_fifo_errors; + unsigned long tx_heartbeat_errors; + unsigned long tx_window_errors; + + /* for cslip etc */ + unsigned long rx_compressed; + unsigned long tx_compressed; +}; +#endif + + +/* Structure/enum declaration ------------------------------- */ +typedef struct mac_info { + void __iomem *io_addr; /* Register I/O base address */ + u16 irq; /* IRQ */ + + u16 tx_pkt_cnt; + u16 queue_pkt_len; + u16 queue_start_addr; + u16 queue_ip_summed; + u16 dbug_cnt; + u8 io_mode; /* 0:word, 2:byte */ + u8 phy_addr; + u8 imr_all; + + unsigned int flags; + unsigned int in_suspend :1; + + void (*inblk)(void __iomem *port, void *data, int length); + void (*outblk)(void __iomem *port, void *data, int length); + void (*dumpblk)(void __iomem *port, int length); + + struct device *dev; /* parent device */ + + struct resource *addr_res; /* resources found */ + struct resource *addr_req; /* resources requested */ + struct resource *irq_res; + + struct mutex addr_lock; /* phy and eeprom access lock */ + + struct delayed_work phy_poll; + struct net_device *ndev; + + spinlock_t lock; + + u32 msg_enable; + + int rx_csum; + int can_csum; + int ip_summed; + int phy_id; + struct clk *clk; + + struct work_struct link_chg_task; +} mac_info_t; + + +// cdh:add, check ok, ignore type +typedef struct synopGMACAdapterStruct +{ + /*Device Dependent Data structur*/ + gmac_device *gmacdev_pt; + + /*Os/Platform Dependent Data Structures*/ + struct mac_info *db_pt; + struct net_device *netdev_pt; + struct net_device_stats net_dev_stats; + u32 pcistate[16]; + +} nt_adapter; + +/* Below is "88E1011/88E1011S Integrated 10/100/1000 Gigabit Ethernet Transceiver" + * Register and their layouts. This Phy has been used in the Dot Aster GMAC Phy daughter. + * Since the Phy register map is standard, this map hardly changes to a different Ppy + */ + +enum MiiRegisters +{ + PHY_CONTROL_REG = 0x0000, /*Control Register*/ + PHY_STATUS_REG = 0x0001, /*Status Register */ + PHY_ID_HI_REG = 0x0002, /*PHY Identifier High Register*/ + PHY_ID_LOW_REG = 0x0003, /*PHY Identifier High Register*/ + PHY_AN_ADV_REG = 0x0004, /*Auto-Negotiation Advertisement Register*/ + PHY_LNK_PART_ABl_REG = 0x0005, /*Link Partner Ability Register (Base Page)*/ + PHY_AN_EXP_REG = 0x0006, /*Auto-Negotiation Expansion Register*/ + PHY_AN_NXT_PAGE_TX_REG = 0x0007, /*Next Page Transmit Register*/ + PHY_LNK_PART_NXT_PAGE_REG = 0x0008, /*Link Partner Next Page Register*/ + PHY_1000BT_CTRL_REG = 0x0009, /*1000BASE-T Control Register*/ + PHY_1000BT_STATUS_REG = 0x000a, /*1000BASE-T Status Register*/ + PHY_SPECIFIC_CTRL_REG = 0x0010, /*Phy specific control register*/ + PHY_SPECIFIC_STATUS_REG = 0x0011, /*Phy specific status register*/ + PHY_INTERRUPT_ENABLE_REG = 0x0012, /*Phy interrupt enable register*/ + PHY_INTERRUPT_STATUS_REG = 0x0013, /*Phy interrupt status register*/ + PHY_EXT_PHY_SPC_CTRL = 0x0014, /*Extended Phy specific control*/ + PHY_RX_ERR_COUNTER = 0x0015, /*Receive Error Counter*/ + PHY_EXT_ADDR_CBL_DIAG = 0x0016, /*Extended address for cable diagnostic register*/ + PHY_LED_CONTROL = 0x0018, /*LED Control*/ + PHY_MAN_LED_OVERIDE = 0x0019, /*Manual LED override register*/ + PHY_EXT_PHY_SPC_CTRL2 = 0x001a, /*Extended Phy specific control 2*/ + PHY_EXT_PHY_SPC_STATUS = 0x001b, /*Extended Phy specific status*/ + PHY_CBL_DIAG_REG = 0x001c, /*Cable diagnostic registers*/ + RTL8201_PAGE_SELECT = 31, + RTL8201_P7_R16 = 16, +}; + + +/* This is Control register layout. Control register is of 16 bit wide. +*/ + +enum Mii_GEN_CTRL +{ + /* Description bits R/W default value */ + Mii_reset = 0x8000, + Mii_Speed_10 = 0x0000, /* 10 Mbps 6:13 RW */ + Mii_Speed_100 = 0x2000, /* 100 Mbps 6:13 RW */ + Mii_Speed_1000 = 0x0040, /* 1000 Mbit/s 6:13 RW */ + + Mii_Duplex = 0x0100, /* Full Duplex mode 8 RW */ + Mii_AN_restart = 0x0200, /* Autonegotiation restart 9 RW */ + + Mii_Manual_Master_Config = 0x0800,/* Manual Master Config 11 RW */ + + Mii_AN_En = 0x1000, /* Autonegotiation enable 12 RW */ + + Mii_Loopback = 0x4000, /* Enable Loop back 14 RW */ + Mii_NoLoopback = 0x0000, /* Enable Loop back 14 RW */ +}; + +/* This is Status register layout. Status register is of 16 bit wide. +*/ +enum Mii_GEN_STATUS +{ + Mii_AutoNegCmplt = 0x0020, /* Autonegotiation completed 5 RW */ + Mii_RemoteFault = 0x0010, /* Remote fault 4 RW */ + Mii_AN_Ability = 0x0008, /* Autonegotiation ability 3 RW */ + Mii_Link = 0x0004, /* Link status 2 RW */ +}; + +enum Mii_Phy_Status +{ + Mii_phy_status_speed_10 = 0x0000, + Mii_phy_status_speed_100 = 0x4000, + Mii_phy_status_speed_1000 = 0x8000, + + Mii_phy_status_full_duplex = 0x0100, // cdh:bit 8, old,13 + Mii_phy_status_half_duplex = 0x0000, + + Mii_phy_status_link_up = 0x0400, +}; + +enum Mii_Link_Status +{ + LINKDOWN = 0, + LINKUP = 1, +}; + +enum Mii_Duplex_Mode +{ + HALFDUPLEX = 1, + FULLDUPLEX = 2, +}; +enum Mii_Link_Speed +{ + SPEED10 = 1, + SPEED100 = 2, + SPEED1000 = 3, +}; + +enum Mii_Loop_Back +{ + NOLOOPBACK = 0, + LOOPBACK = 1, +}; + + + +/********************************************************** + * GMAC registers Map + * For Pci based system address is BARx + GmacRegisterBase + * For any other system translation is done accordingly + **********************************************************/ +enum GmacRegisters +{ + GmacConfig = 0x0000, /* Mac config Register */ + GmacFrameFilter = 0x0004, /* Mac frame filtering controls */ + GmacHashHigh = 0x0008, /* Multi-cast hash table high */ + GmacHashLow = 0x000C, /* Multi-cast hash table low */ + GmacGmiiAddr = 0x0010, /* GMII address Register(ext. Phy) */ + GmacGmiiData = 0x0014, /* GMII data Register(ext. Phy) */ + GmacFlowControl = 0x0018, /* Flow control Register */ + GmacVlan = 0x001C, /* VLAN tag Register (IEEE 802.1Q) */ + + GmacVersion = 0x0020, /* GMAC Core Version Register */ + GmacWakeupAddr = 0x0028, /* GMAC wake-up frame filter adrress reg */ + GmacPmtCtrlStatus = 0x002C, /* PMT control and status register */ + +#ifdef LPI_SUPPORT + GmacLPICtrlSts = 0x0030, /* LPI (low power idle) Control and Status Register */ + GmacLPITimerCtrl = 0x0034, /* LPI timer control register */ +#endif + + GmacInterruptStatus = 0x0038, /* Mac Interrupt ststus register */ + GmacInterruptMask = 0x003C, /* Mac Interrupt Mask register */ + + GmacAddr0High = 0x0040, /* Mac address0 high Register */ + GmacAddr0Low = 0x0044, /* Mac address0 low Register */ + GmacAddr1High = 0x0048, /* Mac address1 high Register */ + GmacAddr1Low = 0x004C, /* Mac address1 low Register */ + GmacAddr2High = 0x0050, /* Mac address2 high Register */ + GmacAddr2Low = 0x0054, /* Mac address2 low Register */ + GmacAddr3High = 0x0058, /* Mac address3 high Register */ + GmacAddr3Low = 0x005C, /* Mac address3 low Register */ + GmacAddr4High = 0x0060, /* Mac address4 high Register */ + GmacAddr4Low = 0x0064, /* Mac address4 low Register */ + GmacAddr5High = 0x0068, /* Mac address5 high Register */ + GmacAddr5Low = 0x006C, /* Mac address5 low Register */ + GmacAddr6High = 0x0070, /* Mac address6 high Register */ + GmacAddr6Low = 0x0074, /* Mac address6 low Register */ + GmacAddr7High = 0x0078, /* Mac address7 high Register */ + GmacAddr7Low = 0x007C, /* Mac address7 low Register */ + GmacAddr8High = 0x0080, /* Mac address8 high Register */ + GmacAddr8Low = 0x0084, /* Mac address8 low Register */ + GmacAddr9High = 0x0088, /* Mac address9 high Register */ + GmacAddr9Low = 0x008C, /* Mac address9 low Register */ + GmacAddr10High = 0x0090, /* Mac address10 high Register */ + GmacAddr10Low = 0x0094, /* Mac address10 low Register */ + GmacAddr11High = 0x0098, /* Mac address11 high Register */ + GmacAddr11Low = 0x009C, /* Mac address11 low Register */ + GmacAddr12High = 0x00A0, /* Mac address12 high Register */ + GmacAddr12Low = 0x00A4, /* Mac address12 low Register */ + GmacAddr13High = 0x00A8, /* Mac address13 high Register */ + GmacAddr13Low = 0x00AC, /* Mac address13 low Register */ + GmacAddr14High = 0x00B0, /* Mac address14 high Register */ + GmacAddr14Low = 0x00B4, /* Mac address14 low Register */ + GmacAddr15High = 0x00B8, /* Mac address15 high Register */ + GmacAddr15Low = 0x00BC, /* Mac address15 low Register */ + + /*Time Stamp Register Map*/ + GmacTSControl = 0x0700, /* Controls the Timestamp update logic : only when IEEE 1588 time stamping is enabled in corekit */ + + GmacTSSubSecIncr = 0x0704, /* 8 bit value by which sub second register is incremented : only when IEEE 1588 time stamping without external timestamp input */ + + GmacTSHigh = 0x0708, /* 32 bit seconds(MS) : only when IEEE 1588 time stamping without external timestamp input */ + GmacTSLow = 0x070C, /* 32 bit nano seconds(MS) : only when IEEE 1588 time stamping without external timestamp input */ + + GmacTSHighUpdate = 0x0710, /* 32 bit seconds(MS) to be written/added/subtracted : only when IEEE 1588 time stamping without external timestamp input */ + GmacTSLowUpdate = 0x0714, /* 32 bit nano seconds(MS) to be writeen/added/subtracted : only when IEEE 1588 time stamping without external timestamp input */ + + GmacTSAddend = 0x0718, /* Used by Software to readjust the clock frequency linearly : only when IEEE 1588 time stamping without external timestamp input */ + + GmacTSTargetTimeHigh = 0x071C, /* 32 bit seconds(MS) to be compared with system time : only when IEEE 1588 time stamping without external timestamp input */ + GmacTSTargetTimeLow = 0x0720, /* 32 bit nano seconds(MS) to be compared with system time : only when IEEE 1588 time stamping without external timestamp input */ + + GmacTSHighWord = 0x0724, /* Time Stamp Higher Word Register (Version 2 only); only lower 16 bits are valid */ + //GmacTSHighWordUpdate = 0x072C, /* Time Stamp Higher Word Update Register (Version 2 only); only lower 16 bits are valid */ + + GmacTSStatus = 0x0728, /* Time Stamp Status Register */ +#ifdef AVB_SUPPORT + GmacAvMacCtrl = 0x0738, /* AV mac control Register */ +#endif + +}; + +/********************************************************** + * GMAC Network interface registers + * This explains the Register's Layout + + * FES is Read only by default and is enabled only when Tx + * Config Parameter is enabled for RGMII/SGMII interface + * during CoreKit Config. + + * DM is Read only with value 1'b1 in Full duplex only Config + **********************************************************/ + +/* GmacConfig = 0x0000, Mac config Register Layout */ +enum GmacConfigReg +{ + /* Bit description Bits R/W Reset value */ + GmacWatchdog = 0x00800000, + GmacWatchdogDisable = 0x00800000, /* (WD)Disable watchdog timer on Rx 23 RW */ + GmacWatchdogEnable = 0x00000000, /* Enable watchdog timer 0 */ + + GmacJabber = 0x00400000, + GmacJabberDisable = 0x00400000, /* (JD)Disable jabber timer on Tx 22 RW */ + GmacJabberEnable = 0x00000000, /* Enable jabber timer 0 */ + + GmacFrameBurst = 0x00200000, + GmacFrameBurstEnable = 0x00200000, /* (BE)Enable frame bursting during Tx 21 RW */ + GmacFrameBurstDisable = 0x00000000, /* Disable frame bursting 0 */ + + GmacJumboFrame = 0x00100000, + GmacJumboFrameEnable = 0x00100000, /* (JE)Enable jumbo frame for Tx 20 RW */ + GmacJumboFrameDisable = 0x00000000, /* Disable jumbo frame 0 */ + + GmacInterFrameGap7 = 0x000E0000, /* (IFG) Config7 - 40 bit times 19:17 RW */ + GmacInterFrameGap6 = 0x000C0000, /* (IFG) Config6 - 48 bit times */ + GmacInterFrameGap5 = 0x000A0000, /* (IFG) Config5 - 56 bit times */ + GmacInterFrameGap4 = 0x00080000, /* (IFG) Config4 - 64 bit times */ + GmacInterFrameGap3 = 0x00040000, /* (IFG) Config3 - 72 bit times */ + GmacInterFrameGap2 = 0x00020000, /* (IFG) Config2 - 80 bit times */ + GmacInterFrameGap1 = 0x00010000, /* (IFG) Config1 - 88 bit times */ + GmacInterFrameGap0 = 0x00000000, /* (IFG) Config0 - 96 bit times 000 */ + + GmacDisableCrs = 0x00010000, + GmacMiiGmii = 0x00008000, + GmacSelectMii = 0x00008000, /* (PS)Port Select-MII mode 15 RW */ + GmacSelectGmii = 0x00000000, /* GMII mode 0 */ + + GmacFESpeed100 = 0x00004000, /*(FES)Fast Ethernet speed 100Mbps 14 RW */ + GmacFESpeed10 = 0x00000000, /* 10Mbps 0 */ + + GmacRxOwn = 0x00002000, + GmacDisableRxOwn = 0x00002000, /* (DO)Disable receive own packets 13 RW */ + GmacEnableRxOwn = 0x00000000, /* Enable receive own packets 0 */ + + GmacLoopback = 0x00001000, + GmacLoopbackOn = 0x00001000, /* (LM)Loopback mode for GMII/MII 12 RW */ + GmacLoopbackOff = 0x00000000, /* Normal mode 0 */ + + GmacDuplex = 0x00000800, + GmacFullDuplex = 0x00000800, /* (DM)Full duplex mode 11 RW */ + GmacHalfDuplex = 0x00000000, /* Half duplex mode 0 */ + + GmacRxIpcOffload = 0x00000400, /*IPC checksum offload 10 RW 0 */ + + GmacRetry = 0x00000200, + GmacRetryDisable = 0x00000200, /* (DR)Disable Retry 9 RW */ + GmacRetryEnable = 0x00000000, /* Enable retransmission as per BL 0 */ + + GmacLinkUp = 0x00000100, /* (LUD)Link UP 8 RW */ + GmacLinkDown = 0x00000100, /* Link Down 0 */ + + GmacPadCrcStrip = 0x00000080, + GmacPadCrcStripEnable = 0x00000080, /* (ACS) Automatic Pad/Crc strip enable 7 RW */ + GmacPadCrcStripDisable = 0x00000000, /* Automatic Pad/Crc stripping disable 0 */ + + GmacBackoffLimit = 0x00000060, + GmacBackoffLimit3 = 0x00000060, /* (BL)Back-off limit in HD mode 6:5 RW */ + GmacBackoffLimit2 = 0x00000040, /* */ + GmacBackoffLimit1 = 0x00000020, /* */ + GmacBackoffLimit0 = 0x00000000, /* 00 */ + + GmacDeferralCheck = 0x00000010, + GmacDeferralCheckEnable = 0x00000010, /* (DC)Deferral check enable in HD mode 4 RW */ + GmacDeferralCheckDisable = 0x00000000, /* Deferral check disable 0 */ + + GmacTx = 0x00000008, + GmacTxEnable = 0x00000008, /* (TE)Transmitter enable 3 RW */ + GmacTxDisable = 0x00000000, /* Transmitter disable 0 */ + + GmacRx = 0x00000004, + GmacRxEnable = 0x00000004, /* (RE)Receiver enable 2 RW */ + GmacRxDisable = 0x00000000, /* Receiver disable 0 */ +}; + +/* GmacFrameFilter = 0x0004, Mac frame filtering controls Register Layout*/ +enum GmacFrameFilterReg +{ + GmacFilter = 0x80000000, + GmacFilterOff = 0x80000000, /* (RA)Receive all incoming packets 31 RW */ + GmacFilterOn = 0x00000000, /* Receive filtered packets only 0 */ + + GmacHashPerfectFilter = 0x00000400, /*Hash or Perfect Filter enable 10 RW 0 */ + + GmacSrcAddrFilter = 0x00000200, + GmacSrcAddrFilterEnable = 0x00000200, /* (SAF)Source Address Filter enable 9 RW */ + GmacSrcAddrFilterDisable = 0x00000000, /* 0 */ + + GmacSrcInvaAddrFilter = 0x00000100, + GmacSrcInvAddrFilterEn = 0x00000100, /* (SAIF)Inv Src Addr Filter enable 8 RW */ + GmacSrcInvAddrFilterDis = 0x00000000, /* 0 */ + + GmacPassControl = 0x000000C0, + GmacPassControl3 = 0x000000C0, /* (PCS)Forwards ctrl frms that pass AF 7:6 RW */ + GmacPassControl2 = 0x00000080, /* Forwards all control frames */ + GmacPassControl1 = 0x00000040, /* Does not pass control frames */ + GmacPassControl0 = 0x00000000, /* Does not pass control frames 00 */ + + GmacBroadcast = 0x00000020, + GmacBroadcastDisable = 0x00000020, /* (DBF)Disable Rx of broadcast frames 5 RW */ + GmacBroadcastEnable = 0x00000000, /* Enable broadcast frames 0 */ + + GmacMulticastFilter = 0x00000010, + GmacMulticastFilterOff = 0x00000010, /* (PM) Pass all multicast packets 4 RW */ + GmacMulticastFilterOn = 0x00000000, /* Pass filtered multicast packets 0 */ + + GmacDestAddrFilter = 0x00000008, + GmacDestAddrFilterInv = 0x00000008, /* (DAIF)Inverse filtering for DA 3 RW */ + GmacDestAddrFilterNor = 0x00000000, /* Normal filtering for DA 0 */ + + GmacMcastHashFilter = 0x00000004, + GmacMcastHashFilterOn = 0x00000004, /* (HMC)perfom multicast hash filtering 2 RW */ + GmacMcastHashFilterOff = 0x00000000, /* perfect filtering only 0 */ + + GmacUcastHashFilter = 0x00000002, + GmacUcastHashFilterOn = 0x00000002, /* (HUC)Unicast Hash filtering only 1 RW */ + GmacUcastHashFilterOff = 0x00000000, /* perfect filtering only 0 */ + + GmacPromiscuousMode = 0x00000001, + GmacPromiscuousModeOn = 0x00000001, /* Receive all frames 0 RW */ + GmacPromiscuousModeOff = 0x00000000, /* Receive filtered packets only 0 */ +}; + + +/*GmacGmiiAddr = 0x0010, GMII address Register(ext. Phy) Layout */ +enum GmacGmiiAddrReg +{ + GmiiDevMask = 0x0000F800, /* (PA)GMII device address 15:11 RW 0x00 */ + GmiiDevShift = 11, + + GmiiRegMask = 0x000007C0, /* (GR)GMII register in selected Phy 10:6 RW 0x00 */ + GmiiRegShift = 6, + + GmiiCsrClkMask = 0x0000003C, /* cdh:CSR Clock bit Mask 4:2 , must :0x0000001C */ + GmiiCsrClk5 = 0x00000014, /* (CR)CSR Clock Range 250-300 MHz 4:2 RW 000 */ + GmiiCsrClk4 = 0x00000010, /* 150-250 MHz */ + GmiiCsrClk3 = 0x0000000C, /* 35-60 MHz */ + GmiiCsrClk2 = 0x00000008, /* 20-35 MHz */ + GmiiCsrClk1 = 0x00000004, /* 100-150 MHz */ + GmiiCsrClk0 = 0x00000000, /* 60-100 MHz */ + + GmiiWrite = 0x00000002, /* (GW)Write to register 1 RW */ + GmiiRead = 0x00000000, /* Read from register 0 */ + + GmiiBusy = 0x00000001, /* (GB)GMII interface is busy 0 RW 0 */ +}; + +/* GmacGmiiData = 0x0014, GMII data Register(ext. Phy) Layout */ +enum GmacGmiiDataReg +{ + GmiiDataMask = 0x0000FFFF, /* (GD)GMII Data 15:0 RW 0x0000 */ +}; + + +/*GmacFlowControl = 0x0018, Flow control Register Layout */ +enum GmacFlowControlReg +{ + GmacPauseTimeMask = 0xFFFF0000, /* (PT) PAUSE TIME field in the control frame 31:16 RW 0x0000 */ + GmacPauseTimeShift = 16, + + GmacPauseLowThresh = 0x00000030, + GmacPauseLowThresh3 = 0x00000030, /* (PLT)thresh for pause tmr 256 slot time 5:4 RW */ + GmacPauseLowThresh2 = 0x00000020, /* 144 slot time */ + GmacPauseLowThresh1 = 0x00000010, /* 28 slot time */ + GmacPauseLowThresh0 = 0x00000000, /* 4 slot time 000 */ + + GmacUnicastPauseFrame = 0x00000008, + GmacUnicastPauseFrameOn = 0x00000008, /* (UP)Detect pause frame with unicast addr. 3 RW */ + GmacUnicastPauseFrameOff = 0x00000000, /* Detect only pause frame with multicast addr. 0 */ + + GmacRxFlowControl = 0x00000004, + GmacRxFlowControlEnable = 0x00000004, /* (RFE)Enable Rx flow control 2 RW */ + GmacRxFlowControlDisable = 0x00000000, /* Disable Rx flow control 0 */ + + GmacTxFlowControl = 0x00000002, + GmacTxFlowControlEnable = 0x00000002, /* (TFE)Enable Tx flow control 1 RW */ + GmacTxFlowControlDisable = 0x00000000, /* Disable flow control 0 */ + + GmacFlowControlBackPressure = 0x00000001, + GmacSendPauseFrame = 0x00000001, /* (FCB/PBA)send pause frm/Apply back pressure 0 RW 0 */ +}; + +/* GmacInterruptStatus = 0x0038, Mac Interrupt ststus register */ +enum GmacInterruptStatusBitDefinition +{ + GmacTSIntSts = 0x00000200, /* set if int generated due to TS (Read Time Stamp Status Register to know details)*/ + GmacMmcRxChksumOffload = 0x00000080, /* set if int generated in MMC RX CHECKSUM OFFLOAD int register */ + GmacMmcTxIntSts = 0x00000040, /* set if int generated in MMC TX Int register */ + GmacMmcRxIntSts = 0x00000020, /* set if int generated in MMC RX Int register */ + GmacMmcIntSts = 0x00000010, /* set if any of the above bit [7:5] is set */ + GmacPmtIntSts = 0x00000008, /* set whenver magic pkt/wake-on-lan frame is received */ + GmacPcsAnComplete = 0x00000004, /* set when AN is complete in TBI/RTBI/SGMIII phy interface */ + GmacPcsLnkStsChange = 0x00000002, /* set if any lnk status change in TBI/RTBI/SGMII interface */ + GmacRgmiiIntSts = 0x00000001, /* set if any change in lnk status of RGMII interface */ + +}; + +/* GmacInterruptMask = 0x003C, Mac Interrupt Mask register */ +enum GmacInterruptMaskBitDefinition +{ + GmacTSIntMask = 0x00000200, /* when set disables the time stamp interrupt generation */ + GmacPmtIntMask = 0x00000008, /* when set Disables the assertion of PMT interrupt */ + GmacPcsAnIntMask = 0x00000004, /* When set disables the assertion of PCS AN complete interrupt */ + GmacPcsLnkStsIntMask = 0x00000002, /* when set disables the assertion of PCS lnk status change interrupt */ + GmacRgmiiIntMask = 0x00000001, /* when set disables the assertion of RGMII int */ +}; + +/********************************************************** + * GMAC DMA registers + * For Pci based system address is BARx + GmaDmaBase + * For any other system translation is done accordingly + **********************************************************/ + +enum DmaRegisters +{ + DmaBusMode = 0x0000, /* CSR0 - Bus Mode Register */ + DmaTxPollDemand = 0x0004, /* CSR1 - Transmit Poll Demand Register */ + DmaRxPollDemand = 0x0008, /* CSR2 - Receive Poll Demand Register */ + DmaRxBaseAddr = 0x000C, /* CSR3 - Receive Descriptor list base address */ + DmaTxBaseAddr = 0x0010, /* CSR4 - Transmit Descriptor list base address */ + DmaStatus = 0x0014, /* CSR5 - Dma status Register */ + DmaControl = 0x0018, /* CSR6 - Dma Operation Mode Register */ + DmaInterrupt = 0x001C, /* CSR7 - Interrupt enable */ + DmaMissedFr = 0x0020, /* CSR8 - Missed Frame & Buffer overflow Counter */ + DmaTxCurrDesc = 0x0048, /* - Current host Tx Desc Register */ + DmaRxCurrDesc = 0x004C, /* - Current host Rx Desc Register */ + DmaTxCurrAddr = 0x0050, /* CSR20 - Current host transmit buffer address */ + DmaRxCurrAddr = 0x0054, /* CSR21 - Current host receive buffer address */ + +#ifdef AVB_SUPPORT + HwFeature = 0x0058, /* Hardware Feature Register */ + + DmaSlotFnCtrlSts = 0x0030, /* Slot function control and status register */ + + DmaChannelCtrl = 0x0060, /* Channel Control register only for Channel1 and Channel2 */ + DmaChannelAvSts = 0x0064, /* Channel Status register only for Channel1 and Channel2 */ + IdleSlopeCredit = 0x0068, /* Idle slope credit register */ + SendSlopeCredit = 0x006C, /* Send slope credit register */ + HighCredit = 0x0070, /* High Credit register */ + LoCredit = 0x0074, /* Lo Credit Register */ +#endif + +}; + +/********************************************************** + * DMA Engine registers Layout + **********************************************************/ + +/*DmaBusMode = 0x0000, CSR0 - Bus Mode */ +enum DmaBusModeReg +{ + /* Bit description Bits R/W Reset value */ +#ifdef AVB_SUPPORT + DmaChannelPrioWt = 0x30000000, /* Channel priority weight mask 29:28 RW 0 */ + DmaChannelPrio1 = 0x00000000, /* Channel priority weight 1 29:28 RW 0 */ + DmaChannelPrio2 = 0x10000000, /* Channel priority weight 2 29:28 RW 0 */ + DmaChannelPrio3 = 0x20000000, /* Channel priority weight 3 29:28 RW 0 */ + DmaChannelPrio4 = 0x30000000, /* Channel priority weight 4 29:28 RW 0 */ + + DmaTxRxPrio = 0x08000000, /* When set indicates Tx Dma has more priority 27 RW 0 */ + + DmaPriorityRatio11 = 0x00000000, /* (PR)TX:RX DMA priority ratio 1:1 15:14 RW 00 */ + DmaPriorityRatio21 = 0x00004000, /* (PR)TX:RX DMA priority ratio 2:1 */ + DmaPriorityRatio31 = 0x00008000, /* (PR)TX:RX DMA priority ratio 3:1 */ + DmaPriorityRatio41 = 0x0000C000, /* (PR)TX:RX DMA priority ratio 4:1 */ + + DmaArbitration = 0x00000002, /* Dma Arbitration decides whether strict prio or RR 1 RW 0 */ + DmaArbitrationStrict = 0x00000002, /* Dma Arbitration decides whether strict prio or RR 1 RW 0 */ + DmaArbitrationRR = 0x00000000, /* Dma Arbitration decides whether strict prio or RR 0 RW 0 */ +#endif + + + DmaFixedBurstEnable = 0x00010000, /* (FB)Fixed Burst SINGLE, INCR4, INCR8 or INCR16 16 RW */ + DmaFixedBurstDisable = 0x00000000, /* SINGLE, INCR 0 */ + + DmaTxPriorityRatio11 = 0x00000000, /* (PR)TX:RX DMA priority ratio 1:1 15:14 RW 00 */ + DmaTxPriorityRatio21 = 0x00004000, /* (PR)TX:RX DMA priority ratio 2:1 */ + DmaTxPriorityRatio31 = 0x00008000, /* (PR)TX:RX DMA priority ratio 3:1 */ + DmaTxPriorityRatio41 = 0x0000C000, /* (PR)TX:RX DMA priority ratio 4:1 */ + + DmaBurstLengthx8 = 0x01000000, /* When set mutiplies the PBL by 8 24 RW 0 */ + + DmaBurstLength256 = 0x01002000, /*(DmaBurstLengthx8 | DmaBurstLength32) = 256 [24]:13:8 */ + DmaBurstLength128 = 0x01001000, /*(DmaBurstLengthx8 | DmaBurstLength16) = 128 [24]:13:8 */ + DmaBurstLength64 = 0x01000800, /*(DmaBurstLengthx8 | DmaBurstLength8) = 64 [24]:13:8 */ + DmaBurstLength32 = 0x00002000, /* (PBL) programmable Dma burst length = 32 13:8 RW */ + DmaBurstLength16 = 0x00001000, /* Dma burst length = 16 */ + DmaBurstLength8 = 0x00000800, /* Dma burst length = 8 */ + DmaBurstLength4 = 0x00000400, /* Dma burst length = 4 */ + DmaBurstLength2 = 0x00000200, /* Dma burst length = 2 */ + DmaBurstLength1 = 0x00000100, /* Dma burst length = 1 */ + DmaBurstLength0 = 0x00000000, /* Dma burst length = 0 0x00 */ + + DmaDescriptor8Words = 0x00000080, /* Enh Descriptor works 1=> 8 word descriptor 7 0 */ + DmaDescriptor4Words = 0x00000000, /* Enh Descriptor works 0=> 4 word descriptor 7 0 */ + + DmaDescriptorSkip16 = 0x00000040, /* (DSL)Descriptor skip length (no.of dwords) 6:2 RW */ + DmaDescriptorSkip8 = 0x00000020, /* between two unchained descriptors */ + DmaDescriptorSkip4 = 0x00000010, /* */ + DmaDescriptorSkip2 = 0x00000008, /* */ + DmaDescriptorSkip1 = 0x00000004, /* */ + DmaDescriptorSkip0 = 0x00000000, /* 0x00 */ + + DmaArbitRr = 0x00000000, /* (DA) DMA RR arbitration 1 RW 0 */ + DmaArbitPr = 0x00000002, /* Rx has priority over Tx */ + + DmaResetOn = 0x00000001, /* (SWR)Software Reset DMA engine 0 RW */ + DmaResetOff = 0x00000000, /* 0 */ +}; + + +/*DmaStatus = 0x0014, CSR5 - Dma status Register */ +enum DmaStatusReg +{ + /*Bit 28 27 and 26 indicate whether the interrupt due to PMT GMACMMC or GMAC LINE Remaining bits are DMA interrupts*/ + +#ifdef AVB_SUPPORT + DmaSlotCounterIntr = 0x40000000, /* For Ch1 and Ch2 AVB slot interrupt status 31 RW 0 */ +#endif +#ifdef LPI_SUPPORT + GmacLPIIntr = 0x40000000, /* GMC LPI interrupt 31 RO 0 */ +#endif + + GmacPmtIntr = 0x10000000, /* (GPI)Gmac subsystem interrupt 28 RO 0 */ + GmacMmcIntr = 0x08000000, /* (GMI)Gmac MMC subsystem interrupt 27 RO 0 */ + GmacLineIntfIntr = 0x04000000, /* Line interface interrupt 26 RO 0 */ + + DmaErrorBit2 = 0x02000000, /* (EB)Error bits 0-data buffer, 1-desc. access 25 RO 0 */ + DmaErrorBit1 = 0x01000000, /* (EB)Error bits 0-write trnsf, 1-read transfr 24 RO 0 */ + DmaErrorBit0 = 0x00800000, /* (EB)Error bits 0-Rx DMA, 1-Tx DMA 23 RO 0 */ + + DmaTxState = 0x00700000, /* (TS)Transmit process state 22:20 RO */ + DmaTxStopped = 0x00000000, /* Stopped - Reset or Stop Tx Command issued 000 */ + DmaTxFetching = 0x00100000, /* Running - fetching the Tx descriptor */ + DmaTxWaiting = 0x00200000, /* Running - waiting for status */ + DmaTxReading = 0x00300000, /* Running - reading the data from host memory */ + DmaTxSuspended = 0x00600000, /* Suspended - Tx Descriptor unavailabe */ + DmaTxClosing = 0x00700000, /* Running - closing Rx descriptor */ + + DmaRxState = 0x000E0000, /* (RS)Receive process state 19:17 RO */ + DmaRxStopped = 0x00000000, /* Stopped - Reset or Stop Rx Command issued 000 */ + DmaRxFetching = 0x00020000, /* Running - fetching the Rx descriptor */ + DmaRxWaiting = 0x00060000, /* Running - waiting for packet */ + DmaRxSuspended = 0x00080000, /* Suspended - Rx Descriptor unavailable */ + DmaRxClosing = 0x000A0000, /* Running - closing descriptor */ + DmaRxQueuing = 0x000E0000, /* Running - queuing the recieve frame into host memory */ + + DmaIntNormal = 0x00010000, /* (NIS)Normal interrupt summary 16 RW 0 */ + DmaIntAbnormal = 0x00008000, /* (AIS)Abnormal interrupt summary 15 RW 0 */ + + DmaIntEarlyRx = 0x00004000, /* Early receive interrupt (Normal) RW 0 */ + DmaIntBusError = 0x00002000, /* Fatal bus error (Abnormal) RW 0 */ + DmaIntEarlyTx = 0x00000400, /* Early transmit interrupt (Abnormal) RW 0 */ + DmaIntRxWdogTO = 0x00000200, /* Receive Watchdog Timeout (Abnormal) RW 0 */ + DmaIntRxStopped = 0x00000100, /* Receive process stopped (Abnormal) RW 0 */ + DmaIntRxNoBuffer = 0x00000080, /* Receive buffer unavailable (Abnormal) RW 0 */ + DmaIntRxCompleted = 0x00000040, /* Completion of frame reception (Normal) RW 0 */ + DmaIntTxUnderflow = 0x00000020, /* Transmit underflow (Abnormal) RW 0 */ + DmaIntRcvOverflow = 0x00000010, /* Receive Buffer overflow interrupt RW 0 */ + DmaIntTxJabberTO = 0x00000008, /* Transmit Jabber Timeout (Abnormal) RW 0 */ + DmaIntTxNoBuffer = 0x00000004, /* Transmit buffer unavailable (Normal) RW 0 */ + DmaIntTxStopped = 0x00000002, /* Transmit process stopped (Abnormal) RW 0 */ + DmaIntTxCompleted = 0x00000001, /* Transmit completed (Normal) RW 0 */ +}; + +/*DmaControl = 0x0018, CSR6 - Dma Operation Mode Register */ +enum DmaControlReg +{ + DmaDisableDropTcpCs = 0x04000000, /* (DT) Dis. drop. of tcp/ip CS error frames 26 RW 0 */ + DmaDisableFlush = 0x01000000, + + DmaStoreAndForward = 0x00200000, /* (SF)Store and forward 21 RW 0 */ + DmaFlushTxFifo = 0x00100000, /* (FTF)Tx FIFO controller is reset to default 20 RW 0 */ + + DmaTxThreshCtrl = 0x0001C000, /* (TTC)Controls thre Threh of MTL tx Fifo 16:14 RW */ + DmaTxThreshCtrl16 = 0x0001C000, /* (TTC)Controls thre Threh of MTL tx Fifo 16 16:14 RW */ + DmaTxThreshCtrl24 = 0x00018000, /* (TTC)Controls thre Threh of MTL tx Fifo 24 16:14 RW */ + DmaTxThreshCtrl32 = 0x00014000, /* (TTC)Controls thre Threh of MTL tx Fifo 32 16:14 RW */ + DmaTxThreshCtrl40 = 0x00010000, /* (TTC)Controls thre Threh of MTL tx Fifo 40 16:14 RW */ + DmaTxThreshCtrl256 = 0x0000c000, /* (TTC)Controls thre Threh of MTL tx Fifo 256 16:14 RW */ + DmaTxThreshCtrl192 = 0x00008000, /* (TTC)Controls thre Threh of MTL tx Fifo 192 16:14 RW */ + DmaTxThreshCtrl128 = 0x00004000, /* (TTC)Controls thre Threh of MTL tx Fifo 128 16:14 RW */ + DmaTxThreshCtrl64 = 0x00000000, /* (TTC)Controls thre Threh of MTL tx Fifo 64 16:14 RW 000 */ + + DmaTxStart = 0x00002000, /* (ST)Start/Stop transmission 13 RW 0 */ + + DmaRxFlowCtrlDeact = 0x00401800, /* (RFD)Rx flow control deact. threhold [22]:12:11 RW */ + DmaRxFlowCtrlDeact1K = 0x00000000, /* (RFD)Rx flow control deact. threhold (1kbytes) [22]:12:11 RW 00 */ + DmaRxFlowCtrlDeact2K = 0x00000800, /* (RFD)Rx flow control deact. threhold (2kbytes) [22]:12:11 RW */ + DmaRxFlowCtrlDeact3K = 0x00001000, /* (RFD)Rx flow control deact. threhold (3kbytes) [22]:12:11 RW */ + DmaRxFlowCtrlDeact4K = 0x00001800, /* (RFD)Rx flow control deact. threhold (4kbytes) [22]:12:11 RW */ + DmaRxFlowCtrlDeact5K = 0x00400000, /* (RFD)Rx flow control deact. threhold (4kbytes) [22]:12:11 RW */ + DmaRxFlowCtrlDeact6K = 0x00400800, /* (RFD)Rx flow control deact. threhold (4kbytes) [22]:12:11 RW */ + DmaRxFlowCtrlDeact7K = 0x00401000, /* (RFD)Rx flow control deact. threhold (4kbytes) [22]:12:11 RW */ + + DmaRxFlowCtrlAct = 0x00800600, /* (RFA)Rx flow control Act. threhold [23]:10:09 RW */ + DmaRxFlowCtrlAct1K = 0x00000000, /* (RFA)Rx flow control Act. threhold (1kbytes) [23]:10:09 RW 00 */ + DmaRxFlowCtrlAct2K = 0x00000200, /* (RFA)Rx flow control Act. threhold (2kbytes) [23]:10:09 RW */ + DmaRxFlowCtrlAct3K = 0x00000400, /* (RFA)Rx flow control Act. threhold (3kbytes) [23]:10:09 RW */ + DmaRxFlowCtrlAct4K = 0x00000300, /* (RFA)Rx flow control Act. threhold (4kbytes) [23]:10:09 RW */ + DmaRxFlowCtrlAct5K = 0x00800000, /* (RFA)Rx flow control Act. threhold (5kbytes) [23]:10:09 RW */ + DmaRxFlowCtrlAct6K = 0x00800200, /* (RFA)Rx flow control Act. threhold (6kbytes) [23]:10:09 RW */ + DmaRxFlowCtrlAct7K = 0x00800400, /* (RFA)Rx flow control Act. threhold (7kbytes) [23]:10:09 RW */ + + DmaRxThreshCtrl = 0x00000018, /* (RTC)Controls thre Threh of MTL rx Fifo 4:3 RW */ + DmaRxThreshCtrl64 = 0x00000000, /* (RTC)Controls thre Threh of MTL tx Fifo 64 4:3 RW */ + DmaRxThreshCtrl32 = 0x00000008, /* (RTC)Controls thre Threh of MTL tx Fifo 32 4:3 RW */ + DmaRxThreshCtrl96 = 0x00000010, /* (RTC)Controls thre Threh of MTL tx Fifo 96 4:3 RW */ + DmaRxThreshCtrl128 = 0x00000018, /* (RTC)Controls thre Threh of MTL tx Fifo 128 4:3 RW */ + + DmaEnHwFlowCtrl = 0x00000100, /* (EFC)Enable HW flow control 8 RW */ + DmaDisHwFlowCtrl = 0x00000000, /* Disable HW flow control 0 */ + + DmaFwdErrorFrames = 0x00000080, /* (FEF)Forward error frames 7 RW 0 */ + DmaFwdUnderSzFrames = 0x00000040, /* (FUF)Forward undersize frames 6 RW 0 */ + DmaTxSecondFrame = 0x00000004, /* (OSF)Operate on second frame 4 RW 0 */ + DmaRxStart = 0x00000002, /* (SR)Start/Stop reception 1 RW 0 */ +}; + + +/*DmaInterrupt = 0x001C, CSR7 - Interrupt enable Register Layout */ +enum DmaInterruptReg +{ + DmaIeNormal = DmaIntNormal , /* Normal interrupt enable RW 0 */ + DmaIeAbnormal = DmaIntAbnormal , /* Abnormal interrupt enable RW 0 */ + + DmaIeEarlyRx = DmaIntEarlyRx , /* Early receive interrupt enable RW 0 */ + DmaIeBusError = DmaIntBusError , /* Fatal bus error enable RW 0 */ + DmaIeEarlyTx = DmaIntEarlyTx , /* Early transmit interrupt enable RW 0 */ + DmaIeRxWdogTO = DmaIntRxWdogTO , /* Receive Watchdog Timeout enable RW 0 */ + DmaIeRxStopped = DmaIntRxStopped , /* Receive process stopped enable RW 0 */ + DmaIeRxNoBuffer = DmaIntRxNoBuffer , /* Receive buffer unavailable enable RW 0 */ + DmaIeRxCompleted = DmaIntRxCompleted, /* Completion of frame reception enable RW 0 */ + DmaIeTxUnderflow = DmaIntTxUnderflow, /* Transmit underflow enable RW 0 */ + + DmaIeRxOverflow = DmaIntRcvOverflow, /* Receive Buffer overflow interrupt RW 0 */ + DmaIeTxJabberTO = DmaIntTxJabberTO , /* Transmit Jabber Timeout enable RW 0 */ + DmaIeTxNoBuffer = DmaIntTxNoBuffer , /* Transmit buffer unavailable enable RW 0 */ + DmaIeTxStopped = DmaIntTxStopped , /* Transmit process stopped enable RW 0 */ + DmaIeTxCompleted = DmaIntTxCompleted, /* Transmit completed enable RW 0 */ +}; + +#ifdef AVB_SUPPORT +/*DmaSlotFnCtrlSts = 0x0030, Slot function control and status register */ +enum DmaSlotFnCtrlStsReg +{ + SlotNum = 0x000F0000, /* Current Slot Number 19:16 R0 0 */ + AdvSlotInt = 0x00000002, /* Advance the slot interval for data fetch 1 RW 0 */ + EnaSlot = 0x00000001, /* Enable checking of Slot number 0 RW 0 */ +}; + +/* DmaChannelCtrl = 0x0060, Channel Control register only for Channel1 and Channel2 */ +enum DmaChannelCtrlReg +{ + ChannelSlotIntEn = 0x00020000, /* Channel Slot Interrupt Enable 16 RW 0 */ + ChannelSlotCount = 0x00000070, /* Channel Slot Count 6:4 RW 0 */ + ChannelCreditCtrl = 0x00000002, /* Channel Credit Control 1 RW 0 */ + ChannelCreditShDis = 0x00000001, /* Channel Credit based shaping disable 0 RW 0 */ +}; + +/* DmaChannelSts = 0x0064, Channel Status register only for Channel1 and Channel2 */ +enum DmaChannelStsReg +{ + ChannelAvBitsPerSlot = 0x0000FFFF, /* Channel Average Bits per slot 16:0 RO 0 */ +}; + +/* IdleSlopeCredit = 0x0068, Idle slope credit register */ +enum IdleSlopeCreditReg +{ + ChannelIdleSlCr = 0x00003FFF, /*Channel Idle Slope Credit 13:0 RW 0 */ +}; + +/*SendSlopeCredit = 0x006C, Send slope credit register */ +enum SendSlopeCreditReg +{ + ChannelSendSlCr = 0x00003FFF, /*Channel Send Slope Credit 13:0 RW 0 */ +}; + +/* HighCredit = 0x0070, High Credit register */ +enum HighCreditReg +{ + ChannelHiCr = 0x1FFFFFFF, /*Channel Hi Credit 28:0 RW 0 */ +}; + +/* LoCredit = 0x0074, Lo Credit Register */ +enum LoCreditReg +{ + ChannelLoCr = 0x1FFFFFFF, /* Channel Lo Credit 28:0 RW 0 */ +}; +/*DmaChannelAvSts */ +enum DmaChannelAvStsReg +{ + ChannelAvgBitsPerSlotMsk = 0x0001FFFF, +}; +#endif + +/********************************************************** + * DMA Engine descriptors + **********************************************************/ +#ifdef ENH_DESC +/* +**********Enhanced Descritpor structure to support 8K buffer per buffer **************************** + +DmaRxBaseAddr = 0x000C, CSR3 - Receive Descriptor list base address +DmaRxBaseAddr is the pointer to the first Rx Descriptors. the Descriptor format in Little endian with a +32 bit Data bus is as shown below + +Similarly +DmaTxBaseAddr = 0x0010, CSR4 - Transmit Descriptor list base address +DmaTxBaseAddr is the pointer to the first Rx Descriptors. the Descriptor format in Little endian with a +32 bit Data bus is as shown below + -------------------------------------------------------------------------- + RDES0 |OWN (31)| Status | + -------------------------------------------------------------------------- + RDES1 | Ctrl | Res | Byte Count Buffer 2 | Ctrl | Res | Byte Count Buffer 1 | + -------------------------------------------------------------------------- + RDES2 | Buffer 1 Address | + -------------------------------------------------------------------------- + RDES3 | Buffer 2 Address / Next Descriptor Address | + -------------------------------------------------------------------------- + + -------------------------------------------------------------------------- + TDES0 |OWN (31)| Ctrl | Res | Ctrl | Res | Status | + -------------------------------------------------------------------------- + TDES1 | Res | Byte Count Buffer 2 | Res | Byte Count Buffer 1 | + -------------------------------------------------------------------------- + TDES2 | Buffer 1 Address | + -------------------------------------------------------------------------- + TDES3 | Buffer 2 Address / Next Descriptor Address | + -------------------------------------------------------------------------- + +*/ + +enum DmaDescriptorStatus /* status word of DMA descriptor */ +{ + + DescOwnByDma = 0x80000000, /* (OWN)Descriptor is owned by DMA engine 31 RW */ + + DescDAFilterFail = 0x40000000, /* (AFM)Rx - DA Filter Fail for the rx frame 30 */ + + DescFrameLengthMask = 0x3FFF0000, /* (FL)Receive descriptor frame length 29:16 */ + DescFrameLengthShift = 16, + + DescError = 0x00008000, /* (ES)Error summary bit - OR of the follo. bits: 15 */ + /* DE || OE || IPC || LC || RWT || RE || CE */ + DescRxTruncated = 0x00004000, /* (DE)Rx - no more descriptors for receive frame 14 */ + DescSAFilterFail = 0x00002000, /* (SAF)Rx - SA Filter Fail for the received frame 13 */ + DescRxLengthError = 0x00001000, /* (LE)Rx - frm size not matching with len field 12 */ + DescRxDamaged = 0x00000800, /* (OE)Rx - frm was damaged due to buffer overflow 11 */ + DescRxVLANTag = 0x00000400, /* (VLAN)Rx - received frame is a VLAN frame 10 */ + DescRxFirst = 0x00000200, /* (FS)Rx - first descriptor of the frame 9 */ + DescRxLast = 0x00000100, /* (LS)Rx - last descriptor of the frame 8 */ + DescRxLongFrame = 0x00000080, /* (Giant Frame)Rx - frame is longer than 1518/1522 7 */ + DescRxCollision = 0x00000040, /* (LC)Rx - late collision occurred during reception 6 */ + DescRxFrameEther = 0x00000020, /* (FT)Rx - Frame type - Ethernet, otherwise 802.3 5 */ + DescRxWatchdog = 0x00000010, /* (RWT)Rx - watchdog timer expired during reception 4 */ + DescRxMiiError = 0x00000008, /* (RE)Rx - error reported by MII interface 3 */ + DescRxDribbling = 0x00000004, /* (DE)Rx - frame contains non int multiple of 8 bits 2 */ + DescRxCrc = 0x00000002, /* (CE)Rx - CRC error 1 */ + //DescRxMacMatch = 0x00000001, /* (RX MAC Address) Rx mac address reg(1 to 15)match 0 */ + + DescRxEXTsts = 0x00000001, /* Extended Status Available (RDES4) 0 */ + + DescTxIntEnable = 0x40000000, /* (IC)Tx - interrupt on completion 30 */ + DescTxLast = 0x20000000, /* (LS)Tx - Last segment of the frame 29 */ + DescTxFirst = 0x10000000, /* (FS)Tx - First segment of the frame 28 */ + DescTxDisableCrc = 0x08000000, /* (DC)Tx - Add CRC disabled (first segment only) 27 */ + DescTxDisablePadd = 0x04000000, /* (DP)disable padding, added by - reyaz 26 */ + + DescTxCisMask = 0x00c00000, /* Tx checksum offloading control mask 23:22 */ + DescTxCisBypass = 0x00000000, /* Checksum bypass */ + DescTxCisIpv4HdrCs = 0x00400000, /* IPv4 header checksum */ + DescTxCisTcpOnlyCs = 0x00800000, /* TCP/UDP/ICMP checksum. Pseudo header checksum is assumed to be present */ + DescTxCisTcpPseudoCs = 0x00c00000, /* TCP/UDP/ICMP checksum fully in hardware including pseudo header */ + + TxDescEndOfRing = 0x00200000, /* (TER)End of descriptors ring 21 */ + TxDescChain = 0x00100000, /* (TCH)Second buffer address is chain address 20 */ + + DescRxChkBit0 = 0x00000001, /*() Rx - Rx Payload Checksum Error 0 */ + DescRxChkBit7 = 0x00000080, /* (IPC CS ERROR)Rx - Ipv4 header checksum error 7 */ + DescRxChkBit5 = 0x00000020, /* (FT)Rx - Frame type - Ethernet, otherwise 802.3 5 */ + + DescRxTSavail = 0x00000080, /* Time stamp available 7 */ + DescRxFrameType = 0x00000020, /* (FT)Rx - Frame type - Ethernet, otherwise 802.3 5 */ + + DescTxIpv4ChkError = 0x00010000, /* (IHE) Tx Ip header error 16 */ + DescTxTimeout = 0x00004000, /* (JT)Tx - Transmit jabber timeout 14 */ + DescTxFrameFlushed = 0x00002000, /* (FF)Tx - DMA/MTL flushed the frame due to SW flush 13 */ + DescTxPayChkError = 0x00001000, /* (PCE) Tx Payload checksum Error 12 */ + DescTxLostCarrier = 0x00000800, /* (LC)Tx - carrier lost during tramsmission 11 */ + DescTxNoCarrier = 0x00000400, /* (NC)Tx - no carrier signal from the tranceiver 10 */ + DescTxLateCollision = 0x00000200, /* (LC)Tx - transmission aborted due to collision 9 */ + DescTxExcCollisions = 0x00000100, /* (EC)Tx - transmission aborted after 16 collisions 8 */ + DescTxVLANFrame = 0x00000080, /* (VF)Tx - VLAN-type frame 7 */ + + DescTxCollMask = 0x00000078, /* (CC)Tx - Collision count 6:3 */ + DescTxCollShift = 3, + + DescTxExcDeferral = 0x00000004, /* (ED)Tx - excessive deferral 2 */ + DescTxUnderflow = 0x00000002, /* (UF)Tx - late data arrival from the memory 1 */ + DescTxDeferred = 0x00000001, /* (DB)Tx - frame transmision deferred 0 */ + + /* + This explains the RDES1/TDES1 bits layout + -------------------------------------------------------------------- + RDES1/TDES1 | Control Bits | Byte Count Buffer 2 | Byte Count Buffer 1 | + -------------------------------------------------------------------- + */ + // DmaDescriptorLength length word of DMA descriptor + + + RxDisIntCompl = 0x80000000, /* (Disable Rx int on completion) 31 */ + RxDescEndOfRing = 0x00008000, /* (TER)End of descriptors ring 15 */ + RxDescChain = 0x00004000, /* (TCH)Second buffer address is chain address 14 */ + + + DescSize2Mask = 0x1FFF0000, /* (TBS2) Buffer 2 size 28:16 */ + DescSize2Shift = 16, + DescSize1Mask = 0x00001FFF, /* (TBS1) Buffer 1 size 12:0 */ + DescSize1Shift = 0, + + + /* + This explains the RDES4 Extended Status bits layout + -------------------------------------------------------------------- + RDES4 | Extended Status | + -------------------------------------------------------------------- + */ + +#ifdef AVB_SUPPORT + DescRxVlanPrioVal = 0x001C0000, /* Gives the VLAN Priority Value 20:18 */ + DescRxVlanPrioShVal = 18, /* Gives the shift value to get priority value in LS bits */ + + DescRxAvTagPktRx = 0x00020000, /* Indicates AV tagged Packet is received 17 */ + DescRxAvPktRx = 0x00010000, /* Indicates AV Packet received 16 */ +#endif + + DescRxPtpAvail = 0x00004000, /* PTP snapshot available 14 */ + DescRxPtpVer = 0x00002000, /* When set indicates IEEE1584 Version 2 (else Ver1) 13 */ + DescRxPtpFrameType = 0x00001000, /* PTP frame type Indicates PTP sent over ethernet 12 */ + DescRxPtpMessageType = 0x00000F00, /* Message Type 11:8 */ + DescRxPtpNo = 0x00000000, /* 0000 => No PTP message received */ + DescRxPtpSync = 0x00000100, /* 0001 => Sync (all clock types) received */ + DescRxPtpFollowUp = 0x00000200, /* 0010 => Follow_Up (all clock types) received */ + DescRxPtpDelayReq = 0x00000300, /* 0011 => Delay_Req (all clock types) received */ + DescRxPtpDelayResp = 0x00000400, /* 0100 => Delay_Resp (all clock types) received */ + DescRxPtpPdelayReq = 0x00000500, /* 0101 => Pdelay_Req (in P to P tras clk) or Announce in Ord and Bound clk */ + DescRxPtpPdelayResp = 0x00000600, /* 0110 => Pdealy_Resp(in P to P trans clk) or Management in Ord and Bound clk */ + DescRxPtpPdelayRespFP = 0x00000700, /* 0111 => Pdealy_Resp_Follow_Up (in P to P trans clk) or Signaling in Ord and Bound clk */ + DescRxPtpIPV6 = 0x00000080, /* Received Packet is in IPV6 Packet 7 */ + DescRxPtpIPV4 = 0x00000040, /* Received Packet is in IPV4 Packet 6 */ + + DescRxChkSumBypass = 0x00000020, /* When set indicates checksum offload engine 5 + is bypassed */ + DescRxIpPayloadError = 0x00000010, /* When set indicates 16bit IP payload CS is in error 4 */ + DescRxIpHeaderError = 0x00000008, /* When set indicates 16bit IPV4 header CS is in 3 + error or IP datagram version is not consistent + with Ethernet type value */ + DescRxIpPayloadType = 0x00000007, /* Indicate the type of payload encapsulated 2:0 + in IPdatagram processed by COE (Rx) */ + DescRxIpPayloadUnknown = 0x00000000, /* Unknown or didnot process IP payload */ + DescRxIpPayloadUDP = 0x00000001, /* UDP */ + DescRxIpPayloadTCP = 0x00000002, /* TCP */ + DescRxIpPayloadICMP = 0x00000003, /* ICMP */ + +}; + +#else +/* + +********** Default Descritpor structure **************************** +DmaRxBaseAddr = 0x000C, CSR3 - Receive Descriptor list base address +DmaRxBaseAddr is the pointer to the first Rx Descriptors. the Descriptor format in Little endian with a +32 bit Data bus is as shown below + +Similarly +DmaTxBaseAddr = 0x0010, CSR4 - Transmit Descriptor list base address +DmaTxBaseAddr is the pointer to the first Rx Descriptors. the Descriptor format in Little endian with a +32 bit Data bus is as shown below + -------------------------------------------------------------------- + RDES0/TDES0 |OWN (31)| Status | + -------------------------------------------------------------------- + RDES1/TDES1 | Control Bits | Byte Count Buffer 2 | Byte Count Buffer 1 | + -------------------------------------------------------------------- + RDES2/TDES2 | Buffer 1 Address | + -------------------------------------------------------------------- + RDES3/TDES3 | Buffer 2 Address / Next Descriptor Address | + -------------------------------------------------------------------- +*/ +enum DmaDescriptorStatus /* status word of DMA descriptor */ +{ + DescOwnByDma = 0x80000000, /* (OWN)Descriptor is owned by DMA engine 31 RW */ + + DescDAFilterFail = 0x40000000, /* (AFM)Rx - DA Filter Fail for the rx frame 30 */ + + DescFrameLengthMask = 0x3FFF0000, /* (FL)Receive descriptor frame length 29:16 */ + DescFrameLengthShift = 16, + + DescError = 0x00008000, /* (ES)Error summary bit - OR of the follo. bits: 15 */ + /* DE || OE || IPC || LC || RWT || RE || CE */ + DescRxTruncated = 0x00004000, /* (DE)Rx - no more descriptors for receive frame 14 */ + DescSAFilterFail = 0x00002000, /* (SAF)Rx - SA Filter Fail for the received frame 13 */ + DescRxLengthError = 0x00001000, /* (LE)Rx - frm size not matching with len field 12 */ + DescRxDamaged = 0x00000800, /* (OE)Rx - frm was damaged due to buffer overflow 11 */ + DescRxVLANTag = 0x00000400, /* (VLAN)Rx - received frame is a VLAN frame 10 */ + DescRxFirst = 0x00000200, /* (FS)Rx - first descriptor of the frame 9 */ + DescRxLast = 0x00000100, /* (LS)Rx - last descriptor of the frame 8 */ + DescRxLongFrame = 0x00000080, /* (Giant Frame)Rx - frame is longer than 1518/1522 7 */ + DescRxCollision = 0x00000040, /* (LC)Rx - late collision occurred during reception 6 */ + DescRxFrameEther = 0x00000020, /* (FT)Rx - Frame type - Ethernet, otherwise 802.3 5 */ + DescRxWatchdog = 0x00000010, /* (RWT)Rx - watchdog timer expired during reception 4 */ + DescRxMiiError = 0x00000008, /* (RE)Rx - error reported by MII interface 3 */ + DescRxDribbling = 0x00000004, /* (DE)Rx - frame contains non int multiple of 8 bits 2 */ + DescRxCrc = 0x00000002, /* (CE)Rx - CRC error 1 */ + DescRxMacMatch = 0x00000001, /* (RX MAC Address) Rx mac address reg(1 to 15)match 0 */ + + //Rx Descriptor Checksum Offload engine (type 2) encoding + //DescRxPayChkError = 0x00000001, /* () Rx - Rx Payload Checksum Error 0 */ + //DescRxIpv4ChkError = 0x00000080, /* (IPC CS ERROR)Rx - Ipv4 header checksum error 7 */ + + DescRxChkBit0 = 0x00000001, /* () Rx - Rx Payload Checksum Error 0 */ + DescRxChkBit7 = 0x00000080, /* (IPC CS ERROR)Rx - Ipv4 header checksum error 7 */ + DescRxChkBit5 = 0x00000020, /* (FT)Rx - Frame type - Ethernet, otherwise 802.3 5 */ + + DescTxIpv4ChkError = 0x00010000, /* (IHE) Tx Ip header error 16 */ + DescTxTimeout = 0x00004000, /* (JT)Tx - Transmit jabber timeout 14 */ + DescTxFrameFlushed = 0x00002000, /* (FF)Tx - DMA/MTL flushed the frame due to SW flush 13 */ + DescTxPayChkError = 0x00001000, /* (PCE) Tx Payload checksum Error 12 */ + DescTxLostCarrier = 0x00000800, /* (LC)Tx - carrier lost during tramsmission 11 */ + DescTxNoCarrier = 0x00000400, /* (NC)Tx - no carrier signal from the tranceiver 10 */ + DescTxLateCollision = 0x00000200, /* (LC)Tx - transmission aborted due to collision 9 */ + DescTxExcCollisions = 0x00000100, /* (EC)Tx - transmission aborted after 16 collisions 8 */ + DescTxVLANFrame = 0x00000080, /* (VF)Tx - VLAN-type frame 7 */ + + DescTxCollMask = 0x00000078, /* (CC)Tx - Collision count 6:3 */ + DescTxCollShift = 3, + + DescTxExcDeferral = 0x00000004, /* (ED)Tx - excessive deferral 2 */ + DescTxUnderflow = 0x00000002, /* (UF)Tx - late data arrival from the memory 1 */ + DescTxDeferred = 0x00000001, /* (DB)Tx - frame transmision deferred 0 */ + + /* + This explains the RDES1/TDES1 bits layout + -------------------------------------------------------------------- + RDES1/TDES1 | Control Bits | Byte Count Buffer 2 | Byte Count Buffer 1 | + -------------------------------------------------------------------- + */ + //DmaDescriptorLength length word of DMA descriptor + + DescTxIntEnable = 0x80000000, /* (IC)Tx - interrupt on completion 31 */ + DescTxLast = 0x40000000, /* (LS)Tx - Last segment of the frame 30 */ + DescTxFirst = 0x20000000, /* (FS)Tx - First segment of the frame 29 */ + DescTxDisableCrc = 0x04000000, /* (DC)Tx - Add CRC disabled (first segment only) 26 */ + + RxDisIntCompl = 0x80000000, /* (Disable Rx int on completion) 31 */ + RxDescEndOfRing = 0x02000000, /* (TER)End of descriptors ring */ + RxDescChain = 0x01000000, /* (TCH)Second buffer address is chain address 24 */ + + DescTxDisablePadd = 0x00800000, /* (DP)disable padding, added by - reyaz 23 */ + + TxDescEndOfRing = 0x02000000, /* (TER)End of descriptors ring */ + TxDescChain = 0x01000000, /* (TCH)Second buffer address is chain address 24 */ + + DescSize2Mask = 0x003FF800, /* (TBS2) Buffer 2 size 21:11 */ + DescSize2Shift = 11, + DescSize1Mask = 0x000007FF, /* (TBS1) Buffer 1 size 10:0 */ + DescSize1Shift = 0, + + + DescTxCisMask = 0x18000000, /* Tx checksum offloading control mask 28:27 */ + DescTxCisBypass = 0x00000000, /* Checksum bypass */ + DescTxCisIpv4HdrCs = 0x08000000,/* IPv4 header checksum */ + DescTxCisTcpOnlyCs = 0x10000000, /* TCP/UDP/ICMP checksum. Pseudo header checksum is assumed to be present */ + DescTxCisTcpPseudoCs = 0x18000000, /* TCP/UDP/ICMP checksum fully in hardware including pseudo header */ +}; +#endif + +// Rx Descriptor COE type2 encoding +enum RxDescCOEEncode +{ + RxLenLT600 = 0, /* Bit(5:7:0)=>0 IEEE 802.3 type frame Length field is Lessthan 0x0600 */ + RxIpHdrPayLoadChkBypass = 1, /* Bit(5:7:0)=>1 Payload & Ip header checksum bypassed (unsuppported payload) */ + RxIpHdrPayLoadRes = 2, /* Bit(5:7:0)=>2 Reserved */ + RxChkBypass = 3, /* Bit(5:7:0)=>3 Neither IPv4 nor IPV6. So checksum bypassed */ + RxNoChkError = 4, /* Bit(5:7:0)=>4 No IPv4/IPv6 Checksum error detected */ + RxPayLoadChkError = 5, /* Bit(5:7:0)=>5 Payload checksum error detected for Ipv4/Ipv6 frames */ + RxIpHdrChkError = 6, /* Bit(5:7:0)=>6 Ip header checksum error detected for Ipv4 frames */ + RxIpHdrPayLoadChkError = 7, /* Bit(5:7:0)=>7 Payload & Ip header checksum error detected for Ipv4/Ipv6 frames */ +}; + +/********************************************************** + * DMA engine interrupt handling functions + **********************************************************/ + +enum synopGMACDmaIntEnum /* Intrerrupt types */ +{ + synopGMACDmaRxNormal = 0x01, /* normal receiver interrupt */ + synopGMACDmaRxAbnormal = 0x02, /* abnormal receiver interrupt */ + synopGMACDmaRxStopped = 0x04, /* receiver stopped */ + synopGMACDmaTxNormal = 0x08, /* normal transmitter interrupt */ + synopGMACDmaTxAbnormal = 0x10, /* abnormal transmitter interrupt */ + synopGMACDmaTxStopped = 0x20, /* transmitter stopped */ + synopGMACDmaError = 0x80, /* Dma engine error */ + +#ifdef AVB_SUPPORT + synopGMADmaSlotCounter = 0x40, /* Dma SlotCounter interrupt mask for Channel1 and Channel2*/ +#endif +}; + + +/********************************************************** + * Initial register values + **********************************************************/ +enum InitialRegisters +{ + /* Full-duplex mode with perfect filter on */ + GmacConfigInitFdx1000 = GmacWatchdogEnable | GmacJabberEnable | GmacFrameBurstEnable | GmacJumboFrameDisable + | GmacSelectGmii | GmacEnableRxOwn | GmacLoopbackOff + | GmacFullDuplex | GmacRetryEnable | GmacPadCrcStripDisable + | GmacBackoffLimit0 | GmacDeferralCheckDisable | GmacTxEnable | GmacRxEnable, + + /* Full-duplex mode with perfect filter on */ + GmacConfigInitFdx110 = GmacWatchdogEnable | GmacJabberEnable | GmacFrameBurstEnable | GmacJumboFrameDisable + | GmacSelectMii | GmacEnableRxOwn | GmacLoopbackOff + | GmacFullDuplex | GmacRetryEnable | GmacPadCrcStripDisable + | GmacBackoffLimit0 | GmacDeferralCheckDisable | GmacTxEnable | GmacRxEnable, + + /* Full-duplex mode */ + // CHANGED: Pass control config, dest addr filter normal, added source address filter, multicast & unicast + // Hash filter. + /* = GmacFilterOff | GmacPassControlOff | GmacBroadcastEnable */ + GmacFrameFilterInitFdx = GmacFilterOn | GmacPassControl0 | GmacBroadcastEnable | GmacSrcAddrFilterDisable + | GmacMulticastFilterOn | GmacDestAddrFilterNor | GmacMcastHashFilterOff + | GmacPromiscuousModeOff | GmacUcastHashFilterOff, + + /* Full-duplex mode */ + GmacFlowControlInitFdx = GmacUnicastPauseFrameOff | GmacRxFlowControlEnable | GmacTxFlowControlEnable, + + /* Full-duplex mode */ + GmacGmiiAddrInitFdx = GmiiCsrClk2, + + + /* Half-duplex mode with perfect filter on */ + // CHANGED: Removed Endian configuration, added single bit config for PAD/CRC strip, + /*| GmacSelectMii | GmacLittleEndian | GmacDisableRxOwn | GmacLoopbackOff*/ + GmacConfigInitHdx1000 = GmacWatchdogEnable | GmacJabberEnable | GmacFrameBurstEnable | GmacJumboFrameDisable + | GmacSelectGmii | GmacDisableRxOwn | GmacLoopbackOff + | GmacHalfDuplex | GmacRetryEnable | GmacPadCrcStripDisable + | GmacBackoffLimit0 | GmacDeferralCheckDisable | GmacTxEnable | GmacRxEnable, + + /* Half-duplex mode with perfect filter on */ + GmacConfigInitHdx110 = GmacWatchdogEnable | GmacJabberEnable | GmacFrameBurstEnable | GmacJumboFrameDisable + | GmacSelectMii | GmacDisableRxOwn | GmacLoopbackOff + | GmacHalfDuplex | GmacRetryEnable | GmacPadCrcStripDisable + | GmacBackoffLimit0 | GmacDeferralCheckDisable | GmacTxEnable | GmacRxEnable, + + /* Half-duplex mode */ + GmacFrameFilterInitHdx = GmacFilterOn | GmacPassControl0 | GmacBroadcastEnable | GmacSrcAddrFilterDisable + | GmacMulticastFilterOn | GmacDestAddrFilterNor | GmacMcastHashFilterOff + | GmacUcastHashFilterOff | GmacPromiscuousModeOff, + + /* Half-duplex mode */ + GmacFlowControlInitHdx = GmacUnicastPauseFrameOff | GmacRxFlowControlDisable | GmacTxFlowControlDisable, + + /* Half-duplex mode */ + GmacGmiiAddrInitHdx = GmiiCsrClk2, + + + + /********************************************** + *DMA configurations + **********************************************/ + + DmaBusModeInit = DmaFixedBurstEnable | DmaBurstLength8 | DmaDescriptorSkip2 | DmaResetOff, + // DmaBusModeInit = DmaFixedBurstEnable | DmaBurstLength8 | DmaDescriptorSkip4 | DmaResetOff, + + /* 1000 Mb/s mode */ + DmaControlInit1000 = DmaStoreAndForward,// | DmaTxSecondFrame , + + /* 100 Mb/s mode */ + DmaControlInit100 = DmaStoreAndForward, + + /* 10 Mb/s mode */ + DmaControlInit10 = DmaStoreAndForward, + + /* Interrupt groups */ + DmaIntErrorMask = DmaIntBusError, /* Error */ + DmaIntRxAbnMask = DmaIntRxNoBuffer, /* receiver abnormal interrupt */ + DmaIntRxNormMask = DmaIntRxCompleted, /* receiver normal interrupt */ + DmaIntRxStoppedMask = DmaIntRxStopped, /* receiver stopped */ + DmaIntTxAbnMask = DmaIntTxUnderflow, /* transmitter abnormal interrupt */ + DmaIntTxNormMask = DmaIntTxCompleted, /* transmitter normal interrupt */ + DmaIntTxStoppedMask = DmaIntTxStopped, /* transmitter stopped */ + + DmaIntEnable = DmaIeNormal | DmaIeAbnormal | DmaIntErrorMask + | DmaIntRxAbnMask | DmaIntRxNormMask | DmaIntRxStoppedMask + | DmaIntTxAbnMask | DmaIntTxNormMask | DmaIntTxStoppedMask + | DmaIeTxNoBuffer , + DmaIntDisable = 0, +}; + + + + +/********************************************************** + * Mac Management Counters (MMC) + **********************************************************/ + +enum MMC_ENABLE +{ + GmacMmcCntrl = 0x0100, /* mmc control for operating mode of MMC */ + GmacMmcIntrRx = 0x0104, /* maintains interrupts generated by rx counters */ + GmacMmcIntrTx = 0x0108, /* maintains interrupts generated by tx counters */ + GmacMmcIntrMaskRx = 0x010C, /* mask for interrupts generated from rx counters */ + GmacMmcIntrMaskTx = 0x0110, /* mask for interrupts generated from tx counters */ +}; +enum MMC_TX +{ + GmacMmcTxOctetCountGb = 0x0114, /*Bytes Tx excl. of preamble and retried bytes (Good or Bad) */ + GmacMmcTxFrameCountGb = 0x0118, /*Frames Tx excl. of retried frames (Good or Bad) */ + GmacMmcTxBcFramesG = 0x011C, /*Broadcast Frames Tx (Good) */ + GmacMmcTxMcFramesG = 0x0120, /*Multicast Frames Tx (Good) */ + + GmacMmcTx64OctetsGb = 0x0124, /*Tx with len 64 bytes excl. of pre and retried (Good or Bad) */ + GmacMmcTx65To127OctetsGb = 0x0128, /*Tx with len >64 bytes <=127 excl. of pre and retried (Good or Bad) */ + GmacMmcTx128To255OctetsGb = 0x012C, /*Tx with len >128 bytes <=255 excl. of pre and retried (Good or Bad) */ + GmacMmcTx256To511OctetsGb = 0x0130, /*Tx with len >256 bytes <=511 excl. of pre and retried (Good or Bad) */ + GmacMmcTx512To1023OctetsGb = 0x0134, /*Tx with len >512 bytes <=1023 excl. of pre and retried (Good or Bad) */ + GmacMmcTx1024ToMaxOctetsGb = 0x0138, /*Tx with len >1024 bytes <=MaxSize excl. of pre and retried (Good or Bad) */ + + GmacMmcTxUcFramesGb = 0x013C, /*Unicast Frames Tx (Good or Bad) */ + GmacMmcTxMcFramesGb = 0x0140, /*Multicast Frames Tx (Good and Bad) */ + GmacMmcTxBcFramesGb = 0x0144, /*Broadcast Frames Tx (Good and Bad) */ + GmacMmcTxUnderFlowError = 0x0148, /*Frames aborted due to Underflow error */ + GmacMmcTxSingleColG = 0x014C, /*Successfully Tx Frames after singel collision in Half duplex mode */ + GmacMmcTxMultiColG = 0x0150, /*Successfully Tx Frames after more than singel collision in Half duplex mode */ + GmacMmcTxDeferred = 0x0154, /*Successfully Tx Frames after a deferral in Half duplex mode */ + GmacMmcTxLateCol = 0x0158, /*Frames aborted due to late collision error */ + GmacMmcTxExessCol = 0x015C, /*Frames aborted due to excessive (16) collision errors */ + GmacMmcTxCarrierError = 0x0160, /*Frames aborted due to carrier sense error (No carrier or Loss of carrier) */ + GmacMmcTxOctetCountG = 0x0164, /*Bytes Tx excl. of preamble and retried bytes (Good) */ + GmacMmcTxFrameCountG = 0x0168, /*Frames Tx (Good) */ + GmacMmcTxExessDef = 0x016C, /*Frames aborted due to excessive deferral errors (deferred for more than 2 max-sized frame times)*/ + + GmacMmcTxPauseFrames = 0x0170, /*Number of good pause frames Tx. */ + GmacMmcTxVlanFramesG = 0x0174, /*Number of good Vlan frames Tx excl. retried frames */ +}; +enum MMC_RX +{ + GmacMmcRxFrameCountGb = 0x0180, /*Frames Rx (Good or Bad) */ + GmacMmcRxOctetCountGb = 0x0184, /*Bytes Rx excl. of preamble and retried bytes (Good or Bad) */ + GmacMmcRxOctetCountG = 0x0188, /*Bytes Rx excl. of preamble and retried bytes (Good) */ + GmacMmcRxBcFramesG = 0x018C, /*Broadcast Frames Rx (Good) */ + GmacMmcRxMcFramesG = 0x0190, /*Multicast Frames Rx (Good) */ + + GmacMmcRxCrcError = 0x0194, /*Number of frames received with CRC error */ + GmacMmcRxAlignError = 0x0198, /*Number of frames received with alignment (dribble) error. Only in 10/100mode */ + GmacMmcRxRuntError = 0x019C, /*Number of frames received with runt (<64 bytes and CRC error) error */ + GmacMmcRxJabberError = 0x01A0, /*Number of frames rx with jabber (>1518/1522 or >9018/9022 and CRC) */ + GmacMmcRxUnderSizeG = 0x01A4, /*Number of frames received with <64 bytes without any error */ + GmacMmcRxOverSizeG = 0x01A8, /*Number of frames received with >1518/1522 bytes without any error */ + + GmacMmcRx64OctetsGb = 0x01AC, /*Rx with len 64 bytes excl. of pre and retried (Good or Bad) */ + GmacMmcRx65To127OctetsGb = 0x01B0, /*Rx with len >64 bytes <=127 excl. of pre and retried (Good or Bad) */ + GmacMmcRx128To255OctetsGb = 0x01B4, /*Rx with len >128 bytes <=255 excl. of pre and retried (Good or Bad) */ + GmacMmcRx256To511OctetsGb = 0x01B8, /*Rx with len >256 bytes <=511 excl. of pre and retried (Good or Bad) */ + GmacMmcRx512To1023OctetsGb = 0x01BC, /*Rx with len >512 bytes <=1023 excl. of pre and retried (Good or Bad) */ + GmacMmcRx1024ToMaxOctetsGb = 0x01C0, /*Rx with len >1024 bytes <=MaxSize excl. of pre and retried (Good or Bad) */ + + GmacMmcRxUcFramesG = 0x01C4, /*Unicast Frames Rx (Good) */ + GmacMmcRxLengthError = 0x01C8, /*Number of frames received with Length type field != frame size */ + GmacMmcRxOutOfRangeType = 0x01CC, /*Number of frames received with length field != valid frame size */ + + GmacMmcRxPauseFrames = 0x01D0, /*Number of good pause frames Rx. */ + GmacMmcRxFifoOverFlow = 0x01D4, /*Number of missed rx frames due to FIFO overflow */ + GmacMmcRxVlanFramesGb = 0x01D8, /*Number of good Vlan frames Rx */ + + GmacMmcRxWatchdobError = 0x01DC, /*Number of frames rx with error due to watchdog timeout error */ +}; + +enum MMC_IP_RELATED +{ + GmacMmcRxIpcIntrMask = 0x0200, /*Maintains the mask for interrupt generated from rx IPC statistic counters */ + GmacMmcRxIpcIntr = 0x0208, /*Maintains the interrupt that rx IPC statistic counters generate */ + + GmacMmcRxIpV4FramesG = 0x0210, /*Good IPV4 datagrams received */ + GmacMmcRxIpV4HdrErrFrames = 0x0214, /*Number of IPV4 datagrams received with header errors */ + GmacMmcRxIpV4NoPayFrames = 0x0218, /*Number of IPV4 datagrams received which didnot have TCP/UDP/ICMP payload */ + GmacMmcRxIpV4FragFrames = 0x021C, /*Number of IPV4 datagrams received with fragmentation */ + GmacMmcRxIpV4UdpChkDsblFrames = 0x0220, /*Number of IPV4 datagrams received that had a UDP payload checksum disabled */ + + GmacMmcRxIpV6FramesG = 0x0224, /*Good IPV6 datagrams received */ + GmacMmcRxIpV6HdrErrFrames = 0x0228, /*Number of IPV6 datagrams received with header errors */ + GmacMmcRxIpV6NoPayFrames = 0x022C, /*Number of IPV6 datagrams received which didnot have TCP/UDP/ICMP payload */ + + GmacMmcRxUdpFramesG = 0x0230, /*Number of good IP datagrams with good UDP payload */ + GmacMmcRxUdpErrorFrames = 0x0234, /*Number of good IP datagrams with UDP payload having checksum error */ + + GmacMmcRxTcpFramesG = 0x0238, /*Number of good IP datagrams with good TDP payload */ + GmacMmcRxTcpErrorFrames = 0x023C, /*Number of good IP datagrams with TCP payload having checksum error */ + + GmacMmcRxIcmpFramesG = 0x0240, /*Number of good IP datagrams with good Icmp payload */ + GmacMmcRxIcmpErrorFrames = 0x0244, /*Number of good IP datagrams with Icmp payload having checksum error */ + + GmacMmcRxIpV4OctetsG = 0x0250, /*Good IPV4 datagrams received excl. Ethernet hdr,FCS,Pad,Ip Pad bytes */ + GmacMmcRxIpV4HdrErrorOctets = 0x0254, /*Number of bytes in IPV4 datagram with header errors */ + GmacMmcRxIpV4NoPayOctets = 0x0258, /*Number of bytes in IPV4 datagram with no TCP/UDP/ICMP payload */ + GmacMmcRxIpV4FragOctets = 0x025C, /*Number of bytes received in fragmented IPV4 datagrams */ + GmacMmcRxIpV4UdpChkDsblOctets = 0x0260, /*Number of bytes received in UDP segment that had UDP checksum disabled */ + + GmacMmcRxIpV6OctetsG = 0x0264, /*Good IPV6 datagrams received excl. Ethernet hdr,FCS,Pad,Ip Pad bytes */ + GmacMmcRxIpV6HdrErrorOctets = 0x0268, /*Number of bytes in IPV6 datagram with header errors */ + GmacMmcRxIpV6NoPayOctets = 0x026C, /*Number of bytes in IPV6 datagram with no TCP/UDP/ICMP payload */ + + GmacMmcRxUdpOctetsG = 0x0270, /*Number of bytes in IP datagrams with good UDP payload */ + GmacMmcRxUdpErrorOctets = 0x0274, /*Number of bytes in IP datagrams with UDP payload having checksum error */ + + GmacMmcRxTcpOctetsG = 0x0278, /*Number of bytes in IP datagrams with good TDP payload */ + GmacMmcRxTcpErrorOctets = 0x027C, /*Number of bytes in IP datagrams with TCP payload having checksum error */ + + GmacMmcRxIcmpOctetsG = 0x0280, /*Number of bytes in IP datagrams with good Icmp payload */ + GmacMmcRxIcmpErrorOctets = 0x0284, /*Number of bytes in IP datagrams with Icmp payload having checksum error */ +}; + + +enum MMC_CNTRL_REG_BIT_DESCRIPTIONS +{ + GmacMmcCounterFreeze = 0x00000008, /* when set MMC counters freeze to current value */ + GmacMmcCounterResetOnRead = 0x00000004, /* when set MMC counters will be reset to 0 after read */ + GmacMmcCounterStopRollover = 0x00000002, /* when set counters will not rollover after max value */ + GmacMmcCounterReset = 0x00000001, /* when set all counters wil be reset (automatically cleared after 1 clk) */ + +}; + +enum MMC_RX_INTR_MASK_AND_STATUS_BIT_DESCRIPTIONS +{ + GmacMmcRxWDInt = 0x00800000, /* set when rxwatchdog error reaches half of max value */ + GmacMmcRxVlanInt = 0x00400000, /* set when GmacMmcRxVlanFramesGb counter reaches half of max value */ + GmacMmcRxFifoOverFlowInt = 0x00200000, /* set when GmacMmcRxFifoOverFlow counter reaches half of max value */ + GmacMmcRxPauseFrameInt = 0x00100000, /* set when GmacMmcRxPauseFrames counter reaches half of max value */ + GmacMmcRxOutOfRangeInt = 0x00080000, /* set when GmacMmcRxOutOfRangeType counter reaches half of max value */ + GmacMmcRxLengthErrorInt = 0x00040000, /* set when GmacMmcRxLengthError counter reaches half of max value */ + GmacMmcRxUcFramesInt = 0x00020000, /* set when GmacMmcRxUcFramesG counter reaches half of max value */ + GmacMmcRx1024OctInt = 0x00010000, /* set when GmacMmcRx1024ToMaxOctetsGb counter reaches half of max value */ + GmacMmcRx512OctInt = 0x00008000, /* set when GmacMmcRx512To1023OctetsGb counter reaches half of max value */ + GmacMmcRx256OctInt = 0x00004000, /* set when GmacMmcRx256To511OctetsGb counter reaches half of max value */ + GmacMmcRx128OctInt = 0x00002000, /* set when GmacMmcRx128To255OctetsGb counter reaches half of max value */ + GmacMmcRx65OctInt = 0x00001000, /* set when GmacMmcRx65To127OctetsG counter reaches half of max value */ + GmacMmcRx64OctInt = 0x00000800, /* set when GmacMmcRx64OctetsGb counter reaches half of max value */ + GmacMmcRxOverSizeInt = 0x00000400, /* set when GmacMmcRxOverSizeG counter reaches half of max value */ + GmacMmcRxUnderSizeInt = 0x00000200, /* set when GmacMmcRxUnderSizeG counter reaches half of max value */ + GmacMmcRxJabberErrorInt = 0x00000100, /* set when GmacMmcRxJabberError counter reaches half of max value */ + GmacMmcRxRuntErrorInt = 0x00000080, /* set when GmacMmcRxRuntError counter reaches half of max value */ + GmacMmcRxAlignErrorInt = 0x00000040, /* set when GmacMmcRxAlignError counter reaches half of max value */ + GmacMmcRxCrcErrorInt = 0x00000020, /* set when GmacMmcRxCrcError counter reaches half of max value */ + GmacMmcRxMcFramesInt = 0x00000010, /* set when GmacMmcRxMcFramesG counter reaches half of max value */ + GmacMmcRxBcFramesInt = 0x00000008, /* set when GmacMmcRxBcFramesG counter reaches half of max value */ + GmacMmcRxOctetGInt = 0x00000004, /* set when GmacMmcRxOctetCountG counter reaches half of max value */ + GmacMmcRxOctetGbInt = 0x00000002, /* set when GmacMmcRxOctetCountGb counter reaches half of max value */ + GmacMmcRxFrameInt = 0x00000001, /* set when GmacMmcRxFrameCountGb counter reaches half of max value */ +}; + +enum MMC_TX_INTR_MASK_AND_STATUS_BIT_DESCRIPTIONS +{ + + GmacMmcTxVlanInt = 0x01000000, /* set when GmacMmcTxVlanFramesG counter reaches half of max value */ + GmacMmcTxPauseFrameInt = 0x00800000, /* set when GmacMmcTxPauseFrames counter reaches half of max value */ + GmacMmcTxExessDefInt = 0x00400000, /* set when GmacMmcTxExessDef counter reaches half of max value */ + GmacMmcTxFrameInt = 0x00200000, /* set when GmacMmcTxFrameCount counter reaches half of max value */ + GmacMmcTxOctetInt = 0x00100000, /* set when GmacMmcTxOctetCountG counter reaches half of max value */ + GmacMmcTxCarrierErrorInt = 0x00080000, /* set when GmacMmcTxCarrierError counter reaches half of max value */ + GmacMmcTxExessColInt = 0x00040000, /* set when GmacMmcTxExessCol counter reaches half of max value */ + GmacMmcTxLateColInt = 0x00020000, /* set when GmacMmcTxLateCol counter reaches half of max value */ + GmacMmcTxDeferredInt = 0x00010000, /* set when GmacMmcTxDeferred counter reaches half of max value */ + GmacMmcTxMultiColInt = 0x00008000, /* set when GmacMmcTxMultiColG counter reaches half of max value */ + GmacMmcTxSingleCol = 0x00004000, /* set when GmacMmcTxSingleColG counter reaches half of max value */ + GmacMmcTxUnderFlowErrorInt = 0x00002000, /* set when GmacMmcTxUnderFlowError counter reaches half of max value */ + GmacMmcTxBcFramesGbInt = 0x00001000, /* set when GmacMmcTxBcFramesGb counter reaches half of max value */ + GmacMmcTxMcFramesGbInt = 0x00000800, /* set when GmacMmcTxMcFramesGb counter reaches half of max value */ + GmacMmcTxUcFramesInt = 0x00000400, /* set when GmacMmcTxUcFramesGb counter reaches half of max value */ + GmacMmcTx1024OctInt = 0x00000200, /* set when GmacMmcTx1024ToMaxOctetsGb counter reaches half of max value */ + GmacMmcTx512OctInt = 0x00000100, /* set when GmacMmcTx512To1023OctetsGb counter reaches half of max value */ + GmacMmcTx256OctInt = 0x00000080, /* set when GmacMmcTx256To511OctetsGb counter reaches half of max value */ + GmacMmcTx128OctInt = 0x00000040, /* set when GmacMmcTx128To255OctetsGb counter reaches half of max value */ + GmacMmcTx65OctInt = 0x00000020, /* set when GmacMmcTx65To127OctetsGb counter reaches half of max value */ + GmacMmcTx64OctInt = 0x00000010, /* set when GmacMmcTx64OctetsGb counter reaches half of max value */ + GmacMmcTxMcFramesInt = 0x00000008, /* set when GmacMmcTxMcFramesG counter reaches half of max value */ + GmacMmcTxBcFramesInt = 0x00000004, /* set when GmacMmcTxBcFramesG counter reaches half of max value */ + GmacMmcTxFrameGbInt = 0x00000002, /* set when GmacMmcTxFrameCountGb counter reaches half of max value */ + GmacMmcTxOctetGbInt = 0x00000001, /* set when GmacMmcTxOctetCountGb counter reaches half of max value */ + +}; + + +/********************************************************** + * Power Management (PMT) Block + **********************************************************/ + +/** + * PMT supports the reception of network (remote) wake-up frames and Magic packet frames. + * It generates interrupts for wake-up frames and Magic packets received by GMAC. + * PMT sits in Rx path and is enabled with remote wake-up frame enable and Magic packet enable. + * These enable are in PMT control and Status register and are programmed by apllication. + * + * When power down mode is enabled in PMT, all rx frames are dropped by the core. Core comes + * out of power down mode only when either Magic packe tor a Remote wake-up frame is received + * and the corresponding detection is enabled. + * + * Driver need not be modified to support this feature. Only Api to put the device in to power + * down mode is sufficient + */ + +#define WAKEUP_REG_LENGTH 8 /*This is the reg length for wake up register configuration*/ + +enum GmacPmtCtrlStatusBitDefinition +{ + GmacPmtFrmFilterPtrReset = 0x80000000, /* when set remote wake-up frame filter register pointer to 3'b000 */ + GmacPmtGlobalUnicast = 0x00000200, /* When set enables any unicast packet to be a wake-up frame */ + GmacPmtWakeupFrameReceived = 0x00000040, /* Wake up frame received */ + GmacPmtMagicPktReceived = 0x00000020, /* Magic Packet received */ + GmacPmtWakeupFrameEnable = 0x00000004, /* Wake-up frame enable */ + GmacPmtMagicPktEnable = 0x00000002, /* Magic packet enable */ + GmacPmtPowerDown = 0x00000001, /* Power Down */ +}; + + + + +/********************************************************** + * IEEE 1588-2008 Precision Time Protocol (PTP) Support + **********************************************************/ +enum PTPMessageType +{ + SYNC = 0x0, + Delay_Req = 0x1, + Pdelay_Req = 0x2, + Pdelay_Resp = 0x3, + Follow_up = 0x8, + Delay_Resp = 0x9, + Pdelay_Resp_Follow_Up = 0xA, + Announce = 0xB, + Signaling = 0xC, + Management = 0xD, +}; + + + +typedef struct TimeStampStruct +{ + u32 TSversion; /* PTP Version 1 or PTP version2 */ + u32 TSmessagetype; /* Message type associated with this time stamp */ + + u16 TShighest16; /* Highest 16 bit time stamp value, Valid onley when ADV_TIME_HIGH_WORD configured in corekit */ + u32 TSupper32; /* Most significant 32 bit time stamp value */ + u32 TSlower32; /* Least Significat 32 bit time stamp value */ + +} TimeStamp; + + +/** + * IEEE 1588-2008 is the optional module to support Ethernet frame time stamping. + * Sixty four (+16) bit time stamps are given in each frames transmit and receive status. + * The driver assumes the following + * 1. "IEEE 1588 Time Stamping" "TIME_STAMPING"is ENABLED in corekit + * 2. "IEEE 1588 External Time Stamp Input Enable" "EXT_TIME_STAMPING" is DISABLED in corekit + * 3. "IEEE 1588 Advanced Time Stamp support" "ADV_TIME_STAMPING" is ENABLED in corekit + * 4. "IEEE 1588 Higher Word Register Enable" "ADV_TIME_HIGH_WORD" is ENABLED in corekit + */ + +/* GmacTSControl = 0x0700, Controls the Timestamp update logic : only when IEEE 1588 time stamping is enabled in corekit */ +enum GmacTSControlReg +{ + GmacTSENMACADDR = 0x00040000, /* Enable Mac Addr for PTP filtering 18 RW 0 */ + + GmacTSCLKTYPE = 0x00030000, /* Select the type of clock node 17:16 RW 00 */ + /* + TSCLKTYPE TSMSTRENA TSEVNTENA Messages for wihich TS snapshot is taken + 00/01 X 0 SYNC, FOLLOW_UP, DELAY_REQ, DELAY_RESP + 00/01 1 0 DELAY_REQ + 00/01 0 1 SYNC + 10 NA 0 SYNC, FOLLOW_UP, DELAY_REQ, DELAY_RESP + 10 NA 1 SYNC, FOLLOW_UP + 11 NA 0 SYNC, FOLLOW_UP, DELAY_REQ, DELAY_RESP, PDELAY_REQ, PDELAY_RESP + 11 NA 1 SYNC, PDELAY_REQ, PDELAY_RESP + */ + GmacTSOrdClk = 0x00000000, /* 00=> Ordinary clock*/ + GmacTSBouClk = 0x00010000, /* 01=> Boundary clock*/ + GmacTSEtoEClk = 0x00020000, /* 10=> End-to-End transparent clock*/ + GmacTSPtoPClk = 0x00030000, /* 11=> P-to-P transparent clock*/ + + GmacTSMSTRENA = 0x00008000, /* Ena TS Snapshot for Master Messages 15 RW 0 */ + GmacTSEVNTENA = 0x00004000, /* Ena TS Snapshot for Event Messages 14 RW 0 */ + GmacTSIPV4ENA = 0x00002000, /* Ena TS snapshot for IPv4 13 RW 1 */ + GmacTSIPV6ENA = 0x00001000, /* Ena TS snapshot for IPv6 12 RW 0 */ + GmacTSIPENA = 0x00000800, /* Ena TS snapshot for PTP over E'net 11 RW 0 */ + GmacTSVER2ENA = 0x00000400, /* Ena PTP snooping for version 2 10 RW 0 */ + + GmacTSCTRLSSR = 0x00000200, /* Digital or Binary Rollover 9 RW 0 */ + + GmacTSENALL = 0x00000100, /* Enable TS fro all frames (Ver2 only) 8 RW 0 */ + + GmacTSADDREG = 0x00000020, /* Addend Register Update 5 RW_SC 0 */ + GmacTSUPDT = 0x00000008, /* Time Stamp Update 3 RW_SC 0 */ + GmacTSINT = 0x00000004, /* Time Atamp Initialize 2 RW_SC 0 */ + + GmacTSTRIG = 0x00000010, /* Time stamp interrupt Trigger Enable 4 RW_SC 0 */ + + GmacTSCFUPDT = 0x00000002, /* Time Stamp Fine/Coarse 1 RW 0 */ + GmacTSCUPDTCoarse = 0x00000000, /* 0=> Time Stamp update method is coarse */ + GmacTSCUPDTFine = 0x00000002, /* 1=> Time Stamp update method is fine */ + + GmacTSENA = 0x00000001, /* Time Stamp Enable 0 RW 0 */ +}; + + +/* GmacTSSubSecIncr = 0x0704, 8 bit value by which sub second register is incremented : only when IEEE 1588 time stamping without external timestamp input */ +enum GmacTSSubSecIncrReg +{ + GmacSSINCMsk = 0x000000FF, /* Only Lower 8 bits are valid bits 7:0 RW 00 */ +}; + +/* GmacTSLow = 0x070C, Indicates whether the timestamp low count is positive or negative; for Adv timestamp it is always zero */ +enum GmacTSSign +{ + GmacTSSign = 0x80000000, /* PSNT 31 RW 0 */ + GmacTSPositive = 0x00000000, + GmacTSNegative = 0x80000000, +}; + +/*GmacTargetTimeLow = 0x0718, 32 bit nano seconds(MS) to be compared with system time : only when IEEE 1588 time stamping without external timestamp input */ +enum GmacTSLowReg +{ + GmacTSDecThr = 0x3B9AC9FF, /*when TSCTRLSSR is set the max value for GmacTargetTimeLowReg and GmacTimeStampLow register is 0x3B9AC9FF at 1ns precision */ +}; + +/* GmacTSHighWord = 0x0724, Time Stamp Higher Word Register (Version 2 only); only lower 16 bits are valid */ +enum GmacTSHighWordReg +{ + GmacTSHighWordMask = 0x0000FFFF, /* Time Stamp Higher work register has only lower 16 bits valid */ +}; +/*GmacTSStatus = 0x0728, Time Stamp Status Register */ +enum GmacTSStatusReg +{ + GmacTSTargTimeReached = 0x00000002, /* Time Stamp Target Time Reached 1 RO 0 */ + GmacTSSecondsOverflow = 0x00000001, /* Time Stamp Seconds Overflow 0 RO 0 */ +}; + +/* GmacAvMacCtrl = 0x0738, AV mac control Register */ +#ifdef AVB_SUPPORT +enum GmacAvMacCtrlReg +{ + GmacAvCtrlCh = 0x03000000, /* Channel on which AV control packets to be received 25:24 RW 0 */ + GmacPtpCh = 0x00180000, /* Channel on which PTP packets to be received 20:19 RW 0 */ + GmacAvPrio = 0x00070000, /* Priority tag value for AV Packets 18:16 RW 4 */ + GmacAvTypeMask = 0x0000FFFF, /* Ethernet Type value to be used for comparing and detecting AV packet 15:0 RW 0 */ +}; +#endif + + + + + + +#endif diff --git a/drivers/net/ethernet/ak-ethernet/eth_ops.h b/drivers/net/ethernet/ak-ethernet/eth_ops.h new file mode 100644 index 00000000..de632b34 --- /dev/null +++ b/drivers/net/ethernet/ak-ethernet/eth_ops.h @@ -0,0 +1,8 @@ +#ifndef _ETHERNET_OPERATION_H_ +#define _ETHERNET_OPERATION_H_ + +#define FLAG(_off) ((unsigned int)1 << (_off)) +#define BIT_SET(_val, _off) ( (_val) |= FLAG(_off) ) //set the bit +#define BIT_CLEAR(_val, _off) ( (_val) &= ~FLAG(_off) ) // clear the bit +#define BIT_TEST(_val, _off) (0 != ( (_val) & FLAG(_off) ) ) //test the bit +#endif diff --git a/drivers/net/ethernet/ak-ethernet/phyhw.h b/drivers/net/ethernet/ak-ethernet/phyhw.h new file mode 100644 index 00000000..a4caf42c --- /dev/null +++ b/drivers/net/ethernet/ak-ethernet/phyhw.h @@ -0,0 +1,140 @@ +#ifndef _ANYKA_PHY_REG_H_ +#define _ANYKA_PHY_REG_H_ +/********************* PHY regs definition ***************************/ +#define LC_10H 0x01 +#define LC_10F 0x02 +#define LC_100H 0x04 +#define LC_100F 0x08 +#define LC_1000F 0x10 +#define LC_ALL (LC_10H|LC_10F|LC_100H|LC_100F|LC_1000F) + +/* PHY Control Register */ +#define MII_BMCR 0x00 + #define BMCR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */ + #define BMCR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ + #define BMCR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ + #define BMCR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ + #define BMCR_ISOLATE 0x0400 /* Isolate PHY from MII */ + #define BMCR_POWER_DOWN 0x0800 /* Power down */ + #define BMCR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ + #define BMCR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */ + #define BMCR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ + #define BMCR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ + #define BMCR_SPEED_MASK 0x2040 + #define BMCR_SPEED_1000 0x0040 + #define BMCR_SPEED_100 0x2000 + #define BMCR_SPEED_10 0x0000 + +/* PHY Status Register */ +#define MII_BMSR 0x01 + #define BMMSR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ + #define BMSR_JABBER_DETECT 0x0002 /* Jabber Detected */ + #define BMSR_LINK_STATUS 0x0004 /* Link Status 1 = link */ + #define BMSR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ + #define BMSR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ + #define BMSR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ + #define BMSR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ + #define BMSR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ + #define BMSR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ + #define BMSR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ + #define BMSR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ + #define BMSR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ + #define BMSR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ + #define BMMII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ + #define BMMII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ + +#define MII_PHYSID1 0x02 +#define MII_PHYSID2 0x03 +#define L1D_MPW_PHYID1 0xD01C /* V7 */ +#define L1D_MPW_PHYID2 0xD01D /* V1-V6 */ +#define L1D_MPW_PHYID3 0xD01E /* V8 */ + + +/* Autoneg Advertisement Register */ +#define MII_ADVERTISE 0x04 + #define ADVERTISE_SELECTOR_FIELD 0x0001 /* indicates IEEE 802.3 CSMA/CD */ + #define ADVERTISE_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */ + #define ADVERTISE_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */ + #define ADVERTISE_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */ + #define ADVERTISE_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */ + #define ADVERTISE_100T4_CAPS 0x0200 /* 100T4 Capable */ + #define ADVERTISE_PAUSE 0x0400 /* Pause operation desired */ + #define ADVERTISE_ASM_DIR 0x0800 /* Asymmetric Pause Direction bit */ + #define ADVERTISE_REMOTE_FAULT 0x2000 /* Remote Fault detected */ + #define ADVERTISE_NEXT_PAGE 0x8000 /* Next Page ability supported */ + #define ADVERTISE_SPEED_MASK 0x01E0 + #define ADVERTISE_DEFAULT_CAP 0x0DE0 + +/* Link partner ability register */ +#define MII_LPA 0x05 + #define LPA_SLCT 0x001 /* Same as advertise selector */ + #define LPA_10HALF 0x002 /* Can do 10mbps half-duplex */ + #define LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */ + #define LPA_100HALF 0x0080 /* Can do 100mbps half-duplex */ + #define LPA_100FULL 0x0100 /* Can do 100mbps full-duplex */ + #define LPA_100BASE4 0x0200 /* 100BASE-T4 */ + #define LPA_PAUSE 0x0400 /* PAUSE */ + #define LPA_ASYPAUSE 0x0800 /* Asymmetrical PAUSE */ + #define LPA_RFAULT 0x2000 /* Link partner faulted */ + #define LPA_LPACK 0x4000 /* Link partner acked us */ + #define LPA_NPAGE 0x8000 /* Next page bit */ + +/* 1000BASE-T Control Register */ +#define MII_GIGA_CR 0x09 + #define GIGA_CR_1000T_HD_CAPS 0x0100 /* Advertise 1000T HD capability */ + #define GIGA_CR_1000T_FD_CAPS 0x0200 /* Advertise 1000T FD capability */ + #define GIGA_CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port */ + /* 0=DTE device */ + #define GIGA_CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master */ + /* 0=Configure PHY as Slave */ + #define GIGA_CR_1000T_MS_ENABLE 0x1000 /* 1=Master/Slave manual config value */ + /* 0=Automatic Master/Slave config */ + #define GIGA_CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */ + #define GIGA_CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */ + #define GIGA_CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */ + #define GIGA_CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */ + #define GIGA_CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */ + #define GIGA_CR_1000T_SPEED_MASK 0x0300 + #define GIGA_CR_1000T_DEFAULT_CAP 0x0300 + +/* 1000BASE-T Status Register */ +#define MII_GIGA_SR 0x0A + +/* PHY Specific Status Register */ +#define MII_GIGA_PSSR 0x11 + #define GIGA_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */ + #define GIGA_PSSR_DPLX 0x2000 /* 1=Duplex 0=Half Duplex */ + #define GIGA_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */ + #define GIGA_PSSR_10MBS 0x0000 /* 00=10Mbs */ + #define GIGA_PSSR_100MBS 0x4000 /* 01=100Mbs */ + #define GIGA_PSSR_1000MBS 0x8000 /* 10=1000Mbs */ + +/* PHY Interrupt Enable Register */ +#define MII_IER 0x12 + #define IER_LINK_UP 0x0400 + #define IER_LINK_DOWN 0x0800 + +/* PHY Interrupt Status Register */ +#define MII_ISR 0x13 + #define ISR_LINK_UP 0x0400 + #define ISR_LINK_DOWN 0x0800 + +/* Cable-Detect-Test Control Register */ +#define MII_CDTC 0x16 + #define CDTC_EN 1 /* sc */ + #define CDTC_PAIR_OFFSET 8 + #define CDTC_PAIR_MASK 3 + + +/* Cable-Detect-Test Status Register */ +#define MII_CDTS 0x1C + #define CDTS_STATUS_OFFSET 8 + #define CDTS_STATUS_MASK 3 + #define CDTS_STATUS_NORMAL 0 + #define CDTS_STATUS_SHORT 1 + #define CDTS_STATUS_OPEN 2 + #define CDTS_STATUS_INVALID 3 + +#define MII_DBG_ADDR 0x1D +#define MII_DBG_DATA 0x1E +#endif \ No newline at end of file diff --git a/drivers/net/ppp/Kconfig b/drivers/net/ppp/Kconfig index 872df3ef..7936ae4c 100644 --- a/drivers/net/ppp/Kconfig +++ b/drivers/net/ppp/Kconfig @@ -148,6 +148,23 @@ config PPPOL2TP used by ISPs and enterprises to tunnel PPP traffic over UDP tunnels. L2TP is replacing PPTP for VPN uses. +config PPPOLAC + tristate "PPP on L2TP Access Concentrator" + depends on PPP && INET + help + L2TP (RFC 2661) is a tunneling protocol widely used in virtual private + networks. This driver handles L2TP data packets between a UDP socket + and a PPP channel, but only permits one session per socket. Thus it is + fairly simple and suited for clients. + +config PPPOPNS + tristate "PPP on PPTP Network Server" + depends on PPP && INET + help + PPTP (RFC 2637) is a tunneling protocol widely used in virtual private + networks. This driver handles PPTP data packets between a RAW socket + and a PPP channel. It is fairly simple and easy to use. + config PPP_ASYNC tristate "PPP support for async serial ports" depends on PPP diff --git a/drivers/net/ppp/Makefile b/drivers/net/ppp/Makefile index a6b6297b..d283d03c 100644 --- a/drivers/net/ppp/Makefile +++ b/drivers/net/ppp/Makefile @@ -11,3 +11,5 @@ obj-$(CONFIG_PPP_SYNC_TTY) += ppp_synctty.o obj-$(CONFIG_PPPOE) += pppox.o pppoe.o obj-$(CONFIG_PPPOL2TP) += pppox.o obj-$(CONFIG_PPTP) += pppox.o pptp.o +obj-$(CONFIG_PPPOLAC) += pppox.o pppolac.o +obj-$(CONFIG_PPPOPNS) += pppox.o pppopns.o diff --git a/drivers/net/ppp/pppolac.c b/drivers/net/ppp/pppolac.c new file mode 100644 index 00000000..a5d3d634 --- /dev/null +++ b/drivers/net/ppp/pppolac.c @@ -0,0 +1,449 @@ +/* drivers/net/pppolac.c + * + * Driver for PPP on L2TP Access Concentrator / PPPoLAC Socket (RFC 2661) + * + * Copyright (C) 2009 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +/* This driver handles L2TP data packets between a UDP socket and a PPP channel. + * The socket must keep connected, and only one session per socket is permitted. + * Sequencing of outgoing packets is controlled by LNS. Incoming packets with + * sequences are reordered within a sliding window of one second. Currently + * reordering only happens when a packet is received. It is done for simplicity + * since no additional locks or threads are required. This driver only works on + * IPv4 due to the lack of UDP encapsulation support in IPv6. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define L2TP_CONTROL_BIT 0x80 +#define L2TP_LENGTH_BIT 0x40 +#define L2TP_SEQUENCE_BIT 0x08 +#define L2TP_OFFSET_BIT 0x02 +#define L2TP_VERSION 0x02 +#define L2TP_VERSION_MASK 0x0F + +#define PPP_ADDR 0xFF +#define PPP_CTRL 0x03 + +union unaligned { + __u32 u32; +} __attribute__((packed)); + +static inline union unaligned *unaligned(void *ptr) +{ + return (union unaligned *)ptr; +} + +struct meta { + __u32 sequence; + __u32 timestamp; +}; + +static inline struct meta *skb_meta(struct sk_buff *skb) +{ + return (struct meta *)skb->cb; +} + +/******************************************************************************/ + +static int pppolac_recv_core(struct sock *sk_udp, struct sk_buff *skb) +{ + struct sock *sk = (struct sock *)sk_udp->sk_user_data; + struct pppolac_opt *opt = &pppox_sk(sk)->proto.lac; + struct meta *meta = skb_meta(skb); + __u32 now = jiffies; + __u8 bits; + __u8 *ptr; + + /* Drop the packet if L2TP header is missing. */ + if (skb->len < sizeof(struct udphdr) + 6) + goto drop; + + /* Put it back if it is a control packet. */ + if (skb->data[sizeof(struct udphdr)] & L2TP_CONTROL_BIT) + return opt->backlog_rcv(sk_udp, skb); + + /* Skip UDP header. */ + skb_pull(skb, sizeof(struct udphdr)); + + /* Check the version. */ + if ((skb->data[1] & L2TP_VERSION_MASK) != L2TP_VERSION) + goto drop; + bits = skb->data[0]; + ptr = &skb->data[2]; + + /* Check the length if it is present. */ + if (bits & L2TP_LENGTH_BIT) { + if ((ptr[0] << 8 | ptr[1]) != skb->len) + goto drop; + ptr += 2; + } + + /* Skip all fields including optional ones. */ + if (!skb_pull(skb, 6 + (bits & L2TP_SEQUENCE_BIT ? 4 : 0) + + (bits & L2TP_LENGTH_BIT ? 2 : 0) + + (bits & L2TP_OFFSET_BIT ? 2 : 0))) + goto drop; + + /* Skip the offset padding if it is present. */ + if (bits & L2TP_OFFSET_BIT && + !skb_pull(skb, skb->data[-2] << 8 | skb->data[-1])) + goto drop; + + /* Check the tunnel and the session. */ + if (unaligned(ptr)->u32 != opt->local) + goto drop; + + /* Check the sequence if it is present. */ + if (bits & L2TP_SEQUENCE_BIT) { + meta->sequence = ptr[4] << 8 | ptr[5]; + if ((__s16)(meta->sequence - opt->recv_sequence) < 0) + goto drop; + } + + /* Skip PPP address and control if they are present. */ + if (skb->len >= 2 && skb->data[0] == PPP_ADDR && + skb->data[1] == PPP_CTRL) + skb_pull(skb, 2); + + /* Fix PPP protocol if it is compressed. */ + if (skb->len >= 1 && skb->data[0] & 1) + skb_push(skb, 1)[0] = 0; + + /* Drop the packet if PPP protocol is missing. */ + if (skb->len < 2) + goto drop; + + /* Perform reordering if sequencing is enabled. */ + atomic_set(&opt->sequencing, bits & L2TP_SEQUENCE_BIT); + if (bits & L2TP_SEQUENCE_BIT) { + struct sk_buff *skb1; + + /* Insert the packet into receive queue in order. */ + skb_set_owner_r(skb, sk); + skb_queue_walk(&sk->sk_receive_queue, skb1) { + struct meta *meta1 = skb_meta(skb1); + __s16 order = meta->sequence - meta1->sequence; + if (order == 0) + goto drop; + if (order < 0) { + meta->timestamp = meta1->timestamp; + skb_insert(skb1, skb, &sk->sk_receive_queue); + skb = NULL; + break; + } + } + if (skb) { + meta->timestamp = now; + skb_queue_tail(&sk->sk_receive_queue, skb); + } + + /* Remove packets from receive queue as long as + * 1. the receive buffer is full, + * 2. they are queued longer than one second, or + * 3. there are no missing packets before them. */ + skb_queue_walk_safe(&sk->sk_receive_queue, skb, skb1) { + meta = skb_meta(skb); + if (atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf && + now - meta->timestamp < HZ && + meta->sequence != opt->recv_sequence) + break; + skb_unlink(skb, &sk->sk_receive_queue); + opt->recv_sequence = (__u16)(meta->sequence + 1); + skb_orphan(skb); + ppp_input(&pppox_sk(sk)->chan, skb); + } + return NET_RX_SUCCESS; + } + + /* Flush receive queue if sequencing is disabled. */ + skb_queue_purge(&sk->sk_receive_queue); + skb_orphan(skb); + ppp_input(&pppox_sk(sk)->chan, skb); + return NET_RX_SUCCESS; +drop: + kfree_skb(skb); + return NET_RX_DROP; +} + +static int pppolac_recv(struct sock *sk_udp, struct sk_buff *skb) +{ + sock_hold(sk_udp); + sk_receive_skb(sk_udp, skb, 0); + return 0; +} + +static struct sk_buff_head delivery_queue; + +static void pppolac_xmit_core(struct work_struct *delivery_work) +{ + mm_segment_t old_fs = get_fs(); + struct sk_buff *skb; + + set_fs(KERNEL_DS); + while ((skb = skb_dequeue(&delivery_queue))) { + struct sock *sk_udp = skb->sk; + struct kvec iov = {.iov_base = skb->data, .iov_len = skb->len}; + struct msghdr msg = { + .msg_iov = (struct iovec *)&iov, + .msg_iovlen = 1, + .msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT, + }; + sk_udp->sk_prot->sendmsg(NULL, sk_udp, &msg, skb->len); + kfree_skb(skb); + } + set_fs(old_fs); +} + +static DECLARE_WORK(delivery_work, pppolac_xmit_core); + +static int pppolac_xmit(struct ppp_channel *chan, struct sk_buff *skb) +{ + struct sock *sk_udp = (struct sock *)chan->private; + struct pppolac_opt *opt = &pppox_sk(sk_udp->sk_user_data)->proto.lac; + + /* Install PPP address and control. */ + skb_push(skb, 2); + skb->data[0] = PPP_ADDR; + skb->data[1] = PPP_CTRL; + + /* Install L2TP header. */ + if (atomic_read(&opt->sequencing)) { + skb_push(skb, 10); + skb->data[0] = L2TP_SEQUENCE_BIT; + skb->data[6] = opt->xmit_sequence >> 8; + skb->data[7] = opt->xmit_sequence; + skb->data[8] = 0; + skb->data[9] = 0; + opt->xmit_sequence++; + } else { + skb_push(skb, 6); + skb->data[0] = 0; + } + skb->data[1] = L2TP_VERSION; + unaligned(&skb->data[2])->u32 = opt->remote; + + /* Now send the packet via the delivery queue. */ + skb_set_owner_w(skb, sk_udp); + skb_queue_tail(&delivery_queue, skb); + schedule_work(&delivery_work); + return 1; +} + +/******************************************************************************/ + +static struct ppp_channel_ops pppolac_channel_ops = { + .start_xmit = pppolac_xmit, +}; + +static int pppolac_connect(struct socket *sock, struct sockaddr *useraddr, + int addrlen, int flags) +{ + struct sock *sk = sock->sk; + struct pppox_sock *po = pppox_sk(sk); + struct sockaddr_pppolac *addr = (struct sockaddr_pppolac *)useraddr; + struct socket *sock_udp = NULL; + struct sock *sk_udp; + int error; + + if (addrlen != sizeof(struct sockaddr_pppolac) || + !addr->local.tunnel || !addr->local.session || + !addr->remote.tunnel || !addr->remote.session) { + return -EINVAL; + } + + lock_sock(sk); + error = -EALREADY; + if (sk->sk_state != PPPOX_NONE) + goto out; + + sock_udp = sockfd_lookup(addr->udp_socket, &error); + if (!sock_udp) + goto out; + sk_udp = sock_udp->sk; + lock_sock(sk_udp); + + /* Remove this check when IPv6 supports UDP encapsulation. */ + error = -EAFNOSUPPORT; + if (sk_udp->sk_family != AF_INET) + goto out; + error = -EPROTONOSUPPORT; + if (sk_udp->sk_protocol != IPPROTO_UDP) + goto out; + error = -EDESTADDRREQ; + if (sk_udp->sk_state != TCP_ESTABLISHED) + goto out; + error = -EBUSY; + if (udp_sk(sk_udp)->encap_type || sk_udp->sk_user_data) + goto out; + if (!sk_udp->sk_bound_dev_if) { + struct dst_entry *dst = sk_dst_get(sk_udp); + error = -ENODEV; + if (!dst) + goto out; + sk_udp->sk_bound_dev_if = dst->dev->ifindex; + dst_release(dst); + } + + po->chan.hdrlen = 12; + po->chan.private = sk_udp; + po->chan.ops = &pppolac_channel_ops; + po->chan.mtu = PPP_MRU - 80; + po->proto.lac.local = unaligned(&addr->local)->u32; + po->proto.lac.remote = unaligned(&addr->remote)->u32; + atomic_set(&po->proto.lac.sequencing, 1); + po->proto.lac.backlog_rcv = sk_udp->sk_backlog_rcv; + + error = ppp_register_channel(&po->chan); + if (error) + goto out; + + sk->sk_state = PPPOX_CONNECTED; + udp_sk(sk_udp)->encap_type = UDP_ENCAP_L2TPINUDP; + udp_sk(sk_udp)->encap_rcv = pppolac_recv; + sk_udp->sk_backlog_rcv = pppolac_recv_core; + sk_udp->sk_user_data = sk; +out: + if (sock_udp) { + release_sock(sk_udp); + if (error) + sockfd_put(sock_udp); + } + release_sock(sk); + return error; +} + +static int pppolac_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + if (!sk) + return 0; + + lock_sock(sk); + if (sock_flag(sk, SOCK_DEAD)) { + release_sock(sk); + return -EBADF; + } + + if (sk->sk_state != PPPOX_NONE) { + struct sock *sk_udp = (struct sock *)pppox_sk(sk)->chan.private; + lock_sock(sk_udp); + skb_queue_purge(&sk->sk_receive_queue); + pppox_unbind_sock(sk); + udp_sk(sk_udp)->encap_type = 0; + udp_sk(sk_udp)->encap_rcv = NULL; + sk_udp->sk_backlog_rcv = pppox_sk(sk)->proto.lac.backlog_rcv; + sk_udp->sk_user_data = NULL; + release_sock(sk_udp); + sockfd_put(sk_udp->sk_socket); + } + + sock_orphan(sk); + sock->sk = NULL; + release_sock(sk); + sock_put(sk); + return 0; +} + +/******************************************************************************/ + +static struct proto pppolac_proto = { + .name = "PPPOLAC", + .owner = THIS_MODULE, + .obj_size = sizeof(struct pppox_sock), +}; + +static struct proto_ops pppolac_proto_ops = { + .family = PF_PPPOX, + .owner = THIS_MODULE, + .release = pppolac_release, + .bind = sock_no_bind, + .connect = pppolac_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = sock_no_getname, + .poll = sock_no_poll, + .ioctl = pppox_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = sock_no_setsockopt, + .getsockopt = sock_no_getsockopt, + .sendmsg = sock_no_sendmsg, + .recvmsg = sock_no_recvmsg, + .mmap = sock_no_mmap, +}; + +static int pppolac_create(struct net *net, struct socket *sock) +{ + struct sock *sk; + + sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppolac_proto); + if (!sk) + return -ENOMEM; + + sock_init_data(sock, sk); + sock->state = SS_UNCONNECTED; + sock->ops = &pppolac_proto_ops; + sk->sk_protocol = PX_PROTO_OLAC; + sk->sk_state = PPPOX_NONE; + return 0; +} + +/******************************************************************************/ + +static struct pppox_proto pppolac_pppox_proto = { + .create = pppolac_create, + .owner = THIS_MODULE, +}; + +static int __init pppolac_init(void) +{ + int error; + + error = proto_register(&pppolac_proto, 0); + if (error) + return error; + + error = register_pppox_proto(PX_PROTO_OLAC, &pppolac_pppox_proto); + if (error) + proto_unregister(&pppolac_proto); + else + skb_queue_head_init(&delivery_queue); + return error; +} + +static void __exit pppolac_exit(void) +{ + unregister_pppox_proto(PX_PROTO_OLAC); + proto_unregister(&pppolac_proto); +} + +module_init(pppolac_init); +module_exit(pppolac_exit); + +MODULE_DESCRIPTION("PPP on L2TP Access Concentrator (PPPoLAC)"); +MODULE_AUTHOR("Chia-chi Yeh "); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ppp/pppopns.c b/drivers/net/ppp/pppopns.c new file mode 100644 index 00000000..6016d29c --- /dev/null +++ b/drivers/net/ppp/pppopns.c @@ -0,0 +1,428 @@ +/* drivers/net/pppopns.c + * + * Driver for PPP on PPTP Network Server / PPPoPNS Socket (RFC 2637) + * + * Copyright (C) 2009 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +/* This driver handles PPTP data packets between a RAW socket and a PPP channel. + * The socket is created in the kernel space and connected to the same address + * of the control socket. Outgoing packets are always sent with sequences but + * without acknowledgements. Incoming packets with sequences are reordered + * within a sliding window of one second. Currently reordering only happens when + * a packet is received. It is done for simplicity since no additional locks or + * threads are required. This driver should work on both IPv4 and IPv6. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GRE_HEADER_SIZE 8 + +#define PPTP_GRE_BITS htons(0x2001) +#define PPTP_GRE_BITS_MASK htons(0xEF7F) +#define PPTP_GRE_SEQ_BIT htons(0x1000) +#define PPTP_GRE_ACK_BIT htons(0x0080) +#define PPTP_GRE_TYPE htons(0x880B) + +#define PPP_ADDR 0xFF +#define PPP_CTRL 0x03 + +struct header { + __u16 bits; + __u16 type; + __u16 length; + __u16 call; + __u32 sequence; +} __attribute__((packed)); + +struct meta { + __u32 sequence; + __u32 timestamp; +}; + +static inline struct meta *skb_meta(struct sk_buff *skb) +{ + return (struct meta *)skb->cb; +} + +/******************************************************************************/ + +static int pppopns_recv_core(struct sock *sk_raw, struct sk_buff *skb) +{ + struct sock *sk = (struct sock *)sk_raw->sk_user_data; + struct pppopns_opt *opt = &pppox_sk(sk)->proto.pns; + struct meta *meta = skb_meta(skb); + __u32 now = jiffies; + struct header *hdr; + + /* Skip transport header */ + skb_pull(skb, skb_transport_header(skb) - skb->data); + + /* Drop the packet if GRE header is missing. */ + if (skb->len < GRE_HEADER_SIZE) + goto drop; + hdr = (struct header *)skb->data; + + /* Check the header. */ + if (hdr->type != PPTP_GRE_TYPE || hdr->call != opt->local || + (hdr->bits & PPTP_GRE_BITS_MASK) != PPTP_GRE_BITS) + goto drop; + + /* Skip all fields including optional ones. */ + if (!skb_pull(skb, GRE_HEADER_SIZE + + (hdr->bits & PPTP_GRE_SEQ_BIT ? 4 : 0) + + (hdr->bits & PPTP_GRE_ACK_BIT ? 4 : 0))) + goto drop; + + /* Check the length. */ + if (skb->len != ntohs(hdr->length)) + goto drop; + + /* Check the sequence if it is present. */ + if (hdr->bits & PPTP_GRE_SEQ_BIT) { + meta->sequence = ntohl(hdr->sequence); + if ((__s32)(meta->sequence - opt->recv_sequence) < 0) + goto drop; + } + + /* Skip PPP address and control if they are present. */ + if (skb->len >= 2 && skb->data[0] == PPP_ADDR && + skb->data[1] == PPP_CTRL) + skb_pull(skb, 2); + + /* Fix PPP protocol if it is compressed. */ + if (skb->len >= 1 && skb->data[0] & 1) + skb_push(skb, 1)[0] = 0; + + /* Drop the packet if PPP protocol is missing. */ + if (skb->len < 2) + goto drop; + + /* Perform reordering if sequencing is enabled. */ + if (hdr->bits & PPTP_GRE_SEQ_BIT) { + struct sk_buff *skb1; + + /* Insert the packet into receive queue in order. */ + skb_set_owner_r(skb, sk); + skb_queue_walk(&sk->sk_receive_queue, skb1) { + struct meta *meta1 = skb_meta(skb1); + __s32 order = meta->sequence - meta1->sequence; + if (order == 0) + goto drop; + if (order < 0) { + meta->timestamp = meta1->timestamp; + skb_insert(skb1, skb, &sk->sk_receive_queue); + skb = NULL; + break; + } + } + if (skb) { + meta->timestamp = now; + skb_queue_tail(&sk->sk_receive_queue, skb); + } + + /* Remove packets from receive queue as long as + * 1. the receive buffer is full, + * 2. they are queued longer than one second, or + * 3. there are no missing packets before them. */ + skb_queue_walk_safe(&sk->sk_receive_queue, skb, skb1) { + meta = skb_meta(skb); + if (atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf && + now - meta->timestamp < HZ && + meta->sequence != opt->recv_sequence) + break; + skb_unlink(skb, &sk->sk_receive_queue); + opt->recv_sequence = meta->sequence + 1; + skb_orphan(skb); + ppp_input(&pppox_sk(sk)->chan, skb); + } + return NET_RX_SUCCESS; + } + + /* Flush receive queue if sequencing is disabled. */ + skb_queue_purge(&sk->sk_receive_queue); + skb_orphan(skb); + ppp_input(&pppox_sk(sk)->chan, skb); + return NET_RX_SUCCESS; +drop: + kfree_skb(skb); + return NET_RX_DROP; +} + +static void pppopns_recv(struct sock *sk_raw, int length) +{ + struct sk_buff *skb; + while ((skb = skb_dequeue(&sk_raw->sk_receive_queue))) { + sock_hold(sk_raw); + sk_receive_skb(sk_raw, skb, 0); + } +} + +static struct sk_buff_head delivery_queue; + +static void pppopns_xmit_core(struct work_struct *delivery_work) +{ + mm_segment_t old_fs = get_fs(); + struct sk_buff *skb; + + set_fs(KERNEL_DS); + while ((skb = skb_dequeue(&delivery_queue))) { + struct sock *sk_raw = skb->sk; + struct kvec iov = {.iov_base = skb->data, .iov_len = skb->len}; + struct msghdr msg = { + .msg_iov = (struct iovec *)&iov, + .msg_iovlen = 1, + .msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT, + }; + sk_raw->sk_prot->sendmsg(NULL, sk_raw, &msg, skb->len); + kfree_skb(skb); + } + set_fs(old_fs); +} + +static DECLARE_WORK(delivery_work, pppopns_xmit_core); + +static int pppopns_xmit(struct ppp_channel *chan, struct sk_buff *skb) +{ + struct sock *sk_raw = (struct sock *)chan->private; + struct pppopns_opt *opt = &pppox_sk(sk_raw->sk_user_data)->proto.pns; + struct header *hdr; + __u16 length; + + /* Install PPP address and control. */ + skb_push(skb, 2); + skb->data[0] = PPP_ADDR; + skb->data[1] = PPP_CTRL; + length = skb->len; + + /* Install PPTP GRE header. */ + hdr = (struct header *)skb_push(skb, 12); + hdr->bits = PPTP_GRE_BITS | PPTP_GRE_SEQ_BIT; + hdr->type = PPTP_GRE_TYPE; + hdr->length = htons(length); + hdr->call = opt->remote; + hdr->sequence = htonl(opt->xmit_sequence); + opt->xmit_sequence++; + + /* Now send the packet via the delivery queue. */ + skb_set_owner_w(skb, sk_raw); + skb_queue_tail(&delivery_queue, skb); + schedule_work(&delivery_work); + return 1; +} + +/******************************************************************************/ + +static struct ppp_channel_ops pppopns_channel_ops = { + .start_xmit = pppopns_xmit, +}; + +static int pppopns_connect(struct socket *sock, struct sockaddr *useraddr, + int addrlen, int flags) +{ + struct sock *sk = sock->sk; + struct pppox_sock *po = pppox_sk(sk); + struct sockaddr_pppopns *addr = (struct sockaddr_pppopns *)useraddr; + struct sockaddr_storage ss; + struct socket *sock_tcp = NULL; + struct socket *sock_raw = NULL; + struct sock *sk_tcp; + struct sock *sk_raw; + int error; + + if (addrlen != sizeof(struct sockaddr_pppopns)) + return -EINVAL; + + lock_sock(sk); + error = -EALREADY; + if (sk->sk_state != PPPOX_NONE) + goto out; + + sock_tcp = sockfd_lookup(addr->tcp_socket, &error); + if (!sock_tcp) + goto out; + sk_tcp = sock_tcp->sk; + error = -EPROTONOSUPPORT; + if (sk_tcp->sk_protocol != IPPROTO_TCP) + goto out; + addrlen = sizeof(struct sockaddr_storage); + error = kernel_getpeername(sock_tcp, (struct sockaddr *)&ss, &addrlen); + if (error) + goto out; + if (!sk_tcp->sk_bound_dev_if) { + struct dst_entry *dst = sk_dst_get(sk_tcp); + error = -ENODEV; + if (!dst) + goto out; + sk_tcp->sk_bound_dev_if = dst->dev->ifindex; + dst_release(dst); + } + + error = sock_create(ss.ss_family, SOCK_RAW, IPPROTO_GRE, &sock_raw); + if (error) + goto out; + sk_raw = sock_raw->sk; + sk_raw->sk_bound_dev_if = sk_tcp->sk_bound_dev_if; + error = kernel_connect(sock_raw, (struct sockaddr *)&ss, addrlen, 0); + if (error) + goto out; + + po->chan.hdrlen = 14; + po->chan.private = sk_raw; + po->chan.ops = &pppopns_channel_ops; + po->chan.mtu = PPP_MRU - 80; + po->proto.pns.local = addr->local; + po->proto.pns.remote = addr->remote; + po->proto.pns.data_ready = sk_raw->sk_data_ready; + po->proto.pns.backlog_rcv = sk_raw->sk_backlog_rcv; + + error = ppp_register_channel(&po->chan); + if (error) + goto out; + + sk->sk_state = PPPOX_CONNECTED; + lock_sock(sk_raw); + sk_raw->sk_data_ready = pppopns_recv; + sk_raw->sk_backlog_rcv = pppopns_recv_core; + sk_raw->sk_user_data = sk; + release_sock(sk_raw); +out: + if (sock_tcp) + sockfd_put(sock_tcp); + if (error && sock_raw) + sock_release(sock_raw); + release_sock(sk); + return error; +} + +static int pppopns_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + if (!sk) + return 0; + + lock_sock(sk); + if (sock_flag(sk, SOCK_DEAD)) { + release_sock(sk); + return -EBADF; + } + + if (sk->sk_state != PPPOX_NONE) { + struct sock *sk_raw = (struct sock *)pppox_sk(sk)->chan.private; + lock_sock(sk_raw); + skb_queue_purge(&sk->sk_receive_queue); + pppox_unbind_sock(sk); + sk_raw->sk_data_ready = pppox_sk(sk)->proto.pns.data_ready; + sk_raw->sk_backlog_rcv = pppox_sk(sk)->proto.pns.backlog_rcv; + sk_raw->sk_user_data = NULL; + release_sock(sk_raw); + sock_release(sk_raw->sk_socket); + } + + sock_orphan(sk); + sock->sk = NULL; + release_sock(sk); + sock_put(sk); + return 0; +} + +/******************************************************************************/ + +static struct proto pppopns_proto = { + .name = "PPPOPNS", + .owner = THIS_MODULE, + .obj_size = sizeof(struct pppox_sock), +}; + +static struct proto_ops pppopns_proto_ops = { + .family = PF_PPPOX, + .owner = THIS_MODULE, + .release = pppopns_release, + .bind = sock_no_bind, + .connect = pppopns_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = sock_no_getname, + .poll = sock_no_poll, + .ioctl = pppox_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = sock_no_setsockopt, + .getsockopt = sock_no_getsockopt, + .sendmsg = sock_no_sendmsg, + .recvmsg = sock_no_recvmsg, + .mmap = sock_no_mmap, +}; + +static int pppopns_create(struct net *net, struct socket *sock) +{ + struct sock *sk; + + sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppopns_proto); + if (!sk) + return -ENOMEM; + + sock_init_data(sock, sk); + sock->state = SS_UNCONNECTED; + sock->ops = &pppopns_proto_ops; + sk->sk_protocol = PX_PROTO_OPNS; + sk->sk_state = PPPOX_NONE; + return 0; +} + +/******************************************************************************/ + +static struct pppox_proto pppopns_pppox_proto = { + .create = pppopns_create, + .owner = THIS_MODULE, +}; + +static int __init pppopns_init(void) +{ + int error; + + error = proto_register(&pppopns_proto, 0); + if (error) + return error; + + error = register_pppox_proto(PX_PROTO_OPNS, &pppopns_pppox_proto); + if (error) + proto_unregister(&pppopns_proto); + else + skb_queue_head_init(&delivery_queue); + return error; +} + +static void __exit pppopns_exit(void) +{ + unregister_pppox_proto(PX_PROTO_OPNS); + proto_unregister(&pppopns_proto); +} + +module_init(pppopns_init); +module_exit(pppopns_exit); + +MODULE_DESCRIPTION("PPP on PPTP Network Server (PPPoPNS)"); +MODULE_AUTHOR("Chia-chi Yeh "); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 147b6284..279d860e 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1254,6 +1254,12 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, int vnet_hdr_sz; int ret; +#ifdef CONFIG_ANDROID_PARANOID_NETWORK + if (cmd != TUNGETIFF && !capable(CAP_NET_ADMIN)) { + return -EPERM; + } +#endif + if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89) { if (copy_from_user(&ifr, argp, ifreq_len)) return -EFAULT; diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c index c8f1b5b3..cf06f88a 100644 --- a/drivers/net/usb/rndis_host.c +++ b/drivers/net/usb/rndis_host.c @@ -137,6 +137,8 @@ int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen) /* Some devices don't respond on the control channel until * polled on the status channel, so do that first. */ + //add for notion 4g module:disable interrupt polling +#if 0 if (dev->driver_info->data & RNDIS_DRIVER_DATA_POLL_STATUS) { retval = usb_interrupt_msg( dev->udev, @@ -147,7 +149,9 @@ int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen) if (unlikely(retval < 0)) return retval; } - +#else + mdelay(200); //wait a short time for ready +#endif /* Poll the control channel; the request probably completed immediately */ rsp = buf->msg_type | RNDIS_MSG_COMPLETION; for (count = 0; count < 10; count++) { diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 174aece3..6c2698d3 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -769,14 +769,15 @@ int usbnet_open (struct net_device *net) } /* start any status interrupt transfer */ - if (dev->interrupt) { + //add for notion 4g module:disable interrupt urb submit + /*if (dev->interrupt) { retval = usb_submit_urb (dev->interrupt, GFP_KERNEL); if (retval < 0) { netif_err(dev, ifup, dev->net, "intr submit %d\n", retval); goto done; } - } + }*/ set_bit(EVENT_DEV_OPEN, &dev->flags); netif_start_queue (net); @@ -1453,8 +1454,9 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) status = 0; } - if (status >= 0 && dev->status) - status = init_status (dev, udev); + //add for notion 4g module:disable interrupt urb submit && polling + //if (status >= 0 && dev->status) + //status = init_status (dev, udev); if (status < 0) goto out3; diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index abd3b71c..d9252ff7 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -268,9 +268,15 @@ config MWL8K To compile this driver as a module, choose M here: the module will be called mwl8k. If unsure, say N. +config WIFI_CONTROL_FUNC + bool "Enable WiFi control function abstraction" + help + Enables Power/Reset/Carddetect function abstraction + source "drivers/net/wireless/ath/Kconfig" source "drivers/net/wireless/b43/Kconfig" source "drivers/net/wireless/b43legacy/Kconfig" +source "drivers/net/wireless/bcmdhd/Kconfig" source "drivers/net/wireless/brcm80211/Kconfig" source "drivers/net/wireless/hostap/Kconfig" source "drivers/net/wireless/ipw2x00/Kconfig" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 98db7619..8db066f3 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -59,5 +59,7 @@ obj-$(CONFIG_IWM) += iwmc3200wifi/ obj-$(CONFIG_MWIFIEX) += mwifiex/ +obj-$(CONFIG_BCMDHD) += bcmdhd/ + obj-$(CONFIG_BRCMFMAC) += brcm80211/ obj-$(CONFIG_BRCMSMAC) += brcm80211/ diff --git a/drivers/net/wireless/bcmdhd/Kconfig b/drivers/net/wireless/bcmdhd/Kconfig new file mode 100644 index 00000000..231ae187 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/Kconfig @@ -0,0 +1,47 @@ +config BCMDHD + tristate "Broadcom 4329/30 wireless cards support" + depends on MMC + ---help--- + This module adds support for wireless adapters based on + Broadcom 4329/30 chipset. + + This driver uses the kernel's wireless extensions subsystem. + + If you choose to build a module, it'll be called dhd. Say M if + unsure. + +config BCMDHD_FW_PATH + depends on BCMDHD + string "Firmware path" + default "/system/etc/firmware/fw_bcmdhd.bin" + ---help--- + Path to the firmware file. + +config BCMDHD_NVRAM_PATH + depends on BCMDHD + string "NVRAM path" + default "/system/etc/wifi/bcmdhd.cal" + ---help--- + Path to the calibration file. + +config BCMDHD_WEXT + bool "Enable WEXT support" + depends on BCMDHD && CFG80211 = n + select WIRELESS_EXT + select WEXT_PRIV + help + Enables WEXT support + +config DHD_USE_STATIC_BUF + bool "Enable memory preallocation" + depends on BCMDHD + default n + ---help--- + Use memory preallocated in platform + +config DHD_USE_SCHED_SCAN + bool "Use CFG80211 sched scan" + depends on BCMDHD && CFG80211 + default n + ---help--- + Use CFG80211 sched scan diff --git a/drivers/net/wireless/bcmdhd/Makefile b/drivers/net/wireless/bcmdhd/Makefile new file mode 100644 index 00000000..85149537 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/Makefile @@ -0,0 +1,45 @@ +# bcmdhd for 43241 +# + +DHDCFLAGS = -Wall -Wstrict-prototypes -Dlinux -DBCMDRIVER \ + -DBCMDONGLEHOST -DUNRELEASEDCHIP -DBCMDMA32 -DBCMFILEIMAGE \ + -DDHDTHREAD -DDHD_DEBUG -DSDTEST -DBDC -DTOE \ + -DDHD_BCMEVENTS -DSHOW_EVENTS -DPROP_TXSTATUS -DBCMDBG \ + -DCUSTOMER_HW2 -DOOB_INTR_ONLY -DHW_OOB \ + -DMMC_SDIO_ABORT -DBCMSDIO -DBCMLXSDMMC -DBCMPLATFORM_BUS -DWLP2P \ + -DWIFI_ACT_FRAME -DARP_OFFLOAD_SUPPORT \ + -DKEEP_ALIVE -DGET_CUSTOM_MAC_ENABLE -DPKT_FILTER_SUPPORT \ + -DEMBEDDED_PLATFORM -DENABLE_INSMOD_NO_FW_LOAD -DPNO_SUPPORT \ + -DDHD_USE_IDLECOUNT -DSET_RANDOM_MAC_SOFTAP -DROAM_ENABLE -DVSDB \ + -DWL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST -DSDIO_CRC_ERROR_FIX \ + -DESCAN_RESULT_PATCH -DHT40_GO -DPASS_ARP_PACKET -DSUPPORT_PM2_ONLY \ + -DDHD_DONOT_FORWARD_BCMEVENT_AS_NETWORK_PKT \ + -DCUSTOM_SDIO_F2_BLKSIZE=128 \ + -Idrivers/net/wireless/bcmdhd -Idrivers/net/wireless/bcmdhd/include + +DHDOFILES = aiutils.o bcmsdh_sdmmc_linux.o dhd_linux.o siutils.o bcmutils.o \ + dhd_linux_sched.o dhd_sdio.o bcmwifi_channels.o bcmevent.o hndpmu.o \ + bcmsdh.o dhd_cdc.o bcmsdh_linux.o dhd_common.o linux_osl.o \ + bcmsdh_sdmmc.o dhd_custom_gpio.o sbutils.o wldev_common.o wl_android.o + +obj-$(CONFIG_BCMDHD) += bcmdhd.o +bcmdhd-objs += $(DHDOFILES) +ifneq ($(CONFIG_WIRELESS_EXT),) +bcmdhd-objs += wl_iw.o +DHDCFLAGS += -DSOFTAP -DWL_WIRELESS_EXT -DUSE_IW +endif +ifneq ($(CONFIG_CFG80211),) +bcmdhd-objs += wl_cfg80211.o wl_cfgp2p.o wl_linux_mon.o dhd_cfg80211.o +DHDCFLAGS += -DWL_CFG80211 -DWL_CFG80211_STA_EVENT -DWL_ENABLE_P2P_IF +DHDCFLAGS += -DCUSTOM_ROAM_TRIGGER_SETTING=-65 +DHDCFLAGS += -DCUSTOM_ROAM_DELTA_SETTING=15 +DHDCFLAGS += -DCUSTOM_KEEP_ALIVE_SETTING=28000 +DHDCFLAGS += -DCUSTOM_PNO_EVENT_LOCK_xTIME=7 +endif +ifneq ($(CONFIG_DHD_USE_SCHED_SCAN),) +DHDCFLAGS += -DWL_SCHED_SCAN +endif +EXTRA_CFLAGS = $(DHDCFLAGS) +ifeq ($(CONFIG_BCMDHD),m) +EXTRA_LDFLAGS += --strip-debug +endif diff --git a/drivers/net/wireless/bcmdhd/aiutils.c b/drivers/net/wireless/bcmdhd/aiutils.c new file mode 100644 index 00000000..3ca17259 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/aiutils.c @@ -0,0 +1,846 @@ +/* + * Misc utility routines for accessing chip-specific features + * of the SiliconBackplane-based Broadcom chips. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: aiutils.c 347614 2012-07-27 10:24:51Z $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "siutils_priv.h" + +#define BCM47162_DMP() (0) +#define BCM5357_DMP() (0) +#define remap_coreid(sih, coreid) (coreid) +#define remap_corerev(sih, corerev) (corerev) + + + +static uint32 +get_erom_ent(si_t *sih, uint32 **eromptr, uint32 mask, uint32 match) +{ + uint32 ent; + uint inv = 0, nom = 0; + + while (TRUE) { + ent = R_REG(si_osh(sih), *eromptr); + (*eromptr)++; + + if (mask == 0) + break; + + if ((ent & ER_VALID) == 0) { + inv++; + continue; + } + + if (ent == (ER_END | ER_VALID)) + break; + + if ((ent & mask) == match) + break; + + nom++; + } + + SI_VMSG(("%s: Returning ent 0x%08x\n", __FUNCTION__, ent)); + if (inv + nom) { + SI_VMSG((" after %d invalid and %d non-matching entries\n", inv, nom)); + } + return ent; +} + +static uint32 +get_asd(si_t *sih, uint32 **eromptr, uint sp, uint ad, uint st, uint32 *addrl, uint32 *addrh, + uint32 *sizel, uint32 *sizeh) +{ + uint32 asd, sz, szd; + + asd = get_erom_ent(sih, eromptr, ER_VALID, ER_VALID); + if (((asd & ER_TAG1) != ER_ADD) || + (((asd & AD_SP_MASK) >> AD_SP_SHIFT) != sp) || + ((asd & AD_ST_MASK) != st)) { + + (*eromptr)--; + return 0; + } + *addrl = asd & AD_ADDR_MASK; + if (asd & AD_AG32) + *addrh = get_erom_ent(sih, eromptr, 0, 0); + else + *addrh = 0; + *sizeh = 0; + sz = asd & AD_SZ_MASK; + if (sz == AD_SZ_SZD) { + szd = get_erom_ent(sih, eromptr, 0, 0); + *sizel = szd & SD_SZ_MASK; + if (szd & SD_SG32) + *sizeh = get_erom_ent(sih, eromptr, 0, 0); + } else + *sizel = AD_SZ_BASE << (sz >> AD_SZ_SHIFT); + + SI_VMSG((" SP %d, ad %d: st = %d, 0x%08x_0x%08x @ 0x%08x_0x%08x\n", + sp, ad, st, *sizeh, *sizel, *addrh, *addrl)); + + return asd; +} + +static void +ai_hwfixup(si_info_t *sii) +{ +} + + + +void +ai_scan(si_t *sih, void *regs, uint devid) +{ + si_info_t *sii = SI_INFO(sih); + chipcregs_t *cc = (chipcregs_t *)regs; + uint32 erombase, *eromptr, *eromlim; + + erombase = R_REG(sii->osh, &cc->eromptr); + + switch (BUSTYPE(sih->bustype)) { + case SI_BUS: + eromptr = (uint32 *)REG_MAP(erombase, SI_CORE_SIZE); + break; + + case PCI_BUS: + + sii->curwrap = (void *)((uintptr)regs + SI_CORE_SIZE); + + + OSL_PCI_WRITE_CONFIG(sii->osh, PCI_BAR0_WIN, 4, erombase); + eromptr = regs; + break; + + case SPI_BUS: + case SDIO_BUS: + eromptr = (uint32 *)(uintptr)erombase; + break; + + case PCMCIA_BUS: + default: + SI_ERROR(("Don't know how to do AXI enumertion on bus %d\n", sih->bustype)); + ASSERT(0); + return; + } + eromlim = eromptr + (ER_REMAPCONTROL / sizeof(uint32)); + + SI_VMSG(("ai_scan: regs = 0x%p, erombase = 0x%08x, eromptr = 0x%p, eromlim = 0x%p\n", + regs, erombase, eromptr, eromlim)); + while (eromptr < eromlim) { + uint32 cia, cib, cid, mfg, crev, nmw, nsw, nmp, nsp; + uint32 mpd, asd, addrl, addrh, sizel, sizeh; + uint i, j, idx; + bool br; + + br = FALSE; + + + cia = get_erom_ent(sih, &eromptr, ER_TAG, ER_CI); + if (cia == (ER_END | ER_VALID)) { + SI_VMSG(("Found END of erom after %d cores\n", sii->numcores)); + ai_hwfixup(sii); + return; + } + + cib = get_erom_ent(sih, &eromptr, 0, 0); + + if ((cib & ER_TAG) != ER_CI) { + SI_ERROR(("CIA not followed by CIB\n")); + goto error; + } + + cid = (cia & CIA_CID_MASK) >> CIA_CID_SHIFT; + mfg = (cia & CIA_MFG_MASK) >> CIA_MFG_SHIFT; + crev = (cib & CIB_REV_MASK) >> CIB_REV_SHIFT; + nmw = (cib & CIB_NMW_MASK) >> CIB_NMW_SHIFT; + nsw = (cib & CIB_NSW_MASK) >> CIB_NSW_SHIFT; + nmp = (cib & CIB_NMP_MASK) >> CIB_NMP_SHIFT; + nsp = (cib & CIB_NSP_MASK) >> CIB_NSP_SHIFT; + +#ifdef BCMDBG_SI + SI_VMSG(("Found component 0x%04x/0x%04x rev %d at erom addr 0x%p, with nmw = %d, " + "nsw = %d, nmp = %d & nsp = %d\n", + mfg, cid, crev, eromptr - 1, nmw, nsw, nmp, nsp)); +#else + BCM_REFERENCE(crev); +#endif + + if (((mfg == MFGID_ARM) && (cid == DEF_AI_COMP)) || (nsp == 0)) + continue; + if ((nmw + nsw == 0)) { + + if (cid == OOB_ROUTER_CORE_ID) { + asd = get_asd(sih, &eromptr, 0, 0, AD_ST_SLAVE, + &addrl, &addrh, &sizel, &sizeh); + if (asd != 0) { + sii->oob_router = addrl; + } + } + if (cid != GMAC_COMMON_4706_CORE_ID) + continue; + } + + idx = sii->numcores; + + sii->cia[idx] = cia; + sii->cib[idx] = cib; + sii->coreid[idx] = remap_coreid(sih, cid); + + for (i = 0; i < nmp; i++) { + mpd = get_erom_ent(sih, &eromptr, ER_VALID, ER_VALID); + if ((mpd & ER_TAG) != ER_MP) { + SI_ERROR(("Not enough MP entries for component 0x%x\n", cid)); + goto error; + } + SI_VMSG((" Master port %d, mp: %d id: %d\n", i, + (mpd & MPD_MP_MASK) >> MPD_MP_SHIFT, + (mpd & MPD_MUI_MASK) >> MPD_MUI_SHIFT)); + } + + + asd = get_asd(sih, &eromptr, 0, 0, AD_ST_SLAVE, &addrl, &addrh, &sizel, &sizeh); + if (asd == 0) { + do { + + asd = get_asd(sih, &eromptr, 0, 0, AD_ST_BRIDGE, &addrl, &addrh, + &sizel, &sizeh); + if (asd != 0) + br = TRUE; + else { + if (br == TRUE) { + break; + } + else if ((addrh != 0) || (sizeh != 0) || + (sizel != SI_CORE_SIZE)) { + SI_ERROR(("addrh = 0x%x\t sizeh = 0x%x\t size1 =" + "0x%x\n", addrh, sizeh, sizel)); + SI_ERROR(("First Slave ASD for" + "core 0x%04x malformed " + "(0x%08x)\n", cid, asd)); + goto error; + } + } + } while (1); + } + sii->coresba[idx] = addrl; + sii->coresba_size[idx] = sizel; + + j = 1; + do { + asd = get_asd(sih, &eromptr, 0, j, AD_ST_SLAVE, &addrl, &addrh, + &sizel, &sizeh); + if ((asd != 0) && (j == 1) && (sizel == SI_CORE_SIZE)) { + sii->coresba2[idx] = addrl; + sii->coresba2_size[idx] = sizel; + } + j++; + } while (asd != 0); + + + for (i = 1; i < nsp; i++) { + j = 0; + do { + asd = get_asd(sih, &eromptr, i, j, AD_ST_SLAVE, &addrl, &addrh, + &sizel, &sizeh); + + if (asd == 0) + break; + j++; + } while (1); + if (j == 0) { + SI_ERROR((" SP %d has no address descriptors\n", i)); + goto error; + } + } + + + for (i = 0; i < nmw; i++) { + asd = get_asd(sih, &eromptr, i, 0, AD_ST_MWRAP, &addrl, &addrh, + &sizel, &sizeh); + if (asd == 0) { + SI_ERROR(("Missing descriptor for MW %d\n", i)); + goto error; + } + if ((sizeh != 0) || (sizel != SI_CORE_SIZE)) { + SI_ERROR(("Master wrapper %d is not 4KB\n", i)); + goto error; + } + if (i == 0) + sii->wrapba[idx] = addrl; + } + + + for (i = 0; i < nsw; i++) { + uint fwp = (nsp == 1) ? 0 : 1; + asd = get_asd(sih, &eromptr, fwp + i, 0, AD_ST_SWRAP, &addrl, &addrh, + &sizel, &sizeh); + if (asd == 0) { + SI_ERROR(("Missing descriptor for SW %d\n", i)); + goto error; + } + if ((sizeh != 0) || (sizel != SI_CORE_SIZE)) { + SI_ERROR(("Slave wrapper %d is not 4KB\n", i)); + goto error; + } + if ((nmw == 0) && (i == 0)) + sii->wrapba[idx] = addrl; + } + + + + if (br) + continue; + + + sii->numcores++; + } + + SI_ERROR(("Reached end of erom without finding END")); + +error: + sii->numcores = 0; + return; +} + + +void * +ai_setcoreidx(si_t *sih, uint coreidx) +{ + si_info_t *sii = SI_INFO(sih); + uint32 addr, wrap; + void *regs; + + if (coreidx >= MIN(sii->numcores, SI_MAXCORES)) + return (NULL); + + addr = sii->coresba[coreidx]; + wrap = sii->wrapba[coreidx]; + + + ASSERT((sii->intrsenabled_fn == NULL) || !(*(sii)->intrsenabled_fn)((sii)->intr_arg)); + + switch (BUSTYPE(sih->bustype)) { + case SI_BUS: + + if (!sii->regs[coreidx]) { + sii->regs[coreidx] = REG_MAP(addr, SI_CORE_SIZE); + ASSERT(GOODREGS(sii->regs[coreidx])); + } + sii->curmap = regs = sii->regs[coreidx]; + if (!sii->wrappers[coreidx]) { + sii->wrappers[coreidx] = REG_MAP(wrap, SI_CORE_SIZE); + ASSERT(GOODREGS(sii->wrappers[coreidx])); + } + sii->curwrap = sii->wrappers[coreidx]; + break; + + + case SPI_BUS: + case SDIO_BUS: + sii->curmap = regs = (void *)((uintptr)addr); + sii->curwrap = (void *)((uintptr)wrap); + break; + + case PCMCIA_BUS: + default: + ASSERT(0); + regs = NULL; + break; + } + + sii->curmap = regs; + sii->curidx = coreidx; + + return regs; +} + +void +ai_coreaddrspaceX(si_t *sih, uint asidx, uint32 *addr, uint32 *size) +{ + si_info_t *sii = SI_INFO(sih); + chipcregs_t *cc = NULL; + uint32 erombase, *eromptr, *eromlim; + uint i, j, cidx; + uint32 cia, cib, nmp, nsp; + uint32 asd, addrl, addrh, sizel, sizeh; + + for (i = 0; i < sii->numcores; i++) { + if (sii->coreid[i] == CC_CORE_ID) { + cc = (chipcregs_t *)sii->regs[i]; + break; + } + } + if (cc == NULL) + goto error; + + erombase = R_REG(sii->osh, &cc->eromptr); + eromptr = (uint32 *)REG_MAP(erombase, SI_CORE_SIZE); + eromlim = eromptr + (ER_REMAPCONTROL / sizeof(uint32)); + + cidx = sii->curidx; + cia = sii->cia[cidx]; + cib = sii->cib[cidx]; + + nmp = (cib & CIB_NMP_MASK) >> CIB_NMP_SHIFT; + nsp = (cib & CIB_NSP_MASK) >> CIB_NSP_SHIFT; + + + while (eromptr < eromlim) { + if ((get_erom_ent(sih, &eromptr, ER_TAG, ER_CI) == cia) && + (get_erom_ent(sih, &eromptr, 0, 0) == cib)) { + break; + } + } + + + for (i = 0; i < nmp; i++) + get_erom_ent(sih, &eromptr, ER_VALID, ER_VALID); + + + asd = get_asd(sih, &eromptr, 0, 0, AD_ST_SLAVE, &addrl, &addrh, &sizel, &sizeh); + if (asd == 0) { + + asd = get_asd(sih, &eromptr, 0, 0, AD_ST_BRIDGE, &addrl, &addrh, + &sizel, &sizeh); + } + + j = 1; + do { + asd = get_asd(sih, &eromptr, 0, j, AD_ST_SLAVE, &addrl, &addrh, + &sizel, &sizeh); + j++; + } while (asd != 0); + + + for (i = 1; i < nsp; i++) { + j = 0; + do { + asd = get_asd(sih, &eromptr, i, j, AD_ST_SLAVE, &addrl, &addrh, + &sizel, &sizeh); + if (asd == 0) + break; + + if (!asidx--) { + *addr = addrl; + *size = sizel; + return; + } + j++; + } while (1); + + if (j == 0) { + SI_ERROR((" SP %d has no address descriptors\n", i)); + break; + } + } + +error: + *size = 0; + return; +} + + +int +ai_numaddrspaces(si_t *sih) +{ + return 2; +} + + +uint32 +ai_addrspace(si_t *sih, uint asidx) +{ + si_info_t *sii; + uint cidx; + + sii = SI_INFO(sih); + cidx = sii->curidx; + + if (asidx == 0) + return sii->coresba[cidx]; + else if (asidx == 1) + return sii->coresba2[cidx]; + else { + SI_ERROR(("%s: Need to parse the erom again to find addr space %d\n", + __FUNCTION__, asidx)); + return 0; + } +} + + +uint32 +ai_addrspacesize(si_t *sih, uint asidx) +{ + si_info_t *sii; + uint cidx; + + sii = SI_INFO(sih); + cidx = sii->curidx; + + if (asidx == 0) + return sii->coresba_size[cidx]; + else if (asidx == 1) + return sii->coresba2_size[cidx]; + else { + SI_ERROR(("%s: Need to parse the erom again to find addr space %d\n", + __FUNCTION__, asidx)); + return 0; + } +} + +uint +ai_flag(si_t *sih) +{ + si_info_t *sii; + aidmp_t *ai; + + sii = SI_INFO(sih); + if (BCM47162_DMP()) { + SI_ERROR(("%s: Attempting to read MIPS DMP registers on 47162a0", __FUNCTION__)); + return sii->curidx; + } + if (BCM5357_DMP()) { + SI_ERROR(("%s: Attempting to read USB20H DMP registers on 5357b0\n", __FUNCTION__)); + return sii->curidx; + } + ai = sii->curwrap; + + return (R_REG(sii->osh, &ai->oobselouta30) & 0x1f); +} + +void +ai_setint(si_t *sih, int siflag) +{ +} + +uint +ai_wrap_reg(si_t *sih, uint32 offset, uint32 mask, uint32 val) +{ + si_info_t *sii = SI_INFO(sih); + uint32 *map = (uint32 *) sii->curwrap; + + if (mask || val) { + uint32 w = R_REG(sii->osh, map+(offset/4)); + w &= ~mask; + w |= val; + W_REG(sii->osh, map+(offset/4), val); + } + + return (R_REG(sii->osh, map+(offset/4))); +} + +uint +ai_corevendor(si_t *sih) +{ + si_info_t *sii; + uint32 cia; + + sii = SI_INFO(sih); + cia = sii->cia[sii->curidx]; + return ((cia & CIA_MFG_MASK) >> CIA_MFG_SHIFT); +} + +uint +ai_corerev(si_t *sih) +{ + si_info_t *sii; + uint32 cib; + + sii = SI_INFO(sih); + cib = sii->cib[sii->curidx]; + return remap_corerev(sih, (cib & CIB_REV_MASK) >> CIB_REV_SHIFT); +} + +bool +ai_iscoreup(si_t *sih) +{ + si_info_t *sii; + aidmp_t *ai; + + sii = SI_INFO(sih); + ai = sii->curwrap; + + return (((R_REG(sii->osh, &ai->ioctrl) & (SICF_FGC | SICF_CLOCK_EN)) == SICF_CLOCK_EN) && + ((R_REG(sii->osh, &ai->resetctrl) & AIRC_RESET) == 0)); +} + + +uint +ai_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val) +{ + uint origidx = 0; + uint32 *r = NULL; + uint w; + uint intr_val = 0; + bool fast = FALSE; + si_info_t *sii; + + sii = SI_INFO(sih); + + ASSERT(GOODIDX(coreidx)); + ASSERT(regoff < SI_CORE_SIZE); + ASSERT((val & ~mask) == 0); + + if (coreidx >= SI_MAXCORES) + return 0; + + if (BUSTYPE(sih->bustype) == SI_BUS) { + + fast = TRUE; + + if (!sii->regs[coreidx]) { + sii->regs[coreidx] = REG_MAP(sii->coresba[coreidx], + SI_CORE_SIZE); + ASSERT(GOODREGS(sii->regs[coreidx])); + } + r = (uint32 *)((uchar *)sii->regs[coreidx] + regoff); + } else if (BUSTYPE(sih->bustype) == PCI_BUS) { + + + if ((sii->coreid[coreidx] == CC_CORE_ID) && SI_FAST(sii)) { + + + fast = TRUE; + r = (uint32 *)((char *)sii->curmap + PCI_16KB0_CCREGS_OFFSET + regoff); + } else if (sii->pub.buscoreidx == coreidx) { + + fast = TRUE; + if (SI_FAST(sii)) + r = (uint32 *)((char *)sii->curmap + + PCI_16KB0_PCIREGS_OFFSET + regoff); + else + r = (uint32 *)((char *)sii->curmap + + ((regoff >= SBCONFIGOFF) ? + PCI_BAR0_PCISBR_OFFSET : PCI_BAR0_PCIREGS_OFFSET) + + regoff); + } + } + + if (!fast) { + INTR_OFF(sii, intr_val); + + + origidx = si_coreidx(&sii->pub); + + + r = (uint32*) ((uchar*) ai_setcoreidx(&sii->pub, coreidx) + regoff); + } + ASSERT(r != NULL); + + + if (mask || val) { + w = (R_REG(sii->osh, r) & ~mask) | val; + W_REG(sii->osh, r, w); + } + + + w = R_REG(sii->osh, r); + + if (!fast) { + + if (origidx != coreidx) + ai_setcoreidx(&sii->pub, origidx); + + INTR_RESTORE(sii, intr_val); + } + + return (w); +} + +void +ai_core_disable(si_t *sih, uint32 bits) +{ + si_info_t *sii; + volatile uint32 dummy; + uint32 status; + aidmp_t *ai; + + sii = SI_INFO(sih); + + ASSERT(GOODREGS(sii->curwrap)); + ai = sii->curwrap; + + + if (R_REG(sii->osh, &ai->resetctrl) & AIRC_RESET) + return; + + + SPINWAIT(((status = R_REG(sii->osh, &ai->resetstatus)) != 0), 300); + + + if (status != 0) { + + + SPINWAIT(((status = R_REG(sii->osh, &ai->resetstatus)) != 0), 10000); + + + } + + W_REG(sii->osh, &ai->ioctrl, bits); + dummy = R_REG(sii->osh, &ai->ioctrl); + BCM_REFERENCE(dummy); + OSL_DELAY(10); + + W_REG(sii->osh, &ai->resetctrl, AIRC_RESET); + dummy = R_REG(sii->osh, &ai->resetctrl); + BCM_REFERENCE(dummy); + OSL_DELAY(1); +} + + +void +ai_core_reset(si_t *sih, uint32 bits, uint32 resetbits) +{ + si_info_t *sii; + aidmp_t *ai; + volatile uint32 dummy; + + sii = SI_INFO(sih); + ASSERT(GOODREGS(sii->curwrap)); + ai = sii->curwrap; + + + ai_core_disable(sih, (bits | resetbits)); + + + W_REG(sii->osh, &ai->ioctrl, (bits | SICF_FGC | SICF_CLOCK_EN)); + dummy = R_REG(sii->osh, &ai->ioctrl); + BCM_REFERENCE(dummy); + + W_REG(sii->osh, &ai->resetctrl, 0); + dummy = R_REG(sii->osh, &ai->resetctrl); + BCM_REFERENCE(dummy); + OSL_DELAY(1); + + W_REG(sii->osh, &ai->ioctrl, (bits | SICF_CLOCK_EN)); + dummy = R_REG(sii->osh, &ai->ioctrl); + BCM_REFERENCE(dummy); + OSL_DELAY(1); +} + +void +ai_core_cflags_wo(si_t *sih, uint32 mask, uint32 val) +{ + si_info_t *sii; + aidmp_t *ai; + uint32 w; + + sii = SI_INFO(sih); + + if (BCM47162_DMP()) { + SI_ERROR(("%s: Accessing MIPS DMP register (ioctrl) on 47162a0", + __FUNCTION__)); + return; + } + if (BCM5357_DMP()) { + SI_ERROR(("%s: Accessing USB20H DMP register (ioctrl) on 5357\n", + __FUNCTION__)); + return; + } + + ASSERT(GOODREGS(sii->curwrap)); + ai = sii->curwrap; + + ASSERT((val & ~mask) == 0); + + if (mask || val) { + w = ((R_REG(sii->osh, &ai->ioctrl) & ~mask) | val); + W_REG(sii->osh, &ai->ioctrl, w); + } +} + +uint32 +ai_core_cflags(si_t *sih, uint32 mask, uint32 val) +{ + si_info_t *sii; + aidmp_t *ai; + uint32 w; + + sii = SI_INFO(sih); + if (BCM47162_DMP()) { + SI_ERROR(("%s: Accessing MIPS DMP register (ioctrl) on 47162a0", + __FUNCTION__)); + return 0; + } + if (BCM5357_DMP()) { + SI_ERROR(("%s: Accessing USB20H DMP register (ioctrl) on 5357\n", + __FUNCTION__)); + return 0; + } + + ASSERT(GOODREGS(sii->curwrap)); + ai = sii->curwrap; + + ASSERT((val & ~mask) == 0); + + if (mask || val) { + w = ((R_REG(sii->osh, &ai->ioctrl) & ~mask) | val); + W_REG(sii->osh, &ai->ioctrl, w); + } + + return R_REG(sii->osh, &ai->ioctrl); +} + +uint32 +ai_core_sflags(si_t *sih, uint32 mask, uint32 val) +{ + si_info_t *sii; + aidmp_t *ai; + uint32 w; + + sii = SI_INFO(sih); + if (BCM47162_DMP()) { + SI_ERROR(("%s: Accessing MIPS DMP register (iostatus) on 47162a0", + __FUNCTION__)); + return 0; + } + if (BCM5357_DMP()) { + SI_ERROR(("%s: Accessing USB20H DMP register (iostatus) on 5357\n", + __FUNCTION__)); + return 0; + } + + ASSERT(GOODREGS(sii->curwrap)); + ai = sii->curwrap; + + ASSERT((val & ~mask) == 0); + ASSERT((mask & ~SISF_CORE_BITS) == 0); + + if (mask || val) { + w = ((R_REG(sii->osh, &ai->iostatus) & ~mask) | val); + W_REG(sii->osh, &ai->iostatus, w); + } + + return R_REG(sii->osh, &ai->iostatus); +} diff --git a/drivers/net/wireless/bcmdhd/bcmevent.c b/drivers/net/wireless/bcmdhd/bcmevent.c new file mode 100644 index 00000000..35859488 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/bcmevent.c @@ -0,0 +1,147 @@ +/* + * bcmevent read-only data shared by kernel or app layers + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * $Id: bcmevent.c 370587 2012-11-22 09:32:38Z $ + */ + +#include +#include +#include +#include +#include + +#if WLC_E_LAST != 107 +#error "You need to add an entry to bcmevent_names[] for the new event" +#endif + +const bcmevent_name_t bcmevent_names[] = { + { WLC_E_SET_SSID, "SET_SSID" }, + { WLC_E_JOIN, "JOIN" }, + { WLC_E_START, "START" }, + { WLC_E_AUTH, "AUTH" }, + { WLC_E_AUTH_IND, "AUTH_IND" }, + { WLC_E_DEAUTH, "DEAUTH" }, + { WLC_E_DEAUTH_IND, "DEAUTH_IND" }, + { WLC_E_ASSOC, "ASSOC" }, + { WLC_E_ASSOC_IND, "ASSOC_IND" }, + { WLC_E_REASSOC, "REASSOC" }, + { WLC_E_REASSOC_IND, "REASSOC_IND" }, + { WLC_E_DISASSOC, "DISASSOC" }, + { WLC_E_DISASSOC_IND, "DISASSOC_IND" }, + { WLC_E_QUIET_START, "START_QUIET" }, + { WLC_E_QUIET_END, "END_QUIET" }, + { WLC_E_BEACON_RX, "BEACON_RX" }, + { WLC_E_LINK, "LINK" }, + { WLC_E_MIC_ERROR, "MIC_ERROR" }, + { WLC_E_NDIS_LINK, "NDIS_LINK" }, + { WLC_E_ROAM, "ROAM" }, + { WLC_E_TXFAIL, "TXFAIL" }, + { WLC_E_PMKID_CACHE, "PMKID_CACHE" }, + { WLC_E_RETROGRADE_TSF, "RETROGRADE_TSF" }, + { WLC_E_PRUNE, "PRUNE" }, + { WLC_E_AUTOAUTH, "AUTOAUTH" }, + { WLC_E_EAPOL_MSG, "EAPOL_MSG" }, + { WLC_E_SCAN_COMPLETE, "SCAN_COMPLETE" }, + { WLC_E_ADDTS_IND, "ADDTS_IND" }, + { WLC_E_DELTS_IND, "DELTS_IND" }, + { WLC_E_BCNSENT_IND, "BCNSENT_IND" }, + { WLC_E_BCNRX_MSG, "BCNRX_MSG" }, + { WLC_E_BCNLOST_MSG, "BCNLOST_IND" }, + { WLC_E_ROAM_PREP, "ROAM_PREP" }, + { WLC_E_PFN_NET_FOUND, "PFNFOUND_IND" }, + { WLC_E_PFN_NET_LOST, "PFNLOST_IND" }, +#if defined(IBSS_PEER_DISCOVERY_EVENT) + { WLC_E_IBSS_ASSOC, "IBSS_ASSOC" }, +#endif /* defined(IBSS_PEER_DISCOVERY_EVENT) */ + { WLC_E_RADIO, "RADIO" }, + { WLC_E_PSM_WATCHDOG, "PSM_WATCHDOG" }, + { WLC_E_PROBREQ_MSG, "PROBE_REQ_MSG" }, + { WLC_E_SCAN_CONFIRM_IND, "SCAN_CONFIRM_IND" }, + { WLC_E_PSK_SUP, "PSK_SUP" }, + { WLC_E_COUNTRY_CODE_CHANGED, "CNTRYCODE_IND" }, + { WLC_E_EXCEEDED_MEDIUM_TIME, "EXCEEDED_MEDIUM_TIME" }, + { WLC_E_ICV_ERROR, "ICV_ERROR" }, + { WLC_E_UNICAST_DECODE_ERROR, "UNICAST_DECODE_ERROR" }, + { WLC_E_MULTICAST_DECODE_ERROR, "MULTICAST_DECODE_ERROR" }, + { WLC_E_TRACE, "TRACE" }, +#ifdef WLBTAMP + { WLC_E_BTA_HCI_EVENT, "BTA_HCI_EVENT" }, +#endif + { WLC_E_IF, "IF" }, +#ifdef WLP2P + { WLC_E_P2P_DISC_LISTEN_COMPLETE, "WLC_E_P2P_DISC_LISTEN_COMPLETE" }, +#endif + { WLC_E_RSSI, "RSSI" }, + { WLC_E_PFN_SCAN_COMPLETE, "SCAN_COMPLETE" }, + { WLC_E_EXTLOG_MSG, "EXTERNAL LOG MESSAGE" }, +#ifdef WIFI_ACT_FRAME + { WLC_E_ACTION_FRAME, "ACTION_FRAME" }, + { WLC_E_ACTION_FRAME_RX, "ACTION_FRAME_RX" }, + { WLC_E_ACTION_FRAME_COMPLETE, "ACTION_FRAME_COMPLETE" }, +#endif +#if 0 && (NDISVER >= 0x0620) + { WLC_E_PRE_ASSOC_IND, "ASSOC_RECV" }, + { WLC_E_PRE_REASSOC_IND, "REASSOC_RECV" }, + { WLC_E_CHANNEL_ADOPTED, "CHANNEL_ADOPTED" }, + { WLC_E_AP_STARTED, "AP_STARTED" }, + { WLC_E_DFS_AP_STOP, "DFS_AP_STOP" }, + { WLC_E_DFS_AP_RESUME, "DFS_AP_RESUME" }, + { WLC_E_ASSOC_IND_NDIS, "ASSOC_IND_NDIS"}, + { WLC_E_REASSOC_IND_NDIS, "REASSOC_IND_NDIS"}, + { WLC_E_ACTION_FRAME_RX_NDIS, "WLC_E_ACTION_FRAME_RX_NDIS" }, + { WLC_E_AUTH_REQ, "WLC_E_AUTH_REQ" }, +#endif + { WLC_E_ESCAN_RESULT, "WLC_E_ESCAN_RESULT" }, + { WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE, "WLC_E_AF_OFF_CHAN_COMPLETE" }, +#ifdef WLP2P + { WLC_E_PROBRESP_MSG, "PROBE_RESP_MSG" }, + { WLC_E_P2P_PROBREQ_MSG, "P2P PROBE_REQ_MSG" }, +#endif +#ifdef PROP_TXSTATUS + { WLC_E_FIFO_CREDIT_MAP, "FIFO_CREDIT_MAP" }, +#endif + { WLC_E_WAKE_EVENT, "WAKE_EVENT" }, + { WLC_E_DCS_REQUEST, "DCS_REQUEST" }, + { WLC_E_RM_COMPLETE, "RM_COMPLETE" }, +#ifdef WLMEDIA_HTSF + { WLC_E_HTSFSYNC, "HTSF_SYNC_EVENT" }, +#endif + { WLC_E_OVERLAY_REQ, "OVERLAY_REQ_EVENT" }, + { WLC_E_CSA_COMPLETE_IND, "WLC_E_CSA_COMPLETE_IND"}, + { WLC_E_EXCESS_PM_WAKE_EVENT, "EXCESS_PM_WAKE_EVENT" }, + { WLC_E_PFN_SCAN_NONE, "PFN_SCAN_NONE" }, + { WLC_E_PFN_SCAN_ALLGONE, "PFN_SCAN_ALLGONE" }, +#ifdef SOFTAP + { WLC_E_GTK_PLUMBED, "GTK_PLUMBED" }, +#endif + { WLC_E_ASSOC_REQ_IE, "ASSOC_REQ_IE" }, + { WLC_E_ASSOC_RESP_IE, "ASSOC_RESP_IE" }, + { WLC_E_ACTION_FRAME_RX_NDIS, "WLC_E_ACTION_FRAME_RX_NDIS" }, +#ifdef WLTDLS + { WLC_E_TDLS_PEER_EVENT, "TDLS_PEER_EVENT" }, +#endif /* WLTDLS */ + { WLC_E_SERVICE_FOUND, "SERVICE_FOUND" }, + { WLC_E_P2PO_ADD_DEVICE, "P2PO_DEV_FOUND" }, + { WLC_E_P2PO_DEL_DEVICE, "P2PO_DEV_LOST" }, +}; + +const int bcmevent_names_size = ARRAYSIZE(bcmevent_names); diff --git a/drivers/net/wireless/bcmdhd/bcmsdh.c b/drivers/net/wireless/bcmdhd/bcmsdh.c new file mode 100644 index 00000000..b05e2956 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/bcmsdh.c @@ -0,0 +1,756 @@ +/* + * BCMSDH interface glue + * implement bcmsdh API for SDIOH driver + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmsdh.c 347614 2012-07-27 10:24:51Z $ + */ + +/** + * @file bcmsdh.c + */ + +/* ****************** BCMSDH Interface Functions *************************** */ + +#include +#include +#include +#include +#include +#include +#include + +#include /* BRCM API for SDIO clients (such as wl, dhd) */ +#include /* common SDIO/controller interface */ +#include /* SDIO device core hardware definitions. */ + +#include /* SDIO Device and Protocol Specs */ + +#define SDIOH_API_ACCESS_RETRY_LIMIT 2 +const uint bcmsdh_msglevel = BCMSDH_ERROR_VAL; + +/** + * BCMSDH API context + */ +struct bcmsdh_info +{ + bool init_success; /* underlying driver successfully attached */ + void *sdioh; /* handler for sdioh */ + uint32 vendevid; /* Target Vendor and Device ID on SD bus */ + osl_t *osh; + bool regfail; /* Save status of last reg_read/reg_write call */ + uint32 sbwad; /* Save backplane window address */ +}; +/* local copy of bcm sd handler */ +bcmsdh_info_t * l_bcmsdh = NULL; + +#if defined(OOB_INTR_ONLY) && defined(HW_OOB) +extern int +sdioh_enable_hw_oob_intr(void *sdioh, bool enable); + +void +bcmsdh_enable_hw_oob_intr(bcmsdh_info_t *sdh, bool enable) +{ + sdioh_enable_hw_oob_intr(sdh->sdioh, enable); +} +#endif + +/* Attach BCMSDH layer to SDIO Host Controller Driver + * + * @param osh OSL Handle. + * @param cfghdl Configuration Handle. + * @param regsva Virtual address of controller registers. + * @param irq Interrupt number of SDIO controller. + * + * @return bcmsdh_info_t Handle to BCMSDH context. + */ +bcmsdh_info_t * +bcmsdh_attach(osl_t *osh, void *cfghdl, void **regsva, uint irq) +{ + bcmsdh_info_t *bcmsdh; + + if ((bcmsdh = (bcmsdh_info_t *)MALLOC(osh, sizeof(bcmsdh_info_t))) == NULL) { + BCMSDH_ERROR(("bcmsdh_attach: out of memory, malloced %d bytes\n", MALLOCED(osh))); + return NULL; + } + bzero((char *)bcmsdh, sizeof(bcmsdh_info_t)); + + /* save the handler locally */ + l_bcmsdh = bcmsdh; + + if (!(bcmsdh->sdioh = sdioh_attach(osh, cfghdl, irq))) { + bcmsdh_detach(osh, bcmsdh); + return NULL; + } + + bcmsdh->osh = osh; + bcmsdh->init_success = TRUE; + + *regsva = (uint32 *)SI_ENUM_BASE; + + /* Report the BAR, to fix if needed */ + bcmsdh->sbwad = SI_ENUM_BASE; + return bcmsdh; +} + +int +bcmsdh_detach(osl_t *osh, void *sdh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + + if (bcmsdh != NULL) { + if (bcmsdh->sdioh) { + sdioh_detach(osh, bcmsdh->sdioh); + bcmsdh->sdioh = NULL; + } + MFREE(osh, bcmsdh, sizeof(bcmsdh_info_t)); + } + + l_bcmsdh = NULL; + return 0; +} + +int +bcmsdh_iovar_op(void *sdh, const char *name, + void *params, int plen, void *arg, int len, bool set) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + return sdioh_iovar_op(bcmsdh->sdioh, name, params, plen, arg, len, set); +} + +bool +bcmsdh_intr_query(void *sdh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + bool on; + + ASSERT(bcmsdh); + status = sdioh_interrupt_query(bcmsdh->sdioh, &on); + if (SDIOH_API_SUCCESS(status)) + return FALSE; + else + return on; +} + +int +bcmsdh_intr_enable(void *sdh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + ASSERT(bcmsdh); + + status = sdioh_interrupt_set(bcmsdh->sdioh, TRUE); + return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR); +} + +int +bcmsdh_intr_disable(void *sdh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + ASSERT(bcmsdh); + + status = sdioh_interrupt_set(bcmsdh->sdioh, FALSE); + return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR); +} + +int +bcmsdh_intr_reg(void *sdh, bcmsdh_cb_fn_t fn, void *argh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + ASSERT(bcmsdh); + + status = sdioh_interrupt_register(bcmsdh->sdioh, fn, argh); + return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR); +} + +int +bcmsdh_intr_dereg(void *sdh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + ASSERT(bcmsdh); + + status = sdioh_interrupt_deregister(bcmsdh->sdioh); + return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR); +} + +#if defined(DHD_DEBUG) +bool +bcmsdh_intr_pending(void *sdh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + + ASSERT(sdh); + return sdioh_interrupt_pending(bcmsdh->sdioh); +} +#endif + + +int +bcmsdh_devremove_reg(void *sdh, bcmsdh_cb_fn_t fn, void *argh) +{ + ASSERT(sdh); + + /* don't support yet */ + return BCME_UNSUPPORTED; +} + +/** + * Read from SDIO Configuration Space + * @param sdh SDIO Host context. + * @param func_num Function number to read from. + * @param addr Address to read from. + * @param err Error return. + * @return value read from SDIO configuration space. + */ +uint8 +bcmsdh_cfg_read(void *sdh, uint fnc_num, uint32 addr, int *err) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; +#ifdef SDIOH_API_ACCESS_RETRY_LIMIT + int32 retry = 0; +#endif + uint8 data = 0; + + if (!bcmsdh) + bcmsdh = l_bcmsdh; + + ASSERT(bcmsdh->init_success); + +#ifdef SDIOH_API_ACCESS_RETRY_LIMIT + do { + if (retry) /* wait for 1 ms till bus get settled down */ + OSL_DELAY(1000); +#endif + status = sdioh_cfg_read(bcmsdh->sdioh, fnc_num, addr, (uint8 *)&data); +#ifdef SDIOH_API_ACCESS_RETRY_LIMIT + } while (!SDIOH_API_SUCCESS(status) && (retry++ < SDIOH_API_ACCESS_RETRY_LIMIT)); +#endif + if (err) + *err = (SDIOH_API_SUCCESS(status) ? 0 : BCME_SDIO_ERROR); + + BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, uint8data = 0x%x\n", __FUNCTION__, + fnc_num, addr, data)); + + return data; +} + +void +bcmsdh_cfg_write(void *sdh, uint fnc_num, uint32 addr, uint8 data, int *err) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; +#ifdef SDIOH_API_ACCESS_RETRY_LIMIT + int32 retry = 0; +#endif + + if (!bcmsdh) + bcmsdh = l_bcmsdh; + + ASSERT(bcmsdh->init_success); + +#ifdef SDIOH_API_ACCESS_RETRY_LIMIT + do { + if (retry) /* wait for 1 ms till bus get settled down */ + OSL_DELAY(1000); +#endif + status = sdioh_cfg_write(bcmsdh->sdioh, fnc_num, addr, (uint8 *)&data); +#ifdef SDIOH_API_ACCESS_RETRY_LIMIT + } while (!SDIOH_API_SUCCESS(status) && (retry++ < SDIOH_API_ACCESS_RETRY_LIMIT)); +#endif + if (err) + *err = SDIOH_API_SUCCESS(status) ? 0 : BCME_SDIO_ERROR; + + BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, uint8data = 0x%x\n", __FUNCTION__, + fnc_num, addr, data)); +} + +uint32 +bcmsdh_cfg_read_word(void *sdh, uint fnc_num, uint32 addr, int *err) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + uint32 data = 0; + + if (!bcmsdh) + bcmsdh = l_bcmsdh; + + ASSERT(bcmsdh->init_success); + + status = sdioh_request_word(bcmsdh->sdioh, SDIOH_CMD_TYPE_NORMAL, SDIOH_READ, fnc_num, + addr, &data, 4); + + if (err) + *err = (SDIOH_API_SUCCESS(status) ? 0 : BCME_SDIO_ERROR); + + BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, uint32data = 0x%x\n", __FUNCTION__, + fnc_num, addr, data)); + + return data; +} + +void +bcmsdh_cfg_write_word(void *sdh, uint fnc_num, uint32 addr, uint32 data, int *err) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + + if (!bcmsdh) + bcmsdh = l_bcmsdh; + + ASSERT(bcmsdh->init_success); + + status = sdioh_request_word(bcmsdh->sdioh, SDIOH_CMD_TYPE_NORMAL, SDIOH_WRITE, fnc_num, + addr, &data, 4); + + if (err) + *err = (SDIOH_API_SUCCESS(status) ? 0 : BCME_SDIO_ERROR); + + BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, uint32data = 0x%x\n", __FUNCTION__, fnc_num, + addr, data)); +} + + +int +bcmsdh_cis_read(void *sdh, uint func, uint8 *cis, uint length) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + + uint8 *tmp_buf, *tmp_ptr; + uint8 *ptr; + bool ascii = func & ~0xf; + func &= 0x7; + + if (!bcmsdh) + bcmsdh = l_bcmsdh; + + ASSERT(bcmsdh->init_success); + ASSERT(cis); + ASSERT(length <= SBSDIO_CIS_SIZE_LIMIT); + + status = sdioh_cis_read(bcmsdh->sdioh, func, cis, length); + + if (ascii) { + /* Move binary bits to tmp and format them into the provided buffer. */ + if ((tmp_buf = (uint8 *)MALLOC(bcmsdh->osh, length)) == NULL) { + BCMSDH_ERROR(("%s: out of memory\n", __FUNCTION__)); + return BCME_NOMEM; + } + bcopy(cis, tmp_buf, length); + for (tmp_ptr = tmp_buf, ptr = cis; ptr < (cis + length - 4); tmp_ptr++) { + ptr += snprintf((char*)ptr, (cis + length - ptr - 4), + "%.2x ", *tmp_ptr & 0xff); + if ((((tmp_ptr - tmp_buf) + 1) & 0xf) == 0) + ptr += snprintf((char *)ptr, (cis + length - ptr -4), "\n"); + } + MFREE(bcmsdh->osh, tmp_buf, length); + } + + return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR); +} + + +int +bcmsdhsdio_set_sbaddr_window(void *sdh, uint32 address, bool force_set) +{ + int err = 0; + uint bar0 = address & ~SBSDIO_SB_OFT_ADDR_MASK; + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + + if (bar0 != bcmsdh->sbwad || force_set) { + bcmsdh_cfg_write(bcmsdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW, + (address >> 8) & SBSDIO_SBADDRLOW_MASK, &err); + if (!err) + bcmsdh_cfg_write(bcmsdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRMID, + (address >> 16) & SBSDIO_SBADDRMID_MASK, &err); + if (!err) + bcmsdh_cfg_write(bcmsdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRHIGH, + (address >> 24) & SBSDIO_SBADDRHIGH_MASK, &err); + + if (!err) + bcmsdh->sbwad = bar0; + else + /* invalidate cached window var */ + bcmsdh->sbwad = 0; + + } + + return err; +} + +uint32 +bcmsdh_reg_read(void *sdh, uint32 addr, uint size) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + uint32 word = 0; + + BCMSDH_INFO(("%s:fun = 1, addr = 0x%x, ", __FUNCTION__, addr)); + + if (!bcmsdh) + bcmsdh = l_bcmsdh; + + ASSERT(bcmsdh->init_success); + + if (bcmsdhsdio_set_sbaddr_window(bcmsdh, addr, FALSE)) + return 0xFFFFFFFF; + + addr &= SBSDIO_SB_OFT_ADDR_MASK; + if (size == 4) + addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; + + status = sdioh_request_word(bcmsdh->sdioh, SDIOH_CMD_TYPE_NORMAL, + SDIOH_READ, SDIO_FUNC_1, addr, &word, size); + + bcmsdh->regfail = !(SDIOH_API_SUCCESS(status)); + + BCMSDH_INFO(("uint32data = 0x%x\n", word)); + + /* if ok, return appropriately masked word */ + if (SDIOH_API_SUCCESS(status)) { + switch (size) { + case sizeof(uint8): + return (word & 0xff); + case sizeof(uint16): + return (word & 0xffff); + case sizeof(uint32): + return word; + default: + bcmsdh->regfail = TRUE; + + } + } + + /* otherwise, bad sdio access or invalid size */ + BCMSDH_ERROR(("%s: error reading addr 0x%04x size %d\n", __FUNCTION__, addr, size)); + return 0xFFFFFFFF; +} + +uint32 +bcmsdh_reg_write(void *sdh, uint32 addr, uint size, uint32 data) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + int err = 0; + + BCMSDH_INFO(("%s:fun = 1, addr = 0x%x, uint%ddata = 0x%x\n", + __FUNCTION__, addr, size*8, data)); + + if (!bcmsdh) + bcmsdh = l_bcmsdh; + + ASSERT(bcmsdh->init_success); + + if ((err = bcmsdhsdio_set_sbaddr_window(bcmsdh, addr, FALSE))) + return err; + + addr &= SBSDIO_SB_OFT_ADDR_MASK; + if (size == 4) + addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; + status = sdioh_request_word(bcmsdh->sdioh, SDIOH_CMD_TYPE_NORMAL, SDIOH_WRITE, SDIO_FUNC_1, + addr, &data, size); + bcmsdh->regfail = !(SDIOH_API_SUCCESS(status)); + + if (SDIOH_API_SUCCESS(status)) + return 0; + + BCMSDH_ERROR(("%s: error writing 0x%08x to addr 0x%04x size %d\n", + __FUNCTION__, data, addr, size)); + return 0xFFFFFFFF; +} + +bool +bcmsdh_regfail(void *sdh) +{ + return ((bcmsdh_info_t *)sdh)->regfail; +} + +int +bcmsdh_recv_buf(void *sdh, uint32 addr, uint fn, uint flags, + uint8 *buf, uint nbytes, void *pkt, + bcmsdh_cmplt_fn_t complete_fn, void *handle) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + uint incr_fix; + uint width; + int err = 0; + + ASSERT(bcmsdh); + ASSERT(bcmsdh->init_success); + + BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, size = %d\n", + __FUNCTION__, fn, addr, nbytes)); + + /* Async not implemented yet */ + ASSERT(!(flags & SDIO_REQ_ASYNC)); + if (flags & SDIO_REQ_ASYNC) + return BCME_UNSUPPORTED; + + if ((err = bcmsdhsdio_set_sbaddr_window(bcmsdh, addr, FALSE))) + return err; + + addr &= SBSDIO_SB_OFT_ADDR_MASK; + + incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC; + width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; + if (width == 4) + addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; + + status = sdioh_request_buffer(bcmsdh->sdioh, SDIOH_DATA_PIO, incr_fix, + SDIOH_READ, fn, addr, width, nbytes, buf, pkt); + + return (SDIOH_API_SUCCESS(status) ? 0 : BCME_SDIO_ERROR); +} + +int +bcmsdh_send_buf(void *sdh, uint32 addr, uint fn, uint flags, + uint8 *buf, uint nbytes, void *pkt, + bcmsdh_cmplt_fn_t complete_fn, void *handle) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + uint incr_fix; + uint width; + int err = 0; + + ASSERT(bcmsdh); + ASSERT(bcmsdh->init_success); + + BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, size = %d\n", + __FUNCTION__, fn, addr, nbytes)); + + /* Async not implemented yet */ + ASSERT(!(flags & SDIO_REQ_ASYNC)); + if (flags & SDIO_REQ_ASYNC) + return BCME_UNSUPPORTED; + + if ((err = bcmsdhsdio_set_sbaddr_window(bcmsdh, addr, FALSE))) + return err; + + addr &= SBSDIO_SB_OFT_ADDR_MASK; + + incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC; + width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; + if (width == 4) + addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; + + status = sdioh_request_buffer(bcmsdh->sdioh, SDIOH_DATA_PIO, incr_fix, + SDIOH_WRITE, fn, addr, width, nbytes, buf, pkt); + + return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR); +} + +int +bcmsdh_rwdata(void *sdh, uint rw, uint32 addr, uint8 *buf, uint nbytes) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + + ASSERT(bcmsdh); + ASSERT(bcmsdh->init_success); + ASSERT((addr & SBSDIO_SBWINDOW_MASK) == 0); + + addr &= SBSDIO_SB_OFT_ADDR_MASK; + addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; + + status = sdioh_request_buffer(bcmsdh->sdioh, SDIOH_DATA_PIO, SDIOH_DATA_INC, + (rw ? SDIOH_WRITE : SDIOH_READ), SDIO_FUNC_1, + addr, 4, nbytes, buf, NULL); + + return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR); +} + +int +bcmsdh_abort(void *sdh, uint fn) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + + return sdioh_abort(bcmsdh->sdioh, fn); +} + +int +bcmsdh_start(void *sdh, int stage) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + + return sdioh_start(bcmsdh->sdioh, stage); +} + +int +bcmsdh_stop(void *sdh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + + return sdioh_stop(bcmsdh->sdioh); +} + +int +bcmsdh_waitlockfree(void *sdh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + if (!bcmsdh) + bcmsdh = l_bcmsdh; + + return sdioh_waitlockfree(bcmsdh->sdioh); +} + + +int +bcmsdh_query_device(void *sdh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + bcmsdh->vendevid = (VENDOR_BROADCOM << 16) | 0; + return (bcmsdh->vendevid); +} + +uint +bcmsdh_query_iofnum(void *sdh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + + if (!bcmsdh) + bcmsdh = l_bcmsdh; + + return (sdioh_query_iofnum(bcmsdh->sdioh)); +} + +int +bcmsdh_reset(bcmsdh_info_t *sdh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + + return sdioh_sdio_reset(bcmsdh->sdioh); +} + +void *bcmsdh_get_sdioh(bcmsdh_info_t *sdh) +{ + ASSERT(sdh); + return sdh->sdioh; +} + +/* Function to pass device-status bits to DHD. */ +uint32 +bcmsdh_get_dstatus(void *sdh) +{ + return 0; +} +uint32 +bcmsdh_cur_sbwad(void *sdh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + + if (!bcmsdh) + bcmsdh = l_bcmsdh; + + return (bcmsdh->sbwad); +} + +void +bcmsdh_chipinfo(void *sdh, uint32 chip, uint32 chiprev) +{ + return; +} + + +int +bcmsdh_sleep(void *sdh, bool enab) +{ +#ifdef SDIOH_SLEEP_ENABLED + bcmsdh_info_t *p = (bcmsdh_info_t *)sdh; + sdioh_info_t *sd = (sdioh_info_t *)(p->sdioh); + + return sdioh_sleep(sd, enab); +#else + return BCME_UNSUPPORTED; +#endif +} + +int +bcmsdh_gpio_init(void *sdh) +{ + bcmsdh_info_t *p = (bcmsdh_info_t *)sdh; + sdioh_info_t *sd = (sdioh_info_t *)(p->sdioh); + + return sdioh_gpio_init(sd); +} + +bool +bcmsdh_gpioin(void *sdh, uint32 gpio) +{ + bcmsdh_info_t *p = (bcmsdh_info_t *)sdh; + sdioh_info_t *sd = (sdioh_info_t *)(p->sdioh); + + return sdioh_gpioin(sd, gpio); +} + +int +bcmsdh_gpioouten(void *sdh, uint32 gpio) +{ + bcmsdh_info_t *p = (bcmsdh_info_t *)sdh; + sdioh_info_t *sd = (sdioh_info_t *)(p->sdioh); + + return sdioh_gpioouten(sd, gpio); +} + +int +bcmsdh_gpioout(void *sdh, uint32 gpio, bool enab) +{ + bcmsdh_info_t *p = (bcmsdh_info_t *)sdh; + sdioh_info_t *sd = (sdioh_info_t *)(p->sdioh); + + return sdioh_gpioout(sd, gpio, enab); +} + +#ifdef BCMSDIOH_TXGLOM +void +bcmsdh_glom_post(void *sdh, uint8 *frame, uint len) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + sdioh_glom_post(bcmsdh->sdioh, frame, len); +} + +void +bcmsdh_glom_clear(void *sdh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + sdioh_glom_clear(bcmsdh->sdioh); +} + +uint +bcmsdh_set_mode(void *sdh, uint mode) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + return (sdioh_set_mode(bcmsdh->sdioh, mode)); +} + +bool +bcmsdh_glom_enabled(void) +{ + return (sdioh_glom_enabled()); +} +#endif /* BCMSDIOH_TXGLOM */ diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_linux.c b/drivers/net/wireless/bcmdhd/bcmsdh_linux.c new file mode 100644 index 00000000..96a126ec --- /dev/null +++ b/drivers/net/wireless/bcmdhd/bcmsdh_linux.c @@ -0,0 +1,750 @@ +/* + * SDIO access interface for drivers - linux specific (pci only) + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmsdh_linux.c 347638 2012-07-27 11:39:03Z $ + */ + +/** + * @file bcmsdh_linux.c + */ + +#define __UNDEF_NO_VERSION__ + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#if defined(OOB_INTR_ONLY) +#include +extern void dhdsdio_isr(void * args); +#include +#include +#include +#endif /* defined(OOB_INTR_ONLY) */ + + +/** + * SDIO Host Controller info + */ +typedef struct bcmsdh_hc bcmsdh_hc_t; + +struct bcmsdh_hc { + bcmsdh_hc_t *next; +#ifdef BCMPLATFORM_BUS + struct device *dev; /* platform device handle */ +#else + struct pci_dev *dev; /* pci device handle */ +#endif /* BCMPLATFORM_BUS */ + osl_t *osh; + void *regs; /* SDIO Host Controller address */ + bcmsdh_info_t *sdh; /* SDIO Host Controller handle */ + void *ch; + unsigned int oob_irq; + unsigned long oob_flags; /* OOB Host specifiction as edge and etc */ + bool oob_irq_registered; + bool oob_irq_enable_flag; +#if defined(OOB_INTR_ONLY) + spinlock_t irq_lock; +#endif /* defined(OOB_INTR_ONLY) */ +}; +static bcmsdh_hc_t *sdhcinfo = NULL; + +/* driver info, initialized when bcmsdh_register is called */ +static bcmsdh_driver_t drvinfo = {NULL, NULL}; + +/* debugging macros */ +#define SDLX_MSG(x) + +/** + * Checks to see if vendor and device IDs match a supported SDIO Host Controller. + */ +bool +bcmsdh_chipmatch(uint16 vendor, uint16 device) +{ + /* Add other vendors and devices as required */ + +#ifdef BCMSDIOH_STD + /* Check for Arasan host controller */ + if (vendor == VENDOR_SI_IMAGE) { + return (TRUE); + } + /* Check for BRCM 27XX Standard host controller */ + if (device == BCM27XX_SDIOH_ID && vendor == VENDOR_BROADCOM) { + return (TRUE); + } + /* Check for BRCM Standard host controller */ + if (device == SDIOH_FPGA_ID && vendor == VENDOR_BROADCOM) { + return (TRUE); + } + /* Check for TI PCIxx21 Standard host controller */ + if (device == PCIXX21_SDIOH_ID && vendor == VENDOR_TI) { + return (TRUE); + } + if (device == PCIXX21_SDIOH0_ID && vendor == VENDOR_TI) { + return (TRUE); + } + /* Ricoh R5C822 Standard SDIO Host */ + if (device == R5C822_SDIOH_ID && vendor == VENDOR_RICOH) { + return (TRUE); + } + /* JMicron Standard SDIO Host */ + if (device == JMICRON_SDIOH_ID && vendor == VENDOR_JMICRON) { + return (TRUE); + } + +#endif /* BCMSDIOH_STD */ +#ifdef BCMSDIOH_SPI + /* This is the PciSpiHost. */ + if (device == SPIH_FPGA_ID && vendor == VENDOR_BROADCOM) { + printf("Found PCI SPI Host Controller\n"); + return (TRUE); + } + +#endif /* BCMSDIOH_SPI */ + + return (FALSE); +} + +#if defined(BCMPLATFORM_BUS) +#if defined(BCMLXSDMMC) +/* forward declarations */ +int bcmsdh_probe(struct device *dev); +int bcmsdh_remove(struct device *dev); + +EXPORT_SYMBOL(bcmsdh_probe); +EXPORT_SYMBOL(bcmsdh_remove); + +#else +/* forward declarations */ +static int __devinit bcmsdh_probe(struct device *dev); +static int __devexit bcmsdh_remove(struct device *dev); +#endif /* defined(BCMLXSDMMC) */ + +#if !defined(BCMLXSDMMC) +static +#endif /* !defined(BCMLXSDMMC) */ +int bcmsdh_probe(struct device *dev) +{ + osl_t *osh = NULL; + bcmsdh_hc_t *sdhc = NULL; + ulong regs = 0; + bcmsdh_info_t *sdh = NULL; +#if !defined(BCMLXSDMMC) && defined(BCMPLATFORM_BUS) + struct platform_device *pdev; + struct resource *r; +#endif /* !defined(BCMLXSDMMC) && defined(BCMPLATFORM_BUS) */ + int irq = 0; + uint32 vendevid; + unsigned long irq_flags = 0; + +#if !defined(BCMLXSDMMC) && defined(BCMPLATFORM_BUS) + pdev = to_platform_device(dev); + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (!r || irq == NO_IRQ) + return -ENXIO; +#endif /* !defined(BCMLXSDMMC) && defined(BCMPLATFORM_BUS) */ + +#if defined(OOB_INTR_ONLY) +#ifdef HW_OOB + irq_flags = + IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE; +#else + irq_flags = IRQF_TRIGGER_FALLING; +#endif /* HW_OOB */ + + /* Get customer specific OOB IRQ parametres: IRQ number as IRQ type */ + irq = dhd_customer_oob_irq_map(&irq_flags); + if (irq < 0) { + SDLX_MSG(("%s: Host irq is not defined\n", __FUNCTION__)); + return 1; + } +#endif /* defined(OOB_INTR_ONLY) */ + /* allocate SDIO Host Controller state info */ + if (!(osh = osl_attach(dev, PCI_BUS, FALSE))) { + SDLX_MSG(("%s: osl_attach failed\n", __FUNCTION__)); + goto err; + } + if (!(sdhc = MALLOC(osh, sizeof(bcmsdh_hc_t)))) { + SDLX_MSG(("%s: out of memory, allocated %d bytes\n", + __FUNCTION__, + MALLOCED(osh))); + goto err; + } + bzero(sdhc, sizeof(bcmsdh_hc_t)); + sdhc->osh = osh; + + sdhc->dev = (void *)dev; + +#if defined(BCMLXSDMMC) + if (!(sdh = bcmsdh_attach(osh, (void *)0, + (void **)®s, irq))) { + SDLX_MSG(("%s: bcmsdh_attach failed\n", __FUNCTION__)); + goto err; + } +#else + if (!(sdh = bcmsdh_attach(osh, (void *)r->start, + (void **)®s, irq))) { + SDLX_MSG(("%s: bcmsdh_attach failed\n", __FUNCTION__)); + goto err; + } +#endif /* defined(BCMLXSDMMC) */ + sdhc->sdh = sdh; + sdhc->oob_irq = irq; + sdhc->oob_flags = irq_flags; + sdhc->oob_irq_registered = FALSE; /* to make sure.. */ + sdhc->oob_irq_enable_flag = FALSE; +#if defined(OOB_INTR_ONLY) + spin_lock_init(&sdhc->irq_lock); +#endif /* defined(BCMLXSDMMC) */ + + /* chain SDIO Host Controller info together */ + sdhc->next = sdhcinfo; + sdhcinfo = sdhc; + + /* Read the vendor/device ID from the CIS */ + vendevid = bcmsdh_query_device(sdh); + /* try to attach to the target device */ + if (!(sdhc->ch = drvinfo.attach((vendevid >> 16), + (vendevid & 0xFFFF), 0, 0, 0, 0, + (void *)regs, NULL, sdh))) { + SDLX_MSG(("%s: device attach failed\n", __FUNCTION__)); + goto err; + } + + return 0; + + /* error handling */ +err: + if (sdhc) { + if (sdhc->sdh) + bcmsdh_detach(sdhc->osh, sdhc->sdh); + MFREE(osh, sdhc, sizeof(bcmsdh_hc_t)); + } + if (osh) + osl_detach(osh); + return -ENODEV; +} + +#if !defined(BCMLXSDMMC) +static +#endif /* !defined(BCMLXSDMMC) */ +int bcmsdh_remove(struct device *dev) +{ + bcmsdh_hc_t *sdhc, *prev; + osl_t *osh; + + sdhc = sdhcinfo; + drvinfo.detach(sdhc->ch); + bcmsdh_detach(sdhc->osh, sdhc->sdh); + + /* find the SDIO Host Controller state for this pdev and take it out from the list */ + for (sdhc = sdhcinfo, prev = NULL; sdhc; sdhc = sdhc->next) { + if (sdhc->dev == (void *)dev) { + if (prev) + prev->next = sdhc->next; + else + sdhcinfo = NULL; + break; + } + prev = sdhc; + } + if (!sdhc) { + SDLX_MSG(("%s: failed\n", __FUNCTION__)); + return 0; + } + + /* release SDIO Host Controller info */ + osh = sdhc->osh; + MFREE(osh, sdhc, sizeof(bcmsdh_hc_t)); + osl_detach(osh); + +#if !defined(BCMLXSDMMC) || defined(OOB_INTR_ONLY) + dev_set_drvdata(dev, NULL); +#endif /* !defined(BCMLXSDMMC) || defined(OOB_INTR_ONLY) */ + + return 0; +} + +#else /* BCMPLATFORM_BUS */ + +#if !defined(BCMLXSDMMC) +/* forward declarations for PCI probe and remove functions. */ +static int __devinit bcmsdh_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent); +static void __devexit bcmsdh_pci_remove(struct pci_dev *pdev); + +/** + * pci id table + */ +static struct pci_device_id bcmsdh_pci_devid[] __devinitdata = { + { vendor: PCI_ANY_ID, + device: PCI_ANY_ID, + subvendor: PCI_ANY_ID, + subdevice: PCI_ANY_ID, + class: 0, + class_mask: 0, + driver_data: 0, + }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, bcmsdh_pci_devid); + +/** + * SDIO Host Controller pci driver info + */ +static struct pci_driver bcmsdh_pci_driver = { + node: {}, + name: "bcmsdh", + id_table: bcmsdh_pci_devid, + probe: bcmsdh_pci_probe, + remove: bcmsdh_pci_remove, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) + save_state: NULL, +#endif + suspend: NULL, + resume: NULL, + }; + + +extern uint sd_pci_slot; /* Force detection to a particular PCI */ + /* slot only . Allows for having multiple */ + /* WL devices at once in a PC */ + /* Only one instance of dhd will be */ + /* usable at a time */ + /* Upper word is bus number, */ + /* lower word is slot number */ + /* Default value of 0xffffffff turns this */ + /* off */ +module_param(sd_pci_slot, uint, 0); + + +/** + * Detect supported SDIO Host Controller and attach if found. + * + * Determine if the device described by pdev is a supported SDIO Host + * Controller. If so, attach to it and attach to the target device. + */ +static int __devinit +bcmsdh_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + osl_t *osh = NULL; + bcmsdh_hc_t *sdhc = NULL; + ulong regs; + bcmsdh_info_t *sdh = NULL; + int rc; + + if (sd_pci_slot != 0xFFFFffff) { + if (pdev->bus->number != (sd_pci_slot>>16) || + PCI_SLOT(pdev->devfn) != (sd_pci_slot&0xffff)) { + SDLX_MSG(("%s: %s: bus %X, slot %X, vend %X, dev %X\n", + __FUNCTION__, + bcmsdh_chipmatch(pdev->vendor, pdev->device) + ?"Found compatible SDIOHC" + :"Probing unknown device", + pdev->bus->number, PCI_SLOT(pdev->devfn), pdev->vendor, + pdev->device)); + return -ENODEV; + } + SDLX_MSG(("%s: %s: bus %X, slot %X, vendor %X, device %X (good PCI location)\n", + __FUNCTION__, + bcmsdh_chipmatch(pdev->vendor, pdev->device) + ?"Using compatible SDIOHC" + :"WARNING, forced use of unkown device", + pdev->bus->number, PCI_SLOT(pdev->devfn), pdev->vendor, pdev->device)); + } + + if ((pdev->vendor == VENDOR_TI) && ((pdev->device == PCIXX21_FLASHMEDIA_ID) || + (pdev->device == PCIXX21_FLASHMEDIA0_ID))) { + uint32 config_reg; + + SDLX_MSG(("%s: Disabling TI FlashMedia Controller.\n", __FUNCTION__)); + if (!(osh = osl_attach(pdev, PCI_BUS, FALSE))) { + SDLX_MSG(("%s: osl_attach failed\n", __FUNCTION__)); + goto err; + } + + config_reg = OSL_PCI_READ_CONFIG(osh, 0x4c, 4); + + /* + * Set MMC_SD_DIS bit in FlashMedia Controller. + * Disbling the SD/MMC Controller in the FlashMedia Controller + * allows the Standard SD Host Controller to take over control + * of the SD Slot. + */ + config_reg |= 0x02; + OSL_PCI_WRITE_CONFIG(osh, 0x4c, 4, config_reg); + osl_detach(osh); + } + /* match this pci device with what we support */ + /* we can't solely rely on this to believe it is our SDIO Host Controller! */ + if (!bcmsdh_chipmatch(pdev->vendor, pdev->device)) { + return -ENODEV; + } + + /* this is a pci device we might support */ + SDLX_MSG(("%s: Found possible SDIO Host Controller: bus %d slot %d func %d irq %d\n", + __FUNCTION__, + pdev->bus->number, PCI_SLOT(pdev->devfn), + PCI_FUNC(pdev->devfn), pdev->irq)); + + /* use bcmsdh_query_device() to get the vendor ID of the target device so + * it will eventually appear in the Broadcom string on the console + */ + + /* allocate SDIO Host Controller state info */ + if (!(osh = osl_attach(pdev, PCI_BUS, FALSE))) { + SDLX_MSG(("%s: osl_attach failed\n", __FUNCTION__)); + goto err; + } + if (!(sdhc = MALLOC(osh, sizeof(bcmsdh_hc_t)))) { + SDLX_MSG(("%s: out of memory, allocated %d bytes\n", + __FUNCTION__, + MALLOCED(osh))); + goto err; + } + bzero(sdhc, sizeof(bcmsdh_hc_t)); + sdhc->osh = osh; + + sdhc->dev = pdev; + + /* map to address where host can access */ + pci_set_master(pdev); + rc = pci_enable_device(pdev); + if (rc) { + SDLX_MSG(("%s: Cannot enable PCI device\n", __FUNCTION__)); + goto err; + } + if (!(sdh = bcmsdh_attach(osh, (void *)(uintptr)pci_resource_start(pdev, 0), + (void **)®s, pdev->irq))) { + SDLX_MSG(("%s: bcmsdh_attach failed\n", __FUNCTION__)); + goto err; + } + + sdhc->sdh = sdh; + + /* try to attach to the target device */ + if (!(sdhc->ch = drvinfo.attach(VENDOR_BROADCOM, /* pdev->vendor, */ + bcmsdh_query_device(sdh) & 0xFFFF, 0, 0, 0, 0, + (void *)regs, NULL, sdh))) { + SDLX_MSG(("%s: device attach failed\n", __FUNCTION__)); + goto err; + } + + /* chain SDIO Host Controller info together */ + sdhc->next = sdhcinfo; + sdhcinfo = sdhc; + + return 0; + + /* error handling */ +err: + if (sdhc) { + if (sdhc->sdh) + bcmsdh_detach(sdhc->osh, sdhc->sdh); + MFREE(osh, sdhc, sizeof(bcmsdh_hc_t)); + } + if (osh) + osl_detach(osh); + return -ENODEV; +} + + +/** + * Detach from target devices and SDIO Host Controller + */ +static void __devexit +bcmsdh_pci_remove(struct pci_dev *pdev) +{ + bcmsdh_hc_t *sdhc, *prev; + osl_t *osh; + + /* find the SDIO Host Controller state for this pdev and take it out from the list */ + for (sdhc = sdhcinfo, prev = NULL; sdhc; sdhc = sdhc->next) { + if (sdhc->dev == pdev) { + if (prev) + prev->next = sdhc->next; + else + sdhcinfo = NULL; + break; + } + prev = sdhc; + } + if (!sdhc) + return; + + drvinfo.detach(sdhc->ch); + + bcmsdh_detach(sdhc->osh, sdhc->sdh); + + /* release SDIO Host Controller info */ + osh = sdhc->osh; + MFREE(osh, sdhc, sizeof(bcmsdh_hc_t)); + osl_detach(osh); +} +#endif /* BCMLXSDMMC */ +#endif /* BCMPLATFORM_BUS */ + +extern int sdio_function_init(void); + +extern int sdio_func_reg_notify(void* semaphore); +extern void sdio_func_unreg_notify(void); + +#if defined(BCMLXSDMMC) +int bcmsdh_reg_sdio_notify(void* semaphore) +{ + return sdio_func_reg_notify(semaphore); +} + +void bcmsdh_unreg_sdio_notify(void) +{ + sdio_func_unreg_notify(); +} +#endif /* defined(BCMLXSDMMC) */ + +int +bcmsdh_register(bcmsdh_driver_t *driver) +{ + int error = 0; + + drvinfo = *driver; + +#if defined(BCMPLATFORM_BUS) + SDLX_MSG(("Linux Kernel SDIO/MMC Driver\n")); + error = sdio_function_init(); + return error; +#endif /* defined(BCMPLATFORM_BUS) */ + +#if !defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC) +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) + if (!(error = pci_module_init(&bcmsdh_pci_driver))) + return 0; +#else + if (!(error = pci_register_driver(&bcmsdh_pci_driver))) + return 0; +#endif + + SDLX_MSG(("%s: pci_module_init failed 0x%x\n", __FUNCTION__, error)); +#endif /* BCMPLATFORM_BUS */ + + return error; +} + +extern void sdio_function_cleanup(void); + +void +bcmsdh_unregister(void) +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) + if (bcmsdh_pci_driver.node.next) +#endif + +#if defined(BCMLXSDMMC) + sdio_function_cleanup(); +#endif /* BCMLXSDMMC */ + +#if !defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC) + pci_unregister_driver(&bcmsdh_pci_driver); +#endif /* BCMPLATFORM_BUS */ +} + +#if defined(OOB_INTR_ONLY) +void bcmsdh_oob_intr_set(bool enable) +{ + static bool curstate = 1; + unsigned long flags; + + spin_lock_irqsave(&sdhcinfo->irq_lock, flags); + if (curstate != enable) { + if (enable) + enable_irq(sdhcinfo->oob_irq); + else + disable_irq_nosync(sdhcinfo->oob_irq); + curstate = enable; + } + spin_unlock_irqrestore(&sdhcinfo->irq_lock, flags); +} + +static irqreturn_t wlan_oob_irq(int irq, void *dev_id) +{ + dhd_pub_t *dhdp; + + dhdp = (dhd_pub_t *)dev_get_drvdata(sdhcinfo->dev); + + bcmsdh_oob_intr_set(0); + + if (dhdp == NULL) { + SDLX_MSG(("Out of band GPIO interrupt fired way too early\n")); + return IRQ_HANDLED; + } + + dhdsdio_isr((void *)dhdp->bus); + + return IRQ_HANDLED; +} + +int bcmsdh_register_oob_intr(void * dhdp) +{ + int error = 0; + + SDLX_MSG(("%s Enter \n", __FUNCTION__)); + + /* IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE; */ + + dev_set_drvdata(sdhcinfo->dev, dhdp); + + if (!sdhcinfo->oob_irq_registered) { + SDLX_MSG(("%s IRQ=%d Type=%X \n", __FUNCTION__, + (int)sdhcinfo->oob_irq, (int)sdhcinfo->oob_flags)); + /* Refer to customer Host IRQ docs about proper irqflags definition */ + error = request_irq(sdhcinfo->oob_irq, wlan_oob_irq, sdhcinfo->oob_flags, + "bcmsdh_sdmmc", NULL); + if (error) + return -ENODEV; + + error = enable_irq_wake(sdhcinfo->oob_irq); + if (error) + SDLX_MSG(("%s enable_irq_wake error=%d \n", __FUNCTION__, error)); + sdhcinfo->oob_irq_registered = TRUE; + sdhcinfo->oob_irq_enable_flag = TRUE; + } + + return 0; +} + +void bcmsdh_set_irq(int flag) +{ + if (sdhcinfo->oob_irq_registered && sdhcinfo->oob_irq_enable_flag != flag) { + SDLX_MSG(("%s Flag = %d", __FUNCTION__, flag)); + sdhcinfo->oob_irq_enable_flag = flag; + if (flag) { + enable_irq(sdhcinfo->oob_irq); + enable_irq_wake(sdhcinfo->oob_irq); + } else { + disable_irq_wake(sdhcinfo->oob_irq); + disable_irq(sdhcinfo->oob_irq); + } + } +} + +void bcmsdh_unregister_oob_intr(void) +{ + SDLX_MSG(("%s: Enter\n", __FUNCTION__)); + + if (sdhcinfo->oob_irq_registered == TRUE) { + bcmsdh_set_irq(FALSE); + free_irq(sdhcinfo->oob_irq, NULL); + sdhcinfo->oob_irq_registered = FALSE; + } +} +#endif /* defined(OOB_INTR_ONLY) */ + +#if defined(BCMLXSDMMC) +void *bcmsdh_get_drvdata(void) +{ + if (!sdhcinfo) + return NULL; + return dev_get_drvdata(sdhcinfo->dev); +} +#endif + +/* Module parameters specific to each host-controller driver */ + +extern uint sd_msglevel; /* Debug message level */ +module_param(sd_msglevel, uint, 0); + +extern uint sd_power; /* 0 = SD Power OFF, 1 = SD Power ON. */ +module_param(sd_power, uint, 0); + +extern uint sd_clock; /* SD Clock Control, 0 = SD Clock OFF, 1 = SD Clock ON */ +module_param(sd_clock, uint, 0); + +extern uint sd_divisor; /* Divisor (-1 means external clock) */ +module_param(sd_divisor, uint, 0); + +extern uint sd_sdmode; /* Default is SD4, 0=SPI, 1=SD1, 2=SD4 */ +module_param(sd_sdmode, uint, 0); + +extern uint sd_hiok; /* Ok to use hi-speed mode */ +module_param(sd_hiok, uint, 0); + +extern uint sd_f2_blocksize; +module_param(sd_f2_blocksize, int, 0); + +#ifdef BCMSDIOH_STD +extern int sd_uhsimode; +module_param(sd_uhsimode, int, 0); +#endif + +#ifdef BCMSDIOH_TXGLOM +extern uint sd_txglom; +module_param(sd_txglom, uint, 0); +#endif + +#ifdef BCMSDH_MODULE +EXPORT_SYMBOL(bcmsdh_attach); +EXPORT_SYMBOL(bcmsdh_detach); +EXPORT_SYMBOL(bcmsdh_intr_query); +EXPORT_SYMBOL(bcmsdh_intr_enable); +EXPORT_SYMBOL(bcmsdh_intr_disable); +EXPORT_SYMBOL(bcmsdh_intr_reg); +EXPORT_SYMBOL(bcmsdh_intr_dereg); + +#if defined(DHD_DEBUG) +EXPORT_SYMBOL(bcmsdh_intr_pending); +#endif + +EXPORT_SYMBOL(bcmsdh_devremove_reg); +EXPORT_SYMBOL(bcmsdh_cfg_read); +EXPORT_SYMBOL(bcmsdh_cfg_write); +EXPORT_SYMBOL(bcmsdh_cis_read); +EXPORT_SYMBOL(bcmsdh_reg_read); +EXPORT_SYMBOL(bcmsdh_reg_write); +EXPORT_SYMBOL(bcmsdh_regfail); +EXPORT_SYMBOL(bcmsdh_send_buf); +EXPORT_SYMBOL(bcmsdh_recv_buf); + +EXPORT_SYMBOL(bcmsdh_rwdata); +EXPORT_SYMBOL(bcmsdh_abort); +EXPORT_SYMBOL(bcmsdh_query_device); +EXPORT_SYMBOL(bcmsdh_query_iofnum); +EXPORT_SYMBOL(bcmsdh_iovar_op); +EXPORT_SYMBOL(bcmsdh_register); +EXPORT_SYMBOL(bcmsdh_unregister); +EXPORT_SYMBOL(bcmsdh_chipmatch); +EXPORT_SYMBOL(bcmsdh_reset); +EXPORT_SYMBOL(bcmsdh_waitlockfree); + +EXPORT_SYMBOL(bcmsdh_get_dstatus); +EXPORT_SYMBOL(bcmsdh_cfg_read_word); +EXPORT_SYMBOL(bcmsdh_cfg_write_word); +EXPORT_SYMBOL(bcmsdh_cur_sbwad); +EXPORT_SYMBOL(bcmsdh_chipinfo); + +#endif /* BCMSDH_MODULE */ diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c new file mode 100644 index 00000000..046bd028 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c @@ -0,0 +1,1504 @@ +/* + * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmsdh_sdmmc.c 379078 2013-01-16 00:41:36Z $ + */ +#include + +#include +#include +#include +#include +#include /* SDIO Device and Protocol Specs */ +#include /* Standard SDIO Host Controller Specification */ +#include /* bcmsdh to/from specific controller APIs */ +#include /* ioctl/iovars */ + +#include +#include +#include +#include + +#include +#include + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) +#include +extern volatile bool dhd_mmc_suspend; +#endif +#include "bcmsdh_sdmmc.h" + +#ifndef BCMSDH_MODULE +extern int sdio_function_init(void); +extern void sdio_function_cleanup(void); +#endif /* BCMSDH_MODULE */ + +#if !defined(OOB_INTR_ONLY) +static void IRQHandler(struct sdio_func *func); +static void IRQHandlerF2(struct sdio_func *func); +#endif /* !defined(OOB_INTR_ONLY) */ +static int sdioh_sdmmc_get_cisaddr(sdioh_info_t *sd, uint32 regaddr); +extern int sdio_reset_comm(struct mmc_card *card); + +extern PBCMSDH_SDMMC_INSTANCE gInstance; + +#define DEFAULT_SDIO_F2_BLKSIZE 512 +#ifndef CUSTOM_SDIO_F2_BLKSIZE +#define CUSTOM_SDIO_F2_BLKSIZE DEFAULT_SDIO_F2_BLKSIZE +#endif + +uint sd_sdmode = SDIOH_MODE_SD4; /* Use SD4 mode by default */ +uint sd_f2_blocksize = CUSTOM_SDIO_F2_BLKSIZE; +uint sd_divisor = 2; /* Default 48MHz/2 = 24MHz */ + +uint sd_power = 1; /* Default to SD Slot powered ON */ +uint sd_clock = 1; /* Default to SD Clock turned ON */ +uint sd_hiok = FALSE; /* Don't use hi-speed mode by default */ +uint sd_msglevel = 0x01; +uint sd_use_dma = TRUE; +DHD_PM_RESUME_WAIT_INIT(sdioh_request_byte_wait); +DHD_PM_RESUME_WAIT_INIT(sdioh_request_word_wait); +DHD_PM_RESUME_WAIT_INIT(sdioh_request_packet_wait); +DHD_PM_RESUME_WAIT_INIT(sdioh_request_buffer_wait); + +#define DMA_ALIGN_MASK 0x03 +#define MMC_SDIO_ABORT_RETRY_LIMIT 5 + +int sdioh_sdmmc_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data); + +static int +sdioh_sdmmc_card_enablefuncs(sdioh_info_t *sd) +{ + int err_ret; + uint32 fbraddr; + uint8 func; + + sd_trace(("%s\n", __FUNCTION__)); + + /* Get the Card's common CIS address */ + sd->com_cis_ptr = sdioh_sdmmc_get_cisaddr(sd, SDIOD_CCCR_CISPTR_0); + sd->func_cis_ptr[0] = sd->com_cis_ptr; + sd_info(("%s: Card's Common CIS Ptr = 0x%x\n", __FUNCTION__, sd->com_cis_ptr)); + + /* Get the Card's function CIS (for each function) */ + for (fbraddr = SDIOD_FBR_STARTADDR, func = 1; + func <= sd->num_funcs; func++, fbraddr += SDIOD_FBR_SIZE) { + sd->func_cis_ptr[func] = sdioh_sdmmc_get_cisaddr(sd, SDIOD_FBR_CISPTR_0 + fbraddr); + sd_info(("%s: Function %d CIS Ptr = 0x%x\n", + __FUNCTION__, func, sd->func_cis_ptr[func])); + } + + sd->func_cis_ptr[0] = sd->com_cis_ptr; + sd_info(("%s: Card's Common CIS Ptr = 0x%x\n", __FUNCTION__, sd->com_cis_ptr)); + + /* Enable Function 1 */ + sdio_claim_host(gInstance->func[1]); + err_ret = sdio_enable_func(gInstance->func[1]); + sdio_release_host(gInstance->func[1]); + if (err_ret) { + sd_err(("bcmsdh_sdmmc: Failed to enable F1 Err: 0x%08x", err_ret)); + } + + return FALSE; +} + +/* + * Public entry points & extern's + */ +extern sdioh_info_t * +sdioh_attach(osl_t *osh, void *bar0, uint irq) +{ + sdioh_info_t *sd; + int err_ret; + + sd_trace(("%s\n", __FUNCTION__)); + + if (gInstance == NULL) { + sd_err(("%s: SDIO Device not present\n", __FUNCTION__)); + return NULL; + } + + if ((sd = (sdioh_info_t *)MALLOC(osh, sizeof(sdioh_info_t))) == NULL) { + sd_err(("sdioh_attach: out of memory, malloced %d bytes\n", MALLOCED(osh))); + return NULL; + } + bzero((char *)sd, sizeof(sdioh_info_t)); + sd->osh = osh; + if (sdioh_sdmmc_osinit(sd) != 0) { + sd_err(("%s:sdioh_sdmmc_osinit() failed\n", __FUNCTION__)); + MFREE(sd->osh, sd, sizeof(sdioh_info_t)); + return NULL; + } + + sd->num_funcs = 2; + sd->sd_blockmode = TRUE; + sd->use_client_ints = TRUE; + sd->client_block_size[0] = 64; + sd->use_rxchain = FALSE; + + gInstance->sd = sd; + + /* Claim host controller */ + if (gInstance->func[1]) { + sdio_claim_host(gInstance->func[1]); + + sd->client_block_size[1] = 64; + err_ret = sdio_set_block_size(gInstance->func[1], 64); + if (err_ret) { + sd_err(("bcmsdh_sdmmc: Failed to set F1 blocksize\n")); + } + + /* Release host controller F1 */ + sdio_release_host(gInstance->func[1]); + } else { + sd_err(("%s:gInstance->func[1] is null\n", __FUNCTION__)); + MFREE(sd->osh, sd, sizeof(sdioh_info_t)); + return NULL; + } + + if (gInstance->func[2]) { + /* Claim host controller F2 */ + sdio_claim_host(gInstance->func[2]); + + sd->client_block_size[2] = sd_f2_blocksize; + err_ret = sdio_set_block_size(gInstance->func[2], sd_f2_blocksize); + if (err_ret) { + sd_err(("bcmsdh_sdmmc: Failed to set F2 blocksize to %d\n", + sd_f2_blocksize)); + } + + /* Release host controller F2 */ + sdio_release_host(gInstance->func[2]); + } else { + sd_err(("%s:gInstance->func[2] is null\n", __FUNCTION__)); + MFREE(sd->osh, sd, sizeof(sdioh_info_t)); + return NULL; + } + + sdioh_sdmmc_card_enablefuncs(sd); + + sd_trace(("%s: Done\n", __FUNCTION__)); + return sd; +} + + +extern SDIOH_API_RC +sdioh_detach(osl_t *osh, sdioh_info_t *sd) +{ + sd_trace(("%s\n", __FUNCTION__)); + + if (sd) { + + /* Disable Function 2 */ + sdio_claim_host(gInstance->func[2]); + sdio_disable_func(gInstance->func[2]); + sdio_release_host(gInstance->func[2]); + + /* Disable Function 1 */ + if (gInstance->func[1]) { + sdio_claim_host(gInstance->func[1]); + sdio_disable_func(gInstance->func[1]); + sdio_release_host(gInstance->func[1]); + } + + gInstance->func[1] = NULL; + gInstance->func[2] = NULL; + + /* deregister irq */ + sdioh_sdmmc_osfree(sd); + + MFREE(sd->osh, sd, sizeof(sdioh_info_t)); + } + return SDIOH_API_RC_SUCCESS; +} + +#if defined(OOB_INTR_ONLY) && defined(HW_OOB) + +extern SDIOH_API_RC +sdioh_enable_func_intr(void) +{ + uint8 reg; + int err; + + if (gInstance->func[0]) { + sdio_claim_host(gInstance->func[0]); + + reg = sdio_readb(gInstance->func[0], SDIOD_CCCR_INTEN, &err); + if (err) { + sd_err(("%s: error for read SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err)); + sdio_release_host(gInstance->func[0]); + return SDIOH_API_RC_FAIL; + } + + /* Enable F1 and F2 interrupts, clear master enable */ + reg &= ~INTR_CTL_MASTER_EN; + reg |= (INTR_CTL_FUNC1_EN | INTR_CTL_FUNC2_EN); + sdio_writeb(gInstance->func[0], reg, SDIOD_CCCR_INTEN, &err); + sdio_release_host(gInstance->func[0]); + + if (err) { + sd_err(("%s: error for write SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err)); + return SDIOH_API_RC_FAIL; + } + } + + return SDIOH_API_RC_SUCCESS; +} + +extern SDIOH_API_RC +sdioh_disable_func_intr(void) +{ + uint8 reg; + int err; + + if (gInstance->func[0]) { + sdio_claim_host(gInstance->func[0]); + reg = sdio_readb(gInstance->func[0], SDIOD_CCCR_INTEN, &err); + if (err) { + sd_err(("%s: error for read SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err)); + sdio_release_host(gInstance->func[0]); + return SDIOH_API_RC_FAIL; + } + + reg &= ~(INTR_CTL_FUNC1_EN | INTR_CTL_FUNC2_EN); + /* Disable master interrupt with the last function interrupt */ + if (!(reg & 0xFE)) + reg = 0; + sdio_writeb(gInstance->func[0], reg, SDIOD_CCCR_INTEN, &err); + + sdio_release_host(gInstance->func[0]); + if (err) { + sd_err(("%s: error for write SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err)); + return SDIOH_API_RC_FAIL; + } + } + return SDIOH_API_RC_SUCCESS; +} +#endif /* defined(OOB_INTR_ONLY) && defined(HW_OOB) */ + +/* Configure callback to client when we recieve client interrupt */ +extern SDIOH_API_RC +sdioh_interrupt_register(sdioh_info_t *sd, sdioh_cb_fn_t fn, void *argh) +{ + sd_trace(("%s: Entering\n", __FUNCTION__)); + if (fn == NULL) { + sd_err(("%s: interrupt handler is NULL, not registering\n", __FUNCTION__)); + return SDIOH_API_RC_FAIL; + } +#if !defined(OOB_INTR_ONLY) + sd->intr_handler = fn; + sd->intr_handler_arg = argh; + sd->intr_handler_valid = TRUE; + + /* register and unmask irq */ + if (gInstance->func[2]) { + sdio_claim_host(gInstance->func[2]); + sdio_claim_irq(gInstance->func[2], IRQHandlerF2); + sdio_release_host(gInstance->func[2]); + } + + if (gInstance->func[1]) { + sdio_claim_host(gInstance->func[1]); + sdio_claim_irq(gInstance->func[1], IRQHandler); + sdio_release_host(gInstance->func[1]); + } +#elif defined(HW_OOB) + sdioh_enable_func_intr(); +#endif /* !defined(OOB_INTR_ONLY) */ + + return SDIOH_API_RC_SUCCESS; +} + +extern SDIOH_API_RC +sdioh_interrupt_deregister(sdioh_info_t *sd) +{ + sd_trace(("%s: Entering\n", __FUNCTION__)); + +#if !defined(OOB_INTR_ONLY) + if (gInstance->func[1]) { + /* register and unmask irq */ + sdio_claim_host(gInstance->func[1]); + sdio_release_irq(gInstance->func[1]); + sdio_release_host(gInstance->func[1]); + } + + if (gInstance->func[2]) { + /* Claim host controller F2 */ + sdio_claim_host(gInstance->func[2]); + sdio_release_irq(gInstance->func[2]); + /* Release host controller F2 */ + sdio_release_host(gInstance->func[2]); + } + + sd->intr_handler_valid = FALSE; + sd->intr_handler = NULL; + sd->intr_handler_arg = NULL; +#elif defined(HW_OOB) + sdioh_disable_func_intr(); +#endif /* !defined(OOB_INTR_ONLY) */ + return SDIOH_API_RC_SUCCESS; +} + +extern SDIOH_API_RC +sdioh_interrupt_query(sdioh_info_t *sd, bool *onoff) +{ + sd_trace(("%s: Entering\n", __FUNCTION__)); + *onoff = sd->client_intr_enabled; + return SDIOH_API_RC_SUCCESS; +} + +#if defined(DHD_DEBUG) +extern bool +sdioh_interrupt_pending(sdioh_info_t *sd) +{ + return (0); +} +#endif + +uint +sdioh_query_iofnum(sdioh_info_t *sd) +{ + return sd->num_funcs; +} + +/* IOVar table */ +enum { + IOV_MSGLEVEL = 1, + IOV_BLOCKMODE, + IOV_BLOCKSIZE, + IOV_DMA, + IOV_USEINTS, + IOV_NUMINTS, + IOV_NUMLOCALINTS, + IOV_HOSTREG, + IOV_DEVREG, + IOV_DIVISOR, + IOV_SDMODE, + IOV_HISPEED, + IOV_HCIREGS, + IOV_POWER, + IOV_CLOCK, + IOV_RXCHAIN +}; + +const bcm_iovar_t sdioh_iovars[] = { + {"sd_msglevel", IOV_MSGLEVEL, 0, IOVT_UINT32, 0 }, + {"sd_blockmode", IOV_BLOCKMODE, 0, IOVT_BOOL, 0 }, + {"sd_blocksize", IOV_BLOCKSIZE, 0, IOVT_UINT32, 0 }, /* ((fn << 16) | size) */ + {"sd_dma", IOV_DMA, 0, IOVT_BOOL, 0 }, + {"sd_ints", IOV_USEINTS, 0, IOVT_BOOL, 0 }, + {"sd_numints", IOV_NUMINTS, 0, IOVT_UINT32, 0 }, + {"sd_numlocalints", IOV_NUMLOCALINTS, 0, IOVT_UINT32, 0 }, + {"sd_hostreg", IOV_HOSTREG, 0, IOVT_BUFFER, sizeof(sdreg_t) }, + {"sd_devreg", IOV_DEVREG, 0, IOVT_BUFFER, sizeof(sdreg_t) }, + {"sd_divisor", IOV_DIVISOR, 0, IOVT_UINT32, 0 }, + {"sd_power", IOV_POWER, 0, IOVT_UINT32, 0 }, + {"sd_clock", IOV_CLOCK, 0, IOVT_UINT32, 0 }, + {"sd_mode", IOV_SDMODE, 0, IOVT_UINT32, 100}, + {"sd_highspeed", IOV_HISPEED, 0, IOVT_UINT32, 0 }, + {"sd_rxchain", IOV_RXCHAIN, 0, IOVT_BOOL, 0 }, + {NULL, 0, 0, 0, 0 } +}; + +int +sdioh_iovar_op(sdioh_info_t *si, const char *name, + void *params, int plen, void *arg, int len, bool set) +{ + const bcm_iovar_t *vi = NULL; + int bcmerror = 0; + int val_size; + int32 int_val = 0; + bool bool_val; + uint32 actionid; + + ASSERT(name); + ASSERT(len >= 0); + + /* Get must have return space; Set does not take qualifiers */ + ASSERT(set || (arg && len)); + ASSERT(!set || (!params && !plen)); + + sd_trace(("%s: Enter (%s %s)\n", __FUNCTION__, (set ? "set" : "get"), name)); + + if ((vi = bcm_iovar_lookup(sdioh_iovars, name)) == NULL) { + bcmerror = BCME_UNSUPPORTED; + goto exit; + } + + if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, set)) != 0) + goto exit; + + /* Set up params so get and set can share the convenience variables */ + if (params == NULL) { + params = arg; + plen = len; + } + + if (vi->type == IOVT_VOID) + val_size = 0; + else if (vi->type == IOVT_BUFFER) + val_size = len; + else + val_size = sizeof(int); + + if (plen >= (int)sizeof(int_val)) + bcopy(params, &int_val, sizeof(int_val)); + + bool_val = (int_val != 0) ? TRUE : FALSE; + BCM_REFERENCE(bool_val); + + actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid); + switch (actionid) { + case IOV_GVAL(IOV_MSGLEVEL): + int_val = (int32)sd_msglevel; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_MSGLEVEL): + sd_msglevel = int_val; + break; + + case IOV_GVAL(IOV_BLOCKMODE): + int_val = (int32)si->sd_blockmode; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_BLOCKMODE): + si->sd_blockmode = (bool)int_val; + /* Haven't figured out how to make non-block mode with DMA */ + break; + + case IOV_GVAL(IOV_BLOCKSIZE): + if ((uint32)int_val > si->num_funcs) { + bcmerror = BCME_BADARG; + break; + } + int_val = (int32)si->client_block_size[int_val]; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_BLOCKSIZE): + { + uint func = ((uint32)int_val >> 16); + uint blksize = (uint16)int_val; + uint maxsize; + + if (func > si->num_funcs) { + bcmerror = BCME_BADARG; + break; + } + + switch (func) { + case 0: maxsize = 32; break; + case 1: maxsize = BLOCK_SIZE_4318; break; + case 2: maxsize = BLOCK_SIZE_4328; break; + default: maxsize = 0; + } + if (blksize > maxsize) { + bcmerror = BCME_BADARG; + break; + } + if (!blksize) { + blksize = maxsize; + } + + /* Now set it */ + si->client_block_size[func] = blksize; + + break; + } + + case IOV_GVAL(IOV_RXCHAIN): + int_val = (int32)si->use_rxchain; + bcopy(&int_val, arg, val_size); + break; + + case IOV_GVAL(IOV_DMA): + int_val = (int32)si->sd_use_dma; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_DMA): + si->sd_use_dma = (bool)int_val; + break; + + case IOV_GVAL(IOV_USEINTS): + int_val = (int32)si->use_client_ints; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_USEINTS): + si->use_client_ints = (bool)int_val; + if (si->use_client_ints) + si->intmask |= CLIENT_INTR; + else + si->intmask &= ~CLIENT_INTR; + + break; + + case IOV_GVAL(IOV_DIVISOR): + int_val = (uint32)sd_divisor; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_DIVISOR): + sd_divisor = int_val; + break; + + case IOV_GVAL(IOV_POWER): + int_val = (uint32)sd_power; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_POWER): + sd_power = int_val; + break; + + case IOV_GVAL(IOV_CLOCK): + int_val = (uint32)sd_clock; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_CLOCK): + sd_clock = int_val; + break; + + case IOV_GVAL(IOV_SDMODE): + int_val = (uint32)sd_sdmode; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_SDMODE): + sd_sdmode = int_val; + break; + + case IOV_GVAL(IOV_HISPEED): + int_val = (uint32)sd_hiok; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_HISPEED): + sd_hiok = int_val; + break; + + case IOV_GVAL(IOV_NUMINTS): + int_val = (int32)si->intrcount; + bcopy(&int_val, arg, val_size); + break; + + case IOV_GVAL(IOV_NUMLOCALINTS): + int_val = (int32)0; + bcopy(&int_val, arg, val_size); + break; + + case IOV_GVAL(IOV_HOSTREG): + { + sdreg_t *sd_ptr = (sdreg_t *)params; + + if (sd_ptr->offset < SD_SysAddr || sd_ptr->offset > SD_MaxCurCap) { + sd_err(("%s: bad offset 0x%x\n", __FUNCTION__, sd_ptr->offset)); + bcmerror = BCME_BADARG; + break; + } + + sd_trace(("%s: rreg%d at offset %d\n", __FUNCTION__, + (sd_ptr->offset & 1) ? 8 : ((sd_ptr->offset & 2) ? 16 : 32), + sd_ptr->offset)); + if (sd_ptr->offset & 1) + int_val = 8; /* sdioh_sdmmc_rreg8(si, sd_ptr->offset); */ + else if (sd_ptr->offset & 2) + int_val = 16; /* sdioh_sdmmc_rreg16(si, sd_ptr->offset); */ + else + int_val = 32; /* sdioh_sdmmc_rreg(si, sd_ptr->offset); */ + + bcopy(&int_val, arg, sizeof(int_val)); + break; + } + + case IOV_SVAL(IOV_HOSTREG): + { + sdreg_t *sd_ptr = (sdreg_t *)params; + + if (sd_ptr->offset < SD_SysAddr || sd_ptr->offset > SD_MaxCurCap) { + sd_err(("%s: bad offset 0x%x\n", __FUNCTION__, sd_ptr->offset)); + bcmerror = BCME_BADARG; + break; + } + + sd_trace(("%s: wreg%d value 0x%08x at offset %d\n", __FUNCTION__, sd_ptr->value, + (sd_ptr->offset & 1) ? 8 : ((sd_ptr->offset & 2) ? 16 : 32), + sd_ptr->offset)); + break; + } + + case IOV_GVAL(IOV_DEVREG): + { + sdreg_t *sd_ptr = (sdreg_t *)params; + uint8 data = 0; + + if (sdioh_cfg_read(si, sd_ptr->func, sd_ptr->offset, &data)) { + bcmerror = BCME_SDIO_ERROR; + break; + } + + int_val = (int)data; + bcopy(&int_val, arg, sizeof(int_val)); + break; + } + + case IOV_SVAL(IOV_DEVREG): + { + sdreg_t *sd_ptr = (sdreg_t *)params; + uint8 data = (uint8)sd_ptr->value; + + if (sdioh_cfg_write(si, sd_ptr->func, sd_ptr->offset, &data)) { + bcmerror = BCME_SDIO_ERROR; + break; + } + break; + } + + default: + bcmerror = BCME_UNSUPPORTED; + break; + } +exit: + + return bcmerror; +} + +#if defined(OOB_INTR_ONLY) && defined(HW_OOB) + +SDIOH_API_RC +sdioh_enable_hw_oob_intr(sdioh_info_t *sd, bool enable) +{ + SDIOH_API_RC status; + uint8 data; + + if (enable) + data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE | SDIO_SEPINT_ACT_HI; + else + data = SDIO_SEPINT_ACT_HI; /* disable hw oob interrupt */ + + status = sdioh_request_byte(sd, SDIOH_WRITE, 0, SDIOD_CCCR_BRCM_SEPINT, &data); + return status; +} +#endif /* defined(OOB_INTR_ONLY) && defined(HW_OOB) */ + +extern SDIOH_API_RC +sdioh_cfg_read(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data) +{ + SDIOH_API_RC status; + /* No lock needed since sdioh_request_byte does locking */ + status = sdioh_request_byte(sd, SDIOH_READ, fnc_num, addr, data); + return status; +} + +extern SDIOH_API_RC +sdioh_cfg_write(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data) +{ + /* No lock needed since sdioh_request_byte does locking */ + SDIOH_API_RC status; + status = sdioh_request_byte(sd, SDIOH_WRITE, fnc_num, addr, data); + return status; +} + +static int +sdioh_sdmmc_get_cisaddr(sdioh_info_t *sd, uint32 regaddr) +{ + /* read 24 bits and return valid 17 bit addr */ + int i; + uint32 scratch, regdata; + uint8 *ptr = (uint8 *)&scratch; + for (i = 0; i < 3; i++) { + if ((sdioh_sdmmc_card_regread (sd, 0, regaddr, 1, ®data)) != SUCCESS) + sd_err(("%s: Can't read!\n", __FUNCTION__)); + + *ptr++ = (uint8) regdata; + regaddr++; + } + + /* Only the lower 17-bits are valid */ + scratch = ltoh32(scratch); + scratch &= 0x0001FFFF; + return (scratch); +} + +extern SDIOH_API_RC +sdioh_cis_read(sdioh_info_t *sd, uint func, uint8 *cisd, uint32 length) +{ + uint32 count; + int offset; + uint32 foo; + uint8 *cis = cisd; + + sd_trace(("%s: Func = %d\n", __FUNCTION__, func)); + + if (!sd->func_cis_ptr[func]) { + bzero(cis, length); + sd_err(("%s: no func_cis_ptr[%d]\n", __FUNCTION__, func)); + return SDIOH_API_RC_FAIL; + } + + sd_err(("%s: func_cis_ptr[%d]=0x%04x\n", __FUNCTION__, func, sd->func_cis_ptr[func])); + + for (count = 0; count < length; count++) { + offset = sd->func_cis_ptr[func] + count; + if (sdioh_sdmmc_card_regread (sd, 0, offset, 1, &foo) < 0) { + sd_err(("%s: regread failed: Can't read CIS\n", __FUNCTION__)); + return SDIOH_API_RC_FAIL; + } + + *cis = (uint8)(foo & 0xff); + cis++; + } + + return SDIOH_API_RC_SUCCESS; +} + +extern SDIOH_API_RC +sdioh_request_byte(sdioh_info_t *sd, uint rw, uint func, uint regaddr, uint8 *byte) +{ + int err_ret; +#if defined(MMC_SDIO_ABORT) + int sdio_abort_retry = MMC_SDIO_ABORT_RETRY_LIMIT; +#endif + sd_info(("%s: rw=%d, func=%d, addr=0x%05x\n", __FUNCTION__, rw, func, regaddr)); + + DHD_PM_RESUME_WAIT(sdioh_request_byte_wait); + DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL); + if(rw) { /* CMD52 Write */ + if (func == 0) { + /* Can only directly write to some F0 registers. Handle F2 enable + * as a special case. + */ + if (regaddr == SDIOD_CCCR_IOEN) { + if (gInstance->func[2]) { + sdio_claim_host(gInstance->func[2]); + if (*byte & SDIO_FUNC_ENABLE_2) { + /* Enable Function 2 */ + err_ret = sdio_enable_func(gInstance->func[2]); + if (err_ret) { + sd_err(("bcmsdh_sdmmc: enable F2 failed:%d", + err_ret)); + } + } else { + /* Disable Function 2 */ + err_ret = sdio_disable_func(gInstance->func[2]); + if (err_ret) { + sd_err(("bcmsdh_sdmmc: Disab F2 failed:%d", + err_ret)); + } + } + sdio_release_host(gInstance->func[2]); + } + } +#if defined(MMC_SDIO_ABORT) + /* to allow abort command through F1 */ + else if (regaddr == SDIOD_CCCR_IOABORT) { + while (sdio_abort_retry--) { + if (gInstance->func[func]) { + sdio_claim_host(gInstance->func[func]); + /* + * this sdio_f0_writeb() can be replaced with + * another api depending upon MMC driver change. + * As of this time, this is temporaray one + */ + sdio_writeb(gInstance->func[func], + *byte, regaddr, &err_ret); + sdio_release_host(gInstance->func[func]); + } + if (!err_ret) + break; + } + } +#endif /* MMC_SDIO_ABORT */ + else if (regaddr < 0xF0) { + sd_err(("bcmsdh_sdmmc: F0 Wr:0x%02x: write disallowed\n", regaddr)); + } else { + /* Claim host controller, perform F0 write, and release */ + if (gInstance->func[func]) { + sdio_claim_host(gInstance->func[func]); + sdio_f0_writeb(gInstance->func[func], + *byte, regaddr, &err_ret); + sdio_release_host(gInstance->func[func]); + } + } + } else { + /* Claim host controller, perform Fn write, and release */ + if (gInstance->func[func]) { + sdio_claim_host(gInstance->func[func]); + sdio_writeb(gInstance->func[func], *byte, regaddr, &err_ret); + sdio_release_host(gInstance->func[func]); + } + } + } else { /* CMD52 Read */ + /* Claim host controller, perform Fn read, and release */ + if (gInstance->func[func]) { + sdio_claim_host(gInstance->func[func]); + if (func == 0) { + *byte = sdio_f0_readb(gInstance->func[func], regaddr, &err_ret); + } else { + *byte = sdio_readb(gInstance->func[func], regaddr, &err_ret); + } + sdio_release_host(gInstance->func[func]); + } + } + + if (err_ret) { + sd_err(("bcmsdh_sdmmc: Failed to %s byte F%d:@0x%05x=%02x, Err: %d\n", + rw ? "Write" : "Read", func, regaddr, *byte, err_ret)); + } + + return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL); +} + +extern SDIOH_API_RC +sdioh_request_word(sdioh_info_t *sd, uint cmd_type, uint rw, uint func, uint addr, + uint32 *word, uint nbytes) +{ + int err_ret = SDIOH_API_RC_FAIL; +#if defined(MMC_SDIO_ABORT) + int sdio_abort_retry = MMC_SDIO_ABORT_RETRY_LIMIT; +#endif + + if (func == 0) { + sd_err(("%s: Only CMD52 allowed to F0.\n", __FUNCTION__)); + return SDIOH_API_RC_FAIL; + } + + sd_info(("%s: cmd_type=%d, rw=%d, func=%d, addr=0x%05x, nbytes=%d\n", + __FUNCTION__, cmd_type, rw, func, addr, nbytes)); + + DHD_PM_RESUME_WAIT(sdioh_request_word_wait); + DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL); + /* Claim host controller */ + sdio_claim_host(gInstance->func[func]); + + if(rw) { /* CMD53 Write */ + if (nbytes == 4) { + sdio_writel(gInstance->func[func], *word, addr, &err_ret); + } else if (nbytes == 2) { + sdio_writew(gInstance->func[func], (*word & 0xFFFF), addr, &err_ret); + } else { + sd_err(("%s: Invalid nbytes: %d\n", __FUNCTION__, nbytes)); + } + } else { /* CMD52 Read */ + if (nbytes == 4) { + *word = sdio_readl(gInstance->func[func], addr, &err_ret); + } else if (nbytes == 2) { + *word = sdio_readw(gInstance->func[func], addr, &err_ret) & 0xFFFF; + } else { + sd_err(("%s: Invalid nbytes: %d\n", __FUNCTION__, nbytes)); + } + } + + /* Release host controller */ + sdio_release_host(gInstance->func[func]); + + if (err_ret) { +#if defined(MMC_SDIO_ABORT) + /* Any error on CMD53 transaction should abort that function using function 0. */ + while (sdio_abort_retry--) { + if (gInstance->func[0]) { + sdio_claim_host(gInstance->func[0]); + /* + * this sdio_f0_writeb() can be replaced with another api + * depending upon MMC driver change. + * As of this time, this is temporaray one + */ + sdio_writeb(gInstance->func[0], + func, SDIOD_CCCR_IOABORT, &err_ret); + sdio_release_host(gInstance->func[0]); + } + if (!err_ret) + break; + } + if (err_ret) +#endif /* MMC_SDIO_ABORT */ + { + sd_err(("bcmsdh_sdmmc: Failed to %s word, Err: 0x%08x\n", + rw ? "Write" : "Read", err_ret)); + } + } + + return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL); +} + +static SDIOH_API_RC +sdioh_request_packet(sdioh_info_t *sd, uint fix_inc, uint write, uint func, + uint addr, void *pkt) +{ + bool fifo = (fix_inc == SDIOH_DATA_FIX); + uint32 SGCount = 0; + int err_ret = 0; + void *pnext, *pprev; + uint ttl_len, dma_len, lft_len, xfred_len, pkt_len; + uint blk_num; + int blk_size; + struct mmc_request mmc_req; + struct mmc_command mmc_cmd; + struct mmc_data mmc_dat; + + sd_trace(("%s: Enter\n", __FUNCTION__)); + + ASSERT(pkt); + DHD_PM_RESUME_WAIT(sdioh_request_packet_wait); + DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL); + + ttl_len = xfred_len = 0; + /* at least 4 bytes alignment of skb buff is guaranteed */ + for (pnext = pkt; pnext; pnext = PKTNEXT(sd->osh, pnext)) + ttl_len += PKTLEN(sd->osh, pnext); + + blk_size = sd->client_block_size[func]; + if (!sd->use_rxchain || ttl_len <= blk_size) { + blk_num = 0; + dma_len = 0; + } else { + blk_num = ttl_len / blk_size; + dma_len = blk_num * blk_size; + } + lft_len = ttl_len - dma_len; + + sd_trace(("%s: %s %dB to func%d:%08x, %d blks with DMA, %dB leftover\n", + __FUNCTION__, write ? "W" : "R", + ttl_len, func, addr, blk_num, lft_len)); + + if (0 != dma_len) { + memset(&mmc_req, 0, sizeof(struct mmc_request)); + memset(&mmc_cmd, 0, sizeof(struct mmc_command)); + memset(&mmc_dat, 0, sizeof(struct mmc_data)); + + /* Set up DMA descriptors */ + pprev = pkt; + for (pnext = pkt; + pnext && dma_len; + pnext = PKTNEXT(sd->osh, pnext)) { + pkt_len = PKTLEN(sd->osh, pnext); + + if (dma_len > pkt_len) + dma_len -= pkt_len; + else { + pkt_len = xfred_len = dma_len; + dma_len = 0; + pkt = pnext; + } + + sg_set_buf(&sd->sg_list[SGCount++], + (uint8*)PKTDATA(sd->osh, pnext), + pkt_len); + + if (SGCount >= SDIOH_SDMMC_MAX_SG_ENTRIES) { + sd_err(("%s: sg list entries exceed limit\n", + __FUNCTION__)); + return (SDIOH_API_RC_FAIL); + } + } + + mmc_dat.sg = sd->sg_list; + mmc_dat.sg_len = SGCount; + mmc_dat.blksz = blk_size; + mmc_dat.blocks = blk_num; + mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; + + mmc_cmd.opcode = 53; /* SD_IO_RW_EXTENDED */ + mmc_cmd.arg = write ? 1<<31 : 0; + mmc_cmd.arg |= (func & 0x7) << 28; + mmc_cmd.arg |= 1<<27; + mmc_cmd.arg |= fifo ? 0 : 1<<26; + mmc_cmd.arg |= (addr & 0x1FFFF) << 9; + mmc_cmd.arg |= blk_num & 0x1FF; + mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; + + mmc_req.cmd = &mmc_cmd; + mmc_req.data = &mmc_dat; + + sdio_claim_host(gInstance->func[func]); + mmc_set_data_timeout(&mmc_dat, gInstance->func[func]->card); + mmc_wait_for_req(gInstance->func[func]->card->host, &mmc_req); + sdio_release_host(gInstance->func[func]); + + err_ret = mmc_cmd.error? mmc_cmd.error : mmc_dat.error; + if (0 != err_ret) { + sd_err(("%s:CMD53 %s failed with code %d\n", + __FUNCTION__, + write ? "write" : "read", + err_ret)); + sd_err(("%s:Disabling rxchain and fire it with PIO\n", + __FUNCTION__)); + sd->use_rxchain = FALSE; + pkt = pprev; + lft_len = ttl_len; + } else if (!fifo) { + addr = addr + ttl_len - lft_len - dma_len; + } + } + + /* PIO mode */ + if (0 != lft_len) { + /* Claim host controller */ + sdio_claim_host(gInstance->func[func]); + for (pnext = pkt; pnext; pnext = PKTNEXT(sd->osh, pnext)) { + uint8 *buf = (uint8*)PKTDATA(sd->osh, pnext) + + xfred_len; + pkt_len = PKTLEN(sd->osh, pnext); + if (0 != xfred_len) { + pkt_len -= xfred_len; + xfred_len = 0; + } + + /* Align Patch + * read or small packet(ex:BDC header) skip 32 byte align + * otherwise, padding DHD_SDALIGN for performance + */ + if (write == 0 || pkt_len < 32) + pkt_len = (pkt_len + 3) & 0xFFFFFFFC; + else if (pkt_len % blk_size) + pkt_len += blk_size - (pkt_len % blk_size); + +#ifdef CONFIG_MMC_MSM7X00A + if ((pkt_len % 64) == 32) { + sd_trace(("%s: Rounding up TX packet +=32\n", __FUNCTION__)); + pkt_len += 32; + } +#endif /* CONFIG_MMC_MSM7X00A */ + + if ((write) && (!fifo)) + err_ret = sdio_memcpy_toio( + gInstance->func[func], + addr, buf, pkt_len); + else if (write) + err_ret = sdio_memcpy_toio( + gInstance->func[func], + addr, buf, pkt_len); + else if (fifo) + err_ret = sdio_readsb( + gInstance->func[func], + buf, addr, pkt_len); + else + err_ret = sdio_memcpy_fromio( + gInstance->func[func], + buf, addr, pkt_len); + + if (err_ret) + sd_err(("%s: %s FAILED %p[%d], addr=0x%05x, pkt_len=%d, ERR=%d\n", + __FUNCTION__, + (write) ? "TX" : "RX", + pnext, SGCount, addr, pkt_len, err_ret)); + else + sd_trace(("%s: %s xfr'd %p[%d], addr=0x%05x, len=%d\n", + __FUNCTION__, + (write) ? "TX" : "RX", + pnext, SGCount, addr, pkt_len)); + + if (!fifo) + addr += pkt_len; + SGCount ++; + } + sdio_release_host(gInstance->func[func]); + } + + sd_trace(("%s: Exit\n", __FUNCTION__)); + return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL); +} + + +/* + * This function takes a buffer or packet, and fixes everything up so that in the + * end, a DMA-able packet is created. + * + * A buffer does not have an associated packet pointer, and may or may not be aligned. + * A packet may consist of a single packet, or a packet chain. If it is a packet chain, + * then all the packets in the chain must be properly aligned. If the packet data is not + * aligned, then there may only be one packet, and in this case, it is copied to a new + * aligned packet. + * + */ +extern SDIOH_API_RC +sdioh_request_buffer(sdioh_info_t *sd, uint pio_dma, uint fix_inc, uint write, uint func, + uint addr, uint reg_width, uint buflen_u, uint8 *buffer, void *pkt) +{ + SDIOH_API_RC Status; + void *mypkt = NULL; + + sd_trace(("%s: Enter\n", __FUNCTION__)); + + DHD_PM_RESUME_WAIT(sdioh_request_buffer_wait); + DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL); + /* Case 1: we don't have a packet. */ + if (pkt == NULL) { + sd_data(("%s: Creating new %s Packet, len=%d\n", + __FUNCTION__, write ? "TX" : "RX", buflen_u)); +#ifdef CONFIG_DHD_USE_STATIC_BUF + if (!(mypkt = PKTGET_STATIC(sd->osh, buflen_u, write ? TRUE : FALSE))) { +#else + if (!(mypkt = PKTGET(sd->osh, buflen_u, write ? TRUE : FALSE))) { +#endif /* CONFIG_DHD_USE_STATIC_BUF */ + sd_err(("%s: PKTGET failed: len %d\n", + __FUNCTION__, buflen_u)); + return SDIOH_API_RC_FAIL; + } + + /* For a write, copy the buffer data into the packet. */ + if (write) { + bcopy(buffer, PKTDATA(sd->osh, mypkt), buflen_u); + } + + Status = sdioh_request_packet(sd, fix_inc, write, func, addr, mypkt); + + /* For a read, copy the packet data back to the buffer. */ + if (!write) { + bcopy(PKTDATA(sd->osh, mypkt), buffer, buflen_u); + } +#ifdef CONFIG_DHD_USE_STATIC_BUF + PKTFREE_STATIC(sd->osh, mypkt, write ? TRUE : FALSE); +#else + PKTFREE(sd->osh, mypkt, write ? TRUE : FALSE); +#endif /* CONFIG_DHD_USE_STATIC_BUF */ + } else if (((uint32)(PKTDATA(sd->osh, pkt)) & DMA_ALIGN_MASK) != 0) { + /* Case 2: We have a packet, but it is unaligned. */ + + /* In this case, we cannot have a chain. */ + ASSERT(PKTNEXT(sd->osh, pkt) == NULL); + + sd_data(("%s: Creating aligned %s Packet, len=%d\n", + __FUNCTION__, write ? "TX" : "RX", PKTLEN(sd->osh, pkt))); +#ifdef CONFIG_DHD_USE_STATIC_BUF + if (!(mypkt = PKTGET_STATIC(sd->osh, PKTLEN(sd->osh, pkt), write ? TRUE : FALSE))) { +#else + if (!(mypkt = PKTGET(sd->osh, PKTLEN(sd->osh, pkt), write ? TRUE : FALSE))) { +#endif /* CONFIG_DHD_USE_STATIC_BUF */ + sd_err(("%s: PKTGET failed: len %d\n", + __FUNCTION__, PKTLEN(sd->osh, pkt))); + return SDIOH_API_RC_FAIL; + } + + /* For a write, copy the buffer data into the packet. */ + if (write) { + bcopy(PKTDATA(sd->osh, pkt), + PKTDATA(sd->osh, mypkt), + PKTLEN(sd->osh, pkt)); + } + + Status = sdioh_request_packet(sd, fix_inc, write, func, addr, mypkt); + + /* For a read, copy the packet data back to the buffer. */ + if (!write) { + bcopy(PKTDATA(sd->osh, mypkt), + PKTDATA(sd->osh, pkt), + PKTLEN(sd->osh, mypkt)); + } +#ifdef CONFIG_DHD_USE_STATIC_BUF + PKTFREE_STATIC(sd->osh, mypkt, write ? TRUE : FALSE); +#else + PKTFREE(sd->osh, mypkt, write ? TRUE : FALSE); +#endif /* CONFIG_DHD_USE_STATIC_BUF */ + } else { /* case 3: We have a packet and it is aligned. */ + sd_data(("%s: Aligned %s Packet, direct DMA\n", + __FUNCTION__, write ? "Tx" : "Rx")); + Status = sdioh_request_packet(sd, fix_inc, write, func, addr, pkt); + } + + return (Status); +} + +/* this function performs "abort" for both of host & device */ +extern int +sdioh_abort(sdioh_info_t *sd, uint func) +{ +#if defined(MMC_SDIO_ABORT) + char t_func = (char) func; +#endif /* defined(MMC_SDIO_ABORT) */ + sd_trace(("%s: Enter\n", __FUNCTION__)); + +#if defined(MMC_SDIO_ABORT) + /* issue abort cmd52 command through F1 */ + sdioh_request_byte(sd, SD_IO_OP_WRITE, SDIO_FUNC_0, SDIOD_CCCR_IOABORT, &t_func); +#endif /* defined(MMC_SDIO_ABORT) */ + + sd_trace(("%s: Exit\n", __FUNCTION__)); + return SDIOH_API_RC_SUCCESS; +} + +/* Reset and re-initialize the device */ +int sdioh_sdio_reset(sdioh_info_t *si) +{ + sd_trace(("%s: Enter\n", __FUNCTION__)); + sd_trace(("%s: Exit\n", __FUNCTION__)); + return SDIOH_API_RC_SUCCESS; +} + +/* Disable device interrupt */ +void +sdioh_sdmmc_devintr_off(sdioh_info_t *sd) +{ + sd_trace(("%s: %d\n", __FUNCTION__, sd->use_client_ints)); + sd->intmask &= ~CLIENT_INTR; +} + +/* Enable device interrupt */ +void +sdioh_sdmmc_devintr_on(sdioh_info_t *sd) +{ + sd_trace(("%s: %d\n", __FUNCTION__, sd->use_client_ints)); + sd->intmask |= CLIENT_INTR; +} + +/* Read client card reg */ +int +sdioh_sdmmc_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data) +{ + + if ((func == 0) || (regsize == 1)) { + uint8 temp = 0; + + sdioh_request_byte(sd, SDIOH_READ, func, regaddr, &temp); + *data = temp; + *data &= 0xff; + sd_data(("%s: byte read data=0x%02x\n", + __FUNCTION__, *data)); + } else { + sdioh_request_word(sd, 0, SDIOH_READ, func, regaddr, data, regsize); + if (regsize == 2) + *data &= 0xffff; + + sd_data(("%s: word read data=0x%08x\n", + __FUNCTION__, *data)); + } + + return SUCCESS; +} + +#if !defined(OOB_INTR_ONLY) +/* bcmsdh_sdmmc interrupt handler */ +static void IRQHandler(struct sdio_func *func) +{ + sdioh_info_t *sd; + + sd_trace(("bcmsdh_sdmmc: ***IRQHandler\n")); + sd = gInstance->sd; + + ASSERT(sd != NULL); + sdio_release_host(gInstance->func[0]); + + if (sd->use_client_ints) { + sd->intrcount++; + ASSERT(sd->intr_handler); + ASSERT(sd->intr_handler_arg); + (sd->intr_handler)(sd->intr_handler_arg); + } else { + sd_err(("bcmsdh_sdmmc: ***IRQHandler\n")); + + sd_err(("%s: Not ready for intr: enabled %d, handler %p\n", + __FUNCTION__, sd->client_intr_enabled, sd->intr_handler)); + } + + sdio_claim_host(gInstance->func[0]); +} + +/* bcmsdh_sdmmc interrupt handler for F2 (dummy handler) */ +static void IRQHandlerF2(struct sdio_func *func) +{ + sdioh_info_t *sd; + + sd_trace(("bcmsdh_sdmmc: ***IRQHandlerF2\n")); + + sd = gInstance->sd; + + ASSERT(sd != NULL); + BCM_REFERENCE(sd); +} +#endif /* !defined(OOB_INTR_ONLY) */ + +#ifdef NOTUSED +/* Write client card reg */ +static int +sdioh_sdmmc_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 data) +{ + + if ((func == 0) || (regsize == 1)) { + uint8 temp; + + temp = data & 0xff; + sdioh_request_byte(sd, SDIOH_READ, func, regaddr, &temp); + sd_data(("%s: byte write data=0x%02x\n", + __FUNCTION__, data)); + } else { + if (regsize == 2) + data &= 0xffff; + + sdioh_request_word(sd, 0, SDIOH_READ, func, regaddr, &data, regsize); + + sd_data(("%s: word write data=0x%08x\n", + __FUNCTION__, data)); + } + + return SUCCESS; +} +#endif /* NOTUSED */ + +int +sdioh_start(sdioh_info_t *si, int stage) +{ + int ret; + sdioh_info_t *sd = gInstance->sd; + + if (!sd) return (0); + + /* Need to do this stages as we can't enable the interrupt till + downloading of the firmware is complete, other wise polling + sdio access will come in way + */ + if (gInstance->func[0]) { + if (stage == 0) { + /* Since the power to the chip is killed, we will have + re enumerate the device again. Set the block size + and enable the fucntion 1 for in preparation for + downloading the code + */ + /* sdio_reset_comm() - has been fixed in latest kernel/msm.git for Linux + 2.6.27. The implementation prior to that is buggy, and needs broadcom's + patch for it + */ + if ((ret = sdio_reset_comm(gInstance->func[0]->card))) { + sd_err(("%s Failed, error = %d\n", __FUNCTION__, ret)); + return ret; + } + else { + sd->num_funcs = 2; + sd->sd_blockmode = TRUE; + sd->use_client_ints = TRUE; + sd->client_block_size[0] = 64; + + if (gInstance->func[1]) { + /* Claim host controller */ + sdio_claim_host(gInstance->func[1]); + + sd->client_block_size[1] = 64; + if (sdio_set_block_size(gInstance->func[1], 64)) { + sd_err(("bcmsdh_sdmmc: Failed to set F1 blocksize\n")); + } + + /* Release host controller F1 */ + sdio_release_host(gInstance->func[1]); + } + + if (gInstance->func[2]) { + /* Claim host controller F2 */ + sdio_claim_host(gInstance->func[2]); + + sd->client_block_size[2] = sd_f2_blocksize; + if (sdio_set_block_size(gInstance->func[2], + sd_f2_blocksize)) { + sd_err(("bcmsdh_sdmmc: Failed to set F2 " + "blocksize to %d\n", sd_f2_blocksize)); + } + + /* Release host controller F2 */ + sdio_release_host(gInstance->func[2]); + } + + sdioh_sdmmc_card_enablefuncs(sd); + } + } else { +#if !defined(OOB_INTR_ONLY) + sdio_claim_host(gInstance->func[0]); + if (gInstance->func[2]) + sdio_claim_irq(gInstance->func[2], IRQHandlerF2); + if (gInstance->func[1]) + sdio_claim_irq(gInstance->func[1], IRQHandler); + sdio_release_host(gInstance->func[0]); +#else /* defined(OOB_INTR_ONLY) */ +#if defined(HW_OOB) + sdioh_enable_func_intr(); +#endif + bcmsdh_oob_intr_set(TRUE); +#endif /* !defined(OOB_INTR_ONLY) */ + } + } + else + sd_err(("%s Failed\n", __FUNCTION__)); + + return (0); +} + +int +sdioh_stop(sdioh_info_t *si) +{ + /* MSM7201A Android sdio stack has bug with interrupt + So internaly within SDIO stack they are polling + which cause issue when device is turned off. So + unregister interrupt with SDIO stack to stop the + polling + */ + if (gInstance->func[0]) { +#if !defined(OOB_INTR_ONLY) + sdio_claim_host(gInstance->func[0]); + if (gInstance->func[1]) + sdio_release_irq(gInstance->func[1]); + if (gInstance->func[2]) + sdio_release_irq(gInstance->func[2]); + sdio_release_host(gInstance->func[0]); +#else /* defined(OOB_INTR_ONLY) */ +#if defined(HW_OOB) + sdioh_disable_func_intr(); +#endif + bcmsdh_oob_intr_set(FALSE); +#endif /* !defined(OOB_INTR_ONLY) */ + } + else + sd_err(("%s Failed\n", __FUNCTION__)); + return (0); +} + +int +sdioh_waitlockfree(sdioh_info_t *sd) +{ + return (1); +} + + +SDIOH_API_RC +sdioh_gpioouten(sdioh_info_t *sd, uint32 gpio) +{ + return SDIOH_API_RC_FAIL; +} + +SDIOH_API_RC +sdioh_gpioout(sdioh_info_t *sd, uint32 gpio, bool enab) +{ + return SDIOH_API_RC_FAIL; +} + +bool +sdioh_gpioin(sdioh_info_t *sd, uint32 gpio) +{ + return FALSE; +} + +SDIOH_API_RC +sdioh_gpio_init(sdioh_info_t *sd) +{ + return SDIOH_API_RC_FAIL; +} diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c new file mode 100644 index 00000000..e9136401 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c @@ -0,0 +1,425 @@ +/* + * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmsdh_sdmmc_linux.c 381548 2013-01-28 17:25:38Z $ + */ + +#include +#include +#include /* SDIO Device and Protocol Specs */ +#include /* bcmsdh to/from specific controller APIs */ +#include /* to get msglevel bit values */ + +#include /* request_irq() */ + +#include +#include +#include +#include + +#if !defined(SDIO_VENDOR_ID_BROADCOM) +#define SDIO_VENDOR_ID_BROADCOM 0x02d0 +#endif /* !defined(SDIO_VENDOR_ID_BROADCOM) */ + +#define SDIO_DEVICE_ID_BROADCOM_DEFAULT 0x0000 + +#if !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) +#define SDIO_DEVICE_ID_BROADCOM_4325_SDGWB 0x0492 /* BCM94325SDGWB */ +#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) */ +#if !defined(SDIO_DEVICE_ID_BROADCOM_4325) +#define SDIO_DEVICE_ID_BROADCOM_4325 0x0493 +#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325) */ +#if !defined(SDIO_DEVICE_ID_BROADCOM_4329) +#define SDIO_DEVICE_ID_BROADCOM_4329 0x4329 +#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4329) */ +#if !defined(SDIO_DEVICE_ID_BROADCOM_4319) +#define SDIO_DEVICE_ID_BROADCOM_4319 0x4319 +#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4319) */ +#if !defined(SDIO_DEVICE_ID_BROADCOM_4330) +#define SDIO_DEVICE_ID_BROADCOM_4330 0x4330 +#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4330) */ +#if !defined(SDIO_DEVICE_ID_BROADCOM_4334) +#define SDIO_DEVICE_ID_BROADCOM_4334 0x4334 +#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4334) */ +#if !defined(SDIO_DEVICE_ID_BROADCOM_4324) +#define SDIO_DEVICE_ID_BROADCOM_4324 0x4324 +#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4324) */ +#if !defined(SDIO_DEVICE_ID_BROADCOM_43239) +#define SDIO_DEVICE_ID_BROADCOM_43239 43239 +#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_43239) */ + + +#include + +#include + +#ifdef WL_CFG80211 +extern void wl_cfg80211_set_parent_dev(void *dev); +#endif + +extern void sdioh_sdmmc_devintr_off(sdioh_info_t *sd); +extern void sdioh_sdmmc_devintr_on(sdioh_info_t *sd); +extern int dhd_os_check_wakelock(void *dhdp); +extern int dhd_os_check_if_up(void *dhdp); +extern void *bcmsdh_get_drvdata(void); + +int sdio_function_init(void); +void sdio_function_cleanup(void); + +#define DESCRIPTION "bcmsdh_sdmmc Driver" +#define AUTHOR "Broadcom Corporation" + +/* module param defaults */ +static int clockoverride = 0; + +module_param(clockoverride, int, 0644); +MODULE_PARM_DESC(clockoverride, "SDIO card clock override"); + +PBCMSDH_SDMMC_INSTANCE gInstance; + +/* Maximum number of bcmsdh_sdmmc devices supported by driver */ +#define BCMSDH_SDMMC_MAX_DEVICES 1 + +extern int bcmsdh_probe(struct device *dev); +extern int bcmsdh_remove(struct device *dev); +extern volatile bool dhd_mmc_suspend; + +static int bcmsdh_sdmmc_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + int ret = 0; + static struct sdio_func sdio_func_0; + + if (func) { + sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__)); + sd_trace(("sdio_bcmsdh: func->class=%x\n", func->class)); + sd_trace(("sdio_vendor: 0x%04x\n", func->vendor)); + sd_trace(("sdio_device: 0x%04x\n", func->device)); + sd_trace(("Function#: 0x%04x\n", func->num)); + + if (func->num == 1) { + sdio_func_0.num = 0; + sdio_func_0.card = func->card; + gInstance->func[0] = &sdio_func_0; + if(func->device == 0x4) { /* 4318 */ + gInstance->func[2] = NULL; + sd_trace(("NIC found, calling bcmsdh_probe...\n")); + ret = bcmsdh_probe(&func->dev); + } + } + + gInstance->func[func->num] = func; + + if (func->num == 2) { + #ifdef WL_CFG80211 + wl_cfg80211_set_parent_dev(&func->dev); + #endif + sd_trace(("F2 found, calling bcmsdh_probe...\n")); + ret = bcmsdh_probe(&func->dev); + if (ret < 0 && gInstance) + gInstance->func[2] = NULL; + } + } else { + ret = -ENODEV; + } + + return ret; +} + +static void bcmsdh_sdmmc_remove(struct sdio_func *func) +{ + if (func) { + sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__)); + sd_info(("sdio_bcmsdh: func->class=%x\n", func->class)); + sd_info(("sdio_vendor: 0x%04x\n", func->vendor)); + sd_info(("sdio_device: 0x%04x\n", func->device)); + sd_info(("Function#: 0x%04x\n", func->num)); + + if (gInstance->func[2]) { + sd_trace(("F2 found, calling bcmsdh_remove...\n")); + bcmsdh_remove(&func->dev); + gInstance->func[2] = NULL; + } + if (func->num == 1) { + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + gInstance->func[1] = NULL; + } + } +} + +/* devices we support, null terminated */ +static const struct sdio_device_id bcmsdh_sdmmc_ids[] = { + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_DEFAULT) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4319) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4324) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43239) }, + { SDIO_DEVICE_CLASS(SDIO_CLASS_NONE) }, + { /* end: all zeroes */ }, +}; + +MODULE_DEVICE_TABLE(sdio, bcmsdh_sdmmc_ids); + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) +static int bcmsdh_sdmmc_suspend(struct device *pdev) +{ + struct sdio_func *func = dev_to_sdio_func(pdev); + mmc_pm_flag_t sdio_flags; + int ret; + + if (func->num != 2) + return 0; + + sd_trace_hw4(("%s Enter\n", __FUNCTION__)); + + if (dhd_os_check_wakelock(bcmsdh_get_drvdata())) + return -EBUSY; + + sdio_flags = sdio_get_host_pm_caps(func); + + if (!(sdio_flags & MMC_PM_KEEP_POWER)) { + sd_err(("%s: can't keep power while host is suspended\n", __FUNCTION__)); + return -EINVAL; + } + + /* keep power while host suspended */ + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); + if (ret) { + sd_err(("%s: error while trying to keep power\n", __FUNCTION__)); + return ret; + } + +#if defined(OOB_INTR_ONLY) + bcmsdh_oob_intr_set(0); +#endif /* defined(OOB_INTR_ONLY) */ + dhd_mmc_suspend = TRUE; + smp_mb(); + + return 0; +} + +static int bcmsdh_sdmmc_resume(struct device *pdev) +{ +#if defined(OOB_INTR_ONLY) + struct sdio_func *func = dev_to_sdio_func(pdev); +#endif /* defined(OOB_INTR_ONLY) */ + sd_trace_hw4(("%s Enter\n", __FUNCTION__)); + + dhd_mmc_suspend = FALSE; +#if defined(OOB_INTR_ONLY) + if ((func->num == 2) && dhd_os_check_if_up(bcmsdh_get_drvdata())) + bcmsdh_oob_intr_set(1); +#endif /* (OOB_INTR_ONLY) */ + smp_mb(); + return 0; +} + +static const struct dev_pm_ops bcmsdh_sdmmc_pm_ops = { + .suspend = bcmsdh_sdmmc_suspend, + .resume = bcmsdh_sdmmc_resume, +}; +#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */ + +#if defined(BCMLXSDMMC) +static struct semaphore *notify_semaphore = NULL; + +static int dummy_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + if (notify_semaphore) + up(notify_semaphore); + return 0; +} + +static void dummy_remove(struct sdio_func *func) +{ +} + +static struct sdio_driver dummy_sdmmc_driver = { + .probe = dummy_probe, + .remove = dummy_remove, + .name = "dummy_sdmmc", + .id_table = bcmsdh_sdmmc_ids, + }; + +int sdio_func_reg_notify(void* semaphore) +{ + notify_semaphore = semaphore; + return sdio_register_driver(&dummy_sdmmc_driver); +} + +void sdio_func_unreg_notify(void) +{ + sdio_unregister_driver(&dummy_sdmmc_driver); +} + +#endif /* defined(BCMLXSDMMC) */ + +static struct sdio_driver bcmsdh_sdmmc_driver = { + .probe = bcmsdh_sdmmc_probe, + .remove = bcmsdh_sdmmc_remove, + .name = "bcmsdh_sdmmc", + .id_table = bcmsdh_sdmmc_ids, +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) + .drv = { + .pm = &bcmsdh_sdmmc_pm_ops, + }, +#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */ + }; + +struct sdos_info { + sdioh_info_t *sd; + spinlock_t lock; +}; + + +int +sdioh_sdmmc_osinit(sdioh_info_t *sd) +{ + struct sdos_info *sdos; + + if (!sd) + return BCME_BADARG; + + sdos = (struct sdos_info*)MALLOC(sd->osh, sizeof(struct sdos_info)); + sd->sdos_info = (void*)sdos; + if (sdos == NULL) + return BCME_NOMEM; + + sdos->sd = sd; + spin_lock_init(&sdos->lock); + return BCME_OK; +} + +void +sdioh_sdmmc_osfree(sdioh_info_t *sd) +{ + struct sdos_info *sdos; + ASSERT(sd && sd->sdos_info); + + sdos = (struct sdos_info *)sd->sdos_info; + MFREE(sd->osh, sdos, sizeof(struct sdos_info)); +} + +/* Interrupt enable/disable */ +SDIOH_API_RC +sdioh_interrupt_set(sdioh_info_t *sd, bool enable) +{ + ulong flags; + struct sdos_info *sdos; + + if (!sd) + return BCME_BADARG; + + sd_trace(("%s: %s\n", __FUNCTION__, enable ? "Enabling" : "Disabling")); + + sdos = (struct sdos_info *)sd->sdos_info; + ASSERT(sdos); + +#if !defined(OOB_INTR_ONLY) + if (enable && !(sd->intr_handler && sd->intr_handler_arg)) { + sd_err(("%s: no handler registered, will not enable\n", __FUNCTION__)); + return SDIOH_API_RC_FAIL; + } +#endif /* !defined(OOB_INTR_ONLY) */ + + /* Ensure atomicity for enable/disable calls */ + spin_lock_irqsave(&sdos->lock, flags); + + sd->client_intr_enabled = enable; + if (enable) { + sdioh_sdmmc_devintr_on(sd); + } else { + sdioh_sdmmc_devintr_off(sd); + } + + spin_unlock_irqrestore(&sdos->lock, flags); + + return SDIOH_API_RC_SUCCESS; +} + + +#ifdef BCMSDH_MODULE +static int __init +bcmsdh_module_init(void) +{ + int error = 0; + error = sdio_function_init(); + return error; +} + +static void __exit +bcmsdh_module_cleanup(void) +{ + sdio_function_cleanup(); +} + +module_init(bcmsdh_module_init); +module_exit(bcmsdh_module_cleanup); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION(DESCRIPTION); +MODULE_AUTHOR(AUTHOR); + +#endif /* BCMSDH_MODULE */ +/* + * module init +*/ +int sdio_function_init(void) +{ + int error = 0; + sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__)); + + gInstance = kzalloc(sizeof(BCMSDH_SDMMC_INSTANCE), GFP_KERNEL); + if (!gInstance) + return -ENOMEM; + + error = sdio_register_driver(&bcmsdh_sdmmc_driver); + if (error && gInstance) { + kfree(gInstance); + gInstance = 0; + } + + return error; +} + +/* + * module cleanup +*/ +extern int bcmsdh_remove(struct device *dev); +void sdio_function_cleanup(void) +{ + sd_trace(("%s Enter\n", __FUNCTION__)); + + + sdio_unregister_driver(&bcmsdh_sdmmc_driver); + + if (gInstance) + kfree(gInstance); +} diff --git a/drivers/net/wireless/bcmdhd/bcmutils.c b/drivers/net/wireless/bcmdhd/bcmutils.c new file mode 100644 index 00000000..05405aba --- /dev/null +++ b/drivers/net/wireless/bcmdhd/bcmutils.c @@ -0,0 +1,2090 @@ +/* + * Driver O/S-independent utility routines + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * $Id: bcmutils.c 312855 2012-02-04 02:01:18Z $ + */ + +#include +#include +#include +#include +#ifdef BCMDRIVER + +#include +#include + +#else /* !BCMDRIVER */ + +#include +#include +#include + +#if defined(BCMEXTSUP) +#include +#endif + + +#endif /* !BCMDRIVER */ + +#include +#include +#include +#include +#include +#include +#include +void *_bcmutils_dummy_fn = NULL; + + +#ifdef BCMDRIVER + + + +/* copy a pkt buffer chain into a buffer */ +uint +pktcopy(osl_t *osh, void *p, uint offset, int len, uchar *buf) +{ + uint n, ret = 0; + + if (len < 0) + len = 4096; /* "infinite" */ + + /* skip 'offset' bytes */ + for (; p && offset; p = PKTNEXT(osh, p)) { + if (offset < (uint)PKTLEN(osh, p)) + break; + offset -= PKTLEN(osh, p); + } + + if (!p) + return 0; + + /* copy the data */ + for (; p && len; p = PKTNEXT(osh, p)) { + n = MIN((uint)PKTLEN(osh, p) - offset, (uint)len); + bcopy(PKTDATA(osh, p) + offset, buf, n); + buf += n; + len -= n; + ret += n; + offset = 0; + } + + return ret; +} + +/* copy a buffer into a pkt buffer chain */ +uint +pktfrombuf(osl_t *osh, void *p, uint offset, int len, uchar *buf) +{ + uint n, ret = 0; + + /* skip 'offset' bytes */ + for (; p && offset; p = PKTNEXT(osh, p)) { + if (offset < (uint)PKTLEN(osh, p)) + break; + offset -= PKTLEN(osh, p); + } + + if (!p) + return 0; + + /* copy the data */ + for (; p && len; p = PKTNEXT(osh, p)) { + n = MIN((uint)PKTLEN(osh, p) - offset, (uint)len); + bcopy(buf, PKTDATA(osh, p) + offset, n); + buf += n; + len -= n; + ret += n; + offset = 0; + } + + return ret; +} + + + +/* return total length of buffer chain */ +uint BCMFASTPATH +pkttotlen(osl_t *osh, void *p) +{ + uint total; + int len; + + total = 0; + for (; p; p = PKTNEXT(osh, p)) { + len = PKTLEN(osh, p); + total += len; + } + + return (total); +} + +/* return the last buffer of chained pkt */ +void * +pktlast(osl_t *osh, void *p) +{ + for (; PKTNEXT(osh, p); p = PKTNEXT(osh, p)) + ; + + return (p); +} + +/* count segments of a chained packet */ +uint BCMFASTPATH +pktsegcnt(osl_t *osh, void *p) +{ + uint cnt; + + for (cnt = 0; p; p = PKTNEXT(osh, p)) + cnt++; + + return cnt; +} + + +/* count segments of a chained packet */ +uint BCMFASTPATH +pktsegcnt_war(osl_t *osh, void *p) +{ + uint cnt; + uint8 *pktdata; + uint len, remain, align64; + + for (cnt = 0; p; p = PKTNEXT(osh, p)) { + cnt++; + len = PKTLEN(osh, p); + if (len > 128) { + pktdata = (uint8 *)PKTDATA(osh, p); /* starting address of data */ + /* Check for page boundary straddle (2048B) */ + if (((uintptr)pktdata & ~0x7ff) != ((uintptr)(pktdata+len) & ~0x7ff)) + cnt++; + + align64 = (uint)((uintptr)pktdata & 0x3f); /* aligned to 64B */ + align64 = (64 - align64) & 0x3f; + len -= align64; /* bytes from aligned 64B to end */ + /* if aligned to 128B, check for MOD 128 between 1 to 4B */ + remain = len % 128; + if (remain > 0 && remain <= 4) + cnt++; /* add extra seg */ + } + } + + return cnt; +} + +uint8 * BCMFASTPATH +pktoffset(osl_t *osh, void *p, uint offset) +{ + uint total = pkttotlen(osh, p); + uint pkt_off = 0, len = 0; + uint8 *pdata = (uint8 *) PKTDATA(osh, p); + + if (offset > total) + return NULL; + + for (; p; p = PKTNEXT(osh, p)) { + pdata = (uint8 *) PKTDATA(osh, p); + pkt_off = offset - len; + len += PKTLEN(osh, p); + if (len > offset) + break; + } + return (uint8*) (pdata+pkt_off); +} + +/* + * osl multiple-precedence packet queue + * hi_prec is always >= the number of the highest non-empty precedence + */ +void * BCMFASTPATH +pktq_penq(struct pktq *pq, int prec, void *p) +{ + struct pktq_prec *q; + + ASSERT(prec >= 0 && prec < pq->num_prec); + ASSERT(PKTLINK(p) == NULL); /* queueing chains not allowed */ + + ASSERT(!pktq_full(pq)); + ASSERT(!pktq_pfull(pq, prec)); + + q = &pq->q[prec]; + + if (q->head) + PKTSETLINK(q->tail, p); + else + q->head = p; + + q->tail = p; + q->len++; + + pq->len++; + + if (pq->hi_prec < prec) + pq->hi_prec = (uint8)prec; + + return p; +} + +void * BCMFASTPATH +pktq_penq_head(struct pktq *pq, int prec, void *p) +{ + struct pktq_prec *q; + + ASSERT(prec >= 0 && prec < pq->num_prec); + ASSERT(PKTLINK(p) == NULL); /* queueing chains not allowed */ + + ASSERT(!pktq_full(pq)); + ASSERT(!pktq_pfull(pq, prec)); + + q = &pq->q[prec]; + + if (q->head == NULL) + q->tail = p; + + PKTSETLINK(p, q->head); + q->head = p; + q->len++; + + pq->len++; + + if (pq->hi_prec < prec) + pq->hi_prec = (uint8)prec; + + return p; +} + +void * BCMFASTPATH +pktq_pdeq(struct pktq *pq, int prec) +{ + struct pktq_prec *q; + void *p; + + ASSERT(prec >= 0 && prec < pq->num_prec); + + q = &pq->q[prec]; + + if ((p = q->head) == NULL) + return NULL; + + if ((q->head = PKTLINK(p)) == NULL) + q->tail = NULL; + + q->len--; + + pq->len--; + + PKTSETLINK(p, NULL); + + return p; +} + +void * BCMFASTPATH +pktq_pdeq_prev(struct pktq *pq, int prec, void *prev_p) +{ + struct pktq_prec *q; + void *p; + + ASSERT(prec >= 0 && prec < pq->num_prec); + + q = &pq->q[prec]; + + if (prev_p == NULL) + return NULL; + + if ((p = PKTLINK(prev_p)) == NULL) + return NULL; + + q->len--; + + pq->len--; + + PKTSETLINK(prev_p, PKTLINK(p)); + PKTSETLINK(p, NULL); + + return p; +} + +void * BCMFASTPATH +pktq_pdeq_tail(struct pktq *pq, int prec) +{ + struct pktq_prec *q; + void *p, *prev; + + ASSERT(prec >= 0 && prec < pq->num_prec); + + q = &pq->q[prec]; + + if ((p = q->head) == NULL) + return NULL; + + for (prev = NULL; p != q->tail; p = PKTLINK(p)) + prev = p; + + if (prev) + PKTSETLINK(prev, NULL); + else + q->head = NULL; + + q->tail = prev; + q->len--; + + pq->len--; + + return p; +} + +void +pktq_pflush(osl_t *osh, struct pktq *pq, int prec, bool dir, ifpkt_cb_t fn, int arg) +{ + struct pktq_prec *q; + void *p, *prev = NULL; + + q = &pq->q[prec]; + p = q->head; + while (p) { + if (fn == NULL || (*fn)(p, arg)) { + bool head = (p == q->head); + if (head) + q->head = PKTLINK(p); + else + PKTSETLINK(prev, PKTLINK(p)); + PKTSETLINK(p, NULL); + PKTFREE(osh, p, dir); + q->len--; + pq->len--; + p = (head ? q->head : PKTLINK(prev)); + } else { + prev = p; + p = PKTLINK(p); + } + } + + if (q->head == NULL) { + ASSERT(q->len == 0); + q->tail = NULL; + } +} + +bool BCMFASTPATH +pktq_pdel(struct pktq *pq, void *pktbuf, int prec) +{ + struct pktq_prec *q; + void *p; + + ASSERT(prec >= 0 && prec < pq->num_prec); + + if (!pktbuf) + return FALSE; + + q = &pq->q[prec]; + + if (q->head == pktbuf) { + if ((q->head = PKTLINK(pktbuf)) == NULL) + q->tail = NULL; + } else { + for (p = q->head; p && PKTLINK(p) != pktbuf; p = PKTLINK(p)) + ; + if (p == NULL) + return FALSE; + + PKTSETLINK(p, PKTLINK(pktbuf)); + if (q->tail == pktbuf) + q->tail = p; + } + + q->len--; + pq->len--; + PKTSETLINK(pktbuf, NULL); + return TRUE; +} + +void +pktq_init(struct pktq *pq, int num_prec, int max_len) +{ + int prec; + + ASSERT(num_prec > 0 && num_prec <= PKTQ_MAX_PREC); + + /* pq is variable size; only zero out what's requested */ + bzero(pq, OFFSETOF(struct pktq, q) + (sizeof(struct pktq_prec) * num_prec)); + + pq->num_prec = (uint16)num_prec; + + pq->max = (uint16)max_len; + + for (prec = 0; prec < num_prec; prec++) + pq->q[prec].max = pq->max; +} + +void +pktq_set_max_plen(struct pktq *pq, int prec, int max_len) +{ + ASSERT(prec >= 0 && prec < pq->num_prec); + + if (prec < pq->num_prec) + pq->q[prec].max = (uint16)max_len; +} + +void * BCMFASTPATH +pktq_deq(struct pktq *pq, int *prec_out) +{ + struct pktq_prec *q; + void *p; + int prec; + + if (pq->len == 0) + return NULL; + + while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL) + pq->hi_prec--; + + q = &pq->q[prec]; + + if ((p = q->head) == NULL) + return NULL; + + if ((q->head = PKTLINK(p)) == NULL) + q->tail = NULL; + + q->len--; + + pq->len--; + + if (prec_out) + *prec_out = prec; + + PKTSETLINK(p, NULL); + + return p; +} + +void * BCMFASTPATH +pktq_deq_tail(struct pktq *pq, int *prec_out) +{ + struct pktq_prec *q; + void *p, *prev; + int prec; + + if (pq->len == 0) + return NULL; + + for (prec = 0; prec < pq->hi_prec; prec++) + if (pq->q[prec].head) + break; + + q = &pq->q[prec]; + + if ((p = q->head) == NULL) + return NULL; + + for (prev = NULL; p != q->tail; p = PKTLINK(p)) + prev = p; + + if (prev) + PKTSETLINK(prev, NULL); + else + q->head = NULL; + + q->tail = prev; + q->len--; + + pq->len--; + + if (prec_out) + *prec_out = prec; + + PKTSETLINK(p, NULL); + + return p; +} + +void * +pktq_peek(struct pktq *pq, int *prec_out) +{ + int prec; + + if (pq->len == 0) + return NULL; + + while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL) + pq->hi_prec--; + + if (prec_out) + *prec_out = prec; + + return (pq->q[prec].head); +} + +void * +pktq_peek_tail(struct pktq *pq, int *prec_out) +{ + int prec; + + if (pq->len == 0) + return NULL; + + for (prec = 0; prec < pq->hi_prec; prec++) + if (pq->q[prec].head) + break; + + if (prec_out) + *prec_out = prec; + + return (pq->q[prec].tail); +} + +void +pktq_flush(osl_t *osh, struct pktq *pq, bool dir, ifpkt_cb_t fn, int arg) +{ + int prec; + + /* Optimize flush, if pktq len = 0, just return. + * pktq len of 0 means pktq's prec q's are all empty. + */ + if (pq->len == 0) { + return; + } + + for (prec = 0; prec < pq->num_prec; prec++) + pktq_pflush(osh, pq, prec, dir, fn, arg); + if (fn == NULL) + ASSERT(pq->len == 0); +} + +/* Return sum of lengths of a specific set of precedences */ +int +pktq_mlen(struct pktq *pq, uint prec_bmp) +{ + int prec, len; + + len = 0; + + for (prec = 0; prec <= pq->hi_prec; prec++) + if (prec_bmp & (1 << prec)) + len += pq->q[prec].len; + + return len; +} + +/* Priority peek from a specific set of precedences */ +void * BCMFASTPATH +pktq_mpeek(struct pktq *pq, uint prec_bmp, int *prec_out) +{ + struct pktq_prec *q; + void *p; + int prec; + + if (pq->len == 0) + { + return NULL; + } + while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL) + pq->hi_prec--; + + while ((prec_bmp & (1 << prec)) == 0 || pq->q[prec].head == NULL) + if (prec-- == 0) + return NULL; + + q = &pq->q[prec]; + + if ((p = q->head) == NULL) + return NULL; + + if (prec_out) + *prec_out = prec; + + return p; +} +/* Priority dequeue from a specific set of precedences */ +void * BCMFASTPATH +pktq_mdeq(struct pktq *pq, uint prec_bmp, int *prec_out) +{ + struct pktq_prec *q; + void *p; + int prec; + + if (pq->len == 0) + return NULL; + + while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL) + pq->hi_prec--; + + while ((pq->q[prec].head == NULL) || ((prec_bmp & (1 << prec)) == 0)) + if (prec-- == 0) + return NULL; + + q = &pq->q[prec]; + + if ((p = q->head) == NULL) + return NULL; + + if ((q->head = PKTLINK(p)) == NULL) + q->tail = NULL; + + q->len--; + + if (prec_out) + *prec_out = prec; + + pq->len--; + + PKTSETLINK(p, NULL); + + return p; +} + +#endif /* BCMDRIVER */ + +const unsigned char bcm_ctype[] = { + + _BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C, /* 0-7 */ + _BCM_C, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C, + _BCM_C, /* 8-15 */ + _BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C, /* 16-23 */ + _BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C, /* 24-31 */ + _BCM_S|_BCM_SP,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 32-39 */ + _BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 40-47 */ + _BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D, /* 48-55 */ + _BCM_D,_BCM_D,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 56-63 */ + _BCM_P, _BCM_U|_BCM_X, _BCM_U|_BCM_X, _BCM_U|_BCM_X, _BCM_U|_BCM_X, _BCM_U|_BCM_X, + _BCM_U|_BCM_X, _BCM_U, /* 64-71 */ + _BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U, /* 72-79 */ + _BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U, /* 80-87 */ + _BCM_U,_BCM_U,_BCM_U,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 88-95 */ + _BCM_P, _BCM_L|_BCM_X, _BCM_L|_BCM_X, _BCM_L|_BCM_X, _BCM_L|_BCM_X, _BCM_L|_BCM_X, + _BCM_L|_BCM_X, _BCM_L, /* 96-103 */ + _BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L, /* 104-111 */ + _BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L, /* 112-119 */ + _BCM_L,_BCM_L,_BCM_L,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_C, /* 120-127 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 128-143 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 144-159 */ + _BCM_S|_BCM_SP, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, + _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, /* 160-175 */ + _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, + _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, /* 176-191 */ + _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, + _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, /* 192-207 */ + _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_P, _BCM_U, _BCM_U, _BCM_U, + _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_L, /* 208-223 */ + _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, + _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, /* 224-239 */ + _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_P, _BCM_L, _BCM_L, _BCM_L, + _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L /* 240-255 */ +}; + +ulong +bcm_strtoul(const char *cp, char **endp, uint base) +{ + ulong result, last_result = 0, value; + bool minus; + + minus = FALSE; + + while (bcm_isspace(*cp)) + cp++; + + if (cp[0] == '+') + cp++; + else if (cp[0] == '-') { + minus = TRUE; + cp++; + } + + if (base == 0) { + if (cp[0] == '0') { + if ((cp[1] == 'x') || (cp[1] == 'X')) { + base = 16; + cp = &cp[2]; + } else { + base = 8; + cp = &cp[1]; + } + } else + base = 10; + } else if (base == 16 && (cp[0] == '0') && ((cp[1] == 'x') || (cp[1] == 'X'))) { + cp = &cp[2]; + } + + result = 0; + + while (bcm_isxdigit(*cp) && + (value = bcm_isdigit(*cp) ? *cp-'0' : bcm_toupper(*cp)-'A'+10) < base) { + result = result*base + value; + /* Detected overflow */ + if (result < last_result && !minus) + return (ulong)-1; + last_result = result; + cp++; + } + + if (minus) + result = (ulong)(-(long)result); + + if (endp) + *endp = DISCARD_QUAL(cp, char); + + return (result); +} + +int +bcm_atoi(const char *s) +{ + return (int)bcm_strtoul(s, NULL, 10); +} + +/* return pointer to location of substring 'needle' in 'haystack' */ +char * +bcmstrstr(const char *haystack, const char *needle) +{ + int len, nlen; + int i; + + if ((haystack == NULL) || (needle == NULL)) + return DISCARD_QUAL(haystack, char); + + nlen = strlen(needle); + len = strlen(haystack) - nlen + 1; + + for (i = 0; i < len; i++) + if (memcmp(needle, &haystack[i], nlen) == 0) + return DISCARD_QUAL(&haystack[i], char); + return (NULL); +} + +char * +bcmstrcat(char *dest, const char *src) +{ + char *p; + + p = dest + strlen(dest); + + while ((*p++ = *src++) != '\0') + ; + + return (dest); +} + +char * +bcmstrncat(char *dest, const char *src, uint size) +{ + char *endp; + char *p; + + p = dest + strlen(dest); + endp = p + size; + + while (p != endp && (*p++ = *src++) != '\0') + ; + + return (dest); +} + + +/**************************************************************************** +* Function: bcmstrtok +* +* Purpose: +* Tokenizes a string. This function is conceptually similiar to ANSI C strtok(), +* but allows strToken() to be used by different strings or callers at the same +* time. Each call modifies '*string' by substituting a NULL character for the +* first delimiter that is encountered, and updates 'string' to point to the char +* after the delimiter. Leading delimiters are skipped. +* +* Parameters: +* string (mod) Ptr to string ptr, updated by token. +* delimiters (in) Set of delimiter characters. +* tokdelim (out) Character that delimits the returned token. (May +* be set to NULL if token delimiter is not required). +* +* Returns: Pointer to the next token found. NULL when no more tokens are found. +***************************************************************************** +*/ +char * +bcmstrtok(char **string, const char *delimiters, char *tokdelim) +{ + unsigned char *str; + unsigned long map[8]; + int count; + char *nextoken; + + if (tokdelim != NULL) { + /* Prime the token delimiter */ + *tokdelim = '\0'; + } + + /* Clear control map */ + for (count = 0; count < 8; count++) { + map[count] = 0; + } + + /* Set bits in delimiter table */ + do { + map[*delimiters >> 5] |= (1 << (*delimiters & 31)); + } + while (*delimiters++); + + str = (unsigned char*)*string; + + /* Find beginning of token (skip over leading delimiters). Note that + * there is no token iff this loop sets str to point to the terminal + * null (*str == '\0') + */ + while (((map[*str >> 5] & (1 << (*str & 31))) && *str) || (*str == ' ')) { + str++; + } + + nextoken = (char*)str; + + /* Find the end of the token. If it is not the end of the string, + * put a null there. + */ + for (; *str; str++) { + if (map[*str >> 5] & (1 << (*str & 31))) { + if (tokdelim != NULL) { + *tokdelim = *str; + } + + *str++ = '\0'; + break; + } + } + + *string = (char*)str; + + /* Determine if a token has been found. */ + if (nextoken == (char *) str) { + return NULL; + } + else { + return nextoken; + } +} + + +#define xToLower(C) \ + ((C >= 'A' && C <= 'Z') ? (char)((int)C - (int)'A' + (int)'a') : C) + + +/**************************************************************************** +* Function: bcmstricmp +* +* Purpose: Compare to strings case insensitively. +* +* Parameters: s1 (in) First string to compare. +* s2 (in) Second string to compare. +* +* Returns: Return 0 if the two strings are equal, -1 if t1 < t2 and 1 if +* t1 > t2, when ignoring case sensitivity. +***************************************************************************** +*/ +int +bcmstricmp(const char *s1, const char *s2) +{ + char dc, sc; + + while (*s2 && *s1) { + dc = xToLower(*s1); + sc = xToLower(*s2); + if (dc < sc) return -1; + if (dc > sc) return 1; + s1++; + s2++; + } + + if (*s1 && !*s2) return 1; + if (!*s1 && *s2) return -1; + return 0; +} + + +/**************************************************************************** +* Function: bcmstrnicmp +* +* Purpose: Compare to strings case insensitively, upto a max of 'cnt' +* characters. +* +* Parameters: s1 (in) First string to compare. +* s2 (in) Second string to compare. +* cnt (in) Max characters to compare. +* +* Returns: Return 0 if the two strings are equal, -1 if t1 < t2 and 1 if +* t1 > t2, when ignoring case sensitivity. +***************************************************************************** +*/ +int +bcmstrnicmp(const char* s1, const char* s2, int cnt) +{ + char dc, sc; + + while (*s2 && *s1 && cnt) { + dc = xToLower(*s1); + sc = xToLower(*s2); + if (dc < sc) return -1; + if (dc > sc) return 1; + s1++; + s2++; + cnt--; + } + + if (!cnt) return 0; + if (*s1 && !*s2) return 1; + if (!*s1 && *s2) return -1; + return 0; +} + +/* parse a xx:xx:xx:xx:xx:xx format ethernet address */ +int +bcm_ether_atoe(const char *p, struct ether_addr *ea) +{ + int i = 0; + char *ep; + + for (;;) { + ea->octet[i++] = (char) bcm_strtoul(p, &ep, 16); + p = ep; + if (!*p++ || i == 6) + break; + } + + return (i == 6); +} + + +#if defined(CONFIG_USBRNDIS_RETAIL) || defined(NDIS_MINIPORT_DRIVER) +/* registry routine buffer preparation utility functions: + * parameter order is like strncpy, but returns count + * of bytes copied. Minimum bytes copied is null char(1)/wchar(2) + */ +ulong +wchar2ascii(char *abuf, ushort *wbuf, ushort wbuflen, ulong abuflen) +{ + ulong copyct = 1; + ushort i; + + if (abuflen == 0) + return 0; + + /* wbuflen is in bytes */ + wbuflen /= sizeof(ushort); + + for (i = 0; i < wbuflen; ++i) { + if (--abuflen == 0) + break; + *abuf++ = (char) *wbuf++; + ++copyct; + } + *abuf = '\0'; + + return copyct; +} +#endif /* CONFIG_USBRNDIS_RETAIL || NDIS_MINIPORT_DRIVER */ + +char * +bcm_ether_ntoa(const struct ether_addr *ea, char *buf) +{ + static const char hex[] = + { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + const uint8 *octet = ea->octet; + char *p = buf; + int i; + + for (i = 0; i < 6; i++, octet++) { + *p++ = hex[(*octet >> 4) & 0xf]; + *p++ = hex[*octet & 0xf]; + *p++ = ':'; + } + + *(p-1) = '\0'; + + return (buf); +} + +char * +bcm_ip_ntoa(struct ipv4_addr *ia, char *buf) +{ + snprintf(buf, 16, "%d.%d.%d.%d", + ia->addr[0], ia->addr[1], ia->addr[2], ia->addr[3]); + return (buf); +} + +#ifdef BCMDRIVER + +void +bcm_mdelay(uint ms) +{ + uint i; + + for (i = 0; i < ms; i++) { + OSL_DELAY(1000); + } +} + + + + + +#if defined(DHD_DEBUG) +/* pretty hex print a pkt buffer chain */ +void +prpkt(const char *msg, osl_t *osh, void *p0) +{ + void *p; + + if (msg && (msg[0] != '\0')) + printf("%s:\n", msg); + + for (p = p0; p; p = PKTNEXT(osh, p)) + prhex(NULL, PKTDATA(osh, p), PKTLEN(osh, p)); +} +#endif + +/* Takes an Ethernet frame and sets out-of-bound PKTPRIO. + * Also updates the inplace vlan tag if requested. + * For debugging, it returns an indication of what it did. + */ +uint BCMFASTPATH +pktsetprio(void *pkt, bool update_vtag) +{ + struct ether_header *eh; + struct ethervlan_header *evh; + uint8 *pktdata; + int priority = 0; + int rc = 0; + + pktdata = (uint8 *)PKTDATA(NULL, pkt); + ASSERT(ISALIGNED((uintptr)pktdata, sizeof(uint16))); + + eh = (struct ether_header *) pktdata; + + if (ntoh16(eh->ether_type) == ETHER_TYPE_8021Q) { + uint16 vlan_tag; + int vlan_prio, dscp_prio = 0; + + evh = (struct ethervlan_header *)eh; + + vlan_tag = ntoh16(evh->vlan_tag); + vlan_prio = (int) (vlan_tag >> VLAN_PRI_SHIFT) & VLAN_PRI_MASK; + + if (ntoh16(evh->ether_type) == ETHER_TYPE_IP) { + uint8 *ip_body = pktdata + sizeof(struct ethervlan_header); + uint8 tos_tc = IP_TOS46(ip_body); + dscp_prio = (int)(tos_tc >> IPV4_TOS_PREC_SHIFT); + } + + /* DSCP priority gets precedence over 802.1P (vlan tag) */ + if (dscp_prio != 0) { + priority = dscp_prio; + rc |= PKTPRIO_VDSCP; + } else { + priority = vlan_prio; + rc |= PKTPRIO_VLAN; + } + /* + * If the DSCP priority is not the same as the VLAN priority, + * then overwrite the priority field in the vlan tag, with the + * DSCP priority value. This is required for Linux APs because + * the VLAN driver on Linux, overwrites the skb->priority field + * with the priority value in the vlan tag + */ + if (update_vtag && (priority != vlan_prio)) { + vlan_tag &= ~(VLAN_PRI_MASK << VLAN_PRI_SHIFT); + vlan_tag |= (uint16)priority << VLAN_PRI_SHIFT; + evh->vlan_tag = hton16(vlan_tag); + rc |= PKTPRIO_UPD; + } + } else if (ntoh16(eh->ether_type) == ETHER_TYPE_IP) { + uint8 *ip_body = pktdata + sizeof(struct ether_header); + uint8 tos_tc = IP_TOS46(ip_body); + priority = (int)(tos_tc >> IPV4_TOS_PREC_SHIFT); + rc |= PKTPRIO_DSCP; + } + + ASSERT(priority >= 0 && priority <= MAXPRIO); + PKTSETPRIO(pkt, priority); + return (rc | priority); +} + + +static char bcm_undeferrstr[32]; +static const char *bcmerrorstrtable[] = BCMERRSTRINGTABLE; + +/* Convert the error codes into related error strings */ +const char * +bcmerrorstr(int bcmerror) +{ + /* check if someone added a bcmerror code but forgot to add errorstring */ + ASSERT(ABS(BCME_LAST) == (ARRAYSIZE(bcmerrorstrtable) - 1)); + + if (bcmerror > 0 || bcmerror < BCME_LAST) { + snprintf(bcm_undeferrstr, sizeof(bcm_undeferrstr), "Undefined error %d", bcmerror); + return bcm_undeferrstr; + } + + ASSERT(strlen(bcmerrorstrtable[-bcmerror]) < BCME_STRLEN); + + return bcmerrorstrtable[-bcmerror]; +} + + + +/* iovar table lookup */ +const bcm_iovar_t* +bcm_iovar_lookup(const bcm_iovar_t *table, const char *name) +{ + const bcm_iovar_t *vi; + const char *lookup_name; + + /* skip any ':' delimited option prefixes */ + lookup_name = strrchr(name, ':'); + if (lookup_name != NULL) + lookup_name++; + else + lookup_name = name; + + ASSERT(table != NULL); + + for (vi = table; vi->name; vi++) { + if (!strcmp(vi->name, lookup_name)) + return vi; + } + /* ran to end of table */ + + return NULL; /* var name not found */ +} + +int +bcm_iovar_lencheck(const bcm_iovar_t *vi, void *arg, int len, bool set) +{ + int bcmerror = 0; + + /* length check on io buf */ + switch (vi->type) { + case IOVT_BOOL: + case IOVT_INT8: + case IOVT_INT16: + case IOVT_INT32: + case IOVT_UINT8: + case IOVT_UINT16: + case IOVT_UINT32: + /* all integers are int32 sized args at the ioctl interface */ + if (len < (int)sizeof(int)) { + bcmerror = BCME_BUFTOOSHORT; + } + break; + + case IOVT_BUFFER: + /* buffer must meet minimum length requirement */ + if (len < vi->minlen) { + bcmerror = BCME_BUFTOOSHORT; + } + break; + + case IOVT_VOID: + if (!set) { + /* Cannot return nil... */ + bcmerror = BCME_UNSUPPORTED; + } else if (len) { + /* Set is an action w/o parameters */ + bcmerror = BCME_BUFTOOLONG; + } + break; + + default: + /* unknown type for length check in iovar info */ + ASSERT(0); + bcmerror = BCME_UNSUPPORTED; + } + + return bcmerror; +} + +#endif /* BCMDRIVER */ + + +/******************************************************************************* + * crc8 + * + * Computes a crc8 over the input data using the polynomial: + * + * x^8 + x^7 +x^6 + x^4 + x^2 + 1 + * + * The caller provides the initial value (either CRC8_INIT_VALUE + * or the previous returned value) to allow for processing of + * discontiguous blocks of data. When generating the CRC the + * caller is responsible for complementing the final return value + * and inserting it into the byte stream. When checking, a final + * return value of CRC8_GOOD_VALUE indicates a valid CRC. + * + * Reference: Dallas Semiconductor Application Note 27 + * Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms", + * ver 3, Aug 1993, ross@guest.adelaide.edu.au, Rocksoft Pty Ltd., + * ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt + * + * **************************************************************************** + */ + +static const uint8 crc8_table[256] = { + 0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B, + 0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21, + 0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF, + 0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5, + 0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14, + 0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E, + 0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80, + 0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA, + 0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95, + 0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF, + 0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01, + 0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B, + 0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA, + 0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0, + 0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E, + 0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34, + 0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0, + 0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A, + 0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54, + 0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E, + 0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF, + 0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5, + 0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B, + 0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61, + 0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E, + 0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74, + 0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA, + 0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0, + 0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41, + 0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B, + 0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5, + 0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F +}; + +#define CRC_INNER_LOOP(n, c, x) \ + (c) = ((c) >> 8) ^ crc##n##_table[((c) ^ (x)) & 0xff] + +uint8 +hndcrc8( + uint8 *pdata, /* pointer to array of data to process */ + uint nbytes, /* number of input data bytes to process */ + uint8 crc /* either CRC8_INIT_VALUE or previous return value */ +) +{ + /* hard code the crc loop instead of using CRC_INNER_LOOP macro + * to avoid the undefined and unnecessary (uint8 >> 8) operation. + */ + while (nbytes-- > 0) + crc = crc8_table[(crc ^ *pdata++) & 0xff]; + + return crc; +} + +/******************************************************************************* + * crc16 + * + * Computes a crc16 over the input data using the polynomial: + * + * x^16 + x^12 +x^5 + 1 + * + * The caller provides the initial value (either CRC16_INIT_VALUE + * or the previous returned value) to allow for processing of + * discontiguous blocks of data. When generating the CRC the + * caller is responsible for complementing the final return value + * and inserting it into the byte stream. When checking, a final + * return value of CRC16_GOOD_VALUE indicates a valid CRC. + * + * Reference: Dallas Semiconductor Application Note 27 + * Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms", + * ver 3, Aug 1993, ross@guest.adelaide.edu.au, Rocksoft Pty Ltd., + * ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt + * + * **************************************************************************** + */ + +static const uint16 crc16_table[256] = { + 0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF, + 0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7, + 0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E, + 0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876, + 0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD, + 0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5, + 0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C, + 0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974, + 0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB, + 0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3, + 0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A, + 0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72, + 0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9, + 0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1, + 0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738, + 0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70, + 0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7, + 0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF, + 0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036, + 0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E, + 0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5, + 0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD, + 0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134, + 0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C, + 0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3, + 0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB, + 0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232, + 0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A, + 0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1, + 0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9, + 0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330, + 0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78 +}; + +uint16 +hndcrc16( + uint8 *pdata, /* pointer to array of data to process */ + uint nbytes, /* number of input data bytes to process */ + uint16 crc /* either CRC16_INIT_VALUE or previous return value */ +) +{ + while (nbytes-- > 0) + CRC_INNER_LOOP(16, crc, *pdata++); + return crc; +} + +static const uint32 crc32_table[256] = { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, + 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, + 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, + 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, + 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, + 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, + 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, + 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, + 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, + 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, + 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, + 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, + 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, + 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, + 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, + 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, + 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, + 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, + 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D +}; + +/* + * crc input is CRC32_INIT_VALUE for a fresh start, or previous return value if + * accumulating over multiple pieces. + */ +uint32 +hndcrc32(uint8 *pdata, uint nbytes, uint32 crc) +{ + uint8 *pend; + pend = pdata + nbytes; + while (pdata < pend) + CRC_INNER_LOOP(32, crc, *pdata++); + + return crc; +} + +#ifdef notdef +#define CLEN 1499 /* CRC Length */ +#define CBUFSIZ (CLEN+4) +#define CNBUFS 5 /* # of bufs */ + +void +testcrc32(void) +{ + uint j, k, l; + uint8 *buf; + uint len[CNBUFS]; + uint32 crcr; + uint32 crc32tv[CNBUFS] = + {0xd2cb1faa, 0xd385c8fa, 0xf5b4f3f3, 0x55789e20, 0x00343110}; + + ASSERT((buf = MALLOC(CBUFSIZ*CNBUFS)) != NULL); + + /* step through all possible alignments */ + for (l = 0; l <= 4; l++) { + for (j = 0; j < CNBUFS; j++) { + len[j] = CLEN; + for (k = 0; k < len[j]; k++) + *(buf + j*CBUFSIZ + (k+l)) = (j+k) & 0xff; + } + + for (j = 0; j < CNBUFS; j++) { + crcr = crc32(buf + j*CBUFSIZ + l, len[j], CRC32_INIT_VALUE); + ASSERT(crcr == crc32tv[j]); + } + } + + MFREE(buf, CBUFSIZ*CNBUFS); + return; +} +#endif /* notdef */ + +/* + * Advance from the current 1-byte tag/1-byte length/variable-length value + * triple, to the next, returning a pointer to the next. + * If the current or next TLV is invalid (does not fit in given buffer length), + * NULL is returned. + * *buflen is not modified if the TLV elt parameter is invalid, or is decremented + * by the TLV parameter's length if it is valid. + */ +bcm_tlv_t * +bcm_next_tlv(bcm_tlv_t *elt, int *buflen) +{ + int len; + + /* validate current elt */ + if (!bcm_valid_tlv(elt, *buflen)) + return NULL; + + /* advance to next elt */ + len = elt->len; + elt = (bcm_tlv_t*)(elt->data + len); + *buflen -= (TLV_HDR_LEN + len); + + /* validate next elt */ + if (!bcm_valid_tlv(elt, *buflen)) + return NULL; + + return elt; +} + +/* + * Traverse a string of 1-byte tag/1-byte length/variable-length value + * triples, returning a pointer to the substring whose first element + * matches tag + */ +bcm_tlv_t * +bcm_parse_tlvs(void *buf, int buflen, uint key) +{ + bcm_tlv_t *elt; + int totlen; + + elt = (bcm_tlv_t*)buf; + totlen = buflen; + + /* find tagged parameter */ + while (totlen >= TLV_HDR_LEN) { + int len = elt->len; + + /* validate remaining totlen */ + if ((elt->id == key) && + (totlen >= (len + TLV_HDR_LEN))) + return (elt); + + elt = (bcm_tlv_t*)((uint8*)elt + (len + TLV_HDR_LEN)); + totlen -= (len + TLV_HDR_LEN); + } + + return NULL; +} + +/* + * Traverse a string of 1-byte tag/1-byte length/variable-length value + * triples, returning a pointer to the substring whose first element + * matches tag. Stop parsing when we see an element whose ID is greater + * than the target key. + */ +bcm_tlv_t * +bcm_parse_ordered_tlvs(void *buf, int buflen, uint key) +{ + bcm_tlv_t *elt; + int totlen; + + elt = (bcm_tlv_t*)buf; + totlen = buflen; + + /* find tagged parameter */ + while (totlen >= TLV_HDR_LEN) { + uint id = elt->id; + int len = elt->len; + + /* Punt if we start seeing IDs > than target key */ + if (id > key) + return (NULL); + + /* validate remaining totlen */ + if ((id == key) && + (totlen >= (len + TLV_HDR_LEN))) + return (elt); + + elt = (bcm_tlv_t*)((uint8*)elt + (len + TLV_HDR_LEN)); + totlen -= (len + TLV_HDR_LEN); + } + return NULL; +} + +#if defined(WLMSG_PRHDRS) || defined(WLMSG_PRPKT) || defined(WLMSG_ASSOC) || \ + defined(DHD_DEBUG) +int +bcm_format_flags(const bcm_bit_desc_t *bd, uint32 flags, char* buf, int len) +{ + int i; + char* p = buf; + char hexstr[16]; + int slen = 0, nlen = 0; + uint32 bit; + const char* name; + + if (len < 2 || !buf) + return 0; + + buf[0] = '\0'; + + for (i = 0; flags != 0; i++) { + bit = bd[i].bit; + name = bd[i].name; + if (bit == 0 && flags != 0) { + /* print any unnamed bits */ + snprintf(hexstr, 16, "0x%X", flags); + name = hexstr; + flags = 0; /* exit loop */ + } else if ((flags & bit) == 0) + continue; + flags &= ~bit; + nlen = strlen(name); + slen += nlen; + /* count btwn flag space */ + if (flags != 0) + slen += 1; + /* need NULL char as well */ + if (len <= slen) + break; + /* copy NULL char but don't count it */ + strncpy(p, name, nlen + 1); + p += nlen; + /* copy btwn flag space and NULL char */ + if (flags != 0) + p += snprintf(p, 2, " "); + } + + /* indicate the str was too short */ + if (flags != 0) { + if (len < 2) + p -= 2 - len; /* overwrite last char */ + p += snprintf(p, 2, ">"); + } + + return (int)(p - buf); +} + +/* print bytes formatted as hex to a string. return the resulting string length */ +int +bcm_format_hex(char *str, const void *bytes, int len) +{ + int i; + char *p = str; + const uint8 *src = (const uint8*)bytes; + + for (i = 0; i < len; i++) { + p += snprintf(p, 3, "%02X", *src); + src++; + } + return (int)(p - str); +} +#endif + +/* pretty hex print a contiguous buffer */ +void +prhex(const char *msg, uchar *buf, uint nbytes) +{ + char line[128], *p; + int len = sizeof(line); + int nchar; + uint i; + + if (msg && (msg[0] != '\0')) + printf("%s:\n", msg); + + p = line; + for (i = 0; i < nbytes; i++) { + if (i % 16 == 0) { + nchar = snprintf(p, len, " %04d: ", i); /* line prefix */ + p += nchar; + len -= nchar; + } + if (len > 0) { + nchar = snprintf(p, len, "%02x ", buf[i]); + p += nchar; + len -= nchar; + } + + if (i % 16 == 15) { + printf("%s\n", line); /* flush line */ + p = line; + len = sizeof(line); + } + } + + /* flush last partial line */ + if (p != line) + printf("%s\n", line); +} + +static const char *crypto_algo_names[] = { + "NONE", + "WEP1", + "TKIP", + "WEP128", + "AES_CCM", + "AES_OCB_MSDU", + "AES_OCB_MPDU", + "NALG" + "UNDEF", + "UNDEF", + "UNDEF", + "UNDEF" +}; + +const char * +bcm_crypto_algo_name(uint algo) +{ + return (algo < ARRAYSIZE(crypto_algo_names)) ? crypto_algo_names[algo] : "ERR"; +} + + +char * +bcm_chipname(uint chipid, char *buf, uint len) +{ + const char *fmt; + + fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x"; + snprintf(buf, len, fmt, chipid); + return buf; +} + +/* Produce a human-readable string for boardrev */ +char * +bcm_brev_str(uint32 brev, char *buf) +{ + if (brev < 0x100) + snprintf(buf, 8, "%d.%d", (brev & 0xf0) >> 4, brev & 0xf); + else + snprintf(buf, 8, "%c%03x", ((brev & 0xf000) == 0x1000) ? 'P' : 'A', brev & 0xfff); + + return (buf); +} + +#define BUFSIZE_TODUMP_ATONCE 512 /* Buffer size */ + +/* dump large strings to console */ +void +printbig(char *buf) +{ + uint len, max_len; + char c; + + len = strlen(buf); + + max_len = BUFSIZE_TODUMP_ATONCE; + + while (len > max_len) { + c = buf[max_len]; + buf[max_len] = '\0'; + printf("%s", buf); + buf[max_len] = c; + + buf += max_len; + len -= max_len; + } + /* print the remaining string */ + printf("%s\n", buf); + return; +} + +/* routine to dump fields in a fileddesc structure */ +uint +bcmdumpfields(bcmutl_rdreg_rtn read_rtn, void *arg0, uint arg1, struct fielddesc *fielddesc_array, + char *buf, uint32 bufsize) +{ + uint filled_len; + int len; + struct fielddesc *cur_ptr; + + filled_len = 0; + cur_ptr = fielddesc_array; + + while (bufsize > 1) { + if (cur_ptr->nameandfmt == NULL) + break; + len = snprintf(buf, bufsize, cur_ptr->nameandfmt, + read_rtn(arg0, arg1, cur_ptr->offset)); + /* check for snprintf overflow or error */ + if (len < 0 || (uint32)len >= bufsize) + len = bufsize - 1; + buf += len; + bufsize -= len; + filled_len += len; + cur_ptr++; + } + return filled_len; +} + +uint +bcm_mkiovar(char *name, char *data, uint datalen, char *buf, uint buflen) +{ + uint len; + + len = strlen(name) + 1; + + if ((len + datalen) > buflen) + return 0; + + strncpy(buf, name, buflen); + + /* append data onto the end of the name string */ + memcpy(&buf[len], data, datalen); + len += datalen; + + return len; +} + +/* Quarter dBm units to mW + * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153 + * Table is offset so the last entry is largest mW value that fits in + * a uint16. + */ + +#define QDBM_OFFSET 153 /* Offset for first entry */ +#define QDBM_TABLE_LEN 40 /* Table size */ + +/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET. + * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2 + */ +#define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */ + +/* Largest mW value that will round down to the last table entry, + * QDBM_OFFSET + QDBM_TABLE_LEN-1. + * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) + mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2. + */ +#define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */ + +static const uint16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = { +/* qdBm: +0 +1 +2 +3 +4 +5 +6 +7 */ +/* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000, +/* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849, +/* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119, +/* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811, +/* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 63096 +}; + +uint16 +bcm_qdbm_to_mw(uint8 qdbm) +{ + uint factor = 1; + int idx = qdbm - QDBM_OFFSET; + + if (idx >= QDBM_TABLE_LEN) { + /* clamp to max uint16 mW value */ + return 0xFFFF; + } + + /* scale the qdBm index up to the range of the table 0-40 + * where an offset of 40 qdBm equals a factor of 10 mW. + */ + while (idx < 0) { + idx += 40; + factor *= 10; + } + + /* return the mW value scaled down to the correct factor of 10, + * adding in factor/2 to get proper rounding. + */ + return ((nqdBm_to_mW_map[idx] + factor/2) / factor); +} + +uint8 +bcm_mw_to_qdbm(uint16 mw) +{ + uint8 qdbm; + int offset; + uint mw_uint = mw; + uint boundary; + + /* handle boundary case */ + if (mw_uint <= 1) + return 0; + + offset = QDBM_OFFSET; + + /* move mw into the range of the table */ + while (mw_uint < QDBM_TABLE_LOW_BOUND) { + mw_uint *= 10; + offset -= 40; + } + + for (qdbm = 0; qdbm < QDBM_TABLE_LEN-1; qdbm++) { + boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm+1] - + nqdBm_to_mW_map[qdbm])/2; + if (mw_uint < boundary) break; + } + + qdbm += (uint8)offset; + + return (qdbm); +} + + +uint +bcm_bitcount(uint8 *bitmap, uint length) +{ + uint bitcount = 0, i; + uint8 tmp; + for (i = 0; i < length; i++) { + tmp = bitmap[i]; + while (tmp) { + bitcount++; + tmp &= (tmp - 1); + } + } + return bitcount; +} + +#ifdef BCMDRIVER + +/* Initialization of bcmstrbuf structure */ +void +bcm_binit(struct bcmstrbuf *b, char *buf, uint size) +{ + b->origsize = b->size = size; + b->origbuf = b->buf = buf; +} + +/* Buffer sprintf wrapper to guard against buffer overflow */ +int +bcm_bprintf(struct bcmstrbuf *b, const char *fmt, ...) +{ + va_list ap; + int r; + + va_start(ap, fmt); + + r = vsnprintf(b->buf, b->size, fmt, ap); + + /* Non Ansi C99 compliant returns -1, + * Ansi compliant return r >= b->size, + * bcmstdlib returns 0, handle all + */ + /* r == 0 is also the case when strlen(fmt) is zero. + * typically the case when "" is passed as argument. + */ + if ((r == -1) || (r >= (int)b->size)) { + b->size = 0; + } else { + b->size -= r; + b->buf += r; + } + + va_end(ap); + + return r; +} + +void +bcm_bprhex(struct bcmstrbuf *b, const char *msg, bool newline, uint8 *buf, int len) +{ + int i; + + if (msg != NULL && msg[0] != '\0') + bcm_bprintf(b, "%s", msg); + for (i = 0; i < len; i ++) + bcm_bprintf(b, "%02X", buf[i]); + if (newline) + bcm_bprintf(b, "\n"); +} + +void +bcm_inc_bytes(uchar *num, int num_bytes, uint8 amount) +{ + int i; + + for (i = 0; i < num_bytes; i++) { + num[i] += amount; + if (num[i] >= amount) + break; + amount = 1; + } +} + +int +bcm_cmp_bytes(const uchar *arg1, const uchar *arg2, uint8 nbytes) +{ + int i; + + for (i = nbytes - 1; i >= 0; i--) { + if (arg1[i] != arg2[i]) + return (arg1[i] - arg2[i]); + } + return 0; +} + +void +bcm_print_bytes(const char *name, const uchar *data, int len) +{ + int i; + int per_line = 0; + + printf("%s: %d \n", name ? name : "", len); + for (i = 0; i < len; i++) { + printf("%02x ", *data++); + per_line++; + if (per_line == 16) { + per_line = 0; + printf("\n"); + } + } + printf("\n"); +} +#if defined(WLTINYDUMP) || defined(WLMSG_INFORM) || defined(WLMSG_ASSOC) || \ + defined(WLMSG_PRPKT) || defined(WLMSG_WSEC) +#define SSID_FMT_BUF_LEN ((4 * DOT11_MAX_SSID_LEN) + 1) + +int +bcm_format_ssid(char* buf, const uchar ssid[], uint ssid_len) +{ + uint i, c; + char *p = buf; + char *endp = buf + SSID_FMT_BUF_LEN; + + if (ssid_len > DOT11_MAX_SSID_LEN) ssid_len = DOT11_MAX_SSID_LEN; + + for (i = 0; i < ssid_len; i++) { + c = (uint)ssid[i]; + if (c == '\\') { + *p++ = '\\'; + *p++ = '\\'; + } else if (bcm_isprint((uchar)c)) { + *p++ = (char)c; + } else { + p += snprintf(p, (endp - p), "\\x%02X", c); + } + } + *p = '\0'; + ASSERT(p < endp); + + return (int)(p - buf); +} +#endif + +#endif /* BCMDRIVER */ + +/* + * ProcessVars:Takes a buffer of "=\n" lines read from a file and ending in a NUL. + * also accepts nvram files which are already in the format of =\0\=\0 + * Removes carriage returns, empty lines, comment lines, and converts newlines to NULs. + * Shortens buffer as needed and pads with NULs. End of buffer is marked by two NULs. +*/ + +unsigned int +process_nvram_vars(char *varbuf, unsigned int len) +{ + char *dp; + bool findNewline; + int column; + unsigned int buf_len, n; + unsigned int pad = 0; + + dp = varbuf; + + findNewline = FALSE; + column = 0; + + for (n = 0; n < len; n++) { + if (varbuf[n] == '\r') + continue; + if (findNewline && varbuf[n] != '\n') + continue; + findNewline = FALSE; + if (varbuf[n] == '#') { + findNewline = TRUE; + continue; + } + if (varbuf[n] == '\n') { + if (column == 0) + continue; + *dp++ = 0; + column = 0; + continue; + } + *dp++ = varbuf[n]; + column++; + } + buf_len = (unsigned int)(dp - varbuf); + if (buf_len % 4) { + pad = 4 - buf_len % 4; + if (pad && (buf_len + pad <= len)) { + buf_len += pad; + } + } + + while (dp < varbuf + n) + *dp++ = 0; + + return buf_len; +} diff --git a/drivers/net/wireless/bcmdhd/bcmwifi_channels.c b/drivers/net/wireless/bcmdhd/bcmwifi_channels.c new file mode 100644 index 00000000..6b5b0a3a --- /dev/null +++ b/drivers/net/wireless/bcmdhd/bcmwifi_channels.c @@ -0,0 +1,1179 @@ +/* + * Misc utility routines used by kernel or app-level. + * Contents are wifi-specific, used by any kernel or app-level + * software that might want wifi things as it grows. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * $Id: bcmwifi_channels.c 309193 2012-01-19 00:03:57Z $ + */ + +#include +#include + +#ifdef BCMDRIVER +#include +#include +#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base)) +#define tolower(c) (bcm_isupper((c)) ? ((c) + 'a' - 'A') : (c)) +#else +#include +#include +#include +#ifndef ASSERT +#define ASSERT(exp) +#endif +#endif /* BCMDRIVER */ + +#ifdef _bcmwifi_c_ +/* temporary for transitional compatibility */ +#include +#else +#include +#endif + +#if defined(WIN32) && (defined(BCMDLL) || defined(WLMDLL)) +#include /* For wl/exe/GNUmakefile.brcm_wlu and GNUmakefile.wlm_dll */ +#endif + +#ifndef D11AC_IOTYPES + +/* Definitions for legacy Chanspec type */ + +/* Chanspec ASCII representation: + * + * digit [AB] [N] [UL] + * + * : channel number of the 10MHz or 20MHz channel, + * or control sideband channel of 40MHz channel. + * : A for 5GHz, B for 2.4GHz + * : N for 10MHz, nothing for 20MHz or 40MHz + * (ctl-sideband spec implies 40MHz) + * : U for upper, L for lower + * + * may be omitted on input, and will be assumed to be + * 2.4GHz if channel number <= 14. + * + * Examples: + * 8 -> 2.4GHz channel 8, 20MHz + * 8b -> 2.4GHz channel 8, 20MHz + * 8l -> 2.4GHz channel 8, 40MHz, lower ctl sideband + * 8a -> 5GHz channel 8 (low 5 GHz band), 20MHz + * 36 -> 5GHz channel 36, 20MHz + * 36l -> 5GHz channel 36, 40MHz, lower ctl sideband + * 40u -> 5GHz channel 40, 40MHz, upper ctl sideband + * 180n -> channel 180, 10MHz + */ + + +/* given a chanspec and a string buffer, format the chanspec as a + * string, and return the original pointer a. + * Min buffer length must be CHANSPEC_STR_LEN. + * On error return NULL + */ +char * +wf_chspec_ntoa(chanspec_t chspec, char *buf) +{ + const char *band, *bw, *sb; + uint channel; + + band = ""; + bw = ""; + sb = ""; + channel = CHSPEC_CHANNEL(chspec); + /* check for non-default band spec */ + if ((CHSPEC_IS2G(chspec) && channel > CH_MAX_2G_CHANNEL) || + (CHSPEC_IS5G(chspec) && channel <= CH_MAX_2G_CHANNEL)) + band = (CHSPEC_IS2G(chspec)) ? "b" : "a"; + if (CHSPEC_IS40(chspec)) { + if (CHSPEC_SB_UPPER(chspec)) { + sb = "u"; + channel += CH_10MHZ_APART; + } else { + sb = "l"; + channel -= CH_10MHZ_APART; + } + } else if (CHSPEC_IS10(chspec)) { + bw = "n"; + } + + /* Outputs a max of 6 chars including '\0' */ + snprintf(buf, 6, "%d%s%s%s", channel, band, bw, sb); + return (buf); +} + +/* given a chanspec string, convert to a chanspec. + * On error return 0 + */ +chanspec_t +wf_chspec_aton(const char *a) +{ + char *endp = NULL; + uint channel, band, bw, ctl_sb; + char c; + + channel = strtoul(a, &endp, 10); + + /* check for no digits parsed */ + if (endp == a) + return 0; + + if (channel > MAXCHANNEL) + return 0; + + band = ((channel <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G); + bw = WL_CHANSPEC_BW_20; + ctl_sb = WL_CHANSPEC_CTL_SB_NONE; + + a = endp; + + c = tolower(a[0]); + if (c == '\0') + goto done; + + /* parse the optional ['A' | 'B'] band spec */ + if (c == 'a' || c == 'b') { + band = (c == 'a') ? WL_CHANSPEC_BAND_5G : WL_CHANSPEC_BAND_2G; + a++; + c = tolower(a[0]); + if (c == '\0') + goto done; + } + + /* parse bandwidth 'N' (10MHz) or 40MHz ctl sideband ['L' | 'U'] */ + if (c == 'n') { + bw = WL_CHANSPEC_BW_10; + } else if (c == 'l') { + bw = WL_CHANSPEC_BW_40; + ctl_sb = WL_CHANSPEC_CTL_SB_LOWER; + /* adjust channel to center of 40MHz band */ + if (channel <= (MAXCHANNEL - CH_20MHZ_APART)) + channel += CH_10MHZ_APART; + else + return 0; + } else if (c == 'u') { + bw = WL_CHANSPEC_BW_40; + ctl_sb = WL_CHANSPEC_CTL_SB_UPPER; + /* adjust channel to center of 40MHz band */ + if (channel > CH_20MHZ_APART) + channel -= CH_10MHZ_APART; + else + return 0; + } else { + return 0; + } + +done: + return (channel | band | bw | ctl_sb); +} + +/* + * Verify the chanspec is using a legal set of parameters, i.e. that the + * chanspec specified a band, bw, ctl_sb and channel and that the + * combination could be legal given any set of circumstances. + * RETURNS: TRUE is the chanspec is malformed, false if it looks good. + */ +bool +wf_chspec_malformed(chanspec_t chanspec) +{ + /* must be 2G or 5G band */ + if (!CHSPEC_IS5G(chanspec) && !CHSPEC_IS2G(chanspec)) + return TRUE; + /* must be 20 or 40 bandwidth */ + if (!CHSPEC_IS40(chanspec) && !CHSPEC_IS20(chanspec)) + return TRUE; + + /* 20MHZ b/w must have no ctl sb, 40 must have a ctl sb */ + if (CHSPEC_IS20(chanspec)) { + if (!CHSPEC_SB_NONE(chanspec)) + return TRUE; + } else { + if (!CHSPEC_SB_UPPER(chanspec) && !CHSPEC_SB_LOWER(chanspec)) + return TRUE; + } + + return FALSE; +} + +/* + * This function returns the channel number that control traffic is being sent on, for legacy + * channels this is just the channel number, for 40MHZ channels it is the upper or lower 20MHZ + * sideband depending on the chanspec selected + */ +uint8 +wf_chspec_ctlchan(chanspec_t chspec) +{ + uint8 ctl_chan; + + /* Is there a sideband ? */ + if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_NONE) { + return CHSPEC_CHANNEL(chspec); + } else { + /* we only support 40MHZ with sidebands */ + ASSERT(CHSPEC_BW(chspec) == WL_CHANSPEC_BW_40); + /* chanspec channel holds the centre frequency, use that and the + * side band information to reconstruct the control channel number + */ + if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_UPPER) { + /* control chan is the upper 20 MHZ SB of the 40MHZ channel */ + ctl_chan = UPPER_20_SB(CHSPEC_CHANNEL(chspec)); + } else { + ASSERT(CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_LOWER); + /* control chan is the lower 20 MHZ SB of the 40MHZ channel */ + ctl_chan = LOWER_20_SB(CHSPEC_CHANNEL(chspec)); + } + } + + return ctl_chan; +} + +chanspec_t +wf_chspec_ctlchspec(chanspec_t chspec) +{ + chanspec_t ctl_chspec = 0; + uint8 channel; + + ASSERT(!wf_chspec_malformed(chspec)); + + /* Is there a sideband ? */ + if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_NONE) { + return chspec; + } else { + if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_UPPER) { + channel = UPPER_20_SB(CHSPEC_CHANNEL(chspec)); + } else { + channel = LOWER_20_SB(CHSPEC_CHANNEL(chspec)); + } + ctl_chspec = channel | WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE; + ctl_chspec |= CHSPEC_BAND(chspec); + } + return ctl_chspec; +} + +#else /* D11AC_IOTYPES */ + +/* Definitions for D11AC capable Chanspec type */ + +/* Chanspec ASCII representation with 802.11ac capability: + * [ 'g'] ['/' []['/'<1st80channel>'-'<2nd80channel>]] + * + * : + * (optional) 2, 3, 4, 5 for 2.4GHz, 3GHz, 4GHz, and 5GHz respectively. + * Default value is 2g if channel <= 14, otherwise 5g. + * : + * channel number of the 5MHz, 10MHz, 20MHz channel, + * or primary channel of 40MHz, 80MHz, 160MHz, or 80+80MHz channel. + * : + * (optional) 5, 10, 20, 40, 80, 160, or 80+80. Default value is 20. + * : + * (only for 2.4GHz band 40MHz) U for upper sideband primary, L for lower. + * + * For 2.4GHz band 40MHz channels, the same primary channel may be the + * upper sideband for one 40MHz channel, and the lower sideband for an + * overlapping 40MHz channel. The U/L disambiguates which 40MHz channel + * is being specified. + * + * For 40MHz in the 5GHz band and all channel bandwidths greater than + * 40MHz, the U/L specificaion is not allowed since the channels are + * non-overlapping and the primary sub-band is derived from its + * position in the wide bandwidth channel. + * + * <1st80Channel>: + * <2nd80Channel>: + * Required for 80+80, otherwise not allowed. + * Specifies the center channel of the first and second 80MHz band. + * + * In its simplest form, it is a 20MHz channel number, with the implied band + * of 2.4GHz if channel number <= 14, and 5GHz otherwise. + * + * To allow for backward compatibility with scripts, the old form for + * 40MHz channels is also allowed: + * + * : + * primary channel of 40MHz, channel <= 14 is 2GHz, otherwise 5GHz + * : + * "U" for upper, "L" for lower (or lower case "u" "l") + * + * 5 GHz Examples: + * Chanspec BW Center Ch Channel Range Primary Ch + * 5g8 20MHz 8 - - + * 52 20MHz 52 - - + * 52/40 40MHz 54 52-56 52 + * 56/40 40MHz 54 52-56 56 + * 52/80 80MHz 58 52-64 52 + * 56/80 80MHz 58 52-64 56 + * 60/80 80MHz 58 52-64 60 + * 64/80 80MHz 58 52-64 64 + * 52/160 160MHz 50 36-64 52 + * 36/160 160MGz 50 36-64 36 + * 36/80+80/42-106 80+80MHz 42,106 36-48,100-112 36 + * + * 2 GHz Examples: + * Chanspec BW Center Ch Channel Range Primary Ch + * 2g8 20MHz 8 - - + * 8 20MHz 8 - - + * 6 20MHz 6 - - + * 6/40l 40MHz 8 6-10 6 + * 6l 40MHz 8 6-10 6 + * 6/40u 40MHz 4 2-6 6 + * 6u 40MHz 4 2-6 6 + */ + +/* bandwidth ASCII string */ +static const char *wf_chspec_bw_str[] = +{ + "5", + "10", + "20", + "40", + "80", + "160", + "80+80", + "na" +}; + +static const uint8 wf_chspec_bw_mhz[] = +{5, 10, 20, 40, 80, 160, 160}; + +#define WF_NUM_BW \ + (sizeof(wf_chspec_bw_mhz)/sizeof(uint8)) + +/* 40MHz channels in 5GHz band */ +static const uint8 wf_5g_40m_chans[] = +{38, 46, 54, 62, 102, 110, 118, 126, 134, 142, 151, 159}; +#define WF_NUM_5G_40M_CHANS \ + (sizeof(wf_5g_40m_chans)/sizeof(uint8)) + +/* 80MHz channels in 5GHz band */ +static const uint8 wf_5g_80m_chans[] = +{42, 58, 106, 122, 138, 155}; +#define WF_NUM_5G_80M_CHANS \ + (sizeof(wf_5g_80m_chans)/sizeof(uint8)) + +/* 160MHz channels in 5GHz band */ +static const uint8 wf_5g_160m_chans[] = +{50, 114}; +#define WF_NUM_5G_160M_CHANS \ + (sizeof(wf_5g_160m_chans)/sizeof(uint8)) + + +/* convert bandwidth from chanspec to MHz */ +static uint +bw_chspec_to_mhz(chanspec_t chspec) +{ + uint bw; + + bw = (chspec & WL_CHANSPEC_BW_MASK) >> WL_CHANSPEC_BW_SHIFT; + return (bw >= WF_NUM_BW ? 0 : wf_chspec_bw_mhz[bw]); +} + +/* bw in MHz, return the channel count from the center channel to the + * the channel at the edge of the band + */ +static uint8 +center_chan_to_edge(uint bw) +{ + /* edge channels separated by BW - 10MHz on each side + * delta from cf to edge is half of that, + * MHz to channel num conversion is 5MHz/channel + */ + return (uint8)(((bw - 20) / 2) / 5); +} + +/* return channel number of the low edge of the band + * given the center channel and BW + */ +static uint8 +channel_low_edge(uint center_ch, uint bw) +{ + return (uint8)(center_ch - center_chan_to_edge(bw)); +} + +/* return side band number given center channel and control channel + * return -1 on error + */ +static int +channel_to_sb(uint center_ch, uint ctl_ch, uint bw) +{ + uint lowest = channel_low_edge(center_ch, bw); + uint sb; + + if ((ctl_ch - lowest) % 4) { + /* bad ctl channel, not mult 4 */ + return -1; + } + + sb = ((ctl_ch - lowest) / 4); + + /* sb must be a index to a 20MHz channel in range */ + if (sb >= (bw / 20)) { + /* ctl_ch must have been too high for the center_ch */ + return -1; + } + + return sb; +} + +/* return control channel given center channel and side band */ +static uint8 +channel_to_ctl_chan(uint center_ch, uint bw, uint sb) +{ + return (uint8)(channel_low_edge(center_ch, bw) + sb * 4); +} + +/* return index of 80MHz channel from channel number + * return -1 on error + */ +static int +channel_80mhz_to_id(uint ch) +{ + uint i; + for (i = 0; i < WF_NUM_5G_80M_CHANS; i ++) { + if (ch == wf_5g_80m_chans[i]) + return i; + } + + return -1; +} + +/* given a chanspec and a string buffer, format the chanspec as a + * string, and return the original pointer a. + * Min buffer length must be CHANSPEC_STR_LEN. + * On error return NULL + */ +char * +wf_chspec_ntoa(chanspec_t chspec, char *buf) +{ + const char *band; + uint ctl_chan; + + if (wf_chspec_malformed(chspec)) + return NULL; + + band = ""; + + /* check for non-default band spec */ + if ((CHSPEC_IS2G(chspec) && CHSPEC_CHANNEL(chspec) > CH_MAX_2G_CHANNEL) || + (CHSPEC_IS5G(chspec) && CHSPEC_CHANNEL(chspec) <= CH_MAX_2G_CHANNEL)) + band = (CHSPEC_IS2G(chspec)) ? "2g" : "5g"; + + /* ctl channel */ + ctl_chan = wf_chspec_ctlchan(chspec); + + /* bandwidth and ctl sideband */ + if (CHSPEC_IS20(chspec)) { + snprintf(buf, CHANSPEC_STR_LEN, "%s%d", band, ctl_chan); + } else if (!CHSPEC_IS8080(chspec)) { + const char *bw; + const char *sb = ""; + + bw = wf_chspec_bw_str[(chspec & WL_CHANSPEC_BW_MASK) >> WL_CHANSPEC_BW_SHIFT]; + +#ifdef CHANSPEC_NEW_40MHZ_FORMAT + /* ctl sideband string if needed for 2g 40MHz */ + if (CHSPEC_IS40(chspec) && CHSPEC_IS2G(chspec)) { + sb = CHSPEC_SB_UPPER(chspec) ? "u" : "l"; + } + + snprintf(buf, CHANSPEC_STR_LEN, "%s%d/%s%s", band, ctl_chan, bw, sb); +#else + /* ctl sideband string instead of BW for 40MHz */ + if (CHSPEC_IS40(chspec)) { + sb = CHSPEC_SB_UPPER(chspec) ? "u" : "l"; + snprintf(buf, CHANSPEC_STR_LEN, "%s%d%s", band, ctl_chan, sb); + } else { + snprintf(buf, CHANSPEC_STR_LEN, "%s%d/%s", band, ctl_chan, bw); + } +#endif /* CHANSPEC_NEW_40MHZ_FORMAT */ + + } else { + /* 80+80 */ + uint chan1 = (chspec & WL_CHANSPEC_CHAN1_MASK) >> WL_CHANSPEC_CHAN1_SHIFT; + uint chan2 = (chspec & WL_CHANSPEC_CHAN2_MASK) >> WL_CHANSPEC_CHAN2_SHIFT; + + /* convert to channel number */ + chan1 = (chan1 < WF_NUM_5G_80M_CHANS) ? wf_5g_80m_chans[chan1] : 0; + chan2 = (chan2 < WF_NUM_5G_80M_CHANS) ? wf_5g_80m_chans[chan2] : 0; + + /* Outputs a max of CHANSPEC_STR_LEN chars including '\0' */ + snprintf(buf, CHANSPEC_STR_LEN, "%d/80+80/%d-%d", ctl_chan, chan1, chan2); + } + + return (buf); +} + +static int +read_uint(const char **p, unsigned int *num) +{ + unsigned long val; + char *endp = NULL; + + val = strtoul(*p, &endp, 10); + /* if endp is the initial pointer value, then a number was not read */ + if (endp == *p) + return 0; + + /* advance the buffer pointer to the end of the integer string */ + *p = endp; + /* return the parsed integer */ + *num = (unsigned int)val; + + return 1; +} + +/* given a chanspec string, convert to a chanspec. + * On error return 0 + */ +chanspec_t +wf_chspec_aton(const char *a) +{ + chanspec_t chspec; + uint chspec_ch, chspec_band, bw, chspec_bw, chspec_sb; + uint num, ctl_ch; + uint ch1, ch2; + char c, sb_ul = '\0'; + int i; + + bw = 20; + chspec_sb = 0; + chspec_ch = ch1 = ch2 = 0; + + /* parse channel num or band */ + if (!read_uint(&a, &num)) + return 0; + + /* if we are looking at a 'g', then the first number was a band */ + c = tolower(a[0]); + if (c == 'g') { + a ++; /* consume the char */ + + /* band must be "2" or "5" */ + if (num == 2) + chspec_band = WL_CHANSPEC_BAND_2G; + else if (num == 5) + chspec_band = WL_CHANSPEC_BAND_5G; + else + return 0; + + /* read the channel number */ + if (!read_uint(&a, &ctl_ch)) + return 0; + + c = tolower(a[0]); + } + else { + /* first number is channel, use default for band */ + ctl_ch = num; + chspec_band = ((ctl_ch <= CH_MAX_2G_CHANNEL) ? + WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G); + } + + if (c == '\0') { + /* default BW of 20MHz */ + chspec_bw = WL_CHANSPEC_BW_20; + goto done_read; + } + + a ++; /* consume the 'u','l', or '/' */ + + /* check 'u'/'l' */ + if (c == 'u' || c == 'l') { + sb_ul = c; + chspec_bw = WL_CHANSPEC_BW_40; + goto done_read; + } + + /* next letter must be '/' */ + if (c != '/') + return 0; + + /* read bandwidth */ + if (!read_uint(&a, &bw)) + return 0; + + /* convert to chspec value */ + if (bw == 20) { + chspec_bw = WL_CHANSPEC_BW_20; + } else if (bw == 40) { + chspec_bw = WL_CHANSPEC_BW_40; + } else if (bw == 80) { + chspec_bw = WL_CHANSPEC_BW_80; + } else if (bw == 160) { + chspec_bw = WL_CHANSPEC_BW_160; + } else { + return 0; + } + + /* So far we have g/ + * Can now be followed by u/l if bw = 40, + * or '+80' if bw = 80, to make '80+80' bw. + */ + + c = tolower(a[0]); + + /* if we have a 2g/40 channel, we should have a l/u spec now */ + if (chspec_band == WL_CHANSPEC_BAND_2G && bw == 40) { + if (c == 'u' || c == 'l') { + a ++; /* consume the u/l char */ + sb_ul = c; + goto done_read; + } + } + + /* check for 80+80 */ + if (c == '+') { + /* 80+80 */ + static const char *plus80 = "80/"; + + /* must be looking at '+80/' + * check and consume this string. + */ + chspec_bw = WL_CHANSPEC_BW_8080; + + a ++; /* consume the char '+' */ + + /* consume the '80/' string */ + for (i = 0; i < 3; i++) { + if (*a++ != *plus80++) { + return 0; + } + } + + /* read primary 80MHz channel */ + if (!read_uint(&a, &ch1)) + return 0; + + /* must followed by '-' */ + if (a[0] != '-') + return 0; + a ++; /* consume the char */ + + /* read secondary 80MHz channel */ + if (!read_uint(&a, &ch2)) + return 0; + } + +done_read: + /* skip trailing white space */ + while (a[0] == ' ') { + a ++; + } + + /* must be end of string */ + if (a[0] != '\0') + return 0; + + /* Now have all the chanspec string parts read; + * chspec_band, ctl_ch, chspec_bw, sb_ul, ch1, ch2. + * chspec_band and chspec_bw are chanspec values. + * Need to convert ctl_ch, sb_ul, and ch1,ch2 into + * a center channel (or two) and sideband. + */ + + /* if a sb u/l string was given, just use that, + * guaranteed to be bw = 40 by sting parse. + */ + if (sb_ul != '\0') { + if (sb_ul == 'l') { + chspec_ch = UPPER_20_SB(ctl_ch); + chspec_sb = WL_CHANSPEC_CTL_SB_LLL; + } else if (sb_ul == 'u') { + chspec_ch = LOWER_20_SB(ctl_ch); + chspec_sb = WL_CHANSPEC_CTL_SB_LLU; + } + } + /* if the bw is 20, center and sideband are trivial */ + else if (chspec_bw == WL_CHANSPEC_BW_20) { + chspec_ch = ctl_ch; + chspec_sb = 0; + } + /* if the bw is 40/80/160, not 80+80, a single method + * can be used to to find the center and sideband + */ + else if (chspec_bw != WL_CHANSPEC_BW_8080) { + /* figure out ctl sideband based on ctl channel and bandwidth */ + const uint8 *center_ch = NULL; + int num_ch = 0; + int sb = -1; + + if (chspec_bw == WL_CHANSPEC_BW_40) { + center_ch = wf_5g_40m_chans; + num_ch = WF_NUM_5G_40M_CHANS; + } else if (chspec_bw == WL_CHANSPEC_BW_80) { + center_ch = wf_5g_80m_chans; + num_ch = WF_NUM_5G_80M_CHANS; + } else if (chspec_bw == WL_CHANSPEC_BW_160) { + center_ch = wf_5g_160m_chans; + num_ch = WF_NUM_5G_160M_CHANS; + } else { + return 0; + } + + for (i = 0; i < num_ch; i ++) { + sb = channel_to_sb(center_ch[i], ctl_ch, bw); + if (sb >= 0) { + chspec_ch = center_ch[i]; + chspec_sb = sb << WL_CHANSPEC_CTL_SB_SHIFT; + break; + } + } + + /* check for no matching sb/center */ + if (sb < 0) { + return 0; + } + } + /* Otherwise, bw is 80+80. Figure out channel pair and sb */ + else { + int ch1_id = 0, ch2_id = 0; + int sb; + + ch1_id = channel_80mhz_to_id(ch1); + ch2_id = channel_80mhz_to_id(ch2); + + /* validate channels */ + if (ch1 >= ch2 || ch1_id < 0 || ch2_id < 0) + return 0; + + /* combined channel in chspec */ + chspec_ch = (((uint16)ch1_id << WL_CHANSPEC_CHAN1_SHIFT) | + ((uint16)ch2_id << WL_CHANSPEC_CHAN2_SHIFT)); + + /* figure out ctl sideband */ + + /* does the primary channel fit with the 1st 80MHz channel ? */ + sb = channel_to_sb(ch1, ctl_ch, bw); + if (sb < 0) { + /* no, so does the primary channel fit with the 2nd 80MHz channel ? */ + sb = channel_to_sb(ch2, ctl_ch, bw); + if (sb < 0) { + /* no match for ctl_ch to either 80MHz center channel */ + return 0; + } + /* sb index is 0-3 for the low 80MHz channel, and 4-7 for + * the high 80MHz channel. Add 4 to to shift to high set. + */ + sb += 4; + } + + chspec_sb = sb << WL_CHANSPEC_CTL_SB_SHIFT; + } + + chspec = (chspec_ch | chspec_band | chspec_bw | chspec_sb); + + if (wf_chspec_malformed(chspec)) + return 0; + + return chspec; +} + +/* + * Verify the chanspec is using a legal set of parameters, i.e. that the + * chanspec specified a band, bw, ctl_sb and channel and that the + * combination could be legal given any set of circumstances. + * RETURNS: TRUE is the chanspec is malformed, false if it looks good. + */ +bool +wf_chspec_malformed(chanspec_t chanspec) +{ + uint chspec_bw = CHSPEC_BW(chanspec); + uint chspec_ch = CHSPEC_CHANNEL(chanspec); + + /* must be 2G or 5G band */ + if (CHSPEC_IS2G(chanspec)) { + /* must be valid bandwidth */ + if (chspec_bw != WL_CHANSPEC_BW_20 && + chspec_bw != WL_CHANSPEC_BW_40) { + return TRUE; + } + } else if (CHSPEC_IS5G(chanspec)) { + if (chspec_bw == WL_CHANSPEC_BW_8080) { + uint ch1_id, ch2_id; + + /* channel number in 80+80 must be in range */ + ch1_id = CHSPEC_CHAN1(chanspec); + ch2_id = CHSPEC_CHAN2(chanspec); + if (ch1_id >= WF_NUM_5G_80M_CHANS || ch2_id >= WF_NUM_5G_80M_CHANS) + return TRUE; + + /* ch2 must be above ch1 for the chanspec */ + if (ch2_id <= ch1_id) + return TRUE; + } else if (chspec_bw == WL_CHANSPEC_BW_20 || chspec_bw == WL_CHANSPEC_BW_40 || + chspec_bw == WL_CHANSPEC_BW_80 || chspec_bw == WL_CHANSPEC_BW_160) { + + if (chspec_ch > MAXCHANNEL) { + return TRUE; + } + } else { + /* invalid bandwidth */ + return TRUE; + } + } else { + /* must be 2G or 5G band */ + return TRUE; + } + + /* side band needs to be consistent with bandwidth */ + if (chspec_bw == WL_CHANSPEC_BW_20) { + if (CHSPEC_CTL_SB(chanspec) != WL_CHANSPEC_CTL_SB_LLL) + return TRUE; + } else if (chspec_bw == WL_CHANSPEC_BW_40) { + if (CHSPEC_CTL_SB(chanspec) > WL_CHANSPEC_CTL_SB_LLU) + return TRUE; + } else if (chspec_bw == WL_CHANSPEC_BW_80) { + if (CHSPEC_CTL_SB(chanspec) > WL_CHANSPEC_CTL_SB_LUU) + return TRUE; + } + + return FALSE; +} + +/* + * Verify the chanspec specifies a valid channel according to 802.11. + * RETURNS: TRUE if the chanspec is a valid 802.11 channel + */ +bool +wf_chspec_valid(chanspec_t chanspec) +{ + uint chspec_bw = CHSPEC_BW(chanspec); + uint chspec_ch = CHSPEC_CHANNEL(chanspec); + + if (wf_chspec_malformed(chanspec)) + return FALSE; + + if (CHSPEC_IS2G(chanspec)) { + /* must be valid bandwidth and channel range */ + if (chspec_bw == WL_CHANSPEC_BW_20) { + if (chspec_ch >= 1 && chspec_ch <= 14) + return TRUE; + } else if (chspec_bw == WL_CHANSPEC_BW_40) { + if (chspec_ch >= 3 && chspec_ch <= 11) + return TRUE; + } + } else if (CHSPEC_IS5G(chanspec)) { + if (chspec_bw == WL_CHANSPEC_BW_8080) { + uint16 ch1, ch2; + + ch1 = wf_5g_80m_chans[CHSPEC_CHAN1(chanspec)]; + ch2 = wf_5g_80m_chans[CHSPEC_CHAN2(chanspec)]; + + /* the two channels must be separated by more than 80MHz by VHT req, + * and ch2 above ch1 for the chanspec + */ + if (ch2 > ch1 + CH_80MHZ_APART) + return TRUE; + } else { + const uint8 *center_ch; + uint num_ch, i; + + if (chspec_bw == WL_CHANSPEC_BW_20 || chspec_bw == WL_CHANSPEC_BW_40) { + center_ch = wf_5g_40m_chans; + num_ch = WF_NUM_5G_40M_CHANS; + } else if (chspec_bw == WL_CHANSPEC_BW_80) { + center_ch = wf_5g_80m_chans; + num_ch = WF_NUM_5G_80M_CHANS; + } else if (chspec_bw == WL_CHANSPEC_BW_160) { + center_ch = wf_5g_160m_chans; + num_ch = WF_NUM_5G_160M_CHANS; + } else { + /* invalid bandwidth */ + return FALSE; + } + + /* check for a valid center channel */ + if (chspec_bw == WL_CHANSPEC_BW_20) { + /* We don't have an array of legal 20MHz 5G channels, but they are + * each side of the legal 40MHz channels. Check the chanspec + * channel against either side of the 40MHz channels. + */ + for (i = 0; i < num_ch; i ++) { + if (chspec_ch == (uint)LOWER_20_SB(center_ch[i]) || + chspec_ch == (uint)UPPER_20_SB(center_ch[i])) + break; /* match found */ + } + + if (i == num_ch) { + /* check for legacy JP channels on failure */ + if (chspec_ch == 34 || chspec_ch == 38 || + chspec_ch == 42 || chspec_ch == 46) + i = 0; + } + } else { + /* check the chanspec channel to each legal channel */ + for (i = 0; i < num_ch; i ++) { + if (chspec_ch == center_ch[i]) + break; /* match found */ + } + } + + if (i < num_ch) { + /* match found */ + return TRUE; + } + } + } + + return FALSE; +} + +/* + * This function returns the channel number that control traffic is being sent on, for 20MHz + * channels this is just the channel number, for 40MHZ, 80MHz, 160MHz channels it is the 20MHZ + * sideband depending on the chanspec selected + */ +uint8 +wf_chspec_ctlchan(chanspec_t chspec) +{ + uint center_chan; + uint bw_mhz; + uint sb; + + ASSERT(!wf_chspec_malformed(chspec)); + + /* Is there a sideband ? */ + if (CHSPEC_IS20(chspec)) { + return CHSPEC_CHANNEL(chspec); + } else { + sb = CHSPEC_CTL_SB(chspec) >> WL_CHANSPEC_CTL_SB_SHIFT; + + if (CHSPEC_IS8080(chspec)) { + bw_mhz = 80; + + if (sb < 4) { + center_chan = CHSPEC_CHAN1(chspec); + } + else { + center_chan = CHSPEC_CHAN2(chspec); + sb -= 4; + } + + /* convert from channel index to channel number */ + center_chan = wf_5g_80m_chans[center_chan]; + } + else { + bw_mhz = bw_chspec_to_mhz(chspec); + center_chan = CHSPEC_CHANNEL(chspec) >> WL_CHANSPEC_CHAN_SHIFT; + } + + return (channel_to_ctl_chan(center_chan, bw_mhz, sb)); + } +} + +/* + * This function returns the chanspec of the control channel of a given chanspec + */ +chanspec_t +wf_chspec_ctlchspec(chanspec_t chspec) +{ + chanspec_t ctl_chspec = chspec; + uint8 ctl_chan; + + ASSERT(!wf_chspec_malformed(chspec)); + + /* Is there a sideband ? */ + if (!CHSPEC_IS20(chspec)) { + ctl_chan = wf_chspec_ctlchan(chspec); + ctl_chspec = ctl_chan | WL_CHANSPEC_BW_20; + ctl_chspec |= CHSPEC_BAND(chspec); + } + return ctl_chspec; +} + +/* return chanspec given control channel and bandwidth + * return 0 on error + */ +uint16 +wf_channel2chspec(uint ctl_ch, uint bw) +{ + uint16 chspec; + const uint8 *center_ch = NULL; + int num_ch = 0; + int sb = -1; + int i = 0; + + chspec = ((ctl_ch <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G); + + chspec |= bw; + + if (bw == WL_CHANSPEC_BW_40) { + center_ch = wf_5g_40m_chans; + num_ch = WF_NUM_5G_40M_CHANS; + bw = 40; + } else if (bw == WL_CHANSPEC_BW_80) { + center_ch = wf_5g_80m_chans; + num_ch = WF_NUM_5G_80M_CHANS; + bw = 80; + } else if (bw == WL_CHANSPEC_BW_160) { + center_ch = wf_5g_160m_chans; + num_ch = WF_NUM_5G_160M_CHANS; + bw = 160; + } else if (bw == WL_CHANSPEC_BW_20) { + chspec |= ctl_ch; + return chspec; + } else { + return 0; + } + + for (i = 0; i < num_ch; i ++) { + sb = channel_to_sb(center_ch[i], ctl_ch, bw); + if (sb >= 0) { + chspec |= center_ch[i]; + chspec |= (sb << WL_CHANSPEC_CTL_SB_SHIFT); + break; + } + } + + /* check for no matching sb/center */ + if (sb < 0) { + return 0; + } + + return chspec; +} +#endif /* D11AC_IOTYPES */ + +/* + * This function returns the chanspec for the primary 40MHz of an 80MHz channel. + * The control sideband specifies the same 20MHz channel that the 80MHz channel is using + * as the primary 20MHz channel. + */ +extern chanspec_t wf_chspec_primary40_chspec(chanspec_t chspec) +{ + chanspec_t chspec40 = chspec; + uint center_chan; + uint sb; + + ASSERT(!wf_chspec_malformed(chspec)); + + if (CHSPEC_IS80(chspec)) { + center_chan = CHSPEC_CHANNEL(chspec); + sb = CHSPEC_CTL_SB(chspec); + + if (sb == WL_CHANSPEC_CTL_SB_UL) { + /* Primary 40MHz is on upper side */ + sb = WL_CHANSPEC_CTL_SB_L; + center_chan += CH_20MHZ_APART; + } else if (sb == WL_CHANSPEC_CTL_SB_UU) { + /* Primary 40MHz is on upper side */ + sb = WL_CHANSPEC_CTL_SB_U; + center_chan += CH_20MHZ_APART; + } else { + /* Primary 40MHz is on lower side */ + /* sideband bits are the same for LL/LU and L/U */ + center_chan -= CH_20MHZ_APART; + } + + /* Create primary 40MHz chanspec */ + chspec40 = (WL_CHANSPEC_BAND_5G | WL_CHANSPEC_BW_40 | + sb | center_chan); + } + + return chspec40; +} + +/* + * Return the channel number for a given frequency and base frequency. + * The returned channel number is relative to the given base frequency. + * If the given base frequency is zero, a base frequency of 5 GHz is assumed for + * frequencies from 5 - 6 GHz, and 2.407 GHz is assumed for 2.4 - 2.5 GHz. + * + * Frequency is specified in MHz. + * The base frequency is specified as (start_factor * 500 kHz). + * Constants WF_CHAN_FACTOR_2_4_G, WF_CHAN_FACTOR_5_G are defined for + * 2.4 GHz and 5 GHz bands. + * + * The returned channel will be in the range [1, 14] in the 2.4 GHz band + * and [0, 200] otherwise. + * -1 is returned if the start_factor is WF_CHAN_FACTOR_2_4_G and the + * frequency is not a 2.4 GHz channel, or if the frequency is not and even + * multiple of 5 MHz from the base frequency to the base plus 1 GHz. + * + * Reference 802.11 REVma, section 17.3.8.3, and 802.11B section 18.4.6.2 + */ +int +wf_mhz2channel(uint freq, uint start_factor) +{ + int ch = -1; + uint base; + int offset; + + /* take the default channel start frequency */ + if (start_factor == 0) { + if (freq >= 2400 && freq <= 2500) + start_factor = WF_CHAN_FACTOR_2_4_G; + else if (freq >= 5000 && freq <= 6000) + start_factor = WF_CHAN_FACTOR_5_G; + } + + if (freq == 2484 && start_factor == WF_CHAN_FACTOR_2_4_G) + return 14; + + base = start_factor / 2; + + /* check that the frequency is in 1GHz range of the base */ + if ((freq < base) || (freq > base + 1000)) + return -1; + + offset = freq - base; + ch = offset / 5; + + /* check that frequency is a 5MHz multiple from the base */ + if (offset != (ch * 5)) + return -1; + + /* restricted channel range check for 2.4G */ + if (start_factor == WF_CHAN_FACTOR_2_4_G && (ch < 1 || ch > 13)) + return -1; + + return ch; +} + +/* + * Return the center frequency in MHz of the given channel and base frequency. + * The channel number is interpreted relative to the given base frequency. + * + * The valid channel range is [1, 14] in the 2.4 GHz band and [0, 200] otherwise. + * The base frequency is specified as (start_factor * 500 kHz). + * Constants WF_CHAN_FACTOR_2_4_G, WF_CHAN_FACTOR_4_G, and WF_CHAN_FACTOR_5_G + * are defined for 2.4 GHz, 4 GHz, and 5 GHz bands. + * The channel range of [1, 14] is only checked for a start_factor of + * WF_CHAN_FACTOR_2_4_G (4814 = 2407 * 2). + * Odd start_factors produce channels on .5 MHz boundaries, in which case + * the answer is rounded down to an integral MHz. + * -1 is returned for an out of range channel. + * + * Reference 802.11 REVma, section 17.3.8.3, and 802.11B section 18.4.6.2 + */ +int +wf_channel2mhz(uint ch, uint start_factor) +{ + int freq; + + if ((start_factor == WF_CHAN_FACTOR_2_4_G && (ch < 1 || ch > 14)) || + (ch > 200)) + freq = -1; + else if ((start_factor == WF_CHAN_FACTOR_2_4_G) && (ch == 14)) + freq = 2484; + else + freq = ch * 5 + start_factor / 2; + + return freq; +} diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h new file mode 100644 index 00000000..84866162 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd.h @@ -0,0 +1,875 @@ +/* + * Header file describing the internal (inter-module) DHD interfaces. + * + * Provides type definitions and function prototypes used to link the + * DHD OS, bus, and protocol modules. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhd.h 373887 2012-12-10 21:58:02Z $ + */ + +/**************** + * Common types * + */ + +#ifndef _dhd_h_ +#define _dhd_h_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_HAS_WAKELOCK) +#include +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined (CONFIG_HAS_WAKELOCK) */ +/* The kernel threading is sdio-specific */ +struct task_struct; +struct sched_param; +int setScheduler(struct task_struct *p, int policy, struct sched_param *param); + +#define ALL_INTERFACES 0xff + +#include +#include + + +/* Forward decls */ +struct dhd_bus; +struct dhd_prot; +struct dhd_info; + +/* The level of bus communication with the dongle */ +enum dhd_bus_state { + DHD_BUS_DOWN, /* Not ready for frame transfers */ + DHD_BUS_LOAD, /* Download access only (CPU reset) */ + DHD_BUS_DATA /* Ready for frame transfers */ +}; + +enum dhd_op_flags { +/* Firmware requested operation mode */ + DHD_FLAG_STA_MODE = BIT(0), /* STA only */ + DHD_FLAG_HOSTAP_MODE = BIT(1), /* SOFTAP only */ + DHD_FLAG_P2P_MODE = BIT(2), /* P2P Only */ + /* STA + P2P */ + DHD_FLAG_CONCURR_SINGLE_CHAN_MODE = (DHD_FLAG_STA_MODE | DHD_FLAG_P2P_MODE), + DHD_FLAG_CONCURR_MULTI_CHAN_MODE = BIT(4), /* STA + P2P */ + /* Current P2P mode for P2P connection */ + DHD_FLAG_P2P_GC_MODE = BIT(5), + DHD_FLAG_P2P_GO_MODE = BIT(6), + DHD_FLAG_MBSS_MODE = BIT(7) /* MBSS in future */ +}; + +#define MANUFACTRING_FW "WLTEST" + +/* max sequential rxcntl timeouts to set HANG event */ +#ifndef MAX_CNTL_TIMEOUT +#define MAX_CNTL_TIMEOUT 2 +#endif + +#define DHD_SCAN_ASSOC_ACTIVE_TIME 40 /* ms: Embedded default Active setting from DHD */ +#define DHD_SCAN_UNASSOC_ACTIVE_TIME 80 /* ms: Embedded def. Unassoc Active setting from DHD */ +#define DHD_SCAN_PASSIVE_TIME 130 /* ms: Embedded default Passive setting from DHD */ + +#ifndef POWERUP_MAX_RETRY +#define POWERUP_MAX_RETRY 3 /* how many times we retry to power up the chip */ +#endif +#ifndef POWERUP_WAIT_MS +#define POWERUP_WAIT_MS 2000 /* ms: time out in waiting wifi to come up */ +#endif + +enum dhd_bus_wake_state { + WAKE_LOCK_OFF, + WAKE_LOCK_PRIV, + WAKE_LOCK_DPC, + WAKE_LOCK_IOCTL, + WAKE_LOCK_DOWNLOAD, + WAKE_LOCK_TMOUT, + WAKE_LOCK_WATCHDOG, + WAKE_LOCK_LINK_DOWN_TMOUT, + WAKE_LOCK_PNO_FIND_TMOUT, + WAKE_LOCK_SOFTAP_SET, + WAKE_LOCK_SOFTAP_STOP, + WAKE_LOCK_SOFTAP_START, + WAKE_LOCK_SOFTAP_THREAD, + WAKE_LOCK_MAX +}; + +enum dhd_prealloc_index { + DHD_PREALLOC_PROT = 0, + DHD_PREALLOC_RXBUF, + DHD_PREALLOC_DATABUF, +#if defined(STATIC_WL_PRIV_STRUCT) + DHD_PREALLOC_OSL_BUF, + DHD_PREALLOC_WIPHY_ESCAN0 = 5, +#else + DHD_PREALLOC_OSL_BUF +#endif /* STATIC_WL_PRIV_STRUCT */ +}; + +typedef enum { + DHD_IF_NONE = 0, + DHD_IF_ADD, + DHD_IF_DEL, + DHD_IF_CHANGE, + DHD_IF_DELETING +} dhd_if_state_t; + + +#if defined(CONFIG_DHD_USE_STATIC_BUF) + +uint8* dhd_os_prealloc(void *osh, int section, uint size); +void dhd_os_prefree(void *osh, void *addr, uint size); +#define DHD_OS_PREALLOC(osh, section, size) dhd_os_prealloc(osh, section, size) +#define DHD_OS_PREFREE(osh, addr, size) dhd_os_prefree(osh, addr, size) + +#else + +#define DHD_OS_PREALLOC(osh, section, size) MALLOC(osh, size) +#define DHD_OS_PREFREE(osh, addr, size) MFREE(osh, addr, size) + +#endif /* defined(CONFIG_DHD_USE_STATIC_BUF) */ + +/* Packet alignment for most efficient SDIO (can change based on platform) */ +#ifndef DHD_SDALIGN +#define DHD_SDALIGN 32 +#endif + +/* host reordering packts logic */ +/* followed the structure to hold the reorder buffers (void **p) */ +typedef struct reorder_info { + void **p; + uint8 flow_id; + uint8 cur_idx; + uint8 exp_idx; + uint8 max_idx; + uint8 pend_pkts; +} reorder_info_t; + +/* Common structure for module and instance linkage */ +typedef struct dhd_pub { + /* Linkage ponters */ + osl_t *osh; /* OSL handle */ + struct dhd_bus *bus; /* Bus module handle */ + struct dhd_prot *prot; /* Protocol module handle */ + struct dhd_info *info; /* Info module handle */ + + /* Internal dhd items */ + bool up; /* Driver up/down (to OS) */ + bool txoff; /* Transmit flow-controlled */ + bool dongle_reset; /* TRUE = DEVRESET put dongle into reset */ + enum dhd_bus_state busstate; + uint hdrlen; /* Total DHD header length (proto + bus) */ + uint maxctl; /* Max size rxctl request from proto to bus */ + uint rxsz; /* Rx buffer size bus module should use */ + uint8 wme_dp; /* wme discard priority */ + + /* Dongle media info */ + bool iswl; /* Dongle-resident driver is wl */ + ulong drv_version; /* Version of dongle-resident driver */ + struct ether_addr mac; /* MAC address obtained from dongle */ + dngl_stats_t dstats; /* Stats for dongle-based data */ + + /* Additional stats for the bus level */ + ulong tx_packets; /* Data packets sent to dongle */ + ulong tx_multicast; /* Multicast data packets sent to dongle */ + ulong tx_errors; /* Errors in sending data to dongle */ + ulong tx_ctlpkts; /* Control packets sent to dongle */ + ulong tx_ctlerrs; /* Errors sending control frames to dongle */ + ulong rx_packets; /* Packets sent up the network interface */ + ulong rx_multicast; /* Multicast packets sent up the network interface */ + ulong rx_errors; /* Errors processing rx data packets */ + ulong rx_ctlpkts; /* Control frames processed from dongle */ + ulong rx_ctlerrs; /* Errors in processing rx control frames */ + ulong rx_dropped; /* Packets dropped locally (no memory) */ + ulong rx_flushed; /* Packets flushed due to unscheduled sendup thread */ + ulong wd_dpc_sched; /* Number of times dhd dpc scheduled by watchdog timer */ + + ulong rx_readahead_cnt; /* Number of packets where header read-ahead was used. */ + ulong tx_realloc; /* Number of tx packets we had to realloc for headroom */ + ulong fc_packets; /* Number of flow control pkts recvd */ + + /* Last error return */ + int bcmerror; + uint tickcnt; + + /* Last error from dongle */ + int dongle_error; + + uint8 country_code[WLC_CNTRY_BUF_SZ]; + + /* Suspend disable flag and "in suspend" flag */ + int suspend_disable_flag; /* "1" to disable all extra powersaving during suspend */ + int in_suspend; /* flag set to 1 when early suspend called */ +#ifdef PNO_SUPPORT + int pno_enable; /* pno status : "1" is pno enable */ + int pno_suspend; /* pno suspend status : "1" is pno suspended */ +#endif /* PNO_SUPPORT */ + /* DTIM skip value, default 0(or 1) means wake each DTIM + * 3 means skip 2 DTIMs and wake up 3rd DTIM(9th beacon when AP DTIM is 3) + */ + int suspend_bcn_li_dtim; /* bcn_li_dtim value in suspend mode */ +#ifdef PKT_FILTER_SUPPORT + int early_suspended; /* Early suspend status */ + int dhcp_in_progress; /* DHCP period */ +#endif + + /* Pkt filter defination */ + char * pktfilter[100]; + int pktfilter_count; + + wl_country_t dhd_cspec; /* Current Locale info */ + char eventmask[WL_EVENTING_MASK_LEN]; + int op_mode; /* STA, HostAPD, WFD, SoftAP */ + +/* Set this to 1 to use a seperate interface (p2p0) for p2p operations. + * For ICS MR1 releases it should be disable to be compatable with ICS MR1 Framework + * see target dhd-cdc-sdmmc-panda-cfg80211-icsmr1-gpl-debug in Makefile + */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 + struct mutex wl_start_stop_lock; /* lock/unlock for Android start/stop */ + struct mutex wl_softap_lock; /* lock/unlock for any SoftAP/STA settings */ +#endif + +#ifdef WLBTAMP + uint16 maxdatablks; +#endif /* WLBTAMP */ +#ifdef PROP_TXSTATUS + int wlfc_enabled; + void* wlfc_state; +#endif + bool dongle_isolation; + bool dongle_trap_occured; /* flag for sending HANG event to upper layer */ + int hang_was_sent; + int rxcnt_timeout; /* counter rxcnt timeout to send HANG */ + int txcnt_timeout; /* counter txcnt timeout to send HANG */ +#ifdef WLMEDIA_HTSF + uint8 htsfdlystat_sz; /* Size of delay stats, max 255B */ +#endif + struct reorder_info *reorder_bufs[WLHOST_REORDERDATA_MAXFLOWS]; +#if defined(ARP_OFFLOAD_SUPPORT) + uint32 arp_version; +#endif +} dhd_pub_t; + + + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) + + #define DHD_PM_RESUME_WAIT_INIT(a) DECLARE_WAIT_QUEUE_HEAD(a); + #define _DHD_PM_RESUME_WAIT(a, b) do {\ + int retry = 0; \ + SMP_RD_BARRIER_DEPENDS(); \ + while (dhd_mmc_suspend && retry++ != b) { \ + SMP_RD_BARRIER_DEPENDS(); \ + wait_event_interruptible_timeout(a, !dhd_mmc_suspend, 1); \ + } \ + } while (0) + #define DHD_PM_RESUME_WAIT(a) _DHD_PM_RESUME_WAIT(a, 200) + #define DHD_PM_RESUME_WAIT_FOREVER(a) _DHD_PM_RESUME_WAIT(a, ~0) + #define DHD_PM_RESUME_RETURN_ERROR(a) do { if (dhd_mmc_suspend) return a; } while (0) + #define DHD_PM_RESUME_RETURN do { if (dhd_mmc_suspend) return; } while (0) + + #define DHD_SPINWAIT_SLEEP_INIT(a) DECLARE_WAIT_QUEUE_HEAD(a); + #define SPINWAIT_SLEEP(a, exp, us) do { \ + uint countdown = (us) + 9999; \ + while ((exp) && (countdown >= 10000)) { \ + wait_event_interruptible_timeout(a, FALSE, 1); \ + countdown -= 10000; \ + } \ + } while (0) + + #else + + #define DHD_PM_RESUME_WAIT_INIT(a) + #define DHD_PM_RESUME_WAIT(a) + #define DHD_PM_RESUME_WAIT_FOREVER(a) + #define DHD_PM_RESUME_RETURN_ERROR(a) + #define DHD_PM_RESUME_RETURN + + #define DHD_SPINWAIT_SLEEP_INIT(a) + #define SPINWAIT_SLEEP(a, exp, us) do { \ + uint countdown = (us) + 9; \ + while ((exp) && (countdown >= 10)) { \ + OSL_DELAY(10); \ + countdown -= 10; \ + } \ + } while (0) + + #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ +#ifndef DHDTHREAD +#undef SPINWAIT_SLEEP +#define SPINWAIT_SLEEP(a, exp, us) SPINWAIT(exp, us) +#endif /* DHDTHREAD */ +#define DHD_IF_VIF 0x01 /* Virtual IF (Hidden from user) */ + +unsigned long dhd_os_spin_lock(dhd_pub_t *pub); +void dhd_os_spin_unlock(dhd_pub_t *pub, unsigned long flags); + +/* Wakelock Functions */ +extern int dhd_os_wake_lock(dhd_pub_t *pub); +extern int dhd_os_wake_unlock(dhd_pub_t *pub); +extern int dhd_os_wake_lock_timeout(dhd_pub_t *pub); +extern int dhd_os_wake_lock_rx_timeout_enable(dhd_pub_t *pub, int val); +extern int dhd_os_wake_lock_ctrl_timeout_enable(dhd_pub_t *pub, int val); +extern int dhd_os_wd_wake_lock(dhd_pub_t *pub); +extern int dhd_os_wd_wake_unlock(dhd_pub_t *pub); + +inline static void MUTEX_LOCK_SOFTAP_SET_INIT(dhd_pub_t * dhdp) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 + mutex_init(&dhdp->wl_softap_lock); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ +} + +inline static void MUTEX_LOCK_SOFTAP_SET(dhd_pub_t * dhdp) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 + mutex_lock(&dhdp->wl_softap_lock); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ +} + +inline static void MUTEX_UNLOCK_SOFTAP_SET(dhd_pub_t * dhdp) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 + mutex_unlock(&dhdp->wl_softap_lock); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ +} + +#define DHD_OS_WAKE_LOCK(pub) dhd_os_wake_lock(pub) +#define DHD_OS_WAKE_UNLOCK(pub) dhd_os_wake_unlock(pub) +#define DHD_OS_WD_WAKE_LOCK(pub) dhd_os_wd_wake_lock(pub) +#define DHD_OS_WD_WAKE_UNLOCK(pub) dhd_os_wd_wake_unlock(pub) +#define DHD_OS_WAKE_LOCK_TIMEOUT(pub) dhd_os_wake_lock_timeout(pub) +#define DHD_OS_WAKE_LOCK_RX_TIMEOUT_ENABLE(pub, val) \ + dhd_os_wake_lock_rx_timeout_enable(pub, val) +#define DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(pub, val) \ + dhd_os_wake_lock_ctrl_timeout_enable(pub, val) +#define DHD_PACKET_TIMEOUT_MS 1000 +#define DHD_EVENT_TIMEOUT_MS 1500 + +/* interface operations (register, remove) should be atomic, use this lock to prevent race + * condition among wifi on/off and interface operation functions + */ +void dhd_net_if_lock(struct net_device *dev); +void dhd_net_if_unlock(struct net_device *dev); + +typedef struct dhd_if_event { + uint8 ifidx; + uint8 action; + uint8 flags; + uint8 bssidx; + uint8 is_AP; +} dhd_if_event_t; + +typedef enum dhd_attach_states +{ + DHD_ATTACH_STATE_INIT = 0x0, + DHD_ATTACH_STATE_NET_ALLOC = 0x1, + DHD_ATTACH_STATE_DHD_ALLOC = 0x2, + DHD_ATTACH_STATE_ADD_IF = 0x4, + DHD_ATTACH_STATE_PROT_ATTACH = 0x8, + DHD_ATTACH_STATE_WL_ATTACH = 0x10, + DHD_ATTACH_STATE_THREADS_CREATED = 0x20, + DHD_ATTACH_STATE_WAKELOCKS_INIT = 0x40, + DHD_ATTACH_STATE_CFG80211 = 0x80, + DHD_ATTACH_STATE_EARLYSUSPEND_DONE = 0x100, + DHD_ATTACH_STATE_DONE = 0x200 +} dhd_attach_states_t; + +/* Value -1 means we are unsuccessful in creating the kthread. */ +#define DHD_PID_KT_INVALID -1 +/* Value -2 means we are unsuccessful in both creating the kthread and tasklet */ +#define DHD_PID_KT_TL_INVALID -2 + +/* + * Exported from dhd OS modules (dhd_linux/dhd_ndis) + */ + +/* To allow osl_attach/detach calls from os-independent modules */ +osl_t *dhd_osl_attach(void *pdev, uint bustype); +void dhd_osl_detach(osl_t *osh); + +/* Indication from bus module regarding presence/insertion of dongle. + * Return dhd_pub_t pointer, used as handle to OS module in later calls. + * Returned structure should have bus and prot pointers filled in. + * bus_hdrlen specifies required headroom for bus module header. + */ +extern dhd_pub_t *dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen); +#if defined(WLP2P) && defined(WL_CFG80211) +/* To allow attach/detach calls corresponding to p2p0 interface */ +extern int dhd_attach_p2p(dhd_pub_t *); +extern int dhd_detach_p2p(dhd_pub_t *); +#endif /* WLP2P && WL_CFG80211 */ +extern int dhd_net_attach(dhd_pub_t *dhdp, int idx); + +/* Indication from bus module regarding removal/absence of dongle */ +extern void dhd_detach(dhd_pub_t *dhdp); +extern void dhd_free(dhd_pub_t *dhdp); + +/* Indication from bus module to change flow-control state */ +extern void dhd_txflowcontrol(dhd_pub_t *dhdp, int ifidx, bool on); + +extern bool dhd_prec_enq(dhd_pub_t *dhdp, struct pktq *q, void *pkt, int prec); + +/* Receive frame for delivery to OS. Callee disposes of rxp. */ +extern void dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *rxp, int numpkt, uint8 chan); + +/* Return pointer to interface name */ +extern char *dhd_ifname(dhd_pub_t *dhdp, int idx); + +/* Request scheduling of the bus dpc */ +extern void dhd_sched_dpc(dhd_pub_t *dhdp); + +/* Notify tx completion */ +extern void dhd_txcomplete(dhd_pub_t *dhdp, void *txp, bool success); + +/* OS independent layer functions */ +extern int dhd_os_proto_block(dhd_pub_t * pub); +extern int dhd_os_proto_unblock(dhd_pub_t * pub); +extern int dhd_os_ioctl_resp_wait(dhd_pub_t * pub, uint * condition, bool * pending); +extern int dhd_os_ioctl_resp_wake(dhd_pub_t * pub); +extern unsigned int dhd_os_get_ioctl_resp_timeout(void); +extern void dhd_os_set_ioctl_resp_timeout(unsigned int timeout_msec); +extern void * dhd_os_open_image(char * filename); +extern int dhd_os_get_image_block(char * buf, int len, void * image); +extern void dhd_os_close_image(void * image); +extern void dhd_os_wd_timer(void *bus, uint wdtick); +extern void dhd_os_sdlock(dhd_pub_t * pub); +extern void dhd_os_sdunlock(dhd_pub_t * pub); +extern void dhd_os_sdlock_txq(dhd_pub_t * pub); +extern void dhd_os_sdunlock_txq(dhd_pub_t * pub); +extern void dhd_os_sdlock_rxq(dhd_pub_t * pub); +extern void dhd_os_sdunlock_rxq(dhd_pub_t * pub); +extern void dhd_os_sdlock_sndup_rxq(dhd_pub_t * pub); +extern void dhd_customer_gpio_wlan_ctrl(int onoff); +extern int dhd_custom_get_mac_address(unsigned char *buf); +extern void dhd_os_sdunlock_sndup_rxq(dhd_pub_t * pub); +extern void dhd_os_sdlock_eventq(dhd_pub_t * pub); +extern void dhd_os_sdunlock_eventq(dhd_pub_t * pub); +extern bool dhd_os_check_hang(dhd_pub_t *dhdp, int ifidx, int ret); +extern int dhd_os_send_hang_message(dhd_pub_t *dhdp); +extern int net_os_send_hang_message(struct net_device *dev); +extern void dhd_set_version_info(dhd_pub_t *pub, char *fw); + +#ifdef PNO_SUPPORT +extern int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled); +extern int dhd_pno_clean(dhd_pub_t *dhd); +extern int dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, + ushort scan_fr, int pno_repeat, int pno_freq_expo_max); +extern int dhd_pno_get_status(dhd_pub_t *dhd); +extern int dhd_dev_pno_reset(struct net_device *dev); +extern int dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, + int nssid, ushort scan_fr, int pno_repeat, int pno_freq_expo_max); +extern int dhd_dev_pno_enable(struct net_device *dev, int pfn_enabled); +extern int dhd_dev_get_pno_status(struct net_device *dev); +#endif /* PNO_SUPPORT */ + +#ifdef PKT_FILTER_SUPPORT +#define DHD_UNICAST_FILTER_NUM 0 +#define DHD_BROADCAST_FILTER_NUM 1 +#define DHD_MULTICAST4_FILTER_NUM 2 +#define DHD_MULTICAST6_FILTER_NUM 3 +#define DHD_MDNS_FILTER_NUM 4 +extern int dhd_os_enable_packet_filter(dhd_pub_t *dhdp, int val); +extern void dhd_enable_packet_filter(int value, dhd_pub_t *dhd); +extern int net_os_enable_packet_filter(struct net_device *dev, int val); +extern int net_os_rxfilter_add_remove(struct net_device *dev, int val, int num); +#endif /* PKT_FILTER_SUPPORT */ + +extern int dhd_get_suspend_bcn_li_dtim(dhd_pub_t *dhd); +extern bool dhd_support_sta_mode(dhd_pub_t *dhd); + +#ifdef DHD_DEBUG +extern int write_to_file(dhd_pub_t *dhd, uint8 *buf, int size); +#endif /* DHD_DEBUG */ +#if defined(OOB_INTR_ONLY) +extern int dhd_customer_oob_irq_map(unsigned long *irq_flags_ptr); +#endif /* defined(OOB_INTR_ONLY) */ +extern void dhd_os_sdtxlock(dhd_pub_t * pub); +extern void dhd_os_sdtxunlock(dhd_pub_t * pub); + +typedef struct { + uint32 limit; /* Expiration time (usec) */ + uint32 increment; /* Current expiration increment (usec) */ + uint32 elapsed; /* Current elapsed time (usec) */ + uint32 tick; /* O/S tick time (usec) */ +} dhd_timeout_t; + +extern void dhd_timeout_start(dhd_timeout_t *tmo, uint usec); +extern int dhd_timeout_expired(dhd_timeout_t *tmo); + +extern int dhd_ifname2idx(struct dhd_info *dhd, char *name); +extern int dhd_net2idx(struct dhd_info *dhd, struct net_device *net); +extern struct net_device * dhd_idx2net(void *pub, int ifidx); +extern int wl_host_event(dhd_pub_t *dhd_pub, int *idx, void *pktdata, + wl_event_msg_t *, void **data_ptr); +extern void wl_event_to_host_order(wl_event_msg_t * evt); + +extern int dhd_wl_ioctl(dhd_pub_t *dhd_pub, int ifindex, wl_ioctl_t *ioc, void *buf, int len); +extern int dhd_wl_ioctl_cmd(dhd_pub_t *dhd_pub, int cmd, void *arg, int len, uint8 set, + int ifindex); + +extern void dhd_common_init(osl_t *osh); + +extern int dhd_do_driver_init(struct net_device *net); +extern int dhd_add_if(struct dhd_info *dhd, int ifidx, void *handle, + char *name, uint8 *mac_addr, uint32 flags, uint8 bssidx); +extern void dhd_del_if(struct dhd_info *dhd, int ifidx); + +extern void dhd_vif_add(struct dhd_info *dhd, int ifidx, char * name); +extern void dhd_vif_del(struct dhd_info *dhd, int ifidx); + +extern void dhd_event(struct dhd_info *dhd, char *evpkt, int evlen, int ifidx); +extern void dhd_vif_sendup(struct dhd_info *dhd, int ifidx, uchar *cp, int len); + + +/* Send packet to dongle via data channel */ +extern int dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pkt); + +/* send up locally generated event */ +extern void dhd_sendup_event_common(dhd_pub_t *dhdp, wl_event_msg_t *event, void *data); +/* Send event to host */ +extern void dhd_sendup_event(dhd_pub_t *dhdp, wl_event_msg_t *event, void *data); +extern int dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag); +extern uint dhd_bus_status(dhd_pub_t *dhdp); +extern int dhd_bus_start(dhd_pub_t *dhdp); +extern int dhd_bus_membytes(dhd_pub_t *dhdp, bool set, uint32 address, uint8 *data, uint size); +extern void dhd_print_buf(void *pbuf, int len, int bytes_per_line); +extern bool dhd_is_associated(dhd_pub_t *dhd, void *bss_buf, int *retval); +extern uint dhd_bus_chip_id(dhd_pub_t *dhdp); +extern uint dhd_bus_chiprev_id(dhd_pub_t *dhdp); +extern uint dhd_bus_chippkg_id(dhd_pub_t *dhdp); + +#if defined(KEEP_ALIVE) +extern int dhd_keep_alive_onoff(dhd_pub_t *dhd); +#endif /* KEEP_ALIVE */ + +extern bool dhd_is_concurrent_mode(dhd_pub_t *dhd); + +typedef enum cust_gpio_modes { + WLAN_RESET_ON, + WLAN_RESET_OFF, + WLAN_POWER_ON, + WLAN_POWER_OFF +} cust_gpio_modes_t; + +extern int wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag); +extern int wl_iw_send_priv_event(struct net_device *dev, char *flag); +/* + * Insmod parameters for debug/test + */ + +/* Watchdog timer interval */ +extern uint dhd_watchdog_ms; + +#if defined(DHD_DEBUG) +/* Console output poll interval */ +extern uint dhd_console_ms; +extern uint wl_msg_level; +#endif /* defined(DHD_DEBUG) */ + +extern uint dhd_slpauto; + +/* Use interrupts */ +extern uint dhd_intr; + +/* Use polling */ +extern uint dhd_poll; + +/* ARP offload agent mode */ +extern uint dhd_arp_mode; + +/* ARP offload enable */ +extern uint dhd_arp_enable; + +/* Pkt filte enable control */ +extern uint dhd_pkt_filter_enable; + +/* Pkt filter init setup */ +extern uint dhd_pkt_filter_init; + +/* Pkt filter mode control */ +extern uint dhd_master_mode; + +/* Roaming mode control */ +extern uint dhd_roam_disable; + +/* Roaming mode control */ +extern uint dhd_radio_up; + +/* Initial idletime ticks (may be -1 for immediate idle, 0 for no idle) */ +extern int dhd_idletime; +#ifdef DHD_USE_IDLECOUNT +#define DHD_IDLETIME_TICKS 5 +#else +#define DHD_IDLETIME_TICKS 1 +#endif /* DHD_USE_IDLECOUNT */ + +/* SDIO Drive Strength */ +extern uint dhd_sdiod_drive_strength; + +/* Override to force tx queueing all the time */ +extern uint dhd_force_tx_queueing; +/* Default KEEP_ALIVE Period is 55 sec to prevent AP from sending Keep Alive probe frame */ +#define DEFAULT_KEEP_ALIVE_VALUE 55000 /* msec */ +#ifndef CUSTOM_KEEP_ALIVE_SETTING +#define CUSTOM_KEEP_ALIVE_SETTING DEFAULT_KEEP_ALIVE_VALUE +#endif /* DEFAULT_KEEP_ALIVE_VALUE */ + +#define NULL_PKT_STR "null_pkt" + +/* hooks for custom glom setting option via Makefile */ +#define DEFAULT_GLOM_VALUE -1 +#ifndef CUSTOM_GLOM_SETTING +#define CUSTOM_GLOM_SETTING DEFAULT_GLOM_VALUE +#endif + +/* hooks for custom Roaming Trigger setting via Makefile */ +#define DEFAULT_ROAM_TRIGGER_VALUE -75 /* dBm default roam trigger all band */ +#define DEFAULT_ROAM_TRIGGER_SETTING -1 +#ifndef CUSTOM_ROAM_TRIGGER_SETTING +#define CUSTOM_ROAM_TRIGGER_SETTING DEFAULT_ROAM_TRIGGER_VALUE +#endif + +/* hooks for custom Roaming Romaing setting via Makefile */ +#define DEFAULT_ROAM_DELTA_VALUE 10 /* dBm default roam delta all band */ +#define DEFAULT_ROAM_DELTA_SETTING -1 +#ifndef CUSTOM_ROAM_DELTA_SETTING +#define CUSTOM_ROAM_DELTA_SETTING DEFAULT_ROAM_DELTA_VALUE +#endif + +/* hooks for custom PNO Event wake lock to guarantee enough time + for the Platform to detect Event before system suspended +*/ +#define DEFAULT_PNO_EVENT_LOCK_xTIME 2 /* multiplay of DHD_PACKET_TIMEOUT_MS */ +#ifndef CUSTOM_PNO_EVENT_LOCK_xTIME +#define CUSTOM_PNO_EVENT_LOCK_xTIME DEFAULT_PNO_EVENT_LOCK_xTIME +#endif + +/* hooks for custom dhd_dpc_prio setting option via Makefile */ +#define DEFAULT_DHP_DPC_PRIO 1 +#ifndef CUSTOM_DPC_PRIO_SETTING +#define CUSTOM_DPC_PRIO_SETTING DEFAULT_DHP_DPC_PRIO +#endif + +#define DEFAULT_SUSPEND_BCN_LI_DTIM 3 +#ifndef CUSTOM_SUSPEND_BCN_LI_DTIM +#define CUSTOM_SUSPEND_BCN_LI_DTIM DEFAULT_SUSPEND_BCN_LI_DTIM +#endif + +#define MAX_DTIM_SKIP_BEACON_ITERVAL 100 /* max allowed associated AP beacon for dtim skip */ + +#ifdef SDTEST +/* Echo packet generator (SDIO), pkts/s */ +extern uint dhd_pktgen; + +/* Echo packet len (0 => sawtooth, max 1800) */ +extern uint dhd_pktgen_len; +#define MAX_PKTGEN_LEN 1800 +#endif + + +/* optionally set by a module_param_string() */ +#define MOD_PARAM_PATHLEN 2048 +extern char fw_path[MOD_PARAM_PATHLEN]; +extern char nv_path[MOD_PARAM_PATHLEN]; + +#define MOD_PARAM_INFOLEN 512 + +#ifdef SOFTAP +extern char fw_path2[MOD_PARAM_PATHLEN]; +#endif + +/* Flag to indicate if we should download firmware on driver load */ +extern uint dhd_download_fw_on_driverload; + + +/* For supporting multiple interfaces */ +#define DHD_MAX_IFS 16 +#define DHD_DEL_IF -0xe +#define DHD_BAD_IF -0xf +#define WL_AUTO_ROAM_TRIGGER -75 + +#ifdef PROP_TXSTATUS +/* Please be mindful that total pkttag space is 32 octets only */ +typedef struct dhd_pkttag { + /* + b[11 ] - 1 = this packet was sent in response to one time packet request, + do not increment credit on status for this one. [WLFC_CTL_TYPE_MAC_REQUEST_PACKET]. + b[10 ] - 1 = signal-only-packet to firmware [i.e. nothing to piggyback on] + b[9 ] - 1 = packet is host->firmware (transmit direction) + - 0 = packet received from firmware (firmware->host) + b[8 ] - 1 = packet was sent due to credit_request (pspoll), + packet does not count against FIFO credit. + - 0 = normal transaction, packet counts against FIFO credit + b[7 ] - 1 = AP, 0 = STA + b[6:4] - AC FIFO number + b[3:0] - interface index + */ + uint16 if_flags; + /* destination MAC address for this packet so that not every + module needs to open the packet to find this + */ + uint8 dstn_ether[ETHER_ADDR_LEN]; + /* + This 32-bit goes from host to device for every packet. + */ + uint32 htod_tag; + /* bus specific stuff */ + union { + struct { + void* stuff; + uint32 thing1; + uint32 thing2; + } sd; + struct { + void* bus; + void* urb; + } usb; + } bus_specific; +} dhd_pkttag_t; + +#define DHD_PKTTAG_SET_H2DTAG(tag, h2dvalue) ((dhd_pkttag_t*)(tag))->htod_tag = (h2dvalue) +#define DHD_PKTTAG_H2DTAG(tag) (((dhd_pkttag_t*)(tag))->htod_tag) + +#define DHD_PKTTAG_IFMASK 0xf +#define DHD_PKTTAG_IFTYPE_MASK 0x1 +#define DHD_PKTTAG_IFTYPE_SHIFT 7 +#define DHD_PKTTAG_FIFO_MASK 0x7 +#define DHD_PKTTAG_FIFO_SHIFT 4 + +#define DHD_PKTTAG_SIGNALONLY_MASK 0x1 +#define DHD_PKTTAG_SIGNALONLY_SHIFT 10 + +#define DHD_PKTTAG_ONETIMEPKTRQST_MASK 0x1 +#define DHD_PKTTAG_ONETIMEPKTRQST_SHIFT 11 + +#define DHD_PKTTAG_PKTDIR_MASK 0x1 +#define DHD_PKTTAG_PKTDIR_SHIFT 9 + +#define DHD_PKTTAG_CREDITCHECK_MASK 0x1 +#define DHD_PKTTAG_CREDITCHECK_SHIFT 8 + +#define DHD_PKTTAG_INVALID_FIFOID 0x7 + +#define DHD_PKTTAG_SETFIFO(tag, fifo) ((dhd_pkttag_t*)(tag))->if_flags = \ + (((dhd_pkttag_t*)(tag))->if_flags & ~(DHD_PKTTAG_FIFO_MASK << DHD_PKTTAG_FIFO_SHIFT)) | \ + (((fifo) & DHD_PKTTAG_FIFO_MASK) << DHD_PKTTAG_FIFO_SHIFT) +#define DHD_PKTTAG_FIFO(tag) ((((dhd_pkttag_t*)(tag))->if_flags >> \ + DHD_PKTTAG_FIFO_SHIFT) & DHD_PKTTAG_FIFO_MASK) + +#define DHD_PKTTAG_SETIF(tag, if) ((dhd_pkttag_t*)(tag))->if_flags = \ + (((dhd_pkttag_t*)(tag))->if_flags & ~DHD_PKTTAG_IFMASK) | ((if) & DHD_PKTTAG_IFMASK) +#define DHD_PKTTAG_IF(tag) (((dhd_pkttag_t*)(tag))->if_flags & DHD_PKTTAG_IFMASK) + +#define DHD_PKTTAG_SETIFTYPE(tag, isAP) ((dhd_pkttag_t*)(tag))->if_flags = \ + (((dhd_pkttag_t*)(tag))->if_flags & \ + ~(DHD_PKTTAG_IFTYPE_MASK << DHD_PKTTAG_IFTYPE_SHIFT)) | \ + (((isAP) & DHD_PKTTAG_IFTYPE_MASK) << DHD_PKTTAG_IFTYPE_SHIFT) +#define DHD_PKTTAG_IFTYPE(tag) ((((dhd_pkttag_t*)(tag))->if_flags >> \ + DHD_PKTTAG_IFTYPE_SHIFT) & DHD_PKTTAG_IFTYPE_MASK) + +#define DHD_PKTTAG_SETCREDITCHECK(tag, check) ((dhd_pkttag_t*)(tag))->if_flags = \ + (((dhd_pkttag_t*)(tag))->if_flags & \ + ~(DHD_PKTTAG_CREDITCHECK_MASK << DHD_PKTTAG_CREDITCHECK_SHIFT)) | \ + (((check) & DHD_PKTTAG_CREDITCHECK_MASK) << DHD_PKTTAG_CREDITCHECK_SHIFT) +#define DHD_PKTTAG_CREDITCHECK(tag) ((((dhd_pkttag_t*)(tag))->if_flags >> \ + DHD_PKTTAG_CREDITCHECK_SHIFT) & DHD_PKTTAG_CREDITCHECK_MASK) + +#define DHD_PKTTAG_SETPKTDIR(tag, dir) ((dhd_pkttag_t*)(tag))->if_flags = \ + (((dhd_pkttag_t*)(tag))->if_flags & \ + ~(DHD_PKTTAG_PKTDIR_MASK << DHD_PKTTAG_PKTDIR_SHIFT)) | \ + (((dir) & DHD_PKTTAG_PKTDIR_MASK) << DHD_PKTTAG_PKTDIR_SHIFT) +#define DHD_PKTTAG_PKTDIR(tag) ((((dhd_pkttag_t*)(tag))->if_flags >> \ + DHD_PKTTAG_PKTDIR_SHIFT) & DHD_PKTTAG_PKTDIR_MASK) + +#define DHD_PKTTAG_SETSIGNALONLY(tag, signalonly) ((dhd_pkttag_t*)(tag))->if_flags = \ + (((dhd_pkttag_t*)(tag))->if_flags & \ + ~(DHD_PKTTAG_SIGNALONLY_MASK << DHD_PKTTAG_SIGNALONLY_SHIFT)) | \ + (((signalonly) & DHD_PKTTAG_SIGNALONLY_MASK) << DHD_PKTTAG_SIGNALONLY_SHIFT) +#define DHD_PKTTAG_SIGNALONLY(tag) ((((dhd_pkttag_t*)(tag))->if_flags >> \ + DHD_PKTTAG_SIGNALONLY_SHIFT) & DHD_PKTTAG_SIGNALONLY_MASK) + +#define DHD_PKTTAG_SETONETIMEPKTRQST(tag) ((dhd_pkttag_t*)(tag))->if_flags = \ + (((dhd_pkttag_t*)(tag))->if_flags & \ + ~(DHD_PKTTAG_ONETIMEPKTRQST_MASK << DHD_PKTTAG_ONETIMEPKTRQST_SHIFT)) | \ + (1 << DHD_PKTTAG_ONETIMEPKTRQST_SHIFT) +#define DHD_PKTTAG_ONETIMEPKTRQST(tag) ((((dhd_pkttag_t*)(tag))->if_flags >> \ + DHD_PKTTAG_ONETIMEPKTRQST_SHIFT) & DHD_PKTTAG_ONETIMEPKTRQST_MASK) + +#define DHD_PKTTAG_SETDSTN(tag, dstn_MAC_ea) memcpy(((dhd_pkttag_t*)((tag)))->dstn_ether, \ + (dstn_MAC_ea), ETHER_ADDR_LEN) +#define DHD_PKTTAG_DSTN(tag) ((dhd_pkttag_t*)(tag))->dstn_ether + +typedef int (*f_commitpkt_t)(void* ctx, void* p); + +#ifdef PROP_TXSTATUS_DEBUG +#define DHD_WLFC_CTRINC_MAC_CLOSE(entry) do { (entry)->closed_ct++; } while (0) +#define DHD_WLFC_CTRINC_MAC_OPEN(entry) do { (entry)->opened_ct++; } while (0) +#else +#define DHD_WLFC_CTRINC_MAC_CLOSE(entry) do {} while (0) +#define DHD_WLFC_CTRINC_MAC_OPEN(entry) do {} while (0) +#endif + +#endif /* PROP_TXSTATUS */ + +extern void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar); +extern void dhd_wait_event_wakeup(dhd_pub_t*dhd); + +#define IFLOCK_INIT(lock) *lock = 0 +#define IFLOCK(lock) while (InterlockedCompareExchange((lock), 1, 0)) \ + NdisStallExecution(1); +#define IFUNLOCK(lock) InterlockedExchange((lock), 0) +#define IFLOCK_FREE(lock) + +#ifdef PNO_SUPPORT +extern int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled); +extern int dhd_pnoenable(dhd_pub_t *dhd, int pfn_enabled); +extern int dhd_pno_clean(dhd_pub_t *dhd); +extern int dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, + ushort scan_fr, int pno_repeat, int pno_freq_expo_max); +extern int dhd_pno_get_status(dhd_pub_t *dhd); +extern int dhd_pno_set_add(dhd_pub_t *dhd, wl_pfn_t *netinfo, int nssid, ushort scan_fr, + ushort slowscan_fr, uint8 pno_repeat, uint8 pno_freq_expo_max, int16 flags); +extern int dhd_pno_cfg(dhd_pub_t *dhd, wl_pfn_cfg_t *pcfg); +extern int dhd_pno_suspend(dhd_pub_t *dhd, int pfn_suspend); +#endif /* PNO_SUPPORT */ +#ifdef ARP_OFFLOAD_SUPPORT +#define MAX_IPV4_ENTRIES 8 +void dhd_arp_offload_set(dhd_pub_t * dhd, int arp_mode); +void dhd_arp_offload_enable(dhd_pub_t * dhd, int arp_enable); + +/* dhd_commn arp offload wrapers */ +void dhd_aoe_hostip_clr(dhd_pub_t *dhd, int idx); +void dhd_aoe_arp_clr(dhd_pub_t *dhd, int idx); +int dhd_arp_get_arp_hostip_table(dhd_pub_t *dhd, void *buf, int buflen, int idx); +void dhd_arp_offload_add_ip(dhd_pub_t *dhd, uint32 ipaddr, int idx); +#endif /* ARP_OFFLOAD_SUPPORT */ + +#endif /* _dhd_h_ */ diff --git a/drivers/net/wireless/bcmdhd/dhd_bta.c b/drivers/net/wireless/bcmdhd/dhd_bta.c new file mode 100644 index 00000000..15c605ea --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_bta.c @@ -0,0 +1,338 @@ +/* + * BT-AMP support routines + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhd_bta.c 303834 2011-12-20 06:17:39Z $ + */ +#ifndef WLBTAMP +#error "WLBTAMP is not defined" +#endif /* WLBTAMP */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#ifdef SEND_HCI_CMD_VIA_IOCTL +#define BTA_HCI_CMD_MAX_LEN HCI_CMD_PREAMBLE_SIZE + HCI_CMD_DATA_SIZE + +/* Send HCI cmd via wl iovar HCI_cmd to the dongle. */ +int +dhd_bta_docmd(dhd_pub_t *pub, void *cmd_buf, uint cmd_len) +{ + amp_hci_cmd_t *cmd = (amp_hci_cmd_t *)cmd_buf; + uint8 buf[BTA_HCI_CMD_MAX_LEN + 16]; + uint len = sizeof(buf); + wl_ioctl_t ioc; + + if (cmd_len < HCI_CMD_PREAMBLE_SIZE) + return BCME_BADLEN; + + if ((uint)cmd->plen + HCI_CMD_PREAMBLE_SIZE > cmd_len) + return BCME_BADLEN; + + len = bcm_mkiovar("HCI_cmd", + (char *)cmd, (uint)cmd->plen + HCI_CMD_PREAMBLE_SIZE, (char *)buf, len); + + + memset(&ioc, 0, sizeof(ioc)); + + ioc.cmd = WLC_SET_VAR; + ioc.buf = buf; + ioc.len = len; + ioc.set = TRUE; + + return dhd_wl_ioctl(pub, &ioc, ioc.buf, ioc.len); +} +#else /* !SEND_HCI_CMD_VIA_IOCTL */ + +static void +dhd_bta_flush_hcidata(dhd_pub_t *pub, uint16 llh) +{ + int prec; + struct pktq *q; + uint count = 0; + + q = dhd_bus_txq(pub->bus); + if (q == NULL) + return; + + DHD_BTA(("dhd: flushing HCI ACL data for logical link %u...\n", llh)); + + dhd_os_sdlock_txq(pub); + + /* Walk through the txq and toss all HCI ACL data packets */ + PKTQ_PREC_ITER(q, prec) { + void *head_pkt = NULL; + + while (pktq_ppeek(q, prec) != head_pkt) { + void *pkt = pktq_pdeq(q, prec); + int ifidx; + + PKTPULL(pub->osh, pkt, dhd_bus_hdrlen(pub->bus)); + dhd_prot_hdrpull(pub, &ifidx, pkt, NULL, NULL); + + if (PKTLEN(pub->osh, pkt) >= RFC1042_HDR_LEN) { + struct ether_header *eh = + (struct ether_header *)PKTDATA(pub->osh, pkt); + + if (ntoh16(eh->ether_type) < ETHER_TYPE_MIN) { + struct dot11_llc_snap_header *lsh = + (struct dot11_llc_snap_header *)&eh[1]; + + if (bcmp(lsh, BT_SIG_SNAP_MPROT, + DOT11_LLC_SNAP_HDR_LEN - 2) == 0 && + ntoh16(lsh->type) == BTA_PROT_L2CAP) { + amp_hci_ACL_data_t *ACL_data = + (amp_hci_ACL_data_t *)&lsh[1]; + uint16 handle = ltoh16(ACL_data->handle); + + if (HCI_ACL_DATA_HANDLE(handle) == llh) { + PKTFREE(pub->osh, pkt, TRUE); + count ++; + continue; + } + } + } + } + + dhd_prot_hdrpush(pub, ifidx, pkt); + PKTPUSH(pub->osh, pkt, dhd_bus_hdrlen(pub->bus)); + + if (head_pkt == NULL) + head_pkt = pkt; + pktq_penq(q, prec, pkt); + } + } + + dhd_os_sdunlock_txq(pub); + + DHD_BTA(("dhd: flushed %u packet(s) for logical link %u...\n", count, llh)); +} + +/* Handle HCI cmd locally. + * Return 0: continue to send the cmd across SDIO + * < 0: stop, fail + * > 0: stop, succuess + */ +static int +_dhd_bta_docmd(dhd_pub_t *pub, amp_hci_cmd_t *cmd) +{ + int status = 0; + + switch (ltoh16_ua((uint8 *)&cmd->opcode)) { + case HCI_Enhanced_Flush: { + eflush_cmd_parms_t *cmdparms = (eflush_cmd_parms_t *)cmd->parms; + dhd_bta_flush_hcidata(pub, ltoh16_ua(cmdparms->llh)); + break; + } + default: + break; + } + + return status; +} + +/* Send HCI cmd encapsulated in BT-SIG frame via data channel to the dongle. */ +int +dhd_bta_docmd(dhd_pub_t *pub, void *cmd_buf, uint cmd_len) +{ + amp_hci_cmd_t *cmd = (amp_hci_cmd_t *)cmd_buf; + struct ether_header *eh; + struct dot11_llc_snap_header *lsh; + osl_t *osh = pub->osh; + uint len; + void *p; + int status; + + if (cmd_len < HCI_CMD_PREAMBLE_SIZE) { + DHD_ERROR(("dhd_bta_docmd: short command, cmd_len %u\n", cmd_len)); + return BCME_BADLEN; + } + + if ((len = (uint)cmd->plen + HCI_CMD_PREAMBLE_SIZE) > cmd_len) { + DHD_ERROR(("dhd_bta_docmd: malformed command, len %u cmd_len %u\n", + len, cmd_len)); + /* return BCME_BADLEN; */ + } + + p = PKTGET(osh, pub->hdrlen + RFC1042_HDR_LEN + len, TRUE); + if (p == NULL) { + DHD_ERROR(("dhd_bta_docmd: out of memory\n")); + return BCME_NOMEM; + } + + + /* intercept and handle the HCI cmd locally */ + if ((status = _dhd_bta_docmd(pub, cmd)) > 0) + return 0; + else if (status < 0) + return status; + + /* copy in HCI cmd */ + PKTPULL(osh, p, pub->hdrlen + RFC1042_HDR_LEN); + bcopy(cmd, PKTDATA(osh, p), len); + + /* copy in partial Ethernet header with BT-SIG LLC/SNAP header */ + PKTPUSH(osh, p, RFC1042_HDR_LEN); + eh = (struct ether_header *)PKTDATA(osh, p); + bzero(eh->ether_dhost, ETHER_ADDR_LEN); + ETHER_SET_LOCALADDR(eh->ether_dhost); + bcopy(&pub->mac, eh->ether_shost, ETHER_ADDR_LEN); + eh->ether_type = hton16(len + DOT11_LLC_SNAP_HDR_LEN); + lsh = (struct dot11_llc_snap_header *)&eh[1]; + bcopy(BT_SIG_SNAP_MPROT, lsh, DOT11_LLC_SNAP_HDR_LEN - 2); + lsh->type = 0; + + return dhd_sendpkt(pub, 0, p); +} +#endif /* !SEND_HCI_CMD_VIA_IOCTL */ + +/* Send HCI ACL data to dongle via data channel */ +int +dhd_bta_tx_hcidata(dhd_pub_t *pub, void *data_buf, uint data_len) +{ + amp_hci_ACL_data_t *data = (amp_hci_ACL_data_t *)data_buf; + struct ether_header *eh; + struct dot11_llc_snap_header *lsh; + osl_t *osh = pub->osh; + uint len; + void *p; + + if (data_len < HCI_ACL_DATA_PREAMBLE_SIZE) { + DHD_ERROR(("dhd_bta_tx_hcidata: short data_buf, data_len %u\n", data_len)); + return BCME_BADLEN; + } + + if ((len = (uint)ltoh16(data->dlen) + HCI_ACL_DATA_PREAMBLE_SIZE) > data_len) { + DHD_ERROR(("dhd_bta_tx_hcidata: malformed hci data, len %u data_len %u\n", + len, data_len)); + /* return BCME_BADLEN; */ + } + + p = PKTGET(osh, pub->hdrlen + RFC1042_HDR_LEN + len, TRUE); + if (p == NULL) { + DHD_ERROR(("dhd_bta_tx_hcidata: out of memory\n")); + return BCME_NOMEM; + } + + + /* copy in HCI ACL data header and HCI ACL data */ + PKTPULL(osh, p, pub->hdrlen + RFC1042_HDR_LEN); + bcopy(data, PKTDATA(osh, p), len); + + /* copy in partial Ethernet header with BT-SIG LLC/SNAP header */ + PKTPUSH(osh, p, RFC1042_HDR_LEN); + eh = (struct ether_header *)PKTDATA(osh, p); + bzero(eh->ether_dhost, ETHER_ADDR_LEN); + bcopy(&pub->mac, eh->ether_shost, ETHER_ADDR_LEN); + eh->ether_type = hton16(len + DOT11_LLC_SNAP_HDR_LEN); + lsh = (struct dot11_llc_snap_header *)&eh[1]; + bcopy(BT_SIG_SNAP_MPROT, lsh, DOT11_LLC_SNAP_HDR_LEN - 2); + lsh->type = HTON16(BTA_PROT_L2CAP); + + return dhd_sendpkt(pub, 0, p); +} + +/* txcomplete callback */ +void +dhd_bta_tx_hcidata_complete(dhd_pub_t *dhdp, void *txp, bool success) +{ + uint8 *pktdata = (uint8 *)PKTDATA(dhdp->osh, txp); + amp_hci_ACL_data_t *ACL_data = (amp_hci_ACL_data_t *)(pktdata + RFC1042_HDR_LEN); + uint16 handle = ltoh16(ACL_data->handle); + uint16 llh = HCI_ACL_DATA_HANDLE(handle); + + wl_event_msg_t event; + uint8 data[HCI_EVT_PREAMBLE_SIZE + sizeof(num_completed_data_blocks_evt_parms_t)]; + amp_hci_event_t *evt; + num_completed_data_blocks_evt_parms_t *parms; + + uint16 len = HCI_EVT_PREAMBLE_SIZE + sizeof(num_completed_data_blocks_evt_parms_t); + + /* update the event struct */ + memset(&event, 0, sizeof(event)); + event.version = hton16(BCM_EVENT_MSG_VERSION); + event.event_type = hton32(WLC_E_BTA_HCI_EVENT); + event.status = 0; + event.reason = 0; + event.auth_type = 0; + event.datalen = hton32(len); + event.flags = 0; + + /* generate Number of Completed Blocks event */ + evt = (amp_hci_event_t *)data; + evt->ecode = HCI_Number_of_Completed_Data_Blocks; + evt->plen = sizeof(num_completed_data_blocks_evt_parms_t); + + parms = (num_completed_data_blocks_evt_parms_t *)evt->parms; + htol16_ua_store(dhdp->maxdatablks, (uint8 *)&parms->num_blocks); + parms->num_handles = 1; + htol16_ua_store(llh, (uint8 *)&parms->completed[0].handle); + parms->completed[0].pkts = 1; + parms->completed[0].blocks = 1; + + dhd_sendup_event_common(dhdp, &event, data); +} + +/* event callback */ +void +dhd_bta_doevt(dhd_pub_t *dhdp, void *data_buf, uint data_len) +{ + amp_hci_event_t *evt = (amp_hci_event_t *)data_buf; + + switch (evt->ecode) { + case HCI_Command_Complete: { + cmd_complete_parms_t *parms = (cmd_complete_parms_t *)evt->parms; + switch (ltoh16_ua((uint8 *)&parms->opcode)) { + case HCI_Read_Data_Block_Size: { + read_data_block_size_evt_parms_t *parms2 = + (read_data_block_size_evt_parms_t *)parms->parms; + dhdp->maxdatablks = ltoh16_ua((uint8 *)&parms2->data_block_num); + break; + } + } + break; + } + + case HCI_Flush_Occurred: { + flush_occurred_evt_parms_t *evt_parms = (flush_occurred_evt_parms_t *)evt->parms; + dhd_bta_flush_hcidata(dhdp, ltoh16_ua((uint8 *)&evt_parms->handle)); + break; + } + default: + break; + } +} diff --git a/drivers/net/wireless/bcmdhd/dhd_bta.h b/drivers/net/wireless/bcmdhd/dhd_bta.h new file mode 100644 index 00000000..0337f15d --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_bta.h @@ -0,0 +1,39 @@ +/* + * BT-AMP support routines + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhd_bta.h 291086 2011-10-21 01:17:24Z $ + */ +#ifndef __dhd_bta_h__ +#define __dhd_bta_h__ + +struct dhd_pub; + +extern int dhd_bta_docmd(struct dhd_pub *pub, void *cmd_buf, uint cmd_len); + +extern void dhd_bta_doevt(struct dhd_pub *pub, void *data_buf, uint data_len); + +extern int dhd_bta_tx_hcidata(struct dhd_pub *pub, void *data_buf, uint data_len); +extern void dhd_bta_tx_hcidata_complete(struct dhd_pub *dhdp, void *txp, bool success); + + +#endif /* __dhd_bta_h__ */ diff --git a/drivers/net/wireless/bcmdhd/dhd_bus.h b/drivers/net/wireless/bcmdhd/dhd_bus.h new file mode 100644 index 00000000..fcb4bbd6 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_bus.h @@ -0,0 +1,111 @@ +/* + * Header file describing the internal (inter-module) DHD interfaces. + * + * Provides type definitions and function prototypes used to link the + * DHD OS, bus, and protocol modules. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhd_bus.h 347614 2012-07-27 10:24:51Z $ + */ + +#ifndef _dhd_bus_h_ +#define _dhd_bus_h_ + +/* + * Exported from dhd bus module (dhd_usb, dhd_sdio) + */ + +/* Indicate (dis)interest in finding dongles. */ +extern int dhd_bus_register(void); +extern void dhd_bus_unregister(void); + +/* Download firmware image and nvram image */ +extern bool dhd_bus_download_firmware(struct dhd_bus *bus, osl_t *osh, + char *fw_path, char *nv_path); + +/* Stop bus module: clear pending frames, disable data flow */ +extern void dhd_bus_stop(struct dhd_bus *bus, bool enforce_mutex); + +/* Initialize bus module: prepare for communication w/dongle */ +extern int dhd_bus_init(dhd_pub_t *dhdp, bool enforce_mutex); + +/* Get the Bus Idle Time */ +extern void dhd_bus_getidletime(dhd_pub_t *dhdp, int *idletime); + +/* Set the Bus Idle Time */ +extern void dhd_bus_setidletime(dhd_pub_t *dhdp, int idle_time); + +/* Send a data frame to the dongle. Callee disposes of txp. */ +extern int dhd_bus_txdata(struct dhd_bus *bus, void *txp); + +/* Send/receive a control message to/from the dongle. + * Expects caller to enforce a single outstanding transaction. + */ +extern int dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen); +extern int dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen); + +/* Watchdog timer function */ +extern bool dhd_bus_watchdog(dhd_pub_t *dhd); +extern void dhd_disable_intr(dhd_pub_t *dhd); + +#if defined(DHD_DEBUG) +/* Device console input function */ +extern int dhd_bus_console_in(dhd_pub_t *dhd, uchar *msg, uint msglen); +#endif /* defined(DHD_DEBUG) */ + +/* Deferred processing for the bus, return TRUE requests reschedule */ +extern bool dhd_bus_dpc(struct dhd_bus *bus); +extern void dhd_bus_isr(bool * InterruptRecognized, bool * QueueMiniportHandleInterrupt, void *arg); + + +/* Check for and handle local prot-specific iovar commands */ +extern int dhd_bus_iovar_op(dhd_pub_t *dhdp, const char *name, + void *params, int plen, void *arg, int len, bool set); + +/* Add bus dump output to a buffer */ +extern void dhd_bus_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf); + +/* Clear any bus counters */ +extern void dhd_bus_clearcounts(dhd_pub_t *dhdp); + +/* return the dongle chipid */ +extern uint dhd_bus_chip(struct dhd_bus *bus); + +/* Set user-specified nvram parameters. */ +extern void dhd_bus_set_nvram_params(struct dhd_bus * bus, const char *nvram_params); + +extern void *dhd_bus_pub(struct dhd_bus *bus); +extern void *dhd_bus_txq(struct dhd_bus *bus); +extern uint dhd_bus_hdrlen(struct dhd_bus *bus); + + +#define DHD_SET_BUS_STATE_DOWN(_bus) do { \ + (_bus)->dhd->busstate = DHD_BUS_DOWN; \ +} while (0) + +/* Register a dummy SDIO client driver in order to be notified of new SDIO device */ +extern int dhd_bus_reg_sdio_notify(void* semaphore); +extern void dhd_bus_unreg_sdio_notify(void); + +extern void dhd_txglom_enable(dhd_pub_t *dhdp, bool enable); + +#endif /* _dhd_bus_h_ */ diff --git a/drivers/net/wireless/bcmdhd/dhd_cdc.c b/drivers/net/wireless/bcmdhd/dhd_cdc.c new file mode 100644 index 00000000..b51dbc6d --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_cdc.c @@ -0,0 +1,3189 @@ +/* + * DHD Protocol Module for CDC and BDC. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhd_cdc.c 368762 2012-11-14 21:59:17Z $ + * + * BDC is like CDC, except it includes a header for data packets to convey + * packet priority over the bus, and flags (e.g. to indicate checksum status + * for dongle offload.) + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + + +#ifdef PROP_TXSTATUS +#include +#include +#endif + + +#define RETRIES 2 /* # of retries to retrieve matching ioctl response */ +#define BUS_HEADER_LEN (24+DHD_SDALIGN) /* Must be at least SDPCM_RESERVE + * defined in dhd_sdio.c (amount of header tha might be added) + * plus any space that might be needed for alignment padding. + */ +#define ROUND_UP_MARGIN 2048 /* Biggest SDIO block size possible for + * round off at the end of buffer + */ + +#define BUS_RETRIES 1 /* # of retries before aborting a bus tx operation */ + +#ifdef PROP_TXSTATUS +typedef struct dhd_wlfc_commit_info { + uint8 needs_hdr; + uint8 ac_fifo_credit_spent; + ewlfc_packet_state_t pkt_type; + wlfc_mac_descriptor_t* mac_entry; + void* p; +} dhd_wlfc_commit_info_t; +#endif /* PROP_TXSTATUS */ + + +typedef struct dhd_prot { + uint16 reqid; + uint8 pending; + uint32 lastcmd; + uint8 bus_header[BUS_HEADER_LEN]; + cdc_ioctl_t msg; + unsigned char buf[WLC_IOCTL_MAXLEN + ROUND_UP_MARGIN]; +} dhd_prot_t; + + +static int +dhdcdc_msg(dhd_pub_t *dhd) +{ + int err = 0; + dhd_prot_t *prot = dhd->prot; + int len = ltoh32(prot->msg.len) + sizeof(cdc_ioctl_t); + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + DHD_OS_WAKE_LOCK(dhd); + + /* NOTE : cdc->msg.len holds the desired length of the buffer to be + * returned. Only up to CDC_MAX_MSG_SIZE of this buffer area + * is actually sent to the dongle + */ + if (len > CDC_MAX_MSG_SIZE) + len = CDC_MAX_MSG_SIZE; + + /* Send request */ + err = dhd_bus_txctl(dhd->bus, (uchar*)&prot->msg, len); + + DHD_OS_WAKE_UNLOCK(dhd); + return err; +} + +static int +dhdcdc_cmplt(dhd_pub_t *dhd, uint32 id, uint32 len) +{ + int ret; + int cdc_len = len + sizeof(cdc_ioctl_t); + dhd_prot_t *prot = dhd->prot; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + do { + ret = dhd_bus_rxctl(dhd->bus, (uchar*)&prot->msg, cdc_len); + if (ret < 0) + break; + } while (CDC_IOC_ID(ltoh32(prot->msg.flags)) != id); + + return ret; +} + +static int +dhdcdc_query_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len, uint8 action) +{ + dhd_prot_t *prot = dhd->prot; + cdc_ioctl_t *msg = &prot->msg; + void *info; + int ret = 0, retries = 0; + uint32 id, flags = 0; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len)); + + + /* Respond "bcmerror" and "bcmerrorstr" with local cache */ + if (cmd == WLC_GET_VAR && buf) + { + if (!strcmp((char *)buf, "bcmerrorstr")) + { + strncpy((char *)buf, bcmerrorstr(dhd->dongle_error), BCME_STRLEN); + goto done; + } + else if (!strcmp((char *)buf, "bcmerror")) + { + *(int *)buf = dhd->dongle_error; + goto done; + } + } + + memset(msg, 0, sizeof(cdc_ioctl_t)); + + msg->cmd = htol32(cmd); + msg->len = htol32(len); + msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT); + CDC_SET_IF_IDX(msg, ifidx); + /* add additional action bits */ + action &= WL_IOCTL_ACTION_MASK; + msg->flags |= (action << CDCF_IOC_ACTION_SHIFT); + msg->flags = htol32(msg->flags); + + if (buf) + memcpy(prot->buf, buf, len); + + if ((ret = dhdcdc_msg(dhd)) < 0) { + if (!dhd->hang_was_sent) + DHD_ERROR(("dhdcdc_query_ioctl: dhdcdc_msg failed w/status %d\n", ret)); + goto done; + } + +retry: + /* wait for interrupt and get first fragment */ + if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0) + goto done; + + flags = ltoh32(msg->flags); + id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT; + + if ((id < prot->reqid) && (++retries < RETRIES)) + goto retry; + if (id != prot->reqid) { + DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n", + dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid)); + ret = -EINVAL; + goto done; + } + + /* Check info buffer */ + info = (void*)&msg[1]; + + /* Copy info buffer */ + if (buf) + { + if (ret < (int)len) + len = ret; + memcpy(buf, info, len); + } + + /* Check the ERROR flag */ + if (flags & CDCF_IOC_ERROR) + { + ret = ltoh32(msg->status); + /* Cache error from dongle */ + dhd->dongle_error = ret; + } + +done: + return ret; +} + +static int +dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len, uint8 action) +{ + dhd_prot_t *prot = dhd->prot; + cdc_ioctl_t *msg = &prot->msg; + int ret = 0; + uint32 flags, id; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len)); + + if (dhd->busstate == DHD_BUS_DOWN) { + DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__)); + return -EIO; + } + + /* don't talk to the dongle if fw is about to be reloaded */ + if (dhd->hang_was_sent) { + DHD_ERROR(("%s: HANG was sent up earlier. Not talking to the chip\n", + __FUNCTION__)); + return -EIO; + } + + memset(msg, 0, sizeof(cdc_ioctl_t)); + + msg->cmd = htol32(cmd); + msg->len = htol32(len); + msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT); + CDC_SET_IF_IDX(msg, ifidx); + /* add additional action bits */ + action &= WL_IOCTL_ACTION_MASK; + msg->flags |= (action << CDCF_IOC_ACTION_SHIFT) | CDCF_IOC_SET; + msg->flags = htol32(msg->flags); + + if (buf) + memcpy(prot->buf, buf, len); + + if ((ret = dhdcdc_msg(dhd)) < 0) { + DHD_ERROR(("%s: dhdcdc_msg failed w/status %d\n", __FUNCTION__, ret)); + goto done; + } + + if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0) + goto done; + + flags = ltoh32(msg->flags); + id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT; + + if (id != prot->reqid) { + DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n", + dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid)); + ret = -EINVAL; + goto done; + } + + /* Check the ERROR flag */ + if (flags & CDCF_IOC_ERROR) + { + ret = ltoh32(msg->status); + /* Cache error from dongle */ + dhd->dongle_error = ret; + } + +done: + return ret; +} + + +int +dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len) +{ + dhd_prot_t *prot = dhd->prot; + int ret = -1; + uint8 action; +#if defined(NDIS630) + bool acquired = FALSE; +#endif + + if ((dhd->busstate == DHD_BUS_DOWN) || dhd->hang_was_sent) { + DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__)); + goto done; + } +#if defined(NDIS630) + if (dhd_os_proto_block(dhd)) + { + acquired = TRUE; + } + else + { + /* attempt to acquire protocol mutex timed out. */ + ret = -1; + return ret; + } +#endif /* NDIS630 */ + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + ASSERT(len <= WLC_IOCTL_MAXLEN); + + if (len > WLC_IOCTL_MAXLEN) + goto done; + + if (prot->pending == TRUE) { + DHD_ERROR(("CDC packet is pending!!!! cmd=0x%x (%lu) lastcmd=0x%x (%lu)\n", + ioc->cmd, (unsigned long)ioc->cmd, prot->lastcmd, + (unsigned long)prot->lastcmd)); + if ((ioc->cmd == WLC_SET_VAR) || (ioc->cmd == WLC_GET_VAR)) { + DHD_TRACE(("iovar cmd=%s\n", (char*)buf)); + } + goto done; + } + + prot->pending = TRUE; + prot->lastcmd = ioc->cmd; + action = ioc->set; + if (action & WL_IOCTL_ACTION_SET) + ret = dhdcdc_set_ioctl(dhd, ifidx, ioc->cmd, buf, len, action); + else { + ret = dhdcdc_query_ioctl(dhd, ifidx, ioc->cmd, buf, len, action); + if (ret > 0) + ioc->used = ret - sizeof(cdc_ioctl_t); + } + + /* Too many programs assume ioctl() returns 0 on success */ + if (ret >= 0) + ret = 0; + else { + cdc_ioctl_t *msg = &prot->msg; + ioc->needed = ltoh32(msg->len); /* len == needed when set/query fails from dongle */ + } + + /* Intercept the wme_dp ioctl here */ + if ((!ret) && (ioc->cmd == WLC_SET_VAR) && (!strcmp(buf, "wme_dp"))) { + int slen, val = 0; + + slen = strlen("wme_dp") + 1; + if (len >= (int)(slen + sizeof(int))) + bcopy(((char *)buf + slen), &val, sizeof(int)); + dhd->wme_dp = (uint8) ltoh32(val); + } + + prot->pending = FALSE; + +done: +#if defined(NDIS630) + if (acquired) + dhd_os_proto_unblock(dhd); +#endif + return ret; +} + +int +dhd_prot_iovar_op(dhd_pub_t *dhdp, const char *name, + void *params, int plen, void *arg, int len, bool set) +{ + return BCME_UNSUPPORTED; +} + +#ifdef PROP_TXSTATUS +void +dhd_wlfc_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf) +{ + int i; + uint8* ea; + athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) + dhdp->wlfc_state; + wlfc_hanger_t* h; + wlfc_mac_descriptor_t* mac_table; + wlfc_mac_descriptor_t* interfaces; + char* iftypes[] = {"STA", "AP", "WDS", "p2pGO", "p2pCL"}; + + if (wlfc == NULL) { + bcm_bprintf(strbuf, "wlfc not initialized yet\n"); + return; + } + h = (wlfc_hanger_t*)wlfc->hanger; + if (h == NULL) { + bcm_bprintf(strbuf, "wlfc-hanger not initialized yet\n"); + } + + mac_table = wlfc->destination_entries.nodes; + interfaces = wlfc->destination_entries.interfaces; + bcm_bprintf(strbuf, "---- wlfc stats ----\n"); + if (h) { + bcm_bprintf(strbuf, "wlfc hanger (pushed,popped,f_push," + "f_pop,f_slot, pending) = (%d,%d,%d,%d,%d,%d)\n", + h->pushed, + h->popped, + h->failed_to_push, + h->failed_to_pop, + h->failed_slotfind, + (h->pushed - h->popped)); + } + + bcm_bprintf(strbuf, "wlfc fail(tlv,credit_rqst,mac_update,psmode_update), " + "(dq_full,sendq_full, rollback_fail) = (%d,%d,%d,%d), (%d,%d,%d)\n", + wlfc->stats.tlv_parse_failed, + wlfc->stats.credit_request_failed, + wlfc->stats.mac_update_failed, + wlfc->stats.psmode_update_failed, + wlfc->stats.delayq_full_error, + wlfc->stats.sendq_full_error, + wlfc->stats.rollback_failed); + + bcm_bprintf(strbuf, "SENDQ (len,credit,sent) " + "(AC0[%d,%d,%d],AC1[%d,%d,%d],AC2[%d,%d,%d],AC3[%d,%d,%d],BC_MC[%d,%d,%d])\n", + wlfc->SENDQ.q[0].len, wlfc->FIFO_credit[0], wlfc->stats.sendq_pkts[0], + wlfc->SENDQ.q[1].len, wlfc->FIFO_credit[1], wlfc->stats.sendq_pkts[1], + wlfc->SENDQ.q[2].len, wlfc->FIFO_credit[2], wlfc->stats.sendq_pkts[2], + wlfc->SENDQ.q[3].len, wlfc->FIFO_credit[3], wlfc->stats.sendq_pkts[3], + wlfc->SENDQ.q[4].len, wlfc->FIFO_credit[4], wlfc->stats.sendq_pkts[4]); + +#ifdef PROP_TXSTATUS_DEBUG + bcm_bprintf(strbuf, "SENDQ dropped: AC[0-3]:(%d,%d,%d,%d), (bcmc,atim):(%d,%d)\n", + wlfc->stats.dropped_qfull[0], wlfc->stats.dropped_qfull[1], + wlfc->stats.dropped_qfull[2], wlfc->stats.dropped_qfull[3], + wlfc->stats.dropped_qfull[4], wlfc->stats.dropped_qfull[5]); +#endif + + bcm_bprintf(strbuf, "\n"); + for (i = 0; i < WLFC_MAX_IFNUM; i++) { + if (interfaces[i].occupied) { + char* iftype_desc; + + if (interfaces[i].iftype > WLC_E_IF_ROLE_P2P_CLIENT) + iftype_desc = "hostif_flow_state[i] == OFF) + ? " OFF":" ON")); + + bcm_bprintf(strbuf, "INTERFACE[%d].DELAYQ(len,state,credit)" + "= (%d,%s,%d)\n", + i, + interfaces[i].psq.len, + ((interfaces[i].state == + WLFC_STATE_OPEN) ? " OPEN":"CLOSE"), + interfaces[i].requested_credit); + + bcm_bprintf(strbuf, "INTERFACE[%d].DELAYQ" + "(sup,ac0),(sup,ac1),(sup,ac2),(sup,ac3) = " + "(%d,%d),(%d,%d),(%d,%d),(%d,%d)\n", + i, + interfaces[i].psq.q[0].len, + interfaces[i].psq.q[1].len, + interfaces[i].psq.q[2].len, + interfaces[i].psq.q[3].len, + interfaces[i].psq.q[4].len, + interfaces[i].psq.q[5].len, + interfaces[i].psq.q[6].len, + interfaces[i].psq.q[7].len); + } + } + + bcm_bprintf(strbuf, "\n"); + for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) { + if (mac_table[i].occupied) { + ea = mac_table[i].ea; + bcm_bprintf(strbuf, "MAC_table[%d].ea = " + "[%02x:%02x:%02x:%02x:%02x:%02x], if:%d \n", i, + ea[0], ea[1], ea[2], ea[3], ea[4], ea[5], + mac_table[i].interface_id); + + bcm_bprintf(strbuf, "MAC_table[%d].DELAYQ(len,state,credit)" + "= (%d,%s,%d)\n", + i, + mac_table[i].psq.len, + ((mac_table[i].state == + WLFC_STATE_OPEN) ? " OPEN":"CLOSE"), + mac_table[i].requested_credit); +#ifdef PROP_TXSTATUS_DEBUG + bcm_bprintf(strbuf, "MAC_table[%d]: (opened, closed) = (%d, %d)\n", + i, mac_table[i].opened_ct, mac_table[i].closed_ct); +#endif + bcm_bprintf(strbuf, "MAC_table[%d].DELAYQ" + "(sup,ac0),(sup,ac1),(sup,ac2),(sup,ac3) = " + "(%d,%d),(%d,%d),(%d,%d),(%d,%d)\n", + i, + mac_table[i].psq.q[0].len, + mac_table[i].psq.q[1].len, + mac_table[i].psq.q[2].len, + mac_table[i].psq.q[3].len, + mac_table[i].psq.q[4].len, + mac_table[i].psq.q[5].len, + mac_table[i].psq.q[6].len, + mac_table[i].psq.q[7].len); + } + } + +#ifdef PROP_TXSTATUS_DEBUG + { + int avg; + int moving_avg = 0; + int moving_samples; + + if (wlfc->stats.latency_sample_count) { + moving_samples = sizeof(wlfc->stats.deltas)/sizeof(uint32); + + for (i = 0; i < moving_samples; i++) + moving_avg += wlfc->stats.deltas[i]; + moving_avg /= moving_samples; + + avg = (100 * wlfc->stats.total_status_latency) / + wlfc->stats.latency_sample_count; + bcm_bprintf(strbuf, "txstatus latency (average, last, moving[%d]) = " + "(%d.%d, %03d, %03d)\n", + moving_samples, avg/100, (avg - (avg/100)*100), + wlfc->stats.latency_most_recent, + moving_avg); + } + } + + bcm_bprintf(strbuf, "wlfc- fifo[0-5] credit stats: sent = (%d,%d,%d,%d,%d,%d), " + "back = (%d,%d,%d,%d,%d,%d)\n", + wlfc->stats.fifo_credits_sent[0], + wlfc->stats.fifo_credits_sent[1], + wlfc->stats.fifo_credits_sent[2], + wlfc->stats.fifo_credits_sent[3], + wlfc->stats.fifo_credits_sent[4], + wlfc->stats.fifo_credits_sent[5], + + wlfc->stats.fifo_credits_back[0], + wlfc->stats.fifo_credits_back[1], + wlfc->stats.fifo_credits_back[2], + wlfc->stats.fifo_credits_back[3], + wlfc->stats.fifo_credits_back[4], + wlfc->stats.fifo_credits_back[5]); + { + uint32 fifo_cr_sent = 0; + uint32 fifo_cr_acked = 0; + uint32 request_cr_sent = 0; + uint32 request_cr_ack = 0; + uint32 bc_mc_cr_ack = 0; + + for (i = 0; i < sizeof(wlfc->stats.fifo_credits_sent)/sizeof(uint32); i++) { + fifo_cr_sent += wlfc->stats.fifo_credits_sent[i]; + } + + for (i = 0; i < sizeof(wlfc->stats.fifo_credits_back)/sizeof(uint32); i++) { + fifo_cr_acked += wlfc->stats.fifo_credits_back[i]; + } + + for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) { + if (wlfc->destination_entries.nodes[i].occupied) { + request_cr_sent += + wlfc->destination_entries.nodes[i].dstncredit_sent_packets; + } + } + for (i = 0; i < WLFC_MAX_IFNUM; i++) { + if (wlfc->destination_entries.interfaces[i].occupied) { + request_cr_sent += + wlfc->destination_entries.interfaces[i].dstncredit_sent_packets; + } + } + for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) { + if (wlfc->destination_entries.nodes[i].occupied) { + request_cr_ack += + wlfc->destination_entries.nodes[i].dstncredit_acks; + } + } + for (i = 0; i < WLFC_MAX_IFNUM; i++) { + if (wlfc->destination_entries.interfaces[i].occupied) { + request_cr_ack += + wlfc->destination_entries.interfaces[i].dstncredit_acks; + } + } + bcm_bprintf(strbuf, "wlfc- (sent, status) => pq(%d,%d), vq(%d,%d)," + "other:%d, bc_mc:%d, signal-only, (sent,freed): (%d,%d)", + fifo_cr_sent, fifo_cr_acked, + request_cr_sent, request_cr_ack, + wlfc->destination_entries.other.dstncredit_acks, + bc_mc_cr_ack, + wlfc->stats.signal_only_pkts_sent, wlfc->stats.signal_only_pkts_freed); + } +#endif /* PROP_TXSTATUS_DEBUG */ + bcm_bprintf(strbuf, "\n"); + bcm_bprintf(strbuf, "wlfc- pkt((in,2bus,txstats,hdrpull),(dropped,hdr_only,wlc_tossed)" + "(freed,free_err,rollback)) = " + "((%d,%d,%d,%d),(%d,%d,%d),(%d,%d,%d))\n", + wlfc->stats.pktin, + wlfc->stats.pkt2bus, + wlfc->stats.txstatus_in, + wlfc->stats.dhd_hdrpulls, + + wlfc->stats.pktdropped, + wlfc->stats.wlfc_header_only_pkt, + wlfc->stats.wlc_tossed_pkts, + + wlfc->stats.pkt_freed, + wlfc->stats.pkt_free_err, wlfc->stats.rollback); + + bcm_bprintf(strbuf, "wlfc- suppress((d11,wlc,err),enq(d11,wl,hq,mac?),retx(d11,wlc,hq)) = " + "((%d,%d,%d),(%d,%d,%d,%d),(%d,%d,%d))\n", + + wlfc->stats.d11_suppress, + wlfc->stats.wl_suppress, + wlfc->stats.bad_suppress, + + wlfc->stats.psq_d11sup_enq, + wlfc->stats.psq_wlsup_enq, + wlfc->stats.psq_hostq_enq, + wlfc->stats.mac_handle_notfound, + + wlfc->stats.psq_d11sup_retx, + wlfc->stats.psq_wlsup_retx, + wlfc->stats.psq_hostq_retx); + return; +} + +/* Create a place to store all packet pointers submitted to the firmware until + a status comes back, suppress or otherwise. + + hang-er: noun, a contrivance on which things are hung, as a hook. +*/ +static void* +dhd_wlfc_hanger_create(osl_t *osh, int max_items) +{ + int i; + wlfc_hanger_t* hanger; + + /* allow only up to a specific size for now */ + ASSERT(max_items == WLFC_HANGER_MAXITEMS); + + if ((hanger = (wlfc_hanger_t*)MALLOC(osh, WLFC_HANGER_SIZE(max_items))) == NULL) + return NULL; + + memset(hanger, 0, WLFC_HANGER_SIZE(max_items)); + hanger->max_items = max_items; + + for (i = 0; i < hanger->max_items; i++) { + hanger->items[i].state = WLFC_HANGER_ITEM_STATE_FREE; + } + return hanger; +} + +static int +dhd_wlfc_hanger_delete(osl_t *osh, void* hanger) +{ + wlfc_hanger_t* h = (wlfc_hanger_t*)hanger; + + if (h) { + MFREE(osh, h, WLFC_HANGER_SIZE(h->max_items)); + return BCME_OK; + } + return BCME_BADARG; +} + +static uint16 +dhd_wlfc_hanger_get_free_slot(void* hanger) +{ + uint32 i; + wlfc_hanger_t* h = (wlfc_hanger_t*)hanger; + + if (h) { + for (i = (h->slot_pos + 1); i != h->slot_pos;) { + if (h->items[i].state == WLFC_HANGER_ITEM_STATE_FREE) { + h->slot_pos = i; + return (uint16)i; + } + (i == h->max_items)? i = 0 : i++; + } + h->failed_slotfind++; + } + return WLFC_HANGER_MAXITEMS; +} + +static int +dhd_wlfc_hanger_get_genbit(void* hanger, void* pkt, uint32 slot_id, int* gen) +{ + int rc = BCME_OK; + wlfc_hanger_t* h = (wlfc_hanger_t*)hanger; + + *gen = 0xff; + + /* this packet was not pushed at the time it went to the firmware */ + if (slot_id == WLFC_HANGER_MAXITEMS) + return BCME_NOTFOUND; + + if (h) { + if ((h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_INUSE) || + (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED)) { + *gen = h->items[slot_id].gen; + } + else { + rc = BCME_NOTFOUND; + } + } + else + rc = BCME_BADARG; + return rc; +} + +static int +dhd_wlfc_hanger_pushpkt(void* hanger, void* pkt, uint32 slot_id) +{ + int rc = BCME_OK; + wlfc_hanger_t* h = (wlfc_hanger_t*)hanger; + + if (h && (slot_id < WLFC_HANGER_MAXITEMS)) { + if (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_FREE) { + h->items[slot_id].state = WLFC_HANGER_ITEM_STATE_INUSE; + h->items[slot_id].pkt = pkt; + h->items[slot_id].identifier = slot_id; + h->pushed++; + } + else { + h->failed_to_push++; + rc = BCME_NOTFOUND; + } + } + else + rc = BCME_BADARG; + return rc; +} + +static int +dhd_wlfc_hanger_poppkt(void* hanger, uint32 slot_id, void** pktout, int remove_from_hanger) +{ + int rc = BCME_OK; + wlfc_hanger_t* h = (wlfc_hanger_t*)hanger; + + /* this packet was not pushed at the time it went to the firmware */ + if (slot_id == WLFC_HANGER_MAXITEMS) + return BCME_NOTFOUND; + + if (h) { + if (h->items[slot_id].state != WLFC_HANGER_ITEM_STATE_FREE) { + *pktout = h->items[slot_id].pkt; + if (remove_from_hanger) { + h->items[slot_id].state = + WLFC_HANGER_ITEM_STATE_FREE; + h->items[slot_id].pkt = NULL; + h->items[slot_id].identifier = 0; + h->items[slot_id].gen = 0xff; + h->popped++; + } + } + else { + h->failed_to_pop++; + rc = BCME_NOTFOUND; + } + } + else + rc = BCME_BADARG; + return rc; +} + +static int +dhd_wlfc_hanger_mark_suppressed(void* hanger, uint32 slot_id, uint8 gen) +{ + int rc = BCME_OK; + wlfc_hanger_t* h = (wlfc_hanger_t*)hanger; + + /* this packet was not pushed at the time it went to the firmware */ + if (slot_id == WLFC_HANGER_MAXITEMS) + return BCME_NOTFOUND; + if (h) { + h->items[slot_id].gen = gen; + if (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_INUSE) { + h->items[slot_id].state = WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED; + } + else + rc = BCME_BADARG; + } + else + rc = BCME_BADARG; + + return rc; +} + +static int +_dhd_wlfc_pushheader(athost_wl_status_info_t* ctx, void* p, bool tim_signal, + uint8 tim_bmp, uint8 mac_handle, uint32 htodtag) +{ + uint32 wl_pktinfo = 0; + uint8* wlh; + uint8 dataOffset; + uint8 fillers; + uint8 tim_signal_len = 0; + + struct bdc_header *h; + + if (tim_signal) { + tim_signal_len = 1 + 1 + WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP; + } + + /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */ + dataOffset = WLFC_CTL_VALUE_LEN_PKTTAG + 2 + tim_signal_len; + fillers = ROUNDUP(dataOffset, 4) - dataOffset; + dataOffset += fillers; + + PKTPUSH(ctx->osh, p, dataOffset); + wlh = (uint8*) PKTDATA(ctx->osh, p); + + wl_pktinfo = htol32(htodtag); + + wlh[0] = WLFC_CTL_TYPE_PKTTAG; + wlh[1] = WLFC_CTL_VALUE_LEN_PKTTAG; + memcpy(&wlh[2], &wl_pktinfo, sizeof(uint32)); + + if (tim_signal_len) { + wlh[dataOffset - fillers - tim_signal_len ] = + WLFC_CTL_TYPE_PENDING_TRAFFIC_BMP; + wlh[dataOffset - fillers - tim_signal_len + 1] = + WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP; + wlh[dataOffset - fillers - tim_signal_len + 2] = mac_handle; + wlh[dataOffset - fillers - tim_signal_len + 3] = tim_bmp; + } + if (fillers) + memset(&wlh[dataOffset - fillers], WLFC_CTL_TYPE_FILLER, fillers); + + PKTPUSH(ctx->osh, p, BDC_HEADER_LEN); + h = (struct bdc_header *)PKTDATA(ctx->osh, p); + h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT); + if (PKTSUMNEEDED(p)) + h->flags |= BDC_FLAG_SUM_NEEDED; + + + h->priority = (PKTPRIO(p) & BDC_PRIORITY_MASK); + h->flags2 = 0; + h->dataOffset = dataOffset >> 2; + BDC_SET_IF_IDX(h, DHD_PKTTAG_IF(PKTTAG(p))); + return BCME_OK; +} + +static int +_dhd_wlfc_pullheader(athost_wl_status_info_t* ctx, void* pktbuf) +{ + struct bdc_header *h; + + if (PKTLEN(ctx->osh, pktbuf) < BDC_HEADER_LEN) { + WLFC_DBGMESG(("%s: rx data too short (%d < %d)\n", __FUNCTION__, + PKTLEN(ctx->osh, pktbuf), BDC_HEADER_LEN)); + return BCME_ERROR; + } + h = (struct bdc_header *)PKTDATA(ctx->osh, pktbuf); + + /* pull BDC header */ + PKTPULL(ctx->osh, pktbuf, BDC_HEADER_LEN); + + if (PKTLEN(ctx->osh, pktbuf) < (h->dataOffset << 2)) { + WLFC_DBGMESG(("%s: rx data too short (%d < %d)\n", __FUNCTION__, + PKTLEN(ctx->osh, pktbuf), (h->dataOffset << 2))); + return BCME_ERROR; + } + /* pull wl-header */ + PKTPULL(ctx->osh, pktbuf, (h->dataOffset << 2)); + return BCME_OK; +} + +static wlfc_mac_descriptor_t* +_dhd_wlfc_find_table_entry(athost_wl_status_info_t* ctx, void* p) +{ + int i; + wlfc_mac_descriptor_t* table = ctx->destination_entries.nodes; + uint8 ifid = DHD_PKTTAG_IF(PKTTAG(p)); + uint8* dstn = DHD_PKTTAG_DSTN(PKTTAG(p)); + + if (((ctx->destination_entries.interfaces[ifid].iftype == WLC_E_IF_ROLE_STA) || + ETHER_ISMULTI(dstn) || + (ctx->destination_entries.interfaces[ifid].iftype == WLC_E_IF_ROLE_P2P_CLIENT)) && + (ctx->destination_entries.interfaces[ifid].occupied)) { + return &ctx->destination_entries.interfaces[ifid]; + } + + for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) { + if (table[i].occupied) { + if (table[i].interface_id == ifid) { + if (!memcmp(table[i].ea, dstn, ETHER_ADDR_LEN)) + return &table[i]; + } + } + } + return &ctx->destination_entries.other; +} + +static int +_dhd_wlfc_rollback_packet_toq(athost_wl_status_info_t* ctx, + void* p, ewlfc_packet_state_t pkt_type, uint32 hslot) +{ + /* + put the packet back to the head of queue + + - a packet from send-q will need to go back to send-q and not delay-q + since that will change the order of packets. + - suppressed packet goes back to suppress sub-queue + - pull out the header, if new or delayed packet + + Note: hslot is used only when header removal is done. + */ + wlfc_mac_descriptor_t* entry; + void* pktout; + int rc = BCME_OK; + int prec; + + entry = _dhd_wlfc_find_table_entry(ctx, p); + prec = DHD_PKTTAG_FIFO(PKTTAG(p)); + if (entry != NULL) { + if (pkt_type == eWLFC_PKTTYPE_SUPPRESSED) { + /* wl-header is saved for suppressed packets */ + if (WLFC_PKTQ_PENQ_HEAD(&entry->psq, ((prec << 1) + 1), p) == NULL) { + WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); + rc = BCME_ERROR; + } + } + else { + /* remove header first */ + rc = _dhd_wlfc_pullheader(ctx, p); + if (rc != BCME_OK) { + WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); + /* free the hanger slot */ + dhd_wlfc_hanger_poppkt(ctx->hanger, hslot, &pktout, 1); + PKTFREE(ctx->osh, p, TRUE); + rc = BCME_ERROR; + return rc; + } + + if (pkt_type == eWLFC_PKTTYPE_DELAYED) { + /* delay-q packets are going to delay-q */ + if (WLFC_PKTQ_PENQ_HEAD(&entry->psq, (prec << 1), p) == NULL) { + WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); + rc = BCME_ERROR; + } + } + else { + /* these are going to SENDQ */ + if (WLFC_PKTQ_PENQ_HEAD(&ctx->SENDQ, prec, p) == NULL) { + WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); + rc = BCME_ERROR; + } + } + /* free the hanger slot */ + dhd_wlfc_hanger_poppkt(ctx->hanger, hslot, &pktout, 1); + + /* decrement sequence count */ + WLFC_DECR_SEQCOUNT(entry, prec); + } + /* + if this packet did not count against FIFO credit, it must have + taken a requested_credit from the firmware (for pspoll etc.) + */ + if (!DHD_PKTTAG_CREDITCHECK(PKTTAG(p))) { + entry->requested_credit++; + } + } + else { + WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); + rc = BCME_ERROR; + } + if (rc != BCME_OK) + ctx->stats.rollback_failed++; + else + ctx->stats.rollback++; + + return rc; +} + +static void +_dhd_wlfc_flow_control_check(athost_wl_status_info_t* ctx, struct pktq* pq, uint8 if_id) +{ + if ((pq->len <= WLFC_FLOWCONTROL_LOWATER) && (ctx->hostif_flow_state[if_id] == ON)) { + /* start traffic */ + ctx->hostif_flow_state[if_id] = OFF; + /* + WLFC_DBGMESG(("qlen:%02d, if:%02d, ->OFF, start traffic %s()\n", + pq->len, if_id, __FUNCTION__)); + */ + WLFC_DBGMESG(("F")); + dhd_txflowcontrol(ctx->dhdp, if_id, OFF); + ctx->toggle_host_if = 0; + } + if ((pq->len >= WLFC_FLOWCONTROL_HIWATER) && (ctx->hostif_flow_state[if_id] == OFF)) { + /* stop traffic */ + ctx->hostif_flow_state[if_id] = ON; + /* + WLFC_DBGMESG(("qlen:%02d, if:%02d, ->ON, stop traffic %s()\n", + pq->len, if_id, __FUNCTION__)); + */ + WLFC_DBGMESG(("N")); + dhd_txflowcontrol(ctx->dhdp, if_id, ON); + ctx->host_ifidx = if_id; + ctx->toggle_host_if = 1; + } + return; +} + +static int +_dhd_wlfc_send_signalonly_packet(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry, + uint8 ta_bmp) +{ + int rc = BCME_OK; + void* p = NULL; + int dummylen = ((dhd_pub_t *)ctx->dhdp)->hdrlen+ 12; + + /* allocate a dummy packet */ + p = PKTGET(ctx->osh, dummylen, TRUE); + if (p) { + PKTPULL(ctx->osh, p, dummylen); + DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), 0); + _dhd_wlfc_pushheader(ctx, p, TRUE, ta_bmp, entry->mac_handle, 0); + DHD_PKTTAG_SETSIGNALONLY(PKTTAG(p), 1); +#ifdef PROP_TXSTATUS_DEBUG + ctx->stats.signal_only_pkts_sent++; +#endif + rc = dhd_bus_txdata(((dhd_pub_t *)ctx->dhdp)->bus, p); + if (rc != BCME_OK) { + PKTFREE(ctx->osh, p, TRUE); + } + } + else { + DHD_ERROR(("%s: couldn't allocate new %d-byte packet\n", + __FUNCTION__, dummylen)); + rc = BCME_NOMEM; + } + return rc; +} + +/* Return TRUE if traffic availability changed */ +static bool +_dhd_wlfc_traffic_pending_check(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry, + int prec) +{ + bool rc = FALSE; + + if (entry->state == WLFC_STATE_CLOSE) { + if ((pktq_plen(&entry->psq, (prec << 1)) == 0) && + (pktq_plen(&entry->psq, ((prec << 1) + 1)) == 0)) { + + if (entry->traffic_pending_bmp & NBITVAL(prec)) { + rc = TRUE; + entry->traffic_pending_bmp = + entry->traffic_pending_bmp & ~ NBITVAL(prec); + } + } + else { + if (!(entry->traffic_pending_bmp & NBITVAL(prec))) { + rc = TRUE; + entry->traffic_pending_bmp = + entry->traffic_pending_bmp | NBITVAL(prec); + } + } + } + if (rc) { + /* request a TIM update to firmware at the next piggyback opportunity */ + if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp) { + entry->send_tim_signal = 1; + _dhd_wlfc_send_signalonly_packet(ctx, entry, entry->traffic_pending_bmp); + entry->traffic_lastreported_bmp = entry->traffic_pending_bmp; + entry->send_tim_signal = 0; + } + else { + rc = FALSE; + } + } + return rc; +} + +static int +_dhd_wlfc_enque_suppressed(athost_wl_status_info_t* ctx, int prec, void* p) +{ + wlfc_mac_descriptor_t* entry; + + entry = _dhd_wlfc_find_table_entry(ctx, p); + if (entry == NULL) { + WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); + return BCME_NOTFOUND; + } + /* + - suppressed packets go to sub_queue[2*prec + 1] AND + - delayed packets go to sub_queue[2*prec + 0] to ensure + order of delivery. + */ + if (WLFC_PKTQ_PENQ(&entry->psq, ((prec << 1) + 1), p) == NULL) { + ctx->stats.delayq_full_error++; + /* WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); */ + WLFC_DBGMESG(("s")); + return BCME_ERROR; + } + /* A packet has been pushed, update traffic availability bitmap, if applicable */ + _dhd_wlfc_traffic_pending_check(ctx, entry, prec); + _dhd_wlfc_flow_control_check(ctx, &entry->psq, DHD_PKTTAG_IF(PKTTAG(p))); + return BCME_OK; +} + +static int +_dhd_wlfc_pretx_pktprocess(athost_wl_status_info_t* ctx, + wlfc_mac_descriptor_t* entry, void* p, int header_needed, uint32* slot) +{ + int rc = BCME_OK; + int hslot = WLFC_HANGER_MAXITEMS; + bool send_tim_update = FALSE; + uint32 htod = 0; + uint8 free_ctr; + + *slot = hslot; + + if (entry == NULL) { + entry = _dhd_wlfc_find_table_entry(ctx, p); + } + + if (entry == NULL) { + WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); + return BCME_ERROR; + } + if (entry->send_tim_signal) { + send_tim_update = TRUE; + entry->send_tim_signal = 0; + entry->traffic_lastreported_bmp = entry->traffic_pending_bmp; + } + if (header_needed) { + hslot = dhd_wlfc_hanger_get_free_slot(ctx->hanger); + free_ctr = WLFC_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p))); + DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), htod); + WLFC_PKTFLAG_SET_GENERATION(htod, entry->generation); + entry->transit_count++; + } + else { + hslot = WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p))); + free_ctr = WLFC_PKTID_FREERUNCTR_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p))); + } + WLFC_PKTID_HSLOT_SET(htod, hslot); + WLFC_PKTID_FREERUNCTR_SET(htod, free_ctr); + DHD_PKTTAG_SETPKTDIR(PKTTAG(p), 1); + WL_TXSTATUS_SET_FLAGS(htod, WLFC_PKTFLAG_PKTFROMHOST); + WL_TXSTATUS_SET_FIFO(htod, DHD_PKTTAG_FIFO(PKTTAG(p))); + + if (!DHD_PKTTAG_CREDITCHECK(PKTTAG(p))) { + /* + Indicate that this packet is being sent in response to an + explicit request from the firmware side. + */ + WLFC_PKTFLAG_SET_PKTREQUESTED(htod); + } + else { + WLFC_PKTFLAG_CLR_PKTREQUESTED(htod); + } + if (header_needed) { + rc = _dhd_wlfc_pushheader(ctx, p, send_tim_update, + entry->traffic_lastreported_bmp, entry->mac_handle, htod); + if (rc == BCME_OK) { + DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), htod); + /* + a new header was created for this packet. + push to hanger slot and scrub q. Since bus + send succeeded, increment seq number as well. + */ + rc = dhd_wlfc_hanger_pushpkt(ctx->hanger, p, hslot); + if (rc == BCME_OK) { + /* increment free running sequence count */ + WLFC_INCR_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p))); +#ifdef PROP_TXSTATUS_DEBUG + ((wlfc_hanger_t*)(ctx->hanger))->items[hslot].push_time = + OSL_SYSUPTIME(); +#endif + } + else { + WLFC_DBGMESG(("%s() hanger_pushpkt() failed, rc: %d\n", + __FUNCTION__, rc)); + } + } + } + else { + int gen; + + /* remove old header */ + rc = _dhd_wlfc_pullheader(ctx, p); + if (rc == BCME_OK) { + hslot = WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p))); + dhd_wlfc_hanger_get_genbit(ctx->hanger, p, hslot, &gen); + + WLFC_PKTFLAG_SET_GENERATION(htod, gen); + free_ctr = WLFC_PKTID_FREERUNCTR_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p))); + /* push new header */ + _dhd_wlfc_pushheader(ctx, p, send_tim_update, + entry->traffic_lastreported_bmp, entry->mac_handle, htod); + } + } + *slot = hslot; + return rc; +} + +static int +_dhd_wlfc_is_destination_closed(athost_wl_status_info_t* ctx, + wlfc_mac_descriptor_t* entry, int prec) +{ + if (ctx->destination_entries.interfaces[entry->interface_id].iftype == + WLC_E_IF_ROLE_P2P_GO) { + /* - destination interface is of type p2p GO. + For a p2pGO interface, if the destination is OPEN but the interface is + CLOSEd, do not send traffic. But if the dstn is CLOSEd while there is + destination-specific-credit left send packets. This is because the + firmware storing the destination-specific-requested packet in queue. + */ + if ((entry->state == WLFC_STATE_CLOSE) && (entry->requested_credit == 0) && + (entry->requested_packet == 0)) + return 1; + } + /* AP, p2p_go -> unicast desc entry, STA/p2p_cl -> interface desc. entry */ + if (((entry->state == WLFC_STATE_CLOSE) && (entry->requested_credit == 0) && + (entry->requested_packet == 0)) || + (!(entry->ac_bitmap & (1 << prec)))) + return 1; + + return 0; +} + +static void* +_dhd_wlfc_deque_delayedq(athost_wl_status_info_t* ctx, + int prec, uint8* ac_credit_spent, uint8* needs_hdr, wlfc_mac_descriptor_t** entry_out) +{ + wlfc_mac_descriptor_t* entry; + wlfc_mac_descriptor_t* table; + uint8 token_pos; + int total_entries; + void* p = NULL; + int pout; + int i; + + *entry_out = NULL; + token_pos = ctx->token_pos[prec]; + /* most cases a packet will count against FIFO credit */ + *ac_credit_spent = 1; + *needs_hdr = 1; + + /* search all entries, include nodes as well as interfaces */ + table = (wlfc_mac_descriptor_t*)&ctx->destination_entries; + total_entries = sizeof(ctx->destination_entries)/sizeof(wlfc_mac_descriptor_t); + + for (i = 0; i < total_entries; i++) { + entry = &table[(token_pos + i) % total_entries]; + if (entry->occupied) { + if (!_dhd_wlfc_is_destination_closed(ctx, entry, prec)) { + p = pktq_mdeq(&entry->psq, + /* higher precedence will be picked up first, + * i.e. suppressed packets before delayed ones + */ + NBITVAL((prec << 1) + 1), &pout); + *needs_hdr = 0; + + if (p == NULL) { + if (entry->suppressed == TRUE) { + if ((entry->suppr_transit_count <= + entry->suppress_count)) { + entry->suppressed = FALSE; + } else { + return NULL; + } + } + /* De-Q from delay Q */ + p = pktq_mdeq(&entry->psq, + NBITVAL((prec << 1)), + &pout); + *needs_hdr = 1; + } + + if (p != NULL) { + /* did the packet come from suppress sub-queue? */ + if (entry->requested_credit > 0) { + entry->requested_credit--; +#ifdef PROP_TXSTATUS_DEBUG + entry->dstncredit_sent_packets++; +#endif + /* + if the packet was pulled out while destination is in + closed state but had a non-zero packets requested, + then this should not count against the FIFO credit. + That is due to the fact that the firmware will + most likely hold onto this packet until a suitable + time later to push it to the appropriate AC FIFO. + */ + if (entry->state == WLFC_STATE_CLOSE) + *ac_credit_spent = 0; + } + else if (entry->requested_packet > 0) { + entry->requested_packet--; + DHD_PKTTAG_SETONETIMEPKTRQST(PKTTAG(p)); + if (entry->state == WLFC_STATE_CLOSE) + *ac_credit_spent = 0; + } + /* move token to ensure fair round-robin */ + ctx->token_pos[prec] = + (token_pos + i + 1) % total_entries; + *entry_out = entry; + _dhd_wlfc_flow_control_check(ctx, &entry->psq, + DHD_PKTTAG_IF(PKTTAG(p))); + /* + A packet has been picked up, update traffic + availability bitmap, if applicable + */ + _dhd_wlfc_traffic_pending_check(ctx, entry, prec); + return p; + } + } + } + } + return NULL; +} + +static void* +_dhd_wlfc_deque_sendq(athost_wl_status_info_t* ctx, int prec) +{ + wlfc_mac_descriptor_t* entry; + void* p; + + + p = pktq_pdeq(&ctx->SENDQ, prec); + if (p != NULL) { + if (ETHER_ISMULTI(DHD_PKTTAG_DSTN(PKTTAG(p)))) + /* bc/mc packets do not have a delay queue */ + return p; + + entry = _dhd_wlfc_find_table_entry(ctx, p); + + if (entry == NULL) { + WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); + return p; + } + + while ((p != NULL)) { + /* + - suppressed packets go to sub_queue[2*prec + 1] AND + - delayed packets go to sub_queue[2*prec + 0] to ensure + order of delivery. + */ + if (WLFC_PKTQ_PENQ(&entry->psq, (prec << 1), p) == NULL) { + WLFC_DBGMESG(("D")); + /* dhd_txcomplete(ctx->dhdp, p, FALSE); */ + PKTFREE(ctx->osh, p, TRUE); + ctx->stats.delayq_full_error++; + } + /* + A packet has been pushed, update traffic availability bitmap, + if applicable + */ + _dhd_wlfc_traffic_pending_check(ctx, entry, prec); + + p = pktq_pdeq(&ctx->SENDQ, prec); + if (p == NULL) + break; + + entry = _dhd_wlfc_find_table_entry(ctx, p); + + if ((entry == NULL) || (ETHER_ISMULTI(DHD_PKTTAG_DSTN(PKTTAG(p))))) { + return p; + } + } + } + return p; +} + +static int +_dhd_wlfc_mac_entry_update(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry, + ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea) +{ + int rc = BCME_OK; + + if (action == eWLFC_MAC_ENTRY_ACTION_ADD) { + entry->occupied = 1; + entry->state = WLFC_STATE_OPEN; + entry->requested_credit = 0; + entry->interface_id = ifid; + entry->iftype = iftype; + entry->ac_bitmap = 0xff; /* update this when handling APSD */ + /* for an interface entry we may not care about the MAC address */ + if (ea != NULL) + memcpy(&entry->ea[0], ea, ETHER_ADDR_LEN); + pktq_init(&entry->psq, WLFC_PSQ_PREC_COUNT, WLFC_PSQ_LEN); + } + else if (action == eWLFC_MAC_ENTRY_ACTION_UPDATE) { + entry->occupied = 1; + entry->state = WLFC_STATE_OPEN; + entry->requested_credit = 0; + entry->interface_id = ifid; + entry->iftype = iftype; + entry->ac_bitmap = 0xff; /* update this when handling APSD */ + /* for an interface entry we may not care about the MAC address */ + if (ea != NULL) + memcpy(&entry->ea[0], ea, ETHER_ADDR_LEN); + } + else if (action == eWLFC_MAC_ENTRY_ACTION_DEL) { + entry->occupied = 0; + entry->state = WLFC_STATE_CLOSE; + entry->requested_credit = 0; + /* enable after packets are queued-deqeued properly. + pktq_flush(dhd->osh, &entry->psq, FALSE, NULL, 0); + */ + } + return rc; +} + +int +_dhd_wlfc_borrow_credit(athost_wl_status_info_t* ctx, uint8 available_credit_map, int borrower_ac) +{ + int lender_ac; + int rc = BCME_ERROR; + + if (ctx == NULL || available_credit_map == 0) { + WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); + return BCME_BADARG; + } + + /* Borrow from lowest priority available AC (including BC/MC credits) */ + for (lender_ac = 0; lender_ac <= AC_COUNT; lender_ac++) { + if ((available_credit_map && (1 << lender_ac)) && + (ctx->FIFO_credit[lender_ac] > 0)) { + ctx->credits_borrowed[borrower_ac][lender_ac]++; + ctx->FIFO_credit[lender_ac]--; + rc = BCME_OK; + break; + } + } + + return rc; +} + +int +dhd_wlfc_interface_entry_update(void* state, + ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea) +{ + athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state; + wlfc_mac_descriptor_t* entry; + + if (ifid >= WLFC_MAX_IFNUM) + return BCME_BADARG; + + entry = &ctx->destination_entries.interfaces[ifid]; + return _dhd_wlfc_mac_entry_update(ctx, entry, action, ifid, iftype, ea); +} + +int +dhd_wlfc_FIFOcreditmap_update(void* state, uint8* credits) +{ + athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state; + + /* update the AC FIFO credit map */ + ctx->FIFO_credit[0] = credits[0]; + ctx->FIFO_credit[1] = credits[1]; + ctx->FIFO_credit[2] = credits[2]; + ctx->FIFO_credit[3] = credits[3]; + /* credit for bc/mc packets */ + ctx->FIFO_credit[4] = credits[4]; + /* credit for ATIM FIFO is not used yet. */ + ctx->FIFO_credit[5] = 0; + return BCME_OK; +} + +int +dhd_wlfc_enque_sendq(void* state, int prec, void* p) +{ + athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state; + + if ((state == NULL) || + /* prec = AC_COUNT is used for bc/mc queue */ + (prec > AC_COUNT) || + (p == NULL)) { + WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); + return BCME_BADARG; + } + if (FALSE == dhd_prec_enq(ctx->dhdp, &ctx->SENDQ, p, prec)) { + ctx->stats.sendq_full_error++; + /* + WLFC_DBGMESG(("Error: %s():%d, qlen:%d\n", + __FUNCTION__, __LINE__, ctx->SENDQ.len)); + */ + WLFC_HOST_FIFO_DROPPEDCTR_INC(ctx, prec); + WLFC_DBGMESG(("Q")); + PKTFREE(ctx->osh, p, TRUE); + return BCME_ERROR; + } + ctx->stats.pktin++; + /* _dhd_wlfc_flow_control_check(ctx, &ctx->SENDQ, DHD_PKTTAG_IF(PKTTAG(p))); */ + return BCME_OK; +} + +int +_dhd_wlfc_handle_packet_commit(athost_wl_status_info_t* ctx, int ac, + dhd_wlfc_commit_info_t *commit_info, f_commitpkt_t fcommit, void* commit_ctx) +{ + uint32 hslot; + int rc; + + /* + if ac_fifo_credit_spent = 0 + + This packet will not count against the FIFO credit. + To ensure the txstatus corresponding to this packet + does not provide an implied credit (default behavior) + mark the packet accordingly. + + if ac_fifo_credit_spent = 1 + + This is a normal packet and it counts against the FIFO + credit count. + */ + DHD_PKTTAG_SETCREDITCHECK(PKTTAG(commit_info->p), commit_info->ac_fifo_credit_spent); + rc = _dhd_wlfc_pretx_pktprocess(ctx, commit_info->mac_entry, commit_info->p, + commit_info->needs_hdr, &hslot); + + if (rc == BCME_OK) + rc = fcommit(commit_ctx, commit_info->p); + else + ctx->stats.generic_error++; + + if (rc == BCME_OK) { + ctx->stats.pkt2bus++; + if (commit_info->ac_fifo_credit_spent) { + ctx->stats.sendq_pkts[ac]++; + WLFC_HOST_FIFO_CREDIT_INC_SENTCTRS(ctx, ac); + } + } else if (rc == BCME_NORESOURCE) + rc = BCME_ERROR; + else { + /* + bus commit has failed, rollback. + - remove wl-header for a delayed packet + - save wl-header header for suppressed packets + */ + rc = _dhd_wlfc_rollback_packet_toq(ctx, commit_info->p, + (commit_info->pkt_type), hslot); + if (rc != BCME_OK) + ctx->stats.rollback_failed++; + + rc = BCME_ERROR; + } + + return rc; +} + +int +dhd_wlfc_commit_packets(void* state, f_commitpkt_t fcommit, void* commit_ctx) +{ + int ac; + int credit; + int rc; + dhd_wlfc_commit_info_t commit_info; + athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state; + int credit_count = 0; + int bus_retry_count = 0; + uint8 ac_available = 0; /* Bitmask for 4 ACs + BC/MC */ + + if ((state == NULL) || + (fcommit == NULL)) { + WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); + return BCME_BADARG; + } + + memset(&commit_info, 0, sizeof(commit_info)); + + /* + Commit packets for regular AC traffic. Higher priority first. + First, use up FIFO credits available to each AC. Based on distribution + and credits left, borrow from other ACs as applicable + + -NOTE: + If the bus between the host and firmware is overwhelmed by the + traffic from host, it is possible that higher priority traffic + starves the lower priority queue. If that occurs often, we may + have to employ weighted round-robin or ucode scheme to avoid + low priority packet starvation. + */ + + for (ac = AC_COUNT; ac >= 0; ac--) { + + int initial_credit_count = ctx->FIFO_credit[ac]; + + /* packets from SENDQ are fresh and they'd need header and have no MAC entry */ + commit_info.needs_hdr = 1; + commit_info.mac_entry = NULL; + commit_info.pkt_type = eWLFC_PKTTYPE_NEW; + + do { + commit_info.p = _dhd_wlfc_deque_sendq(ctx, ac); + if (commit_info.p == NULL) + break; + else if (ETHER_ISMULTI(DHD_PKTTAG_DSTN(PKTTAG(commit_info.p)))) { + ASSERT(ac == AC_COUNT); + + if (ctx->FIFO_credit[ac]) { + rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info, + fcommit, commit_ctx); + + /* Bus commits may fail (e.g. flow control); abort after retries */ + if (rc == BCME_OK) { + if (commit_info.ac_fifo_credit_spent) { + (void) _dhd_wlfc_borrow_credit(ctx, + ac_available, ac); + credit_count--; + } + } else { + bus_retry_count++; + if (bus_retry_count >= BUS_RETRIES) { + DHD_ERROR((" %s: bus error\n", + __FUNCTION__)); + return rc; + } + } + } + } + + } while (commit_info.p); + + for (credit = 0; credit < ctx->FIFO_credit[ac];) { + commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac, + &(commit_info.ac_fifo_credit_spent), + &(commit_info.needs_hdr), + &(commit_info.mac_entry)); + + if (commit_info.p == NULL) + break; + + commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED : + eWLFC_PKTTYPE_SUPPRESSED; + + rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info, + fcommit, commit_ctx); + + /* Bus commits may fail (e.g. flow control); abort after retries */ + if (rc == BCME_OK) { + if (commit_info.ac_fifo_credit_spent) { + credit++; + } + } + else { + bus_retry_count++; + if (bus_retry_count >= BUS_RETRIES) { + DHD_ERROR(("dhd_wlfc_commit_packets(): bus error\n")); + ctx->FIFO_credit[ac] -= credit; + return rc; + } + } + } + + ctx->FIFO_credit[ac] -= credit; + + + /* If no credits were used, the queue is idle and can be re-used + Note that resv credits cannot be borrowed + */ + if (initial_credit_count == ctx->FIFO_credit[ac]) { + ac_available |= (1 << ac); + credit_count += ctx->FIFO_credit[ac]; + } + } + + /* We borrow only for AC_BE and only if no other traffic seen for DEFER_PERIOD + + Note that (ac_available & WLFC_AC_BE_TRAFFIC_ONLY) is done to: + a) ignore BC/MC for deferring borrow + b) ignore AC_BE being available along with other ACs + (this should happen only for pure BC/MC traffic) + + i.e. AC_VI, AC_VO, AC_BK all MUST be available (i.e. no traffic) and + we do not care if AC_BE and BC/MC are available or not + */ + if ((ac_available & WLFC_AC_BE_TRAFFIC_ONLY) == WLFC_AC_BE_TRAFFIC_ONLY) { + + if (ctx->allow_credit_borrow) { + ac = 1; /* Set ac to AC_BE and borrow credits */ + } + else { + int delta; + int curr_t = OSL_SYSUPTIME(); + + if (curr_t > ctx->borrow_defer_timestamp) + delta = curr_t - ctx->borrow_defer_timestamp; + else + delta = 0xffffffff + curr_t - ctx->borrow_defer_timestamp; + + if (delta >= WLFC_BORROW_DEFER_PERIOD_MS) { + /* Reset borrow but defer to next iteration (defensive borrowing) */ + ctx->allow_credit_borrow = TRUE; + ctx->borrow_defer_timestamp = 0; + } + return BCME_OK; + } + } + else { + /* If we have multiple AC traffic, turn off borrowing, mark time and bail out */ + ctx->allow_credit_borrow = FALSE; + ctx->borrow_defer_timestamp = OSL_SYSUPTIME(); + return BCME_OK; + } + + /* At this point, borrow all credits only for "ac" (which should be set above to AC_BE) + Generically use "ac" only in case we extend to all ACs in future + */ + for (; (credit_count > 0);) { + + commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac, + &(commit_info.ac_fifo_credit_spent), + &(commit_info.needs_hdr), + &(commit_info.mac_entry)); + if (commit_info.p == NULL) + break; + + commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED : + eWLFC_PKTTYPE_SUPPRESSED; + + rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info, + fcommit, commit_ctx); + + /* Bus commits may fail (e.g. flow control); abort after retries */ + if (rc == BCME_OK) { + if (commit_info.ac_fifo_credit_spent) { + (void) _dhd_wlfc_borrow_credit(ctx, ac_available, ac); + credit_count--; + } + } + else { + bus_retry_count++; + if (bus_retry_count >= BUS_RETRIES) { + DHD_ERROR(("dhd_wlfc_commit_packets(): bus error\n")); + return rc; + } + } + } + + return BCME_OK; +} + +static uint8 +dhd_wlfc_find_mac_desc_id_from_mac(dhd_pub_t *dhdp, uint8* ea) +{ + wlfc_mac_descriptor_t* table = + ((athost_wl_status_info_t*)dhdp->wlfc_state)->destination_entries.nodes; + uint8 table_index; + + if (ea != NULL) { + for (table_index = 0; table_index < WLFC_MAC_DESC_TABLE_SIZE; table_index++) { + if ((memcmp(ea, &table[table_index].ea[0], ETHER_ADDR_LEN) == 0) && + table[table_index].occupied) + return table_index; + } + } + return WLFC_MAC_DESC_ID_INVALID; +} + +void +dhd_wlfc_txcomplete(dhd_pub_t *dhd, void *txp, bool success) +{ + athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) + dhd->wlfc_state; + void* p; + int fifo_id; + + dhd_os_wlfc_block(dhd); + + if (DHD_PKTTAG_SIGNALONLY(PKTTAG(txp))) { +#ifdef PROP_TXSTATUS_DEBUG + wlfc->stats.signal_only_pkts_freed++; +#endif + if (success) + /* is this a signal-only packet? */ + PKTFREE(wlfc->osh, txp, TRUE); + dhd_os_wlfc_unblock(dhd); + return; + } + if (!success) { + WLFC_DBGMESG(("At: %s():%d, bus_complete() failure for %p, htod_tag:0x%08x\n", + __FUNCTION__, __LINE__, txp, DHD_PKTTAG_H2DTAG(PKTTAG(txp)))); + dhd_wlfc_hanger_poppkt(wlfc->hanger, WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG + (PKTTAG(txp))), &p, 1); + + /* indicate failure and free the packet */ + dhd_txcomplete(dhd, txp, FALSE); + + /* return the credit, if necessary */ + if (DHD_PKTTAG_CREDITCHECK(PKTTAG(txp))) { + int lender, credit_returned = 0; /* Note that borrower is fifo_id */ + + fifo_id = DHD_PKTTAG_FIFO(PKTTAG(txp)); + + /* Return credits to highest priority lender first */ + for (lender = AC_COUNT; lender >= 0; lender--) { + if (wlfc->credits_borrowed[fifo_id][lender] > 0) { + wlfc->FIFO_credit[lender]++; + wlfc->credits_borrowed[fifo_id][lender]--; + credit_returned = 1; + break; + } + } + + if (!credit_returned) { + wlfc->FIFO_credit[fifo_id]++; + } + } + + PKTFREE(wlfc->osh, txp, TRUE); + } + dhd_os_wlfc_unblock(dhd); + return; +} + +static int +dhd_wlfc_compressed_txstatus_update(dhd_pub_t *dhd, uint8* pkt_info, uint8 len) +{ + uint8 status_flag; + uint32 status; + int ret; + int remove_from_hanger = 1; + void* pktbuf; + uint8 fifo_id; + uint8 count = 0; + uint32 status_g; + uint32 hslot, hcnt; + wlfc_mac_descriptor_t* entry = NULL; + athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) + dhd->wlfc_state; + + memcpy(&status, pkt_info, sizeof(uint32)); + status_flag = WL_TXSTATUS_GET_FLAGS(status); + status_g = status & 0xff000000; + hslot = (status & 0x00ffff00) >> 8; + hcnt = status & 0xff; + len = pkt_info[4]; + + wlfc->stats.txstatus_in++; + + if (status_flag == WLFC_CTL_PKTFLAG_DISCARD) { + wlfc->stats.pkt_freed++; + } + + else if (status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) { + wlfc->stats.d11_suppress++; + remove_from_hanger = 0; + } + + else if (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS) { + wlfc->stats.wl_suppress++; + remove_from_hanger = 0; + } + + else if (status_flag == WLFC_CTL_PKTFLAG_TOSSED_BYWLC) { + wlfc->stats.wlc_tossed_pkts++; + } + while (count < len) { + status = (status_g << 24) | (hslot << 8) | (hcnt); + count++; + hslot++; + hcnt++; + + ret = dhd_wlfc_hanger_poppkt(wlfc->hanger, + WLFC_PKTID_HSLOT_GET(status), &pktbuf, remove_from_hanger); + if (ret != BCME_OK) { + /* do something */ + continue; + } + + entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf); + + if (!remove_from_hanger) { + /* this packet was suppressed */ + if (!entry->suppressed || entry->generation != WLFC_PKTID_GEN(status)) { + entry->suppressed = TRUE; + entry->suppress_count = pktq_mlen(&entry->psq, + NBITVAL((WL_TXSTATUS_GET_FIFO(status) << 1) + 1)); + entry->suppr_transit_count = entry->transit_count; + } + entry->generation = WLFC_PKTID_GEN(status); + } + +#ifdef PROP_TXSTATUS_DEBUG + { + uint32 new_t = OSL_SYSUPTIME(); + uint32 old_t; + uint32 delta; + old_t = ((wlfc_hanger_t*)(wlfc->hanger))->items[ + WLFC_PKTID_HSLOT_GET(status)].push_time; + + + wlfc->stats.latency_sample_count++; + if (new_t > old_t) + delta = new_t - old_t; + else + delta = 0xffffffff + new_t - old_t; + wlfc->stats.total_status_latency += delta; + wlfc->stats.latency_most_recent = delta; + + wlfc->stats.deltas[wlfc->stats.idx_delta++] = delta; + if (wlfc->stats.idx_delta == sizeof(wlfc->stats.deltas)/sizeof(uint32)) + wlfc->stats.idx_delta = 0; + } +#endif /* PROP_TXSTATUS_DEBUG */ + + fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pktbuf)); + + /* pick up the implicit credit from this packet */ + if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pktbuf))) { + if (wlfc->proptxstatus_mode == WLFC_FCMODE_IMPLIED_CREDIT) { + + int lender, credit_returned = 0; /* Note that borrower is fifo_id */ + + /* Return credits to highest priority lender first */ + for (lender = AC_COUNT; lender >= 0; lender--) { + if (wlfc->credits_borrowed[fifo_id][lender] > 0) { + wlfc->FIFO_credit[lender]++; + wlfc->credits_borrowed[fifo_id][lender]--; + credit_returned = 1; + break; + } + } + + if (!credit_returned) { + wlfc->FIFO_credit[fifo_id]++; + } + } + } + else { + /* + if this packet did not count against FIFO credit, it must have + taken a requested_credit from the destination entry (for pspoll etc.) + */ + if (!entry) { + + entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf); + } + if (!DHD_PKTTAG_ONETIMEPKTRQST(PKTTAG(pktbuf))) + entry->requested_credit++; +#ifdef PROP_TXSTATUS_DEBUG + entry->dstncredit_acks++; +#endif + } + if ((status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) || + (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS)) { + + ret = _dhd_wlfc_enque_suppressed(wlfc, fifo_id, pktbuf); + if (ret != BCME_OK) { + /* delay q is full, drop this packet */ + dhd_wlfc_hanger_poppkt(wlfc->hanger, WLFC_PKTID_HSLOT_GET(status), + &pktbuf, 1); + + /* indicate failure and free the packet */ + dhd_txcomplete(dhd, pktbuf, FALSE); + entry->transit_count--; + /* packet is transmitted Successfully by dongle + * after first suppress. + */ + if (entry->suppressed) { + entry->suppr_transit_count--; + } + PKTFREE(wlfc->osh, pktbuf, TRUE); + } else { + /* Mark suppressed to avoid a double free during wlfc cleanup */ + + dhd_wlfc_hanger_mark_suppressed(wlfc->hanger, + WLFC_PKTID_HSLOT_GET(status), WLFC_PKTID_GEN(status)); + entry->suppress_count++; + } + } + else { + dhd_txcomplete(dhd, pktbuf, TRUE); + entry->transit_count--; + + /* This packet is transmitted Successfully by dongle + * even after first suppress. + */ + if (entry->suppressed) { + entry->suppr_transit_count--; + } + /* free the packet */ + PKTFREE(wlfc->osh, pktbuf, TRUE); + } + } + return BCME_OK; +} + +/* Handle discard or suppress indication */ +static int +dhd_wlfc_txstatus_update(dhd_pub_t *dhd, uint8* pkt_info) +{ + uint8 status_flag; + uint32 status; + int ret; + int remove_from_hanger = 1; + void* pktbuf; + uint8 fifo_id; + wlfc_mac_descriptor_t* entry = NULL; + athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) + dhd->wlfc_state; + + memcpy(&status, pkt_info, sizeof(uint32)); + status_flag = WL_TXSTATUS_GET_FLAGS(status); + wlfc->stats.txstatus_in++; + + if (status_flag == WLFC_CTL_PKTFLAG_DISCARD) { + wlfc->stats.pkt_freed++; + } + + else if (status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) { + wlfc->stats.d11_suppress++; + remove_from_hanger = 0; + } + + else if (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS) { + wlfc->stats.wl_suppress++; + remove_from_hanger = 0; + } + + else if (status_flag == WLFC_CTL_PKTFLAG_TOSSED_BYWLC) { + wlfc->stats.wlc_tossed_pkts++; + } + + ret = dhd_wlfc_hanger_poppkt(wlfc->hanger, + WLFC_PKTID_HSLOT_GET(status), &pktbuf, remove_from_hanger); + if (ret != BCME_OK) { + /* do something */ + return ret; + } + + entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf); + + if (!remove_from_hanger) { + /* this packet was suppressed */ + if (!entry->suppressed || entry->generation != WLFC_PKTID_GEN(status)) { + entry->suppressed = TRUE; + entry->suppress_count = pktq_mlen(&entry->psq, + NBITVAL((WL_TXSTATUS_GET_FIFO(status) << 1) + 1)); + entry->suppr_transit_count = entry->transit_count; + } + entry->generation = WLFC_PKTID_GEN(status); + } + +#ifdef PROP_TXSTATUS_DEBUG + { + uint32 new_t = OSL_SYSUPTIME(); + uint32 old_t; + uint32 delta; + old_t = ((wlfc_hanger_t*)(wlfc->hanger))->items[ + WLFC_PKTID_HSLOT_GET(status)].push_time; + + + wlfc->stats.latency_sample_count++; + if (new_t > old_t) + delta = new_t - old_t; + else + delta = 0xffffffff + new_t - old_t; + wlfc->stats.total_status_latency += delta; + wlfc->stats.latency_most_recent = delta; + + wlfc->stats.deltas[wlfc->stats.idx_delta++] = delta; + if (wlfc->stats.idx_delta == sizeof(wlfc->stats.deltas)/sizeof(uint32)) + wlfc->stats.idx_delta = 0; + } +#endif /* PROP_TXSTATUS_DEBUG */ + + fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pktbuf)); + + /* pick up the implicit credit from this packet */ + if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pktbuf))) { + if (wlfc->proptxstatus_mode == WLFC_FCMODE_IMPLIED_CREDIT) { + + int lender, credit_returned = 0; /* Note that borrower is fifo_id */ + + /* Return credits to highest priority lender first */ + for (lender = AC_COUNT; lender >= 0; lender--) { + if (wlfc->credits_borrowed[fifo_id][lender] > 0) { + wlfc->FIFO_credit[lender]++; + wlfc->credits_borrowed[fifo_id][lender]--; + credit_returned = 1; + break; + } + } + + if (!credit_returned) { + wlfc->FIFO_credit[fifo_id]++; + } + } + } + else { + /* + if this packet did not count against FIFO credit, it must have + taken a requested_credit from the destination entry (for pspoll etc.) + */ + if (!entry) { + + entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf); + } + if (!DHD_PKTTAG_ONETIMEPKTRQST(PKTTAG(pktbuf))) + entry->requested_credit++; +#ifdef PROP_TXSTATUS_DEBUG + entry->dstncredit_acks++; +#endif + } + if ((status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) || + (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS)) { + + ret = _dhd_wlfc_enque_suppressed(wlfc, fifo_id, pktbuf); + if (ret != BCME_OK) { + /* delay q is full, drop this packet */ + dhd_wlfc_hanger_poppkt(wlfc->hanger, WLFC_PKTID_HSLOT_GET(status), + &pktbuf, 1); + + /* indicate failure and free the packet */ + dhd_txcomplete(dhd, pktbuf, FALSE); + entry->transit_count--; + /* This packet is transmitted Successfully by + * dongle even after first suppress. + */ + if (entry->suppressed) { + entry->suppr_transit_count--; + } + PKTFREE(wlfc->osh, pktbuf, TRUE); + } else { + /* Mark suppressed to avoid a double free during wlfc cleanup */ + dhd_wlfc_hanger_mark_suppressed(wlfc->hanger, + WLFC_PKTID_HSLOT_GET(status), WLFC_PKTID_GEN(status)); + entry->suppress_count++; + } + } + else { + dhd_txcomplete(dhd, pktbuf, TRUE); + entry->transit_count--; + + /* This packet is transmitted Successfully by dongle even after first suppress. */ + if (entry->suppressed) { + entry->suppr_transit_count--; + } + /* free the packet */ + PKTFREE(wlfc->osh, pktbuf, TRUE); + } + return BCME_OK; +} + +static int +dhd_wlfc_fifocreditback_indicate(dhd_pub_t *dhd, uint8* credits) +{ + int i; + athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) + dhd->wlfc_state; + for (i = 0; i < WLFC_CTL_VALUE_LEN_FIFO_CREDITBACK; i++) { +#ifdef PROP_TXSTATUS_DEBUG + wlfc->stats.fifo_credits_back[i] += credits[i]; +#endif + /* update FIFO credits */ + if (wlfc->proptxstatus_mode == WLFC_FCMODE_EXPLICIT_CREDIT) + { + int lender; /* Note that borrower is i */ + + /* Return credits to highest priority lender first */ + for (lender = AC_COUNT; (lender >= 0) && (credits[i] > 0); lender--) { + if (wlfc->credits_borrowed[i][lender] > 0) { + if (credits[i] >= wlfc->credits_borrowed[i][lender]) { + credits[i] -= wlfc->credits_borrowed[i][lender]; + wlfc->FIFO_credit[lender] += + wlfc->credits_borrowed[i][lender]; + wlfc->credits_borrowed[i][lender] = 0; + } + else { + wlfc->credits_borrowed[i][lender] -= credits[i]; + wlfc->FIFO_credit[lender] += credits[i]; + credits[i] = 0; + } + } + } + + /* If we have more credits left over, these must belong to the AC */ + if (credits[i] > 0) { + wlfc->FIFO_credit[i] += credits[i]; + } + } + } + + return BCME_OK; +} + +static int +dhd_wlfc_dbg_senum_check(dhd_pub_t *dhd, uint8 *value) +{ + uint32 timestamp; + + (void)dhd; + + bcopy(&value[2], ×tamp, sizeof(uint32)); + DHD_INFO(("RXPKT: SEQ: %d, timestamp %d\n", value[1], timestamp)); + return BCME_OK; +} + + +static int +dhd_wlfc_rssi_indicate(dhd_pub_t *dhd, uint8* rssi) +{ + (void)dhd; + (void)rssi; + return BCME_OK; +} + +static int +dhd_wlfc_mac_table_update(dhd_pub_t *dhd, uint8* value, uint8 type) +{ + int rc; + athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) + dhd->wlfc_state; + wlfc_mac_descriptor_t* table; + uint8 existing_index; + uint8 table_index; + uint8 ifid; + uint8* ea; + + WLFC_DBGMESG(("%s(), mac [%02x:%02x:%02x:%02x:%02x:%02x],%s,idx:%d,id:0x%02x\n", + __FUNCTION__, value[2], value[3], value[4], value[5], value[6], value[7], + ((type == WLFC_CTL_TYPE_MACDESC_ADD) ? "ADD":"DEL"), + WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]), value[0])); + + table = wlfc->destination_entries.nodes; + table_index = WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]); + ifid = value[1]; + ea = &value[2]; + + if (type == WLFC_CTL_TYPE_MACDESC_ADD) { + existing_index = dhd_wlfc_find_mac_desc_id_from_mac(dhd, &value[2]); + if (existing_index == WLFC_MAC_DESC_ID_INVALID) { + /* this MAC entry does not exist, create one */ + if (!table[table_index].occupied) { + table[table_index].mac_handle = value[0]; + rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index], + eWLFC_MAC_ENTRY_ACTION_ADD, ifid, + wlfc->destination_entries.interfaces[ifid].iftype, + ea); + } + else { + /* the space should have been empty, but it's not */ + wlfc->stats.mac_update_failed++; + } + } + else { + /* + there is an existing entry, move it to new index + if necessary. + */ + if (existing_index != table_index) { + /* if we already have an entry, free the old one */ + table[existing_index].occupied = 0; + table[existing_index].state = WLFC_STATE_CLOSE; + table[existing_index].requested_credit = 0; + table[existing_index].interface_id = 0; + /* enable after packets are queued-deqeued properly. + pktq_flush(dhd->osh, &table[existing_index].psq, FALSE, NULL, 0); + */ + } + } + } + if (type == WLFC_CTL_TYPE_MACDESC_DEL) { + if (table[table_index].occupied) { + rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index], + eWLFC_MAC_ENTRY_ACTION_DEL, ifid, + wlfc->destination_entries.interfaces[ifid].iftype, + ea); + } + else { + /* the space should have been occupied, but it's not */ + wlfc->stats.mac_update_failed++; + } + } + BCM_REFERENCE(rc); + return BCME_OK; +} + +static int +dhd_wlfc_psmode_update(dhd_pub_t *dhd, uint8* value, uint8 type) +{ + /* Handle PS on/off indication */ + athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) + dhd->wlfc_state; + wlfc_mac_descriptor_t* table; + wlfc_mac_descriptor_t* desc; + uint8 mac_handle = value[0]; + int i; + + table = wlfc->destination_entries.nodes; + desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)]; + if (desc->occupied) { + /* a fresh PS mode should wipe old ps credits? */ + desc->requested_credit = 0; + if (type == WLFC_CTL_TYPE_MAC_OPEN) { + desc->state = WLFC_STATE_OPEN; + DHD_WLFC_CTRINC_MAC_OPEN(desc); + } + else { + desc->state = WLFC_STATE_CLOSE; + DHD_WLFC_CTRINC_MAC_CLOSE(desc); + /* + Indicate to firmware if there is any traffic pending. + */ + for (i = AC_BE; i < AC_COUNT; i++) { + _dhd_wlfc_traffic_pending_check(wlfc, desc, i); + } + } + } + else { + wlfc->stats.psmode_update_failed++; + } + return BCME_OK; +} + +static int +dhd_wlfc_interface_update(dhd_pub_t *dhd, uint8* value, uint8 type) +{ + /* Handle PS on/off indication */ + athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) + dhd->wlfc_state; + wlfc_mac_descriptor_t* table; + uint8 if_id = value[0]; + + if (if_id < WLFC_MAX_IFNUM) { + table = wlfc->destination_entries.interfaces; + if (table[if_id].occupied) { + if (type == WLFC_CTL_TYPE_INTERFACE_OPEN) { + table[if_id].state = WLFC_STATE_OPEN; + /* WLFC_DBGMESG(("INTERFACE[%d] OPEN\n", if_id)); */ + } + else { + table[if_id].state = WLFC_STATE_CLOSE; + /* WLFC_DBGMESG(("INTERFACE[%d] CLOSE\n", if_id)); */ + } + return BCME_OK; + } + } + wlfc->stats.interface_update_failed++; + + return BCME_OK; +} + +static int +dhd_wlfc_credit_request(dhd_pub_t *dhd, uint8* value) +{ + athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) + dhd->wlfc_state; + wlfc_mac_descriptor_t* table; + wlfc_mac_descriptor_t* desc; + uint8 mac_handle; + uint8 credit; + + table = wlfc->destination_entries.nodes; + mac_handle = value[1]; + credit = value[0]; + + desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)]; + if (desc->occupied) { + desc->requested_credit = credit; + + desc->ac_bitmap = value[2]; + } + else { + wlfc->stats.credit_request_failed++; + } + return BCME_OK; +} + +static int +dhd_wlfc_packet_request(dhd_pub_t *dhd, uint8* value) +{ + athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) + dhd->wlfc_state; + wlfc_mac_descriptor_t* table; + wlfc_mac_descriptor_t* desc; + uint8 mac_handle; + uint8 packet_count; + + table = wlfc->destination_entries.nodes; + mac_handle = value[1]; + packet_count = value[0]; + + desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)]; + if (desc->occupied) { + desc->requested_packet = packet_count; + + desc->ac_bitmap = value[2]; + } + else { + wlfc->stats.packet_request_failed++; + } + return BCME_OK; +} + +static void +dhd_wlfc_reorderinfo_indicate(uint8 *val, uint8 len, uchar *info_buf, uint *info_len) +{ + if (info_len) { + if (info_buf) { + bcopy(val, info_buf, len); + *info_len = len; + } + else + *info_len = 0; + } +} + +static int +dhd_wlfc_parse_header_info(dhd_pub_t *dhd, void* pktbuf, int tlv_hdr_len, uchar *reorder_info_buf, + uint *reorder_info_len) +{ + uint8 type, len; + uint8* value; + uint8* tmpbuf; + uint16 remainder = tlv_hdr_len; + uint16 processed = 0; + athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) + dhd->wlfc_state; + tmpbuf = (uint8*)PKTDATA(dhd->osh, pktbuf); + if (remainder) { + while ((processed < (WLFC_MAX_PENDING_DATALEN * 2)) && (remainder > 0)) { + type = tmpbuf[processed]; + if (type == WLFC_CTL_TYPE_FILLER) { + remainder -= 1; + processed += 1; + continue; + } + + len = tmpbuf[processed + 1]; + value = &tmpbuf[processed + 2]; + + if (remainder < (2 + len)) + break; + + remainder -= 2 + len; + processed += 2 + len; + if (type == WLFC_CTL_TYPE_TXSTATUS) + dhd_wlfc_txstatus_update(dhd, value); + if (type == WLFC_CTL_TYPE_COMP_TXSTATUS) + dhd_wlfc_compressed_txstatus_update(dhd, value, len); + + else if (type == WLFC_CTL_TYPE_HOST_REORDER_RXPKTS) + dhd_wlfc_reorderinfo_indicate(value, len, reorder_info_buf, + reorder_info_len); + else if (type == WLFC_CTL_TYPE_FIFO_CREDITBACK) + dhd_wlfc_fifocreditback_indicate(dhd, value); + + else if (type == WLFC_CTL_TYPE_RSSI) + dhd_wlfc_rssi_indicate(dhd, value); + + else if (type == WLFC_CTL_TYPE_MAC_REQUEST_CREDIT) + dhd_wlfc_credit_request(dhd, value); + + else if (type == WLFC_CTL_TYPE_MAC_REQUEST_PACKET) + dhd_wlfc_packet_request(dhd, value); + + else if ((type == WLFC_CTL_TYPE_MAC_OPEN) || + (type == WLFC_CTL_TYPE_MAC_CLOSE)) + dhd_wlfc_psmode_update(dhd, value, type); + + else if ((type == WLFC_CTL_TYPE_MACDESC_ADD) || + (type == WLFC_CTL_TYPE_MACDESC_DEL)) + dhd_wlfc_mac_table_update(dhd, value, type); + + else if (type == WLFC_CTL_TYPE_TRANS_ID) + dhd_wlfc_dbg_senum_check(dhd, value); + + else if ((type == WLFC_CTL_TYPE_INTERFACE_OPEN) || + (type == WLFC_CTL_TYPE_INTERFACE_CLOSE)) { + dhd_wlfc_interface_update(dhd, value, type); + } + } + if (remainder != 0) { + /* trouble..., something is not right */ + wlfc->stats.tlv_parse_failed++; + } + } + return BCME_OK; +} + +int +dhd_wlfc_init(dhd_pub_t *dhd) +{ + char iovbuf[12]; /* Room for "tlv" + '\0' + parameter */ + /* enable all signals & indicate host proptxstatus logic is active */ + uint32 tlv = dhd->wlfc_enabled? + WLFC_FLAGS_RSSI_SIGNALS | + WLFC_FLAGS_XONXOFF_SIGNALS | + WLFC_FLAGS_CREDIT_STATUS_SIGNALS | + WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE | + WLFC_FLAGS_HOST_RXRERODER_ACTIVE : 0; + /* WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE | WLFC_FLAGS_HOST_RXRERODER_ACTIVE : 0; */ + + + /* + try to enable/disable signaling by sending "tlv" iovar. if that fails, + fallback to no flow control? Print a message for now. + */ + + /* enable proptxtstatus signaling by default */ + bcm_mkiovar("tlv", (char *)&tlv, 4, iovbuf, sizeof(iovbuf)); + if (dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0) < 0) { + DHD_ERROR(("dhd_wlfc_init(): failed to enable/disable bdcv2 tlv signaling\n")); + } + else { + /* + Leaving the message for now, it should be removed after a while; once + the tlv situation is stable. + */ + DHD_ERROR(("dhd_wlfc_init(): successfully %s bdcv2 tlv signaling, %d\n", + dhd->wlfc_enabled?"enabled":"disabled", tlv)); + } + return BCME_OK; +} + +int +dhd_wlfc_enable(dhd_pub_t *dhd) +{ + int i; + athost_wl_status_info_t* wlfc; + + DHD_TRACE(("Enter %s\n", __FUNCTION__)); + + if (!dhd->wlfc_enabled || dhd->wlfc_state) + return BCME_OK; + + /* allocate space to track txstatus propagated from firmware */ + dhd->wlfc_state = MALLOC(dhd->osh, sizeof(athost_wl_status_info_t)); + if (dhd->wlfc_state == NULL) + return BCME_NOMEM; + + /* initialize state space */ + wlfc = (athost_wl_status_info_t*)dhd->wlfc_state; + memset(wlfc, 0, sizeof(athost_wl_status_info_t)); + + /* remember osh & dhdp */ + wlfc->osh = dhd->osh; + wlfc->dhdp = dhd; + + wlfc->hanger = + dhd_wlfc_hanger_create(dhd->osh, WLFC_HANGER_MAXITEMS); + if (wlfc->hanger == NULL) { + MFREE(dhd->osh, dhd->wlfc_state, sizeof(athost_wl_status_info_t)); + dhd->wlfc_state = NULL; + return BCME_NOMEM; + } + + /* initialize all interfaces to accept traffic */ + for (i = 0; i < WLFC_MAX_IFNUM; i++) { + wlfc->hostif_flow_state[i] = OFF; + } + + /* + create the SENDQ containing + sub-queues for all AC precedences + 1 for bc/mc traffic + */ + pktq_init(&wlfc->SENDQ, (AC_COUNT + 1), WLFC_SENDQ_LEN); + + wlfc->destination_entries.other.state = WLFC_STATE_OPEN; + /* bc/mc FIFO is always open [credit aside], i.e. b[5] */ + wlfc->destination_entries.other.ac_bitmap = 0x1f; + wlfc->destination_entries.other.interface_id = 0; + + wlfc->proptxstatus_mode = WLFC_FCMODE_EXPLICIT_CREDIT; + + wlfc->allow_credit_borrow = TRUE; + wlfc->borrow_defer_timestamp = 0; + + return BCME_OK; +} + +/* release all packet resources */ +void +dhd_wlfc_cleanup(dhd_pub_t *dhd) +{ + int i; + int total_entries; + athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) + dhd->wlfc_state; + wlfc_mac_descriptor_t* table; + wlfc_hanger_t* h; + int prec; + void *pkt = NULL; + struct pktq *txq = NULL; + + DHD_TRACE(("Enter %s\n", __FUNCTION__)); + if (dhd->wlfc_state == NULL) + return; + /* flush bus->txq */ + txq = dhd_bus_txq(dhd->bus); + + /* any in the hanger? */ + h = (wlfc_hanger_t*)wlfc->hanger; + total_entries = sizeof(wlfc->destination_entries)/sizeof(wlfc_mac_descriptor_t); + /* search all entries, include nodes as well as interfaces */ + table = (wlfc_mac_descriptor_t*)&wlfc->destination_entries; + + for (i = 0; i < total_entries; i++) { + if (table[i].occupied) { + if (table[i].psq.len) { + WLFC_DBGMESG(("%s(): DELAYQ[%d].len = %d\n", + __FUNCTION__, i, table[i].psq.len)); + /* release packets held in DELAYQ */ + pktq_flush(wlfc->osh, &table[i].psq, TRUE, NULL, 0); + } + table[i].occupied = 0; + } + } + /* release packets held in SENDQ */ + if (wlfc->SENDQ.len) + pktq_flush(wlfc->osh, &wlfc->SENDQ, TRUE, NULL, 0); + for (prec = 0; prec < txq->num_prec; prec++) { + pkt = pktq_pdeq(txq, prec); + while (pkt) { + for (i = 0; i < h->max_items; i++) { + if (pkt == h->items[i].pkt) { + if (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE) { + PKTFREE(wlfc->osh, h->items[i].pkt, TRUE); + h->items[i].state = WLFC_HANGER_ITEM_STATE_FREE; + h->items[i].pkt = NULL; + h->items[i].identifier = 0; + } else if (h->items[i].state == + WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED) { + /* These are already freed from the psq */ + h->items[i].state = WLFC_HANGER_ITEM_STATE_FREE; + } + break; + } + } + pkt = pktq_pdeq(txq, prec); + } + } + /* flush remained pkt in hanger queue, not in bus->txq */ + for (i = 0; i < h->max_items; i++) { + if (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE) { + if (!dhd->hang_was_sent) { + PKTFREE(wlfc->osh, h->items[i].pkt, TRUE); + } else { + printk("%s: Skip freeing skb %p\n", __func__, h->items[i].pkt); + } + h->items[i].state = WLFC_HANGER_ITEM_STATE_FREE; + h->items[i].pkt = NULL; + h->items[i].identifier = 0; + } else if (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED) { + /* These are freed from the psq so no need to free again */ + h->items[i].state = WLFC_HANGER_ITEM_STATE_FREE; + } + } + return; +} + +void +dhd_wlfc_deinit(dhd_pub_t *dhd) +{ + /* cleanup all psq related resources */ + athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) + dhd->wlfc_state; + + DHD_TRACE(("Enter %s\n", __FUNCTION__)); + + dhd_os_wlfc_block(dhd); + if (dhd->wlfc_state == NULL) { + dhd_os_wlfc_unblock(dhd); + return; + } +#ifdef PROP_TXSTATUS_DEBUG + { + int i; + wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger; + for (i = 0; i < h->max_items; i++) { + if (h->items[i].state != WLFC_HANGER_ITEM_STATE_FREE) { + WLFC_DBGMESG(("%s() pkt[%d] = 0x%p, FIFO_credit_used:%d\n", + __FUNCTION__, i, h->items[i].pkt, + DHD_PKTTAG_CREDITCHECK(PKTTAG(h->items[i].pkt)))); + } + } + } +#endif + /* delete hanger */ + dhd_wlfc_hanger_delete(dhd->osh, wlfc->hanger); + + /* free top structure */ + MFREE(dhd->osh, dhd->wlfc_state, sizeof(athost_wl_status_info_t)); + dhd->wlfc_state = NULL; + dhd_os_wlfc_unblock(dhd); + return; +} +#endif /* PROP_TXSTATUS */ + +void +dhd_prot_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf) +{ + bcm_bprintf(strbuf, "Protocol CDC: reqid %d\n", dhdp->prot->reqid); +#ifdef PROP_TXSTATUS + dhd_os_wlfc_block(dhdp); + if (dhdp->wlfc_state) + dhd_wlfc_dump(dhdp, strbuf); + dhd_os_wlfc_unblock(dhdp); +#endif +} + +void +dhd_prot_hdrpush(dhd_pub_t *dhd, int ifidx, void *pktbuf) +{ +#ifdef BDC + struct bdc_header *h; +#endif /* BDC */ + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + +#ifdef BDC + /* Push BDC header used to convey priority for buses that don't */ + + PKTPUSH(dhd->osh, pktbuf, BDC_HEADER_LEN); + + h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf); + + h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT); + if (PKTSUMNEEDED(pktbuf)) + h->flags |= BDC_FLAG_SUM_NEEDED; + + + h->priority = (PKTPRIO(pktbuf) & BDC_PRIORITY_MASK); + h->flags2 = 0; + h->dataOffset = 0; +#endif /* BDC */ + BDC_SET_IF_IDX(h, ifidx); +} + +int +dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, void *pktbuf, uchar *reorder_buf_info, + uint *reorder_info_len) +{ +#ifdef BDC + struct bdc_header *h; +#endif + uint8 data_offset = 0; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + +#ifdef BDC + if (reorder_info_len) + *reorder_info_len = 0; + /* Pop BDC header used to convey priority for buses that don't */ + + if (PKTLEN(dhd->osh, pktbuf) < BDC_HEADER_LEN) { + DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__, + PKTLEN(dhd->osh, pktbuf), BDC_HEADER_LEN)); + return BCME_ERROR; + } + + h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf); + +#if defined(NDIS630) + h->dataOffset = 0; +#endif + + if (!ifidx) { + /* for tx packet, skip the analysis and just exit */ + data_offset = h->dataOffset; + PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN); + goto exit; + } + + if ((*ifidx = BDC_GET_IF_IDX(h)) >= DHD_MAX_IFS) { + DHD_ERROR(("%s: rx data ifnum out of range (%d)\n", + __FUNCTION__, *ifidx)); + return BCME_ERROR; + } + + if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) != BDC_PROTO_VER) { + DHD_ERROR(("%s: non-BDC packet received, flags = 0x%x\n", + dhd_ifname(dhd, *ifidx), h->flags)); + if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) == BDC_PROTO_VER_1) + h->dataOffset = 0; + else + return BCME_ERROR; + } + + if (h->flags & BDC_FLAG_SUM_GOOD) { + DHD_INFO(("%s: BDC packet received with good rx-csum, flags 0x%x\n", + dhd_ifname(dhd, *ifidx), h->flags)); + PKTSETSUMGOOD(pktbuf, TRUE); + } + + PKTSETPRIO(pktbuf, (h->priority & BDC_PRIORITY_MASK)); + data_offset = h->dataOffset; + PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN); +#endif /* BDC */ + +#if !defined(NDIS630) + if (PKTLEN(dhd->osh, pktbuf) < (uint32) (data_offset << 2)) { + DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__, + PKTLEN(dhd->osh, pktbuf), (data_offset * 4))); + return BCME_ERROR; + } +#endif +#ifdef PROP_TXSTATUS + if (dhd->wlfc_state && + ((athost_wl_status_info_t*)dhd->wlfc_state)->proptxstatus_mode + != WLFC_FCMODE_NONE && + (!DHD_PKTTAG_PKTDIR(PKTTAG(pktbuf)))) { + /* + - parse txstatus only for packets that came from the firmware + */ + dhd_os_wlfc_block(dhd); + dhd_wlfc_parse_header_info(dhd, pktbuf, (data_offset << 2), + reorder_buf_info, reorder_info_len); + ((athost_wl_status_info_t*)dhd->wlfc_state)->stats.dhd_hdrpulls++; + dhd_os_wlfc_unblock(dhd); + } +#endif /* PROP_TXSTATUS */ +exit: +#if !defined(NDIS630) + PKTPULL(dhd->osh, pktbuf, (data_offset << 2)); +#endif + return 0; +} + +#if defined(PROP_TXSTATUS) +void +dhd_wlfc_trigger_pktcommit(dhd_pub_t *dhd) +{ + if (dhd->wlfc_state && + (((athost_wl_status_info_t*)dhd->wlfc_state)->proptxstatus_mode + != WLFC_FCMODE_NONE)) { + dhd_os_wlfc_block(dhd); + dhd_wlfc_commit_packets(dhd->wlfc_state, (f_commitpkt_t)dhd_bus_txdata, + (void *)dhd->bus); + dhd_os_wlfc_unblock(dhd); + } +} +#endif + +int +dhd_prot_attach(dhd_pub_t *dhd) +{ + dhd_prot_t *cdc; + + if (!(cdc = (dhd_prot_t *)DHD_OS_PREALLOC(dhd->osh, DHD_PREALLOC_PROT, + sizeof(dhd_prot_t)))) { + DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); + goto fail; + } + memset(cdc, 0, sizeof(dhd_prot_t)); + + /* ensure that the msg buf directly follows the cdc msg struct */ + if ((uintptr)(&cdc->msg + 1) != (uintptr)cdc->buf) { + DHD_ERROR(("dhd_prot_t is not correctly defined\n")); + goto fail; + } + + dhd->prot = cdc; +#ifdef BDC + dhd->hdrlen += BDC_HEADER_LEN; +#endif + dhd->maxctl = WLC_IOCTL_MAXLEN + sizeof(cdc_ioctl_t) + ROUND_UP_MARGIN; + return 0; + +fail: +#ifndef CONFIG_DHD_USE_STATIC_BUF + if (cdc != NULL) + MFREE(dhd->osh, cdc, sizeof(dhd_prot_t)); +#endif /* CONFIG_DHD_USE_STATIC_BUF */ + return BCME_NOMEM; +} + +/* ~NOTE~ What if another thread is waiting on the semaphore? Holding it? */ +void +dhd_prot_detach(dhd_pub_t *dhd) +{ +#ifdef PROP_TXSTATUS + dhd_wlfc_deinit(dhd); +#endif +#ifndef CONFIG_DHD_USE_STATIC_BUF + MFREE(dhd->osh, dhd->prot, sizeof(dhd_prot_t)); +#endif /* CONFIG_DHD_USE_STATIC_BUF */ + dhd->prot = NULL; +} + +void +dhd_prot_dstats(dhd_pub_t *dhd) +{ + /* No stats from dongle added yet, copy bus stats */ + dhd->dstats.tx_packets = dhd->tx_packets; + dhd->dstats.tx_errors = dhd->tx_errors; + dhd->dstats.rx_packets = dhd->rx_packets; + dhd->dstats.rx_errors = dhd->rx_errors; + dhd->dstats.rx_dropped = dhd->rx_dropped; + dhd->dstats.multicast = dhd->rx_multicast; + return; +} + +int +dhd_prot_init(dhd_pub_t *dhd) +{ + int ret = 0; + wlc_rev_info_t revinfo; + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + + /* Get the device rev info */ + memset(&revinfo, 0, sizeof(revinfo)); + ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_REVINFO, &revinfo, sizeof(revinfo), FALSE, 0); + if (ret < 0) + goto done; + + +#if defined(WL_CFG80211) + if (dhd_download_fw_on_driverload) +#endif /* defined(WL_CFG80211) */ + ret = dhd_preinit_ioctls(dhd); + +#ifdef PROP_TXSTATUS + ret = dhd_wlfc_init(dhd); +#endif + + /* Always assumes wl for now */ + dhd->iswl = TRUE; + +done: + return ret; +} + +void +dhd_prot_stop(dhd_pub_t *dhd) +{ + /* Nothing to do for CDC */ +} + + +static void +dhd_get_hostreorder_pkts(void *osh, struct reorder_info *ptr, void **pkt, + uint32 *pkt_count, void **pplast, uint8 start, uint8 end) +{ + uint i; + void *plast = NULL, *p; + uint32 pkt_cnt = 0; + + if (ptr->pend_pkts == 0) { + DHD_REORDER(("%s: no packets in reorder queue \n", __FUNCTION__)); + *pplast = NULL; + *pkt_count = 0; + *pkt = NULL; + return; + } + if (start == end) + i = ptr->max_idx + 1; + else { + if (start > end) + i = ((ptr->max_idx + 1) - start) + end; + else + i = end - start; + } + while (i) { + p = (void *)(ptr->p[start]); + ptr->p[start] = NULL; + + if (p != NULL) { + if (plast == NULL) + *pkt = p; + else + PKTSETNEXT(osh, plast, p); + + plast = p; + pkt_cnt++; + } + i--; + if (start++ == ptr->max_idx) + start = 0; + } + *pplast = plast; + *pkt_count = (uint32)pkt_cnt; +} + +int +dhd_process_pkt_reorder_info(dhd_pub_t *dhd, uchar *reorder_info_buf, uint reorder_info_len, + void **pkt, uint32 *pkt_count) +{ + uint8 flow_id, max_idx, cur_idx, exp_idx; + struct reorder_info *ptr; + uint8 flags; + void *cur_pkt, *plast = NULL; + uint32 cnt = 0; + + if (pkt == NULL) { + if (pkt_count != NULL) + *pkt_count = 0; + return 0; + } + + flow_id = reorder_info_buf[WLHOST_REORDERDATA_FLOWID_OFFSET]; + flags = reorder_info_buf[WLHOST_REORDERDATA_FLAGS_OFFSET]; + + DHD_REORDER(("flow_id %d, flags 0x%02x, idx(%d, %d, %d)\n", flow_id, flags, + reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET], + reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET], + reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET])); + + /* validate flags and flow id */ + if (flags == 0xFF) { + DHD_ERROR(("%s: invalid flags...so ignore this packet\n", __FUNCTION__)); + *pkt_count = 1; + return 0; + } + + cur_pkt = *pkt; + *pkt = NULL; + + ptr = dhd->reorder_bufs[flow_id]; + if (flags & WLHOST_REORDERDATA_DEL_FLOW) { + uint32 buf_size = sizeof(struct reorder_info); + + DHD_REORDER(("%s: Flags indicating to delete a flow id %d\n", + __FUNCTION__, flow_id)); + + if (ptr == NULL) { + DHD_ERROR(("%s: received flags to cleanup, but no flow (%d) yet\n", + __FUNCTION__, flow_id)); + *pkt_count = 1; + *pkt = cur_pkt; + return 0; + } + + dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast, + ptr->exp_idx, ptr->exp_idx); + /* set it to the last packet */ + if (plast) { + PKTSETNEXT(dhd->osh, plast, cur_pkt); + cnt++; + } + else { + if (cnt != 0) { + DHD_ERROR(("%s: del flow: something fishy, pending packets %d\n", + __FUNCTION__, cnt)); + } + *pkt = cur_pkt; + cnt = 1; + } + buf_size += ((ptr->max_idx + 1) * sizeof(void *)); + MFREE(dhd->osh, ptr, buf_size); + dhd->reorder_bufs[flow_id] = NULL; + *pkt_count = cnt; + return 0; + } + /* all the other cases depend on the existance of the reorder struct for that flow id */ + if (ptr == NULL) { + uint32 buf_size_alloc = sizeof(reorder_info_t); + max_idx = reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET]; + + buf_size_alloc += ((max_idx + 1) * sizeof(void*)); + /* allocate space to hold the buffers, index etc */ + + DHD_REORDER(("%s: alloc buffer of size %d size, reorder info id %d, maxidx %d\n", + __FUNCTION__, buf_size_alloc, flow_id, max_idx)); + ptr = (struct reorder_info *)MALLOC(dhd->osh, buf_size_alloc); + if (ptr == NULL) { + DHD_ERROR(("%s: Malloc failed to alloc buffer\n", __FUNCTION__)); + *pkt_count = 1; + return 0; + } + bzero(ptr, buf_size_alloc); + dhd->reorder_bufs[flow_id] = ptr; + ptr->p = (void *)(ptr+1); + ptr->max_idx = max_idx; + } + if (flags & WLHOST_REORDERDATA_NEW_HOLE) { + DHD_REORDER(("%s: new hole, so cleanup pending buffers\n", __FUNCTION__)); + if (ptr->pend_pkts) { + dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast, + ptr->exp_idx, ptr->exp_idx); + ptr->pend_pkts = 0; + } + ptr->cur_idx = reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET]; + ptr->exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET]; + ptr->max_idx = reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET]; + ptr->p[ptr->cur_idx] = cur_pkt; + ptr->pend_pkts++; + *pkt_count = cnt; + } + else if (flags & WLHOST_REORDERDATA_CURIDX_VALID) { + cur_idx = reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET]; + exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET]; + + + if ((exp_idx == ptr->exp_idx) && (cur_idx != ptr->exp_idx)) { + /* still in the current hole */ + /* enqueue the current on the buffer chain */ + if (ptr->p[cur_idx] != NULL) { + DHD_REORDER(("%s: HOLE: ERROR buffer pending..free it\n", + __FUNCTION__)); + PKTFREE(dhd->osh, ptr->p[cur_idx], TRUE); + ptr->p[cur_idx] = NULL; + } + ptr->p[cur_idx] = cur_pkt; + ptr->pend_pkts++; + ptr->cur_idx = cur_idx; + DHD_REORDER(("%s: fill up a hole..pending packets is %d\n", + __FUNCTION__, ptr->pend_pkts)); + *pkt_count = 0; + *pkt = NULL; + } + else if (ptr->exp_idx == cur_idx) { + /* got the right one ..flush from cur to exp and update exp */ + DHD_REORDER(("%s: got the right one now, cur_idx is %d\n", + __FUNCTION__, cur_idx)); + if (ptr->p[cur_idx] != NULL) { + DHD_REORDER(("%s: Error buffer pending..free it\n", + __FUNCTION__)); + PKTFREE(dhd->osh, ptr->p[cur_idx], TRUE); + ptr->p[cur_idx] = NULL; + } + ptr->p[cur_idx] = cur_pkt; + ptr->pend_pkts++; + + ptr->cur_idx = cur_idx; + ptr->exp_idx = exp_idx; + + dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast, + cur_idx, exp_idx); + ptr->pend_pkts -= (uint8)cnt; + *pkt_count = cnt; + DHD_REORDER(("%s: freeing up buffers %d, still pending %d\n", + __FUNCTION__, cnt, ptr->pend_pkts)); + } + else { + uint8 end_idx; + bool flush_current = FALSE; + /* both cur and exp are moved now .. */ + DHD_REORDER(("%s:, flow %d, both moved, cur %d(%d), exp %d(%d)\n", + __FUNCTION__, flow_id, ptr->cur_idx, cur_idx, + ptr->exp_idx, exp_idx)); + if (flags & WLHOST_REORDERDATA_FLUSH_ALL) + end_idx = ptr->exp_idx; + else + end_idx = exp_idx; + + /* flush pkts first */ + dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast, + ptr->exp_idx, end_idx); + + if (cur_idx == ptr->max_idx) { + if (exp_idx == 0) + flush_current = TRUE; + } else { + if (exp_idx == cur_idx + 1) + flush_current = TRUE; + } + if (flush_current) { + if (plast) + PKTSETNEXT(dhd->osh, plast, cur_pkt); + else + *pkt = cur_pkt; + cnt++; + } + else { + ptr->p[cur_idx] = cur_pkt; + ptr->pend_pkts++; + } + ptr->exp_idx = exp_idx; + ptr->cur_idx = cur_idx; + *pkt_count = cnt; + } + } + else { + uint8 end_idx; + /* no real packet but update to exp_seq...that means explicit window move */ + exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET]; + + DHD_REORDER(("%s: move the window, cur_idx is %d, exp is %d, new exp is %d\n", + __FUNCTION__, ptr->cur_idx, ptr->exp_idx, exp_idx)); + if (flags & WLHOST_REORDERDATA_FLUSH_ALL) + end_idx = ptr->exp_idx; + else + end_idx = exp_idx; + + dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast, ptr->exp_idx, end_idx); + ptr->pend_pkts -= (uint8)cnt; + if (plast) + PKTSETNEXT(dhd->osh, plast, cur_pkt); + else + *pkt = cur_pkt; + cnt++; + *pkt_count = cnt; + /* set the new expected idx */ + ptr->exp_idx = exp_idx; + } + return 0; +} diff --git a/drivers/net/wireless/bcmdhd/dhd_cfg80211.c b/drivers/net/wireless/bcmdhd/dhd_cfg80211.c new file mode 100644 index 00000000..03671c46 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_cfg80211.c @@ -0,0 +1,678 @@ +/* + * Linux cfg80211 driver - Dongle Host Driver (DHD) related + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_cfg80211.c,v 1.1.4.1.2.14 2011/02/09 01:40:07 Exp $ + */ + +#include + +#include +#include +#include +#include + +#ifdef PKT_FILTER_SUPPORT +#include +#include +#endif + +extern struct wl_priv *wlcfg_drv_priv; + +#ifdef PKT_FILTER_SUPPORT +extern uint dhd_pkt_filter_enable; +extern uint dhd_master_mode; +extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode); +#endif + +static int dhd_dongle_up = FALSE; + +#include +#include +#include +#include +#include + +static s32 wl_dongle_up(struct net_device *ndev, u32 up); + +/** + * Function implementations + */ + +s32 dhd_cfg80211_init(struct wl_priv *wl) +{ + dhd_dongle_up = FALSE; + return 0; +} + +s32 dhd_cfg80211_deinit(struct wl_priv *wl) +{ + dhd_dongle_up = FALSE; + return 0; +} + +s32 dhd_cfg80211_down(struct wl_priv *wl) +{ + dhd_dongle_up = FALSE; + return 0; +} + +s32 dhd_cfg80211_set_p2p_info(struct wl_priv *wl, int val) +{ + dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); + dhd->op_mode |= val; + WL_ERR(("Set : op_mode=0x%04x\n", dhd->op_mode)); +#ifdef ARP_OFFLOAD_SUPPORT + if (dhd->arp_version == 1) { + /* IF P2P is enabled, disable arpoe */ + dhd_arp_offload_set(dhd, 0); + dhd_arp_offload_enable(dhd, false); + } +#endif /* ARP_OFFLOAD_SUPPORT */ + + return 0; +} + +s32 dhd_cfg80211_clean_p2p_info(struct wl_priv *wl) +{ + dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); + dhd->op_mode &= ~(DHD_FLAG_P2P_GC_MODE | DHD_FLAG_P2P_GO_MODE); + WL_ERR(("Clean : op_mode=0x%04x\n", dhd->op_mode)); + +#ifdef ARP_OFFLOAD_SUPPORT + if (dhd->arp_version == 1) { + /* IF P2P is disabled, enable arpoe back for STA mode. */ + dhd_arp_offload_set(dhd, dhd_arp_mode); + dhd_arp_offload_enable(dhd, true); + } +#endif /* ARP_OFFLOAD_SUPPORT */ + + return 0; +} + +static s32 wl_dongle_up(struct net_device *ndev, u32 up) +{ + s32 err = 0; + + err = wldev_ioctl(ndev, WLC_UP, &up, sizeof(up), true); + if (unlikely(err)) { + WL_ERR(("WLC_UP error (%d)\n", err)); + } + return err; +} +s32 dhd_config_dongle(struct wl_priv *wl, bool need_lock) +{ +#ifndef DHD_SDALIGN +#define DHD_SDALIGN 32 +#endif + struct net_device *ndev; + s32 err = 0; + + WL_TRACE(("In\n")); + if (dhd_dongle_up) { + WL_ERR(("Dongle is already up\n")); + return err; + } + + ndev = wl_to_prmry_ndev(wl); + + if (need_lock) + rtnl_lock(); + + err = wl_dongle_up(ndev, 0); + if (unlikely(err)) { + WL_ERR(("wl_dongle_up failed\n")); + goto default_conf_out; + } + dhd_dongle_up = true; + +default_conf_out: + if (need_lock) + rtnl_unlock(); + return err; + +} + + +/* TODO: clean up the BT-Coex code, it still have some legacy ioctl/iovar functions */ +#define COEX_DHCP + +#if defined(COEX_DHCP) + +/* use New SCO/eSCO smart YG suppression */ +#define BT_DHCP_eSCO_FIX +/* this flag boost wifi pkt priority to max, caution: -not fair to sco */ +#define BT_DHCP_USE_FLAGS +/* T1 start SCO/ESCo priority suppression */ +#define BT_DHCP_OPPR_WIN_TIME 2500 +/* T2 turn off SCO/SCO supperesion is (timeout) */ +#define BT_DHCP_FLAG_FORCE_TIME 5500 + +enum wl_cfg80211_btcoex_status { + BT_DHCP_IDLE, + BT_DHCP_START, + BT_DHCP_OPPR_WIN, + BT_DHCP_FLAG_FORCE_TIMEOUT +}; + +/* + * get named driver variable to uint register value and return error indication + * calling example: dev_wlc_intvar_get_reg(dev, "btc_params",66, ®_value) + */ +static int +dev_wlc_intvar_get_reg(struct net_device *dev, char *name, + uint reg, int *retval) +{ + union { + char buf[WLC_IOCTL_SMLEN]; + int val; + } var; + int error; + + bcm_mkiovar(name, (char *)(®), sizeof(reg), + (char *)(&var), sizeof(var.buf)); + error = wldev_ioctl(dev, WLC_GET_VAR, (char *)(&var), sizeof(var.buf), false); + + *retval = dtoh32(var.val); + return (error); +} + +static int +dev_wlc_bufvar_set(struct net_device *dev, char *name, char *buf, int len) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) + char ioctlbuf_local[1024]; +#else + static char ioctlbuf_local[1024]; +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */ + + bcm_mkiovar(name, buf, len, ioctlbuf_local, sizeof(ioctlbuf_local)); + + return (wldev_ioctl(dev, WLC_SET_VAR, ioctlbuf_local, sizeof(ioctlbuf_local), true)); +} +/* +get named driver variable to uint register value and return error indication +calling example: dev_wlc_intvar_set_reg(dev, "btc_params",66, value) +*/ +static int +dev_wlc_intvar_set_reg(struct net_device *dev, char *name, char *addr, char * val) +{ + char reg_addr[8]; + + memset(reg_addr, 0, sizeof(reg_addr)); + memcpy((char *)®_addr[0], (char *)addr, 4); + memcpy((char *)®_addr[4], (char *)val, 4); + + return (dev_wlc_bufvar_set(dev, name, (char *)®_addr[0], sizeof(reg_addr))); +} + +static bool btcoex_is_sco_active(struct net_device *dev) +{ + int ioc_res = 0; + bool res = FALSE; + int sco_id_cnt = 0; + int param27; + int i; + + for (i = 0; i < 12; i++) { + + ioc_res = dev_wlc_intvar_get_reg(dev, "btc_params", 27, ¶m27); + + WL_TRACE(("%s, sample[%d], btc params: 27:%x\n", + __FUNCTION__, i, param27)); + + if (ioc_res < 0) { + WL_ERR(("%s ioc read btc params error\n", __FUNCTION__)); + break; + } + + if ((param27 & 0x6) == 2) { /* count both sco & esco */ + sco_id_cnt++; + } + + if (sco_id_cnt > 2) { + WL_TRACE(("%s, sco/esco detected, pkt id_cnt:%d samples:%d\n", + __FUNCTION__, sco_id_cnt, i)); + res = TRUE; + break; + } + + msleep(5); + } + + return res; +} + +#if defined(BT_DHCP_eSCO_FIX) +/* Enhanced BT COEX settings for eSCO compatibility during DHCP window */ +static int set_btc_esco_params(struct net_device *dev, bool trump_sco) +{ + static bool saved_status = FALSE; + + char buf_reg50va_dhcp_on[8] = + { 50, 00, 00, 00, 0x22, 0x80, 0x00, 0x00 }; + char buf_reg51va_dhcp_on[8] = + { 51, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + char buf_reg64va_dhcp_on[8] = + { 64, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + char buf_reg65va_dhcp_on[8] = + { 65, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + char buf_reg71va_dhcp_on[8] = + { 71, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + uint32 regaddr; + static uint32 saved_reg50; + static uint32 saved_reg51; + static uint32 saved_reg64; + static uint32 saved_reg65; + static uint32 saved_reg71; + + if (trump_sco) { + /* this should reduce eSCO agressive retransmit + * w/o breaking it + */ + + /* 1st save current */ + WL_TRACE(("Do new SCO/eSCO coex algo {save &" + "override}\n")); + if ((!dev_wlc_intvar_get_reg(dev, "btc_params", 50, &saved_reg50)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 51, &saved_reg51)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 64, &saved_reg64)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 65, &saved_reg65)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 71, &saved_reg71))) { + saved_status = TRUE; + WL_TRACE(("%s saved bt_params[50,51,64,65,71]:" + "0x%x 0x%x 0x%x 0x%x 0x%x\n", + __FUNCTION__, saved_reg50, saved_reg51, + saved_reg64, saved_reg65, saved_reg71)); + } else { + WL_ERR((":%s: save btc_params failed\n", + __FUNCTION__)); + saved_status = FALSE; + return -1; + } + + WL_TRACE(("override with [50,51,64,65,71]:" + "0x%x 0x%x 0x%x 0x%x 0x%x\n", + *(u32 *)(buf_reg50va_dhcp_on+4), + *(u32 *)(buf_reg51va_dhcp_on+4), + *(u32 *)(buf_reg64va_dhcp_on+4), + *(u32 *)(buf_reg65va_dhcp_on+4), + *(u32 *)(buf_reg71va_dhcp_on+4))); + + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg50va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg51va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg64va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg65va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg71va_dhcp_on[0], 8); + + saved_status = TRUE; + } else if (saved_status) { + /* restore previously saved bt params */ + WL_TRACE(("Do new SCO/eSCO coex algo {save &" + "override}\n")); + + regaddr = 50; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg50); + regaddr = 51; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg51); + regaddr = 64; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg64); + regaddr = 65; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg65); + regaddr = 71; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg71); + + WL_TRACE(("restore bt_params[50,51,64,65,71]:" + "0x%x 0x%x 0x%x 0x%x 0x%x\n", + saved_reg50, saved_reg51, saved_reg64, + saved_reg65, saved_reg71)); + + saved_status = FALSE; + } else { + WL_ERR((":%s att to restore not saved BTCOEX params\n", + __FUNCTION__)); + return -1; + } + return 0; +} +#endif /* BT_DHCP_eSCO_FIX */ + +static void +wl_cfg80211_bt_setflag(struct net_device *dev, bool set) +{ +#if defined(BT_DHCP_USE_FLAGS) + char buf_flag7_dhcp_on[8] = { 7, 00, 00, 00, 0x1, 0x0, 0x00, 0x00 }; + char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00}; +#endif + + +#if defined(BT_DHCP_eSCO_FIX) + /* set = 1, save & turn on 0 - off & restore prev settings */ + set_btc_esco_params(dev, set); +#endif + +#if defined(BT_DHCP_USE_FLAGS) + WL_TRACE(("WI-FI priority boost via bt flags, set:%d\n", set)); + if (set == TRUE) + /* Forcing bt_flag7 */ + dev_wlc_bufvar_set(dev, "btc_flags", + (char *)&buf_flag7_dhcp_on[0], + sizeof(buf_flag7_dhcp_on)); + else + /* Restoring default bt flag7 */ + dev_wlc_bufvar_set(dev, "btc_flags", + (char *)&buf_flag7_default[0], + sizeof(buf_flag7_default)); +#endif +} + +static void wl_cfg80211_bt_timerfunc(ulong data) +{ + struct btcoex_info *bt_local = (struct btcoex_info *)data; + WL_TRACE(("%s\n", __FUNCTION__)); + bt_local->timer_on = 0; + schedule_work(&bt_local->work); +} + +static void wl_cfg80211_bt_handler(struct work_struct *work) +{ + struct btcoex_info *btcx_inf; + + btcx_inf = container_of(work, struct btcoex_info, work); + + if (btcx_inf->timer_on) { + btcx_inf->timer_on = 0; + del_timer_sync(&btcx_inf->timer); + } + + switch (btcx_inf->bt_state) { + case BT_DHCP_START: + /* DHCP started + * provide OPPORTUNITY window to get DHCP address + */ + WL_TRACE(("%s bt_dhcp stm: started \n", + __FUNCTION__)); + btcx_inf->bt_state = BT_DHCP_OPPR_WIN; + mod_timer(&btcx_inf->timer, + jiffies + msecs_to_jiffies(BT_DHCP_OPPR_WIN_TIME)); + btcx_inf->timer_on = 1; + break; + + case BT_DHCP_OPPR_WIN: + if (btcx_inf->dhcp_done) { + WL_TRACE(("%s DHCP Done before T1 expiration\n", + __FUNCTION__)); + goto btc_coex_idle; + } + + /* DHCP is not over yet, start lowering BT priority + * enforce btc_params + flags if necessary + */ + WL_TRACE(("%s DHCP T1:%d expired\n", __FUNCTION__, + BT_DHCP_OPPR_WIN_TIME)); + if (btcx_inf->dev) + wl_cfg80211_bt_setflag(btcx_inf->dev, TRUE); + btcx_inf->bt_state = BT_DHCP_FLAG_FORCE_TIMEOUT; + mod_timer(&btcx_inf->timer, + jiffies + msecs_to_jiffies(BT_DHCP_FLAG_FORCE_TIME)); + btcx_inf->timer_on = 1; + break; + + case BT_DHCP_FLAG_FORCE_TIMEOUT: + if (btcx_inf->dhcp_done) { + WL_TRACE(("%s DHCP Done before T2 expiration\n", + __FUNCTION__)); + } else { + /* Noo dhcp during T1+T2, restore BT priority */ + WL_TRACE(("%s DHCP wait interval T2:%d" + "msec expired\n", __FUNCTION__, + BT_DHCP_FLAG_FORCE_TIME)); + } + + /* Restoring default bt priority */ + if (btcx_inf->dev) + wl_cfg80211_bt_setflag(btcx_inf->dev, FALSE); +btc_coex_idle: + btcx_inf->bt_state = BT_DHCP_IDLE; + btcx_inf->timer_on = 0; + break; + + default: + WL_ERR(("%s error g_status=%d !!!\n", __FUNCTION__, + btcx_inf->bt_state)); + if (btcx_inf->dev) + wl_cfg80211_bt_setflag(btcx_inf->dev, FALSE); + btcx_inf->bt_state = BT_DHCP_IDLE; + btcx_inf->timer_on = 0; + break; + } + + net_os_wake_unlock(btcx_inf->dev); +} + +int wl_cfg80211_btcoex_init(struct wl_priv *wl) +{ + struct btcoex_info *btco_inf = NULL; + + btco_inf = kmalloc(sizeof(struct btcoex_info), GFP_KERNEL); + if (!btco_inf) + return -ENOMEM; + + btco_inf->bt_state = BT_DHCP_IDLE; + btco_inf->ts_dhcp_start = 0; + btco_inf->ts_dhcp_ok = 0; + /* Set up timer for BT */ + btco_inf->timer_ms = 10; + init_timer(&btco_inf->timer); + btco_inf->timer.data = (ulong)btco_inf; + btco_inf->timer.function = wl_cfg80211_bt_timerfunc; + + btco_inf->dev = wl->wdev->netdev; + + INIT_WORK(&btco_inf->work, wl_cfg80211_bt_handler); + + wl->btcoex_info = btco_inf; + return 0; +} + +void wl_cfg80211_btcoex_deinit(struct wl_priv *wl) +{ + if (!wl->btcoex_info) + return; + + if (wl->btcoex_info->timer_on) { + wl->btcoex_info->timer_on = 0; + del_timer_sync(&wl->btcoex_info->timer); + } + + cancel_work_sync(&wl->btcoex_info->work); + + kfree(wl->btcoex_info); + wl->btcoex_info = NULL; +} +#endif + +int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command) +{ + + struct wl_priv *wl = wlcfg_drv_priv; + char powermode_val = 0; + char buf_reg66va_dhcp_on[8] = { 66, 00, 00, 00, 0x10, 0x27, 0x00, 0x00 }; + char buf_reg41va_dhcp_on[8] = { 41, 00, 00, 00, 0x33, 0x00, 0x00, 0x00 }; + char buf_reg68va_dhcp_on[8] = { 68, 00, 00, 00, 0x90, 0x01, 0x00, 0x00 }; + + uint32 regaddr; + static uint32 saved_reg66; + static uint32 saved_reg41; + static uint32 saved_reg68; + static bool saved_status = FALSE; + +#ifdef COEX_DHCP + char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00}; + struct btcoex_info *btco_inf = wl->btcoex_info; +#endif /* COEX_DHCP */ + +#ifdef PKT_FILTER_SUPPORT + dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); +#endif + + /* Figure out powermode 1 or o command */ + strncpy((char *)&powermode_val, command + strlen("BTCOEXMODE") +1, 1); + + if (strnicmp((char *)&powermode_val, "1", strlen("1")) == 0) { + WL_TRACE_HW4(("%s: DHCP session starts\n", __FUNCTION__)); + +#if defined(DHCP_SCAN_SUPPRESS) + /* Suppress scan during the DHCP */ + wl_cfg80211_scan_suppress(dev, 1); +#endif + +#ifdef PKT_FILTER_SUPPORT + dhd->dhcp_in_progress = 1; + + if (dhd->early_suspended) { + WL_TRACE_HW4(("DHCP in progressing , disable packet filter!!!\n")); + dhd_enable_packet_filter(0, dhd); + } +#endif + + /* Retrieve and saved orig regs value */ + if ((saved_status == FALSE) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 66, &saved_reg66)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 41, &saved_reg41)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 68, &saved_reg68))) { + saved_status = TRUE; + WL_TRACE(("Saved 0x%x 0x%x 0x%x\n", + saved_reg66, saved_reg41, saved_reg68)); + + /* Disable PM mode during dhpc session */ + + /* Disable PM mode during dhpc session */ +#ifdef COEX_DHCP + /* Start BT timer only for SCO connection */ + if (btcoex_is_sco_active(dev)) { + /* btc_params 66 */ + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg66va_dhcp_on[0], + sizeof(buf_reg66va_dhcp_on)); + /* btc_params 41 0x33 */ + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg41va_dhcp_on[0], + sizeof(buf_reg41va_dhcp_on)); + /* btc_params 68 0x190 */ + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg68va_dhcp_on[0], + sizeof(buf_reg68va_dhcp_on)); + saved_status = TRUE; + + btco_inf->bt_state = BT_DHCP_START; + btco_inf->timer_on = 1; + mod_timer(&btco_inf->timer, btco_inf->timer.expires); + WL_TRACE(("%s enable BT DHCP Timer\n", + __FUNCTION__)); + } +#endif /* COEX_DHCP */ + } + else if (saved_status == TRUE) { + WL_ERR(("%s was called w/o DHCP OFF. Continue\n", __FUNCTION__)); + } + } + else if (strnicmp((char *)&powermode_val, "2", strlen("2")) == 0) { + + +#ifdef PKT_FILTER_SUPPORT + dhd->dhcp_in_progress = 0; + WL_TRACE_HW4(("%s: DHCP is complete \n", __FUNCTION__)); + +#if defined(DHCP_SCAN_SUPPRESS) + /* Since DHCP is complete, enable the scan back */ + wl_cfg80211_scan_suppress(dev, 0); +#endif + + /* Enable packet filtering */ + if (dhd->early_suspended) { + WL_TRACE_HW4(("DHCP is complete , enable packet filter!!!\n")); + dhd_enable_packet_filter(1, dhd); + } +#endif /* PKT_FILTER_SUPPORT */ + + /* Restoring PM mode */ + +#ifdef COEX_DHCP + /* Stop any bt timer because DHCP session is done */ + WL_TRACE(("%s disable BT DHCP Timer\n", __FUNCTION__)); + if (btco_inf->timer_on) { + btco_inf->timer_on = 0; + del_timer_sync(&btco_inf->timer); + + if (btco_inf->bt_state != BT_DHCP_IDLE) { + /* need to restore original btc flags & extra btc params */ + WL_TRACE(("%s bt->bt_state:%d\n", + __FUNCTION__, btco_inf->bt_state)); + /* wake up btcoex thread to restore btlags+params */ + schedule_work(&btco_inf->work); + } + } + + /* Restoring btc_flag paramter anyway */ + if (saved_status == TRUE) + dev_wlc_bufvar_set(dev, "btc_flags", + (char *)&buf_flag7_default[0], sizeof(buf_flag7_default)); +#endif /* COEX_DHCP */ + + /* Restore original values */ + if (saved_status == TRUE) { + regaddr = 66; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg66); + regaddr = 41; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg41); + regaddr = 68; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg68); + + WL_TRACE(("restore regs {66,41,68} <- 0x%x 0x%x 0x%x\n", + saved_reg66, saved_reg41, saved_reg68)); + } + saved_status = FALSE; + + } + else { + WL_ERR(("%s Unkwown yet power setting, ignored\n", + __FUNCTION__)); + } + + snprintf(command, 3, "OK"); + + return (strlen("OK")); +} diff --git a/drivers/net/wireless/bcmdhd/dhd_cfg80211.h b/drivers/net/wireless/bcmdhd/dhd_cfg80211.h new file mode 100644 index 00000000..922d6edd --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_cfg80211.h @@ -0,0 +1,44 @@ +/* + * Linux cfg80211 driver - Dongle Host Driver (DHD) related + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_cfg80211.c,v 1.1.4.1.2.14 2011/02/09 01:40:07 Exp $ + */ + + +#ifndef __DHD_CFG80211__ +#define __DHD_CFG80211__ + +#include +#include + +s32 dhd_cfg80211_init(struct wl_priv *wl); +s32 dhd_cfg80211_deinit(struct wl_priv *wl); +s32 dhd_cfg80211_down(struct wl_priv *wl); +s32 dhd_cfg80211_set_p2p_info(struct wl_priv *wl, int val); +s32 dhd_cfg80211_clean_p2p_info(struct wl_priv *wl); +s32 dhd_config_dongle(struct wl_priv *wl, bool need_lock); + +int wl_cfg80211_btcoex_init(struct wl_priv *wl); +void wl_cfg80211_btcoex_deinit(struct wl_priv *wl); + +#endif /* __DHD_CFG80211__ */ diff --git a/drivers/net/wireless/bcmdhd/dhd_common.c b/drivers/net/wireless/bcmdhd/dhd_common.c new file mode 100644 index 00000000..bb765562 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_common.c @@ -0,0 +1,2205 @@ +/* + * Broadcom Dongle Host Driver (DHD), common DHD core. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhd_common.c 375022 2012-12-17 06:11:41Z $ + */ +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#ifdef WL_CFG80211 +#include +#endif +#ifdef WLBTAMP +#include +#include +#endif +#ifdef SET_RANDOM_MAC_SOFTAP +#include +#include +#endif + +#define htod32(i) i +#define htod16(i) i +#define dtoh32(i) i +#define dtoh16(i) i +#define htodchanspec(i) i +#define dtohchanspec(i) i + +#ifdef PROP_TXSTATUS +#include +#include +#endif + + +#ifdef WLMEDIA_HTSF +extern void htsf_update(struct dhd_info *dhd, void *data); +#endif +int dhd_msg_level = DHD_ERROR_VAL; + + +#include + +char fw_path[MOD_PARAM_PATHLEN]; +char nv_path[MOD_PARAM_PATHLEN]; + +#ifdef SOFTAP +char fw_path2[MOD_PARAM_PATHLEN]; +extern bool softap_enabled; +#endif + +/* Last connection success/failure status */ +uint32 dhd_conn_event; +uint32 dhd_conn_status; +uint32 dhd_conn_reason; + +extern int dhd_iscan_request(void * dhdp, uint16 action); +extern void dhd_ind_scan_confirm(void *h, bool status); +extern int dhd_iscan_in_progress(void *h); +void dhd_iscan_lock(void); +void dhd_iscan_unlock(void); +extern int dhd_change_mtu(dhd_pub_t *dhd, int new_mtu, int ifidx); +#if !defined(AP) && defined(WLP2P) +extern int dhd_get_concurrent_capabilites(dhd_pub_t *dhd); +#endif +bool ap_cfg_running = FALSE; +bool ap_fw_loaded = FALSE; + + +#ifdef DHD_DEBUG +const char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR "\nCompiled on " + __DATE__ " at " __TIME__; +#else +const char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR; +#endif + +void dhd_set_timer(void *bus, uint wdtick); + +/* IOVar table */ +enum { + IOV_VERSION = 1, + IOV_MSGLEVEL, + IOV_BCMERRORSTR, + IOV_BCMERROR, + IOV_WDTICK, + IOV_DUMP, + IOV_CLEARCOUNTS, + IOV_LOGDUMP, + IOV_LOGCAL, + IOV_LOGSTAMP, + IOV_GPIOOB, + IOV_IOCTLTIMEOUT, +#ifdef WLBTAMP + IOV_HCI_CMD, /* HCI command */ + IOV_HCI_ACL_DATA, /* HCI data packet */ +#endif +#if defined(DHD_DEBUG) + IOV_CONS, + IOV_DCONSOLE_POLL, +#endif /* defined(DHD_DEBUG) */ +#ifdef PROP_TXSTATUS + IOV_PROPTXSTATUS_ENABLE, + IOV_PROPTXSTATUS_MODE, +#endif + IOV_BUS_TYPE, +#ifdef WLMEDIA_HTSF + IOV_WLPKTDLYSTAT_SZ, +#endif + IOV_CHANGEMTU, + IOV_HOSTREORDER_FLOWS, + IOV_LAST +}; + +const bcm_iovar_t dhd_iovars[] = { + {"version", IOV_VERSION, 0, IOVT_BUFFER, sizeof(dhd_version) }, +#ifdef DHD_DEBUG + {"msglevel", IOV_MSGLEVEL, 0, IOVT_UINT32, 0 }, +#endif /* DHD_DEBUG */ + {"bcmerrorstr", IOV_BCMERRORSTR, 0, IOVT_BUFFER, BCME_STRLEN }, + {"bcmerror", IOV_BCMERROR, 0, IOVT_INT8, 0 }, + {"wdtick", IOV_WDTICK, 0, IOVT_UINT32, 0 }, + {"dump", IOV_DUMP, 0, IOVT_BUFFER, DHD_IOCTL_MAXLEN }, +#ifdef DHD_DEBUG + {"cons", IOV_CONS, 0, IOVT_BUFFER, 0 }, + {"dconpoll", IOV_DCONSOLE_POLL, 0, IOVT_UINT32, 0 }, +#endif + {"clearcounts", IOV_CLEARCOUNTS, 0, IOVT_VOID, 0 }, + {"gpioob", IOV_GPIOOB, 0, IOVT_UINT32, 0 }, + {"ioctl_timeout", IOV_IOCTLTIMEOUT, 0, IOVT_UINT32, 0 }, +#ifdef WLBTAMP + {"HCI_cmd", IOV_HCI_CMD, 0, IOVT_BUFFER, 0}, + {"HCI_ACL_data", IOV_HCI_ACL_DATA, 0, IOVT_BUFFER, 0}, +#endif +#ifdef PROP_TXSTATUS + {"proptx", IOV_PROPTXSTATUS_ENABLE, 0, IOVT_UINT32, 0 }, + /* + set the proptxtstatus operation mode: + 0 - Do not do any proptxtstatus flow control + 1 - Use implied credit from a packet status + 2 - Use explicit credit + */ + {"ptxmode", IOV_PROPTXSTATUS_MODE, 0, IOVT_UINT32, 0 }, +#endif + {"bustype", IOV_BUS_TYPE, 0, IOVT_UINT32, 0}, +#ifdef WLMEDIA_HTSF + {"pktdlystatsz", IOV_WLPKTDLYSTAT_SZ, 0, IOVT_UINT8, 0 }, +#endif + {"changemtu", IOV_CHANGEMTU, 0, IOVT_UINT32, 0 }, + {"host_reorder_flows", IOV_HOSTREORDER_FLOWS, 0, IOVT_BUFFER, + (WLHOST_REORDERDATA_MAXFLOWS + 1) }, + {NULL, 0, 0, 0, 0 } +}; + +void +dhd_common_init(osl_t *osh) +{ +#ifdef CONFIG_BCMDHD_FW_PATH + bcm_strncpy_s(fw_path, sizeof(fw_path), CONFIG_BCMDHD_FW_PATH, MOD_PARAM_PATHLEN-1); +#else /* CONFIG_BCMDHD_FW_PATH */ + fw_path[0] = '\0'; +#endif /* CONFIG_BCMDHD_FW_PATH */ +#ifdef CONFIG_BCMDHD_NVRAM_PATH + bcm_strncpy_s(nv_path, sizeof(nv_path), CONFIG_BCMDHD_NVRAM_PATH, MOD_PARAM_PATHLEN-1); +#else /* CONFIG_BCMDHD_NVRAM_PATH */ + nv_path[0] = '\0'; +#endif /* CONFIG_BCMDHD_NVRAM_PATH */ +#ifdef SOFTAP + fw_path2[0] = '\0'; +#endif +} + +static int +dhd_dump(dhd_pub_t *dhdp, char *buf, int buflen) +{ + char eabuf[ETHER_ADDR_STR_LEN]; + + struct bcmstrbuf b; + struct bcmstrbuf *strbuf = &b; + + bcm_binit(strbuf, buf, buflen); + + /* Base DHD info */ + bcm_bprintf(strbuf, "%s\n", dhd_version); + bcm_bprintf(strbuf, "\n"); + bcm_bprintf(strbuf, "pub.up %d pub.txoff %d pub.busstate %d\n", + dhdp->up, dhdp->txoff, dhdp->busstate); + bcm_bprintf(strbuf, "pub.hdrlen %d pub.maxctl %d pub.rxsz %d\n", + dhdp->hdrlen, dhdp->maxctl, dhdp->rxsz); + bcm_bprintf(strbuf, "pub.iswl %d pub.drv_version %ld pub.mac %s\n", + dhdp->iswl, dhdp->drv_version, bcm_ether_ntoa(&dhdp->mac, eabuf)); + bcm_bprintf(strbuf, "pub.bcmerror %d tickcnt %d\n", dhdp->bcmerror, dhdp->tickcnt); + + bcm_bprintf(strbuf, "dongle stats:\n"); + bcm_bprintf(strbuf, "tx_packets %ld tx_bytes %ld tx_errors %ld tx_dropped %ld\n", + dhdp->dstats.tx_packets, dhdp->dstats.tx_bytes, + dhdp->dstats.tx_errors, dhdp->dstats.tx_dropped); + bcm_bprintf(strbuf, "rx_packets %ld rx_bytes %ld rx_errors %ld rx_dropped %ld\n", + dhdp->dstats.rx_packets, dhdp->dstats.rx_bytes, + dhdp->dstats.rx_errors, dhdp->dstats.rx_dropped); + bcm_bprintf(strbuf, "multicast %ld\n", dhdp->dstats.multicast); + + bcm_bprintf(strbuf, "bus stats:\n"); + bcm_bprintf(strbuf, "tx_packets %ld tx_multicast %ld tx_errors %ld\n", + dhdp->tx_packets, dhdp->tx_multicast, dhdp->tx_errors); + bcm_bprintf(strbuf, "tx_ctlpkts %ld tx_ctlerrs %ld\n", + dhdp->tx_ctlpkts, dhdp->tx_ctlerrs); + bcm_bprintf(strbuf, "rx_packets %ld rx_multicast %ld rx_errors %ld \n", + dhdp->rx_packets, dhdp->rx_multicast, dhdp->rx_errors); + bcm_bprintf(strbuf, "rx_ctlpkts %ld rx_ctlerrs %ld rx_dropped %ld\n", + dhdp->rx_ctlpkts, dhdp->rx_ctlerrs, dhdp->rx_dropped); + bcm_bprintf(strbuf, "rx_readahead_cnt %ld tx_realloc %ld\n", + dhdp->rx_readahead_cnt, dhdp->tx_realloc); + bcm_bprintf(strbuf, "\n"); + + /* Add any prot info */ + dhd_prot_dump(dhdp, strbuf); + bcm_bprintf(strbuf, "\n"); + + /* Add any bus info */ + dhd_bus_dump(dhdp, strbuf); + + return (!strbuf->size ? BCME_BUFTOOSHORT : 0); +} + +int +dhd_wl_ioctl_cmd(dhd_pub_t *dhd_pub, int cmd, void *arg, int len, uint8 set, int ifindex) +{ + wl_ioctl_t ioc; + + ioc.cmd = cmd; + ioc.buf = arg; + ioc.len = len; + ioc.set = set; + + return dhd_wl_ioctl(dhd_pub, ifindex, &ioc, arg, len); +} + + +int +dhd_wl_ioctl(dhd_pub_t *dhd_pub, int ifindex, wl_ioctl_t *ioc, void *buf, int len) +{ + int ret; + + dhd_os_proto_block(dhd_pub); + + ret = dhd_prot_ioctl(dhd_pub, ifindex, ioc, buf, len); + if ((ret) && (dhd_pub->up)) + /* Send hang event only if dhd_open() was success */ + dhd_os_check_hang(dhd_pub, ifindex, ret); + + dhd_os_proto_unblock(dhd_pub); + + return ret; +} + +static int +dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const char *name, + void *params, int plen, void *arg, int len, int val_size) +{ + int bcmerror = 0; + int32 int_val = 0; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + DHD_TRACE(("%s: actionid = %d; name %s\n", __FUNCTION__, actionid, name)); + + if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, IOV_ISSET(actionid))) != 0) + goto exit; + + if (plen >= (int)sizeof(int_val)) + bcopy(params, &int_val, sizeof(int_val)); + + switch (actionid) { + case IOV_GVAL(IOV_VERSION): + /* Need to have checked buffer length */ + bcm_strncpy_s((char*)arg, len, dhd_version, len); + break; + + case IOV_GVAL(IOV_MSGLEVEL): + int_val = (int32)dhd_msg_level; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_MSGLEVEL): +#ifdef WL_CFG80211 + /* Enable DHD and WL logs in oneshot */ + if (int_val & DHD_WL_VAL2) + wl_cfg80211_enable_trace(TRUE, int_val & (~DHD_WL_VAL2)); + else if (int_val & DHD_WL_VAL) + wl_cfg80211_enable_trace(FALSE, WL_DBG_DBG); + if (!(int_val & DHD_WL_VAL2)) +#endif /* WL_CFG80211 */ + dhd_msg_level = int_val; + break; + case IOV_GVAL(IOV_BCMERRORSTR): + bcm_strncpy_s((char *)arg, len, bcmerrorstr(dhd_pub->bcmerror), BCME_STRLEN); + ((char *)arg)[BCME_STRLEN - 1] = 0x00; + break; + + case IOV_GVAL(IOV_BCMERROR): + int_val = (int32)dhd_pub->bcmerror; + bcopy(&int_val, arg, val_size); + break; + + case IOV_GVAL(IOV_WDTICK): + int_val = (int32)dhd_watchdog_ms; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_WDTICK): + if (!dhd_pub->up) { + bcmerror = BCME_NOTUP; + break; + } + dhd_os_wd_timer(dhd_pub, (uint)int_val); + break; + + case IOV_GVAL(IOV_DUMP): + bcmerror = dhd_dump(dhd_pub, arg, len); + break; + +#ifdef DHD_DEBUG + case IOV_GVAL(IOV_DCONSOLE_POLL): + int_val = (int32)dhd_console_ms; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_DCONSOLE_POLL): + dhd_console_ms = (uint)int_val; + break; + + case IOV_SVAL(IOV_CONS): + if (len > 0) + bcmerror = dhd_bus_console_in(dhd_pub, arg, len - 1); + break; +#endif /* DHD_DEBUG */ + + case IOV_SVAL(IOV_CLEARCOUNTS): + dhd_pub->tx_packets = dhd_pub->rx_packets = 0; + dhd_pub->tx_errors = dhd_pub->rx_errors = 0; + dhd_pub->tx_ctlpkts = dhd_pub->rx_ctlpkts = 0; + dhd_pub->tx_ctlerrs = dhd_pub->rx_ctlerrs = 0; + dhd_pub->rx_dropped = 0; + dhd_pub->rx_readahead_cnt = 0; + dhd_pub->tx_realloc = 0; + dhd_pub->wd_dpc_sched = 0; + memset(&dhd_pub->dstats, 0, sizeof(dhd_pub->dstats)); + dhd_bus_clearcounts(dhd_pub); +#ifdef PROP_TXSTATUS + /* clear proptxstatus related counters */ + if (dhd_pub->wlfc_state) { + athost_wl_status_info_t *wlfc = + (athost_wl_status_info_t*)dhd_pub->wlfc_state; + wlfc_hanger_t* hanger; + + memset(&wlfc->stats, 0, sizeof(athost_wl_stat_counters_t)); + + hanger = (wlfc_hanger_t*)wlfc->hanger; + hanger->pushed = 0; + hanger->popped = 0; + hanger->failed_slotfind = 0; + hanger->failed_to_pop = 0; + hanger->failed_to_push = 0; + } +#endif /* PROP_TXSTATUS */ + break; + + + case IOV_GVAL(IOV_IOCTLTIMEOUT): { + int_val = (int32)dhd_os_get_ioctl_resp_timeout(); + bcopy(&int_val, arg, sizeof(int_val)); + break; + } + + case IOV_SVAL(IOV_IOCTLTIMEOUT): { + if (int_val <= 0) + bcmerror = BCME_BADARG; + else + dhd_os_set_ioctl_resp_timeout((unsigned int)int_val); + break; + } + +#ifdef WLBTAMP + case IOV_SVAL(IOV_HCI_CMD): { + amp_hci_cmd_t *cmd = (amp_hci_cmd_t *)arg; + + /* sanity check: command preamble present */ + if (len < HCI_CMD_PREAMBLE_SIZE) + return BCME_BUFTOOSHORT; + + /* sanity check: command parameters are present */ + if (len < (int)(HCI_CMD_PREAMBLE_SIZE + cmd->plen)) + return BCME_BUFTOOSHORT; + + dhd_bta_docmd(dhd_pub, cmd, len); + break; + } + + case IOV_SVAL(IOV_HCI_ACL_DATA): { + amp_hci_ACL_data_t *ACL_data = (amp_hci_ACL_data_t *)arg; + + /* sanity check: HCI header present */ + if (len < HCI_ACL_DATA_PREAMBLE_SIZE) + return BCME_BUFTOOSHORT; + + /* sanity check: ACL data is present */ + if (len < (int)(HCI_ACL_DATA_PREAMBLE_SIZE + ACL_data->dlen)) + return BCME_BUFTOOSHORT; + + dhd_bta_tx_hcidata(dhd_pub, ACL_data, len); + break; + } +#endif /* WLBTAMP */ + +#ifdef PROP_TXSTATUS + case IOV_GVAL(IOV_PROPTXSTATUS_ENABLE): + int_val = dhd_pub->wlfc_enabled? 1 : 0; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_PROPTXSTATUS_ENABLE): + dhd_pub->wlfc_enabled = int_val? 1 : 0; + break; + + case IOV_GVAL(IOV_PROPTXSTATUS_MODE): { + athost_wl_status_info_t *wlfc = + (athost_wl_status_info_t*)dhd_pub->wlfc_state; + int_val = dhd_pub->wlfc_state ? (int32)wlfc->proptxstatus_mode : 0; + bcopy(&int_val, arg, val_size); + break; + } + + case IOV_SVAL(IOV_PROPTXSTATUS_MODE): + if (dhd_pub->wlfc_state) { + athost_wl_status_info_t *wlfc = + (athost_wl_status_info_t*)dhd_pub->wlfc_state; + wlfc->proptxstatus_mode = int_val & 0xff; + } + break; +#endif /* PROP_TXSTATUS */ + + case IOV_GVAL(IOV_BUS_TYPE): + /* The dhd application queries the driver to check if its usb or sdio. */ +#ifdef BCMDHDUSB + int_val = BUS_TYPE_USB; +#endif + int_val = BUS_TYPE_SDIO; + bcopy(&int_val, arg, val_size); + break; + + +#ifdef WLMEDIA_HTSF + case IOV_GVAL(IOV_WLPKTDLYSTAT_SZ): + int_val = dhd_pub->htsfdlystat_sz; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_WLPKTDLYSTAT_SZ): + dhd_pub->htsfdlystat_sz = int_val & 0xff; + printf("Setting tsfdlystat_sz:%d\n", dhd_pub->htsfdlystat_sz); + break; +#endif + case IOV_SVAL(IOV_CHANGEMTU): + int_val &= 0xffff; + bcmerror = dhd_change_mtu(dhd_pub, int_val, 0); + break; + + case IOV_GVAL(IOV_HOSTREORDER_FLOWS): + { + uint i = 0; + uint8 *ptr = (uint8 *)arg; + uint8 count = 0; + + ptr++; + for (i = 0; i < WLHOST_REORDERDATA_MAXFLOWS; i++) { + if (dhd_pub->reorder_bufs[i] != NULL) { + *ptr = dhd_pub->reorder_bufs[i]->flow_id; + ptr++; + count++; + } + } + ptr = (uint8 *)arg; + *ptr = count; + break; + } + + default: + bcmerror = BCME_UNSUPPORTED; + break; + } + +exit: + DHD_TRACE(("%s: actionid %d, bcmerror %d\n", __FUNCTION__, actionid, bcmerror)); + return bcmerror; +} + +/* Store the status of a connection attempt for later retrieval by an iovar */ +void +dhd_store_conn_status(uint32 event, uint32 status, uint32 reason) +{ + /* Do not overwrite a WLC_E_PRUNE with a WLC_E_SET_SSID + * because an encryption/rsn mismatch results in both events, and + * the important information is in the WLC_E_PRUNE. + */ + if (!(event == WLC_E_SET_SSID && status == WLC_E_STATUS_FAIL && + dhd_conn_event == WLC_E_PRUNE)) { + dhd_conn_event = event; + dhd_conn_status = status; + dhd_conn_reason = reason; + } +} + +bool +dhd_prec_enq(dhd_pub_t *dhdp, struct pktq *q, void *pkt, int prec) +{ + void *p; + int eprec = -1; /* precedence to evict from */ + bool discard_oldest; + + /* Fast case, precedence queue is not full and we are also not + * exceeding total queue length + */ + if (!pktq_pfull(q, prec) && !pktq_full(q)) { + pktq_penq(q, prec, pkt); + return TRUE; + } + + /* Determine precedence from which to evict packet, if any */ + if (pktq_pfull(q, prec)) + eprec = prec; + else if (pktq_full(q)) { + pktq_peek_tail(q, &eprec); + if (eprec > prec || eprec < 0) + return FALSE; + } + + /* Evict if needed */ + if (eprec >= 0) { + /* Detect queueing to unconfigured precedence */ + ASSERT(!pktq_pempty(q, eprec)); + discard_oldest = AC_BITMAP_TST(dhdp->wme_dp, eprec); + if (eprec == prec && !discard_oldest) + return FALSE; /* refuse newer (incoming) packet */ + /* Evict packet according to discard policy */ + p = discard_oldest ? pktq_pdeq(q, eprec) : pktq_pdeq_tail(q, eprec); + ASSERT(p); + + PKTFREE(dhdp->osh, p, TRUE); + } + + /* Enqueue */ + pktq_penq(q, prec, pkt); + + return TRUE; +} + +static int +dhd_iovar_op(dhd_pub_t *dhd_pub, const char *name, + void *params, int plen, void *arg, int len, bool set) +{ + int bcmerror = 0; + int val_size; + const bcm_iovar_t *vi = NULL; + uint32 actionid; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + ASSERT(name); + ASSERT(len >= 0); + + /* Get MUST have return space */ + ASSERT(set || (arg && len)); + + /* Set does NOT take qualifiers */ + ASSERT(!set || (!params && !plen)); + + if ((vi = bcm_iovar_lookup(dhd_iovars, name)) == NULL) { + bcmerror = BCME_UNSUPPORTED; + goto exit; + } + + DHD_CTL(("%s: %s %s, len %d plen %d\n", __FUNCTION__, + name, (set ? "set" : "get"), len, plen)); + + /* set up 'params' pointer in case this is a set command so that + * the convenience int and bool code can be common to set and get + */ + if (params == NULL) { + params = arg; + plen = len; + } + + if (vi->type == IOVT_VOID) + val_size = 0; + else if (vi->type == IOVT_BUFFER) + val_size = len; + else + /* all other types are integer sized */ + val_size = sizeof(int); + + actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid); + + bcmerror = dhd_doiovar(dhd_pub, vi, actionid, name, params, plen, arg, len, val_size); + +exit: + return bcmerror; +} + +int +dhd_ioctl(dhd_pub_t * dhd_pub, dhd_ioctl_t *ioc, void * buf, uint buflen) +{ + int bcmerror = 0; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (!buf) { + return BCME_BADARG; + } + + switch (ioc->cmd) { + case DHD_GET_MAGIC: + if (buflen < sizeof(int)) + bcmerror = BCME_BUFTOOSHORT; + else + *(int*)buf = DHD_IOCTL_MAGIC; + break; + + case DHD_GET_VERSION: + if (buflen < sizeof(int)) + bcmerror = BCME_BUFTOOSHORT; + else + *(int*)buf = DHD_IOCTL_VERSION; + break; + + case DHD_GET_VAR: + case DHD_SET_VAR: { + char *arg; + uint arglen; + + /* scan past the name to any arguments */ + for (arg = buf, arglen = buflen; *arg && arglen; arg++, arglen--) + ; + + if (*arg) { + bcmerror = BCME_BUFTOOSHORT; + break; + } + + /* account for the NUL terminator */ + arg++, arglen--; + + /* call with the appropriate arguments */ + if (ioc->cmd == DHD_GET_VAR) + bcmerror = dhd_iovar_op(dhd_pub, buf, arg, arglen, + buf, buflen, IOV_GET); + else + bcmerror = dhd_iovar_op(dhd_pub, buf, NULL, 0, arg, arglen, IOV_SET); + if (bcmerror != BCME_UNSUPPORTED) + break; + + /* not in generic table, try protocol module */ + if (ioc->cmd == DHD_GET_VAR) + bcmerror = dhd_prot_iovar_op(dhd_pub, buf, arg, + arglen, buf, buflen, IOV_GET); + else + bcmerror = dhd_prot_iovar_op(dhd_pub, buf, + NULL, 0, arg, arglen, IOV_SET); + if (bcmerror != BCME_UNSUPPORTED) + break; + + /* if still not found, try bus module */ + if (ioc->cmd == DHD_GET_VAR) { + bcmerror = dhd_bus_iovar_op(dhd_pub, buf, + arg, arglen, buf, buflen, IOV_GET); + } else { + bcmerror = dhd_bus_iovar_op(dhd_pub, buf, + NULL, 0, arg, arglen, IOV_SET); + } + + break; + } + + default: + bcmerror = BCME_UNSUPPORTED; + } + + return bcmerror; +} + +#ifdef SHOW_EVENTS +static void +wl_show_host_event(wl_event_msg_t *event, void *event_data) +{ + uint i, status, reason; + bool group = FALSE, flush_txq = FALSE, link = FALSE; + const char *auth_str; + const char *event_name; + uchar *buf; + char err_msg[256], eabuf[ETHER_ADDR_STR_LEN]; + uint event_type, flags, auth_type, datalen; + + event_type = ntoh32(event->event_type); + flags = ntoh16(event->flags); + status = ntoh32(event->status); + reason = ntoh32(event->reason); + BCM_REFERENCE(reason); + auth_type = ntoh32(event->auth_type); + datalen = ntoh32(event->datalen); + + /* debug dump of event messages */ + snprintf(eabuf, sizeof(eabuf), "%02x:%02x:%02x:%02x:%02x:%02x", + (uchar)event->addr.octet[0]&0xff, + (uchar)event->addr.octet[1]&0xff, + (uchar)event->addr.octet[2]&0xff, + (uchar)event->addr.octet[3]&0xff, + (uchar)event->addr.octet[4]&0xff, + (uchar)event->addr.octet[5]&0xff); + + event_name = "UNKNOWN"; + for (i = 0; i < (uint)bcmevent_names_size; i++) + if (bcmevent_names[i].event == event_type) + event_name = bcmevent_names[i].name; + + if (flags & WLC_EVENT_MSG_LINK) + link = TRUE; + if (flags & WLC_EVENT_MSG_GROUP) + group = TRUE; + if (flags & WLC_EVENT_MSG_FLUSHTXQ) + flush_txq = TRUE; + + switch (event_type) { + case WLC_E_START: + case WLC_E_DEAUTH: + case WLC_E_DISASSOC: + DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf)); + break; + + case WLC_E_ASSOC_IND: + case WLC_E_REASSOC_IND: + + DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf)); + break; + + case WLC_E_ASSOC: + case WLC_E_REASSOC: + if (status == WLC_E_STATUS_SUCCESS) { + DHD_EVENT(("MACEVENT: %s, MAC %s, SUCCESS\n", event_name, eabuf)); + } else if (status == WLC_E_STATUS_TIMEOUT) { + DHD_EVENT(("MACEVENT: %s, MAC %s, TIMEOUT\n", event_name, eabuf)); + } else if (status == WLC_E_STATUS_FAIL) { + DHD_EVENT(("MACEVENT: %s, MAC %s, FAILURE, reason %d\n", + event_name, eabuf, (int)reason)); + } else { + DHD_EVENT(("MACEVENT: %s, MAC %s, unexpected status %d\n", + event_name, eabuf, (int)status)); + } + break; + + case WLC_E_DEAUTH_IND: + case WLC_E_DISASSOC_IND: + DHD_EVENT(("MACEVENT: %s, MAC %s, reason %d\n", event_name, eabuf, (int)reason)); + break; + + case WLC_E_AUTH: + case WLC_E_AUTH_IND: + if (auth_type == DOT11_OPEN_SYSTEM) + auth_str = "Open System"; + else if (auth_type == DOT11_SHARED_KEY) + auth_str = "Shared Key"; + else { + snprintf(err_msg, sizeof(err_msg), "AUTH unknown: %d", (int)auth_type); + auth_str = err_msg; + } + if (event_type == WLC_E_AUTH_IND) { + DHD_EVENT(("MACEVENT: %s, MAC %s, %s\n", event_name, eabuf, auth_str)); + } else if (status == WLC_E_STATUS_SUCCESS) { + DHD_EVENT(("MACEVENT: %s, MAC %s, %s, SUCCESS\n", + event_name, eabuf, auth_str)); + } else if (status == WLC_E_STATUS_TIMEOUT) { + DHD_EVENT(("MACEVENT: %s, MAC %s, %s, TIMEOUT\n", + event_name, eabuf, auth_str)); + } else if (status == WLC_E_STATUS_FAIL) { + DHD_EVENT(("MACEVENT: %s, MAC %s, %s, FAILURE, reason %d\n", + event_name, eabuf, auth_str, (int)reason)); + } + BCM_REFERENCE(auth_str); + + break; + + case WLC_E_JOIN: + case WLC_E_ROAM: + case WLC_E_SET_SSID: + if (status == WLC_E_STATUS_SUCCESS) { + DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf)); + } else if (status == WLC_E_STATUS_FAIL) { + DHD_EVENT(("MACEVENT: %s, failed\n", event_name)); + } else if (status == WLC_E_STATUS_NO_NETWORKS) { + DHD_EVENT(("MACEVENT: %s, no networks found\n", event_name)); + } else { + DHD_EVENT(("MACEVENT: %s, unexpected status %d\n", + event_name, (int)status)); + } + break; + + case WLC_E_BEACON_RX: + if (status == WLC_E_STATUS_SUCCESS) { + DHD_EVENT(("MACEVENT: %s, SUCCESS\n", event_name)); + } else if (status == WLC_E_STATUS_FAIL) { + DHD_EVENT(("MACEVENT: %s, FAIL\n", event_name)); + } else { + DHD_EVENT(("MACEVENT: %s, status %d\n", event_name, status)); + } + break; + + case WLC_E_LINK: + DHD_EVENT(("MACEVENT: %s %s\n", event_name, link?"UP":"DOWN")); + BCM_REFERENCE(link); + break; + + case WLC_E_MIC_ERROR: + DHD_EVENT(("MACEVENT: %s, MAC %s, Group %d, Flush %d\n", + event_name, eabuf, group, flush_txq)); + BCM_REFERENCE(group); + BCM_REFERENCE(flush_txq); + break; + + case WLC_E_ICV_ERROR: + case WLC_E_UNICAST_DECODE_ERROR: + case WLC_E_MULTICAST_DECODE_ERROR: + DHD_EVENT(("MACEVENT: %s, MAC %s\n", + event_name, eabuf)); + break; + + case WLC_E_TXFAIL: + DHD_EVENT(("MACEVENT: %s, RA %s\n", event_name, eabuf)); + break; + + case WLC_E_SCAN_COMPLETE: + case WLC_E_ASSOC_REQ_IE: + case WLC_E_ASSOC_RESP_IE: + case WLC_E_PMKID_CACHE: + DHD_EVENT(("MACEVENT: %s\n", event_name)); + break; + + case WLC_E_PFN_NET_FOUND: + case WLC_E_PFN_NET_LOST: + case WLC_E_PFN_SCAN_COMPLETE: + case WLC_E_PFN_SCAN_NONE: + case WLC_E_PFN_SCAN_ALLGONE: + DHD_EVENT(("PNOEVENT: %s\n", event_name)); + break; + + case WLC_E_PSK_SUP: + case WLC_E_PRUNE: + DHD_EVENT(("MACEVENT: %s, status %d, reason %d\n", + event_name, (int)status, (int)reason)); + break; + +#ifdef WIFI_ACT_FRAME + case WLC_E_ACTION_FRAME: + DHD_TRACE(("MACEVENT: %s Bssid %s\n", event_name, eabuf)); + break; +#endif /* WIFI_ACT_FRAME */ + + case WLC_E_TRACE: { + static uint32 seqnum_prev = 0; + msgtrace_hdr_t hdr; + uint32 nblost; + char *s, *p; + + buf = (uchar *) event_data; + memcpy(&hdr, buf, MSGTRACE_HDRLEN); + + if (hdr.version != MSGTRACE_VERSION) { + printf("\nMACEVENT: %s [unsupported version --> " + "dhd version:%d dongle version:%d]\n", + event_name, MSGTRACE_VERSION, hdr.version); + /* Reset datalen to avoid display below */ + datalen = 0; + break; + } + + /* There are 2 bytes available at the end of data */ + buf[MSGTRACE_HDRLEN + ntoh16(hdr.len)] = '\0'; + + if (ntoh32(hdr.discarded_bytes) || ntoh32(hdr.discarded_printf)) { + printf("\nWLC_E_TRACE: [Discarded traces in dongle -->" + "discarded_bytes %d discarded_printf %d]\n", + ntoh32(hdr.discarded_bytes), ntoh32(hdr.discarded_printf)); + } + + nblost = ntoh32(hdr.seqnum) - seqnum_prev - 1; + if (nblost > 0) { + printf("\nWLC_E_TRACE: [Event lost --> seqnum %d nblost %d\n", + ntoh32(hdr.seqnum), nblost); + } + seqnum_prev = ntoh32(hdr.seqnum); + + /* Display the trace buffer. Advance from \n to \n to avoid display big + * printf (issue with Linux printk ) + */ + p = (char *)&buf[MSGTRACE_HDRLEN]; + while ((s = strstr(p, "\n")) != NULL) { + *s = '\0'; + printf("%s\n", p); + p = s+1; + } + printf("%s\n", p); + + /* Reset datalen to avoid display below */ + datalen = 0; + break; + } + + + case WLC_E_RSSI: + DHD_EVENT(("MACEVENT: %s %d\n", event_name, ntoh32(*((int *)event_data)))); + break; + + case WLC_E_SERVICE_FOUND: + case WLC_E_P2PO_ADD_DEVICE: + case WLC_E_P2PO_DEL_DEVICE: + DHD_EVENT(("MACEVENT: %s, MAC: %s\n", event_name, eabuf)); + break; + + default: + DHD_EVENT(("MACEVENT: %s %d, MAC %s, status %d, reason %d, auth %d\n", + event_name, event_type, eabuf, (int)status, (int)reason, + (int)auth_type)); + break; + } + + /* show any appended data */ + if (datalen) { + buf = (uchar *) event_data; + DHD_EVENT((" data (%d) : ", datalen)); + for (i = 0; i < datalen; i++) + DHD_EVENT((" 0x%02x ", *buf++)); + DHD_EVENT(("\n")); + } +} +#endif /* SHOW_EVENTS */ + +int +wl_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, + wl_event_msg_t *event, void **data_ptr) +{ + /* check whether packet is a BRCM event pkt */ + bcm_event_t *pvt_data = (bcm_event_t *)pktdata; + uint8 *event_data; + uint32 type, status, datalen; + uint16 flags; + int evlen; + + if (bcmp(BRCM_OUI, &pvt_data->bcm_hdr.oui[0], DOT11_OUI_LEN)) { + DHD_ERROR(("%s: mismatched OUI, bailing\n", __FUNCTION__)); + return (BCME_ERROR); + } + + /* BRCM event pkt may be unaligned - use xxx_ua to load user_subtype. */ + if (ntoh16_ua((void *)&pvt_data->bcm_hdr.usr_subtype) != BCMILCP_BCM_SUBTYPE_EVENT) { + DHD_ERROR(("%s: mismatched subtype, bailing\n", __FUNCTION__)); + return (BCME_ERROR); + } + + *data_ptr = &pvt_data[1]; + event_data = *data_ptr; + + /* memcpy since BRCM event pkt may be unaligned. */ + memcpy(event, &pvt_data->event, sizeof(wl_event_msg_t)); + + type = ntoh32_ua((void *)&event->event_type); + flags = ntoh16_ua((void *)&event->flags); + status = ntoh32_ua((void *)&event->status); + datalen = ntoh32_ua((void *)&event->datalen); + evlen = datalen + sizeof(bcm_event_t); + + switch (type) { +#ifdef PROP_TXSTATUS + case WLC_E_FIFO_CREDIT_MAP: + dhd_wlfc_event(dhd_pub->info); + dhd_wlfc_FIFOcreditmap_event(dhd_pub->info, event_data); + WLFC_DBGMESG(("WLC_E_FIFO_CREDIT_MAP:(AC0,AC1,AC2,AC3),(BC_MC),(OTHER): " + "(%d,%d,%d,%d),(%d),(%d)\n", event_data[0], event_data[1], + event_data[2], + event_data[3], event_data[4], event_data[5])); + break; +#endif + + case WLC_E_IF: + { + dhd_if_event_t *ifevent = (dhd_if_event_t *)event_data; +#ifdef PROP_TXSTATUS + { + uint8* ea = pvt_data->eth.ether_dhost; + WLFC_DBGMESG(("WLC_E_IF: idx:%d, action:%s, iftype:%s, " + "[%02x:%02x:%02x:%02x:%02x:%02x]\n", + ifevent->ifidx, + ((ifevent->action == WLC_E_IF_ADD) ? "ADD":"DEL"), + ((ifevent->is_AP == 0) ? "STA":"AP "), + ea[0], ea[1], ea[2], ea[3], ea[4], ea[5])); + (void)ea; + if (ifevent->action == WLC_E_IF_CHANGE) + dhd_wlfc_interface_event(dhd_pub->info, + eWLFC_MAC_ENTRY_ACTION_UPDATE, + ifevent->ifidx, ifevent->is_AP, ea); + else + dhd_wlfc_interface_event(dhd_pub->info, + ((ifevent->action == WLC_E_IF_ADD) ? + eWLFC_MAC_ENTRY_ACTION_ADD : eWLFC_MAC_ENTRY_ACTION_DEL), + ifevent->ifidx, ifevent->is_AP, ea); + + + /* dhd already has created an interface by default, for 0 */ + if (ifevent->ifidx == 0) + break; + } +#endif /* PROP_TXSTATUS */ + +#ifdef WL_CFG80211 + if (wl_cfg80211_is_progress_ifchange()) { + DHD_ERROR(("%s: ifidx %d for %s action %d\n", + __FUNCTION__, ifevent->ifidx, + event->ifname, ifevent->action)); + if (ifevent->action == WLC_E_IF_ADD || + ifevent->action == WLC_E_IF_CHANGE) + wl_cfg80211_notify_ifchange(); + return (BCME_OK); + } +#endif /* WL_CFG80211 */ + if (ifevent->ifidx > 0 && ifevent->ifidx < DHD_MAX_IFS) { + if (ifevent->action == WLC_E_IF_ADD) { + if (dhd_add_if(dhd_pub->info, ifevent->ifidx, + NULL, event->ifname, + event->addr.octet, + ifevent->flags, ifevent->bssidx)) { + DHD_ERROR(("%s: dhd_add_if failed!!" + " ifidx: %d for %s\n", + __FUNCTION__, + ifevent->ifidx, + event->ifname)); + return (BCME_ERROR); + } + } + else if (ifevent->action == WLC_E_IF_DEL) + dhd_del_if(dhd_pub->info, ifevent->ifidx); + } else { +#ifndef PROP_TXSTATUS + DHD_ERROR(("%s: Invalid ifidx %d for %s\n", + __FUNCTION__, ifevent->ifidx, event->ifname)); +#endif /* !PROP_TXSTATUS */ + } + } + /* send up the if event: btamp user needs it */ + *ifidx = dhd_ifname2idx(dhd_pub->info, event->ifname); + /* push up to external supp/auth */ + dhd_event(dhd_pub->info, (char *)pvt_data, evlen, *ifidx); + break; + + +#ifdef WLMEDIA_HTSF + case WLC_E_HTSFSYNC: + htsf_update(dhd_pub->info, event_data); + break; +#endif /* WLMEDIA_HTSF */ +#if defined(NDIS630) + case WLC_E_NDIS_LINK: + break; +#else /* defined(NDIS630) && defined(BCMDONGLEHOST) */ + case WLC_E_NDIS_LINK: { + uint32 temp = hton32(WLC_E_LINK); + + memcpy((void *)(&pvt_data->event.event_type), &temp, + sizeof(pvt_data->event.event_type)); + } +#endif + /* These are what external supplicant/authenticator wants */ + /* fall through */ + case WLC_E_LINK: + case WLC_E_DEAUTH: + case WLC_E_DEAUTH_IND: + case WLC_E_DISASSOC: + case WLC_E_DISASSOC_IND: + DHD_EVENT(("%s: Link event %d, flags %x, status %x\n", + __FUNCTION__, type, flags, status)); + /* fall through */ + default: + *ifidx = dhd_ifname2idx(dhd_pub->info, event->ifname); + /* push up to external supp/auth */ + dhd_event(dhd_pub->info, (char *)pvt_data, evlen, *ifidx); + DHD_TRACE(("%s: MAC event %d, flags %x, status %x\n", + __FUNCTION__, type, flags, status)); + BCM_REFERENCE(flags); + BCM_REFERENCE(status); + + /* put it back to WLC_E_NDIS_LINK */ + if (type == WLC_E_NDIS_LINK) { + uint32 temp; + + temp = ntoh32_ua((void *)&event->event_type); + DHD_TRACE(("Converted to WLC_E_LINK type %d\n", temp)); + + temp = ntoh32(WLC_E_NDIS_LINK); + memcpy((void *)(&pvt_data->event.event_type), &temp, + sizeof(pvt_data->event.event_type)); + } + break; + } + +#ifdef SHOW_EVENTS + wl_show_host_event(event, (void *)event_data); +#endif /* SHOW_EVENTS */ + + return (BCME_OK); +} + +void +wl_event_to_host_order(wl_event_msg_t * evt) +{ + /* Event struct members passed from dongle to host are stored in network + * byte order. Convert all members to host-order. + */ + evt->event_type = ntoh32(evt->event_type); + evt->flags = ntoh16(evt->flags); + evt->status = ntoh32(evt->status); + evt->reason = ntoh32(evt->reason); + evt->auth_type = ntoh32(evt->auth_type); + evt->datalen = ntoh32(evt->datalen); + evt->version = ntoh16(evt->version); +} + +void +dhd_print_buf(void *pbuf, int len, int bytes_per_line) +{ +#ifdef DHD_DEBUG + int i, j = 0; + unsigned char *buf = pbuf; + + if (bytes_per_line == 0) { + bytes_per_line = len; + } + + for (i = 0; i < len; i++) { + printf("%2.2x", *buf++); + j++; + if (j == bytes_per_line) { + printf("\n"); + j = 0; + } else { + printf(":"); + } + } + printf("\n"); +#endif /* DHD_DEBUG */ +} + +#ifndef strtoul +#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base)) +#endif + +#ifdef PKT_FILTER_SUPPORT +/* Convert user's input in hex pattern to byte-size mask */ +static int +wl_pattern_atoh(char *src, char *dst) +{ + int i; + if (strncmp(src, "0x", 2) != 0 && + strncmp(src, "0X", 2) != 0) { + DHD_ERROR(("Mask invalid format. Needs to start with 0x\n")); + return -1; + } + src = src + 2; /* Skip past 0x */ + if (strlen(src) % 2 != 0) { + DHD_ERROR(("Mask invalid format. Needs to be of even length\n")); + return -1; + } + for (i = 0; *src != '\0'; i++) { + char num[3]; + bcm_strncpy_s(num, sizeof(num), src, 2); + num[2] = '\0'; + dst[i] = (uint8)strtoul(num, NULL, 16); + src += 2; + } + return i; +} + +void +dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode) +{ + char *argv[8]; + int i = 0; + const char *str; + int buf_len; + int str_len; + char *arg_save = 0, *arg_org = 0; + int rc; + char buf[128]; + wl_pkt_filter_enable_t enable_parm; + wl_pkt_filter_enable_t * pkt_filterp; + + if (!arg) + return; + + if (!(arg_save = MALLOC(dhd->osh, strlen(arg) + 1))) { + DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); + goto fail; + } + arg_org = arg_save; + memcpy(arg_save, arg, strlen(arg) + 1); + + argv[i] = bcmstrtok(&arg_save, " ", 0); + + i = 0; + if (argv[i] == NULL) { + DHD_ERROR(("No args provided\n")); + goto fail; + } + + str = "pkt_filter_enable"; + str_len = strlen(str); + bcm_strncpy_s(buf, sizeof(buf), str, str_len); + buf[str_len] = '\0'; + buf_len = str_len + 1; + + pkt_filterp = (wl_pkt_filter_enable_t *)(buf + str_len + 1); + + /* Parse packet filter id. */ + enable_parm.id = htod32(strtoul(argv[i], NULL, 0)); + + /* Parse enable/disable value. */ + enable_parm.enable = htod32(enable); + + buf_len += sizeof(enable_parm); + memcpy((char *)pkt_filterp, + &enable_parm, + sizeof(enable_parm)); + + /* Enable/disable the specified filter. */ + rc = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, buf_len, TRUE, 0); + rc = rc >= 0 ? 0 : rc; + if (rc) + DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n", + __FUNCTION__, arg, rc)); + else + DHD_TRACE(("%s: successfully added pktfilter %s\n", + __FUNCTION__, arg)); + + /* Contorl the master mode */ + bcm_mkiovar("pkt_filter_mode", (char *)&master_mode, 4, buf, sizeof(buf)); + rc = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0); + rc = rc >= 0 ? 0 : rc; + if (rc) + DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n", + __FUNCTION__, arg, rc)); + +fail: + if (arg_org) + MFREE(dhd->osh, arg_org, strlen(arg) + 1); +} + +void +dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg) +{ + const char *str; + wl_pkt_filter_t pkt_filter; + wl_pkt_filter_t *pkt_filterp; + int buf_len; + int str_len; + int rc; + uint32 mask_size; + uint32 pattern_size; + char *argv[8], * buf = 0; + int i = 0; + char *arg_save = 0, *arg_org = 0; +#define BUF_SIZE 2048 + + if (!arg) + return; + + if (!(arg_save = MALLOC(dhd->osh, strlen(arg) + 1))) { + DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); + goto fail; + } + + arg_org = arg_save; + + if (!(buf = MALLOC(dhd->osh, BUF_SIZE))) { + DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); + goto fail; + } + + memcpy(arg_save, arg, strlen(arg) + 1); + + if (strlen(arg) > BUF_SIZE) { + DHD_ERROR(("Not enough buffer %d < %d\n", (int)strlen(arg), (int)sizeof(buf))); + goto fail; + } + + argv[i] = bcmstrtok(&arg_save, " ", 0); + while (argv[i++]) + argv[i] = bcmstrtok(&arg_save, " ", 0); + + i = 0; + if (argv[i] == NULL) { + DHD_ERROR(("No args provided\n")); + goto fail; + } + + str = "pkt_filter_add"; + str_len = strlen(str); + bcm_strncpy_s(buf, BUF_SIZE, str, str_len); + buf[ str_len ] = '\0'; + buf_len = str_len + 1; + + pkt_filterp = (wl_pkt_filter_t *) (buf + str_len + 1); + + /* Parse packet filter id. */ + pkt_filter.id = htod32(strtoul(argv[i], NULL, 0)); + + if (argv[++i] == NULL) { + DHD_ERROR(("Polarity not provided\n")); + goto fail; + } + + /* Parse filter polarity. */ + pkt_filter.negate_match = htod32(strtoul(argv[i], NULL, 0)); + + if (argv[++i] == NULL) { + DHD_ERROR(("Filter type not provided\n")); + goto fail; + } + + /* Parse filter type. */ + pkt_filter.type = htod32(strtoul(argv[i], NULL, 0)); + + if (argv[++i] == NULL) { + DHD_ERROR(("Offset not provided\n")); + goto fail; + } + + /* Parse pattern filter offset. */ + pkt_filter.u.pattern.offset = htod32(strtoul(argv[i], NULL, 0)); + + if (argv[++i] == NULL) { + DHD_ERROR(("Bitmask not provided\n")); + goto fail; + } + + /* Parse pattern filter mask. */ + mask_size = + htod32(wl_pattern_atoh(argv[i], (char *) pkt_filterp->u.pattern.mask_and_pattern)); + + if (argv[++i] == NULL) { + DHD_ERROR(("Pattern not provided\n")); + goto fail; + } + + /* Parse pattern filter pattern. */ + pattern_size = + htod32(wl_pattern_atoh(argv[i], + (char *) &pkt_filterp->u.pattern.mask_and_pattern[mask_size])); + + if (mask_size != pattern_size) { + DHD_ERROR(("Mask and pattern not the same size\n")); + goto fail; + } + + pkt_filter.u.pattern.size_bytes = mask_size; + buf_len += WL_PKT_FILTER_FIXED_LEN; + buf_len += (WL_PKT_FILTER_PATTERN_FIXED_LEN + 2 * mask_size); + + /* Keep-alive attributes are set in local variable (keep_alive_pkt), and + ** then memcpy'ed into buffer (keep_alive_pktp) since there is no + ** guarantee that the buffer is properly aligned. + */ + memcpy((char *)pkt_filterp, + &pkt_filter, + WL_PKT_FILTER_FIXED_LEN + WL_PKT_FILTER_PATTERN_FIXED_LEN); + + rc = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, buf_len, TRUE, 0); + rc = rc >= 0 ? 0 : rc; + + if (rc) + DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n", + __FUNCTION__, arg, rc)); + else + DHD_TRACE(("%s: successfully added pktfilter %s\n", + __FUNCTION__, arg)); + +fail: + if (arg_org) + MFREE(dhd->osh, arg_org, strlen(arg) + 1); + + if (buf) + MFREE(dhd->osh, buf, BUF_SIZE); +} +#endif /* PKT_FILTER_SUPPORT */ + +/* ========================== */ +/* ==== ARP OFFLOAD SUPPORT = */ +/* ========================== */ +#ifdef ARP_OFFLOAD_SUPPORT +void +dhd_arp_offload_set(dhd_pub_t * dhd, int arp_mode) +{ + char iovbuf[32]; + int retcode; + + bcm_mkiovar("arp_ol", (char *)&arp_mode, 4, iovbuf, sizeof(iovbuf)); + retcode = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + retcode = retcode >= 0 ? 0 : retcode; + if (retcode) + DHD_TRACE(("%s: failed to set ARP offload mode to 0x%x, retcode = %d\n", + __FUNCTION__, arp_mode, retcode)); + else + DHD_TRACE(("%s: successfully set ARP offload mode to 0x%x\n", + __FUNCTION__, arp_mode)); +} + +void +dhd_arp_offload_enable(dhd_pub_t * dhd, int arp_enable) +{ + char iovbuf[32]; + int retcode; + + bcm_mkiovar("arpoe", (char *)&arp_enable, 4, iovbuf, sizeof(iovbuf)); + retcode = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + retcode = retcode >= 0 ? 0 : retcode; + if (retcode) + DHD_TRACE(("%s: failed to enabe ARP offload to %d, retcode = %d\n", + __FUNCTION__, arp_enable, retcode)); + else + DHD_TRACE(("%s: successfully enabed ARP offload to %d\n", + __FUNCTION__, arp_enable)); + if (arp_enable) { + uint32 version; + bcm_mkiovar("arp_version", 0, 0, iovbuf, sizeof(iovbuf)); + retcode = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, iovbuf, sizeof(iovbuf), FALSE, 0); + if (retcode) { + DHD_INFO(("%s: fail to get version (maybe version 1:retcode = %d\n", + __FUNCTION__, retcode)); + dhd->arp_version = 1; + } + else { + memcpy(&version, iovbuf, sizeof(version)); + DHD_INFO(("%s: ARP Version= %x\n", __FUNCTION__, version)); + dhd->arp_version = version; + } + } +} + +void +dhd_aoe_arp_clr(dhd_pub_t *dhd, int idx) +{ + int ret = 0; + int iov_len = 0; + char iovbuf[128]; + + if (dhd == NULL) return; + if (dhd->arp_version == 1) + idx = 0; + + iov_len = bcm_mkiovar("arp_table_clear", 0, 0, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, iov_len, TRUE, idx)) < 0) + DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret)); +} + +void +dhd_aoe_hostip_clr(dhd_pub_t *dhd, int idx) +{ + int ret = 0; + int iov_len = 0; + char iovbuf[128]; + + if (dhd == NULL) return; + if (dhd->arp_version == 1) + idx = 0; + + iov_len = bcm_mkiovar("arp_hostip_clear", 0, 0, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, iov_len, TRUE, idx)) < 0) + DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret)); +} + +void +dhd_arp_offload_add_ip(dhd_pub_t *dhd, uint32 ipaddr, int idx) +{ + int iov_len = 0; + char iovbuf[32]; + int retcode; + + + if (dhd == NULL) return; + if (dhd->arp_version == 1) + idx = 0; + iov_len = bcm_mkiovar("arp_hostip", (char *)&ipaddr, + sizeof(ipaddr), iovbuf, sizeof(iovbuf)); + retcode = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, iov_len, TRUE, idx); + + if (retcode) + DHD_TRACE(("%s: ARP ip addr add failed, retcode = %d\n", + __FUNCTION__, retcode)); + else + DHD_TRACE(("%s: sARP H ipaddr entry added \n", + __FUNCTION__)); +} + +int +dhd_arp_get_arp_hostip_table(dhd_pub_t *dhd, void *buf, int buflen, int idx) +{ + int retcode, i; + int iov_len; + uint32 *ptr32 = buf; + bool clr_bottom = FALSE; + + if (!buf) + return -1; + if (dhd == NULL) return -1; + if (dhd->arp_version == 1) + idx = 0; + + iov_len = bcm_mkiovar("arp_hostip", 0, 0, buf, buflen); + BCM_REFERENCE(iov_len); + retcode = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, buflen, FALSE, idx); + + if (retcode) { + DHD_TRACE(("%s: ioctl WLC_GET_VAR error %d\n", + __FUNCTION__, retcode)); + + return -1; + } + + /* clean up the buf, ascii reminder */ + for (i = 0; i < MAX_IPV4_ENTRIES; i++) { + if (!clr_bottom) { + if (*ptr32 == 0) + clr_bottom = TRUE; + } else { + *ptr32 = 0; + } + ptr32++; + } + + return 0; +} +#endif /* ARP_OFFLOAD_SUPPORT */ + +/* send up locally generated event */ +void +dhd_sendup_event_common(dhd_pub_t *dhdp, wl_event_msg_t *event, void *data) +{ + switch (ntoh32(event->event_type)) { +#ifdef WLBTAMP + case WLC_E_BTA_HCI_EVENT: + break; +#endif /* WLBTAMP */ + default: + break; + } + + /* Call per-port handler. */ + dhd_sendup_event(dhdp, event, data); +} + + +/* + * returns = TRUE if associated, FALSE if not associated + */ +bool dhd_is_associated(dhd_pub_t *dhd, void *bss_buf, int *retval) +{ + char bssid[6], zbuf[6]; + int ret = -1; + + bzero(bssid, 6); + bzero(zbuf, 6); + + ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_BSSID, (char *)&bssid, ETHER_ADDR_LEN, FALSE, 0); + DHD_TRACE((" %s WLC_GET_BSSID ioctl res = %d\n", __FUNCTION__, ret)); + + if (ret == BCME_NOTASSOCIATED) { + DHD_TRACE(("%s: not associated! res:%d\n", __FUNCTION__, ret)); + } + + if (retval) + *retval = ret; + + if (ret < 0) + return FALSE; + + if ((memcmp(bssid, zbuf, ETHER_ADDR_LEN) != 0)) { + /* STA is assocoated BSSID is non zero */ + + if (bss_buf) { + /* return bss if caller provided buf */ + memcpy(bss_buf, bssid, ETHER_ADDR_LEN); + } + return TRUE; + } else { + DHD_TRACE(("%s: WLC_GET_BSSID ioctl returned zero bssid\n", __FUNCTION__)); + return FALSE; + } +} + + +/* Function to estimate possible DTIM_SKIP value */ +int +dhd_get_suspend_bcn_li_dtim(dhd_pub_t *dhd) +{ + int bcn_li_dtim = 1; /* deafult no dtim skip setting */ + int ret = -1; + int dtim_assoc = 0; + int ap_beacon = 0; + + /* Check if associated */ + if (dhd_is_associated(dhd, NULL, NULL) == FALSE) { + DHD_TRACE(("%s NOT assoc ret %d\n", __FUNCTION__, ret)); + goto exit; + } + + /* read associated AP beacon interval */ + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_BCNPRD, + &ap_beacon, sizeof(ap_beacon), FALSE, 0)) < 0) { + DHD_ERROR(("%s get beacon failed code %d\n", __FUNCTION__, ret)); + goto exit; + } + + /* if associated APs Beacon more that 100msec do no dtim skip */ + if (ap_beacon > MAX_DTIM_SKIP_BEACON_ITERVAL) { + DHD_ERROR(("%s NO dtim skip for AP with beacon %d ms\n", __FUNCTION__, ap_beacon)); + goto exit; + } + + /* read associated ap's dtim setup */ + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_DTIMPRD, + &dtim_assoc, sizeof(dtim_assoc), FALSE, 0)) < 0) { + DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret)); + goto exit; + } + + /* if not assocated just eixt */ + if (dtim_assoc == 0) { + goto exit; + } + + /* attemp to use platform defined dtim skip interval */ + bcn_li_dtim = dhd->suspend_bcn_li_dtim; + + /* check if sta listen interval fits into AP dtim */ + if (dtim_assoc > LISTEN_INTERVAL) { + /* AP DTIM to big for our Listen Interval : no dtim skiping */ + bcn_li_dtim = 1; + DHD_ERROR(("%s DTIM=%d > Listen=%d : too big ...\n", + __FUNCTION__, dtim_assoc, LISTEN_INTERVAL)); + goto exit; + } + + if ((bcn_li_dtim * dtim_assoc) > LISTEN_INTERVAL) { + /* Round up dtim_skip to fit into STAs Listen Interval */ + bcn_li_dtim = (int)(LISTEN_INTERVAL / dtim_assoc); + DHD_TRACE(("%s agjust dtim_skip as %d\n", __FUNCTION__, bcn_li_dtim)); + } + + DHD_ERROR(("%s beacon=%d bcn_li_dtim=%d DTIM=%d Listen=%d\n", + __FUNCTION__, ap_beacon, bcn_li_dtim, dtim_assoc, LISTEN_INTERVAL)); + +exit: + return bcn_li_dtim; +} + +/* Check if the mode supports STA MODE */ +bool dhd_support_sta_mode(dhd_pub_t *dhd) +{ + +#ifdef WL_CFG80211 + if (!(dhd->op_mode & DHD_FLAG_STA_MODE)) + return FALSE; + else +#endif /* WL_CFG80211 */ + return TRUE; +} + +#if defined(PNO_SUPPORT) +int +dhd_pno_clean(dhd_pub_t *dhd) +{ + char iovbuf[128]; + int pfn_enabled = 0; + int iov_len = 0; + int ret; + + /* Disable pfn */ + iov_len = bcm_mkiovar("pfn", (char *)&pfn_enabled, 4, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) >= 0) { + /* clear pfn */ + iov_len = bcm_mkiovar("pfnclear", 0, 0, iovbuf, sizeof(iovbuf)); + if (iov_len) { + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, + iov_len, TRUE, 0)) < 0) { + DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret)); + } + } + else { + ret = -1; + DHD_ERROR(("%s failed code %d\n", __FUNCTION__, iov_len)); + } + } + else + DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret)); + + return ret; +} + +int +dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled) +{ + char iovbuf[128]; + int ret = -1; + + if ((!dhd) && ((pfn_enabled != 0) || (pfn_enabled != 1))) { + DHD_ERROR(("%s error exit\n", __FUNCTION__)); + return ret; + } + +#ifndef WL_SCHED_SCAN + if (!dhd_support_sta_mode(dhd)) + return (ret); + + memset(iovbuf, 0, sizeof(iovbuf)); + + if ((pfn_enabled) && (dhd_is_associated(dhd, NULL, NULL) == TRUE)) { + DHD_ERROR(("%s pno is NOT enable : called in assoc mode , ignore\n", __FUNCTION__)); + return ret; + } +#endif /* !WL_SCHED_SCAN */ + + /* Enable/disable PNO */ + if ((ret = bcm_mkiovar("pfn", (char *)&pfn_enabled, 4, iovbuf, sizeof(iovbuf))) > 0) { + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, + iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s failed for error=%d\n", __FUNCTION__, ret)); + return ret; + } + else { + dhd->pno_enable = pfn_enabled; + DHD_TRACE(("%s set pno as %s\n", + __FUNCTION__, dhd->pno_enable ? "Enable" : "Disable")); + } + } + else DHD_ERROR(("%s failed err=%d\n", __FUNCTION__, ret)); + + return ret; +} + +/* Function to execute combined scan */ +int +dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr, + int pno_repeat, int pno_freq_expo_max) +{ + int err = -1; + char iovbuf[128]; + int k, i; + wl_pfn_param_t pfn_param; + wl_pfn_t pfn_element; + uint len = 0; + + DHD_TRACE(("%s nssid=%d nchan=%d\n", __FUNCTION__, nssid, scan_fr)); + + if ((!dhd) || (!ssids_local)) { + DHD_ERROR(("%s error exit(%s %s)\n", __FUNCTION__, + (!dhd)?"dhd is null":"", (!ssids_local)?"ssid is null":"")); + err = -1; + return err; + } +#ifndef WL_SCHED_SCAN + if (!dhd_support_sta_mode(dhd)) + return err; +#endif /* !WL_SCHED_SCAN */ + + /* Check for broadcast ssid */ + for (k = 0; k < nssid; k++) { + if (!ssids_local[k].SSID_len) { + DHD_ERROR(("%d: Broadcast SSID is ilegal for PNO setting\n", k)); + return err; + } + } +/* #define PNO_DUMP 1 */ +#ifdef PNO_DUMP + { + int j; + for (j = 0; j < nssid; j++) { + DHD_ERROR(("%d: scan for %s size =%d\n", j, + ssids_local[j].SSID, ssids_local[j].SSID_len)); + } + } +#endif /* PNO_DUMP */ + + /* clean up everything */ + if ((err = dhd_pno_clean(dhd)) < 0) { + DHD_ERROR(("%s failed error=%d\n", __FUNCTION__, err)); + return err; + } + memset(iovbuf, 0, sizeof(iovbuf)); + memset(&pfn_param, 0, sizeof(pfn_param)); + memset(&pfn_element, 0, sizeof(pfn_element)); + + /* set pfn parameters */ + pfn_param.version = htod32(PFN_VERSION); + pfn_param.flags = htod16((PFN_LIST_ORDER << SORT_CRITERIA_BIT)); + + /* check and set extra pno params */ + if ((pno_repeat != 0) || (pno_freq_expo_max != 0)) { + pfn_param.flags |= htod16(ENABLE << ENABLE_ADAPTSCAN_BIT); + pfn_param.repeat = (uchar) (pno_repeat); + pfn_param.exp = (uchar) (pno_freq_expo_max); + } + /* set up pno scan fr */ + if (scan_fr != 0) + pfn_param.scan_freq = htod32(scan_fr); + + if (pfn_param.scan_freq > PNO_SCAN_MAX_FW_SEC) { + DHD_ERROR(("%s pno freq above %d sec\n", __FUNCTION__, PNO_SCAN_MAX_FW_SEC)); + return err; + } + if (pfn_param.scan_freq < PNO_SCAN_MIN_FW_SEC) { + DHD_ERROR(("%s pno freq less %d sec\n", __FUNCTION__, PNO_SCAN_MIN_FW_SEC)); + return err; + } + + len = bcm_mkiovar("pfn_set", (char *)&pfn_param, sizeof(pfn_param), iovbuf, sizeof(iovbuf)); + if ((err = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, len, TRUE, 0)) < 0) { + DHD_ERROR(("%s pfn_set failed for error=%d\n", + __FUNCTION__, err)); + return err; + } + + /* set all pfn ssid */ + for (i = 0; i < nssid; i++) { + + pfn_element.infra = htod32(DOT11_BSSTYPE_INFRASTRUCTURE); + pfn_element.auth = (DOT11_OPEN_SYSTEM); + pfn_element.wpa_auth = htod32(WPA_AUTH_PFN_ANY); + pfn_element.wsec = htod32(0); + pfn_element.infra = htod32(1); + pfn_element.flags = htod32(ENABLE << WL_PFN_HIDDEN_BIT); + memcpy((char *)pfn_element.ssid.SSID, ssids_local[i].SSID, ssids_local[i].SSID_len); + pfn_element.ssid.SSID_len = ssids_local[i].SSID_len; + + if ((len = + bcm_mkiovar("pfn_add", (char *)&pfn_element, + sizeof(pfn_element), iovbuf, sizeof(iovbuf))) > 0) { + if ((err = + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, len, TRUE, 0)) < 0) { + DHD_ERROR(("%s failed for i=%d error=%d\n", + __FUNCTION__, i, err)); + return err; + } + else + DHD_TRACE(("%s set OK with PNO time=%d repeat=%d max_adjust=%d\n", + __FUNCTION__, pfn_param.scan_freq, + pfn_param.repeat, pfn_param.exp)); + } + else DHD_ERROR(("%s failed err=%d\n", __FUNCTION__, err)); + } + + /* Enable PNO */ + /* dhd_pno_enable(dhd, 1); */ + return err; +} + +int +dhd_pno_get_status(dhd_pub_t *dhd) +{ + int ret = -1; + + if (!dhd) + return ret; + else + return (dhd->pno_enable); +} + +#endif /* OEM_ANDROID && PNO_SUPPORT */ + +#if defined(KEEP_ALIVE) +int dhd_keep_alive_onoff(dhd_pub_t *dhd) +{ + char buf[256]; + const char *str; + wl_mkeep_alive_pkt_t mkeep_alive_pkt; + wl_mkeep_alive_pkt_t *mkeep_alive_pktp; + int buf_len; + int str_len; + int res = -1; + + if (!dhd_support_sta_mode(dhd)) + return res; + + DHD_TRACE(("%s execution\n", __FUNCTION__)); + + str = "mkeep_alive"; + str_len = strlen(str); + strncpy(buf, str, str_len); + buf[ str_len ] = '\0'; + mkeep_alive_pktp = (wl_mkeep_alive_pkt_t *) (buf + str_len + 1); + mkeep_alive_pkt.period_msec = CUSTOM_KEEP_ALIVE_SETTING; + buf_len = str_len + 1; + mkeep_alive_pkt.version = htod16(WL_MKEEP_ALIVE_VERSION); + mkeep_alive_pkt.length = htod16(WL_MKEEP_ALIVE_FIXED_LEN); + /* Setup keep alive zero for null packet generation */ + mkeep_alive_pkt.keep_alive_id = 0; + mkeep_alive_pkt.len_bytes = 0; + buf_len += WL_MKEEP_ALIVE_FIXED_LEN; + bzero(mkeep_alive_pkt.data, sizeof(mkeep_alive_pkt.data)); + /* Keep-alive attributes are set in local variable (mkeep_alive_pkt), and + * then memcpy'ed into buffer (mkeep_alive_pktp) since there is no + * guarantee that the buffer is properly aligned. + */ + memcpy((char *)mkeep_alive_pktp, &mkeep_alive_pkt, WL_MKEEP_ALIVE_FIXED_LEN); + + res = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, buf_len, TRUE, 0); + + return res; +} +#endif /* defined(KEEP_ALIVE) */ +/* Android ComboSCAN support */ + +/* + * data parsing from ComboScan tlv list +*/ +int +wl_iw_parse_data_tlv(char** list_str, void *dst, int dst_size, const char token, + int input_size, int *bytes_left) +{ + char* str; + uint16 short_temp; + uint32 int_temp; + + if ((list_str == NULL) || (*list_str == NULL) ||(bytes_left == NULL) || (*bytes_left < 0)) { + DHD_ERROR(("%s error paramters\n", __FUNCTION__)); + return -1; + } + str = *list_str; + + /* Clean all dest bytes */ + memset(dst, 0, dst_size); + while (*bytes_left > 0) { + + if (str[0] != token) { + DHD_TRACE(("%s NOT Type=%d get=%d left_parse=%d \n", + __FUNCTION__, token, str[0], *bytes_left)); + return -1; + } + + *bytes_left -= 1; + str += 1; + + if (input_size == 1) { + memcpy(dst, str, input_size); + } + else if (input_size == 2) { + memcpy(dst, (char *)htod16(memcpy(&short_temp, str, input_size)), + input_size); + } + else if (input_size == 4) { + memcpy(dst, (char *)htod32(memcpy(&int_temp, str, input_size)), + input_size); + } + + *bytes_left -= input_size; + str += input_size; + *list_str = str; + return 1; + } + return 1; +} + +/* + * channel list parsing from cscan tlv list +*/ +int +wl_iw_parse_channel_list_tlv(char** list_str, uint16* channel_list, + int channel_num, int *bytes_left) +{ + char* str; + int idx = 0; + + if ((list_str == NULL) || (*list_str == NULL) ||(bytes_left == NULL) || (*bytes_left < 0)) { + DHD_ERROR(("%s error paramters\n", __FUNCTION__)); + return -1; + } + str = *list_str; + + while (*bytes_left > 0) { + + if (str[0] != CSCAN_TLV_TYPE_CHANNEL_IE) { + *list_str = str; + DHD_TRACE(("End channel=%d left_parse=%d %d\n", idx, *bytes_left, str[0])); + return idx; + } + /* Get proper CSCAN_TLV_TYPE_CHANNEL_IE */ + *bytes_left -= 1; + str += 1; + + if (str[0] == 0) { + /* All channels */ + channel_list[idx] = 0x0; + } + else { + channel_list[idx] = (uint16)str[0]; + DHD_TRACE(("%s channel=%d \n", __FUNCTION__, channel_list[idx])); + } + *bytes_left -= 1; + str += 1; + + if (idx++ > 255) { + DHD_ERROR(("%s Too many channels \n", __FUNCTION__)); + return -1; + } + } + + *list_str = str; + return idx; +} + +/* + * SSIDs list parsing from cscan tlv list + */ +int +wl_iw_parse_ssid_list_tlv(char** list_str, wlc_ssid_t* ssid, int max, int *bytes_left) +{ + char* str; + int idx = 0; + + if ((list_str == NULL) || (*list_str == NULL) || (*bytes_left < 0)) { + DHD_ERROR(("%s error paramters\n", __FUNCTION__)); + return -1; + } + str = *list_str; + while (*bytes_left > 0) { + + if (str[0] != CSCAN_TLV_TYPE_SSID_IE) { + *list_str = str; + DHD_TRACE(("nssid=%d left_parse=%d %d\n", idx, *bytes_left, str[0])); + return idx; + } + + /* Get proper CSCAN_TLV_TYPE_SSID_IE */ + *bytes_left -= 1; + str += 1; + + if (str[0] == 0) { + /* Broadcast SSID */ + ssid[idx].SSID_len = 0; + memset((char*)ssid[idx].SSID, 0x0, DOT11_MAX_SSID_LEN); + *bytes_left -= 1; + str += 1; + + DHD_TRACE(("BROADCAST SCAN left=%d\n", *bytes_left)); + } + else if (str[0] <= DOT11_MAX_SSID_LEN) { + /* Get proper SSID size */ + ssid[idx].SSID_len = str[0]; + *bytes_left -= 1; + str += 1; + + /* Get SSID */ + if (ssid[idx].SSID_len > *bytes_left) { + DHD_ERROR(("%s out of memory range len=%d but left=%d\n", + __FUNCTION__, ssid[idx].SSID_len, *bytes_left)); + return -1; + } + + memcpy((char*)ssid[idx].SSID, str, ssid[idx].SSID_len); + + *bytes_left -= ssid[idx].SSID_len; + str += ssid[idx].SSID_len; + + DHD_TRACE(("%s :size=%d left=%d\n", + (char*)ssid[idx].SSID, ssid[idx].SSID_len, *bytes_left)); + } + else { + DHD_ERROR(("### SSID size more that %d\n", str[0])); + return -1; + } + + if (idx++ > max) { + DHD_ERROR(("%s number of SSIDs more that %d\n", __FUNCTION__, idx)); + return -1; + } + } + + *list_str = str; + return idx; +} + +/* Parse a comma-separated list from list_str into ssid array, starting + * at index idx. Max specifies size of the ssid array. Parses ssids + * and returns updated idx; if idx >= max not all fit, the excess have + * not been copied. Returns -1 on empty string, or on ssid too long. + */ +int +wl_iw_parse_ssid_list(char** list_str, wlc_ssid_t* ssid, int idx, int max) +{ + char* str, *ptr; + + if ((list_str == NULL) || (*list_str == NULL)) + return -1; + + for (str = *list_str; str != NULL; str = ptr) { + + /* check for next TAG */ + if (!strncmp(str, GET_CHANNEL, strlen(GET_CHANNEL))) { + *list_str = str + strlen(GET_CHANNEL); + return idx; + } + + if ((ptr = strchr(str, ',')) != NULL) { + *ptr++ = '\0'; + } + + if (strlen(str) > DOT11_MAX_SSID_LEN) { + DHD_ERROR(("ssid <%s> exceeds %d\n", str, DOT11_MAX_SSID_LEN)); + return -1; + } + + if (strlen(str) == 0) + ssid[idx].SSID_len = 0; + + if (idx < max) { + bzero(ssid[idx].SSID, sizeof(ssid[idx].SSID)); + strncpy((char*)ssid[idx].SSID, str, sizeof(ssid[idx].SSID) - 1); + ssid[idx].SSID_len = strlen(str); + } + idx++; + } + return idx; +} + +/* + * Parse channel list from iwpriv CSCAN + */ +int +wl_iw_parse_channel_list(char** list_str, uint16* channel_list, int channel_num) +{ + int num; + int val; + char* str; + char* endptr = NULL; + + if ((list_str == NULL)||(*list_str == NULL)) + return -1; + + str = *list_str; + num = 0; + while (strncmp(str, GET_NPROBE, strlen(GET_NPROBE))) { + val = (int)strtoul(str, &endptr, 0); + if (endptr == str) { + printf("could not parse channel number starting at" + " substring \"%s\" in list:\n%s\n", + str, *list_str); + return -1; + } + str = endptr + strspn(endptr, " ,"); + + if (num == channel_num) { + DHD_ERROR(("too many channels (more than %d) in channel list:\n%s\n", + channel_num, *list_str)); + return -1; + } + + channel_list[num++] = (uint16)val; + } + *list_str = str; + return num; +} diff --git a/drivers/net/wireless/bcmdhd/dhd_custom_gpio.c b/drivers/net/wireless/bcmdhd/dhd_custom_gpio.c new file mode 100644 index 00000000..cfd1ac31 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_custom_gpio.c @@ -0,0 +1,293 @@ +/* +* Customer code to add GPIO control during WLAN start/stop +* Copyright (C) 1999-2012, Broadcom Corporation +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed to you +* under the terms of the GNU General Public License version 2 (the "GPL"), +* available at http://www.broadcom.com/licenses/GPLv2.php, with the +* following added to such license: +* +* As a special exception, the copyright holders of this software give you +* permission to link this software with independent modules, and to copy and +* distribute the resulting executable under terms of your choice, provided that +* you also meet, for each linked independent module, the terms and conditions of +* the license of that module. An independent module is a module which is not +* derived from this software. The special exception does not apply to any +* modifications of the software. +* +* Notwithstanding the above, under no circumstances may you combine this +* software in any way with any other Broadcom software provided under a license +* other than the GPL, without Broadcom's express prior written consent. +* +* $Id: dhd_custom_gpio.c 345514 2012-07-18 07:47:36Z $ +*/ + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define WL_ERROR(x) printf x +#define WL_TRACE(x) + +#ifdef CUSTOMER_HW +extern void bcm_wlan_power_off(int); +extern void bcm_wlan_power_on(int); +#endif /* CUSTOMER_HW */ +#if defined(CUSTOMER_HW2) +#ifdef CONFIG_WIFI_CONTROL_FUNC +int wifi_set_power(int on, unsigned long msec); +int wifi_get_irq_number(unsigned long *irq_flags_ptr); +int wifi_get_mac_addr(unsigned char *buf); +void *wifi_get_country_code(char *ccode); +#else +int wifi_set_power(int on, unsigned long msec) { return -1; } +int wifi_get_irq_number(unsigned long *irq_flags_ptr) { return -1; } +int wifi_get_mac_addr(unsigned char *buf) { return -1; } +void *wifi_get_country_code(char *ccode) { return NULL; } +#endif /* CONFIG_WIFI_CONTROL_FUNC */ +#endif /* CUSTOMER_HW2 */ + +#if defined(OOB_INTR_ONLY) + +#if defined(BCMLXSDMMC) +extern int sdioh_mmc_irq(int irq); +#endif /* (BCMLXSDMMC) */ + +#ifdef CUSTOMER_HW3 +#include +#endif + +/* Customer specific Host GPIO defintion */ +static int dhd_oob_gpio_num = -1; + +module_param(dhd_oob_gpio_num, int, 0644); +MODULE_PARM_DESC(dhd_oob_gpio_num, "DHD oob gpio number"); + +/* This function will return: + * 1) return : Host gpio interrupt number per customer platform + * 2) irq_flags_ptr : Type of Host interrupt as Level or Edge + * + * NOTE : + * Customer should check his platform definitions + * and his Host Interrupt spec + * to figure out the proper setting for his platform. + * Broadcom provides just reference settings as example. + * + */ +int dhd_customer_oob_irq_map(unsigned long *irq_flags_ptr) +{ + int host_oob_irq = 0; + +#if defined(CUSTOMER_HW2) + host_oob_irq = wifi_get_irq_number(irq_flags_ptr); + +#else +#if defined(CUSTOM_OOB_GPIO_NUM) + if (dhd_oob_gpio_num < 0) { + dhd_oob_gpio_num = CUSTOM_OOB_GPIO_NUM; + } +#endif /* CUSTOMER_OOB_GPIO_NUM */ + + if (dhd_oob_gpio_num < 0) { + WL_ERROR(("%s: ERROR customer specific Host GPIO is NOT defined \n", + __FUNCTION__)); + return (dhd_oob_gpio_num); + } + + WL_ERROR(("%s: customer specific Host GPIO number is (%d)\n", + __FUNCTION__, dhd_oob_gpio_num)); + +#if defined CUSTOMER_HW + host_oob_irq = MSM_GPIO_TO_INT(dhd_oob_gpio_num); +#elif defined CUSTOMER_HW3 + gpio_request(dhd_oob_gpio_num, "oob irq"); + host_oob_irq = gpio_to_irq(dhd_oob_gpio_num); + gpio_direction_input(dhd_oob_gpio_num); +#endif /* CUSTOMER_HW */ +#endif /* CUSTOMER_HW2 */ + + return (host_oob_irq); +} +#endif /* defined(OOB_INTR_ONLY) */ + +/* Customer function to control hw specific wlan gpios */ +void +dhd_customer_gpio_wlan_ctrl(int onoff) +{ + switch (onoff) { + case WLAN_RESET_OFF: + WL_TRACE(("%s: call customer specific GPIO to insert WLAN RESET\n", + __FUNCTION__)); +#ifdef CUSTOMER_HW + bcm_wlan_power_off(2); +#endif /* CUSTOMER_HW */ +#if defined(CUSTOMER_HW2) + wifi_set_power(0, 0); +#endif + WL_ERROR(("=========== WLAN placed in RESET ========\n")); + break; + + case WLAN_RESET_ON: + WL_TRACE(("%s: callc customer specific GPIO to remove WLAN RESET\n", + __FUNCTION__)); +#ifdef CUSTOMER_HW + bcm_wlan_power_on(2); +#endif /* CUSTOMER_HW */ +#if defined(CUSTOMER_HW2) + wifi_set_power(1, 0); +#endif + WL_ERROR(("=========== WLAN going back to live ========\n")); + break; + + case WLAN_POWER_OFF: + WL_TRACE(("%s: call customer specific GPIO to turn off WL_REG_ON\n", + __FUNCTION__)); +#ifdef CUSTOMER_HW + bcm_wlan_power_off(1); +#endif /* CUSTOMER_HW */ + break; + + case WLAN_POWER_ON: + WL_TRACE(("%s: call customer specific GPIO to turn on WL_REG_ON\n", + __FUNCTION__)); +#ifdef CUSTOMER_HW + bcm_wlan_power_on(1); + /* Lets customer power to get stable */ + OSL_DELAY(200); +#endif /* CUSTOMER_HW */ + break; + } +} + +#ifdef GET_CUSTOM_MAC_ENABLE +/* Function to get custom MAC address */ +int +dhd_custom_get_mac_address(unsigned char *buf) +{ + int ret = 0; + + WL_TRACE(("%s Enter\n", __FUNCTION__)); + if (!buf) + return -EINVAL; + + /* Customer access to MAC address stored outside of DHD driver */ +#if defined(CUSTOMER_HW2) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) + ret = wifi_get_mac_addr(buf); +#endif + +#ifdef EXAMPLE_GET_MAC + /* EXAMPLE code */ + { + struct ether_addr ea_example = {{0x00, 0x11, 0x22, 0x33, 0x44, 0xFF}}; + bcopy((char *)&ea_example, buf, sizeof(struct ether_addr)); + } +#endif /* EXAMPLE_GET_MAC */ + + return ret; +} +#endif /* GET_CUSTOM_MAC_ENABLE */ + +/* Customized Locale table : OPTIONAL feature */ +const struct cntry_locales_custom translate_custom_table[] = { +/* Table should be filled out based on custom platform regulatory requirement */ +#ifdef EXAMPLE_TABLE + {"", "XY", 4}, /* Universal if Country code is unknown or empty */ + {"US", "US", 69}, /* input ISO "US" to : US regrev 69 */ + {"CA", "US", 69}, /* input ISO "CA" to : US regrev 69 */ + {"EU", "EU", 5}, /* European union countries to : EU regrev 05 */ + {"AT", "EU", 5}, + {"BE", "EU", 5}, + {"BG", "EU", 5}, + {"CY", "EU", 5}, + {"CZ", "EU", 5}, + {"DK", "EU", 5}, + {"EE", "EU", 5}, + {"FI", "EU", 5}, + {"FR", "EU", 5}, + {"DE", "EU", 5}, + {"GR", "EU", 5}, + {"HU", "EU", 5}, + {"IE", "EU", 5}, + {"IT", "EU", 5}, + {"LV", "EU", 5}, + {"LI", "EU", 5}, + {"LT", "EU", 5}, + {"LU", "EU", 5}, + {"MT", "EU", 5}, + {"NL", "EU", 5}, + {"PL", "EU", 5}, + {"PT", "EU", 5}, + {"RO", "EU", 5}, + {"SK", "EU", 5}, + {"SI", "EU", 5}, + {"ES", "EU", 5}, + {"SE", "EU", 5}, + {"GB", "EU", 5}, + {"KR", "XY", 3}, + {"AU", "XY", 3}, + {"CN", "XY", 3}, /* input ISO "CN" to : XY regrev 03 */ + {"TW", "XY", 3}, + {"AR", "XY", 3}, + {"MX", "XY", 3}, + {"IL", "IL", 0}, + {"CH", "CH", 0}, + {"TR", "TR", 0}, + {"NO", "NO", 0}, +#endif /* EXMAPLE_TABLE */ +}; + + +/* Customized Locale convertor +* input : ISO 3166-1 country abbreviation +* output: customized cspec +*/ +void get_customized_country_code(char *country_iso_code, wl_country_t *cspec) +{ +#if defined(CUSTOMER_HW2) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) + + struct cntry_locales_custom *cloc_ptr; + + if (!cspec) + return; + + cloc_ptr = wifi_get_country_code(country_iso_code); + if (cloc_ptr) { + strlcpy(cspec->ccode, cloc_ptr->custom_locale, WLC_CNTRY_BUF_SZ); + cspec->rev = cloc_ptr->custom_locale_rev; + } + return; +#else + int size, i; + + size = ARRAYSIZE(translate_custom_table); + + if (cspec == 0) + return; + + if (size == 0) + return; + + for (i = 0; i < size; i++) { + if (strcmp(country_iso_code, translate_custom_table[i].iso_abbrev) == 0) { + memcpy(cspec->ccode, + translate_custom_table[i].custom_locale, WLC_CNTRY_BUF_SZ); + cspec->rev = translate_custom_table[i].custom_locale_rev; + return; + } + } +#ifdef EXAMPLE_TABLE + /* if no country code matched return first universal code from translate_custom_table */ + memcpy(cspec->ccode, translate_custom_table[0].custom_locale, WLC_CNTRY_BUF_SZ); + cspec->rev = translate_custom_table[0].custom_locale_rev; +#endif /* EXMAPLE_TABLE */ + return; +#endif /* defined(CUSTOMER_HW2) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)) */ +} diff --git a/drivers/net/wireless/bcmdhd/dhd_dbg.h b/drivers/net/wireless/bcmdhd/dhd_dbg.h new file mode 100644 index 00000000..e1660ce1 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_dbg.h @@ -0,0 +1,116 @@ +/* + * Debug/trace/assert driver definitions for Dongle Host Driver. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhd_dbg.h 353490 2012-08-27 21:10:02Z $ + */ + +#ifndef _dhd_dbg_ +#define _dhd_dbg_ + +#define USE_NET_RATELIMIT net_ratelimit() + +#if defined(DHD_DEBUG) + +#define DHD_ERROR(args) do {if ((dhd_msg_level & DHD_ERROR_VAL) && USE_NET_RATELIMIT) \ + printf args;} while (0) +#define DHD_TRACE(args) do {if (dhd_msg_level & DHD_TRACE_VAL) printf args;} while (0) +#define DHD_INFO(args) do {if (dhd_msg_level & DHD_INFO_VAL) printf args;} while (0) +#define DHD_DATA(args) do {if (dhd_msg_level & DHD_DATA_VAL) printf args;} while (0) +#define DHD_CTL(args) do {if (dhd_msg_level & DHD_CTL_VAL) printf args;} while (0) +#define DHD_TIMER(args) do {if (dhd_msg_level & DHD_TIMER_VAL) printf args;} while (0) +#define DHD_HDRS(args) do {if (dhd_msg_level & DHD_HDRS_VAL) printf args;} while (0) +#define DHD_BYTES(args) do {if (dhd_msg_level & DHD_BYTES_VAL) printf args;} while (0) +#define DHD_INTR(args) do {if (dhd_msg_level & DHD_INTR_VAL) printf args;} while (0) +#define DHD_GLOM(args) do {if (dhd_msg_level & DHD_GLOM_VAL) printf args;} while (0) +#define DHD_EVENT(args) do {if (dhd_msg_level & DHD_EVENT_VAL) printf args;} while (0) +#define DHD_BTA(args) do {if (dhd_msg_level & DHD_BTA_VAL) printf args;} while (0) +#define DHD_ISCAN(args) do {if (dhd_msg_level & DHD_ISCAN_VAL) printf args;} while (0) +#define DHD_ARPOE(args) do {if (dhd_msg_level & DHD_ARPOE_VAL) printf args;} while (0) +#define DHD_REORDER(args) do {if (dhd_msg_level & DHD_REORDER_VAL) printf args;} while (0) + +#define DHD_TRACE_HW4 DHD_TRACE + +#define DHD_ERROR_ON() (dhd_msg_level & DHD_ERROR_VAL) +#define DHD_TRACE_ON() (dhd_msg_level & DHD_TRACE_VAL) +#define DHD_INFO_ON() (dhd_msg_level & DHD_INFO_VAL) +#define DHD_DATA_ON() (dhd_msg_level & DHD_DATA_VAL) +#define DHD_CTL_ON() (dhd_msg_level & DHD_CTL_VAL) +#define DHD_TIMER_ON() (dhd_msg_level & DHD_TIMER_VAL) +#define DHD_HDRS_ON() (dhd_msg_level & DHD_HDRS_VAL) +#define DHD_BYTES_ON() (dhd_msg_level & DHD_BYTES_VAL) +#define DHD_INTR_ON() (dhd_msg_level & DHD_INTR_VAL) +#define DHD_GLOM_ON() (dhd_msg_level & DHD_GLOM_VAL) +#define DHD_EVENT_ON() (dhd_msg_level & DHD_EVENT_VAL) +#define DHD_BTA_ON() (dhd_msg_level & DHD_BTA_VAL) +#define DHD_ISCAN_ON() (dhd_msg_level & DHD_ISCAN_VAL) +#define DHD_ARPOE_ON() (dhd_msg_level & DHD_ARPOE_VAL) +#define DHD_REORDER_ON() (dhd_msg_level & DHD_REORDER_VAL) + +#else /* defined(BCMDBG) || defined(DHD_DEBUG) */ + +#define DHD_ERROR(args) do {if (USE_NET_RATELIMIT) printf args;} while (0) +#define DHD_TRACE(args) +#define DHD_INFO(args) +#define DHD_DATA(args) +#define DHD_CTL(args) +#define DHD_TIMER(args) +#define DHD_HDRS(args) +#define DHD_BYTES(args) +#define DHD_INTR(args) +#define DHD_GLOM(args) +#define DHD_EVENT(args) +#define DHD_BTA(args) +#define DHD_ISCAN(args) +#define DHD_ARPOE(args) +#define DHD_REORDER(args) + +#define DHD_TRACE_HW4 DHD_TRACE + +#define DHD_ERROR_ON() 0 +#define DHD_TRACE_ON() 0 +#define DHD_INFO_ON() 0 +#define DHD_DATA_ON() 0 +#define DHD_CTL_ON() 0 +#define DHD_TIMER_ON() 0 +#define DHD_HDRS_ON() 0 +#define DHD_BYTES_ON() 0 +#define DHD_INTR_ON() 0 +#define DHD_GLOM_ON() 0 +#define DHD_EVENT_ON() 0 +#define DHD_BTA_ON() 0 +#define DHD_ISCAN_ON() 0 +#define DHD_ARPOE_ON() 0 +#define DHD_REORDER_ON() 0 +#endif + +#define DHD_LOG(args) + +#define DHD_BLOG(cp, size) + +#define DHD_NONE(args) +extern int dhd_msg_level; + +/* Defines msg bits */ +#include + +#endif /* _dhd_dbg_ */ diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c new file mode 100644 index 00000000..76824951 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -0,0 +1,6007 @@ +/* + * Broadcom Dongle Host Driver (DHD), Linux-specific network interface + * Basically selected code segments from usb-cdc.c and usb-rndis.c + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhd_linux.c 380566 2013-01-23 05:29:02Z $ + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HAS_WAKELOCK +#include +#endif +#ifdef WL_CFG80211 +#include +#endif + +#ifdef WLBTAMP +#include +#include +#include +#endif + +#ifdef WLMEDIA_HTSF +#include +#include + +#define HTSF_MINLEN 200 /* min. packet length to timestamp */ +#define HTSF_BUS_DELAY 150 /* assume a fix propagation in us */ +#define TSMAX 1000 /* max no. of timing record kept */ +#define NUMBIN 34 +static uint32 tsidx = 0; +static uint32 htsf_seqnum = 0; +uint32 tsfsync; +struct timeval tsync; +static uint32 tsport = 5010; + +typedef struct histo_ { + uint32 bin[NUMBIN]; +} histo_t; + +#if !ISPOWEROF2(DHD_SDALIGN) +#error DHD_SDALIGN is not a power of 2! +#endif + +static histo_t vi_d1, vi_d2, vi_d3, vi_d4; +#endif /* WLMEDIA_HTSF */ + +#if defined(PKT_FILTER_SUPPORT) +#endif /* PKT_FILTER_SUPPORT */ + +#if defined(SOFTAP) +extern bool ap_cfg_running; +extern bool ap_fw_loaded; +#endif + +/* enable HOSTIP cache update from the host side when an eth0:N is up */ +#define AOE_IP_ALIAS_SUPPORT 1 + +#ifdef BCM_FD_AGGR +#include +#include +#endif +#ifdef PROP_TXSTATUS +#include +#include +#endif + +#include + +#ifdef ARP_OFFLOAD_SUPPORT +void aoe_update_host_ipv4_table(dhd_pub_t *dhd_pub, u32 ipa, bool add, int idx); +static int dhd_device_event(struct notifier_block *this, + unsigned long event, + void *ptr); + +static struct notifier_block dhd_notifier = { + .notifier_call = dhd_device_event +}; +#endif /* ARP_OFFLOAD_SUPPORT */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) +#include +volatile bool dhd_mmc_suspend = FALSE; +DECLARE_WAIT_QUEUE_HEAD(dhd_dpc_wait); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ + +#if defined(OOB_INTR_ONLY) +extern void dhd_enable_oob_intr(struct dhd_bus *bus, bool enable); +#endif /* defined(OOB_INTR_ONLY) */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) +static void dhd_hang_process(struct work_struct *work); +#endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) +MODULE_LICENSE("GPL v2"); +#endif /* LinuxVer */ + +#include + +#ifdef BCM_FD_AGGR +#define DBUS_RX_BUFFER_SIZE_DHD(net) (BCM_RPC_TP_DNGL_AGG_MAX_BYTE) +#else +#ifndef PROP_TXSTATUS +#define DBUS_RX_BUFFER_SIZE_DHD(net) (net->mtu + net->hard_header_len + dhd->pub.hdrlen) +#else +#define DBUS_RX_BUFFER_SIZE_DHD(net) (net->mtu + net->hard_header_len + dhd->pub.hdrlen + 128) +#endif +#endif /* BCM_FD_AGGR */ + +#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15) +const char * +print_tainted() +{ + return ""; +} +#endif /* LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15) */ + +/* Linux wireless extension support */ +#if defined(CONFIG_WIRELESS_EXT) +#include +extern wl_iw_extra_params_t g_wl_iw_params; +#endif /* defined(CONFIG_WIRELESS_EXT) */ + +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) +#include +#endif /* defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) */ + +extern int dhd_get_suspend_bcn_li_dtim(dhd_pub_t *dhd); + +#ifdef PKT_FILTER_SUPPORT +extern void dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg); +extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode); +#endif + +#ifdef READ_MACADDR +extern int dhd_read_macaddr(struct dhd_info *dhd, struct ether_addr *mac); +#endif +#ifdef RDWR_MACADDR +extern int dhd_check_rdwr_macaddr(struct dhd_info *dhd, dhd_pub_t *dhdp, struct ether_addr *mac); +extern int dhd_write_rdwr_macaddr(struct ether_addr *mac); +#endif +#ifdef WRITE_MACADDR +extern int dhd_write_macaddr(struct ether_addr *mac); +#endif +#ifdef GET_MAC_FROM_OTP +extern int dhd_check_module_mac(dhd_pub_t *dhd, struct ether_addr *mac); +#endif +#ifdef MIMO_ANT_SETTING +extern int dhd_sel_ant_from_file(dhd_pub_t *dhd); +#endif + +#ifdef GLOBALCONFIG_WLAN_COUNTRY_CODE +int dhd_customer_set_country(dhd_pub_t *dhd); +#endif + +/* Interface control information */ +typedef struct dhd_if { + struct dhd_info *info; /* back pointer to dhd_info */ + /* OS/stack specifics */ + struct net_device *net; + struct net_device_stats stats; + int idx; /* iface idx in dongle */ + dhd_if_state_t state; /* interface state */ + uint subunit; /* subunit */ + uint8 mac_addr[ETHER_ADDR_LEN]; /* assigned MAC address */ + bool attached; /* Delayed attachment when unset */ + bool txflowcontrol; /* Per interface flow control indicator */ + char name[IFNAMSIZ+1]; /* linux interface name */ + uint8 bssidx; /* bsscfg index for the interface */ + bool set_multicast; + bool event2cfg80211; /* To determine if pass event to cfg80211 */ +} dhd_if_t; + +#ifdef WLMEDIA_HTSF +typedef struct { + uint32 low; + uint32 high; +} tsf_t; + +typedef struct { + uint32 last_cycle; + uint32 last_sec; + uint32 last_tsf; + uint32 coef; /* scaling factor */ + uint32 coefdec1; /* first decimal */ + uint32 coefdec2; /* second decimal */ +} htsf_t; + +typedef struct { + uint32 t1; + uint32 t2; + uint32 t3; + uint32 t4; +} tstamp_t; + +static tstamp_t ts[TSMAX]; +static tstamp_t maxdelayts; +static uint32 maxdelay = 0, tspktcnt = 0, maxdelaypktno = 0; + +#endif /* WLMEDIA_HTSF */ + +/* Local private structure (extension of pub) */ +typedef struct dhd_info { +#if defined(CONFIG_WIRELESS_EXT) + wl_iw_t iw; /* wireless extensions state (must be first) */ +#endif /* defined(CONFIG_WIRELESS_EXT) */ + + dhd_pub_t pub; + + /* For supporting multiple interfaces */ + dhd_if_t *iflist[DHD_MAX_IFS]; + + struct semaphore proto_sem; +#ifdef PROP_TXSTATUS + spinlock_t wlfc_spinlock; +#endif /* PROP_TXSTATUS */ +#ifdef WLMEDIA_HTSF + htsf_t htsf; +#endif + wait_queue_head_t ioctl_resp_wait; + struct timer_list timer; + bool wd_timer_valid; + struct tasklet_struct tasklet; + spinlock_t sdlock; + spinlock_t txqlock; + spinlock_t dhd_lock; +#ifdef DHDTHREAD + /* Thread based operation */ + bool threads_only; + struct semaphore sdsem; + + tsk_ctl_t thr_dpc_ctl; + tsk_ctl_t thr_wdt_ctl; +#endif /* DHDTHREAD */ + bool dhd_tasklet_create; + tsk_ctl_t thr_sysioc_ctl; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + struct work_struct work_hang; +#endif + + /* Wakelocks */ +#if defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + struct wake_lock wl_wifi; /* Wifi wakelock */ + struct wake_lock wl_rxwake; /* Wifi rx wakelock */ + struct wake_lock wl_ctrlwake; /* Wifi ctrl wakelock */ + struct wake_lock wl_wdwake; /* Wifi wd wakelock */ +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + /* net_device interface lock, prevent race conditions among net_dev interface + * calls and wifi_on or wifi_off + */ + struct mutex dhd_net_if_mutex; + struct mutex dhd_suspend_mutex; +#endif + spinlock_t wakelock_spinlock; + int wakelock_counter; + int wakelock_wd_counter; + int wakelock_rx_timeout_enable; + int wakelock_ctrl_timeout_enable; + + /* Thread to issue ioctl for multicast */ + unsigned char set_macaddress; + struct ether_addr macvalue; + wait_queue_head_t ctrl_wait; + atomic_t pend_8021x_cnt; + dhd_attach_states_t dhd_state; + +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif /* CONFIG_HAS_EARLYSUSPEND && defined(DHD_USE_EARLYSUSPEND) */ + +#ifdef ARP_OFFLOAD_SUPPORT + u32 pend_ipaddr; +#endif /* ARP_OFFLOAD_SUPPORT */ +#ifdef BCM_FD_AGGR + void *rpc_th; + void *rpc_osh; + struct timer_list rpcth_timer; + bool rpcth_timer_active; + bool fdaggr; +#endif +} dhd_info_t; + +/* Flag to indicate if we should download firmware on driver load */ +uint dhd_download_fw_on_driverload = TRUE; + +/* Definitions to provide path to the firmware and nvram + * example nvram_path[MOD_PARAM_PATHLEN]="/projects/wlan/nvram.txt" + */ +char firmware_path[MOD_PARAM_PATHLEN]; +char nvram_path[MOD_PARAM_PATHLEN]; + +/* information string to keep firmware, chio, cheip version info visiable from log */ +char info_string[MOD_PARAM_INFOLEN]; +module_param_string(info_string, info_string, MOD_PARAM_INFOLEN, 0444); + +int op_mode = 0; +int disable_proptx = 0; +module_param(op_mode, int, 0644); +extern int wl_control_wl_start(struct net_device *dev); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) +struct semaphore dhd_registration_sem; +struct semaphore dhd_chipup_sem; +int dhd_registration_check = FALSE; + +#define DHD_REGISTRATION_TIMEOUT 12000 /* msec : allowed time to finished dhd registration */ +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ + +/* Spawn a thread for system ioctls (set mac, set mcast) */ +uint dhd_sysioc = TRUE; +module_param(dhd_sysioc, uint, 0); + +/* Error bits */ +module_param(dhd_msg_level, int, 0); + +/* Disable Prop tx */ +module_param(disable_proptx, int, 0644); + +/* load firmware and/or nvram values from the filesystem */ +module_param_string(firmware_path, firmware_path, MOD_PARAM_PATHLEN, 0660); +module_param_string(nvram_path, nvram_path, MOD_PARAM_PATHLEN, 0); + +/* Watchdog interval */ +uint dhd_watchdog_ms = 10; +module_param(dhd_watchdog_ms, uint, 0); + +#if defined(DHD_DEBUG) +/* Console poll interval */ +uint dhd_console_ms = 0; +module_param(dhd_console_ms, uint, 0644); +#endif /* defined(DHD_DEBUG) */ + +extern uint dhd_doflow; +/* tunable paramter to update tx credit in each dpc */ +extern uint dhd_dpcpoll; +module_param(dhd_doflow, uint, 0644); +module_param(dhd_dpcpoll, uint, 0644); +uint dhd_slpauto = TRUE; +module_param(dhd_slpauto, uint, 0); + +/* ARP offload agent mode : Enable ARP Peer Auto-Reply */ +uint dhd_arp_mode = ARP_OL_AGENT | ARP_OL_PEER_AUTO_REPLY; +module_param(dhd_arp_mode, uint, 0); + +/* ARP offload enable */ +uint dhd_arp_enable = TRUE; +module_param(dhd_arp_enable, uint, 0); + +#ifdef PKT_FILTER_SUPPORT +/* Global Pkt filter enable control */ +uint dhd_pkt_filter_enable = TRUE; +module_param(dhd_pkt_filter_enable, uint, 0); +#endif + +/* Pkt filter init setup */ +uint dhd_pkt_filter_init = 0; +module_param(dhd_pkt_filter_init, uint, 0); + +/* Pkt filter mode control */ +#ifdef GAN_LITE_NAT_KEEPALIVE_FILTER +uint dhd_master_mode = FALSE; +#else +uint dhd_master_mode = TRUE; +#endif /* GAL_LITE_NAT_KEEPALIVE_FILTER */ +module_param(dhd_master_mode, uint, 0); + +#ifdef DHDTHREAD +/* Watchdog thread priority, -1 to use kernel timer */ +int dhd_watchdog_prio = 0; +module_param(dhd_watchdog_prio, int, 0); + +/* DPC thread priority */ +int dhd_dpc_prio = CUSTOM_DPC_PRIO_SETTING; +module_param(dhd_dpc_prio, int, 0); + +extern int dhd_dongle_memsize; +module_param(dhd_dongle_memsize, int, 0); +#endif /* DHDTHREAD */ +/* Control fw roaming */ +uint dhd_roam_disable = 0; + +/* Control radio state */ +uint dhd_radio_up = 1; + +/* Network inteface name */ +char iface_name[IFNAMSIZ] = {'\0'}; +module_param_string(iface_name, iface_name, IFNAMSIZ, 0); + +/* The following are specific to the SDIO dongle */ + +/* IOCTL response timeout */ +int dhd_ioctl_timeout_msec = IOCTL_RESP_TIMEOUT; + +/* Idle timeout for backplane clock */ +int dhd_idletime = DHD_IDLETIME_TICKS; +module_param(dhd_idletime, int, 0); + +/* Use polling */ +uint dhd_poll = FALSE; +module_param(dhd_poll, uint, 0); + +/* Use interrupts */ +uint dhd_intr = TRUE; +module_param(dhd_intr, uint, 0); + +/* SDIO Drive Strength (in milliamps) */ +uint dhd_sdiod_drive_strength = 6; +module_param(dhd_sdiod_drive_strength, uint, 0); + +/* Tx/Rx bounds */ +extern uint dhd_txbound; +extern uint dhd_rxbound; +module_param(dhd_txbound, uint, 0); +module_param(dhd_rxbound, uint, 0); + +/* Deferred transmits */ +extern uint dhd_deferred_tx; +module_param(dhd_deferred_tx, uint, 0); + +#ifdef BCMDBGFS +extern void dhd_dbg_init(dhd_pub_t *dhdp); +extern void dhd_dbg_remove(void); +#endif /* BCMDBGFS */ + + + +#ifdef SDTEST +/* Echo packet generator (pkts/s) */ +uint dhd_pktgen = 0; +module_param(dhd_pktgen, uint, 0); + +/* Echo packet len (0 => sawtooth, max 2040) */ +uint dhd_pktgen_len = 0; +module_param(dhd_pktgen_len, uint, 0); +#endif /* SDTEST */ + +/* Version string to report */ +#ifdef DHD_DEBUG +#ifndef SRCBASE +#define SRCBASE "drivers/net/wireless/bcmdhd" +#endif +#define DHD_COMPILED "\nCompiled in " SRCBASE +#else +#define DHD_COMPILED +#endif /* DHD_DEBUG */ + +static char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR +#ifdef DHD_DEBUG +"\nCompiled in " SRCBASE " on " __DATE__ " at " __TIME__ +#endif +; +static void dhd_net_if_lock_local(dhd_info_t *dhd); +static void dhd_net_if_unlock_local(dhd_info_t *dhd); +static void dhd_suspend_lock(dhd_pub_t *dhdp); +static void dhd_suspend_unlock(dhd_pub_t *dhdp); + +#ifdef WLMEDIA_HTSF +void htsf_update(dhd_info_t *dhd, void *data); +tsf_t prev_tsf, cur_tsf; + +uint32 dhd_get_htsf(dhd_info_t *dhd, int ifidx); +static int dhd_ioctl_htsf_get(dhd_info_t *dhd, int ifidx); +static void dhd_dump_latency(void); +static void dhd_htsf_addtxts(dhd_pub_t *dhdp, void *pktbuf); +static void dhd_htsf_addrxts(dhd_pub_t *dhdp, void *pktbuf); +static void dhd_dump_htsfhisto(histo_t *his, char *s); +#endif /* WLMEDIA_HTSF */ + +/* Monitor interface */ +int dhd_monitor_init(void *dhd_pub); +int dhd_monitor_uninit(void); + + +#if defined(CONFIG_WIRELESS_EXT) +struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev); +#endif /* defined(CONFIG_WIRELESS_EXT) */ + +static void dhd_dpc(ulong data); +/* forward decl */ +extern int dhd_wait_pend8021x(struct net_device *dev); + +#ifdef TOE +#ifndef BDC +#error TOE requires BDC +#endif /* !BDC */ +static int dhd_toe_get(dhd_info_t *dhd, int idx, uint32 *toe_ol); +static int dhd_toe_set(dhd_info_t *dhd, int idx, uint32 toe_ol); +#endif /* TOE */ + +static int dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, + wl_event_msg_t *event_ptr, void **data_ptr); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) +static int dhd_sleep_pm_callback(struct notifier_block *nfb, unsigned long action, void *ignored) +{ + int ret = NOTIFY_DONE; + +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) + switch (action) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + dhd_mmc_suspend = TRUE; + ret = NOTIFY_OK; + break; + case PM_POST_HIBERNATION: + case PM_POST_SUSPEND: + dhd_mmc_suspend = FALSE; + ret = NOTIFY_OK; + break; + } + smp_mb(); +#endif + return ret; +} + +static struct notifier_block dhd_sleep_pm_notifier = { + .notifier_call = dhd_sleep_pm_callback, + .priority = 10 +}; +extern int register_pm_notifier(struct notifier_block *nb); +extern int unregister_pm_notifier(struct notifier_block *nb); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ + +void dhd_set_packet_filter(dhd_pub_t *dhd) +{ +#ifdef PKT_FILTER_SUPPORT + int i; + + DHD_TRACE(("%s: enter\n", __FUNCTION__)); + if (dhd_pkt_filter_enable) { + for (i = 0; i < dhd->pktfilter_count; i++) { + dhd_pktfilter_offload_set(dhd, dhd->pktfilter[i]); + } + } +#endif /* PKT_FILTER_SUPPORT */ +} + +void dhd_enable_packet_filter(int value, dhd_pub_t *dhd) +{ +#ifdef PKT_FILTER_SUPPORT + int i; + + DHD_TRACE(("%s: enter, value = %d\n", __FUNCTION__, value)); + /* 1 - Enable packet filter, only allow unicast packet to send up */ + /* 0 - Disable packet filter */ + if (dhd_pkt_filter_enable && (!value || + (dhd_support_sta_mode(dhd) && !dhd->dhcp_in_progress))) { + for (i = 0; i < dhd->pktfilter_count; i++) { +#ifdef PASS_ARP_PACKET + if (value && (i == dhd->pktfilter_count -1) && + !(dhd->op_mode & (DHD_FLAG_P2P_GC_MODE | DHD_FLAG_P2P_GO_MODE))) { + DHD_TRACE_HW4(("Do not turn on ARP white list pkt filter:" + "val %d, cnt %d, op_mode 0x%x\n", + value, i, dhd->op_mode)); + continue; + } +#endif + dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i], + value, dhd_master_mode); + } + } +#endif /* PKT_FILTER_SUPPORT */ +} + +static int dhd_set_suspend(int value, dhd_pub_t *dhd) +{ +#if !defined(SUPPORT_PM2_ONLY) + int power_mode = PM_MAX; +#endif + /* wl_pkt_filter_enable_t enable_parm; */ + char iovbuf[32]; + int bcn_li_dtim = 0; /* Default bcn_li_dtim in resume mode is 0 */ +#ifndef DISABLE_FW_ROAM_SUSPEND + uint roamvar = 1; +#endif +#ifdef ENABLE_BCN_LI_BCN_WAKEUP + int bcn_li_bcn; +#endif /* ENABLE_BCN_LI_BCN_WAKEUP */ +#ifdef PASS_ALL_MCAST_PKTS + struct dhd_info *dhdinfo = dhd->info; + uint32 allmulti; + uint i; +#endif /* PASS_ALL_MCAST_PKTS */ + + DHD_TRACE(("%s: enter, value = %d in_suspend=%d\n", + __FUNCTION__, value, dhd->in_suspend)); + + dhd_suspend_lock(dhd); + if (dhd && dhd->up) { + if (value && dhd->in_suspend) { +#ifdef PKT_FILTER_SUPPORT + dhd->early_suspended = 1; +#endif + /* Kernel suspended */ + DHD_ERROR(("%s: force extra Suspend setting\n", __FUNCTION__)); + +#if !defined(SUPPORT_PM2_ONLY) + dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, + sizeof(power_mode), TRUE, 0); +#endif + /* Enable packet filter, only allow unicast packet to send up */ + dhd_enable_packet_filter(1, dhd); +#ifdef PASS_ALL_MCAST_PKTS + allmulti = 0; + bcm_mkiovar("allmulti", (char *)&allmulti, + 4, iovbuf, sizeof(iovbuf)); + for (i = 0; i < DHD_MAX_IFS; i++) { + if (dhdinfo->iflist[i] && dhdinfo->iflist[i]->net) + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, + sizeof(iovbuf), TRUE, i); + } +#endif /* PASS_ALL_MCAST_PKTS */ + + /* If DTIM skip is set up as default, force it to wake + * each third DTIM for better power savings. Note that + * one side effect is a chance to miss BC/MC packet. + */ + bcn_li_dtim = dhd_get_suspend_bcn_li_dtim(dhd); + bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim, + 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + +#ifndef DISABLE_FW_ROAM_SUSPEND + /* Disable firmware roaming during suspend */ + bcm_mkiovar("roam_off", (char *)&roamvar, 4, + iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +#endif +#ifdef ENABLE_BCN_LI_BCN_WAKEUP + bcn_li_bcn = 0; + bcm_mkiovar("bcn_li_bcn", (char *)&bcn_li_bcn, + 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +#endif /* ENABLE_BCN_LI_BCN_WAKEUP */ + + } else { +#ifdef PKT_FILTER_SUPPORT + dhd->early_suspended = 0; +#endif + /* Kernel resumed */ + DHD_ERROR(("%s: Remove extra suspend setting\n", __FUNCTION__)); + +#if !defined(SUPPORT_PM2_ONLY) + power_mode = PM_FAST; + dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, + sizeof(power_mode), TRUE, 0); +#endif + /* disable pkt filter */ + dhd_enable_packet_filter(0, dhd); +#ifdef PASS_ALL_MCAST_PKTS + allmulti = 1; + bcm_mkiovar("allmulti", (char *)&allmulti, + 4, iovbuf, sizeof(iovbuf)); + for (i = 0; i < DHD_MAX_IFS; i++) { + if (dhdinfo->iflist[i] && dhdinfo->iflist[i]->net) + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, + sizeof(iovbuf), TRUE, i); + } +#endif /* PASS_ALL_MCAST_PKTS */ + + /* restore pre-suspend setting for dtim_skip */ + bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim, + 4, iovbuf, sizeof(iovbuf)); + + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +#ifndef DISABLE_FW_ROAM_SUSPEND + roamvar = dhd_roam_disable; + bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, + sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +#endif +#ifdef ENABLE_BCN_LI_BCN_WAKEUP + bcn_li_bcn = 1; + bcm_mkiovar("bcn_li_bcn", (char *)&bcn_li_bcn, + 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +#endif /* ENABLE_BCN_LI_BCN_WAKEUP */ + + } + } + + dhd_suspend_unlock(dhd); + return 0; +} + +static int dhd_suspend_resume_helper(struct dhd_info *dhd, int val, int force) +{ + dhd_pub_t *dhdp = &dhd->pub; + int ret = 0; + + DHD_OS_WAKE_LOCK(dhdp); + /* Set flag when early suspend was called */ + dhdp->in_suspend = val; + if ((force || !dhdp->suspend_disable_flag) && + dhd_support_sta_mode(dhdp)) + { + ret = dhd_set_suspend(val, dhdp); + } + + DHD_OS_WAKE_UNLOCK(dhdp); + return ret; +} + +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) +static void dhd_early_suspend(struct early_suspend *h) +{ + struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend); + DHD_TRACE_HW4(("%s: enter\n", __FUNCTION__)); + + if (dhd) + dhd_suspend_resume_helper(dhd, 1, 0); +} + +static void dhd_late_resume(struct early_suspend *h) +{ + struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend); + DHD_TRACE_HW4(("%s: enter\n", __FUNCTION__)); + + if (dhd) + dhd_suspend_resume_helper(dhd, 0, 0); +} +#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */ + +/* + * Generalized timeout mechanism. Uses spin sleep with exponential back-off until + * the sleep time reaches one jiffy, then switches over to task delay. Usage: + * + * dhd_timeout_start(&tmo, usec); + * while (!dhd_timeout_expired(&tmo)) + * if (poll_something()) + * break; + * if (dhd_timeout_expired(&tmo)) + * fatal(); + */ + +void +dhd_timeout_start(dhd_timeout_t *tmo, uint usec) +{ + tmo->limit = usec; + tmo->increment = 0; + tmo->elapsed = 0; + tmo->tick = jiffies_to_usecs(1); +} + +int +dhd_timeout_expired(dhd_timeout_t *tmo) +{ + /* Does nothing the first call */ + if (tmo->increment == 0) { + tmo->increment = 1; + return 0; + } + + if (tmo->elapsed >= tmo->limit) + return 1; + + /* Add the delay that's about to take place */ + tmo->elapsed += tmo->increment; + + if (tmo->increment < tmo->tick) { + OSL_DELAY(tmo->increment); + tmo->increment *= 2; + if (tmo->increment > tmo->tick) + tmo->increment = tmo->tick; + } else { + wait_queue_head_t delay_wait; + DECLARE_WAITQUEUE(wait, current); + init_waitqueue_head(&delay_wait); + add_wait_queue(&delay_wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + remove_wait_queue(&delay_wait, &wait); + set_current_state(TASK_RUNNING); + } + + return 0; +} + +int +dhd_net2idx(dhd_info_t *dhd, struct net_device *net) +{ + int i = 0; + + ASSERT(dhd); + while (i < DHD_MAX_IFS) { + if (dhd->iflist[i] && (dhd->iflist[i]->net == net)) + return i; + i++; + } + + return DHD_BAD_IF; +} + +struct net_device * dhd_idx2net(void *pub, int ifidx) +{ + struct dhd_pub *dhd_pub = (struct dhd_pub *)pub; + struct dhd_info *dhd_info; + + if (!dhd_pub || ifidx < 0 || ifidx >= DHD_MAX_IFS) + return NULL; + dhd_info = dhd_pub->info; + if (dhd_info && dhd_info->iflist[ifidx]) + return dhd_info->iflist[ifidx]->net; + return NULL; +} + +int +dhd_ifname2idx(dhd_info_t *dhd, char *name) +{ + int i = DHD_MAX_IFS; + + ASSERT(dhd); + + if (name == NULL || *name == '\0') + return 0; + + while (--i > 0) + if (dhd->iflist[i] && !strncmp(dhd->iflist[i]->name, name, IFNAMSIZ)) + break; + + DHD_TRACE(("%s: return idx %d for \"%s\"\n", __FUNCTION__, i, name)); + + return i; /* default - the primary interface */ +} + +char * +dhd_ifname(dhd_pub_t *dhdp, int ifidx) +{ + dhd_info_t *dhd = (dhd_info_t *)dhdp->info; + + ASSERT(dhd); + + if (ifidx < 0 || ifidx >= DHD_MAX_IFS) { + DHD_ERROR(("%s: ifidx %d out of range\n", __FUNCTION__, ifidx)); + return ""; + } + + if (dhd->iflist[ifidx] == NULL) { + DHD_ERROR(("%s: null i/f %d\n", __FUNCTION__, ifidx)); + return ""; + } + + if (dhd->iflist[ifidx]->net) + return dhd->iflist[ifidx]->net->name; + + return ""; +} + +uint8 * +dhd_bssidx2bssid(dhd_pub_t *dhdp, int idx) +{ + int i; + dhd_info_t *dhd = (dhd_info_t *)dhdp; + + ASSERT(dhd); + for (i = 0; i < DHD_MAX_IFS; i++) + if (dhd->iflist[i] && dhd->iflist[i]->bssidx == idx) + return dhd->iflist[i]->mac_addr; + + return NULL; +} + + +static void +_dhd_set_multicast_list(dhd_info_t *dhd, int ifidx) +{ + struct net_device *dev; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) + struct netdev_hw_addr *ha; +#else + struct dev_mc_list *mclist; +#endif + uint32 allmulti, cnt; + + wl_ioctl_t ioc; + char *buf, *bufp; + uint buflen; + int ret; + + ASSERT(dhd && dhd->iflist[ifidx]); + dev = dhd->iflist[ifidx]->net; + if (!dev) + return; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) + netif_addr_lock_bh(dev); +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) + cnt = netdev_mc_count(dev); +#else + cnt = dev->mc_count; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) + netif_addr_unlock_bh(dev); +#endif + + /* Determine initial value of allmulti flag */ + allmulti = (dev->flags & IFF_ALLMULTI) ? TRUE : FALSE; +#ifdef PASS_ALL_MCAST_PKTS +#ifdef PKT_FILTER_SUPPORT + if (!dhd->pub.early_suspended) +#endif /* PKT_FILTER_SUPPORT */ + allmulti = TRUE; +#endif /* PASS_ALL_MCAST_PKTS */ + + /* Send down the multicast list first. */ + + + buflen = sizeof("mcast_list") + sizeof(cnt) + (cnt * ETHER_ADDR_LEN); + if (!(bufp = buf = MALLOC(dhd->pub.osh, buflen))) { + DHD_ERROR(("%s: out of memory for mcast_list, cnt %d\n", + dhd_ifname(&dhd->pub, ifidx), cnt)); + return; + } + + strncpy(bufp, "mcast_list", buflen - 1); + bufp[buflen - 1] = '\0'; + bufp += strlen("mcast_list") + 1; + + cnt = htol32(cnt); + memcpy(bufp, &cnt, sizeof(cnt)); + bufp += sizeof(cnt); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) + netif_addr_lock_bh(dev); +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) + netdev_for_each_mc_addr(ha, dev) { + if (!cnt) + break; + memcpy(bufp, ha->addr, ETHER_ADDR_LEN); + bufp += ETHER_ADDR_LEN; + cnt--; + } +#else + for (mclist = dev->mc_list; (mclist && (cnt > 0)); + cnt--, mclist = mclist->next) { + memcpy(bufp, (void *)mclist->dmi_addr, ETHER_ADDR_LEN); + bufp += ETHER_ADDR_LEN; + } +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) + netif_addr_unlock_bh(dev); +#endif + + memset(&ioc, 0, sizeof(ioc)); + ioc.cmd = WLC_SET_VAR; + ioc.buf = buf; + ioc.len = buflen; + ioc.set = TRUE; + + ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len); + if (ret < 0) { + DHD_ERROR(("%s: set mcast_list failed, cnt %d\n", + dhd_ifname(&dhd->pub, ifidx), cnt)); + allmulti = cnt ? TRUE : allmulti; + } + + MFREE(dhd->pub.osh, buf, buflen); + + /* Now send the allmulti setting. This is based on the setting in the + * net_device flags, but might be modified above to be turned on if we + * were trying to set some addresses and dongle rejected it... + */ + + buflen = sizeof("allmulti") + sizeof(allmulti); + if (!(buf = MALLOC(dhd->pub.osh, buflen))) { + DHD_ERROR(("%s: out of memory for allmulti\n", dhd_ifname(&dhd->pub, ifidx))); + return; + } + allmulti = htol32(allmulti); + + if (!bcm_mkiovar("allmulti", (void*)&allmulti, sizeof(allmulti), buf, buflen)) { + DHD_ERROR(("%s: mkiovar failed for allmulti, datalen %d buflen %u\n", + dhd_ifname(&dhd->pub, ifidx), (int)sizeof(allmulti), buflen)); + MFREE(dhd->pub.osh, buf, buflen); + return; + } + + + memset(&ioc, 0, sizeof(ioc)); + ioc.cmd = WLC_SET_VAR; + ioc.buf = buf; + ioc.len = buflen; + ioc.set = TRUE; + + ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len); + if (ret < 0) { + DHD_ERROR(("%s: set allmulti %d failed\n", + dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti))); + } + + MFREE(dhd->pub.osh, buf, buflen); + + /* Finally, pick up the PROMISC flag as well, like the NIC driver does */ + + allmulti = (dev->flags & IFF_PROMISC) ? TRUE : FALSE; + allmulti = htol32(allmulti); + + memset(&ioc, 0, sizeof(ioc)); + ioc.cmd = WLC_SET_PROMISC; + ioc.buf = &allmulti; + ioc.len = sizeof(allmulti); + ioc.set = TRUE; + + ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len); + if (ret < 0) { + DHD_ERROR(("%s: set promisc %d failed\n", + dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti))); + } +} + +int +_dhd_set_mac_address(dhd_info_t *dhd, int ifidx, struct ether_addr *addr) +{ + char buf[32]; + wl_ioctl_t ioc; + int ret; + + if (!bcm_mkiovar("cur_etheraddr", (char*)addr, ETHER_ADDR_LEN, buf, 32)) { + DHD_ERROR(("%s: mkiovar failed for cur_etheraddr\n", dhd_ifname(&dhd->pub, ifidx))); + return -1; + } + memset(&ioc, 0, sizeof(ioc)); + ioc.cmd = WLC_SET_VAR; + ioc.buf = buf; + ioc.len = 32; + ioc.set = TRUE; + + ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len); + if (ret < 0) { + DHD_ERROR(("%s: set cur_etheraddr failed\n", dhd_ifname(&dhd->pub, ifidx))); + } else { + memcpy(dhd->iflist[ifidx]->net->dev_addr, addr, ETHER_ADDR_LEN); + memcpy(dhd->pub.mac.octet, addr, ETHER_ADDR_LEN); + } + + return ret; +} + +#ifdef SOFTAP +extern struct net_device *ap_net_dev; +extern tsk_ctl_t ap_eth_ctl; /* ap netdev heper thread ctl */ +#endif + +static void +dhd_op_if(dhd_if_t *ifp) +{ + dhd_info_t *dhd; + int ret = 0, err = 0; +#ifdef SOFTAP + unsigned long flags; +#endif + + if (!ifp || !ifp->info || !ifp->idx) + return; + ASSERT(ifp && ifp->info && ifp->idx); /* Virtual interfaces only */ + dhd = ifp->info; + + DHD_TRACE(("%s: idx %d, state %d\n", __FUNCTION__, ifp->idx, ifp->state)); + +#ifdef WL_CFG80211 + if (wl_cfg80211_is_progress_ifchange()) + return; + +#endif + switch (ifp->state) { + case DHD_IF_ADD: + /* + * Delete the existing interface before overwriting it + * in case we missed the WLC_E_IF_DEL event. + */ + if (ifp->net != NULL) { + DHD_ERROR(("%s: ERROR: netdev:%s already exists, try free & unregister \n", + __FUNCTION__, ifp->net->name)); + netif_stop_queue(ifp->net); + unregister_netdev(ifp->net); + free_netdev(ifp->net); + } + /* Allocate etherdev, including space for private structure */ + if (!(ifp->net = alloc_etherdev(sizeof(dhd)))) { + DHD_ERROR(("%s: OOM - alloc_etherdev\n", __FUNCTION__)); + ret = -ENOMEM; + } + if (ret == 0) { + strncpy(ifp->net->name, ifp->name, IFNAMSIZ); + ifp->net->name[IFNAMSIZ - 1] = '\0'; + memcpy(netdev_priv(ifp->net), &dhd, sizeof(dhd)); +#ifdef WL_CFG80211 + if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) + if (!wl_cfg80211_notify_ifadd(ifp->net, ifp->idx, ifp->bssidx, + (void*)dhd_net_attach)) { + ifp->state = DHD_IF_NONE; + ifp->event2cfg80211 = TRUE; + return; + } +#endif + if ((err = dhd_net_attach(&dhd->pub, ifp->idx)) != 0) { + DHD_ERROR(("%s: dhd_net_attach failed, err %d\n", + __FUNCTION__, err)); + ret = -EOPNOTSUPP; + } else { +#if defined(SOFTAP) + if (ap_fw_loaded && !(dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)) { + /* semaphore that the soft AP CODE waits on */ + flags = dhd_os_spin_lock(&dhd->pub); + + /* save ptr to wl0.1 netdev for use in wl_iw.c */ + ap_net_dev = ifp->net; + /* signal to the SOFTAP 'sleeper' thread, wl0.1 is ready */ + up(&ap_eth_ctl.sema); + dhd_os_spin_unlock(&dhd->pub, flags); + } +#endif + DHD_TRACE(("\n ==== pid:%x, net_device for if:%s created ===\n\n", + current->pid, ifp->net->name)); + ifp->state = DHD_IF_NONE; + } + } + break; + case DHD_IF_DEL: + /* Make sure that we don't enter again here if .. */ + /* dhd_op_if is called again from some other context */ + ifp->state = DHD_IF_DELETING; + if (ifp->net != NULL) { + DHD_TRACE(("\n%s: got 'DHD_IF_DEL' state\n", __FUNCTION__)); + netif_stop_queue(ifp->net); +#ifdef WL_CFG80211 + if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) { + wl_cfg80211_ifdel_ops(ifp->net); + } +#endif + unregister_netdev(ifp->net); + ret = DHD_DEL_IF; /* Make sure the free_netdev() is called */ +#ifdef WL_CFG80211 + if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) { + wl_cfg80211_notify_ifdel(); + } +#endif + } + break; + case DHD_IF_DELETING: + break; + default: + DHD_ERROR(("%s: bad op %d\n", __FUNCTION__, ifp->state)); + ASSERT(!ifp->state); + break; + } + + if (ret < 0) { + ifp->set_multicast = FALSE; + if (ifp->net) { + free_netdev(ifp->net); + ifp->net = NULL; + } + dhd->iflist[ifp->idx] = NULL; +#ifdef SOFTAP + flags = dhd_os_spin_lock(&dhd->pub); + if (ifp->net == ap_net_dev) + ap_net_dev = NULL; /* NULL SOFTAP global wl0.1 as well */ + dhd_os_spin_unlock(&dhd->pub, flags); +#endif /* SOFTAP */ + MFREE(dhd->pub.osh, ifp, sizeof(*ifp)); + } +} + +static int +_dhd_sysioc_thread(void *data) +{ + tsk_ctl_t *tsk = (tsk_ctl_t *)data; + dhd_info_t *dhd = (dhd_info_t *)tsk->parent; + + + int i; +#ifdef SOFTAP + bool in_ap = FALSE; + unsigned long flags; +#endif + + DAEMONIZE("dhd_sysioc"); + + complete(&tsk->completed); + + while (down_interruptible(&tsk->sema) == 0) { + + SMP_RD_BARRIER_DEPENDS(); + if (tsk->terminated) { + break; + } + + dhd_net_if_lock_local(dhd); + DHD_OS_WAKE_LOCK(&dhd->pub); + + for (i = 0; i < DHD_MAX_IFS; i++) { + if (dhd->iflist[i]) { + DHD_TRACE(("%s: interface %d\n", __FUNCTION__, i)); +#ifdef SOFTAP + flags = dhd_os_spin_lock(&dhd->pub); + in_ap = (ap_net_dev != NULL); + dhd_os_spin_unlock(&dhd->pub, flags); +#endif /* SOFTAP */ + if (dhd->iflist[i] && dhd->iflist[i]->state) + dhd_op_if(dhd->iflist[i]); + + if (dhd->iflist[i] == NULL) { + DHD_TRACE(("\n\n %s: interface %d just been removed," + "!\n\n", __FUNCTION__, i)); + continue; + } +#ifdef SOFTAP + if (in_ap && dhd->set_macaddress == i+1) { + DHD_TRACE(("attempt to set MAC for %s in AP Mode," + "blocked. \n", dhd->iflist[i]->net->name)); + dhd->set_macaddress = 0; + continue; + } + + if (in_ap && dhd->iflist[i]->set_multicast) { + DHD_TRACE(("attempt to set MULTICAST list for %s" + "in AP Mode, blocked. \n", dhd->iflist[i]->net->name)); + dhd->iflist[i]->set_multicast = FALSE; + continue; + } +#endif /* SOFTAP */ + if (dhd->pub.up == 0) + continue; + if (dhd->iflist[i]->set_multicast) { + dhd->iflist[i]->set_multicast = FALSE; + _dhd_set_multicast_list(dhd, i); + } + if (dhd->set_macaddress == i+1) { + dhd->set_macaddress = 0; + if (_dhd_set_mac_address(dhd, i, &dhd->macvalue) == 0) { + DHD_INFO(( + "dhd_sysioc_thread: MACID is overwritten\n")); + } else { + DHD_ERROR(( + "dhd_sysioc_thread: _dhd_set_mac_address() failed\n")); + } + } + } + } + + DHD_OS_WAKE_UNLOCK(&dhd->pub); + dhd_net_if_unlock_local(dhd); + } + DHD_TRACE(("%s: stopped\n", __FUNCTION__)); + complete_and_exit(&tsk->completed, 0); +} + +static int +dhd_set_mac_address(struct net_device *dev, void *addr) +{ + int ret = 0; + + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + struct sockaddr *sa = (struct sockaddr *)addr; + int ifidx; + + ifidx = dhd_net2idx(dhd, dev); + if (ifidx == DHD_BAD_IF) + return -1; + + ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0); + memcpy(&dhd->macvalue, sa->sa_data, ETHER_ADDR_LEN); + dhd->set_macaddress = ifidx+1; + up(&dhd->thr_sysioc_ctl.sema); + + return ret; +} + +static void +dhd_set_multicast_list(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int ifidx; + + ifidx = dhd_net2idx(dhd, dev); + if (ifidx == DHD_BAD_IF) + return; + + ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0); + dhd->iflist[ifidx]->set_multicast = TRUE; + up(&dhd->thr_sysioc_ctl.sema); +} + +#ifdef PROP_TXSTATUS +int +dhd_os_wlfc_block(dhd_pub_t *pub) +{ + dhd_info_t *di = (dhd_info_t *)(pub->info); + ASSERT(di != NULL); + spin_lock_bh(&di->wlfc_spinlock); + return 1; +} + +int +dhd_os_wlfc_unblock(dhd_pub_t *pub) +{ + dhd_info_t *di = (dhd_info_t *)(pub->info); + + ASSERT(di != NULL); + spin_unlock_bh(&di->wlfc_spinlock); + return 1; +} + +const uint8 wme_fifo2ac[] = { 0, 1, 2, 3, 1, 1 }; +uint8 prio2fifo[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; +#define WME_PRIO2AC(prio) wme_fifo2ac[prio2fifo[(prio)]] + +#endif /* PROP_TXSTATUS */ +int +dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf) +{ + int ret; + dhd_info_t *dhd = (dhd_info_t *)(dhdp->info); + struct ether_header *eh = NULL; + + /* Reject if down */ + if (!dhdp->up || (dhdp->busstate == DHD_BUS_DOWN)) { + /* free the packet here since the caller won't */ + PKTFREE(dhdp->osh, pktbuf, TRUE); + return -ENODEV; + } + + /* Update multicast statistic */ + if (PKTLEN(dhdp->osh, pktbuf) >= ETHER_HDR_LEN) { + uint8 *pktdata = (uint8 *)PKTDATA(dhdp->osh, pktbuf); + eh = (struct ether_header *)pktdata; + + if (ETHER_ISMULTI(eh->ether_dhost)) + dhdp->tx_multicast++; + if (ntoh16(eh->ether_type) == ETHER_TYPE_802_1X) + atomic_inc(&dhd->pend_8021x_cnt); + } else { + PKTFREE(dhd->pub.osh, pktbuf, TRUE); + return BCME_ERROR; + } + + /* Look into the packet and update the packet priority */ +#ifndef PKTPRIO_OVERRIDE + if (PKTPRIO(pktbuf) == 0) +#endif + pktsetprio(pktbuf, FALSE); + +#ifdef PROP_TXSTATUS + if (dhdp->wlfc_state) { + /* store the interface ID */ + DHD_PKTTAG_SETIF(PKTTAG(pktbuf), ifidx); + + /* store destination MAC in the tag as well */ + DHD_PKTTAG_SETDSTN(PKTTAG(pktbuf), eh->ether_dhost); + + /* decide which FIFO this packet belongs to */ + if (ETHER_ISMULTI(eh->ether_dhost)) + /* one additional queue index (highest AC + 1) is used for bc/mc queue */ + DHD_PKTTAG_SETFIFO(PKTTAG(pktbuf), AC_COUNT); + else + DHD_PKTTAG_SETFIFO(PKTTAG(pktbuf), WME_PRIO2AC(PKTPRIO(pktbuf))); + } else +#endif /* PROP_TXSTATUS */ + /* If the protocol uses a data header, apply it */ + dhd_prot_hdrpush(dhdp, ifidx, pktbuf); + + /* Use bus module to send data frame */ +#ifdef WLMEDIA_HTSF + dhd_htsf_addtxts(dhdp, pktbuf); +#endif +#ifdef PROP_TXSTATUS + dhd_os_wlfc_block(dhdp); + if (dhdp->wlfc_state && ((athost_wl_status_info_t*)dhdp->wlfc_state)->proptxstatus_mode + != WLFC_FCMODE_NONE) { + ret = dhd_wlfc_enque_sendq(dhdp->wlfc_state, DHD_PKTTAG_FIFO(PKTTAG(pktbuf)), + pktbuf); + dhd_wlfc_commit_packets(dhdp->wlfc_state, (f_commitpkt_t)dhd_bus_txdata, + dhdp->bus); + if (((athost_wl_status_info_t*)dhdp->wlfc_state)->toggle_host_if) { + ((athost_wl_status_info_t*)dhdp->wlfc_state)->toggle_host_if = 0; + } + dhd_os_wlfc_unblock(dhdp); + } + else { + dhd_os_wlfc_unblock(dhdp); + /* non-proptxstatus way */ + ret = dhd_bus_txdata(dhdp->bus, pktbuf); + } +#else + ret = dhd_bus_txdata(dhdp->bus, pktbuf); +#endif /* PROP_TXSTATUS */ + + return ret; +} + +int +dhd_start_xmit(struct sk_buff *skb, struct net_device *net) +{ + int ret; + void *pktbuf; + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); + int ifidx; +#ifdef WLMEDIA_HTSF + uint8 htsfdlystat_sz = dhd->pub.htsfdlystat_sz; +#else + uint8 htsfdlystat_sz = 0; +#endif + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + DHD_OS_WAKE_LOCK(&dhd->pub); + + /* Reject if down */ + if (dhd->pub.busstate == DHD_BUS_DOWN || dhd->pub.hang_was_sent) { + DHD_ERROR(("%s: xmit rejected pub.up=%d busstate=%d \n", + __FUNCTION__, dhd->pub.up, dhd->pub.busstate)); + netif_stop_queue(net); + /* Send Event when bus down detected during data session */ + if (dhd->pub.up) { + DHD_ERROR(("%s: Event HANG sent up\n", __FUNCTION__)); + net_os_send_hang_message(net); + } + DHD_OS_WAKE_UNLOCK(&dhd->pub); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)) + return -ENODEV; +#else + return NETDEV_TX_BUSY; +#endif + } + + ifidx = dhd_net2idx(dhd, net); + if (ifidx == DHD_BAD_IF) { + DHD_ERROR(("%s: bad ifidx %d\n", __FUNCTION__, ifidx)); + netif_stop_queue(net); + DHD_OS_WAKE_UNLOCK(&dhd->pub); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)) + return -ENODEV; +#else + return NETDEV_TX_BUSY; +#endif + } + + /* Make sure there's enough room for any header */ + + if (skb_headroom(skb) < dhd->pub.hdrlen + htsfdlystat_sz) { + struct sk_buff *skb2; + + DHD_INFO(("%s: insufficient headroom\n", + dhd_ifname(&dhd->pub, ifidx))); + dhd->pub.tx_realloc++; + + skb2 = skb_realloc_headroom(skb, dhd->pub.hdrlen + htsfdlystat_sz); + + dev_kfree_skb(skb); + if ((skb = skb2) == NULL) { + DHD_ERROR(("%s: skb_realloc_headroom failed\n", + dhd_ifname(&dhd->pub, ifidx))); + ret = -ENOMEM; + goto done; + } + } + + /* Convert to packet */ + if (!(pktbuf = PKTFRMNATIVE(dhd->pub.osh, skb))) { + DHD_ERROR(("%s: PKTFRMNATIVE failed\n", + dhd_ifname(&dhd->pub, ifidx))); + dev_kfree_skb_any(skb); + ret = -ENOMEM; + goto done; + } +#ifdef WLMEDIA_HTSF + if (htsfdlystat_sz && PKTLEN(dhd->pub.osh, pktbuf) >= ETHER_ADDR_LEN) { + uint8 *pktdata = (uint8 *)PKTDATA(dhd->pub.osh, pktbuf); + struct ether_header *eh = (struct ether_header *)pktdata; + + if (!ETHER_ISMULTI(eh->ether_dhost) && + (ntoh16(eh->ether_type) == ETHER_TYPE_IP)) { + eh->ether_type = hton16(ETHER_TYPE_BRCM_PKTDLYSTATS); + } + } +#endif + + ret = dhd_sendpkt(&dhd->pub, ifidx, pktbuf); + + +done: + if (ret) + dhd->pub.dstats.tx_dropped++; + else + dhd->pub.tx_packets++; + + DHD_OS_WAKE_UNLOCK(&dhd->pub); + + /* Return ok: we always eat the packet */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)) + return 0; +#else + return NETDEV_TX_OK; +#endif +} + +void +dhd_txflowcontrol(dhd_pub_t *dhdp, int ifidx, bool state) +{ + struct net_device *net; + dhd_info_t *dhd = dhdp->info; + int i; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + ASSERT(dhd); + + if (ifidx == ALL_INTERFACES) { + /* Flow control on all active interfaces */ + dhdp->txoff = state; + for (i = 0; i < DHD_MAX_IFS; i++) { + if (dhd->iflist[i]) { + net = dhd->iflist[i]->net; + if (state == ON) + netif_stop_queue(net); + else + netif_wake_queue(net); + } + } + } + else { + if (dhd->iflist[ifidx]) { + net = dhd->iflist[ifidx]->net; + if (state == ON) + netif_stop_queue(net); + else + netif_wake_queue(net); + } + } +} + +#ifdef DHD_RX_DUMP +typedef struct { + uint16 type; + const char *str; +} PKTTYPE_INFO; + +static const PKTTYPE_INFO packet_type_info[] = +{ + { ETHER_TYPE_IP, "IP" }, + { ETHER_TYPE_ARP, "ARP" }, + { ETHER_TYPE_BRCM, "BRCM" }, + { ETHER_TYPE_802_1X, "802.1X" }, + { ETHER_TYPE_WAI, "WAPI" }, + { 0, ""} +}; + +static const char *_get_packet_type_str(uint16 type) +{ + int i; + int n = sizeof(packet_type_info)/sizeof(packet_type_info[1]) - 1; + + for (i = 0; i < n; i++) { + if (packet_type_info[i].type == type) + return packet_type_info[i].str; + } + + return packet_type_info[n].str; +} +#endif /* DHD_RX_DUMP */ + +void +dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) +{ + dhd_info_t *dhd = (dhd_info_t *)dhdp->info; + struct sk_buff *skb; + uchar *eth; + uint len; + void *data, *pnext = NULL; + int i; + dhd_if_t *ifp; + wl_event_msg_t event; + int tout_rx = 0; + int tout_ctrl = 0; + +#ifdef DHD_RX_DUMP +#ifdef DHD_RX_FULL_DUMP + int k; +#endif /* DHD_RX_FULL_DUMP */ + char *dump_data; + uint16 protocol; +#endif /* DHD_RX_DUMP */ + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + for (i = 0; pktbuf && i < numpkt; i++, pktbuf = pnext) { +#ifdef WLBTAMP + struct ether_header *eh; + struct dot11_llc_snap_header *lsh; +#endif + + ifp = dhd->iflist[ifidx]; + if (ifp == NULL) { + DHD_ERROR(("%s: ifp is NULL. drop packet\n", + __FUNCTION__)); + pnext = PKTNEXT(dhdp->osh, pktbuf); + PKTSETNEXT(wl->sh.osh, pktbuf, NULL); + PKTFREE(dhdp->osh, pktbuf, TRUE); + continue; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) + /* Dropping packets before registering net device to avoid kernel panic */ +#ifndef PROP_TXSTATUS_VSDB + if (!ifp->net || ifp->net->reg_state != NETREG_REGISTERED) { +#else + if (!ifp->net || ifp->net->reg_state != NETREG_REGISTERED || !dhd->pub.up) { +#endif /* PROP_TXSTATUS_VSDB */ + DHD_ERROR(("%s: net device is NOT registered yet. drop packet\n", + __FUNCTION__)); + pnext = PKTNEXT(dhdp->osh, pktbuf); + PKTSETNEXT(wl->sh.osh, pktbuf, NULL); + PKTFREE(dhdp->osh, pktbuf, TRUE); + continue; + } +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */ + + pnext = PKTNEXT(dhdp->osh, pktbuf); + PKTSETNEXT(wl->sh.osh, pktbuf, NULL); + +#ifdef WLBTAMP + eh = (struct ether_header *)PKTDATA(wl->sh.osh, pktbuf); + lsh = (struct dot11_llc_snap_header *)&eh[1]; + + if ((ntoh16(eh->ether_type) < ETHER_TYPE_MIN) && + (PKTLEN(wl->sh.osh, pktbuf) >= RFC1042_HDR_LEN) && + bcmp(lsh, BT_SIG_SNAP_MPROT, DOT11_LLC_SNAP_HDR_LEN - 2) == 0 && + lsh->type == HTON16(BTA_PROT_L2CAP)) { + amp_hci_ACL_data_t *ACL_data = (amp_hci_ACL_data_t *) + ((uint8 *)eh + RFC1042_HDR_LEN); + ACL_data = NULL; + } +#endif /* WLBTAMP */ + +#ifdef PROP_TXSTATUS + if (dhdp->wlfc_state && PKTLEN(wl->sh.osh, pktbuf) == 0) { + /* WLFC may send header only packet when + there is an urgent message but no packet to + piggy-back on + */ + ((athost_wl_status_info_t*)dhdp->wlfc_state)->stats.wlfc_header_only_pkt++; + PKTFREE(dhdp->osh, pktbuf, TRUE); + continue; + } +#endif + + skb = PKTTONATIVE(dhdp->osh, pktbuf); + + /* Get the protocol, maintain skb around eth_type_trans() + * The main reason for this hack is for the limitation of + * Linux 2.4 where 'eth_type_trans' uses the 'net->hard_header_len' + * to perform skb_pull inside vs ETH_HLEN. Since to avoid + * coping of the packet coming from the network stack to add + * BDC, Hardware header etc, during network interface registration + * we set the 'net->hard_header_len' to ETH_HLEN + extra space required + * for BDC, Hardware header etc. and not just the ETH_HLEN + */ + eth = skb->data; + len = skb->len; + +#ifdef DHD_RX_DUMP + dump_data = skb->data; + protocol = (dump_data[12] << 8) | dump_data[13]; + DHD_ERROR(("RX DUMP - %s\n", _get_packet_type_str(protocol))); + +#ifdef DHD_RX_FULL_DUMP + if (protocol != ETHER_TYPE_BRCM) { + for (k = 0; k < skb->len; k++) { + DHD_ERROR(("%02X ", dump_data[k])); + if ((k & 15) == 15) + DHD_ERROR(("\n")); + } + DHD_ERROR(("\n")); + } +#endif /* DHD_RX_FULL_DUMP */ + + if (protocol != ETHER_TYPE_BRCM) { + if (dump_data[0] == 0xFF) { + DHD_ERROR(("%s: BROADCAST\n", __FUNCTION__)); + + if ((dump_data[12] == 8) && + (dump_data[13] == 6)) { + DHD_ERROR(("%s: ARP %d\n", + __FUNCTION__, dump_data[0x15])); + } + } else if (dump_data[0] & 1) { + DHD_ERROR(("%s: MULTICAST: " MACDBG "\n", + __FUNCTION__, MAC2STRDBG(dump_data))); + } + + if (protocol == ETHER_TYPE_802_1X) { + DHD_ERROR(("ETHER_TYPE_802_1X: " + "ver %d, type %d, replay %d\n", + dump_data[14], dump_data[15], + dump_data[30])); + } + } + +#endif /* DHD_RX_DUMP */ + + ifp = dhd->iflist[ifidx]; + if (ifp == NULL) + ifp = dhd->iflist[0]; + + ASSERT(ifp); + skb->dev = ifp->net; + skb->protocol = eth_type_trans(skb, skb->dev); + + if (skb->pkt_type == PACKET_MULTICAST) { + dhd->pub.rx_multicast++; + } + + skb->data = eth; + skb->len = len; + +#ifdef WLMEDIA_HTSF + dhd_htsf_addrxts(dhdp, pktbuf); +#endif + /* Strip header, count, deliver upward */ + skb_pull(skb, ETH_HLEN); + + /* Process special event packets and then discard them */ + if (ntoh16(skb->protocol) == ETHER_TYPE_BRCM) { + dhd_wl_host_event(dhd, &ifidx, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) + skb->mac_header, +#else + skb->mac.raw, +#endif + &event, + &data); + + wl_event_to_host_order(&event); + if (!tout_ctrl) + tout_ctrl = DHD_PACKET_TIMEOUT_MS; +#ifdef WLBTAMP + if (event.event_type == WLC_E_BTA_HCI_EVENT) { + dhd_bta_doevt(dhdp, data, event.datalen); + } +#endif /* WLBTAMP */ + +#if defined(PNO_SUPPORT) + if (event.event_type == WLC_E_PFN_NET_FOUND) { + /* enforce custom wake lock to garantee that Kernel not suspended */ + tout_ctrl = CUSTOM_PNO_EVENT_LOCK_xTIME * DHD_PACKET_TIMEOUT_MS; + } +#endif /* PNO_SUPPORT */ + +#ifdef DHD_DONOT_FORWARD_BCMEVENT_AS_NETWORK_PKT + PKTFREE(dhdp->osh, pktbuf, TRUE); + continue; +#endif + } else { + tout_rx = DHD_PACKET_TIMEOUT_MS; + } + + ASSERT(ifidx < DHD_MAX_IFS && dhd->iflist[ifidx]); + if (dhd->iflist[ifidx] && !dhd->iflist[ifidx]->state) + ifp = dhd->iflist[ifidx]; + + if (ifp->net) + ifp->net->last_rx = jiffies; + + dhdp->dstats.rx_bytes += skb->len; + dhdp->rx_packets++; /* Local count */ + + if (in_interrupt()) { + netif_rx(skb); + } else { + /* If the receive is not processed inside an ISR, + * the softirqd must be woken explicitly to service + * the NET_RX_SOFTIRQ. In 2.6 kernels, this is handled + * by netif_rx_ni(), but in earlier kernels, we need + * to do it manually. + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) + netif_rx_ni(skb); +#else + ulong flags; + netif_rx(skb); + local_irq_save(flags); + RAISE_RX_SOFTIRQ(); + local_irq_restore(flags); +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */ + } + } + + DHD_OS_WAKE_LOCK_RX_TIMEOUT_ENABLE(dhdp, tout_rx); + DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(dhdp, tout_ctrl); +} + +void +dhd_event(struct dhd_info *dhd, char *evpkt, int evlen, int ifidx) +{ + /* Linux version has nothing to do */ + return; +} + +void +dhd_txcomplete(dhd_pub_t *dhdp, void *txp, bool success) +{ + dhd_info_t *dhd = (dhd_info_t *)(dhdp->info); + struct ether_header *eh; + uint16 type; +#ifdef WLBTAMP + uint len; +#endif + + dhd_prot_hdrpull(dhdp, NULL, txp, NULL, NULL); + + eh = (struct ether_header *)PKTDATA(dhdp->osh, txp); + type = ntoh16(eh->ether_type); + + if (type == ETHER_TYPE_802_1X) + atomic_dec(&dhd->pend_8021x_cnt); + +#ifdef WLBTAMP + /* Crack open the packet and check to see if it is BT HCI ACL data packet. + * If yes generate packet completion event. + */ + len = PKTLEN(dhdp->osh, txp); + + /* Generate ACL data tx completion event locally to avoid SDIO bus transaction */ + if ((type < ETHER_TYPE_MIN) && (len >= RFC1042_HDR_LEN)) { + struct dot11_llc_snap_header *lsh = (struct dot11_llc_snap_header *)&eh[1]; + + if (bcmp(lsh, BT_SIG_SNAP_MPROT, DOT11_LLC_SNAP_HDR_LEN - 2) == 0 && + ntoh16(lsh->type) == BTA_PROT_L2CAP) { + + dhd_bta_tx_hcidata_complete(dhdp, txp, success); + } + } +#endif /* WLBTAMP */ +} + +static struct net_device_stats * +dhd_get_stats(struct net_device *net) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); + dhd_if_t *ifp; + int ifidx; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + ifidx = dhd_net2idx(dhd, net); + if (ifidx == DHD_BAD_IF) { + DHD_ERROR(("%s: BAD_IF\n", __FUNCTION__)); + return NULL; + } + + ifp = dhd->iflist[ifidx]; + ASSERT(dhd && ifp); + + if (dhd->pub.up) { + /* Use the protocol to get dongle stats */ + dhd_prot_dstats(&dhd->pub); + } + + /* Copy dongle stats to net device stats */ + ifp->stats.rx_packets = dhd->pub.dstats.rx_packets; + ifp->stats.tx_packets = dhd->pub.dstats.tx_packets; + ifp->stats.rx_bytes = dhd->pub.dstats.rx_bytes; + ifp->stats.tx_bytes = dhd->pub.dstats.tx_bytes; + ifp->stats.rx_errors = dhd->pub.dstats.rx_errors; + ifp->stats.tx_errors = dhd->pub.dstats.tx_errors; + ifp->stats.rx_dropped = dhd->pub.dstats.rx_dropped; + ifp->stats.tx_dropped = dhd->pub.dstats.tx_dropped; + ifp->stats.multicast = dhd->pub.dstats.multicast; + + return &ifp->stats; +} + +#ifdef DHDTHREAD +static int +dhd_watchdog_thread(void *data) +{ + tsk_ctl_t *tsk = (tsk_ctl_t *)data; + dhd_info_t *dhd = (dhd_info_t *)tsk->parent; + /* This thread doesn't need any user-level access, + * so get rid of all our resources + */ + if (dhd_watchdog_prio > 0) { + struct sched_param param; + param.sched_priority = (dhd_watchdog_prio < MAX_RT_PRIO)? + dhd_watchdog_prio:(MAX_RT_PRIO-1); + setScheduler(current, SCHED_FIFO, ¶m); + } + + DAEMONIZE("dhd_watchdog"); + + /* Run until signal received */ + complete(&tsk->completed); + + while (1) + if (down_interruptible (&tsk->sema) == 0) { + unsigned long flags; + unsigned long jiffies_at_start = jiffies; + unsigned long time_lapse; + + SMP_RD_BARRIER_DEPENDS(); + if (tsk->terminated) { + break; + } + + dhd_os_sdlock(&dhd->pub); + if (dhd->pub.dongle_reset == FALSE) { + DHD_TIMER(("%s:\n", __FUNCTION__)); + + /* Call the bus module watchdog */ + dhd_bus_watchdog(&dhd->pub); + + flags = dhd_os_spin_lock(&dhd->pub); + /* Count the tick for reference */ + dhd->pub.tickcnt++; + time_lapse = jiffies - jiffies_at_start; + + /* Reschedule the watchdog */ + if (dhd->wd_timer_valid) + mod_timer(&dhd->timer, + jiffies + + msecs_to_jiffies(dhd_watchdog_ms) - + min(msecs_to_jiffies(dhd_watchdog_ms), time_lapse)); + dhd_os_spin_unlock(&dhd->pub, flags); + } + dhd_os_sdunlock(&dhd->pub); + } else { + break; + } + + complete_and_exit(&tsk->completed, 0); +} +#endif /* DHDTHREAD */ + +static void dhd_watchdog(ulong data) +{ + dhd_info_t *dhd = (dhd_info_t *)data; + unsigned long flags; + + if (dhd->pub.dongle_reset) { + return; + } + +#ifdef DHDTHREAD + if (dhd->thr_wdt_ctl.thr_pid >= 0) { + up(&dhd->thr_wdt_ctl.sema); + return; + } +#endif /* DHDTHREAD */ + + dhd_os_sdlock(&dhd->pub); + /* Call the bus module watchdog */ + dhd_bus_watchdog(&dhd->pub); + + flags = dhd_os_spin_lock(&dhd->pub); + /* Count the tick for reference */ + dhd->pub.tickcnt++; + + /* Reschedule the watchdog */ + if (dhd->wd_timer_valid) + mod_timer(&dhd->timer, jiffies + msecs_to_jiffies(dhd_watchdog_ms)); + dhd_os_spin_unlock(&dhd->pub, flags); + dhd_os_sdunlock(&dhd->pub); +} + +#ifdef DHDTHREAD +static int +dhd_dpc_thread(void *data) +{ + tsk_ctl_t *tsk = (tsk_ctl_t *)data; + dhd_info_t *dhd = (dhd_info_t *)tsk->parent; + + /* This thread doesn't need any user-level access, + * so get rid of all our resources + */ + if (dhd_dpc_prio > 0) + { + struct sched_param param; + param.sched_priority = (dhd_dpc_prio < MAX_RT_PRIO)?dhd_dpc_prio:(MAX_RT_PRIO-1); + setScheduler(current, SCHED_FIFO, ¶m); + } + + DAEMONIZE("dhd_dpc"); + /* DHD_OS_WAKE_LOCK is called in dhd_sched_dpc[dhd_linux.c] down below */ + + /* signal: thread has started */ + complete(&tsk->completed); + + /* Run until signal received */ + while (1) { + if (down_interruptible(&tsk->sema) == 0) { + + SMP_RD_BARRIER_DEPENDS(); + if (tsk->terminated) { + break; + } + + /* Call bus dpc unless it indicated down (then clean stop) */ + if (dhd->pub.busstate != DHD_BUS_DOWN) { + if (dhd_bus_dpc(dhd->pub.bus)) { + up(&tsk->sema); + } + else { + DHD_OS_WAKE_UNLOCK(&dhd->pub); + } + } else { + if (dhd->pub.up) + dhd_bus_stop(dhd->pub.bus, TRUE); + DHD_OS_WAKE_UNLOCK(&dhd->pub); + } + } + else + break; + } + + complete_and_exit(&tsk->completed, 0); +} +#endif /* DHDTHREAD */ + +static void +dhd_dpc(ulong data) +{ + dhd_info_t *dhd; + + dhd = (dhd_info_t *)data; + + /* this (tasklet) can be scheduled in dhd_sched_dpc[dhd_linux.c] + * down below , wake lock is set, + * the tasklet is initialized in dhd_attach() + */ + /* Call bus dpc unless it indicated down (then clean stop) */ + if (dhd->pub.busstate != DHD_BUS_DOWN) { + if (dhd_bus_dpc(dhd->pub.bus)) + tasklet_schedule(&dhd->tasklet); + else + DHD_OS_WAKE_UNLOCK(&dhd->pub); + } else { + dhd_bus_stop(dhd->pub.bus, TRUE); + DHD_OS_WAKE_UNLOCK(&dhd->pub); + } +} + +void +dhd_sched_dpc(dhd_pub_t *dhdp) +{ + dhd_info_t *dhd = (dhd_info_t *)dhdp->info; + + DHD_OS_WAKE_LOCK(dhdp); +#ifdef DHDTHREAD + if (dhd->thr_dpc_ctl.thr_pid >= 0) { + up(&dhd->thr_dpc_ctl.sema); + return; + } +#endif /* DHDTHREAD */ + + if (dhd->dhd_tasklet_create) + tasklet_schedule(&dhd->tasklet); +} + +#ifdef TOE +/* Retrieve current toe component enables, which are kept as a bitmap in toe_ol iovar */ +static int +dhd_toe_get(dhd_info_t *dhd, int ifidx, uint32 *toe_ol) +{ + wl_ioctl_t ioc; + char buf[32]; + int ret; + + memset(&ioc, 0, sizeof(ioc)); + + ioc.cmd = WLC_GET_VAR; + ioc.buf = buf; + ioc.len = (uint)sizeof(buf); + ioc.set = FALSE; + + strncpy(buf, "toe_ol", sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; + if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) { + /* Check for older dongle image that doesn't support toe_ol */ + if (ret == -EIO) { + DHD_ERROR(("%s: toe not supported by device\n", + dhd_ifname(&dhd->pub, ifidx))); + return -EOPNOTSUPP; + } + + DHD_INFO(("%s: could not get toe_ol: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret)); + return ret; + } + + memcpy(toe_ol, buf, sizeof(uint32)); + return 0; +} + +/* Set current toe component enables in toe_ol iovar, and set toe global enable iovar */ +static int +dhd_toe_set(dhd_info_t *dhd, int ifidx, uint32 toe_ol) +{ + wl_ioctl_t ioc; + char buf[32]; + int toe, ret; + + memset(&ioc, 0, sizeof(ioc)); + + ioc.cmd = WLC_SET_VAR; + ioc.buf = buf; + ioc.len = (uint)sizeof(buf); + ioc.set = TRUE; + + /* Set toe_ol as requested */ + + strncpy(buf, "toe_ol", sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; + memcpy(&buf[sizeof("toe_ol")], &toe_ol, sizeof(uint32)); + + if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) { + DHD_ERROR(("%s: could not set toe_ol: ret=%d\n", + dhd_ifname(&dhd->pub, ifidx), ret)); + return ret; + } + + /* Enable toe globally only if any components are enabled. */ + + toe = (toe_ol != 0); + + strcpy(buf, "toe"); + memcpy(&buf[sizeof("toe")], &toe, sizeof(uint32)); + + if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) { + DHD_ERROR(("%s: could not set toe: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret)); + return ret; + } + + return 0; +} +#endif /* TOE */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) +static void +dhd_ethtool_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); + + snprintf(info->driver, sizeof(info->driver), "wl"); + snprintf(info->version, sizeof(info->version), "%lu", dhd->pub.drv_version); +} + +struct ethtool_ops dhd_ethtool_ops = { + .get_drvinfo = dhd_ethtool_get_drvinfo +}; +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */ + + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) +static int +dhd_ethtool(dhd_info_t *dhd, void *uaddr) +{ + struct ethtool_drvinfo info; + char drvname[sizeof(info.driver)]; + uint32 cmd; +#ifdef TOE + struct ethtool_value edata; + uint32 toe_cmpnt, csum_dir; + int ret; +#endif + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + /* all ethtool calls start with a cmd word */ + if (copy_from_user(&cmd, uaddr, sizeof (uint32))) + return -EFAULT; + + switch (cmd) { + case ETHTOOL_GDRVINFO: + /* Copy out any request driver name */ + if (copy_from_user(&info, uaddr, sizeof(info))) + return -EFAULT; + strncpy(drvname, info.driver, sizeof(info.driver)); + drvname[sizeof(info.driver)-1] = '\0'; + + /* clear struct for return */ + memset(&info, 0, sizeof(info)); + info.cmd = cmd; + + /* if dhd requested, identify ourselves */ + if (strcmp(drvname, "?dhd") == 0) { + snprintf(info.driver, sizeof(info.driver), "dhd"); + strncpy(info.version, EPI_VERSION_STR, sizeof(info.version) - 1); + info.version[sizeof(info.version) - 1] = '\0'; + } + + /* otherwise, require dongle to be up */ + else if (!dhd->pub.up) { + DHD_ERROR(("%s: dongle is not up\n", __FUNCTION__)); + return -ENODEV; + } + + /* finally, report dongle driver type */ + else if (dhd->pub.iswl) + snprintf(info.driver, sizeof(info.driver), "wl"); + else + snprintf(info.driver, sizeof(info.driver), "xx"); + + snprintf(info.version, sizeof(info.version), "%lu", dhd->pub.drv_version); + if (copy_to_user(uaddr, &info, sizeof(info))) + return -EFAULT; + DHD_CTL(("%s: given %*s, returning %s\n", __FUNCTION__, + (int)sizeof(drvname), drvname, info.driver)); + break; + +#ifdef TOE + /* Get toe offload components from dongle */ + case ETHTOOL_GRXCSUM: + case ETHTOOL_GTXCSUM: + if ((ret = dhd_toe_get(dhd, 0, &toe_cmpnt)) < 0) + return ret; + + csum_dir = (cmd == ETHTOOL_GTXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL; + + edata.cmd = cmd; + edata.data = (toe_cmpnt & csum_dir) ? 1 : 0; + + if (copy_to_user(uaddr, &edata, sizeof(edata))) + return -EFAULT; + break; + + /* Set toe offload components in dongle */ + case ETHTOOL_SRXCSUM: + case ETHTOOL_STXCSUM: + if (copy_from_user(&edata, uaddr, sizeof(edata))) + return -EFAULT; + + /* Read the current settings, update and write back */ + if ((ret = dhd_toe_get(dhd, 0, &toe_cmpnt)) < 0) + return ret; + + csum_dir = (cmd == ETHTOOL_STXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL; + + if (edata.data != 0) + toe_cmpnt |= csum_dir; + else + toe_cmpnt &= ~csum_dir; + + if ((ret = dhd_toe_set(dhd, 0, toe_cmpnt)) < 0) + return ret; + + /* If setting TX checksum mode, tell Linux the new mode */ + if (cmd == ETHTOOL_STXCSUM) { + if (edata.data) + dhd->iflist[0]->net->features |= NETIF_F_IP_CSUM; + else + dhd->iflist[0]->net->features &= ~NETIF_F_IP_CSUM; + } + + break; +#endif /* TOE */ + + default: + return -EOPNOTSUPP; + } + + return 0; +} +#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) */ + +static bool dhd_check_hang(struct net_device *net, dhd_pub_t *dhdp, int error) +{ + dhd_info_t * dhd; + + if (!dhdp) + return FALSE; + + dhd = (dhd_info_t *)dhdp->info; + if (dhd->thr_sysioc_ctl.thr_pid < 0) { + DHD_ERROR(("%s : skipped due to negative pid - unloading?\n", __FUNCTION__)); + return FALSE; + } + + if ((error == -ETIMEDOUT) || (error == -EREMOTEIO) || + ((dhdp->busstate == DHD_BUS_DOWN) && (!dhdp->dongle_reset))) { + DHD_ERROR(("%s: Event HANG send up due to re=%d te=%d e=%d s=%d\n", __FUNCTION__, + dhdp->rxcnt_timeout, dhdp->txcnt_timeout, error, dhdp->busstate)); + net_os_send_hang_message(net); + return TRUE; + } + return FALSE; +} + +static int +dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); + dhd_ioctl_t ioc; + int bcmerror = 0; + int buflen = 0; + void *buf = NULL; + uint driver = 0; + int ifidx; + int ret; + + DHD_OS_WAKE_LOCK(&dhd->pub); + + /* send to dongle only if we are not waiting for reload already */ + if (dhd->pub.hang_was_sent) { + DHD_ERROR(("%s: HANG was sent up earlier\n", __FUNCTION__)); + DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(&dhd->pub, DHD_EVENT_TIMEOUT_MS); + DHD_OS_WAKE_UNLOCK(&dhd->pub); + return OSL_ERROR(BCME_DONGLE_DOWN); + } + + ifidx = dhd_net2idx(dhd, net); + DHD_TRACE(("%s: ifidx %d, cmd 0x%04x\n", __FUNCTION__, ifidx, cmd)); + + if (ifidx == DHD_BAD_IF) { + DHD_ERROR(("%s: BAD IF\n", __FUNCTION__)); + DHD_OS_WAKE_UNLOCK(&dhd->pub); + return -1; + } + +#if defined(CONFIG_WIRELESS_EXT) + /* linux wireless extensions */ + if ((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST)) { + /* may recurse, do NOT lock */ + ret = wl_iw_ioctl(net, ifr, cmd); + DHD_OS_WAKE_UNLOCK(&dhd->pub); + return ret; + } +#endif /* defined(CONFIG_WIRELESS_EXT) */ + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) + if (cmd == SIOCETHTOOL) { + ret = dhd_ethtool(dhd, (void*)ifr->ifr_data); + DHD_OS_WAKE_UNLOCK(&dhd->pub); + return ret; + } +#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) */ + + if (cmd == SIOCDEVPRIVATE+1) { + ret = wl_android_priv_cmd(net, ifr, cmd); + dhd_check_hang(net, &dhd->pub, ret); + DHD_OS_WAKE_UNLOCK(&dhd->pub); + return ret; + } + + if (cmd != SIOCDEVPRIVATE) { + DHD_OS_WAKE_UNLOCK(&dhd->pub); + return -EOPNOTSUPP; + } + + memset(&ioc, 0, sizeof(ioc)); + + /* Copy the ioc control structure part of ioctl request */ + if (copy_from_user(&ioc, ifr->ifr_data, sizeof(wl_ioctl_t))) { + bcmerror = BCME_BADADDR; + goto done; + } + + /* Copy out any buffer passed */ + if (ioc.buf) { + if (ioc.len == 0) { + DHD_TRACE(("%s: ioc.len=0, returns BCME_BADARG \n", __FUNCTION__)); + bcmerror = BCME_BADARG; + goto done; + } + buflen = MIN(ioc.len, DHD_IOCTL_MAXLEN); + /* optimization for direct ioctl calls from kernel */ + /* + if (segment_eq(get_fs(), KERNEL_DS)) { + buf = ioc.buf; + } else { + */ + { + if (!(buf = (char*)MALLOC(dhd->pub.osh, buflen))) { + bcmerror = BCME_NOMEM; + goto done; + } + if (copy_from_user(buf, ioc.buf, buflen)) { + bcmerror = BCME_BADADDR; + goto done; + } + } + } + + /* To differentiate between wl and dhd read 4 more byes */ + if ((copy_from_user(&driver, (char *)ifr->ifr_data + sizeof(wl_ioctl_t), + sizeof(uint)) != 0)) { + bcmerror = BCME_BADADDR; + goto done; + } + + if (!capable(CAP_NET_ADMIN)) { + bcmerror = BCME_EPERM; + goto done; + } + + /* check for local dhd ioctl and handle it */ + if (driver == DHD_IOCTL_MAGIC) { + bcmerror = dhd_ioctl((void *)&dhd->pub, &ioc, buf, buflen); + if (bcmerror) + dhd->pub.bcmerror = bcmerror; + goto done; + } + + /* send to dongle (must be up, and wl). */ + if (dhd->pub.busstate != DHD_BUS_DATA) { + bcmerror = BCME_DONGLE_DOWN; + goto done; + } + + if (!dhd->pub.iswl) { + bcmerror = BCME_DONGLE_DOWN; + goto done; + } + + /* + * Flush the TX queue if required for proper message serialization: + * Intercept WLC_SET_KEY IOCTL - serialize M4 send and set key IOCTL to + * prevent M4 encryption and + * intercept WLC_DISASSOC IOCTL - serialize WPS-DONE and WLC_DISASSOC IOCTL to + * prevent disassoc frame being sent before WPS-DONE frame. + */ + if (ioc.cmd == WLC_SET_KEY || + (ioc.cmd == WLC_SET_VAR && ioc.buf != NULL && + strncmp("wsec_key", ioc.buf, 9) == 0) || + (ioc.cmd == WLC_SET_VAR && ioc.buf != NULL && + strncmp("bsscfg:wsec_key", ioc.buf, 15) == 0) || + ioc.cmd == WLC_DISASSOC) + dhd_wait_pend8021x(net); + +#ifdef WLMEDIA_HTSF + if (ioc.buf) { + /* short cut wl ioctl calls here */ + if (strcmp("htsf", ioc.buf) == 0) { + dhd_ioctl_htsf_get(dhd, 0); + return BCME_OK; + } + + if (strcmp("htsflate", ioc.buf) == 0) { + if (ioc.set) { + memset(ts, 0, sizeof(tstamp_t)*TSMAX); + memset(&maxdelayts, 0, sizeof(tstamp_t)); + maxdelay = 0; + tspktcnt = 0; + maxdelaypktno = 0; + memset(&vi_d1.bin, 0, sizeof(uint32)*NUMBIN); + memset(&vi_d2.bin, 0, sizeof(uint32)*NUMBIN); + memset(&vi_d3.bin, 0, sizeof(uint32)*NUMBIN); + memset(&vi_d4.bin, 0, sizeof(uint32)*NUMBIN); + } else { + dhd_dump_latency(); + } + return BCME_OK; + } + if (strcmp("htsfclear", ioc.buf) == 0) { + memset(&vi_d1.bin, 0, sizeof(uint32)*NUMBIN); + memset(&vi_d2.bin, 0, sizeof(uint32)*NUMBIN); + memset(&vi_d3.bin, 0, sizeof(uint32)*NUMBIN); + memset(&vi_d4.bin, 0, sizeof(uint32)*NUMBIN); + htsf_seqnum = 0; + return BCME_OK; + } + if (strcmp("htsfhis", ioc.buf) == 0) { + dhd_dump_htsfhisto(&vi_d1, "H to D"); + dhd_dump_htsfhisto(&vi_d2, "D to D"); + dhd_dump_htsfhisto(&vi_d3, "D to H"); + dhd_dump_htsfhisto(&vi_d4, "H to H"); + return BCME_OK; + } + if (strcmp("tsport", ioc.buf) == 0) { + if (ioc.set) { + memcpy(&tsport, ioc.buf + 7, 4); + } else { + DHD_ERROR(("current timestamp port: %d \n", tsport)); + } + return BCME_OK; + } + } +#endif /* WLMEDIA_HTSF */ + + if ((ioc.cmd == WLC_SET_VAR || ioc.cmd == WLC_GET_VAR) && + ioc.buf != NULL && strncmp("rpc_", ioc.buf, 4) == 0) { +#ifdef BCM_FD_AGGR + bcmerror = dhd_fdaggr_ioctl(&dhd->pub, ifidx, (wl_ioctl_t *)&ioc, buf, buflen); +#else + bcmerror = BCME_UNSUPPORTED; +#endif + goto done; + } + bcmerror = dhd_wl_ioctl(&dhd->pub, ifidx, (wl_ioctl_t *)&ioc, buf, buflen); + +done: + dhd_check_hang(net, &dhd->pub, bcmerror); + + if (!bcmerror && buf && ioc.buf) { + if (copy_to_user(ioc.buf, buf, buflen)) + bcmerror = -EFAULT; + } + + if (buf) + MFREE(dhd->pub.osh, buf, buflen); + + DHD_OS_WAKE_UNLOCK(&dhd->pub); + + return OSL_ERROR(bcmerror); +} + +#ifdef WL_CFG80211 +static int +dhd_cleanup_virt_ifaces(dhd_info_t *dhd) +{ + int i = 1; /* Leave ifidx 0 [Primary Interface] */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + int rollback_lock = FALSE; +#endif + + DHD_TRACE(("%s: Enter \n", __func__)); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + /* release lock for unregister_netdev */ + if (rtnl_is_locked()) { + rtnl_unlock(); + rollback_lock = TRUE; + } +#endif + + for (i = 1; i < DHD_MAX_IFS; i++) { + dhd_net_if_lock_local(dhd); + if (dhd->iflist[i]) { + DHD_TRACE(("Deleting IF: %d \n", i)); + if ((dhd->iflist[i]->state != DHD_IF_DEL) && + (dhd->iflist[i]->state != DHD_IF_DELETING)) { + dhd->iflist[i]->state = DHD_IF_DEL; + dhd->iflist[i]->idx = i; + dhd_op_if(dhd->iflist[i]); + } + } + dhd_net_if_unlock_local(dhd); + } + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + if (rollback_lock) + rtnl_lock(); +#endif + + return 0; +} +#endif /* WL_CFG80211 */ + + +static int +dhd_stop(struct net_device *net) +{ + int ifidx = 0; + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); + DHD_OS_WAKE_LOCK(&dhd->pub); + DHD_TRACE(("%s: Enter %p\n", __FUNCTION__, net)); + if (dhd->pub.up == 0) { + goto exit; + } + ifidx = dhd_net2idx(dhd, net); + BCM_REFERENCE(ifidx); + + /* Set state and stop OS transmissions */ + netif_stop_queue(net); + dhd->pub.up = 0; + +#ifdef WL_CFG80211 + if (ifidx == 0) { + wl_cfg80211_down(NULL); + + /* + * For CFG80211: Clean up all the left over virtual interfaces + * when the primary Interface is brought down. [ifconfig wlan0 down] + */ + if ((dhd->dhd_state & DHD_ATTACH_STATE_ADD_IF) && + (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)) { + dhd_cleanup_virt_ifaces(dhd); + } + } +#endif + +#ifdef PROP_TXSTATUS + dhd_os_wlfc_block(&dhd->pub); + dhd_wlfc_cleanup(&dhd->pub); + dhd_os_wlfc_unblock(&dhd->pub); +#endif + /* Stop the protocol module */ + dhd_prot_stop(&dhd->pub); + + OLD_MOD_DEC_USE_COUNT; +exit: +#if defined(WL_CFG80211) + if (ifidx == 0) { + if (!dhd_download_fw_on_driverload) + wl_android_wifi_off(net); + } +#endif + dhd->pub.rxcnt_timeout = 0; + dhd->pub.txcnt_timeout = 0; + + DHD_OS_WAKE_UNLOCK(&dhd->pub); + return 0; +} + +static int +dhd_open(struct net_device *net) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); +#ifdef TOE + uint32 toe_ol; +#endif + int ifidx; + int32 ret = 0; + + DHD_OS_WAKE_LOCK(&dhd->pub); + /* Update FW path if it was changed */ + if (strlen(firmware_path) != 0) { + if (firmware_path[strlen(firmware_path)-1] == '\n') + firmware_path[strlen(firmware_path)-1] = '\0'; + strncpy(fw_path, firmware_path, sizeof(fw_path)-1); + fw_path[sizeof(fw_path)-1] = '\0'; + firmware_path[0] = '\0'; + } + + + dhd->pub.dongle_trap_occured = 0; + dhd->pub.hang_was_sent = 0; +#if !defined(WL_CFG80211) + /* + * Force start if ifconfig_up gets called before START command + * We keep WEXT's wl_control_wl_start to provide backward compatibility + * This should be removed in the future + */ + ret = wl_control_wl_start(net); + if (ret != 0) { + DHD_ERROR(("%s: failed with code %d\n", __FUNCTION__, ret)); + ret = -1; + goto exit; + } +#endif + + ifidx = dhd_net2idx(dhd, net); + DHD_TRACE(("%s: ifidx %d\n", __FUNCTION__, ifidx)); + + if (ifidx < 0) { + DHD_ERROR(("%s: Error: called with invalid IF\n", __FUNCTION__)); + ret = -1; + goto exit; + } + + if (!dhd->iflist[ifidx] || dhd->iflist[ifidx]->state == DHD_IF_DEL) { + DHD_ERROR(("%s: Error: called when IF already deleted\n", __FUNCTION__)); + ret = -1; + goto exit; + } + + if (ifidx == 0) { + atomic_set(&dhd->pend_8021x_cnt, 0); +#if defined(WL_CFG80211) + DHD_ERROR(("\n%s\n", dhd_version)); + if (!dhd_download_fw_on_driverload) { + ret = wl_android_wifi_on(net); + if (ret != 0) { + DHD_ERROR(("%s: failed with code %d\n", __FUNCTION__, ret)); + ret = -1; + goto exit; + } + } else { + } +#endif + + if (dhd->pub.busstate != DHD_BUS_DATA) { + + /* try to bring up bus */ + if ((ret = dhd_bus_start(&dhd->pub)) != 0) { + DHD_ERROR(("%s: failed with code %d\n", __FUNCTION__, ret)); + ret = -1; + goto exit; + } + + } + + /* dhd_prot_init has been called in dhd_bus_start or wl_android_wifi_on */ + memcpy(net->dev_addr, dhd->pub.mac.octet, ETHER_ADDR_LEN); + +#ifdef TOE + /* Get current TOE mode from dongle */ + if (dhd_toe_get(dhd, ifidx, &toe_ol) >= 0 && (toe_ol & TOE_TX_CSUM_OL) != 0) + dhd->iflist[ifidx]->net->features |= NETIF_F_IP_CSUM; + else + dhd->iflist[ifidx]->net->features &= ~NETIF_F_IP_CSUM; +#endif /* TOE */ + +#if defined(WL_CFG80211) + if (unlikely(wl_cfg80211_up(NULL))) { + DHD_ERROR(("%s: failed to bring up cfg80211\n", __FUNCTION__)); + ret = -1; + goto exit; + } +#endif /* WL_CFG80211 */ + } + + /* Allow transmit calls */ + netif_start_queue(net); + dhd->pub.up = 1; + +#ifdef BCMDBGFS + dhd_dbg_init(&dhd->pub); +#endif + + OLD_MOD_INC_USE_COUNT; +exit: + if (ret) + dhd_stop(net); + + DHD_OS_WAKE_UNLOCK(&dhd->pub); + return ret; +} + +int dhd_do_driver_init(struct net_device *net) +{ + dhd_info_t *dhd = NULL; + + if (!net) { + DHD_ERROR(("Primary Interface not initialized \n")); + return -EINVAL; + } + + dhd = *(dhd_info_t **)netdev_priv(net); + + /* If driver is already initialized, do nothing + */ + if (dhd->pub.busstate == DHD_BUS_DATA) { + DHD_TRACE(("Driver already Inititalized. Nothing to do")); + return 0; + } + + if (dhd_open(net) < 0) { + DHD_ERROR(("Driver Init Failed \n")); + return -1; + } + + return 0; +} + +osl_t * +dhd_osl_attach(void *pdev, uint bustype) +{ + return osl_attach(pdev, bustype, TRUE); +} + +void +dhd_osl_detach(osl_t *osh) +{ + if (MALLOCED(osh)) { + DHD_ERROR(("%s: MEMORY LEAK %d bytes\n", __FUNCTION__, MALLOCED(osh))); + } + osl_detach(osh); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + dhd_registration_check = FALSE; + up(&dhd_registration_sem); +#if defined(BCMLXSDMMC) + up(&dhd_chipup_sem); +#endif +#endif +} + +int +dhd_add_if(dhd_info_t *dhd, int ifidx, void *handle, char *name, + uint8 *mac_addr, uint32 flags, uint8 bssidx) +{ + dhd_if_t *ifp; + + DHD_TRACE(("%s: idx %d, handle->%p\n", __FUNCTION__, ifidx, handle)); + + ASSERT(dhd && (ifidx < DHD_MAX_IFS)); + + ifp = dhd->iflist[ifidx]; + if (ifp != NULL) { + if (ifp->net != NULL) { + netif_stop_queue(ifp->net); + unregister_netdev(ifp->net); + free_netdev(ifp->net); + } + } else + if ((ifp = MALLOC(dhd->pub.osh, sizeof(dhd_if_t))) == NULL) { + DHD_ERROR(("%s: OOM - dhd_if_t\n", __FUNCTION__)); + return -ENOMEM; + } + + memset(ifp, 0, sizeof(dhd_if_t)); + ifp->event2cfg80211 = FALSE; + ifp->info = dhd; + dhd->iflist[ifidx] = ifp; + strncpy(ifp->name, name, IFNAMSIZ); + ifp->name[IFNAMSIZ] = '\0'; + if (mac_addr != NULL) + memcpy(&ifp->mac_addr, mac_addr, ETHER_ADDR_LEN); + + if (handle == NULL) { + ifp->state = DHD_IF_ADD; + ifp->idx = ifidx; + ifp->bssidx = bssidx; + ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0); + up(&dhd->thr_sysioc_ctl.sema); + } else + ifp->net = (struct net_device *)handle; + + if (ifidx == 0) { + ifp->event2cfg80211 = TRUE; + } + + return 0; +} + +void +dhd_del_if(dhd_info_t *dhd, int ifidx) +{ + dhd_if_t *ifp; + + DHD_TRACE(("%s: idx %d\n", __FUNCTION__, ifidx)); + + ASSERT(dhd && ifidx && (ifidx < DHD_MAX_IFS)); + ifp = dhd->iflist[ifidx]; + if (!ifp) { + DHD_ERROR(("%s: Null interface\n", __FUNCTION__)); + return; + } + + ifp->state = DHD_IF_DEL; + ifp->idx = ifidx; + ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0); + up(&dhd->thr_sysioc_ctl.sema); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) +static struct net_device_ops dhd_ops_pri = { + .ndo_open = dhd_open, + .ndo_stop = dhd_stop, + .ndo_get_stats = dhd_get_stats, + .ndo_do_ioctl = dhd_ioctl_entry, + .ndo_start_xmit = dhd_start_xmit, + .ndo_set_mac_address = dhd_set_mac_address, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)) + .ndo_set_rx_mode = dhd_set_multicast_list, +#else + .ndo_set_multicast_list = dhd_set_multicast_list, +#endif +}; + +static struct net_device_ops dhd_ops_virt = { + .ndo_get_stats = dhd_get_stats, + .ndo_do_ioctl = dhd_ioctl_entry, + .ndo_start_xmit = dhd_start_xmit, + .ndo_set_mac_address = dhd_set_mac_address, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)) + .ndo_set_rx_mode = dhd_set_multicast_list, +#else + .ndo_set_multicast_list = dhd_set_multicast_list, +#endif +}; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) */ + +dhd_pub_t * +dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) +{ + dhd_info_t *dhd = NULL; + struct net_device *net = NULL; + + dhd_attach_states_t dhd_state = DHD_ATTACH_STATE_INIT; + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + /* updates firmware nvram path if it was provided as module parameters */ + if (strlen(firmware_path) != 0) { + strncpy(fw_path, firmware_path, sizeof(fw_path) - 1); + fw_path[sizeof(fw_path) - 1] = '\0'; + } + if (strlen(nvram_path) != 0) { + strncpy(nv_path, nvram_path, sizeof(nv_path) -1); + nv_path[sizeof(nv_path) -1] = '\0'; + } + + /* Allocate etherdev, including space for private structure */ + if (!(net = alloc_etherdev(sizeof(dhd)))) { + DHD_ERROR(("%s: OOM - alloc_etherdev\n", __FUNCTION__)); + goto fail; + } + dhd_state |= DHD_ATTACH_STATE_NET_ALLOC; + + /* Allocate primary dhd_info */ + if (!(dhd = MALLOC(osh, sizeof(dhd_info_t)))) { + DHD_ERROR(("%s: OOM - alloc dhd_info\n", __FUNCTION__)); + goto fail; + } + memset(dhd, 0, sizeof(dhd_info_t)); + +#ifdef DHDTHREAD + dhd->thr_dpc_ctl.thr_pid = DHD_PID_KT_TL_INVALID; + dhd->thr_wdt_ctl.thr_pid = DHD_PID_KT_INVALID; +#endif /* DHDTHREAD */ + dhd->dhd_tasklet_create = FALSE; + dhd->thr_sysioc_ctl.thr_pid = DHD_PID_KT_INVALID; + dhd_state |= DHD_ATTACH_STATE_DHD_ALLOC; + + /* + * Save the dhd_info into the priv + */ + memcpy((void *)netdev_priv(net), &dhd, sizeof(dhd)); + dhd->pub.osh = osh; + + /* Link to info module */ + dhd->pub.info = dhd; + /* Link to bus module */ + dhd->pub.bus = bus; + dhd->pub.hdrlen = bus_hdrlen; + + /* Set network interface name if it was provided as module parameter */ + if (iface_name[0]) { + int len; + char ch; + strncpy(net->name, iface_name, IFNAMSIZ); + net->name[IFNAMSIZ - 1] = 0; + len = strlen(net->name); + ch = net->name[len - 1]; + if ((ch > '9' || ch < '0') && (len < IFNAMSIZ - 2)) + strcat(net->name, "%d"); + } + + if (dhd_add_if(dhd, 0, (void *)net, net->name, NULL, 0, 0) == DHD_BAD_IF) + goto fail; + dhd_state |= DHD_ATTACH_STATE_ADD_IF; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) + net->open = NULL; +#else + net->netdev_ops = NULL; +#endif + + sema_init(&dhd->proto_sem, 1); + +#ifdef PROP_TXSTATUS + spin_lock_init(&dhd->wlfc_spinlock); +#ifdef PROP_TXSTATUS_VSDB + dhd->pub.wlfc_enabled = FALSE; +#else + dhd->pub.wlfc_enabled = TRUE; +#endif /* PROP_TXSTATUS_VSDB */ +#endif /* PROP_TXSTATUS */ + + /* Initialize other structure content */ + init_waitqueue_head(&dhd->ioctl_resp_wait); + init_waitqueue_head(&dhd->ctrl_wait); + + /* Initialize the spinlocks */ + spin_lock_init(&dhd->sdlock); + spin_lock_init(&dhd->txqlock); + spin_lock_init(&dhd->dhd_lock); + + /* Initialize Wakelock stuff */ + spin_lock_init(&dhd->wakelock_spinlock); + dhd->wakelock_counter = 0; + dhd->wakelock_wd_counter = 0; + dhd->wakelock_rx_timeout_enable = 0; + dhd->wakelock_ctrl_timeout_enable = 0; +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&dhd->wl_wifi, WAKE_LOCK_SUSPEND, "wlan_wake"); + wake_lock_init(&dhd->wl_rxwake, WAKE_LOCK_SUSPEND, "wlan_rx_wake"); + wake_lock_init(&dhd->wl_ctrlwake, WAKE_LOCK_SUSPEND, "wlan_ctrl_wake"); + wake_lock_init(&dhd->wl_wdwake, WAKE_LOCK_SUSPEND, "wlan_wd_wake"); +#endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + mutex_init(&dhd->dhd_net_if_mutex); + mutex_init(&dhd->dhd_suspend_mutex); +#endif + dhd_state |= DHD_ATTACH_STATE_WAKELOCKS_INIT; + + /* Attach and link in the protocol */ + if (dhd_prot_attach(&dhd->pub) != 0) { + DHD_ERROR(("dhd_prot_attach failed\n")); + goto fail; + } + dhd_state |= DHD_ATTACH_STATE_PROT_ATTACH; + +#ifdef WL_CFG80211 + /* Attach and link in the cfg80211 */ + if (unlikely(wl_cfg80211_attach(net, &dhd->pub))) { + DHD_ERROR(("wl_cfg80211_attach failed\n")); + goto fail; + } + + dhd_monitor_init(&dhd->pub); + dhd_state |= DHD_ATTACH_STATE_CFG80211; +#endif +#if defined(CONFIG_WIRELESS_EXT) + /* Attach and link in the iw */ + if (!(dhd_state & DHD_ATTACH_STATE_CFG80211)) { + if (wl_iw_attach(net, (void *)&dhd->pub) != 0) { + DHD_ERROR(("wl_iw_attach failed\n")); + goto fail; + } + dhd_state |= DHD_ATTACH_STATE_WL_ATTACH; + } +#endif /* defined(CONFIG_WIRELESS_EXT) */ + + + /* Set up the watchdog timer */ + init_timer(&dhd->timer); + dhd->timer.data = (ulong)dhd; + dhd->timer.function = dhd_watchdog; + +#ifdef DHDTHREAD + /* Initialize thread based operation and lock */ + sema_init(&dhd->sdsem, 1); + if ((dhd_watchdog_prio >= 0) && (dhd_dpc_prio >= 0)) { + dhd->threads_only = TRUE; + } + else { + dhd->threads_only = FALSE; + } + + if (dhd_watchdog_prio >= 0) { + /* Initialize watchdog thread */ + PROC_START(dhd_watchdog_thread, dhd, &dhd->thr_wdt_ctl, 0); + } else { + dhd->thr_wdt_ctl.thr_pid = -1; + } + + /* Set up the bottom half handler */ + if (dhd_dpc_prio >= 0) { + /* Initialize DPC thread */ + PROC_START(dhd_dpc_thread, dhd, &dhd->thr_dpc_ctl, 0); + } else { + /* use tasklet for dpc */ + tasklet_init(&dhd->tasklet, dhd_dpc, (ulong)dhd); + dhd->thr_dpc_ctl.thr_pid = -1; + } +#else + /* Set up the bottom half handler */ + tasklet_init(&dhd->tasklet, dhd_dpc, (ulong)dhd); + dhd->dhd_tasklet_create = TRUE; +#endif /* DHDTHREAD */ + + if (dhd_sysioc) { + PROC_START(_dhd_sysioc_thread, dhd, &dhd->thr_sysioc_ctl, 0); + } else { + dhd->thr_sysioc_ctl.thr_pid = -1; + } + dhd_state |= DHD_ATTACH_STATE_THREADS_CREATED; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + INIT_WORK(&dhd->work_hang, dhd_hang_process); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ + /* + * Save the dhd_info into the priv + */ + memcpy(netdev_priv(net), &dhd, sizeof(dhd)); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) + register_pm_notifier(&dhd_sleep_pm_notifier); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ + +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) + dhd->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 20; + dhd->early_suspend.suspend = dhd_early_suspend; + dhd->early_suspend.resume = dhd_late_resume; + register_early_suspend(&dhd->early_suspend); + dhd_state |= DHD_ATTACH_STATE_EARLYSUSPEND_DONE; +#endif + +#ifdef ARP_OFFLOAD_SUPPORT + dhd->pend_ipaddr = 0; + register_inetaddr_notifier(&dhd_notifier); +#endif /* ARP_OFFLOAD_SUPPORT */ + + dhd_state |= DHD_ATTACH_STATE_DONE; + dhd->dhd_state = dhd_state; + return &dhd->pub; + +fail: + if (dhd_state < DHD_ATTACH_STATE_DHD_ALLOC) { + if (net) free_netdev(net); + } else { + DHD_TRACE(("%s: Calling dhd_detach dhd_state 0x%x &dhd->pub %p\n", + __FUNCTION__, dhd_state, &dhd->pub)); + dhd->dhd_state = dhd_state; + dhd_detach(&dhd->pub); + dhd_free(&dhd->pub); + } + + return NULL; +} + +int +dhd_bus_start(dhd_pub_t *dhdp) +{ + int ret = -1; + dhd_info_t *dhd = (dhd_info_t*)dhdp->info; + unsigned long flags; + + ASSERT(dhd); + + DHD_TRACE(("Enter %s:\n", __FUNCTION__)); + +#ifdef DHDTHREAD + if (dhd->threads_only) + dhd_os_sdlock(dhdp); +#endif /* DHDTHREAD */ + + + /* try to download image and nvram to the dongle */ + if ((dhd->pub.busstate == DHD_BUS_DOWN) && + (fw_path != NULL) && (fw_path[0] != '\0') && + (nv_path != NULL) && (nv_path[0] != '\0')) { + /* wake lock moved to dhdsdio_download_firmware */ + if (!(dhd_bus_download_firmware(dhd->pub.bus, dhd->pub.osh, + fw_path, nv_path))) { + DHD_ERROR(("%s: dhdsdio_probe_download failed. firmware = %s nvram = %s\n", + __FUNCTION__, fw_path, nv_path)); +#ifdef DHDTHREAD + if (dhd->threads_only) + dhd_os_sdunlock(dhdp); +#endif /* DHDTHREAD */ + return -1; + } + } + if (dhd->pub.busstate != DHD_BUS_LOAD) { +#ifdef DHDTHREAD + if (dhd->threads_only) + dhd_os_sdunlock(dhdp); +#endif /* DHDTHREAD */ + return -ENETDOWN; + } + + /* Start the watchdog timer */ + dhd->pub.tickcnt = 0; + dhd_os_wd_timer(&dhd->pub, dhd_watchdog_ms); + + /* Bring up the bus */ + if ((ret = dhd_bus_init(&dhd->pub, FALSE)) != 0) { + + DHD_ERROR(("%s, dhd_bus_init failed %d\n", __FUNCTION__, ret)); +#ifdef DHDTHREAD + if (dhd->threads_only) + dhd_os_sdunlock(dhdp); +#endif /* DHDTHREAD */ + return ret; + } +#if defined(OOB_INTR_ONLY) + /* Host registration for OOB interrupt */ + if (bcmsdh_register_oob_intr(dhdp)) { + /* deactivate timer and wait for the handler to finish */ + + flags = dhd_os_spin_lock(&dhd->pub); + dhd->wd_timer_valid = FALSE; + dhd_os_spin_unlock(&dhd->pub, flags); + del_timer_sync(&dhd->timer); + DHD_ERROR(("%s Host failed to register for OOB\n", __FUNCTION__)); +#ifdef DHDTHREAD + if (dhd->threads_only) + dhd_os_sdunlock(dhdp); +#endif /* DHDTHREAD */ + DHD_OS_WD_WAKE_UNLOCK(&dhd->pub); + return -ENODEV; + } + + /* Enable oob at firmware */ + dhd_enable_oob_intr(dhd->pub.bus, TRUE); +#endif /* defined(OOB_INTR_ONLY) */ + + /* If bus is not ready, can't come up */ + if (dhd->pub.busstate != DHD_BUS_DATA) { + flags = dhd_os_spin_lock(&dhd->pub); + dhd->wd_timer_valid = FALSE; + dhd_os_spin_unlock(&dhd->pub, flags); + del_timer_sync(&dhd->timer); + DHD_ERROR(("%s failed bus is not ready\n", __FUNCTION__)); +#ifdef DHDTHREAD + if (dhd->threads_only) + dhd_os_sdunlock(dhdp); +#endif /* DHDTHREAD */ + DHD_OS_WD_WAKE_UNLOCK(&dhd->pub); + return -ENODEV; + } + +#ifdef DHDTHREAD + if (dhd->threads_only) + dhd_os_sdunlock(dhdp); +#endif /* DHDTHREAD */ + +#ifdef BCMSDIOH_TXGLOM + if ((dhd->pub.busstate == DHD_BUS_DATA) && bcmsdh_glom_enabled()) { + dhd_txglom_enable(dhdp, TRUE); + } +#endif + +#ifdef READ_MACADDR + dhd_read_macaddr(dhd); +#endif + + /* Bus is ready, do any protocol initialization */ + if ((ret = dhd_prot_init(&dhd->pub)) < 0) + return ret; + +#ifdef WRITE_MACADDR + dhd_write_macaddr(dhd->pub.mac.octet); +#endif + +#ifdef ARP_OFFLOAD_SUPPORT + if (dhd->pend_ipaddr) { +#ifdef AOE_IP_ALIAS_SUPPORT + aoe_update_host_ipv4_table(&dhd->pub, dhd->pend_ipaddr, TRUE, 0); +#endif /* AOE_IP_ALIAS_SUPPORT */ + dhd->pend_ipaddr = 0; + } +#endif /* ARP_OFFLOAD_SUPPORT */ + + return 0; +} + +bool dhd_is_concurrent_mode(dhd_pub_t *dhd) +{ + if (!dhd) + return FALSE; + + if (dhd->op_mode & DHD_FLAG_CONCURR_MULTI_CHAN_MODE) + return TRUE; + else if ((dhd->op_mode & DHD_FLAG_CONCURR_SINGLE_CHAN_MODE) == + DHD_FLAG_CONCURR_SINGLE_CHAN_MODE) + return TRUE; + else + return FALSE; +} + +#if !defined(AP) && defined(WLP2P) +/* From Android JerryBean release, the concurrent mode is enabled by default and the firmware + * name would be fw_bcmdhd.bin. So we need to determine whether P2P is enabled in the STA + * firmware and accordingly enable concurrent mode (Apply P2P settings). SoftAP firmware + * would still be named as fw_bcmdhd_apsta. + */ +uint32 +dhd_get_concurrent_capabilites(dhd_pub_t *dhd) +{ + int32 ret = 0; + char buf[WLC_IOCTL_SMLEN]; + bool mchan_supported = FALSE; + /* if dhd->op_mode is already set for HOSTAP, + * that means we only will use the mode as it is + */ + if (dhd->op_mode & DHD_FLAG_HOSTAP_MODE) + return 0; + memset(buf, 0, sizeof(buf)); + bcm_mkiovar("cap", 0, 0, buf, sizeof(buf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), + FALSE, 0)) < 0) { + DHD_ERROR(("%s: Get Capability failed (error=%d)\n", + __FUNCTION__, ret)); + return 0; + } + if (strstr(buf, "vsdb")) { + mchan_supported = TRUE; + } + if (strstr(buf, "p2p") == NULL) { + DHD_TRACE(("Chip does not support p2p\n")); + return 0; + } + else { + /* Chip supports p2p but ensure that p2p is really implemented in firmware or not */ + memset(buf, 0, sizeof(buf)); + bcm_mkiovar("p2p", 0, 0, buf, sizeof(buf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), + FALSE, 0)) < 0) { + DHD_ERROR(("%s: Get P2P failed (error=%d)\n", __FUNCTION__, ret)); + return 0; + } + else { + if (buf[0] == 1) { + /* By default, chip supports single chan concurrency, + * now lets check for mchan + */ + ret = DHD_FLAG_CONCURR_SINGLE_CHAN_MODE; + if (mchan_supported) + ret |= DHD_FLAG_CONCURR_MULTI_CHAN_MODE; +#if defined(WL_ENABLE_P2P_IF) + /* For customer_hw4, although ICS, + * we still support concurrent mode + */ + return ret; +#else + return 0; +#endif + } + } + } + return 0; +} +#endif +int +dhd_preinit_ioctls(dhd_pub_t *dhd) +{ + int ret = 0; + char eventmask[WL_EVENTING_MASK_LEN]; + char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ + +#if !defined(WL_CFG80211) + uint up = 0; +#endif /* !defined(WL_CFG80211) */ + uint power_mode = PM_FAST; + uint32 dongle_align = DHD_SDALIGN; + uint32 glom = CUSTOM_GLOM_SETTING; +#if defined(VSDB) || defined(ROAM_ENABLE) + uint bcn_timeout = 8; +#else + uint bcn_timeout = 4; +#endif +#ifdef ENABLE_BCN_LI_BCN_WAKEUP + uint32 bcn_li_bcn = 1; +#endif /* ENABLE_BCN_LI_BCN_WAKEUP */ + uint retry_max = 3; +#if defined(ARP_OFFLOAD_SUPPORT) + int arpoe = 1; +#endif + int scan_assoc_time = DHD_SCAN_ASSOC_ACTIVE_TIME; + int scan_unassoc_time = DHD_SCAN_UNASSOC_ACTIVE_TIME; + int scan_passive_time = DHD_SCAN_PASSIVE_TIME; + char buf[WLC_IOCTL_SMLEN]; + char *ptr; + uint32 listen_interval = LISTEN_INTERVAL; /* Default Listen Interval in Beacons */ +#ifdef ROAM_ENABLE + uint roamvar = 0; + int roam_trigger[2] = {CUSTOM_ROAM_TRIGGER_SETTING, WLC_BAND_ALL}; + int roam_scan_period[2] = {10, WLC_BAND_ALL}; + int roam_delta[2] = {CUSTOM_ROAM_DELTA_SETTING, WLC_BAND_ALL}; +#ifdef FULL_ROAMING_SCAN_PERIOD_60_SEC + int roam_fullscan_period = 60; +#else /* FULL_ROAMING_SCAN_PERIOD_60_SEC */ + int roam_fullscan_period = 120; +#endif /* FULL_ROAMING_SCAN_PERIOD_60_SEC */ +#else +#ifdef DISABLE_BUILTIN_ROAM + uint roamvar = 1; +#endif /* DISABLE_BUILTIN_ROAM */ +#endif /* ROAM_ENABLE */ + +#if defined(SOFTAP) + uint dtim = 1; +#endif +#if (defined(AP) && !defined(WLP2P)) || (!defined(AP) && defined(WL_CFG80211)) + uint32 mpc = 0; /* Turn MPC off for AP/APSTA mode */ + struct ether_addr p2p_ea; +#endif + +#if defined(AP) || defined(WLP2P) + uint32 apsta = 1; /* Enable APSTA mode */ +#endif /* defined(AP) || defined(WLP2P) */ +#ifdef GET_CUSTOM_MAC_ENABLE + struct ether_addr ea_addr; +#endif /* GET_CUSTOM_MAC_ENABLE */ +#ifdef DISABLE_11N + uint32 nmode = 0; +#else +#ifdef AMPDU_HOSTREORDER + uint32 hostreorder = 1; +#endif +#endif /* DISABLE_11N */ + dhd->suspend_bcn_li_dtim = CUSTOM_SUSPEND_BCN_LI_DTIM; +#ifdef PROP_TXSTATUS +#ifdef PROP_TXSTATUS_VSDB + dhd->wlfc_enabled = FALSE; + /* enable WLFC only if the firmware is VSDB */ +#else + dhd->wlfc_enabled = TRUE; +#endif /* PROP_TXSTATUS_VSDB */ +#endif /* PROP_TXSTATUS */ + DHD_TRACE(("Enter %s\n", __FUNCTION__)); + dhd->op_mode = 0; +#ifdef GET_CUSTOM_MAC_ENABLE + ret = dhd_custom_get_mac_address(ea_addr.octet); + if (!ret) { + memset(buf, 0, sizeof(buf)); + bcm_mkiovar("cur_etheraddr", (void *)&ea_addr, ETHER_ADDR_LEN, buf, sizeof(buf)); + ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0); + if (ret < 0) { + DHD_ERROR(("%s: can't set MAC address , error=%d\n", __FUNCTION__, ret)); + return BCME_NOTUP; + } + memcpy(dhd->mac.octet, ea_addr.octet, ETHER_ADDR_LEN); + } else { +#endif /* GET_CUSTOM_MAC_ENABLE */ + /* Get the default device MAC address directly from firmware */ + memset(buf, 0, sizeof(buf)); + bcm_mkiovar("cur_etheraddr", 0, 0, buf, sizeof(buf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), + FALSE, 0)) < 0) { + DHD_ERROR(("%s: can't get MAC address , error=%d\n", __FUNCTION__, ret)); + return BCME_NOTUP; + } + /* Update public MAC address after reading from Firmware */ + memcpy(dhd->mac.octet, buf, ETHER_ADDR_LEN); + +#ifdef GET_CUSTOM_MAC_ENABLE + } +#endif /* GET_CUSTOM_MAC_ENABLE */ + + DHD_TRACE(("Firmware = %s\n", fw_path)); + + if ((!op_mode && strstr(fw_path, "_apsta") != NULL) || + (op_mode == DHD_FLAG_HOSTAP_MODE)) { +#ifdef SET_RANDOM_MAC_SOFTAP + uint rand_mac; +#endif + dhd->op_mode = DHD_FLAG_HOSTAP_MODE; +#if defined(ARP_OFFLOAD_SUPPORT) + arpoe = 0; +#endif +#ifdef PKT_FILTER_SUPPORT + dhd_pkt_filter_enable = FALSE; +#endif +#ifdef SET_RANDOM_MAC_SOFTAP + srandom32((uint)jiffies); + rand_mac = random32(); + iovbuf[0] = 0x02; /* locally administered bit */ + iovbuf[1] = 0x1A; + iovbuf[2] = 0x11; + iovbuf[3] = (unsigned char)(rand_mac & 0x0F) | 0xF0; + iovbuf[4] = (unsigned char)(rand_mac >> 8); + iovbuf[5] = (unsigned char)(rand_mac >> 16); + + bcm_mkiovar("cur_etheraddr", (void *)iovbuf, ETHER_ADDR_LEN, buf, sizeof(buf)); + ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0); + if (ret < 0) { + DHD_ERROR(("%s: can't set MAC address , error=%d\n", __FUNCTION__, ret)); + } else + memcpy(dhd->mac.octet, iovbuf, ETHER_ADDR_LEN); +#endif /* SET_RANDOM_MAC_SOFTAP */ +#if !defined(AP) && defined(WL_CFG80211) + /* Turn off MPC in AP mode */ + bcm_mkiovar("mpc", (char *)&mpc, 4, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, + sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s mpc for HostAPD failed %d\n", __FUNCTION__, ret)); + } +#endif + + } + else { + uint32 concurrent_mode = 0; + if ((!op_mode && strstr(fw_path, "_p2p") != NULL) || + (op_mode == DHD_FLAG_P2P_MODE)) { +#if defined(ARP_OFFLOAD_SUPPORT) + arpoe = 0; +#endif +#ifdef PKT_FILTER_SUPPORT + dhd_pkt_filter_enable = FALSE; +#endif + dhd->op_mode = DHD_FLAG_P2P_MODE; + } + else + dhd->op_mode = DHD_FLAG_STA_MODE; +#if !defined(AP) && defined(WLP2P) + if ((concurrent_mode = dhd_get_concurrent_capabilites(dhd))) { +#if defined(ARP_OFFLOAD_SUPPORT) + arpoe = 1; +#endif + dhd->op_mode |= concurrent_mode; + } + + /* Check if we are enabling p2p */ + if (dhd->op_mode & DHD_FLAG_P2P_MODE) { + bcm_mkiovar("apsta", (char *)&apsta, 4, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, + iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s APSTA for P2P failed ret= %d\n", __FUNCTION__, ret)); + } + + memcpy(&p2p_ea, &dhd->mac, ETHER_ADDR_LEN); + ETHER_SET_LOCALADDR(&p2p_ea); + bcm_mkiovar("p2p_da_override", (char *)&p2p_ea, + ETHER_ADDR_LEN, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, + iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s p2p_da_override ret= %d\n", __FUNCTION__, ret)); + } else { + DHD_INFO(("dhd_preinit_ioctls: p2p_da_override succeeded\n")); + } + } +#else + (void)concurrent_mode; +#endif + } + + DHD_ERROR(("Firmware up: op_mode=0x%04x, " + "Broadcom Dongle Host Driver mac="MACDBG"\n", + dhd->op_mode, + MAC2STRDBG(dhd->mac.octet))); + /* Set Country code */ + if (dhd->dhd_cspec.ccode[0] != 0) { + bcm_mkiovar("country", (char *)&dhd->dhd_cspec, + sizeof(wl_country_t), iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) + DHD_ERROR(("%s: country code setting failed\n", __FUNCTION__)); + } + + /* Set Listen Interval */ + bcm_mkiovar("assoc_listen", (char *)&listen_interval, 4, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) + DHD_ERROR(("%s assoc_listen failed %d\n", __FUNCTION__, ret)); + +#if defined(ROAM_ENABLE) || defined(DISABLE_BUILTIN_ROAM) + /* Disable built-in roaming to allowed ext supplicant to take care of roaming */ + bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +#endif /* ROAM_ENABLE || DISABLE_BUILTIN_ROAM */ +#ifdef ROAM_ENABLE + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_TRIGGER, roam_trigger, + sizeof(roam_trigger), TRUE, 0)) < 0) + DHD_ERROR(("%s: roam trigger set failed %d\n", __FUNCTION__, ret)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_SCAN_PERIOD, roam_scan_period, + sizeof(roam_scan_period), TRUE, 0)) < 0) + DHD_ERROR(("%s: roam scan period set failed %d\n", __FUNCTION__, ret)); + if ((dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_DELTA, roam_delta, + sizeof(roam_delta), TRUE, 0)) < 0) + DHD_ERROR(("%s: roam delta set failed %d\n", __FUNCTION__, ret)); + bcm_mkiovar("fullroamperiod", (char *)&roam_fullscan_period, 4, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) + DHD_ERROR(("%s: roam fullscan period set failed %d\n", __FUNCTION__, ret)); +#endif /* ROAM_ENABLE */ + + /* Set PowerSave mode */ + dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, sizeof(power_mode), TRUE, 0); + + /* Match Host and Dongle rx alignment */ + bcm_mkiovar("bus:txglomalign", (char *)&dongle_align, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + + if (glom != DEFAULT_GLOM_VALUE) { + DHD_INFO(("%s set glom=0x%X\n", __FUNCTION__, glom)); + bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + } + + /* Setup timeout if Beacons are lost and roam is off to report link down */ + bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + /* Setup assoc_retry_max count to reconnect target AP in dongle */ + bcm_mkiovar("assoc_retry_max", (char *)&retry_max, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +#if defined(AP) && !defined(WLP2P) + /* Turn off MPC in AP mode */ + bcm_mkiovar("mpc", (char *)&mpc, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + bcm_mkiovar("apsta", (char *)&apsta, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +#endif /* defined(AP) && !defined(WLP2P) */ + +#if defined(SOFTAP) + if (ap_fw_loaded == TRUE) { + dhd_wl_ioctl_cmd(dhd, WLC_SET_DTIMPRD, (char *)&dtim, sizeof(dtim), TRUE, 0); + } +#endif + +#if defined(KEEP_ALIVE) + { + /* Set Keep Alive : be sure to use FW with -keepalive */ + int res; + +#if defined(SOFTAP) + if (ap_fw_loaded == FALSE) +#endif + if (!(dhd->op_mode & DHD_FLAG_HOSTAP_MODE)) { + if ((res = dhd_keep_alive_onoff(dhd)) < 0) + DHD_ERROR(("%s set keeplive failed %d\n", + __FUNCTION__, res)); + } + } +#endif /* defined(KEEP_ALIVE) */ + + /* Read event_msgs mask */ + bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, iovbuf, sizeof(iovbuf), FALSE, 0)) < 0) { + DHD_ERROR(("%s read Event mask failed %d\n", __FUNCTION__, ret)); + goto done; + } + bcopy(iovbuf, eventmask, WL_EVENTING_MASK_LEN); + + /* Setup event_msgs */ + setbit(eventmask, WLC_E_SET_SSID); + setbit(eventmask, WLC_E_PRUNE); + setbit(eventmask, WLC_E_AUTH); + setbit(eventmask, WLC_E_ASSOC); + setbit(eventmask, WLC_E_REASSOC); + setbit(eventmask, WLC_E_REASSOC_IND); + setbit(eventmask, WLC_E_DEAUTH); + setbit(eventmask, WLC_E_DEAUTH_IND); + setbit(eventmask, WLC_E_DISASSOC_IND); + setbit(eventmask, WLC_E_DISASSOC); + setbit(eventmask, WLC_E_JOIN); + setbit(eventmask, WLC_E_ASSOC_IND); + setbit(eventmask, WLC_E_PSK_SUP); + setbit(eventmask, WLC_E_LINK); + setbit(eventmask, WLC_E_NDIS_LINK); + setbit(eventmask, WLC_E_MIC_ERROR); + setbit(eventmask, WLC_E_ASSOC_REQ_IE); + setbit(eventmask, WLC_E_ASSOC_RESP_IE); +#ifndef WL_CFG80211 + setbit(eventmask, WLC_E_PMKID_CACHE); + setbit(eventmask, WLC_E_TXFAIL); +#endif + setbit(eventmask, WLC_E_JOIN_START); + setbit(eventmask, WLC_E_SCAN_COMPLETE); +#ifdef WLMEDIA_HTSF + setbit(eventmask, WLC_E_HTSFSYNC); +#endif /* WLMEDIA_HTSF */ +#ifdef PNO_SUPPORT + setbit(eventmask, WLC_E_PFN_NET_FOUND); +#endif /* PNO_SUPPORT */ + /* enable dongle roaming event */ + setbit(eventmask, WLC_E_ROAM); +#ifdef WL_CFG80211 + setbit(eventmask, WLC_E_ESCAN_RESULT); + if (dhd->op_mode & DHD_FLAG_P2P_MODE) { + setbit(eventmask, WLC_E_ACTION_FRAME_RX); + setbit(eventmask, WLC_E_P2P_DISC_LISTEN_COMPLETE); + } +#endif /* WL_CFG80211 */ + + /* Write updated Event mask */ + bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s Set Event mask failed %d\n", __FUNCTION__, ret)); + goto done; + } + + dhd_wl_ioctl_cmd(dhd, WLC_SET_SCAN_CHANNEL_TIME, (char *)&scan_assoc_time, + sizeof(scan_assoc_time), TRUE, 0); + dhd_wl_ioctl_cmd(dhd, WLC_SET_SCAN_UNASSOC_TIME, (char *)&scan_unassoc_time, + sizeof(scan_unassoc_time), TRUE, 0); + dhd_wl_ioctl_cmd(dhd, WLC_SET_SCAN_PASSIVE_TIME, (char *)&scan_passive_time, + sizeof(scan_passive_time), TRUE, 0); + +#ifdef ARP_OFFLOAD_SUPPORT + /* Set and enable ARP offload feature for STA only */ +#if defined(SOFTAP) + if (arpoe && !ap_fw_loaded) { +#else + if (arpoe) { +#endif + dhd_arp_offload_enable(dhd, TRUE); + dhd_arp_offload_set(dhd, dhd_arp_mode); + } else { + dhd_arp_offload_enable(dhd, FALSE); + dhd_arp_offload_set(dhd, 0); + } + dhd_arp_enable = arpoe; +#endif /* ARP_OFFLOAD_SUPPORT */ + +#ifdef PKT_FILTER_SUPPORT + /* Setup default defintions for pktfilter , enable in suspend */ + dhd->pktfilter_count = 5; + /* Setup filter to allow only unicast */ + dhd->pktfilter[0] = "100 0 0 0 0x01 0x00"; + dhd->pktfilter[1] = NULL; + dhd->pktfilter[2] = NULL; + dhd->pktfilter[3] = NULL; + /* Add filter to pass multicastDNS packet and NOT filter out as Broadcast */ + dhd->pktfilter[4] = "104 0 0 0 0xFFFFFFFFFFFF 0x01005E0000FB"; + dhd_set_packet_filter(dhd); +#if defined(SOFTAP) + if (ap_fw_loaded) { + dhd_enable_packet_filter(0, dhd); + } +#endif /* defined(SOFTAP) */ +#endif /* PKT_FILTER_SUPPORT */ +#ifdef DISABLE_11N + bcm_mkiovar("nmode", (char *)&nmode, 4, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) + DHD_ERROR(("%s wl nmode 0 failed %d\n", __FUNCTION__, ret)); +#else +#ifdef AMPDU_HOSTREORDER + bcm_mkiovar("ampdu_hostreorder", (char *)&hostreorder, 4, buf, sizeof(buf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0); +#endif /* AMPDU_HOSTREORDER */ +#endif /* DISABLE_11N */ + +#if !defined(WL_CFG80211) + /* Force STA UP */ + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_UP, (char *)&up, sizeof(up), TRUE, 0)) < 0) { + DHD_ERROR(("%s Setting WL UP failed %d\n", __FUNCTION__, ret)); + goto done; + } +#endif + +#ifdef ENABLE_BCN_LI_BCN_WAKEUP + bcm_mkiovar("bcn_li_bcn", (char *)&bcn_li_bcn, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +#endif /* ENABLE_BCN_LI_BCN_WAKEUP */ + + /* query for 'ver' to get version info from firmware */ + memset(buf, 0, sizeof(buf)); + ptr = buf; + bcm_mkiovar("ver", (char *)&buf, 4, buf, sizeof(buf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), FALSE, 0)) < 0) + DHD_ERROR(("%s failed %d\n", __FUNCTION__, ret)); + else { + bcmstrtok(&ptr, "\n", 0); + /* Print fw version info */ + DHD_ERROR(("Firmware version = %s\n", buf)); + + dhd_set_version_info(dhd, buf); + + DHD_BLOG(buf, strlen(buf) + 1); + DHD_BLOG(dhd_version, strlen(dhd_version) + 1); + + /* Check and adjust IOCTL response timeout for Manufactring firmware */ + if (strstr(buf, MANUFACTRING_FW) != NULL) { + dhd_os_set_ioctl_resp_timeout(IOCTL_RESP_TIMEOUT * 10); + DHD_ERROR(("%s : adjust IOCTL response time for Manufactring Firmware\n", + __FUNCTION__)); + } + } + +done: + return ret; +} + + +int +dhd_iovar(dhd_pub_t *pub, int ifidx, char *name, char *cmd_buf, uint cmd_len, int set) +{ + char buf[strlen(name) + 1 + cmd_len]; + int len = sizeof(buf); + wl_ioctl_t ioc; + int ret; + + len = bcm_mkiovar(name, cmd_buf, cmd_len, buf, len); + + memset(&ioc, 0, sizeof(ioc)); + + ioc.cmd = set? WLC_SET_VAR : WLC_GET_VAR; + ioc.buf = buf; + ioc.len = len; + ioc.set = TRUE; + + ret = dhd_wl_ioctl(pub, ifidx, &ioc, ioc.buf, ioc.len); + if (!set && ret >= 0) + memcpy(cmd_buf, buf, cmd_len); + + return ret; +} + +int dhd_change_mtu(dhd_pub_t *dhdp, int new_mtu, int ifidx) +{ + struct dhd_info *dhd = dhdp->info; + struct net_device *dev = NULL; + + ASSERT(dhd && dhd->iflist[ifidx]); + dev = dhd->iflist[ifidx]->net; + ASSERT(dev); + + if (netif_running(dev)) { + DHD_ERROR(("%s: Must be down to change its MTU", dev->name)); + return BCME_NOTDOWN; + } + +#define DHD_MIN_MTU 1500 +#define DHD_MAX_MTU 1752 + + if ((new_mtu < DHD_MIN_MTU) || (new_mtu > DHD_MAX_MTU)) { + DHD_ERROR(("%s: MTU size %d is invalid.\n", __FUNCTION__, new_mtu)); + return BCME_BADARG; + } + + dev->mtu = new_mtu; + return 0; +} + +#ifdef ARP_OFFLOAD_SUPPORT +/* add or remove AOE host ip(s) (up to 8 IPs on the interface) */ +void +aoe_update_host_ipv4_table(dhd_pub_t *dhd_pub, u32 ipa, bool add, int idx) +{ + u32 ipv4_buf[MAX_IPV4_ENTRIES]; /* temp save for AOE host_ip table */ + int i; + int ret; + + bzero(ipv4_buf, sizeof(ipv4_buf)); + + /* display what we've got */ + ret = dhd_arp_get_arp_hostip_table(dhd_pub, ipv4_buf, sizeof(ipv4_buf), idx); + DHD_ARPOE(("%s: hostip table read from Dongle:\n", __FUNCTION__)); +#ifdef AOE_DBG + dhd_print_buf(ipv4_buf, 32, 4); /* max 8 IPs 4b each */ +#endif + /* now we saved hoste_ip table, clr it in the dongle AOE */ + dhd_aoe_hostip_clr(dhd_pub, idx); + + if (ret) { + DHD_ERROR(("%s failed\n", __FUNCTION__)); + return; + } + + for (i = 0; i < MAX_IPV4_ENTRIES; i++) { + if (add && (ipv4_buf[i] == 0)) { + ipv4_buf[i] = ipa; + add = FALSE; /* added ipa to local table */ + DHD_ARPOE(("%s: Saved new IP in temp arp_hostip[%d]\n", + __FUNCTION__, i)); + } else if (ipv4_buf[i] == ipa) { + ipv4_buf[i] = 0; + DHD_ARPOE(("%s: removed IP:%x from temp table %d\n", + __FUNCTION__, ipa, i)); + } + + if (ipv4_buf[i] != 0) { + /* add back host_ip entries from our local cache */ + dhd_arp_offload_add_ip(dhd_pub, ipv4_buf[i], idx); + DHD_ARPOE(("%s: added IP:%x to dongle arp_hostip[%d]\n\n", + __FUNCTION__, ipv4_buf[i], i)); + } + } +#ifdef AOE_DBG + /* see the resulting hostip table */ + dhd_arp_get_arp_hostip_table(dhd_pub, ipv4_buf, sizeof(ipv4_buf), idx); + DHD_ARPOE(("%s: read back arp_hostip table:\n", __FUNCTION__)); + dhd_print_buf(ipv4_buf, 32, 4); /* max 8 IPs 4b each */ +#endif +} + +/* + * Notification mechanism from kernel to our driver. This function is called by the Linux kernel + * whenever there is an event related to an IP address. + * ptr : kernel provided pointer to IP address that has changed + */ +static int dhd_device_event(struct notifier_block *this, + unsigned long event, + void *ptr) +{ + struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; + + dhd_info_t *dhd; + dhd_pub_t *dhd_pub; + int idx; + + if (!dhd_arp_enable) + return NOTIFY_DONE; + if (!ifa || !(ifa->ifa_dev->dev)) + return NOTIFY_DONE; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) + /* Filter notifications meant for non Broadcom devices */ + if ((ifa->ifa_dev->dev->netdev_ops != &dhd_ops_pri) && + (ifa->ifa_dev->dev->netdev_ops != &dhd_ops_virt)) { +#ifdef WLP2P + if (!wl_cfgp2p_is_ifops(ifa->ifa_dev->dev->netdev_ops)) +#endif + return NOTIFY_DONE; + } +#endif /* LINUX_VERSION_CODE */ + + dhd = *(dhd_info_t **)netdev_priv(ifa->ifa_dev->dev); + if (!dhd) + return NOTIFY_DONE; + + dhd_pub = &dhd->pub; + + if (dhd_pub->arp_version == 1) { + idx = 0; + } + else { + for (idx = 0; idx < DHD_MAX_IFS; idx++) { + if (dhd->iflist[idx] && dhd->iflist[idx]->net == ifa->ifa_dev->dev) + break; + } + if (idx < DHD_MAX_IFS) + DHD_TRACE(("ifidx : %p %s %d\n", dhd->iflist[idx]->net, + dhd->iflist[idx]->name, dhd->iflist[idx]->idx)); + else { + DHD_ERROR(("Cannot find ifidx for(%s) set to 0\n", ifa->ifa_label)); + idx = 0; + } + } + + switch (event) { + case NETDEV_UP: + DHD_ARPOE(("%s: [%s] Up IP: 0x%x\n", + __FUNCTION__, ifa->ifa_label, ifa->ifa_address)); + + if (dhd->pub.busstate != DHD_BUS_DATA) { + DHD_ERROR(("%s: bus not ready, exit\n", __FUNCTION__)); + if (dhd->pend_ipaddr) { + DHD_ERROR(("%s: overwrite pending ipaddr: 0x%x\n", + __FUNCTION__, dhd->pend_ipaddr)); + } + dhd->pend_ipaddr = ifa->ifa_address; + break; + } + +#ifdef AOE_IP_ALIAS_SUPPORT + DHD_ARPOE(("%s:add aliased IP to AOE hostip cache\n", + __FUNCTION__)); + aoe_update_host_ipv4_table(dhd_pub, ifa->ifa_address, TRUE, idx); +#endif + break; + + case NETDEV_DOWN: + DHD_ARPOE(("%s: [%s] Down IP: 0x%x\n", + __FUNCTION__, ifa->ifa_label, ifa->ifa_address)); + dhd->pend_ipaddr = 0; +#ifdef AOE_IP_ALIAS_SUPPORT + DHD_ARPOE(("%s:interface is down, AOE clr all for this if\n", + __FUNCTION__)); + aoe_update_host_ipv4_table(dhd_pub, ifa->ifa_address, FALSE, idx); +#else + dhd_aoe_hostip_clr(&dhd->pub, idx); + dhd_aoe_arp_clr(&dhd->pub, idx); +#endif /* AOE_IP_ALIAS_SUPPORT */ + break; + + default: + DHD_ARPOE(("%s: do noting for [%s] Event: %lu\n", + __func__, ifa->ifa_label, event)); + break; + } + return NOTIFY_DONE; +} +#endif /* ARP_OFFLOAD_SUPPORT */ + +int +dhd_net_attach(dhd_pub_t *dhdp, int ifidx) +{ + dhd_info_t *dhd = (dhd_info_t *)dhdp->info; + struct net_device *net = NULL; + int err = 0; + uint8 temp_addr[ETHER_ADDR_LEN] = { 0x00, 0x90, 0x4c, 0x11, 0x22, 0x33 }; + + DHD_TRACE(("%s: ifidx %d\n", __FUNCTION__, ifidx)); + + ASSERT(dhd && dhd->iflist[ifidx]); + + net = dhd->iflist[ifidx]->net; + ASSERT(net); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) + ASSERT(!net->open); + net->get_stats = dhd_get_stats; + net->do_ioctl = dhd_ioctl_entry; + net->hard_start_xmit = dhd_start_xmit; + net->set_mac_address = dhd_set_mac_address; + net->set_multicast_list = dhd_set_multicast_list; + net->open = net->stop = NULL; +#else + ASSERT(!net->netdev_ops); + net->netdev_ops = &dhd_ops_virt; +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */ + + /* Ok, link into the network layer... */ + if (ifidx == 0) { + /* + * device functions for the primary interface only + */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) + net->open = dhd_open; + net->stop = dhd_stop; +#else + net->netdev_ops = &dhd_ops_pri; +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */ + if (!ETHER_ISNULLADDR(dhd->pub.mac.octet)) + memcpy(temp_addr, dhd->pub.mac.octet, ETHER_ADDR_LEN); + } else { + /* + * We have to use the primary MAC for virtual interfaces + */ + memcpy(temp_addr, dhd->iflist[ifidx]->mac_addr, ETHER_ADDR_LEN); + /* + * Android sets the locally administered bit to indicate that this is a + * portable hotspot. This will not work in simultaneous AP/STA mode, + * nor with P2P. Need to set the Donlge's MAC address, and then use that. + */ + if (!memcmp(temp_addr, dhd->iflist[0]->mac_addr, + ETHER_ADDR_LEN)) { + DHD_ERROR(("%s interface [%s]: set locally administered bit in MAC\n", + __func__, net->name)); + temp_addr[0] |= 0x02; + } + } + + net->hard_header_len = ETH_HLEN + dhd->pub.hdrlen; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) + net->ethtool_ops = &dhd_ethtool_ops; +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */ + +#if defined(CONFIG_WIRELESS_EXT) +#if WIRELESS_EXT < 19 + net->get_wireless_stats = dhd_get_wireless_stats; +#endif /* WIRELESS_EXT < 19 */ +#if WIRELESS_EXT > 12 + net->wireless_handlers = (struct iw_handler_def *)&wl_iw_handler_def; +#endif /* WIRELESS_EXT > 12 */ +#endif /* defined(CONFIG_WIRELESS_EXT) */ + + dhd->pub.rxsz = DBUS_RX_BUFFER_SIZE_DHD(net); + + memcpy(net->dev_addr, temp_addr, ETHER_ADDR_LEN); + + if ((err = register_netdev(net)) != 0) { + DHD_ERROR(("couldn't register the net device, err %d\n", err)); + goto fail; + } + printf("Broadcom Dongle Host Driver: register interface [%s]" + " MAC: "MACDBG"\n", + net->name, + MAC2STRDBG(net->dev_addr)); + +#if defined(SOFTAP) && defined(CONFIG_WIRELESS_EXT) && !defined(WL_CFG80211) + wl_iw_iscan_set_scan_broadcast_prep(net, 1); +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + if (ifidx == 0) { + dhd_registration_check = TRUE; + up(&dhd_registration_sem); + } +#endif + return 0; + +fail: +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) + net->open = NULL; +#else + net->netdev_ops = NULL; +#endif + return err; +} + +void +dhd_bus_detach(dhd_pub_t *dhdp) +{ + dhd_info_t *dhd; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (dhdp) { + dhd = (dhd_info_t *)dhdp->info; + if (dhd) { + + /* + * In case of Android cfg80211 driver, the bus is down in dhd_stop, + * calling stop again will cuase SD read/write errors. + */ + if (dhd->pub.busstate != DHD_BUS_DOWN) { + /* Stop the protocol module */ + dhd_prot_stop(&dhd->pub); + + /* Stop the bus module */ + dhd_bus_stop(dhd->pub.bus, TRUE); + } + +#if defined(OOB_INTR_ONLY) + bcmsdh_unregister_oob_intr(); +#endif /* defined(OOB_INTR_ONLY) */ + } + } +} + + +void dhd_detach(dhd_pub_t *dhdp) +{ + dhd_info_t *dhd; + unsigned long flags; + int timer_valid = FALSE; + + if (!dhdp) + return; + + dhd = (dhd_info_t *)dhdp->info; + if (!dhd) + return; + + DHD_TRACE(("%s: Enter state 0x%x\n", __FUNCTION__, dhd->dhd_state)); + + dhd->pub.up = 0; + if (!(dhd->dhd_state & DHD_ATTACH_STATE_DONE)) { + /* Give sufficient time for threads to start running in case + * dhd_attach() has failed + */ + osl_delay(1000*100); + } + + if (dhd->dhd_state & DHD_ATTACH_STATE_PROT_ATTACH) { + dhd_bus_detach(dhdp); + + if (dhdp->prot) + dhd_prot_detach(dhdp); + } + +#ifdef ARP_OFFLOAD_SUPPORT + unregister_inetaddr_notifier(&dhd_notifier); +#endif /* ARP_OFFLOAD_SUPPORT */ + +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) + if (dhd->dhd_state & DHD_ATTACH_STATE_EARLYSUSPEND_DONE) { + if (dhd->early_suspend.suspend) + unregister_early_suspend(&dhd->early_suspend); + } +#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + cancel_work_sync(&dhd->work_hang); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ + +#if defined(CONFIG_WIRELESS_EXT) + if (dhd->dhd_state & DHD_ATTACH_STATE_WL_ATTACH) { + /* Detatch and unlink in the iw */ + wl_iw_detach(); + } +#endif /* defined(CONFIG_WIRELESS_EXT) */ + + if (dhd->thr_sysioc_ctl.thr_pid >= 0) { + PROC_STOP(&dhd->thr_sysioc_ctl); + } + + /* delete all interfaces, start with virtual */ + if (dhd->dhd_state & DHD_ATTACH_STATE_ADD_IF) { + int i = 1; + dhd_if_t *ifp; + + /* Cleanup virtual interfaces */ + for (i = 1; i < DHD_MAX_IFS; i++) { + dhd_net_if_lock_local(dhd); + if (dhd->iflist[i]) { + dhd->iflist[i]->state = DHD_IF_DEL; + dhd->iflist[i]->idx = i; + dhd_op_if(dhd->iflist[i]); + } + + dhd_net_if_unlock_local(dhd); + } + /* delete primary interface 0 */ + ifp = dhd->iflist[0]; + ASSERT(ifp); + ASSERT(ifp->net); + if (ifp && ifp->net) { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) + if (ifp->net->open) +#else + if (ifp->net->netdev_ops == &dhd_ops_pri) +#endif + { + unregister_netdev(ifp->net); + free_netdev(ifp->net); + ifp->net = NULL; + MFREE(dhd->pub.osh, ifp, sizeof(*ifp)); + dhd->iflist[0] = NULL; + } + } + } + + /* Clear the watchdog timer */ + flags = dhd_os_spin_lock(&dhd->pub); + timer_valid = dhd->wd_timer_valid; + dhd->wd_timer_valid = FALSE; + dhd_os_spin_unlock(&dhd->pub, flags); + if (timer_valid) + del_timer_sync(&dhd->timer); + + if (dhd->dhd_state & DHD_ATTACH_STATE_THREADS_CREATED) { +#ifdef DHDTHREAD + if (dhd->thr_wdt_ctl.thr_pid >= 0) { + PROC_STOP(&dhd->thr_wdt_ctl); + } + + if (dhd->thr_dpc_ctl.thr_pid >= 0) { + PROC_STOP(&dhd->thr_dpc_ctl); + } + else +#endif /* DHDTHREAD */ + tasklet_kill(&dhd->tasklet); + } + +#ifdef WL_CFG80211 + if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) { + wl_cfg80211_detach(NULL); + dhd_monitor_uninit(); + } +#endif + + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) + unregister_pm_notifier(&dhd_sleep_pm_notifier); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ + /* && defined(CONFIG_PM_SLEEP) */ + + if (dhd->dhd_state & DHD_ATTACH_STATE_WAKELOCKS_INIT) { +#ifdef CONFIG_HAS_WAKELOCK + dhd->wakelock_counter = 0; + dhd->wakelock_wd_counter = 0; + dhd->wakelock_rx_timeout_enable = 0; + dhd->wakelock_ctrl_timeout_enable = 0; + wake_lock_destroy(&dhd->wl_wifi); + wake_lock_destroy(&dhd->wl_rxwake); + wake_lock_destroy(&dhd->wl_ctrlwake); + wake_lock_destroy(&dhd->wl_wdwake); +#endif /* CONFIG_HAS_WAKELOCK */ + } +} + + +void +dhd_free(dhd_pub_t *dhdp) +{ + dhd_info_t *dhd; + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (dhdp) { + int i; + for (i = 0; i < ARRAYSIZE(dhdp->reorder_bufs); i++) { + if (dhdp->reorder_bufs[i]) { + reorder_info_t *ptr; + uint32 buf_size = sizeof(struct reorder_info); + + ptr = dhdp->reorder_bufs[i]; + + buf_size += ((ptr->max_idx + 1) * sizeof(void*)); + DHD_REORDER(("free flow id buf %d, maxidx is %d, buf_size %d\n", + i, ptr->max_idx, buf_size)); + + MFREE(dhdp->osh, dhdp->reorder_bufs[i], buf_size); + dhdp->reorder_bufs[i] = NULL; + } + } + dhd = (dhd_info_t *)dhdp->info; + if (dhd) + MFREE(dhd->pub.osh, dhd, sizeof(*dhd)); + } +} + +static void __exit +dhd_module_cleanup(void) +{ + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + dhd_bus_unregister(); + +#if defined(CONFIG_WIFI_CONTROL_FUNC) + wl_android_wifictrl_func_del(); +#endif /* CONFIG_WIFI_CONTROL_FUNC */ + wl_android_exit(); + + /* Call customer gpio to turn off power with WL_REG_ON signal */ + dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF); +} + + +static int __init +dhd_module_init(void) +{ + int error = 0; + +#if defined(BCMLXSDMMC) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + int retry = POWERUP_MAX_RETRY; + int chip_up = 0; +#endif + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + wl_android_init(); + +#if defined(DHDTHREAD) + /* Sanity check on the module parameters */ + do { + /* Both watchdog and DPC as tasklets are ok */ + if ((dhd_watchdog_prio < 0) && (dhd_dpc_prio < 0)) + break; + + /* If both watchdog and DPC are threads, TX must be deferred */ + if ((dhd_watchdog_prio >= 0) && (dhd_dpc_prio >= 0) && dhd_deferred_tx) + break; + + DHD_ERROR(("Invalid module parameters.\n")); + return -EINVAL; + } while (0); +#endif + +#if defined(BCMLXSDMMC) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + do { + sema_init(&dhd_chipup_sem, 0); + dhd_bus_reg_sdio_notify(&dhd_chipup_sem); + dhd_customer_gpio_wlan_ctrl(WLAN_POWER_ON); +#if defined(CONFIG_WIFI_CONTROL_FUNC) + if (wl_android_wifictrl_func_add() < 0) { + dhd_bus_unreg_sdio_notify(); + goto fail_1; + } +#endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */ + if (down_timeout(&dhd_chipup_sem, + msecs_to_jiffies(POWERUP_WAIT_MS)) == 0) { + dhd_bus_unreg_sdio_notify(); + chip_up = 1; + break; + } + DHD_ERROR(("\nfailed to power up wifi chip, retry again (%d left) **\n\n", + retry+1)); + dhd_bus_unreg_sdio_notify(); +#if defined(CONFIG_WIFI_CONTROL_FUNC) + wl_android_wifictrl_func_del(); +#endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */ + dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF); + } while (retry-- > 0); + + if (!chip_up) { + DHD_ERROR(("\nfailed to power up wifi chip, max retry reached, exits **\n\n")); + return -ENODEV; + } +#else + dhd_customer_gpio_wlan_ctrl(WLAN_POWER_ON); +#if defined(CONFIG_WIFI_CONTROL_FUNC) + if (wl_android_wifictrl_func_add() < 0) + goto fail_1; +#endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */ + +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + sema_init(&dhd_registration_sem, 0); +#endif + + + error = dhd_bus_register(); + + if (!error) + printf("\n%s\n", dhd_version); + else { + DHD_ERROR(("%s: sdio_register_driver failed\n", __FUNCTION__)); + goto fail_1; + } + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + /* + * Wait till MMC sdio_register_driver callback called and made driver attach. + * It's needed to make sync up exit from dhd insmod and + * Kernel MMC sdio device callback registration + */ + if ((down_timeout(&dhd_registration_sem, + msecs_to_jiffies(DHD_REGISTRATION_TIMEOUT)) != 0) || + (dhd_registration_check != TRUE)) { + error = -ENODEV; + DHD_ERROR(("%s: sdio_register_driver timeout or error \n", __FUNCTION__)); + goto fail_2; + } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ +#if defined(WL_CFG80211) + wl_android_post_init(); +#endif /* defined(WL_CFG80211) */ + + return error; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) +fail_2: + dhd_bus_unregister(); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ + +fail_1: + +#if defined(CONFIG_WIFI_CONTROL_FUNC) + wl_android_wifictrl_func_del(); +#endif + + /* Call customer gpio to turn off power with WL_REG_ON signal */ + dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF); + + return error; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) +late_initcall(dhd_module_init); +#else +module_init(dhd_module_init); +#endif + +module_exit(dhd_module_cleanup); + +/* + * OS specific functions required to implement DHD driver in OS independent way + */ +int +dhd_os_proto_block(dhd_pub_t *pub) +{ + dhd_info_t * dhd = (dhd_info_t *)(pub->info); + + if (dhd) { + down(&dhd->proto_sem); + return 1; + } + + return 0; +} + +int +dhd_os_proto_unblock(dhd_pub_t *pub) +{ + dhd_info_t * dhd = (dhd_info_t *)(pub->info); + + if (dhd) { + up(&dhd->proto_sem); + return 1; + } + + return 0; +} + +unsigned int +dhd_os_get_ioctl_resp_timeout(void) +{ + return ((unsigned int)dhd_ioctl_timeout_msec); +} + +void +dhd_os_set_ioctl_resp_timeout(unsigned int timeout_msec) +{ + dhd_ioctl_timeout_msec = (int)timeout_msec; +} + +int +dhd_os_ioctl_resp_wait(dhd_pub_t *pub, uint *condition, bool *pending) +{ + dhd_info_t * dhd = (dhd_info_t *)(pub->info); + int timeout; + + /* Convert timeout in millsecond to jiffies */ + timeout = msecs_to_jiffies(dhd_ioctl_timeout_msec); + + timeout = wait_event_timeout(dhd->ioctl_resp_wait, (*condition), timeout); + return timeout; +} + +int +dhd_os_ioctl_resp_wake(dhd_pub_t *pub) +{ + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + + if (waitqueue_active(&dhd->ioctl_resp_wait)) { + wake_up(&dhd->ioctl_resp_wait); + } + + return 0; +} + +void +dhd_os_wd_timer(void *bus, uint wdtick) +{ + dhd_pub_t *pub = bus; + dhd_info_t *dhd = (dhd_info_t *)pub->info; + unsigned long flags; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (!dhd) + return; + + flags = dhd_os_spin_lock(pub); + + /* don't start the wd until fw is loaded */ + if (pub->busstate == DHD_BUS_DOWN) { + dhd_os_spin_unlock(pub, flags); + if (!wdtick) + DHD_OS_WD_WAKE_UNLOCK(pub); + return; + } + + /* totally stop the timer */ + if (!wdtick && dhd->wd_timer_valid == TRUE) { + dhd->wd_timer_valid = FALSE; + dhd_os_spin_unlock(pub, flags); +#ifdef DHDTHREAD + del_timer_sync(&dhd->timer); +#else + del_timer(&dhd->timer); +#endif /* DHDTHREAD */ + DHD_OS_WD_WAKE_UNLOCK(pub); + return; + } + + if (wdtick) { + DHD_OS_WD_WAKE_LOCK(pub); + dhd_watchdog_ms = (uint)wdtick; + /* Re arm the timer, at last watchdog period */ + mod_timer(&dhd->timer, jiffies + msecs_to_jiffies(dhd_watchdog_ms)); + dhd->wd_timer_valid = TRUE; + } + dhd_os_spin_unlock(pub, flags); +} + +void * +dhd_os_open_image(char *filename) +{ + struct file *fp; + + fp = filp_open(filename, O_RDONLY, 0); + /* + * 2.6.11 (FC4) supports filp_open() but later revs don't? + * Alternative: + * fp = open_namei(AT_FDCWD, filename, O_RD, 0); + * ??? + */ + if (IS_ERR(fp)) + fp = NULL; + + return fp; +} + +int +dhd_os_get_image_block(char *buf, int len, void *image) +{ + struct file *fp = (struct file *)image; + int rdlen; + + if (!image) + return 0; + + rdlen = kernel_read(fp, fp->f_pos, buf, len); + if (rdlen > 0) + fp->f_pos += rdlen; + + return rdlen; +} + +void +dhd_os_close_image(void *image) +{ + if (image) + filp_close((struct file *)image, NULL); +} + + +void +dhd_os_sdlock(dhd_pub_t *pub) +{ + dhd_info_t *dhd; + + dhd = (dhd_info_t *)(pub->info); + +#ifdef DHDTHREAD + if (dhd->threads_only) + down(&dhd->sdsem); + else +#endif /* DHDTHREAD */ + spin_lock_bh(&dhd->sdlock); +} + +void +dhd_os_sdunlock(dhd_pub_t *pub) +{ + dhd_info_t *dhd; + + dhd = (dhd_info_t *)(pub->info); + +#ifdef DHDTHREAD + if (dhd->threads_only) + up(&dhd->sdsem); + else +#endif /* DHDTHREAD */ + spin_unlock_bh(&dhd->sdlock); +} + +void +dhd_os_sdlock_txq(dhd_pub_t *pub) +{ + dhd_info_t *dhd; + + dhd = (dhd_info_t *)(pub->info); + spin_lock_bh(&dhd->txqlock); +} + +void +dhd_os_sdunlock_txq(dhd_pub_t *pub) +{ + dhd_info_t *dhd; + + dhd = (dhd_info_t *)(pub->info); + spin_unlock_bh(&dhd->txqlock); +} + +void +dhd_os_sdlock_rxq(dhd_pub_t *pub) +{ +} + +void +dhd_os_sdunlock_rxq(dhd_pub_t *pub) +{ +} + +void +dhd_os_sdtxlock(dhd_pub_t *pub) +{ + dhd_os_sdlock(pub); +} + +void +dhd_os_sdtxunlock(dhd_pub_t *pub) +{ + dhd_os_sdunlock(pub); +} + +#if defined(CONFIG_DHD_USE_STATIC_BUF) +uint8* dhd_os_prealloc(void *osh, int section, uint size) +{ + return (uint8*)wl_android_prealloc(section, size); +} + +void dhd_os_prefree(void *osh, void *addr, uint size) +{ +} +#endif /* defined(CONFIG_DHD_USE_STATIC_BUF) */ + +#if defined(CONFIG_WIRELESS_EXT) +struct iw_statistics * +dhd_get_wireless_stats(struct net_device *dev) +{ + int res = 0; + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + if (!dhd->pub.up) { + return NULL; + } + + res = wl_iw_get_wireless_stats(dev, &dhd->iw.wstats); + + if (res == 0) + return &dhd->iw.wstats; + else + return NULL; +} +#endif /* defined(CONFIG_WIRELESS_EXT) */ + +static int +dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, + wl_event_msg_t *event, void **data) +{ + int bcmerror = 0; + ASSERT(dhd != NULL); + + bcmerror = wl_host_event(&dhd->pub, ifidx, pktdata, event, data); + if (bcmerror != BCME_OK) + return (bcmerror); + +#if defined(CONFIG_WIRELESS_EXT) + if (event->bsscfgidx == 0) { + /* + * Wireless ext is on primary interface only + */ + + ASSERT(dhd->iflist[*ifidx] != NULL); + ASSERT(dhd->iflist[*ifidx]->net != NULL); + + if (dhd->iflist[*ifidx]->net) { + wl_iw_event(dhd->iflist[*ifidx]->net, event, *data); + } + } +#endif /* defined(CONFIG_WIRELESS_EXT) */ + +#ifdef WL_CFG80211 + if ((ntoh32(event->event_type) == WLC_E_IF) && + (((dhd_if_event_t *)*data)->action == WLC_E_IF_ADD)) + /* If ADD_IF has been called directly by wl utility then we + * should not report this. In case if ADD_IF was called from + * CFG stack, then too this event need not be reported back + */ + return (BCME_OK); + if ((wl_cfg80211_is_progress_ifchange() || + wl_cfg80211_is_progress_ifadd()) && (*ifidx != 0)) { + /* + * If IF_ADD/CHANGE operation is going on, + * discard any event received on the virtual I/F + */ + return (BCME_OK); + } + + ASSERT(dhd->iflist[*ifidx] != NULL); + ASSERT(dhd->iflist[*ifidx]->net != NULL); + if (dhd->iflist[*ifidx]->event2cfg80211 && dhd->iflist[*ifidx]->net) { + wl_cfg80211_event(dhd->iflist[*ifidx]->net, event, *data); + } +#endif /* defined(WL_CFG80211) */ + + return (bcmerror); +} + +/* send up locally generated event */ +void +dhd_sendup_event(dhd_pub_t *dhdp, wl_event_msg_t *event, void *data) +{ + switch (ntoh32(event->event_type)) { +#ifdef WLBTAMP + /* Send up locally generated AMP HCI Events */ + case WLC_E_BTA_HCI_EVENT: { + struct sk_buff *p, *skb; + bcm_event_t *msg; + wl_event_msg_t *p_bcm_event; + char *ptr; + uint32 len; + uint32 pktlen; + dhd_if_t *ifp; + dhd_info_t *dhd; + uchar *eth; + int ifidx; + + len = ntoh32(event->datalen); + pktlen = sizeof(bcm_event_t) + len + 2; + dhd = dhdp->info; + ifidx = dhd_ifname2idx(dhd, event->ifname); + + if ((p = PKTGET(dhdp->osh, pktlen, FALSE))) { + ASSERT(ISALIGNED((uintptr)PKTDATA(dhdp->osh, p), sizeof(uint32))); + + msg = (bcm_event_t *) PKTDATA(dhdp->osh, p); + + bcopy(&dhdp->mac, &msg->eth.ether_dhost, ETHER_ADDR_LEN); + bcopy(&dhdp->mac, &msg->eth.ether_shost, ETHER_ADDR_LEN); + ETHER_TOGGLE_LOCALADDR(&msg->eth.ether_shost); + + msg->eth.ether_type = hton16(ETHER_TYPE_BRCM); + + /* BCM Vendor specific header... */ + msg->bcm_hdr.subtype = hton16(BCMILCP_SUBTYPE_VENDOR_LONG); + msg->bcm_hdr.version = BCMILCP_BCM_SUBTYPEHDR_VERSION; + bcopy(BRCM_OUI, &msg->bcm_hdr.oui[0], DOT11_OUI_LEN); + + /* vendor spec header length + pvt data length (private indication + * hdr + actual message itself) + */ + msg->bcm_hdr.length = hton16(BCMILCP_BCM_SUBTYPEHDR_MINLENGTH + + BCM_MSG_LEN + sizeof(wl_event_msg_t) + (uint16)len); + msg->bcm_hdr.usr_subtype = hton16(BCMILCP_BCM_SUBTYPE_EVENT); + + PKTSETLEN(dhdp->osh, p, (sizeof(bcm_event_t) + len + 2)); + + /* copy wl_event_msg_t into sk_buf */ + + /* pointer to wl_event_msg_t in sk_buf */ + p_bcm_event = &msg->event; + bcopy(event, p_bcm_event, sizeof(wl_event_msg_t)); + + /* copy hci event into sk_buf */ + bcopy(data, (p_bcm_event + 1), len); + + msg->bcm_hdr.length = hton16(sizeof(wl_event_msg_t) + + ntoh16(msg->bcm_hdr.length)); + PKTSETLEN(dhdp->osh, p, (sizeof(bcm_event_t) + len + 2)); + + ptr = (char *)(msg + 1); + /* Last 2 bytes of the message are 0x00 0x00 to signal that there + * are no ethertypes which are following this + */ + ptr[len+0] = 0x00; + ptr[len+1] = 0x00; + + skb = PKTTONATIVE(dhdp->osh, p); + eth = skb->data; + len = skb->len; + + ifp = dhd->iflist[ifidx]; + if (ifp == NULL) + ifp = dhd->iflist[0]; + + ASSERT(ifp); + skb->dev = ifp->net; + skb->protocol = eth_type_trans(skb, skb->dev); + + skb->data = eth; + skb->len = len; + + /* Strip header, count, deliver upward */ + skb_pull(skb, ETH_HLEN); + + /* Send the packet */ + if (in_interrupt()) { + netif_rx(skb); + } else { + netif_rx_ni(skb); + } + } + else { + /* Could not allocate a sk_buf */ + DHD_ERROR(("%s: unable to alloc sk_buf", __FUNCTION__)); + } + break; + } /* case WLC_E_BTA_HCI_EVENT */ +#endif /* WLBTAMP */ + + default: + break; + } +} + +void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) + struct dhd_info *dhdinfo = dhd->info; + int timeout = msecs_to_jiffies(IOCTL_RESP_TIMEOUT); + dhd_os_sdunlock(dhd); + wait_event_timeout(dhdinfo->ctrl_wait, (*lockvar == FALSE), timeout); + dhd_os_sdlock(dhd); +#endif + return; +} + +void dhd_wait_event_wakeup(dhd_pub_t *dhd) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) + struct dhd_info *dhdinfo = dhd->info; + if (waitqueue_active(&dhdinfo->ctrl_wait)) + wake_up(&dhdinfo->ctrl_wait); +#endif + return; +} + +int +dhd_dev_reset(struct net_device *dev, uint8 flag) +{ + int ret; + + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + if (flag == TRUE) { + /* Issue wl down command before resetting the chip */ + if (dhd_wl_ioctl_cmd(&dhd->pub, WLC_DOWN, NULL, 0, TRUE, 0) < 0) { + DHD_TRACE(("%s: wl down failed\n", __FUNCTION__)); + } + } + + ret = dhd_bus_devreset(&dhd->pub, flag); + if (ret) { + DHD_ERROR(("%s: dhd_bus_devreset: %d\n", __FUNCTION__, ret)); + return ret; + } + + return ret; +} + +int net_os_set_suspend_disable(struct net_device *dev, int val) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int ret = 0; + + if (dhd) { + ret = dhd->pub.suspend_disable_flag; + dhd->pub.suspend_disable_flag = val; + } + return ret; +} + +int net_os_set_suspend(struct net_device *dev, int val, int force) +{ + int ret = 0; + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + if (dhd) { +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) + ret = dhd_set_suspend(val, &dhd->pub); +#else + ret = dhd_suspend_resume_helper(dhd, val, force); +#endif +#ifdef WL_CFG80211 + wl_cfg80211_update_power_mode(dev); +#endif + } + return ret; +} + +int net_os_set_suspend_bcn_li_dtim(struct net_device *dev, int val) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + if (dhd) + dhd->pub.suspend_bcn_li_dtim = val; + + return 0; +} + +#ifdef PKT_FILTER_SUPPORT +int net_os_rxfilter_add_remove(struct net_device *dev, int add_remove, int num) +{ +#ifndef GAN_LITE_NAT_KEEPALIVE_FILTER + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + char *filterp = NULL; + int ret = 0; + + if (!dhd || (num == DHD_UNICAST_FILTER_NUM) || + (num == DHD_MDNS_FILTER_NUM)) + return ret; + if (num >= dhd->pub.pktfilter_count) + return -EINVAL; + if (add_remove) { + switch (num) { + case DHD_BROADCAST_FILTER_NUM: + filterp = "101 0 0 0 0xFFFFFFFFFFFF 0xFFFFFFFFFFFF"; + break; + case DHD_MULTICAST4_FILTER_NUM: + filterp = "102 0 0 0 0xFFFFFF 0x01005E"; + break; + case DHD_MULTICAST6_FILTER_NUM: + filterp = "103 0 0 0 0xFFFF 0x3333"; + break; + default: + return -EINVAL; + } + } + dhd->pub.pktfilter[num] = filterp; + dhd_pktfilter_offload_set(&dhd->pub, dhd->pub.pktfilter[num]); + return ret; +#else + return 0; +#endif +} + +int dhd_os_enable_packet_filter(dhd_pub_t *dhdp, int val) +{ + int ret = 0; + + /* Packet filtering is set only if we still in early-suspend and + * we need either to turn it ON or turn it OFF + * We can always turn it OFF in case of early-suspend, but we turn it + * back ON only if suspend_disable_flag was not set + */ + if (dhdp && dhdp->up) { + if (dhdp->in_suspend) { + if (!val || (val && !dhdp->suspend_disable_flag)) + dhd_enable_packet_filter(val, dhdp); + } + } + return ret; + +} + +int net_os_enable_packet_filter(struct net_device *dev, int val) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + return dhd_os_enable_packet_filter(&dhd->pub, val); +} +#endif /* PKT_FILTER_SUPPORT */ + +int +dhd_dev_init_ioctl(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + return dhd_preinit_ioctls(&dhd->pub); +} + +#ifdef PNO_SUPPORT +/* Linux wrapper to call common dhd_pno_clean */ +int +dhd_dev_pno_reset(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + return (dhd_pno_clean(&dhd->pub)); +} + + +/* Linux wrapper to call common dhd_pno_enable */ +int +dhd_dev_pno_enable(struct net_device *dev, int pfn_enabled) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + return (dhd_pno_enable(&dhd->pub, pfn_enabled)); +} + + +/* Linux wrapper to call common dhd_pno_set */ +int +dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, int nssid, + ushort scan_fr, int pno_repeat, int pno_freq_expo_max) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + return (dhd_pno_set(&dhd->pub, ssids_local, nssid, scan_fr, pno_repeat, pno_freq_expo_max)); +} + +/* Linux wrapper to get pno status */ +int +dhd_dev_get_pno_status(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + return (dhd_pno_get_status(&dhd->pub)); +} + +#endif /* PNO_SUPPORT */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) +static void dhd_hang_process(struct work_struct *work) +{ + dhd_info_t *dhd; + struct net_device *dev; + + dhd = (dhd_info_t *)container_of(work, dhd_info_t, work_hang); + dev = dhd->iflist[0]->net; + + if (dev) { + rtnl_lock(); + dev_close(dev); + rtnl_unlock(); +#if defined(WL_WIRELESS_EXT) + wl_iw_send_priv_event(dev, "HANG"); +#endif +#if defined(WL_CFG80211) + wl_cfg80211_hang(dev, WLAN_REASON_UNSPECIFIED); +#endif + } +} + +int dhd_os_send_hang_message(dhd_pub_t *dhdp) +{ + int ret = 0; + if (dhdp) { + if (!dhdp->hang_was_sent) { + dhdp->hang_was_sent = 1; + schedule_work(&dhdp->info->work_hang); + } + } + return ret; +} + +int net_os_send_hang_message(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int ret = 0; + + if (dhd) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + ret = dhd_os_send_hang_message(&dhd->pub); +#else + ret = wl_cfg80211_hang(dev, WLAN_REASON_UNSPECIFIED); +#endif + return ret; +} +#endif + +void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec, bool notify) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + if (dhd && dhd->pub.up) { + memcpy(&dhd->pub.dhd_cspec, cspec, sizeof(wl_country_t)); +#ifdef WL_CFG80211 + wl_update_wiphybands(NULL, notify); +#endif + } +} + +void dhd_bus_band_set(struct net_device *dev, uint band) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + if (dhd && dhd->pub.up) { +#ifdef WL_CFG80211 + wl_update_wiphybands(NULL, true); +#endif + } +} + +void dhd_net_if_lock(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + dhd_net_if_lock_local(dhd); +} + +void dhd_net_if_unlock(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + dhd_net_if_unlock_local(dhd); +} + +static void dhd_net_if_lock_local(dhd_info_t *dhd) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + if (dhd) + mutex_lock(&dhd->dhd_net_if_mutex); +#endif +} + +static void dhd_net_if_unlock_local(dhd_info_t *dhd) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + if (dhd) + mutex_unlock(&dhd->dhd_net_if_mutex); +#endif +} + +static void dhd_suspend_lock(dhd_pub_t *pub) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + if (dhd) + mutex_lock(&dhd->dhd_suspend_mutex); +#endif +} + +static void dhd_suspend_unlock(dhd_pub_t *pub) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + if (dhd) + mutex_unlock(&dhd->dhd_suspend_mutex); +#endif +} + +unsigned long dhd_os_spin_lock(dhd_pub_t *pub) +{ + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + unsigned long flags = 0; + + if (dhd) + spin_lock_irqsave(&dhd->dhd_lock, flags); + + return flags; +} + +void dhd_os_spin_unlock(dhd_pub_t *pub, unsigned long flags) +{ + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + + if (dhd) + spin_unlock_irqrestore(&dhd->dhd_lock, flags); +} + +static int +dhd_get_pend_8021x_cnt(dhd_info_t *dhd) +{ + return (atomic_read(&dhd->pend_8021x_cnt)); +} + +#define MAX_WAIT_FOR_8021X_TX 25 + +int +dhd_wait_pend8021x(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int timeout = msecs_to_jiffies(10); + int ntimes = MAX_WAIT_FOR_8021X_TX; + int pend = dhd_get_pend_8021x_cnt(dhd); + + while (ntimes && pend) { + if (pend) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(timeout); + set_current_state(TASK_RUNNING); + ntimes--; + } + pend = dhd_get_pend_8021x_cnt(dhd); + } + if (ntimes == 0) + DHD_ERROR(("%s: TIMEOUT\n", __FUNCTION__)); + return pend; +} + +#ifdef DHD_DEBUG +int +write_to_file(dhd_pub_t *dhd, uint8 *buf, int size) +{ + int ret = 0; + struct file *fp; + mm_segment_t old_fs; + loff_t pos = 0; + + /* change to KERNEL_DS address limit */ + old_fs = get_fs(); + set_fs(KERNEL_DS); + + /* open file to write */ + fp = filp_open("/tmp/mem_dump", O_WRONLY|O_CREAT, 0640); + if (!fp) { + printf("%s: open file error\n", __FUNCTION__); + ret = -1; + goto exit; + } + + /* Write buf to file */ + fp->f_op->write(fp, buf, size, &pos); + +exit: + /* free buf before return */ + MFREE(dhd->osh, buf, size); + /* close file before return */ + if (fp) + filp_close(fp, current->files); + /* restore previous address limit */ + set_fs(old_fs); + + return ret; +} +#endif /* DHD_DEBUG */ + +int dhd_os_wake_lock_timeout(dhd_pub_t *pub) +{ + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + unsigned long flags; + int ret = 0; + + if (dhd) { + spin_lock_irqsave(&dhd->wakelock_spinlock, flags); + ret = dhd->wakelock_rx_timeout_enable > dhd->wakelock_ctrl_timeout_enable ? + dhd->wakelock_rx_timeout_enable : dhd->wakelock_ctrl_timeout_enable; +#ifdef CONFIG_HAS_WAKELOCK + if (dhd->wakelock_rx_timeout_enable) + wake_lock_timeout(&dhd->wl_rxwake, + msecs_to_jiffies(dhd->wakelock_rx_timeout_enable)); + if (dhd->wakelock_ctrl_timeout_enable) + wake_lock_timeout(&dhd->wl_ctrlwake, + msecs_to_jiffies(dhd->wakelock_ctrl_timeout_enable)); +#endif + dhd->wakelock_rx_timeout_enable = 0; + dhd->wakelock_ctrl_timeout_enable = 0; + spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); + } + return ret; +} + +int net_os_wake_lock_timeout(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int ret = 0; + + if (dhd) + ret = dhd_os_wake_lock_timeout(&dhd->pub); + return ret; +} + +int dhd_os_wake_lock_rx_timeout_enable(dhd_pub_t *pub, int val) +{ + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + unsigned long flags; + + if (dhd) { + spin_lock_irqsave(&dhd->wakelock_spinlock, flags); + if (val > dhd->wakelock_rx_timeout_enable) + dhd->wakelock_rx_timeout_enable = val; + spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); + } + return 0; +} + +int dhd_os_wake_lock_ctrl_timeout_enable(dhd_pub_t *pub, int val) +{ + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + unsigned long flags; + + if (dhd) { + spin_lock_irqsave(&dhd->wakelock_spinlock, flags); + if (val > dhd->wakelock_ctrl_timeout_enable) + dhd->wakelock_ctrl_timeout_enable = val; + spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); + } + return 0; +} + +int net_os_wake_lock_rx_timeout_enable(struct net_device *dev, int val) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int ret = 0; + + if (dhd) + ret = dhd_os_wake_lock_rx_timeout_enable(&dhd->pub, val); + return ret; +} + +int net_os_wake_lock_ctrl_timeout_enable(struct net_device *dev, int val) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int ret = 0; + + if (dhd) + ret = dhd_os_wake_lock_ctrl_timeout_enable(&dhd->pub, val); + return ret; +} + +int dhd_os_wake_lock(dhd_pub_t *pub) +{ + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + unsigned long flags; + int ret = 0; + + if (dhd) { + spin_lock_irqsave(&dhd->wakelock_spinlock, flags); +#ifdef CONFIG_HAS_WAKELOCK + if (!dhd->wakelock_counter) + wake_lock(&dhd->wl_wifi); +#endif + dhd->wakelock_counter++; + ret = dhd->wakelock_counter; + spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); + } + return ret; +} + +int net_os_wake_lock(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int ret = 0; + + if (dhd) + ret = dhd_os_wake_lock(&dhd->pub); + return ret; +} + +int dhd_os_wake_unlock(dhd_pub_t *pub) +{ + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + unsigned long flags; + int ret = 0; + + dhd_os_wake_lock_timeout(pub); + if (dhd) { + spin_lock_irqsave(&dhd->wakelock_spinlock, flags); + if (dhd->wakelock_counter) { + dhd->wakelock_counter--; +#ifdef CONFIG_HAS_WAKELOCK + if (!dhd->wakelock_counter) + wake_unlock(&dhd->wl_wifi); +#endif + ret = dhd->wakelock_counter; + } + spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); + } + return ret; +} + +int dhd_os_check_wakelock(void *dhdp) +{ +#ifdef CONFIG_HAS_WAKELOCK + dhd_pub_t *pub = (dhd_pub_t *)dhdp; + dhd_info_t *dhd; + + if (!pub) + return 0; + dhd = (dhd_info_t *)(pub->info); + + if (dhd && (wake_lock_active(&dhd->wl_wifi) || + wake_lock_active(&dhd->wl_wdwake))) + return 1; +#endif + return 0; +} + +int net_os_wake_unlock(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int ret = 0; + + if (dhd) + ret = dhd_os_wake_unlock(&dhd->pub); + return ret; +} + +int dhd_os_wd_wake_lock(dhd_pub_t *pub) +{ + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + unsigned long flags; + int ret = 0; + + if (dhd) { + spin_lock_irqsave(&dhd->wakelock_spinlock, flags); +#ifdef CONFIG_HAS_WAKELOCK + if (!dhd->wakelock_wd_counter) + wake_lock(&dhd->wl_wdwake); +#endif + dhd->wakelock_wd_counter++; + ret = dhd->wakelock_wd_counter; + spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); + } + return ret; +} + +int dhd_os_wd_wake_unlock(dhd_pub_t *pub) +{ + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + unsigned long flags; + int ret = 0; + + if (dhd) { + spin_lock_irqsave(&dhd->wakelock_spinlock, flags); + if (dhd->wakelock_wd_counter) { + dhd->wakelock_wd_counter = 0; +#ifdef CONFIG_HAS_WAKELOCK + wake_unlock(&dhd->wl_wdwake); +#endif + } + spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); + } + return ret; +} + +int dhd_os_check_if_up(void *dhdp) +{ + dhd_pub_t *pub = (dhd_pub_t *)dhdp; + + if (!pub) + return 0; + return pub->up; +} + +/* function to collect firmware, chip id and chip version info */ +void dhd_set_version_info(dhd_pub_t *dhdp, char *fw) +{ + int i; + + i = snprintf(info_string, sizeof(info_string), + " Driver: %s\n Firmware: %s ", EPI_VERSION_STR, fw); + + if (!dhdp) + return; + + i = snprintf(&info_string[i], sizeof(info_string) - i, + "\n Chip: %x Rev %x Pkg %x", dhd_bus_chip_id(dhdp), + dhd_bus_chiprev_id(dhdp), dhd_bus_chippkg_id(dhdp)); +} + +int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd) +{ + int ifidx; + int ret = 0; + dhd_info_t *dhd = NULL; + + if (!net || !netdev_priv(net)) { + DHD_ERROR(("%s invalid parameter\n", __FUNCTION__)); + return -EINVAL; + } + + dhd = *(dhd_info_t **)netdev_priv(net); + ifidx = dhd_net2idx(dhd, net); + if (ifidx == DHD_BAD_IF) { + DHD_ERROR(("%s bad ifidx\n", __FUNCTION__)); + return -ENODEV; + } + + DHD_OS_WAKE_LOCK(&dhd->pub); + ret = dhd_wl_ioctl(&dhd->pub, ifidx, ioc, ioc->buf, ioc->len); + dhd_check_hang(net, &dhd->pub, ret); + DHD_OS_WAKE_UNLOCK(&dhd->pub); + + return ret; +} + +bool dhd_os_check_hang(dhd_pub_t *dhdp, int ifidx, int ret) +{ + struct net_device *net; + + net = dhd_idx2net(dhdp, ifidx); + return dhd_check_hang(net, dhdp, ret); +} + + +#ifdef PROP_TXSTATUS +extern int dhd_wlfc_interface_entry_update(void* state, ewlfc_mac_entry_action_t action, uint8 ifid, + uint8 iftype, uint8* ea); +extern int dhd_wlfc_FIFOcreditmap_update(void* state, uint8* credits); + +int dhd_wlfc_interface_event(struct dhd_info *dhd, + ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea) +{ + int ret = BCME_OK; + + dhd_os_wlfc_block(&dhd->pub); + if (dhd->pub.wlfc_state != NULL) + ret = dhd_wlfc_interface_entry_update(dhd->pub.wlfc_state, action, ifid, iftype, ea); + dhd_os_wlfc_unblock(&dhd->pub); + return ret; +} + +int dhd_wlfc_FIFOcreditmap_event(struct dhd_info *dhd, uint8* event_data) +{ + int ret = BCME_OK; + + dhd_os_wlfc_block(&dhd->pub); + if (dhd->pub.wlfc_state != NULL) + ret = dhd_wlfc_FIFOcreditmap_update(dhd->pub.wlfc_state, event_data); + dhd_os_wlfc_unblock(&dhd->pub); + return ret; +} + +int dhd_wlfc_event(struct dhd_info *dhd) +{ + int ret; + + dhd_os_wlfc_block(&dhd->pub); + ret = dhd_wlfc_enable(&dhd->pub); + dhd_os_wlfc_unblock(&dhd->pub); + return ret; +} +#endif /* PROP_TXSTATUS */ + +#ifdef BCMDBGFS + +#include + +extern uint32 dhd_readregl(void *bp, uint32 addr); +extern uint32 dhd_writeregl(void *bp, uint32 addr, uint32 data); + +typedef struct dhd_dbgfs { + struct dentry *debugfs_dir; + struct dentry *debugfs_mem; + dhd_pub_t *dhdp; + uint32 size; +} dhd_dbgfs_t; + +dhd_dbgfs_t g_dbgfs; + +static int +dhd_dbg_state_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t +dhd_dbg_state_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + ssize_t rval; + uint32 tmp; + loff_t pos = *ppos; + size_t ret; + + if (pos < 0) + return -EINVAL; + if (pos >= g_dbgfs.size || !count) + return 0; + if (count > g_dbgfs.size - pos) + count = g_dbgfs.size - pos; + + /* Basically enforce aligned 4 byte reads. It's up to the user to work out the details */ + tmp = dhd_readregl(g_dbgfs.dhdp->bus, file->f_pos & (~3)); + + ret = copy_to_user(ubuf, &tmp, 4); + if (ret == count) + return -EFAULT; + + count -= ret; + *ppos = pos + count; + rval = count; + + return rval; +} + + +static ssize_t +dhd_debugfs_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) +{ + loff_t pos = *ppos; + size_t ret; + uint32 buf; + + if (pos < 0) + return -EINVAL; + if (pos >= g_dbgfs.size || !count) + return 0; + if (count > g_dbgfs.size - pos) + count = g_dbgfs.size - pos; + + ret = copy_from_user(&buf, ubuf, sizeof(uint32)); + if (ret == count) + return -EFAULT; + + /* Basically enforce aligned 4 byte writes. It's up to the user to work out the details */ + dhd_writeregl(g_dbgfs.dhdp->bus, file->f_pos & (~3), buf); + + return count; +} + + +loff_t +dhd_debugfs_lseek(struct file *file, loff_t off, int whence) +{ + loff_t pos = -1; + + switch (whence) { + case 0: + pos = off; + break; + case 1: + pos = file->f_pos + off; + break; + case 2: + pos = g_dbgfs.size - off; + } + return (pos < 0 || pos > g_dbgfs.size) ? -EINVAL : (file->f_pos = pos); +} + +static const struct file_operations dhd_dbg_state_ops = { + .read = dhd_dbg_state_read, + .write = dhd_debugfs_write, + .open = dhd_dbg_state_open, + .llseek = dhd_debugfs_lseek +}; + +static void dhd_dbg_create(void) +{ + if (g_dbgfs.debugfs_dir) { + g_dbgfs.debugfs_mem = debugfs_create_file("mem", 0644, g_dbgfs.debugfs_dir, + NULL, &dhd_dbg_state_ops); + } +} + +void dhd_dbg_init(dhd_pub_t *dhdp) +{ + int err; + + g_dbgfs.dhdp = dhdp; + g_dbgfs.size = 0x20000000; /* Allow access to various cores regs */ + + g_dbgfs.debugfs_dir = debugfs_create_dir("dhd", 0); + if (IS_ERR(g_dbgfs.debugfs_dir)) { + err = PTR_ERR(g_dbgfs.debugfs_dir); + g_dbgfs.debugfs_dir = NULL; + return; + } + + dhd_dbg_create(); + + return; +} + +void dhd_dbg_remove(void) +{ + debugfs_remove(g_dbgfs.debugfs_mem); + debugfs_remove(g_dbgfs.debugfs_dir); + + bzero((unsigned char *) &g_dbgfs, sizeof(g_dbgfs)); + +} +#endif /* ifdef BCMDBGFS */ + +#ifdef WLMEDIA_HTSF + +static +void dhd_htsf_addtxts(dhd_pub_t *dhdp, void *pktbuf) +{ + dhd_info_t *dhd = (dhd_info_t *)(dhdp->info); + struct sk_buff *skb; + uint32 htsf = 0; + uint16 dport = 0, oldmagic = 0xACAC; + char *p1; + htsfts_t ts; + + /* timestamp packet */ + + p1 = (char*) PKTDATA(dhdp->osh, pktbuf); + + if (PKTLEN(dhdp->osh, pktbuf) > HTSF_MINLEN) { +/* memcpy(&proto, p1+26, 4); */ + memcpy(&dport, p1+40, 2); +/* proto = ((ntoh32(proto))>> 16) & 0xFF; */ + dport = ntoh16(dport); + } + + /* timestamp only if icmp or udb iperf with port 5555 */ +/* if (proto == 17 && dport == tsport) { */ + if (dport >= tsport && dport <= tsport + 20) { + + skb = (struct sk_buff *) pktbuf; + + htsf = dhd_get_htsf(dhd, 0); + memset(skb->data + 44, 0, 2); /* clear checksum */ + memcpy(skb->data+82, &oldmagic, 2); + memcpy(skb->data+84, &htsf, 4); + + memset(&ts, 0, sizeof(htsfts_t)); + ts.magic = HTSFMAGIC; + ts.prio = PKTPRIO(pktbuf); + ts.seqnum = htsf_seqnum++; + ts.c10 = get_cycles(); + ts.t10 = htsf; + ts.endmagic = HTSFENDMAGIC; + + memcpy(skb->data + HTSF_HOSTOFFSET, &ts, sizeof(ts)); + } +} + +static void dhd_dump_htsfhisto(histo_t *his, char *s) +{ + int pktcnt = 0, curval = 0, i; + for (i = 0; i < (NUMBIN-2); i++) { + curval += 500; + printf("%d ", his->bin[i]); + pktcnt += his->bin[i]; + } + printf(" max: %d TotPkt: %d neg: %d [%s]\n", his->bin[NUMBIN-2], pktcnt, + his->bin[NUMBIN-1], s); +} + +static +void sorttobin(int value, histo_t *histo) +{ + int i, binval = 0; + + if (value < 0) { + histo->bin[NUMBIN-1]++; + return; + } + if (value > histo->bin[NUMBIN-2]) /* store the max value */ + histo->bin[NUMBIN-2] = value; + + for (i = 0; i < (NUMBIN-2); i++) { + binval += 500; /* 500m s bins */ + if (value <= binval) { + histo->bin[i]++; + return; + } + } + histo->bin[NUMBIN-3]++; +} + +static +void dhd_htsf_addrxts(dhd_pub_t *dhdp, void *pktbuf) +{ + dhd_info_t *dhd = (dhd_info_t *)dhdp->info; + struct sk_buff *skb; + char *p1; + uint16 old_magic; + int d1, d2, d3, end2end; + htsfts_t *htsf_ts; + uint32 htsf; + + skb = PKTTONATIVE(dhdp->osh, pktbuf); + p1 = (char*)PKTDATA(dhdp->osh, pktbuf); + + if (PKTLEN(osh, pktbuf) > HTSF_MINLEN) { + memcpy(&old_magic, p1+78, 2); + htsf_ts = (htsfts_t*) (p1 + HTSF_HOSTOFFSET - 4); + } + else + return; + + if (htsf_ts->magic == HTSFMAGIC) { + htsf_ts->tE0 = dhd_get_htsf(dhd, 0); + htsf_ts->cE0 = get_cycles(); + } + + if (old_magic == 0xACAC) { + + tspktcnt++; + htsf = dhd_get_htsf(dhd, 0); + memcpy(skb->data+92, &htsf, sizeof(uint32)); + + memcpy(&ts[tsidx].t1, skb->data+80, 16); + + d1 = ts[tsidx].t2 - ts[tsidx].t1; + d2 = ts[tsidx].t3 - ts[tsidx].t2; + d3 = ts[tsidx].t4 - ts[tsidx].t3; + end2end = ts[tsidx].t4 - ts[tsidx].t1; + + sorttobin(d1, &vi_d1); + sorttobin(d2, &vi_d2); + sorttobin(d3, &vi_d3); + sorttobin(end2end, &vi_d4); + + if (end2end > 0 && end2end > maxdelay) { + maxdelay = end2end; + maxdelaypktno = tspktcnt; + memcpy(&maxdelayts, &ts[tsidx], 16); + } + if (++tsidx >= TSMAX) + tsidx = 0; + } +} + +uint32 dhd_get_htsf(dhd_info_t *dhd, int ifidx) +{ + uint32 htsf = 0, cur_cycle, delta, delta_us; + uint32 factor, baseval, baseval2; + cycles_t t; + + t = get_cycles(); + cur_cycle = t; + + if (cur_cycle > dhd->htsf.last_cycle) + delta = cur_cycle - dhd->htsf.last_cycle; + else { + delta = cur_cycle + (0xFFFFFFFF - dhd->htsf.last_cycle); + } + + delta = delta >> 4; + + if (dhd->htsf.coef) { + /* times ten to get the first digit */ + factor = (dhd->htsf.coef*10 + dhd->htsf.coefdec1); + baseval = (delta*10)/factor; + baseval2 = (delta*10)/(factor+1); + delta_us = (baseval - (((baseval - baseval2) * dhd->htsf.coefdec2)) / 10); + htsf = (delta_us << 4) + dhd->htsf.last_tsf + HTSF_BUS_DELAY; + } + else { + DHD_ERROR(("-------dhd->htsf.coef = 0 -------\n")); + } + + return htsf; +} + +static void dhd_dump_latency(void) +{ + int i, max = 0; + int d1, d2, d3, d4, d5; + + printf("T1 T2 T3 T4 d1 d2 t4-t1 i \n"); + for (i = 0; i < TSMAX; i++) { + d1 = ts[i].t2 - ts[i].t1; + d2 = ts[i].t3 - ts[i].t2; + d3 = ts[i].t4 - ts[i].t3; + d4 = ts[i].t4 - ts[i].t1; + d5 = ts[max].t4-ts[max].t1; + if (d4 > d5 && d4 > 0) { + max = i; + } + printf("%08X %08X %08X %08X \t%d %d %d %d i=%d\n", + ts[i].t1, ts[i].t2, ts[i].t3, ts[i].t4, + d1, d2, d3, d4, i); + } + + printf("current idx = %d \n", tsidx); + + printf("Highest latency %d pkt no.%d total=%d\n", maxdelay, maxdelaypktno, tspktcnt); + printf("%08X %08X %08X %08X \t%d %d %d %d\n", + maxdelayts.t1, maxdelayts.t2, maxdelayts.t3, maxdelayts.t4, + maxdelayts.t2 - maxdelayts.t1, + maxdelayts.t3 - maxdelayts.t2, + maxdelayts.t4 - maxdelayts.t3, + maxdelayts.t4 - maxdelayts.t1); +} + + +static int +dhd_ioctl_htsf_get(dhd_info_t *dhd, int ifidx) +{ + wl_ioctl_t ioc; + char buf[32]; + int ret; + uint32 s1, s2; + + struct tsf { + uint32 low; + uint32 high; + } tsf_buf; + + memset(&ioc, 0, sizeof(ioc)); + memset(&tsf_buf, 0, sizeof(tsf_buf)); + + ioc.cmd = WLC_GET_VAR; + ioc.buf = buf; + ioc.len = (uint)sizeof(buf); + ioc.set = FALSE; + + strncpy(buf, "tsf", sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; + s1 = dhd_get_htsf(dhd, 0); + if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) { + if (ret == -EIO) { + DHD_ERROR(("%s: tsf is not supported by device\n", + dhd_ifname(&dhd->pub, ifidx))); + return -EOPNOTSUPP; + } + return ret; + } + s2 = dhd_get_htsf(dhd, 0); + + memcpy(&tsf_buf, buf, sizeof(tsf_buf)); + printf(" TSF_h=%04X lo=%08X Calc:htsf=%08X, coef=%d.%d%d delta=%d ", + tsf_buf.high, tsf_buf.low, s2, dhd->htsf.coef, dhd->htsf.coefdec1, + dhd->htsf.coefdec2, s2-tsf_buf.low); + printf("lasttsf=%08X lastcycle=%08X\n", dhd->htsf.last_tsf, dhd->htsf.last_cycle); + return 0; +} + +void htsf_update(dhd_info_t *dhd, void *data) +{ + static ulong cur_cycle = 0, prev_cycle = 0; + uint32 htsf, tsf_delta = 0; + uint32 hfactor = 0, cyc_delta, dec1 = 0, dec2, dec3, tmp; + ulong b, a; + cycles_t t; + + /* cycles_t in inlcude/mips/timex.h */ + + t = get_cycles(); + + prev_cycle = cur_cycle; + cur_cycle = t; + + if (cur_cycle > prev_cycle) + cyc_delta = cur_cycle - prev_cycle; + else { + b = cur_cycle; + a = prev_cycle; + cyc_delta = cur_cycle + (0xFFFFFFFF - prev_cycle); + } + + if (data == NULL) + printf(" tsf update ata point er is null \n"); + + memcpy(&prev_tsf, &cur_tsf, sizeof(tsf_t)); + memcpy(&cur_tsf, data, sizeof(tsf_t)); + + if (cur_tsf.low == 0) { + DHD_INFO((" ---- 0 TSF, do not update, return\n")); + return; + } + + if (cur_tsf.low > prev_tsf.low) + tsf_delta = (cur_tsf.low - prev_tsf.low); + else { + DHD_INFO((" ---- tsf low is smaller cur_tsf= %08X, prev_tsf=%08X, \n", + cur_tsf.low, prev_tsf.low)); + if (cur_tsf.high > prev_tsf.high) { + tsf_delta = cur_tsf.low + (0xFFFFFFFF - prev_tsf.low); + DHD_INFO((" ---- Wrap around tsf coutner adjusted TSF=%08X\n", tsf_delta)); + } + else + return; /* do not update */ + } + + if (tsf_delta) { + hfactor = cyc_delta / tsf_delta; + tmp = (cyc_delta - (hfactor * tsf_delta))*10; + dec1 = tmp/tsf_delta; + dec2 = ((tmp - dec1*tsf_delta)*10) / tsf_delta; + tmp = (tmp - (dec1*tsf_delta))*10; + dec3 = ((tmp - dec2*tsf_delta)*10) / tsf_delta; + + if (dec3 > 4) { + if (dec2 == 9) { + dec2 = 0; + if (dec1 == 9) { + dec1 = 0; + hfactor++; + } + else { + dec1++; + } + } + else + dec2++; + } + } + + if (hfactor) { + htsf = ((cyc_delta * 10) / (hfactor*10+dec1)) + prev_tsf.low; + dhd->htsf.coef = hfactor; + dhd->htsf.last_cycle = cur_cycle; + dhd->htsf.last_tsf = cur_tsf.low; + dhd->htsf.coefdec1 = dec1; + dhd->htsf.coefdec2 = dec2; + } + else { + htsf = prev_tsf.low; + } +} + +#endif /* WLMEDIA_HTSF */ diff --git a/drivers/net/wireless/bcmdhd/dhd_linux_sched.c b/drivers/net/wireless/bcmdhd/dhd_linux_sched.c new file mode 100644 index 00000000..290caf7e --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_linux_sched.c @@ -0,0 +1,39 @@ +/* + * Expose some of the kernel scheduler routines + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhd_linux_sched.c 291086 2011-10-21 01:17:24Z $ + */ +#include +#include +#include +#include +#include + +int setScheduler(struct task_struct *p, int policy, struct sched_param *param) +{ + int rc = 0; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) + rc = sched_setscheduler(p, policy, param); +#endif /* LinuxVer */ + return rc; +} diff --git a/drivers/net/wireless/bcmdhd/dhd_proto.h b/drivers/net/wireless/bcmdhd/dhd_proto.h new file mode 100644 index 00000000..09d54680 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_proto.h @@ -0,0 +1,113 @@ +/* + * Header file describing the internal (inter-module) DHD interfaces. + * + * Provides type definitions and function prototypes used to link the + * DHD OS, bus, and protocol modules. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhd_proto.h 343390 2012-07-06 22:34:19Z $ + */ + +#ifndef _dhd_proto_h_ +#define _dhd_proto_h_ + +#include +#include + +#ifndef IOCTL_RESP_TIMEOUT +#define IOCTL_RESP_TIMEOUT 2000 /* In milli second default value for Production FW */ +#endif /* IOCTL_RESP_TIMEOUT */ + +/* + * Exported from the dhd protocol module (dhd_cdc, dhd_rndis) + */ + +/* Linkage, sets prot link and updates hdrlen in pub */ +extern int dhd_prot_attach(dhd_pub_t *dhdp); + +/* Unlink, frees allocated protocol memory (including dhd_prot) */ +extern void dhd_prot_detach(dhd_pub_t *dhdp); + +/* Initialize protocol: sync w/dongle state. + * Sets dongle media info (iswl, drv_version, mac address). + */ +extern int dhd_prot_init(dhd_pub_t *dhdp); + +/* Stop protocol: sync w/dongle state. */ +extern void dhd_prot_stop(dhd_pub_t *dhdp); +#ifdef PROP_TXSTATUS +extern int dhd_wlfc_init(dhd_pub_t *dhd); +extern void dhd_wlfc_deinit(dhd_pub_t *dhd); +#endif /* PROP_TXSTATUS */ + +/* Add any protocol-specific data header. + * Caller must reserve prot_hdrlen prepend space. + */ +extern void dhd_prot_hdrpush(dhd_pub_t *, int ifidx, void *txp); + +/* Remove any protocol-specific data header. */ +extern int dhd_prot_hdrpull(dhd_pub_t *, int *ifidx, void *rxp, uchar *buf, uint *len); + +/* Use protocol to issue ioctl to dongle */ +extern int dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len); + +/* Handles a protocol control response asynchronously */ +extern int dhd_prot_ctl_complete(dhd_pub_t *dhd); + +/* Check for and handle local prot-specific iovar commands */ +extern int dhd_prot_iovar_op(dhd_pub_t *dhdp, const char *name, + void *params, int plen, void *arg, int len, bool set); + +/* Add prot dump output to a buffer */ +extern void dhd_prot_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf); + +/* Update local copy of dongle statistics */ +extern void dhd_prot_dstats(dhd_pub_t *dhdp); + +extern int dhd_ioctl(dhd_pub_t * dhd_pub, dhd_ioctl_t *ioc, void * buf, uint buflen); + +extern int dhd_preinit_ioctls(dhd_pub_t *dhd); + +#ifdef PROP_TXSTATUS +extern int dhd_wlfc_enque_sendq(void* state, int prec, void* p); +extern int dhd_wlfc_commit_packets(void* state, f_commitpkt_t fcommit, void* commit_ctx); +extern void dhd_wlfc_cleanup(dhd_pub_t *dhd); +#endif /* PROP_TXSTATUS */ + +extern int dhd_process_pkt_reorder_info(dhd_pub_t *dhd, uchar *reorder_info_buf, + uint reorder_info_len, void **pkt, uint32 *free_buf_count); + + +/******************************** + * For version-string expansion * + */ +#if defined(BDC) +#define DHD_PROTOCOL "bdc" +#elif defined(CDC) +#define DHD_PROTOCOL "cdc" +#elif defined(RNDIS) +#define DHD_PROTOCOL "rndis" +#else +#define DHD_PROTOCOL "unknown" +#endif /* proto */ + +#endif /* _dhd_proto_h_ */ diff --git a/drivers/net/wireless/bcmdhd/dhd_sdio.c b/drivers/net/wireless/bcmdhd/dhd_sdio.c new file mode 100644 index 00000000..c6342934 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_sdio.c @@ -0,0 +1,7694 @@ +/* + * DHD Bus Module for SDIO + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhd_sdio.c 378263 2013-01-11 03:03:05Z $ + */ + +#include +#include +#include + +#ifdef BCMEMBEDIMAGE +#include BCMEMBEDIMAGE +#endif /* BCMEMBEDIMAGE */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#if defined(DHD_DEBUG) +#include +#include +#endif /* defined(DHD_DEBUG) */ +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef DHDSDIO_MEM_DUMP_FNAME +#define DHDSDIO_MEM_DUMP_FNAME "mem_dump" +#endif + +#define QLEN 256 /* bulk rx and tx queue lengths */ +#define FCHI (QLEN - 10) +#define FCLOW (FCHI / 2) +#define PRIOMASK 7 + +#define TXRETRIES 2 /* # of retries for tx frames */ + +#define DHD_RXBOUND 50 /* Default for max rx frames in one scheduling */ + +#define DHD_TXBOUND 20 /* Default for max tx frames in one scheduling */ + +#define DHD_TXMINMAX 1 /* Max tx frames if rx still pending */ + +#define MEMBLOCK 2048 /* Block size used for downloading of dongle image */ +#define MAX_NVRAMBUF_SIZE 4096 /* max nvram buf size */ +#define MAX_DATA_BUF (32 * 1024) /* Must be large enough to hold biggest possible glom */ + +#ifndef DHD_FIRSTREAD +#define DHD_FIRSTREAD 32 +#endif +#if !ISPOWEROF2(DHD_FIRSTREAD) +#error DHD_FIRSTREAD is not a power of 2! +#endif + +#ifdef BCMSDIOH_TXGLOM +/* Total length of TX frame header for dongle protocol */ +#define SDPCM_HDRLEN (SDPCM_FRAMETAG_LEN + SDPCM_HWEXT_LEN + SDPCM_SWHEADER_LEN) +/* Total length of RX frame for dongle protocol */ +#else +/* Total length of TX frame header for dongle protocol */ +#define SDPCM_HDRLEN (SDPCM_FRAMETAG_LEN + SDPCM_SWHEADER_LEN) +#endif + +#define SDPCM_HDRLEN_RX (SDPCM_FRAMETAG_LEN + SDPCM_SWHEADER_LEN) + +#ifdef SDTEST +#define SDPCM_RESERVE (SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + DHD_SDALIGN) +#else +#define SDPCM_RESERVE (SDPCM_HDRLEN + DHD_SDALIGN) +#endif + +/* Space for header read, limit for data packets */ +#ifndef MAX_HDR_READ +#define MAX_HDR_READ 32 +#endif +#if !ISPOWEROF2(MAX_HDR_READ) +#error MAX_HDR_READ is not a power of 2! +#endif + +#define MAX_RX_DATASZ 2048 + +/* Maximum milliseconds to wait for F2 to come up */ +#define DHD_WAIT_F2RDY 3000 + +/* Bump up limit on waiting for HT to account for first startup; + * if the image is doing a CRC calculation before programming the PMU + * for HT availability, it could take a couple hundred ms more, so + * max out at a 1 second (1000000us). + */ +#if (PMU_MAX_TRANSITION_DLY <= 1000000) +#undef PMU_MAX_TRANSITION_DLY +#define PMU_MAX_TRANSITION_DLY 1000000 +#endif + +/* Value for ChipClockCSR during initial setup */ +#define DHD_INIT_CLKCTL1 (SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ) +#define DHD_INIT_CLKCTL2 (SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP) + +/* Flags for SDH calls */ +#define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED) + +/* Packet free applicable unconditionally for sdio and sdspi. Conditional if + * bufpool was present for gspi bus. + */ +#define PKTFREE2() if ((bus->bus != SPI_BUS) || bus->usebufpool) \ + PKTFREE(bus->dhd->osh, pkt, FALSE); +DHD_SPINWAIT_SLEEP_INIT(sdioh_spinwait_sleep); +#if defined(OOB_INTR_ONLY) +extern void bcmsdh_set_irq(int flag); +#endif /* defined(OOB_INTR_ONLY) */ +#ifdef PROP_TXSTATUS +extern void dhd_wlfc_txcomplete(dhd_pub_t *dhd, void *txp, bool success); +extern void dhd_wlfc_trigger_pktcommit(dhd_pub_t *dhd); +#endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) +DEFINE_MUTEX(_dhd_sdio_mutex_lock_); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ + +#ifdef DHD_DEBUG +/* Device console log buffer state */ +#define CONSOLE_LINE_MAX 192 +#define CONSOLE_BUFFER_MAX 2024 +typedef struct dhd_console { + uint count; /* Poll interval msec counter */ + uint log_addr; /* Log struct address (fixed) */ + hndrte_log_t log; /* Log struct (host copy) */ + uint bufsize; /* Size of log buffer */ + uint8 *buf; /* Log buffer (host copy) */ + uint last; /* Last buffer read index */ +} dhd_console_t; +#endif /* DHD_DEBUG */ + +#define REMAP_ENAB(bus) ((bus)->remap) +#define REMAP_ISADDR(bus, a) (((a) >= ((bus)->orig_ramsize)) && ((a) < ((bus)->ramsize))) +#define KSO_ENAB(bus) ((bus)->kso) +#define SR_ENAB(bus) ((bus)->_srenab) +#define SLPAUTO_ENAB(bus) ((SR_ENAB(bus)) && ((bus)->_slpauto)) +#define MIN_RSRC_ADDR (SI_ENUM_BASE + 0x618) +#define MIN_RSRC_SR 0x3 +#define CORE_CAPEXT_ADDR (SI_ENUM_BASE + 0x64c) +#define CORE_CAPEXT_SR_SUPPORTED_MASK (1 << 1) +#define RCTL_MACPHY_DISABLE_MASK (1 << 26) +#define RCTL_LOGIC_DISABLE_MASK (1 << 27) + +#define OOB_WAKEUP_ENAB(bus) ((bus)->_oobwakeup) +#define GPIO_DEV_SRSTATE 16 /* Host gpio17 mapped to device gpio0 SR state */ +#define GPIO_DEV_SRSTATE_TIMEOUT 320000 /* 320ms */ +#define GPIO_DEV_WAKEUP 17 /* Host gpio17 mapped to device gpio1 wakeup */ +#define CC_CHIPCTRL2_GPIO1_WAKEUP (1 << 0) + +#define CC_PMUCC3 (0x3) +/* Private data for SDIO bus interaction */ +typedef struct dhd_bus { + dhd_pub_t *dhd; + + bcmsdh_info_t *sdh; /* Handle for BCMSDH calls */ + si_t *sih; /* Handle for SI calls */ + char *vars; /* Variables (from CIS and/or other) */ + uint varsz; /* Size of variables buffer */ + uint32 sbaddr; /* Current SB window pointer (-1, invalid) */ + + sdpcmd_regs_t *regs; /* Registers for SDIO core */ + uint sdpcmrev; /* SDIO core revision */ + uint armrev; /* CPU core revision */ + uint ramrev; /* SOCRAM core revision */ + uint32 ramsize; /* Size of RAM in SOCRAM (bytes) */ + uint32 orig_ramsize; /* Size of RAM in SOCRAM (bytes) */ + uint32 srmemsize; /* Size of SRMEM */ + + uint32 bus; /* gSPI or SDIO bus */ + uint32 hostintmask; /* Copy of Host Interrupt Mask */ + uint32 intstatus; /* Intstatus bits (events) pending */ + bool dpc_sched; /* Indicates DPC schedule (intrpt rcvd) */ + bool fcstate; /* State of dongle flow-control */ + + uint16 cl_devid; /* cached devid for dhdsdio_probe_attach() */ + char *fw_path; /* module_param: path to firmware image */ + char *nv_path; /* module_param: path to nvram vars file */ + const char *nvram_params; /* user specified nvram params. */ + + uint blocksize; /* Block size of SDIO transfers */ + uint roundup; /* Max roundup limit */ + + struct pktq txq; /* Queue length used for flow-control */ + uint8 flowcontrol; /* per prio flow control bitmask */ + uint8 tx_seq; /* Transmit sequence number (next) */ + uint8 tx_max; /* Maximum transmit sequence allowed */ + + uint8 hdrbuf[MAX_HDR_READ + DHD_SDALIGN]; + uint8 *rxhdr; /* Header of current rx frame (in hdrbuf) */ + uint16 nextlen; /* Next Read Len from last header */ + uint8 rx_seq; /* Receive sequence number (expected) */ + bool rxskip; /* Skip receive (awaiting NAK ACK) */ + + void *glomd; /* Packet containing glomming descriptor */ + void *glom; /* Packet chain for glommed superframe */ + uint glomerr; /* Glom packet read errors */ + + uint8 *rxbuf; /* Buffer for receiving control packets */ + uint rxblen; /* Allocated length of rxbuf */ + uint8 *rxctl; /* Aligned pointer into rxbuf */ + uint8 *databuf; /* Buffer for receiving big glom packet */ + uint8 *dataptr; /* Aligned pointer into databuf */ + uint rxlen; /* Length of valid data in buffer */ + + uint8 sdpcm_ver; /* Bus protocol reported by dongle */ + + bool intr; /* Use interrupts */ + bool poll; /* Use polling */ + bool ipend; /* Device interrupt is pending */ + bool intdis; /* Interrupts disabled by isr */ + uint intrcount; /* Count of device interrupt callbacks */ + uint lastintrs; /* Count as of last watchdog timer */ + uint spurious; /* Count of spurious interrupts */ + uint pollrate; /* Ticks between device polls */ + uint polltick; /* Tick counter */ + uint pollcnt; /* Count of active polls */ + +#ifdef DHD_DEBUG + dhd_console_t console; /* Console output polling support */ + uint console_addr; /* Console address from shared struct */ +#endif /* DHD_DEBUG */ + + uint regfails; /* Count of R_REG/W_REG failures */ + + uint clkstate; /* State of sd and backplane clock(s) */ + bool activity; /* Activity flag for clock down */ + int32 idletime; /* Control for activity timeout */ + int32 idlecount; /* Activity timeout counter */ + int32 idleclock; /* How to set bus driver when idle */ + int32 sd_divisor; /* Speed control to bus driver */ + int32 sd_mode; /* Mode control to bus driver */ + int32 sd_rxchain; /* If bcmsdh api accepts PKT chains */ + bool use_rxchain; /* If dhd should use PKT chains */ + bool sleeping; /* Is SDIO bus sleeping? */ + uint rxflow_mode; /* Rx flow control mode */ + bool rxflow; /* Is rx flow control on */ + uint prev_rxlim_hit; /* Is prev rx limit exceeded (per dpc schedule) */ + bool alp_only; /* Don't use HT clock (ALP only) */ + /* Field to decide if rx of control frames happen in rxbuf or lb-pool */ + bool usebufpool; + +#ifdef SDTEST + /* external loopback */ + bool ext_loop; + uint8 loopid; + + /* pktgen configuration */ + uint pktgen_freq; /* Ticks between bursts */ + uint pktgen_count; /* Packets to send each burst */ + uint pktgen_print; /* Bursts between count displays */ + uint pktgen_total; /* Stop after this many */ + uint pktgen_minlen; /* Minimum packet data len */ + uint pktgen_maxlen; /* Maximum packet data len */ + uint pktgen_mode; /* Configured mode: tx, rx, or echo */ + uint pktgen_stop; /* Number of tx failures causing stop */ + + /* active pktgen fields */ + uint pktgen_tick; /* Tick counter for bursts */ + uint pktgen_ptick; /* Burst counter for printing */ + uint pktgen_sent; /* Number of test packets generated */ + uint pktgen_rcvd; /* Number of test packets received */ + uint pktgen_prev_time; /* Time at which previous stats where printed */ + uint pktgen_prev_sent; /* Number of test packets generated when + * previous stats were printed + */ + uint pktgen_prev_rcvd; /* Number of test packets received when + * previous stats were printed + */ + uint pktgen_fail; /* Number of failed send attempts */ + uint16 pktgen_len; /* Length of next packet to send */ +#define PKTGEN_RCV_IDLE (0) +#define PKTGEN_RCV_ONGOING (1) + uint16 pktgen_rcv_state; /* receive state */ + uint pktgen_rcvd_rcvsession; /* test pkts rcvd per rcv session. */ +#endif /* SDTEST */ + + /* Some additional counters */ + uint tx_sderrs; /* Count of tx attempts with sd errors */ + uint fcqueued; /* Tx packets that got queued */ + uint rxrtx; /* Count of rtx requests (NAK to dongle) */ + uint rx_toolong; /* Receive frames too long to receive */ + uint rxc_errors; /* SDIO errors when reading control frames */ + uint rx_hdrfail; /* SDIO errors on header reads */ + uint rx_badhdr; /* Bad received headers (roosync?) */ + uint rx_badseq; /* Mismatched rx sequence number */ + uint fc_rcvd; /* Number of flow-control events received */ + uint fc_xoff; /* Number which turned on flow-control */ + uint fc_xon; /* Number which turned off flow-control */ + uint rxglomfail; /* Failed deglom attempts */ + uint rxglomframes; /* Number of glom frames (superframes) */ + uint rxglompkts; /* Number of packets from glom frames */ + uint f2rxhdrs; /* Number of header reads */ + uint f2rxdata; /* Number of frame data reads */ + uint f2txdata; /* Number of f2 frame writes */ + uint f1regdata; /* Number of f1 register accesses */ + + uint8 *ctrl_frame_buf; + uint32 ctrl_frame_len; + bool ctrl_frame_stat; + uint32 rxint_mode; /* rx interrupt mode */ + bool remap; /* Contiguous 1MB RAM: 512K socram + 512K devram + * Available with socram rev 16 + * Remap region not DMA-able + */ + bool kso; + bool _slpauto; + bool _oobwakeup; + bool _srenab; + bool readframes; + bool reqbussleep; + uint32 resetinstr; + uint32 dongle_ram_base; +#ifdef BCMSDIOH_TXGLOM + void *glom_pkt_arr[SDPCM_MAXGLOM_SIZE]; /* Array of pkts for glomming */ + uint16 glom_cnt; /* Number of pkts in the glom array */ + uint16 glom_total_len; /* Total length of pkts in glom array */ + bool glom_enable; /* Flag to indicate whether tx glom is enabled/disabled */ + uint8 glom_mode; /* Glom mode - 0-copy mode, 1 - Multi-descriptor mode */ + uint32 glomsize; /* Glom size limitation */ +#endif +} dhd_bus_t; + +/* clkstate */ +#define CLK_NONE 0 +#define CLK_SDONLY 1 +#define CLK_PENDING 2 /* Not used yet */ +#define CLK_AVAIL 3 + +#define DHD_NOPMU(dhd) (FALSE) + +#ifdef DHD_DEBUG +static int qcount[NUMPRIO]; +static int tx_packets[NUMPRIO]; +#endif /* DHD_DEBUG */ + +/* Deferred transmit */ +const uint dhd_deferred_tx = 1; + +extern uint dhd_watchdog_ms; +extern void dhd_os_wd_timer(void *bus, uint wdtick); + +/* Tx/Rx bounds */ +uint dhd_txbound; +uint dhd_rxbound; +uint dhd_txminmax = DHD_TXMINMAX; + +/* override the RAM size if possible */ +#define DONGLE_MIN_MEMSIZE (128 *1024) +int dhd_dongle_memsize; + +uint dhd_doflow = TRUE; +uint dhd_dpcpoll = FALSE; +static bool dhd_alignctl; + +static bool sd1idle; + +static bool retrydata; +#define RETRYCHAN(chan) (((chan) == SDPCM_EVENT_CHANNEL) || retrydata) + +#if defined(SDIO_CRC_ERROR_FIX) +static uint watermark = 48; +static uint mesbusyctrl = 80; +#else +static const uint watermark = 8; +static const uint mesbusyctrl = 0; +#endif +static const uint firstread = DHD_FIRSTREAD; + +#define HDATLEN (firstread - (SDPCM_HDRLEN)) + +/* Retry count for register access failures */ +static const uint retry_limit = 2; + +/* Force even SD lengths (some host controllers mess up on odd bytes) */ +static bool forcealign; + +#define ALIGNMENT 4 + +#if defined(OOB_INTR_ONLY) && defined(HW_OOB) +extern void bcmsdh_enable_hw_oob_intr(void *sdh, bool enable); +#endif + +#if defined(OOB_INTR_ONLY) && defined(SDIO_ISR_THREAD) +#error OOB_INTR_ONLY is NOT working with SDIO_ISR_THREAD +#endif /* defined(OOB_INTR_ONLY) && defined(SDIO_ISR_THREAD) */ +#define PKTALIGN(osh, p, len, align) \ + do { \ + uint datalign; \ + datalign = (uintptr)PKTDATA((osh), (p)); \ + datalign = ROUNDUP(datalign, (align)) - datalign; \ + ASSERT(datalign < (align)); \ + ASSERT(PKTLEN((osh), (p)) >= ((len) + datalign)); \ + if (datalign) \ + PKTPULL((osh), (p), datalign); \ + PKTSETLEN((osh), (p), (len)); \ + } while (0) + +/* Limit on rounding up frames */ +static const uint max_roundup = 512; + +/* Try doing readahead */ +static bool dhd_readahead; + + +/* To check if there's window offered */ +#define DATAOK(bus) \ + (((uint8)(bus->tx_max - bus->tx_seq) > 1) && \ + (((uint8)(bus->tx_max - bus->tx_seq) & 0x80) == 0)) + +/* To check if there's window offered for ctrl frame */ +#define TXCTLOK(bus) \ + (((uint8)(bus->tx_max - bus->tx_seq) != 0) && \ + (((uint8)(bus->tx_max - bus->tx_seq) & 0x80) == 0)) + +/* Number of pkts available in dongle for data RX */ +#define DATABUFCNT(bus) \ + ((uint8)(bus->tx_max - bus->tx_seq) - 1) + +/* Macros to get register read/write status */ +/* NOTE: these assume a local dhdsdio_bus_t *bus! */ +#define R_SDREG(regvar, regaddr, retryvar) \ +do { \ + retryvar = 0; \ + do { \ + regvar = R_REG(bus->dhd->osh, regaddr); \ + } while (bcmsdh_regfail(bus->sdh) && (++retryvar <= retry_limit)); \ + if (retryvar) { \ + bus->regfails += (retryvar-1); \ + if (retryvar > retry_limit) { \ + DHD_ERROR(("%s: FAILED" #regvar "READ, LINE %d\n", \ + __FUNCTION__, __LINE__)); \ + regvar = 0; \ + } \ + } \ +} while (0) + +#define W_SDREG(regval, regaddr, retryvar) \ +do { \ + retryvar = 0; \ + do { \ + W_REG(bus->dhd->osh, regaddr, regval); \ + } while (bcmsdh_regfail(bus->sdh) && (++retryvar <= retry_limit)); \ + if (retryvar) { \ + bus->regfails += (retryvar-1); \ + if (retryvar > retry_limit) \ + DHD_ERROR(("%s: FAILED REGISTER WRITE, LINE %d\n", \ + __FUNCTION__, __LINE__)); \ + } \ +} while (0) + +#define BUS_WAKE(bus) \ + do { \ + bus->idlecount = 0; \ + if ((bus)->sleeping) \ + dhdsdio_bussleep((bus), FALSE); \ + } while (0); + +/* + * pktavail interrupts from dongle to host can be managed in 3 different ways + * whenever there is a packet available in dongle to transmit to host. + * + * Mode 0: Dongle writes the software host mailbox and host is interrupted. + * Mode 1: (sdiod core rev >= 4) + * Device sets a new bit in the intstatus whenever there is a packet + * available in fifo. Host can't clear this specific status bit until all the + * packets are read from the FIFO. No need to ack dongle intstatus. + * Mode 2: (sdiod core rev >= 4) + * Device sets a bit in the intstatus, and host acks this by writing + * one to this bit. Dongle won't generate anymore packet interrupts + * until host reads all the packets from the dongle and reads a zero to + * figure that there are no more packets. No need to disable host ints. + * Need to ack the intstatus. + */ + +#define SDIO_DEVICE_HMB_RXINT 0 /* default old way */ +#define SDIO_DEVICE_RXDATAINT_MODE_0 1 /* from sdiod rev 4 */ +#define SDIO_DEVICE_RXDATAINT_MODE_1 2 /* from sdiod rev 4 */ + + +#define FRAME_AVAIL_MASK(bus) \ + ((bus->rxint_mode == SDIO_DEVICE_HMB_RXINT) ? I_HMB_FRAME_IND : I_XMTDATA_AVAIL) + +#define DHD_BUS SDIO_BUS + +#define PKT_AVAILABLE(bus, intstatus) ((intstatus) & (FRAME_AVAIL_MASK(bus))) + +#define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE) + +#define GSPI_PR55150_BAILOUT + +#ifdef SDTEST +static void dhdsdio_testrcv(dhd_bus_t *bus, void *pkt, uint seq); +static void dhdsdio_sdtest_set(dhd_bus_t *bus, uint count); +#endif + +#ifdef DHD_DEBUG +static int dhdsdio_checkdied(dhd_bus_t *bus, char *data, uint size); +static int dhd_serialconsole(dhd_bus_t *bus, bool get, bool enable, int *bcmerror); +#endif /* DHD_DEBUG */ + +static int dhdsdio_devcap_set(dhd_bus_t *bus, uint8 cap); +static int dhdsdio_download_state(dhd_bus_t *bus, bool enter); + +static void dhdsdio_release(dhd_bus_t *bus, osl_t *osh); +static void dhdsdio_release_malloc(dhd_bus_t *bus, osl_t *osh); +static void dhdsdio_disconnect(void *ptr); +static bool dhdsdio_chipmatch(uint16 chipid); +static bool dhdsdio_probe_attach(dhd_bus_t *bus, osl_t *osh, void *sdh, + void * regsva, uint16 devid); +static bool dhdsdio_probe_malloc(dhd_bus_t *bus, osl_t *osh, void *sdh); +static bool dhdsdio_probe_init(dhd_bus_t *bus, osl_t *osh, void *sdh); +static void dhdsdio_release_dongle(dhd_bus_t *bus, osl_t *osh, bool dongle_isolation, + bool reset_flag); + +static void dhd_dongle_setmemsize(struct dhd_bus *bus, int mem_size); +static int dhd_bcmsdh_recv_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags, + uint8 *buf, uint nbytes, + void *pkt, bcmsdh_cmplt_fn_t complete, void *handle); +static int dhd_bcmsdh_send_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags, + uint8 *buf, uint nbytes, + void *pkt, bcmsdh_cmplt_fn_t complete, void *handle); +#ifdef BCMSDIOH_TXGLOM +static void dhd_bcmsdh_glom_post(dhd_bus_t *bus, uint8 *frame, uint len); +static void dhd_bcmsdh_glom_clear(dhd_bus_t *bus); +#endif + +static bool dhdsdio_download_firmware(dhd_bus_t *bus, osl_t *osh, void *sdh); +static int _dhdsdio_download_firmware(dhd_bus_t *bus); + +static int dhdsdio_download_code_file(dhd_bus_t *bus, char *image_path); +static int dhdsdio_download_nvram(dhd_bus_t *bus); +#ifdef BCMEMBEDIMAGE +static int dhdsdio_download_code_array(dhd_bus_t *bus); +#endif +static int dhdsdio_bussleep(dhd_bus_t *bus, bool sleep); +static int dhdsdio_clkctl(dhd_bus_t *bus, uint target, bool pendok); +static uint8 dhdsdio_sleepcsr_get(dhd_bus_t *bus); + +#ifdef WLMEDIA_HTSF +#include +extern uint32 dhd_get_htsf(void *dhd, int ifidx); +#endif /* WLMEDIA_HTSF */ + +static void +dhd_dongle_setmemsize(struct dhd_bus *bus, int mem_size) +{ + int32 min_size = DONGLE_MIN_MEMSIZE; + /* Restrict the memsize to user specified limit */ + DHD_ERROR(("user: Restrict the dongle ram size to %d, min accepted %d\n", + dhd_dongle_memsize, min_size)); + if ((dhd_dongle_memsize > min_size) && + (dhd_dongle_memsize < (int32)bus->orig_ramsize)) + bus->ramsize = dhd_dongle_memsize; +} + +static int +dhdsdio_set_siaddr_window(dhd_bus_t *bus, uint32 address) +{ + int err = 0; + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW, + (address >> 8) & SBSDIO_SBADDRLOW_MASK, &err); + if (!err) + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRMID, + (address >> 16) & SBSDIO_SBADDRMID_MASK, &err); + if (!err) + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRHIGH, + (address >> 24) & SBSDIO_SBADDRHIGH_MASK, &err); + return err; +} + + +#ifdef USE_OOB_GPIO1 +static int +dhdsdio_oobwakeup_init(dhd_bus_t *bus) +{ + uint32 val, addr, data; + + bcmsdh_gpioouten(bus->sdh, GPIO_DEV_WAKEUP); + + addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_addr); + data = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_data); + + /* Set device for gpio1 wakeup */ + bcmsdh_reg_write(bus->sdh, addr, 4, 2); + val = bcmsdh_reg_read(bus->sdh, data, 4); + val |= CC_CHIPCTRL2_GPIO1_WAKEUP; + bcmsdh_reg_write(bus->sdh, data, 4, val); + + bus->_oobwakeup = TRUE; + + return 0; +} +#endif /* USE_OOB_GPIO1 */ + +/* + * Query if FW is in SR mode + */ +static bool +dhdsdio_sr_cap(dhd_bus_t *bus) +{ + bool cap = FALSE; + uint32 min = 0, core_capext, addr, data; + if (bus->sih->chip == BCM4324_CHIP_ID) { + addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_addr); + data = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_data); + bcmsdh_reg_write(bus->sdh, addr, 4, 3); + core_capext = bcmsdh_reg_read(bus->sdh, data, 4); + } else if (bus->sih->chip == BCM4330_CHIP_ID) { + core_capext = FALSE; + } else if (bus->sih->chip == BCM4335_CHIP_ID) { + core_capext = TRUE; + } else { + core_capext = bcmsdh_reg_read(bus->sdh, CORE_CAPEXT_ADDR, 4); + core_capext = (core_capext & CORE_CAPEXT_SR_SUPPORTED_MASK); + } + if (!(core_capext)) + return FALSE; + + if (bus->sih->chip == BCM4324_CHIP_ID) { + /* FIX: Should change to query SR control register instead */ + min = bcmsdh_reg_read(bus->sdh, MIN_RSRC_ADDR, 4); + if (min == MIN_RSRC_SR) + cap = TRUE; + } else if (bus->sih->chip == BCM4335_CHIP_ID) { + uint32 enabval = 0; + addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_addr); + data = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_data); + bcmsdh_reg_write(bus->sdh, addr, 4, CC_PMUCC3); + enabval = bcmsdh_reg_read(bus->sdh, data, 4); + + if (enabval) + cap = TRUE; + } else { + data = bcmsdh_reg_read(bus->sdh, + SI_ENUM_BASE + OFFSETOF(chipcregs_t, retention_ctl), 4); + if ((data & (RCTL_MACPHY_DISABLE_MASK | RCTL_LOGIC_DISABLE_MASK)) == 0) + cap = TRUE; + } + + return cap; +} + +static int +dhdsdio_srwar_init(dhd_bus_t *bus) +{ + + bcmsdh_gpio_init(bus->sdh); + +#ifdef USE_OOB_GPIO1 + dhdsdio_oobwakeup_init(bus); +#endif + + + return 0; +} + +static int +dhdsdio_sr_init(dhd_bus_t *bus) +{ + uint8 val; + int err = 0; + + if ((bus->sih->chip == BCM4334_CHIP_ID) && (bus->sih->chiprev == 2)) + dhdsdio_srwar_init(bus); + + val = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_WAKEUPCTRL, NULL); + val |= 1 << SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT; + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_WAKEUPCTRL, + 1 << SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT, &err); + val = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_WAKEUPCTRL, NULL); + + /* Add CMD14 Support */ + dhdsdio_devcap_set(bus, + (SDIOD_CCCR_BRCM_CARDCAP_CMD14_SUPPORT | SDIOD_CCCR_BRCM_CARDCAP_CMD14_EXT)); + + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, + SBSDIO_FUNC1_CHIPCLKCSR, SBSDIO_FORCE_HT, &err); + + bus->_slpauto = dhd_slpauto ? TRUE : FALSE; + + bus->_srenab = TRUE; + + return 0; +} + +/* + * FIX: Be sure KSO bit is enabled + * Currently, it's defaulting to 0 which should be 1. + */ +static int +dhdsdio_clk_kso_init(dhd_bus_t *bus) +{ + uint8 val; + int err = 0; + + /* set flag */ + bus->kso = TRUE; + + /* + * Enable KeepSdioOn (KSO) bit for normal operation + * Default is 0 (4334A0) so set it. Fixed in B0. + */ + val = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, NULL); + if (!(val & SBSDIO_FUNC1_SLEEPCSR_KSO_MASK)) { + val |= (SBSDIO_FUNC1_SLEEPCSR_KSO_EN << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, val, &err); + if (err) + DHD_ERROR(("%s: SBSDIO_FUNC1_SLEEPCSR err: 0x%x\n", __FUNCTION__, err)); + } + + return 0; +} + +#define KSO_DBG(x) +#define MAX_KSO_ATTEMPTS 64 +static int +dhdsdio_clk_kso_enab(dhd_bus_t *bus, bool on) +{ + uint8 wr_val = 0, rd_val, cmp_val, bmask; + int err = 0; + int try_cnt = 0; + + KSO_DBG(("%s> op:%s\n", __FUNCTION__, (on ? "KSO_SET" : "KSO_CLR"))); + + wr_val |= (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); + + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, wr_val, &err); + + if (on) { + cmp_val = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK | SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK; + bmask = cmp_val; + + msleep(3); + + } else { + /* Put device to sleep, turn off KSO */ + cmp_val = 0; + bmask = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK; + } + + do { + rd_val = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, &err); + if (((rd_val & bmask) == cmp_val) && !err) + break; + + KSO_DBG(("%s> KSO wr/rd retry:%d, ERR:%x \n", __FUNCTION__, try_cnt, err)); + OSL_DELAY(50); + + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, wr_val, &err); + + } while (try_cnt++ < MAX_KSO_ATTEMPTS); + + + if (try_cnt > 1) { + KSO_DBG(("%s> op:%s, try_cnt:%d, rd_val:%x, ERR:%x \n", + __FUNCTION__, (on ? "KSO_SET" : "KSO_CLR"), try_cnt, rd_val, err)); + } + + if (try_cnt > MAX_KSO_ATTEMPTS) { + DHD_ERROR(("%s> op:%s, ERROR: try_cnt:%d, rd_val:%x, ERR:%x \n", + __FUNCTION__, (on ? "KSO_SET" : "KSO_CLR"), try_cnt, rd_val, err)); + } + return err; +} + +static int +dhdsdio_clk_kso_iovar(dhd_bus_t *bus, bool on) +{ + int err = 0; + + if (on == FALSE) { + + BUS_WAKE(bus); + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + + DHD_ERROR(("%s: KSO disable clk: 0x%x\n", __FUNCTION__, + bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, + SBSDIO_FUNC1_CHIPCLKCSR, &err))); + dhdsdio_clk_kso_enab(bus, FALSE); + } else { + DHD_ERROR(("%s: KSO enable\n", __FUNCTION__)); + + /* Make sure we have SD bus access */ + if (bus->clkstate == CLK_NONE) { + DHD_ERROR(("%s: Request SD clk\n", __FUNCTION__)); + dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); + } + + /* Double-write to be safe in case transition of AOS */ + dhdsdio_clk_kso_enab(bus, TRUE); + dhdsdio_clk_kso_enab(bus, TRUE); + OSL_DELAY(4000); + + /* Wait for device ready during transition to wake-up */ + SPINWAIT(((dhdsdio_sleepcsr_get(bus)) != + (SBSDIO_FUNC1_SLEEPCSR_KSO_MASK | + SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK)), + (10000)); + + DHD_ERROR(("%s: sleepcsr: 0x%x\n", __FUNCTION__, + dhdsdio_sleepcsr_get(bus))); + } + + bus->kso = on; + BCM_REFERENCE(err); + + return 0; +} + +static uint8 +dhdsdio_sleepcsr_get(dhd_bus_t *bus) +{ + int err = 0; + uint8 val = 0; + + val = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, &err); + if (err) + DHD_TRACE(("Failed to read SLEEPCSR: %d\n", err)); + + return val; +} + +uint8 +dhdsdio_devcap_get(dhd_bus_t *bus) +{ + return bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_BRCM_CARDCAP, NULL); +} + +static int +dhdsdio_devcap_set(dhd_bus_t *bus, uint8 cap) +{ + int err = 0; + + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_BRCM_CARDCAP, cap, &err); + if (err) + DHD_ERROR(("%s: devcap set err: 0x%x\n", __FUNCTION__, err)); + + return 0; +} + +static int +dhdsdio_clk_devsleep_iovar(dhd_bus_t *bus, bool on) +{ + int err = 0, retry; + uint8 val; + + retry = 0; + if (on == TRUE) { + /* Enter Sleep */ + + /* Be sure we request clk before going to sleep + * so we can wake-up with clk request already set + * else device can go back to sleep immediately + */ + if (!SLPAUTO_ENAB(bus)) + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + else { + val = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err); + if ((val & SBSDIO_CSR_MASK) == 0) { + DHD_ERROR(("%s: No clock before enter sleep:0x%x\n", + __FUNCTION__, val)); + + /* Reset clock request */ + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, + SBSDIO_ALP_AVAIL_REQ, &err); + DHD_ERROR(("%s: clock before sleep:0x%x\n", __FUNCTION__, + bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, + SBSDIO_FUNC1_CHIPCLKCSR, &err))); + } + } + + DHD_TRACE(("%s: clk before sleep: 0x%x\n", __FUNCTION__, + bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, + SBSDIO_FUNC1_CHIPCLKCSR, &err))); +#ifdef USE_CMD14 + err = bcmsdh_sleep(bus->sdh, TRUE); +#else + err = dhdsdio_clk_kso_enab(bus, FALSE); + if (OOB_WAKEUP_ENAB(bus)) + err = bcmsdh_gpioout(bus->sdh, GPIO_DEV_WAKEUP, FALSE); /* GPIO_1 is off */ +#endif + } else { + /* Exit Sleep */ + /* Make sure we have SD bus access */ + if (bus->clkstate == CLK_NONE) { + DHD_TRACE(("%s: Request SD clk\n", __FUNCTION__)); + dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); + } + + if ((bus->sih->chip == BCM4334_CHIP_ID) && (bus->sih->chiprev == 2)) { + SPINWAIT((bcmsdh_gpioin(bus->sdh, GPIO_DEV_SRSTATE) != TRUE), + GPIO_DEV_SRSTATE_TIMEOUT); + + if (bcmsdh_gpioin(bus->sdh, GPIO_DEV_SRSTATE) == FALSE) { + DHD_ERROR(("ERROR: GPIO_DEV_SRSTATE still low!\n")); + } + } +#ifdef USE_CMD14 + err = bcmsdh_sleep(bus->sdh, FALSE); + if (SLPAUTO_ENAB(bus) && (err != 0)) { + OSL_DELAY(10000); + DHD_TRACE(("%s: Resync device sleep\n", __FUNCTION__)); + + /* Toggle sleep to resync with host and device */ + err = bcmsdh_sleep(bus->sdh, TRUE); + OSL_DELAY(10000); + err = bcmsdh_sleep(bus->sdh, FALSE); + + if (err) { + OSL_DELAY(10000); + DHD_ERROR(("%s: CMD14 exit failed again!\n", __FUNCTION__)); + + /* Toggle sleep to resync with host and device */ + err = bcmsdh_sleep(bus->sdh, TRUE); + OSL_DELAY(10000); + err = bcmsdh_sleep(bus->sdh, FALSE); + if (err) { + DHD_ERROR(("%s: CMD14 exit failed twice!\n", __FUNCTION__)); + DHD_ERROR(("%s: FATAL: Device non-response!\n", + __FUNCTION__)); + err = 0; + } + } + } +#else + if (OOB_WAKEUP_ENAB(bus)) + err = bcmsdh_gpioout(bus->sdh, GPIO_DEV_WAKEUP, TRUE); /* GPIO_1 is on */ + + do { + err = dhdsdio_clk_kso_enab(bus, TRUE); + if (err) + OSL_DELAY(10000); + } while ((err != 0) && (++retry < 3)); + + if (err != 0) { + DHD_ERROR(("ERROR: kso set failed retry: %d\n", retry)); + err = 0; /* continue anyway */ + } +#endif /* !USE_CMD14 */ + + if (err == 0) { + uint8 csr; + + /* Wait for device ready during transition to wake-up */ + SPINWAIT((((csr = dhdsdio_sleepcsr_get(bus)) & + SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK) != + (SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK)), (20000)); + + DHD_TRACE(("%s: ExitSleep sleepcsr: 0x%x\n", __FUNCTION__, csr)); + + if (!(csr & SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK)) { + DHD_ERROR(("%s:ERROR: ExitSleep device NOT Ready! 0x%x\n", + __FUNCTION__, csr)); + err = BCME_NODEVICE; + } + + SPINWAIT((((csr = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, + SBSDIO_FUNC1_CHIPCLKCSR, &err)) & SBSDIO_HT_AVAIL) != + (SBSDIO_HT_AVAIL)), (10000)); + + } + } + + /* Update if successful */ + if (err == 0) + bus->kso = on ? FALSE : TRUE; + else { + DHD_ERROR(("%s: Sleep request failed: on:%d err:%d\n", __FUNCTION__, on, err)); + if (!on && retry > 2) + bus->kso = TRUE; + } + + return err; +} + +/* Turn backplane clock on or off */ +static int +dhdsdio_htclk(dhd_bus_t *bus, bool on, bool pendok) +{ +#define HT_AVAIL_ERROR_MAX 10 + static int ht_avail_error = 0; + int err; + uint8 clkctl, clkreq, devctl; + bcmsdh_info_t *sdh; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + +#if defined(OOB_INTR_ONLY) + pendok = FALSE; +#endif /* defined(OOB_INTR_ONLY) */ + clkctl = 0; + sdh = bus->sdh; + + + if (!KSO_ENAB(bus)) + return BCME_OK; + + if (SLPAUTO_ENAB(bus)) { + bus->clkstate = (on ? CLK_AVAIL : CLK_SDONLY); + return BCME_OK; + } + + if (on) { + /* Request HT Avail */ + clkreq = bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ; + + + + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err); + if (err) { + ht_avail_error++; + if (ht_avail_error < HT_AVAIL_ERROR_MAX) { + DHD_ERROR(("%s: HT Avail request error: %d\n", __FUNCTION__, err)); + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) + else if (ht_avail_error == HT_AVAIL_ERROR_MAX) { + dhd_os_send_hang_message(bus->dhd); + } +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) */ + return BCME_ERROR; + } else { + ht_avail_error = 0; + } + + if (pendok && + ((bus->sih->buscoretype == PCMCIA_CORE_ID) && (bus->sih->buscorerev == 9))) { + uint32 dummy, retries; + R_SDREG(dummy, &bus->regs->clockctlstatus, retries); + BCM_REFERENCE(dummy); + } + + /* Check current status */ + clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err); + if (err) { + DHD_ERROR(("%s: HT Avail read error: %d\n", __FUNCTION__, err)); + return BCME_ERROR; + } + + /* Go to pending and await interrupt if appropriate */ + if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) { + /* Allow only clock-available interrupt */ + devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err); + if (err) { + DHD_ERROR(("%s: Devctl access error setting CA: %d\n", + __FUNCTION__, err)); + return BCME_ERROR; + } + + devctl |= SBSDIO_DEVCTL_CA_INT_ONLY; + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err); + DHD_INFO(("CLKCTL: set PENDING\n")); + bus->clkstate = CLK_PENDING; + return BCME_OK; + } else if (bus->clkstate == CLK_PENDING) { + /* Cancel CA-only interrupt filter */ + devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err); + devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err); + } + + /* Otherwise, wait here (polling) for HT Avail */ + if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) { + SPINWAIT_SLEEP(sdioh_spinwait_sleep, + ((clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, + SBSDIO_FUNC1_CHIPCLKCSR, &err)), + !SBSDIO_CLKAV(clkctl, bus->alp_only)), PMU_MAX_TRANSITION_DLY); + } + if (err) { + DHD_ERROR(("%s: HT Avail request error: %d\n", __FUNCTION__, err)); + return BCME_ERROR; + } + if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) { + DHD_ERROR(("%s: HT Avail timeout (%d): clkctl 0x%02x\n", + __FUNCTION__, PMU_MAX_TRANSITION_DLY, clkctl)); + return BCME_ERROR; + } + + /* Mark clock available */ + bus->clkstate = CLK_AVAIL; + DHD_INFO(("CLKCTL: turned ON\n")); + +#if defined(DHD_DEBUG) + if (bus->alp_only == TRUE) { +#if !defined(BCMLXSDMMC) + if (!SBSDIO_ALPONLY(clkctl)) { + DHD_ERROR(("%s: HT Clock, when ALP Only\n", __FUNCTION__)); + } +#endif /* !defined(BCMLXSDMMC) */ + } else { + if (SBSDIO_ALPONLY(clkctl)) { + DHD_ERROR(("%s: HT Clock should be on.\n", __FUNCTION__)); + } + } +#endif /* defined (DHD_DEBUG) */ + + bus->activity = TRUE; +#ifdef DHD_USE_IDLECOUNT + bus->idlecount = 0; +#endif /* DHD_USE_IDLECOUNT */ + } else { + clkreq = 0; + if (bus->clkstate == CLK_PENDING) { + /* Cancel CA-only interrupt filter */ + devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err); + devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err); + } + + bus->clkstate = CLK_SDONLY; + if (!SR_ENAB(bus)) { + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err); + DHD_INFO(("CLKCTL: turned OFF\n")); + if (err) { + DHD_ERROR(("%s: Failed access turning clock off: %d\n", + __FUNCTION__, err)); + return BCME_ERROR; + } + } + } + return BCME_OK; +} + +/* Change idle/active SD state */ +static int +dhdsdio_sdclk(dhd_bus_t *bus, bool on) +{ + int err; + int32 iovalue; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (on) { + if (bus->idleclock == DHD_IDLE_STOP) { + /* Turn on clock and restore mode */ + iovalue = 1; + err = bcmsdh_iovar_op(bus->sdh, "sd_clock", NULL, 0, + &iovalue, sizeof(iovalue), TRUE); + if (err) { + DHD_ERROR(("%s: error enabling sd_clock: %d\n", + __FUNCTION__, err)); + return BCME_ERROR; + } + + iovalue = bus->sd_mode; + err = bcmsdh_iovar_op(bus->sdh, "sd_mode", NULL, 0, + &iovalue, sizeof(iovalue), TRUE); + if (err) { + DHD_ERROR(("%s: error changing sd_mode: %d\n", + __FUNCTION__, err)); + return BCME_ERROR; + } + } else if (bus->idleclock != DHD_IDLE_ACTIVE) { + /* Restore clock speed */ + iovalue = bus->sd_divisor; + err = bcmsdh_iovar_op(bus->sdh, "sd_divisor", NULL, 0, + &iovalue, sizeof(iovalue), TRUE); + if (err) { + DHD_ERROR(("%s: error restoring sd_divisor: %d\n", + __FUNCTION__, err)); + return BCME_ERROR; + } + } + bus->clkstate = CLK_SDONLY; + } else { + /* Stop or slow the SD clock itself */ + if ((bus->sd_divisor == -1) || (bus->sd_mode == -1)) { + DHD_TRACE(("%s: can't idle clock, divisor %d mode %d\n", + __FUNCTION__, bus->sd_divisor, bus->sd_mode)); + return BCME_ERROR; + } + if (bus->idleclock == DHD_IDLE_STOP) { + if (sd1idle) { + /* Change to SD1 mode and turn off clock */ + iovalue = 1; + err = bcmsdh_iovar_op(bus->sdh, "sd_mode", NULL, 0, + &iovalue, sizeof(iovalue), TRUE); + if (err) { + DHD_ERROR(("%s: error changing sd_clock: %d\n", + __FUNCTION__, err)); + return BCME_ERROR; + } + } + + iovalue = 0; + err = bcmsdh_iovar_op(bus->sdh, "sd_clock", NULL, 0, + &iovalue, sizeof(iovalue), TRUE); + if (err) { + DHD_ERROR(("%s: error disabling sd_clock: %d\n", + __FUNCTION__, err)); + return BCME_ERROR; + } + } else if (bus->idleclock != DHD_IDLE_ACTIVE) { + /* Set divisor to idle value */ + iovalue = bus->idleclock; + err = bcmsdh_iovar_op(bus->sdh, "sd_divisor", NULL, 0, + &iovalue, sizeof(iovalue), TRUE); + if (err) { + DHD_ERROR(("%s: error changing sd_divisor: %d\n", + __FUNCTION__, err)); + return BCME_ERROR; + } + } + bus->clkstate = CLK_NONE; + } + + return BCME_OK; +} + +/* Transition SD and backplane clock readiness */ +static int +dhdsdio_clkctl(dhd_bus_t *bus, uint target, bool pendok) +{ + int ret = BCME_OK; +#ifdef DHD_DEBUG + uint oldstate = bus->clkstate; +#endif /* DHD_DEBUG */ + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + /* Early exit if we're already there */ + if (bus->clkstate == target) { + if (target == CLK_AVAIL) { + dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms); + bus->activity = TRUE; +#ifdef DHD_USE_IDLECOUNT + bus->idlecount = 0; +#endif /* DHD_USE_IDLECOUNT */ + } + return ret; + } + + switch (target) { + case CLK_AVAIL: + /* Make sure SD clock is available */ + if (bus->clkstate == CLK_NONE) + dhdsdio_sdclk(bus, TRUE); + /* Now request HT Avail on the backplane */ + ret = dhdsdio_htclk(bus, TRUE, pendok); + if (ret == BCME_OK) { + dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms); + bus->activity = TRUE; +#ifdef DHD_USE_IDLECOUNT + bus->idlecount = 0; +#endif /* DHD_USE_IDLECOUNT */ + } + break; + + case CLK_SDONLY: + /* Remove HT request, or bring up SD clock */ + if (bus->clkstate == CLK_NONE) + ret = dhdsdio_sdclk(bus, TRUE); + else if (bus->clkstate == CLK_AVAIL) + ret = dhdsdio_htclk(bus, FALSE, FALSE); + else + DHD_ERROR(("dhdsdio_clkctl: request for %d -> %d\n", + bus->clkstate, target)); + if (ret == BCME_OK) { + dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms); + } + break; + + case CLK_NONE: + /* Make sure to remove HT request */ + if (bus->clkstate == CLK_AVAIL) + ret = dhdsdio_htclk(bus, FALSE, FALSE); + /* Now remove the SD clock */ + ret = dhdsdio_sdclk(bus, FALSE); +#ifdef DHD_DEBUG + if (dhd_console_ms == 0) +#endif /* DHD_DEBUG */ + if (bus->poll == 0) + dhd_os_wd_timer(bus->dhd, 0); + break; + } +#ifdef DHD_DEBUG + DHD_INFO(("dhdsdio_clkctl: %d -> %d\n", oldstate, bus->clkstate)); +#endif /* DHD_DEBUG */ + + return ret; +} + +static int +dhdsdio_bussleep(dhd_bus_t *bus, bool sleep) +{ + int err = 0; + bcmsdh_info_t *sdh = bus->sdh; + sdpcmd_regs_t *regs = bus->regs; + uint retries = 0; + + DHD_INFO(("dhdsdio_bussleep: request %s (currently %s)\n", + (sleep ? "SLEEP" : "WAKE"), + (bus->sleeping ? "SLEEP" : "WAKE"))); + + /* Done if we're already in the requested state */ + if (sleep == bus->sleeping) + return BCME_OK; + + /* Going to sleep: set the alarm and turn off the lights... */ + if (sleep) { + /* Don't sleep if something is pending */ + if (bus->dpc_sched || bus->rxskip || pktq_len(&bus->txq)) + return BCME_BUSY; + + + if (!SLPAUTO_ENAB(bus)) { + /* Disable SDIO interrupts (no longer interested) */ + bcmsdh_intr_disable(bus->sdh); + + /* Make sure the controller has the bus up */ + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + + /* Tell device to start using OOB wakeup */ + W_SDREG(SMB_USE_OOB, ®s->tosbmailbox, retries); + if (retries > retry_limit) + DHD_ERROR(("CANNOT SIGNAL CHIP, WILL NOT WAKE UP!!\n")); + + /* Turn off our contribution to the HT clock request */ + dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); + + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, + SBSDIO_FORCE_HW_CLKREQ_OFF, NULL); + + /* Isolate the bus */ + if (bus->sih->chip != BCM4329_CHIP_ID && + bus->sih->chip != BCM4319_CHIP_ID) { + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, + SBSDIO_DEVCTL_PADS_ISO, NULL); + } + } else { + /* Leave interrupts enabled since device can exit sleep and + * interrupt host + */ + err = dhdsdio_clk_devsleep_iovar(bus, TRUE /* sleep */); + } + + /* Change state */ + bus->sleeping = TRUE; + + } else { + /* Waking up: bus power up is ok, set local state */ + + if (!SLPAUTO_ENAB(bus)) { + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, 0, &err); + + /* Force pad isolation off if possible (in case power never toggled) */ + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, 0, NULL); + + + /* Make sure the controller has the bus up */ + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + + /* Send misc interrupt to indicate OOB not needed */ + W_SDREG(0, ®s->tosbmailboxdata, retries); + if (retries <= retry_limit) + W_SDREG(SMB_DEV_INT, ®s->tosbmailbox, retries); + + if (retries > retry_limit) + DHD_ERROR(("CANNOT SIGNAL CHIP TO CLEAR OOB!!\n")); + + /* Make sure we have SD bus access */ + dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); + + /* Enable interrupts again */ + if (bus->intr && (bus->dhd->busstate == DHD_BUS_DATA)) { + bus->intdis = FALSE; + bcmsdh_intr_enable(bus->sdh); + } + } else { + err = dhdsdio_clk_devsleep_iovar(bus, FALSE /* wake */); + } + + if (err == 0) { + /* Change state */ + bus->sleeping = FALSE; + } + } + + return err; +} + +#if defined(OOB_INTR_ONLY) +void +dhd_enable_oob_intr(struct dhd_bus *bus, bool enable) +{ +#if defined(HW_OOB) + bcmsdh_enable_hw_oob_intr(bus->sdh, enable); +#else + sdpcmd_regs_t *regs = bus->regs; + uint retries = 0; + + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + if (enable == TRUE) { + + /* Tell device to start using OOB wakeup */ + W_SDREG(SMB_USE_OOB, ®s->tosbmailbox, retries); + if (retries > retry_limit) + DHD_ERROR(("CANNOT SIGNAL CHIP, WILL NOT WAKE UP!!\n")); + + } else { + /* Send misc interrupt to indicate OOB not needed */ + W_SDREG(0, ®s->tosbmailboxdata, retries); + if (retries <= retry_limit) + W_SDREG(SMB_DEV_INT, ®s->tosbmailbox, retries); + } + + /* Turn off our contribution to the HT clock request */ + dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); +#endif /* !defined(HW_OOB) */ +} +#endif /* defined(OOB_INTR_ONLY) */ + +/* Writes a HW/SW header into the packet and sends it. */ +/* Assumes: (a) header space already there, (b) caller holds lock */ +static int +dhdsdio_txpkt(dhd_bus_t *bus, void *pkt, uint chan, bool free_pkt, bool queue_only) +{ + int ret; + osl_t *osh; + uint8 *frame; + uint16 len, pad1 = 0; + uint32 swheader; + uint retries = 0; + bcmsdh_info_t *sdh; + void *new; + int i; + int pkt_cnt; +#ifdef BCMSDIOH_TXGLOM + uint8 *frame_tmp; +#endif +#ifdef WLMEDIA_HTSF + char *p; + htsfts_t *htsf_ts; +#endif + + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + sdh = bus->sdh; + osh = bus->dhd->osh; + + if (bus->dhd->dongle_reset) { + ret = BCME_NOTREADY; + goto done; + } + + frame = (uint8*)PKTDATA(osh, pkt); + +#ifdef WLMEDIA_HTSF + if (PKTLEN(osh, pkt) >= 100) { + p = PKTDATA(osh, pkt); + htsf_ts = (htsfts_t*) (p + HTSF_HOSTOFFSET + 12); + if (htsf_ts->magic == HTSFMAGIC) { + htsf_ts->c20 = get_cycles(); + htsf_ts->t20 = dhd_get_htsf(bus->dhd->info, 0); + } + } +#endif /* WLMEDIA_HTSF */ + + /* Add alignment padding, allocate new packet if needed */ + if (!((uintptr)frame & 1) && (pad1 = ((uintptr)frame % DHD_SDALIGN))) { + if (PKTHEADROOM(osh, pkt) < pad1) { + DHD_INFO(("%s: insufficient headroom %d for %d pad1\n", + __FUNCTION__, (int)PKTHEADROOM(osh, pkt), pad1)); + bus->dhd->tx_realloc++; + new = PKTGET(osh, (PKTLEN(osh, pkt) + DHD_SDALIGN), TRUE); + if (!new) { + DHD_ERROR(("%s: couldn't allocate new %d-byte packet\n", + __FUNCTION__, PKTLEN(osh, pkt) + DHD_SDALIGN)); + ret = BCME_NOMEM; + goto done; + } + + PKTALIGN(osh, new, PKTLEN(osh, pkt), DHD_SDALIGN); + bcopy(PKTDATA(osh, pkt), PKTDATA(osh, new), PKTLEN(osh, pkt)); + if (free_pkt) + PKTFREE(osh, pkt, TRUE); + /* free the pkt if canned one is not used */ + free_pkt = TRUE; + pkt = new; + frame = (uint8*)PKTDATA(osh, pkt); + ASSERT(((uintptr)frame % DHD_SDALIGN) == 0); + pad1 = 0; + } else { + PKTPUSH(osh, pkt, pad1); + frame = (uint8*)PKTDATA(osh, pkt); + + ASSERT((pad1 + SDPCM_HDRLEN) <= (int) PKTLEN(osh, pkt)); + bzero(frame, pad1 + SDPCM_HDRLEN); + } + } + ASSERT(pad1 < DHD_SDALIGN); + + /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */ + len = (uint16)PKTLEN(osh, pkt); + *(uint16*)frame = htol16(len); + *(((uint16*)frame) + 1) = htol16(~len); + +#ifdef BCMSDIOH_TXGLOM + if (bus->glom_enable) { + uint32 hwheader1 = 0, hwheader2 = 0, act_len = len; + + /* Software tag: channel, sequence number, data offset */ + swheader = ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | + ((bus->tx_seq + bus->glom_cnt) % SDPCM_SEQUENCE_WRAP) | + (((pad1 + SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK); + htol32_ua_store(swheader, frame + SDPCM_FRAMETAG_LEN + SDPCM_HWEXT_LEN); + htol32_ua_store(0, frame + SDPCM_FRAMETAG_LEN + SDPCM_HWEXT_LEN + sizeof(swheader)); + + if (queue_only) { + if (forcealign && (len & (ALIGNMENT - 1))) + len = ROUNDUP(len, ALIGNMENT); + /* Hardware extention tag */ + /* 2byte frame length, 1byte-, 1byte frame flag, + * 2byte-hdrlength, 2byte padlenght + */ + hwheader1 = (act_len - SDPCM_FRAMETAG_LEN) | (0 << 24); + hwheader2 = (len - act_len) << 16; + htol32_ua_store(hwheader1, frame + SDPCM_FRAMETAG_LEN); + htol32_ua_store(hwheader2, frame + SDPCM_FRAMETAG_LEN + 4); + /* Post the frame pointer to sdio glom array */ + dhd_bcmsdh_glom_post(bus, frame, len); + /* Save the pkt pointer in bus glom array */ + bus->glom_pkt_arr[bus->glom_cnt] = pkt; + bus->glom_total_len += len; + bus->glom_cnt++; + return BCME_OK; + } else { + /* Raise len to next SDIO block to eliminate tail command */ + if (bus->roundup && bus->blocksize && + ((bus->glom_total_len + len) > bus->blocksize)) { + uint16 pad2 = bus->blocksize - + ((bus->glom_total_len + len) % bus->blocksize); + if ((pad2 <= bus->roundup) && (pad2 < bus->blocksize)) { + len += pad2; + } else { + } + } else if ((bus->glom_total_len + len) % DHD_SDALIGN) { + len += DHD_SDALIGN + - ((bus->glom_total_len + len) % DHD_SDALIGN); + } + if (forcealign && (len & (ALIGNMENT - 1))) { + len = ROUNDUP(len, ALIGNMENT); + } + + /* Hardware extention tag */ + /* 2byte frame length, 1byte-, 1byte frame flag, + * 2byte-hdrlength, 2byte padlenght + */ + hwheader1 = (act_len - SDPCM_FRAMETAG_LEN) | (1 << 24); + hwheader2 = (len - act_len) << 16; + htol32_ua_store(hwheader1, frame + SDPCM_FRAMETAG_LEN); + htol32_ua_store(hwheader2, frame + SDPCM_FRAMETAG_LEN + 4); + + /* Post the frame pointer to sdio glom array */ + dhd_bcmsdh_glom_post(bus, frame, len); + /* Save the pkt pointer in bus glom array */ + bus->glom_pkt_arr[bus->glom_cnt] = pkt; + bus->glom_cnt++; + bus->glom_total_len += len; + + /* Update the total length on the first pkt */ + frame_tmp = (uint8*)PKTDATA(osh, bus->glom_pkt_arr[0]); + *(uint16*)frame_tmp = htol16(bus->glom_total_len); + *(((uint16*)frame_tmp) + 1) = htol16(~bus->glom_total_len); + } + } else +#endif /* BCMSDIOH_TXGLOM */ + { + /* Software tag: channel, sequence number, data offset */ + swheader = ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | bus->tx_seq | + (((pad1 + SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK); + htol32_ua_store(swheader, frame + SDPCM_FRAMETAG_LEN); + htol32_ua_store(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader)); + +#ifdef DHD_DEBUG + if (PKTPRIO(pkt) < ARRAYSIZE(tx_packets)) { + tx_packets[PKTPRIO(pkt)]++; + } + if (DHD_BYTES_ON() && + (((DHD_CTL_ON() && (chan == SDPCM_CONTROL_CHANNEL)) || + (DHD_DATA_ON() && (chan != SDPCM_CONTROL_CHANNEL))))) { + prhex("Tx Frame", frame, len); + } else if (DHD_HDRS_ON()) { + prhex("TxHdr", frame, MIN(len, 16)); + } +#endif + + /* Raise len to next SDIO block to eliminate tail command */ + if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { + uint16 pad2 = bus->blocksize - (len % bus->blocksize); + if ((pad2 <= bus->roundup) && (pad2 < bus->blocksize)) +#ifdef NOTUSED + if (pad2 <= PKTTAILROOM(osh, pkt)) +#endif /* NOTUSED */ + len += pad2; + } else if (len % DHD_SDALIGN) { + len += DHD_SDALIGN - (len % DHD_SDALIGN); + } + + /* Some controllers have trouble with odd bytes -- round to even */ + if (forcealign && (len & (ALIGNMENT - 1))) { +#ifdef NOTUSED + if (PKTTAILROOM(osh, pkt)) +#endif + len = ROUNDUP(len, ALIGNMENT); +#ifdef NOTUSED + else + DHD_ERROR(("%s: sending unrounded %d-byte packet\n", __FUNCTION__, len)); +#endif + } + } + + do { + ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, + frame, len, pkt, NULL, NULL); + bus->f2txdata++; + ASSERT(ret != BCME_PENDING); + + if (ret == BCME_NODEVICE) { + DHD_ERROR(("%s: Device asleep already\n", __FUNCTION__)); + } else if (ret < 0) { + /* On failure, abort the command and terminate the frame */ + DHD_ERROR(("%s: sdio error %d, abort command and terminate frame.\n", + __FUNCTION__, ret)); + bus->tx_sderrs++; + + bcmsdh_abort(sdh, SDIO_FUNC_2); + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL, + SFC_WF_TERM, NULL); + bus->f1regdata++; + + for (i = 0; i < 3; i++) { + uint8 hi, lo; + hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, + SBSDIO_FUNC1_WFRAMEBCHI, NULL); + lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, + SBSDIO_FUNC1_WFRAMEBCLO, NULL); + bus->f1regdata += 2; + if ((hi == 0) && (lo == 0)) + break; + } + } + if (ret == 0) { +#ifdef BCMSDIOH_TXGLOM + if (bus->glom_enable) { + bus->tx_seq = (bus->tx_seq + bus->glom_cnt) % SDPCM_SEQUENCE_WRAP; + } else +#endif + { + bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; + } + } + } while ((ret < 0) && retrydata && retries++ < TXRETRIES); + +done: + +#ifdef BCMSDIOH_TXGLOM + if (bus->glom_enable) { + dhd_bcmsdh_glom_clear(bus); + pkt_cnt = bus->glom_cnt; + } else +#endif + { + pkt_cnt = 1; + } + /* restore pkt buffer pointer before calling tx complete routine */ + while (pkt_cnt) { +#ifdef BCMSDIOH_TXGLOM + uint32 doff; + if (bus->glom_enable) { + pkt = bus->glom_pkt_arr[bus->glom_cnt - pkt_cnt]; + frame = (uint8*)PKTDATA(osh, pkt); + doff = ltoh32_ua(frame + SDPCM_FRAMETAG_LEN + SDPCM_HWEXT_LEN); + doff = (doff & SDPCM_DOFFSET_MASK) >> SDPCM_DOFFSET_SHIFT; + PKTPULL(osh, pkt, doff); + } else +#endif + { + PKTPULL(osh, pkt, SDPCM_HDRLEN + pad1); + } +#ifdef PROP_TXSTATUS + if (bus->dhd->wlfc_state) { + dhd_os_sdunlock(bus->dhd); + dhd_wlfc_txcomplete(bus->dhd, pkt, ret == 0); + dhd_os_sdlock(bus->dhd); + } else { +#endif /* PROP_TXSTATUS */ +#ifdef SDTEST + if (chan != SDPCM_TEST_CHANNEL) { + dhd_txcomplete(bus->dhd, pkt, ret != 0); + } +#else /* SDTEST */ + dhd_txcomplete(bus->dhd, pkt, ret != 0); +#endif /* SDTEST */ + if (free_pkt) + PKTFREE(osh, pkt, TRUE); + +#ifdef PROP_TXSTATUS + } +#endif + pkt_cnt--; + } + +#ifdef BCMSDIOH_TXGLOM + /* Reset the glom array */ + if (bus->glom_enable) { + bus->glom_cnt = 0; + bus->glom_total_len = 0; + } +#endif + return ret; +} + +int +dhd_bus_txdata(struct dhd_bus *bus, void *pkt) +{ + int ret = BCME_ERROR; + osl_t *osh; + uint datalen, prec; +#ifdef DHD_TX_DUMP + uint8 *dump_data; + uint16 protocol; +#ifdef DHD_TX_FULL_DUMP + int i; +#endif /* DHD_TX_FULL_DUMP */ +#endif /* DHD_TX_DUMP */ + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + osh = bus->dhd->osh; + datalen = PKTLEN(osh, pkt); + +#ifdef SDTEST + /* Push the test header if doing loopback */ + if (bus->ext_loop) { + uint8* data; + PKTPUSH(osh, pkt, SDPCM_TEST_HDRLEN); + data = PKTDATA(osh, pkt); + *data++ = SDPCM_TEST_ECHOREQ; + *data++ = (uint8)bus->loopid++; + *data++ = (datalen >> 0); + *data++ = (datalen >> 8); + datalen += SDPCM_TEST_HDRLEN; + } +#endif /* SDTEST */ + +#ifdef DHD_TX_DUMP + dump_data = PKTDATA(osh, pkt); + dump_data += 4; /* skip 4 bytes header */ + protocol = (dump_data[12] << 8) | dump_data[13]; +#ifdef DHD_TX_FULL_DUMP + DHD_ERROR(("TX DUMP\n")); + + for (i = 0; i < (datalen - 4); i++) { + DHD_ERROR(("%02X ", dump_data[i])); + if ((i & 15) == 15) + printk("\n"); + } + DHD_ERROR(("\n")); + +#endif /* DHD_TX_FULL_DUMP */ + if (protocol == ETHER_TYPE_802_1X) { + DHD_ERROR(("ETHER_TYPE_802_1X: ver %d, type %d, replay %d\n", + dump_data[14], dump_data[15], dump_data[30])); + } +#endif /* DHD_TX_DUMP */ + + /* Add space for the header */ + PKTPUSH(osh, pkt, SDPCM_HDRLEN); + ASSERT(ISALIGNED((uintptr)PKTDATA(osh, pkt), 2)); + + prec = PRIO2PREC((PKTPRIO(pkt) & PRIOMASK)); +#ifndef DHDTHREAD + /* Lock: we're about to use shared data/code (and SDIO) */ + dhd_os_sdlock(bus->dhd); +#endif /* DHDTHREAD */ + + /* Check for existing queue, current flow-control, pending event, or pending clock */ + if (dhd_deferred_tx || bus->fcstate || pktq_len(&bus->txq) || bus->dpc_sched || + (!DATAOK(bus)) || (bus->flowcontrol & NBITVAL(prec)) || + (bus->clkstate != CLK_AVAIL)) { + DHD_TRACE(("%s: deferring pktq len %d\n", __FUNCTION__, + pktq_len(&bus->txq))); + bus->fcqueued++; + + /* Priority based enq */ + dhd_os_sdlock_txq(bus->dhd); + if (dhd_prec_enq(bus->dhd, &bus->txq, pkt, prec) == FALSE) { + PKTPULL(osh, pkt, SDPCM_HDRLEN); +#ifndef DHDTHREAD + /* Need to also release txqlock before releasing sdlock. + * This thread still has txqlock and releases sdlock. + * Deadlock happens when dpc() grabs sdlock first then + * attempts to grab txqlock. + */ + dhd_os_sdunlock_txq(bus->dhd); + dhd_os_sdunlock(bus->dhd); +#endif +#ifdef PROP_TXSTATUS + if (bus->dhd->wlfc_state) + dhd_wlfc_txcomplete(bus->dhd, pkt, FALSE); + else +#endif + dhd_txcomplete(bus->dhd, pkt, FALSE); +#ifndef DHDTHREAD + dhd_os_sdlock(bus->dhd); + dhd_os_sdlock_txq(bus->dhd); +#endif +#ifdef PROP_TXSTATUS + /* let the caller decide whether to free the packet */ + if (!bus->dhd->wlfc_state) +#endif + PKTFREE(osh, pkt, TRUE); + ret = BCME_NORESOURCE; + } + else + ret = BCME_OK; + dhd_os_sdunlock_txq(bus->dhd); + + if ((pktq_len(&bus->txq) >= FCHI) && dhd_doflow) + dhd_txflowcontrol(bus->dhd, ALL_INTERFACES, ON); + +#ifdef DHD_DEBUG + if (pktq_plen(&bus->txq, prec) > qcount[prec]) + qcount[prec] = pktq_plen(&bus->txq, prec); +#endif + /* Schedule DPC if needed to send queued packet(s) */ + if (dhd_deferred_tx && !bus->dpc_sched) { + bus->dpc_sched = TRUE; + dhd_sched_dpc(bus->dhd); + } + } else { +#ifdef DHDTHREAD + /* Lock: we're about to use shared data/code (and SDIO) */ + dhd_os_sdlock(bus->dhd); +#endif /* DHDTHREAD */ + + /* Otherwise, send it now */ + BUS_WAKE(bus); + /* Make sure back plane ht clk is on, no pending allowed */ + dhdsdio_clkctl(bus, CLK_AVAIL, TRUE); +#ifndef SDTEST + ret = dhdsdio_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, TRUE, FALSE); +#else + ret = dhdsdio_txpkt(bus, pkt, + (bus->ext_loop ? SDPCM_TEST_CHANNEL : SDPCM_DATA_CHANNEL), TRUE, FALSE); +#endif + if (ret) + bus->dhd->tx_errors++; + else + bus->dhd->dstats.tx_bytes += datalen; + + if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) { + bus->activity = FALSE; + dhdsdio_clkctl(bus, CLK_NONE, TRUE); + } + +#ifdef DHDTHREAD + dhd_os_sdunlock(bus->dhd); +#endif /* DHDTHREAD */ + } + +#ifndef DHDTHREAD + dhd_os_sdunlock(bus->dhd); +#endif /* DHDTHREAD */ + + return ret; +} + +static uint +dhdsdio_sendfromq(dhd_bus_t *bus, uint maxframes) +{ + void *pkt; + uint32 intstatus = 0; + uint retries = 0; + int ret = 0, prec_out; + uint cnt = 0; + uint datalen; + uint8 tx_prec_map; +#ifdef BCMSDIOH_TXGLOM + uint i; + uint8 glom_cnt; +#endif + + dhd_pub_t *dhd = bus->dhd; + sdpcmd_regs_t *regs = bus->regs; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (!KSO_ENAB(bus)) { + DHD_ERROR(("%s: Device asleep\n", __FUNCTION__)); + return BCME_NODEVICE; + } + + tx_prec_map = ~bus->flowcontrol; + + /* Send frames until the limit or some other event */ + for (cnt = 0; (cnt < maxframes) && DATAOK(bus); cnt++) { +#ifdef BCMSDIOH_TXGLOM + if (bus->glom_enable) { + glom_cnt = MIN(DATABUFCNT(bus), bus->glomsize); + glom_cnt = MIN(glom_cnt, pktq_mlen(&bus->txq, tx_prec_map)); + glom_cnt = MIN(glom_cnt, maxframes-cnt); + + /* Limiting the size to 2pkts in case of copy */ + if (bus->glom_mode == SDPCM_TXGLOM_CPY) + glom_cnt = MIN(glom_cnt, 5); + + if (glom_cnt == 0) + break; + datalen = 0; + for (i = 0; i < glom_cnt; i++) { + dhd_os_sdlock_txq(bus->dhd); + if ((pkt = pktq_mdeq(&bus->txq, tx_prec_map, &prec_out)) == NULL) { + /* This case should not happen */ + DHD_ERROR(("No pkts in the queue for glomming\n")); + dhd_os_sdunlock_txq(bus->dhd); + break; + } + dhd_os_sdunlock_txq(bus->dhd); + + datalen += (PKTLEN(bus->dhd->osh, pkt) - SDPCM_HDRLEN); +#ifndef SDTEST + ret = dhdsdio_txpkt(bus, + pkt, + SDPCM_DATA_CHANNEL, + TRUE, + (i == (glom_cnt-1))? FALSE: TRUE); +#else + ret = dhdsdio_txpkt(bus, + pkt, + (bus->ext_loop ? SDPCM_TEST_CHANNEL : SDPCM_DATA_CHANNEL), + TRUE, + (i == (glom_cnt-1))? FALSE: TRUE); +#endif + } + cnt += i-1; + } else +#endif /* BCMSDIOH_TXGLOM */ + { + dhd_os_sdlock_txq(bus->dhd); + if ((pkt = pktq_mdeq(&bus->txq, tx_prec_map, &prec_out)) == NULL) { + dhd_os_sdunlock_txq(bus->dhd); + break; + } + dhd_os_sdunlock_txq(bus->dhd); + datalen = PKTLEN(bus->dhd->osh, pkt) - SDPCM_HDRLEN; + +#ifndef SDTEST + ret = dhdsdio_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, TRUE, FALSE); +#else + ret = dhdsdio_txpkt(bus, + pkt, + (bus->ext_loop ? SDPCM_TEST_CHANNEL : SDPCM_DATA_CHANNEL), + TRUE, + FALSE); +#endif + } + + if (ret) + bus->dhd->tx_errors++; + else + bus->dhd->dstats.tx_bytes += datalen; + + /* In poll mode, need to check for other events */ + if (!bus->intr && cnt) + { + /* Check device status, signal pending interrupt */ + R_SDREG(intstatus, ®s->intstatus, retries); + bus->f2txdata++; + if (bcmsdh_regfail(bus->sdh)) + break; + if (intstatus & bus->hostintmask) + bus->ipend = TRUE; + } + } + + /* Deflow-control stack if needed */ + if (dhd_doflow && dhd->up && (dhd->busstate == DHD_BUS_DATA) && + dhd->txoff && (pktq_len(&bus->txq) < FCLOW)) + dhd_txflowcontrol(dhd, ALL_INTERFACES, OFF); + + return cnt; +} + +int +dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen) +{ + uint8 *frame; + uint16 len; + uint32 swheader; + uint retries = 0; + bcmsdh_info_t *sdh = bus->sdh; + uint8 doff = 0; + int ret = -1; + int i; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (bus->dhd->dongle_reset) + return -EIO; + + /* Back the pointer to make a room for bus header */ + frame = msg - SDPCM_HDRLEN; + len = (msglen += SDPCM_HDRLEN); + + /* Add alignment padding (optional for ctl frames) */ + if (dhd_alignctl) { + if ((doff = ((uintptr)frame % DHD_SDALIGN))) { + frame -= doff; + len += doff; + msglen += doff; + bzero(frame, doff + SDPCM_HDRLEN); + } + ASSERT(doff < DHD_SDALIGN); + } + doff += SDPCM_HDRLEN; + + /* Round send length to next SDIO block */ + if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { + uint16 pad = bus->blocksize - (len % bus->blocksize); + if ((pad <= bus->roundup) && (pad < bus->blocksize)) + len += pad; + } else if (len % DHD_SDALIGN) { + len += DHD_SDALIGN - (len % DHD_SDALIGN); + } + + /* Satisfy length-alignment requirements */ + if (forcealign && (len & (ALIGNMENT - 1))) + len = ROUNDUP(len, ALIGNMENT); + + ASSERT(ISALIGNED((uintptr)frame, 2)); + + + /* Need to lock here to protect txseq and SDIO tx calls */ + dhd_os_sdlock(bus->dhd); + + BUS_WAKE(bus); + + /* Make sure backplane clock is on */ + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + + /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */ + *(uint16*)frame = htol16((uint16)msglen); + *(((uint16*)frame) + 1) = htol16(~msglen); + +#ifdef BCMSDIOH_TXGLOM + if (bus->glom_enable) { + uint32 hwheader1, hwheader2; + /* Software tag: channel, sequence number, data offset */ + swheader = ((SDPCM_CONTROL_CHANNEL << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) + | bus->tx_seq + | ((doff << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK); + htol32_ua_store(swheader, frame + SDPCM_FRAMETAG_LEN + SDPCM_HWEXT_LEN); + htol32_ua_store(0, frame + SDPCM_FRAMETAG_LEN + + SDPCM_HWEXT_LEN + sizeof(swheader)); + + hwheader1 = (msglen - SDPCM_FRAMETAG_LEN) | (1 << 24); + hwheader2 = (len - (msglen)) << 16; + htol32_ua_store(hwheader1, frame + SDPCM_FRAMETAG_LEN); + htol32_ua_store(hwheader2, frame + SDPCM_FRAMETAG_LEN + 4); + + *(uint16*)frame = htol16(len); + *(((uint16*)frame) + 1) = htol16(~(len)); + } else +#endif /* BCMSDIOH_TXGLOM */ + { + /* Software tag: channel, sequence number, data offset */ + swheader = ((SDPCM_CONTROL_CHANNEL << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) + | bus->tx_seq | ((doff << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK); + htol32_ua_store(swheader, frame + SDPCM_FRAMETAG_LEN); + htol32_ua_store(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader)); + } + if (!TXCTLOK(bus)) { + DHD_INFO(("%s: No bus credit bus->tx_max %d, bus->tx_seq %d\n", + __FUNCTION__, bus->tx_max, bus->tx_seq)); + bus->ctrl_frame_stat = TRUE; + /* Send from dpc */ + bus->ctrl_frame_buf = frame; + bus->ctrl_frame_len = len; + + if (!bus->dpc_sched) { + bus->dpc_sched = TRUE; + dhd_sched_dpc(bus->dhd); + } + if (bus->ctrl_frame_stat) { + dhd_wait_for_event(bus->dhd, &bus->ctrl_frame_stat); + } + + if (bus->ctrl_frame_stat == FALSE) { + DHD_INFO(("%s: ctrl_frame_stat == FALSE\n", __FUNCTION__)); + ret = 0; + } else { + bus->dhd->txcnt_timeout++; + if (!bus->dhd->hang_was_sent) { + DHD_ERROR(("%s: ctrl_frame_stat == TRUE txcnt_timeout=%d\n", + __FUNCTION__, bus->dhd->txcnt_timeout)); + } + ret = -1; + bus->ctrl_frame_stat = FALSE; + goto done; + } + } + + bus->dhd->txcnt_timeout = 0; + + if (ret == -1) { +#ifdef DHD_DEBUG + if (DHD_BYTES_ON() && DHD_CTL_ON()) { + prhex("Tx Frame", frame, len); + } else if (DHD_HDRS_ON()) { + prhex("TxHdr", frame, MIN(len, 16)); + } +#endif + + do { + ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, + frame, len, NULL, NULL, NULL); + ASSERT(ret != BCME_PENDING); + + if (ret == BCME_NODEVICE) { + DHD_ERROR(("%s: Device asleep already\n", __FUNCTION__)); + } else if (ret < 0) { + /* On failure, abort the command and terminate the frame */ + DHD_INFO(("%s: sdio error %d, abort command and terminate frame.\n", + __FUNCTION__, ret)); + bus->tx_sderrs++; + + bcmsdh_abort(sdh, SDIO_FUNC_2); + + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL, + SFC_WF_TERM, NULL); + bus->f1regdata++; + + for (i = 0; i < 3; i++) { + uint8 hi, lo; + hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, + SBSDIO_FUNC1_WFRAMEBCHI, NULL); + lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, + SBSDIO_FUNC1_WFRAMEBCLO, NULL); + bus->f1regdata += 2; + if ((hi == 0) && (lo == 0)) + break; + } + } + if (ret == 0) { + bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; + } + } while ((ret < 0) && retries++ < TXRETRIES); + } + +done: + if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) { + bus->activity = FALSE; + dhdsdio_clkctl(bus, CLK_NONE, TRUE); + } + + dhd_os_sdunlock(bus->dhd); + + if (ret) + bus->dhd->tx_ctlerrs++; + else + bus->dhd->tx_ctlpkts++; + + if (bus->dhd->txcnt_timeout >= MAX_CNTL_TIMEOUT) + return -ETIMEDOUT; + + return ret ? -EIO : 0; +} + +int +dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen) +{ + int timeleft; + uint rxlen = 0; + bool pending; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (bus->dhd->dongle_reset) + return -EIO; + + /* Wait until control frame is available */ + timeleft = dhd_os_ioctl_resp_wait(bus->dhd, &bus->rxlen, &pending); + + dhd_os_sdlock(bus->dhd); + rxlen = bus->rxlen; + bcopy(bus->rxctl, msg, MIN(msglen, rxlen)); + bus->rxlen = 0; + dhd_os_sdunlock(bus->dhd); + + if (rxlen) { + DHD_CTL(("%s: resumed on rxctl frame, got %d expected %d\n", + __FUNCTION__, rxlen, msglen)); + } else if (timeleft == 0) { +#ifdef DHD_DEBUG + uint32 status, retry = 0; + R_SDREG(status, &bus->regs->intstatus, retry); + DHD_ERROR(("%s: resumed on timeout, INT status=0x%08X\n", + __FUNCTION__, status)); +#else + DHD_ERROR(("%s: resumed on timeout\n", __FUNCTION__)); +#endif /* DHD_DEBUG */ +#ifdef DHD_DEBUG + dhd_os_sdlock(bus->dhd); + dhdsdio_checkdied(bus, NULL, 0); + dhd_os_sdunlock(bus->dhd); +#endif /* DHD_DEBUG */ + } else if (pending == TRUE) { + /* signal pending */ + DHD_ERROR(("%s: signal pending\n", __FUNCTION__)); + return -EINTR; + } else { + DHD_CTL(("%s: resumed for unknown reason?\n", __FUNCTION__)); +#ifdef DHD_DEBUG + dhd_os_sdlock(bus->dhd); + dhdsdio_checkdied(bus, NULL, 0); + dhd_os_sdunlock(bus->dhd); +#endif /* DHD_DEBUG */ + } + if (timeleft == 0) { + bus->dhd->rxcnt_timeout++; + DHD_ERROR(("%s: rxcnt_timeout=%d\n", __FUNCTION__, bus->dhd->rxcnt_timeout)); + } + else + bus->dhd->rxcnt_timeout = 0; + + if (rxlen) + bus->dhd->rx_ctlpkts++; + else + bus->dhd->rx_ctlerrs++; + + if (bus->dhd->rxcnt_timeout >= MAX_CNTL_TIMEOUT) + return -ETIMEDOUT; + + if (bus->dhd->dongle_trap_occured) + return -EREMOTEIO; + + return rxlen ? (int)rxlen : -EIO; +} + +/* IOVar table */ +enum { + IOV_INTR = 1, + IOV_POLLRATE, + IOV_SDREG, + IOV_SBREG, + IOV_SDCIS, + IOV_MEMBYTES, + IOV_MEMSIZE, +#ifdef DHD_DEBUG + IOV_CHECKDIED, + IOV_SERIALCONS, +#endif /* DHD_DEBUG */ + IOV_SET_DOWNLOAD_STATE, + IOV_SOCRAM_STATE, + IOV_FORCEEVEN, + IOV_SDIOD_DRIVE, + IOV_READAHEAD, + IOV_SDRXCHAIN, + IOV_ALIGNCTL, + IOV_SDALIGN, + IOV_DEVRESET, + IOV_CPU, +#if defined(SDIO_CRC_ERROR_FIX) + IOV_WATERMARK, + IOV_MESBUSYCTRL, +#endif /* SDIO_CRC_ERROR_FIX */ +#ifdef SDTEST + IOV_PKTGEN, + IOV_EXTLOOP, +#endif /* SDTEST */ + IOV_SPROM, + IOV_TXBOUND, + IOV_RXBOUND, + IOV_TXMINMAX, + IOV_IDLETIME, + IOV_IDLECLOCK, + IOV_SD1IDLE, + IOV_SLEEP, + IOV_DONGLEISOLATION, + IOV_KSO, + IOV_DEVSLEEP, + IOV_DEVCAP, + IOV_VARS, +#ifdef SOFTAP + IOV_FWPATH, +#endif + IOV_TXGLOMSIZE, + IOV_TXGLOMMODE +}; + +const bcm_iovar_t dhdsdio_iovars[] = { + {"intr", IOV_INTR, 0, IOVT_BOOL, 0 }, + {"sleep", IOV_SLEEP, 0, IOVT_BOOL, 0 }, + {"pollrate", IOV_POLLRATE, 0, IOVT_UINT32, 0 }, + {"idletime", IOV_IDLETIME, 0, IOVT_INT32, 0 }, + {"idleclock", IOV_IDLECLOCK, 0, IOVT_INT32, 0 }, + {"sd1idle", IOV_SD1IDLE, 0, IOVT_BOOL, 0 }, + {"membytes", IOV_MEMBYTES, 0, IOVT_BUFFER, 2 * sizeof(int) }, + {"memsize", IOV_MEMSIZE, 0, IOVT_UINT32, 0 }, + {"dwnldstate", IOV_SET_DOWNLOAD_STATE, 0, IOVT_BOOL, 0 }, + {"socram_state", IOV_SOCRAM_STATE, 0, IOVT_BOOL, 0 }, + {"vars", IOV_VARS, 0, IOVT_BUFFER, 0 }, + {"sdiod_drive", IOV_SDIOD_DRIVE, 0, IOVT_UINT32, 0 }, + {"readahead", IOV_READAHEAD, 0, IOVT_BOOL, 0 }, + {"sdrxchain", IOV_SDRXCHAIN, 0, IOVT_BOOL, 0 }, + {"alignctl", IOV_ALIGNCTL, 0, IOVT_BOOL, 0 }, + {"sdalign", IOV_SDALIGN, 0, IOVT_BOOL, 0 }, + {"devreset", IOV_DEVRESET, 0, IOVT_BOOL, 0 }, +#ifdef DHD_DEBUG + {"sdreg", IOV_SDREG, 0, IOVT_BUFFER, sizeof(sdreg_t) }, + {"sbreg", IOV_SBREG, 0, IOVT_BUFFER, sizeof(sdreg_t) }, + {"sd_cis", IOV_SDCIS, 0, IOVT_BUFFER, DHD_IOCTL_MAXLEN }, + {"forcealign", IOV_FORCEEVEN, 0, IOVT_BOOL, 0 }, + {"txbound", IOV_TXBOUND, 0, IOVT_UINT32, 0 }, + {"rxbound", IOV_RXBOUND, 0, IOVT_UINT32, 0 }, + {"txminmax", IOV_TXMINMAX, 0, IOVT_UINT32, 0 }, + {"cpu", IOV_CPU, 0, IOVT_BOOL, 0 }, +#ifdef DHD_DEBUG + {"checkdied", IOV_CHECKDIED, 0, IOVT_BUFFER, 0 }, + {"serial", IOV_SERIALCONS, 0, IOVT_UINT32, 0 }, +#endif /* DHD_DEBUG */ +#endif /* DHD_DEBUG */ +#ifdef SDTEST + {"extloop", IOV_EXTLOOP, 0, IOVT_BOOL, 0 }, + {"pktgen", IOV_PKTGEN, 0, IOVT_BUFFER, sizeof(dhd_pktgen_t) }, +#endif /* SDTEST */ +#if defined(SDIO_CRC_ERROR_FIX) + {"watermark", IOV_WATERMARK, 0, IOVT_UINT32, 0 }, + {"mesbusyctrl", IOV_MESBUSYCTRL, 0, IOVT_UINT32, 0 }, +#endif /* SDIO_CRC_ERROR_FIX */ + {"devcap", IOV_DEVCAP, 0, IOVT_UINT32, 0 }, + {"dngl_isolation", IOV_DONGLEISOLATION, 0, IOVT_UINT32, 0 }, + {"kso", IOV_KSO, 0, IOVT_UINT32, 0 }, + {"devsleep", IOV_DEVSLEEP, 0, IOVT_UINT32, 0 }, +#ifdef SOFTAP + {"fwpath", IOV_FWPATH, 0, IOVT_BUFFER, 0 }, +#endif + {"txglomsize", IOV_TXGLOMSIZE, 0, IOVT_UINT32, 0 }, + {"txglommode", IOV_TXGLOMMODE, 0, IOVT_UINT32, 0 }, + {NULL, 0, 0, 0, 0 } +}; + +static void +dhd_dump_pct(struct bcmstrbuf *strbuf, char *desc, uint num, uint div) +{ + uint q1, q2; + + if (!div) { + bcm_bprintf(strbuf, "%s N/A", desc); + } else { + q1 = num / div; + q2 = (100 * (num - (q1 * div))) / div; + bcm_bprintf(strbuf, "%s %d.%02d", desc, q1, q2); + } +} + +void +dhd_bus_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf) +{ + dhd_bus_t *bus = dhdp->bus; + + bcm_bprintf(strbuf, "Bus SDIO structure:\n"); + bcm_bprintf(strbuf, "hostintmask 0x%08x intstatus 0x%08x sdpcm_ver %d\n", + bus->hostintmask, bus->intstatus, bus->sdpcm_ver); + bcm_bprintf(strbuf, "fcstate %d qlen %d tx_seq %d, max %d, rxskip %d rxlen %d rx_seq %d\n", + bus->fcstate, pktq_len(&bus->txq), bus->tx_seq, bus->tx_max, bus->rxskip, + bus->rxlen, bus->rx_seq); + bcm_bprintf(strbuf, "intr %d intrcount %d lastintrs %d spurious %d\n", + bus->intr, bus->intrcount, bus->lastintrs, bus->spurious); + bcm_bprintf(strbuf, "pollrate %d pollcnt %d regfails %d\n", + bus->pollrate, bus->pollcnt, bus->regfails); + + bcm_bprintf(strbuf, "\nAdditional counters:\n"); + bcm_bprintf(strbuf, "tx_sderrs %d fcqueued %d rxrtx %d rx_toolong %d rxc_errors %d\n", + bus->tx_sderrs, bus->fcqueued, bus->rxrtx, bus->rx_toolong, + bus->rxc_errors); + bcm_bprintf(strbuf, "rx_hdrfail %d badhdr %d badseq %d\n", + bus->rx_hdrfail, bus->rx_badhdr, bus->rx_badseq); + bcm_bprintf(strbuf, "fc_rcvd %d, fc_xoff %d, fc_xon %d\n", + bus->fc_rcvd, bus->fc_xoff, bus->fc_xon); + bcm_bprintf(strbuf, "rxglomfail %d, rxglomframes %d, rxglompkts %d\n", + bus->rxglomfail, bus->rxglomframes, bus->rxglompkts); + bcm_bprintf(strbuf, "f2rx (hdrs/data) %d (%d/%d), f2tx %d f1regs %d\n", + (bus->f2rxhdrs + bus->f2rxdata), bus->f2rxhdrs, bus->f2rxdata, + bus->f2txdata, bus->f1regdata); + { + dhd_dump_pct(strbuf, "\nRx: pkts/f2rd", bus->dhd->rx_packets, + (bus->f2rxhdrs + bus->f2rxdata)); + dhd_dump_pct(strbuf, ", pkts/f1sd", bus->dhd->rx_packets, bus->f1regdata); + dhd_dump_pct(strbuf, ", pkts/sd", bus->dhd->rx_packets, + (bus->f2rxhdrs + bus->f2rxdata + bus->f1regdata)); + dhd_dump_pct(strbuf, ", pkts/int", bus->dhd->rx_packets, bus->intrcount); + bcm_bprintf(strbuf, "\n"); + + dhd_dump_pct(strbuf, "Rx: glom pct", (100 * bus->rxglompkts), + bus->dhd->rx_packets); + dhd_dump_pct(strbuf, ", pkts/glom", bus->rxglompkts, bus->rxglomframes); + bcm_bprintf(strbuf, "\n"); + + dhd_dump_pct(strbuf, "Tx: pkts/f2wr", bus->dhd->tx_packets, bus->f2txdata); + dhd_dump_pct(strbuf, ", pkts/f1sd", bus->dhd->tx_packets, bus->f1regdata); + dhd_dump_pct(strbuf, ", pkts/sd", bus->dhd->tx_packets, + (bus->f2txdata + bus->f1regdata)); + dhd_dump_pct(strbuf, ", pkts/int", bus->dhd->tx_packets, bus->intrcount); + bcm_bprintf(strbuf, "\n"); + + dhd_dump_pct(strbuf, "Total: pkts/f2rw", + (bus->dhd->tx_packets + bus->dhd->rx_packets), + (bus->f2txdata + bus->f2rxhdrs + bus->f2rxdata)); + dhd_dump_pct(strbuf, ", pkts/f1sd", + (bus->dhd->tx_packets + bus->dhd->rx_packets), bus->f1regdata); + dhd_dump_pct(strbuf, ", pkts/sd", + (bus->dhd->tx_packets + bus->dhd->rx_packets), + (bus->f2txdata + bus->f2rxhdrs + bus->f2rxdata + bus->f1regdata)); + dhd_dump_pct(strbuf, ", pkts/int", + (bus->dhd->tx_packets + bus->dhd->rx_packets), bus->intrcount); + bcm_bprintf(strbuf, "\n\n"); + } + +#ifdef SDTEST + if (bus->pktgen_count) { + bcm_bprintf(strbuf, "pktgen config and count:\n"); + bcm_bprintf(strbuf, "freq %d count %d print %d total %d min %d len %d\n", + bus->pktgen_freq, bus->pktgen_count, bus->pktgen_print, + bus->pktgen_total, bus->pktgen_minlen, bus->pktgen_maxlen); + bcm_bprintf(strbuf, "send attempts %d rcvd %d fail %d\n", + bus->pktgen_sent, bus->pktgen_rcvd, bus->pktgen_fail); + } +#endif /* SDTEST */ +#ifdef DHD_DEBUG + bcm_bprintf(strbuf, "dpc_sched %d host interrupt%spending\n", + bus->dpc_sched, (bcmsdh_intr_pending(bus->sdh) ? " " : " not ")); + bcm_bprintf(strbuf, "blocksize %d roundup %d\n", bus->blocksize, bus->roundup); +#endif /* DHD_DEBUG */ + bcm_bprintf(strbuf, "clkstate %d activity %d idletime %d idlecount %d sleeping %d\n", + bus->clkstate, bus->activity, bus->idletime, bus->idlecount, bus->sleeping); +} + +void +dhd_bus_clearcounts(dhd_pub_t *dhdp) +{ + dhd_bus_t *bus = (dhd_bus_t *)dhdp->bus; + + bus->intrcount = bus->lastintrs = bus->spurious = bus->regfails = 0; + bus->rxrtx = bus->rx_toolong = bus->rxc_errors = 0; + bus->rx_hdrfail = bus->rx_badhdr = bus->rx_badseq = 0; + bus->tx_sderrs = bus->fc_rcvd = bus->fc_xoff = bus->fc_xon = 0; + bus->rxglomfail = bus->rxglomframes = bus->rxglompkts = 0; + bus->f2rxhdrs = bus->f2rxdata = bus->f2txdata = bus->f1regdata = 0; +} + +#ifdef SDTEST +static int +dhdsdio_pktgen_get(dhd_bus_t *bus, uint8 *arg) +{ + dhd_pktgen_t pktgen; + + pktgen.version = DHD_PKTGEN_VERSION; + pktgen.freq = bus->pktgen_freq; + pktgen.count = bus->pktgen_count; + pktgen.print = bus->pktgen_print; + pktgen.total = bus->pktgen_total; + pktgen.minlen = bus->pktgen_minlen; + pktgen.maxlen = bus->pktgen_maxlen; + pktgen.numsent = bus->pktgen_sent; + pktgen.numrcvd = bus->pktgen_rcvd; + pktgen.numfail = bus->pktgen_fail; + pktgen.mode = bus->pktgen_mode; + pktgen.stop = bus->pktgen_stop; + + bcopy(&pktgen, arg, sizeof(pktgen)); + + return 0; +} + +static int +dhdsdio_pktgen_set(dhd_bus_t *bus, uint8 *arg) +{ + dhd_pktgen_t pktgen; + uint oldcnt, oldmode; + + bcopy(arg, &pktgen, sizeof(pktgen)); + if (pktgen.version != DHD_PKTGEN_VERSION) + return BCME_BADARG; + + oldcnt = bus->pktgen_count; + oldmode = bus->pktgen_mode; + + bus->pktgen_freq = pktgen.freq; + bus->pktgen_count = pktgen.count; + bus->pktgen_print = pktgen.print; + bus->pktgen_total = pktgen.total; + bus->pktgen_minlen = pktgen.minlen; + bus->pktgen_maxlen = pktgen.maxlen; + bus->pktgen_mode = pktgen.mode; + bus->pktgen_stop = pktgen.stop; + + bus->pktgen_tick = bus->pktgen_ptick = 0; + bus->pktgen_prev_time = jiffies; + bus->pktgen_len = MAX(bus->pktgen_len, bus->pktgen_minlen); + bus->pktgen_len = MIN(bus->pktgen_len, bus->pktgen_maxlen); + + /* Clear counts for a new pktgen (mode change, or was stopped) */ + if (bus->pktgen_count && (!oldcnt || oldmode != bus->pktgen_mode)) { + bus->pktgen_sent = bus->pktgen_prev_sent = bus->pktgen_rcvd = 0; + bus->pktgen_prev_rcvd = bus->pktgen_fail = 0; + } + + return 0; +} +#endif /* SDTEST */ + +static void +dhdsdio_devram_remap(dhd_bus_t *bus, bool val) +{ + uint8 enable, protect, remap; + + si_socdevram(bus->sih, FALSE, &enable, &protect, &remap); + remap = val ? TRUE : FALSE; + si_socdevram(bus->sih, TRUE, &enable, &protect, &remap); +} + +static int +dhdsdio_membytes(dhd_bus_t *bus, bool write, uint32 address, uint8 *data, uint size) +{ + int bcmerror = 0; + uint32 sdaddr; + uint dsize; + + /* In remap mode, adjust address beyond socram and redirect + * to devram at SOCDEVRAM_BP_ADDR since remap address > orig_ramsize + * is not backplane accessible + */ + if (REMAP_ENAB(bus) && REMAP_ISADDR(bus, address)) { + address -= bus->orig_ramsize; + address += SOCDEVRAM_BP_ADDR; + } + + /* Determine initial transfer parameters */ + sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK; + if ((sdaddr + size) & SBSDIO_SBWINDOW_MASK) + dsize = (SBSDIO_SB_OFT_ADDR_LIMIT - sdaddr); + else + dsize = size; + + /* Set the backplane window to include the start address */ + if ((bcmerror = dhdsdio_set_siaddr_window(bus, address))) { + DHD_ERROR(("%s: window change failed\n", __FUNCTION__)); + goto xfer_done; + } + + /* Do the transfer(s) */ + while (size) { + DHD_INFO(("%s: %s %d bytes at offset 0x%08x in window 0x%08x\n", + __FUNCTION__, (write ? "write" : "read"), dsize, sdaddr, + (address & SBSDIO_SBWINDOW_MASK))); + if ((bcmerror = bcmsdh_rwdata(bus->sdh, write, sdaddr, data, dsize))) { + DHD_ERROR(("%s: membytes transfer failed\n", __FUNCTION__)); + break; + } + + /* Adjust for next transfer (if any) */ + if ((size -= dsize)) { + data += dsize; + address += dsize; + if ((bcmerror = dhdsdio_set_siaddr_window(bus, address))) { + DHD_ERROR(("%s: window change failed\n", __FUNCTION__)); + break; + } + sdaddr = 0; + dsize = MIN(SBSDIO_SB_OFT_ADDR_LIMIT, size); + } + + } + +xfer_done: + /* Return the window to backplane enumeration space for core access */ + if (dhdsdio_set_siaddr_window(bus, bcmsdh_cur_sbwad(bus->sdh))) { + DHD_ERROR(("%s: FAILED to set window back to 0x%x\n", __FUNCTION__, + bcmsdh_cur_sbwad(bus->sdh))); + } + + return bcmerror; +} + +#ifdef DHD_DEBUG +static int +dhdsdio_readshared(dhd_bus_t *bus, sdpcm_shared_t *sh) +{ + uint32 addr; + int rv, i; + uint32 shaddr = 0; + + shaddr = bus->dongle_ram_base + bus->ramsize - 4; + i = 0; + do { + /* Read last word in memory to determine address of sdpcm_shared structure */ + if ((rv = dhdsdio_membytes(bus, FALSE, shaddr, (uint8 *)&addr, 4)) < 0) + return rv; + + addr = ltoh32(addr); + + DHD_INFO(("sdpcm_shared address 0x%08X\n", addr)); + + /* + * Check if addr is valid. + * NVRAM length at the end of memory should have been overwritten. + */ + if (addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)) { + if ((bus->srmemsize > 0) && (i++ == 0)) { + shaddr -= bus->srmemsize; + } else { + DHD_ERROR(("%s: address (0x%08x) of sdpcm_shared invalid\n", + __FUNCTION__, addr)); + return BCME_ERROR; + } + } else + break; + } while (i < 2); + + /* Read hndrte_shared structure */ + if ((rv = dhdsdio_membytes(bus, FALSE, addr, (uint8 *)sh, sizeof(sdpcm_shared_t))) < 0) + return rv; + + /* Endianness */ + sh->flags = ltoh32(sh->flags); + sh->trap_addr = ltoh32(sh->trap_addr); + sh->assert_exp_addr = ltoh32(sh->assert_exp_addr); + sh->assert_file_addr = ltoh32(sh->assert_file_addr); + sh->assert_line = ltoh32(sh->assert_line); + sh->console_addr = ltoh32(sh->console_addr); + sh->msgtrace_addr = ltoh32(sh->msgtrace_addr); + + if ((sh->flags & SDPCM_SHARED_VERSION_MASK) == 3 && SDPCM_SHARED_VERSION == 1) + return BCME_OK; + + if ((sh->flags & SDPCM_SHARED_VERSION_MASK) != SDPCM_SHARED_VERSION) { + DHD_ERROR(("%s: sdpcm_shared version %d in dhd " + "is different than sdpcm_shared version %d in dongle\n", + __FUNCTION__, SDPCM_SHARED_VERSION, + sh->flags & SDPCM_SHARED_VERSION_MASK)); + return BCME_ERROR; + } + + return BCME_OK; +} + +#define CONSOLE_LINE_MAX 192 + +static int +dhdsdio_readconsole(dhd_bus_t *bus) +{ + dhd_console_t *c = &bus->console; + uint8 line[CONSOLE_LINE_MAX], ch; + uint32 n, idx, addr; + int rv; + + /* Don't do anything until FWREADY updates console address */ + if (bus->console_addr == 0) + return 0; + + if (!KSO_ENAB(bus)) + return 0; + + /* Read console log struct */ + addr = bus->console_addr + OFFSETOF(hndrte_cons_t, log); + if ((rv = dhdsdio_membytes(bus, FALSE, addr, (uint8 *)&c->log, sizeof(c->log))) < 0) + return rv; + + /* Allocate console buffer (one time only) */ + if (c->buf == NULL) { + c->bufsize = ltoh32(c->log.buf_size); + if ((c->buf = MALLOC(bus->dhd->osh, c->bufsize)) == NULL) + return BCME_NOMEM; + } + + idx = ltoh32(c->log.idx); + + /* Protect against corrupt value */ + if (idx > c->bufsize) + return BCME_ERROR; + + /* Skip reading the console buffer if the index pointer has not moved */ + if (idx == c->last) + return BCME_OK; + + /* Read the console buffer */ + addr = ltoh32(c->log.buf); + if ((rv = dhdsdio_membytes(bus, FALSE, addr, c->buf, c->bufsize)) < 0) + return rv; + + while (c->last != idx) { + for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) { + if (c->last == idx) { + /* This would output a partial line. Instead, back up + * the buffer pointer and output this line next time around. + */ + if (c->last >= n) + c->last -= n; + else + c->last = c->bufsize - n; + goto break2; + } + ch = c->buf[c->last]; + c->last = (c->last + 1) % c->bufsize; + if (ch == '\n') + break; + line[n] = ch; + } + + if (n > 0) { + if (line[n - 1] == '\r') + n--; + line[n] = 0; + printf("CONSOLE: %s\n", line); + } + } +break2: + + return BCME_OK; +} + +static int +dhdsdio_checkdied(dhd_bus_t *bus, char *data, uint size) +{ + int bcmerror = 0; + uint msize = 512; + char *mbuffer = NULL; + char *console_buffer = NULL; + uint maxstrlen = 256; + char *str = NULL; + trap_t tr; + sdpcm_shared_t sdpcm_shared; + struct bcmstrbuf strbuf; + uint32 console_ptr, console_size, console_index; + uint8 line[CONSOLE_LINE_MAX], ch; + uint32 n, i, addr; + int rv; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (data == NULL) { + /* + * Called after a rx ctrl timeout. "data" is NULL. + * allocate memory to trace the trap or assert. + */ + size = msize; + mbuffer = data = MALLOC(bus->dhd->osh, msize); + if (mbuffer == NULL) { + DHD_ERROR(("%s: MALLOC(%d) failed \n", __FUNCTION__, msize)); + bcmerror = BCME_NOMEM; + goto done; + } + } + + if ((str = MALLOC(bus->dhd->osh, maxstrlen)) == NULL) { + DHD_ERROR(("%s: MALLOC(%d) failed \n", __FUNCTION__, maxstrlen)); + bcmerror = BCME_NOMEM; + goto done; + } + + if ((bcmerror = dhdsdio_readshared(bus, &sdpcm_shared)) < 0) + goto done; + + bcm_binit(&strbuf, data, size); + + bcm_bprintf(&strbuf, "msgtrace address : 0x%08X\nconsole address : 0x%08X\n", + sdpcm_shared.msgtrace_addr, sdpcm_shared.console_addr); + + if ((sdpcm_shared.flags & SDPCM_SHARED_ASSERT_BUILT) == 0) { + /* NOTE: Misspelled assert is intentional - DO NOT FIX. + * (Avoids conflict with real asserts for programmatic parsing of output.) + */ + bcm_bprintf(&strbuf, "Assrt not built in dongle\n"); + } + + if ((sdpcm_shared.flags & (SDPCM_SHARED_ASSERT|SDPCM_SHARED_TRAP)) == 0) { + /* NOTE: Misspelled assert is intentional - DO NOT FIX. + * (Avoids conflict with real asserts for programmatic parsing of output.) + */ + bcm_bprintf(&strbuf, "No trap%s in dongle", + (sdpcm_shared.flags & SDPCM_SHARED_ASSERT_BUILT) + ?"/assrt" :""); + } else { + if (sdpcm_shared.flags & SDPCM_SHARED_ASSERT) { + /* Download assert */ + bcm_bprintf(&strbuf, "Dongle assert"); + if (sdpcm_shared.assert_exp_addr != 0) { + str[0] = '\0'; + if ((bcmerror = dhdsdio_membytes(bus, FALSE, + sdpcm_shared.assert_exp_addr, + (uint8 *)str, maxstrlen)) < 0) + goto done; + + str[maxstrlen - 1] = '\0'; + bcm_bprintf(&strbuf, " expr \"%s\"", str); + } + + if (sdpcm_shared.assert_file_addr != 0) { + str[0] = '\0'; + if ((bcmerror = dhdsdio_membytes(bus, FALSE, + sdpcm_shared.assert_file_addr, + (uint8 *)str, maxstrlen)) < 0) + goto done; + + str[maxstrlen - 1] = '\0'; + bcm_bprintf(&strbuf, " file \"%s\"", str); + } + + bcm_bprintf(&strbuf, " line %d ", sdpcm_shared.assert_line); + } + + if (sdpcm_shared.flags & SDPCM_SHARED_TRAP) { + bus->dhd->dongle_trap_occured = TRUE; + if ((bcmerror = dhdsdio_membytes(bus, FALSE, + sdpcm_shared.trap_addr, + (uint8*)&tr, sizeof(trap_t))) < 0) + goto done; + + bcm_bprintf(&strbuf, + "Dongle trap type 0x%x @ epc 0x%x, cpsr 0x%x, spsr 0x%x, sp 0x%x," + "lp 0x%x, rpc 0x%x Trap offset 0x%x, " + "r0 0x%x, r1 0x%x, r2 0x%x, r3 0x%x, " + "r4 0x%x, r5 0x%x, r6 0x%x, r7 0x%x\n\n", + ltoh32(tr.type), ltoh32(tr.epc), ltoh32(tr.cpsr), ltoh32(tr.spsr), + ltoh32(tr.r13), ltoh32(tr.r14), ltoh32(tr.pc), + ltoh32(sdpcm_shared.trap_addr), + ltoh32(tr.r0), ltoh32(tr.r1), ltoh32(tr.r2), ltoh32(tr.r3), + ltoh32(tr.r4), ltoh32(tr.r5), ltoh32(tr.r6), ltoh32(tr.r7)); + + addr = sdpcm_shared.console_addr + OFFSETOF(hndrte_cons_t, log); + if ((rv = dhdsdio_membytes(bus, FALSE, addr, + (uint8 *)&console_ptr, sizeof(console_ptr))) < 0) + goto printbuf; + + addr = sdpcm_shared.console_addr + OFFSETOF(hndrte_cons_t, log.buf_size); + if ((rv = dhdsdio_membytes(bus, FALSE, addr, + (uint8 *)&console_size, sizeof(console_size))) < 0) + goto printbuf; + + addr = sdpcm_shared.console_addr + OFFSETOF(hndrte_cons_t, log.idx); + if ((rv = dhdsdio_membytes(bus, FALSE, addr, + (uint8 *)&console_index, sizeof(console_index))) < 0) + goto printbuf; + + console_ptr = ltoh32(console_ptr); + console_size = ltoh32(console_size); + console_index = ltoh32(console_index); + + if (console_size > CONSOLE_BUFFER_MAX || + !(console_buffer = MALLOC(bus->dhd->osh, console_size))) + goto printbuf; + + if ((rv = dhdsdio_membytes(bus, FALSE, console_ptr, + (uint8 *)console_buffer, console_size)) < 0) + goto printbuf; + + for (i = 0, n = 0; i < console_size; i += n + 1) { + for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) { + ch = console_buffer[(console_index + i + n) % console_size]; + if (ch == '\n') + break; + line[n] = ch; + } + + + if (n > 0) { + if (line[n - 1] == '\r') + n--; + line[n] = 0; + /* Don't use DHD_ERROR macro since we print + * a lot of information quickly. The macro + * will truncate a lot of the printfs + */ + + if (dhd_msg_level & DHD_ERROR_VAL) + printf("CONSOLE: %s\n", line); + } + } + } + } + +printbuf: + if (sdpcm_shared.flags & (SDPCM_SHARED_ASSERT | SDPCM_SHARED_TRAP)) { + DHD_ERROR(("%s: %s\n", __FUNCTION__, strbuf.origbuf)); + } + + +done: + if (mbuffer) + MFREE(bus->dhd->osh, mbuffer, msize); + if (str) + MFREE(bus->dhd->osh, str, maxstrlen); + if (console_buffer) + MFREE(bus->dhd->osh, console_buffer, console_size); + + return bcmerror; +} +#endif /* #ifdef DHD_DEBUG */ + + +int +dhdsdio_downloadvars(dhd_bus_t *bus, void *arg, int len) +{ + int bcmerror = BCME_OK; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + /* Basic sanity checks */ + if (bus->dhd->up) { + bcmerror = BCME_NOTDOWN; + goto err; + } + if (!len) { + bcmerror = BCME_BUFTOOSHORT; + goto err; + } + + /* Free the old ones and replace with passed variables */ + if (bus->vars) + MFREE(bus->dhd->osh, bus->vars, bus->varsz); + + bus->vars = MALLOC(bus->dhd->osh, len); + bus->varsz = bus->vars ? len : 0; + if (bus->vars == NULL) { + bcmerror = BCME_NOMEM; + goto err; + } + + /* Copy the passed variables, which should include the terminating double-null */ + bcopy(arg, bus->vars, bus->varsz); +err: + return bcmerror; +} + +#ifdef DHD_DEBUG + +#define CC_PLL_CHIPCTRL_SERIAL_ENAB (1 << 24) +#define CC_CHIPCTRL_JTAG_SEL (1 << 3) +#define CC_CHIPCTRL_GPIO_SEL (0x3) +#define CC_PLL_CHIPCTRL_SERIAL_ENAB_4334 (1 << 28) + +static int +dhd_serialconsole(dhd_bus_t *bus, bool set, bool enable, int *bcmerror) +{ + int int_val; + uint32 addr, data, uart_enab = 0; + uint32 jtag_sel = CC_CHIPCTRL_JTAG_SEL; + uint32 gpio_sel = CC_CHIPCTRL_GPIO_SEL; + + addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_addr); + data = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_data); + *bcmerror = 0; + + bcmsdh_reg_write(bus->sdh, addr, 4, 1); + if (bcmsdh_regfail(bus->sdh)) { + *bcmerror = BCME_SDIO_ERROR; + return -1; + } + int_val = bcmsdh_reg_read(bus->sdh, data, 4); + if (bcmsdh_regfail(bus->sdh)) { + *bcmerror = BCME_SDIO_ERROR; + return -1; + } + if (bus->sih->chip == BCM4330_CHIP_ID) { + uart_enab = CC_PLL_CHIPCTRL_SERIAL_ENAB; + } + else if (bus->sih->chip == BCM4334_CHIP_ID || + bus->sih->chip == BCM43341_CHIP_ID) { + if (enable) { + /* Moved to PMU chipcontrol 1 from 4330 */ + int_val &= ~gpio_sel; + int_val |= jtag_sel; + } else { + int_val |= gpio_sel; + int_val &= ~jtag_sel; + } + uart_enab = CC_PLL_CHIPCTRL_SERIAL_ENAB_4334; + } + + if (!set) + return (int_val & uart_enab); + if (enable) + int_val |= uart_enab; + else + int_val &= ~uart_enab; + bcmsdh_reg_write(bus->sdh, data, 4, int_val); + if (bcmsdh_regfail(bus->sdh)) { + *bcmerror = BCME_SDIO_ERROR; + return -1; + } + if (bus->sih->chip == BCM4330_CHIP_ID) { + uint32 chipcontrol; + addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol); + chipcontrol = bcmsdh_reg_read(bus->sdh, addr, 4); + chipcontrol &= ~jtag_sel; + if (enable) { + chipcontrol |= jtag_sel; + chipcontrol &= ~gpio_sel; + } + bcmsdh_reg_write(bus->sdh, addr, 4, chipcontrol); + } + + return (int_val & uart_enab); +} +#endif + +static int +dhdsdio_doiovar(dhd_bus_t *bus, const bcm_iovar_t *vi, uint32 actionid, const char *name, + void *params, int plen, void *arg, int len, int val_size) +{ + int bcmerror = 0; + int32 int_val = 0; + bool bool_val = 0; + + DHD_TRACE(("%s: Enter, action %d name %s params %p plen %d arg %p len %d val_size %d\n", + __FUNCTION__, actionid, name, params, plen, arg, len, val_size)); + + if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, IOV_ISSET(actionid))) != 0) + goto exit; + + if (plen >= (int)sizeof(int_val)) + bcopy(params, &int_val, sizeof(int_val)); + + bool_val = (int_val != 0) ? TRUE : FALSE; + + + /* Some ioctls use the bus */ + dhd_os_sdlock(bus->dhd); + + /* Check if dongle is in reset. If so, only allow DEVRESET iovars */ + if (bus->dhd->dongle_reset && !(actionid == IOV_SVAL(IOV_DEVRESET) || + actionid == IOV_GVAL(IOV_DEVRESET))) { + bcmerror = BCME_NOTREADY; + goto exit; + } + + /* + * Special handling for keepSdioOn: New SDIO Wake-up Mechanism + */ + if ((vi->varid == IOV_KSO) && (IOV_ISSET(actionid))) { + dhdsdio_clk_kso_iovar(bus, bool_val); + goto exit; + } else if ((vi->varid == IOV_DEVSLEEP) && (IOV_ISSET(actionid))) { + { + dhdsdio_clk_devsleep_iovar(bus, bool_val); + if (!SLPAUTO_ENAB(bus) && (bool_val == FALSE) && (bus->ipend)) { + DHD_ERROR(("INT pending in devsleep 1, dpc_sched: %d\n", + bus->dpc_sched)); + if (!bus->dpc_sched) { + bus->dpc_sched = TRUE; + dhd_sched_dpc(bus->dhd); + } + } + } + goto exit; + } + + /* Handle sleep stuff before any clock mucking */ + if (vi->varid == IOV_SLEEP) { + if (IOV_ISSET(actionid)) { + bcmerror = dhdsdio_bussleep(bus, bool_val); + } else { + int_val = (int32)bus->sleeping; + bcopy(&int_val, arg, val_size); + } + goto exit; + } + + /* Request clock to allow SDIO accesses */ + if (!bus->dhd->dongle_reset) { + BUS_WAKE(bus); + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + } + + switch (actionid) { + case IOV_GVAL(IOV_INTR): + int_val = (int32)bus->intr; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_INTR): + bus->intr = bool_val; + bus->intdis = FALSE; + if (bus->dhd->up) { + if (bus->intr) { + DHD_INTR(("%s: enable SDIO device interrupts\n", __FUNCTION__)); + bcmsdh_intr_enable(bus->sdh); + } else { + DHD_INTR(("%s: disable SDIO interrupts\n", __FUNCTION__)); + bcmsdh_intr_disable(bus->sdh); + } + } + break; + + case IOV_GVAL(IOV_POLLRATE): + int_val = (int32)bus->pollrate; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_POLLRATE): + bus->pollrate = (uint)int_val; + bus->poll = (bus->pollrate != 0); + break; + + case IOV_GVAL(IOV_IDLETIME): + int_val = bus->idletime; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_IDLETIME): + if ((int_val < 0) && (int_val != DHD_IDLE_IMMEDIATE)) { + bcmerror = BCME_BADARG; + } else { + bus->idletime = int_val; + } + break; + + case IOV_GVAL(IOV_IDLECLOCK): + int_val = (int32)bus->idleclock; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_IDLECLOCK): + bus->idleclock = int_val; + break; + + case IOV_GVAL(IOV_SD1IDLE): + int_val = (int32)sd1idle; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_SD1IDLE): + sd1idle = bool_val; + break; + + + case IOV_SVAL(IOV_MEMBYTES): + case IOV_GVAL(IOV_MEMBYTES): + { + uint32 address; + uint size, dsize; + uint8 *data; + + bool set = (actionid == IOV_SVAL(IOV_MEMBYTES)); + + ASSERT(plen >= 2*sizeof(int)); + + address = (uint32)int_val; + bcopy((char *)params + sizeof(int_val), &int_val, sizeof(int_val)); + size = (uint)int_val; + + /* Do some validation */ + dsize = set ? plen - (2 * sizeof(int)) : len; + if (dsize < size) { + DHD_ERROR(("%s: error on %s membytes, addr 0x%08x size %d dsize %d\n", + __FUNCTION__, (set ? "set" : "get"), address, size, dsize)); + bcmerror = BCME_BADARG; + break; + } + + DHD_INFO(("%s: Request to %s %d bytes at address 0x%08x\n", __FUNCTION__, + (set ? "write" : "read"), size, address)); + + /* If we know about SOCRAM, check for a fit */ + if ((bus->orig_ramsize) && + ((address > bus->orig_ramsize) || (address + size > bus->orig_ramsize))) + { + uint8 enable, protect, remap; + si_socdevram(bus->sih, FALSE, &enable, &protect, &remap); + if (!enable || protect) { + DHD_ERROR(("%s: ramsize 0x%08x doesn't have %d bytes at 0x%08x\n", + __FUNCTION__, bus->orig_ramsize, size, address)); + DHD_ERROR(("%s: socram enable %d, protect %d\n", + __FUNCTION__, enable, protect)); + bcmerror = BCME_BADARG; + break; + } + + if (!REMAP_ENAB(bus) && (address >= SOCDEVRAM_ARM_ADDR)) { + uint32 devramsize = si_socdevram_size(bus->sih); + if ((address < SOCDEVRAM_ARM_ADDR) || + (address + size > (SOCDEVRAM_ARM_ADDR + devramsize))) { + DHD_ERROR(("%s: bad address 0x%08x, size 0x%08x\n", + __FUNCTION__, address, size)); + DHD_ERROR(("%s: socram range 0x%08x,size 0x%08x\n", + __FUNCTION__, SOCDEVRAM_ARM_ADDR, devramsize)); + bcmerror = BCME_BADARG; + break; + } + /* move it such that address is real now */ + address -= SOCDEVRAM_ARM_ADDR; + address += SOCDEVRAM_BP_ADDR; + DHD_INFO(("%s: Request to %s %d bytes @ Mapped address 0x%08x\n", + __FUNCTION__, (set ? "write" : "read"), size, address)); + } else if (REMAP_ENAB(bus) && REMAP_ISADDR(bus, address) && remap) { + /* Can not access remap region while devram remap bit is set + * ROM content would be returned in this case + */ + DHD_ERROR(("%s: Need to disable remap for address 0x%08x\n", + __FUNCTION__, address)); + bcmerror = BCME_ERROR; + break; + } + } + + /* Generate the actual data pointer */ + data = set ? (uint8*)params + 2 * sizeof(int): (uint8*)arg; + + /* Call to do the transfer */ + bcmerror = dhdsdio_membytes(bus, set, address, data, size); + + break; + } + + case IOV_GVAL(IOV_MEMSIZE): + int_val = (int32)bus->ramsize; + bcopy(&int_val, arg, val_size); + break; + + case IOV_GVAL(IOV_SDIOD_DRIVE): + int_val = (int32)dhd_sdiod_drive_strength; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_SDIOD_DRIVE): + dhd_sdiod_drive_strength = int_val; + si_sdiod_drive_strength_init(bus->sih, bus->dhd->osh, dhd_sdiod_drive_strength); + break; + + case IOV_SVAL(IOV_SET_DOWNLOAD_STATE): + bcmerror = dhdsdio_download_state(bus, bool_val); + break; + + case IOV_SVAL(IOV_SOCRAM_STATE): + bcmerror = dhdsdio_download_state(bus, bool_val); + break; + + case IOV_SVAL(IOV_VARS): + bcmerror = dhdsdio_downloadvars(bus, arg, len); + break; + + case IOV_GVAL(IOV_READAHEAD): + int_val = (int32)dhd_readahead; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_READAHEAD): + if (bool_val && !dhd_readahead) + bus->nextlen = 0; + dhd_readahead = bool_val; + break; + + case IOV_GVAL(IOV_SDRXCHAIN): + int_val = (int32)bus->use_rxchain; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_SDRXCHAIN): + if (bool_val && !bus->sd_rxchain) + bcmerror = BCME_UNSUPPORTED; + else + bus->use_rxchain = bool_val; + break; + case IOV_GVAL(IOV_ALIGNCTL): + int_val = (int32)dhd_alignctl; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_ALIGNCTL): + dhd_alignctl = bool_val; + break; + + case IOV_GVAL(IOV_SDALIGN): + int_val = DHD_SDALIGN; + bcopy(&int_val, arg, val_size); + break; + +#ifdef DHD_DEBUG + case IOV_GVAL(IOV_VARS): + if (bus->varsz < (uint)len) + bcopy(bus->vars, arg, bus->varsz); + else + bcmerror = BCME_BUFTOOSHORT; + break; +#endif /* DHD_DEBUG */ + +#ifdef DHD_DEBUG + case IOV_GVAL(IOV_SDREG): + { + sdreg_t *sd_ptr; + uint32 addr, size; + + sd_ptr = (sdreg_t *)params; + + addr = (uintptr)bus->regs + sd_ptr->offset; + size = sd_ptr->func; + int_val = (int32)bcmsdh_reg_read(bus->sdh, addr, size); + if (bcmsdh_regfail(bus->sdh)) + bcmerror = BCME_SDIO_ERROR; + bcopy(&int_val, arg, sizeof(int32)); + break; + } + + case IOV_SVAL(IOV_SDREG): + { + sdreg_t *sd_ptr; + uint32 addr, size; + + sd_ptr = (sdreg_t *)params; + + addr = (uintptr)bus->regs + sd_ptr->offset; + size = sd_ptr->func; + bcmsdh_reg_write(bus->sdh, addr, size, sd_ptr->value); + if (bcmsdh_regfail(bus->sdh)) + bcmerror = BCME_SDIO_ERROR; + break; + } + + /* Same as above, but offset is not backplane (not SDIO core) */ + case IOV_GVAL(IOV_SBREG): + { + sdreg_t sdreg; + uint32 addr, size; + + bcopy(params, &sdreg, sizeof(sdreg)); + + addr = SI_ENUM_BASE + sdreg.offset; + size = sdreg.func; + int_val = (int32)bcmsdh_reg_read(bus->sdh, addr, size); + if (bcmsdh_regfail(bus->sdh)) + bcmerror = BCME_SDIO_ERROR; + bcopy(&int_val, arg, sizeof(int32)); + break; + } + + case IOV_SVAL(IOV_SBREG): + { + sdreg_t sdreg; + uint32 addr, size; + + bcopy(params, &sdreg, sizeof(sdreg)); + + addr = SI_ENUM_BASE + sdreg.offset; + size = sdreg.func; + bcmsdh_reg_write(bus->sdh, addr, size, sdreg.value); + if (bcmsdh_regfail(bus->sdh)) + bcmerror = BCME_SDIO_ERROR; + break; + } + + case IOV_GVAL(IOV_SDCIS): + { + *(char *)arg = 0; + + bcmstrcat(arg, "\nFunc 0\n"); + bcmsdh_cis_read(bus->sdh, 0x10, (uint8 *)arg + strlen(arg), SBSDIO_CIS_SIZE_LIMIT); + bcmstrcat(arg, "\nFunc 1\n"); + bcmsdh_cis_read(bus->sdh, 0x11, (uint8 *)arg + strlen(arg), SBSDIO_CIS_SIZE_LIMIT); + bcmstrcat(arg, "\nFunc 2\n"); + bcmsdh_cis_read(bus->sdh, 0x12, (uint8 *)arg + strlen(arg), SBSDIO_CIS_SIZE_LIMIT); + break; + } + + case IOV_GVAL(IOV_FORCEEVEN): + int_val = (int32)forcealign; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_FORCEEVEN): + forcealign = bool_val; + break; + + case IOV_GVAL(IOV_TXBOUND): + int_val = (int32)dhd_txbound; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_TXBOUND): + dhd_txbound = (uint)int_val; + break; + + case IOV_GVAL(IOV_RXBOUND): + int_val = (int32)dhd_rxbound; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_RXBOUND): + dhd_rxbound = (uint)int_val; + break; + + case IOV_GVAL(IOV_TXMINMAX): + int_val = (int32)dhd_txminmax; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_TXMINMAX): + dhd_txminmax = (uint)int_val; + break; + + case IOV_GVAL(IOV_SERIALCONS): + int_val = dhd_serialconsole(bus, FALSE, 0, &bcmerror); + if (bcmerror != 0) + break; + + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_SERIALCONS): + dhd_serialconsole(bus, TRUE, bool_val, &bcmerror); + break; + + + +#endif /* DHD_DEBUG */ + + +#ifdef SDTEST + case IOV_GVAL(IOV_EXTLOOP): + int_val = (int32)bus->ext_loop; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_EXTLOOP): + bus->ext_loop = bool_val; + break; + + case IOV_GVAL(IOV_PKTGEN): + bcmerror = dhdsdio_pktgen_get(bus, arg); + break; + + case IOV_SVAL(IOV_PKTGEN): + bcmerror = dhdsdio_pktgen_set(bus, arg); + break; +#endif /* SDTEST */ + +#if defined(SDIO_CRC_ERROR_FIX) + case IOV_GVAL(IOV_WATERMARK): + int_val = (int32)watermark; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_WATERMARK): + watermark = (uint)int_val; + watermark = (watermark > SBSDIO_WATERMARK_MASK) ? SBSDIO_WATERMARK_MASK : watermark; + DHD_ERROR(("Setting watermark as 0x%x.\n", watermark)); + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_WATERMARK, (uint8)watermark, NULL); + break; + + case IOV_GVAL(IOV_MESBUSYCTRL): + int_val = (int32)mesbusyctrl; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_MESBUSYCTRL): + mesbusyctrl = (uint)int_val; + mesbusyctrl = (mesbusyctrl > SBSDIO_MESBUSYCTRL_MASK) + ? SBSDIO_MESBUSYCTRL_MASK : mesbusyctrl; + DHD_ERROR(("Setting mesbusyctrl as 0x%x.\n", mesbusyctrl)); + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_MESBUSYCTRL, + ((uint8)mesbusyctrl | 0x80), NULL); + break; +#endif /* SDIO_CRC_ERROR_FIX */ + + case IOV_GVAL(IOV_DONGLEISOLATION): + int_val = bus->dhd->dongle_isolation; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_DONGLEISOLATION): + bus->dhd->dongle_isolation = bool_val; + break; + + case IOV_SVAL(IOV_DEVRESET): + DHD_TRACE(("%s: Called set IOV_DEVRESET=%d dongle_reset=%d busstate=%d\n", + __FUNCTION__, bool_val, bus->dhd->dongle_reset, + bus->dhd->busstate)); + + ASSERT(bus->dhd->osh); + /* ASSERT(bus->cl_devid); */ + + dhd_bus_devreset(bus->dhd, (uint8)bool_val); + + break; +#ifdef SOFTAP + case IOV_GVAL(IOV_FWPATH): + { + uint32 fw_path_len; + + fw_path_len = strlen(bus->fw_path); + DHD_INFO(("[softap] get fwpath, l=%d\n", len)); + + if (fw_path_len > len-1) { + bcmerror = BCME_BUFTOOSHORT; + break; + } + + if (fw_path_len) { + bcopy(bus->fw_path, arg, fw_path_len); + ((uchar*)arg)[fw_path_len] = 0; + } + break; + } + + case IOV_SVAL(IOV_FWPATH): + DHD_INFO(("[softap] set fwpath, idx=%d\n", int_val)); + + switch (int_val) { + case 1: + bus->fw_path = fw_path; /* ordinary one */ + break; + case 2: + bus->fw_path = fw_path2; + break; + default: + bcmerror = BCME_BADARG; + break; + } + + DHD_INFO(("[softap] new fw path: %s\n", (bus->fw_path[0] ? bus->fw_path : "NULL"))); + break; + +#endif /* SOFTAP */ + case IOV_GVAL(IOV_DEVRESET): + DHD_TRACE(("%s: Called get IOV_DEVRESET\n", __FUNCTION__)); + + /* Get its status */ + int_val = (bool) bus->dhd->dongle_reset; + bcopy(&int_val, arg, val_size); + + break; + + case IOV_GVAL(IOV_KSO): + int_val = dhdsdio_sleepcsr_get(bus); + bcopy(&int_val, arg, val_size); + break; + + case IOV_GVAL(IOV_DEVCAP): + int_val = dhdsdio_devcap_get(bus); + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_DEVCAP): + dhdsdio_devcap_set(bus, (uint8) int_val); + break; + +#ifdef BCMSDIOH_TXGLOM + case IOV_GVAL(IOV_TXGLOMSIZE): + int_val = (int32)bus->glomsize; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_TXGLOMSIZE): + if (int_val > SDPCM_MAXGLOM_SIZE) { + bcmerror = BCME_ERROR; + } else { + bus->glomsize = (uint)int_val; + } + break; + case IOV_GVAL(IOV_TXGLOMMODE): + int_val = (int32)bus->glom_mode; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_TXGLOMMODE): + if ((int_val != SDPCM_TXGLOM_CPY) && (int_val != SDPCM_TXGLOM_MDESC)) { + bcmerror = BCME_RANGE; + } else { + if ((bus->glom_mode = bcmsdh_set_mode(bus->sdh, (uint)int_val)) != int_val) + bcmerror = BCME_ERROR; + } + break; +#endif /* BCMSDIOH_TXGLOM */ + default: + bcmerror = BCME_UNSUPPORTED; + break; + } + +exit: + if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) { + bus->activity = FALSE; + dhdsdio_clkctl(bus, CLK_NONE, TRUE); + } + + dhd_os_sdunlock(bus->dhd); + + return bcmerror; +} + +static int +dhdsdio_write_vars(dhd_bus_t *bus) +{ + int bcmerror = 0; + uint32 varsize, phys_size; + uint32 varaddr; + uint8 *vbuffer; + uint32 varsizew; +#ifdef DHD_DEBUG + uint8 *nvram_ularray; +#endif /* DHD_DEBUG */ + + /* Even if there are no vars are to be written, we still need to set the ramsize. */ + varsize = bus->varsz ? ROUNDUP(bus->varsz, 4) : 0; + varaddr = (bus->ramsize - 4) - varsize; + + varaddr += bus->dongle_ram_base; + + if (bus->vars) { + if ((bus->sih->buscoretype == SDIOD_CORE_ID) && (bus->sdpcmrev == 7)) { + if (((varaddr & 0x3C) == 0x3C) && (varsize > 4)) { + DHD_ERROR(("PR85623WAR in place\n")); + varsize += 4; + varaddr -= 4; + } + } + + vbuffer = (uint8 *)MALLOC(bus->dhd->osh, varsize); + if (!vbuffer) + return BCME_NOMEM; + + bzero(vbuffer, varsize); + bcopy(bus->vars, vbuffer, bus->varsz); + + /* Write the vars list */ + bcmerror = dhdsdio_membytes(bus, TRUE, varaddr, vbuffer, varsize); +#ifdef DHD_DEBUG + /* Verify NVRAM bytes */ + DHD_INFO(("Compare NVRAM dl & ul; varsize=%d\n", varsize)); + nvram_ularray = (uint8*)MALLOC(bus->dhd->osh, varsize); + if (!nvram_ularray) + return BCME_NOMEM; + + /* Upload image to verify downloaded contents. */ + memset(nvram_ularray, 0xaa, varsize); + + /* Read the vars list to temp buffer for comparison */ + bcmerror = dhdsdio_membytes(bus, FALSE, varaddr, nvram_ularray, varsize); + if (bcmerror) { + DHD_ERROR(("%s: error %d on reading %d nvram bytes at 0x%08x\n", + __FUNCTION__, bcmerror, varsize, varaddr)); + } + /* Compare the org NVRAM with the one read from RAM */ + if (memcmp(vbuffer, nvram_ularray, varsize)) { + DHD_ERROR(("%s: Downloaded NVRAM image is corrupted.\n", __FUNCTION__)); + } else + DHD_ERROR(("%s: Download, Upload and compare of NVRAM succeeded.\n", + __FUNCTION__)); + + MFREE(bus->dhd->osh, nvram_ularray, varsize); +#endif /* DHD_DEBUG */ + + MFREE(bus->dhd->osh, vbuffer, varsize); + } + + phys_size = REMAP_ENAB(bus) ? bus->ramsize : bus->orig_ramsize; + + phys_size += bus->dongle_ram_base; + + /* adjust to the user specified RAM */ + DHD_INFO(("Physical memory size: %d, usable memory size: %d\n", + phys_size, bus->ramsize)); + DHD_INFO(("Vars are at %d, orig varsize is %d\n", + varaddr, varsize)); + varsize = ((phys_size - 4) - varaddr); + + /* + * Determine the length token: + * Varsize, converted to words, in lower 16-bits, checksum in upper 16-bits. + */ + if (bcmerror) { + varsizew = 0; + } else { + varsizew = varsize / 4; + varsizew = (~varsizew << 16) | (varsizew & 0x0000FFFF); + varsizew = htol32(varsizew); + } + + DHD_INFO(("New varsize is %d, length token=0x%08x\n", varsize, varsizew)); + + /* Write the length token to the last word */ + bcmerror = dhdsdio_membytes(bus, TRUE, (phys_size - 4), + (uint8*)&varsizew, 4); + + return bcmerror; +} + +static int +dhdsdio_download_state(dhd_bus_t *bus, bool enter) +{ + uint retries; + int bcmerror = 0; + int foundcr4 = 0; + + /* To enter download state, disable ARM and reset SOCRAM. + * To exit download state, simply reset ARM (default is RAM boot). + */ + if (enter) { + bus->alp_only = TRUE; + + if (!(si_setcore(bus->sih, ARM7S_CORE_ID, 0)) && + !(si_setcore(bus->sih, ARMCM3_CORE_ID, 0))) { + if (si_setcore(bus->sih, ARMCR4_CORE_ID, 0)) { + foundcr4 = 1; + } else { + DHD_ERROR(("%s: Failed to find ARM core!\n", __FUNCTION__)); + bcmerror = BCME_ERROR; + goto fail; + } + } + + if (!foundcr4) { + si_core_disable(bus->sih, 0); + if (bcmsdh_regfail(bus->sdh)) { + bcmerror = BCME_SDIO_ERROR; + goto fail; + } + + if (!(si_setcore(bus->sih, SOCRAM_CORE_ID, 0))) { + DHD_ERROR(("%s: Failed to find SOCRAM core!\n", __FUNCTION__)); + bcmerror = BCME_ERROR; + goto fail; + } + + si_core_reset(bus->sih, 0, 0); + if (bcmsdh_regfail(bus->sdh)) { + DHD_ERROR(("%s: Failure trying reset SOCRAM core?\n", __FUNCTION__)); + bcmerror = BCME_SDIO_ERROR; + goto fail; + } + + /* Disable remap for download */ + if (REMAP_ENAB(bus) && si_socdevram_remap_isenb(bus->sih)) + dhdsdio_devram_remap(bus, FALSE); + + /* Clear the top bit of memory */ + if (bus->ramsize) { + uint32 zeros = 0; + if (dhdsdio_membytes(bus, TRUE, bus->ramsize - 4, (uint8*)&zeros, 4) < 0) { + bcmerror = BCME_SDIO_ERROR; + goto fail; + } + } + } else { + /* For CR4, + * Halt ARM + * Remove ARM reset + * Read RAM base address [0x18_0000] + * [next] Download firmware + * [done at else] Populate the reset vector + * [done at else] Remove ARM halt + */ + /* Halt ARM & remove reset */ + si_core_reset(bus->sih, SICF_CPUHALT, SICF_CPUHALT); + } + } else { + if (!si_setcore(bus->sih, ARMCR4_CORE_ID, 0)) { + if (!(si_setcore(bus->sih, SOCRAM_CORE_ID, 0))) { + DHD_ERROR(("%s: Failed to find SOCRAM core!\n", __FUNCTION__)); + bcmerror = BCME_ERROR; + goto fail; + } + + if (!si_iscoreup(bus->sih)) { + DHD_ERROR(("%s: SOCRAM core is down after reset?\n", __FUNCTION__)); + bcmerror = BCME_ERROR; + goto fail; + } + + if ((bcmerror = dhdsdio_write_vars(bus))) { + DHD_ERROR(("%s: could not write vars to RAM\n", __FUNCTION__)); + goto fail; + } + + /* Enable remap before ARM reset but after vars. + * No backplane access in remap mode + */ + if (REMAP_ENAB(bus) && !si_socdevram_remap_isenb(bus->sih)) + dhdsdio_devram_remap(bus, TRUE); + + if (!si_setcore(bus->sih, PCMCIA_CORE_ID, 0) && + !si_setcore(bus->sih, SDIOD_CORE_ID, 0)) { + DHD_ERROR(("%s: Can't change back to SDIO core?\n", __FUNCTION__)); + bcmerror = BCME_ERROR; + goto fail; + } + W_SDREG(0xFFFFFFFF, &bus->regs->intstatus, retries); + + + if (!(si_setcore(bus->sih, ARM7S_CORE_ID, 0)) && + !(si_setcore(bus->sih, ARMCM3_CORE_ID, 0))) { + DHD_ERROR(("%s: Failed to find ARM core!\n", __FUNCTION__)); + bcmerror = BCME_ERROR; + goto fail; + } + } else { + /* cr4 has no socram, but tcm's */ + /* write vars */ + if ((bcmerror = dhdsdio_write_vars(bus))) { + DHD_ERROR(("%s: could not write vars to RAM\n", __FUNCTION__)); + goto fail; + } + + if (!si_setcore(bus->sih, PCMCIA_CORE_ID, 0) && + !si_setcore(bus->sih, SDIOD_CORE_ID, 0)) { + DHD_ERROR(("%s: Can't change back to SDIO core?\n", __FUNCTION__)); + bcmerror = BCME_ERROR; + goto fail; + } + W_SDREG(0xFFFFFFFF, &bus->regs->intstatus, retries); + + /* switch back to arm core again */ + if (!(si_setcore(bus->sih, ARMCR4_CORE_ID, 0))) { + DHD_ERROR(("%s: Failed to find ARM CR4 core!\n", __FUNCTION__)); + bcmerror = BCME_ERROR; + goto fail; + } + /* write address 0 with reset instruction */ + bcmerror = dhdsdio_membytes(bus, TRUE, 0, + (uint8 *)&bus->resetinstr, sizeof(bus->resetinstr)); + + /* now remove reset and halt and continue to run CR4 */ + } + + si_core_reset(bus->sih, 0, 0); + if (bcmsdh_regfail(bus->sdh)) { + DHD_ERROR(("%s: Failure trying to reset ARM core?\n", __FUNCTION__)); + bcmerror = BCME_SDIO_ERROR; + goto fail; + } + + /* Allow HT Clock now that the ARM is running. */ + bus->alp_only = FALSE; + + bus->dhd->busstate = DHD_BUS_LOAD; + } + +fail: + /* Always return to SDIOD core */ + if (!si_setcore(bus->sih, PCMCIA_CORE_ID, 0)) + si_setcore(bus->sih, SDIOD_CORE_ID, 0); + + return bcmerror; +} + +int +dhd_bus_iovar_op(dhd_pub_t *dhdp, const char *name, + void *params, int plen, void *arg, int len, bool set) +{ + dhd_bus_t *bus = dhdp->bus; + const bcm_iovar_t *vi = NULL; + int bcmerror = 0; + int val_size; + uint32 actionid; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + ASSERT(name); + ASSERT(len >= 0); + + /* Get MUST have return space */ + ASSERT(set || (arg && len)); + + /* Set does NOT take qualifiers */ + ASSERT(!set || (!params && !plen)); + + /* Look up var locally; if not found pass to host driver */ + if ((vi = bcm_iovar_lookup(dhdsdio_iovars, name)) == NULL) { + dhd_os_sdlock(bus->dhd); + + BUS_WAKE(bus); + + /* Turn on clock in case SD command needs backplane */ + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + + bcmerror = bcmsdh_iovar_op(bus->sdh, name, params, plen, arg, len, set); + + /* Check for bus configuration changes of interest */ + + /* If it was divisor change, read the new one */ + if (set && strcmp(name, "sd_divisor") == 0) { + if (bcmsdh_iovar_op(bus->sdh, "sd_divisor", NULL, 0, + &bus->sd_divisor, sizeof(int32), FALSE) != BCME_OK) { + bus->sd_divisor = -1; + DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, name)); + } else { + DHD_INFO(("%s: noted %s update, value now %d\n", + __FUNCTION__, name, bus->sd_divisor)); + } + } + /* If it was a mode change, read the new one */ + if (set && strcmp(name, "sd_mode") == 0) { + if (bcmsdh_iovar_op(bus->sdh, "sd_mode", NULL, 0, + &bus->sd_mode, sizeof(int32), FALSE) != BCME_OK) { + bus->sd_mode = -1; + DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, name)); + } else { + DHD_INFO(("%s: noted %s update, value now %d\n", + __FUNCTION__, name, bus->sd_mode)); + } + } + /* Similar check for blocksize change */ + if (set && strcmp(name, "sd_blocksize") == 0) { + int32 fnum = 2; + if (bcmsdh_iovar_op(bus->sdh, "sd_blocksize", &fnum, sizeof(int32), + &bus->blocksize, sizeof(int32), FALSE) != BCME_OK) { + bus->blocksize = 0; + DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, "sd_blocksize")); + } else { + DHD_INFO(("%s: noted %s update, value now %d\n", + __FUNCTION__, "sd_blocksize", bus->blocksize)); + } + } + bus->roundup = MIN(max_roundup, bus->blocksize); + + if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) { + bus->activity = FALSE; + dhdsdio_clkctl(bus, CLK_NONE, TRUE); + } + + dhd_os_sdunlock(bus->dhd); + goto exit; + } + + DHD_CTL(("%s: %s %s, len %d plen %d\n", __FUNCTION__, + name, (set ? "set" : "get"), len, plen)); + + /* set up 'params' pointer in case this is a set command so that + * the convenience int and bool code can be common to set and get + */ + if (params == NULL) { + params = arg; + plen = len; + } + + if (vi->type == IOVT_VOID) + val_size = 0; + else if (vi->type == IOVT_BUFFER) + val_size = len; + else + /* all other types are integer sized */ + val_size = sizeof(int); + + actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid); + bcmerror = dhdsdio_doiovar(bus, vi, actionid, name, params, plen, arg, len, val_size); + +exit: + return bcmerror; +} + +void +dhd_bus_stop(struct dhd_bus *bus, bool enforce_mutex) +{ + osl_t *osh; + uint32 local_hostintmask; + uint8 saveclk, dat; + uint retries; + int err; + if (!bus->dhd) + return; + + osh = bus->dhd->osh; + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + bcmsdh_waitlockfree(NULL); + + if (enforce_mutex) + dhd_os_sdlock(bus->dhd); + + if ((bus->dhd->busstate == DHD_BUS_DOWN) || bus->dhd->hang_was_sent) { + bus->dhd->busstate = DHD_BUS_DOWN; + bus->hostintmask = 0; + bcmsdh_intr_disable(bus->sdh); + } else { + BUS_WAKE(bus); + + if (KSO_ENAB(bus)) { + /* Mask the interrupt */ + dat = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_INTEN, NULL); + dat &= ~(INTR_CTL_FUNC1_EN | INTR_CTL_FUNC2_EN); + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_INTEN, dat, NULL); + } + + /* Change our idea of bus state */ + bus->dhd->busstate = DHD_BUS_DOWN; + + if (KSO_ENAB(bus)) { + + /* Enable clock for device interrupts */ + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + + /* Disable and clear interrupts at the chip level also */ + W_SDREG(0, &bus->regs->hostintmask, retries); + local_hostintmask = bus->hostintmask; + bus->hostintmask = 0; + + /* Force clocks on backplane to be sure F2 interrupt propagates */ + saveclk = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err); + if (!err) { + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, + (saveclk | SBSDIO_FORCE_HT), &err); + } + if (err) { + DHD_ERROR(("%s: Failed to force clock for F2: err %d\n", __FUNCTION__, err)); + } + + /* Turn off the bus (F2), free any pending packets */ + DHD_INTR(("%s: disable SDIO interrupts\n", __FUNCTION__)); + bcmsdh_intr_disable(bus->sdh); + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN, SDIO_FUNC_ENABLE_1, NULL); + + /* Clear any pending interrupts now that F2 is disabled */ + W_SDREG(local_hostintmask, &bus->regs->intstatus, retries); + } + + /* Turn off the backplane clock (only) */ + dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); + } + + /* Clear the data packet queues */ + pktq_flush(osh, &bus->txq, TRUE, NULL, 0); + + /* Clear any held glomming stuff */ + if (bus->glomd) + PKTFREE(osh, bus->glomd, FALSE); + + if (bus->glom) + PKTFREE(osh, bus->glom, FALSE); + + bus->glom = bus->glomd = NULL; + + /* Clear rx control and wake any waiters */ + bus->rxlen = 0; + dhd_os_ioctl_resp_wake(bus->dhd); + + /* Reset some F2 state stuff */ + bus->rxskip = FALSE; + bus->tx_seq = bus->rx_seq = 0; + + if (enforce_mutex) + dhd_os_sdunlock(bus->dhd); +} + +#ifdef BCMSDIOH_TXGLOM +void +dhd_txglom_enable(dhd_pub_t *dhdp, bool enable) +{ + dhd_bus_t *bus = dhdp->bus; + + char buf[256]; + uint32 rxglom; + int32 ret; + + if (enable) { + rxglom = 1; + memset(buf, 0, sizeof(buf)); + bcm_mkiovar("bus:rxglom", + (void *)&rxglom, + 4, buf, sizeof(buf)); + ret = dhd_wl_ioctl_cmd(dhdp, + WLC_SET_VAR, buf, + sizeof(buf), TRUE, 0); + if (!(ret < 0)) { + bus->glom_enable = TRUE; + } + } else { + bus->glom_enable = FALSE; + } +} +#endif /* BCMSDIOH_TXGLOM */ + +int +dhd_bus_init(dhd_pub_t *dhdp, bool enforce_mutex) +{ + dhd_bus_t *bus = dhdp->bus; + dhd_timeout_t tmo; + uint retries = 0; + uint8 ready, enable; + int err, ret = 0; + uint8 saveclk; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + ASSERT(bus->dhd); + if (!bus->dhd) + return 0; + + if (enforce_mutex) + dhd_os_sdlock(bus->dhd); + + /* Make sure backplane clock is on, needed to generate F2 interrupt */ + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + if (bus->clkstate != CLK_AVAIL) { + DHD_ERROR(("%s: clock state is wrong. state = %d\n", __FUNCTION__, bus->clkstate)); + ret = -1; + goto exit; + } + + + /* Force clocks on backplane to be sure F2 interrupt propagates */ + saveclk = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err); + if (!err) { + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, + (saveclk | SBSDIO_FORCE_HT), &err); + } + if (err) { + DHD_ERROR(("%s: Failed to force clock for F2: err %d\n", __FUNCTION__, err)); + ret = -1; + goto exit; + } + + /* Enable function 2 (frame transfers) */ + W_SDREG((SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT), + &bus->regs->tosbmailboxdata, retries); + enable = (SDIO_FUNC_ENABLE_1 | SDIO_FUNC_ENABLE_2); + + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN, enable, NULL); + + /* Give the dongle some time to do its thing and set IOR2 */ + dhd_timeout_start(&tmo, DHD_WAIT_F2RDY * 1000); + + ready = 0; + while (ready != enable && !dhd_timeout_expired(&tmo)) + ready = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IORDY, NULL); + + DHD_ERROR(("%s: enable 0x%02x, ready 0x%02x (waited %uus)\n", + __FUNCTION__, enable, ready, tmo.elapsed)); + + + /* If F2 successfully enabled, set core and enable interrupts */ + if (ready == enable) { + /* Make sure we're talking to the core. */ + if (!(bus->regs = si_setcore(bus->sih, PCMCIA_CORE_ID, 0))) + bus->regs = si_setcore(bus->sih, SDIOD_CORE_ID, 0); + ASSERT(bus->regs != NULL); + + /* Set up the interrupt mask and enable interrupts */ + bus->hostintmask = HOSTINTMASK; + /* corerev 4 could use the newer interrupt logic to detect the frames */ + if ((bus->sih->buscoretype == SDIOD_CORE_ID) && (bus->sdpcmrev == 4) && + (bus->rxint_mode != SDIO_DEVICE_HMB_RXINT)) { + bus->hostintmask &= ~I_HMB_FRAME_IND; + bus->hostintmask |= I_XMTDATA_AVAIL; + } + W_SDREG(bus->hostintmask, &bus->regs->hostintmask, retries); +#ifdef SDIO_CRC_ERROR_FIX + if (bus->blocksize < 512) { + mesbusyctrl = watermark = bus->blocksize / 4; + } +#endif /* SDIO_CRC_ERROR_FIX */ + + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_WATERMARK, (uint8)watermark, &err); +#ifdef SDIO_CRC_ERROR_FIX + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_MESBUSYCTRL, + (uint8)mesbusyctrl|0x80, &err); + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, + SBSDIO_DEVCTL_EN_F2_BLK_WATERMARK, NULL); +#endif /* SDIO_CRC_ERROR_FIX */ + + /* Set bus state according to enable result */ + dhdp->busstate = DHD_BUS_DATA; + + /* bcmsdh_intr_unmask(bus->sdh); */ + + bus->intdis = FALSE; + if (bus->intr) { + DHD_INTR(("%s: enable SDIO device interrupts\n", __FUNCTION__)); + bcmsdh_intr_enable(bus->sdh); + } else { + DHD_INTR(("%s: disable SDIO interrupts\n", __FUNCTION__)); + bcmsdh_intr_disable(bus->sdh); + } + + } + + + else { + /* Disable F2 again */ + enable = SDIO_FUNC_ENABLE_1; + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN, enable, NULL); + } + + if (dhdsdio_sr_cap(bus)) + dhdsdio_sr_init(bus); + else + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, + SBSDIO_FUNC1_CHIPCLKCSR, saveclk, &err); + + /* If we didn't come up, turn off backplane clock */ + if (dhdp->busstate != DHD_BUS_DATA) + dhdsdio_clkctl(bus, CLK_NONE, FALSE); + +exit: + if (enforce_mutex) + dhd_os_sdunlock(bus->dhd); + + return ret; +} + +static void +dhdsdio_rxfail(dhd_bus_t *bus, bool abort, bool rtx) +{ + bcmsdh_info_t *sdh = bus->sdh; + sdpcmd_regs_t *regs = bus->regs; + uint retries = 0; + uint16 lastrbc; + uint8 hi, lo; + int err; + + DHD_ERROR(("%s: %sterminate frame%s\n", __FUNCTION__, + (abort ? "abort command, " : ""), (rtx ? ", send NAK" : ""))); + + if (!KSO_ENAB(bus)) { + DHD_ERROR(("%s: Device asleep\n", __FUNCTION__)); + return; + } + + if (abort) { + bcmsdh_abort(sdh, SDIO_FUNC_2); + } + + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL, SFC_RF_TERM, &err); + bus->f1regdata++; + + /* Wait until the packet has been flushed (device/FIFO stable) */ + for (lastrbc = retries = 0xffff; retries > 0; retries--) { + hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_RFRAMEBCHI, NULL); + lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_RFRAMEBCLO, NULL); + bus->f1regdata += 2; + + if ((hi == 0) && (lo == 0)) + break; + + if ((hi > (lastrbc >> 8)) && (lo > (lastrbc & 0x00ff))) { + DHD_ERROR(("%s: count growing: last 0x%04x now 0x%04x\n", + __FUNCTION__, lastrbc, ((hi << 8) + lo))); + } + lastrbc = (hi << 8) + lo; + } + + if (!retries) { + DHD_ERROR(("%s: count never zeroed: last 0x%04x\n", __FUNCTION__, lastrbc)); + } else { + DHD_INFO(("%s: flush took %d iterations\n", __FUNCTION__, (0xffff - retries))); + } + + if (rtx) { + bus->rxrtx++; + W_SDREG(SMB_NAK, ®s->tosbmailbox, retries); + bus->f1regdata++; + if (retries <= retry_limit) { + bus->rxskip = TRUE; + } + } + + /* Clear partial in any case */ + bus->nextlen = 0; + + /* If we can't reach the device, signal failure */ + if (err || bcmsdh_regfail(sdh)) + bus->dhd->busstate = DHD_BUS_DOWN; +} + +static void +dhdsdio_read_control(dhd_bus_t *bus, uint8 *hdr, uint len, uint doff) +{ + bcmsdh_info_t *sdh = bus->sdh; + uint rdlen, pad; + + int sdret; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + /* Control data already received in aligned rxctl */ + if ((bus->bus == SPI_BUS) && (!bus->usebufpool)) + goto gotpkt; + + ASSERT(bus->rxbuf); + /* Set rxctl for frame (w/optional alignment) */ + bus->rxctl = bus->rxbuf; + if (dhd_alignctl) { + bus->rxctl += firstread; + if ((pad = ((uintptr)bus->rxctl % DHD_SDALIGN))) + bus->rxctl += (DHD_SDALIGN - pad); + bus->rxctl -= firstread; + } + ASSERT(bus->rxctl >= bus->rxbuf); + + /* Copy the already-read portion over */ + bcopy(hdr, bus->rxctl, firstread); + if (len <= firstread) + goto gotpkt; + + /* Copy the full data pkt in gSPI case and process ioctl. */ + if (bus->bus == SPI_BUS) { + bcopy(hdr, bus->rxctl, len); + goto gotpkt; + } + + /* Raise rdlen to next SDIO block to avoid tail command */ + rdlen = len - firstread; + if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) { + pad = bus->blocksize - (rdlen % bus->blocksize); + if ((pad <= bus->roundup) && (pad < bus->blocksize) && + ((len + pad) < bus->dhd->maxctl)) + rdlen += pad; + } else if (rdlen % DHD_SDALIGN) { + rdlen += DHD_SDALIGN - (rdlen % DHD_SDALIGN); + } + + /* Satisfy length-alignment requirements */ + if (forcealign && (rdlen & (ALIGNMENT - 1))) + rdlen = ROUNDUP(rdlen, ALIGNMENT); + + /* Drop if the read is too big or it exceeds our maximum */ + if ((rdlen + firstread) > bus->dhd->maxctl) { + DHD_ERROR(("%s: %d-byte control read exceeds %d-byte buffer\n", + __FUNCTION__, rdlen, bus->dhd->maxctl)); + bus->dhd->rx_errors++; + dhdsdio_rxfail(bus, FALSE, FALSE); + goto done; + } + + if ((len - doff) > bus->dhd->maxctl) { + DHD_ERROR(("%s: %d-byte ctl frame (%d-byte ctl data) exceeds %d-byte limit\n", + __FUNCTION__, len, (len - doff), bus->dhd->maxctl)); + bus->dhd->rx_errors++; bus->rx_toolong++; + dhdsdio_rxfail(bus, FALSE, FALSE); + goto done; + } + + + /* Read remainder of frame body into the rxctl buffer */ + sdret = dhd_bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, + (bus->rxctl + firstread), rdlen, NULL, NULL, NULL); + bus->f2rxdata++; + ASSERT(sdret != BCME_PENDING); + + /* Control frame failures need retransmission */ + if (sdret < 0) { + DHD_ERROR(("%s: read %d control bytes failed: %d\n", __FUNCTION__, rdlen, sdret)); + bus->rxc_errors++; /* dhd.rx_ctlerrs is higher level */ + dhdsdio_rxfail(bus, TRUE, TRUE); + goto done; + } + +gotpkt: + +#ifdef DHD_DEBUG + if (DHD_BYTES_ON() && DHD_CTL_ON()) { + prhex("RxCtrl", bus->rxctl, len); + } +#endif + + /* Point to valid data and indicate its length */ + bus->rxctl += doff; + bus->rxlen = len - doff; + +done: + /* Awake any waiters */ + dhd_os_ioctl_resp_wake(bus->dhd); +} + +static uint8 +dhdsdio_rxglom(dhd_bus_t *bus, uint8 rxseq) +{ + uint16 dlen, totlen; + uint8 *dptr, num = 0; + + uint16 sublen, check; + void *pfirst, *plast, *pnext; + void * list_tail[DHD_MAX_IFS] = { NULL }; + void * list_head[DHD_MAX_IFS] = { NULL }; + uint8 idx; + osl_t *osh = bus->dhd->osh; + + int errcode; + uint8 chan, seq, doff, sfdoff; + uint8 txmax; + uchar reorder_info_buf[WLHOST_REORDERDATA_TOTLEN]; + uint reorder_info_len; + + int ifidx = 0; + bool usechain = bus->use_rxchain; + + /* If packets, issue read(s) and send up packet chain */ + /* Return sequence numbers consumed? */ + + DHD_TRACE(("dhdsdio_rxglom: start: glomd %p glom %p\n", bus->glomd, bus->glom)); + + /* If there's a descriptor, generate the packet chain */ + if (bus->glomd) { + dhd_os_sdlock_rxq(bus->dhd); + + pfirst = plast = pnext = NULL; + dlen = (uint16)PKTLEN(osh, bus->glomd); + dptr = PKTDATA(osh, bus->glomd); + if (!dlen || (dlen & 1)) { + DHD_ERROR(("%s: bad glomd len (%d), ignore descriptor\n", + __FUNCTION__, dlen)); + dlen = 0; + } + + for (totlen = num = 0; dlen; num++) { + /* Get (and move past) next length */ + sublen = ltoh16_ua(dptr); + dlen -= sizeof(uint16); + dptr += sizeof(uint16); + if ((sublen < SDPCM_HDRLEN_RX) || + ((num == 0) && (sublen < (2 * SDPCM_HDRLEN_RX)))) { + DHD_ERROR(("%s: descriptor len %d bad: %d\n", + __FUNCTION__, num, sublen)); + pnext = NULL; + break; + } + if (sublen % DHD_SDALIGN) { + DHD_ERROR(("%s: sublen %d not a multiple of %d\n", + __FUNCTION__, sublen, DHD_SDALIGN)); + usechain = FALSE; + } + totlen += sublen; + + /* For last frame, adjust read len so total is a block multiple */ + if (!dlen) { + sublen += (ROUNDUP(totlen, bus->blocksize) - totlen); + totlen = ROUNDUP(totlen, bus->blocksize); + } + + /* Allocate/chain packet for next subframe */ + if ((pnext = PKTGET(osh, sublen + DHD_SDALIGN, FALSE)) == NULL) { + DHD_ERROR(("%s: PKTGET failed, num %d len %d\n", + __FUNCTION__, num, sublen)); + break; + } + ASSERT(!PKTLINK(pnext)); + if (!pfirst) { + ASSERT(!plast); + pfirst = plast = pnext; + } else { + ASSERT(plast); + PKTSETNEXT(osh, plast, pnext); + plast = pnext; + } + + /* Adhere to start alignment requirements */ + PKTALIGN(osh, pnext, sublen, DHD_SDALIGN); + } + + /* If all allocations succeeded, save packet chain in bus structure */ + if (pnext) { + DHD_GLOM(("%s: allocated %d-byte packet chain for %d subframes\n", + __FUNCTION__, totlen, num)); + if (DHD_GLOM_ON() && bus->nextlen) { + if (totlen != bus->nextlen) { + DHD_GLOM(("%s: glomdesc mismatch: nextlen %d glomdesc %d " + "rxseq %d\n", __FUNCTION__, bus->nextlen, + totlen, rxseq)); + } + } + bus->glom = pfirst; + pfirst = pnext = NULL; + } else { + if (pfirst) + PKTFREE(osh, pfirst, FALSE); + bus->glom = NULL; + num = 0; + } + + /* Done with descriptor packet */ + PKTFREE(osh, bus->glomd, FALSE); + bus->glomd = NULL; + bus->nextlen = 0; + + dhd_os_sdunlock_rxq(bus->dhd); + } + + /* Ok -- either we just generated a packet chain, or had one from before */ + if (bus->glom) { + if (DHD_GLOM_ON()) { + DHD_GLOM(("%s: attempt superframe read, packet chain:\n", __FUNCTION__)); + for (pnext = bus->glom; pnext; pnext = PKTNEXT(osh, pnext)) { + DHD_GLOM((" %p: %p len 0x%04x (%d)\n", + pnext, (uint8*)PKTDATA(osh, pnext), + PKTLEN(osh, pnext), PKTLEN(osh, pnext))); + } + } + + pfirst = bus->glom; + dlen = (uint16)pkttotlen(osh, pfirst); + + /* Do an SDIO read for the superframe. Configurable iovar to + * read directly into the chained packet, or allocate a large + * packet and and copy into the chain. + */ + if (usechain) { + errcode = dhd_bcmsdh_recv_buf(bus, + bcmsdh_cur_sbwad(bus->sdh), SDIO_FUNC_2, + F2SYNC, (uint8*)PKTDATA(osh, pfirst), + dlen, pfirst, NULL, NULL); + } else if (bus->dataptr) { + errcode = dhd_bcmsdh_recv_buf(bus, + bcmsdh_cur_sbwad(bus->sdh), SDIO_FUNC_2, + F2SYNC, bus->dataptr, + dlen, NULL, NULL, NULL); + sublen = (uint16)pktfrombuf(osh, pfirst, 0, dlen, bus->dataptr); + if (sublen != dlen) { + DHD_ERROR(("%s: FAILED TO COPY, dlen %d sublen %d\n", + __FUNCTION__, dlen, sublen)); + errcode = -1; + } + pnext = NULL; + } else { + DHD_ERROR(("COULDN'T ALLOC %d-BYTE GLOM, FORCE FAILURE\n", dlen)); + errcode = -1; + } + bus->f2rxdata++; + ASSERT(errcode != BCME_PENDING); + + /* On failure, kill the superframe, allow a couple retries */ + if (errcode < 0) { + DHD_ERROR(("%s: glom read of %d bytes failed: %d\n", + __FUNCTION__, dlen, errcode)); + bus->dhd->rx_errors++; + + if (bus->glomerr++ < 3) { + dhdsdio_rxfail(bus, TRUE, TRUE); + } else { + bus->glomerr = 0; + dhdsdio_rxfail(bus, TRUE, FALSE); + dhd_os_sdlock_rxq(bus->dhd); + PKTFREE(osh, bus->glom, FALSE); + dhd_os_sdunlock_rxq(bus->dhd); + bus->rxglomfail++; + bus->glom = NULL; + } + return 0; + } + +#ifdef DHD_DEBUG + if (DHD_GLOM_ON()) { + prhex("SUPERFRAME", PKTDATA(osh, pfirst), + MIN(PKTLEN(osh, pfirst), 48)); + } +#endif + + + /* Validate the superframe header */ + dptr = (uint8 *)PKTDATA(osh, pfirst); + sublen = ltoh16_ua(dptr); + check = ltoh16_ua(dptr + sizeof(uint16)); + + chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]); + seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]); + bus->nextlen = dptr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET]; + if ((bus->nextlen << 4) > MAX_RX_DATASZ) { + DHD_INFO(("%s: got frame w/nextlen too large (%d) seq %d\n", + __FUNCTION__, bus->nextlen, seq)); + bus->nextlen = 0; + } + doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]); + txmax = SDPCM_WINDOW_VALUE(&dptr[SDPCM_FRAMETAG_LEN]); + + errcode = 0; + if ((uint16)~(sublen^check)) { + DHD_ERROR(("%s (superframe): HW hdr error: len/check 0x%04x/0x%04x\n", + __FUNCTION__, sublen, check)); + errcode = -1; + } else if (ROUNDUP(sublen, bus->blocksize) != dlen) { + DHD_ERROR(("%s (superframe): len 0x%04x, rounded 0x%04x, expect 0x%04x\n", + __FUNCTION__, sublen, ROUNDUP(sublen, bus->blocksize), dlen)); + errcode = -1; + } else if (SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]) != SDPCM_GLOM_CHANNEL) { + DHD_ERROR(("%s (superframe): bad channel %d\n", __FUNCTION__, + SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]))); + errcode = -1; + } else if (SDPCM_GLOMDESC(&dptr[SDPCM_FRAMETAG_LEN])) { + DHD_ERROR(("%s (superframe): got second descriptor?\n", __FUNCTION__)); + errcode = -1; + } else if ((doff < SDPCM_HDRLEN_RX) || + (doff > (PKTLEN(osh, pfirst) - SDPCM_HDRLEN_RX))) { + DHD_ERROR(("%s (superframe): Bad data offset %d: HW %d pkt %d min %d\n", + __FUNCTION__, doff, sublen, PKTLEN(osh, pfirst), + SDPCM_HDRLEN_RX)); + errcode = -1; + } + + /* Check sequence number of superframe SW header */ + if (rxseq != seq) { + DHD_INFO(("%s: (superframe) rx_seq %d, expected %d\n", + __FUNCTION__, seq, rxseq)); + bus->rx_badseq++; + rxseq = seq; + } + + /* Check window for sanity */ + if ((uint8)(txmax - bus->tx_seq) > 0x40) { + DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n", + __FUNCTION__, txmax, bus->tx_seq)); + txmax = bus->tx_max; + } + bus->tx_max = txmax; + + /* Remove superframe header, remember offset */ + PKTPULL(osh, pfirst, doff); + sfdoff = doff; + + /* Validate all the subframe headers */ + for (num = 0, pnext = pfirst; pnext && !errcode; + num++, pnext = PKTNEXT(osh, pnext)) { + dptr = (uint8 *)PKTDATA(osh, pnext); + dlen = (uint16)PKTLEN(osh, pnext); + sublen = ltoh16_ua(dptr); + check = ltoh16_ua(dptr + sizeof(uint16)); + chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]); + doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]); +#ifdef DHD_DEBUG + if (DHD_GLOM_ON()) { + prhex("subframe", dptr, 32); + } +#endif + + if ((uint16)~(sublen^check)) { + DHD_ERROR(("%s (subframe %d): HW hdr error: " + "len/check 0x%04x/0x%04x\n", + __FUNCTION__, num, sublen, check)); + errcode = -1; + } else if ((sublen > dlen) || (sublen < SDPCM_HDRLEN_RX)) { + DHD_ERROR(("%s (subframe %d): length mismatch: " + "len 0x%04x, expect 0x%04x\n", + __FUNCTION__, num, sublen, dlen)); + errcode = -1; + } else if ((chan != SDPCM_DATA_CHANNEL) && + (chan != SDPCM_EVENT_CHANNEL)) { + DHD_ERROR(("%s (subframe %d): bad channel %d\n", + __FUNCTION__, num, chan)); + errcode = -1; + } else if ((doff < SDPCM_HDRLEN_RX) || (doff > sublen)) { + DHD_ERROR(("%s (subframe %d): Bad data offset %d: HW %d min %d\n", + __FUNCTION__, num, doff, sublen, SDPCM_HDRLEN_RX)); + errcode = -1; + } + } + + if (errcode) { + /* Terminate frame on error, request a couple retries */ + if (bus->glomerr++ < 3) { + /* Restore superframe header space */ + PKTPUSH(osh, pfirst, sfdoff); + dhdsdio_rxfail(bus, TRUE, TRUE); + } else { + bus->glomerr = 0; + dhdsdio_rxfail(bus, TRUE, FALSE); + dhd_os_sdlock_rxq(bus->dhd); + PKTFREE(osh, bus->glom, FALSE); + dhd_os_sdunlock_rxq(bus->dhd); + bus->rxglomfail++; + bus->glom = NULL; + } + bus->nextlen = 0; + return 0; + } + + /* Basic SD framing looks ok - process each packet (header) */ + bus->glom = NULL; + plast = NULL; + + dhd_os_sdlock_rxq(bus->dhd); + for (num = 0; pfirst; rxseq++, pfirst = pnext) { + pnext = PKTNEXT(osh, pfirst); + PKTSETNEXT(osh, pfirst, NULL); + + dptr = (uint8 *)PKTDATA(osh, pfirst); + sublen = ltoh16_ua(dptr); + chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]); + seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]); + doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]); + + DHD_GLOM(("%s: Get subframe %d, %p(%p/%d), sublen %d chan %d seq %d\n", + __FUNCTION__, num, pfirst, PKTDATA(osh, pfirst), + PKTLEN(osh, pfirst), sublen, chan, seq)); + + ASSERT((chan == SDPCM_DATA_CHANNEL) || (chan == SDPCM_EVENT_CHANNEL)); + + if (rxseq != seq) { + DHD_GLOM(("%s: rx_seq %d, expected %d\n", + __FUNCTION__, seq, rxseq)); + bus->rx_badseq++; + rxseq = seq; + } + +#ifdef DHD_DEBUG + if (DHD_BYTES_ON() && DHD_DATA_ON()) { + prhex("Rx Subframe Data", dptr, dlen); + } +#endif + + PKTSETLEN(osh, pfirst, sublen); + PKTPULL(osh, pfirst, doff); + + reorder_info_len = sizeof(reorder_info_buf); + + if (PKTLEN(osh, pfirst) == 0) { + PKTFREE(bus->dhd->osh, pfirst, FALSE); + continue; + } else if (dhd_prot_hdrpull(bus->dhd, &ifidx, pfirst, reorder_info_buf, + &reorder_info_len) != 0) { + DHD_ERROR(("%s: rx protocol error\n", __FUNCTION__)); + bus->dhd->rx_errors++; + PKTFREE(osh, pfirst, FALSE); + continue; + } + if (reorder_info_len) { + uint32 free_buf_count; + void *ppfirst; + + ppfirst = pfirst; + /* Reordering info from the firmware */ + dhd_process_pkt_reorder_info(bus->dhd, reorder_info_buf, + reorder_info_len, &ppfirst, &free_buf_count); + + if (free_buf_count == 0) { + continue; + } + else { + void *temp; + + /* go to the end of the chain and attach the pnext there */ + temp = ppfirst; + while (PKTNEXT(osh, temp) != NULL) { + temp = PKTNEXT(osh, temp); + } + pfirst = temp; + if (list_tail[ifidx] == NULL) { + list_head[ifidx] = ppfirst; + list_tail[ifidx] = pfirst; + } + else { + PKTSETNEXT(osh, list_tail[ifidx], ppfirst); + list_tail[ifidx] = pfirst; + } + } + + num += (uint8)free_buf_count; + } + else { + /* this packet will go up, link back into chain and count it */ + + if (list_tail[ifidx] == NULL) { + list_head[ifidx] = list_tail[ifidx] = pfirst; + } + else { + PKTSETNEXT(osh, list_tail[ifidx], pfirst); + list_tail[ifidx] = pfirst; + } + num++; + } +#ifdef DHD_DEBUG + if (DHD_GLOM_ON()) { + DHD_GLOM(("%s subframe %d to stack, %p(%p/%d) nxt/lnk %p/%p\n", + __FUNCTION__, num, pfirst, + PKTDATA(osh, pfirst), PKTLEN(osh, pfirst), + PKTNEXT(osh, pfirst), PKTLINK(pfirst))); + prhex("", (uint8 *)PKTDATA(osh, pfirst), + MIN(PKTLEN(osh, pfirst), 32)); + } +#endif /* DHD_DEBUG */ + } + dhd_os_sdunlock_rxq(bus->dhd); + + for (idx = 0; idx < DHD_MAX_IFS; idx++) { + if (list_head[idx]) { + void *temp; + uint8 cnt = 0; + temp = list_head[idx]; + do { + temp = PKTNEXT(osh, temp); + cnt++; + } while (temp); + if (cnt) { + dhd_os_sdunlock(bus->dhd); + dhd_rx_frame(bus->dhd, idx, list_head[idx], cnt, 0); + dhd_os_sdlock(bus->dhd); + } + } + } + bus->rxglomframes++; + bus->rxglompkts += num; + } + return num; +} + + +/* Return TRUE if there may be more frames to read */ +static uint +dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished) +{ + osl_t *osh = bus->dhd->osh; + bcmsdh_info_t *sdh = bus->sdh; + + uint16 len, check; /* Extracted hardware header fields */ + uint8 chan, seq, doff; /* Extracted software header fields */ + uint8 fcbits; /* Extracted fcbits from software header */ + uint8 delta; + + void *pkt; /* Packet for event or data frames */ + uint16 pad; /* Number of pad bytes to read */ + uint16 rdlen; /* Total number of bytes to read */ + uint8 rxseq; /* Next sequence number to expect */ + uint rxleft = 0; /* Remaining number of frames allowed */ + int sdret; /* Return code from bcmsdh calls */ + uint8 txmax; /* Maximum tx sequence offered */ + bool len_consistent; /* Result of comparing readahead len and len from hw-hdr */ + uint8 *rxbuf; + int ifidx = 0; + uint rxcount = 0; /* Total frames read */ + uchar reorder_info_buf[WLHOST_REORDERDATA_TOTLEN]; + uint reorder_info_len; + uint pkt_count; + +#if defined(DHD_DEBUG) || defined(SDTEST) + bool sdtest = FALSE; /* To limit message spew from test mode */ +#endif + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + bus->readframes = TRUE; + + if (!KSO_ENAB(bus)) { + DHD_ERROR(("%s: KSO off\n", __FUNCTION__)); + bus->readframes = FALSE; + return 0; + } + + ASSERT(maxframes); + +#ifdef SDTEST + /* Allow pktgen to override maxframes */ + if (bus->pktgen_count && (bus->pktgen_mode == DHD_PKTGEN_RECV)) { + maxframes = bus->pktgen_count; + sdtest = TRUE; + } +#endif + + /* Not finished unless we encounter no more frames indication */ + *finished = FALSE; + + + for (rxseq = bus->rx_seq, rxleft = maxframes; + !bus->rxskip && rxleft && bus->dhd->busstate != DHD_BUS_DOWN; + rxseq++, rxleft--) { + +#ifdef DHDTHREAD + /* tx more to improve rx performance */ + if ((bus->clkstate == CLK_AVAIL) && !bus->fcstate && + pktq_mlen(&bus->txq, ~bus->flowcontrol) && DATAOK(bus)) { + dhdsdio_sendfromq(bus, dhd_txbound); + } +#endif /* DHDTHREAD */ + + /* Handle glomming separately */ + if (bus->glom || bus->glomd) { + uint8 cnt; + DHD_GLOM(("%s: calling rxglom: glomd %p, glom %p\n", + __FUNCTION__, bus->glomd, bus->glom)); + cnt = dhdsdio_rxglom(bus, rxseq); + DHD_GLOM(("%s: rxglom returned %d\n", __FUNCTION__, cnt)); + rxseq += cnt - 1; + rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1; + continue; + } + + /* Try doing single read if we can */ + if (dhd_readahead && bus->nextlen) { + uint16 nextlen = bus->nextlen; + bus->nextlen = 0; + + if (bus->bus == SPI_BUS) { + rdlen = len = nextlen; + } + else { + rdlen = len = nextlen << 4; + + /* Pad read to blocksize for efficiency */ + if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) { + pad = bus->blocksize - (rdlen % bus->blocksize); + if ((pad <= bus->roundup) && (pad < bus->blocksize) && + ((rdlen + pad + firstread) < MAX_RX_DATASZ)) + rdlen += pad; + } else if (rdlen % DHD_SDALIGN) { + rdlen += DHD_SDALIGN - (rdlen % DHD_SDALIGN); + } + } + + /* We use bus->rxctl buffer in WinXP for initial control pkt receives. + * Later we use buffer-poll for data as well as control packets. + * This is required because dhd receives full frame in gSPI unlike SDIO. + * After the frame is received we have to distinguish whether it is data + * or non-data frame. + */ + /* Allocate a packet buffer */ + dhd_os_sdlock_rxq(bus->dhd); + if (!(pkt = PKTGET(osh, rdlen + DHD_SDALIGN, FALSE))) { + if (bus->bus == SPI_BUS) { + bus->usebufpool = FALSE; + bus->rxctl = bus->rxbuf; + if (dhd_alignctl) { + bus->rxctl += firstread; + if ((pad = ((uintptr)bus->rxctl % DHD_SDALIGN))) + bus->rxctl += (DHD_SDALIGN - pad); + bus->rxctl -= firstread; + } + ASSERT(bus->rxctl >= bus->rxbuf); + rxbuf = bus->rxctl; + /* Read the entire frame */ + sdret = dhd_bcmsdh_recv_buf(bus, + bcmsdh_cur_sbwad(sdh), + SDIO_FUNC_2, + F2SYNC, rxbuf, rdlen, + NULL, NULL, NULL); + bus->f2rxdata++; + ASSERT(sdret != BCME_PENDING); + + + /* Control frame failures need retransmission */ + if (sdret < 0) { + DHD_ERROR(("%s: read %d control bytes failed: %d\n", + __FUNCTION__, rdlen, sdret)); + /* dhd.rx_ctlerrs is higher level */ + bus->rxc_errors++; + dhd_os_sdunlock_rxq(bus->dhd); + dhdsdio_rxfail(bus, TRUE, + (bus->bus == SPI_BUS) ? FALSE : TRUE); + continue; + } + } else { + /* Give up on data, request rtx of events */ + DHD_ERROR(("%s (nextlen): PKTGET failed: len %d rdlen %d " + "expected rxseq %d\n", + __FUNCTION__, len, rdlen, rxseq)); + /* Just go try again w/normal header read */ + dhd_os_sdunlock_rxq(bus->dhd); + continue; + } + } else { + if (bus->bus == SPI_BUS) + bus->usebufpool = TRUE; + + ASSERT(!PKTLINK(pkt)); + PKTALIGN(osh, pkt, rdlen, DHD_SDALIGN); + rxbuf = (uint8 *)PKTDATA(osh, pkt); + /* Read the entire frame */ + sdret = dhd_bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh), + SDIO_FUNC_2, + F2SYNC, rxbuf, rdlen, + pkt, NULL, NULL); + bus->f2rxdata++; + ASSERT(sdret != BCME_PENDING); + + if (sdret < 0) { + DHD_ERROR(("%s (nextlen): read %d bytes failed: %d\n", + __FUNCTION__, rdlen, sdret)); + PKTFREE(bus->dhd->osh, pkt, FALSE); + bus->dhd->rx_errors++; + dhd_os_sdunlock_rxq(bus->dhd); + /* Force retry w/normal header read. Don't attempt NAK for + * gSPI + */ + dhdsdio_rxfail(bus, TRUE, + (bus->bus == SPI_BUS) ? FALSE : TRUE); + continue; + } + } + dhd_os_sdunlock_rxq(bus->dhd); + + /* Now check the header */ + bcopy(rxbuf, bus->rxhdr, SDPCM_HDRLEN_RX); + + /* Extract hardware header fields */ + len = ltoh16_ua(bus->rxhdr); + check = ltoh16_ua(bus->rxhdr + sizeof(uint16)); + + /* All zeros means readahead info was bad */ + if (!(len|check)) { + DHD_INFO(("%s (nextlen): read zeros in HW header???\n", + __FUNCTION__)); + dhd_os_sdlock_rxq(bus->dhd); + PKTFREE2(); + dhd_os_sdunlock_rxq(bus->dhd); + GSPI_PR55150_BAILOUT; + continue; + } + + /* Validate check bytes */ + if ((uint16)~(len^check)) { + DHD_ERROR(("%s (nextlen): HW hdr error: nextlen/len/check" + " 0x%04x/0x%04x/0x%04x\n", __FUNCTION__, nextlen, + len, check)); + dhd_os_sdlock_rxq(bus->dhd); + PKTFREE2(); + dhd_os_sdunlock_rxq(bus->dhd); + bus->rx_badhdr++; + dhdsdio_rxfail(bus, FALSE, FALSE); + GSPI_PR55150_BAILOUT; + continue; + } + + /* Validate frame length */ + if (len < SDPCM_HDRLEN_RX) { + DHD_ERROR(("%s (nextlen): HW hdr length invalid: %d\n", + __FUNCTION__, len)); + dhd_os_sdlock_rxq(bus->dhd); + PKTFREE2(); + dhd_os_sdunlock_rxq(bus->dhd); + GSPI_PR55150_BAILOUT; + continue; + } + + /* Check for consistency with readahead info */ + len_consistent = (nextlen != (ROUNDUP(len, 16) >> 4)); + if (len_consistent) { + /* Mismatch, force retry w/normal header (may be >4K) */ + DHD_ERROR(("%s (nextlen): mismatch, nextlen %d len %d rnd %d; " + "expected rxseq %d\n", + __FUNCTION__, nextlen, len, ROUNDUP(len, 16), rxseq)); + dhd_os_sdlock_rxq(bus->dhd); + PKTFREE2(); + dhd_os_sdunlock_rxq(bus->dhd); + dhdsdio_rxfail(bus, TRUE, (bus->bus == SPI_BUS) ? FALSE : TRUE); + GSPI_PR55150_BAILOUT; + continue; + } + + + /* Extract software header fields */ + chan = SDPCM_PACKET_CHANNEL(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); + seq = SDPCM_PACKET_SEQUENCE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); + doff = SDPCM_DOFFSET_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); + txmax = SDPCM_WINDOW_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); + + bus->nextlen = + bus->rxhdr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET]; + if ((bus->nextlen << 4) > MAX_RX_DATASZ) { + DHD_INFO(("%s (nextlen): got frame w/nextlen too large" + " (%d), seq %d\n", __FUNCTION__, bus->nextlen, + seq)); + bus->nextlen = 0; + } + + bus->dhd->rx_readahead_cnt ++; + /* Handle Flow Control */ + fcbits = SDPCM_FCMASK_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); + + delta = 0; + if (~bus->flowcontrol & fcbits) { + bus->fc_xoff++; + delta = 1; + } + if (bus->flowcontrol & ~fcbits) { + bus->fc_xon++; + delta = 1; + } + + if (delta) { + bus->fc_rcvd++; + bus->flowcontrol = fcbits; + } + + /* Check and update sequence number */ + if (rxseq != seq) { + DHD_INFO(("%s (nextlen): rx_seq %d, expected %d\n", + __FUNCTION__, seq, rxseq)); + bus->rx_badseq++; + rxseq = seq; + } + + /* Check window for sanity */ + if ((uint8)(txmax - bus->tx_seq) > 0x40) { + DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n", + __FUNCTION__, txmax, bus->tx_seq)); + txmax = bus->tx_max; + } + bus->tx_max = txmax; + +#ifdef DHD_DEBUG + if (DHD_BYTES_ON() && DHD_DATA_ON()) { + prhex("Rx Data", rxbuf, len); + } else if (DHD_HDRS_ON()) { + prhex("RxHdr", bus->rxhdr, SDPCM_HDRLEN_RX); + } +#endif + + if (chan == SDPCM_CONTROL_CHANNEL) { + if (bus->bus == SPI_BUS) { + dhdsdio_read_control(bus, rxbuf, len, doff); + if (bus->usebufpool) { + dhd_os_sdlock_rxq(bus->dhd); + PKTFREE(bus->dhd->osh, pkt, FALSE); + dhd_os_sdunlock_rxq(bus->dhd); + } + continue; + } else { + DHD_ERROR(("%s (nextlen): readahead on control" + " packet %d?\n", __FUNCTION__, seq)); + /* Force retry w/normal header read */ + bus->nextlen = 0; + dhdsdio_rxfail(bus, FALSE, TRUE); + dhd_os_sdlock_rxq(bus->dhd); + PKTFREE2(); + dhd_os_sdunlock_rxq(bus->dhd); + continue; + } + } + + if ((bus->bus == SPI_BUS) && !bus->usebufpool) { + DHD_ERROR(("Received %d bytes on %d channel. Running out of " + "rx pktbuf's or not yet malloced.\n", len, chan)); + continue; + } + + /* Validate data offset */ + if ((doff < SDPCM_HDRLEN_RX) || (doff > len)) { + DHD_ERROR(("%s (nextlen): bad data offset %d: HW len %d min %d\n", + __FUNCTION__, doff, len, SDPCM_HDRLEN_RX)); + dhd_os_sdlock_rxq(bus->dhd); + PKTFREE2(); + dhd_os_sdunlock_rxq(bus->dhd); + ASSERT(0); + dhdsdio_rxfail(bus, FALSE, FALSE); + continue; + } + + /* All done with this one -- now deliver the packet */ + goto deliver; + } + /* gSPI frames should not be handled in fractions */ + if (bus->bus == SPI_BUS) { + break; + } + + /* Read frame header (hardware and software) */ + sdret = dhd_bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, + bus->rxhdr, firstread, NULL, NULL, NULL); + bus->f2rxhdrs++; + ASSERT(sdret != BCME_PENDING); + + if (sdret < 0) { + DHD_ERROR(("%s: RXHEADER FAILED: %d\n", __FUNCTION__, sdret)); + bus->rx_hdrfail++; + dhdsdio_rxfail(bus, TRUE, TRUE); + continue; + } + +#ifdef DHD_DEBUG + if (DHD_BYTES_ON() || DHD_HDRS_ON()) { + prhex("RxHdr", bus->rxhdr, SDPCM_HDRLEN_RX); + } +#endif + + /* Extract hardware header fields */ + len = ltoh16_ua(bus->rxhdr); + check = ltoh16_ua(bus->rxhdr + sizeof(uint16)); + + /* All zeros means no more frames */ + if (!(len|check)) { + *finished = TRUE; + break; + } + + /* Validate check bytes */ + if ((uint16)~(len^check)) { + DHD_ERROR(("%s: HW hdr error: len/check 0x%04x/0x%04x\n", + __FUNCTION__, len, check)); + bus->rx_badhdr++; + dhdsdio_rxfail(bus, FALSE, FALSE); + continue; + } + + /* Validate frame length */ + if (len < SDPCM_HDRLEN_RX) { + DHD_ERROR(("%s: HW hdr length invalid: %d\n", __FUNCTION__, len)); + continue; + } + + /* Extract software header fields */ + chan = SDPCM_PACKET_CHANNEL(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); + seq = SDPCM_PACKET_SEQUENCE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); + doff = SDPCM_DOFFSET_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); + txmax = SDPCM_WINDOW_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); + + /* Validate data offset */ + if ((doff < SDPCM_HDRLEN_RX) || (doff > len)) { + DHD_ERROR(("%s: Bad data offset %d: HW len %d, min %d seq %d\n", + __FUNCTION__, doff, len, SDPCM_HDRLEN_RX, seq)); + bus->rx_badhdr++; + ASSERT(0); + dhdsdio_rxfail(bus, FALSE, FALSE); + continue; + } + + /* Save the readahead length if there is one */ + bus->nextlen = bus->rxhdr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET]; + if ((bus->nextlen << 4) > MAX_RX_DATASZ) { + DHD_INFO(("%s (nextlen): got frame w/nextlen too large (%d), seq %d\n", + __FUNCTION__, bus->nextlen, seq)); + bus->nextlen = 0; + } + + /* Handle Flow Control */ + fcbits = SDPCM_FCMASK_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); + + delta = 0; + if (~bus->flowcontrol & fcbits) { + bus->fc_xoff++; + delta = 1; + } + if (bus->flowcontrol & ~fcbits) { + bus->fc_xon++; + delta = 1; + } + + if (delta) { + bus->fc_rcvd++; + bus->flowcontrol = fcbits; + } + + /* Check and update sequence number */ + if (rxseq != seq) { + DHD_INFO(("%s: rx_seq %d, expected %d\n", __FUNCTION__, seq, rxseq)); + bus->rx_badseq++; + rxseq = seq; + } + + /* Check window for sanity */ + if ((uint8)(txmax - bus->tx_seq) > 0x40) { + DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n", + __FUNCTION__, txmax, bus->tx_seq)); + txmax = bus->tx_max; + } + bus->tx_max = txmax; + + /* Call a separate function for control frames */ + if (chan == SDPCM_CONTROL_CHANNEL) { + dhdsdio_read_control(bus, bus->rxhdr, len, doff); + continue; + } + + ASSERT((chan == SDPCM_DATA_CHANNEL) || (chan == SDPCM_EVENT_CHANNEL) || + (chan == SDPCM_TEST_CHANNEL) || (chan == SDPCM_GLOM_CHANNEL)); + + /* Length to read */ + rdlen = (len > firstread) ? (len - firstread) : 0; + + /* May pad read to blocksize for efficiency */ + if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) { + pad = bus->blocksize - (rdlen % bus->blocksize); + if ((pad <= bus->roundup) && (pad < bus->blocksize) && + ((rdlen + pad + firstread) < MAX_RX_DATASZ)) + rdlen += pad; + } else if (rdlen % DHD_SDALIGN) { + rdlen += DHD_SDALIGN - (rdlen % DHD_SDALIGN); + } + + /* Satisfy length-alignment requirements */ + if (forcealign && (rdlen & (ALIGNMENT - 1))) + rdlen = ROUNDUP(rdlen, ALIGNMENT); + + if ((rdlen + firstread) > MAX_RX_DATASZ) { + /* Too long -- skip this frame */ + DHD_ERROR(("%s: too long: len %d rdlen %d\n", __FUNCTION__, len, rdlen)); + bus->dhd->rx_errors++; bus->rx_toolong++; + dhdsdio_rxfail(bus, FALSE, FALSE); + continue; + } + + dhd_os_sdlock_rxq(bus->dhd); + if (!(pkt = PKTGET(osh, (rdlen + firstread + DHD_SDALIGN), FALSE))) { + /* Give up on data, request rtx of events */ + DHD_ERROR(("%s: PKTGET failed: rdlen %d chan %d\n", + __FUNCTION__, rdlen, chan)); + bus->dhd->rx_dropped++; + dhd_os_sdunlock_rxq(bus->dhd); + dhdsdio_rxfail(bus, FALSE, RETRYCHAN(chan)); + continue; + } + dhd_os_sdunlock_rxq(bus->dhd); + + ASSERT(!PKTLINK(pkt)); + + /* Leave room for what we already read, and align remainder */ + ASSERT(firstread < (PKTLEN(osh, pkt))); + PKTPULL(osh, pkt, firstread); + PKTALIGN(osh, pkt, rdlen, DHD_SDALIGN); + + /* Read the remaining frame data */ + sdret = dhd_bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, + ((uint8 *)PKTDATA(osh, pkt)), rdlen, pkt, NULL, NULL); + bus->f2rxdata++; + ASSERT(sdret != BCME_PENDING); + + if (sdret < 0) { + DHD_ERROR(("%s: read %d %s bytes failed: %d\n", __FUNCTION__, rdlen, + ((chan == SDPCM_EVENT_CHANNEL) ? "event" : + ((chan == SDPCM_DATA_CHANNEL) ? "data" : "test")), sdret)); + dhd_os_sdlock_rxq(bus->dhd); + PKTFREE(bus->dhd->osh, pkt, FALSE); + dhd_os_sdunlock_rxq(bus->dhd); + bus->dhd->rx_errors++; + dhdsdio_rxfail(bus, TRUE, RETRYCHAN(chan)); + continue; + } + + /* Copy the already-read portion */ + PKTPUSH(osh, pkt, firstread); + bcopy(bus->rxhdr, PKTDATA(osh, pkt), firstread); + +#ifdef DHD_DEBUG + if (DHD_BYTES_ON() && DHD_DATA_ON()) { + prhex("Rx Data", PKTDATA(osh, pkt), len); + } +#endif + +deliver: + /* Save superframe descriptor and allocate packet frame */ + if (chan == SDPCM_GLOM_CHANNEL) { + if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_FRAMETAG_LEN])) { + DHD_GLOM(("%s: got glom descriptor, %d bytes:\n", + __FUNCTION__, len)); +#ifdef DHD_DEBUG + if (DHD_GLOM_ON()) { + prhex("Glom Data", PKTDATA(osh, pkt), len); + } +#endif + PKTSETLEN(osh, pkt, len); + ASSERT(doff == SDPCM_HDRLEN_RX); + PKTPULL(osh, pkt, SDPCM_HDRLEN_RX); + bus->glomd = pkt; + } else { + DHD_ERROR(("%s: glom superframe w/o descriptor!\n", __FUNCTION__)); + dhdsdio_rxfail(bus, FALSE, FALSE); + } + continue; + } + + /* Fill in packet len and prio, deliver upward */ + PKTSETLEN(osh, pkt, len); + PKTPULL(osh, pkt, doff); + +#ifdef SDTEST + /* Test channel packets are processed separately */ + if (chan == SDPCM_TEST_CHANNEL) { + dhdsdio_testrcv(bus, pkt, seq); + continue; + } +#endif /* SDTEST */ + + if (PKTLEN(osh, pkt) == 0) { + dhd_os_sdlock_rxq(bus->dhd); + PKTFREE(bus->dhd->osh, pkt, FALSE); + dhd_os_sdunlock_rxq(bus->dhd); + continue; + } else if (dhd_prot_hdrpull(bus->dhd, &ifidx, pkt, reorder_info_buf, + &reorder_info_len) != 0) { + DHD_ERROR(("%s: rx protocol error\n", __FUNCTION__)); + dhd_os_sdlock_rxq(bus->dhd); + PKTFREE(bus->dhd->osh, pkt, FALSE); + dhd_os_sdunlock_rxq(bus->dhd); + bus->dhd->rx_errors++; + continue; + } + if (reorder_info_len) { + /* Reordering info from the firmware */ + dhd_process_pkt_reorder_info(bus->dhd, reorder_info_buf, reorder_info_len, + &pkt, &pkt_count); + if (pkt_count == 0) + continue; + } + else + pkt_count = 1; + + + /* Unlock during rx call */ + dhd_os_sdunlock(bus->dhd); + dhd_rx_frame(bus->dhd, ifidx, pkt, pkt_count, chan); + dhd_os_sdlock(bus->dhd); + } + rxcount = maxframes - rxleft; +#ifdef DHD_DEBUG + /* Message if we hit the limit */ + if (!rxleft && !sdtest) + DHD_DATA(("%s: hit rx limit of %d frames\n", __FUNCTION__, maxframes)); + else +#endif /* DHD_DEBUG */ + DHD_DATA(("%s: processed %d frames\n", __FUNCTION__, rxcount)); + /* Back off rxseq if awaiting rtx, update rx_seq */ + if (bus->rxskip) + rxseq--; + bus->rx_seq = rxseq; + + if (bus->reqbussleep) + { + dhdsdio_bussleep(bus, TRUE); + bus->reqbussleep = FALSE; + } + bus->readframes = FALSE; + + return rxcount; +} + +static uint32 +dhdsdio_hostmail(dhd_bus_t *bus) +{ + sdpcmd_regs_t *regs = bus->regs; + uint32 intstatus = 0; + uint32 hmb_data; + uint8 fcbits; + uint retries = 0; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + /* Read mailbox data and ack that we did so */ + R_SDREG(hmb_data, ®s->tohostmailboxdata, retries); + if (retries <= retry_limit) + W_SDREG(SMB_INT_ACK, ®s->tosbmailbox, retries); + bus->f1regdata += 2; + + /* Dongle recomposed rx frames, accept them again */ + if (hmb_data & HMB_DATA_NAKHANDLED) { + DHD_INFO(("Dongle reports NAK handled, expect rtx of %d\n", bus->rx_seq)); + if (!bus->rxskip) { + DHD_ERROR(("%s: unexpected NAKHANDLED!\n", __FUNCTION__)); + } + bus->rxskip = FALSE; + intstatus |= FRAME_AVAIL_MASK(bus); + } + + /* + * DEVREADY does not occur with gSPI. + */ + if (hmb_data & (HMB_DATA_DEVREADY | HMB_DATA_FWREADY)) { + bus->sdpcm_ver = (hmb_data & HMB_DATA_VERSION_MASK) >> HMB_DATA_VERSION_SHIFT; + if (bus->sdpcm_ver != SDPCM_PROT_VERSION) + DHD_ERROR(("Version mismatch, dongle reports %d, expecting %d\n", + bus->sdpcm_ver, SDPCM_PROT_VERSION)); + else + DHD_INFO(("Dongle ready, protocol version %d\n", bus->sdpcm_ver)); + /* make sure for the SDIO_DEVICE_RXDATAINT_MODE_1 corecontrol is proper */ + if ((bus->sih->buscoretype == SDIOD_CORE_ID) && (bus->sdpcmrev >= 4) && + (bus->rxint_mode == SDIO_DEVICE_RXDATAINT_MODE_1)) { + uint32 val; + + val = R_REG(bus->dhd->osh, &bus->regs->corecontrol); + val &= ~CC_XMTDATAAVAIL_MODE; + val |= CC_XMTDATAAVAIL_CTRL; + W_REG(bus->dhd->osh, &bus->regs->corecontrol, val); + + val = R_REG(bus->dhd->osh, &bus->regs->corecontrol); + } + +#ifdef DHD_DEBUG + /* Retrieve console state address now that firmware should have updated it */ + { + sdpcm_shared_t shared; + if (dhdsdio_readshared(bus, &shared) == 0) + bus->console_addr = shared.console_addr; + } +#endif /* DHD_DEBUG */ + } + + /* + * Flow Control has been moved into the RX headers and this out of band + * method isn't used any more. Leave this here for possibly remaining backward + * compatible with older dongles + */ + if (hmb_data & HMB_DATA_FC) { + fcbits = (hmb_data & HMB_DATA_FCDATA_MASK) >> HMB_DATA_FCDATA_SHIFT; + + if (fcbits & ~bus->flowcontrol) + bus->fc_xoff++; + if (bus->flowcontrol & ~fcbits) + bus->fc_xon++; + + bus->fc_rcvd++; + bus->flowcontrol = fcbits; + } + +#ifdef DHD_DEBUG + /* At least print a message if FW halted */ + if (hmb_data & HMB_DATA_FWHALT) { + DHD_ERROR(("INTERNAL ERROR: FIRMWARE HALTED : set BUS DOWN\n")); + dhdsdio_checkdied(bus, NULL, 0); + bus->dhd->busstate = DHD_BUS_DOWN; + } +#endif /* DHD_DEBUG */ + + /* Shouldn't be any others */ + if (hmb_data & ~(HMB_DATA_DEVREADY | + HMB_DATA_FWHALT | + HMB_DATA_NAKHANDLED | + HMB_DATA_FC | + HMB_DATA_FWREADY | + HMB_DATA_FCDATA_MASK | + HMB_DATA_VERSION_MASK)) { + DHD_ERROR(("Unknown mailbox data content: 0x%02x\n", hmb_data)); + } + + return intstatus; +} + +static bool +dhdsdio_dpc(dhd_bus_t *bus) +{ + bcmsdh_info_t *sdh = bus->sdh; + sdpcmd_regs_t *regs = bus->regs; + uint32 intstatus, newstatus = 0; + uint retries = 0; + uint rxlimit = dhd_rxbound; /* Rx frames to read before resched */ + uint txlimit = dhd_txbound; /* Tx frames to send before resched */ + uint framecnt = 0; /* Temporary counter of tx/rx frames */ + bool rxdone = TRUE; /* Flag for no more read data */ + bool resched = FALSE; /* Flag indicating resched wanted */ + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (bus->dhd->busstate == DHD_BUS_DOWN) { + DHD_ERROR(("%s: Bus down, ret\n", __FUNCTION__)); + bus->intstatus = 0; + return 0; + } + + /* Start with leftover status bits */ + intstatus = bus->intstatus; + + dhd_os_sdlock(bus->dhd); + + if (!SLPAUTO_ENAB(bus) && !KSO_ENAB(bus)) { + DHD_ERROR(("%s: Device asleep\n", __FUNCTION__)); + goto exit; + } + + /* If waiting for HTAVAIL, check status */ + if (!SLPAUTO_ENAB(bus) && (bus->clkstate == CLK_PENDING)) { + int err; + uint8 clkctl, devctl = 0; + +#ifdef DHD_DEBUG + /* Check for inconsistent device control */ + devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err); + if (err) { + DHD_ERROR(("%s: error reading DEVCTL: %d\n", __FUNCTION__, err)); + bus->dhd->busstate = DHD_BUS_DOWN; + } else { + ASSERT(devctl & SBSDIO_DEVCTL_CA_INT_ONLY); + } +#endif /* DHD_DEBUG */ + + /* Read CSR, if clock on switch to AVAIL, else ignore */ + clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err); + if (err) { + DHD_ERROR(("%s: error reading CSR: %d\n", __FUNCTION__, err)); + bus->dhd->busstate = DHD_BUS_DOWN; + } + + DHD_INFO(("DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n", devctl, clkctl)); + + if (SBSDIO_HTAV(clkctl)) { + devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err); + if (err) { + DHD_ERROR(("%s: error reading DEVCTL: %d\n", + __FUNCTION__, err)); + bus->dhd->busstate = DHD_BUS_DOWN; + } + devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err); + if (err) { + DHD_ERROR(("%s: error writing DEVCTL: %d\n", + __FUNCTION__, err)); + bus->dhd->busstate = DHD_BUS_DOWN; + } + bus->clkstate = CLK_AVAIL; + } else { + goto clkwait; + } + } + + BUS_WAKE(bus); + + /* Make sure backplane clock is on */ + dhdsdio_clkctl(bus, CLK_AVAIL, TRUE); + if (bus->clkstate != CLK_AVAIL) + goto clkwait; + + /* Pending interrupt indicates new device status */ + if (bus->ipend) { + bus->ipend = FALSE; + R_SDREG(newstatus, ®s->intstatus, retries); + bus->f1regdata++; + if (bcmsdh_regfail(bus->sdh)) + newstatus = 0; + newstatus &= bus->hostintmask; + bus->fcstate = !!(newstatus & I_HMB_FC_STATE); + if (newstatus) { + bus->f1regdata++; + if ((bus->rxint_mode == SDIO_DEVICE_RXDATAINT_MODE_0) && + (newstatus == I_XMTDATA_AVAIL)) { + } + else + W_SDREG(newstatus, ®s->intstatus, retries); + } + } + + /* Merge new bits with previous */ + intstatus |= newstatus; + bus->intstatus = 0; + + /* Handle flow-control change: read new state in case our ack + * crossed another change interrupt. If change still set, assume + * FC ON for safety, let next loop through do the debounce. + */ + if (intstatus & I_HMB_FC_CHANGE) { + intstatus &= ~I_HMB_FC_CHANGE; + W_SDREG(I_HMB_FC_CHANGE, ®s->intstatus, retries); + R_SDREG(newstatus, ®s->intstatus, retries); + bus->f1regdata += 2; + bus->fcstate = !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE)); + intstatus |= (newstatus & bus->hostintmask); + } + + /* Just being here means nothing more to do for chipactive */ + if (intstatus & I_CHIPACTIVE) { + /* ASSERT(bus->clkstate == CLK_AVAIL); */ + intstatus &= ~I_CHIPACTIVE; + } + + /* Handle host mailbox indication */ + if (intstatus & I_HMB_HOST_INT) { + intstatus &= ~I_HMB_HOST_INT; + intstatus |= dhdsdio_hostmail(bus); + } + + /* Generally don't ask for these, can get CRC errors... */ + if (intstatus & I_WR_OOSYNC) { + DHD_ERROR(("Dongle reports WR_OOSYNC\n")); + intstatus &= ~I_WR_OOSYNC; + } + + if (intstatus & I_RD_OOSYNC) { + DHD_ERROR(("Dongle reports RD_OOSYNC\n")); + intstatus &= ~I_RD_OOSYNC; + } + + if (intstatus & I_SBINT) { + DHD_ERROR(("Dongle reports SBINT\n")); + intstatus &= ~I_SBINT; + } + + /* Would be active due to wake-wlan in gSPI */ + if (intstatus & I_CHIPACTIVE) { + DHD_INFO(("Dongle reports CHIPACTIVE\n")); + intstatus &= ~I_CHIPACTIVE; + } + + /* Ignore frame indications if rxskip is set */ + if (bus->rxskip) { + intstatus &= ~FRAME_AVAIL_MASK(bus); + } + + /* On frame indication, read available frames */ + if (PKT_AVAILABLE(bus, intstatus)) { + framecnt = dhdsdio_readframes(bus, rxlimit, &rxdone); + if (rxdone || bus->rxskip) + intstatus &= ~FRAME_AVAIL_MASK(bus); + rxlimit -= MIN(framecnt, rxlimit); + } + + /* Keep still-pending events for next scheduling */ + bus->intstatus = intstatus; + +clkwait: + /* Re-enable interrupts to detect new device events (mailbox, rx frame) + * or clock availability. (Allows tx loop to check ipend if desired.) + * (Unless register access seems hosed, as we may not be able to ACK...) + */ + if (bus->intr && bus->intdis && !bcmsdh_regfail(sdh)) { + DHD_INTR(("%s: enable SDIO interrupts, rxdone %d framecnt %d\n", + __FUNCTION__, rxdone, framecnt)); + bus->intdis = FALSE; +#if defined(OOB_INTR_ONLY) + bcmsdh_oob_intr_set(1); +#endif /* defined(OOB_INTR_ONLY) */ + bcmsdh_intr_enable(sdh); + } + +#if defined(OOB_INTR_ONLY) && !defined(HW_OOB) + /* In case of SW-OOB(using edge trigger), + * Check interrupt status in the dongle again after enable irq on the host. + * and rechedule dpc if interrupt is pended in the dongle. + * There is a chance to miss OOB interrupt while irq is disabled on the host. + * No need to do this with HW-OOB(level trigger) + */ + R_SDREG(newstatus, ®s->intstatus, retries); + if (bcmsdh_regfail(bus->sdh)) + newstatus = 0; + if (newstatus & bus->hostintmask) { + bus->ipend = TRUE; + resched = TRUE; + } +#endif /* defined(OOB_INTR_ONLY) && !defined(HW_OOB) */ +#ifdef PROP_TXSTATUS + dhd_wlfc_trigger_pktcommit(bus->dhd); +#endif + if (TXCTLOK(bus) && bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL)) { + int ret, i; + + uint8* frame_seq = bus->ctrl_frame_buf + SDPCM_FRAMETAG_LEN; + + if (*frame_seq != bus->tx_seq) { + DHD_INFO(("%s IOCTL frame seq lag detected!" + " frm_seq:%d != bus->tx_seq:%d, corrected\n", + __FUNCTION__, *frame_seq, bus->tx_seq)); + *frame_seq = bus->tx_seq; + } + + ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, + (uint8 *)bus->ctrl_frame_buf, (uint32)bus->ctrl_frame_len, + NULL, NULL, NULL); + ASSERT(ret != BCME_PENDING); + if (ret == BCME_NODEVICE) { + DHD_ERROR(("%s: Device asleep already\n", __FUNCTION__)); + } else if (ret < 0) { + /* On failure, abort the command and terminate the frame */ + DHD_INFO(("%s: sdio error %d, abort command and terminate frame.\n", + __FUNCTION__, ret)); + bus->tx_sderrs++; + + bcmsdh_abort(sdh, SDIO_FUNC_2); + + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL, + SFC_WF_TERM, NULL); + bus->f1regdata++; + + for (i = 0; i < 3; i++) { + uint8 hi, lo; + hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, + SBSDIO_FUNC1_WFRAMEBCHI, NULL); + lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, + SBSDIO_FUNC1_WFRAMEBCLO, NULL); + bus->f1regdata += 2; + if ((hi == 0) && (lo == 0)) + break; + } + } + if (ret == 0) { + bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; + } + + bus->ctrl_frame_stat = FALSE; + dhd_wait_event_wakeup(bus->dhd); + } + /* Send queued frames (limit 1 if rx may still be pending) */ + else if ((bus->clkstate == CLK_AVAIL) && !bus->fcstate && + pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit && DATAOK(bus)) { + framecnt = rxdone ? txlimit : MIN(txlimit, dhd_txminmax); + framecnt = dhdsdio_sendfromq(bus, framecnt); + txlimit -= framecnt; + } + /* Resched the DPC if ctrl cmd is pending on bus credit */ + if (bus->ctrl_frame_stat) + resched = TRUE; + + /* Resched if events or tx frames are pending, else await next interrupt */ + /* On failed register access, all bets are off: no resched or interrupts */ + if ((bus->dhd->busstate == DHD_BUS_DOWN) || bcmsdh_regfail(sdh)) { + if ((bus->sih && bus->sih->buscorerev >= 12) && !(dhdsdio_sleepcsr_get(bus) & + SBSDIO_FUNC1_SLEEPCSR_KSO_MASK)) { + /* Bus failed because of KSO */ + DHD_ERROR(("%s: Bus failed due to KSO\n", __FUNCTION__)); + bus->kso = FALSE; + } else { + DHD_ERROR(("%s: failed backplane access over SDIO, halting operation\n", + __FUNCTION__)); + bus->dhd->busstate = DHD_BUS_DOWN; + bus->intstatus = 0; + } + } else if (bus->clkstate == CLK_PENDING) { + /* Awaiting I_CHIPACTIVE; don't resched */ + } else if (bus->intstatus || bus->ipend || + (!bus->fcstate && pktq_mlen(&bus->txq, ~bus->flowcontrol) && DATAOK(bus)) || + PKT_AVAILABLE(bus, bus->intstatus)) { /* Read multiple frames */ + resched = TRUE; + } + + bus->dpc_sched = resched; + + /* If we're done for now, turn off clock request. */ + if ((bus->idletime == DHD_IDLE_IMMEDIATE) && (bus->clkstate != CLK_PENDING)) { + bus->activity = FALSE; + dhdsdio_clkctl(bus, CLK_NONE, FALSE); + } + +exit: + /* attemp to update tx credit before exiting dpc */ + if (!resched && dhd_dpcpoll) { + if (dhdsdio_readframes(bus, dhd_rxbound, &rxdone) != 0) + resched = TRUE; + } + dhd_os_sdunlock(bus->dhd); + return resched; +} + +bool +dhd_bus_dpc(struct dhd_bus *bus) +{ + bool resched; + + /* Call the DPC directly. */ + DHD_TRACE(("Calling dhdsdio_dpc() from %s\n", __FUNCTION__)); + resched = dhdsdio_dpc(bus); + + return resched; +} + +void +dhdsdio_isr(void *arg) +{ + dhd_bus_t *bus = (dhd_bus_t*)arg; + bcmsdh_info_t *sdh; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (!bus) { + DHD_ERROR(("%s : bus is null pointer , exit \n", __FUNCTION__)); + return; + } + sdh = bus->sdh; + + if (bus->dhd->busstate == DHD_BUS_DOWN) { + DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__)); + return; + } + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + /* Count the interrupt call */ + bus->intrcount++; + bus->ipend = TRUE; + + /* Shouldn't get this interrupt if we're sleeping? */ + if (!SLPAUTO_ENAB(bus)) { + if (bus->sleeping) { + DHD_ERROR(("INTERRUPT WHILE SLEEPING??\n")); + return; + } else if (!KSO_ENAB(bus)) { + DHD_ERROR(("ISR in devsleep 1\n")); + } + } + + /* Disable additional interrupts (is this needed now)? */ + if (bus->intr) { + DHD_INTR(("%s: disable SDIO interrupts\n", __FUNCTION__)); + } else { + DHD_ERROR(("dhdsdio_isr() w/o interrupt configured!\n")); + } + + bcmsdh_intr_disable(sdh); + bus->intdis = TRUE; + +#if defined(SDIO_ISR_THREAD) + DHD_TRACE(("Calling dhdsdio_dpc() from %s\n", __FUNCTION__)); + DHD_OS_WAKE_LOCK(bus->dhd); + while (dhdsdio_dpc(bus)); + DHD_OS_WAKE_UNLOCK(bus->dhd); +#else + bus->dpc_sched = TRUE; + dhd_sched_dpc(bus->dhd); +#endif + +} + +#ifdef SDTEST +static void +dhdsdio_pktgen_init(dhd_bus_t *bus) +{ + /* Default to specified length, or full range */ + if (dhd_pktgen_len) { + bus->pktgen_maxlen = MIN(dhd_pktgen_len, MAX_PKTGEN_LEN); + bus->pktgen_minlen = bus->pktgen_maxlen; + } else { + bus->pktgen_maxlen = MAX_PKTGEN_LEN; + bus->pktgen_minlen = 0; + } + bus->pktgen_len = (uint16)bus->pktgen_minlen; + + /* Default to per-watchdog burst with 10s print time */ + bus->pktgen_freq = 1; + bus->pktgen_print = dhd_watchdog_ms ? (10000/dhd_watchdog_ms):0; + bus->pktgen_count = (dhd_pktgen * dhd_watchdog_ms + 999) / 1000; + + /* Default to echo mode */ + bus->pktgen_mode = DHD_PKTGEN_ECHO; + bus->pktgen_stop = 1; +} + +static void +dhdsdio_pktgen(dhd_bus_t *bus) +{ + void *pkt; + uint8 *data; + uint pktcount; + uint fillbyte; + osl_t *osh = bus->dhd->osh; + uint16 len; + ulong time_lapse; + uint sent_pkts; + uint rcvd_pkts; + + /* Display current count if appropriate */ + if (bus->pktgen_print && (++bus->pktgen_ptick >= bus->pktgen_print)) { + bus->pktgen_ptick = 0; + printf("%s: send attempts %d, rcvd %d, errors %d\n", + __FUNCTION__, bus->pktgen_sent, bus->pktgen_rcvd, bus->pktgen_fail); + + /* Print throughput stats only for constant length packet runs */ + if (bus->pktgen_minlen == bus->pktgen_maxlen) { + time_lapse = jiffies - bus->pktgen_prev_time; + bus->pktgen_prev_time = jiffies; + sent_pkts = bus->pktgen_sent - bus->pktgen_prev_sent; + bus->pktgen_prev_sent = bus->pktgen_sent; + rcvd_pkts = bus->pktgen_rcvd - bus->pktgen_prev_rcvd; + bus->pktgen_prev_rcvd = bus->pktgen_rcvd; + + printf("%s: Tx Throughput %d kbps, Rx Throughput %d kbps\n", + __FUNCTION__, + (sent_pkts * bus->pktgen_len / jiffies_to_msecs(time_lapse)) * 8, + (rcvd_pkts * bus->pktgen_len / jiffies_to_msecs(time_lapse)) * 8); + } + } + + /* For recv mode, just make sure dongle has started sending */ + if (bus->pktgen_mode == DHD_PKTGEN_RECV) { + if (bus->pktgen_rcv_state == PKTGEN_RCV_IDLE) { + bus->pktgen_rcv_state = PKTGEN_RCV_ONGOING; + dhdsdio_sdtest_set(bus, bus->pktgen_total); + } + return; + } + + /* Otherwise, generate or request the specified number of packets */ + for (pktcount = 0; pktcount < bus->pktgen_count; pktcount++) { + /* Stop if total has been reached */ + if (bus->pktgen_total && (bus->pktgen_sent >= bus->pktgen_total)) { + bus->pktgen_count = 0; + break; + } + + /* Allocate an appropriate-sized packet */ + if (bus->pktgen_mode == DHD_PKTGEN_RXBURST) { + len = SDPCM_TEST_PKT_CNT_FLD_LEN; + } else { + len = bus->pktgen_len; + } + if (!(pkt = PKTGET(osh, (len + SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + DHD_SDALIGN), + TRUE))) {; + DHD_ERROR(("%s: PKTGET failed!\n", __FUNCTION__)); + break; + } + PKTALIGN(osh, pkt, (len + SDPCM_HDRLEN + SDPCM_TEST_HDRLEN), DHD_SDALIGN); + data = (uint8*)PKTDATA(osh, pkt) + SDPCM_HDRLEN; + + /* Write test header cmd and extra based on mode */ + switch (bus->pktgen_mode) { + case DHD_PKTGEN_ECHO: + *data++ = SDPCM_TEST_ECHOREQ; + *data++ = (uint8)bus->pktgen_sent; + break; + + case DHD_PKTGEN_SEND: + *data++ = SDPCM_TEST_DISCARD; + *data++ = (uint8)bus->pktgen_sent; + break; + + case DHD_PKTGEN_RXBURST: + *data++ = SDPCM_TEST_BURST; + *data++ = (uint8)bus->pktgen_count; /* Just for backward compatability */ + break; + + default: + DHD_ERROR(("Unrecognized pktgen mode %d\n", bus->pktgen_mode)); + PKTFREE(osh, pkt, TRUE); + bus->pktgen_count = 0; + return; + } + + /* Write test header length field */ + *data++ = (bus->pktgen_len >> 0); + *data++ = (bus->pktgen_len >> 8); + + /* Write frame count in a 4 byte field adjucent to SDPCM test header for + * burst mode + */ + if (bus->pktgen_mode == DHD_PKTGEN_RXBURST) { + *data++ = (uint8)(bus->pktgen_count >> 0); + *data++ = (uint8)(bus->pktgen_count >> 8); + *data++ = (uint8)(bus->pktgen_count >> 16); + *data++ = (uint8)(bus->pktgen_count >> 24); + } else { + + /* Then fill in the remainder -- N/A for burst */ + for (fillbyte = 0; fillbyte < len; fillbyte++) + *data++ = SDPCM_TEST_FILL(fillbyte, (uint8)bus->pktgen_sent); + } + +#ifdef DHD_DEBUG + if (DHD_BYTES_ON() && DHD_DATA_ON()) { + data = (uint8*)PKTDATA(osh, pkt) + SDPCM_HDRLEN; + prhex("dhdsdio_pktgen: Tx Data", data, PKTLEN(osh, pkt) - SDPCM_HDRLEN); + } +#endif + + /* Send it */ + if (dhdsdio_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, TRUE, FALSE)) { + bus->pktgen_fail++; + if (bus->pktgen_stop && bus->pktgen_stop == bus->pktgen_fail) + bus->pktgen_count = 0; + } + bus->pktgen_sent++; + + /* Bump length if not fixed, wrap at max */ + if (++bus->pktgen_len > bus->pktgen_maxlen) + bus->pktgen_len = (uint16)bus->pktgen_minlen; + + /* Special case for burst mode: just send one request! */ + if (bus->pktgen_mode == DHD_PKTGEN_RXBURST) + break; + } +} + +static void +dhdsdio_sdtest_set(dhd_bus_t *bus, uint count) +{ + void *pkt; + uint8 *data; + osl_t *osh = bus->dhd->osh; + + /* Allocate the packet */ + if (!(pkt = PKTGET(osh, SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + + SDPCM_TEST_PKT_CNT_FLD_LEN + DHD_SDALIGN, TRUE))) { + DHD_ERROR(("%s: PKTGET failed!\n", __FUNCTION__)); + return; + } + PKTALIGN(osh, pkt, (SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + + SDPCM_TEST_PKT_CNT_FLD_LEN), DHD_SDALIGN); + data = (uint8*)PKTDATA(osh, pkt) + SDPCM_HDRLEN; + + /* Fill in the test header */ + *data++ = SDPCM_TEST_SEND; + *data++ = (count > 0)?TRUE:FALSE; + *data++ = (bus->pktgen_maxlen >> 0); + *data++ = (bus->pktgen_maxlen >> 8); + *data++ = (uint8)(count >> 0); + *data++ = (uint8)(count >> 8); + *data++ = (uint8)(count >> 16); + *data++ = (uint8)(count >> 24); + + /* Send it */ + if (dhdsdio_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, TRUE, FALSE)) + bus->pktgen_fail++; +} + + +static void +dhdsdio_testrcv(dhd_bus_t *bus, void *pkt, uint seq) +{ + osl_t *osh = bus->dhd->osh; + uint8 *data; + uint pktlen; + + uint8 cmd; + uint8 extra; + uint16 len; + uint16 offset; + + /* Check for min length */ + if ((pktlen = PKTLEN(osh, pkt)) < SDPCM_TEST_HDRLEN) { + DHD_ERROR(("dhdsdio_restrcv: toss runt frame, pktlen %d\n", pktlen)); + PKTFREE(osh, pkt, FALSE); + return; + } + + /* Extract header fields */ + data = PKTDATA(osh, pkt); + cmd = *data++; + extra = *data++; + len = *data++; len += *data++ << 8; + DHD_TRACE(("%s:cmd:%d, xtra:%d,len:%d\n", __FUNCTION__, cmd, extra, len)); + /* Check length for relevant commands */ + if (cmd == SDPCM_TEST_DISCARD || cmd == SDPCM_TEST_ECHOREQ || cmd == SDPCM_TEST_ECHORSP) { + if (pktlen != len + SDPCM_TEST_HDRLEN) { + DHD_ERROR(("dhdsdio_testrcv: frame length mismatch, pktlen %d seq %d" + " cmd %d extra %d len %d\n", pktlen, seq, cmd, extra, len)); + PKTFREE(osh, pkt, FALSE); + return; + } + } + + /* Process as per command */ + switch (cmd) { + case SDPCM_TEST_ECHOREQ: + /* Rx->Tx turnaround ok (even on NDIS w/current implementation) */ + *(uint8 *)(PKTDATA(osh, pkt)) = SDPCM_TEST_ECHORSP; + if (dhdsdio_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, TRUE, FALSE) == 0) { + bus->pktgen_sent++; + } else { + bus->pktgen_fail++; + PKTFREE(osh, pkt, FALSE); + } + bus->pktgen_rcvd++; + break; + + case SDPCM_TEST_ECHORSP: + if (bus->ext_loop) { + PKTFREE(osh, pkt, FALSE); + bus->pktgen_rcvd++; + break; + } + + for (offset = 0; offset < len; offset++, data++) { + if (*data != SDPCM_TEST_FILL(offset, extra)) { + DHD_ERROR(("dhdsdio_testrcv: echo data mismatch: " + "offset %d (len %d) expect 0x%02x rcvd 0x%02x\n", + offset, len, SDPCM_TEST_FILL(offset, extra), *data)); + break; + } + } + PKTFREE(osh, pkt, FALSE); + bus->pktgen_rcvd++; + break; + + case SDPCM_TEST_DISCARD: + { + int i = 0; + uint8 *prn = data; + uint8 testval = extra; + for (i = 0; i < len; i++) { + if (*prn != testval) { + DHD_ERROR(("DIErr@Pkt#:%d,Ix:%d, expected:0x%x, got:0x%x\n", + i, bus->pktgen_rcvd_rcvsession, testval, *prn)); + prn++; testval++; + } + } + } + PKTFREE(osh, pkt, FALSE); + bus->pktgen_rcvd++; + break; + + case SDPCM_TEST_BURST: + case SDPCM_TEST_SEND: + default: + DHD_INFO(("dhdsdio_testrcv: unsupported or unknown command, pktlen %d seq %d" + " cmd %d extra %d len %d\n", pktlen, seq, cmd, extra, len)); + PKTFREE(osh, pkt, FALSE); + break; + } + + /* For recv mode, stop at limit (and tell dongle to stop sending) */ + if (bus->pktgen_mode == DHD_PKTGEN_RECV) { + if (bus->pktgen_rcv_state != PKTGEN_RCV_IDLE) { + bus->pktgen_rcvd_rcvsession++; + + if (bus->pktgen_total && + (bus->pktgen_rcvd_rcvsession >= bus->pktgen_total)) { + bus->pktgen_count = 0; + DHD_ERROR(("Pktgen:rcv test complete!\n")); + bus->pktgen_rcv_state = PKTGEN_RCV_IDLE; + dhdsdio_sdtest_set(bus, FALSE); + bus->pktgen_rcvd_rcvsession = 0; + } + } + } +} +#endif /* SDTEST */ + +extern void +dhd_disable_intr(dhd_pub_t *dhdp) +{ + dhd_bus_t *bus; + bus = dhdp->bus; + bcmsdh_intr_disable(bus->sdh); +} + +extern bool +dhd_bus_watchdog(dhd_pub_t *dhdp) +{ + dhd_bus_t *bus; + + DHD_TIMER(("%s: Enter\n", __FUNCTION__)); + + bus = dhdp->bus; + + if (bus->dhd->dongle_reset) + return FALSE; + + /* Ignore the timer if simulating bus down */ + if (!SLPAUTO_ENAB(bus) && bus->sleeping) + return FALSE; + + if (dhdp->busstate == DHD_BUS_DOWN) + return FALSE; + + /* Poll period: check device if appropriate. */ + if (!SLPAUTO_ENAB(bus) && (bus->poll && (++bus->polltick >= bus->pollrate))) { + uint32 intstatus = 0; + + /* Reset poll tick */ + bus->polltick = 0; + + /* Check device if no interrupts */ + if (!bus->intr || (bus->intrcount == bus->lastintrs)) { + + if (!bus->dpc_sched) { + uint8 devpend; + devpend = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0, + SDIOD_CCCR_INTPEND, NULL); + intstatus = devpend & (INTR_STATUS_FUNC1 | INTR_STATUS_FUNC2); + } + + /* If there is something, make like the ISR and schedule the DPC */ + if (intstatus) { + bus->pollcnt++; + bus->ipend = TRUE; + if (bus->intr) { + bcmsdh_intr_disable(bus->sdh); + } + bus->dpc_sched = TRUE; + dhd_sched_dpc(bus->dhd); + + } + } + + /* Update interrupt tracking */ + bus->lastintrs = bus->intrcount; + } + +#ifdef DHD_DEBUG + /* Poll for console output periodically */ + if (dhdp->busstate == DHD_BUS_DATA && dhd_console_ms != 0) { + bus->console.count += dhd_watchdog_ms; + if (bus->console.count >= dhd_console_ms) { + bus->console.count -= dhd_console_ms; + /* Make sure backplane clock is on */ + if (SLPAUTO_ENAB(bus)) + dhdsdio_bussleep(bus, FALSE); + else + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + if (dhdsdio_readconsole(bus) < 0) + dhd_console_ms = 0; /* On error, stop trying */ + } + } +#endif /* DHD_DEBUG */ + +#ifdef SDTEST + /* Generate packets if configured */ + if (bus->pktgen_count && (++bus->pktgen_tick >= bus->pktgen_freq)) { + /* Make sure backplane clock is on */ + if (SLPAUTO_ENAB(bus)) + dhdsdio_bussleep(bus, FALSE); + else + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + bus->pktgen_tick = 0; + dhdsdio_pktgen(bus); + } +#endif + + /* On idle timeout clear activity flag and/or turn off clock */ +#ifdef DHD_USE_IDLECOUNT + if (bus->activity) + bus->activity = FALSE; + else { + bus->idlecount++; + + if (bus->idlecount >= bus->idletime) { + DHD_TIMER(("%s: DHD Idle state!!\n", __FUNCTION__)); + + if (SLPAUTO_ENAB(bus)) { + if (dhdsdio_bussleep(bus, TRUE) != BCME_BUSY) + dhd_os_wd_timer(bus->dhd, 0); + } else + dhdsdio_clkctl(bus, CLK_NONE, FALSE); + + bus->idlecount = 0; + } + } +#else + if ((bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) { + if (++bus->idlecount > bus->idletime) { + bus->idlecount = 0; + if (bus->activity) { + bus->activity = FALSE; + if (SLPAUTO_ENAB(bus)) { + if (!bus->readframes) + dhdsdio_bussleep(bus, TRUE); + else + bus->reqbussleep = TRUE; + } + else + dhdsdio_clkctl(bus, CLK_NONE, FALSE); + } + } + } +#endif /* DHD_USE_IDLECOUNT */ + + return bus->ipend; +} + +#ifdef DHD_DEBUG +extern int +dhd_bus_console_in(dhd_pub_t *dhdp, uchar *msg, uint msglen) +{ + dhd_bus_t *bus = dhdp->bus; + uint32 addr, val; + int rv; + void *pkt; + + /* Address could be zero if CONSOLE := 0 in dongle Makefile */ + if (bus->console_addr == 0) + return BCME_UNSUPPORTED; + + /* Exclusive bus access */ + dhd_os_sdlock(bus->dhd); + + /* Don't allow input if dongle is in reset */ + if (bus->dhd->dongle_reset) { + dhd_os_sdunlock(bus->dhd); + return BCME_NOTREADY; + } + + /* Request clock to allow SDIO accesses */ + BUS_WAKE(bus); + /* No pend allowed since txpkt is called later, ht clk has to be on */ + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + + /* Zero cbuf_index */ + addr = bus->console_addr + OFFSETOF(hndrte_cons_t, cbuf_idx); + val = htol32(0); + if ((rv = dhdsdio_membytes(bus, TRUE, addr, (uint8 *)&val, sizeof(val))) < 0) + goto done; + + /* Write message into cbuf */ + addr = bus->console_addr + OFFSETOF(hndrte_cons_t, cbuf); + if ((rv = dhdsdio_membytes(bus, TRUE, addr, (uint8 *)msg, msglen)) < 0) + goto done; + + /* Write length into vcons_in */ + addr = bus->console_addr + OFFSETOF(hndrte_cons_t, vcons_in); + val = htol32(msglen); + if ((rv = dhdsdio_membytes(bus, TRUE, addr, (uint8 *)&val, sizeof(val))) < 0) + goto done; + + /* Bump dongle by sending an empty packet on the event channel. + * sdpcm_sendup (RX) checks for virtual console input. + */ + if ((pkt = PKTGET(bus->dhd->osh, 4 + SDPCM_RESERVE, TRUE)) != NULL) + dhdsdio_txpkt(bus, pkt, SDPCM_EVENT_CHANNEL, TRUE, FALSE); + +done: + if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) { + bus->activity = FALSE; + dhdsdio_clkctl(bus, CLK_NONE, TRUE); + } + + dhd_os_sdunlock(bus->dhd); + + return rv; +} +#endif /* DHD_DEBUG */ + +#ifdef DHD_DEBUG +static void +dhd_dump_cis(uint fn, uint8 *cis) +{ + uint byte, tag, tdata; + DHD_INFO(("Function %d CIS:\n", fn)); + + for (tdata = byte = 0; byte < SBSDIO_CIS_SIZE_LIMIT; byte++) { + if ((byte % 16) == 0) + DHD_INFO((" ")); + DHD_INFO(("%02x ", cis[byte])); + if ((byte % 16) == 15) + DHD_INFO(("\n")); + if (!tdata--) { + tag = cis[byte]; + if (tag == 0xff) + break; + else if (!tag) + tdata = 0; + else if ((byte + 1) < SBSDIO_CIS_SIZE_LIMIT) + tdata = cis[byte + 1] + 1; + else + DHD_INFO(("]")); + } + } + if ((byte % 16) != 15) + DHD_INFO(("\n")); +} +#endif /* DHD_DEBUG */ + +static bool +dhdsdio_chipmatch(uint16 chipid) +{ + if (chipid == BCM4325_CHIP_ID) + return TRUE; + if (chipid == BCM4329_CHIP_ID) + return TRUE; + if (chipid == BCM4315_CHIP_ID) + return TRUE; + if (chipid == BCM4319_CHIP_ID) + return TRUE; + if (chipid == BCM4336_CHIP_ID) + return TRUE; + if (chipid == BCM4330_CHIP_ID) + return TRUE; + if (chipid == BCM43237_CHIP_ID) + return TRUE; + if (chipid == BCM43362_CHIP_ID) + return TRUE; + if (chipid == BCM4314_CHIP_ID) + return TRUE; + if (chipid == BCM4334_CHIP_ID) + return TRUE; + if (chipid == BCM43341_CHIP_ID) + return TRUE; + if (chipid == BCM43239_CHIP_ID) + return TRUE; + if (chipid == BCM4324_CHIP_ID) + return TRUE; + if (chipid == BCM4335_CHIP_ID) + return TRUE; + return FALSE; +} + +static void * +dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot, + uint16 func, uint bustype, void *regsva, osl_t * osh, void *sdh) +{ + int ret; + dhd_bus_t *bus; +#ifdef GET_CUSTOM_MAC_ENABLE + struct ether_addr ea_addr; +#endif /* GET_CUSTOM_MAC_ENABLE */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + + if (mutex_is_locked(&_dhd_sdio_mutex_lock_) == 0) { + DHD_ERROR(("%s : no mutex held. set lock\n", __FUNCTION__)); + } + else { + DHD_ERROR(("%s : mutex is locked!. wait for unlocking\n", __FUNCTION__)); + } + mutex_lock(&_dhd_sdio_mutex_lock_); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ + + /* Init global variables at run-time, not as part of the declaration. + * This is required to support init/de-init of the driver. Initialization + * of globals as part of the declaration results in non-deterministic + * behavior since the value of the globals may be different on the + * first time that the driver is initialized vs subsequent initializations. + */ + dhd_txbound = DHD_TXBOUND; + dhd_rxbound = DHD_RXBOUND; + dhd_alignctl = TRUE; + sd1idle = TRUE; + dhd_readahead = TRUE; + retrydata = FALSE; + dhd_doflow = FALSE; + dhd_dongle_memsize = 0; + dhd_txminmax = DHD_TXMINMAX; + + forcealign = TRUE; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + DHD_INFO(("%s: venid 0x%04x devid 0x%04x\n", __FUNCTION__, venid, devid)); + + /* We make assumptions about address window mappings */ + ASSERT((uintptr)regsva == SI_ENUM_BASE); + + /* BCMSDH passes venid and devid based on CIS parsing -- but low-power start + * means early parse could fail, so here we should get either an ID + * we recognize OR (-1) indicating we must request power first. + */ + /* Check the Vendor ID */ + switch (venid) { + case 0x0000: + case VENDOR_BROADCOM: + break; + default: + DHD_ERROR(("%s: unknown vendor: 0x%04x\n", + __FUNCTION__, venid)); + goto forcereturn; + } + + /* Check the Device ID and make sure it's one that we support */ + switch (devid) { + case BCM4325_D11DUAL_ID: /* 4325 802.11a/g id */ + case BCM4325_D11G_ID: /* 4325 802.11g 2.4Ghz band id */ + case BCM4325_D11A_ID: /* 4325 802.11a 5Ghz band id */ + DHD_INFO(("%s: found 4325 Dongle\n", __FUNCTION__)); + break; + case BCM4329_D11N_ID: /* 4329 802.11n dualband device */ + case BCM4329_D11N2G_ID: /* 4329 802.11n 2.4G device */ + case BCM4329_D11N5G_ID: /* 4329 802.11n 5G device */ + case 0x4329: + DHD_INFO(("%s: found 4329 Dongle\n", __FUNCTION__)); + break; + case BCM4315_D11DUAL_ID: /* 4315 802.11a/g id */ + case BCM4315_D11G_ID: /* 4315 802.11g id */ + case BCM4315_D11A_ID: /* 4315 802.11a id */ + DHD_INFO(("%s: found 4315 Dongle\n", __FUNCTION__)); + break; + case BCM4319_D11N_ID: /* 4319 802.11n id */ + case BCM4319_D11N2G_ID: /* 4319 802.11n2g id */ + case BCM4319_D11N5G_ID: /* 4319 802.11n5g id */ + DHD_INFO(("%s: found 4319 Dongle\n", __FUNCTION__)); + break; + case 0: + DHD_INFO(("%s: allow device id 0, will check chip internals\n", + __FUNCTION__)); + break; + + default: + DHD_ERROR(("%s: skipping 0x%04x/0x%04x, not a dongle\n", + __FUNCTION__, venid, devid)); + goto forcereturn; + } + + if (osh == NULL) { + /* Ask the OS interface part for an OSL handle */ + if (!(osh = dhd_osl_attach(sdh, DHD_BUS))) { + DHD_ERROR(("%s: osl_attach failed!\n", __FUNCTION__)); + goto forcereturn; + } + } + + /* Allocate private bus interface state */ + if (!(bus = MALLOC(osh, sizeof(dhd_bus_t)))) { + DHD_ERROR(("%s: MALLOC of dhd_bus_t failed\n", __FUNCTION__)); + goto fail; + } + bzero(bus, sizeof(dhd_bus_t)); + bus->sdh = sdh; + bus->cl_devid = (uint16)devid; + bus->bus = DHD_BUS; + bus->tx_seq = SDPCM_SEQUENCE_WRAP - 1; + bus->usebufpool = FALSE; /* Use bufpool if allocated, else use locally malloced rxbuf */ + + /* attach the common module */ + dhd_common_init(osh); + + /* attempt to attach to the dongle */ + if (!(dhdsdio_probe_attach(bus, osh, sdh, regsva, devid))) { + DHD_ERROR(("%s: dhdsdio_probe_attach failed\n", __FUNCTION__)); + goto fail; + } + + /* Attach to the dhd/OS/network interface */ + if (!(bus->dhd = dhd_attach(osh, bus, SDPCM_RESERVE))) { + DHD_ERROR(("%s: dhd_attach failed\n", __FUNCTION__)); + goto fail; + } + + /* Allocate buffers */ + if (!(dhdsdio_probe_malloc(bus, osh, sdh))) { + DHD_ERROR(("%s: dhdsdio_probe_malloc failed\n", __FUNCTION__)); + goto fail; + } + + if (!(dhdsdio_probe_init(bus, osh, sdh))) { + DHD_ERROR(("%s: dhdsdio_probe_init failed\n", __FUNCTION__)); + goto fail; + } + + if (bus->intr) { + /* Register interrupt callback, but mask it (not operational yet). */ + DHD_INTR(("%s: disable SDIO interrupts (not interested yet)\n", __FUNCTION__)); + bcmsdh_intr_disable(sdh); + if ((ret = bcmsdh_intr_reg(sdh, dhdsdio_isr, bus)) != 0) { + DHD_ERROR(("%s: FAILED: bcmsdh_intr_reg returned %d\n", + __FUNCTION__, ret)); + goto fail; + } + DHD_INTR(("%s: registered SDIO interrupt function ok\n", __FUNCTION__)); + } else { + DHD_INFO(("%s: SDIO interrupt function is NOT registered due to polling mode\n", + __FUNCTION__)); + } + + DHD_INFO(("%s: completed!!\n", __FUNCTION__)); + +#ifdef GET_CUSTOM_MAC_ENABLE + /* Read MAC address from external customer place */ + memset(&ea_addr, 0, sizeof(ea_addr)); + ret = dhd_custom_get_mac_address(ea_addr.octet); + if (!ret) { + memcpy(bus->dhd->mac.octet, (void *)&ea_addr, ETHER_ADDR_LEN); + } +#endif /* GET_CUSTOM_MAC_ENABLE */ + + /* if firmware path present try to download and bring up bus */ + if (dhd_download_fw_on_driverload) { + if ((ret = dhd_bus_start(bus->dhd)) != 0) { + DHD_ERROR(("%s: dhd_bus_start failed\n", __FUNCTION__)); + goto fail; + } + } + /* Ok, have the per-port tell the stack we're open for business */ + if (dhd_net_attach(bus->dhd, 0) != 0) { + DHD_ERROR(("%s: Net attach failed!!\n", __FUNCTION__)); + goto fail; + } + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + mutex_unlock(&_dhd_sdio_mutex_lock_); + DHD_ERROR(("%s : the lock is released.\n", __FUNCTION__)); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ + + return bus; + +fail: + dhdsdio_release(bus, osh); + +forcereturn: +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + mutex_unlock(&_dhd_sdio_mutex_lock_); + DHD_ERROR(("%s : the lock is released.\n", __FUNCTION__)); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ + + return NULL; +} + +static bool +dhdsdio_probe_attach(struct dhd_bus *bus, osl_t *osh, void *sdh, void *regsva, + uint16 devid) +{ + int err = 0; + uint8 clkctl = 0; + + bus->alp_only = TRUE; + bus->sih = NULL; + + /* Return the window to backplane enumeration space for core access */ + if (dhdsdio_set_siaddr_window(bus, SI_ENUM_BASE)) { + DHD_ERROR(("%s: FAILED to return to SI_ENUM_BASE\n", __FUNCTION__)); + } + +#ifdef DHD_DEBUG + DHD_ERROR(("F1 signature read @0x18000000=0x%4x\n", + bcmsdh_reg_read(bus->sdh, SI_ENUM_BASE, 4))); + +#endif /* DHD_DEBUG */ + + + /* Force PLL off until si_attach() programs PLL control regs */ + + + + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, DHD_INIT_CLKCTL1, &err); + if (!err) + clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err); + + if (err || ((clkctl & ~SBSDIO_AVBITS) != DHD_INIT_CLKCTL1)) { + DHD_ERROR(("dhdsdio_probe: ChipClkCSR access: err %d wrote 0x%02x read 0x%02x\n", + err, DHD_INIT_CLKCTL1, clkctl)); + goto fail; + } + +#ifdef DHD_DEBUG + if (DHD_INFO_ON()) { + uint fn, numfn; + uint8 *cis[SDIOD_MAX_IOFUNCS]; + int err = 0; + + numfn = bcmsdh_query_iofnum(sdh); + ASSERT(numfn <= SDIOD_MAX_IOFUNCS); + + /* Make sure ALP is available before trying to read CIS */ + SPINWAIT(((clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, + SBSDIO_FUNC1_CHIPCLKCSR, NULL)), + !SBSDIO_ALPAV(clkctl)), PMU_MAX_TRANSITION_DLY); + + /* Now request ALP be put on the bus */ + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, + DHD_INIT_CLKCTL2, &err); + OSL_DELAY(65); + + for (fn = 0; fn <= numfn; fn++) { + if (!(cis[fn] = MALLOC(osh, SBSDIO_CIS_SIZE_LIMIT))) { + DHD_INFO(("dhdsdio_probe: fn %d cis malloc failed\n", fn)); + break; + } + bzero(cis[fn], SBSDIO_CIS_SIZE_LIMIT); + + if ((err = bcmsdh_cis_read(sdh, fn, cis[fn], SBSDIO_CIS_SIZE_LIMIT))) { + DHD_INFO(("dhdsdio_probe: fn %d cis read err %d\n", fn, err)); + MFREE(osh, cis[fn], SBSDIO_CIS_SIZE_LIMIT); + break; + } + dhd_dump_cis(fn, cis[fn]); + } + + while (fn-- > 0) { + ASSERT(cis[fn]); + MFREE(osh, cis[fn], SBSDIO_CIS_SIZE_LIMIT); + } + + if (err) { + DHD_ERROR(("dhdsdio_probe: failure reading or parsing CIS\n")); + goto fail; + } + } +#endif /* DHD_DEBUG */ + + /* si_attach() will provide an SI handle and scan the backplane */ + if (!(bus->sih = si_attach((uint)devid, osh, regsva, DHD_BUS, sdh, + &bus->vars, &bus->varsz))) { + DHD_ERROR(("%s: si_attach failed!\n", __FUNCTION__)); + goto fail; + } + + bcmsdh_chipinfo(sdh, bus->sih->chip, bus->sih->chiprev); + + if (!dhdsdio_chipmatch((uint16)bus->sih->chip)) { + DHD_ERROR(("%s: unsupported chip: 0x%04x\n", + __FUNCTION__, bus->sih->chip)); + goto fail; + } + + if (bus->sih->buscorerev >= 12) + dhdsdio_clk_kso_init(bus); + else + bus->kso = TRUE; + + if (CST4330_CHIPMODE_SDIOD(bus->sih->chipst)) { + } + + si_sdiod_drive_strength_init(bus->sih, osh, dhd_sdiod_drive_strength); + + + /* Get info on the ARM and SOCRAM cores... */ + if (!DHD_NOPMU(bus)) { + if ((si_setcore(bus->sih, ARM7S_CORE_ID, 0)) || + (si_setcore(bus->sih, ARMCM3_CORE_ID, 0)) || + (si_setcore(bus->sih, ARMCR4_CORE_ID, 0))) { + bus->armrev = si_corerev(bus->sih); + } else { + DHD_ERROR(("%s: failed to find ARM core!\n", __FUNCTION__)); + goto fail; + } + + if (!si_setcore(bus->sih, ARMCR4_CORE_ID, 0)) { + if (!(bus->orig_ramsize = si_socram_size(bus->sih))) { + DHD_ERROR(("%s: failed to find SOCRAM memory!\n", __FUNCTION__)); + goto fail; + } + } else { + /* cr4 has a different way to find the RAM size from TCM's */ + if (!(bus->orig_ramsize = si_tcm_size(bus->sih))) { + DHD_ERROR(("%s: failed to find CR4-TCM memory!\n", __FUNCTION__)); + goto fail; + } + /* also populate base address */ + bus->dongle_ram_base = CR4_RAM_BASE; + } + bus->ramsize = bus->orig_ramsize; + if (dhd_dongle_memsize) + dhd_dongle_setmemsize(bus, dhd_dongle_memsize); + + DHD_ERROR(("DHD: dongle ram size is set to %d(orig %d)\n", + bus->ramsize, bus->orig_ramsize)); + + bus->srmemsize = si_socram_srmem_size(bus->sih); + } + + /* ...but normally deal with the SDPCMDEV core */ + if (!(bus->regs = si_setcore(bus->sih, PCMCIA_CORE_ID, 0)) && + !(bus->regs = si_setcore(bus->sih, SDIOD_CORE_ID, 0))) { + DHD_ERROR(("%s: failed to find SDIODEV core!\n", __FUNCTION__)); + goto fail; + } + bus->sdpcmrev = si_corerev(bus->sih); + + /* Set core control so an SDIO reset does a backplane reset */ + OR_REG(osh, &bus->regs->corecontrol, CC_BPRESEN); + bus->rxint_mode = SDIO_DEVICE_HMB_RXINT; + + if ((bus->sih->buscoretype == SDIOD_CORE_ID) && (bus->sdpcmrev >= 4) && + (bus->rxint_mode == SDIO_DEVICE_RXDATAINT_MODE_1)) + { + uint32 val; + + val = R_REG(osh, &bus->regs->corecontrol); + val &= ~CC_XMTDATAAVAIL_MODE; + val |= CC_XMTDATAAVAIL_CTRL; + W_REG(osh, &bus->regs->corecontrol, val); + } + + + pktq_init(&bus->txq, (PRIOMASK + 1), QLEN); + + /* Locate an appropriately-aligned portion of hdrbuf */ + bus->rxhdr = (uint8 *)ROUNDUP((uintptr)&bus->hdrbuf[0], DHD_SDALIGN); + + /* Set the poll and/or interrupt flags */ + bus->intr = (bool)dhd_intr; + if ((bus->poll = (bool)dhd_poll)) + bus->pollrate = 1; + +#ifdef BCMSDIOH_TXGLOM + /* Setting default Glom mode */ + bus->glom_mode = SDPCM_TXGLOM_CPY; + /* Setting default Glom size */ + bus->glomsize = SDPCM_DEFGLOM_SIZE; +#endif + + return TRUE; + +fail: + if (bus->sih != NULL) { + si_detach(bus->sih); + bus->sih = NULL; + } + return FALSE; +} + +static bool +dhdsdio_probe_malloc(dhd_bus_t *bus, osl_t *osh, void *sdh) +{ + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (bus->dhd->maxctl) { + bus->rxblen = ROUNDUP((bus->dhd->maxctl + SDPCM_HDRLEN), ALIGNMENT) + DHD_SDALIGN; + if (!(bus->rxbuf = DHD_OS_PREALLOC(osh, DHD_PREALLOC_RXBUF, bus->rxblen))) { + DHD_ERROR(("%s: MALLOC of %d-byte rxbuf failed\n", + __FUNCTION__, bus->rxblen)); + goto fail; + } + } + /* Allocate buffer to receive glomed packet */ + if (!(bus->databuf = DHD_OS_PREALLOC(osh, DHD_PREALLOC_DATABUF, MAX_DATA_BUF))) { + DHD_ERROR(("%s: MALLOC of %d-byte databuf failed\n", + __FUNCTION__, MAX_DATA_BUF)); + /* release rxbuf which was already located as above */ + if (!bus->rxblen) + DHD_OS_PREFREE(osh, bus->rxbuf, bus->rxblen); + goto fail; + } + + /* Align the buffer */ + if ((uintptr)bus->databuf % DHD_SDALIGN) + bus->dataptr = bus->databuf + (DHD_SDALIGN - ((uintptr)bus->databuf % DHD_SDALIGN)); + else + bus->dataptr = bus->databuf; + + return TRUE; + +fail: + return FALSE; +} + +static bool +dhdsdio_probe_init(dhd_bus_t *bus, osl_t *osh, void *sdh) +{ + int32 fnum; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + +#ifdef SDTEST + dhdsdio_pktgen_init(bus); +#endif /* SDTEST */ + + /* Disable F2 to clear any intermediate frame state on the dongle */ + bcmsdh_cfg_write(sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN, SDIO_FUNC_ENABLE_1, NULL); + + bus->dhd->busstate = DHD_BUS_DOWN; + bus->sleeping = FALSE; + bus->rxflow = FALSE; + bus->prev_rxlim_hit = 0; + + /* Done with backplane-dependent accesses, can drop clock... */ + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL); + + /* ...and initialize clock/power states */ + bus->clkstate = CLK_SDONLY; + bus->idletime = (int32)dhd_idletime; + bus->idleclock = DHD_IDLE_ACTIVE; + + /* Query the SD clock speed */ + if (bcmsdh_iovar_op(sdh, "sd_divisor", NULL, 0, + &bus->sd_divisor, sizeof(int32), FALSE) != BCME_OK) { + DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, "sd_divisor")); + bus->sd_divisor = -1; + } else { + DHD_INFO(("%s: Initial value for %s is %d\n", + __FUNCTION__, "sd_divisor", bus->sd_divisor)); + } + + /* Query the SD bus mode */ + if (bcmsdh_iovar_op(sdh, "sd_mode", NULL, 0, + &bus->sd_mode, sizeof(int32), FALSE) != BCME_OK) { + DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, "sd_mode")); + bus->sd_mode = -1; + } else { + DHD_INFO(("%s: Initial value for %s is %d\n", + __FUNCTION__, "sd_mode", bus->sd_mode)); + } + + /* Query the F2 block size, set roundup accordingly */ + fnum = 2; + if (bcmsdh_iovar_op(sdh, "sd_blocksize", &fnum, sizeof(int32), + &bus->blocksize, sizeof(int32), FALSE) != BCME_OK) { + bus->blocksize = 0; + DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, "sd_blocksize")); + } else { + DHD_INFO(("%s: Initial value for %s is %d\n", + __FUNCTION__, "sd_blocksize", bus->blocksize)); + } + bus->roundup = MIN(max_roundup, bus->blocksize); + + /* Query if bus module supports packet chaining, default to use if supported */ + if (bcmsdh_iovar_op(sdh, "sd_rxchain", NULL, 0, + &bus->sd_rxchain, sizeof(int32), FALSE) != BCME_OK) { + bus->sd_rxchain = FALSE; + } else { + DHD_INFO(("%s: bus module (through bcmsdh API) %s chaining\n", + __FUNCTION__, (bus->sd_rxchain ? "supports" : "does not support"))); + } + bus->use_rxchain = (bool)bus->sd_rxchain; + + return TRUE; +} + +bool +dhd_bus_download_firmware(struct dhd_bus *bus, osl_t *osh, + char *pfw_path, char *pnv_path) +{ + bool ret; + bus->fw_path = pfw_path; + bus->nv_path = pnv_path; + + ret = dhdsdio_download_firmware(bus, osh, bus->sdh); + + + return ret; +} + +static bool +dhdsdio_download_firmware(struct dhd_bus *bus, osl_t *osh, void *sdh) +{ + bool ret; + + DHD_OS_WAKE_LOCK(bus->dhd); + + /* Download the firmware */ + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + + ret = _dhdsdio_download_firmware(bus) == 0; + + dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); + + DHD_OS_WAKE_UNLOCK(bus->dhd); + return ret; +} + +/* Detach and free everything */ +static void +dhdsdio_release(dhd_bus_t *bus, osl_t *osh) +{ + bool dongle_isolation = FALSE; + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (bus) { + ASSERT(osh); + + if (bus->dhd) { + dongle_isolation = bus->dhd->dongle_isolation; + dhd_detach(bus->dhd); + } + + /* De-register interrupt handler */ + bcmsdh_intr_disable(bus->sdh); + bcmsdh_intr_dereg(bus->sdh); + + if (bus->dhd) { + dhdsdio_release_dongle(bus, osh, dongle_isolation, TRUE); + dhd_free(bus->dhd); + bus->dhd = NULL; + } + + dhdsdio_release_malloc(bus, osh); + +#ifdef DHD_DEBUG + if (bus->console.buf != NULL) + MFREE(osh, bus->console.buf, bus->console.bufsize); +#endif + + MFREE(osh, bus, sizeof(dhd_bus_t)); + } + + if (osh) + dhd_osl_detach(osh); + + DHD_TRACE(("%s: Disconnected\n", __FUNCTION__)); +} + +static void +dhdsdio_release_malloc(dhd_bus_t *bus, osl_t *osh) +{ + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (bus->dhd && bus->dhd->dongle_reset) + return; + + if (bus->rxbuf) { +#ifndef CONFIG_DHD_USE_STATIC_BUF + MFREE(osh, bus->rxbuf, bus->rxblen); +#endif + bus->rxctl = bus->rxbuf = NULL; + bus->rxlen = 0; + } + + if (bus->databuf) { +#ifndef CONFIG_DHD_USE_STATIC_BUF + MFREE(osh, bus->databuf, MAX_DATA_BUF); +#endif + bus->databuf = NULL; + } + + if (bus->vars && bus->varsz) { + MFREE(osh, bus->vars, bus->varsz); + bus->vars = NULL; + } + +} + + +static void +dhdsdio_release_dongle(dhd_bus_t *bus, osl_t *osh, bool dongle_isolation, bool reset_flag) +{ + DHD_TRACE(("%s: Enter bus->dhd %p bus->dhd->dongle_reset %d \n", __FUNCTION__, + bus->dhd, bus->dhd->dongle_reset)); + + if ((bus->dhd && bus->dhd->dongle_reset) && reset_flag) + return; + + if (bus->sih) { +#if !defined(BCMLXSDMMC) + if (bus->dhd) { + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + } + if (KSO_ENAB(bus) && (dongle_isolation == FALSE)) + si_watchdog(bus->sih, 4); +#endif /* !defined(BCMLXSDMMC) */ + if (bus->dhd) { + dhdsdio_clkctl(bus, CLK_NONE, FALSE); + } + si_detach(bus->sih); + bus->sih = NULL; + if (bus->vars && bus->varsz) + MFREE(osh, bus->vars, bus->varsz); + bus->vars = NULL; + } + + DHD_TRACE(("%s: Disconnected\n", __FUNCTION__)); +} + +static void +dhdsdio_disconnect(void *ptr) +{ + dhd_bus_t *bus = (dhd_bus_t *)ptr; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + + if (mutex_is_locked(&_dhd_sdio_mutex_lock_) == 0) { + DHD_ERROR(("%s : no mutex held. set lock\n", __FUNCTION__)); + } + else { + DHD_ERROR(("%s : mutex is locked!. wait for unlocking\n", __FUNCTION__)); + } + mutex_lock(&_dhd_sdio_mutex_lock_); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (bus) { + ASSERT(bus->dhd); + dhdsdio_release(bus, bus->dhd->osh); + } + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + mutex_unlock(&_dhd_sdio_mutex_lock_); + DHD_ERROR(("%s : the lock is released.\n", __FUNCTION__)); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ + + DHD_TRACE(("%s: Disconnected\n", __FUNCTION__)); +} + + +/* Register/Unregister functions are called by the main DHD entry + * point (e.g. module insertion) to link with the bus driver, in + * order to look for or await the device. + */ + +static bcmsdh_driver_t dhd_sdio = { + dhdsdio_probe, + dhdsdio_disconnect +}; + +int +dhd_bus_register(void) +{ + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + return bcmsdh_register(&dhd_sdio); +} + +void +dhd_bus_unregister(void) +{ + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + bcmsdh_unregister(); +} + +#if defined(BCMLXSDMMC) +/* Register a dummy SDIO client driver in order to be notified of new SDIO device */ +int dhd_bus_reg_sdio_notify(void* semaphore) +{ + return bcmsdh_reg_sdio_notify(semaphore); +} + +void dhd_bus_unreg_sdio_notify(void) +{ + bcmsdh_unreg_sdio_notify(); +} +#endif /* defined(BCMLXSDMMC) */ + +#ifdef BCMEMBEDIMAGE +static int +dhdsdio_download_code_array(struct dhd_bus *bus) +{ + int bcmerror = -1; + int offset = 0; + unsigned char *ularray = NULL; + + DHD_INFO(("%s: download embedded firmware...\n", __FUNCTION__)); + + /* Download image */ + while ((offset + MEMBLOCK) < sizeof(dlarray)) { + bcmerror = dhdsdio_membytes(bus, TRUE, offset, + (uint8 *) (dlarray + offset), MEMBLOCK); + if (bcmerror) { + DHD_ERROR(("%s: error %d on writing %d membytes at 0x%08x\n", + __FUNCTION__, bcmerror, MEMBLOCK, offset)); + goto err; + } + + offset += MEMBLOCK; + } + + if (offset < sizeof(dlarray)) { + bcmerror = dhdsdio_membytes(bus, TRUE, offset, + (uint8 *) (dlarray + offset), sizeof(dlarray) - offset); + if (bcmerror) { + DHD_ERROR(("%s: error %d on writing %d membytes at 0x%08x\n", + __FUNCTION__, bcmerror, sizeof(dlarray) - offset, offset)); + goto err; + } + } + +#ifdef DHD_DEBUG + /* Upload and compare the downloaded code */ + { + ularray = MALLOC(bus->dhd->osh, bus->ramsize); + /* Upload image to verify downloaded contents. */ + offset = 0; + memset(ularray, 0xaa, bus->ramsize); + while ((offset + MEMBLOCK) < sizeof(dlarray)) { + bcmerror = dhdsdio_membytes(bus, FALSE, offset, ularray + offset, MEMBLOCK); + if (bcmerror) { + DHD_ERROR(("%s: error %d on reading %d membytes at 0x%08x\n", + __FUNCTION__, bcmerror, MEMBLOCK, offset)); + goto err; + } + + offset += MEMBLOCK; + } + + if (offset < sizeof(dlarray)) { + bcmerror = dhdsdio_membytes(bus, FALSE, offset, + ularray + offset, sizeof(dlarray) - offset); + if (bcmerror) { + DHD_ERROR(("%s: error %d on reading %d membytes at 0x%08x\n", + __FUNCTION__, bcmerror, sizeof(dlarray) - offset, offset)); + goto err; + } + } + + if (memcmp(dlarray, ularray, sizeof(dlarray))) { + DHD_ERROR(("%s: Downloaded image is corrupted (%s, %s, %s).\n", + __FUNCTION__, dlimagename, dlimagever, dlimagedate)); + goto err; + } else + DHD_ERROR(("%s: Download, Upload and compare succeeded (%s, %s, %s).\n", + __FUNCTION__, dlimagename, dlimagever, dlimagedate)); + + } +#endif /* DHD_DEBUG */ + +err: + if (ularray) + MFREE(bus->dhd->osh, ularray, bus->ramsize); + return bcmerror; +} +#endif /* BCMEMBEDIMAGE */ + +static int +dhdsdio_download_code_file(struct dhd_bus *bus, char *pfw_path) +{ + int bcmerror = -1; + int offset = 0; + int len; + void *image = NULL; + uint8 *memblock = NULL, *memptr; + + DHD_INFO(("%s: download firmware %s\n", __FUNCTION__, pfw_path)); + + image = dhd_os_open_image(pfw_path); + if (image == NULL) + goto err; + + memptr = memblock = MALLOC(bus->dhd->osh, MEMBLOCK + DHD_SDALIGN); + if (memblock == NULL) { + DHD_ERROR(("%s: Failed to allocate memory %d bytes\n", __FUNCTION__, MEMBLOCK)); + goto err; + } + if ((uint32)(uintptr)memblock % DHD_SDALIGN) + memptr += (DHD_SDALIGN - ((uint32)(uintptr)memblock % DHD_SDALIGN)); + + /* Download image */ + while ((len = dhd_os_get_image_block((char*)memptr, MEMBLOCK, image))) { + if (len < 0) { + DHD_ERROR(("%s: dhd_os_get_image_block failed (%d)\n", __FUNCTION__, len)); + bcmerror = BCME_ERROR; + goto err; + } + bcmerror = dhdsdio_membytes(bus, TRUE, offset, memptr, len); + if (bcmerror) { + DHD_ERROR(("%s: error %d on writing %d membytes at 0x%08x\n", + __FUNCTION__, bcmerror, MEMBLOCK, offset)); + goto err; + } + + offset += MEMBLOCK; + } + +err: + if (memblock) + MFREE(bus->dhd->osh, memblock, MEMBLOCK + DHD_SDALIGN); + + if (image) + dhd_os_close_image(image); + + return bcmerror; +} + +/* + EXAMPLE: nvram_array + nvram_arry format: + name=value + Use carriage return at the end of each assignment, and an empty string with + carriage return at the end of array. + + For example: + unsigned char nvram_array[] = {"name1=value1\n", "name2=value2\n", "\n"}; + Hex values start with 0x, and mac addr format: xx:xx:xx:xx:xx:xx. + + Search "EXAMPLE: nvram_array" to see how the array is activated. +*/ + +void +dhd_bus_set_nvram_params(struct dhd_bus * bus, const char *nvram_params) +{ + bus->nvram_params = nvram_params; +} + +static int +dhdsdio_download_nvram(struct dhd_bus *bus) +{ + int bcmerror = -1; + uint len; + void * image = NULL; + char * memblock = NULL; + char *bufp; + char *pnv_path; + bool nvram_file_exists; + + pnv_path = bus->nv_path; + + nvram_file_exists = ((pnv_path != NULL) && (pnv_path[0] != '\0')); + if (!nvram_file_exists && (bus->nvram_params == NULL)) + return (0); + + if (nvram_file_exists) { + image = dhd_os_open_image(pnv_path); + if (image == NULL) + goto err; + } + + memblock = MALLOC(bus->dhd->osh, MAX_NVRAMBUF_SIZE); + if (memblock == NULL) { + DHD_ERROR(("%s: Failed to allocate memory %d bytes\n", + __FUNCTION__, MAX_NVRAMBUF_SIZE)); + goto err; + } + + /* Download variables */ + if (nvram_file_exists) { + len = dhd_os_get_image_block(memblock, MAX_NVRAMBUF_SIZE, image); + } + else { + len = strlen(bus->nvram_params); + ASSERT(len <= MAX_NVRAMBUF_SIZE); + memcpy(memblock, bus->nvram_params, len); + } + if (len > 0 && len < MAX_NVRAMBUF_SIZE) { + bufp = (char *)memblock; + bufp[len] = 0; + len = process_nvram_vars(bufp, len); + if (len % 4) { + len += 4 - (len % 4); + } + bufp += len; + *bufp++ = 0; + if (len) + bcmerror = dhdsdio_downloadvars(bus, memblock, len + 1); + if (bcmerror) { + DHD_ERROR(("%s: error downloading vars: %d\n", + __FUNCTION__, bcmerror)); + } + } + else { + DHD_ERROR(("%s: error reading nvram file: %d\n", + __FUNCTION__, len)); + bcmerror = BCME_SDIO_ERROR; + } + +err: + if (memblock) + MFREE(bus->dhd->osh, memblock, MAX_NVRAMBUF_SIZE); + + if (image) + dhd_os_close_image(image); + + return bcmerror; +} + +static int +_dhdsdio_download_firmware(struct dhd_bus *bus) +{ + int bcmerror = -1; + + bool embed = FALSE; /* download embedded firmware */ + bool dlok = FALSE; /* download firmware succeeded */ + + /* Out immediately if no image to download */ + if ((bus->fw_path == NULL) || (bus->fw_path[0] == '\0')) { +#ifdef BCMEMBEDIMAGE + embed = TRUE; +#else + return 0; +#endif + } + + /* Keep arm in reset */ + if (dhdsdio_download_state(bus, TRUE)) { + DHD_ERROR(("%s: error placing ARM core in reset\n", __FUNCTION__)); + goto err; + } + + /* External image takes precedence if specified */ + if ((bus->fw_path != NULL) && (bus->fw_path[0] != '\0')) { + if (dhdsdio_download_code_file(bus, bus->fw_path)) { + DHD_ERROR(("%s: dongle image file download failed\n", __FUNCTION__)); +#ifdef BCMEMBEDIMAGE + embed = TRUE; +#else + goto err; +#endif + } + else { + embed = FALSE; + dlok = TRUE; + } + } +#ifdef BCMEMBEDIMAGE + if (embed) { + if (dhdsdio_download_code_array(bus)) { + DHD_ERROR(("%s: dongle image array download failed\n", __FUNCTION__)); + goto err; + } + else { + dlok = TRUE; + } + } +#else + BCM_REFERENCE(embed); +#endif + if (!dlok) { + DHD_ERROR(("%s: dongle image download failed\n", __FUNCTION__)); + goto err; + } + + /* EXAMPLE: nvram_array */ + /* If a valid nvram_arry is specified as above, it can be passed down to dongle */ + /* dhd_bus_set_nvram_params(bus, (char *)&nvram_array); */ + + /* External nvram takes precedence if specified */ + if (dhdsdio_download_nvram(bus)) { + DHD_ERROR(("%s: dongle nvram file download failed\n", __FUNCTION__)); + goto err; + } + + /* Take arm out of reset */ + if (dhdsdio_download_state(bus, FALSE)) { + DHD_ERROR(("%s: error getting out of ARM core reset\n", __FUNCTION__)); + goto err; + } + + bcmerror = 0; + +err: + return bcmerror; +} + +static int +dhd_bcmsdh_recv_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags, uint8 *buf, uint nbytes, + void *pkt, bcmsdh_cmplt_fn_t complete, void *handle) +{ + int status; + + if (!KSO_ENAB(bus)) { + DHD_ERROR(("%s: Device asleep\n", __FUNCTION__)); + return BCME_NODEVICE; + } + + status = bcmsdh_recv_buf(bus->sdh, addr, fn, flags, buf, nbytes, pkt, complete, handle); + + return status; +} + +static int +dhd_bcmsdh_send_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags, uint8 *buf, uint nbytes, + void *pkt, bcmsdh_cmplt_fn_t complete, void *handle) +{ + if (!KSO_ENAB(bus)) { + DHD_ERROR(("%s: Device asleep\n", __FUNCTION__)); + return BCME_NODEVICE; + } + + return (bcmsdh_send_buf(bus->sdh, addr, fn, flags, buf, nbytes, pkt, complete, handle)); +} + +#ifdef BCMSDIOH_TXGLOM +static void +dhd_bcmsdh_glom_post(dhd_bus_t *bus, uint8 *frame, uint len) +{ + bcmsdh_glom_post(bus->sdh, frame, len); +} + +static void +dhd_bcmsdh_glom_clear(dhd_bus_t *bus) +{ + bcmsdh_glom_clear(bus->sdh); +} +#endif + +uint +dhd_bus_chip(struct dhd_bus *bus) +{ + ASSERT(bus->sih != NULL); + return bus->sih->chip; +} + +void * +dhd_bus_pub(struct dhd_bus *bus) +{ + return bus->dhd; +} + +void * +dhd_bus_txq(struct dhd_bus *bus) +{ + return &bus->txq; +} + +uint +dhd_bus_hdrlen(struct dhd_bus *bus) +{ + return SDPCM_HDRLEN; +} + +int +dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag) +{ + int bcmerror = 0; + dhd_bus_t *bus; + + bus = dhdp->bus; + + if (flag == TRUE) { + if (!bus->dhd->dongle_reset) { + dhd_os_sdlock(dhdp); + dhd_os_wd_timer(dhdp, 0); +#if !defined(IGNORE_ETH0_DOWN) + /* Force flow control as protection when stop come before ifconfig_down */ + dhd_txflowcontrol(bus->dhd, ALL_INTERFACES, ON); +#endif /* !defined(IGNORE_ETH0_DOWN) */ + /* Expect app to have torn down any connection before calling */ + /* Stop the bus, disable F2 */ + dhd_bus_stop(bus, FALSE); + +#if defined(OOB_INTR_ONLY) + /* Clean up any pending IRQ */ + bcmsdh_set_irq(FALSE); +#endif /* defined(OOB_INTR_ONLY) */ + + /* Clean tx/rx buffer pointers, detach from the dongle */ + dhdsdio_release_dongle(bus, bus->dhd->osh, TRUE, TRUE); + + bus->dhd->dongle_reset = TRUE; + bus->dhd->up = FALSE; +#ifdef BCMSDIOH_TXGLOM + dhd_txglom_enable(dhdp, FALSE); +#endif + dhd_os_sdunlock(dhdp); + + DHD_TRACE(("%s: WLAN OFF DONE\n", __FUNCTION__)); + /* App can now remove power from device */ + } else + bcmerror = BCME_SDIO_ERROR; + } else { + /* App must have restored power to device before calling */ + + DHD_TRACE(("\n\n%s: == WLAN ON ==\n", __FUNCTION__)); + + if (bus->dhd->dongle_reset) { + /* Turn on WLAN */ +#ifdef DHDTHREAD + dhd_os_sdlock(dhdp); +#endif /* DHDTHREAD */ + /* Reset SD client */ + bcmsdh_reset(bus->sdh); + + /* Attempt to re-attach & download */ + if (dhdsdio_probe_attach(bus, bus->dhd->osh, bus->sdh, + (uint32 *)SI_ENUM_BASE, + bus->cl_devid)) { + /* Attempt to download binary to the dongle */ + if (dhdsdio_probe_init(bus, bus->dhd->osh, bus->sdh) && + dhdsdio_download_firmware(bus, bus->dhd->osh, bus->sdh)) { + + /* Re-init bus, enable F2 transfer */ + bcmerror = dhd_bus_init((dhd_pub_t *) bus->dhd, FALSE); + if (bcmerror == BCME_OK) { +#if defined(OOB_INTR_ONLY) + bcmsdh_set_irq(TRUE); + dhd_enable_oob_intr(bus, TRUE); +#endif /* defined(OOB_INTR_ONLY) */ + + bus->dhd->dongle_reset = FALSE; + bus->dhd->up = TRUE; + +#if !defined(IGNORE_ETH0_DOWN) + /* Restore flow control */ + dhd_txflowcontrol(bus->dhd, ALL_INTERFACES, OFF); +#endif + dhd_os_wd_timer(dhdp, dhd_watchdog_ms); +#ifdef BCMSDIOH_TXGLOM + if ((dhdp->busstate == DHD_BUS_DATA) && + bcmsdh_glom_enabled()) { + dhd_txglom_enable(dhdp, TRUE); + } +#endif /* BCMSDIOH_TXGLOM */ + DHD_TRACE(("%s: WLAN ON DONE\n", __FUNCTION__)); + } else { + dhd_bus_stop(bus, FALSE); + dhdsdio_release_dongle(bus, bus->dhd->osh, + TRUE, FALSE); + } + } else + bcmerror = BCME_SDIO_ERROR; + } else + bcmerror = BCME_SDIO_ERROR; + +#ifdef DHDTHREAD + dhd_os_sdunlock(dhdp); +#endif /* DHDTHREAD */ + } else { + bcmerror = BCME_SDIO_ERROR; + DHD_INFO(("%s called when dongle is not in reset\n", + __FUNCTION__)); + DHD_INFO(("Will call dhd_bus_start instead\n")); + sdioh_start(NULL, 1); + if ((bcmerror = dhd_bus_start(dhdp)) != 0) + DHD_ERROR(("%s: dhd_bus_start fail with %d\n", + __FUNCTION__, bcmerror)); + } + } + return bcmerror; +} + +/* Get Chip ID version */ +uint dhd_bus_chip_id(dhd_pub_t *dhdp) +{ + dhd_bus_t *bus = dhdp->bus; + + return bus->sih->chip; +} + +/* Get Chip Rev ID version */ +uint dhd_bus_chiprev_id(dhd_pub_t *dhdp) +{ + dhd_bus_t *bus = dhdp->bus; + + return bus->sih->chiprev; +} + +/* Get Chip Pkg ID version */ +uint dhd_bus_chippkg_id(dhd_pub_t *dhdp) +{ + dhd_bus_t *bus = dhdp->bus; + + return bus->sih->chippkg; +} + +int +dhd_bus_membytes(dhd_pub_t *dhdp, bool set, uint32 address, uint8 *data, uint size) +{ + dhd_bus_t *bus; + + bus = dhdp->bus; + return dhdsdio_membytes(bus, set, address, data, size); +} diff --git a/drivers/net/wireless/bcmdhd/dhd_wlfc.h b/drivers/net/wireless/bcmdhd/dhd_wlfc.h new file mode 100644 index 00000000..5a64e6fd --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_wlfc.h @@ -0,0 +1,287 @@ +/* +* Copyright (C) 1999-2012, Broadcom Corporation +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed to you +* under the terms of the GNU General Public License version 2 (the "GPL"), +* available at http://www.broadcom.com/licenses/GPLv2.php, with the +* following added to such license: +* +* As a special exception, the copyright holders of this software give you +* permission to link this software with independent modules, and to copy and +* distribute the resulting executable under terms of your choice, provided that +* you also meet, for each linked independent module, the terms and conditions of +* the license of that module. An independent module is a module which is not +* derived from this software. The special exception does not apply to any +* modifications of the software. +* +* Notwithstanding the above, under no circumstances may you combine this +* software in any way with any other Broadcom software provided under a license +* other than the GPL, without Broadcom's express prior written consent. +* $Id: dhd_wlfc.h 361006 2012-10-05 07:45:51Z $ +* +*/ +#ifndef __wlfc_host_driver_definitions_h__ +#define __wlfc_host_driver_definitions_h__ + +/* 16 bits will provide an absolute max of 65536 slots */ +#define WLFC_HANGER_MAXITEMS 1024 + +#define WLFC_HANGER_ITEM_STATE_FREE 1 +#define WLFC_HANGER_ITEM_STATE_INUSE 2 +#define WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED 3 +#define WLFC_PKTID_HSLOT_MASK 0xffff /* allow 16 bits only */ +#define WLFC_PKTID_HSLOT_SHIFT 8 + +/* x -> TXSTATUS TAG to/from firmware */ +#define WLFC_PKTID_HSLOT_GET(x) \ + (((x) >> WLFC_PKTID_HSLOT_SHIFT) & WLFC_PKTID_HSLOT_MASK) +#define WLFC_PKTID_HSLOT_SET(var, slot) \ + ((var) = ((var) & ~(WLFC_PKTID_HSLOT_MASK << WLFC_PKTID_HSLOT_SHIFT)) | \ + (((slot) & WLFC_PKTID_HSLOT_MASK) << WLFC_PKTID_HSLOT_SHIFT)) + +#define WLFC_PKTID_FREERUNCTR_MASK 0xff + +#define WLFC_PKTID_FREERUNCTR_GET(x) ((x) & WLFC_PKTID_FREERUNCTR_MASK) +#define WLFC_PKTID_FREERUNCTR_SET(var, ctr) \ + ((var) = (((var) & ~WLFC_PKTID_FREERUNCTR_MASK) | \ + (((ctr) & WLFC_PKTID_FREERUNCTR_MASK)))) + +#define WLFC_PKTQ_PENQ(pq, prec, p) ((pktq_full((pq)) || pktq_pfull((pq), (prec)))? \ + NULL : pktq_penq((pq), (prec), (p))) +#define WLFC_PKTQ_PENQ_HEAD(pq, prec, p) ((pktq_full((pq)) || pktq_pfull((pq), (prec))) ? \ + NULL : pktq_penq_head((pq), (prec), (p))) + +typedef enum ewlfc_packet_state { + eWLFC_PKTTYPE_NEW, + eWLFC_PKTTYPE_DELAYED, + eWLFC_PKTTYPE_SUPPRESSED, + eWLFC_PKTTYPE_MAX +} ewlfc_packet_state_t; + +typedef enum ewlfc_mac_entry_action { + eWLFC_MAC_ENTRY_ACTION_ADD, + eWLFC_MAC_ENTRY_ACTION_DEL, + eWLFC_MAC_ENTRY_ACTION_UPDATE, + eWLFC_MAC_ENTRY_ACTION_MAX +} ewlfc_mac_entry_action_t; + +typedef struct wlfc_hanger_item { + uint8 state; + uint8 gen; + uint8 pad[2]; + uint32 identifier; + void* pkt; +#ifdef PROP_TXSTATUS_DEBUG + uint32 push_time; +#endif +} wlfc_hanger_item_t; + +typedef struct wlfc_hanger { + int max_items; + uint32 pushed; + uint32 popped; + uint32 failed_to_push; + uint32 failed_to_pop; + uint32 failed_slotfind; + wlfc_hanger_item_t items[1]; + uint32 slot_pos; +} wlfc_hanger_t; + +#define WLFC_HANGER_SIZE(n) ((sizeof(wlfc_hanger_t) - \ + sizeof(wlfc_hanger_item_t)) + ((n)*sizeof(wlfc_hanger_item_t))) + +#define WLFC_STATE_OPEN 1 +#define WLFC_STATE_CLOSE 2 + +#define WLFC_PSQ_PREC_COUNT ((AC_COUNT + 1) * 2) /* 2 for each AC traffic and bc/mc */ + +#define WLFC_PSQ_LEN 2048 + +#define WLFC_SENDQ_LEN 256 + + +#define WLFC_FLOWCONTROL_HIWATER (2048 - 256) +#define WLFC_FLOWCONTROL_LOWATER 256 + + +typedef struct wlfc_mac_descriptor { + uint8 occupied; + uint8 interface_id; + uint8 iftype; + uint8 state; + uint8 ac_bitmap; /* for APSD */ + uint8 requested_credit; + uint8 requested_packet; + uint8 ea[ETHER_ADDR_LEN]; + /* + maintain (MAC,AC) based seq count for + packets going to the device. As well as bc/mc. + */ + uint8 seq[AC_COUNT + 1]; + uint8 generation; + struct pktq psq; + /* The AC pending bitmap that was reported to the fw at last change */ + uint8 traffic_lastreported_bmp; + /* The new AC pending bitmap */ + uint8 traffic_pending_bmp; + /* 1= send on next opportunity */ + uint8 send_tim_signal; + uint8 mac_handle; + uint transit_count; + uint suppr_transit_count; + uint suppress_count; + uint8 suppressed; + +#ifdef PROP_TXSTATUS_DEBUG + uint32 dstncredit_sent_packets; + uint32 dstncredit_acks; + uint32 opened_ct; + uint32 closed_ct; +#endif +} wlfc_mac_descriptor_t; + +#define WLFC_DECR_SEQCOUNT(entry, prec) do { if (entry->seq[(prec)] == 0) {\ + entry->seq[prec] = 0xff; } else entry->seq[prec]--;} while (0) + +#define WLFC_INCR_SEQCOUNT(entry, prec) entry->seq[(prec)]++ +#define WLFC_SEQCOUNT(entry, prec) entry->seq[(prec)] + +typedef struct athost_wl_stat_counters { + uint32 pktin; + uint32 pkt2bus; + uint32 pktdropped; + uint32 tlv_parse_failed; + uint32 rollback; + uint32 rollback_failed; + uint32 sendq_full_error; + uint32 delayq_full_error; + uint32 credit_request_failed; + uint32 packet_request_failed; + uint32 mac_update_failed; + uint32 psmode_update_failed; + uint32 interface_update_failed; + uint32 wlfc_header_only_pkt; + uint32 txstatus_in; + uint32 d11_suppress; + uint32 wl_suppress; + uint32 bad_suppress; + uint32 pkt_freed; + uint32 pkt_free_err; + uint32 psq_wlsup_retx; + uint32 psq_wlsup_enq; + uint32 psq_d11sup_retx; + uint32 psq_d11sup_enq; + uint32 psq_hostq_retx; + uint32 psq_hostq_enq; + uint32 mac_handle_notfound; + uint32 wlc_tossed_pkts; + uint32 dhd_hdrpulls; + uint32 generic_error; + /* an extra one for bc/mc traffic */ + uint32 sendq_pkts[AC_COUNT + 1]; +#ifdef PROP_TXSTATUS_DEBUG + /* all pkt2bus -> txstatus latency accumulated */ + uint32 latency_sample_count; + uint32 total_status_latency; + uint32 latency_most_recent; + int idx_delta; + uint32 deltas[10]; + uint32 fifo_credits_sent[6]; + uint32 fifo_credits_back[6]; + uint32 dropped_qfull[6]; + uint32 signal_only_pkts_sent; + uint32 signal_only_pkts_freed; +#endif +} athost_wl_stat_counters_t; + +#ifdef PROP_TXSTATUS_DEBUG +#define WLFC_HOST_FIFO_CREDIT_INC_SENTCTRS(ctx, ac) do { \ + (ctx)->stats.fifo_credits_sent[(ac)]++;} while (0) +#define WLFC_HOST_FIFO_CREDIT_INC_BACKCTRS(ctx, ac) do { \ + (ctx)->stats.fifo_credits_back[(ac)]++;} while (0) +#define WLFC_HOST_FIFO_DROPPEDCTR_INC(ctx, ac) do { \ + (ctx)->stats.dropped_qfull[(ac)]++;} while (0) +#else +#define WLFC_HOST_FIFO_CREDIT_INC_SENTCTRS(ctx, ac) do {} while (0) +#define WLFC_HOST_FIFO_CREDIT_INC_BACKCTRS(ctx, ac) do {} while (0) +#define WLFC_HOST_FIFO_DROPPEDCTR_INC(ctx, ac) do {} while (0) +#endif + +#define WLFC_FCMODE_NONE 0 +#define WLFC_FCMODE_IMPLIED_CREDIT 1 +#define WLFC_FCMODE_EXPLICIT_CREDIT 2 + +/* How long to defer borrowing in milliseconds */ +#define WLFC_BORROW_DEFER_PERIOD_MS 100 + +/* Mask to represent available ACs (note: BC/MC is ignored */ +#define WLFC_AC_MASK 0xF + +/* Mask to check for only on-going AC_BE traffic */ +#define WLFC_AC_BE_TRAFFIC_ONLY 0xD + +typedef struct athost_wl_status_info { + uint8 last_seqid_to_wlc; + + /* OSL handle */ + osl_t* osh; + /* dhd pub */ + void* dhdp; + + /* stats */ + athost_wl_stat_counters_t stats; + + /* the additional ones are for bc/mc and ATIM FIFO */ + int FIFO_credit[AC_COUNT + 2]; + + /* Credit borrow counts for each FIFO from each of the other FIFOs */ + int credits_borrowed[AC_COUNT + 2][AC_COUNT + 2]; + + struct pktq SENDQ; + + /* packet hanger and MAC->handle lookup table */ + void* hanger; + struct { + /* table for individual nodes */ + wlfc_mac_descriptor_t nodes[WLFC_MAC_DESC_TABLE_SIZE]; + /* table for interfaces */ + wlfc_mac_descriptor_t interfaces[WLFC_MAX_IFNUM]; + /* OS may send packets to unknown (unassociated) destinations */ + /* A place holder for bc/mc and packets to unknown destinations */ + wlfc_mac_descriptor_t other; + } destination_entries; + /* token position for different priority packets */ + uint8 token_pos[AC_COUNT+1]; + /* ON/OFF state for flow control to the host network interface */ + uint8 hostif_flow_state[WLFC_MAX_IFNUM]; + uint8 host_ifidx; + /* to flow control an OS interface */ + uint8 toggle_host_if; + + /* + Mode in which the dhd flow control shall operate. Must be set before + traffic starts to the device. + 0 - Do not do any proptxtstatus flow control + 1 - Use implied credit from a packet status + 2 - Use explicit credit + */ + uint8 proptxstatus_mode; + + /* To borrow credits */ + uint8 allow_credit_borrow; + + /* Timestamp to compute how long to defer borrowing for */ + uint32 borrow_defer_timestamp; + +} athost_wl_status_info_t; + +int dhd_wlfc_enable(dhd_pub_t *dhd); +int dhd_wlfc_interface_event(struct dhd_info *, + ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea); +int dhd_wlfc_FIFOcreditmap_event(struct dhd_info *dhd, uint8* event_data); +int dhd_wlfc_event(struct dhd_info *dhd); +int dhd_os_wlfc_block(dhd_pub_t *pub); +int dhd_os_wlfc_unblock(dhd_pub_t *pub); + +#endif /* __wlfc_host_driver_definitions_h__ */ diff --git a/drivers/net/wireless/bcmdhd/dngl_stats.h b/drivers/net/wireless/bcmdhd/dngl_stats.h new file mode 100644 index 00000000..5e5a2e2e --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dngl_stats.h @@ -0,0 +1,43 @@ +/* + * Common stats definitions for clients of dongle + * ports + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dngl_stats.h 241182 2011-02-17 21:50:03Z $ + */ + +#ifndef _dngl_stats_h_ +#define _dngl_stats_h_ + +typedef struct { + unsigned long rx_packets; /* total packets received */ + unsigned long tx_packets; /* total packets transmitted */ + unsigned long rx_bytes; /* total bytes received */ + unsigned long tx_bytes; /* total bytes transmitted */ + unsigned long rx_errors; /* bad packets received */ + unsigned long tx_errors; /* packet transmit problems */ + unsigned long rx_dropped; /* packets dropped by dongle */ + unsigned long tx_dropped; /* packets dropped by dongle */ + unsigned long multicast; /* multicast packets received */ +} dngl_stats_t; + +#endif /* _dngl_stats_h_ */ diff --git a/drivers/net/wireless/bcmdhd/dngl_wlhdr.h b/drivers/net/wireless/bcmdhd/dngl_wlhdr.h new file mode 100644 index 00000000..0e37df6e --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dngl_wlhdr.h @@ -0,0 +1,40 @@ +/* + * Dongle WL Header definitions + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dngl_wlhdr.h 241182 2011-02-17 21:50:03Z $ + */ + +#ifndef _dngl_wlhdr_h_ +#define _dngl_wlhdr_h_ + +typedef struct wl_header { + uint8 type; /* Header type */ + uint8 version; /* Header version */ + int8 rssi; /* RSSI */ + uint8 pad; /* Unused */ +} wl_header_t; + +#define WL_HEADER_LEN sizeof(wl_header_t) +#define WL_HEADER_TYPE 0 +#define WL_HEADER_VER 1 +#endif /* _dngl_wlhdr_h_ */ diff --git a/drivers/net/wireless/bcmdhd/hndpmu.c b/drivers/net/wireless/bcmdhd/hndpmu.c new file mode 100644 index 00000000..e639015b --- /dev/null +++ b/drivers/net/wireless/bcmdhd/hndpmu.c @@ -0,0 +1,208 @@ +/* + * Misc utility routines for accessing PMU corerev specific features + * of the SiliconBackplane-based Broadcom chips. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: hndpmu.c 354194 2012-08-30 08:39:03Z $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PMU_ERROR(args) + +#define PMU_MSG(args) + +/* To check in verbose debugging messages not intended + * to be on except on private builds. + */ +#define PMU_NONE(args) + + +/* SDIO Pad drive strength to select value mappings. + * The last strength value in each table must be 0 (the tri-state value). + */ +typedef struct { + uint8 strength; /* Pad Drive Strength in mA */ + uint8 sel; /* Chip-specific select value */ +} sdiod_drive_str_t; + +/* SDIO Drive Strength to sel value table for PMU Rev 1 */ +static const sdiod_drive_str_t sdiod_drive_strength_tab1[] = { + {4, 0x2}, + {2, 0x3}, + {1, 0x0}, + {0, 0x0} }; + +/* SDIO Drive Strength to sel value table for PMU Rev 2, 3 */ +static const sdiod_drive_str_t sdiod_drive_strength_tab2[] = { + {12, 0x7}, + {10, 0x6}, + {8, 0x5}, + {6, 0x4}, + {4, 0x2}, + {2, 0x1}, + {0, 0x0} }; + +/* SDIO Drive Strength to sel value table for PMU Rev 8 (1.8V) */ +static const sdiod_drive_str_t sdiod_drive_strength_tab3[] = { + {32, 0x7}, + {26, 0x6}, + {22, 0x5}, + {16, 0x4}, + {12, 0x3}, + {8, 0x2}, + {4, 0x1}, + {0, 0x0} }; + +/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8v) */ +static const sdiod_drive_str_t sdiod_drive_strength_tab4_1v8[] = { + {32, 0x6}, + {26, 0x7}, + {22, 0x4}, + {16, 0x5}, + {12, 0x2}, + {8, 0x3}, + {4, 0x0}, + {0, 0x1} }; + +/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.2v) */ + +/* SDIO Drive Strength to sel value table for PMU Rev 11 (2.5v) */ + +/* SDIO Drive Strength to sel value table for PMU Rev 13 (1.8v) */ +static const sdiod_drive_str_t sdiod_drive_strength_tab5_1v8[] = { + {6, 0x7}, + {5, 0x6}, + {4, 0x5}, + {3, 0x4}, + {2, 0x2}, + {1, 0x1}, + {0, 0x0} }; + +/* SDIO Drive Strength to sel value table for PMU Rev 13 (3.3v) */ + +/* SDIO Drive Strength to sel value table for PMU Rev 17 (1.8v) */ +static const sdiod_drive_str_t sdiod_drive_strength_tab6_1v8[] = { + {3, 0x3}, + {2, 0x2}, + {1, 0x1}, + {0, 0x0} }; + +#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu)) + +void +si_sdiod_drive_strength_init(si_t *sih, osl_t *osh, uint32 drivestrength) +{ + chipcregs_t *cc; + uint origidx, intr_val = 0; + sdiod_drive_str_t *str_tab = NULL; + uint32 str_mask = 0; + uint32 str_shift = 0; + + if (!(sih->cccaps & CC_CAP_PMU)) { + return; + } + + /* Remember original core before switch to chipc */ + cc = (chipcregs_t *) si_switch_core(sih, CC_CORE_ID, &origidx, &intr_val); + + switch (SDIOD_DRVSTR_KEY(sih->chip, sih->pmurev)) { + case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 1): + str_tab = (sdiod_drive_str_t *)&sdiod_drive_strength_tab1; + str_mask = 0x30000000; + str_shift = 28; + break; + case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 2): + case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 3): + case SDIOD_DRVSTR_KEY(BCM4315_CHIP_ID, 4): + str_tab = (sdiod_drive_str_t *)&sdiod_drive_strength_tab2; + str_mask = 0x00003800; + str_shift = 11; + break; + case SDIOD_DRVSTR_KEY(BCM4336_CHIP_ID, 8): + case SDIOD_DRVSTR_KEY(BCM4336_CHIP_ID, 11): + if (sih->pmurev == 8) { + str_tab = (sdiod_drive_str_t *)&sdiod_drive_strength_tab3; + } + else if (sih->pmurev == 11) { + str_tab = (sdiod_drive_str_t *)&sdiod_drive_strength_tab4_1v8; + } + str_mask = 0x00003800; + str_shift = 11; + break; + case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12): + str_tab = (sdiod_drive_str_t *)&sdiod_drive_strength_tab4_1v8; + str_mask = 0x00003800; + str_shift = 11; + break; + case SDIOD_DRVSTR_KEY(BCM43362_CHIP_ID, 13): + str_tab = (sdiod_drive_str_t *)&sdiod_drive_strength_tab5_1v8; + str_mask = 0x00003800; + str_shift = 11; + break; + case SDIOD_DRVSTR_KEY(BCM4334_CHIP_ID, 17): + str_tab = (sdiod_drive_str_t *)&sdiod_drive_strength_tab6_1v8; + str_mask = 0x00001800; + str_shift = 11; + break; + default: + PMU_MSG(("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n", + bcm_chipname(sih->chip, chn, 8), sih->chiprev, sih->pmurev)); + + break; + } + + if (str_tab != NULL && cc != NULL) { + uint32 cc_data_temp; + int i; + + /* Pick the lowest available drive strength equal or greater than the + * requested strength. Drive strength of 0 requests tri-state. + */ + for (i = 0; drivestrength < str_tab[i].strength; i++) + ; + + if (i > 0 && drivestrength > str_tab[i].strength) + i--; + + W_REG(osh, &cc->chipcontrol_addr, 1); + cc_data_temp = R_REG(osh, &cc->chipcontrol_data); + cc_data_temp &= ~str_mask; + cc_data_temp |= str_tab[i].sel << str_shift; + W_REG(osh, &cc->chipcontrol_data, cc_data_temp); + + PMU_MSG(("SDIO: %dmA drive strength requested; set to %dmA\n", + drivestrength, str_tab[i].strength)); + } + + /* Return to original core */ + si_restore_core(sih, origidx, intr_val); +} diff --git a/drivers/net/wireless/bcmdhd/include/Makefile b/drivers/net/wireless/bcmdhd/include/Makefile new file mode 100644 index 00000000..42b3b689 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/Makefile @@ -0,0 +1,53 @@ +#!/bin/bash +# +# This script serves following purpose: +# +# 1. It generates native version information by querying +# automerger maintained database to see where src/include +# came from +# 2. For select components, as listed in compvers.sh +# it generates component version files +# +# Copyright 2005, Broadcom, Inc. +# +# $Id: Makefile 241686 2011-02-19 00:22:45Z $ +# + +SRCBASE := .. + +TARGETS := epivers.h + +ifdef VERBOSE +export VERBOSE +endif + +all release: epivers compvers + +# Generate epivers.h for native branch version +epivers: + bash epivers.sh + +# Generate epivers.h for native branch version +compvers: + @if [ -s "compvers.sh" ]; then \ + echo "Generating component versions, if any"; \ + bash compvers.sh; \ + else \ + echo "Skipping component version generation"; \ + fi + +# Generate epivers.h for native branch version +clean_compvers: + @if [ -s "compvers.sh" ]; then \ + echo "bash compvers.sh clean"; \ + bash compvers.sh clean; \ + else \ + echo "Skipping component version clean"; \ + fi + +clean: + rm -f $(TARGETS) *.prev + +clean_all: clean clean_compvers + +.PHONY: all release clean epivers compvers clean_compvers diff --git a/drivers/net/wireless/bcmdhd/include/aidmp.h b/drivers/net/wireless/bcmdhd/include/aidmp.h new file mode 100644 index 00000000..63513e6f --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/aidmp.h @@ -0,0 +1,375 @@ +/* + * Broadcom AMBA Interconnect definitions. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: aidmp.h 241182 2011-02-17 21:50:03Z $ + */ + +#ifndef _AIDMP_H +#define _AIDMP_H + + +#define MFGID_ARM 0x43b +#define MFGID_BRCM 0x4bf +#define MFGID_MIPS 0x4a7 + + +#define CC_SIM 0 +#define CC_EROM 1 +#define CC_CORESIGHT 9 +#define CC_VERIF 0xb +#define CC_OPTIMO 0xd +#define CC_GEN 0xe +#define CC_PRIMECELL 0xf + + +#define ER_EROMENTRY 0x000 +#define ER_REMAPCONTROL 0xe00 +#define ER_REMAPSELECT 0xe04 +#define ER_MASTERSELECT 0xe10 +#define ER_ITCR 0xf00 +#define ER_ITIP 0xf04 + + +#define ER_TAG 0xe +#define ER_TAG1 0x6 +#define ER_VALID 1 +#define ER_CI 0 +#define ER_MP 2 +#define ER_ADD 4 +#define ER_END 0xe +#define ER_BAD 0xffffffff + + +#define CIA_MFG_MASK 0xfff00000 +#define CIA_MFG_SHIFT 20 +#define CIA_CID_MASK 0x000fff00 +#define CIA_CID_SHIFT 8 +#define CIA_CCL_MASK 0x000000f0 +#define CIA_CCL_SHIFT 4 + + +#define CIB_REV_MASK 0xff000000 +#define CIB_REV_SHIFT 24 +#define CIB_NSW_MASK 0x00f80000 +#define CIB_NSW_SHIFT 19 +#define CIB_NMW_MASK 0x0007c000 +#define CIB_NMW_SHIFT 14 +#define CIB_NSP_MASK 0x00003e00 +#define CIB_NSP_SHIFT 9 +#define CIB_NMP_MASK 0x000001f0 +#define CIB_NMP_SHIFT 4 + + +#define MPD_MUI_MASK 0x0000ff00 +#define MPD_MUI_SHIFT 8 +#define MPD_MP_MASK 0x000000f0 +#define MPD_MP_SHIFT 4 + + +#define AD_ADDR_MASK 0xfffff000 +#define AD_SP_MASK 0x00000f00 +#define AD_SP_SHIFT 8 +#define AD_ST_MASK 0x000000c0 +#define AD_ST_SHIFT 6 +#define AD_ST_SLAVE 0x00000000 +#define AD_ST_BRIDGE 0x00000040 +#define AD_ST_SWRAP 0x00000080 +#define AD_ST_MWRAP 0x000000c0 +#define AD_SZ_MASK 0x00000030 +#define AD_SZ_SHIFT 4 +#define AD_SZ_4K 0x00000000 +#define AD_SZ_8K 0x00000010 +#define AD_SZ_16K 0x00000020 +#define AD_SZ_SZD 0x00000030 +#define AD_AG32 0x00000008 +#define AD_ADDR_ALIGN 0x00000fff +#define AD_SZ_BASE 0x00001000 + + +#define SD_SZ_MASK 0xfffff000 +#define SD_SG32 0x00000008 +#define SD_SZ_ALIGN 0x00000fff + + +#ifndef _LANGUAGE_ASSEMBLY + +typedef volatile struct _aidmp { + uint32 oobselina30; + uint32 oobselina74; + uint32 PAD[6]; + uint32 oobselinb30; + uint32 oobselinb74; + uint32 PAD[6]; + uint32 oobselinc30; + uint32 oobselinc74; + uint32 PAD[6]; + uint32 oobselind30; + uint32 oobselind74; + uint32 PAD[38]; + uint32 oobselouta30; + uint32 oobselouta74; + uint32 PAD[6]; + uint32 oobseloutb30; + uint32 oobseloutb74; + uint32 PAD[6]; + uint32 oobseloutc30; + uint32 oobseloutc74; + uint32 PAD[6]; + uint32 oobseloutd30; + uint32 oobseloutd74; + uint32 PAD[38]; + uint32 oobsynca; + uint32 oobseloutaen; + uint32 PAD[6]; + uint32 oobsyncb; + uint32 oobseloutben; + uint32 PAD[6]; + uint32 oobsyncc; + uint32 oobseloutcen; + uint32 PAD[6]; + uint32 oobsyncd; + uint32 oobseloutden; + uint32 PAD[38]; + uint32 oobaextwidth; + uint32 oobainwidth; + uint32 oobaoutwidth; + uint32 PAD[5]; + uint32 oobbextwidth; + uint32 oobbinwidth; + uint32 oobboutwidth; + uint32 PAD[5]; + uint32 oobcextwidth; + uint32 oobcinwidth; + uint32 oobcoutwidth; + uint32 PAD[5]; + uint32 oobdextwidth; + uint32 oobdinwidth; + uint32 oobdoutwidth; + uint32 PAD[37]; + uint32 ioctrlset; + uint32 ioctrlclear; + uint32 ioctrl; + uint32 PAD[61]; + uint32 iostatus; + uint32 PAD[127]; + uint32 ioctrlwidth; + uint32 iostatuswidth; + uint32 PAD[62]; + uint32 resetctrl; + uint32 resetstatus; + uint32 resetreadid; + uint32 resetwriteid; + uint32 PAD[60]; + uint32 errlogctrl; + uint32 errlogdone; + uint32 errlogstatus; + uint32 errlogaddrlo; + uint32 errlogaddrhi; + uint32 errlogid; + uint32 errloguser; + uint32 errlogflags; + uint32 PAD[56]; + uint32 intstatus; + uint32 PAD[255]; + uint32 config; + uint32 PAD[63]; + uint32 itcr; + uint32 PAD[3]; + uint32 itipooba; + uint32 itipoobb; + uint32 itipoobc; + uint32 itipoobd; + uint32 PAD[4]; + uint32 itipoobaout; + uint32 itipoobbout; + uint32 itipoobcout; + uint32 itipoobdout; + uint32 PAD[4]; + uint32 itopooba; + uint32 itopoobb; + uint32 itopoobc; + uint32 itopoobd; + uint32 PAD[4]; + uint32 itopoobain; + uint32 itopoobbin; + uint32 itopoobcin; + uint32 itopoobdin; + uint32 PAD[4]; + uint32 itopreset; + uint32 PAD[15]; + uint32 peripherialid4; + uint32 peripherialid5; + uint32 peripherialid6; + uint32 peripherialid7; + uint32 peripherialid0; + uint32 peripherialid1; + uint32 peripherialid2; + uint32 peripherialid3; + uint32 componentid0; + uint32 componentid1; + uint32 componentid2; + uint32 componentid3; +} aidmp_t; + +#endif + + +#define OOB_BUSCONFIG 0x020 +#define OOB_STATUSA 0x100 +#define OOB_STATUSB 0x104 +#define OOB_STATUSC 0x108 +#define OOB_STATUSD 0x10c +#define OOB_ENABLEA0 0x200 +#define OOB_ENABLEA1 0x204 +#define OOB_ENABLEA2 0x208 +#define OOB_ENABLEA3 0x20c +#define OOB_ENABLEB0 0x280 +#define OOB_ENABLEB1 0x284 +#define OOB_ENABLEB2 0x288 +#define OOB_ENABLEB3 0x28c +#define OOB_ENABLEC0 0x300 +#define OOB_ENABLEC1 0x304 +#define OOB_ENABLEC2 0x308 +#define OOB_ENABLEC3 0x30c +#define OOB_ENABLED0 0x380 +#define OOB_ENABLED1 0x384 +#define OOB_ENABLED2 0x388 +#define OOB_ENABLED3 0x38c +#define OOB_ITCR 0xf00 +#define OOB_ITIPOOBA 0xf10 +#define OOB_ITIPOOBB 0xf14 +#define OOB_ITIPOOBC 0xf18 +#define OOB_ITIPOOBD 0xf1c +#define OOB_ITOPOOBA 0xf30 +#define OOB_ITOPOOBB 0xf34 +#define OOB_ITOPOOBC 0xf38 +#define OOB_ITOPOOBD 0xf3c + + +#define AI_OOBSELINA30 0x000 +#define AI_OOBSELINA74 0x004 +#define AI_OOBSELINB30 0x020 +#define AI_OOBSELINB74 0x024 +#define AI_OOBSELINC30 0x040 +#define AI_OOBSELINC74 0x044 +#define AI_OOBSELIND30 0x060 +#define AI_OOBSELIND74 0x064 +#define AI_OOBSELOUTA30 0x100 +#define AI_OOBSELOUTA74 0x104 +#define AI_OOBSELOUTB30 0x120 +#define AI_OOBSELOUTB74 0x124 +#define AI_OOBSELOUTC30 0x140 +#define AI_OOBSELOUTC74 0x144 +#define AI_OOBSELOUTD30 0x160 +#define AI_OOBSELOUTD74 0x164 +#define AI_OOBSYNCA 0x200 +#define AI_OOBSELOUTAEN 0x204 +#define AI_OOBSYNCB 0x220 +#define AI_OOBSELOUTBEN 0x224 +#define AI_OOBSYNCC 0x240 +#define AI_OOBSELOUTCEN 0x244 +#define AI_OOBSYNCD 0x260 +#define AI_OOBSELOUTDEN 0x264 +#define AI_OOBAEXTWIDTH 0x300 +#define AI_OOBAINWIDTH 0x304 +#define AI_OOBAOUTWIDTH 0x308 +#define AI_OOBBEXTWIDTH 0x320 +#define AI_OOBBINWIDTH 0x324 +#define AI_OOBBOUTWIDTH 0x328 +#define AI_OOBCEXTWIDTH 0x340 +#define AI_OOBCINWIDTH 0x344 +#define AI_OOBCOUTWIDTH 0x348 +#define AI_OOBDEXTWIDTH 0x360 +#define AI_OOBDINWIDTH 0x364 +#define AI_OOBDOUTWIDTH 0x368 + + +#define AI_IOCTRLSET 0x400 +#define AI_IOCTRLCLEAR 0x404 +#define AI_IOCTRL 0x408 +#define AI_IOSTATUS 0x500 +#define AI_RESETCTRL 0x800 +#define AI_RESETSTATUS 0x804 + +#define AI_IOCTRLWIDTH 0x700 +#define AI_IOSTATUSWIDTH 0x704 + +#define AI_RESETREADID 0x808 +#define AI_RESETWRITEID 0x80c +#define AI_ERRLOGCTRL 0xa00 +#define AI_ERRLOGDONE 0xa04 +#define AI_ERRLOGSTATUS 0xa08 +#define AI_ERRLOGADDRLO 0xa0c +#define AI_ERRLOGADDRHI 0xa10 +#define AI_ERRLOGID 0xa14 +#define AI_ERRLOGUSER 0xa18 +#define AI_ERRLOGFLAGS 0xa1c +#define AI_INTSTATUS 0xa00 +#define AI_CONFIG 0xe00 +#define AI_ITCR 0xf00 +#define AI_ITIPOOBA 0xf10 +#define AI_ITIPOOBB 0xf14 +#define AI_ITIPOOBC 0xf18 +#define AI_ITIPOOBD 0xf1c +#define AI_ITIPOOBAOUT 0xf30 +#define AI_ITIPOOBBOUT 0xf34 +#define AI_ITIPOOBCOUT 0xf38 +#define AI_ITIPOOBDOUT 0xf3c +#define AI_ITOPOOBA 0xf50 +#define AI_ITOPOOBB 0xf54 +#define AI_ITOPOOBC 0xf58 +#define AI_ITOPOOBD 0xf5c +#define AI_ITOPOOBAIN 0xf70 +#define AI_ITOPOOBBIN 0xf74 +#define AI_ITOPOOBCIN 0xf78 +#define AI_ITOPOOBDIN 0xf7c +#define AI_ITOPRESET 0xf90 +#define AI_PERIPHERIALID4 0xfd0 +#define AI_PERIPHERIALID5 0xfd4 +#define AI_PERIPHERIALID6 0xfd8 +#define AI_PERIPHERIALID7 0xfdc +#define AI_PERIPHERIALID0 0xfe0 +#define AI_PERIPHERIALID1 0xfe4 +#define AI_PERIPHERIALID2 0xfe8 +#define AI_PERIPHERIALID3 0xfec +#define AI_COMPONENTID0 0xff0 +#define AI_COMPONENTID1 0xff4 +#define AI_COMPONENTID2 0xff8 +#define AI_COMPONENTID3 0xffc + + +#define AIRC_RESET 1 + + +#define AICFG_OOB 0x00000020 +#define AICFG_IOS 0x00000010 +#define AICFG_IOC 0x00000008 +#define AICFG_TO 0x00000004 +#define AICFG_ERRL 0x00000002 +#define AICFG_RST 0x00000001 + + +#define OOB_SEL_OUTEN_B_5 15 +#define OOB_SEL_OUTEN_B_6 23 + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/bcm_cfg.h b/drivers/net/wireless/bcmdhd/include/bcm_cfg.h new file mode 100644 index 00000000..26da7521 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcm_cfg.h @@ -0,0 +1,29 @@ +/* + * BCM common config options + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcm_cfg.h 294399 2011-11-07 03:31:22Z $ + */ + +#ifndef _bcm_cfg_h_ +#define _bcm_cfg_h_ +#endif diff --git a/drivers/net/wireless/bcmdhd/include/bcm_mpool_pub.h b/drivers/net/wireless/bcmdhd/include/bcm_mpool_pub.h new file mode 100644 index 00000000..8fe3de7a --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcm_mpool_pub.h @@ -0,0 +1,361 @@ +/* + * Memory pools library, Public interface + * + * API Overview + * + * This package provides a memory allocation subsystem based on pools of + * homogenous objects. + * + * Instrumentation is available for reporting memory utilization both + * on a per-data-structure basis and system wide. + * + * There are two main types defined in this API. + * + * pool manager: A singleton object that acts as a factory for + * pool allocators. It also is used for global + * instrumentation, such as reporting all blocks + * in use across all data structures. The pool manager + * creates and provides individual memory pools + * upon request to application code. + * + * memory pool: An object for allocating homogenous memory blocks. + * + * Global identifiers in this module use the following prefixes: + * bcm_mpm_* Memory pool manager + * bcm_mp_* Memory pool + * + * There are two main types of memory pools: + * + * prealloc: The contiguous memory block of objects can either be supplied + * by the client or malloc'ed by the memory manager. The objects are + * allocated out of a block of memory and freed back to the block. + * + * heap: The memory pool allocator uses the heap (malloc/free) for memory. + * In this case, the pool allocator is just providing statistics + * and instrumentation on top of the heap, without modifying the heap + * allocation implementation. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id$ + */ + +#ifndef _BCM_MPOOL_PUB_H +#define _BCM_MPOOL_PUB_H 1 + +#include /* needed for uint16 */ + + +/* +************************************************************************** +* +* Type definitions, handles +* +************************************************************************** +*/ + +/* Forward declaration of OSL handle. */ +struct osl_info; + +/* Forward declaration of string buffer. */ +struct bcmstrbuf; + +/* + * Opaque type definition for the pool manager handle. This object is used for global + * memory pool operations such as obtaining a new pool, deleting a pool, iterating and + * instrumentation/debugging. + */ +struct bcm_mpm_mgr; +typedef struct bcm_mpm_mgr *bcm_mpm_mgr_h; + +/* + * Opaque type definition for an instance of a pool. This handle is used for allocating + * and freeing memory through the pool, as well as management/instrumentation on this + * specific pool. + */ +struct bcm_mp_pool; +typedef struct bcm_mp_pool *bcm_mp_pool_h; + + +/* + * To make instrumentation more readable, every memory + * pool must have a readable name. Pool names are up to + * 8 bytes including '\0' termination. (7 printable characters.) + */ +#define BCM_MP_NAMELEN 8 + + +/* + * Type definition for pool statistics. + */ +typedef struct bcm_mp_stats { + char name[BCM_MP_NAMELEN]; /* Name of this pool. */ + unsigned int objsz; /* Object size allocated in this pool */ + uint16 nobj; /* Total number of objects in this pool */ + uint16 num_alloc; /* Number of objects currently allocated */ + uint16 high_water; /* Max number of allocated objects. */ + uint16 failed_alloc; /* Failed allocations. */ +} bcm_mp_stats_t; + + +/* +************************************************************************** +* +* API Routines on the pool manager. +* +************************************************************************** +*/ + +/* + * bcm_mpm_init() - initialize the whole memory pool system. + * + * Parameters: + * osh: INPUT Operating system handle. Needed for heap memory allocation. + * max_pools: INPUT Maximum number of mempools supported. + * mgr: OUTPUT The handle is written with the new pools manager object/handle. + * + * Returns: + * BCME_OK Object initialized successfully. May be used. + * BCME_NOMEM Initialization failed due to no memory. Object must not be used. + */ +int bcm_mpm_init(struct osl_info *osh, int max_pools, bcm_mpm_mgr_h *mgrp); + + +/* + * bcm_mpm_deinit() - de-initialize the whole memory pool system. + * + * Parameters: + * mgr: INPUT Pointer to pool manager handle. + * + * Returns: + * BCME_OK Memory pool manager successfully de-initialized. + * other Indicated error occured during de-initialization. + */ +int bcm_mpm_deinit(bcm_mpm_mgr_h *mgrp); + +/* + * bcm_mpm_create_prealloc_pool() - Create a new pool for fixed size objects. The + * pool uses a contiguous block of pre-alloced + * memory. The memory block may either be provided + * by the client or dynamically allocated by the + * pool manager. + * + * Parameters: + * mgr: INPUT The handle to the pool manager + * obj_sz: INPUT Size of objects that will be allocated by the new pool + * Must be >= sizeof(void *). + * nobj: INPUT Maximum number of concurrently existing objects to support + * memstart INPUT Pointer to the memory to use, or NULL to malloc() + * memsize INPUT Number of bytes referenced from memstart (for error checking). + * Must be 0 if 'memstart' is NULL. + * poolname INPUT For instrumentation, the name of the pool + * newp: OUTPUT The handle for the new pool, if creation is successful + * + * Returns: + * BCME_OK Pool created ok. + * other Pool not created due to indicated error. newpoolp set to NULL. + * + * + */ +int bcm_mpm_create_prealloc_pool(bcm_mpm_mgr_h mgr, + unsigned int obj_sz, + int nobj, + void *memstart, + unsigned int memsize, + char poolname[BCM_MP_NAMELEN], + bcm_mp_pool_h *newp); + + +/* + * bcm_mpm_delete_prealloc_pool() - Delete a memory pool. This should only be called after + * all memory objects have been freed back to the pool. + * + * Parameters: + * mgr: INPUT The handle to the pools manager + * pool: INPUT The handle of the pool to delete + * + * Returns: + * BCME_OK Pool deleted ok. + * other Pool not deleted due to indicated error. + * + */ +int bcm_mpm_delete_prealloc_pool(bcm_mpm_mgr_h mgr, bcm_mp_pool_h *poolp); + +/* + * bcm_mpm_create_heap_pool() - Create a new pool for fixed size objects. The memory + * pool allocator uses the heap (malloc/free) for memory. + * In this case, the pool allocator is just providing + * statistics and instrumentation on top of the heap, + * without modifying the heap allocation implementation. + * + * Parameters: + * mgr: INPUT The handle to the pool manager + * obj_sz: INPUT Size of objects that will be allocated by the new pool + * poolname INPUT For instrumentation, the name of the pool + * newp: OUTPUT The handle for the new pool, if creation is successful + * + * Returns: + * BCME_OK Pool created ok. + * other Pool not created due to indicated error. newpoolp set to NULL. + * + * + */ +int bcm_mpm_create_heap_pool(bcm_mpm_mgr_h mgr, unsigned int obj_sz, + char poolname[BCM_MP_NAMELEN], + bcm_mp_pool_h *newp); + + +/* + * bcm_mpm_delete_heap_pool() - Delete a memory pool. This should only be called after + * all memory objects have been freed back to the pool. + * + * Parameters: + * mgr: INPUT The handle to the pools manager + * pool: INPUT The handle of the pool to delete + * + * Returns: + * BCME_OK Pool deleted ok. + * other Pool not deleted due to indicated error. + * + */ +int bcm_mpm_delete_heap_pool(bcm_mpm_mgr_h mgr, bcm_mp_pool_h *poolp); + + +/* + * bcm_mpm_stats() - Return stats for all pools + * + * Parameters: + * mgr: INPUT The handle to the pools manager + * stats: OUTPUT Array of pool statistics. + * nentries: MOD Max elements in 'stats' array on INPUT. Actual number + * of array elements copied to 'stats' on OUTPUT. + * + * Returns: + * BCME_OK Ok + * other Error getting stats. + * + */ +int bcm_mpm_stats(bcm_mpm_mgr_h mgr, bcm_mp_stats_t *stats, int *nentries); + + +/* + * bcm_mpm_dump() - Display statistics on all pools + * + * Parameters: + * mgr: INPUT The handle to the pools manager + * b: OUTPUT Output buffer. + * + * Returns: + * BCME_OK Ok + * other Error during dump. + * + */ +int bcm_mpm_dump(bcm_mpm_mgr_h mgr, struct bcmstrbuf *b); + + +/* + * bcm_mpm_get_obj_size() - The size of memory objects may need to be padded to + * compensate for alignment requirements of the objects. + * This function provides the padded object size. If clients + * pre-allocate a memory slab for a memory pool, the + * padded object size should be used by the client to allocate + * the memory slab (in order to provide sufficent space for + * the maximum number of objects). + * + * Parameters: + * mgr: INPUT The handle to the pools manager. + * obj_sz: INPUT Input object size. + * padded_obj_sz: OUTPUT Padded object size. + * + * Returns: + * BCME_OK Ok + * BCME_BADARG Bad arguments. + * + */ +int bcm_mpm_get_obj_size(bcm_mpm_mgr_h mgr, unsigned int obj_sz, unsigned int *padded_obj_sz); + + +/* +*************************************************************************** +* +* API Routines on a specific pool. +* +*************************************************************************** +*/ + + +/* + * bcm_mp_alloc() - Allocate a memory pool object. + * + * Parameters: + * pool: INPUT The handle to the pool. + * + * Returns: + * A pointer to the new object. NULL on error. + * + */ +void* bcm_mp_alloc(bcm_mp_pool_h pool); + +/* + * bcm_mp_free() - Free a memory pool object. + * + * Parameters: + * pool: INPUT The handle to the pool. + * objp: INPUT A pointer to the object to free. + * + * Returns: + * BCME_OK Ok + * other Error during free. + * + */ +int bcm_mp_free(bcm_mp_pool_h pool, void *objp); + +/* + * bcm_mp_stats() - Return stats for this pool + * + * Parameters: + * pool: INPUT The handle to the pool + * stats: OUTPUT Pool statistics + * + * Returns: + * BCME_OK Ok + * other Error getting statistics. + * + */ +int bcm_mp_stats(bcm_mp_pool_h pool, bcm_mp_stats_t *stats); + + +/* + * bcm_mp_dump() - Dump a pool + * + * Parameters: + * pool: INPUT The handle to the pool + * b OUTPUT Output buffer + * + * Returns: + * BCME_OK Ok + * other Error during dump. + * + */ +int bcm_mp_dump(bcm_mp_pool_h pool, struct bcmstrbuf *b); + + +#endif /* _BCM_MPOOL_PUB_H */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmcdc.h b/drivers/net/wireless/bcmdhd/include/bcmcdc.h new file mode 100644 index 00000000..9bae1c20 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmcdc.h @@ -0,0 +1,126 @@ +/* + * CDC network driver ioctl/indication encoding + * Broadcom 802.11abg Networking Device Driver + * + * Definitions subject to change without notice. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmcdc.h 318308 2012-03-02 02:23:42Z $ + */ +#ifndef _bcmcdc_h_ +#define _bcmcdc_h_ +#include + +typedef struct cdc_ioctl { + uint32 cmd; + uint32 len; + uint32 flags; + uint32 status; +} cdc_ioctl_t; + + +#define CDC_MAX_MSG_SIZE ETHER_MAX_LEN + + +#define CDCL_IOC_OUTLEN_MASK 0x0000FFFF + +#define CDCL_IOC_OUTLEN_SHIFT 0 +#define CDCL_IOC_INLEN_MASK 0xFFFF0000 +#define CDCL_IOC_INLEN_SHIFT 16 + + +#define CDCF_IOC_ERROR 0x01 +#define CDCF_IOC_SET 0x02 +#define CDCF_IOC_OVL_IDX_MASK 0x3c +#define CDCF_IOC_OVL_RSV 0x40 +#define CDCF_IOC_OVL 0x80 +#define CDCF_IOC_ACTION_MASK 0xfe +#define CDCF_IOC_ACTION_SHIFT 1 +#define CDCF_IOC_IF_MASK 0xF000 +#define CDCF_IOC_IF_SHIFT 12 +#define CDCF_IOC_ID_MASK 0xFFFF0000 +#define CDCF_IOC_ID_SHIFT 16 + +#define CDC_IOC_IF_IDX(flags) (((flags) & CDCF_IOC_IF_MASK) >> CDCF_IOC_IF_SHIFT) +#define CDC_IOC_ID(flags) (((flags) & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT) + +#define CDC_GET_IF_IDX(hdr) \ + ((int)((((hdr)->flags) & CDCF_IOC_IF_MASK) >> CDCF_IOC_IF_SHIFT)) +#define CDC_SET_IF_IDX(hdr, idx) \ + ((hdr)->flags = (((hdr)->flags & ~CDCF_IOC_IF_MASK) | ((idx) << CDCF_IOC_IF_SHIFT))) + + + +struct bdc_header { + uint8 flags; + uint8 priority; + uint8 flags2; + uint8 dataOffset; +}; + +#define BDC_HEADER_LEN 4 + + +#define BDC_FLAG_80211_PKT 0x01 +#define BDC_FLAG_SUM_GOOD 0x04 +#define BDC_FLAG_SUM_NEEDED 0x08 +#define BDC_FLAG_EVENT_MSG 0x08 +#define BDC_FLAG_VER_MASK 0xf0 +#define BDC_FLAG_VER_SHIFT 4 + + +#define BDC_PRIORITY_MASK 0x07 +#define BDC_PRIORITY_FC_MASK 0xf0 +#define BDC_PRIORITY_FC_SHIFT 4 + + +#define BDC_FLAG2_IF_MASK 0x0f +#define BDC_FLAG2_IF_SHIFT 0 +#define BDC_FLAG2_FC_FLAG 0x10 + + + +#define BDC_PROTO_VER_1 1 +#define BDC_PROTO_VER 2 + + +#define BDC_GET_IF_IDX(hdr) \ + ((int)((((hdr)->flags2) & BDC_FLAG2_IF_MASK) >> BDC_FLAG2_IF_SHIFT)) +#define BDC_SET_IF_IDX(hdr, idx) \ + ((hdr)->flags2 = (((hdr)->flags2 & ~BDC_FLAG2_IF_MASK) | ((idx) << BDC_FLAG2_IF_SHIFT))) + +#define BDC_FLAG2_PAD_MASK 0xf0 +#define BDC_FLAG_PAD_MASK 0x03 +#define BDC_FLAG2_PAD_SHIFT 2 +#define BDC_FLAG_PAD_SHIFT 0 +#define BDC_FLAG2_PAD_IDX 0x3c +#define BDC_FLAG_PAD_IDX 0x03 +#define BDC_GET_PAD_LEN(hdr) \ + ((int)(((((hdr)->flags2) & BDC_FLAG2_PAD_MASK) >> BDC_FLAG2_PAD_SHIFT) | \ + ((((hdr)->flags) & BDC_FLAG_PAD_MASK) >> BDC_FLAG_PAD_SHIFT))) +#define BDC_SET_PAD_LEN(hdr, idx) \ + ((hdr)->flags2 = (((hdr)->flags2 & ~BDC_FLAG2_PAD_MASK) | \ + (((idx) & BDC_FLAG2_PAD_IDX) << BDC_FLAG2_PAD_SHIFT))); \ + ((hdr)->flags = (((hdr)->flags & ~BDC_FLAG_PAD_MASK) | \ + (((idx) & BDC_FLAG_PAD_IDX) << BDC_FLAG_PAD_SHIFT))) + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/bcmdefs.h b/drivers/net/wireless/bcmdhd/include/bcmdefs.h new file mode 100644 index 00000000..a35ed72a --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmdefs.h @@ -0,0 +1,239 @@ +/* + * Misc system wide definitions + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmdefs.h 316830 2012-02-23 20:29:22Z $ + */ + +#ifndef _bcmdefs_h_ +#define _bcmdefs_h_ + + + + +#define BCM_REFERENCE(data) ((void)(data)) + + +#define STATIC_ASSERT(expr) { \ + \ + typedef enum { _STATIC_ASSERT_NOT_CONSTANT = (expr) } _static_assert_e; \ + \ + typedef char STATIC_ASSERT_FAIL[(expr) ? 1 : -1]; \ +} + + + +#define bcmreclaimed 0 +#define _data _data +#define _fn _fn +#define BCMPREATTACHDATA(_data) _data +#define BCMPREATTACHFN(_fn) _fn +#define _data _data +#define _fn _fn +#define _fn _fn +#define BCMNMIATTACHFN(_fn) _fn +#define BCMNMIATTACHDATA(_data) _data +#define CONST const +#ifndef BCMFASTPATH +#define BCMFASTPATH +#define BCMFASTPATH_HOST +#endif + + + +#define _data _data +#define BCMROMDAT_NAME(_data) _data +#define _fn _fn +#define _fn _fn +#define STATIC static +#define BCMROMDAT_ARYSIZ(data) ARRAYSIZE(data) +#define BCMROMDAT_SIZEOF(data) sizeof(data) +#define BCMROMDAT_APATCH(data) +#define BCMROMDAT_SPATCH(data) + + +#define SI_BUS 0 +#define PCI_BUS 1 +#define PCMCIA_BUS 2 +#define SDIO_BUS 3 +#define JTAG_BUS 4 +#define USB_BUS 5 +#define SPI_BUS 6 +#define RPC_BUS 7 + + +#ifdef BCMBUSTYPE +#define BUSTYPE(bus) (BCMBUSTYPE) +#else +#define BUSTYPE(bus) (bus) +#endif + + +#ifdef BCMCHIPTYPE +#define CHIPTYPE(bus) (BCMCHIPTYPE) +#else +#define CHIPTYPE(bus) (bus) +#endif + + + +#if defined(BCMSPROMBUS) +#define SPROMBUS (BCMSPROMBUS) +#elif defined(SI_PCMCIA_SROM) +#define SPROMBUS (PCMCIA_BUS) +#else +#define SPROMBUS (PCI_BUS) +#endif + + +#ifdef BCMCHIPID +#define CHIPID(chip) (BCMCHIPID) +#else +#define CHIPID(chip) (chip) +#endif + +#ifdef BCMCHIPREV +#define CHIPREV(rev) (BCMCHIPREV) +#else +#define CHIPREV(rev) (rev) +#endif + + +#define DMADDR_MASK_32 0x0 +#define DMADDR_MASK_30 0xc0000000 +#define DMADDR_MASK_0 0xffffffff + +#define DMADDRWIDTH_30 30 +#define DMADDRWIDTH_32 32 +#define DMADDRWIDTH_63 63 +#define DMADDRWIDTH_64 64 + +#ifdef BCMDMA64OSL +typedef struct { + uint32 loaddr; + uint32 hiaddr; +} dma64addr_t; + +typedef dma64addr_t dmaaddr_t; +#define PHYSADDRHI(_pa) ((_pa).hiaddr) +#define PHYSADDRHISET(_pa, _val) \ + do { \ + (_pa).hiaddr = (_val); \ + } while (0) +#define PHYSADDRLO(_pa) ((_pa).loaddr) +#define PHYSADDRLOSET(_pa, _val) \ + do { \ + (_pa).loaddr = (_val); \ + } while (0) + +#else +typedef unsigned long dmaaddr_t; +#define PHYSADDRHI(_pa) (0) +#define PHYSADDRHISET(_pa, _val) +#define PHYSADDRLO(_pa) ((_pa)) +#define PHYSADDRLOSET(_pa, _val) \ + do { \ + (_pa) = (_val); \ + } while (0) +#endif + + +typedef struct { + dmaaddr_t addr; + uint32 length; +} hnddma_seg_t; + +#define MAX_DMA_SEGS 4 + + +typedef struct { + void *oshdmah; + uint origsize; + uint nsegs; + hnddma_seg_t segs[MAX_DMA_SEGS]; +} hnddma_seg_map_t; + + + + +#if defined(BCM_RPC_NOCOPY) || defined(BCM_RCP_TXNOCOPY) + +#define BCMEXTRAHDROOM 220 +#else +#define BCMEXTRAHDROOM 172 +#endif + + +#ifndef SDALIGN +#define SDALIGN 32 +#endif + + +#define BCMDONGLEHDRSZ 12 +#define BCMDONGLEPADSZ 16 + +#define BCMDONGLEOVERHEAD (BCMDONGLEHDRSZ + BCMDONGLEPADSZ) + + +#if defined(NO_BCMDBG_ASSERT) +# undef BCMDBG_ASSERT +# undef BCMASSERT_LOG +#endif + +#if defined(BCMASSERT_LOG) +#define BCMASSERT_SUPPORT +#endif + + +#define BITFIELD_MASK(width) \ + (((unsigned)1 << (width)) - 1) +#define GFIELD(val, field) \ + (((val) >> field ## _S) & field ## _M) +#define SFIELD(val, field, bits) \ + (((val) & (~(field ## _M << field ## _S))) | \ + ((unsigned)(bits) << field ## _S)) + + +#ifdef BCMSMALL +#undef BCMSPACE +#define bcmspace FALSE +#else +#define BCMSPACE +#define bcmspace TRUE +#endif + + +#define MAXSZ_NVRAM_VARS 4096 + + + +#ifdef DL_NVRAM +#define NVRAM_ARRAY_MAXSIZE DL_NVRAM +#else +#define NVRAM_ARRAY_MAXSIZE MAXSZ_NVRAM_VARS +#endif + +#ifdef BCMUSBDEV_ENABLED +extern uint32 gFWID; +#endif + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/bcmdevs.h b/drivers/net/wireless/bcmdhd/include/bcmdevs.h new file mode 100644 index 00000000..c7e06ff4 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmdevs.h @@ -0,0 +1,495 @@ +/* + * Broadcom device-specific manifest constants. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmdevs.h 329854 2012-04-27 01:42:28Z $ + */ + +#ifndef _BCMDEVS_H +#define _BCMDEVS_H + + +#define VENDOR_EPIGRAM 0xfeda +#define VENDOR_BROADCOM 0x14e4 +#define VENDOR_3COM 0x10b7 +#define VENDOR_NETGEAR 0x1385 +#define VENDOR_DIAMOND 0x1092 +#define VENDOR_INTEL 0x8086 +#define VENDOR_DELL 0x1028 +#define VENDOR_HP 0x103c +#define VENDOR_HP_COMPAQ 0x0e11 +#define VENDOR_APPLE 0x106b +#define VENDOR_SI_IMAGE 0x1095 +#define VENDOR_BUFFALO 0x1154 +#define VENDOR_TI 0x104c +#define VENDOR_RICOH 0x1180 +#define VENDOR_JMICRON 0x197b + + + +#define VENDOR_BROADCOM_PCMCIA 0x02d0 + + +#define VENDOR_BROADCOM_SDIO 0x00BF + + +#define BCM_DNGL_VID 0x0a5c +#define BCM_DNGL_BL_PID_4328 0xbd12 +#define BCM_DNGL_BL_PID_4322 0xbd13 +#define BCM_DNGL_BL_PID_4319 0xbd16 +#define BCM_DNGL_BL_PID_43236 0xbd17 +#define BCM_DNGL_BL_PID_4332 0xbd18 +#define BCM_DNGL_BL_PID_4330 0xbd19 +#define BCM_DNGL_BL_PID_4334 0xbd1a +#define BCM_DNGL_BL_PID_43239 0xbd1b +#define BCM_DNGL_BL_PID_4324 0xbd1c +#define BCM_DNGL_BL_PID_4360 0xbd1d + +#define BCM_DNGL_BDC_PID 0x0bdc +#define BCM_DNGL_JTAG_PID 0x4a44 + + +#define BCM_HWUSB_PID_43239 43239 + + +#define BCM4210_DEVICE_ID 0x1072 +#define BCM4230_DEVICE_ID 0x1086 +#define BCM4401_ENET_ID 0x170c +#define BCM3352_DEVICE_ID 0x3352 +#define BCM3360_DEVICE_ID 0x3360 +#define BCM4211_DEVICE_ID 0x4211 +#define BCM4231_DEVICE_ID 0x4231 +#define BCM4303_D11B_ID 0x4303 +#define BCM4311_D11G_ID 0x4311 +#define BCM4311_D11DUAL_ID 0x4312 +#define BCM4311_D11A_ID 0x4313 +#define BCM4328_D11DUAL_ID 0x4314 +#define BCM4328_D11G_ID 0x4315 +#define BCM4328_D11A_ID 0x4316 +#define BCM4318_D11G_ID 0x4318 +#define BCM4318_D11DUAL_ID 0x4319 +#define BCM4318_D11A_ID 0x431a +#define BCM4325_D11DUAL_ID 0x431b +#define BCM4325_D11G_ID 0x431c +#define BCM4325_D11A_ID 0x431d +#define BCM4306_D11G_ID 0x4320 +#define BCM4306_D11A_ID 0x4321 +#define BCM4306_UART_ID 0x4322 +#define BCM4306_V90_ID 0x4323 +#define BCM4306_D11DUAL_ID 0x4324 +#define BCM4306_D11G_ID2 0x4325 +#define BCM4321_D11N_ID 0x4328 +#define BCM4321_D11N2G_ID 0x4329 +#define BCM4321_D11N5G_ID 0x432a +#define BCM4322_D11N_ID 0x432b +#define BCM4322_D11N2G_ID 0x432c +#define BCM4322_D11N5G_ID 0x432d +#define BCM4329_D11N_ID 0x432e +#define BCM4329_D11N2G_ID 0x432f +#define BCM4329_D11N5G_ID 0x4330 +#define BCM4315_D11DUAL_ID 0x4334 +#define BCM4315_D11G_ID 0x4335 +#define BCM4315_D11A_ID 0x4336 +#define BCM4319_D11N_ID 0x4337 +#define BCM4319_D11N2G_ID 0x4338 +#define BCM4319_D11N5G_ID 0x4339 +#define BCM43231_D11N2G_ID 0x4340 +#define BCM43221_D11N2G_ID 0x4341 +#define BCM43222_D11N_ID 0x4350 +#define BCM43222_D11N2G_ID 0x4351 +#define BCM43222_D11N5G_ID 0x4352 +#define BCM43224_D11N_ID 0x4353 +#define BCM43224_D11N_ID_VEN1 0x0576 +#define BCM43226_D11N_ID 0x4354 +#define BCM43236_D11N_ID 0x4346 +#define BCM43236_D11N2G_ID 0x4347 +#define BCM43236_D11N5G_ID 0x4348 +#define BCM43225_D11N2G_ID 0x4357 +#define BCM43421_D11N_ID 0xA99D +#define BCM4313_D11N2G_ID 0x4727 +#define BCM4330_D11N_ID 0x4360 +#define BCM4330_D11N2G_ID 0x4361 +#define BCM4330_D11N5G_ID 0x4362 +#define BCM4336_D11N_ID 0x4343 +#define BCM6362_D11N_ID 0x435f +#define BCM4331_D11N_ID 0x4331 +#define BCM4331_D11N2G_ID 0x4332 +#define BCM4331_D11N5G_ID 0x4333 +#define BCM43237_D11N_ID 0x4355 +#define BCM43237_D11N5G_ID 0x4356 +#define BCM43227_D11N2G_ID 0x4358 +#define BCM43228_D11N_ID 0x4359 +#define BCM43228_D11N5G_ID 0x435a +#define BCM43362_D11N_ID 0x4363 +#define BCM43239_D11N_ID 0x4370 +#define BCM4324_D11N_ID 0x4374 +#define BCM43217_D11N2G_ID 0x43a9 +#define BCM43131_D11N2G_ID 0x43aa +#define BCM4314_D11N2G_ID 0x4364 +#define BCM43142_D11N2G_ID 0x4365 +#define BCM4334_D11N_ID 0x4380 +#define BCM4334_D11N2G_ID 0x4381 +#define BCM4334_D11N5G_ID 0x4382 +#define BCM43341_D11N_ID 0x4386 +#define BCM43341_D11N2G_ID 0x4387 +#define BCM43341_D11N5G_ID 0x4388 +#define BCM4360_D11AC_ID 0x43a0 +#define BCM4360_D11AC2G_ID 0x43a1 +#define BCM4360_D11AC5G_ID 0x43a2 + + +#define BCM943228HMB_SSID_VEN1 0x0607 +#define BCM94313HMGBL_SSID_VEN1 0x0608 +#define BCM94313HMG_SSID_VEN1 0x0609 + + +#define BCM4335_D11AC_ID 0x43ae +#define BCM4335_D11AC2G_ID 0x43af +#define BCM4335_D11AC5G_ID 0x43b0 +#define BCM4352_D11AC_ID 0x43b1 +#define BCM4352_D11AC2G_ID 0x43b2 +#define BCM4352_D11AC5G_ID 0x43b3 + +#define BCMGPRS_UART_ID 0x4333 +#define BCMGPRS2_UART_ID 0x4344 +#define FPGA_JTAGM_ID 0x43f0 +#define BCM_JTAGM_ID 0x43f1 +#define SDIOH_FPGA_ID 0x43f2 +#define BCM_SDIOH_ID 0x43f3 +#define SDIOD_FPGA_ID 0x43f4 +#define SPIH_FPGA_ID 0x43f5 +#define BCM_SPIH_ID 0x43f6 +#define MIMO_FPGA_ID 0x43f8 +#define BCM_JTAGM2_ID 0x43f9 +#define SDHCI_FPGA_ID 0x43fa +#define BCM4402_ENET_ID 0x4402 +#define BCM4402_V90_ID 0x4403 +#define BCM4410_DEVICE_ID 0x4410 +#define BCM4412_DEVICE_ID 0x4412 +#define BCM4430_DEVICE_ID 0x4430 +#define BCM4432_DEVICE_ID 0x4432 +#define BCM4704_ENET_ID 0x4706 +#define BCM4710_DEVICE_ID 0x4710 +#define BCM47XX_AUDIO_ID 0x4711 +#define BCM47XX_V90_ID 0x4712 +#define BCM47XX_ENET_ID 0x4713 +#define BCM47XX_EXT_ID 0x4714 +#define BCM47XX_GMAC_ID 0x4715 +#define BCM47XX_USBH_ID 0x4716 +#define BCM47XX_USBD_ID 0x4717 +#define BCM47XX_IPSEC_ID 0x4718 +#define BCM47XX_ROBO_ID 0x4719 +#define BCM47XX_USB20H_ID 0x471a +#define BCM47XX_USB20D_ID 0x471b +#define BCM47XX_ATA100_ID 0x471d +#define BCM47XX_SATAXOR_ID 0x471e +#define BCM47XX_GIGETH_ID 0x471f +#define BCM4712_MIPS_ID 0x4720 +#define BCM4716_DEVICE_ID 0x4722 +#define BCM47XX_SMBUS_EMU_ID 0x47fe +#define BCM47XX_XOR_EMU_ID 0x47ff +#define EPI41210_DEVICE_ID 0xa0fa +#define EPI41230_DEVICE_ID 0xa10e +#define JINVANI_SDIOH_ID 0x4743 +#define BCM27XX_SDIOH_ID 0x2702 +#define PCIXX21_FLASHMEDIA_ID 0x803b +#define PCIXX21_SDIOH_ID 0x803c +#define R5C822_SDIOH_ID 0x0822 +#define JMICRON_SDIOH_ID 0x2381 + + +#define BCM4306_CHIP_ID 0x4306 +#define BCM4311_CHIP_ID 0x4311 +#define BCM43111_CHIP_ID 43111 +#define BCM43112_CHIP_ID 43112 +#define BCM4312_CHIP_ID 0x4312 +#define BCM4313_CHIP_ID 0x4313 +#define BCM43131_CHIP_ID 43131 +#define BCM4315_CHIP_ID 0x4315 +#define BCM4318_CHIP_ID 0x4318 +#define BCM4319_CHIP_ID 0x4319 +#define BCM4320_CHIP_ID 0x4320 +#define BCM4321_CHIP_ID 0x4321 +#define BCM43217_CHIP_ID 43217 +#define BCM4322_CHIP_ID 0x4322 +#define BCM43221_CHIP_ID 43221 +#define BCM43222_CHIP_ID 43222 +#define BCM43224_CHIP_ID 43224 +#define BCM43225_CHIP_ID 43225 +#define BCM43227_CHIP_ID 43227 +#define BCM43228_CHIP_ID 43228 +#define BCM43226_CHIP_ID 43226 +#define BCM43231_CHIP_ID 43231 +#define BCM43234_CHIP_ID 43234 +#define BCM43235_CHIP_ID 43235 +#define BCM43236_CHIP_ID 43236 +#define BCM43237_CHIP_ID 43237 +#define BCM43238_CHIP_ID 43238 +#define BCM43239_CHIP_ID 43239 +#define BCM43420_CHIP_ID 43420 +#define BCM43421_CHIP_ID 43421 +#define BCM43428_CHIP_ID 43428 +#define BCM43431_CHIP_ID 43431 +#define BCM43460_CHIP_ID 43460 +#define BCM4325_CHIP_ID 0x4325 +#define BCM4328_CHIP_ID 0x4328 +#define BCM4329_CHIP_ID 0x4329 +#define BCM4331_CHIP_ID 0x4331 +#define BCM4336_CHIP_ID 0x4336 +#define BCM43362_CHIP_ID 43362 +#define BCM4330_CHIP_ID 0x4330 +#define BCM6362_CHIP_ID 0x6362 +#define BCM4314_CHIP_ID 0x4314 +#define BCM43142_CHIP_ID 43142 +#define BCM4324_CHIP_ID 0x4324 +#define BCM43242_CHIP_ID 43242 +#define BCM4334_CHIP_ID 0x4334 +#define BCM4360_CHIP_ID 0x4360 +#define BCM4352_CHIP_ID 0x4352 +#define BCM43526_CHIP_ID 0xAA06 +#define BCM43341_CHIP_ID 43341 +#define BCM43342_CHIP_ID 43342 + +#define BCM4335_CHIP_ID 0x4335 + +#define BCM4342_CHIP_ID 4342 +#define BCM4402_CHIP_ID 0x4402 +#define BCM4704_CHIP_ID 0x4704 +#define BCM4706_CHIP_ID 0x5300 +#define BCM4710_CHIP_ID 0x4710 +#define BCM4712_CHIP_ID 0x4712 +#define BCM4716_CHIP_ID 0x4716 +#define BCM47162_CHIP_ID 47162 +#define BCM4748_CHIP_ID 0x4748 +#define BCM4749_CHIP_ID 0x4749 +#define BCM4785_CHIP_ID 0x4785 +#define BCM5350_CHIP_ID 0x5350 +#define BCM5352_CHIP_ID 0x5352 +#define BCM5354_CHIP_ID 0x5354 +#define BCM5365_CHIP_ID 0x5365 +#define BCM5356_CHIP_ID 0x5356 +#define BCM5357_CHIP_ID 0x5357 +#define BCM53572_CHIP_ID 53572 + + +#define BCM4303_PKG_ID 2 +#define BCM4309_PKG_ID 1 +#define BCM4712LARGE_PKG_ID 0 +#define BCM4712SMALL_PKG_ID 1 +#define BCM4712MID_PKG_ID 2 +#define BCM4328USBD11G_PKG_ID 2 +#define BCM4328USBDUAL_PKG_ID 3 +#define BCM4328SDIOD11G_PKG_ID 4 +#define BCM4328SDIODUAL_PKG_ID 5 +#define BCM4329_289PIN_PKG_ID 0 +#define BCM4329_182PIN_PKG_ID 1 +#define BCM5354E_PKG_ID 1 +#define BCM4716_PKG_ID 8 +#define BCM4717_PKG_ID 9 +#define BCM4718_PKG_ID 10 +#define BCM5356_PKG_NONMODE 1 +#define BCM5358U_PKG_ID 8 +#define BCM5358_PKG_ID 9 +#define BCM47186_PKG_ID 10 +#define BCM5357_PKG_ID 11 +#define BCM5356U_PKG_ID 12 +#define BCM53572_PKG_ID 8 +#define BCM5357C0_PKG_ID 8 +#define BCM47188_PKG_ID 9 +#define BCM5358C0_PKG_ID 0xa +#define BCM5356C0_PKG_ID 0xb +#define BCM4331TT_PKG_ID 8 +#define BCM4331TN_PKG_ID 9 +#define BCM4331TNA0_PKG_ID 0xb +#define BCM4706L_PKG_ID 1 + +#define HDLSIM5350_PKG_ID 1 +#define HDLSIM_PKG_ID 14 +#define HWSIM_PKG_ID 15 +#define BCM43224_FAB_CSM 0x8 +#define BCM43224_FAB_SMIC 0xa +#define BCM4336_WLBGA_PKG_ID 0x8 +#define BCM4330_WLBGA_PKG_ID 0x0 +#define BCM4314PCIE_ARM_PKG_ID (8 | 0) +#define BCM4314SDIO_PKG_ID (8 | 1) +#define BCM4314PCIE_PKG_ID (8 | 2) +#define BCM4314SDIO_ARM_PKG_ID (8 | 3) +#define BCM4314SDIO_FPBGA_PKG_ID (8 | 4) +#define BCM4314DEV_PKG_ID (8 | 6) + +#define PCIXX21_FLASHMEDIA0_ID 0x8033 +#define PCIXX21_SDIOH0_ID 0x8034 + + +#define BFL_BTC2WIRE 0x00000001 +#define BFL_BTCOEX 0x00000001 +#define BFL_PACTRL 0x00000002 +#define BFL_AIRLINEMODE 0x00000004 +#define BFL_ADCDIV 0x00000008 +#define BFL_RFPLL 0x00000008 +#define BFL_ENETROBO 0x00000010 +#define BFL_NOPLLDOWN 0x00000020 +#define BFL_CCKHIPWR 0x00000040 +#define BFL_ENETADM 0x00000080 +#define BFL_ENETVLAN 0x00000100 +#define BFL_UNUSED 0x00000200 +#define BFL_NOPCI 0x00000400 +#define BFL_FEM 0x00000800 +#define BFL_EXTLNA 0x00001000 +#define BFL_HGPA 0x00002000 +#define BFL_BTC2WIRE_ALTGPIO 0x00004000 +#define BFL_ALTIQ 0x00008000 +#define BFL_NOPA 0x00010000 +#define BFL_RSSIINV 0x00020000 +#define BFL_PAREF 0x00040000 +#define BFL_3TSWITCH 0x00080000 +#define BFL_PHASESHIFT 0x00100000 +#define BFL_BUCKBOOST 0x00200000 +#define BFL_FEM_BT 0x00400000 +#define BFL_NOCBUCK 0x00800000 +#define BFL_CCKFAVOREVM 0x01000000 +#define BFL_PALDO 0x02000000 +#define BFL_LNLDO2_2P5 0x04000000 +#define BFL_FASTPWR 0x08000000 +#define BFL_UCPWRCTL_MININDX 0x08000000 +#define BFL_EXTLNA_5GHz 0x10000000 +#define BFL_TRSW_1by2 0x20000000 +#define BFL_LO_TRSW_R_5GHz 0x40000000 +#define BFL_ELNA_GAINDEF 0x80000000 +#define BFL_EXTLNA_TX 0x20000000 + + +#define BFL2_RXBB_INT_REG_DIS 0x00000001 +#define BFL2_APLL_WAR 0x00000002 +#define BFL2_TXPWRCTRL_EN 0x00000004 +#define BFL2_2X4_DIV 0x00000008 +#define BFL2_5G_PWRGAIN 0x00000010 +#define BFL2_PCIEWAR_OVR 0x00000020 +#define BFL2_CAESERS_BRD 0x00000040 +#define BFL2_BTC3WIRE 0x00000080 +#define BFL2_BTCLEGACY 0x00000080 +#define BFL2_SKWRKFEM_BRD 0x00000100 +#define BFL2_SPUR_WAR 0x00000200 +#define BFL2_GPLL_WAR 0x00000400 +#define BFL2_TRISTATE_LED 0x00000800 +#define BFL2_SINGLEANT_CCK 0x00001000 +#define BFL2_2G_SPUR_WAR 0x00002000 +#define BFL2_BPHY_ALL_TXCORES 0x00004000 +#define BFL2_FCC_BANDEDGE_WAR 0x00008000 +#define BFL2_GPLL_WAR2 0x00010000 +#define BFL2_IPALVLSHIFT_3P3 0x00020000 +#define BFL2_INTERNDET_TXIQCAL 0x00040000 +#define BFL2_XTALBUFOUTEN 0x00080000 + + + +#define BFL2_ANAPACTRL_2G 0x00100000 +#define BFL2_ANAPACTRL_5G 0x00200000 +#define BFL2_ELNACTRL_TRSW_2G 0x00400000 +#define BFL2_BT_SHARE_ANT0 0x00800000 +#define BFL2_TEMPSENSE_HIGHER 0x01000000 +#define BFL2_BTC3WIREONLY 0x02000000 +#define BFL2_PWR_NOMINAL 0x04000000 +#define BFL2_EXTLNA_PWRSAVE 0x08000000 + +#define BFL2_4313_RADIOREG 0x10000000 + +#define BFL2_SDR_EN 0x20000000 + + +#define BOARD_GPIO_BTC3W_IN 0x850 +#define BOARD_GPIO_BTC3W_OUT 0x020 +#define BOARD_GPIO_BTCMOD_IN 0x010 +#define BOARD_GPIO_BTCMOD_OUT 0x020 +#define BOARD_GPIO_BTC_IN 0x080 +#define BOARD_GPIO_BTC_OUT 0x100 +#define BOARD_GPIO_PACTRL 0x200 +#define BOARD_GPIO_12 0x1000 +#define BOARD_GPIO_13 0x2000 +#define BOARD_GPIO_BTC4_IN 0x0800 +#define BOARD_GPIO_BTC4_BT 0x2000 +#define BOARD_GPIO_BTC4_STAT 0x4000 +#define BOARD_GPIO_BTC4_WLAN 0x8000 +#define BOARD_GPIO_1_WLAN_PWR 0x02 +#define BOARD_GPIO_3_WLAN_PWR 0x08 +#define BOARD_GPIO_4_WLAN_PWR 0x10 + +#define GPIO_BTC4W_OUT_4312 0x010 +#define GPIO_BTC4W_OUT_43224 0x020 +#define GPIO_BTC4W_OUT_43224_SHARED 0x0e0 +#define GPIO_BTC4W_OUT_43225 0x0e0 +#define GPIO_BTC4W_OUT_43421 0x020 +#define GPIO_BTC4W_OUT_4313 0x060 +#define GPIO_BTC4W_OUT_4331_SHARED 0x010 + +#define PCI_CFG_GPIO_SCS 0x10 +#define PCI_CFG_GPIO_HWRAD 0x20 +#define PCI_CFG_GPIO_XTAL 0x40 +#define PCI_CFG_GPIO_PLL 0x80 + + +#define PLL_DELAY 150 +#define FREF_DELAY 200 +#define MIN_SLOW_CLK 32 +#define XTAL_ON_DELAY 1000 + + + +#define BCM943341WLABGS_SSID 0x062d + + +#define GPIO_NUMPINS 32 + + +#define RDL_RAM_BASE_4319 0x60000000 +#define RDL_RAM_BASE_4329 0x60000000 +#define RDL_RAM_SIZE_4319 0x48000 +#define RDL_RAM_SIZE_4329 0x48000 +#define RDL_RAM_SIZE_43236 0x70000 +#define RDL_RAM_BASE_43236 0x60000000 +#define RDL_RAM_SIZE_4328 0x60000 +#define RDL_RAM_BASE_4328 0x80000000 +#define RDL_RAM_SIZE_4322 0x60000 +#define RDL_RAM_BASE_4322 0x60000000 + + +#define MUXENAB_UART 0x00000001 +#define MUXENAB_GPIO 0x00000002 +#define MUXENAB_ERCX 0x00000004 +#define MUXENAB_JTAG 0x00000008 +#define MUXENAB_HOST_WAKE 0x00000010 +#define MUXENAB_I2S_EN 0x00000020 +#define MUXENAB_I2S_MASTER 0x00000040 +#define MUXENAB_I2S_FULL 0x00000080 +#define MUXENAB_SFLASH 0x00000100 +#define MUXENAB_RFSWCTRL0 0x00000200 +#define MUXENAB_RFSWCTRL1 0x00000400 +#define MUXENAB_RFSWCTRL2 0x00000800 +#define MUXENAB_SECI 0x00001000 +#define MUXENAB_BT_LEGACY 0x00002000 +#define MUXENAB_HOST_WAKE1 0x00004000 + + +#define FLASH_KERNEL_NFLASH 0x00000001 +#define FLASH_BOOT_NFLASH 0x00000002 + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/bcmendian.h b/drivers/net/wireless/bcmdhd/include/bcmendian.h new file mode 100644 index 00000000..22eb7dbc --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmendian.h @@ -0,0 +1,278 @@ +/* + * Byte order utilities + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmendian.h 241182 2011-02-17 21:50:03Z $ + * + * This file by default provides proper behavior on little-endian architectures. + * On big-endian architectures, IL_BIGENDIAN should be defined. + */ + +#ifndef _BCMENDIAN_H_ +#define _BCMENDIAN_H_ + +#include + + +#define BCMSWAP16(val) \ + ((uint16)((((uint16)(val) & (uint16)0x00ffU) << 8) | \ + (((uint16)(val) & (uint16)0xff00U) >> 8))) + + +#define BCMSWAP32(val) \ + ((uint32)((((uint32)(val) & (uint32)0x000000ffU) << 24) | \ + (((uint32)(val) & (uint32)0x0000ff00U) << 8) | \ + (((uint32)(val) & (uint32)0x00ff0000U) >> 8) | \ + (((uint32)(val) & (uint32)0xff000000U) >> 24))) + + +#define BCMSWAP32BY16(val) \ + ((uint32)((((uint32)(val) & (uint32)0x0000ffffU) << 16) | \ + (((uint32)(val) & (uint32)0xffff0000U) >> 16))) + + +#ifndef hton16 +#define HTON16(i) BCMSWAP16(i) +#define hton16(i) bcmswap16(i) +#define HTON32(i) BCMSWAP32(i) +#define hton32(i) bcmswap32(i) +#define NTOH16(i) BCMSWAP16(i) +#define ntoh16(i) bcmswap16(i) +#define NTOH32(i) BCMSWAP32(i) +#define ntoh32(i) bcmswap32(i) +#define LTOH16(i) (i) +#define ltoh16(i) (i) +#define LTOH32(i) (i) +#define ltoh32(i) (i) +#define HTOL16(i) (i) +#define htol16(i) (i) +#define HTOL32(i) (i) +#define htol32(i) (i) +#endif + +#define ltoh16_buf(buf, i) +#define htol16_buf(buf, i) + + +#define load32_ua(a) ltoh32_ua(a) +#define store32_ua(a, v) htol32_ua_store(v, a) +#define load16_ua(a) ltoh16_ua(a) +#define store16_ua(a, v) htol16_ua_store(v, a) + +#define _LTOH16_UA(cp) ((cp)[0] | ((cp)[1] << 8)) +#define _LTOH32_UA(cp) ((cp)[0] | ((cp)[1] << 8) | ((cp)[2] << 16) | ((cp)[3] << 24)) +#define _NTOH16_UA(cp) (((cp)[0] << 8) | (cp)[1]) +#define _NTOH32_UA(cp) (((cp)[0] << 24) | ((cp)[1] << 16) | ((cp)[2] << 8) | (cp)[3]) + +#define ltoh_ua(ptr) \ + (sizeof(*(ptr)) == sizeof(uint8) ? *(const uint8 *)(ptr) : \ + sizeof(*(ptr)) == sizeof(uint16) ? _LTOH16_UA((const uint8 *)(ptr)) : \ + sizeof(*(ptr)) == sizeof(uint32) ? _LTOH32_UA((const uint8 *)(ptr)) : \ + *(uint8 *)0) + +#define ntoh_ua(ptr) \ + (sizeof(*(ptr)) == sizeof(uint8) ? *(const uint8 *)(ptr) : \ + sizeof(*(ptr)) == sizeof(uint16) ? _NTOH16_UA((const uint8 *)(ptr)) : \ + sizeof(*(ptr)) == sizeof(uint32) ? _NTOH32_UA((const uint8 *)(ptr)) : \ + *(uint8 *)0) + +#ifdef __GNUC__ + + + +#define bcmswap16(val) ({ \ + uint16 _val = (val); \ + BCMSWAP16(_val); \ +}) + +#define bcmswap32(val) ({ \ + uint32 _val = (val); \ + BCMSWAP32(_val); \ +}) + +#define bcmswap32by16(val) ({ \ + uint32 _val = (val); \ + BCMSWAP32BY16(_val); \ +}) + +#define bcmswap16_buf(buf, len) ({ \ + uint16 *_buf = (uint16 *)(buf); \ + uint _wds = (len) / 2; \ + while (_wds--) { \ + *_buf = bcmswap16(*_buf); \ + _buf++; \ + } \ +}) + +#define htol16_ua_store(val, bytes) ({ \ + uint16 _val = (val); \ + uint8 *_bytes = (uint8 *)(bytes); \ + _bytes[0] = _val & 0xff; \ + _bytes[1] = _val >> 8; \ +}) + +#define htol32_ua_store(val, bytes) ({ \ + uint32 _val = (val); \ + uint8 *_bytes = (uint8 *)(bytes); \ + _bytes[0] = _val & 0xff; \ + _bytes[1] = (_val >> 8) & 0xff; \ + _bytes[2] = (_val >> 16) & 0xff; \ + _bytes[3] = _val >> 24; \ +}) + +#define hton16_ua_store(val, bytes) ({ \ + uint16 _val = (val); \ + uint8 *_bytes = (uint8 *)(bytes); \ + _bytes[0] = _val >> 8; \ + _bytes[1] = _val & 0xff; \ +}) + +#define hton32_ua_store(val, bytes) ({ \ + uint32 _val = (val); \ + uint8 *_bytes = (uint8 *)(bytes); \ + _bytes[0] = _val >> 24; \ + _bytes[1] = (_val >> 16) & 0xff; \ + _bytes[2] = (_val >> 8) & 0xff; \ + _bytes[3] = _val & 0xff; \ +}) + +#define ltoh16_ua(bytes) ({ \ + const uint8 *_bytes = (const uint8 *)(bytes); \ + _LTOH16_UA(_bytes); \ +}) + +#define ltoh32_ua(bytes) ({ \ + const uint8 *_bytes = (const uint8 *)(bytes); \ + _LTOH32_UA(_bytes); \ +}) + +#define ntoh16_ua(bytes) ({ \ + const uint8 *_bytes = (const uint8 *)(bytes); \ + _NTOH16_UA(_bytes); \ +}) + +#define ntoh32_ua(bytes) ({ \ + const uint8 *_bytes = (const uint8 *)(bytes); \ + _NTOH32_UA(_bytes); \ +}) + +#else + + +static INLINE uint16 +bcmswap16(uint16 val) +{ + return BCMSWAP16(val); +} + +static INLINE uint32 +bcmswap32(uint32 val) +{ + return BCMSWAP32(val); +} + +static INLINE uint32 +bcmswap32by16(uint32 val) +{ + return BCMSWAP32BY16(val); +} + + + + +static INLINE void +bcmswap16_buf(uint16 *buf, uint len) +{ + len = len / 2; + + while (len--) { + *buf = bcmswap16(*buf); + buf++; + } +} + + +static INLINE void +htol16_ua_store(uint16 val, uint8 *bytes) +{ + bytes[0] = val & 0xff; + bytes[1] = val >> 8; +} + + +static INLINE void +htol32_ua_store(uint32 val, uint8 *bytes) +{ + bytes[0] = val & 0xff; + bytes[1] = (val >> 8) & 0xff; + bytes[2] = (val >> 16) & 0xff; + bytes[3] = val >> 24; +} + + +static INLINE void +hton16_ua_store(uint16 val, uint8 *bytes) +{ + bytes[0] = val >> 8; + bytes[1] = val & 0xff; +} + + +static INLINE void +hton32_ua_store(uint32 val, uint8 *bytes) +{ + bytes[0] = val >> 24; + bytes[1] = (val >> 16) & 0xff; + bytes[2] = (val >> 8) & 0xff; + bytes[3] = val & 0xff; +} + + +static INLINE uint16 +ltoh16_ua(const void *bytes) +{ + return _LTOH16_UA((const uint8 *)bytes); +} + + +static INLINE uint32 +ltoh32_ua(const void *bytes) +{ + return _LTOH32_UA((const uint8 *)bytes); +} + + +static INLINE uint16 +ntoh16_ua(const void *bytes) +{ + return _NTOH16_UA((const uint8 *)bytes); +} + + +static INLINE uint32 +ntoh32_ua(const void *bytes) +{ + return _NTOH32_UA((const uint8 *)bytes); +} + +#endif +#endif diff --git a/drivers/net/wireless/bcmdhd/include/bcmpcispi.h b/drivers/net/wireless/bcmdhd/include/bcmpcispi.h new file mode 100644 index 00000000..44b263ce --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmpcispi.h @@ -0,0 +1,181 @@ +/* + * Broadcom PCI-SPI Host Controller Register Definitions + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmpcispi.h 241182 2011-02-17 21:50:03Z $ + */ +#ifndef _BCM_PCI_SPI_H +#define _BCM_PCI_SPI_H + +/* cpp contortions to concatenate w/arg prescan */ +#ifndef PAD +#define _PADLINE(line) pad ## line +#define _XSTR(line) _PADLINE(line) +#define PAD _XSTR(__LINE__) +#endif /* PAD */ + + +typedef volatile struct { + uint32 spih_ctrl; /* 0x00 SPI Control Register */ + uint32 spih_stat; /* 0x04 SPI Status Register */ + uint32 spih_data; /* 0x08 SPI Data Register, 32-bits wide */ + uint32 spih_ext; /* 0x0C SPI Extension Register */ + uint32 PAD[4]; /* 0x10-0x1F PADDING */ + + uint32 spih_gpio_ctrl; /* 0x20 SPI GPIO Control Register */ + uint32 spih_gpio_data; /* 0x24 SPI GPIO Data Register */ + uint32 PAD[6]; /* 0x28-0x3F PADDING */ + + uint32 spih_int_edge; /* 0x40 SPI Interrupt Edge Register (0=Level, 1=Edge) */ + uint32 spih_int_pol; /* 0x44 SPI Interrupt Polarity Register (0=Active Low, */ + /* 1=Active High) */ + uint32 spih_int_mask; /* 0x48 SPI Interrupt Mask */ + uint32 spih_int_status; /* 0x4C SPI Interrupt Status */ + uint32 PAD[4]; /* 0x50-0x5F PADDING */ + + uint32 spih_hex_disp; /* 0x60 SPI 4-digit hex display value */ + uint32 spih_current_ma; /* 0x64 SPI SD card current consumption in mA */ + uint32 PAD[1]; /* 0x68 PADDING */ + uint32 spih_disp_sel; /* 0x6c SPI 4-digit hex display mode select (1=current) */ + uint32 PAD[4]; /* 0x70-0x7F PADDING */ + uint32 PAD[8]; /* 0x80-0x9F PADDING */ + uint32 PAD[8]; /* 0xA0-0xBF PADDING */ + uint32 spih_pll_ctrl; /* 0xC0 PLL Control Register */ + uint32 spih_pll_status; /* 0xC4 PLL Status Register */ + uint32 spih_xtal_freq; /* 0xC8 External Clock Frequency in units of 10000Hz */ + uint32 spih_clk_count; /* 0xCC External Clock Count Register */ + +} spih_regs_t; + +typedef volatile struct { + uint32 cfg_space[0x40]; /* 0x000-0x0FF PCI Configuration Space (Read Only) */ + uint32 P_IMG_CTRL0; /* 0x100 PCI Image0 Control Register */ + + uint32 P_BA0; /* 0x104 32 R/W PCI Image0 Base Address register */ + uint32 P_AM0; /* 0x108 32 R/W PCI Image0 Address Mask register */ + uint32 P_TA0; /* 0x10C 32 R/W PCI Image0 Translation Address register */ + uint32 P_IMG_CTRL1; /* 0x110 32 R/W PCI Image1 Control register */ + uint32 P_BA1; /* 0x114 32 R/W PCI Image1 Base Address register */ + uint32 P_AM1; /* 0x118 32 R/W PCI Image1 Address Mask register */ + uint32 P_TA1; /* 0x11C 32 R/W PCI Image1 Translation Address register */ + uint32 P_IMG_CTRL2; /* 0x120 32 R/W PCI Image2 Control register */ + uint32 P_BA2; /* 0x124 32 R/W PCI Image2 Base Address register */ + uint32 P_AM2; /* 0x128 32 R/W PCI Image2 Address Mask register */ + uint32 P_TA2; /* 0x12C 32 R/W PCI Image2 Translation Address register */ + uint32 P_IMG_CTRL3; /* 0x130 32 R/W PCI Image3 Control register */ + uint32 P_BA3; /* 0x134 32 R/W PCI Image3 Base Address register */ + uint32 P_AM3; /* 0x138 32 R/W PCI Image3 Address Mask register */ + uint32 P_TA3; /* 0x13C 32 R/W PCI Image3 Translation Address register */ + uint32 P_IMG_CTRL4; /* 0x140 32 R/W PCI Image4 Control register */ + uint32 P_BA4; /* 0x144 32 R/W PCI Image4 Base Address register */ + uint32 P_AM4; /* 0x148 32 R/W PCI Image4 Address Mask register */ + uint32 P_TA4; /* 0x14C 32 R/W PCI Image4 Translation Address register */ + uint32 P_IMG_CTRL5; /* 0x150 32 R/W PCI Image5 Control register */ + uint32 P_BA5; /* 0x154 32 R/W PCI Image5 Base Address register */ + uint32 P_AM5; /* 0x158 32 R/W PCI Image5 Address Mask register */ + uint32 P_TA5; /* 0x15C 32 R/W PCI Image5 Translation Address register */ + uint32 P_ERR_CS; /* 0x160 32 R/W PCI Error Control and Status register */ + uint32 P_ERR_ADDR; /* 0x164 32 R PCI Erroneous Address register */ + uint32 P_ERR_DATA; /* 0x168 32 R PCI Erroneous Data register */ + + uint32 PAD[5]; /* 0x16C-0x17F PADDING */ + + uint32 WB_CONF_SPC_BAR; /* 0x180 32 R WISHBONE Configuration Space Base Address */ + uint32 W_IMG_CTRL1; /* 0x184 32 R/W WISHBONE Image1 Control register */ + uint32 W_BA1; /* 0x188 32 R/W WISHBONE Image1 Base Address register */ + uint32 W_AM1; /* 0x18C 32 R/W WISHBONE Image1 Address Mask register */ + uint32 W_TA1; /* 0x190 32 R/W WISHBONE Image1 Translation Address reg */ + uint32 W_IMG_CTRL2; /* 0x194 32 R/W WISHBONE Image2 Control register */ + uint32 W_BA2; /* 0x198 32 R/W WISHBONE Image2 Base Address register */ + uint32 W_AM2; /* 0x19C 32 R/W WISHBONE Image2 Address Mask register */ + uint32 W_TA2; /* 0x1A0 32 R/W WISHBONE Image2 Translation Address reg */ + uint32 W_IMG_CTRL3; /* 0x1A4 32 R/W WISHBONE Image3 Control register */ + uint32 W_BA3; /* 0x1A8 32 R/W WISHBONE Image3 Base Address register */ + uint32 W_AM3; /* 0x1AC 32 R/W WISHBONE Image3 Address Mask register */ + uint32 W_TA3; /* 0x1B0 32 R/W WISHBONE Image3 Translation Address reg */ + uint32 W_IMG_CTRL4; /* 0x1B4 32 R/W WISHBONE Image4 Control register */ + uint32 W_BA4; /* 0x1B8 32 R/W WISHBONE Image4 Base Address register */ + uint32 W_AM4; /* 0x1BC 32 R/W WISHBONE Image4 Address Mask register */ + uint32 W_TA4; /* 0x1C0 32 R/W WISHBONE Image4 Translation Address reg */ + uint32 W_IMG_CTRL5; /* 0x1C4 32 R/W WISHBONE Image5 Control register */ + uint32 W_BA5; /* 0x1C8 32 R/W WISHBONE Image5 Base Address register */ + uint32 W_AM5; /* 0x1CC 32 R/W WISHBONE Image5 Address Mask register */ + uint32 W_TA5; /* 0x1D0 32 R/W WISHBONE Image5 Translation Address reg */ + uint32 W_ERR_CS; /* 0x1D4 32 R/W WISHBONE Error Control and Status reg */ + uint32 W_ERR_ADDR; /* 0x1D8 32 R WISHBONE Erroneous Address register */ + uint32 W_ERR_DATA; /* 0x1DC 32 R WISHBONE Erroneous Data register */ + uint32 CNF_ADDR; /* 0x1E0 32 R/W Configuration Cycle register */ + uint32 CNF_DATA; /* 0x1E4 32 R/W Configuration Cycle Generation Data reg */ + + uint32 INT_ACK; /* 0x1E8 32 R Interrupt Acknowledge register */ + uint32 ICR; /* 0x1EC 32 R/W Interrupt Control register */ + uint32 ISR; /* 0x1F0 32 R/W Interrupt Status register */ +} spih_pciregs_t; + +/* + * PCI Core interrupt enable and status bit definitions. + */ + +/* PCI Core ICR Register bit definitions */ +#define PCI_INT_PROP_EN (1 << 0) /* Interrupt Propagation Enable */ +#define PCI_WB_ERR_INT_EN (1 << 1) /* Wishbone Error Interrupt Enable */ +#define PCI_PCI_ERR_INT_EN (1 << 2) /* PCI Error Interrupt Enable */ +#define PCI_PAR_ERR_INT_EN (1 << 3) /* Parity Error Interrupt Enable */ +#define PCI_SYS_ERR_INT_EN (1 << 4) /* System Error Interrupt Enable */ +#define PCI_SOFTWARE_RESET (1U << 31) /* Software reset of the PCI Core. */ + + +/* PCI Core ISR Register bit definitions */ +#define PCI_INT_PROP_ST (1 << 0) /* Interrupt Propagation Status */ +#define PCI_WB_ERR_INT_ST (1 << 1) /* Wishbone Error Interrupt Status */ +#define PCI_PCI_ERR_INT_ST (1 << 2) /* PCI Error Interrupt Status */ +#define PCI_PAR_ERR_INT_ST (1 << 3) /* Parity Error Interrupt Status */ +#define PCI_SYS_ERR_INT_ST (1 << 4) /* System Error Interrupt Status */ + + +/* Registers on the Wishbone bus */ +#define SPIH_CTLR_INTR (1 << 0) /* SPI Host Controller Core Interrupt */ +#define SPIH_DEV_INTR (1 << 1) /* SPI Device Interrupt */ +#define SPIH_WFIFO_INTR (1 << 2) /* SPI Tx FIFO Empty Intr (FPGA Rev >= 8) */ + +/* GPIO Bit definitions */ +#define SPIH_CS (1 << 0) /* SPI Chip Select (active low) */ +#define SPIH_SLOT_POWER (1 << 1) /* SD Card Slot Power Enable */ +#define SPIH_CARD_DETECT (1 << 2) /* SD Card Detect */ + +/* SPI Status Register Bit definitions */ +#define SPIH_STATE_MASK 0x30 /* SPI Transfer State Machine state mask */ +#define SPIH_STATE_SHIFT 4 /* SPI Transfer State Machine state shift */ +#define SPIH_WFFULL (1 << 3) /* SPI Write FIFO Full */ +#define SPIH_WFEMPTY (1 << 2) /* SPI Write FIFO Empty */ +#define SPIH_RFFULL (1 << 1) /* SPI Read FIFO Full */ +#define SPIH_RFEMPTY (1 << 0) /* SPI Read FIFO Empty */ + +#define SPIH_EXT_CLK (1U << 31) /* Use External Clock as PLL Clock source. */ + +#define SPIH_PLL_NO_CLK (1 << 1) /* Set to 1 if the PLL's input clock is lost. */ +#define SPIH_PLL_LOCKED (1 << 3) /* Set to 1 when the PLL is locked. */ + +/* Spin bit loop bound check */ +#define SPI_SPIN_BOUND 0xf4240 /* 1 million */ + +#endif /* _BCM_PCI_SPI_H */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmperf.h b/drivers/net/wireless/bcmdhd/include/bcmperf.h new file mode 100644 index 00000000..74383076 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmperf.h @@ -0,0 +1,36 @@ +/* + * Performance counters software interface. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmperf.h 241182 2011-02-17 21:50:03Z $ + */ +/* essai */ +#ifndef _BCMPERF_H_ +#define _BCMPERF_H_ +/* get cache hits and misses */ +#define BCMPERF_ENABLE_INSTRCOUNT() +#define BCMPERF_ENABLE_ICACHE_MISS() +#define BCMPERF_ENABLE_ICACHE_HIT() +#define BCMPERF_GETICACHE_MISS(x) ((x) = 0) +#define BCMPERF_GETICACHE_HIT(x) ((x) = 0) +#define BCMPERF_GETINSTRCOUNT(x) ((x) = 0) +#endif /* _BCMPERF_H_ */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdbus.h b/drivers/net/wireless/bcmdhd/include/bcmsdbus.h new file mode 100644 index 00000000..2fa706db --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmsdbus.h @@ -0,0 +1,152 @@ +/* + * Definitions for API from sdio common code (bcmsdh) to individual + * host controller drivers. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmsdbus.h 347614 2012-07-27 10:24:51Z $ + */ + +#ifndef _sdio_api_h_ +#define _sdio_api_h_ + + +#define SDIOH_API_RC_SUCCESS (0x00) +#define SDIOH_API_RC_FAIL (0x01) +#define SDIOH_API_SUCCESS(status) (status == 0) + +#define SDIOH_READ 0 /* Read request */ +#define SDIOH_WRITE 1 /* Write request */ + +#define SDIOH_DATA_FIX 0 /* Fixed addressing */ +#define SDIOH_DATA_INC 1 /* Incremental addressing */ + +#define SDIOH_CMD_TYPE_NORMAL 0 /* Normal command */ +#define SDIOH_CMD_TYPE_APPEND 1 /* Append command */ +#define SDIOH_CMD_TYPE_CUTTHRU 2 /* Cut-through command */ + +#define SDIOH_DATA_PIO 0 /* PIO mode */ +#define SDIOH_DATA_DMA 1 /* DMA mode */ + +#ifdef BCMSDIOH_TXGLOM +/* Max number of glommed pkts */ +#define SDPCM_MAXGLOM_SIZE 10 +#define SDPCM_DEFGLOM_SIZE 3 + +#define SDPCM_TXGLOM_CPY 0 /* SDIO 2.0 should use copy mode */ +#define SDPCM_TXGLOM_MDESC 1 /* SDIO 3.0 should use multi-desc mode */ +#endif + + +typedef int SDIOH_API_RC; + +/* SDio Host structure */ +typedef struct sdioh_info sdioh_info_t; + +/* callback function, taking one arg */ +typedef void (*sdioh_cb_fn_t)(void *); + +/* attach, return handler on success, NULL if failed. + * The handler shall be provided by all subsequent calls. No local cache + * cfghdl points to the starting address of pci device mapped memory + */ +extern sdioh_info_t * sdioh_attach(osl_t *osh, void *cfghdl, uint irq); +extern SDIOH_API_RC sdioh_detach(osl_t *osh, sdioh_info_t *si); +extern SDIOH_API_RC sdioh_interrupt_register(sdioh_info_t *si, sdioh_cb_fn_t fn, void *argh); +extern SDIOH_API_RC sdioh_interrupt_deregister(sdioh_info_t *si); + +/* query whether SD interrupt is enabled or not */ +extern SDIOH_API_RC sdioh_interrupt_query(sdioh_info_t *si, bool *onoff); + +/* enable or disable SD interrupt */ +extern SDIOH_API_RC sdioh_interrupt_set(sdioh_info_t *si, bool enable_disable); + +#if defined(DHD_DEBUG) +extern bool sdioh_interrupt_pending(sdioh_info_t *si); +#endif + +/* read or write one byte using cmd52 */ +extern SDIOH_API_RC sdioh_request_byte(sdioh_info_t *si, uint rw, uint fnc, uint addr, uint8 *byte); + +/* read or write 2/4 bytes using cmd53 */ +extern SDIOH_API_RC sdioh_request_word(sdioh_info_t *si, uint cmd_type, uint rw, uint fnc, + uint addr, uint32 *word, uint nbyte); + +/* read or write any buffer using cmd53 */ +extern SDIOH_API_RC sdioh_request_buffer(sdioh_info_t *si, uint pio_dma, uint fix_inc, + uint rw, uint fnc_num, uint32 addr, uint regwidth, uint32 buflen, uint8 *buffer, + void *pkt); + +#ifdef BCMSDIOH_TXGLOM +extern void sdioh_glom_post(sdioh_info_t *sd, uint8 *frame, uint len); +extern void sdioh_glom_clear(sdioh_info_t *sd); +extern uint sdioh_set_mode(sdioh_info_t *sd, uint mode); +extern bool sdioh_glom_enabled(void); +#else +#define sdioh_glom_post(a, b, c) +#define sdioh_glom_clear(a) +#define sdioh_set_mode(a) (0) +#define sdioh_glom_enabled() (FALSE) +#endif + +/* get cis data */ +extern SDIOH_API_RC sdioh_cis_read(sdioh_info_t *si, uint fuc, uint8 *cis, uint32 length); + +extern SDIOH_API_RC sdioh_cfg_read(sdioh_info_t *si, uint fuc, uint32 addr, uint8 *data); +extern SDIOH_API_RC sdioh_cfg_write(sdioh_info_t *si, uint fuc, uint32 addr, uint8 *data); + +/* query number of io functions */ +extern uint sdioh_query_iofnum(sdioh_info_t *si); + +/* handle iovars */ +extern int sdioh_iovar_op(sdioh_info_t *si, const char *name, + void *params, int plen, void *arg, int len, bool set); + +/* Issue abort to the specified function and clear controller as needed */ +extern int sdioh_abort(sdioh_info_t *si, uint fnc); + +/* Start and Stop SDIO without re-enumerating the SD card. */ +extern int sdioh_start(sdioh_info_t *si, int stage); +extern int sdioh_stop(sdioh_info_t *si); + +/* Wait system lock free */ +extern int sdioh_waitlockfree(sdioh_info_t *si); + +/* Reset and re-initialize the device */ +extern int sdioh_sdio_reset(sdioh_info_t *si); + +/* Helper function */ +void *bcmsdh_get_sdioh(bcmsdh_info_t *sdh); + + + +#if defined(BCMSDIOH_STD) + #define SDIOH_SLEEP_ENABLED +#endif +extern SDIOH_API_RC sdioh_sleep(sdioh_info_t *si, bool enab); + +/* GPIO support */ +extern SDIOH_API_RC sdioh_gpio_init(sdioh_info_t *sd); +extern bool sdioh_gpioin(sdioh_info_t *sd, uint32 gpio); +extern SDIOH_API_RC sdioh_gpioouten(sdioh_info_t *sd, uint32 gpio); +extern SDIOH_API_RC sdioh_gpioout(sdioh_info_t *sd, uint32 gpio, bool enab); + +#endif /* _sdio_api_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdh.h b/drivers/net/wireless/bcmdhd/include/bcmsdh.h new file mode 100644 index 00000000..1c8a6b3e --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmsdh.h @@ -0,0 +1,242 @@ +/* + * SDIO host client driver interface of Broadcom HNBU + * export functions to client drivers + * abstract OS and BUS specific details of SDIO + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmsdh.h 347614 2012-07-27 10:24:51Z $ + */ + +/** + * @file bcmsdh.h + */ + +#ifndef _bcmsdh_h_ +#define _bcmsdh_h_ + +#define BCMSDH_ERROR_VAL 0x0001 /* Error */ +#define BCMSDH_INFO_VAL 0x0002 /* Info */ +extern const uint bcmsdh_msglevel; + +#define BCMSDH_ERROR(x) +#define BCMSDH_INFO(x) + +#if (defined(BCMSDIOH_STD) || defined(BCMSDIOH_BCM) || defined(BCMSDIOH_SPI)) +#define BCMSDH_ADAPTER +#endif /* BCMSDIO && (BCMSDIOH_STD || BCMSDIOH_BCM || BCMSDIOH_SPI) */ + +/* forward declarations */ +typedef struct bcmsdh_info bcmsdh_info_t; +typedef void (*bcmsdh_cb_fn_t)(void *); + +/* Attach and build an interface to the underlying SD host driver. + * - Allocates resources (structs, arrays, mem, OS handles, etc) needed by bcmsdh. + * - Returns the bcmsdh handle and virtual address base for register access. + * The returned handle should be used in all subsequent calls, but the bcmsh + * implementation may maintain a single "default" handle (e.g. the first or + * most recent one) to enable single-instance implementations to pass NULL. + */ + +#if 0 && (NDISVER >= 0x0630) && 1 +extern bcmsdh_info_t *bcmsdh_attach(osl_t *osh, void *cfghdl, + void **regsva, uint irq, shared_info_t *sh); +#else +extern bcmsdh_info_t *bcmsdh_attach(osl_t *osh, void *cfghdl, void **regsva, uint irq); +#endif + +/* Detach - freeup resources allocated in attach */ +extern int bcmsdh_detach(osl_t *osh, void *sdh); + +/* Query if SD device interrupts are enabled */ +extern bool bcmsdh_intr_query(void *sdh); + +/* Enable/disable SD interrupt */ +extern int bcmsdh_intr_enable(void *sdh); +extern int bcmsdh_intr_disable(void *sdh); + +/* Register/deregister device interrupt handler. */ +extern int bcmsdh_intr_reg(void *sdh, bcmsdh_cb_fn_t fn, void *argh); +extern int bcmsdh_intr_dereg(void *sdh); +/* Enable/disable SD card interrupt forward */ +extern void bcmsdh_intr_forward(void *sdh, bool pass); + +#if defined(DHD_DEBUG) +/* Query pending interrupt status from the host controller */ +extern bool bcmsdh_intr_pending(void *sdh); +#endif + +/* Register a callback to be called if and when bcmsdh detects + * device removal. No-op in the case of non-removable/hardwired devices. + */ +extern int bcmsdh_devremove_reg(void *sdh, bcmsdh_cb_fn_t fn, void *argh); + +/* Access SDIO address space (e.g. CCCR) using CMD52 (single-byte interface). + * fn: function number + * addr: unmodified SDIO-space address + * data: data byte to write + * err: pointer to error code (or NULL) + */ +extern uint8 bcmsdh_cfg_read(void *sdh, uint func, uint32 addr, int *err); +extern void bcmsdh_cfg_write(void *sdh, uint func, uint32 addr, uint8 data, int *err); + +/* Read/Write 4bytes from/to cfg space */ +extern uint32 bcmsdh_cfg_read_word(void *sdh, uint fnc_num, uint32 addr, int *err); +extern void bcmsdh_cfg_write_word(void *sdh, uint fnc_num, uint32 addr, uint32 data, int *err); + +/* Read CIS content for specified function. + * fn: function whose CIS is being requested (0 is common CIS) + * cis: pointer to memory location to place results + * length: number of bytes to read + * Internally, this routine uses the values from the cis base regs (0x9-0xB) + * to form an SDIO-space address to read the data from. + */ +extern int bcmsdh_cis_read(void *sdh, uint func, uint8 *cis, uint length); + +/* Synchronous access to device (client) core registers via CMD53 to F1. + * addr: backplane address (i.e. >= regsva from attach) + * size: register width in bytes (2 or 4) + * data: data for register write + */ +extern uint32 bcmsdh_reg_read(void *sdh, uint32 addr, uint size); +extern uint32 bcmsdh_reg_write(void *sdh, uint32 addr, uint size, uint32 data); + +/* set sb address window */ +extern int bcmsdhsdio_set_sbaddr_window(void *sdh, uint32 address, bool force_set); + +/* Indicate if last reg read/write failed */ +extern bool bcmsdh_regfail(void *sdh); + +/* Buffer transfer to/from device (client) core via cmd53. + * fn: function number + * addr: backplane address (i.e. >= regsva from attach) + * flags: backplane width, address increment, sync/async + * buf: pointer to memory data buffer + * nbytes: number of bytes to transfer to/from buf + * pkt: pointer to packet associated with buf (if any) + * complete: callback function for command completion (async only) + * handle: handle for completion callback (first arg in callback) + * Returns 0 or error code. + * NOTE: Async operation is not currently supported. + */ +typedef void (*bcmsdh_cmplt_fn_t)(void *handle, int status, bool sync_waiting); +extern int bcmsdh_send_buf(void *sdh, uint32 addr, uint fn, uint flags, + uint8 *buf, uint nbytes, void *pkt, + bcmsdh_cmplt_fn_t complete_fn, void *handle); +extern int bcmsdh_recv_buf(void *sdh, uint32 addr, uint fn, uint flags, + uint8 *buf, uint nbytes, void *pkt, + bcmsdh_cmplt_fn_t complete_fn, void *handle); + +extern void bcmsdh_glom_post(void *sdh, uint8 *frame, uint len); +extern void bcmsdh_glom_clear(void *sdh); +extern uint bcmsdh_set_mode(void *sdh, uint mode); +extern bool bcmsdh_glom_enabled(void); +/* Flags bits */ +#define SDIO_REQ_4BYTE 0x1 /* Four-byte target (backplane) width (vs. two-byte) */ +#define SDIO_REQ_FIXED 0x2 /* Fixed address (FIFO) (vs. incrementing address) */ +#define SDIO_REQ_ASYNC 0x4 /* Async request (vs. sync request) */ +#define SDIO_BYTE_MODE 0x8 /* Byte mode request(non-block mode) */ + +/* Pending (non-error) return code */ +#define BCME_PENDING 1 + +/* Read/write to memory block (F1, no FIFO) via CMD53 (sync only). + * rw: read or write (0/1) + * addr: direct SDIO address + * buf: pointer to memory data buffer + * nbytes: number of bytes to transfer to/from buf + * Returns 0 or error code. + */ +extern int bcmsdh_rwdata(void *sdh, uint rw, uint32 addr, uint8 *buf, uint nbytes); + +/* Issue an abort to the specified function */ +extern int bcmsdh_abort(void *sdh, uint fn); + +/* Start SDIO Host Controller communication */ +extern int bcmsdh_start(void *sdh, int stage); + +/* Stop SDIO Host Controller communication */ +extern int bcmsdh_stop(void *sdh); + +/* Wait system lock free */ +extern int bcmsdh_waitlockfree(void *sdh); + +/* Returns the "Device ID" of target device on the SDIO bus. */ +extern int bcmsdh_query_device(void *sdh); + +/* Returns the number of IO functions reported by the device */ +extern uint bcmsdh_query_iofnum(void *sdh); + +/* Miscellaneous knob tweaker. */ +extern int bcmsdh_iovar_op(void *sdh, const char *name, + void *params, int plen, void *arg, int len, bool set); + +/* Reset and reinitialize the device */ +extern int bcmsdh_reset(bcmsdh_info_t *sdh); + +/* helper functions */ + +extern void *bcmsdh_get_sdioh(bcmsdh_info_t *sdh); + +/* callback functions */ +typedef struct { + /* attach to device */ + void *(*attach)(uint16 vend_id, uint16 dev_id, uint16 bus, uint16 slot, + uint16 func, uint bustype, void * regsva, osl_t * osh, + void * param); + /* detach from device */ + void (*detach)(void *ch); +} bcmsdh_driver_t; + +/* platform specific/high level functions */ +extern int bcmsdh_register(bcmsdh_driver_t *driver); +extern void bcmsdh_unregister(void); +extern bool bcmsdh_chipmatch(uint16 vendor, uint16 device); +extern void bcmsdh_device_remove(void * sdh); + +extern int bcmsdh_reg_sdio_notify(void* semaphore); +extern void bcmsdh_unreg_sdio_notify(void); + +#if defined(OOB_INTR_ONLY) +extern int bcmsdh_register_oob_intr(void * dhdp); +extern void bcmsdh_unregister_oob_intr(void); +extern void bcmsdh_oob_intr_set(bool enable); +#endif /* defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) */ + +/* Function to pass device-status bits to DHD. */ +extern uint32 bcmsdh_get_dstatus(void *sdh); + +/* Function to return current window addr */ +extern uint32 bcmsdh_cur_sbwad(void *sdh); + +/* Function to pass chipid and rev to lower layers for controlling pr's */ +extern void bcmsdh_chipinfo(void *sdh, uint32 chip, uint32 chiprev); + + +extern int bcmsdh_sleep(void *sdh, bool enab); + +/* GPIO support */ +extern int bcmsdh_gpio_init(void *sd); +extern bool bcmsdh_gpioin(void *sd, uint32 gpio); +extern int bcmsdh_gpioouten(void *sd, uint32 gpio); +extern int bcmsdh_gpioout(void *sd, uint32 gpio, bool enab); + +#endif /* _bcmsdh_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h b/drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h new file mode 100644 index 00000000..0e11b11e --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h @@ -0,0 +1,125 @@ +/* + * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmsdh_sdmmc.h 355594 2012-09-07 10:22:02Z $ + */ + +#ifndef __BCMSDH_SDMMC_H__ +#define __BCMSDH_SDMMC_H__ + +#define sd_err(x) +#define sd_trace(x) +#define sd_info(x) +#define sd_debug(x) +#define sd_data(x) +#define sd_ctrl(x) + +#define sd_trace_hw4 sd_trace + +#define sd_sync_dma(sd, read, nbytes) +#define sd_init_dma(sd) +#define sd_ack_intr(sd) +#define sd_wakeup(sd); + +/* Allocate/init/free per-OS private data */ +extern int sdioh_sdmmc_osinit(sdioh_info_t *sd); +extern void sdioh_sdmmc_osfree(sdioh_info_t *sd); + +#define sd_log(x) + +#define SDIOH_ASSERT(exp) \ + do { if (!(exp)) \ + printf("!!!ASSERT fail: file %s lines %d", __FILE__, __LINE__); \ + } while (0) + +#define BLOCK_SIZE_4318 64 +#define BLOCK_SIZE_4328 512 + +/* internal return code */ +#define SUCCESS 0 +#define ERROR 1 + +/* private bus modes */ +#define SDIOH_MODE_SD4 2 +#define CLIENT_INTR 0x100 /* Get rid of this! */ + +struct sdioh_info { + osl_t *osh; /* osh handler */ + bool client_intr_enabled; /* interrupt connnected flag */ + bool intr_handler_valid; /* client driver interrupt handler valid */ + sdioh_cb_fn_t intr_handler; /* registered interrupt handler */ + void *intr_handler_arg; /* argument to call interrupt handler */ + uint16 intmask; /* Current active interrupts */ + void *sdos_info; /* Pointer to per-OS private data */ + + uint irq; /* Client irq */ + int intrcount; /* Client interrupts */ + + bool sd_use_dma; /* DMA on CMD53 */ + bool sd_blockmode; /* sd_blockmode == FALSE => 64 Byte Cmd 53s. */ + /* Must be on for sd_multiblock to be effective */ + bool use_client_ints; /* If this is false, make sure to restore */ + int sd_mode; /* SD1/SD4/SPI */ + int client_block_size[SDIOD_MAX_IOFUNCS]; /* Blocksize */ + uint8 num_funcs; /* Supported funcs on client */ + uint32 com_cis_ptr; + uint32 func_cis_ptr[SDIOD_MAX_IOFUNCS]; + +#define SDIOH_SDMMC_MAX_SG_ENTRIES 32 + struct scatterlist sg_list[SDIOH_SDMMC_MAX_SG_ENTRIES]; + bool use_rxchain; +}; + +/************************************************************ + * Internal interfaces: per-port references into bcmsdh_sdmmc.c + */ + +/* Global message bits */ +extern uint sd_msglevel; + +/* OS-independent interrupt handler */ +extern bool check_client_intr(sdioh_info_t *sd); + +/* Core interrupt enable/disable of device interrupts */ +extern void sdioh_sdmmc_devintr_on(sdioh_info_t *sd); +extern void sdioh_sdmmc_devintr_off(sdioh_info_t *sd); + + +/************************************************************** + * Internal interfaces: bcmsdh_sdmmc.c references to per-port code + */ + +/* Register mapping routines */ +extern uint32 *sdioh_sdmmc_reg_map(osl_t *osh, int32 addr, int size); +extern void sdioh_sdmmc_reg_unmap(osl_t *osh, int32 addr, int size); + +/* Interrupt (de)registration routines */ +extern int sdioh_sdmmc_register_irq(sdioh_info_t *sd, uint irq); +extern void sdioh_sdmmc_free_irq(uint irq, sdioh_info_t *sd); + +typedef struct _BCMSDH_SDMMC_INSTANCE { + sdioh_info_t *sd; + struct sdio_func *func[SDIOD_MAX_IOFUNCS]; +} BCMSDH_SDMMC_INSTANCE, *PBCMSDH_SDMMC_INSTANCE; + +#endif /* __BCMSDH_SDMMC_H__ */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdpcm.h b/drivers/net/wireless/bcmdhd/include/bcmsdpcm.h new file mode 100644 index 00000000..fb2ec3af --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmsdpcm.h @@ -0,0 +1,281 @@ +/* + * Broadcom SDIO/PCMCIA + * Software-specific definitions shared between device and host side + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmsdpcm.h 362722 2012-10-12 23:55:55Z $ + */ + +#ifndef _bcmsdpcm_h_ +#define _bcmsdpcm_h_ + +/* + * Software allocation of To SB Mailbox resources + */ + +/* intstatus bits */ +#define I_SMB_NAK I_SMB_SW0 /* To SB Mailbox Frame NAK */ +#define I_SMB_INT_ACK I_SMB_SW1 /* To SB Mailbox Host Interrupt ACK */ +#define I_SMB_USE_OOB I_SMB_SW2 /* To SB Mailbox Use OOB Wakeup */ +#define I_SMB_DEV_INT I_SMB_SW3 /* To SB Mailbox Miscellaneous Interrupt */ + +#define I_TOSBMAIL (I_SMB_NAK | I_SMB_INT_ACK | I_SMB_USE_OOB | I_SMB_DEV_INT) + +/* tosbmailbox bits corresponding to intstatus bits */ +#define SMB_NAK (1 << 0) /* To SB Mailbox Frame NAK */ +#define SMB_INT_ACK (1 << 1) /* To SB Mailbox Host Interrupt ACK */ +#define SMB_USE_OOB (1 << 2) /* To SB Mailbox Use OOB Wakeup */ +#define SMB_DEV_INT (1 << 3) /* To SB Mailbox Miscellaneous Interrupt */ +#define SMB_MASK 0x0000000f /* To SB Mailbox Mask */ + +/* tosbmailboxdata */ +#define SMB_DATA_VERSION_MASK 0x00ff0000 /* host protocol version (sent with F2 enable) */ +#define SMB_DATA_VERSION_SHIFT 16 /* host protocol version (sent with F2 enable) */ + +/* + * Software allocation of To Host Mailbox resources + */ + +/* intstatus bits */ +#define I_HMB_FC_STATE I_HMB_SW0 /* To Host Mailbox Flow Control State */ +#define I_HMB_FC_CHANGE I_HMB_SW1 /* To Host Mailbox Flow Control State Changed */ +#define I_HMB_FRAME_IND I_HMB_SW2 /* To Host Mailbox Frame Indication */ +#define I_HMB_HOST_INT I_HMB_SW3 /* To Host Mailbox Miscellaneous Interrupt */ + +#define I_TOHOSTMAIL (I_HMB_FC_CHANGE | I_HMB_FRAME_IND | I_HMB_HOST_INT) + +/* tohostmailbox bits corresponding to intstatus bits */ +#define HMB_FC_ON (1 << 0) /* To Host Mailbox Flow Control State */ +#define HMB_FC_CHANGE (1 << 1) /* To Host Mailbox Flow Control State Changed */ +#define HMB_FRAME_IND (1 << 2) /* To Host Mailbox Frame Indication */ +#define HMB_HOST_INT (1 << 3) /* To Host Mailbox Miscellaneous Interrupt */ +#define HMB_MASK 0x0000000f /* To Host Mailbox Mask */ + +/* tohostmailboxdata */ +#define HMB_DATA_NAKHANDLED 0x01 /* we're ready to retransmit NAK'd frame to host */ +#define HMB_DATA_DEVREADY 0x02 /* we're ready to to talk to host after enable */ +#define HMB_DATA_FC 0x04 /* per prio flowcontrol update flag to host */ +#define HMB_DATA_FWREADY 0x08 /* firmware is ready for protocol activity */ +#define HMB_DATA_FWHALT 0x10 /* firmware has halted operation */ + +#define HMB_DATA_FCDATA_MASK 0xff000000 /* per prio flowcontrol data */ +#define HMB_DATA_FCDATA_SHIFT 24 /* per prio flowcontrol data */ + +#define HMB_DATA_VERSION_MASK 0x00ff0000 /* device protocol version (with devready) */ +#define HMB_DATA_VERSION_SHIFT 16 /* device protocol version (with devready) */ + +/* + * Software-defined protocol header + */ + +/* Current protocol version */ +#define SDPCM_PROT_VERSION 4 + +/* SW frame header */ +#define SDPCM_SEQUENCE_MASK 0x000000ff /* Sequence Number Mask */ +#define SDPCM_PACKET_SEQUENCE(p) (((uint8 *)p)[0] & 0xff) /* p starts w/SW Header */ + +#define SDPCM_CHANNEL_MASK 0x00000f00 /* Channel Number Mask */ +#define SDPCM_CHANNEL_SHIFT 8 /* Channel Number Shift */ +#define SDPCM_PACKET_CHANNEL(p) (((uint8 *)p)[1] & 0x0f) /* p starts w/SW Header */ + +#define SDPCM_FLAGS_MASK 0x0000f000 /* Mask of flag bits */ +#define SDPCM_FLAGS_SHIFT 12 /* Flag bits shift */ +#define SDPCM_PACKET_FLAGS(p) ((((uint8 *)p)[1] & 0xf0) >> 4) /* p starts w/SW Header */ + +/* Next Read Len: lookahead length of next frame, in 16-byte units (rounded up) */ +#define SDPCM_NEXTLEN_MASK 0x00ff0000 /* Next Read Len Mask */ +#define SDPCM_NEXTLEN_SHIFT 16 /* Next Read Len Shift */ +#define SDPCM_NEXTLEN_VALUE(p) ((((uint8 *)p)[2] & 0xff) << 4) /* p starts w/SW Header */ +#define SDPCM_NEXTLEN_OFFSET 2 + +/* Data Offset from SOF (HW Tag, SW Tag, Pad) */ +#define SDPCM_DOFFSET_OFFSET 3 /* Data Offset */ +#define SDPCM_DOFFSET_VALUE(p) (((uint8 *)p)[SDPCM_DOFFSET_OFFSET] & 0xff) +#define SDPCM_DOFFSET_MASK 0xff000000 +#define SDPCM_DOFFSET_SHIFT 24 + +#define SDPCM_FCMASK_OFFSET 4 /* Flow control */ +#define SDPCM_FCMASK_VALUE(p) (((uint8 *)p)[SDPCM_FCMASK_OFFSET ] & 0xff) +#define SDPCM_WINDOW_OFFSET 5 /* Credit based fc */ +#define SDPCM_WINDOW_VALUE(p) (((uint8 *)p)[SDPCM_WINDOW_OFFSET] & 0xff) +#define SDPCM_VERSION_OFFSET 6 /* Version # */ +#define SDPCM_VERSION_VALUE(p) (((uint8 *)p)[SDPCM_VERSION_OFFSET] & 0xff) +#define SDPCM_UNUSED_OFFSET 7 /* Spare */ +#define SDPCM_UNUSED_VALUE(p) (((uint8 *)p)[SDPCM_UNUSED_OFFSET] & 0xff) + +#define SDPCM_SWHEADER_LEN 8 /* SW header is 64 bits */ + +/* logical channel numbers */ +#define SDPCM_CONTROL_CHANNEL 0 /* Control Request/Response Channel Id */ +#define SDPCM_EVENT_CHANNEL 1 /* Asyc Event Indication Channel Id */ +#define SDPCM_DATA_CHANNEL 2 /* Data Xmit/Recv Channel Id */ +#define SDPCM_GLOM_CHANNEL 3 /* For coalesced packets (superframes) */ +#define SDPCM_TEST_CHANNEL 15 /* Reserved for test/debug packets */ +#define SDPCM_MAX_CHANNEL 15 + +#define SDPCM_SEQUENCE_WRAP 256 /* wrap-around val for eight-bit frame seq number */ + +#define SDPCM_FLAG_RESVD0 0x01 +#define SDPCM_FLAG_RESVD1 0x02 +#define SDPCM_FLAG_GSPI_TXENAB 0x04 +#define SDPCM_FLAG_GLOMDESC 0x08 /* Superframe descriptor mask */ + +/* For GLOM_CHANNEL frames, use a flag to indicate descriptor frame */ +#define SDPCM_GLOMDESC_FLAG (SDPCM_FLAG_GLOMDESC << SDPCM_FLAGS_SHIFT) + +#define SDPCM_GLOMDESC(p) (((uint8 *)p)[1] & 0x80) + +/* For TEST_CHANNEL packets, define another 4-byte header */ +#define SDPCM_TEST_HDRLEN 4 /* Generally: Cmd(1), Ext(1), Len(2); + * Semantics of Ext byte depend on command. + * Len is current or requested frame length, not + * including test header; sent little-endian. + */ +#define SDPCM_TEST_PKT_CNT_FLD_LEN 4 /* Packet count filed legth */ +#define SDPCM_TEST_DISCARD 0x01 /* Receiver discards. Ext is a pattern id. */ +#define SDPCM_TEST_ECHOREQ 0x02 /* Echo request. Ext is a pattern id. */ +#define SDPCM_TEST_ECHORSP 0x03 /* Echo response. Ext is a pattern id. */ +#define SDPCM_TEST_BURST 0x04 /* Receiver to send a burst. Ext is a frame count + * (Backward compatabilty) Set frame count in a + * 4 byte filed adjacent to the HDR + */ +#define SDPCM_TEST_SEND 0x05 /* Receiver sets send mode. Ext is boolean on/off + * Set frame count in a 4 byte filed adjacent to + * the HDR + */ + +/* Handy macro for filling in datagen packets with a pattern */ +#define SDPCM_TEST_FILL(byteno, id) ((uint8)(id + byteno)) + +/* + * Software counters (first part matches hardware counters) + */ + +typedef volatile struct { + uint32 cmd52rd; /* Cmd52RdCount, SDIO: cmd52 reads */ + uint32 cmd52wr; /* Cmd52WrCount, SDIO: cmd52 writes */ + uint32 cmd53rd; /* Cmd53RdCount, SDIO: cmd53 reads */ + uint32 cmd53wr; /* Cmd53WrCount, SDIO: cmd53 writes */ + uint32 abort; /* AbortCount, SDIO: aborts */ + uint32 datacrcerror; /* DataCrcErrorCount, SDIO: frames w/CRC error */ + uint32 rdoutofsync; /* RdOutOfSyncCount, SDIO/PCMCIA: Rd Frm out of sync */ + uint32 wroutofsync; /* RdOutOfSyncCount, SDIO/PCMCIA: Wr Frm out of sync */ + uint32 writebusy; /* WriteBusyCount, SDIO: device asserted "busy" */ + uint32 readwait; /* ReadWaitCount, SDIO: no data ready for a read cmd */ + uint32 readterm; /* ReadTermCount, SDIO: read frame termination cmds */ + uint32 writeterm; /* WriteTermCount, SDIO: write frames termination cmds */ + uint32 rxdescuflo; /* receive descriptor underflows */ + uint32 rxfifooflo; /* receive fifo overflows */ + uint32 txfifouflo; /* transmit fifo underflows */ + uint32 runt; /* runt (too short) frames recv'd from bus */ + uint32 badlen; /* frame's rxh len does not match its hw tag len */ + uint32 badcksum; /* frame's hw tag chksum doesn't agree with len value */ + uint32 seqbreak; /* break in sequence # space from one rx frame to the next */ + uint32 rxfcrc; /* frame rx header indicates crc error */ + uint32 rxfwoos; /* frame rx header indicates write out of sync */ + uint32 rxfwft; /* frame rx header indicates write frame termination */ + uint32 rxfabort; /* frame rx header indicates frame aborted */ + uint32 woosint; /* write out of sync interrupt */ + uint32 roosint; /* read out of sync interrupt */ + uint32 rftermint; /* read frame terminate interrupt */ + uint32 wftermint; /* write frame terminate interrupt */ +} sdpcmd_cnt_t; + +/* + * Register Access Macros + */ + +#define SDIODREV_IS(var, val) ((var) == (val)) +#define SDIODREV_GE(var, val) ((var) >= (val)) +#define SDIODREV_GT(var, val) ((var) > (val)) +#define SDIODREV_LT(var, val) ((var) < (val)) +#define SDIODREV_LE(var, val) ((var) <= (val)) + +#define SDIODDMAREG32(h, dir, chnl) \ + ((dir) == DMA_TX ? \ + (void *)(uintptr)&((h)->regs->dma.sdiod32.dma32regs[chnl].xmt) : \ + (void *)(uintptr)&((h)->regs->dma.sdiod32.dma32regs[chnl].rcv)) + +#define SDIODDMAREG64(h, dir, chnl) \ + ((dir) == DMA_TX ? \ + (void *)(uintptr)&((h)->regs->dma.sdiod64.dma64regs[chnl].xmt) : \ + (void *)(uintptr)&((h)->regs->dma.sdiod64.dma64regs[chnl].rcv)) + +#define SDIODDMAREG(h, dir, chnl) \ + (SDIODREV_LT((h)->corerev, 1) ? \ + SDIODDMAREG32((h), (dir), (chnl)) : \ + SDIODDMAREG64((h), (dir), (chnl))) + +#define PCMDDMAREG(h, dir, chnl) \ + ((dir) == DMA_TX ? \ + (void *)(uintptr)&((h)->regs->dma.pcm32.dmaregs.xmt) : \ + (void *)(uintptr)&((h)->regs->dma.pcm32.dmaregs.rcv)) + +#define SDPCMDMAREG(h, dir, chnl, coreid) \ + ((coreid) == SDIOD_CORE_ID ? \ + SDIODDMAREG(h, dir, chnl) : \ + PCMDDMAREG(h, dir, chnl)) + +#define SDIODFIFOREG(h, corerev) \ + (SDIODREV_LT((corerev), 1) ? \ + ((dma32diag_t *)(uintptr)&((h)->regs->dma.sdiod32.dmafifo)) : \ + ((dma32diag_t *)(uintptr)&((h)->regs->dma.sdiod64.dmafifo))) + +#define PCMDFIFOREG(h) \ + ((dma32diag_t *)(uintptr)&((h)->regs->dma.pcm32.dmafifo)) + +#define SDPCMFIFOREG(h, coreid, corerev) \ + ((coreid) == SDIOD_CORE_ID ? \ + SDIODFIFOREG(h, corerev) : \ + PCMDFIFOREG(h)) + +/* + * Shared structure between dongle and the host. + * The structure contains pointers to trap or assert information. + */ +#define SDPCM_SHARED_VERSION 0x0001 +#define SDPCM_SHARED_VERSION_MASK 0x00FF +#define SDPCM_SHARED_ASSERT_BUILT 0x0100 +#define SDPCM_SHARED_ASSERT 0x0200 +#define SDPCM_SHARED_TRAP 0x0400 +#define SDPCM_SHARED_IN_BRPT 0x0800 +#define SDPCM_SHARED_SET_BRPT 0x1000 +#define SDPCM_SHARED_PENDING_BRPT 0x2000 + +typedef struct { + uint32 flags; + uint32 trap_addr; + uint32 assert_exp_addr; + uint32 assert_file_addr; + uint32 assert_line; + uint32 console_addr; /* Address of hndrte_cons_t */ + uint32 msgtrace_addr; + uint32 brpt_addr; +} sdpcm_shared_t; + +extern sdpcm_shared_t sdpcm_shared; + +/* Function can be used to notify host of FW halt */ +extern void sdpcmd_fwhalt(void); + +#endif /* _bcmsdpcm_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdspi.h b/drivers/net/wireless/bcmdhd/include/bcmsdspi.h new file mode 100644 index 00000000..3d444f3b --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmsdspi.h @@ -0,0 +1,135 @@ +/* + * SD-SPI Protocol Conversion - BCMSDH->SPI Translation Layer + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmsdspi.h 294363 2011-11-06 23:02:20Z $ + */ +#ifndef _BCM_SD_SPI_H +#define _BCM_SD_SPI_H + +/* global msglevel for debug messages - bitvals come from sdiovar.h */ + +#define sd_err(x) +#define sd_trace(x) +#define sd_info(x) +#define sd_debug(x) +#define sd_data(x) +#define sd_ctrl(x) + +#define sd_log(x) + +#define SDIOH_ASSERT(exp) \ + do { if (!(exp)) \ + printf("!!!ASSERT fail: file %s lines %d", __FILE__, __LINE__); \ + } while (0) + +#define BLOCK_SIZE_4318 64 +#define BLOCK_SIZE_4328 512 + +/* internal return code */ +#define SUCCESS 0 +#undef ERROR +#define ERROR 1 + +/* private bus modes */ +#define SDIOH_MODE_SPI 0 + +#define USE_BLOCKMODE 0x2 /* Block mode can be single block or multi */ +#define USE_MULTIBLOCK 0x4 + +struct sdioh_info { + uint cfg_bar; /* pci cfg address for bar */ + uint32 caps; /* cached value of capabilities reg */ + uint bar0; /* BAR0 for PCI Device */ + osl_t *osh; /* osh handler */ + void *controller; /* Pointer to SPI Controller's private data struct */ + + uint lockcount; /* nest count of sdspi_lock() calls */ + bool client_intr_enabled; /* interrupt connnected flag */ + bool intr_handler_valid; /* client driver interrupt handler valid */ + sdioh_cb_fn_t intr_handler; /* registered interrupt handler */ + void *intr_handler_arg; /* argument to call interrupt handler */ + bool initialized; /* card initialized */ + uint32 target_dev; /* Target device ID */ + uint32 intmask; /* Current active interrupts */ + void *sdos_info; /* Pointer to per-OS private data */ + + uint32 controller_type; /* Host controller type */ + uint8 version; /* Host Controller Spec Compliance Version */ + uint irq; /* Client irq */ + uint32 intrcount; /* Client interrupts */ + uint32 local_intrcount; /* Controller interrupts */ + bool host_init_done; /* Controller initted */ + bool card_init_done; /* Client SDIO interface initted */ + bool polled_mode; /* polling for command completion */ + + bool sd_use_dma; /* DMA on CMD53 */ + bool sd_blockmode; /* sd_blockmode == FALSE => 64 Byte Cmd 53s. */ + /* Must be on for sd_multiblock to be effective */ + bool use_client_ints; /* If this is false, make sure to restore */ + bool got_hcint; /* Host Controller interrupt. */ + /* polling hack in wl_linux.c:wl_timer() */ + int adapter_slot; /* Maybe dealing with multiple slots/controllers */ + int sd_mode; /* SD1/SD4/SPI */ + int client_block_size[SDIOD_MAX_IOFUNCS]; /* Blocksize */ + uint32 data_xfer_count; /* Current register transfer size */ + uint32 cmd53_wr_data; /* Used to pass CMD53 write data */ + uint32 card_response; /* Used to pass back response status byte */ + uint32 card_rsp_data; /* Used to pass back response data word */ + uint16 card_rca; /* Current Address */ + uint8 num_funcs; /* Supported funcs on client */ + uint32 com_cis_ptr; + uint32 func_cis_ptr[SDIOD_MAX_IOFUNCS]; + void *dma_buf; + ulong dma_phys; + int r_cnt; /* rx count */ + int t_cnt; /* tx_count */ +}; + +/************************************************************ + * Internal interfaces: per-port references into bcmsdspi.c + */ + +/* Global message bits */ +extern uint sd_msglevel; + +/************************************************************** + * Internal interfaces: bcmsdspi.c references to per-port code + */ + +/* Register mapping routines */ +extern uint32 *spi_reg_map(osl_t *osh, uintptr addr, int size); +extern void spi_reg_unmap(osl_t *osh, uintptr addr, int size); + +/* Interrupt (de)registration routines */ +extern int spi_register_irq(sdioh_info_t *sd, uint irq); +extern void spi_free_irq(uint irq, sdioh_info_t *sd); + +/* OS-specific interrupt wrappers (atomic interrupt enable/disable) */ +extern void spi_lock(sdioh_info_t *sd); +extern void spi_unlock(sdioh_info_t *sd); + +/* Allocate/init/free per-OS private data */ +extern int spi_osinit(sdioh_info_t *sd); +extern void spi_osfree(sdioh_info_t *sd); + +#endif /* _BCM_SD_SPI_H */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdstd.h b/drivers/net/wireless/bcmdhd/include/bcmsdstd.h new file mode 100644 index 00000000..896686cf --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmsdstd.h @@ -0,0 +1,264 @@ +/* + * 'Standard' SDIO HOST CONTROLLER driver + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmsdstd.h 347614 2012-07-27 10:24:51Z $ + */ +#ifndef _BCM_SD_STD_H +#define _BCM_SD_STD_H + +/* global msglevel for debug messages - bitvals come from sdiovar.h */ +#define sd_err(x) do { if (sd_msglevel & SDH_ERROR_VAL) printf x; } while (0) +#define sd_trace(x) +#define sd_info(x) +#define sd_debug(x) +#define sd_data(x) +#define sd_ctrl(x) +#define sd_dma(x) + +#define sd_sync_dma(sd, read, nbytes) +#define sd_init_dma(sd) +#define sd_ack_intr(sd) +#define sd_wakeup(sd); +/* Allocate/init/free per-OS private data */ +extern int sdstd_osinit(sdioh_info_t *sd); +extern void sdstd_osfree(sdioh_info_t *sd); + +#define sd_log(x) + +#define SDIOH_ASSERT(exp) \ + do { if (!(exp)) \ + printf("!!!ASSERT fail: file %s lines %d", __FILE__, __LINE__); \ + } while (0) + +#define BLOCK_SIZE_4318 64 +#define BLOCK_SIZE_4328 512 + +/* internal return code */ +#define SUCCESS 0 +#define ERROR 1 + +/* private bus modes */ +#define SDIOH_MODE_SPI 0 +#define SDIOH_MODE_SD1 1 +#define SDIOH_MODE_SD4 2 + +#define MAX_SLOTS 6 /* For PCI: Only 6 BAR entries => 6 slots */ +#define SDIOH_REG_WINSZ 0x100 /* Number of registers in Standard Host Controller */ + +#define SDIOH_TYPE_ARASAN_HDK 1 +#define SDIOH_TYPE_BCM27XX 2 +#define SDIOH_TYPE_TI_PCIXX21 4 /* TI PCIxx21 Standard Host Controller */ +#define SDIOH_TYPE_RICOH_R5C822 5 /* Ricoh Co Ltd R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter */ +#define SDIOH_TYPE_JMICRON 6 /* JMicron Standard SDIO Host Controller */ + +/* For linux, allow yielding for dongle */ +#define BCMSDYIELD + +/* Expected card status value for CMD7 */ +#define SDIOH_CMD7_EXP_STATUS 0x00001E00 + +#define RETRIES_LARGE 100000 +#define sdstd_os_yield(sd) do {} while (0) +#define RETRIES_SMALL 100 + + +#define USE_BLOCKMODE 0x2 /* Block mode can be single block or multi */ +#define USE_MULTIBLOCK 0x4 + +#define USE_FIFO 0x8 /* Fifo vs non-fifo */ + +#define CLIENT_INTR 0x100 /* Get rid of this! */ + +#define HC_INTR_RETUNING 0x1000 + + +#ifdef BCMSDIOH_TXGLOM +/* Setting the MAX limit to 10 */ +#define SDIOH_MAXGLOM_SIZE 10 + +typedef struct glom_buf { + uint32 count; /* Total number of pkts queued */ + void *dma_buf_arr[SDIOH_MAXGLOM_SIZE]; /* Frame address */ + ulong dma_phys_arr[SDIOH_MAXGLOM_SIZE]; /* DMA_MAPed address of frames */ + uint16 nbytes[SDIOH_MAXGLOM_SIZE]; /* Size of each frame */ +} glom_buf_t; +#endif + +struct sdioh_info { + uint cfg_bar; /* pci cfg address for bar */ + uint32 caps; /* cached value of capabilities reg */ + uint32 curr_caps; /* max current capabilities reg */ + + osl_t *osh; /* osh handler */ + volatile char *mem_space; /* pci device memory va */ + uint lockcount; /* nest count of sdstd_lock() calls */ + bool client_intr_enabled; /* interrupt connnected flag */ + bool intr_handler_valid; /* client driver interrupt handler valid */ + sdioh_cb_fn_t intr_handler; /* registered interrupt handler */ + void *intr_handler_arg; /* argument to call interrupt handler */ + bool initialized; /* card initialized */ + uint target_dev; /* Target device ID */ + uint16 intmask; /* Current active interrupts */ + void *sdos_info; /* Pointer to per-OS private data */ + + uint32 controller_type; /* Host controller type */ + uint8 version; /* Host Controller Spec Compliance Version */ + uint irq; /* Client irq */ + int intrcount; /* Client interrupts */ + int local_intrcount; /* Controller interrupts */ + bool host_init_done; /* Controller initted */ + bool card_init_done; /* Client SDIO interface initted */ + bool polled_mode; /* polling for command completion */ + + bool sd_blockmode; /* sd_blockmode == FALSE => 64 Byte Cmd 53s. */ + /* Must be on for sd_multiblock to be effective */ + bool use_client_ints; /* If this is false, make sure to restore */ + /* polling hack in wl_linux.c:wl_timer() */ + int adapter_slot; /* Maybe dealing with multiple slots/controllers */ + int sd_mode; /* SD1/SD4/SPI */ + int client_block_size[SDIOD_MAX_IOFUNCS]; /* Blocksize */ + uint32 data_xfer_count; /* Current transfer */ + uint16 card_rca; /* Current Address */ + int8 sd_dma_mode; /* DMA Mode (PIO, SDMA, ... ADMA2) on CMD53 */ + uint8 num_funcs; /* Supported funcs on client */ + uint32 com_cis_ptr; + uint32 func_cis_ptr[SDIOD_MAX_IOFUNCS]; + void *dma_buf; /* DMA Buffer virtual address */ + ulong dma_phys; /* DMA Buffer physical address */ + void *adma2_dscr_buf; /* ADMA2 Descriptor Buffer virtual address */ + ulong adma2_dscr_phys; /* ADMA2 Descriptor Buffer physical address */ + + /* adjustments needed to make the dma align properly */ + void *dma_start_buf; + ulong dma_start_phys; + uint alloced_dma_size; + void *adma2_dscr_start_buf; + ulong adma2_dscr_start_phys; + uint alloced_adma2_dscr_size; + + int r_cnt; /* rx count */ + int t_cnt; /* tx_count */ + bool got_hcint; /* local interrupt flag */ + uint16 last_intrstatus; /* to cache intrstatus */ + int host_UHSISupported; /* whether UHSI is supported for HC. */ + int card_UHSI_voltage_Supported; /* whether UHSI is supported for + * Card in terms of Voltage [1.8 or 3.3]. + */ + int global_UHSI_Supp; /* type of UHSI support in both host and card. + * HOST_SDR_UNSUPP: capabilities not supported/matched + * HOST_SDR_12_25: SDR12 and SDR25 supported + * HOST_SDR_50_104_DDR: one of SDR50/SDR104 or DDR50 supptd + */ + volatile int sd3_dat_state; /* data transfer state used for retuning check */ + volatile int sd3_tun_state; /* tuning state used for retuning check */ + bool sd3_tuning_reqd; /* tuning requirement parameter */ + uint32 caps3; /* cached value of 32 MSbits capabilities reg (SDIO 3.0) */ +#ifdef BCMSDIOH_TXGLOM + glom_buf_t glom_info; /* pkt information used for glomming */ + uint txglom_mode; /* Txglom mode: 0 - copy, 1 - multi-descriptor */ +#endif +}; + +#define DMA_MODE_NONE 0 +#define DMA_MODE_SDMA 1 +#define DMA_MODE_ADMA1 2 +#define DMA_MODE_ADMA2 3 +#define DMA_MODE_ADMA2_64 4 +#define DMA_MODE_AUTO -1 + +#define USE_DMA(sd) ((bool)((sd->sd_dma_mode > 0) ? TRUE : FALSE)) + +/* States for Tuning and corr data */ +#define TUNING_IDLE 0 +#define TUNING_START 1 +#define TUNING_START_AFTER_DAT 2 +#define TUNING_ONGOING 3 + +#define DATA_TRANSFER_IDLE 0 +#define DATA_TRANSFER_ONGOING 1 + +#define CHECK_TUNING_PRE_DATA 1 +#define CHECK_TUNING_POST_DATA 2 + +/************************************************************ + * Internal interfaces: per-port references into bcmsdstd.c + */ + +/* Global message bits */ +extern uint sd_msglevel; + +/* OS-independent interrupt handler */ +extern bool check_client_intr(sdioh_info_t *sd); + +/* Core interrupt enable/disable of device interrupts */ +extern void sdstd_devintr_on(sdioh_info_t *sd); +extern void sdstd_devintr_off(sdioh_info_t *sd); + +/* Enable/disable interrupts for local controller events */ +extern void sdstd_intrs_on(sdioh_info_t *sd, uint16 norm, uint16 err); +extern void sdstd_intrs_off(sdioh_info_t *sd, uint16 norm, uint16 err); + +/* Wait for specified interrupt and error bits to be set */ +extern void sdstd_spinbits(sdioh_info_t *sd, uint16 norm, uint16 err); + + +/************************************************************** + * Internal interfaces: bcmsdstd.c references to per-port code + */ + +/* Register mapping routines */ +extern uint32 *sdstd_reg_map(osl_t *osh, int32 addr, int size); +extern void sdstd_reg_unmap(osl_t *osh, int32 addr, int size); + +/* Interrupt (de)registration routines */ +extern int sdstd_register_irq(sdioh_info_t *sd, uint irq); +extern void sdstd_free_irq(uint irq, sdioh_info_t *sd); + +/* OS-specific interrupt wrappers (atomic interrupt enable/disable) */ +extern void sdstd_lock(sdioh_info_t *sd); +extern void sdstd_unlock(sdioh_info_t *sd); +extern void sdstd_waitlockfree(sdioh_info_t *sd); + +/* OS-specific wait-for-interrupt-or-status */ +extern int sdstd_waitbits(sdioh_info_t *sd, uint16 norm, uint16 err, bool yield, uint16 *bits); + +/* used by bcmsdstd_linux [implemented in sdstd] */ +extern void sdstd_3_enable_retuning_int(sdioh_info_t *sd); +extern void sdstd_3_disable_retuning_int(sdioh_info_t *sd); +extern bool sdstd_3_is_retuning_int_set(sdioh_info_t *sd); +extern void sdstd_3_check_and_do_tuning(sdioh_info_t *sd, int tuning_param); +extern bool sdstd_3_check_and_set_retuning(sdioh_info_t *sd); +extern int sdstd_3_get_tune_state(sdioh_info_t *sd); +extern int sdstd_3_get_data_state(sdioh_info_t *sd); +extern void sdstd_3_set_tune_state(sdioh_info_t *sd, int state); +extern void sdstd_3_set_data_state(sdioh_info_t *sd, int state); +extern uint8 sdstd_3_get_tuning_exp(sdioh_info_t *sd); +extern uint32 sdstd_3_get_uhsi_clkmode(sdioh_info_t *sd); +extern int sdstd_3_clk_tuning(sdioh_info_t *sd, uint32 sd3ClkMode); + +/* used by sdstd [implemented in bcmsdstd_linux/ndis] */ +extern void sdstd_3_start_tuning(sdioh_info_t *sd); +extern void sdstd_3_osinit_tuning(sdioh_info_t *sd); +extern void sdstd_3_osclean_tuning(sdioh_info_t *sd); + +#endif /* _BCM_SD_STD_H */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmspi.h b/drivers/net/wireless/bcmdhd/include/bcmspi.h new file mode 100644 index 00000000..e226cb10 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmspi.h @@ -0,0 +1,40 @@ +/* + * Broadcom SPI Low-Level Hardware Driver API + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmspi.h 241182 2011-02-17 21:50:03Z $ + */ +#ifndef _BCM_SPI_H +#define _BCM_SPI_H + +extern void spi_devintr_off(sdioh_info_t *sd); +extern void spi_devintr_on(sdioh_info_t *sd); +extern bool spi_start_clock(sdioh_info_t *sd, uint16 new_sd_divisor); +extern bool spi_controller_highspeed_mode(sdioh_info_t *sd, bool hsmode); +extern bool spi_check_client_intr(sdioh_info_t *sd, int *is_dev_intr); +extern bool spi_hw_attach(sdioh_info_t *sd); +extern bool spi_hw_detach(sdioh_info_t *sd); +extern void spi_sendrecv(sdioh_info_t *sd, uint8 *msg_out, uint8 *msg_in, int msglen); +extern void spi_spinbits(sdioh_info_t *sd); +extern void spi_waitbits(sdioh_info_t *sd, bool yield); + +#endif /* _BCM_SPI_H */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmutils.h b/drivers/net/wireless/bcmdhd/include/bcmutils.h new file mode 100644 index 00000000..6db5e932 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmutils.h @@ -0,0 +1,773 @@ +/* + * Misc useful os-independent macros and functions. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmutils.h 347624 2012-07-27 10:49:56Z $ + */ + +#ifndef _bcmutils_h_ +#define _bcmutils_h_ + +#define bcm_strcpy_s(dst, noOfElements, src) strcpy((dst), (src)) +#define bcm_strncpy_s(dst, noOfElements, src, count) strncpy((dst), (src), (count)) +#define bcm_strcat_s(dst, noOfElements, src) strcat((dst), (src)) + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef PKTQ_LOG +#include +#endif + + +#define _BCM_U 0x01 +#define _BCM_L 0x02 +#define _BCM_D 0x04 +#define _BCM_C 0x08 +#define _BCM_P 0x10 +#define _BCM_S 0x20 +#define _BCM_X 0x40 +#define _BCM_SP 0x80 + +extern const unsigned char bcm_ctype[]; +#define bcm_ismask(x) (bcm_ctype[(int)(unsigned char)(x)]) + +#define bcm_isalnum(c) ((bcm_ismask(c)&(_BCM_U|_BCM_L|_BCM_D)) != 0) +#define bcm_isalpha(c) ((bcm_ismask(c)&(_BCM_U|_BCM_L)) != 0) +#define bcm_iscntrl(c) ((bcm_ismask(c)&(_BCM_C)) != 0) +#define bcm_isdigit(c) ((bcm_ismask(c)&(_BCM_D)) != 0) +#define bcm_isgraph(c) ((bcm_ismask(c)&(_BCM_P|_BCM_U|_BCM_L|_BCM_D)) != 0) +#define bcm_islower(c) ((bcm_ismask(c)&(_BCM_L)) != 0) +#define bcm_isprint(c) ((bcm_ismask(c)&(_BCM_P|_BCM_U|_BCM_L|_BCM_D|_BCM_SP)) != 0) +#define bcm_ispunct(c) ((bcm_ismask(c)&(_BCM_P)) != 0) +#define bcm_isspace(c) ((bcm_ismask(c)&(_BCM_S)) != 0) +#define bcm_isupper(c) ((bcm_ismask(c)&(_BCM_U)) != 0) +#define bcm_isxdigit(c) ((bcm_ismask(c)&(_BCM_D|_BCM_X)) != 0) +#define bcm_tolower(c) (bcm_isupper((c)) ? ((c) + 'a' - 'A') : (c)) +#define bcm_toupper(c) (bcm_islower((c)) ? ((c) + 'A' - 'a') : (c)) + + + +struct bcmstrbuf { + char *buf; + unsigned int size; + char *origbuf; + unsigned int origsize; +}; + + +#ifdef BCMDRIVER +#include + +#define GPIO_PIN_NOTDEFINED 0x20 + + +#define SPINWAIT(exp, us) { \ + uint countdown = (us) + 9; \ + while ((exp) && (countdown >= 10)) {\ + OSL_DELAY(10); \ + countdown -= 10; \ + } \ +} + + +#ifndef PKTQ_LEN_DEFAULT +#define PKTQ_LEN_DEFAULT 128 +#endif +#ifndef PKTQ_MAX_PREC +#define PKTQ_MAX_PREC 16 +#endif + +typedef struct pktq_prec { + void *head; + void *tail; + uint16 len; + uint16 max; +} pktq_prec_t; + +#ifdef PKTQ_LOG +typedef struct { + uint32 requested; + uint32 stored; + uint32 saved; + uint32 selfsaved; + uint32 full_dropped; + uint32 dropped; + uint32 sacrificed; + uint32 busy; + uint32 retry; + uint32 ps_retry; + uint32 retry_drop; + uint32 max_avail; + uint32 max_used; + uint32 queue_capacity; +} pktq_counters_t; +#endif + + +#define PKTQ_COMMON \ + uint16 num_prec; \ + uint16 hi_prec; \ + uint16 max; \ + uint16 len; + + +struct pktq { + PKTQ_COMMON + + struct pktq_prec q[PKTQ_MAX_PREC]; +#ifdef PKTQ_LOG + pktq_counters_t _prec_cnt[PKTQ_MAX_PREC]; +#endif +}; + + +struct spktq { + PKTQ_COMMON + + struct pktq_prec q[1]; +}; + +#define PKTQ_PREC_ITER(pq, prec) for (prec = (pq)->num_prec - 1; prec >= 0; prec--) + + +typedef bool (*ifpkt_cb_t)(void*, int); + +#ifdef BCMPKTPOOL +#define POOL_ENAB(pool) ((pool) && (pool)->inited) +#define SHARED_POOL (pktpool_shared) +#else +#define POOL_ENAB(bus) 0 +#define SHARED_POOL ((struct pktpool *)NULL) +#endif + +#ifndef PKTPOOL_LEN_MAX +#define PKTPOOL_LEN_MAX 40 +#endif +#define PKTPOOL_CB_MAX 3 + +struct pktpool; +typedef void (*pktpool_cb_t)(struct pktpool *pool, void *arg); +typedef struct { + pktpool_cb_t cb; + void *arg; +} pktpool_cbinfo_t; + +#ifdef BCMDBG_POOL + +#define POOL_IDLE 0 +#define POOL_RXFILL 1 +#define POOL_RXDH 2 +#define POOL_RXD11 3 +#define POOL_TXDH 4 +#define POOL_TXD11 5 +#define POOL_AMPDU 6 +#define POOL_TXENQ 7 + +typedef struct { + void *p; + uint32 cycles; + uint32 dur; +} pktpool_dbg_t; + +typedef struct { + uint8 txdh; + uint8 txd11; + uint8 enq; + uint8 rxdh; + uint8 rxd11; + uint8 rxfill; + uint8 idle; +} pktpool_stats_t; +#endif + +typedef struct pktpool { + bool inited; + uint16 r; + uint16 w; + uint16 len; + uint16 maxlen; + uint16 plen; + bool istx; + bool empty; + uint8 cbtoggle; + uint8 cbcnt; + uint8 ecbcnt; + bool emptycb_disable; + pktpool_cbinfo_t *availcb_excl; + pktpool_cbinfo_t cbs[PKTPOOL_CB_MAX]; + pktpool_cbinfo_t ecbs[PKTPOOL_CB_MAX]; + void *q[PKTPOOL_LEN_MAX + 1]; + +#ifdef BCMDBG_POOL + uint8 dbg_cbcnt; + pktpool_cbinfo_t dbg_cbs[PKTPOOL_CB_MAX]; + uint16 dbg_qlen; + pktpool_dbg_t dbg_q[PKTPOOL_LEN_MAX + 1]; +#endif +} pktpool_t; + +extern pktpool_t *pktpool_shared; + +extern int pktpool_init(osl_t *osh, pktpool_t *pktp, int *pktplen, int plen, bool istx); +extern int pktpool_deinit(osl_t *osh, pktpool_t *pktp); +extern int pktpool_fill(osl_t *osh, pktpool_t *pktp, bool minimal); +extern void* pktpool_get(pktpool_t *pktp); +extern void pktpool_free(pktpool_t *pktp, void *p); +extern int pktpool_add(pktpool_t *pktp, void *p); +extern uint16 pktpool_avail(pktpool_t *pktp); +extern int pktpool_avail_notify_normal(osl_t *osh, pktpool_t *pktp); +extern int pktpool_avail_notify_exclusive(osl_t *osh, pktpool_t *pktp, pktpool_cb_t cb); +extern int pktpool_avail_register(pktpool_t *pktp, pktpool_cb_t cb, void *arg); +extern int pktpool_empty_register(pktpool_t *pktp, pktpool_cb_t cb, void *arg); +extern int pktpool_setmaxlen(pktpool_t *pktp, uint16 maxlen); +extern int pktpool_setmaxlen_strict(osl_t *osh, pktpool_t *pktp, uint16 maxlen); +extern void pktpool_emptycb_disable(pktpool_t *pktp, bool disable); +extern bool pktpool_emptycb_disabled(pktpool_t *pktp); + +#define POOLPTR(pp) ((pktpool_t *)(pp)) +#define pktpool_len(pp) (POOLPTR(pp)->len - 1) +#define pktpool_plen(pp) (POOLPTR(pp)->plen) +#define pktpool_maxlen(pp) (POOLPTR(pp)->maxlen) + +#ifdef BCMDBG_POOL +extern int pktpool_dbg_register(pktpool_t *pktp, pktpool_cb_t cb, void *arg); +extern int pktpool_start_trigger(pktpool_t *pktp, void *p); +extern int pktpool_dbg_dump(pktpool_t *pktp); +extern int pktpool_dbg_notify(pktpool_t *pktp); +extern int pktpool_stats_dump(pktpool_t *pktp, pktpool_stats_t *stats); +#endif + + + +struct ether_addr; + +extern int ether_isbcast(const void *ea); +extern int ether_isnulladdr(const void *ea); + + + +#define pktq_psetmax(pq, prec, _max) ((pq)->q[prec].max = (_max)) +#define pktq_pmax(pq, prec) ((pq)->q[prec].max) +#define pktq_plen(pq, prec) ((pq)->q[prec].len) +#define pktq_pavail(pq, prec) ((pq)->q[prec].max - (pq)->q[prec].len) +#define pktq_pfull(pq, prec) ((pq)->q[prec].len >= (pq)->q[prec].max) +#define pktq_pempty(pq, prec) ((pq)->q[prec].len == 0) + +#define pktq_ppeek(pq, prec) ((pq)->q[prec].head) +#define pktq_ppeek_tail(pq, prec) ((pq)->q[prec].tail) + +extern void *pktq_penq(struct pktq *pq, int prec, void *p); +extern void *pktq_penq_head(struct pktq *pq, int prec, void *p); +extern void *pktq_pdeq(struct pktq *pq, int prec); +extern void *pktq_pdeq_prev(struct pktq *pq, int prec, void *prev_p); +extern void *pktq_pdeq_tail(struct pktq *pq, int prec); + +extern void pktq_pflush(osl_t *osh, struct pktq *pq, int prec, bool dir, + ifpkt_cb_t fn, int arg); + +extern bool pktq_pdel(struct pktq *pq, void *p, int prec); + + + +extern int pktq_mlen(struct pktq *pq, uint prec_bmp); +extern void *pktq_mdeq(struct pktq *pq, uint prec_bmp, int *prec_out); +extern void *pktq_mpeek(struct pktq *pq, uint prec_bmp, int *prec_out); + + + +#define pktq_len(pq) ((int)(pq)->len) +#define pktq_max(pq) ((int)(pq)->max) +#define pktq_avail(pq) ((int)((pq)->max - (pq)->len)) +#define pktq_full(pq) ((pq)->len >= (pq)->max) +#define pktq_empty(pq) ((pq)->len == 0) + + +#define pktenq(pq, p) pktq_penq(((struct pktq *)(void *)pq), 0, (p)) +#define pktenq_head(pq, p) pktq_penq_head(((struct pktq *)(void *)pq), 0, (p)) +#define pktdeq(pq) pktq_pdeq(((struct pktq *)(void *)pq), 0) +#define pktdeq_tail(pq) pktq_pdeq_tail(((struct pktq *)(void *)pq), 0) +#define pktqinit(pq, len) pktq_init(((struct pktq *)(void *)pq), 1, len) + +extern void pktq_init(struct pktq *pq, int num_prec, int max_len); +extern void pktq_set_max_plen(struct pktq *pq, int prec, int max_len); + + +extern void *pktq_deq(struct pktq *pq, int *prec_out); +extern void *pktq_deq_tail(struct pktq *pq, int *prec_out); +extern void *pktq_peek(struct pktq *pq, int *prec_out); +extern void *pktq_peek_tail(struct pktq *pq, int *prec_out); +extern void pktq_flush(osl_t *osh, struct pktq *pq, bool dir, ifpkt_cb_t fn, int arg); + + + +extern uint pktcopy(osl_t *osh, void *p, uint offset, int len, uchar *buf); +extern uint pktfrombuf(osl_t *osh, void *p, uint offset, int len, uchar *buf); +extern uint pkttotlen(osl_t *osh, void *p); +extern void *pktlast(osl_t *osh, void *p); +extern uint pktsegcnt(osl_t *osh, void *p); +extern uint pktsegcnt_war(osl_t *osh, void *p); +extern uint8 *pktoffset(osl_t *osh, void *p, uint offset); + + +#define PKTPRIO_VDSCP 0x100 +#define PKTPRIO_VLAN 0x200 +#define PKTPRIO_UPD 0x400 +#define PKTPRIO_DSCP 0x800 + +extern uint pktsetprio(void *pkt, bool update_vtag); + + +extern int bcm_atoi(const char *s); +extern ulong bcm_strtoul(const char *cp, char **endp, uint base); +extern char *bcmstrstr(const char *haystack, const char *needle); +extern char *bcmstrcat(char *dest, const char *src); +extern char *bcmstrncat(char *dest, const char *src, uint size); +extern ulong wchar2ascii(char *abuf, ushort *wbuf, ushort wbuflen, ulong abuflen); +char* bcmstrtok(char **string, const char *delimiters, char *tokdelim); +int bcmstricmp(const char *s1, const char *s2); +int bcmstrnicmp(const char* s1, const char* s2, int cnt); + + + +extern char *bcm_ether_ntoa(const struct ether_addr *ea, char *buf); +extern int bcm_ether_atoe(const char *p, struct ether_addr *ea); + + +struct ipv4_addr; +extern char *bcm_ip_ntoa(struct ipv4_addr *ia, char *buf); + + +extern void bcm_mdelay(uint ms); + +#define NVRAM_RECLAIM_CHECK(name) + +extern char *getvar(char *vars, const char *name); +extern int getintvar(char *vars, const char *name); +extern int getintvararray(char *vars, const char *name, int index); +extern int getintvararraysize(char *vars, const char *name); +extern uint getgpiopin(char *vars, char *pin_name, uint def_pin); +#define bcm_perf_enable() +#define bcmstats(fmt) +#define bcmlog(fmt, a1, a2) +#define bcmdumplog(buf, size) *buf = '\0' +#define bcmdumplogent(buf, idx) -1 + +#define bcmtslog(tstamp, fmt, a1, a2) +#define bcmprinttslogs() +#define bcmprinttstamp(us) +#define bcmdumptslog(buf, size) + +extern char *bcm_nvram_vars(uint *length); +extern int bcm_nvram_cache(void *sih); + + + + +typedef struct bcm_iovar { + const char *name; + uint16 varid; + uint16 flags; + uint16 type; + uint16 minlen; +} bcm_iovar_t; + + + + +#define IOV_GET 0 +#define IOV_SET 1 + + +#define IOV_GVAL(id) ((id) * 2) +#define IOV_SVAL(id) ((id) * 2 + IOV_SET) +#define IOV_ISSET(actionid) ((actionid & IOV_SET) == IOV_SET) +#define IOV_ID(actionid) (actionid >> 1) + + + +extern const bcm_iovar_t *bcm_iovar_lookup(const bcm_iovar_t *table, const char *name); +extern int bcm_iovar_lencheck(const bcm_iovar_t *table, void *arg, int len, bool set); +#if defined(WLTINYDUMP) || defined(WLMSG_INFORM) || defined(WLMSG_ASSOC) || \ + defined(WLMSG_PRPKT) || defined(WLMSG_WSEC) +extern int bcm_format_ssid(char* buf, const uchar ssid[], uint ssid_len); +#endif +#endif + + +#define IOVT_VOID 0 +#define IOVT_BOOL 1 +#define IOVT_INT8 2 +#define IOVT_UINT8 3 +#define IOVT_INT16 4 +#define IOVT_UINT16 5 +#define IOVT_INT32 6 +#define IOVT_UINT32 7 +#define IOVT_BUFFER 8 +#define BCM_IOVT_VALID(type) (((unsigned int)(type)) <= IOVT_BUFFER) + + +#define BCM_IOV_TYPE_INIT { \ + "void", \ + "bool", \ + "int8", \ + "uint8", \ + "int16", \ + "uint16", \ + "int32", \ + "uint32", \ + "buffer", \ + "" } + +#define BCM_IOVT_IS_INT(type) (\ + (type == IOVT_BOOL) || \ + (type == IOVT_INT8) || \ + (type == IOVT_UINT8) || \ + (type == IOVT_INT16) || \ + (type == IOVT_UINT16) || \ + (type == IOVT_INT32) || \ + (type == IOVT_UINT32)) + + + +#define BCME_STRLEN 64 +#define VALID_BCMERROR(e) ((e <= 0) && (e >= BCME_LAST)) + + + + +#define BCME_OK 0 +#define BCME_ERROR -1 +#define BCME_BADARG -2 +#define BCME_BADOPTION -3 +#define BCME_NOTUP -4 +#define BCME_NOTDOWN -5 +#define BCME_NOTAP -6 +#define BCME_NOTSTA -7 +#define BCME_BADKEYIDX -8 +#define BCME_RADIOOFF -9 +#define BCME_NOTBANDLOCKED -10 +#define BCME_NOCLK -11 +#define BCME_BADRATESET -12 +#define BCME_BADBAND -13 +#define BCME_BUFTOOSHORT -14 +#define BCME_BUFTOOLONG -15 +#define BCME_BUSY -16 +#define BCME_NOTASSOCIATED -17 +#define BCME_BADSSIDLEN -18 +#define BCME_OUTOFRANGECHAN -19 +#define BCME_BADCHAN -20 +#define BCME_BADADDR -21 +#define BCME_NORESOURCE -22 +#define BCME_UNSUPPORTED -23 +#define BCME_BADLEN -24 +#define BCME_NOTREADY -25 +#define BCME_EPERM -26 +#define BCME_NOMEM -27 +#define BCME_ASSOCIATED -28 +#define BCME_RANGE -29 +#define BCME_NOTFOUND -30 +#define BCME_WME_NOT_ENABLED -31 +#define BCME_TSPEC_NOTFOUND -32 +#define BCME_ACM_NOTSUPPORTED -33 +#define BCME_NOT_WME_ASSOCIATION -34 +#define BCME_SDIO_ERROR -35 +#define BCME_DONGLE_DOWN -36 +#define BCME_VERSION -37 +#define BCME_TXFAIL -38 +#define BCME_RXFAIL -39 +#define BCME_NODEVICE -40 +#define BCME_NMODE_DISABLED -41 +#define BCME_NONRESIDENT -42 +#define BCME_LAST BCME_NONRESIDENT + + +#define BCMERRSTRINGTABLE { \ + "OK", \ + "Undefined error", \ + "Bad Argument", \ + "Bad Option", \ + "Not up", \ + "Not down", \ + "Not AP", \ + "Not STA", \ + "Bad Key Index", \ + "Radio Off", \ + "Not band locked", \ + "No clock", \ + "Bad Rate valueset", \ + "Bad Band", \ + "Buffer too short", \ + "Buffer too long", \ + "Busy", \ + "Not Associated", \ + "Bad SSID len", \ + "Out of Range Channel", \ + "Bad Channel", \ + "Bad Address", \ + "Not Enough Resources", \ + "Unsupported", \ + "Bad length", \ + "Not Ready", \ + "Not Permitted", \ + "No Memory", \ + "Associated", \ + "Not In Range", \ + "Not Found", \ + "WME Not Enabled", \ + "TSPEC Not Found", \ + "ACM Not Supported", \ + "Not WME Association", \ + "SDIO Bus Error", \ + "Dongle Not Accessible", \ + "Incorrect version", \ + "TX Failure", \ + "RX Failure", \ + "Device Not Present", \ + "NMODE Disabled", \ + "Nonresident overlay access", \ +} + +#ifndef ABS +#define ABS(a) (((a) < 0) ? -(a) : (a)) +#endif + +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +#define CEIL(x, y) (((x) + ((y) - 1)) / (y)) +#define ROUNDUP(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) +#define ISALIGNED(a, x) (((uintptr)(a) & ((x) - 1)) == 0) +#define ALIGN_ADDR(addr, boundary) (void *)(((uintptr)(addr) + (boundary) - 1) \ + & ~((boundary) - 1)) +#define ALIGN_SIZE(size, boundary) (((size) + (boundary) - 1) \ + & ~((boundary) - 1)) +#define ISPOWEROF2(x) ((((x) - 1) & (x)) == 0) +#define VALID_MASK(mask) !((mask) & ((mask) + 1)) + +#ifndef OFFSETOF +#ifdef __ARMCC_VERSION + +#include +#define OFFSETOF(type, member) offsetof(type, member) +#else +#define OFFSETOF(type, member) ((uint)(uintptr)&((type *)0)->member) +#endif +#endif + +#ifndef ARRAYSIZE +#define ARRAYSIZE(a) (sizeof(a) / sizeof(a[0])) +#endif + + +extern void *_bcmutils_dummy_fn; +#define REFERENCE_FUNCTION(f) (_bcmutils_dummy_fn = (void *)(f)) + + +#ifndef setbit +#ifndef NBBY +#define NBBY 8 +#endif +#define setbit(a, i) (((uint8 *)a)[(i) / NBBY] |= 1 << ((i) % NBBY)) +#define clrbit(a, i) (((uint8 *)a)[(i) / NBBY] &= ~(1 << ((i) % NBBY))) +#define isset(a, i) (((const uint8 *)a)[(i) / NBBY] & (1 << ((i) % NBBY))) +#define isclr(a, i) ((((const uint8 *)a)[(i) / NBBY] & (1 << ((i) % NBBY))) == 0) +#endif + +#define NBITS(type) (sizeof(type) * 8) +#define NBITVAL(nbits) (1 << (nbits)) +#define MAXBITVAL(nbits) ((1 << (nbits)) - 1) +#define NBITMASK(nbits) MAXBITVAL(nbits) +#define MAXNBVAL(nbyte) MAXBITVAL((nbyte) * 8) + + +#define MUX(pred, true, false) ((pred) ? (true) : (false)) + + +#define MODDEC(x, bound) MUX((x) == 0, (bound) - 1, (x) - 1) +#define MODINC(x, bound) MUX((x) == (bound) - 1, 0, (x) + 1) + + +#define MODDEC_POW2(x, bound) (((x) - 1) & ((bound) - 1)) +#define MODINC_POW2(x, bound) (((x) + 1) & ((bound) - 1)) + + +#define MODADD(x, y, bound) \ + MUX((x) + (y) >= (bound), (x) + (y) - (bound), (x) + (y)) +#define MODSUB(x, y, bound) \ + MUX(((int)(x)) - ((int)(y)) < 0, (x) - (y) + (bound), (x) - (y)) + + +#define MODADD_POW2(x, y, bound) (((x) + (y)) & ((bound) - 1)) +#define MODSUB_POW2(x, y, bound) (((x) - (y)) & ((bound) - 1)) + + +#define CRC8_INIT_VALUE 0xff +#define CRC8_GOOD_VALUE 0x9f +#define CRC16_INIT_VALUE 0xffff +#define CRC16_GOOD_VALUE 0xf0b8 +#define CRC32_INIT_VALUE 0xffffffff +#define CRC32_GOOD_VALUE 0xdebb20e3 + + +#define MACF "%02x:%02x:%02x:%02x:%02x:%02x" +#define ETHERP_TO_MACF(ea) ((struct ether_addr *) (ea))->octet[0], \ + ((struct ether_addr *) (ea))->octet[1], \ + ((struct ether_addr *) (ea))->octet[2], \ + ((struct ether_addr *) (ea))->octet[3], \ + ((struct ether_addr *) (ea))->octet[4], \ + ((struct ether_addr *) (ea))->octet[5] + +#define ETHER_TO_MACF(ea) (ea).octet[0], \ + (ea).octet[1], \ + (ea).octet[2], \ + (ea).octet[3], \ + (ea).octet[4], \ + (ea).octet[5] +#if !defined(SIMPLE_MAC_PRINT) +#define MACDBG "%02x:%02x:%02x:%02x:%02x:%02x" +#define MAC2STRDBG(ea) (ea)[0], (ea)[1], (ea)[2], (ea)[3], (ea)[4], (ea)[5] +#else +#define MACDBG "%02x:%02x:%02x" +#define MAC2STRDBG(ea) (ea)[0], (ea)[4], (ea)[5] +#endif /* SIMPLE_MAC_PRINT */ + +typedef struct bcm_bit_desc { + uint32 bit; + const char* name; +} bcm_bit_desc_t; + + +typedef struct bcm_tlv { + uint8 id; + uint8 len; + uint8 data[1]; +} bcm_tlv_t; + + +#define bcm_valid_tlv(elt, buflen) ((buflen) >= 2 && (int)(buflen) >= (int)(2 + (elt)->len)) + + +#define ETHER_ADDR_STR_LEN 18 + + + +static INLINE void +xor_128bit_block(const uint8 *src1, const uint8 *src2, uint8 *dst) +{ + if ( +#ifdef __i386__ + 1 || +#endif + (((uintptr)src1 | (uintptr)src2 | (uintptr)dst) & 3) == 0) { + + + ((uint32 *)dst)[0] = ((const uint32 *)src1)[0] ^ ((const uint32 *)src2)[0]; + ((uint32 *)dst)[1] = ((const uint32 *)src1)[1] ^ ((const uint32 *)src2)[1]; + ((uint32 *)dst)[2] = ((const uint32 *)src1)[2] ^ ((const uint32 *)src2)[2]; + ((uint32 *)dst)[3] = ((const uint32 *)src1)[3] ^ ((const uint32 *)src2)[3]; + } else { + + int k; + for (k = 0; k < 16; k++) + dst[k] = src1[k] ^ src2[k]; + } +} + + + +extern uint8 hndcrc8(uint8 *p, uint nbytes, uint8 crc); +extern uint16 hndcrc16(uint8 *p, uint nbytes, uint16 crc); +extern uint32 hndcrc32(uint8 *p, uint nbytes, uint32 crc); + + +#if defined(DHD_DEBUG) || defined(WLMSG_PRHDRS) || defined(WLMSG_PRPKT) || \ + defined(WLMSG_ASSOC) +extern int bcm_format_flags(const bcm_bit_desc_t *bd, uint32 flags, char* buf, int len); +#endif + +#if defined(DHD_DEBUG) || defined(WLMSG_PRHDRS) || defined(WLMSG_PRPKT) || \ + defined(WLMSG_ASSOC) || defined(WLMEDIA_PEAKRATE) +extern int bcm_format_hex(char *str, const void *bytes, int len); +#endif + +extern const char *bcm_crypto_algo_name(uint algo); +extern char *bcm_chipname(uint chipid, char *buf, uint len); +extern char *bcm_brev_str(uint32 brev, char *buf); +extern void printbig(char *buf); +extern void prhex(const char *msg, uchar *buf, uint len); + + +extern bcm_tlv_t *bcm_next_tlv(bcm_tlv_t *elt, int *buflen); +extern bcm_tlv_t *bcm_parse_tlvs(void *buf, int buflen, uint key); +extern bcm_tlv_t *bcm_parse_ordered_tlvs(void *buf, int buflen, uint key); + + +extern const char *bcmerrorstr(int bcmerror); +extern bcm_tlv_t *bcm_parse_tlvs(void *buf, int buflen, uint key); + + +typedef uint32 mbool; +#define mboolset(mb, bit) ((mb) |= (bit)) +#define mboolclr(mb, bit) ((mb) &= ~(bit)) +#define mboolisset(mb, bit) (((mb) & (bit)) != 0) +#define mboolmaskset(mb, mask, val) ((mb) = (((mb) & ~(mask)) | (val))) + + +struct fielddesc { + const char *nameandfmt; + uint32 offset; + uint32 len; +}; + +extern void bcm_binit(struct bcmstrbuf *b, char *buf, uint size); +extern void bcm_bprhex(struct bcmstrbuf *b, const char *msg, bool newline, uint8 *buf, int len); + +extern void bcm_inc_bytes(uchar *num, int num_bytes, uint8 amount); +extern int bcm_cmp_bytes(const uchar *arg1, const uchar *arg2, uint8 nbytes); +extern void bcm_print_bytes(const char *name, const uchar *cdata, int len); + +typedef uint32 (*bcmutl_rdreg_rtn)(void *arg0, uint arg1, uint32 offset); +extern uint bcmdumpfields(bcmutl_rdreg_rtn func_ptr, void *arg0, uint arg1, struct fielddesc *str, + char *buf, uint32 bufsize); +extern uint bcm_bitcount(uint8 *bitmap, uint bytelength); + +extern int bcm_bprintf(struct bcmstrbuf *b, const char *fmt, ...); + + +extern uint16 bcm_qdbm_to_mw(uint8 qdbm); +extern uint8 bcm_mw_to_qdbm(uint16 mw); +extern uint bcm_mkiovar(char *name, char *data, uint datalen, char *buf, uint len); + +unsigned int process_nvram_vars(char *varbuf, unsigned int len); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/bcmwifi_channels.h b/drivers/net/wireless/bcmdhd/include/bcmwifi_channels.h new file mode 100644 index 00000000..bc57aca2 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmwifi_channels.h @@ -0,0 +1,490 @@ +/* + * Misc utility routines for WL and Apps + * This header file housing the define and function prototype use by + * both the wl driver, tools & Apps. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmwifi_channels.h 309193 2012-01-19 00:03:57Z $ + */ + +#ifndef _bcmwifi_channels_h_ +#define _bcmwifi_channels_h_ + + +/* A chanspec holds the channel number, band, bandwidth and control sideband */ +typedef uint16 chanspec_t; + +/* channel defines */ +#define CH_UPPER_SB 0x01 +#define CH_LOWER_SB 0x02 +#define CH_EWA_VALID 0x04 +#define CH_80MHZ_APART 16 +#define CH_40MHZ_APART 8 +#define CH_20MHZ_APART 4 +#define CH_10MHZ_APART 2 +#define CH_5MHZ_APART 1 /* 2G band channels are 5 Mhz apart */ +#define CH_MAX_2G_CHANNEL 14 /* Max channel in 2G band */ +#define MAXCHANNEL 224 /* max # supported channels. The max channel no is 216, + * this is that + 1 rounded up to a multiple of NBBY (8). + * DO NOT MAKE it > 255: channels are uint8's all over + */ +#define CHSPEC_CTLOVLP(sp1, sp2, sep) ABS(wf_chspec_ctlchan(sp1) - wf_chspec_ctlchan(sp2)) < (sep) + +/* All builds use the new 11ac ratespec/chanspec */ +#undef D11AC_IOTYPES +#define D11AC_IOTYPES + +#ifndef D11AC_IOTYPES + +#define WL_CHANSPEC_CHAN_MASK 0x00ff +#define WL_CHANSPEC_CHAN_SHIFT 0 + +#define WL_CHANSPEC_CTL_SB_MASK 0x0300 +#define WL_CHANSPEC_CTL_SB_SHIFT 8 +#define WL_CHANSPEC_CTL_SB_LOWER 0x0100 +#define WL_CHANSPEC_CTL_SB_UPPER 0x0200 +#define WL_CHANSPEC_CTL_SB_NONE 0x0300 + +#define WL_CHANSPEC_BW_MASK 0x0C00 +#define WL_CHANSPEC_BW_SHIFT 10 +#define WL_CHANSPEC_BW_10 0x0400 +#define WL_CHANSPEC_BW_20 0x0800 +#define WL_CHANSPEC_BW_40 0x0C00 + +#define WL_CHANSPEC_BAND_MASK 0xf000 +#define WL_CHANSPEC_BAND_SHIFT 12 +#ifdef WL_CHANSPEC_BAND_5G +#undef WL_CHANSPEC_BAND_5G +#endif +#ifdef WL_CHANSPEC_BAND_2G +#undef WL_CHANSPEC_BAND_2G +#endif +#define WL_CHANSPEC_BAND_5G 0x1000 +#define WL_CHANSPEC_BAND_2G 0x2000 +#define INVCHANSPEC 255 + +/* channel defines */ +#define LOWER_20_SB(channel) (((channel) > CH_10MHZ_APART) ? ((channel) - CH_10MHZ_APART) : 0) +#define UPPER_20_SB(channel) (((channel) < (MAXCHANNEL - CH_10MHZ_APART)) ? \ + ((channel) + CH_10MHZ_APART) : 0) +#define CHSPEC_WLCBANDUNIT(chspec) (CHSPEC_IS5G(chspec) ? BAND_5G_INDEX : BAND_2G_INDEX) +#define CH20MHZ_CHSPEC(channel) (chanspec_t)((chanspec_t)(channel) | WL_CHANSPEC_BW_20 | \ + WL_CHANSPEC_CTL_SB_NONE | (((channel) <= CH_MAX_2G_CHANNEL) ? \ + WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G)) +#define NEXT_20MHZ_CHAN(channel) (((channel) < (MAXCHANNEL - CH_20MHZ_APART)) ? \ + ((channel) + CH_20MHZ_APART) : 0) +#define CH40MHZ_CHSPEC(channel, ctlsb) (chanspec_t) \ + ((channel) | (ctlsb) | WL_CHANSPEC_BW_40 | \ + ((channel) <= CH_MAX_2G_CHANNEL ? WL_CHANSPEC_BAND_2G : \ + WL_CHANSPEC_BAND_5G)) +#define CHSPEC_CHANNEL(chspec) ((uint8)((chspec) & WL_CHANSPEC_CHAN_MASK)) +#define CHSPEC_BAND(chspec) ((chspec) & WL_CHANSPEC_BAND_MASK) + +/* chanspec stores radio channel & flags to indicate control channel location, i.e. upper/lower */ +#define CHSPEC_CTL_SB(chspec) ((chspec) & WL_CHANSPEC_CTL_SB_MASK) +#define CHSPEC_BW(chspec) ((chspec) & WL_CHANSPEC_BW_MASK) + +#ifdef WL11N_20MHZONLY + +#define CHSPEC_IS10(chspec) 0 +#define CHSPEC_IS20(chspec) 1 +#ifndef CHSPEC_IS40 +#define CHSPEC_IS40(chspec) 0 +#endif + +#else /* !WL11N_20MHZONLY */ + +#define CHSPEC_IS10(chspec) (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_10) +#define CHSPEC_IS20(chspec) (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_20) +#ifndef CHSPEC_IS40 +#define CHSPEC_IS40(chspec) (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40) +#endif + +#endif /* !WL11N_20MHZONLY */ + +#define CHSPEC_IS5G(chspec) (((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_5G) +#define CHSPEC_IS2G(chspec) (((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_2G) +#define CHSPEC_SB_NONE(chspec) (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_NONE) +#define CHSPEC_SB_UPPER(chspec) (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_UPPER) +#define CHSPEC_SB_LOWER(chspec) (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_LOWER) +#define CHSPEC_CTL_CHAN(chspec) ((CHSPEC_SB_LOWER(chspec)) ? \ + (LOWER_20_SB(((chspec) & WL_CHANSPEC_CHAN_MASK))) : \ + (UPPER_20_SB(((chspec) & WL_CHANSPEC_CHAN_MASK)))) +#define CHSPEC2WLC_BAND(chspec) (CHSPEC_IS5G(chspec) ? WLC_BAND_5G : WLC_BAND_2G) + +#define CHANSPEC_STR_LEN 8 + +#else /* D11AC_IOTYPES */ + +#define WL_CHANSPEC_CHAN_MASK 0x00ff +#define WL_CHANSPEC_CHAN_SHIFT 0 +#define WL_CHANSPEC_CHAN1_MASK 0x000f +#define WL_CHANSPEC_CHAN1_SHIFT 0 +#define WL_CHANSPEC_CHAN2_MASK 0x00f0 +#define WL_CHANSPEC_CHAN2_SHIFT 4 + +#define WL_CHANSPEC_CTL_SB_MASK 0x0700 +#define WL_CHANSPEC_CTL_SB_SHIFT 8 +#define WL_CHANSPEC_CTL_SB_LLL 0x0000 +#define WL_CHANSPEC_CTL_SB_LLU 0x0100 +#define WL_CHANSPEC_CTL_SB_LUL 0x0200 +#define WL_CHANSPEC_CTL_SB_LUU 0x0300 +#define WL_CHANSPEC_CTL_SB_ULL 0x0400 +#define WL_CHANSPEC_CTL_SB_ULU 0x0500 +#define WL_CHANSPEC_CTL_SB_UUL 0x0600 +#define WL_CHANSPEC_CTL_SB_UUU 0x0700 +#define WL_CHANSPEC_CTL_SB_LL WL_CHANSPEC_CTL_SB_LLL +#define WL_CHANSPEC_CTL_SB_LU WL_CHANSPEC_CTL_SB_LLU +#define WL_CHANSPEC_CTL_SB_UL WL_CHANSPEC_CTL_SB_LUL +#define WL_CHANSPEC_CTL_SB_UU WL_CHANSPEC_CTL_SB_LUU +#define WL_CHANSPEC_CTL_SB_L WL_CHANSPEC_CTL_SB_LLL +#define WL_CHANSPEC_CTL_SB_U WL_CHANSPEC_CTL_SB_LLU +#define WL_CHANSPEC_CTL_SB_LOWER WL_CHANSPEC_CTL_SB_LLL +#define WL_CHANSPEC_CTL_SB_UPPER WL_CHANSPEC_CTL_SB_LLU + +#define WL_CHANSPEC_BW_MASK 0x3800 +#define WL_CHANSPEC_BW_SHIFT 11 +#define WL_CHANSPEC_BW_5 0x0000 +#define WL_CHANSPEC_BW_10 0x0800 +#define WL_CHANSPEC_BW_20 0x1000 +#define WL_CHANSPEC_BW_40 0x1800 +#define WL_CHANSPEC_BW_80 0x2000 +#define WL_CHANSPEC_BW_160 0x2800 +#define WL_CHANSPEC_BW_8080 0x3000 + +#define WL_CHANSPEC_BAND_MASK 0xc000 +#define WL_CHANSPEC_BAND_SHIFT 14 +#define WL_CHANSPEC_BAND_2G 0x0000 +#define WL_CHANSPEC_BAND_3G 0x4000 +#define WL_CHANSPEC_BAND_4G 0x8000 +#define WL_CHANSPEC_BAND_5G 0xc000 +#define INVCHANSPEC 255 + +/* channel defines */ +#define LOWER_20_SB(channel) (((channel) > CH_10MHZ_APART) ? \ + ((channel) - CH_10MHZ_APART) : 0) +#define UPPER_20_SB(channel) (((channel) < (MAXCHANNEL - CH_10MHZ_APART)) ? \ + ((channel) + CH_10MHZ_APART) : 0) +#define LOWER_40_SB(channel) ((channel) - CH_20MHZ_APART) +#define UPPER_40_SB(channel) ((channel) + CH_20MHZ_APART) +#define CHSPEC_WLCBANDUNIT(chspec) (CHSPEC_IS5G(chspec) ? BAND_5G_INDEX : BAND_2G_INDEX) +#define CH20MHZ_CHSPEC(channel) (chanspec_t)((chanspec_t)(channel) | WL_CHANSPEC_BW_20 | \ + (((channel) <= CH_MAX_2G_CHANNEL) ? \ + WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G)) +#define NEXT_20MHZ_CHAN(channel) (((channel) < (MAXCHANNEL - CH_20MHZ_APART)) ? \ + ((channel) + CH_20MHZ_APART) : 0) +#define CH40MHZ_CHSPEC(channel, ctlsb) (chanspec_t) \ + ((channel) | (ctlsb) | WL_CHANSPEC_BW_40 | \ + ((channel) <= CH_MAX_2G_CHANNEL ? WL_CHANSPEC_BAND_2G : \ + WL_CHANSPEC_BAND_5G)) +#define CH80MHZ_CHSPEC(channel, ctlsb) (chanspec_t) \ + ((channel) | (ctlsb) | \ + WL_CHANSPEC_BW_80 | WL_CHANSPEC_BAND_5G) +#define CH160MHZ_CHSPEC(channel, ctlsb) (chanspec_t) \ + ((channel) | (ctlsb) | \ + WL_CHANSPEC_BW_160 | WL_CHANSPEC_BAND_5G) + +/* simple MACROs to get different fields of chanspec */ +#define CHSPEC_CHANNEL(chspec) ((uint8)((chspec) & WL_CHANSPEC_CHAN_MASK)) +#define CHSPEC_CHAN1(chspec) ((chspec) & WL_CHANSPEC_CHAN1_MASK) +#define CHSPEC_CHAN2(chspec) ((chspec) & WL_CHANSPEC_CHAN2_MASK) +#define CHSPEC_BAND(chspec) ((chspec) & WL_CHANSPEC_BAND_MASK) +#define CHSPEC_CTL_SB(chspec) ((chspec) & WL_CHANSPEC_CTL_SB_MASK) +#define CHSPEC_BW(chspec) ((chspec) & WL_CHANSPEC_BW_MASK) + +#ifdef WL11N_20MHZONLY + +#define CHSPEC_IS10(chspec) 0 +#define CHSPEC_IS20(chspec) 1 +#ifndef CHSPEC_IS40 +#define CHSPEC_IS40(chspec) 0 +#endif +#ifndef CHSPEC_IS80 +#define CHSPEC_IS80(chspec) 0 +#endif +#ifndef CHSPEC_IS160 +#define CHSPEC_IS160(chspec) 0 +#endif +#ifndef CHSPEC_IS8080 +#define CHSPEC_IS8080(chspec) 0 +#endif + +#else /* !WL11N_20MHZONLY */ + +#define CHSPEC_IS10(chspec) (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_10) +#define CHSPEC_IS20(chspec) (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_20) +#ifndef CHSPEC_IS40 +#define CHSPEC_IS40(chspec) (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40) +#endif +#ifndef CHSPEC_IS80 +#define CHSPEC_IS80(chspec) (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_80) +#endif +#ifndef CHSPEC_IS160 +#define CHSPEC_IS160(chspec) (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_160) +#endif +#ifndef CHSPEC_IS8080 +#define CHSPEC_IS8080(chspec) (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_8080) +#endif + +#endif /* !WL11N_20MHZONLY */ + +#define CHSPEC_IS5G(chspec) (((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_5G) +#define CHSPEC_IS2G(chspec) (((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_2G) +#define CHSPEC_SB_UPPER(chspec) \ + ((((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_UPPER) && \ + (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40)) +#define CHSPEC_SB_LOWER(chspec) \ + ((((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_LOWER) && \ + (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40)) +#define CHSPEC2WLC_BAND(chspec) (CHSPEC_IS5G(chspec) ? WLC_BAND_5G : WLC_BAND_2G) + +/** + * Number of chars needed for wf_chspec_ntoa() destination character buffer. + */ +#define CHANSPEC_STR_LEN 20 + + +/* Legacy Chanspec defines + * These are the defines for the previous format of the chanspec_t + */ +#define WL_LCHANSPEC_CHAN_MASK 0x00ff +#define WL_LCHANSPEC_CHAN_SHIFT 0 + +#define WL_LCHANSPEC_CTL_SB_MASK 0x0300 +#define WL_LCHANSPEC_CTL_SB_SHIFT 8 +#define WL_LCHANSPEC_CTL_SB_LOWER 0x0100 +#define WL_LCHANSPEC_CTL_SB_UPPER 0x0200 +#define WL_LCHANSPEC_CTL_SB_NONE 0x0300 + +#define WL_LCHANSPEC_BW_MASK 0x0C00 +#define WL_LCHANSPEC_BW_SHIFT 10 +#define WL_LCHANSPEC_BW_10 0x0400 +#define WL_LCHANSPEC_BW_20 0x0800 +#define WL_LCHANSPEC_BW_40 0x0C00 + +#define WL_LCHANSPEC_BAND_MASK 0xf000 +#define WL_LCHANSPEC_BAND_SHIFT 12 +#define WL_LCHANSPEC_BAND_5G 0x1000 +#define WL_LCHANSPEC_BAND_2G 0x2000 + +#define LCHSPEC_CHANNEL(chspec) ((uint8)((chspec) & WL_LCHANSPEC_CHAN_MASK)) +#define LCHSPEC_BAND(chspec) ((chspec) & WL_LCHANSPEC_BAND_MASK) +#define LCHSPEC_CTL_SB(chspec) ((chspec) & WL_LCHANSPEC_CTL_SB_MASK) +#define LCHSPEC_BW(chspec) ((chspec) & WL_LCHANSPEC_BW_MASK) +#define LCHSPEC_IS10(chspec) (((chspec) & WL_LCHANSPEC_BW_MASK) == WL_LCHANSPEC_BW_10) +#define LCHSPEC_IS20(chspec) (((chspec) & WL_LCHANSPEC_BW_MASK) == WL_LCHANSPEC_BW_20) +#define LCHSPEC_IS40(chspec) (((chspec) & WL_LCHANSPEC_BW_MASK) == WL_LCHANSPEC_BW_40) +#define LCHSPEC_IS5G(chspec) (((chspec) & WL_LCHANSPEC_BAND_MASK) == WL_LCHANSPEC_BAND_5G) +#define LCHSPEC_IS2G(chspec) (((chspec) & WL_LCHANSPEC_BAND_MASK) == WL_LCHANSPEC_BAND_2G) + +#define LCHSPEC_CREATE(chan, band, bw, sb) ((uint16)((chan) | (sb) | (bw) | (band))) + +#endif /* D11AC_IOTYPES */ + +/* + * WF_CHAN_FACTOR_* constants are used to calculate channel frequency + * given a channel number. + * chan_freq = chan_factor * 500Mhz + chan_number * 5 + */ + +/** + * Channel Factor for the starting frequence of 2.4 GHz channels. + * The value corresponds to 2407 MHz. + */ +#define WF_CHAN_FACTOR_2_4_G 4814 /* 2.4 GHz band, 2407 MHz */ + +/** + * Channel Factor for the starting frequence of 5 GHz channels. + * The value corresponds to 5000 MHz. + */ +#define WF_CHAN_FACTOR_5_G 10000 /* 5 GHz band, 5000 MHz */ + +/** + * Channel Factor for the starting frequence of 4.9 GHz channels. + * The value corresponds to 4000 MHz. + */ +#define WF_CHAN_FACTOR_4_G 8000 /* 4.9 GHz band for Japan */ + +/* defined rate in 500kbps */ +#define WLC_MAXRATE 108 /* in 500kbps units */ +#define WLC_RATE_1M 2 /* in 500kbps units */ +#define WLC_RATE_2M 4 /* in 500kbps units */ +#define WLC_RATE_5M5 11 /* in 500kbps units */ +#define WLC_RATE_11M 22 /* in 500kbps units */ +#define WLC_RATE_6M 12 /* in 500kbps units */ +#define WLC_RATE_9M 18 /* in 500kbps units */ +#define WLC_RATE_12M 24 /* in 500kbps units */ +#define WLC_RATE_18M 36 /* in 500kbps units */ +#define WLC_RATE_24M 48 /* in 500kbps units */ +#define WLC_RATE_36M 72 /* in 500kbps units */ +#define WLC_RATE_48M 96 /* in 500kbps units */ +#define WLC_RATE_54M 108 /* in 500kbps units */ + +#define WLC_2G_25MHZ_OFFSET 5 /* 2.4GHz band channel offset */ + +/** + * Convert chanspec to ascii string + * + * @param chspec chanspec format + * @param buf ascii string of chanspec + * + * @return pointer to buf with room for at least CHANSPEC_STR_LEN bytes + * + * @see CHANSPEC_STR_LEN + */ +extern char * wf_chspec_ntoa(chanspec_t chspec, char *buf); + +/** + * Convert ascii string to chanspec + * + * @param a pointer to input string + * + * @return >= 0 if successful or 0 otherwise + */ +extern chanspec_t wf_chspec_aton(const char *a); + +/** + * Verify the chanspec fields are valid. + * + * Verify the chanspec is using a legal set field values, i.e. that the chanspec + * specified a band, bw, ctl_sb and channel and that the combination could be + * legal given some set of circumstances. + * + * @param chanspec input chanspec to verify + * + * @return TRUE if the chanspec is malformed, FALSE if it looks good. + */ +extern bool wf_chspec_malformed(chanspec_t chanspec); + +/** + * Verify the chanspec specifies a valid channel according to 802.11. + * + * @param chanspec input chanspec to verify + * + * @return TRUE if the chanspec is a valid 802.11 channel + */ +extern bool wf_chspec_valid(chanspec_t chanspec); + +/** + * Return the primary (control) channel. + * + * This function returns the channel number of the primary 20MHz channel. For + * 20MHz channels this is just the channel number. For 40MHz or wider channels + * it is the primary 20MHz channel specified by the chanspec. + * + * @param chspec input chanspec + * + * @return Returns the channel number of the primary 20MHz channel + */ +extern uint8 wf_chspec_ctlchan(chanspec_t chspec); + +/** + * Return the primary (control) chanspec. + * + * This function returns the chanspec of the primary 20MHz channel. For 20MHz + * channels this is just the chanspec. For 40MHz or wider channels it is the + * chanspec of the primary 20MHZ channel specified by the chanspec. + * + * @param chspec input chanspec + * + * @return Returns the chanspec of the primary 20MHz channel + */ +extern chanspec_t wf_chspec_ctlchspec(chanspec_t chspec); + +/** + * Return a channel number corresponding to a frequency. + * + * This function returns the chanspec for the primary 40MHz of an 80MHz channel. + * The control sideband specifies the same 20MHz channel that the 80MHz channel is using + * as the primary 20MHz channel. + */ +extern chanspec_t wf_chspec_primary40_chspec(chanspec_t chspec); + +/* + * Return the channel number for a given frequency and base frequency. + * The returned channel number is relative to the given base frequency. + * If the given base frequency is zero, a base frequency of 5 GHz is assumed for + * frequencies from 5 - 6 GHz, and 2.407 GHz is assumed for 2.4 - 2.5 GHz. + * + * Frequency is specified in MHz. + * The base frequency is specified as (start_factor * 500 kHz). + * Constants WF_CHAN_FACTOR_2_4_G, WF_CHAN_FACTOR_5_G are defined for + * 2.4 GHz and 5 GHz bands. + * + * The returned channel will be in the range [1, 14] in the 2.4 GHz band + * and [0, 200] otherwise. + * -1 is returned if the start_factor is WF_CHAN_FACTOR_2_4_G and the + * frequency is not a 2.4 GHz channel, or if the frequency is not and even + * multiple of 5 MHz from the base frequency to the base plus 1 GHz. + * + * Reference 802.11 REVma, section 17.3.8.3, and 802.11B section 18.4.6.2 + * + * @param freq frequency in MHz + * @param start_factor base frequency in 500 kHz units, e.g. 10000 for 5 GHz + * + * @return Returns a channel number + * + * @see WF_CHAN_FACTOR_2_4_G + * @see WF_CHAN_FACTOR_5_G + */ +extern int wf_mhz2channel(uint freq, uint start_factor); + +/** + * Return the center frequency in MHz of the given channel and base frequency. + * + * Return the center frequency in MHz of the given channel and base frequency. + * The channel number is interpreted relative to the given base frequency. + * + * The valid channel range is [1, 14] in the 2.4 GHz band and [0, 200] otherwise. + * The base frequency is specified as (start_factor * 500 kHz). + * Constants WF_CHAN_FACTOR_2_4_G, WF_CHAN_FACTOR_5_G are defined for + * 2.4 GHz and 5 GHz bands. + * The channel range of [1, 14] is only checked for a start_factor of + * WF_CHAN_FACTOR_2_4_G (4814). + * Odd start_factors produce channels on .5 MHz boundaries, in which case + * the answer is rounded down to an integral MHz. + * -1 is returned for an out of range channel. + * + * Reference 802.11 REVma, section 17.3.8.3, and 802.11B section 18.4.6.2 + * + * @param channel input channel number + * @param start_factor base frequency in 500 kHz units, e.g. 10000 for 5 GHz + * + * @return Returns a frequency in MHz + * + * @see WF_CHAN_FACTOR_2_4_G + * @see WF_CHAN_FACTOR_5_G + */ +extern int wf_channel2mhz(uint channel, uint start_factor); + +/** + * Convert ctl chan and bw to chanspec + * + * @param ctl_ch channel + * @param bw bandwidth + * + * @return > 0 if successful or 0 otherwise + * + */ +extern uint16 wf_channel2chspec(uint ctl_ch, uint bw); + +#endif /* _bcmwifi_channels_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmwifi_rates.h b/drivers/net/wireless/bcmdhd/include/bcmwifi_rates.h new file mode 100644 index 00000000..9896b236 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmwifi_rates.h @@ -0,0 +1,306 @@ +/* + * Indices for 802.11 a/b/g/n/ac 1-3 chain symmetric transmit rates + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmwifi_rates.h 252708 2011-04-12 06:45:56Z $ + */ + +#ifndef _bcmwifi_rates_h_ +#define _bcmwifi_rates_h_ + +#ifdef __cplusplus +extern "C" { +#endif + + +#define WL_RATESET_SZ_DSSS 4 +#define WL_RATESET_SZ_OFDM 8 +#define WL_RATESET_SZ_HT_MCS 8 +#define WL_RATESET_SZ_VHT_MCS 10 + +#define WL_TX_CHAINS_MAX 3 + +#define WL_RATE_DISABLED (-128) + + +typedef enum wl_tx_bw { + WL_TX_BW_20, + WL_TX_BW_40, + WL_TX_BW_80, + WL_TX_BW_20IN40, + WL_TX_BW_20IN80, + WL_TX_BW_40IN80, + WL_TX_BW_ALL +} wl_tx_bw_t; + + + +typedef enum wl_tx_mode { + WL_TX_MODE_NONE, + WL_TX_MODE_STBC, + WL_TX_MODE_CDD, + WL_TX_MODE_SDM +} wl_tx_mode_t; + + + +typedef enum wl_tx_chains { + WL_TX_CHAINS_1 = 1, + WL_TX_CHAINS_2, + WL_TX_CHAINS_3 +} wl_tx_chains_t; + + + +typedef enum wl_tx_nss { + WL_TX_NSS_1 = 1, + WL_TX_NSS_2, + WL_TX_NSS_3 +} wl_tx_nss_t; + + +typedef enum clm_rates { + + + + WL_RATE_1X1_DSSS_1 = 0, + WL_RATE_1X1_DSSS_2 = 1, + WL_RATE_1X1_DSSS_5_5 = 2, + WL_RATE_1X1_DSSS_11 = 3, + + WL_RATE_1X1_OFDM_6 = 4, + WL_RATE_1X1_OFDM_9 = 5, + WL_RATE_1X1_OFDM_12 = 6, + WL_RATE_1X1_OFDM_18 = 7, + WL_RATE_1X1_OFDM_24 = 8, + WL_RATE_1X1_OFDM_36 = 9, + WL_RATE_1X1_OFDM_48 = 10, + WL_RATE_1X1_OFDM_54 = 11, + + WL_RATE_1X1_MCS0 = 12, + WL_RATE_1X1_MCS1 = 13, + WL_RATE_1X1_MCS2 = 14, + WL_RATE_1X1_MCS3 = 15, + WL_RATE_1X1_MCS4 = 16, + WL_RATE_1X1_MCS5 = 17, + WL_RATE_1X1_MCS6 = 18, + WL_RATE_1X1_MCS7 = 19, + + WL_RATE_1X1_VHT0SS1 = 12, + WL_RATE_1X1_VHT1SS1 = 13, + WL_RATE_1X1_VHT2SS1 = 14, + WL_RATE_1X1_VHT3SS1 = 15, + WL_RATE_1X1_VHT4SS1 = 16, + WL_RATE_1X1_VHT5SS1 = 17, + WL_RATE_1X1_VHT6SS1 = 18, + WL_RATE_1X1_VHT7SS1 = 19, + WL_RATE_1X1_VHT8SS1 = 20, + WL_RATE_1X1_VHT9SS1 = 21, + + + + + + WL_RATE_1X2_DSSS_1 = 22, + WL_RATE_1X2_DSSS_2 = 23, + WL_RATE_1X2_DSSS_5_5 = 24, + WL_RATE_1X2_DSSS_11 = 25, + + WL_RATE_1X2_CDD_OFDM_6 = 26, + WL_RATE_1X2_CDD_OFDM_9 = 27, + WL_RATE_1X2_CDD_OFDM_12 = 28, + WL_RATE_1X2_CDD_OFDM_18 = 29, + WL_RATE_1X2_CDD_OFDM_24 = 30, + WL_RATE_1X2_CDD_OFDM_36 = 31, + WL_RATE_1X2_CDD_OFDM_48 = 32, + WL_RATE_1X2_CDD_OFDM_54 = 33, + + WL_RATE_1X2_CDD_MCS0 = 34, + WL_RATE_1X2_CDD_MCS1 = 35, + WL_RATE_1X2_CDD_MCS2 = 36, + WL_RATE_1X2_CDD_MCS3 = 37, + WL_RATE_1X2_CDD_MCS4 = 38, + WL_RATE_1X2_CDD_MCS5 = 39, + WL_RATE_1X2_CDD_MCS6 = 40, + WL_RATE_1X2_CDD_MCS7 = 41, + + WL_RATE_1X2_VHT0SS1 = 34, + WL_RATE_1X2_VHT1SS1 = 35, + WL_RATE_1X2_VHT2SS1 = 36, + WL_RATE_1X2_VHT3SS1 = 37, + WL_RATE_1X2_VHT4SS1 = 38, + WL_RATE_1X2_VHT5SS1 = 39, + WL_RATE_1X2_VHT6SS1 = 40, + WL_RATE_1X2_VHT7SS1 = 41, + WL_RATE_1X2_VHT8SS1 = 42, + WL_RATE_1X2_VHT9SS1 = 43, + + + WL_RATE_2X2_STBC_MCS0 = 44, + WL_RATE_2X2_STBC_MCS1 = 45, + WL_RATE_2X2_STBC_MCS2 = 46, + WL_RATE_2X2_STBC_MCS3 = 47, + WL_RATE_2X2_STBC_MCS4 = 48, + WL_RATE_2X2_STBC_MCS5 = 49, + WL_RATE_2X2_STBC_MCS6 = 50, + WL_RATE_2X2_STBC_MCS7 = 51, + + WL_RATE_2X2_STBC_VHT0SS1 = 44, + WL_RATE_2X2_STBC_VHT1SS1 = 45, + WL_RATE_2X2_STBC_VHT2SS1 = 46, + WL_RATE_2X2_STBC_VHT3SS1 = 47, + WL_RATE_2X2_STBC_VHT4SS1 = 48, + WL_RATE_2X2_STBC_VHT5SS1 = 49, + WL_RATE_2X2_STBC_VHT6SS1 = 50, + WL_RATE_2X2_STBC_VHT7SS1 = 51, + WL_RATE_2X2_STBC_VHT8SS1 = 52, + WL_RATE_2X2_STBC_VHT9SS1 = 53, + + WL_RATE_2X2_SDM_MCS8 = 54, + WL_RATE_2X2_SDM_MCS9 = 55, + WL_RATE_2X2_SDM_MCS10 = 56, + WL_RATE_2X2_SDM_MCS11 = 57, + WL_RATE_2X2_SDM_MCS12 = 58, + WL_RATE_2X2_SDM_MCS13 = 59, + WL_RATE_2X2_SDM_MCS14 = 60, + WL_RATE_2X2_SDM_MCS15 = 61, + + WL_RATE_2X2_VHT0SS2 = 54, + WL_RATE_2X2_VHT1SS2 = 55, + WL_RATE_2X2_VHT2SS2 = 56, + WL_RATE_2X2_VHT3SS2 = 57, + WL_RATE_2X2_VHT4SS2 = 58, + WL_RATE_2X2_VHT5SS2 = 59, + WL_RATE_2X2_VHT6SS2 = 60, + WL_RATE_2X2_VHT7SS2 = 61, + WL_RATE_2X2_VHT8SS2 = 62, + WL_RATE_2X2_VHT9SS2 = 63, + + + + + + WL_RATE_1X3_DSSS_1 = 64, + WL_RATE_1X3_DSSS_2 = 65, + WL_RATE_1X3_DSSS_5_5 = 66, + WL_RATE_1X3_DSSS_11 = 67, + + WL_RATE_1X3_CDD_OFDM_6 = 68, + WL_RATE_1X3_CDD_OFDM_9 = 69, + WL_RATE_1X3_CDD_OFDM_12 = 70, + WL_RATE_1X3_CDD_OFDM_18 = 71, + WL_RATE_1X3_CDD_OFDM_24 = 72, + WL_RATE_1X3_CDD_OFDM_36 = 73, + WL_RATE_1X3_CDD_OFDM_48 = 74, + WL_RATE_1X3_CDD_OFDM_54 = 75, + + WL_RATE_1X3_CDD_MCS0 = 76, + WL_RATE_1X3_CDD_MCS1 = 77, + WL_RATE_1X3_CDD_MCS2 = 78, + WL_RATE_1X3_CDD_MCS3 = 79, + WL_RATE_1X3_CDD_MCS4 = 80, + WL_RATE_1X3_CDD_MCS5 = 81, + WL_RATE_1X3_CDD_MCS6 = 82, + WL_RATE_1X3_CDD_MCS7 = 83, + + WL_RATE_1X3_VHT0SS1 = 76, + WL_RATE_1X3_VHT1SS1 = 77, + WL_RATE_1X3_VHT2SS1 = 78, + WL_RATE_1X3_VHT3SS1 = 79, + WL_RATE_1X3_VHT4SS1 = 80, + WL_RATE_1X3_VHT5SS1 = 81, + WL_RATE_1X3_VHT6SS1 = 82, + WL_RATE_1X3_VHT7SS1 = 83, + WL_RATE_1X3_VHT8SS1 = 84, + WL_RATE_1X3_VHT9SS1 = 85, + + + WL_RATE_2X3_STBC_MCS0 = 86, + WL_RATE_2X3_STBC_MCS1 = 87, + WL_RATE_2X3_STBC_MCS2 = 88, + WL_RATE_2X3_STBC_MCS3 = 89, + WL_RATE_2X3_STBC_MCS4 = 90, + WL_RATE_2X3_STBC_MCS5 = 91, + WL_RATE_2X3_STBC_MCS6 = 92, + WL_RATE_2X3_STBC_MCS7 = 93, + + WL_RATE_2X3_STBC_VHT0SS1 = 86, + WL_RATE_2X3_STBC_VHT1SS1 = 87, + WL_RATE_2X3_STBC_VHT2SS1 = 88, + WL_RATE_2X3_STBC_VHT3SS1 = 89, + WL_RATE_2X3_STBC_VHT4SS1 = 90, + WL_RATE_2X3_STBC_VHT5SS1 = 91, + WL_RATE_2X3_STBC_VHT6SS1 = 92, + WL_RATE_2X3_STBC_VHT7SS1 = 93, + WL_RATE_2X3_STBC_VHT8SS1 = 94, + WL_RATE_2X3_STBC_VHT9SS1 = 95, + + WL_RATE_2X3_SDM_MCS8 = 96, + WL_RATE_2X3_SDM_MCS9 = 97, + WL_RATE_2X3_SDM_MCS10 = 98, + WL_RATE_2X3_SDM_MCS11 = 99, + WL_RATE_2X3_SDM_MCS12 = 100, + WL_RATE_2X3_SDM_MCS13 = 101, + WL_RATE_2X3_SDM_MCS14 = 102, + WL_RATE_2X3_SDM_MCS15 = 103, + + WL_RATE_2X3_VHT0SS2 = 96, + WL_RATE_2X3_VHT1SS2 = 97, + WL_RATE_2X3_VHT2SS2 = 98, + WL_RATE_2X3_VHT3SS2 = 99, + WL_RATE_2X3_VHT4SS2 = 100, + WL_RATE_2X3_VHT5SS2 = 101, + WL_RATE_2X3_VHT6SS2 = 102, + WL_RATE_2X3_VHT7SS2 = 103, + WL_RATE_2X3_VHT8SS2 = 104, + WL_RATE_2X3_VHT9SS2 = 105, + + + WL_RATE_3X3_SDM_MCS16 = 106, + WL_RATE_3X3_SDM_MCS17 = 107, + WL_RATE_3X3_SDM_MCS18 = 108, + WL_RATE_3X3_SDM_MCS19 = 109, + WL_RATE_3X3_SDM_MCS20 = 110, + WL_RATE_3X3_SDM_MCS21 = 111, + WL_RATE_3X3_SDM_MCS22 = 112, + WL_RATE_3X3_SDM_MCS23 = 113, + + WL_RATE_3X3_VHT0SS3 = 106, + WL_RATE_3X3_VHT1SS3 = 107, + WL_RATE_3X3_VHT2SS3 = 108, + WL_RATE_3X3_VHT3SS3 = 109, + WL_RATE_3X3_VHT4SS3 = 110, + WL_RATE_3X3_VHT5SS3 = 111, + WL_RATE_3X3_VHT6SS3 = 112, + WL_RATE_3X3_VHT7SS3 = 113, + WL_RATE_3X3_VHT8SS3 = 114, + WL_RATE_3X3_VHT9SS3 = 115, + + + WL_NUMRATES = 116 +} clm_rates_t; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/dhdioctl.h b/drivers/net/wireless/bcmdhd/include/dhdioctl.h new file mode 100644 index 00000000..11fff555 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/dhdioctl.h @@ -0,0 +1,136 @@ +/* + * Definitions for ioctls to access DHD iovars. + * Based on wlioctl.h (for Broadcom 802.11abg driver). + * (Moves towards generic ioctls for BCM drivers/iovars.) + * + * Definitions subject to change without notice. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhdioctl.h 354894 2012-09-04 12:34:07Z $ + */ + +#ifndef _dhdioctl_h_ +#define _dhdioctl_h_ + +#include + + +/* require default structure packing */ +#define BWL_DEFAULT_PACKING +#include + + +/* Linux network driver ioctl encoding */ +typedef struct dhd_ioctl { + uint cmd; /* common ioctl definition */ + void *buf; /* pointer to user buffer */ + uint len; /* length of user buffer */ + bool set; /* get or set request (optional) */ + uint used; /* bytes read or written (optional) */ + uint needed; /* bytes needed (optional) */ + uint driver; /* to identify target driver */ +} dhd_ioctl_t; + +/* Underlying BUS definition */ +enum { + BUS_TYPE_USB = 0, /* for USB dongles */ + BUS_TYPE_SDIO /* for SDIO dongles */ +}; + +/* per-driver magic numbers */ +#define DHD_IOCTL_MAGIC 0x00444944 + +/* bump this number if you change the ioctl interface */ +#define DHD_IOCTL_VERSION 1 + +#define DHD_IOCTL_MAXLEN 8192 /* max length ioctl buffer required */ +#define DHD_IOCTL_SMLEN 256 /* "small" length ioctl buffer required */ + +/* common ioctl definitions */ +#define DHD_GET_MAGIC 0 +#define DHD_GET_VERSION 1 +#define DHD_GET_VAR 2 +#define DHD_SET_VAR 3 + +/* message levels */ +#define DHD_ERROR_VAL 0x0001 +#define DHD_TRACE_VAL 0x0002 +#define DHD_INFO_VAL 0x0004 +#define DHD_DATA_VAL 0x0008 +#define DHD_CTL_VAL 0x0010 +#define DHD_TIMER_VAL 0x0020 +#define DHD_HDRS_VAL 0x0040 +#define DHD_BYTES_VAL 0x0080 +#define DHD_INTR_VAL 0x0100 +#define DHD_LOG_VAL 0x0200 +#define DHD_GLOM_VAL 0x0400 +#define DHD_EVENT_VAL 0x0800 +#define DHD_BTA_VAL 0x1000 +#if 0 && (NDISVER >= 0x0630) && 1 +#define DHD_SCAN_VAL 0x2000 +#else +#define DHD_ISCAN_VAL 0x2000 +#endif +#define DHD_ARPOE_VAL 0x4000 +#define DHD_REORDER_VAL 0x8000 +#define DHD_WL_VAL 0x10000 +#define DHD_WL_VAL2 0x20000 + +#ifdef SDTEST +/* For pktgen iovar */ +typedef struct dhd_pktgen { + uint version; /* To allow structure change tracking */ + uint freq; /* Max ticks between tx/rx attempts */ + uint count; /* Test packets to send/rcv each attempt */ + uint print; /* Print counts every attempts */ + uint total; /* Total packets (or bursts) */ + uint minlen; /* Minimum length of packets to send */ + uint maxlen; /* Maximum length of packets to send */ + uint numsent; /* Count of test packets sent */ + uint numrcvd; /* Count of test packets received */ + uint numfail; /* Count of test send failures */ + uint mode; /* Test mode (type of test packets) */ + uint stop; /* Stop after this many tx failures */ +} dhd_pktgen_t; + +/* Version in case structure changes */ +#define DHD_PKTGEN_VERSION 2 + +/* Type of test packets to use */ +#define DHD_PKTGEN_ECHO 1 /* Send echo requests */ +#define DHD_PKTGEN_SEND 2 /* Send discard packets */ +#define DHD_PKTGEN_RXBURST 3 /* Request dongle send N packets */ +#define DHD_PKTGEN_RECV 4 /* Continuous rx from continuous tx dongle */ +#endif /* SDTEST */ + +/* Enter idle immediately (no timeout) */ +#define DHD_IDLE_IMMEDIATE (-1) + +/* Values for idleclock iovar: other values are the sd_divisor to use when idle */ +#define DHD_IDLE_ACTIVE 0 /* Do not request any SD clock change when idle */ +#define DHD_IDLE_STOP (-1) /* Request SD clock be stopped (and use SD1 mode) */ + + +/* require default structure packing */ +#include + +#endif /* _dhdioctl_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/epivers.h b/drivers/net/wireless/bcmdhd/include/epivers.h new file mode 100644 index 00000000..7f5a9710 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/epivers.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: epivers.h.in,v 13.33 2010-09-08 22:08:53 $ + * +*/ + +#ifndef _epivers_h_ +#define _epivers_h_ + +#define EPI_MAJOR_VERSION 1 + +#define EPI_MINOR_VERSION 28 + +#define EPI_RC_NUMBER 27 + +#define EPI_INCREMENTAL_NUMBER 1 + +#define EPI_BUILD_NUMBER 0 + +#define EPI_VERSION 1, 28, 27, 1 + +#define EPI_VERSION_NUM 0x011c1b01 + +#define EPI_VERSION_DEV 1.28.27 + +/* Driver Version String, ASCII, 32 chars max */ +#ifdef BCMINTERNAL +#define EPI_VERSION_STR "1.28.27.1 (r BCMINT)" +#else +#ifdef WLTEST +#define EPI_VERSION_STR "1.28.27.1 (r WLTEST)" +#else +#define EPI_VERSION_STR "1.28.27.1 (r)" +#endif +#endif /* BCMINTERNAL */ + +#endif /* _epivers_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/hndpmu.h b/drivers/net/wireless/bcmdhd/include/hndpmu.h new file mode 100644 index 00000000..c41def6c --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/hndpmu.h @@ -0,0 +1,36 @@ +/* + * HND SiliconBackplane PMU support. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: hndpmu.h 241182 2011-02-17 21:50:03Z $ + */ + +#ifndef _hndpmu_h_ +#define _hndpmu_h_ + + +extern void si_pmu_otp_power(si_t *sih, osl_t *osh, bool on); +extern void si_sdiod_drive_strength_init(si_t *sih, osl_t *osh, uint32 drivestrength); + +extern void si_pmu_minresmask_htavail_set(si_t *sih, osl_t *osh, bool set_clear); + +#endif /* _hndpmu_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/hndrte_armtrap.h b/drivers/net/wireless/bcmdhd/include/hndrte_armtrap.h new file mode 100644 index 00000000..90d97992 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/hndrte_armtrap.h @@ -0,0 +1,88 @@ +/* + * HNDRTE arm trap handling. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: hndrte_armtrap.h 261365 2011-05-24 20:42:23Z $ + */ + +#ifndef _hndrte_armtrap_h +#define _hndrte_armtrap_h + + +/* ARM trap handling */ + +/* Trap types defined by ARM (see arminc.h) */ + +/* Trap locations in lo memory */ +#define TRAP_STRIDE 4 +#define FIRST_TRAP TR_RST +#define LAST_TRAP (TR_FIQ * TRAP_STRIDE) + +#if defined(__ARM_ARCH_4T__) +#define MAX_TRAP_TYPE (TR_FIQ + 1) +#elif defined(__ARM_ARCH_7M__) +#define MAX_TRAP_TYPE (TR_ISR + ARMCM3_NUMINTS) +#endif /* __ARM_ARCH_7M__ */ + +/* The trap structure is defined here as offsets for assembly */ +#define TR_TYPE 0x00 +#define TR_EPC 0x04 +#define TR_CPSR 0x08 +#define TR_SPSR 0x0c +#define TR_REGS 0x10 +#define TR_REG(n) (TR_REGS + (n) * 4) +#define TR_SP TR_REG(13) +#define TR_LR TR_REG(14) +#define TR_PC TR_REG(15) + +#define TRAP_T_SIZE 80 + +#ifndef _LANGUAGE_ASSEMBLY + +#include + +typedef struct _trap_struct { + uint32 type; + uint32 epc; + uint32 cpsr; + uint32 spsr; + uint32 r0; /* a1 */ + uint32 r1; /* a2 */ + uint32 r2; /* a3 */ + uint32 r3; /* a4 */ + uint32 r4; /* v1 */ + uint32 r5; /* v2 */ + uint32 r6; /* v3 */ + uint32 r7; /* v4 */ + uint32 r8; /* v5 */ + uint32 r9; /* sb/v6 */ + uint32 r10; /* sl/v7 */ + uint32 r11; /* fp/v8 */ + uint32 r12; /* ip */ + uint32 r13; /* sp */ + uint32 r14; /* lr */ + uint32 pc; /* r15 */ +} trap_t; + +#endif /* !_LANGUAGE_ASSEMBLY */ + +#endif /* _hndrte_armtrap_h */ diff --git a/drivers/net/wireless/bcmdhd/include/hndrte_cons.h b/drivers/net/wireless/bcmdhd/include/hndrte_cons.h new file mode 100644 index 00000000..57abbbd5 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/hndrte_cons.h @@ -0,0 +1,67 @@ +/* + * Console support for hndrte. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: hndrte_cons.h 300516 2011-12-04 17:39:44Z $ + */ +#ifndef _HNDRTE_CONS_H +#define _HNDRTE_CONS_H + +#include + +#define CBUF_LEN (128) + +#define LOG_BUF_LEN 1024 + +typedef struct { + uint32 buf; /* Can't be pointer on (64-bit) hosts */ + uint buf_size; + uint idx; + char *_buf_compat; /* redundant pointer for backward compat. */ +} hndrte_log_t; + +typedef struct { + /* Virtual UART + * When there is no UART (e.g. Quickturn), the host should write a complete + * input line directly into cbuf and then write the length into vcons_in. + * This may also be used when there is a real UART (at risk of conflicting with + * the real UART). vcons_out is currently unused. + */ + volatile uint vcons_in; + volatile uint vcons_out; + + /* Output (logging) buffer + * Console output is written to a ring buffer log_buf at index log_idx. + * The host may read the output when it sees log_idx advance. + * Output will be lost if the output wraps around faster than the host polls. + */ + hndrte_log_t log; + + /* Console input line buffer + * Characters are read one at a time into cbuf until is received, then + * the buffer is processed as a command line. Also used for virtual UART. + */ + uint cbuf_idx; + char cbuf[CBUF_LEN]; +} hndrte_cons_t; + +#endif /* _HNDRTE_CONS_H */ diff --git a/drivers/net/wireless/bcmdhd/include/hndsoc.h b/drivers/net/wireless/bcmdhd/include/hndsoc.h new file mode 100644 index 00000000..66640c3b --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/hndsoc.h @@ -0,0 +1,235 @@ +/* + * Broadcom HND chip & on-chip-interconnect-related definitions. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: hndsoc.h 309193 2012-01-19 00:03:57Z $ + */ + +#ifndef _HNDSOC_H +#define _HNDSOC_H + +/* Include the soci specific files */ +#include +#include + +/* + * SOC Interconnect Address Map. + * All regions may not exist on all chips. + */ +#define SI_SDRAM_BASE 0x00000000 /* Physical SDRAM */ +#define SI_PCI_MEM 0x08000000 /* Host Mode sb2pcitranslation0 (64 MB) */ +#define SI_PCI_MEM_SZ (64 * 1024 * 1024) +#define SI_PCI_CFG 0x0c000000 /* Host Mode sb2pcitranslation1 (64 MB) */ +#define SI_SDRAM_SWAPPED 0x10000000 /* Byteswapped Physical SDRAM */ +#define SI_SDRAM_R2 0x80000000 /* Region 2 for sdram (512 MB) */ + +#define SI_ENUM_BASE 0x18000000 /* Enumeration space base */ + +#define SI_WRAP_BASE 0x18100000 /* Wrapper space base */ +#define SI_CORE_SIZE 0x1000 /* each core gets 4Kbytes for registers */ +#define SI_MAXCORES 16 /* Max cores (this is arbitrary, for software + * convenience and could be changed if we + * make any larger chips + */ + +#define SI_FASTRAM 0x19000000 /* On-chip RAM on chips that also have DDR */ +#define SI_FASTRAM_SWAPPED 0x19800000 + +#define SI_FLASH2 0x1c000000 /* Flash Region 2 (region 1 shadowed here) */ +#define SI_FLASH2_SZ 0x02000000 /* Size of Flash Region 2 */ +#define SI_ARMCM3_ROM 0x1e000000 /* ARM Cortex-M3 ROM */ +#define SI_FLASH1 0x1fc00000 /* MIPS Flash Region 1 */ +#define SI_FLASH1_SZ 0x00400000 /* MIPS Size of Flash Region 1 */ +#define SI_ARM7S_ROM 0x20000000 /* ARM7TDMI-S ROM */ +#define SI_ARMCR4_ROM 0x000f0000 /* ARM Cortex-R4 ROM */ +#define SI_ARMCM3_SRAM2 0x60000000 /* ARM Cortex-M3 SRAM Region 2 */ +#define SI_ARM7S_SRAM2 0x80000000 /* ARM7TDMI-S SRAM Region 2 */ +#define SI_ARM_FLASH1 0xffff0000 /* ARM Flash Region 1 */ +#define SI_ARM_FLASH1_SZ 0x00010000 /* ARM Size of Flash Region 1 */ + +#define SI_PCI_DMA 0x40000000 /* Client Mode sb2pcitranslation2 (1 GB) */ +#define SI_PCI_DMA2 0x80000000 /* Client Mode sb2pcitranslation2 (1 GB) */ +#define SI_PCI_DMA_SZ 0x40000000 /* Client Mode sb2pcitranslation2 size in bytes */ +#define SI_PCIE_DMA_L32 0x00000000 /* PCIE Client Mode sb2pcitranslation2 + * (2 ZettaBytes), low 32 bits + */ +#define SI_PCIE_DMA_H32 0x80000000 /* PCIE Client Mode sb2pcitranslation2 + * (2 ZettaBytes), high 32 bits + */ + +/* core codes */ +#define NODEV_CORE_ID 0x700 /* Invalid coreid */ +#define CC_CORE_ID 0x800 /* chipcommon core */ +#define ILINE20_CORE_ID 0x801 /* iline20 core */ +#define SRAM_CORE_ID 0x802 /* sram core */ +#define SDRAM_CORE_ID 0x803 /* sdram core */ +#define PCI_CORE_ID 0x804 /* pci core */ +#define MIPS_CORE_ID 0x805 /* mips core */ +#define ENET_CORE_ID 0x806 /* enet mac core */ +#define CODEC_CORE_ID 0x807 /* v90 codec core */ +#define USB_CORE_ID 0x808 /* usb 1.1 host/device core */ +#define ADSL_CORE_ID 0x809 /* ADSL core */ +#define ILINE100_CORE_ID 0x80a /* iline100 core */ +#define IPSEC_CORE_ID 0x80b /* ipsec core */ +#define UTOPIA_CORE_ID 0x80c /* utopia core */ +#define PCMCIA_CORE_ID 0x80d /* pcmcia core */ +#define SOCRAM_CORE_ID 0x80e /* internal memory core */ +#define MEMC_CORE_ID 0x80f /* memc sdram core */ +#define OFDM_CORE_ID 0x810 /* OFDM phy core */ +#define EXTIF_CORE_ID 0x811 /* external interface core */ +#define D11_CORE_ID 0x812 /* 802.11 MAC core */ +#define APHY_CORE_ID 0x813 /* 802.11a phy core */ +#define BPHY_CORE_ID 0x814 /* 802.11b phy core */ +#define GPHY_CORE_ID 0x815 /* 802.11g phy core */ +#define MIPS33_CORE_ID 0x816 /* mips3302 core */ +#define USB11H_CORE_ID 0x817 /* usb 1.1 host core */ +#define USB11D_CORE_ID 0x818 /* usb 1.1 device core */ +#define USB20H_CORE_ID 0x819 /* usb 2.0 host core */ +#define USB20D_CORE_ID 0x81a /* usb 2.0 device core */ +#define SDIOH_CORE_ID 0x81b /* sdio host core */ +#define ROBO_CORE_ID 0x81c /* roboswitch core */ +#define ATA100_CORE_ID 0x81d /* parallel ATA core */ +#define SATAXOR_CORE_ID 0x81e /* serial ATA & XOR DMA core */ +#define GIGETH_CORE_ID 0x81f /* gigabit ethernet core */ +#define PCIE_CORE_ID 0x820 /* pci express core */ +#define NPHY_CORE_ID 0x821 /* 802.11n 2x2 phy core */ +#define SRAMC_CORE_ID 0x822 /* SRAM controller core */ +#define MINIMAC_CORE_ID 0x823 /* MINI MAC/phy core */ +#define ARM11_CORE_ID 0x824 /* ARM 1176 core */ +#define ARM7S_CORE_ID 0x825 /* ARM7tdmi-s core */ +#define LPPHY_CORE_ID 0x826 /* 802.11a/b/g phy core */ +#define PMU_CORE_ID 0x827 /* PMU core */ +#define SSNPHY_CORE_ID 0x828 /* 802.11n single-stream phy core */ +#define SDIOD_CORE_ID 0x829 /* SDIO device core */ +#define ARMCM3_CORE_ID 0x82a /* ARM Cortex M3 core */ +#define HTPHY_CORE_ID 0x82b /* 802.11n 4x4 phy core */ +#define MIPS74K_CORE_ID 0x82c /* mips 74k core */ +#define GMAC_CORE_ID 0x82d /* Gigabit MAC core */ +#define DMEMC_CORE_ID 0x82e /* DDR1/2 memory controller core */ +#define PCIERC_CORE_ID 0x82f /* PCIE Root Complex core */ +#define OCP_CORE_ID 0x830 /* OCP2OCP bridge core */ +#define SC_CORE_ID 0x831 /* shared common core */ +#define AHB_CORE_ID 0x832 /* OCP2AHB bridge core */ +#define SPIH_CORE_ID 0x833 /* SPI host core */ +#define I2S_CORE_ID 0x834 /* I2S core */ +#define DMEMS_CORE_ID 0x835 /* SDR/DDR1 memory controller core */ +#define DEF_SHIM_COMP 0x837 /* SHIM component in ubus/6362 */ + +#define ACPHY_CORE_ID 0x83b /* Dot11 ACPHY */ +#define PCIE2_CORE_ID 0x83c /* pci express Gen2 core */ +#define USB30D_CORE_ID 0x83d /* usb 3.0 device core */ +#define ARMCR4_CORE_ID 0x83e /* ARM CR4 CPU */ +#define APB_BRIDGE_CORE_ID 0x135 /* APB bridge core ID */ +#define AXI_CORE_ID 0x301 /* AXI/GPV core ID */ +#define EROM_CORE_ID 0x366 /* EROM core ID */ +#define OOB_ROUTER_CORE_ID 0x367 /* OOB router core ID */ +#define DEF_AI_COMP 0xfff /* Default component, in ai chips it maps all + * unused address ranges + */ + +#define CC_4706_CORE_ID 0x500 /* chipcommon core */ +#define SOCRAM_4706_CORE_ID 0x50e /* internal memory core */ +#define GMAC_COMMON_4706_CORE_ID 0x5dc /* Gigabit MAC core */ +#define GMAC_4706_CORE_ID 0x52d /* Gigabit MAC core */ +#define AMEMC_CORE_ID 0x52e /* DDR1/2 memory controller core */ +#define ALTA_CORE_ID 0x534 /* I2S core */ +#define DDR23_PHY_CORE_ID 0x5dd + +#define SI_PCI1_MEM 0x40000000 /* Host Mode sb2pcitranslation0 (64 MB) */ +#define SI_PCI1_CFG 0x44000000 /* Host Mode sb2pcitranslation1 (64 MB) */ +#define SI_PCIE1_DMA_H32 0xc0000000 /* PCIE Client Mode sb2pcitranslation2 + * (2 ZettaBytes), high 32 bits + */ +#define CC_4706B0_CORE_REV 0x8000001f /* chipcommon core */ +#define SOCRAM_4706B0_CORE_REV 0x80000005 /* internal memory core */ +#define GMAC_4706B0_CORE_REV 0x80000000 /* Gigabit MAC core */ + +/* There are TWO constants on all HND chips: SI_ENUM_BASE above, + * and chipcommon being the first core: + */ +#define SI_CC_IDX 0 + +/* SOC Interconnect types (aka chip types) */ +#define SOCI_SB 0 +#define SOCI_AI 1 +#define SOCI_UBUS 2 + +/* Common core control flags */ +#define SICF_BIST_EN 0x8000 +#define SICF_PME_EN 0x4000 +#define SICF_CORE_BITS 0x3ffc +#define SICF_FGC 0x0002 +#define SICF_CLOCK_EN 0x0001 + +/* Common core status flags */ +#define SISF_BIST_DONE 0x8000 +#define SISF_BIST_ERROR 0x4000 +#define SISF_GATED_CLK 0x2000 +#define SISF_DMA64 0x1000 +#define SISF_CORE_BITS 0x0fff + +/* A register that is common to all cores to + * communicate w/PMU regarding clock control. + */ +#define SI_CLK_CTL_ST 0x1e0 /* clock control and status */ + +/* clk_ctl_st register */ +#define CCS_FORCEALP 0x00000001 /* force ALP request */ +#define CCS_FORCEHT 0x00000002 /* force HT request */ +#define CCS_FORCEILP 0x00000004 /* force ILP request */ +#define CCS_ALPAREQ 0x00000008 /* ALP Avail Request */ +#define CCS_HTAREQ 0x00000010 /* HT Avail Request */ +#define CCS_FORCEHWREQOFF 0x00000020 /* Force HW Clock Request Off */ +#define CCS_HQCLKREQ 0x00000040 /* HQ Clock Required */ +#define CCS_USBCLKREQ 0x00000100 /* USB Clock Req */ +#define CCS_ERSRC_REQ_MASK 0x00000700 /* external resource requests */ +#define CCS_ERSRC_REQ_SHIFT 8 +#define CCS_ALPAVAIL 0x00010000 /* ALP is available */ +#define CCS_HTAVAIL 0x00020000 /* HT is available */ +#define CCS_BP_ON_APL 0x00040000 /* RO: Backplane is running on ALP clock */ +#define CCS_BP_ON_HT 0x00080000 /* RO: Backplane is running on HT clock */ +#define CCS_ERSRC_STS_MASK 0x07000000 /* external resource status */ +#define CCS_ERSRC_STS_SHIFT 24 + +#define CCS0_HTAVAIL 0x00010000 /* HT avail in chipc and pcmcia on 4328a0 */ +#define CCS0_ALPAVAIL 0x00020000 /* ALP avail in chipc and pcmcia on 4328a0 */ + +/* Not really related to SOC Interconnect, but a couple of software + * conventions for the use the flash space: + */ + +/* Minumum amount of flash we support */ +#define FLASH_MIN 0x00020000 /* Minimum flash size */ + +/* A boot/binary may have an embedded block that describes its size */ +#define BISZ_OFFSET 0x3e0 /* At this offset into the binary */ +#define BISZ_MAGIC 0x4249535a /* Marked with this value: 'BISZ' */ +#define BISZ_MAGIC_IDX 0 /* Word 0: magic */ +#define BISZ_TXTST_IDX 1 /* 1: text start */ +#define BISZ_TXTEND_IDX 2 /* 2: text end */ +#define BISZ_DATAST_IDX 3 /* 3: data start */ +#define BISZ_DATAEND_IDX 4 /* 4: data end */ +#define BISZ_BSSST_IDX 5 /* 5: bss start */ +#define BISZ_BSSEND_IDX 6 /* 6: bss end */ +#define BISZ_SIZE 7 /* descriptor size in 32-bit integers */ + +#endif /* _HNDSOC_H */ diff --git a/drivers/net/wireless/bcmdhd/include/linux_osl.h b/drivers/net/wireless/bcmdhd/include/linux_osl.h new file mode 100644 index 00000000..d2f3a987 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/linux_osl.h @@ -0,0 +1,425 @@ +/* + * Linux OS Independent Layer + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: linux_osl.h 354452 2012-08-31 04:59:17Z $ + */ + +#ifndef _linux_osl_h_ +#define _linux_osl_h_ + +#include + + +extern void * osl_os_open_image(char * filename); +extern int osl_os_get_image_block(char * buf, int len, void * image); +extern void osl_os_close_image(void * image); +extern int osl_os_image_size(void *image); + + +#ifdef BCMDRIVER + + +extern osl_t *osl_attach(void *pdev, uint bustype, bool pkttag); +extern void osl_detach(osl_t *osh); + + +extern uint32 g_assert_type; + + +#if defined(BCMASSERT_LOG) + #define ASSERT(exp) \ + do { if (!(exp)) osl_assert(#exp, __FILE__, __LINE__); } while (0) +extern void osl_assert(const char *exp, const char *file, int line); +#else + #ifdef __GNUC__ + #define GCC_VERSION \ + (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) + #if GCC_VERSION > 30100 + #define ASSERT(exp) do {} while (0) + #else + + #define ASSERT(exp) + #endif + #endif +#endif + + +#define OSL_DELAY(usec) osl_delay(usec) +extern void osl_delay(uint usec); + +#define OSL_PCMCIA_READ_ATTR(osh, offset, buf, size) \ + osl_pcmcia_read_attr((osh), (offset), (buf), (size)) +#define OSL_PCMCIA_WRITE_ATTR(osh, offset, buf, size) \ + osl_pcmcia_write_attr((osh), (offset), (buf), (size)) +extern void osl_pcmcia_read_attr(osl_t *osh, uint offset, void *buf, int size); +extern void osl_pcmcia_write_attr(osl_t *osh, uint offset, void *buf, int size); + + +#define OSL_PCI_READ_CONFIG(osh, offset, size) \ + osl_pci_read_config((osh), (offset), (size)) +#define OSL_PCI_WRITE_CONFIG(osh, offset, size, val) \ + osl_pci_write_config((osh), (offset), (size), (val)) +extern uint32 osl_pci_read_config(osl_t *osh, uint offset, uint size); +extern void osl_pci_write_config(osl_t *osh, uint offset, uint size, uint val); + + +#define OSL_PCI_BUS(osh) osl_pci_bus(osh) +#define OSL_PCI_SLOT(osh) osl_pci_slot(osh) +extern uint osl_pci_bus(osl_t *osh); +extern uint osl_pci_slot(osl_t *osh); +extern struct pci_dev *osl_pci_device(osl_t *osh); + + +typedef struct { + bool pkttag; + uint pktalloced; + bool mmbus; + pktfree_cb_fn_t tx_fn; + void *tx_ctx; + void *unused[3]; +} osl_pubinfo_t; + +#define PKTFREESETCB(osh, _tx_fn, _tx_ctx) \ + do { \ + ((osl_pubinfo_t*)osh)->tx_fn = _tx_fn; \ + ((osl_pubinfo_t*)osh)->tx_ctx = _tx_ctx; \ + } while (0) + + + +#define BUS_SWAP32(v) (v) + + #define MALLOC(osh, size) osl_malloc((osh), (size)) + #define MFREE(osh, addr, size) osl_mfree((osh), (addr), (size)) + #define MALLOCED(osh) osl_malloced((osh)) + extern void *osl_malloc(osl_t *osh, uint size); + extern void osl_mfree(osl_t *osh, void *addr, uint size); + extern uint osl_malloced(osl_t *osh); + +#define NATIVE_MALLOC(osh, size) kmalloc(size, GFP_ATOMIC) +#define NATIVE_MFREE(osh, addr, size) kfree(addr) + +#define MALLOC_FAILED(osh) osl_malloc_failed((osh)) +extern uint osl_malloc_failed(osl_t *osh); + + +#define DMA_CONSISTENT_ALIGN osl_dma_consistent_align() +#define DMA_ALLOC_CONSISTENT(osh, size, align, tot, pap, dmah) \ + osl_dma_alloc_consistent((osh), (size), (align), (tot), (pap)) +#define DMA_FREE_CONSISTENT(osh, va, size, pa, dmah) \ + osl_dma_free_consistent((osh), (void*)(va), (size), (pa)) +extern uint osl_dma_consistent_align(void); +extern void *osl_dma_alloc_consistent(osl_t *osh, uint size, uint16 align, uint *tot, ulong *pap); +extern void osl_dma_free_consistent(osl_t *osh, void *va, uint size, ulong pa); + + +#define DMA_TX 1 +#define DMA_RX 2 + + +#define DMA_UNMAP(osh, pa, size, direction, p, dmah) \ + osl_dma_unmap((osh), (pa), (size), (direction)) +extern uint osl_dma_map(osl_t *osh, void *va, uint size, int direction); +extern void osl_dma_unmap(osl_t *osh, uint pa, uint size, int direction); + + +#define OSL_DMADDRWIDTH(osh, addrwidth) do {} while (0) + + + #include + #define OSL_WRITE_REG(osh, r, v) (bcmsdh_reg_write(NULL, (uintptr)(r), sizeof(*(r)), (v))) + #define OSL_READ_REG(osh, r) (bcmsdh_reg_read(NULL, (uintptr)(r), sizeof(*(r)))) + + #define SELECT_BUS_WRITE(osh, mmap_op, bus_op) if (((osl_pubinfo_t*)(osh))->mmbus) \ + mmap_op else bus_op + #define SELECT_BUS_READ(osh, mmap_op, bus_op) (((osl_pubinfo_t*)(osh))->mmbus) ? \ + mmap_op : bus_op + +#define OSL_ERROR(bcmerror) osl_error(bcmerror) +extern int osl_error(int bcmerror); + + +#define PKTBUFSZ 2048 + + +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 29) +#define OSL_SYSUPTIME() ((uint32)jiffies_to_msecs(jiffies)) +#else +#define OSL_SYSUPTIME() ((uint32)jiffies * (1000 / HZ)) +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 29) */ +#define printf(fmt, args...) printk(fmt , ## args) +#include +#include + +#define bcopy(src, dst, len) memcpy((dst), (src), (len)) +#define bcmp(b1, b2, len) memcmp((b1), (b2), (len)) +#define bzero(b, len) memset((b), '\0', (len)) + + + +#define R_REG(osh, r) (\ + SELECT_BUS_READ(osh, \ + ({ \ + __typeof(*(r)) __osl_v; \ + BCM_REFERENCE(osh); \ + switch (sizeof(*(r))) { \ + case sizeof(uint8): __osl_v = \ + readb((volatile uint8*)(r)); break; \ + case sizeof(uint16): __osl_v = \ + readw((volatile uint16*)(r)); break; \ + case sizeof(uint32): __osl_v = \ + readl((volatile uint32*)(r)); break; \ + } \ + __osl_v; \ + }), \ + OSL_READ_REG(osh, r)) \ +) + +#define W_REG(osh, r, v) do { \ + BCM_REFERENCE(osh); \ + SELECT_BUS_WRITE(osh, \ + switch (sizeof(*(r))) { \ + case sizeof(uint8): writeb((uint8)(v), (volatile uint8*)(r)); break; \ + case sizeof(uint16): writew((uint16)(v), (volatile uint16*)(r)); break; \ + case sizeof(uint32): writel((uint32)(v), (volatile uint32*)(r)); break; \ + }, \ + (OSL_WRITE_REG(osh, r, v))); \ + } while (0) + +#define AND_REG(osh, r, v) W_REG(osh, (r), R_REG(osh, r) & (v)) +#define OR_REG(osh, r, v) W_REG(osh, (r), R_REG(osh, r) | (v)) + + +#define bcopy(src, dst, len) memcpy((dst), (src), (len)) +#define bcmp(b1, b2, len) memcmp((b1), (b2), (len)) +#define bzero(b, len) memset((b), '\0', (len)) + + +#define OSL_UNCACHED(va) ((void *)va) +#define OSL_CACHED(va) ((void *)va) + +#define OSL_PREF_RANGE_LD(va, sz) +#define OSL_PREF_RANGE_ST(va, sz) + + +#if defined(__i386__) +#define OSL_GETCYCLES(x) rdtscl((x)) +#else +#define OSL_GETCYCLES(x) ((x) = 0) +#endif + + +#define BUSPROBE(val, addr) ({ (val) = R_REG(NULL, (addr)); 0; }) + + +#if !defined(CONFIG_MMC_MSM7X00A) +#define REG_MAP(pa, size) ioremap_nocache((unsigned long)(pa), (unsigned long)(size)) +#else +#define REG_MAP(pa, size) (void *)(0) +#endif +#define REG_UNMAP(va) iounmap((va)) + + +#define R_SM(r) *(r) +#define W_SM(r, v) (*(r) = (v)) +#define BZERO_SM(r, len) memset((r), '\0', (len)) + + +#include + + +#define PKTGET(osh, len, send) osl_pktget((osh), (len)) +#define PKTDUP(osh, skb) osl_pktdup((osh), (skb)) +#define PKTLIST_DUMP(osh, buf) +#define PKTDBG_TRACE(osh, pkt, bit) +#define PKTFREE(osh, skb, send) osl_pktfree((osh), (skb), (send)) +#ifdef CONFIG_DHD_USE_STATIC_BUF +#define PKTGET_STATIC(osh, len, send) osl_pktget_static((osh), (len)) +#define PKTFREE_STATIC(osh, skb, send) osl_pktfree_static((osh), (skb), (send)) +#endif +#define PKTDATA(osh, skb) (((struct sk_buff*)(skb))->data) +#define PKTLEN(osh, skb) (((struct sk_buff*)(skb))->len) +#define PKTHEADROOM(osh, skb) (PKTDATA(osh, skb)-(((struct sk_buff*)(skb))->head)) +#define PKTTAILROOM(osh, skb) ((((struct sk_buff*)(skb))->end)-(((struct sk_buff*)(skb))->tail)) +#define PKTNEXT(osh, skb) (((struct sk_buff*)(skb))->next) +#define PKTSETNEXT(osh, skb, x) (((struct sk_buff*)(skb))->next = (struct sk_buff*)(x)) +#define PKTSETLEN(osh, skb, len) __skb_trim((struct sk_buff*)(skb), (len)) +#define PKTPUSH(osh, skb, bytes) skb_push((struct sk_buff*)(skb), (bytes)) +#define PKTPULL(osh, skb, bytes) skb_pull((struct sk_buff*)(skb), (bytes)) +#define PKTTAG(skb) ((void*)(((struct sk_buff*)(skb))->cb)) +#define PKTALLOCED(osh) ((osl_pubinfo_t *)(osh))->pktalloced +#define PKTSETPOOL(osh, skb, x, y) do {} while (0) +#define PKTPOOL(osh, skb) FALSE +#define PKTSHRINK(osh, m) (m) + +#ifdef CTFPOOL +#define CTFPOOL_REFILL_THRESH 3 +typedef struct ctfpool { + void *head; + spinlock_t lock; + uint max_obj; + uint curr_obj; + uint obj_size; + uint refills; + uint fast_allocs; + uint fast_frees; + uint slow_allocs; +} ctfpool_t; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) +#define FASTBUF (1 << 16) +#define CTFBUF (1 << 17) +#define PKTSETFAST(osh, skb) ((((struct sk_buff*)(skb))->mac_len) |= FASTBUF) +#define PKTCLRFAST(osh, skb) ((((struct sk_buff*)(skb))->mac_len) &= (~FASTBUF)) +#define PKTSETCTF(osh, skb) ((((struct sk_buff*)(skb))->mac_len) |= CTFBUF) +#define PKTCLRCTF(osh, skb) ((((struct sk_buff*)(skb))->mac_len) &= (~CTFBUF)) +#define PKTISFAST(osh, skb) ((((struct sk_buff*)(skb))->mac_len) & FASTBUF) +#define PKTISCTF(osh, skb) ((((struct sk_buff*)(skb))->mac_len) & CTFBUF) +#define PKTFAST(osh, skb) (((struct sk_buff*)(skb))->mac_len) +#else +#define FASTBUF (1 << 0) +#define CTFBUF (1 << 1) +#define PKTSETFAST(osh, skb) ((((struct sk_buff*)(skb))->__unused) |= FASTBUF) +#define PKTCLRFAST(osh, skb) ((((struct sk_buff*)(skb))->__unused) &= (~FASTBUF)) +#define PKTSETCTF(osh, skb) ((((struct sk_buff*)(skb))->__unused) |= CTFBUF) +#define PKTCLRCTF(osh, skb) ((((struct sk_buff*)(skb))->__unused) &= (~CTFBUF)) +#define PKTISFAST(osh, skb) ((((struct sk_buff*)(skb))->__unused) & FASTBUF) +#define PKTISCTF(osh, skb) ((((struct sk_buff*)(skb))->__unused) & CTFBUF) +#define PKTFAST(osh, skb) (((struct sk_buff*)(skb))->__unused) +#endif + +#define CTFPOOLPTR(osh, skb) (((struct sk_buff*)(skb))->sk) +#define CTFPOOLHEAD(osh, skb) (((ctfpool_t *)((struct sk_buff*)(skb))->sk)->head) + +extern void *osl_ctfpool_add(osl_t *osh); +extern void osl_ctfpool_replenish(osl_t *osh, uint thresh); +extern int32 osl_ctfpool_init(osl_t *osh, uint numobj, uint size); +extern void osl_ctfpool_cleanup(osl_t *osh); +extern void osl_ctfpool_stats(osl_t *osh, void *b); +#endif + + +#ifdef HNDCTF +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) +#define SKIPCT (1 << 18) +#define PKTSETSKIPCT(osh, skb) (((struct sk_buff*)(skb))->mac_len |= SKIPCT) +#define PKTCLRSKIPCT(osh, skb) (((struct sk_buff*)(skb))->mac_len &= (~SKIPCT)) +#define PKTSKIPCT(osh, skb) (((struct sk_buff*)(skb))->mac_len & SKIPCT) +#else +#define SKIPCT (1 << 2) +#define PKTSETSKIPCT(osh, skb) (((struct sk_buff*)(skb))->__unused |= SKIPCT) +#define PKTCLRSKIPCT(osh, skb) (((struct sk_buff*)(skb))->__unused &= (~SKIPCT)) +#define PKTSKIPCT(osh, skb) (((struct sk_buff*)(skb))->__unused & SKIPCT) +#endif +#else +#define PKTSETSKIPCT(osh, skb) +#define PKTCLRSKIPCT(osh, skb) +#define PKTSKIPCT(osh, skb) +#endif + +extern void osl_pktfree(osl_t *osh, void *skb, bool send); +extern void *osl_pktget_static(osl_t *osh, uint len); +extern void osl_pktfree_static(osl_t *osh, void *skb, bool send); + +extern void *osl_pkt_frmnative(osl_t *osh, void *skb); +extern void *osl_pktget(osl_t *osh, uint len); +extern void *osl_pktdup(osl_t *osh, void *skb); +extern struct sk_buff *osl_pkt_tonative(osl_t *osh, void *pkt); +#define PKTFRMNATIVE(osh, skb) osl_pkt_frmnative(((osl_t *)osh), (struct sk_buff*)(skb)) +#define PKTTONATIVE(osh, pkt) osl_pkt_tonative((osl_t *)(osh), (pkt)) + +#define PKTLINK(skb) (((struct sk_buff*)(skb))->prev) +#define PKTSETLINK(skb, x) (((struct sk_buff*)(skb))->prev = (struct sk_buff*)(x)) +#define PKTPRIO(skb) (((struct sk_buff*)(skb))->priority) +#define PKTSETPRIO(skb, x) (((struct sk_buff*)(skb))->priority = (x)) +#define PKTSUMNEEDED(skb) (((struct sk_buff*)(skb))->ip_summed == CHECKSUM_HW) +#define PKTSETSUMGOOD(skb, x) (((struct sk_buff*)(skb))->ip_summed = \ + ((x) ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE)) + +#define PKTSHARED(skb) (((struct sk_buff*)(skb))->cloned) + +#define DMA_MAP(osh, va, size, direction, p, dmah) \ + osl_dma_map((osh), (va), (size), (direction)) + +#ifdef PKTC + +struct chain_node { + struct sk_buff *link; + unsigned int flags:3, pkts:9, bytes:20; +}; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14) +#define CHAIN_NODE(skb) ((struct chain_node*)&(((struct sk_buff*)skb)->tstamp)) +#else +#define CHAIN_NODE(skb) ((struct chain_node*)&(((struct sk_buff*)skb)->stamp)) +#endif + +#define PKTCCNT(skb) (CHAIN_NODE(skb)->pkts) +#define PKTCLEN(skb) (CHAIN_NODE(skb)->bytes) +#define PKTCFLAGS(skb) (CHAIN_NODE(skb)->flags) +#define PKTCSETCNT(skb, c) (CHAIN_NODE(skb)->pkts = (c) & ((1 << 9) - 1)) +#define PKTCSETLEN(skb, l) (CHAIN_NODE(skb)->bytes = (l) & ((1 << 20) - 1)) +#define PKTCSETFLAG(skb, fb) (CHAIN_NODE(skb)->flags |= (fb)) +#define PKTCCLRFLAG(skb, fb) (CHAIN_NODE(skb)->flags &= ~(fb)) +#define PKTCLINK(skb) (CHAIN_NODE(skb)->link) +#define PKTSETCLINK(skb, x) (CHAIN_NODE(skb)->link = (struct sk_buff*)(x)) +#define PKTISCHAINED(skb) (PKTCLINK(skb) != NULL) +#define FOREACH_CHAINED_PKT(skb, nskb) \ + for (; (skb) != NULL; (skb) = (nskb)) \ + if ((nskb) = PKTCLINK(skb), PKTSETCLINK((skb), NULL), 1) +#define PKTCFREE(osh, skb, send) \ +do { \ + void *nskb; \ + ASSERT((skb) != NULL); \ + FOREACH_CHAINED_PKT((skb), nskb) { \ + PKTFREE((osh), (skb), (send)); \ + } \ +} while (0) +#endif + +#else + + + + #define ASSERT(exp) do {} while (0) + + +#define MALLOC(o, l) malloc(l) +#define MFREE(o, p, l) free(p) +#include + + +#include + + +#include + + +extern void bcopy(const void *src, void *dst, size_t len); +extern int bcmp(const void *b1, const void *b2, size_t len); +extern void bzero(void *b, size_t len); +#endif + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/linuxver.h b/drivers/net/wireless/bcmdhd/include/linuxver.h new file mode 100644 index 00000000..f242aad4 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/linuxver.h @@ -0,0 +1,630 @@ +/* + * Linux-specific abstractions to gain some independence from linux kernel versions. + * Pave over some 2.2 versus 2.4 versus 2.6 kernel differences. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: linuxver.h 353905 2012-08-29 07:33:08Z $ + */ + +#ifndef _linuxver_h_ +#define _linuxver_h_ + +#include +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) +#include +#else +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)) +#include +#else +#include +#endif +#endif +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0)) + +#ifdef __UNDEF_NO_VERSION__ +#undef __NO_VERSION__ +#else +#define __NO_VERSION__ +#endif +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) +#define module_param(_name_, _type_, _perm_) MODULE_PARM(_name_, "i") +#define module_param_string(_name_, _string_, _size_, _perm_) \ + MODULE_PARM(_string_, "c" __MODULE_STRING(_size_)) +#endif + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 9)) +#include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) +#include +#else +#include +#endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) +#undef IP_TOS +#endif +#include + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 41)) +#include +#else +#include +#ifndef work_struct +#define work_struct tq_struct +#endif +#ifndef INIT_WORK +#define INIT_WORK(_work, _func, _data) INIT_TQUEUE((_work), (_func), (_data)) +#endif +#ifndef schedule_work +#define schedule_work(_work) schedule_task((_work)) +#endif +#ifndef flush_scheduled_work +#define flush_scheduled_work() flush_scheduled_tasks() +#endif +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) +#define DAEMONIZE(a) daemonize(a); \ + allow_signal(SIGKILL); \ + allow_signal(SIGTERM); +#else +#define RAISE_RX_SOFTIRQ() \ + cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ) +#define DAEMONIZE(a) daemonize(); \ + do { if (a) \ + strncpy(current->comm, a, MIN(sizeof(current->comm), (strlen(a)))); \ + } while (0); +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) +#define MY_INIT_WORK(_work, _func) INIT_WORK(_work, _func) +#else +#define MY_INIT_WORK(_work, _func) INIT_WORK(_work, _func, _work) +#if !(LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 18) && defined(RHEL_MAJOR) && \ + (RHEL_MAJOR == 5)) + +typedef void (*work_func_t)(void *work); +#endif +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) + +#ifndef IRQ_NONE +typedef void irqreturn_t; +#define IRQ_NONE +#define IRQ_HANDLED +#define IRQ_RETVAL(x) +#endif +#else +typedef irqreturn_t(*FN_ISR) (int irq, void *dev_id, struct pt_regs *ptregs); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) +#define IRQF_SHARED SA_SHIRQ +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 17) +#ifdef CONFIG_NET_RADIO +#define CONFIG_WIRELESS_EXT +#endif +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 67) +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) +#include +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) +#include +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) +#include +#else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14) +#include +#endif +#endif + + +#ifndef __exit +#define __exit +#endif +#ifndef __devexit +#define __devexit +#endif +#ifndef __devinit +#define __devinit __init +#endif +#ifndef __devinitdata +#define __devinitdata +#endif +#ifndef __devexit_p +#define __devexit_p(x) x +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0)) + +#define pci_get_drvdata(dev) (dev)->sysdata +#define pci_set_drvdata(dev, value) (dev)->sysdata = (value) + + + +struct pci_device_id { + unsigned int vendor, device; + unsigned int subvendor, subdevice; + unsigned int class, class_mask; + unsigned long driver_data; +}; + +struct pci_driver { + struct list_head node; + char *name; + const struct pci_device_id *id_table; + int (*probe)(struct pci_dev *dev, + const struct pci_device_id *id); + void (*remove)(struct pci_dev *dev); + void (*suspend)(struct pci_dev *dev); + void (*resume)(struct pci_dev *dev); +}; + +#define MODULE_DEVICE_TABLE(type, name) +#define PCI_ANY_ID (~0) + + +#define pci_module_init pci_register_driver +extern int pci_register_driver(struct pci_driver *drv); +extern void pci_unregister_driver(struct pci_driver *drv); + +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)) +#define pci_module_init pci_register_driver +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 18)) +#ifdef MODULE +#define module_init(x) int init_module(void) { return x(); } +#define module_exit(x) void cleanup_module(void) { x(); } +#else +#define module_init(x) __initcall(x); +#define module_exit(x) __exitcall(x); +#endif +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) +#define WL_USE_NETDEV_OPS +#else +#undef WL_USE_NETDEV_OPS +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) && defined(CONFIG_RFKILL) +#define WL_CONFIG_RFKILL +#else +#undef WL_CONFIG_RFKILL +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 48)) +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 13)) +#define pci_resource_start(dev, bar) ((dev)->base_address[(bar)]) +#elif (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 44)) +#define pci_resource_start(dev, bar) ((dev)->resource[(bar)].start) +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 23)) +#define pci_enable_device(dev) do { } while (0) +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 14)) +#define net_device device +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 42)) + + + +#ifndef PCI_DMA_TODEVICE +#define PCI_DMA_TODEVICE 1 +#define PCI_DMA_FROMDEVICE 2 +#endif + +typedef u32 dma_addr_t; + + +static inline int get_order(unsigned long size) +{ + int order; + + size = (size-1) >> (PAGE_SHIFT-1); + order = -1; + do { + size >>= 1; + order++; + } while (size); + return order; +} + +static inline void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle) +{ + void *ret; + int gfp = GFP_ATOMIC | GFP_DMA; + + ret = (void *)__get_free_pages(gfp, get_order(size)); + + if (ret != NULL) { + memset(ret, 0, size); + *dma_handle = virt_to_bus(ret); + } + return ret; +} +static inline void pci_free_consistent(struct pci_dev *hwdev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + free_pages((unsigned long)vaddr, get_order(size)); +} +#define pci_map_single(cookie, address, size, dir) virt_to_bus(address) +#define pci_unmap_single(cookie, address, size, dir) + +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 43)) + +#define dev_kfree_skb_any(a) dev_kfree_skb(a) +#define netif_down(dev) do { (dev)->start = 0; } while (0) + + +#ifndef _COMPAT_NETDEVICE_H + + + +#define dev_kfree_skb_irq(a) dev_kfree_skb(a) +#define netif_wake_queue(dev) \ + do { clear_bit(0, &(dev)->tbusy); mark_bh(NET_BH); } while (0) +#define netif_stop_queue(dev) set_bit(0, &(dev)->tbusy) + +static inline void netif_start_queue(struct net_device *dev) +{ + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; +} + +#define netif_queue_stopped(dev) (dev)->tbusy +#define netif_running(dev) (dev)->start + +#endif + +#define netif_device_attach(dev) netif_start_queue(dev) +#define netif_device_detach(dev) netif_stop_queue(dev) + + +#define tasklet_struct tq_struct +static inline void tasklet_schedule(struct tasklet_struct *tasklet) +{ + queue_task(tasklet, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static inline void tasklet_init(struct tasklet_struct *tasklet, + void (*func)(unsigned long), + unsigned long data) +{ + tasklet->next = NULL; + tasklet->sync = 0; + tasklet->routine = (void (*)(void *))func; + tasklet->data = (void *)data; +} +#define tasklet_kill(tasklet) { do {} while (0); } + + +#define del_timer_sync(timer) del_timer(timer) + +#else + +#define netif_down(dev) + +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 3)) + + +#define PREPARE_TQUEUE(_tq, _routine, _data) \ + do { \ + (_tq)->routine = _routine; \ + (_tq)->data = _data; \ + } while (0) + + +#define INIT_TQUEUE(_tq, _routine, _data) \ + do { \ + INIT_LIST_HEAD(&(_tq)->list); \ + (_tq)->sync = 0; \ + PREPARE_TQUEUE((_tq), (_routine), (_data)); \ + } while (0) + +#endif + + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 9) +#define PCI_SAVE_STATE(a, b) pci_save_state(a) +#define PCI_RESTORE_STATE(a, b) pci_restore_state(a) +#else +#define PCI_SAVE_STATE(a, b) pci_save_state(a, b) +#define PCI_RESTORE_STATE(a, b) pci_restore_state(a, b) +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 6)) +static inline int +pci_save_state(struct pci_dev *dev, u32 *buffer) +{ + int i; + if (buffer) { + for (i = 0; i < 16; i++) + pci_read_config_dword(dev, i * 4, &buffer[i]); + } + return 0; +} + +static inline int +pci_restore_state(struct pci_dev *dev, u32 *buffer) +{ + int i; + + if (buffer) { + for (i = 0; i < 16; i++) + pci_write_config_dword(dev, i * 4, buffer[i]); + } + + else { + for (i = 0; i < 6; i ++) + pci_write_config_dword(dev, + PCI_BASE_ADDRESS_0 + (i * 4), + pci_resource_start(dev, i)); + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); + } + return 0; +} +#endif + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 19)) +#define read_c0_count() read_32bit_cp0_register(CP0_COUNT) +#endif + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)) +#ifndef SET_MODULE_OWNER +#define SET_MODULE_OWNER(dev) do {} while (0) +#define OLD_MOD_INC_USE_COUNT MOD_INC_USE_COUNT +#define OLD_MOD_DEC_USE_COUNT MOD_DEC_USE_COUNT +#else +#define OLD_MOD_INC_USE_COUNT do {} while (0) +#define OLD_MOD_DEC_USE_COUNT do {} while (0) +#endif +#else +#ifndef SET_MODULE_OWNER +#define SET_MODULE_OWNER(dev) do {} while (0) +#endif +#ifndef MOD_INC_USE_COUNT +#define MOD_INC_USE_COUNT do {} while (0) +#endif +#ifndef MOD_DEC_USE_COUNT +#define MOD_DEC_USE_COUNT do {} while (0) +#endif +#define OLD_MOD_INC_USE_COUNT MOD_INC_USE_COUNT +#define OLD_MOD_DEC_USE_COUNT MOD_DEC_USE_COUNT +#endif + +#ifndef SET_NETDEV_DEV +#define SET_NETDEV_DEV(net, pdev) do {} while (0) +#endif + +#ifndef HAVE_FREE_NETDEV +#define free_netdev(dev) kfree(dev) +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) + +#define af_packet_priv data +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) +#define DRV_SUSPEND_STATE_TYPE pm_message_t +#else +#define DRV_SUSPEND_STATE_TYPE uint32 +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) +#define CHECKSUM_HW CHECKSUM_PARTIAL +#endif + +typedef struct { + void *parent; + struct task_struct *p_task; + long thr_pid; + int prio; + struct semaphore sema; + int terminated; + struct completion completed; +} tsk_ctl_t; + + + + +#ifdef DHD_DEBUG +#define DBG_THR(x) printk x +#else +#define DBG_THR(x) +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) +#define SMP_RD_BARRIER_DEPENDS(x) smp_read_barrier_depends(x) +#else +#define SMP_RD_BARRIER_DEPENDS(x) smp_rmb(x) +#endif + + +#define PROC_START(thread_func, owner, tsk_ctl, flags) \ +{ \ + sema_init(&((tsk_ctl)->sema), 0); \ + init_completion(&((tsk_ctl)->completed)); \ + (tsk_ctl)->parent = owner; \ + (tsk_ctl)->terminated = FALSE; \ + (tsk_ctl)->thr_pid = kernel_thread(thread_func, tsk_ctl, flags); \ + DBG_THR(("%s thr:%lx created\n", __FUNCTION__, (tsk_ctl)->thr_pid)); \ + if ((tsk_ctl)->thr_pid > 0) \ + wait_for_completion(&((tsk_ctl)->completed)); \ + DBG_THR(("%s thr:%lx started\n", __FUNCTION__, (tsk_ctl)->thr_pid)); \ +} + +#ifdef USE_KTHREAD_API +#define PROC_START2(thread_func, owner, tsk_ctl, flags, name) \ +{ \ + sema_init(&((tsk_ctl)->sema), 0); \ + init_completion(&((tsk_ctl)->completed)); \ + (tsk_ctl)->parent = owner; \ + (tsk_ctl)->terminated = FALSE; \ + (tsk_ctl)->p_task = kthread_run(thread_func, tsk_ctl, (char*)name); \ + (tsk_ctl)->thr_pid = (tsk_ctl)->p_task->pid; \ + DBG_THR(("%s thr:%lx created\n", __FUNCTION__, (tsk_ctl)->thr_pid)); \ +} +#endif + +#define PROC_STOP(tsk_ctl) \ +{ \ + (tsk_ctl)->terminated = TRUE; \ + smp_wmb(); \ + up(&((tsk_ctl)->sema)); \ + wait_for_completion(&((tsk_ctl)->completed)); \ + DBG_THR(("%s thr:%lx terminated OK\n", __FUNCTION__, (tsk_ctl)->thr_pid)); \ + (tsk_ctl)->thr_pid = -1; \ +} + + + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) +#define KILL_PROC(nr, sig) \ +{ \ +struct task_struct *tsk; \ +struct pid *pid; \ +pid = find_get_pid((pid_t)nr); \ +tsk = pid_task(pid, PIDTYPE_PID); \ +if (tsk) send_sig(sig, tsk, 1); \ +} +#else +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && (LINUX_VERSION_CODE <= \ + KERNEL_VERSION(2, 6, 30)) +#define KILL_PROC(pid, sig) \ +{ \ + struct task_struct *tsk; \ + tsk = find_task_by_vpid(pid); \ + if (tsk) send_sig(sig, tsk, 1); \ +} +#else +#define KILL_PROC(pid, sig) \ +{ \ + kill_proc(pid, sig, 1); \ +} +#endif +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) +#include +#include +#else +#include + +#define __wait_event_interruptible_timeout(wq, condition, ret) \ +do { \ + wait_queue_t __wait; \ + init_waitqueue_entry(&__wait, current); \ + \ + add_wait_queue(&wq, &__wait); \ + for (;;) { \ + set_current_state(TASK_INTERRUPTIBLE); \ + if (condition) \ + break; \ + if (!signal_pending(current)) { \ + ret = schedule_timeout(ret); \ + if (!ret) \ + break; \ + continue; \ + } \ + ret = -ERESTARTSYS; \ + break; \ + } \ + current->state = TASK_RUNNING; \ + remove_wait_queue(&wq, &__wait); \ +} while (0) + +#define wait_event_interruptible_timeout(wq, condition, timeout) \ +({ \ + long __ret = timeout; \ + if (!(condition)) \ + __wait_event_interruptible_timeout(wq, condition, __ret); \ + __ret; \ +}) + +#endif + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)) +#define DEV_PRIV(dev) (dev->priv) +#else +#define DEV_PRIV(dev) netdev_priv(dev) +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) +#define WL_ISR(i, d, p) wl_isr((i), (d)) +#else +#define WL_ISR(i, d, p) wl_isr((i), (d), (p)) +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) +#define netdev_priv(dev) dev->priv +#endif + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/miniopt.h b/drivers/net/wireless/bcmdhd/include/miniopt.h new file mode 100644 index 00000000..c1eca68a --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/miniopt.h @@ -0,0 +1,77 @@ +/* + * Command line options parser. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * $Id: miniopt.h 241182 2011-02-17 21:50:03Z $ + */ + + +#ifndef MINI_OPT_H +#define MINI_OPT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* ---- Include Files ---------------------------------------------------- */ +/* ---- Constants and Types ---------------------------------------------- */ + +#define MINIOPT_MAXKEY 128 /* Max options */ +typedef struct miniopt { + + /* These are persistent after miniopt_init() */ + const char* name; /* name for prompt in error strings */ + const char* flags; /* option chars that take no args */ + bool longflags; /* long options may be flags */ + bool opt_end; /* at end of options (passed a "--") */ + + /* These are per-call to miniopt() */ + + int consumed; /* number of argv entries cosumed in + * the most recent call to miniopt() + */ + bool positional; + bool good_int; /* 'val' member is the result of a sucessful + * strtol conversion of the option value + */ + char opt; + char key[MINIOPT_MAXKEY]; + char* valstr; /* positional param, or value for the option, + * or null if the option had + * no accompanying value + */ + uint uval; /* strtol translation of valstr */ + int val; /* strtol translation of valstr */ +} miniopt_t; + +void miniopt_init(miniopt_t *t, const char* name, const char* flags, bool longflags); +int miniopt(miniopt_t *t, char **argv); + + +/* ---- Variable Externs ------------------------------------------------- */ +/* ---- Function Prototypes ---------------------------------------------- */ + + +#ifdef __cplusplus + } +#endif + +#endif /* MINI_OPT_H */ diff --git a/drivers/net/wireless/bcmdhd/include/msgtrace.h b/drivers/net/wireless/bcmdhd/include/msgtrace.h new file mode 100644 index 00000000..7c5fd810 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/msgtrace.h @@ -0,0 +1,74 @@ +/* + * Trace messages sent over HBUS + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: msgtrace.h 281527 2011-09-02 17:12:53Z $ + */ + +#ifndef _MSGTRACE_H +#define _MSGTRACE_H + +#ifndef _TYPEDEFS_H_ +#include +#endif + + +/* This marks the start of a packed structure section. */ +#include + +#define MSGTRACE_VERSION 1 + +/* Message trace header */ +typedef BWL_PRE_PACKED_STRUCT struct msgtrace_hdr { + uint8 version; + uint8 spare; + uint16 len; /* Len of the trace */ + uint32 seqnum; /* Sequence number of message. Useful if the messsage has been lost + * because of DMA error or a bus reset (ex: SDIO Func2) + */ + uint32 discarded_bytes; /* Number of discarded bytes because of trace overflow */ + uint32 discarded_printf; /* Number of discarded printf because of trace overflow */ +} BWL_POST_PACKED_STRUCT msgtrace_hdr_t; + +#define MSGTRACE_HDRLEN sizeof(msgtrace_hdr_t) + +/* The hbus driver generates traces when sending a trace message. This causes endless traces. + * This flag must be set to TRUE in any hbus traces. The flag is reset in the function msgtrace_put. + * This prevents endless traces but generates hasardous lost of traces only in bus device code. + * It is recommendat to set this flag in macro SD_TRACE but not in SD_ERROR for avoiding missing + * hbus error traces. hbus error trace should not generates endless traces. + */ +extern bool msgtrace_hbus_trace; + +typedef void (*msgtrace_func_send_t)(void *hdl1, void *hdl2, uint8 *hdr, + uint16 hdrlen, uint8 *buf, uint16 buflen); +extern void msgtrace_start(void); +extern void msgtrace_stop(void); +extern void msgtrace_sent(void); +extern void msgtrace_put(char *buf, int count); +extern void msgtrace_init(void *hdl1, void *hdl2, msgtrace_func_send_t func_send); +extern bool msgtrace_event_enabled(void); + +/* This marks the end of a packed structure section. */ +#include + +#endif /* _MSGTRACE_H */ diff --git a/drivers/net/wireless/bcmdhd/include/osl.h b/drivers/net/wireless/bcmdhd/include/osl.h new file mode 100644 index 00000000..ca171d8a --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/osl.h @@ -0,0 +1,88 @@ +/* + * OS Abstraction Layer + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: osl.h 320905 2012-03-13 15:33:25Z $ + */ + +#ifndef _osl_h_ +#define _osl_h_ + + +typedef struct osl_info osl_t; +typedef struct osl_dmainfo osldma_t; + +#define OSL_PKTTAG_SZ 32 + + +typedef void (*pktfree_cb_fn_t)(void *ctx, void *pkt, unsigned int status); + + +typedef unsigned int (*osl_rreg_fn_t)(void *ctx, volatile void *reg, unsigned int size); +typedef void (*osl_wreg_fn_t)(void *ctx, volatile void *reg, unsigned int val, unsigned int size); + + +#include + +#ifndef PKTDBG_TRACE +#define PKTDBG_TRACE(osh, pkt, bit) +#endif + +#define PKTCTFMAP(osh, p) + + + +#define SET_REG(osh, r, mask, val) W_REG((osh), (r), ((R_REG((osh), r) & ~(mask)) | (val))) + +#ifndef AND_REG +#define AND_REG(osh, r, v) W_REG(osh, (r), R_REG(osh, r) & (v)) +#endif + +#ifndef OR_REG +#define OR_REG(osh, r, v) W_REG(osh, (r), R_REG(osh, r) | (v)) +#endif + +#if !defined(OSL_SYSUPTIME) +#define OSL_SYSUPTIME() (0) +#define OSL_SYSUPTIME_SUPPORT FALSE +#else +#define OSL_SYSUPTIME_SUPPORT TRUE +#endif + +#if !defined(PKTC) +#define PKTCCNT(skb) (0) +#define PKTCLEN(skb) (0) +#define PKTCFLAGS(skb) (0) +#define PKTCSETCNT(skb, c) +#define PKTCSETLEN(skb, l) +#define PKTCSETFLAG(skb, fb) +#define PKTCCLRFLAG(skb, fb) +#define PKTCLINK(skb) PKTLINK(skb) +#define PKTSETCLINK(skb, x) PKTSETLINK((skb), (x)) +#define PKTISCHAINED(skb) FALSE +#define FOREACH_CHAINED_PKT(skb, nskb) \ + for ((nskb) = NULL; (skb) != NULL; (skb) = (nskb)) +#define PKTCFREE PKTFREE +#endif + + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/packed_section_end.h b/drivers/net/wireless/bcmdhd/include/packed_section_end.h new file mode 100644 index 00000000..24ff4672 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/packed_section_end.h @@ -0,0 +1,53 @@ +/* + * Declare directives for structure packing. No padding will be provided + * between the members of packed structures, and therefore, there is no + * guarantee that structure members will be aligned. + * + * Declaring packed structures is compiler specific. In order to handle all + * cases, packed structures should be delared as: + * + * #include + * + * typedef BWL_PRE_PACKED_STRUCT struct foobar_t { + * some_struct_members; + * } BWL_POST_PACKED_STRUCT foobar_t; + * + * #include + * + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * $Id: packed_section_end.h 241182 2011-02-17 21:50:03Z $ + */ + + + +#ifdef BWL_PACKED_SECTION + #undef BWL_PACKED_SECTION +#else + #error "BWL_PACKED_SECTION is NOT defined!" +#endif + + + + + +#undef BWL_PRE_PACKED_STRUCT +#undef BWL_POST_PACKED_STRUCT diff --git a/drivers/net/wireless/bcmdhd/include/packed_section_start.h b/drivers/net/wireless/bcmdhd/include/packed_section_start.h new file mode 100644 index 00000000..7fce0ddf --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/packed_section_start.h @@ -0,0 +1,60 @@ +/* + * Declare directives for structure packing. No padding will be provided + * between the members of packed structures, and therefore, there is no + * guarantee that structure members will be aligned. + * + * Declaring packed structures is compiler specific. In order to handle all + * cases, packed structures should be delared as: + * + * #include + * + * typedef BWL_PRE_PACKED_STRUCT struct foobar_t { + * some_struct_members; + * } BWL_POST_PACKED_STRUCT foobar_t; + * + * #include + * + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * $Id: packed_section_start.h 286783 2011-09-29 06:18:57Z $ + */ + + + +#ifdef BWL_PACKED_SECTION + #error "BWL_PACKED_SECTION is already defined!" +#else + #define BWL_PACKED_SECTION +#endif + + + + + +#if defined(__GNUC__) || defined(__lint) + #define BWL_PRE_PACKED_STRUCT + #define BWL_POST_PACKED_STRUCT __attribute__ ((packed)) +#elif defined(__CC_ARM) + #define BWL_PRE_PACKED_STRUCT __packed + #define BWL_POST_PACKED_STRUCT +#else + #error "Unknown compiler!" +#endif diff --git a/drivers/net/wireless/bcmdhd/include/pcicfg.h b/drivers/net/wireless/bcmdhd/include/pcicfg.h new file mode 100644 index 00000000..5f7df6a7 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/pcicfg.h @@ -0,0 +1,90 @@ +/* + * pcicfg.h: PCI configuration constants and structures. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: pcicfg.h 309193 2012-01-19 00:03:57Z $ + */ + +#ifndef _h_pcicfg_ +#define _h_pcicfg_ + + +#define PCI_CFG_VID 0 +#define PCI_CFG_DID 2 +#define PCI_CFG_CMD 4 +#define PCI_CFG_STAT 6 +#define PCI_CFG_REV 8 +#define PCI_CFG_PROGIF 9 +#define PCI_CFG_SUBCL 0xa +#define PCI_CFG_BASECL 0xb +#define PCI_CFG_CLSZ 0xc +#define PCI_CFG_LATTIM 0xd +#define PCI_CFG_HDR 0xe +#define PCI_CFG_BIST 0xf +#define PCI_CFG_BAR0 0x10 +#define PCI_CFG_BAR1 0x14 +#define PCI_CFG_BAR2 0x18 +#define PCI_CFG_BAR3 0x1c +#define PCI_CFG_BAR4 0x20 +#define PCI_CFG_BAR5 0x24 +#define PCI_CFG_CIS 0x28 +#define PCI_CFG_SVID 0x2c +#define PCI_CFG_SSID 0x2e +#define PCI_CFG_ROMBAR 0x30 +#define PCI_CFG_CAPPTR 0x34 +#define PCI_CFG_INT 0x3c +#define PCI_CFG_PIN 0x3d +#define PCI_CFG_MINGNT 0x3e +#define PCI_CFG_MAXLAT 0x3f +#define PCI_BAR0_WIN 0x80 +#define PCI_BAR1_WIN 0x84 +#define PCI_SPROM_CONTROL 0x88 +#define PCI_BAR1_CONTROL 0x8c +#define PCI_INT_STATUS 0x90 +#define PCI_INT_MASK 0x94 +#define PCI_TO_SB_MB 0x98 +#define PCI_BACKPLANE_ADDR 0xa0 +#define PCI_BACKPLANE_DATA 0xa4 +#define PCI_CLK_CTL_ST 0xa8 +#define PCI_BAR0_WIN2 0xac +#define PCI_GPIO_IN 0xb0 +#define PCI_GPIO_OUT 0xb4 +#define PCI_GPIO_OUTEN 0xb8 + +#define PCI_BAR0_SHADOW_OFFSET (2 * 1024) +#define PCI_BAR0_SPROM_OFFSET (4 * 1024) +#define PCI_BAR0_PCIREGS_OFFSET (6 * 1024) +#define PCI_BAR0_PCISBR_OFFSET (4 * 1024) + +#define PCIE2_BAR0_WIN2 0x70 +#define PCIE2_BAR0_CORE2_WIN 0x74 +#define PCIE2_BAR0_CORE2_WIN2 0x78 + +#define PCI_BAR0_WINSZ (16 * 1024) + +#define PCI_16KB0_PCIREGS_OFFSET (8 * 1024) +#define PCI_16KB0_CCREGS_OFFSET (12 * 1024) +#define PCI_16KBB0_WINSZ (16 * 1024) + + +#define PCI_CONFIG_SPACE_SIZE 256 +#endif diff --git a/drivers/net/wireless/bcmdhd/include/proto/802.11.h b/drivers/net/wireless/bcmdhd/include/proto/802.11.h new file mode 100644 index 00000000..dc648eef --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/802.11.h @@ -0,0 +1,2243 @@ +/* + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * Fundamental types and constants relating to 802.11 + * + * $Id: 802.11.h 346820 2012-07-24 13:53:12Z $ + */ + +#ifndef _802_11_H_ +#define _802_11_H_ + +#ifndef _TYPEDEFS_H_ +#include +#endif + +#ifndef _NET_ETHERNET_H_ +#include +#endif + +#include + + +#include + + +#define DOT11_TU_TO_US 1024 + + +#define DOT11_A3_HDR_LEN 24 +#define DOT11_A4_HDR_LEN 30 +#define DOT11_MAC_HDR_LEN DOT11_A3_HDR_LEN +#define DOT11_FCS_LEN 4 +#define DOT11_ICV_LEN 4 +#define DOT11_ICV_AES_LEN 8 +#define DOT11_QOS_LEN 2 +#define DOT11_HTC_LEN 4 + +#define DOT11_KEY_INDEX_SHIFT 6 +#define DOT11_IV_LEN 4 +#define DOT11_IV_TKIP_LEN 8 +#define DOT11_IV_AES_OCB_LEN 4 +#define DOT11_IV_AES_CCM_LEN 8 +#define DOT11_IV_MAX_LEN 8 + + +#define DOT11_MAX_MPDU_BODY_LEN 2304 + +#define DOT11_MAX_MPDU_LEN (DOT11_A4_HDR_LEN + \ + DOT11_QOS_LEN + \ + DOT11_IV_AES_CCM_LEN + \ + DOT11_MAX_MPDU_BODY_LEN + \ + DOT11_ICV_LEN + \ + DOT11_FCS_LEN) + +#define DOT11_MAX_SSID_LEN 32 + + +#define DOT11_DEFAULT_RTS_LEN 2347 +#define DOT11_MAX_RTS_LEN 2347 + + +#define DOT11_MIN_FRAG_LEN 256 +#define DOT11_MAX_FRAG_LEN 2346 +#define DOT11_DEFAULT_FRAG_LEN 2346 + + +#define DOT11_MIN_BEACON_PERIOD 1 +#define DOT11_MAX_BEACON_PERIOD 0xFFFF + + +#define DOT11_MIN_DTIM_PERIOD 1 +#define DOT11_MAX_DTIM_PERIOD 0xFF + + +#define DOT11_LLC_SNAP_HDR_LEN 8 +#define DOT11_OUI_LEN 3 +BWL_PRE_PACKED_STRUCT struct dot11_llc_snap_header { + uint8 dsap; + uint8 ssap; + uint8 ctl; + uint8 oui[DOT11_OUI_LEN]; + uint16 type; +} BWL_POST_PACKED_STRUCT; + + +#define RFC1042_HDR_LEN (ETHER_HDR_LEN + DOT11_LLC_SNAP_HDR_LEN) + + + +BWL_PRE_PACKED_STRUCT struct dot11_header { + uint16 fc; + uint16 durid; + struct ether_addr a1; + struct ether_addr a2; + struct ether_addr a3; + uint16 seq; + struct ether_addr a4; +} BWL_POST_PACKED_STRUCT; + + + +BWL_PRE_PACKED_STRUCT struct dot11_rts_frame { + uint16 fc; + uint16 durid; + struct ether_addr ra; + struct ether_addr ta; +} BWL_POST_PACKED_STRUCT; +#define DOT11_RTS_LEN 16 + +BWL_PRE_PACKED_STRUCT struct dot11_cts_frame { + uint16 fc; + uint16 durid; + struct ether_addr ra; +} BWL_POST_PACKED_STRUCT; +#define DOT11_CTS_LEN 10 + +BWL_PRE_PACKED_STRUCT struct dot11_ack_frame { + uint16 fc; + uint16 durid; + struct ether_addr ra; +} BWL_POST_PACKED_STRUCT; +#define DOT11_ACK_LEN 10 + +BWL_PRE_PACKED_STRUCT struct dot11_ps_poll_frame { + uint16 fc; + uint16 durid; + struct ether_addr bssid; + struct ether_addr ta; +} BWL_POST_PACKED_STRUCT; +#define DOT11_PS_POLL_LEN 16 + +BWL_PRE_PACKED_STRUCT struct dot11_cf_end_frame { + uint16 fc; + uint16 durid; + struct ether_addr ra; + struct ether_addr bssid; +} BWL_POST_PACKED_STRUCT; +#define DOT11_CS_END_LEN 16 + + +BWL_PRE_PACKED_STRUCT struct dot11_action_wifi_vendor_specific { + uint8 category; + uint8 OUI[3]; + uint8 type; + uint8 subtype; + uint8 data[1040]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_action_wifi_vendor_specific dot11_action_wifi_vendor_specific_t; + + +BWL_PRE_PACKED_STRUCT struct dot11_action_vs_frmhdr { + uint8 category; + uint8 OUI[3]; + uint8 type; + uint8 subtype; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_action_vs_frmhdr dot11_action_vs_frmhdr_t; +#define DOT11_ACTION_VS_HDR_LEN 6 + +#define BCM_ACTION_OUI_BYTE0 0x00 +#define BCM_ACTION_OUI_BYTE1 0x90 +#define BCM_ACTION_OUI_BYTE2 0x4c + + +#define DOT11_BA_CTL_POLICY_NORMAL 0x0000 +#define DOT11_BA_CTL_POLICY_NOACK 0x0001 +#define DOT11_BA_CTL_POLICY_MASK 0x0001 + +#define DOT11_BA_CTL_MTID 0x0002 +#define DOT11_BA_CTL_COMPRESSED 0x0004 + +#define DOT11_BA_CTL_NUMMSDU_MASK 0x0FC0 +#define DOT11_BA_CTL_NUMMSDU_SHIFT 6 + +#define DOT11_BA_CTL_TID_MASK 0xF000 +#define DOT11_BA_CTL_TID_SHIFT 12 + + +BWL_PRE_PACKED_STRUCT struct dot11_ctl_header { + uint16 fc; + uint16 durid; + struct ether_addr ra; + struct ether_addr ta; +} BWL_POST_PACKED_STRUCT; +#define DOT11_CTL_HDR_LEN 16 + + +BWL_PRE_PACKED_STRUCT struct dot11_bar { + uint16 bar_control; + uint16 seqnum; +} BWL_POST_PACKED_STRUCT; +#define DOT11_BAR_LEN 4 + +#define DOT11_BA_BITMAP_LEN 128 +#define DOT11_BA_CMP_BITMAP_LEN 8 + +BWL_PRE_PACKED_STRUCT struct dot11_ba { + uint16 ba_control; + uint16 seqnum; + uint8 bitmap[DOT11_BA_BITMAP_LEN]; +} BWL_POST_PACKED_STRUCT; +#define DOT11_BA_LEN 4 + + +BWL_PRE_PACKED_STRUCT struct dot11_management_header { + uint16 fc; + uint16 durid; + struct ether_addr da; + struct ether_addr sa; + struct ether_addr bssid; + uint16 seq; +} BWL_POST_PACKED_STRUCT; +#define DOT11_MGMT_HDR_LEN 24 + + + +BWL_PRE_PACKED_STRUCT struct dot11_bcn_prb { + uint32 timestamp[2]; + uint16 beacon_interval; + uint16 capability; +} BWL_POST_PACKED_STRUCT; +#define DOT11_BCN_PRB_LEN 12 +#define DOT11_BCN_PRB_FIXED_LEN 12 + +BWL_PRE_PACKED_STRUCT struct dot11_auth { + uint16 alg; + uint16 seq; + uint16 status; +} BWL_POST_PACKED_STRUCT; +#define DOT11_AUTH_FIXED_LEN 6 + +BWL_PRE_PACKED_STRUCT struct dot11_assoc_req { + uint16 capability; + uint16 listen; +} BWL_POST_PACKED_STRUCT; +#define DOT11_ASSOC_REQ_FIXED_LEN 4 + +BWL_PRE_PACKED_STRUCT struct dot11_reassoc_req { + uint16 capability; + uint16 listen; + struct ether_addr ap; +} BWL_POST_PACKED_STRUCT; +#define DOT11_REASSOC_REQ_FIXED_LEN 10 + +BWL_PRE_PACKED_STRUCT struct dot11_assoc_resp { + uint16 capability; + uint16 status; + uint16 aid; +} BWL_POST_PACKED_STRUCT; +#define DOT11_ASSOC_RESP_FIXED_LEN 6 + +BWL_PRE_PACKED_STRUCT struct dot11_action_measure { + uint8 category; + uint8 action; + uint8 token; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +#define DOT11_ACTION_MEASURE_LEN 3 + +BWL_PRE_PACKED_STRUCT struct dot11_action_ht_ch_width { + uint8 category; + uint8 action; + uint8 ch_width; +} BWL_POST_PACKED_STRUCT; + +BWL_PRE_PACKED_STRUCT struct dot11_action_ht_mimops { + uint8 category; + uint8 action; + uint8 control; +} BWL_POST_PACKED_STRUCT; + +BWL_PRE_PACKED_STRUCT struct dot11_action_sa_query { + uint8 category; + uint8 action; + uint16 id; +} BWL_POST_PACKED_STRUCT; + +#define SM_PWRSAVE_ENABLE 1 +#define SM_PWRSAVE_MODE 2 + + +BWL_PRE_PACKED_STRUCT struct dot11_power_cnst { + uint8 id; + uint8 len; + uint8 power; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_power_cnst dot11_power_cnst_t; + +BWL_PRE_PACKED_STRUCT struct dot11_power_cap { + uint8 min; + uint8 max; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_power_cap dot11_power_cap_t; + +BWL_PRE_PACKED_STRUCT struct dot11_tpc_rep { + uint8 id; + uint8 len; + uint8 tx_pwr; + uint8 margin; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_tpc_rep dot11_tpc_rep_t; +#define DOT11_MNG_IE_TPC_REPORT_LEN 2 + +BWL_PRE_PACKED_STRUCT struct dot11_supp_channels { + uint8 id; + uint8 len; + uint8 first_channel; + uint8 num_channels; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_supp_channels dot11_supp_channels_t; + + +BWL_PRE_PACKED_STRUCT struct dot11_extch { + uint8 id; + uint8 len; + uint8 extch; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_extch dot11_extch_ie_t; + +BWL_PRE_PACKED_STRUCT struct dot11_brcm_extch { + uint8 id; + uint8 len; + uint8 oui[3]; + uint8 type; + uint8 extch; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_brcm_extch dot11_brcm_extch_ie_t; + +#define BRCM_EXTCH_IE_LEN 5 +#define BRCM_EXTCH_IE_TYPE 53 +#define DOT11_EXTCH_IE_LEN 1 +#define DOT11_EXT_CH_MASK 0x03 +#define DOT11_EXT_CH_UPPER 0x01 +#define DOT11_EXT_CH_LOWER 0x03 +#define DOT11_EXT_CH_NONE 0x00 + +BWL_PRE_PACKED_STRUCT struct dot11_action_frmhdr { + uint8 category; + uint8 action; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +#define DOT11_ACTION_FRMHDR_LEN 2 + + +BWL_PRE_PACKED_STRUCT struct dot11_channel_switch { + uint8 id; + uint8 len; + uint8 mode; + uint8 channel; + uint8 count; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_channel_switch dot11_chan_switch_ie_t; + +#define DOT11_SWITCH_IE_LEN 3 + +#define DOT11_CSA_MODE_ADVISORY 0 +#define DOT11_CSA_MODE_NO_TX 1 + +BWL_PRE_PACKED_STRUCT struct dot11_action_switch_channel { + uint8 category; + uint8 action; + dot11_chan_switch_ie_t chan_switch_ie; + dot11_brcm_extch_ie_t extch_ie; +} BWL_POST_PACKED_STRUCT; + +BWL_PRE_PACKED_STRUCT struct dot11_csa_body { + uint8 mode; + uint8 reg; + uint8 channel; + uint8 count; +} BWL_POST_PACKED_STRUCT; + + +BWL_PRE_PACKED_STRUCT struct dot11_ext_csa { + uint8 id; + uint8 len; + struct dot11_csa_body b; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_ext_csa dot11_ext_csa_ie_t; +#define DOT11_EXT_CSA_IE_LEN 4 + +BWL_PRE_PACKED_STRUCT struct dot11_action_ext_csa { + uint8 category; + uint8 action; + dot11_ext_csa_ie_t chan_switch_ie; +} BWL_POST_PACKED_STRUCT; + +BWL_PRE_PACKED_STRUCT struct dot11y_action_ext_csa { + uint8 category; + uint8 action; + struct dot11_csa_body b; +} BWL_POST_PACKED_STRUCT; + +BWL_PRE_PACKED_STRUCT struct dot11_obss_coex { + uint8 id; + uint8 len; + uint8 info; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_obss_coex dot11_obss_coex_t; +#define DOT11_OBSS_COEXINFO_LEN 1 + +#define DOT11_OBSS_COEX_INFO_REQ 0x01 +#define DOT11_OBSS_COEX_40MHZ_INTOLERANT 0x02 +#define DOT11_OBSS_COEX_20MHZ_WIDTH_REQ 0x04 + +BWL_PRE_PACKED_STRUCT struct dot11_obss_chanlist { + uint8 id; + uint8 len; + uint8 regclass; + uint8 chanlist[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_obss_chanlist dot11_obss_chanlist_t; +#define DOT11_OBSS_CHANLIST_FIXED_LEN 1 + +BWL_PRE_PACKED_STRUCT struct dot11_extcap_ie { + uint8 id; + uint8 len; + uint8 cap[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_extcap_ie dot11_extcap_ie_t; + +#define DOT11_EXTCAP_LEN_MAX 7 +#define DOT11_EXTCAP_LEN_COEX 1 +#define DOT11_EXTCAP_LEN_BT 3 +#define DOT11_EXTCAP_LEN_IW 4 +#define DOT11_EXTCAP_LEN_SI 6 + +#define DOT11_EXTCAP_LEN_TDLS 5 +BWL_PRE_PACKED_STRUCT struct dot11_extcap { + uint8 extcap[DOT11_EXTCAP_LEN_TDLS]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_extcap dot11_extcap_t; + + +#define TDLS_CAP_TDLS 37 +#define TDLS_CAP_PU_BUFFER_STA 28 +#define TDLS_CAP_PEER_PSM 20 +#define TDLS_CAP_CH_SW 30 +#define TDLS_CAP_PROH 38 +#define TDLS_CAP_CH_SW_PROH 39 + +#define TDLS_CAP_MAX_BIT 39 + + + +#define DOT11_MEASURE_TYPE_BASIC 0 +#define DOT11_MEASURE_TYPE_CCA 1 +#define DOT11_MEASURE_TYPE_RPI 2 +#define DOT11_MEASURE_TYPE_CHLOAD 3 +#define DOT11_MEASURE_TYPE_NOISE 4 +#define DOT11_MEASURE_TYPE_BEACON 5 +#define DOT11_MEASURE_TYPE_FRAME 6 +#define DOT11_MEASURE_TYPE_STATS 7 +#define DOT11_MEASURE_TYPE_LCI 8 +#define DOT11_MEASURE_TYPE_TXSTREAM 9 +#define DOT11_MEASURE_TYPE_PAUSE 255 + + +#define DOT11_MEASURE_MODE_PARALLEL (1<<0) +#define DOT11_MEASURE_MODE_ENABLE (1<<1) +#define DOT11_MEASURE_MODE_REQUEST (1<<2) +#define DOT11_MEASURE_MODE_REPORT (1<<3) +#define DOT11_MEASURE_MODE_DUR (1<<4) + +#define DOT11_MEASURE_MODE_LATE (1<<0) +#define DOT11_MEASURE_MODE_INCAPABLE (1<<1) +#define DOT11_MEASURE_MODE_REFUSED (1<<2) + +#define DOT11_MEASURE_BASIC_MAP_BSS ((uint8)(1<<0)) +#define DOT11_MEASURE_BASIC_MAP_OFDM ((uint8)(1<<1)) +#define DOT11_MEASURE_BASIC_MAP_UKNOWN ((uint8)(1<<2)) +#define DOT11_MEASURE_BASIC_MAP_RADAR ((uint8)(1<<3)) +#define DOT11_MEASURE_BASIC_MAP_UNMEAS ((uint8)(1<<4)) + +BWL_PRE_PACKED_STRUCT struct dot11_meas_req { + uint8 id; + uint8 len; + uint8 token; + uint8 mode; + uint8 type; + uint8 channel; + uint8 start_time[8]; + uint16 duration; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_meas_req dot11_meas_req_t; +#define DOT11_MNG_IE_MREQ_LEN 14 + +#define DOT11_MNG_IE_MREQ_FIXED_LEN 3 + +BWL_PRE_PACKED_STRUCT struct dot11_meas_rep { + uint8 id; + uint8 len; + uint8 token; + uint8 mode; + uint8 type; + BWL_PRE_PACKED_STRUCT union + { + BWL_PRE_PACKED_STRUCT struct { + uint8 channel; + uint8 start_time[8]; + uint16 duration; + uint8 map; + } BWL_POST_PACKED_STRUCT basic; + uint8 data[1]; + } BWL_POST_PACKED_STRUCT rep; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_meas_rep dot11_meas_rep_t; + + +#define DOT11_MNG_IE_MREP_FIXED_LEN 3 + +BWL_PRE_PACKED_STRUCT struct dot11_meas_rep_basic { + uint8 channel; + uint8 start_time[8]; + uint16 duration; + uint8 map; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_meas_rep_basic dot11_meas_rep_basic_t; +#define DOT11_MEASURE_BASIC_REP_LEN 12 + +BWL_PRE_PACKED_STRUCT struct dot11_quiet { + uint8 id; + uint8 len; + uint8 count; + uint8 period; + uint16 duration; + uint16 offset; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_quiet dot11_quiet_t; + +BWL_PRE_PACKED_STRUCT struct chan_map_tuple { + uint8 channel; + uint8 map; +} BWL_POST_PACKED_STRUCT; +typedef struct chan_map_tuple chan_map_tuple_t; + +BWL_PRE_PACKED_STRUCT struct dot11_ibss_dfs { + uint8 id; + uint8 len; + uint8 eaddr[ETHER_ADDR_LEN]; + uint8 interval; + chan_map_tuple_t map[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_ibss_dfs dot11_ibss_dfs_t; + + +#define WME_OUI "\x00\x50\xf2" +#define WME_OUI_LEN 3 +#define WME_OUI_TYPE 2 +#define WME_TYPE 2 +#define WME_SUBTYPE_IE 0 +#define WME_SUBTYPE_PARAM_IE 1 +#define WME_SUBTYPE_TSPEC 2 +#define WME_VER 1 + + +#define AC_BE 0 +#define AC_BK 1 +#define AC_VI 2 +#define AC_VO 3 +#define AC_COUNT 4 + +typedef uint8 ac_bitmap_t; + +#define AC_BITMAP_NONE 0x0 +#define AC_BITMAP_ALL 0xf +#define AC_BITMAP_TST(ab, ac) (((ab) & (1 << (ac))) != 0) +#define AC_BITMAP_SET(ab, ac) (((ab) |= (1 << (ac)))) +#define AC_BITMAP_RESET(ab, ac) (((ab) &= ~(1 << (ac)))) + + +BWL_PRE_PACKED_STRUCT struct wme_ie { + uint8 oui[3]; + uint8 type; + uint8 subtype; + uint8 version; + uint8 qosinfo; +} BWL_POST_PACKED_STRUCT; +typedef struct wme_ie wme_ie_t; +#define WME_IE_LEN 7 + +BWL_PRE_PACKED_STRUCT struct edcf_acparam { + uint8 ACI; + uint8 ECW; + uint16 TXOP; +} BWL_POST_PACKED_STRUCT; +typedef struct edcf_acparam edcf_acparam_t; + + +BWL_PRE_PACKED_STRUCT struct wme_param_ie { + uint8 oui[3]; + uint8 type; + uint8 subtype; + uint8 version; + uint8 qosinfo; + uint8 rsvd; + edcf_acparam_t acparam[AC_COUNT]; +} BWL_POST_PACKED_STRUCT; +typedef struct wme_param_ie wme_param_ie_t; +#define WME_PARAM_IE_LEN 24 + + +#define WME_QI_AP_APSD_MASK 0x80 +#define WME_QI_AP_APSD_SHIFT 7 +#define WME_QI_AP_COUNT_MASK 0x0f +#define WME_QI_AP_COUNT_SHIFT 0 + + +#define WME_QI_STA_MAXSPLEN_MASK 0x60 +#define WME_QI_STA_MAXSPLEN_SHIFT 5 +#define WME_QI_STA_APSD_ALL_MASK 0xf +#define WME_QI_STA_APSD_ALL_SHIFT 0 +#define WME_QI_STA_APSD_BE_MASK 0x8 +#define WME_QI_STA_APSD_BE_SHIFT 3 +#define WME_QI_STA_APSD_BK_MASK 0x4 +#define WME_QI_STA_APSD_BK_SHIFT 2 +#define WME_QI_STA_APSD_VI_MASK 0x2 +#define WME_QI_STA_APSD_VI_SHIFT 1 +#define WME_QI_STA_APSD_VO_MASK 0x1 +#define WME_QI_STA_APSD_VO_SHIFT 0 + + +#define EDCF_AIFSN_MIN 1 +#define EDCF_AIFSN_MAX 15 +#define EDCF_AIFSN_MASK 0x0f +#define EDCF_ACM_MASK 0x10 +#define EDCF_ACI_MASK 0x60 +#define EDCF_ACI_SHIFT 5 +#define EDCF_AIFSN_SHIFT 12 + + +#define EDCF_ECW_MIN 0 +#define EDCF_ECW_MAX 15 +#define EDCF_ECW2CW(exp) ((1 << (exp)) - 1) +#define EDCF_ECWMIN_MASK 0x0f +#define EDCF_ECWMAX_MASK 0xf0 +#define EDCF_ECWMAX_SHIFT 4 + + +#define EDCF_TXOP_MIN 0 +#define EDCF_TXOP_MAX 65535 +#define EDCF_TXOP2USEC(txop) ((txop) << 5) + + +#define NON_EDCF_AC_BE_ACI_STA 0x02 + + +#define EDCF_AC_BE_ACI_STA 0x03 +#define EDCF_AC_BE_ECW_STA 0xA4 +#define EDCF_AC_BE_TXOP_STA 0x0000 +#define EDCF_AC_BK_ACI_STA 0x27 +#define EDCF_AC_BK_ECW_STA 0xA4 +#define EDCF_AC_BK_TXOP_STA 0x0000 +#define EDCF_AC_VI_ACI_STA 0x42 +#define EDCF_AC_VI_ECW_STA 0x43 +#define EDCF_AC_VI_TXOP_STA 0x005e +#define EDCF_AC_VO_ACI_STA 0x62 +#define EDCF_AC_VO_ECW_STA 0x32 +#define EDCF_AC_VO_TXOP_STA 0x002f + + +#define EDCF_AC_BE_ACI_AP 0x03 +#define EDCF_AC_BE_ECW_AP 0x64 +#define EDCF_AC_BE_TXOP_AP 0x0000 +#define EDCF_AC_BK_ACI_AP 0x27 +#define EDCF_AC_BK_ECW_AP 0xA4 +#define EDCF_AC_BK_TXOP_AP 0x0000 +#define EDCF_AC_VI_ACI_AP 0x41 +#define EDCF_AC_VI_ECW_AP 0x43 +#define EDCF_AC_VI_TXOP_AP 0x005e +#define EDCF_AC_VO_ACI_AP 0x61 +#define EDCF_AC_VO_ECW_AP 0x32 +#define EDCF_AC_VO_TXOP_AP 0x002f + + +BWL_PRE_PACKED_STRUCT struct edca_param_ie { + uint8 qosinfo; + uint8 rsvd; + edcf_acparam_t acparam[AC_COUNT]; +} BWL_POST_PACKED_STRUCT; +typedef struct edca_param_ie edca_param_ie_t; +#define EDCA_PARAM_IE_LEN 18 + + +BWL_PRE_PACKED_STRUCT struct qos_cap_ie { + uint8 qosinfo; +} BWL_POST_PACKED_STRUCT; +typedef struct qos_cap_ie qos_cap_ie_t; + +BWL_PRE_PACKED_STRUCT struct dot11_qbss_load_ie { + uint8 id; + uint8 length; + uint16 station_count; + uint8 channel_utilization; + uint16 aac; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_qbss_load_ie dot11_qbss_load_ie_t; +#define BSS_LOAD_IE_SIZE 7 + + +#define FIXED_MSDU_SIZE 0x8000 +#define MSDU_SIZE_MASK 0x7fff + + + +#define INTEGER_SHIFT 13 +#define FRACTION_MASK 0x1FFF + + +BWL_PRE_PACKED_STRUCT struct dot11_management_notification { + uint8 category; + uint8 action; + uint8 token; + uint8 status; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +#define DOT11_MGMT_NOTIFICATION_LEN 4 + + +BWL_PRE_PACKED_STRUCT struct ti_ie { + uint8 ti_type; + uint32 ti_val; +} BWL_POST_PACKED_STRUCT; +typedef struct ti_ie ti_ie_t; +#define TI_TYPE_REASSOC_DEADLINE 1 +#define TI_TYPE_KEY_LIFETIME 2 + + +#define WME_ADDTS_REQUEST 0 +#define WME_ADDTS_RESPONSE 1 +#define WME_DELTS_REQUEST 2 + + +#define WME_ADMISSION_ACCEPTED 0 +#define WME_INVALID_PARAMETERS 1 +#define WME_ADMISSION_REFUSED 3 + + +#define BCN_PRB_SSID(body) ((char*)(body) + DOT11_BCN_PRB_LEN) + + +#define DOT11_OPEN_SYSTEM 0 +#define DOT11_SHARED_KEY 1 +#define DOT11_FAST_BSS 2 +#define DOT11_CHALLENGE_LEN 128 + + +#define FC_PVER_MASK 0x3 +#define FC_PVER_SHIFT 0 +#define FC_TYPE_MASK 0xC +#define FC_TYPE_SHIFT 2 +#define FC_SUBTYPE_MASK 0xF0 +#define FC_SUBTYPE_SHIFT 4 +#define FC_TODS 0x100 +#define FC_TODS_SHIFT 8 +#define FC_FROMDS 0x200 +#define FC_FROMDS_SHIFT 9 +#define FC_MOREFRAG 0x400 +#define FC_MOREFRAG_SHIFT 10 +#define FC_RETRY 0x800 +#define FC_RETRY_SHIFT 11 +#define FC_PM 0x1000 +#define FC_PM_SHIFT 12 +#define FC_MOREDATA 0x2000 +#define FC_MOREDATA_SHIFT 13 +#define FC_WEP 0x4000 +#define FC_WEP_SHIFT 14 +#define FC_ORDER 0x8000 +#define FC_ORDER_SHIFT 15 + + +#define SEQNUM_SHIFT 4 +#define SEQNUM_MAX 0x1000 +#define FRAGNUM_MASK 0xF + + + + +#define FC_TYPE_MNG 0 +#define FC_TYPE_CTL 1 +#define FC_TYPE_DATA 2 + + +#define FC_SUBTYPE_ASSOC_REQ 0 +#define FC_SUBTYPE_ASSOC_RESP 1 +#define FC_SUBTYPE_REASSOC_REQ 2 +#define FC_SUBTYPE_REASSOC_RESP 3 +#define FC_SUBTYPE_PROBE_REQ 4 +#define FC_SUBTYPE_PROBE_RESP 5 +#define FC_SUBTYPE_BEACON 8 +#define FC_SUBTYPE_ATIM 9 +#define FC_SUBTYPE_DISASSOC 10 +#define FC_SUBTYPE_AUTH 11 +#define FC_SUBTYPE_DEAUTH 12 +#define FC_SUBTYPE_ACTION 13 +#define FC_SUBTYPE_ACTION_NOACK 14 + + +#define FC_SUBTYPE_CTL_WRAPPER 7 +#define FC_SUBTYPE_BLOCKACK_REQ 8 +#define FC_SUBTYPE_BLOCKACK 9 +#define FC_SUBTYPE_PS_POLL 10 +#define FC_SUBTYPE_RTS 11 +#define FC_SUBTYPE_CTS 12 +#define FC_SUBTYPE_ACK 13 +#define FC_SUBTYPE_CF_END 14 +#define FC_SUBTYPE_CF_END_ACK 15 + + +#define FC_SUBTYPE_DATA 0 +#define FC_SUBTYPE_DATA_CF_ACK 1 +#define FC_SUBTYPE_DATA_CF_POLL 2 +#define FC_SUBTYPE_DATA_CF_ACK_POLL 3 +#define FC_SUBTYPE_NULL 4 +#define FC_SUBTYPE_CF_ACK 5 +#define FC_SUBTYPE_CF_POLL 6 +#define FC_SUBTYPE_CF_ACK_POLL 7 +#define FC_SUBTYPE_QOS_DATA 8 +#define FC_SUBTYPE_QOS_DATA_CF_ACK 9 +#define FC_SUBTYPE_QOS_DATA_CF_POLL 10 +#define FC_SUBTYPE_QOS_DATA_CF_ACK_POLL 11 +#define FC_SUBTYPE_QOS_NULL 12 +#define FC_SUBTYPE_QOS_CF_POLL 14 +#define FC_SUBTYPE_QOS_CF_ACK_POLL 15 + + +#define FC_SUBTYPE_ANY_QOS(s) (((s) & 8) != 0) +#define FC_SUBTYPE_ANY_NULL(s) (((s) & 4) != 0) +#define FC_SUBTYPE_ANY_CF_POLL(s) (((s) & 2) != 0) +#define FC_SUBTYPE_ANY_CF_ACK(s) (((s) & 1) != 0) + + +#define FC_KIND_MASK (FC_TYPE_MASK | FC_SUBTYPE_MASK) + +#define FC_KIND(t, s) (((t) << FC_TYPE_SHIFT) | ((s) << FC_SUBTYPE_SHIFT)) + +#define FC_SUBTYPE(fc) (((fc) & FC_SUBTYPE_MASK) >> FC_SUBTYPE_SHIFT) +#define FC_TYPE(fc) (((fc) & FC_TYPE_MASK) >> FC_TYPE_SHIFT) + +#define FC_ASSOC_REQ FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_ASSOC_REQ) +#define FC_ASSOC_RESP FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_ASSOC_RESP) +#define FC_REASSOC_REQ FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_REASSOC_REQ) +#define FC_REASSOC_RESP FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_REASSOC_RESP) +#define FC_PROBE_REQ FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_PROBE_REQ) +#define FC_PROBE_RESP FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_PROBE_RESP) +#define FC_BEACON FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_BEACON) +#define FC_DISASSOC FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_DISASSOC) +#define FC_AUTH FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_AUTH) +#define FC_DEAUTH FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_DEAUTH) +#define FC_ACTION FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_ACTION) +#define FC_ACTION_NOACK FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_ACTION_NOACK) + +#define FC_CTL_WRAPPER FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_CTL_WRAPPER) +#define FC_BLOCKACK_REQ FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_BLOCKACK_REQ) +#define FC_BLOCKACK FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_BLOCKACK) +#define FC_PS_POLL FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_PS_POLL) +#define FC_RTS FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_RTS) +#define FC_CTS FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_CTS) +#define FC_ACK FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_ACK) +#define FC_CF_END FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_CF_END) +#define FC_CF_END_ACK FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_CF_END_ACK) + +#define FC_DATA FC_KIND(FC_TYPE_DATA, FC_SUBTYPE_DATA) +#define FC_NULL_DATA FC_KIND(FC_TYPE_DATA, FC_SUBTYPE_NULL) +#define FC_DATA_CF_ACK FC_KIND(FC_TYPE_DATA, FC_SUBTYPE_DATA_CF_ACK) +#define FC_QOS_DATA FC_KIND(FC_TYPE_DATA, FC_SUBTYPE_QOS_DATA) +#define FC_QOS_NULL FC_KIND(FC_TYPE_DATA, FC_SUBTYPE_QOS_NULL) + + + + +#define QOS_PRIO_SHIFT 0 +#define QOS_PRIO_MASK 0x0007 +#define QOS_PRIO(qos) (((qos) & QOS_PRIO_MASK) >> QOS_PRIO_SHIFT) + + +#define QOS_TID_SHIFT 0 +#define QOS_TID_MASK 0x000f +#define QOS_TID(qos) (((qos) & QOS_TID_MASK) >> QOS_TID_SHIFT) + + +#define QOS_EOSP_SHIFT 4 +#define QOS_EOSP_MASK 0x0010 +#define QOS_EOSP(qos) (((qos) & QOS_EOSP_MASK) >> QOS_EOSP_SHIFT) + + +#define QOS_ACK_NORMAL_ACK 0 +#define QOS_ACK_NO_ACK 1 +#define QOS_ACK_NO_EXP_ACK 2 +#define QOS_ACK_BLOCK_ACK 3 +#define QOS_ACK_SHIFT 5 +#define QOS_ACK_MASK 0x0060 +#define QOS_ACK(qos) (((qos) & QOS_ACK_MASK) >> QOS_ACK_SHIFT) + + +#define QOS_AMSDU_SHIFT 7 +#define QOS_AMSDU_MASK 0x0080 + + + + + + +#define DOT11_MNG_AUTH_ALGO_LEN 2 +#define DOT11_MNG_AUTH_SEQ_LEN 2 +#define DOT11_MNG_BEACON_INT_LEN 2 +#define DOT11_MNG_CAP_LEN 2 +#define DOT11_MNG_AP_ADDR_LEN 6 +#define DOT11_MNG_LISTEN_INT_LEN 2 +#define DOT11_MNG_REASON_LEN 2 +#define DOT11_MNG_AID_LEN 2 +#define DOT11_MNG_STATUS_LEN 2 +#define DOT11_MNG_TIMESTAMP_LEN 8 + + +#define DOT11_AID_MASK 0x3fff + + +#define DOT11_RC_RESERVED 0 +#define DOT11_RC_UNSPECIFIED 1 +#define DOT11_RC_AUTH_INVAL 2 +#define DOT11_RC_DEAUTH_LEAVING 3 +#define DOT11_RC_INACTIVITY 4 +#define DOT11_RC_BUSY 5 +#define DOT11_RC_INVAL_CLASS_2 6 +#define DOT11_RC_INVAL_CLASS_3 7 +#define DOT11_RC_DISASSOC_LEAVING 8 +#define DOT11_RC_NOT_AUTH 9 +#define DOT11_RC_BAD_PC 10 +#define DOT11_RC_BAD_CHANNELS 11 + + + +#define DOT11_RC_UNSPECIFIED_QOS 32 +#define DOT11_RC_INSUFFCIENT_BW 33 +#define DOT11_RC_EXCESSIVE_FRAMES 34 +#define DOT11_RC_TX_OUTSIDE_TXOP 35 +#define DOT11_RC_LEAVING_QBSS 36 +#define DOT11_RC_BAD_MECHANISM 37 +#define DOT11_RC_SETUP_NEEDED 38 +#define DOT11_RC_TIMEOUT 39 + +#define DOT11_RC_MAX 23 + +#define DOT11_RC_TDLS_PEER_UNREACH 25 +#define DOT11_RC_TDLS_DOWN_UNSPECIFIED 26 + + +#define DOT11_SC_SUCCESS 0 +#define DOT11_SC_FAILURE 1 +#define DOT11_SC_TDLS_WAKEUP_SCH_ALT 2 + +#define DOT11_SC_TDLS_WAKEUP_SCH_REJ 3 +#define DOT11_SC_TDLS_SEC_DISABLED 5 +#define DOT11_SC_LIFETIME_REJ 6 +#define DOT11_SC_NOT_SAME_BSS 7 +#define DOT11_SC_CAP_MISMATCH 10 +#define DOT11_SC_REASSOC_FAIL 11 +#define DOT11_SC_ASSOC_FAIL 12 +#define DOT11_SC_AUTH_MISMATCH 13 +#define DOT11_SC_AUTH_SEQ 14 +#define DOT11_SC_AUTH_CHALLENGE_FAIL 15 +#define DOT11_SC_AUTH_TIMEOUT 16 +#define DOT11_SC_ASSOC_BUSY_FAIL 17 +#define DOT11_SC_ASSOC_RATE_MISMATCH 18 +#define DOT11_SC_ASSOC_SHORT_REQUIRED 19 +#define DOT11_SC_ASSOC_PBCC_REQUIRED 20 +#define DOT11_SC_ASSOC_AGILITY_REQUIRED 21 +#define DOT11_SC_ASSOC_SPECTRUM_REQUIRED 22 +#define DOT11_SC_ASSOC_BAD_POWER_CAP 23 +#define DOT11_SC_ASSOC_BAD_SUP_CHANNELS 24 +#define DOT11_SC_ASSOC_SHORTSLOT_REQUIRED 25 +#define DOT11_SC_ASSOC_ERPBCC_REQUIRED 26 +#define DOT11_SC_ASSOC_DSSOFDM_REQUIRED 27 +#define DOT11_SC_ASSOC_R0KH_UNREACHABLE 28 +#define DOT11_SC_ASSOC_TRY_LATER 30 +#define DOT11_SC_ASSOC_MFP_VIOLATION 31 + +#define DOT11_SC_DECLINED 37 +#define DOT11_SC_INVALID_PARAMS 38 +#define DOT11_SC_INVALID_PAIRWISE_CIPHER 42 +#define DOT11_SC_INVALID_AKMP 43 +#define DOT11_SC_INVALID_RSNIE_CAP 45 +#define DOT11_SC_DLS_NOT_ALLOWED 48 +#define DOT11_SC_INVALID_PMKID 53 +#define DOT11_SC_INVALID_MDID 54 +#define DOT11_SC_INVALID_FTIE 55 + +#define DOT11_SC_UNEXP_MSG 70 +#define DOT11_SC_INVALID_SNONCE 71 +#define DOT11_SC_INVALID_RSNIE 72 + + +#define DOT11_MNG_DS_PARAM_LEN 1 +#define DOT11_MNG_IBSS_PARAM_LEN 2 + + +#define DOT11_MNG_TIM_FIXED_LEN 3 +#define DOT11_MNG_TIM_DTIM_COUNT 0 +#define DOT11_MNG_TIM_DTIM_PERIOD 1 +#define DOT11_MNG_TIM_BITMAP_CTL 2 +#define DOT11_MNG_TIM_PVB 3 + + +#define TLV_TAG_OFF 0 +#define TLV_LEN_OFF 1 +#define TLV_HDR_LEN 2 +#define TLV_BODY_OFF 2 + + +#define DOT11_MNG_SSID_ID 0 +#define DOT11_MNG_RATES_ID 1 +#define DOT11_MNG_FH_PARMS_ID 2 +#define DOT11_MNG_DS_PARMS_ID 3 +#define DOT11_MNG_CF_PARMS_ID 4 +#define DOT11_MNG_TIM_ID 5 +#define DOT11_MNG_IBSS_PARMS_ID 6 +#define DOT11_MNG_COUNTRY_ID 7 +#define DOT11_MNG_HOPPING_PARMS_ID 8 +#define DOT11_MNG_HOPPING_TABLE_ID 9 +#define DOT11_MNG_REQUEST_ID 10 +#define DOT11_MNG_QBSS_LOAD_ID 11 +#define DOT11_MNG_EDCA_PARAM_ID 12 +#define DOT11_MNG_CHALLENGE_ID 16 +#define DOT11_MNG_PWR_CONSTRAINT_ID 32 +#define DOT11_MNG_PWR_CAP_ID 33 +#define DOT11_MNG_TPC_REQUEST_ID 34 +#define DOT11_MNG_TPC_REPORT_ID 35 +#define DOT11_MNG_SUPP_CHANNELS_ID 36 +#define DOT11_MNG_CHANNEL_SWITCH_ID 37 +#define DOT11_MNG_MEASURE_REQUEST_ID 38 +#define DOT11_MNG_MEASURE_REPORT_ID 39 +#define DOT11_MNG_QUIET_ID 40 +#define DOT11_MNG_IBSS_DFS_ID 41 +#define DOT11_MNG_ERP_ID 42 +#define DOT11_MNG_TS_DELAY_ID 43 +#define DOT11_MNG_HT_CAP 45 +#define DOT11_MNG_QOS_CAP_ID 46 +#define DOT11_MNG_NONERP_ID 47 +#define DOT11_MNG_RSN_ID 48 +#define DOT11_MNG_EXT_RATES_ID 50 +#define DOT11_MNG_AP_CHREP_ID 51 +#define DOT11_MNG_NBR_REP_ID 52 +#define DOT11_MNG_MDIE_ID 54 +#define DOT11_MNG_FTIE_ID 55 +#define DOT11_MNG_FT_TI_ID 56 +#define DOT11_MNG_REGCLASS_ID 59 +#define DOT11_MNG_EXT_CSA_ID 60 +#define DOT11_MNG_HT_ADD 61 +#define DOT11_MNG_EXT_CHANNEL_OFFSET 62 +#define DOT11_MNG_WAPI_ID 68 +#define DOT11_MNG_TIME_ADVERTISE_ID 69 +#define DOT11_MNG_RRM_CAP_ID 70 +#define DOT11_MNG_HT_BSS_COEXINFO_ID 72 +#define DOT11_MNG_HT_BSS_CHANNEL_REPORT_ID 73 +#define DOT11_MNG_HT_OBSS_ID 74 +#define DOT11_MNG_CHANNEL_USAGE 97 +#define DOT11_MNG_TIME_ZONE_ID 98 +#define DOT11_MNG_LINK_IDENTIFIER_ID 101 +#define DOT11_MNG_WAKEUP_SCHEDULE_ID 102 +#define DOT11_MNG_CHANNEL_SWITCH_TIMING_ID 104 +#define DOT11_MNG_PTI_CONTROL_ID 105 +#define DOT11_MNG_PU_BUFFER_STATUS_ID 106 +#define DOT11_MNG_INTERWORKING_ID 107 +#define DOT11_MNG_ADVERTISEMENT_ID 108 +#define DOT11_MNG_EXP_BW_REQ_ID 109 +#define DOT11_MNG_QOS_MAP_ID 110 +#define DOT11_MNG_ROAM_CONSORT_ID 111 +#define DOT11_MNG_EMERGCY_ALERT_ID 112 +#define DOT11_MNG_EXT_CAP_ID 127 +#define DOT11_MNG_VHT_CAP_ID 191 +#define DOT11_MNG_VHT_OPERATION_ID 192 + +#define DOT11_MNG_WPA_ID 221 +#define DOT11_MNG_PROPR_ID 221 + +#define DOT11_MNG_VS_ID 221 + + +#define DOT11_RATE_BASIC 0x80 +#define DOT11_RATE_MASK 0x7F + + +#define DOT11_MNG_ERP_LEN 1 +#define DOT11_MNG_NONERP_PRESENT 0x01 +#define DOT11_MNG_USE_PROTECTION 0x02 +#define DOT11_MNG_BARKER_PREAMBLE 0x04 + +#define DOT11_MGN_TS_DELAY_LEN 4 +#define TS_DELAY_FIELD_SIZE 4 + + +#define DOT11_CAP_ESS 0x0001 +#define DOT11_CAP_IBSS 0x0002 +#define DOT11_CAP_POLLABLE 0x0004 +#define DOT11_CAP_POLL_RQ 0x0008 +#define DOT11_CAP_PRIVACY 0x0010 +#define DOT11_CAP_SHORT 0x0020 +#define DOT11_CAP_PBCC 0x0040 +#define DOT11_CAP_AGILITY 0x0080 +#define DOT11_CAP_SPECTRUM 0x0100 +#define DOT11_CAP_SHORTSLOT 0x0400 +#define DOT11_CAP_RRM 0x1000 +#define DOT11_CAP_CCK_OFDM 0x2000 + + + +#define DOT11_EXT_CAP_OBSS_COEX_MGMT 0 + +#define DOT11_EXT_CAP_SPSMP 6 + +#define DOT11_EXT_CAP_BSS_TRANSITION_MGMT 19 + +#define DOT11_EXT_CAP_IW 31 + +#define DOT11_EXT_CAP_SI 41 +#define DOT11_EXT_CAP_SI_MASK 0x0E + + +#define DOT11_ACTION_HDR_LEN 2 +#define DOT11_ACTION_CAT_OFF 0 +#define DOT11_ACTION_ACT_OFF 1 + + +#define DOT11_ACTION_CAT_ERR_MASK 0x80 +#define DOT11_ACTION_CAT_MASK 0x7F +#define DOT11_ACTION_CAT_SPECT_MNG 0 +#define DOT11_ACTION_CAT_QOS 1 +#define DOT11_ACTION_CAT_DLS 2 +#define DOT11_ACTION_CAT_BLOCKACK 3 +#define DOT11_ACTION_CAT_PUBLIC 4 +#define DOT11_ACTION_CAT_RRM 5 +#define DOT11_ACTION_CAT_FBT 6 +#define DOT11_ACTION_CAT_HT 7 +#define DOT11_ACTION_CAT_SA_QUERY 8 +#define DOT11_ACTION_CAT_PDPA 9 +#define DOT11_ACTION_CAT_BSSMGMT 10 +#define DOT11_ACTION_NOTIFICATION 17 +#define DOT11_ACTION_CAT_VSP 126 +#define DOT11_ACTION_CAT_VS 127 + + +#define DOT11_SM_ACTION_M_REQ 0 +#define DOT11_SM_ACTION_M_REP 1 +#define DOT11_SM_ACTION_TPC_REQ 2 +#define DOT11_SM_ACTION_TPC_REP 3 +#define DOT11_SM_ACTION_CHANNEL_SWITCH 4 +#define DOT11_SM_ACTION_EXT_CSA 5 + + +#define DOT11_ACTION_ID_HT_CH_WIDTH 0 +#define DOT11_ACTION_ID_HT_MIMO_PS 1 + + +#define DOT11_PUB_ACTION_BSS_COEX_MNG 0 +#define DOT11_PUB_ACTION_CHANNEL_SWITCH 4 + + +#define DOT11_BA_ACTION_ADDBA_REQ 0 +#define DOT11_BA_ACTION_ADDBA_RESP 1 +#define DOT11_BA_ACTION_DELBA 2 + + +#define DOT11_ADDBA_PARAM_AMSDU_SUP 0x0001 +#define DOT11_ADDBA_PARAM_POLICY_MASK 0x0002 +#define DOT11_ADDBA_PARAM_POLICY_SHIFT 1 +#define DOT11_ADDBA_PARAM_TID_MASK 0x003c +#define DOT11_ADDBA_PARAM_TID_SHIFT 2 +#define DOT11_ADDBA_PARAM_BSIZE_MASK 0xffc0 +#define DOT11_ADDBA_PARAM_BSIZE_SHIFT 6 + +#define DOT11_ADDBA_POLICY_DELAYED 0 +#define DOT11_ADDBA_POLICY_IMMEDIATE 1 + + +#define DOT11_FT_ACTION_FT_RESERVED 0 +#define DOT11_FT_ACTION_FT_REQ 1 +#define DOT11_FT_ACTION_FT_RES 2 +#define DOT11_FT_ACTION_FT_CON 3 +#define DOT11_FT_ACTION_FT_ACK 4 + + +#define DOT11_DLS_ACTION_REQ 0 +#define DOT11_DLS_ACTION_RESP 1 +#define DOT11_DLS_ACTION_TD 2 + + +#define DOT11_WNM_ACTION_EVENT_REQ 0 +#define DOT11_WNM_ACTION_EVENT_REP 1 +#define DOT11_WNM_ACTION_DIAG_REQ 2 +#define DOT11_WNM_ACTION_DIAG_REP 3 +#define DOT11_WNM_ACTION_LOC_CFG_REQ 4 +#define DOT11_WNM_ACTION_LOC_RFG_RESP 5 +#define DOT11_WNM_ACTION_BSS_TRANS_QURY 6 +#define DOT11_WNM_ACTION_BSS_TRANS_REQ 7 +#define DOT11_WNM_ACTION_BSS_TRANS_RESP 8 +#define DOT11_WNM_ACTION_FMS_REQ 9 +#define DOT11_WNM_ACTION_FMS_RESP 10 +#define DOT11_WNM_ACTION_COL_INTRFRNCE_REQ 11 +#define DOT11_WNM_ACTION_COL_INTRFRNCE_REP 12 +#define DOT11_WNM_ACTION_TFS_REQ 13 +#define DOT11_WNM_ACTION_TFS_RESP 14 +#define DOT11_WNM_ACTION_TFS_NOTIFY 15 +#define DOT11_WNM_ACTION_WNM_SLEEP_REQ 16 +#define DOT11_WNM_ACTION_WNM_SLEEP_RESP 17 +#define DOT11_WNM_ACTION_TIM_BCAST_REQ 18 +#define DOT11_WNM_ACTION_TIM_BCAST_RESP 19 +#define DOT11_WNM_ACTION_QOS_TRFC_CAP_UPD 20 +#define DOT11_WNM_ACTION_CHAN_USAGE_REQ 21 +#define DOT11_WNM_ACTION_CHAN_USAGE_RESP 22 +#define DOT11_WNM_ACTION_DMS_REQ 23 +#define DOT11_WNM_ACTION_DMS_RESP 24 +#define DOT11_WNM_ACTION_TMNG_MEASUR_REQ 25 +#define DOT11_WNM_ACTION_NOTFCTN_REQ 26 +#define DOT11_WNM_ACTION_NOTFCTN_RES 27 + +#define DOT11_MNG_COUNTRY_ID_LEN 3 + + +BWL_PRE_PACKED_STRUCT struct dot11_dls_req { + uint8 category; + uint8 action; + struct ether_addr da; + struct ether_addr sa; + uint16 cap; + uint16 timeout; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_dls_req dot11_dls_req_t; +#define DOT11_DLS_REQ_LEN 18 + + +BWL_PRE_PACKED_STRUCT struct dot11_dls_resp { + uint8 category; + uint8 action; + uint16 status; + struct ether_addr da; + struct ether_addr sa; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_dls_resp dot11_dls_resp_t; +#define DOT11_DLS_RESP_LEN 16 + + + +BWL_PRE_PACKED_STRUCT struct dot11_bss_trans_query { + uint8 category; + uint8 action; + uint8 token; + uint8 reason; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_bss_trans_query dot11_bss_trans_query_t; +#define DOT11_BSS_TRANS_QUERY_LEN 4 + + +BWL_PRE_PACKED_STRUCT struct dot11_bss_trans_req { + uint8 category; + uint8 action; + uint8 token; + uint8 reqmode; + uint16 disassoc_tmr; + uint8 validity_intrvl; + uint8 data[1]; + +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_bss_trans_req dot11_bss_trans_req_t; +#define DOT11_BSS_TRANS_REQ_LEN 7 + +#define DOT11_BSS_TERM_DUR_LEN 12 + + + +#define DOT11_BSS_TRNS_REQMODE_PREF_LIST_INCL 0x01 +#define DOT11_BSS_TRNS_REQMODE_ABRIDGED 0x02 +#define DOT11_BSS_TRNS_REQMODE_DISASSOC_IMMINENT 0x04 +#define DOT11_BSS_TRNS_REQMODE_BSS_TERM_INCL 0x08 +#define DOT11_BSS_TRNS_REQMODE_ESS_DISASSOC_IMNT 0x10 + + + +BWL_PRE_PACKED_STRUCT struct dot11_bss_trans_res { + uint8 category; + uint8 action; + uint8 token; + uint8 status; + uint8 term_delay; + uint8 data[1]; + +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_bss_trans_res dot11_bss_trans_res_t; +#define DOT11_BSS_TRANS_RES_LEN 5 + + +#define DOT11_BSS_TRNS_RES_STATUS_ACCEPT 0 +#define DOT11_BSS_TRNS_RES_STATUS_REJECT 1 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_INSUFF_BCN 2 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_INSUFF_CAP 3 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_TERM_UNDESIRED 4 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_TERM_DELAY_REQ 5 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_BSS_LIST_PROVIDED 6 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_NO_SUITABLE_BSS 7 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_LEAVING_ESS 8 + + + +#define DOT11_NBR_RPRT_BSSID_INFO_REACHABILTY 0x0003 +#define DOT11_NBR_RPRT_BSSID_INFO_SEC 0x0004 +#define DOT11_NBR_RPRT_BSSID_INFO_KEY_SCOPE 0x0008 +#define DOT11_NBR_RPRT_BSSID_INFO_CAP 0x03f0 + +#define DOT11_NBR_RPRT_BSSID_INFO_CAP_SPEC_MGMT 0x0010 +#define DOT11_NBR_RPRT_BSSID_INFO_CAP_QOS 0x0020 +#define DOT11_NBR_RPRT_BSSID_INFO_CAP_APSD 0x0040 +#define DOT11_NBR_RPRT_BSSID_INFO_CAP_RDIO_MSMT 0x0080 +#define DOT11_NBR_RPRT_BSSID_INFO_CAP_DEL_BA 0x0100 +#define DOT11_NBR_RPRT_BSSID_INFO_CAP_IMM_BA 0x0200 + + +#define DOT11_NBR_RPRT_SUBELEM_BSS_CANDDT_PREF_ID 3 + + +BWL_PRE_PACKED_STRUCT struct dot11_addba_req { + uint8 category; + uint8 action; + uint8 token; + uint16 addba_param_set; + uint16 timeout; + uint16 start_seqnum; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_addba_req dot11_addba_req_t; +#define DOT11_ADDBA_REQ_LEN 9 + +BWL_PRE_PACKED_STRUCT struct dot11_addba_resp { + uint8 category; + uint8 action; + uint8 token; + uint16 status; + uint16 addba_param_set; + uint16 timeout; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_addba_resp dot11_addba_resp_t; +#define DOT11_ADDBA_RESP_LEN 9 + + +#define DOT11_DELBA_PARAM_INIT_MASK 0x0800 +#define DOT11_DELBA_PARAM_INIT_SHIFT 11 +#define DOT11_DELBA_PARAM_TID_MASK 0xf000 +#define DOT11_DELBA_PARAM_TID_SHIFT 12 + +BWL_PRE_PACKED_STRUCT struct dot11_delba { + uint8 category; + uint8 action; + uint16 delba_param_set; + uint16 reason; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_delba dot11_delba_t; +#define DOT11_DELBA_LEN 6 + + +#define SA_QUERY_REQUEST 0 +#define SA_QUERY_RESPONSE 1 + + + + +BWL_PRE_PACKED_STRUCT struct dot11_ft_req { + uint8 category; + uint8 action; + uint8 sta_addr[ETHER_ADDR_LEN]; + uint8 tgt_ap_addr[ETHER_ADDR_LEN]; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_ft_req dot11_ft_req_t; +#define DOT11_FT_REQ_FIXED_LEN 14 + + +BWL_PRE_PACKED_STRUCT struct dot11_ft_res { + uint8 category; + uint8 action; + uint8 sta_addr[ETHER_ADDR_LEN]; + uint8 tgt_ap_addr[ETHER_ADDR_LEN]; + uint16 status; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_ft_res dot11_ft_res_t; +#define DOT11_FT_RES_FIXED_LEN 16 + + + + + + +#define DOT11_RRM_CAP_LEN 5 +BWL_PRE_PACKED_STRUCT struct dot11_rrm_cap_ie { + uint8 cap[DOT11_RRM_CAP_LEN]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_rrm_cap_ie dot11_rrm_cap_ie_t; + + +#define DOT11_RRM_CAP_LINK 0 +#define DOT11_RRM_CAP_NEIGHBOR_REPORT 1 +#define DOT11_RRM_CAP_PARALLEL 2 +#define DOT11_RRM_CAP_REPEATED 3 +#define DOT11_RRM_CAP_BCN_PASSIVE 4 +#define DOT11_RRM_CAP_BCN_ACTIVE 5 +#define DOT11_RRM_CAP_BCN_TABLE 6 +#define DOT11_RRM_CAP_BCN_REP_COND 7 +#define DOT11_RRM_CAP_AP_CHANREP 16 + + + +#define DOT11_OP_CLASS_NONE 255 + + + +#define DOT11_RM_ACTION_RM_REQ 0 +#define DOT11_RM_ACTION_RM_REP 1 +#define DOT11_RM_ACTION_LM_REQ 2 +#define DOT11_RM_ACTION_LM_REP 3 +#define DOT11_RM_ACTION_NR_REQ 4 +#define DOT11_RM_ACTION_NR_REP 5 + + +BWL_PRE_PACKED_STRUCT struct dot11_rm_action { + uint8 category; + uint8 action; + uint8 token; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_rm_action dot11_rm_action_t; +#define DOT11_RM_ACTION_LEN 3 + +BWL_PRE_PACKED_STRUCT struct dot11_rmreq { + uint8 category; + uint8 action; + uint8 token; + uint16 reps; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_rmreq dot11_rmreq_t; +#define DOT11_RMREQ_LEN 5 + +BWL_PRE_PACKED_STRUCT struct dot11_rm_ie { + uint8 id; + uint8 len; + uint8 token; + uint8 mode; + uint8 type; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_rm_ie dot11_rm_ie_t; +#define DOT11_RM_IE_LEN 5 + + +#define DOT11_RMREQ_MODE_PARALLEL 1 +#define DOT11_RMREQ_MODE_ENABLE 2 +#define DOT11_RMREQ_MODE_REQUEST 4 +#define DOT11_RMREQ_MODE_REPORT 8 +#define DOT11_RMREQ_MODE_DURMAND 0x10 + + +#define DOT11_RMREP_MODE_LATE 1 +#define DOT11_RMREP_MODE_INCAPABLE 2 +#define DOT11_RMREP_MODE_REFUSED 4 + +BWL_PRE_PACKED_STRUCT struct dot11_rmreq_bcn { + uint8 id; + uint8 len; + uint8 token; + uint8 mode; + uint8 type; + uint8 reg; + uint8 channel; + uint16 interval; + uint16 duration; + uint8 bcn_mode; + struct ether_addr bssid; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_rmreq_bcn dot11_rmreq_bcn_t; +#define DOT11_RMREQ_BCN_LEN 18 + +BWL_PRE_PACKED_STRUCT struct dot11_rmrep_bcn { + uint8 reg; + uint8 channel; + uint32 starttime[2]; + uint16 duration; + uint8 frame_info; + uint8 rcpi; + uint8 rsni; + struct ether_addr bssid; + uint8 antenna_id; + uint32 parent_tsf; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_rmrep_bcn dot11_rmrep_bcn_t; +#define DOT11_RMREP_BCN_LEN 26 + + +#define DOT11_RMREQ_BCN_PASSIVE 0 +#define DOT11_RMREQ_BCN_ACTIVE 1 +#define DOT11_RMREQ_BCN_TABLE 2 + + +#define DOT11_RMREQ_BCN_SSID_ID 0 +#define DOT11_RMREQ_BCN_REPINFO_ID 1 +#define DOT11_RMREQ_BCN_REPDET_ID 2 +#define DOT11_RMREQ_BCN_REQUEST_ID 10 +#define DOT11_RMREQ_BCN_APCHREP_ID 51 + + +#define DOT11_RMREQ_BCN_REPDET_FIXED 0 +#define DOT11_RMREQ_BCN_REPDET_REQUEST 1 +#define DOT11_RMREQ_BCN_REPDET_ALL 2 + + +#define DOT11_RMREP_BCN_FRM_BODY 1 + + +BWL_PRE_PACKED_STRUCT struct dot11_rmrep_nbr { + struct ether_addr bssid; + uint32 bssid_info; + uint8 reg; + uint8 channel; + uint8 phytype; + uchar sub_elements[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_rmrep_nbr dot11_rmrep_nbr_t; +#define DOT11_RMREP_NBR_LEN 13 + + +#define DOT11_BSSTYPE_INFRASTRUCTURE 0 +#define DOT11_BSSTYPE_INDEPENDENT 1 +#define DOT11_BSSTYPE_ANY 2 +#define DOT11_SCANTYPE_ACTIVE 0 +#define DOT11_SCANTYPE_PASSIVE 1 + + +BWL_PRE_PACKED_STRUCT struct dot11_lmreq { + uint8 category; + uint8 action; + uint8 token; + uint8 txpwr; + uint8 maxtxpwr; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_lmreq dot11_lmreq_t; +#define DOT11_LMREQ_LEN 5 + +BWL_PRE_PACKED_STRUCT struct dot11_lmrep { + uint8 category; + uint8 action; + uint8 token; + dot11_tpc_rep_t tpc; + uint8 rxant; + uint8 txant; + uint8 rcpi; + uint8 rsni; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_lmrep dot11_lmrep_t; +#define DOT11_LMREP_LEN 11 + + +#define PREN_PREAMBLE 24 +#define PREN_MM_EXT 12 +#define PREN_PREAMBLE_EXT 4 + + +#define RIFS_11N_TIME 2 + + + +#define HT_SIG1_MCS_MASK 0x00007F +#define HT_SIG1_CBW 0x000080 +#define HT_SIG1_HT_LENGTH 0xFFFF00 + + +#define HT_SIG2_SMOOTHING 0x000001 +#define HT_SIG2_NOT_SOUNDING 0x000002 +#define HT_SIG2_RESERVED 0x000004 +#define HT_SIG2_AGGREGATION 0x000008 +#define HT_SIG2_STBC_MASK 0x000030 +#define HT_SIG2_STBC_SHIFT 4 +#define HT_SIG2_FEC_CODING 0x000040 +#define HT_SIG2_SHORT_GI 0x000080 +#define HT_SIG2_ESS_MASK 0x000300 +#define HT_SIG2_ESS_SHIFT 8 +#define HT_SIG2_CRC 0x03FC00 +#define HT_SIG2_TAIL 0x1C0000 + + +#define APHY_SLOT_TIME 9 +#define APHY_SIFS_TIME 16 +#define APHY_DIFS_TIME (APHY_SIFS_TIME + (2 * APHY_SLOT_TIME)) +#define APHY_PREAMBLE_TIME 16 +#define APHY_SIGNAL_TIME 4 +#define APHY_SYMBOL_TIME 4 +#define APHY_SERVICE_NBITS 16 +#define APHY_TAIL_NBITS 6 +#define APHY_CWMIN 15 + + +#define BPHY_SLOT_TIME 20 +#define BPHY_SIFS_TIME 10 +#define BPHY_DIFS_TIME 50 +#define BPHY_PLCP_TIME 192 +#define BPHY_PLCP_SHORT_TIME 96 +#define BPHY_CWMIN 31 + + +#define DOT11_OFDM_SIGNAL_EXTENSION 6 + +#define PHY_CWMAX 1023 + +#define DOT11_MAXNUMFRAGS 16 + + + +typedef int vht_group_id_t; + + + +#define VHT_SIGA1_CONST_MASK 0x800004 + +#define VHT_SIGA1_20MHZ_VAL 0x000000 +#define VHT_SIGA1_40MHZ_VAL 0x000001 +#define VHT_SIGA1_80MHZ_VAL 0x000002 +#define VHT_SIGA1_160MHZ_VAL 0x000003 + +#define VHT_SIGA1_STBC 0x000008 + +#define VHT_SIGA1_GID_MAX_GID 0x3f +#define VHT_SIGA1_GID_SHIFT 4 +#define VHT_SIGA1_GID_TO_AP 0x00 +#define VHT_SIGA1_GID_NOT_TO_AP 0x3f + +#define VHT_SIGA1_NSTS_SHIFT 10 +#define VHT_SIGA1_NSTS_SHIFT_MASK_USER0 0x001C00 + +#define VHT_SIGA1_PARTIAL_AID_SHIFT 13 + + +#define VHT_SIGA2_GI_NONE 0x000000 +#define VHT_SIGA2_GI_SHORT 0x000001 +#define VHT_SIGA2_GI_W_MOD10 0x000002 +#define VHT_SIGA2_CODING_LDPC 0x000004 +#define VHT_SIGA2_BEAMFORM_ENABLE 0x000100 +#define VHT_SIGA2_MCS_SHIFT 4 + +#define VHT_SIGA2_B9_RESERVED 0x000200 +#define VHT_SIGA2_TAIL_MASK 0xfc0000 +#define VHT_SIGA2_TAIL_VALUE 0x000000 + +#define VHT_SIGA2_SVC_BITS 16 +#define VHT_SIGA2_TAIL_BITS 6 + + + +typedef struct d11cnt { + uint32 txfrag; + uint32 txmulti; + uint32 txfail; + uint32 txretry; + uint32 txretrie; + uint32 rxdup; + uint32 txrts; + uint32 txnocts; + uint32 txnoack; + uint32 rxfrag; + uint32 rxmulti; + uint32 rxcrc; + uint32 txfrmsnt; + uint32 rxundec; +} d11cnt_t; + + +#define BRCM_PROP_OUI "\x00\x90\x4C" + + + +#define BRCM_OUI "\x00\x10\x18" + + +BWL_PRE_PACKED_STRUCT struct brcm_ie { + uint8 id; + uint8 len; + uint8 oui[3]; + uint8 ver; + uint8 assoc; + uint8 flags; + uint8 flags1; + uint16 amsdu_mtu_pref; +} BWL_POST_PACKED_STRUCT; +typedef struct brcm_ie brcm_ie_t; +#define BRCM_IE_LEN 11 +#define BRCM_IE_VER 2 +#define BRCM_IE_LEGACY_AES_VER 1 + + +#define BRF_LZWDS 0x4 +#define BRF_BLOCKACK 0x8 + + +#define BRF1_AMSDU 0x1 +#define BRF1_WMEPS 0x4 +#define BRF1_PSOFIX 0x8 +#define BRF1_RX_LARGE_AGG 0x10 +#define BRF1_RFAWARE_DCS 0x20 +#define BRF1_SOFTAP 0x40 + + +BWL_PRE_PACKED_STRUCT struct vndr_ie { + uchar id; + uchar len; + uchar oui [3]; + uchar data [1]; +} BWL_POST_PACKED_STRUCT; +typedef struct vndr_ie vndr_ie_t; + +#define VNDR_IE_HDR_LEN 2 +#define VNDR_IE_MIN_LEN 3 +#define VNDR_IE_FIXED_LEN (VNDR_IE_HDR_LEN + VNDR_IE_MIN_LEN) +#define VNDR_IE_MAX_LEN 256 + + +#define MCSSET_LEN 16 +#define MAX_MCS_NUM (128) + +BWL_PRE_PACKED_STRUCT struct ht_cap_ie { + uint16 cap; + uint8 params; + uint8 supp_mcs[MCSSET_LEN]; + uint16 ext_htcap; + uint32 txbf_cap; + uint8 as_cap; +} BWL_POST_PACKED_STRUCT; +typedef struct ht_cap_ie ht_cap_ie_t; + + + +BWL_PRE_PACKED_STRUCT struct ht_prop_cap_ie { + uint8 id; + uint8 len; + uint8 oui[3]; + uint8 type; + ht_cap_ie_t cap_ie; +} BWL_POST_PACKED_STRUCT; +typedef struct ht_prop_cap_ie ht_prop_cap_ie_t; + +#define HT_PROP_IE_OVERHEAD 4 +#define HT_CAP_IE_LEN 26 +#define HT_CAP_IE_TYPE 51 + +#define HT_CAP_LDPC_CODING 0x0001 +#define HT_CAP_40MHZ 0x0002 +#define HT_CAP_MIMO_PS_MASK 0x000C +#define HT_CAP_MIMO_PS_SHIFT 0x0002 +#define HT_CAP_MIMO_PS_OFF 0x0003 +#define HT_CAP_MIMO_PS_RTS 0x0001 +#define HT_CAP_MIMO_PS_ON 0x0000 +#define HT_CAP_GF 0x0010 +#define HT_CAP_SHORT_GI_20 0x0020 +#define HT_CAP_SHORT_GI_40 0x0040 +#define HT_CAP_TX_STBC 0x0080 +#define HT_CAP_RX_STBC_MASK 0x0300 +#define HT_CAP_RX_STBC_SHIFT 8 +#define HT_CAP_DELAYED_BA 0x0400 +#define HT_CAP_MAX_AMSDU 0x0800 + +#define HT_CAP_DSSS_CCK 0x1000 +#define HT_CAP_PSMP 0x2000 +#define HT_CAP_40MHZ_INTOLERANT 0x4000 +#define HT_CAP_LSIG_TXOP 0x8000 + +#define HT_CAP_RX_STBC_NO 0x0 +#define HT_CAP_RX_STBC_ONE_STREAM 0x1 +#define HT_CAP_RX_STBC_TWO_STREAM 0x2 +#define HT_CAP_RX_STBC_THREE_STREAM 0x3 + +#define VHT_MAX_MPDU 11454 +#define VHT_MPDU_MSDU_DELTA 56 + +#define VHT_MAX_AMSDU (VHT_MAX_MPDU - VHT_MPDU_MSDU_DELTA) + +#define HT_MAX_AMSDU 7935 +#define HT_MIN_AMSDU 3835 + +#define HT_PARAMS_RX_FACTOR_MASK 0x03 +#define HT_PARAMS_DENSITY_MASK 0x1C +#define HT_PARAMS_DENSITY_SHIFT 2 + + +#define AMPDU_MAX_MPDU_DENSITY 7 +#define AMPDU_DENSITY_NONE 0 +#define AMPDU_DENSITY_1over4_US 1 +#define AMPDU_DENSITY_1over2_US 2 +#define AMPDU_DENSITY_1_US 3 +#define AMPDU_DENSITY_2_US 4 +#define AMPDU_DENSITY_4_US 5 +#define AMPDU_DENSITY_8_US 6 +#define AMPDU_DENSITY_16_US 7 +#define AMPDU_RX_FACTOR_8K 0 +#define AMPDU_RX_FACTOR_16K 1 +#define AMPDU_RX_FACTOR_32K 2 +#define AMPDU_RX_FACTOR_64K 3 +#define AMPDU_RX_FACTOR_BASE 8*1024 + +#define AMPDU_DELIMITER_LEN 4 +#define AMPDU_DELIMITER_LEN_MAX 63 + +#define HT_CAP_EXT_PCO 0x0001 +#define HT_CAP_EXT_PCO_TTIME_MASK 0x0006 +#define HT_CAP_EXT_PCO_TTIME_SHIFT 1 +#define HT_CAP_EXT_MCS_FEEDBACK_MASK 0x0300 +#define HT_CAP_EXT_MCS_FEEDBACK_SHIFT 8 +#define HT_CAP_EXT_HTC 0x0400 +#define HT_CAP_EXT_RD_RESP 0x0800 + +BWL_PRE_PACKED_STRUCT struct ht_add_ie { + uint8 ctl_ch; + uint8 byte1; + uint16 opmode; + uint16 misc_bits; + uint8 basic_mcs[MCSSET_LEN]; +} BWL_POST_PACKED_STRUCT; +typedef struct ht_add_ie ht_add_ie_t; + + + +BWL_PRE_PACKED_STRUCT struct ht_prop_add_ie { + uint8 id; + uint8 len; + uint8 oui[3]; + uint8 type; + ht_add_ie_t add_ie; +} BWL_POST_PACKED_STRUCT; +typedef struct ht_prop_add_ie ht_prop_add_ie_t; + +#define HT_ADD_IE_LEN 22 +#define HT_ADD_IE_TYPE 52 + + +#define HT_BW_ANY 0x04 +#define HT_RIFS_PERMITTED 0x08 + + +#define HT_OPMODE_MASK 0x0003 +#define HT_OPMODE_SHIFT 0 +#define HT_OPMODE_PURE 0x0000 +#define HT_OPMODE_OPTIONAL 0x0001 +#define HT_OPMODE_HT20IN40 0x0002 +#define HT_OPMODE_MIXED 0x0003 +#define HT_OPMODE_NONGF 0x0004 +#define DOT11N_TXBURST 0x0008 +#define DOT11N_OBSS_NONHT 0x0010 + + +#define HT_BASIC_STBC_MCS 0x007f +#define HT_DUAL_STBC_PROT 0x0080 +#define HT_SECOND_BCN 0x0100 +#define HT_LSIG_TXOP 0x0200 +#define HT_PCO_ACTIVE 0x0400 +#define HT_PCO_PHASE 0x0800 +#define HT_DUALCTS_PROTECTION 0x0080 + + +#define DOT11N_2G_TXBURST_LIMIT 6160 +#define DOT11N_5G_TXBURST_LIMIT 3080 + + +#define GET_HT_OPMODE(add_ie) ((ltoh16_ua(&add_ie->opmode) & HT_OPMODE_MASK) \ + >> HT_OPMODE_SHIFT) +#define HT_MIXEDMODE_PRESENT(add_ie) ((ltoh16_ua(&add_ie->opmode) & HT_OPMODE_MASK) \ + == HT_OPMODE_MIXED) +#define HT_HT20_PRESENT(add_ie) ((ltoh16_ua(&add_ie->opmode) & HT_OPMODE_MASK) \ + == HT_OPMODE_HT20IN40) +#define HT_OPTIONAL_PRESENT(add_ie) ((ltoh16_ua(&add_ie->opmode) & HT_OPMODE_MASK) \ + == HT_OPMODE_OPTIONAL) +#define HT_USE_PROTECTION(add_ie) (HT_HT20_PRESENT((add_ie)) || \ + HT_MIXEDMODE_PRESENT((add_ie))) +#define HT_NONGF_PRESENT(add_ie) ((ltoh16_ua(&add_ie->opmode) & HT_OPMODE_NONGF) \ + == HT_OPMODE_NONGF) +#define DOT11N_TXBURST_PRESENT(add_ie) ((ltoh16_ua(&add_ie->opmode) & DOT11N_TXBURST) \ + == DOT11N_TXBURST) +#define DOT11N_OBSS_NONHT_PRESENT(add_ie) ((ltoh16_ua(&add_ie->opmode) & DOT11N_OBSS_NONHT) \ + == DOT11N_OBSS_NONHT) + +BWL_PRE_PACKED_STRUCT struct obss_params { + uint16 passive_dwell; + uint16 active_dwell; + uint16 bss_widthscan_interval; + uint16 passive_total; + uint16 active_total; + uint16 chanwidth_transition_dly; + uint16 activity_threshold; +} BWL_POST_PACKED_STRUCT; +typedef struct obss_params obss_params_t; + +BWL_PRE_PACKED_STRUCT struct dot11_obss_ie { + uint8 id; + uint8 len; + obss_params_t obss_params; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_obss_ie dot11_obss_ie_t; +#define DOT11_OBSS_SCAN_IE_LEN sizeof(obss_params_t) + + +#define HT_CTRL_LA_TRQ 0x00000002 +#define HT_CTRL_LA_MAI 0x0000003C +#define HT_CTRL_LA_MAI_SHIFT 2 +#define HT_CTRL_LA_MAI_MRQ 0x00000004 +#define HT_CTRL_LA_MAI_MSI 0x00000038 +#define HT_CTRL_LA_MFSI 0x000001C0 +#define HT_CTRL_LA_MFSI_SHIFT 6 +#define HT_CTRL_LA_MFB_ASELC 0x0000FE00 +#define HT_CTRL_LA_MFB_ASELC_SH 9 +#define HT_CTRL_LA_ASELC_CMD 0x00000C00 +#define HT_CTRL_LA_ASELC_DATA 0x0000F000 +#define HT_CTRL_CAL_POS 0x00030000 +#define HT_CTRL_CAL_SEQ 0x000C0000 +#define HT_CTRL_CSI_STEERING 0x00C00000 +#define HT_CTRL_CSI_STEER_SHIFT 22 +#define HT_CTRL_CSI_STEER_NFB 0 +#define HT_CTRL_CSI_STEER_CSI 1 +#define HT_CTRL_CSI_STEER_NCOM 2 +#define HT_CTRL_CSI_STEER_COM 3 +#define HT_CTRL_NDP_ANNOUNCE 0x01000000 +#define HT_CTRL_AC_CONSTRAINT 0x40000000 +#define HT_CTRL_RDG_MOREPPDU 0x80000000 + +#define HT_OPMODE_OPTIONAL 0x0001 +#define HT_OPMODE_HT20IN40 0x0002 +#define HT_OPMODE_MIXED 0x0003 +#define HT_OPMODE_NONGF 0x0004 +#define DOT11N_TXBURST 0x0008 +#define DOT11N_OBSS_NONHT 0x0010 + + + +BWL_PRE_PACKED_STRUCT struct vht_cap_ie { + uint32 vht_cap_info; + + uint16 rx_mcs_map; + uint16 rx_max_rate; + uint16 tx_mcs_map; + uint16 tx_max_rate; +} BWL_POST_PACKED_STRUCT; +typedef struct vht_cap_ie vht_cap_ie_t; + +#define VHT_CAP_IE_LEN 12 + +#define VHT_CAP_INFO_MAX_MPDU_LEN_MASK 0x00000003 +#define VHT_CAP_INFO_SUPP_CHAN_WIDTH_MASK 0x0000000c +#define VHT_CAP_INFO_LDPC 0x00000010 +#define VHT_CAP_INFO_SGI_80MHZ 0x00000020 + +#define VHT_CAP_INFO_SGI_160MHZ 0x00000040 +#define VHT_CAP_INFO_TX_STBC 0x00000080 + +#define VHT_CAP_INFO_RX_STBC_MASK 0x00000700 +#define VHT_CAP_INFO_RX_STBC_SHIFT 8 +#define VHT_CAP_INFO_SU_BEAMFMR 0x00000800 +#define VHT_CAP_INFO_SU_BEAMFMEE 0x00001000 +#define VHT_CAP_INFO_NUM_BMFMR_ANT_MASK 0x0000e000 +#define VHT_CAP_INFO_NUM_BMFMR_ANT_SHIFT 13 + +#define VHT_CAP_INFO_NUM_SOUNDING_DIM_MASK 0x00070000 +#define VHT_CAP_INFO_NUM_SOUNDING_DIM_SHIFT 16 +#define VHT_CAP_INFO_MU_BEAMFMR 0x00080000 +#define VHT_CAP_INFO_MU_BEAMFMEE 0x00100000 +#define VHT_CAP_INFO_TXOPPS 0x00200000 +#define VHT_CAP_INFO_HTCVHT 0x00400000 +#define VHT_CAP_INFO_AMPDU_MAXLEN_EXP_MASK 0x03800000 +#define VHT_CAP_INFO_AMPDU_MAXLEN_EXP_SHIFT 23 + +#define VHT_CAP_INFO_LINK_ADAPT_CAP_MASK 0x0c000000 +#define VHT_CAP_INFO_LINK_ADAPT_CAP_SHIFT 26 + + +#define VHT_CAP_SUPP_MCS_RX_HIGHEST_RATE_MASK 0x1fff +#define VHT_CAP_SUPP_MCS_RX_HIGHEST_RATE_SHIFT 0 + +#define VHT_CAP_SUPP_MCS_TX_HIGHEST_RATE_MASK 0x1fff +#define VHT_CAP_SUPP_MCS_TX_HIGHEST_RATE_SHIFT 0 + +#define VHT_CAP_MCS_MAP_0_7 0 +#define VHT_CAP_MCS_MAP_0_8 1 +#define VHT_CAP_MCS_MAP_0_9 2 +#define VHT_CAP_MCS_MAP_NONE 3 + +#define VHT_CAP_MCS_MAP_NSS_MAX 8 + + +typedef enum vht_cap_chan_width { + VHT_CAP_CHAN_WIDTH_20_40 = 0x00, + VHT_CAP_CHAN_WIDTH_80 = 0x04, + VHT_CAP_CHAN_WIDTH_160 = 0x08 +} vht_cap_chan_width_t; + + +typedef enum vht_cap_max_mpdu_len { + VHT_CAP_MPDU_MAX_4K = 0x00, + VHT_CAP_MPDU_MAX_8K = 0x01, + VHT_CAP_MPDU_MAX_11K = 0x02 +} vht_cap_max_mpdu_len_t; + + +BWL_PRE_PACKED_STRUCT struct vht_op_ie { + uint8 chan_width; + uint8 chan1; + uint8 chan2; + uint16 supp_mcs; +} BWL_POST_PACKED_STRUCT; +typedef struct vht_op_ie vht_op_ie_t; + +#define VHT_OP_IE_LEN 5 + +typedef enum vht_op_chan_width { + VHT_OP_CHAN_WIDTH_20_40 = 0, + VHT_OP_CHAN_WIDTH_80 = 1, + VHT_OP_CHAN_WIDTH_160 = 2, + VHT_OP_CHAN_WIDTH_80_80 = 3 +} vht_op_chan_width_t; + + +#define VHT_MCS_MAP_GET_SS_IDX(nss) (((nss)-1)*2) +#define VHT_MCS_MAP_GET_MCS_PER_SS(nss, mcsMap) \ + (((mcsMap) >> VHT_MCS_MAP_GET_SS_IDX(nss)) & 0x3) +#define VHT_MCS_MAP_SET_MCS_PER_SS(nss, numMcs, mcsMap) \ + ((mcsMap) |= (((numMcs) & 0x3) << VHT_MCS_MAP_GET_SS_IDX(nss))) + + +#define WPA_OUI "\x00\x50\xF2" +#define WPA_OUI_LEN 3 +#define WPA_OUI_TYPE 1 +#define WPA_VERSION 1 +#define WPA2_OUI "\x00\x0F\xAC" +#define WPA2_OUI_LEN 3 +#define WPA2_VERSION 1 +#define WPA2_VERSION_LEN 2 + + +#define WPS_OUI "\x00\x50\xF2" +#define WPS_OUI_LEN 3 +#define WPS_OUI_TYPE 4 + + + +#ifdef P2P_IE_OVRD +#define WFA_OUI MAC_OUI +#else +#define WFA_OUI "\x50\x6F\x9A" +#endif +#define WFA_OUI_LEN 3 +#ifdef P2P_IE_OVRD +#define WFA_OUI_TYPE_P2P MAC_OUI_TYPE_P2P +#else +#define WFA_OUI_TYPE_P2P 9 +#endif + +#define WFA_OUI_TYPE_TPC 8 +#ifdef WLTDLS +#define WFA_OUI_TYPE_WFD 10 +#endif + + +#define RSN_AKM_NONE 0 +#define RSN_AKM_UNSPECIFIED 1 +#define RSN_AKM_PSK 2 +#define RSN_AKM_FBT_1X 3 +#define RSN_AKM_FBT_PSK 4 +#define RSN_AKM_MFP_1X 5 +#define RSN_AKM_MFP_PSK 6 +#define RSN_AKM_TPK 7 + + +#define DOT11_MAX_DEFAULT_KEYS 4 +#define DOT11_MAX_KEY_SIZE 32 +#define DOT11_MAX_IV_SIZE 16 +#define DOT11_EXT_IV_FLAG (1<<5) +#define DOT11_WPA_KEY_RSC_LEN 8 + +#define WEP1_KEY_SIZE 5 +#define WEP1_KEY_HEX_SIZE 10 +#define WEP128_KEY_SIZE 13 +#define WEP128_KEY_HEX_SIZE 26 +#define TKIP_MIC_SIZE 8 +#define TKIP_EOM_SIZE 7 +#define TKIP_EOM_FLAG 0x5a +#define TKIP_KEY_SIZE 32 +#define TKIP_MIC_AUTH_TX 16 +#define TKIP_MIC_AUTH_RX 24 +#define TKIP_MIC_SUP_RX TKIP_MIC_AUTH_TX +#define TKIP_MIC_SUP_TX TKIP_MIC_AUTH_RX +#define AES_KEY_SIZE 16 +#define AES_MIC_SIZE 8 +#define BIP_KEY_SIZE 16 + + +#define WCN_OUI "\x00\x50\xf2" +#define WCN_TYPE 4 + + + + + +BWL_PRE_PACKED_STRUCT struct dot11_mdid_ie { + uint8 id; + uint8 len; + uint16 mdid; + uint8 cap; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_mdid_ie dot11_mdid_ie_t; + +#define FBT_MDID_CAP_OVERDS 0x01 +#define FBT_MDID_CAP_RRP 0x02 + + +BWL_PRE_PACKED_STRUCT struct dot11_ft_ie { + uint8 id; + uint8 len; + uint16 mic_control; + uint8 mic[16]; + uint8 anonce[32]; + uint8 snonce[32]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_ft_ie dot11_ft_ie_t; + +#define TIE_TYPE_RESERVED 0 +#define TIE_TYPE_REASSOC_DEADLINE 1 +#define TIE_TYPE_KEY_LIEFTIME 2 +#define TIE_TYPE_ASSOC_COMEBACK 3 +BWL_PRE_PACKED_STRUCT struct dot11_timeout_ie { + uint8 id; + uint8 len; + uint8 type; + uint32 value; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_timeout_ie dot11_timeout_ie_t; + + + +BWL_PRE_PACKED_STRUCT struct dot11_gtk_ie { + uint8 id; + uint8 len; + uint16 key_info; + uint8 key_len; + uint8 rsc[8]; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_gtk_ie dot11_gtk_ie_t; + +#define BSSID_INVALID "\x00\x00\x00\x00\x00\x00" +#define BSSID_BROADCAST "\xFF\xFF\xFF\xFF\xFF\xFF" + + + +#define WMM_OUI "\x00\x50\xF2" +#define WMM_OUI_LEN 3 +#define WMM_OUI_TYPE 2 +#define WMM_VERSION 1 +#define WMM_VERSION_LEN 1 + + +#define WMM_OUI_SUBTYPE_PARAMETER 1 +#define WMM_PARAMETER_IE_LEN 24 + + +BWL_PRE_PACKED_STRUCT struct link_id_ie { + uint8 id; + uint8 len; + struct ether_addr bssid; + struct ether_addr tdls_init_mac; + struct ether_addr tdls_resp_mac; +} BWL_POST_PACKED_STRUCT; +typedef struct link_id_ie link_id_ie_t; +#define TDLS_LINK_ID_IE_LEN 18 + + +BWL_PRE_PACKED_STRUCT struct wakeup_sch_ie { + uint8 id; + uint8 len; + uint32 offset; + uint32 interval; + uint32 awake_win_slots; + uint32 max_wake_win; + uint16 idle_cnt; +} BWL_POST_PACKED_STRUCT; +typedef struct wakeup_sch_ie wakeup_sch_ie_t; +#define TDLS_WAKEUP_SCH_IE_LEN 18 + + +BWL_PRE_PACKED_STRUCT struct channel_switch_timing_ie { + uint8 id; + uint8 len; + uint16 switch_time; + uint16 switch_timeout; +} BWL_POST_PACKED_STRUCT; +typedef struct channel_switch_timing_ie channel_switch_timing_ie_t; +#define TDLS_CHANNEL_SWITCH_TIMING_IE_LEN 4 + + +BWL_PRE_PACKED_STRUCT struct pti_control_ie { + uint8 id; + uint8 len; + uint8 tid; + uint16 seq_control; +} BWL_POST_PACKED_STRUCT; +typedef struct pti_control_ie pti_control_ie_t; +#define TDLS_PTI_CONTROL_IE_LEN 3 + + +BWL_PRE_PACKED_STRUCT struct pu_buffer_status_ie { + uint8 id; + uint8 len; + uint8 status; +} BWL_POST_PACKED_STRUCT; +typedef struct pu_buffer_status_ie pu_buffer_status_ie_t; +#define TDLS_PU_BUFFER_STATUS_IE_LEN 1 +#define TDLS_PU_BUFFER_STATUS_AC_BK 1 +#define TDLS_PU_BUFFER_STATUS_AC_BE 2 +#define TDLS_PU_BUFFER_STATUS_AC_VI 4 +#define TDLS_PU_BUFFER_STATUS_AC_VO 8 + + +#include + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/proto/802.11_bta.h b/drivers/net/wireless/bcmdhd/include/proto/802.11_bta.h new file mode 100644 index 00000000..3ee5a748 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/802.11_bta.h @@ -0,0 +1,45 @@ +/* + * BT-AMP (BlueTooth Alternate Mac and Phy) 802.11 PAL (Protocol Adaptation Layer) + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: 802.11_bta.h 294267 2011-11-04 23:41:52Z $ +*/ + +#ifndef _802_11_BTA_H_ +#define _802_11_BTA_H_ + +#define BT_SIG_SNAP_MPROT "\xAA\xAA\x03\x00\x19\x58" + +/* BT-AMP 802.11 PAL Protocols */ +#define BTA_PROT_L2CAP 1 +#define BTA_PROT_ACTIVITY_REPORT 2 +#define BTA_PROT_SECURITY 3 +#define BTA_PROT_LINK_SUPERVISION_REQUEST 4 +#define BTA_PROT_LINK_SUPERVISION_REPLY 5 + +/* BT-AMP 802.11 PAL AMP_ASSOC Type IDs */ +#define BTA_TYPE_ID_MAC_ADDRESS 1 +#define BTA_TYPE_ID_PREFERRED_CHANNELS 2 +#define BTA_TYPE_ID_CONNECTED_CHANNELS 3 +#define BTA_TYPE_ID_CAPABILITIES 4 +#define BTA_TYPE_ID_VERSION 5 +#endif /* _802_11_bta_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/proto/802.11e.h b/drivers/net/wireless/bcmdhd/include/proto/802.11e.h new file mode 100644 index 00000000..f391e68c --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/802.11e.h @@ -0,0 +1,131 @@ +/* + * 802.11e protocol header file + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: 802.11e.h 241182 2011-02-17 21:50:03Z $ + */ + +#ifndef _802_11e_H_ +#define _802_11e_H_ + +#ifndef _TYPEDEFS_H_ +#include +#endif + +/* This marks the start of a packed structure section. */ +#include + + +/* WME Traffic Specification (TSPEC) element */ +#define WME_TSPEC_HDR_LEN 2 /* WME TSPEC header length */ +#define WME_TSPEC_BODY_OFF 2 /* WME TSPEC body offset */ + +#define WME_CATEGORY_CODE_OFFSET 0 /* WME Category code offset */ +#define WME_ACTION_CODE_OFFSET 1 /* WME Action code offset */ +#define WME_TOKEN_CODE_OFFSET 2 /* WME Token code offset */ +#define WME_STATUS_CODE_OFFSET 3 /* WME Status code offset */ + +BWL_PRE_PACKED_STRUCT struct tsinfo { + uint8 octets[3]; +} BWL_POST_PACKED_STRUCT; + +typedef struct tsinfo tsinfo_t; + +/* 802.11e TSPEC IE */ +typedef BWL_PRE_PACKED_STRUCT struct tspec { + uint8 oui[DOT11_OUI_LEN]; /* WME_OUI */ + uint8 type; /* WME_TYPE */ + uint8 subtype; /* WME_SUBTYPE_TSPEC */ + uint8 version; /* WME_VERSION */ + tsinfo_t tsinfo; /* TS Info bit field */ + uint16 nom_msdu_size; /* (Nominal or fixed) MSDU Size (bytes) */ + uint16 max_msdu_size; /* Maximum MSDU Size (bytes) */ + uint32 min_srv_interval; /* Minimum Service Interval (us) */ + uint32 max_srv_interval; /* Maximum Service Interval (us) */ + uint32 inactivity_interval; /* Inactivity Interval (us) */ + uint32 suspension_interval; /* Suspension Interval (us) */ + uint32 srv_start_time; /* Service Start Time (us) */ + uint32 min_data_rate; /* Minimum Data Rate (bps) */ + uint32 mean_data_rate; /* Mean Data Rate (bps) */ + uint32 peak_data_rate; /* Peak Data Rate (bps) */ + uint32 max_burst_size; /* Maximum Burst Size (bytes) */ + uint32 delay_bound; /* Delay Bound (us) */ + uint32 min_phy_rate; /* Minimum PHY Rate (bps) */ + uint16 surplus_bw; /* Surplus Bandwidth Allowance (range 1.0-8.0) */ + uint16 medium_time; /* Medium Time (32 us/s periods) */ +} BWL_POST_PACKED_STRUCT tspec_t; + +#define WME_TSPEC_LEN (sizeof(tspec_t)) /* not including 2-bytes of header */ + +/* ts_info */ +/* 802.1D priority is duplicated - bits 13-11 AND bits 3-1 */ +#define TS_INFO_TID_SHIFT 1 /* TS info. TID shift */ +#define TS_INFO_TID_MASK (0xf << TS_INFO_TID_SHIFT) /* TS info. TID mask */ +#define TS_INFO_CONTENTION_SHIFT 7 /* TS info. contention shift */ +#define TS_INFO_CONTENTION_MASK (0x1 << TS_INFO_CONTENTION_SHIFT) /* TS info. contention mask */ +#define TS_INFO_DIRECTION_SHIFT 5 /* TS info. direction shift */ +#define TS_INFO_DIRECTION_MASK (0x3 << TS_INFO_DIRECTION_SHIFT) /* TS info. direction mask */ +#define TS_INFO_PSB_SHIFT 2 /* TS info. PSB bit Shift */ +#define TS_INFO_PSB_MASK (1 << TS_INFO_PSB_SHIFT) /* TS info. PSB mask */ +#define TS_INFO_UPLINK (0 << TS_INFO_DIRECTION_SHIFT) /* TS info. uplink */ +#define TS_INFO_DOWNLINK (1 << TS_INFO_DIRECTION_SHIFT) /* TS info. downlink */ +#define TS_INFO_BIDIRECTIONAL (3 << TS_INFO_DIRECTION_SHIFT) /* TS info. bidirectional */ +#define TS_INFO_USER_PRIO_SHIFT 3 /* TS info. user priority shift */ +/* TS info. user priority mask */ +#define TS_INFO_USER_PRIO_MASK (0x7 << TS_INFO_USER_PRIO_SHIFT) + +/* Macro to get/set bit(s) field in TSINFO */ +#define WLC_CAC_GET_TID(pt) ((((pt).octets[0]) & TS_INFO_TID_MASK) >> TS_INFO_TID_SHIFT) +#define WLC_CAC_GET_DIR(pt) ((((pt).octets[0]) & \ + TS_INFO_DIRECTION_MASK) >> TS_INFO_DIRECTION_SHIFT) +#define WLC_CAC_GET_PSB(pt) ((((pt).octets[1]) & TS_INFO_PSB_MASK) >> TS_INFO_PSB_SHIFT) +#define WLC_CAC_GET_USER_PRIO(pt) ((((pt).octets[1]) & \ + TS_INFO_USER_PRIO_MASK) >> TS_INFO_USER_PRIO_SHIFT) + +#define WLC_CAC_SET_TID(pt, id) ((((pt).octets[0]) & (~TS_INFO_TID_MASK)) | \ + ((id) << TS_INFO_TID_SHIFT)) +#define WLC_CAC_SET_USER_PRIO(pt, prio) ((((pt).octets[0]) & (~TS_INFO_USER_PRIO_MASK)) | \ + ((prio) << TS_INFO_USER_PRIO_SHIFT)) + +/* 802.11e QBSS Load IE */ +#define QBSS_LOAD_IE_LEN 5 /* QBSS Load IE length */ +#define QBSS_LOAD_AAC_OFF 3 /* AAC offset in IE */ + +#define CAC_ADDTS_RESP_TIMEOUT 300 /* default ADDTS response timeout in ms */ + +/* 802.11e ADDTS status code */ +#define DOT11E_STATUS_ADMISSION_ACCEPTED 0 /* TSPEC Admission accepted status */ +#define DOT11E_STATUS_ADDTS_INVALID_PARAM 1 /* TSPEC invalid parameter status */ +#define DOT11E_STATUS_ADDTS_REFUSED_NSBW 3 /* ADDTS refused (non-sufficient BW) */ +#define DOT11E_STATUS_ADDTS_REFUSED_AWHILE 47 /* ADDTS refused but could retry later */ + +/* 802.11e DELTS status code */ +#define DOT11E_STATUS_QSTA_LEAVE_QBSS 36 /* STA leave QBSS */ +#define DOT11E_STATUS_END_TS 37 /* END TS */ +#define DOT11E_STATUS_UNKNOWN_TS 38 /* UNKNOWN TS */ +#define DOT11E_STATUS_QSTA_REQ_TIMEOUT 39 /* STA ADDTS request timeout */ + + +/* This marks the end of a packed structure section. */ +#include + +#endif /* _802_11e_CAC_H_ */ diff --git a/drivers/net/wireless/bcmdhd/include/proto/802.1d.h b/drivers/net/wireless/bcmdhd/include/proto/802.1d.h new file mode 100644 index 00000000..116a226b --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/802.1d.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * Fundamental types and constants relating to 802.1D + * + * $Id: 802.1d.h 241182 2011-02-17 21:50:03Z $ + */ + +#ifndef _802_1_D_ +#define _802_1_D_ + + +#define PRIO_8021D_NONE 2 +#define PRIO_8021D_BK 1 +#define PRIO_8021D_BE 0 +#define PRIO_8021D_EE 3 +#define PRIO_8021D_CL 4 +#define PRIO_8021D_VI 5 +#define PRIO_8021D_VO 6 +#define PRIO_8021D_NC 7 +#define MAXPRIO 7 +#define NUMPRIO (MAXPRIO + 1) + +#define ALLPRIO -1 + + +#define PRIO2PREC(prio) \ + (((prio) == PRIO_8021D_NONE || (prio) == PRIO_8021D_BE) ? ((prio^2)) : (prio)) + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/proto/bcmeth.h b/drivers/net/wireless/bcmdhd/include/proto/bcmeth.h new file mode 100644 index 00000000..e54b2e38 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/bcmeth.h @@ -0,0 +1,82 @@ +/* + * Broadcom Ethernettype protocol definitions + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmeth.h 294352 2011-11-06 19:23:00Z $ + */ + + + +#ifndef _BCMETH_H_ +#define _BCMETH_H_ + +#ifndef _TYPEDEFS_H_ +#include +#endif + + +#include + + + + + + + +#define BCMILCP_SUBTYPE_RATE 1 +#define BCMILCP_SUBTYPE_LINK 2 +#define BCMILCP_SUBTYPE_CSA 3 +#define BCMILCP_SUBTYPE_LARQ 4 +#define BCMILCP_SUBTYPE_VENDOR 5 +#define BCMILCP_SUBTYPE_FLH 17 + +#define BCMILCP_SUBTYPE_VENDOR_LONG 32769 +#define BCMILCP_SUBTYPE_CERT 32770 +#define BCMILCP_SUBTYPE_SES 32771 + + +#define BCMILCP_BCM_SUBTYPE_RESERVED 0 +#define BCMILCP_BCM_SUBTYPE_EVENT 1 +#define BCMILCP_BCM_SUBTYPE_SES 2 + + +#define BCMILCP_BCM_SUBTYPE_DPT 4 + +#define BCMILCP_BCM_SUBTYPEHDR_MINLENGTH 8 +#define BCMILCP_BCM_SUBTYPEHDR_VERSION 0 + + +typedef BWL_PRE_PACKED_STRUCT struct bcmeth_hdr +{ + uint16 subtype; + uint16 length; + uint8 version; + uint8 oui[3]; + + uint16 usr_subtype; +} BWL_POST_PACKED_STRUCT bcmeth_hdr_t; + + + +#include + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h b/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h new file mode 100644 index 00000000..c4397074 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h @@ -0,0 +1,368 @@ +/* + * Broadcom Event protocol definitions + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * Dependencies: proto/bcmeth.h + * + * $Id: bcmevent.h 374275 2012-12-12 11:44:18Z $ + * + */ + +/* + * Broadcom Ethernet Events protocol defines + * + */ + +#ifndef _BCMEVENT_H_ +#define _BCMEVENT_H_ + +#ifndef _TYPEDEFS_H_ +#include +#endif + +/* This marks the start of a packed structure section. */ +#include + +#define BCM_EVENT_MSG_VERSION 2 /* wl_event_msg_t struct version */ +#define BCM_MSG_IFNAME_MAX 16 /* max length of interface name */ + +/* flags */ +#define WLC_EVENT_MSG_LINK 0x01 /* link is up */ +#define WLC_EVENT_MSG_FLUSHTXQ 0x02 /* flush tx queue on MIC error */ +#define WLC_EVENT_MSG_GROUP 0x04 /* group MIC error */ +#define WLC_EVENT_MSG_UNKBSS 0x08 /* unknown source bsscfg */ +#define WLC_EVENT_MSG_UNKIF 0x10 /* unknown source OS i/f */ + +/* these fields are stored in network order */ + +/* version 1 */ +typedef BWL_PRE_PACKED_STRUCT struct +{ + uint16 version; + uint16 flags; /* see flags below */ + uint32 event_type; /* Message (see below) */ + uint32 status; /* Status code (see below) */ + uint32 reason; /* Reason code (if applicable) */ + uint32 auth_type; /* WLC_E_AUTH */ + uint32 datalen; /* data buf */ + struct ether_addr addr; /* Station address (if applicable) */ + char ifname[BCM_MSG_IFNAME_MAX]; /* name of the packet incoming interface */ +} BWL_POST_PACKED_STRUCT wl_event_msg_v1_t; + +/* the current version */ +typedef BWL_PRE_PACKED_STRUCT struct +{ + uint16 version; + uint16 flags; /* see flags below */ + uint32 event_type; /* Message (see below) */ + uint32 status; /* Status code (see below) */ + uint32 reason; /* Reason code (if applicable) */ + uint32 auth_type; /* WLC_E_AUTH */ + uint32 datalen; /* data buf */ + struct ether_addr addr; /* Station address (if applicable) */ + char ifname[BCM_MSG_IFNAME_MAX]; /* name of the packet incoming interface */ + uint8 ifidx; /* destination OS i/f index */ + uint8 bsscfgidx; /* source bsscfg index */ +} BWL_POST_PACKED_STRUCT wl_event_msg_t; + +/* used by driver msgs */ +typedef BWL_PRE_PACKED_STRUCT struct bcm_event { + struct ether_header eth; + bcmeth_hdr_t bcm_hdr; + wl_event_msg_t event; + /* data portion follows */ +} BWL_POST_PACKED_STRUCT bcm_event_t; + +#define BCM_MSG_LEN (sizeof(bcm_event_t) - sizeof(bcmeth_hdr_t) - sizeof(struct ether_header)) + +/* Event messages */ +#define WLC_E_SET_SSID 0 /* indicates status of set SSID */ +#define WLC_E_JOIN 1 /* differentiates join IBSS from found (WLC_E_START) IBSS */ +#define WLC_E_START 2 /* STA founded an IBSS or AP started a BSS */ +#define WLC_E_AUTH 3 /* 802.11 AUTH request */ +#define WLC_E_AUTH_IND 4 /* 802.11 AUTH indication */ +#define WLC_E_DEAUTH 5 /* 802.11 DEAUTH request */ +#define WLC_E_DEAUTH_IND 6 /* 802.11 DEAUTH indication */ +#define WLC_E_ASSOC 7 /* 802.11 ASSOC request */ +#define WLC_E_ASSOC_IND 8 /* 802.11 ASSOC indication */ +#define WLC_E_REASSOC 9 /* 802.11 REASSOC request */ +#define WLC_E_REASSOC_IND 10 /* 802.11 REASSOC indication */ +#define WLC_E_DISASSOC 11 /* 802.11 DISASSOC request */ +#define WLC_E_DISASSOC_IND 12 /* 802.11 DISASSOC indication */ +#define WLC_E_QUIET_START 13 /* 802.11h Quiet period started */ +#define WLC_E_QUIET_END 14 /* 802.11h Quiet period ended */ +#define WLC_E_BEACON_RX 15 /* BEACONS received/lost indication */ +#define WLC_E_LINK 16 /* generic link indication */ +#define WLC_E_MIC_ERROR 17 /* TKIP MIC error occurred */ +#define WLC_E_NDIS_LINK 18 /* NDIS style link indication */ +#define WLC_E_ROAM 19 /* roam attempt occurred: indicate status & reason */ +#define WLC_E_TXFAIL 20 /* change in dot11FailedCount (txfail) */ +#define WLC_E_PMKID_CACHE 21 /* WPA2 pmkid cache indication */ +#define WLC_E_RETROGRADE_TSF 22 /* current AP's TSF value went backward */ +#define WLC_E_PRUNE 23 /* AP was pruned from join list for reason */ +#define WLC_E_AUTOAUTH 24 /* report AutoAuth table entry match for join attempt */ +#define WLC_E_EAPOL_MSG 25 /* Event encapsulating an EAPOL message */ +#define WLC_E_SCAN_COMPLETE 26 /* Scan results are ready or scan was aborted */ +#define WLC_E_ADDTS_IND 27 /* indicate to host addts fail/success */ +#define WLC_E_DELTS_IND 28 /* indicate to host delts fail/success */ +#define WLC_E_BCNSENT_IND 29 /* indicate to host of beacon transmit */ +#define WLC_E_BCNRX_MSG 30 /* Send the received beacon up to the host */ +#define WLC_E_BCNLOST_MSG 31 /* indicate to host loss of beacon */ +#define WLC_E_ROAM_PREP 32 /* before attempting to roam */ +#define WLC_E_PFN_NET_FOUND 33 /* PFN network found event */ +#define WLC_E_PFN_NET_LOST 34 /* PFN network lost event */ +#define WLC_E_RESET_COMPLETE 35 +#define WLC_E_JOIN_START 36 +#define WLC_E_ROAM_START 37 +#define WLC_E_ASSOC_START 38 +#define WLC_E_IBSS_ASSOC 39 +#define WLC_E_RADIO 40 +#define WLC_E_PSM_WATCHDOG 41 /* PSM microcode watchdog fired */ +#define WLC_E_PROBREQ_MSG 44 /* probe request received */ +#define WLC_E_SCAN_CONFIRM_IND 45 +#define WLC_E_PSK_SUP 46 /* WPA Handshake fail */ +#define WLC_E_COUNTRY_CODE_CHANGED 47 +#define WLC_E_EXCEEDED_MEDIUM_TIME 48 /* WMMAC excedded medium time */ +#define WLC_E_ICV_ERROR 49 /* WEP ICV error occurred */ +#define WLC_E_UNICAST_DECODE_ERROR 50 /* Unsupported unicast encrypted frame */ +#define WLC_E_MULTICAST_DECODE_ERROR 51 /* Unsupported multicast encrypted frame */ +#define WLC_E_TRACE 52 +#ifdef WLBTAMP +#define WLC_E_BTA_HCI_EVENT 53 /* BT-AMP HCI event */ +#endif +#define WLC_E_IF 54 /* I/F change (for dongle host notification) */ +#define WLC_E_P2P_DISC_LISTEN_COMPLETE 55 /* listen state expires */ +#define WLC_E_RSSI 56 /* indicate RSSI change based on configured levels */ +#define WLC_E_PFN_SCAN_COMPLETE 57 /* PFN completed scan of network list */ +#define WLC_E_EXTLOG_MSG 58 +#define WLC_E_ACTION_FRAME 59 /* Action frame Rx */ +#define WLC_E_ACTION_FRAME_COMPLETE 60 /* Action frame Tx complete */ +#define WLC_E_PRE_ASSOC_IND 61 /* assoc request received */ +#define WLC_E_PRE_REASSOC_IND 62 /* re-assoc request received */ +#define WLC_E_CHANNEL_ADOPTED 63 +#define WLC_E_AP_STARTED 64 /* AP started */ +#define WLC_E_DFS_AP_STOP 65 /* AP stopped due to DFS */ +#define WLC_E_DFS_AP_RESUME 66 /* AP resumed due to DFS */ +#define WLC_E_WAI_STA_EVENT 67 /* WAI stations event */ +#define WLC_E_WAI_MSG 68 /* event encapsulating an WAI message */ +#define WLC_E_ESCAN_RESULT 69 /* escan result event */ +#define WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE 70 /* action frame off channel complete */ +#define WLC_E_PROBRESP_MSG 71 /* probe response received */ +#define WLC_E_P2P_PROBREQ_MSG 72 /* P2P Probe request received */ +#define WLC_E_DCS_REQUEST 73 + +#define WLC_E_FIFO_CREDIT_MAP 74 /* credits for D11 FIFOs. [AC0,AC1,AC2,AC3,BC_MC,ATIM] */ + +#define WLC_E_ACTION_FRAME_RX 75 /* Received action frame event WITH + * wl_event_rx_frame_data_t header + */ +#define WLC_E_WAKE_EVENT 76 /* Wake Event timer fired, used for wake WLAN test mode */ +#define WLC_E_RM_COMPLETE 77 /* Radio measurement complete */ +#define WLC_E_HTSFSYNC 78 /* Synchronize TSF with the host */ +#define WLC_E_OVERLAY_REQ 79 /* request an overlay IOCTL/iovar from the host */ +#define WLC_E_CSA_COMPLETE_IND 80 /* 802.11 CHANNEL SWITCH ACTION completed */ +#define WLC_E_EXCESS_PM_WAKE_EVENT 81 /* excess PM Wake Event to inform host */ +#define WLC_E_PFN_SCAN_NONE 82 /* no PFN networks around */ +#define WLC_E_PFN_SCAN_ALLGONE 83 /* last found PFN network gets lost */ +#define WLC_E_GTK_PLUMBED 84 +#define WLC_E_ASSOC_IND_NDIS 85 /* 802.11 ASSOC indication for NDIS only */ +#define WLC_E_REASSOC_IND_NDIS 86 /* 802.11 REASSOC indication for NDIS only */ +#define WLC_E_ASSOC_REQ_IE 87 +#define WLC_E_ASSOC_RESP_IE 88 +#define WLC_E_ASSOC_RECREATED 89 /* association recreated on resume */ +#define WLC_E_ACTION_FRAME_RX_NDIS 90 /* rx action frame event for NDIS only */ +#define WLC_E_AUTH_REQ 91 /* authentication request received */ +#define WLC_E_TDLS_PEER_EVENT 92 /* discovered peer, connected or disconnected peer */ +#define WLC_E_SPEEDY_RECREATE_FAIL 93 /* fast assoc recreation failed */ +#define WLC_E_SERVICE_FOUND 102 /* desired service found */ +#define WLC_E_GAS_FRAGMENT_RX 103 /* GAS fragment received */ +#define WLC_E_GAS_COMPLETE 104 /* GAS sessions all complete */ +#define WLC_E_P2PO_ADD_DEVICE 105 /* New device found by p2p offload */ +#define WLC_E_P2PO_DEL_DEVICE 106 /* device has been removed by p2p offload */ +#define WLC_E_LAST 107 /* highest val + 1 for range checking */ + + +/* Table of event name strings for UIs and debugging dumps */ +typedef struct { + uint event; + const char *name; +} bcmevent_name_t; + +extern const bcmevent_name_t bcmevent_names[]; +extern const int bcmevent_names_size; + +/* Event status codes */ +#define WLC_E_STATUS_SUCCESS 0 /* operation was successful */ +#define WLC_E_STATUS_FAIL 1 /* operation failed */ +#define WLC_E_STATUS_TIMEOUT 2 /* operation timed out */ +#define WLC_E_STATUS_NO_NETWORKS 3 /* failed due to no matching network found */ +#define WLC_E_STATUS_ABORT 4 /* operation was aborted */ +#define WLC_E_STATUS_NO_ACK 5 /* protocol failure: packet not ack'd */ +#define WLC_E_STATUS_UNSOLICITED 6 /* AUTH or ASSOC packet was unsolicited */ +#define WLC_E_STATUS_ATTEMPT 7 /* attempt to assoc to an auto auth configuration */ +#define WLC_E_STATUS_PARTIAL 8 /* scan results are incomplete */ +#define WLC_E_STATUS_NEWSCAN 9 /* scan aborted by another scan */ +#define WLC_E_STATUS_NEWASSOC 10 /* scan aborted due to assoc in progress */ +#define WLC_E_STATUS_11HQUIET 11 /* 802.11h quiet period started */ +#define WLC_E_STATUS_SUPPRESS 12 /* user disabled scanning (WLC_SET_SCANSUPPRESS) */ +#define WLC_E_STATUS_NOCHANS 13 /* no allowable channels to scan */ +#define WLC_E_STATUS_CS_ABORT 15 /* abort channel select */ +#define WLC_E_STATUS_ERROR 16 /* request failed due to error */ + +/* roam reason codes */ +#define WLC_E_REASON_INITIAL_ASSOC 0 /* initial assoc */ +#define WLC_E_REASON_LOW_RSSI 1 /* roamed due to low RSSI */ +#define WLC_E_REASON_DEAUTH 2 /* roamed due to DEAUTH indication */ +#define WLC_E_REASON_DISASSOC 3 /* roamed due to DISASSOC indication */ +#define WLC_E_REASON_BCNS_LOST 4 /* roamed due to lost beacons */ +#define WLC_E_REASON_MINTXRATE 9 /* roamed because at mintxrate for too long */ +#define WLC_E_REASON_TXFAIL 10 /* We can hear AP, but AP can't hear us */ + +/* Roam codes used primarily by CCX */ +#define WLC_E_REASON_FAST_ROAM_FAILED 5 /* roamed due to fast roam failure */ +#define WLC_E_REASON_DIRECTED_ROAM 6 /* roamed due to request by AP */ +#define WLC_E_REASON_TSPEC_REJECTED 7 /* roamed due to TSPEC rejection */ +#define WLC_E_REASON_BETTER_AP 8 /* roamed due to finding better AP */ + + +#define WLC_E_REASON_REQUESTED_ROAM 11 /* roamed due to BSS Mgmt Transition request by AP */ + +/* prune reason codes */ +#define WLC_E_PRUNE_ENCR_MISMATCH 1 /* encryption mismatch */ +#define WLC_E_PRUNE_BCAST_BSSID 2 /* AP uses a broadcast BSSID */ +#define WLC_E_PRUNE_MAC_DENY 3 /* STA's MAC addr is in AP's MAC deny list */ +#define WLC_E_PRUNE_MAC_NA 4 /* STA's MAC addr is not in AP's MAC allow list */ +#define WLC_E_PRUNE_REG_PASSV 5 /* AP not allowed due to regulatory restriction */ +#define WLC_E_PRUNE_SPCT_MGMT 6 /* AP does not support STA locale spectrum mgmt */ +#define WLC_E_PRUNE_RADAR 7 /* AP is on a radar channel of STA locale */ +#define WLC_E_RSN_MISMATCH 8 /* STA does not support AP's RSN */ +#define WLC_E_PRUNE_NO_COMMON_RATES 9 /* No rates in common with AP */ +#define WLC_E_PRUNE_BASIC_RATES 10 /* STA does not support all basic rates of BSS */ +#define WLC_E_PRUNE_CIPHER_NA 12 /* BSS's cipher not supported */ +#define WLC_E_PRUNE_KNOWN_STA 13 /* AP is already known to us as a STA */ +#define WLC_E_PRUNE_WDS_PEER 15 /* AP is already known to us as a WDS peer */ +#define WLC_E_PRUNE_QBSS_LOAD 16 /* QBSS LOAD - AAC is too low */ +#define WLC_E_PRUNE_HOME_AP 17 /* prune home AP */ + +/* WPA failure reason codes carried in the WLC_E_PSK_SUP event */ +#define WLC_E_SUP_OTHER 0 /* Other reason */ +#define WLC_E_SUP_DECRYPT_KEY_DATA 1 /* Decryption of key data failed */ +#define WLC_E_SUP_BAD_UCAST_WEP128 2 /* Illegal use of ucast WEP128 */ +#define WLC_E_SUP_BAD_UCAST_WEP40 3 /* Illegal use of ucast WEP40 */ +#define WLC_E_SUP_UNSUP_KEY_LEN 4 /* Unsupported key length */ +#define WLC_E_SUP_PW_KEY_CIPHER 5 /* Unicast cipher mismatch in pairwise key */ +#define WLC_E_SUP_MSG3_TOO_MANY_IE 6 /* WPA IE contains > 1 RSN IE in key msg 3 */ +#define WLC_E_SUP_MSG3_IE_MISMATCH 7 /* WPA IE mismatch in key message 3 */ +#define WLC_E_SUP_NO_INSTALL_FLAG 8 /* INSTALL flag unset in 4-way msg */ +#define WLC_E_SUP_MSG3_NO_GTK 9 /* encapsulated GTK missing from msg 3 */ +#define WLC_E_SUP_GRP_KEY_CIPHER 10 /* Multicast cipher mismatch in group key */ +#define WLC_E_SUP_GRP_MSG1_NO_GTK 11 /* encapsulated GTK missing from group msg 1 */ +#define WLC_E_SUP_GTK_DECRYPT_FAIL 12 /* GTK decrypt failure */ +#define WLC_E_SUP_SEND_FAIL 13 /* message send failure */ +#define WLC_E_SUP_DEAUTH 14 /* received FC_DEAUTH */ +#define WLC_E_SUP_WPA_PSK_TMO 15 /* WPA PSK 4-way handshake timeout */ + +/* Event data for events that include frames received over the air */ +/* WLC_E_PROBRESP_MSG + * WLC_E_P2P_PROBREQ_MSG + * WLC_E_ACTION_FRAME_RX + */ +typedef BWL_PRE_PACKED_STRUCT struct wl_event_rx_frame_data { + uint16 version; + uint16 channel; /* Matches chanspec_t format from bcmwifi_channels.h */ + int32 rssi; + uint32 mactime; + uint32 rate; +} BWL_POST_PACKED_STRUCT wl_event_rx_frame_data_t; + +#define BCM_RX_FRAME_DATA_VERSION 1 + +/* WLC_E_IF event data */ +typedef struct wl_event_data_if { + uint8 ifidx; /* RTE virtual device index (for dongle) */ + uint8 opcode; /* see I/F opcode */ + uint8 reserved; + uint8 bssidx; /* bsscfg index */ + uint8 role; /* see I/F role */ +} wl_event_data_if_t; + +/* opcode in WLC_E_IF event */ +#define WLC_E_IF_ADD 1 /* bsscfg add */ +#define WLC_E_IF_DEL 2 /* bsscfg delete */ +#define WLC_E_IF_CHANGE 3 /* bsscfg role change */ + +/* I/F role code in WLC_E_IF event */ +#define WLC_E_IF_ROLE_STA 0 /* Infra STA */ +#define WLC_E_IF_ROLE_AP 1 /* Access Point */ +#define WLC_E_IF_ROLE_WDS 2 /* WDS link */ +#define WLC_E_IF_ROLE_P2P_GO 3 /* P2P Group Owner */ +#define WLC_E_IF_ROLE_P2P_CLIENT 4 /* P2P Client */ +#ifdef WLBTAMP +#define WLC_E_IF_ROLE_BTA_CREATOR 5 /* BT-AMP Creator */ +#define WLC_E_IF_ROLE_BTA_ACCEPTOR 6 /* BT-AMP Acceptor */ +#endif + +/* Reason codes for LINK */ +#define WLC_E_LINK_BCN_LOSS 1 /* Link down because of beacon loss */ +#define WLC_E_LINK_DISASSOC 2 /* Link down because of disassoc */ +#define WLC_E_LINK_ASSOC_REC 3 /* Link down because assoc recreate failed */ +#define WLC_E_LINK_BSSCFG_DIS 4 /* Link down due to bsscfg down */ + +/* reason codes for WLC_E_OVERLAY_REQ event */ +#define WLC_E_OVL_DOWNLOAD 0 /* overlay download request */ +#define WLC_E_OVL_UPDATE_IND 1 /* device indication of host overlay update */ + +/* reason codes for WLC_E_TDLS_PEER_EVENT event */ +#define WLC_E_TDLS_PEER_DISCOVERED 0 /* peer is ready to establish TDLS */ +#define WLC_E_TDLS_PEER_CONNECTED 1 +#define WLC_E_TDLS_PEER_DISCONNECTED 2 + +/* GAS event data */ +typedef BWL_PRE_PACKED_STRUCT struct wl_event_gas { + uint16 channel; /* channel of GAS protocol */ + uint8 dialog_token; /* GAS dialog token */ + uint8 fragment_id; /* fragment id */ + uint16 status_code; /* status code on GAS completion */ + uint16 data_len; /* length of data to follow */ + uint8 data[1]; /* variable length specified by data_len */ +} BWL_POST_PACKED_STRUCT wl_event_gas_t; + +/* service discovery TLV */ +typedef BWL_PRE_PACKED_STRUCT struct wl_sd_tlv { + uint16 length; /* length of response_data */ + uint8 protocol; /* service protocol type */ + uint8 transaction_id; /* service transaction id */ + uint8 status_code; /* status code */ + uint8 data[1]; /* response data */ +} BWL_POST_PACKED_STRUCT wl_sd_tlv_t; + +/* service discovery event data */ +typedef BWL_PRE_PACKED_STRUCT struct wl_event_sd { + uint16 channel; /* channel */ + uint8 count; /* number of tlvs */ + wl_sd_tlv_t tlv[1]; /* service discovery TLV */ +} BWL_POST_PACKED_STRUCT wl_event_sd_t; + +/* This marks the end of a packed structure section. */ +#include + +#endif /* _BCMEVENT_H_ */ diff --git a/drivers/net/wireless/bcmdhd/include/proto/bcmip.h b/drivers/net/wireless/bcmdhd/include/proto/bcmip.h new file mode 100644 index 00000000..d5c3b76d --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/bcmip.h @@ -0,0 +1,210 @@ +/* + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * Fundamental constants relating to IP Protocol + * + * $Id: bcmip.h 290206 2011-10-17 19:13:51Z $ + */ + +#ifndef _bcmip_h_ +#define _bcmip_h_ + +#ifndef _TYPEDEFS_H_ +#include +#endif + + +#include + + + +#define IP_VER_OFFSET 0x0 +#define IP_VER_MASK 0xf0 +#define IP_VER_SHIFT 4 +#define IP_VER_4 4 +#define IP_VER_6 6 + +#define IP_VER(ip_body) \ + ((((uint8 *)(ip_body))[IP_VER_OFFSET] & IP_VER_MASK) >> IP_VER_SHIFT) + +#define IP_PROT_ICMP 0x1 +#define IP_PROT_IGMP 0x2 +#define IP_PROT_TCP 0x6 +#define IP_PROT_UDP 0x11 +#define IP_PROT_ICMP6 0x3a + + +#define IPV4_VER_HL_OFFSET 0 +#define IPV4_TOS_OFFSET 1 +#define IPV4_PKTLEN_OFFSET 2 +#define IPV4_PKTFLAG_OFFSET 6 +#define IPV4_PROT_OFFSET 9 +#define IPV4_CHKSUM_OFFSET 10 +#define IPV4_SRC_IP_OFFSET 12 +#define IPV4_DEST_IP_OFFSET 16 +#define IPV4_OPTIONS_OFFSET 20 + + +#define IPV4_VER_MASK 0xf0 +#define IPV4_VER_SHIFT 4 + +#define IPV4_HLEN_MASK 0x0f +#define IPV4_HLEN(ipv4_body) (4 * (((uint8 *)(ipv4_body))[IPV4_VER_HL_OFFSET] & IPV4_HLEN_MASK)) + +#define IPV4_ADDR_LEN 4 + +#define IPV4_ADDR_NULL(a) ((((uint8 *)(a))[0] | ((uint8 *)(a))[1] | \ + ((uint8 *)(a))[2] | ((uint8 *)(a))[3]) == 0) + +#define IPV4_ADDR_BCAST(a) ((((uint8 *)(a))[0] & ((uint8 *)(a))[1] & \ + ((uint8 *)(a))[2] & ((uint8 *)(a))[3]) == 0xff) + +#define IPV4_TOS_DSCP_MASK 0xfc +#define IPV4_TOS_DSCP_SHIFT 2 + +#define IPV4_TOS(ipv4_body) (((uint8 *)(ipv4_body))[IPV4_TOS_OFFSET]) + +#define IPV4_TOS_PREC_MASK 0xe0 +#define IPV4_TOS_PREC_SHIFT 5 + +#define IPV4_TOS_LOWDELAY 0x10 +#define IPV4_TOS_THROUGHPUT 0x8 +#define IPV4_TOS_RELIABILITY 0x4 + +#define IPV4_PROT(ipv4_body) (((uint8 *)(ipv4_body))[IPV4_PROT_OFFSET]) + +#define IPV4_FRAG_RESV 0x8000 +#define IPV4_FRAG_DONT 0x4000 +#define IPV4_FRAG_MORE 0x2000 +#define IPV4_FRAG_OFFSET_MASK 0x1fff + +#define IPV4_ADDR_STR_LEN 16 + + +BWL_PRE_PACKED_STRUCT struct ipv4_addr { + uint8 addr[IPV4_ADDR_LEN]; +} BWL_POST_PACKED_STRUCT; + +BWL_PRE_PACKED_STRUCT struct ipv4_hdr { + uint8 version_ihl; + uint8 tos; + uint16 tot_len; + uint16 id; + uint16 frag; + uint8 ttl; + uint8 prot; + uint16 hdr_chksum; + uint8 src_ip[IPV4_ADDR_LEN]; + uint8 dst_ip[IPV4_ADDR_LEN]; +} BWL_POST_PACKED_STRUCT; + + +#define IPV6_PAYLOAD_LEN_OFFSET 4 +#define IPV6_NEXT_HDR_OFFSET 6 +#define IPV6_HOP_LIMIT_OFFSET 7 +#define IPV6_SRC_IP_OFFSET 8 +#define IPV6_DEST_IP_OFFSET 24 + + +#define IPV6_TRAFFIC_CLASS(ipv6_body) \ + (((((uint8 *)(ipv6_body))[0] & 0x0f) << 4) | \ + ((((uint8 *)(ipv6_body))[1] & 0xf0) >> 4)) + +#define IPV6_FLOW_LABEL(ipv6_body) \ + (((((uint8 *)(ipv6_body))[1] & 0x0f) << 16) | \ + (((uint8 *)(ipv6_body))[2] << 8) | \ + (((uint8 *)(ipv6_body))[3])) + +#define IPV6_PAYLOAD_LEN(ipv6_body) \ + ((((uint8 *)(ipv6_body))[IPV6_PAYLOAD_LEN_OFFSET + 0] << 8) | \ + ((uint8 *)(ipv6_body))[IPV6_PAYLOAD_LEN_OFFSET + 1]) + +#define IPV6_NEXT_HDR(ipv6_body) \ + (((uint8 *)(ipv6_body))[IPV6_NEXT_HDR_OFFSET]) + +#define IPV6_PROT(ipv6_body) IPV6_NEXT_HDR(ipv6_body) + +#define IPV6_ADDR_LEN 16 + + +#define IP_TOS46(ip_body) \ + (IP_VER(ip_body) == IP_VER_4 ? IPV4_TOS(ip_body) : \ + IP_VER(ip_body) == IP_VER_6 ? IPV6_TRAFFIC_CLASS(ip_body) : 0) + + +#define IPV6_EXTHDR_HOP 0 +#define IPV6_EXTHDR_ROUTING 43 +#define IPV6_EXTHDR_FRAGMENT 44 +#define IPV6_EXTHDR_AUTH 51 +#define IPV6_EXTHDR_NONE 59 +#define IPV6_EXTHDR_DEST 60 + +#define IPV6_EXTHDR(prot) (((prot) == IPV6_EXTHDR_HOP) || \ + ((prot) == IPV6_EXTHDR_ROUTING) || \ + ((prot) == IPV6_EXTHDR_FRAGMENT) || \ + ((prot) == IPV6_EXTHDR_AUTH) || \ + ((prot) == IPV6_EXTHDR_NONE) || \ + ((prot) == IPV6_EXTHDR_DEST)) + +#define IPV6_MIN_HLEN 40 + +#define IPV6_EXTHDR_LEN(eh) ((((struct ipv6_exthdr *)(eh))->hdrlen + 1) << 3) + +BWL_PRE_PACKED_STRUCT struct ipv6_exthdr { + uint8 nexthdr; + uint8 hdrlen; +} BWL_POST_PACKED_STRUCT; + +BWL_PRE_PACKED_STRUCT struct ipv6_exthdr_frag { + uint8 nexthdr; + uint8 rsvd; + uint16 frag_off; + uint32 ident; +} BWL_POST_PACKED_STRUCT; + +static INLINE int32 +ipv6_exthdr_len(uint8 *h, uint8 *proto) +{ + uint16 len = 0, hlen; + struct ipv6_exthdr *eh = (struct ipv6_exthdr *)h; + + while (IPV6_EXTHDR(eh->nexthdr)) { + if (eh->nexthdr == IPV6_EXTHDR_NONE) + return -1; + else if (eh->nexthdr == IPV6_EXTHDR_FRAGMENT) + hlen = 8; + else if (eh->nexthdr == IPV6_EXTHDR_AUTH) + hlen = (eh->hdrlen + 2) << 2; + else + hlen = IPV6_EXTHDR_LEN(eh); + + len += hlen; + eh = (struct ipv6_exthdr *)(h + len); + } + + *proto = eh->nexthdr; + return len; +} + + +#include + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/proto/bt_amp_hci.h b/drivers/net/wireless/bcmdhd/include/proto/bt_amp_hci.h new file mode 100644 index 00000000..8617985d --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/bt_amp_hci.h @@ -0,0 +1,441 @@ +/* + * BT-AMP (BlueTooth Alternate Mac and Phy) HCI (Host/Controller Interface) + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bt_amp_hci.h 294267 2011-11-04 23:41:52Z $ +*/ + +#ifndef _bt_amp_hci_h +#define _bt_amp_hci_h + +/* This marks the start of a packed structure section. */ +#include + + +/* AMP HCI CMD packet format */ +typedef BWL_PRE_PACKED_STRUCT struct amp_hci_cmd { + uint16 opcode; + uint8 plen; + uint8 parms[1]; +} BWL_POST_PACKED_STRUCT amp_hci_cmd_t; + +#define HCI_CMD_PREAMBLE_SIZE OFFSETOF(amp_hci_cmd_t, parms) +#define HCI_CMD_DATA_SIZE 255 + +/* AMP HCI CMD opcode layout */ +#define HCI_CMD_OPCODE(ogf, ocf) ((((ogf) & 0x3F) << 10) | ((ocf) & 0x03FF)) +#define HCI_CMD_OGF(opcode) ((uint8)(((opcode) >> 10) & 0x3F)) +#define HCI_CMD_OCF(opcode) ((opcode) & 0x03FF) + +/* AMP HCI command opcodes */ +#define HCI_Read_Failed_Contact_Counter HCI_CMD_OPCODE(0x05, 0x0001) +#define HCI_Reset_Failed_Contact_Counter HCI_CMD_OPCODE(0x05, 0x0002) +#define HCI_Read_Link_Quality HCI_CMD_OPCODE(0x05, 0x0003) +#define HCI_Read_Local_AMP_Info HCI_CMD_OPCODE(0x05, 0x0009) +#define HCI_Read_Local_AMP_ASSOC HCI_CMD_OPCODE(0x05, 0x000A) +#define HCI_Write_Remote_AMP_ASSOC HCI_CMD_OPCODE(0x05, 0x000B) +#define HCI_Create_Physical_Link HCI_CMD_OPCODE(0x01, 0x0035) +#define HCI_Accept_Physical_Link_Request HCI_CMD_OPCODE(0x01, 0x0036) +#define HCI_Disconnect_Physical_Link HCI_CMD_OPCODE(0x01, 0x0037) +#define HCI_Create_Logical_Link HCI_CMD_OPCODE(0x01, 0x0038) +#define HCI_Accept_Logical_Link HCI_CMD_OPCODE(0x01, 0x0039) +#define HCI_Disconnect_Logical_Link HCI_CMD_OPCODE(0x01, 0x003A) +#define HCI_Logical_Link_Cancel HCI_CMD_OPCODE(0x01, 0x003B) +#define HCI_Flow_Spec_Modify HCI_CMD_OPCODE(0x01, 0x003C) +#define HCI_Write_Flow_Control_Mode HCI_CMD_OPCODE(0x01, 0x0067) +#define HCI_Read_Best_Effort_Flush_Timeout HCI_CMD_OPCODE(0x01, 0x0069) +#define HCI_Write_Best_Effort_Flush_Timeout HCI_CMD_OPCODE(0x01, 0x006A) +#define HCI_Short_Range_Mode HCI_CMD_OPCODE(0x01, 0x006B) +#define HCI_Reset HCI_CMD_OPCODE(0x03, 0x0003) +#define HCI_Read_Connection_Accept_Timeout HCI_CMD_OPCODE(0x03, 0x0015) +#define HCI_Write_Connection_Accept_Timeout HCI_CMD_OPCODE(0x03, 0x0016) +#define HCI_Read_Link_Supervision_Timeout HCI_CMD_OPCODE(0x03, 0x0036) +#define HCI_Write_Link_Supervision_Timeout HCI_CMD_OPCODE(0x03, 0x0037) +#define HCI_Enhanced_Flush HCI_CMD_OPCODE(0x03, 0x005F) +#define HCI_Read_Logical_Link_Accept_Timeout HCI_CMD_OPCODE(0x03, 0x0061) +#define HCI_Write_Logical_Link_Accept_Timeout HCI_CMD_OPCODE(0x03, 0x0062) +#define HCI_Set_Event_Mask_Page_2 HCI_CMD_OPCODE(0x03, 0x0063) +#define HCI_Read_Location_Data_Command HCI_CMD_OPCODE(0x03, 0x0064) +#define HCI_Write_Location_Data_Command HCI_CMD_OPCODE(0x03, 0x0065) +#define HCI_Read_Local_Version_Info HCI_CMD_OPCODE(0x04, 0x0001) +#define HCI_Read_Local_Supported_Commands HCI_CMD_OPCODE(0x04, 0x0002) +#define HCI_Read_Buffer_Size HCI_CMD_OPCODE(0x04, 0x0005) +#define HCI_Read_Data_Block_Size HCI_CMD_OPCODE(0x04, 0x000A) + +/* AMP HCI command parameters */ +typedef BWL_PRE_PACKED_STRUCT struct read_local_cmd_parms { + uint8 plh; + uint8 offset[2]; /* length so far */ + uint8 max_remote[2]; +} BWL_POST_PACKED_STRUCT read_local_cmd_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct write_remote_cmd_parms { + uint8 plh; + uint8 offset[2]; + uint8 len[2]; + uint8 frag[1]; +} BWL_POST_PACKED_STRUCT write_remote_cmd_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct phy_link_cmd_parms { + uint8 plh; + uint8 key_length; + uint8 key_type; + uint8 key[1]; +} BWL_POST_PACKED_STRUCT phy_link_cmd_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct dis_phy_link_cmd_parms { + uint8 plh; + uint8 reason; +} BWL_POST_PACKED_STRUCT dis_phy_link_cmd_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct log_link_cmd_parms { + uint8 plh; + uint8 txflow[16]; + uint8 rxflow[16]; +} BWL_POST_PACKED_STRUCT log_link_cmd_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct ext_flow_spec { + uint8 id; + uint8 service_type; + uint8 max_sdu[2]; + uint8 sdu_ia_time[4]; + uint8 access_latency[4]; + uint8 flush_timeout[4]; +} BWL_POST_PACKED_STRUCT ext_flow_spec_t; + +typedef BWL_PRE_PACKED_STRUCT struct log_link_cancel_cmd_parms { + uint8 plh; + uint8 tx_fs_ID; +} BWL_POST_PACKED_STRUCT log_link_cancel_cmd_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct flow_spec_mod_cmd_parms { + uint8 llh[2]; + uint8 txflow[16]; + uint8 rxflow[16]; +} BWL_POST_PACKED_STRUCT flow_spec_mod_cmd_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct plh_pad { + uint8 plh; + uint8 pad; +} BWL_POST_PACKED_STRUCT plh_pad_t; + +typedef BWL_PRE_PACKED_STRUCT union hci_handle { + uint16 bredr; + plh_pad_t amp; +} BWL_POST_PACKED_STRUCT hci_handle_t; + +typedef BWL_PRE_PACKED_STRUCT struct ls_to_cmd_parms { + hci_handle_t handle; + uint8 timeout[2]; +} BWL_POST_PACKED_STRUCT ls_to_cmd_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct befto_cmd_parms { + uint8 llh[2]; + uint8 befto[4]; +} BWL_POST_PACKED_STRUCT befto_cmd_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct srm_cmd_parms { + uint8 plh; + uint8 srm; +} BWL_POST_PACKED_STRUCT srm_cmd_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct ld_cmd_parms { + uint8 ld_aware; + uint8 ld[2]; + uint8 ld_opts; + uint8 l_opts; +} BWL_POST_PACKED_STRUCT ld_cmd_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct eflush_cmd_parms { + uint8 llh[2]; + uint8 packet_type; +} BWL_POST_PACKED_STRUCT eflush_cmd_parms_t; + +/* Generic AMP extended flow spec service types */ +#define EFS_SVCTYPE_NO_TRAFFIC 0 +#define EFS_SVCTYPE_BEST_EFFORT 1 +#define EFS_SVCTYPE_GUARANTEED 2 + +/* AMP HCI event packet format */ +typedef BWL_PRE_PACKED_STRUCT struct amp_hci_event { + uint8 ecode; + uint8 plen; + uint8 parms[1]; +} BWL_POST_PACKED_STRUCT amp_hci_event_t; + +#define HCI_EVT_PREAMBLE_SIZE OFFSETOF(amp_hci_event_t, parms) + +/* AMP HCI event codes */ +#define HCI_Command_Complete 0x0E +#define HCI_Command_Status 0x0F +#define HCI_Flush_Occurred 0x11 +#define HCI_Enhanced_Flush_Complete 0x39 +#define HCI_Physical_Link_Complete 0x40 +#define HCI_Channel_Select 0x41 +#define HCI_Disconnect_Physical_Link_Complete 0x42 +#define HCI_Logical_Link_Complete 0x45 +#define HCI_Disconnect_Logical_Link_Complete 0x46 +#define HCI_Flow_Spec_Modify_Complete 0x47 +#define HCI_Number_of_Completed_Data_Blocks 0x48 +#define HCI_Short_Range_Mode_Change_Complete 0x4C +#define HCI_Status_Change_Event 0x4D +#define HCI_Vendor_Specific 0xFF + +/* AMP HCI event mask bit positions */ +#define HCI_Physical_Link_Complete_Event_Mask 0x0001 +#define HCI_Channel_Select_Event_Mask 0x0002 +#define HCI_Disconnect_Physical_Link_Complete_Event_Mask 0x0004 +#define HCI_Logical_Link_Complete_Event_Mask 0x0020 +#define HCI_Disconnect_Logical_Link_Complete_Event_Mask 0x0040 +#define HCI_Flow_Spec_Modify_Complete_Event_Mask 0x0080 +#define HCI_Number_of_Completed_Data_Blocks_Event_Mask 0x0100 +#define HCI_Short_Range_Mode_Change_Complete_Event_Mask 0x1000 +#define HCI_Status_Change_Event_Mask 0x2000 +#define HCI_All_Event_Mask 0x31e7 +/* AMP HCI event parameters */ +typedef BWL_PRE_PACKED_STRUCT struct cmd_status_parms { + uint8 status; + uint8 cmdpkts; + uint16 opcode; +} BWL_POST_PACKED_STRUCT cmd_status_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct cmd_complete_parms { + uint8 cmdpkts; + uint16 opcode; + uint8 parms[1]; +} BWL_POST_PACKED_STRUCT cmd_complete_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct flush_occurred_evt_parms { + uint16 handle; +} BWL_POST_PACKED_STRUCT flush_occurred_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct write_remote_evt_parms { + uint8 status; + uint8 plh; +} BWL_POST_PACKED_STRUCT write_remote_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct read_local_evt_parms { + uint8 status; + uint8 plh; + uint16 len; + uint8 frag[1]; +} BWL_POST_PACKED_STRUCT read_local_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct read_local_info_evt_parms { + uint8 status; + uint8 AMP_status; + uint32 bandwidth; + uint32 gbandwidth; + uint32 latency; + uint32 PDU_size; + uint8 ctrl_type; + uint16 PAL_cap; + uint16 AMP_ASSOC_len; + uint32 max_flush_timeout; + uint32 be_flush_timeout; +} BWL_POST_PACKED_STRUCT read_local_info_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct log_link_evt_parms { + uint8 status; + uint16 llh; + uint8 plh; + uint8 tx_fs_ID; +} BWL_POST_PACKED_STRUCT log_link_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct disc_log_link_evt_parms { + uint8 status; + uint16 llh; + uint8 reason; +} BWL_POST_PACKED_STRUCT disc_log_link_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct log_link_cancel_evt_parms { + uint8 status; + uint8 plh; + uint8 tx_fs_ID; +} BWL_POST_PACKED_STRUCT log_link_cancel_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct flow_spec_mod_evt_parms { + uint8 status; + uint16 llh; +} BWL_POST_PACKED_STRUCT flow_spec_mod_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct phy_link_evt_parms { + uint8 status; + uint8 plh; +} BWL_POST_PACKED_STRUCT phy_link_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct dis_phy_link_evt_parms { + uint8 status; + uint8 plh; + uint8 reason; +} BWL_POST_PACKED_STRUCT dis_phy_link_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct read_ls_to_evt_parms { + uint8 status; + hci_handle_t handle; + uint16 timeout; +} BWL_POST_PACKED_STRUCT read_ls_to_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct read_lla_ca_to_evt_parms { + uint8 status; + uint16 timeout; +} BWL_POST_PACKED_STRUCT read_lla_ca_to_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct read_data_block_size_evt_parms { + uint8 status; + uint16 ACL_pkt_len; + uint16 data_block_len; + uint16 data_block_num; +} BWL_POST_PACKED_STRUCT read_data_block_size_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct data_blocks { + uint16 handle; + uint16 pkts; + uint16 blocks; +} BWL_POST_PACKED_STRUCT data_blocks_t; + +typedef BWL_PRE_PACKED_STRUCT struct num_completed_data_blocks_evt_parms { + uint16 num_blocks; + uint8 num_handles; + data_blocks_t completed[1]; +} BWL_POST_PACKED_STRUCT num_completed_data_blocks_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct befto_evt_parms { + uint8 status; + uint32 befto; +} BWL_POST_PACKED_STRUCT befto_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct srm_evt_parms { + uint8 status; + uint8 plh; + uint8 srm; +} BWL_POST_PACKED_STRUCT srm_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct contact_counter_evt_parms { + uint8 status; + uint8 llh[2]; + uint16 counter; +} BWL_POST_PACKED_STRUCT contact_counter_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct contact_counter_reset_evt_parms { + uint8 status; + uint8 llh[2]; +} BWL_POST_PACKED_STRUCT contact_counter_reset_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct read_linkq_evt_parms { + uint8 status; + hci_handle_t handle; + uint8 link_quality; +} BWL_POST_PACKED_STRUCT read_linkq_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct ld_evt_parms { + uint8 status; + uint8 ld_aware; + uint8 ld[2]; + uint8 ld_opts; + uint8 l_opts; +} BWL_POST_PACKED_STRUCT ld_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct eflush_complete_evt_parms { + uint16 handle; +} BWL_POST_PACKED_STRUCT eflush_complete_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct vendor_specific_evt_parms { + uint8 len; + uint8 parms[1]; +} BWL_POST_PACKED_STRUCT vendor_specific_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct local_version_info_evt_parms { + uint8 status; + uint8 hci_version; + uint16 hci_revision; + uint8 pal_version; + uint16 mfg_name; + uint16 pal_subversion; +} BWL_POST_PACKED_STRUCT local_version_info_evt_parms_t; + +#define MAX_SUPPORTED_CMD_BYTE 64 +typedef BWL_PRE_PACKED_STRUCT struct local_supported_cmd_evt_parms { + uint8 status; + uint8 cmd[MAX_SUPPORTED_CMD_BYTE]; +} BWL_POST_PACKED_STRUCT local_supported_cmd_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct status_change_evt_parms { + uint8 status; + uint8 amp_status; +} BWL_POST_PACKED_STRUCT status_change_evt_parms_t; + +/* AMP HCI error codes */ +#define HCI_SUCCESS 0x00 +#define HCI_ERR_ILLEGAL_COMMAND 0x01 +#define HCI_ERR_NO_CONNECTION 0x02 +#define HCI_ERR_MEMORY_FULL 0x07 +#define HCI_ERR_CONNECTION_TIMEOUT 0x08 +#define HCI_ERR_MAX_NUM_OF_CONNECTIONS 0x09 +#define HCI_ERR_CONNECTION_EXISTS 0x0B +#define HCI_ERR_CONNECTION_DISALLOWED 0x0C +#define HCI_ERR_CONNECTION_ACCEPT_TIMEOUT 0x10 +#define HCI_ERR_UNSUPPORTED_VALUE 0x11 +#define HCI_ERR_ILLEGAL_PARAMETER_FMT 0x12 +#define HCI_ERR_CONN_TERM_BY_LOCAL_HOST 0x16 +#define HCI_ERR_UNSPECIFIED 0x1F +#define HCI_ERR_UNIT_KEY_USED 0x26 +#define HCI_ERR_QOS_REJECTED 0x2D +#define HCI_ERR_PARAM_OUT_OF_RANGE 0x30 +#define HCI_ERR_NO_SUITABLE_CHANNEL 0x39 +#define HCI_ERR_CHANNEL_MOVE 0xFF + +/* AMP HCI ACL Data packet format */ +typedef BWL_PRE_PACKED_STRUCT struct amp_hci_ACL_data { + uint16 handle; /* 12-bit connection handle + 2-bit PB and 2-bit BC flags */ + uint16 dlen; /* data total length */ + uint8 data[1]; +} BWL_POST_PACKED_STRUCT amp_hci_ACL_data_t; + +#define HCI_ACL_DATA_PREAMBLE_SIZE OFFSETOF(amp_hci_ACL_data_t, data) + +#define HCI_ACL_DATA_BC_FLAGS (0x0 << 14) +#define HCI_ACL_DATA_PB_FLAGS (0x3 << 12) + +#define HCI_ACL_DATA_HANDLE(handle) ((handle) & 0x0fff) +#define HCI_ACL_DATA_FLAGS(handle) ((handle) >> 12) + +/* AMP Activity Report packet formats */ +typedef BWL_PRE_PACKED_STRUCT struct amp_hci_activity_report { + uint8 ScheduleKnown; + uint8 NumReports; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT amp_hci_activity_report_t; + +typedef BWL_PRE_PACKED_STRUCT struct amp_hci_activity_report_triple { + uint32 StartTime; + uint32 Duration; + uint32 Periodicity; +} BWL_POST_PACKED_STRUCT amp_hci_activity_report_triple_t; + +#define HCI_AR_SCHEDULE_KNOWN 0x01 + + +/* This marks the end of a packed structure section. */ +#include + +#endif /* _bt_amp_hci_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/proto/eapol.h b/drivers/net/wireless/bcmdhd/include/proto/eapol.h new file mode 100644 index 00000000..8936d164 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/eapol.h @@ -0,0 +1,193 @@ +/* + * 802.1x EAPOL definitions + * + * See + * IEEE Std 802.1X-2001 + * IEEE 802.1X RADIUS Usage Guidelines + * + * Copyright (C) 2002 Broadcom Corporation + * + * $Id: eapol.h 241182 2011-02-17 21:50:03Z $ + */ + +#ifndef _eapol_h_ +#define _eapol_h_ + +#ifndef _TYPEDEFS_H_ +#include +#endif + +/* This marks the start of a packed structure section. */ +#include + +#include + +/* EAPOL for 802.3/Ethernet */ +typedef BWL_PRE_PACKED_STRUCT struct { + struct ether_header eth; /* 802.3/Ethernet header */ + unsigned char version; /* EAPOL protocol version */ + unsigned char type; /* EAPOL type */ + unsigned short length; /* Length of body */ + unsigned char body[1]; /* Body (optional) */ +} BWL_POST_PACKED_STRUCT eapol_header_t; + +#define EAPOL_HEADER_LEN 18 + +typedef struct { + unsigned char version; /* EAPOL protocol version */ + unsigned char type; /* EAPOL type */ + unsigned short length; /* Length of body */ +} eapol_hdr_t; + +#define EAPOL_HDR_LEN 4 + +/* EAPOL version */ +#define WPA2_EAPOL_VERSION 2 +#define WPA_EAPOL_VERSION 1 +#define LEAP_EAPOL_VERSION 1 +#define SES_EAPOL_VERSION 1 + +/* EAPOL types */ +#define EAP_PACKET 0 +#define EAPOL_START 1 +#define EAPOL_LOGOFF 2 +#define EAPOL_KEY 3 +#define EAPOL_ASF 4 + +/* EAPOL-Key types */ +#define EAPOL_RC4_KEY 1 +#define EAPOL_WPA2_KEY 2 /* 802.11i/WPA2 */ +#define EAPOL_WPA_KEY 254 /* WPA */ + +/* RC4 EAPOL-Key header field sizes */ +#define EAPOL_KEY_REPLAY_LEN 8 +#define EAPOL_KEY_IV_LEN 16 +#define EAPOL_KEY_SIG_LEN 16 + +/* RC4 EAPOL-Key */ +typedef BWL_PRE_PACKED_STRUCT struct { + unsigned char type; /* Key Descriptor Type */ + unsigned short length; /* Key Length (unaligned) */ + unsigned char replay[EAPOL_KEY_REPLAY_LEN]; /* Replay Counter */ + unsigned char iv[EAPOL_KEY_IV_LEN]; /* Key IV */ + unsigned char index; /* Key Flags & Index */ + unsigned char signature[EAPOL_KEY_SIG_LEN]; /* Key Signature */ + unsigned char key[1]; /* Key (optional) */ +} BWL_POST_PACKED_STRUCT eapol_key_header_t; + +#define EAPOL_KEY_HEADER_LEN 44 + +/* RC4 EAPOL-Key flags */ +#define EAPOL_KEY_FLAGS_MASK 0x80 +#define EAPOL_KEY_BROADCAST 0 +#define EAPOL_KEY_UNICAST 0x80 + +/* RC4 EAPOL-Key index */ +#define EAPOL_KEY_INDEX_MASK 0x7f + +/* WPA/802.11i/WPA2 EAPOL-Key header field sizes */ +#define EAPOL_WPA_KEY_REPLAY_LEN 8 +#define EAPOL_WPA_KEY_NONCE_LEN 32 +#define EAPOL_WPA_KEY_IV_LEN 16 +#define EAPOL_WPA_KEY_RSC_LEN 8 +#define EAPOL_WPA_KEY_ID_LEN 8 +#define EAPOL_WPA_KEY_MIC_LEN 16 +#define EAPOL_WPA_KEY_DATA_LEN (EAPOL_WPA_MAX_KEY_SIZE + AKW_BLOCK_LEN) +#define EAPOL_WPA_MAX_KEY_SIZE 32 + +/* WPA EAPOL-Key */ +typedef BWL_PRE_PACKED_STRUCT struct { + unsigned char type; /* Key Descriptor Type */ + unsigned short key_info; /* Key Information (unaligned) */ + unsigned short key_len; /* Key Length (unaligned) */ + unsigned char replay[EAPOL_WPA_KEY_REPLAY_LEN]; /* Replay Counter */ + unsigned char nonce[EAPOL_WPA_KEY_NONCE_LEN]; /* Nonce */ + unsigned char iv[EAPOL_WPA_KEY_IV_LEN]; /* Key IV */ + unsigned char rsc[EAPOL_WPA_KEY_RSC_LEN]; /* Key RSC */ + unsigned char id[EAPOL_WPA_KEY_ID_LEN]; /* WPA:Key ID, 802.11i/WPA2: Reserved */ + unsigned char mic[EAPOL_WPA_KEY_MIC_LEN]; /* Key MIC */ + unsigned short data_len; /* Key Data Length */ + unsigned char data[EAPOL_WPA_KEY_DATA_LEN]; /* Key data */ +} BWL_POST_PACKED_STRUCT eapol_wpa_key_header_t; + +#define EAPOL_WPA_KEY_LEN 95 + +/* WPA/802.11i/WPA2 KEY KEY_INFO bits */ +#define WPA_KEY_DESC_V1 0x01 +#define WPA_KEY_DESC_V2 0x02 +#define WPA_KEY_DESC_V3 0x03 +#define WPA_KEY_PAIRWISE 0x08 +#define WPA_KEY_INSTALL 0x40 +#define WPA_KEY_ACK 0x80 +#define WPA_KEY_MIC 0x100 +#define WPA_KEY_SECURE 0x200 +#define WPA_KEY_ERROR 0x400 +#define WPA_KEY_REQ 0x800 + +#define WPA_KEY_DESC_V2_OR_V3 WPA_KEY_DESC_V2 + +/* WPA-only KEY KEY_INFO bits */ +#define WPA_KEY_INDEX_0 0x00 +#define WPA_KEY_INDEX_1 0x10 +#define WPA_KEY_INDEX_2 0x20 +#define WPA_KEY_INDEX_3 0x30 +#define WPA_KEY_INDEX_MASK 0x30 +#define WPA_KEY_INDEX_SHIFT 0x04 + +/* 802.11i/WPA2-only KEY KEY_INFO bits */ +#define WPA_KEY_ENCRYPTED_DATA 0x1000 + +/* Key Data encapsulation */ +typedef BWL_PRE_PACKED_STRUCT struct { + uint8 type; + uint8 length; + uint8 oui[3]; + uint8 subtype; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT eapol_wpa2_encap_data_t; + +#define EAPOL_WPA2_ENCAP_DATA_HDR_LEN 6 + +#define WPA2_KEY_DATA_SUBTYPE_GTK 1 +#define WPA2_KEY_DATA_SUBTYPE_STAKEY 2 +#define WPA2_KEY_DATA_SUBTYPE_MAC 3 +#define WPA2_KEY_DATA_SUBTYPE_PMKID 4 +#define WPA2_KEY_DATA_SUBTYPE_IGTK 9 + +/* GTK encapsulation */ +typedef BWL_PRE_PACKED_STRUCT struct { + uint8 flags; + uint8 reserved; + uint8 gtk[EAPOL_WPA_MAX_KEY_SIZE]; +} BWL_POST_PACKED_STRUCT eapol_wpa2_key_gtk_encap_t; + +#define EAPOL_WPA2_KEY_GTK_ENCAP_HDR_LEN 2 + +#define WPA2_GTK_INDEX_MASK 0x03 +#define WPA2_GTK_INDEX_SHIFT 0x00 + +#define WPA2_GTK_TRANSMIT 0x04 + +/* IGTK encapsulation */ +typedef BWL_PRE_PACKED_STRUCT struct { + uint16 key_id; + uint8 ipn[6]; + uint8 key[EAPOL_WPA_MAX_KEY_SIZE]; +} BWL_POST_PACKED_STRUCT eapol_wpa2_key_igtk_encap_t; + +#define EAPOL_WPA2_KEY_IGTK_ENCAP_HDR_LEN 8 + +/* STAKey encapsulation */ +typedef BWL_PRE_PACKED_STRUCT struct { + uint8 reserved[2]; + uint8 mac[ETHER_ADDR_LEN]; + uint8 stakey[EAPOL_WPA_MAX_KEY_SIZE]; +} BWL_POST_PACKED_STRUCT eapol_wpa2_key_stakey_encap_t; + +#define WPA2_KEY_DATA_PAD 0xdd + + +/* This marks the end of a packed structure section. */ +#include + +#endif /* _eapol_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/proto/ethernet.h b/drivers/net/wireless/bcmdhd/include/proto/ethernet.h new file mode 100644 index 00000000..e4551856 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/ethernet.h @@ -0,0 +1,162 @@ +/* + * From FreeBSD 2.2.7: Fundamental constants relating to ethernet. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: ethernet.h 309193 2012-01-19 00:03:57Z $ + */ + +#ifndef _NET_ETHERNET_H_ +#define _NET_ETHERNET_H_ + +#ifndef _TYPEDEFS_H_ +#include "typedefs.h" +#endif + + +#include + + + +#define ETHER_ADDR_LEN 6 + + +#define ETHER_TYPE_LEN 2 + + +#define ETHER_CRC_LEN 4 + + +#define ETHER_HDR_LEN (ETHER_ADDR_LEN * 2 + ETHER_TYPE_LEN) + + +#define ETHER_MIN_LEN 64 + + +#define ETHER_MIN_DATA 46 + + +#define ETHER_MAX_LEN 1518 + + +#define ETHER_MAX_DATA 1500 + + +#define ETHER_TYPE_MIN 0x0600 +#define ETHER_TYPE_IP 0x0800 +#define ETHER_TYPE_ARP 0x0806 +#define ETHER_TYPE_8021Q 0x8100 +#define ETHER_TYPE_IPV6 0x86dd +#define ETHER_TYPE_BRCM 0x886c +#define ETHER_TYPE_802_1X 0x888e +#define ETHER_TYPE_802_1X_PREAUTH 0x88c7 +#define ETHER_TYPE_WAI 0x88b4 +#define ETHER_TYPE_89_0D 0x890d + +#define ETHER_TYPE_IPV6 0x86dd + + +#define ETHER_BRCM_SUBTYPE_LEN 4 + + +#define ETHER_DEST_OFFSET (0 * ETHER_ADDR_LEN) +#define ETHER_SRC_OFFSET (1 * ETHER_ADDR_LEN) +#define ETHER_TYPE_OFFSET (2 * ETHER_ADDR_LEN) + + +#define ETHER_IS_VALID_LEN(foo) \ + ((foo) >= ETHER_MIN_LEN && (foo) <= ETHER_MAX_LEN) + +#define ETHER_FILL_MCAST_ADDR_FROM_IP(ea, mgrp_ip) { \ + ((uint8 *)ea)[0] = 0x01; \ + ((uint8 *)ea)[1] = 0x00; \ + ((uint8 *)ea)[2] = 0x5e; \ + ((uint8 *)ea)[3] = ((mgrp_ip) >> 16) & 0x7f; \ + ((uint8 *)ea)[4] = ((mgrp_ip) >> 8) & 0xff; \ + ((uint8 *)ea)[5] = ((mgrp_ip) >> 0) & 0xff; \ +} + +#ifndef __INCif_etherh + +BWL_PRE_PACKED_STRUCT struct ether_header { + uint8 ether_dhost[ETHER_ADDR_LEN]; + uint8 ether_shost[ETHER_ADDR_LEN]; + uint16 ether_type; +} BWL_POST_PACKED_STRUCT; + + +BWL_PRE_PACKED_STRUCT struct ether_addr { + uint8 octet[ETHER_ADDR_LEN]; +} BWL_POST_PACKED_STRUCT; +#endif + + +#define ETHER_SET_LOCALADDR(ea) (((uint8 *)(ea))[0] = (((uint8 *)(ea))[0] | 2)) +#define ETHER_IS_LOCALADDR(ea) (((uint8 *)(ea))[0] & 2) +#define ETHER_CLR_LOCALADDR(ea) (((uint8 *)(ea))[0] = (((uint8 *)(ea))[0] & 0xfd)) +#define ETHER_TOGGLE_LOCALADDR(ea) (((uint8 *)(ea))[0] = (((uint8 *)(ea))[0] ^ 2)) + + +#define ETHER_SET_UNICAST(ea) (((uint8 *)(ea))[0] = (((uint8 *)(ea))[0] & ~1)) + + +#define ETHER_ISMULTI(ea) (((const uint8 *)(ea))[0] & 1) + + + +#define ether_cmp(a, b) (!(((short*)(a))[0] == ((short*)(b))[0]) | \ + !(((short*)(a))[1] == ((short*)(b))[1]) | \ + !(((short*)(a))[2] == ((short*)(b))[2])) + + +#define ether_copy(s, d) { \ + ((short*)(d))[0] = ((const short*)(s))[0]; \ + ((short*)(d))[1] = ((const short*)(s))[1]; \ + ((short*)(d))[2] = ((const short*)(s))[2]; } + + +static const struct ether_addr ether_bcast = {{255, 255, 255, 255, 255, 255}}; +static const struct ether_addr ether_null = {{0, 0, 0, 0, 0, 0}}; + +#define ETHER_ISBCAST(ea) ((((uint8 *)(ea))[0] & \ + ((uint8 *)(ea))[1] & \ + ((uint8 *)(ea))[2] & \ + ((uint8 *)(ea))[3] & \ + ((uint8 *)(ea))[4] & \ + ((uint8 *)(ea))[5]) == 0xff) +#define ETHER_ISNULLADDR(ea) ((((uint8 *)(ea))[0] | \ + ((uint8 *)(ea))[1] | \ + ((uint8 *)(ea))[2] | \ + ((uint8 *)(ea))[3] | \ + ((uint8 *)(ea))[4] | \ + ((uint8 *)(ea))[5]) == 0) + +#define ETHER_MOVE_HDR(d, s) \ +do { \ + struct ether_header t; \ + t = *(struct ether_header *)(s); \ + *(struct ether_header *)(d) = t; \ +} while (0) + + +#include + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/proto/p2p.h b/drivers/net/wireless/bcmdhd/include/proto/p2p.h new file mode 100644 index 00000000..1bfe73bc --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/p2p.h @@ -0,0 +1,564 @@ +/* + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * Fundamental types and constants relating to WFA P2P (aka WiFi Direct) + * + * $Id: p2p.h 326276 2012-04-06 23:16:42Z $ + */ + +#ifndef _P2P_H_ +#define _P2P_H_ + +#ifndef _TYPEDEFS_H_ +#include +#endif +#include +#include + + +#include + + + +#define P2P_OUI WFA_OUI +#define P2P_VER WFA_OUI_TYPE_P2P + +#define P2P_IE_ID 0xdd + + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_ie { + uint8 id; + uint8 len; + uint8 OUI[3]; + uint8 oui_type; + uint8 subelts[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_ie wifi_p2p_ie_t; + +#define P2P_IE_FIXED_LEN 6 + +#define P2P_ATTR_ID_OFF 0 +#define P2P_ATTR_LEN_OFF 1 +#define P2P_ATTR_DATA_OFF 3 + +#define P2P_ATTR_ID_LEN 1 +#define P2P_ATTR_LEN_LEN 2 +#define P2P_ATTR_HDR_LEN 3 + + +#define P2P_SEID_STATUS 0 +#define P2P_SEID_MINOR_RC 1 +#define P2P_SEID_P2P_INFO 2 +#define P2P_SEID_DEV_ID 3 +#define P2P_SEID_INTENT 4 +#define P2P_SEID_CFG_TIMEOUT 5 +#define P2P_SEID_CHANNEL 6 +#define P2P_SEID_GRP_BSSID 7 +#define P2P_SEID_XT_TIMING 8 +#define P2P_SEID_INTINTADDR 9 +#define P2P_SEID_P2P_MGBTY 10 +#define P2P_SEID_CHAN_LIST 11 +#define P2P_SEID_ABSENCE 12 +#define P2P_SEID_DEV_INFO 13 +#define P2P_SEID_GROUP_INFO 14 +#define P2P_SEID_GROUP_ID 15 +#define P2P_SEID_P2P_IF 16 +#define P2P_SEID_OP_CHANNEL 17 +#define P2P_SEID_INVITE_FLAGS 18 +#define P2P_SEID_VNDR 221 + +#define P2P_SE_VS_ID_SERVICES 0x1b + + + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_info_se_s { + uint8 eltId; + uint8 len[2]; + uint8 dev; + uint8 group; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_info_se_s wifi_p2p_info_se_t; + + +#define P2P_CAPSE_DEV_SERVICE_DIS 0x1 +#define P2P_CAPSE_DEV_CLIENT_DIS 0x2 +#define P2P_CAPSE_DEV_CONCURRENT 0x4 +#define P2P_CAPSE_DEV_INFRA_MAN 0x8 +#define P2P_CAPSE_DEV_LIMIT 0x10 +#define P2P_CAPSE_INVITE_PROC 0x20 + + +#define P2P_CAPSE_GRP_OWNER 0x1 +#define P2P_CAPSE_PERSIST_GRP 0x2 +#define P2P_CAPSE_GRP_LIMIT 0x4 +#define P2P_CAPSE_GRP_INTRA_BSS 0x8 +#define P2P_CAPSE_GRP_X_CONNECT 0x10 +#define P2P_CAPSE_GRP_PERSISTENT 0x20 +#define P2P_CAPSE_GRP_FORMATION 0x40 + + + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_intent_se_s { + uint8 eltId; + uint8 len[2]; + uint8 intent; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_intent_se_s wifi_p2p_intent_se_t; + + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_cfg_tmo_se_s { + uint8 eltId; + uint8 len[2]; + uint8 go_tmo; + uint8 client_tmo; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_cfg_tmo_se_s wifi_p2p_cfg_tmo_se_t; + + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_listen_channel_se_s { + uint8 eltId; + uint8 len[2]; + uint8 country[3]; + uint8 op_class; + uint8 channel; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_listen_channel_se_s wifi_p2p_listen_channel_se_t; + + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_grp_bssid_se_s { + uint8 eltId; + uint8 len[2]; + uint8 mac[6]; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_grp_bssid_se_s wifi_p2p_grp_bssid_se_t; + + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_grp_id_se_s { + uint8 eltId; + uint8 len[2]; + uint8 mac[6]; + uint8 ssid[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_grp_id_se_s wifi_p2p_grp_id_se_t; + + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_intf_se_s { + uint8 eltId; + uint8 len[2]; + uint8 mac[6]; + uint8 ifaddrs; + uint8 ifaddr[1][6]; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_intf_se_s wifi_p2p_intf_se_t; + + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_status_se_s { + uint8 eltId; + uint8 len[2]; + uint8 status; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_status_se_s wifi_p2p_status_se_t; + + +#define P2P_STATSE_SUCCESS 0 + +#define P2P_STATSE_FAIL_INFO_CURR_UNAVAIL 1 + +#define P2P_STATSE_PASSED_UP P2P_STATSE_FAIL_INFO_CURR_UNAVAIL + +#define P2P_STATSE_FAIL_INCOMPAT_PARAMS 2 + +#define P2P_STATSE_FAIL_LIMIT_REACHED 3 + +#define P2P_STATSE_FAIL_INVALID_PARAMS 4 + +#define P2P_STATSE_FAIL_UNABLE_TO_ACCOM 5 + +#define P2P_STATSE_FAIL_PROTO_ERROR 6 + +#define P2P_STATSE_FAIL_NO_COMMON_CHAN 7 + +#define P2P_STATSE_FAIL_UNKNOWN_GROUP 8 + +#define P2P_STATSE_FAIL_INTENT 9 + +#define P2P_STATSE_FAIL_INCOMPAT_PROVIS 10 + +#define P2P_STATSE_FAIL_USER_REJECT 11 + + + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_ext_se_s { + uint8 eltId; + uint8 len[2]; + uint8 avail[2]; + uint8 interval[2]; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_ext_se_s wifi_p2p_ext_se_t; + +#define P2P_EXT_MIN 10 + + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_intintad_se_s { + uint8 eltId; + uint8 len[2]; + uint8 mac[6]; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_intintad_se_s wifi_p2p_intintad_se_t; + + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_channel_se_s { + uint8 eltId; + uint8 len[2]; + uint8 band; + uint8 channel; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_channel_se_s wifi_p2p_channel_se_t; + + + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_chanlist_entry_s { + uint8 band; + uint8 num_channels; + uint8 channels[WL_NUMCHANNELS]; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_chanlist_entry_s wifi_p2p_chanlist_entry_t; +#define WIFI_P2P_CHANLIST_SE_MAX_ENTRIES 2 + + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_chanlist_se_s { + uint8 eltId; + uint8 len[2]; + uint8 country[3]; + uint8 num_entries; + wifi_p2p_chanlist_entry_t entries[WIFI_P2P_CHANLIST_SE_MAX_ENTRIES]; + +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_chanlist_se_s wifi_p2p_chanlist_se_t; + + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_pri_devtype_s { + uint16 cat_id; + uint8 OUI[3]; + uint8 oui_type; + uint16 sub_cat_id; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_pri_devtype_s wifi_p2p_pri_devtype_t; + + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_devinfo_se_s { + uint8 eltId; + uint8 len[2]; + uint8 mac[6]; + uint16 wps_cfg_meths; + uint8 pri_devtype[8]; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_devinfo_se_s wifi_p2p_devinfo_se_t; + +#define P2P_DEV_TYPE_LEN 8 + + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_cid_fixed_s { + uint8 len; + uint8 devaddr[ETHER_ADDR_LEN]; + uint8 ifaddr[ETHER_ADDR_LEN]; + uint8 devcap; + uint8 cfg_meths[2]; + uint8 pridt[P2P_DEV_TYPE_LEN]; + uint8 secdts; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_cid_fixed_s wifi_p2p_cid_fixed_t; + + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_devid_se_s { + uint8 eltId; + uint8 len[2]; + struct ether_addr addr; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_devid_se_s wifi_p2p_devid_se_t; + + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_mgbt_se_s { + uint8 eltId; + uint8 len[2]; + uint8 mg_bitmap; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_mgbt_se_s wifi_p2p_mgbt_se_t; + +#define P2P_MGBTSE_P2PDEVMGMT_FLAG 0x1 + + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_grpinfo_se_s { + uint8 eltId; + uint8 len[2]; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_grpinfo_se_s wifi_p2p_grpinfo_se_t; + + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_op_channel_se_s { + uint8 eltId; + uint8 len[2]; + uint8 country[3]; + uint8 op_class; + uint8 channel; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_op_channel_se_s wifi_p2p_op_channel_se_t; + + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_invite_flags_se_s { + uint8 eltId; + uint8 len[2]; + uint8 flags; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_invite_flags_se_s wifi_p2p_invite_flags_se_t; + + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_action_frame { + uint8 category; + uint8 OUI[3]; + uint8 type; + uint8 subtype; + uint8 dialog_token; + uint8 elts[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_action_frame wifi_p2p_action_frame_t; +#define P2P_AF_CATEGORY 0x7f + +#define P2P_AF_FIXED_LEN 7 + + +#define P2P_AF_NOTICE_OF_ABSENCE 0 +#define P2P_AF_PRESENCE_REQ 1 +#define P2P_AF_PRESENCE_RSP 2 +#define P2P_AF_GO_DISC_REQ 3 + + + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_pub_act_frame { + uint8 category; + uint8 action; + uint8 oui[3]; + uint8 oui_type; + uint8 subtype; + uint8 dialog_token; + uint8 elts[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_pub_act_frame wifi_p2p_pub_act_frame_t; +#define P2P_PUB_AF_FIXED_LEN 8 +#define P2P_PUB_AF_CATEGORY 0x04 +#define P2P_PUB_AF_ACTION 0x09 + + +#define P2P_PAF_GON_REQ 0 +#define P2P_PAF_GON_RSP 1 +#define P2P_PAF_GON_CONF 2 +#define P2P_PAF_INVITE_REQ 3 +#define P2P_PAF_INVITE_RSP 4 +#define P2P_PAF_DEVDIS_REQ 5 +#define P2P_PAF_DEVDIS_RSP 6 +#define P2P_PAF_PROVDIS_REQ 7 +#define P2P_PAF_PROVDIS_RSP 8 +#define P2P_PAF_SUBTYPE_INVALID 255 /* Invalid Subtype */ + +#define P2P_TYPE_MNREQ P2P_PAF_GON_REQ +#define P2P_TYPE_MNRSP P2P_PAF_GON_RSP +#define P2P_TYPE_MNCONF P2P_PAF_GON_CONF + + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_noa_desc { + uint8 cnt_type; + uint32 duration; + uint32 interval; + uint32 start; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_noa_desc wifi_p2p_noa_desc_t; + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_noa_se { + uint8 eltId; + uint8 len[2]; + uint8 index; + uint8 ops_ctw_parms; + wifi_p2p_noa_desc_t desc[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_noa_se wifi_p2p_noa_se_t; + +#define P2P_NOA_SE_FIXED_LEN 5 + + +#define P2P_NOA_DESC_CNT_RESERVED 0 +#define P2P_NOA_DESC_CNT_REPEAT 255 +#define P2P_NOA_DESC_TYPE_PREFERRED 1 +#define P2P_NOA_DESC_TYPE_ACCEPTABLE 2 + + +#define P2P_NOA_CTW_MASK 0x7f +#define P2P_NOA_OPS_MASK 0x80 +#define P2P_NOA_OPS_SHIFT 7 + +#define P2P_CTW_MIN 10 + + +#define P2PSD_ACTION_CATEGORY 0x04 + +#define P2PSD_ACTION_ID_GAS_IREQ 0x0a + +#define P2PSD_ACTION_ID_GAS_IRESP 0x0b + +#define P2PSD_ACTION_ID_GAS_CREQ 0x0c + +#define P2PSD_ACTION_ID_GAS_CRESP 0x0d + +#define P2PSD_AD_EID 0x6c + +#define P2PSD_ADP_TUPLE_QLMT_PAMEBI 0x00 + +#define P2PSD_ADP_PROTO_ID 0x00 + +#define P2PSD_GAS_OUI P2P_OUI + +#define P2PSD_GAS_OUI_SUBTYPE P2P_VER + +#define P2PSD_GAS_NQP_INFOID 0xDDDD + +#define P2PSD_GAS_COMEBACKDEALY 0x00 + + + +typedef enum p2psd_svc_protype { + SVC_RPOTYPE_ALL = 0, + SVC_RPOTYPE_BONJOUR = 1, + SVC_RPOTYPE_UPNP = 2, + SVC_RPOTYPE_WSD = 3, + SVC_RPOTYPE_VENDOR = 255 +} p2psd_svc_protype_t; + + +typedef enum { + P2PSD_RESP_STATUS_SUCCESS = 0, + P2PSD_RESP_STATUS_PROTYPE_NA = 1, + P2PSD_RESP_STATUS_DATA_NA = 2, + P2PSD_RESP_STATUS_BAD_REQUEST = 3 +} p2psd_resp_status_t; + + +BWL_PRE_PACKED_STRUCT struct wifi_p2psd_adp_tpl { + uint8 llm_pamebi; + uint8 adp_id; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2psd_adp_tpl wifi_p2psd_adp_tpl_t; + + +BWL_PRE_PACKED_STRUCT struct wifi_p2psd_adp_ie { + uint8 id; + uint8 len; + wifi_p2psd_adp_tpl_t adp_tpl; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2psd_adp_ie wifi_p2psd_adp_ie_t; + + +BWL_PRE_PACKED_STRUCT struct wifi_p2psd_nqp_query_vsc { + uint8 oui_subtype; + uint16 svc_updi; + uint8 svc_tlvs[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2psd_nqp_query_vsc wifi_p2psd_nqp_query_vsc_t; + + +BWL_PRE_PACKED_STRUCT struct wifi_p2psd_qreq_tlv { + uint16 len; + uint8 svc_prot; + uint8 svc_tscid; + uint8 query_data[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2psd_qreq_tlv wifi_p2psd_qreq_tlv_t; + + +BWL_PRE_PACKED_STRUCT struct wifi_p2psd_qreq_frame { + uint16 info_id; + uint16 len; + uint8 oui[3]; + uint8 qreq_vsc[1]; + +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2psd_qreq_frame wifi_p2psd_qreq_frame_t; + + +BWL_PRE_PACKED_STRUCT struct wifi_p2psd_gas_ireq_frame { + wifi_p2psd_adp_ie_t adp_ie; + uint16 qreq_len; + uint8 qreq_frm[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2psd_gas_ireq_frame wifi_p2psd_gas_ireq_frame_t; + + +BWL_PRE_PACKED_STRUCT struct wifi_p2psd_qresp_tlv { + uint16 len; + uint8 svc_prot; + uint8 svc_tscid; + uint8 status; + uint8 query_data[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2psd_qresp_tlv wifi_p2psd_qresp_tlv_t; + + +BWL_PRE_PACKED_STRUCT struct wifi_p2psd_qresp_frame { + uint16 info_id; + uint16 len; + uint8 oui[3]; + uint8 qresp_vsc[1]; + +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2psd_qresp_frame wifi_p2psd_qresp_frame_t; + + +BWL_PRE_PACKED_STRUCT struct wifi_p2psd_gas_iresp_frame { + uint16 status; + uint16 cb_delay; + wifi_p2psd_adp_ie_t adp_ie; + uint16 qresp_len; + uint8 qresp_frm[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2psd_gas_iresp_frame wifi_p2psd_gas_iresp_frame_t; + + +BWL_PRE_PACKED_STRUCT struct wifi_p2psd_gas_cresp_frame { + uint16 status; + uint8 fragment_id; + uint16 cb_delay; + wifi_p2psd_adp_ie_t adp_ie; + uint16 qresp_len; + uint8 qresp_frm[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2psd_gas_cresp_frame wifi_p2psd_gas_cresp_frame_t; + + +BWL_PRE_PACKED_STRUCT struct wifi_p2psd_gas_pub_act_frame { + uint8 category; + uint8 action; + uint8 dialog_token; + uint8 query_data[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2psd_gas_pub_act_frame wifi_p2psd_gas_pub_act_frame_t; + + +#include + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/proto/sdspi.h b/drivers/net/wireless/bcmdhd/include/proto/sdspi.h new file mode 100644 index 00000000..a4900edd --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/sdspi.h @@ -0,0 +1,75 @@ +/* + * SD-SPI Protocol Standard + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: sdspi.h 241182 2011-02-17 21:50:03Z $ + */ +#ifndef _SD_SPI_H +#define _SD_SPI_H + +#define SPI_START_M BITFIELD_MASK(1) /* Bit [31] - Start Bit */ +#define SPI_START_S 31 +#define SPI_DIR_M BITFIELD_MASK(1) /* Bit [30] - Direction */ +#define SPI_DIR_S 30 +#define SPI_CMD_INDEX_M BITFIELD_MASK(6) /* Bits [29:24] - Command number */ +#define SPI_CMD_INDEX_S 24 +#define SPI_RW_M BITFIELD_MASK(1) /* Bit [23] - Read=0, Write=1 */ +#define SPI_RW_S 23 +#define SPI_FUNC_M BITFIELD_MASK(3) /* Bits [22:20] - Function Number */ +#define SPI_FUNC_S 20 +#define SPI_RAW_M BITFIELD_MASK(1) /* Bit [19] - Read After Wr */ +#define SPI_RAW_S 19 +#define SPI_STUFF_M BITFIELD_MASK(1) /* Bit [18] - Stuff bit */ +#define SPI_STUFF_S 18 +#define SPI_BLKMODE_M BITFIELD_MASK(1) /* Bit [19] - Blockmode 1=blk */ +#define SPI_BLKMODE_S 19 +#define SPI_OPCODE_M BITFIELD_MASK(1) /* Bit [18] - OP Code */ +#define SPI_OPCODE_S 18 +#define SPI_ADDR_M BITFIELD_MASK(17) /* Bits [17:1] - Address */ +#define SPI_ADDR_S 1 +#define SPI_STUFF0_M BITFIELD_MASK(1) /* Bit [0] - Stuff bit */ +#define SPI_STUFF0_S 0 + +#define SPI_RSP_START_M BITFIELD_MASK(1) /* Bit [7] - Start Bit (always 0) */ +#define SPI_RSP_START_S 7 +#define SPI_RSP_PARAM_ERR_M BITFIELD_MASK(1) /* Bit [6] - Parameter Error */ +#define SPI_RSP_PARAM_ERR_S 6 +#define SPI_RSP_RFU5_M BITFIELD_MASK(1) /* Bit [5] - RFU (Always 0) */ +#define SPI_RSP_RFU5_S 5 +#define SPI_RSP_FUNC_ERR_M BITFIELD_MASK(1) /* Bit [4] - Function number error */ +#define SPI_RSP_FUNC_ERR_S 4 +#define SPI_RSP_CRC_ERR_M BITFIELD_MASK(1) /* Bit [3] - COM CRC Error */ +#define SPI_RSP_CRC_ERR_S 3 +#define SPI_RSP_ILL_CMD_M BITFIELD_MASK(1) /* Bit [2] - Illegal Command error */ +#define SPI_RSP_ILL_CMD_S 2 +#define SPI_RSP_RFU1_M BITFIELD_MASK(1) /* Bit [1] - RFU (Always 0) */ +#define SPI_RSP_RFU1_S 1 +#define SPI_RSP_IDLE_M BITFIELD_MASK(1) /* Bit [0] - In idle state */ +#define SPI_RSP_IDLE_S 0 + +/* SD-SPI Protocol Definitions */ +#define SDSPI_COMMAND_LEN 6 /* Number of bytes in an SD command */ +#define SDSPI_START_BLOCK 0xFE /* SD Start Block Token */ +#define SDSPI_IDLE_PAD 0xFF /* SD-SPI idle value for MOSI */ +#define SDSPI_START_BIT_MASK 0x80 + +#endif /* _SD_SPI_H */ diff --git a/drivers/net/wireless/bcmdhd/include/proto/vlan.h b/drivers/net/wireless/bcmdhd/include/proto/vlan.h new file mode 100644 index 00000000..9c94985c --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/vlan.h @@ -0,0 +1,69 @@ +/* + * 802.1Q VLAN protocol definitions + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: vlan.h 241182 2011-02-17 21:50:03Z $ + */ + +#ifndef _vlan_h_ +#define _vlan_h_ + +#ifndef _TYPEDEFS_H_ +#include +#endif + + +#include + +#define VLAN_VID_MASK 0xfff +#define VLAN_CFI_SHIFT 12 +#define VLAN_PRI_SHIFT 13 + +#define VLAN_PRI_MASK 7 + +#define VLAN_TAG_LEN 4 +#define VLAN_TAG_OFFSET (2 * ETHER_ADDR_LEN) + +#define VLAN_TPID 0x8100 + +struct ethervlan_header { + uint8 ether_dhost[ETHER_ADDR_LEN]; + uint8 ether_shost[ETHER_ADDR_LEN]; + uint16 vlan_type; + uint16 vlan_tag; + uint16 ether_type; +}; + +#define ETHERVLAN_HDR_LEN (ETHER_HDR_LEN + VLAN_TAG_LEN) + + + +#include + +#define ETHERVLAN_MOVE_HDR(d, s) \ +do { \ + struct ethervlan_header t; \ + t = *(struct ethervlan_header *)(s); \ + *(struct ethervlan_header *)(d) = t; \ +} while (0) + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/proto/wpa.h b/drivers/net/wireless/bcmdhd/include/proto/wpa.h new file mode 100644 index 00000000..5b640ec3 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/wpa.h @@ -0,0 +1,173 @@ +/* + * Fundamental types and constants relating to WPA + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wpa.h 261155 2011-05-23 23:51:32Z $ + */ + +#ifndef _proto_wpa_h_ +#define _proto_wpa_h_ + +#include +#include + + + +#include + + + + +#define DOT11_RC_INVALID_WPA_IE 13 +#define DOT11_RC_MIC_FAILURE 14 +#define DOT11_RC_4WH_TIMEOUT 15 +#define DOT11_RC_GTK_UPDATE_TIMEOUT 16 +#define DOT11_RC_WPA_IE_MISMATCH 17 +#define DOT11_RC_INVALID_MC_CIPHER 18 +#define DOT11_RC_INVALID_UC_CIPHER 19 +#define DOT11_RC_INVALID_AKMP 20 +#define DOT11_RC_BAD_WPA_VERSION 21 +#define DOT11_RC_INVALID_WPA_CAP 22 +#define DOT11_RC_8021X_AUTH_FAIL 23 + +#define WPA2_PMKID_LEN 16 + + +typedef BWL_PRE_PACKED_STRUCT struct +{ + uint8 tag; + uint8 length; + uint8 oui[3]; + uint8 oui_type; + BWL_PRE_PACKED_STRUCT struct { + uint8 low; + uint8 high; + } BWL_POST_PACKED_STRUCT version; +} BWL_POST_PACKED_STRUCT wpa_ie_fixed_t; +#define WPA_IE_OUITYPE_LEN 4 +#define WPA_IE_FIXED_LEN 8 +#define WPA_IE_TAG_FIXED_LEN 6 + +typedef BWL_PRE_PACKED_STRUCT struct { + uint8 tag; + uint8 length; + BWL_PRE_PACKED_STRUCT struct { + uint8 low; + uint8 high; + } BWL_POST_PACKED_STRUCT version; +} BWL_POST_PACKED_STRUCT wpa_rsn_ie_fixed_t; +#define WPA_RSN_IE_FIXED_LEN 4 +#define WPA_RSN_IE_TAG_FIXED_LEN 2 +typedef uint8 wpa_pmkid_t[WPA2_PMKID_LEN]; + + +typedef BWL_PRE_PACKED_STRUCT struct +{ + uint8 oui[3]; + uint8 type; +} BWL_POST_PACKED_STRUCT wpa_suite_t, wpa_suite_mcast_t; +#define WPA_SUITE_LEN 4 + + +typedef BWL_PRE_PACKED_STRUCT struct +{ + BWL_PRE_PACKED_STRUCT struct { + uint8 low; + uint8 high; + } BWL_POST_PACKED_STRUCT count; + wpa_suite_t list[1]; +} BWL_POST_PACKED_STRUCT wpa_suite_ucast_t, wpa_suite_auth_key_mgmt_t; +#define WPA_IE_SUITE_COUNT_LEN 2 +typedef BWL_PRE_PACKED_STRUCT struct +{ + BWL_PRE_PACKED_STRUCT struct { + uint8 low; + uint8 high; + } BWL_POST_PACKED_STRUCT count; + wpa_pmkid_t list[1]; +} BWL_POST_PACKED_STRUCT wpa_pmkid_list_t; + + +#define WPA_CIPHER_NONE 0 +#define WPA_CIPHER_WEP_40 1 +#define WPA_CIPHER_TKIP 2 +#define WPA_CIPHER_AES_OCB 3 +#define WPA_CIPHER_AES_CCM 4 +#define WPA_CIPHER_WEP_104 5 +#define WPA_CIPHER_BIP 6 +#define WPA_CIPHER_TPK 7 + + +#define IS_WPA_CIPHER(cipher) ((cipher) == WPA_CIPHER_NONE || \ + (cipher) == WPA_CIPHER_WEP_40 || \ + (cipher) == WPA_CIPHER_WEP_104 || \ + (cipher) == WPA_CIPHER_TKIP || \ + (cipher) == WPA_CIPHER_AES_OCB || \ + (cipher) == WPA_CIPHER_AES_CCM || \ + (cipher) == WPA_CIPHER_TPK) + + + +#define WPA_TKIP_CM_DETECT 60 +#define WPA_TKIP_CM_BLOCK 60 + + +#define RSN_CAP_LEN 2 + + +#define RSN_CAP_PREAUTH 0x0001 +#define RSN_CAP_NOPAIRWISE 0x0002 +#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C +#define RSN_CAP_PTK_REPLAY_CNTR_SHIFT 2 +#define RSN_CAP_GTK_REPLAY_CNTR_MASK 0x0030 +#define RSN_CAP_GTK_REPLAY_CNTR_SHIFT 4 +#define RSN_CAP_1_REPLAY_CNTR 0 +#define RSN_CAP_2_REPLAY_CNTRS 1 +#define RSN_CAP_4_REPLAY_CNTRS 2 +#define RSN_CAP_16_REPLAY_CNTRS 3 +#ifdef MFP +#define RSN_CAP_MFPR 0x0040 +#define RSN_CAP_MFPC 0x0080 +#endif + + +#define WPA_CAP_4_REPLAY_CNTRS RSN_CAP_4_REPLAY_CNTRS +#define WPA_CAP_16_REPLAY_CNTRS RSN_CAP_16_REPLAY_CNTRS +#define WPA_CAP_REPLAY_CNTR_SHIFT RSN_CAP_PTK_REPLAY_CNTR_SHIFT +#define WPA_CAP_REPLAY_CNTR_MASK RSN_CAP_PTK_REPLAY_CNTR_MASK + + +#define WPA_CAP_PEER_KEY_ENABLE (0x1 << 1) + + +#define WPA_CAP_LEN RSN_CAP_LEN +#define WPA_PMKID_CNT_LEN 2 + +#define WPA_CAP_WPA2_PREAUTH RSN_CAP_PREAUTH + +#define WPA2_PMKID_COUNT_LEN 2 + + + +#include + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/sbchipc.h b/drivers/net/wireless/bcmdhd/include/sbchipc.h new file mode 100644 index 00000000..a605d012 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/sbchipc.h @@ -0,0 +1,2380 @@ +/* + * SiliconBackplane Chipcommon core hardware definitions. + * + * The chipcommon core provides chip identification, SB control, + * JTAG, 0/1/2 UARTs, clock frequency control, a watchdog interrupt timer, + * GPIO interface, extbus, and support for serial and parallel flashes. + * + * $Id: sbchipc.h 347614 2012-07-27 10:24:51Z $ + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + */ + +#ifndef _SBCHIPC_H +#define _SBCHIPC_H + +#ifndef _LANGUAGE_ASSEMBLY + + +#ifndef PAD +#define _PADLINE(line) pad ## line +#define _XSTR(line) _PADLINE(line) +#define PAD _XSTR(__LINE__) +#endif + +typedef struct eci_prerev35 { + uint32 eci_output; + uint32 eci_control; + uint32 eci_inputlo; + uint32 eci_inputmi; + uint32 eci_inputhi; + uint32 eci_inputintpolaritylo; + uint32 eci_inputintpolaritymi; + uint32 eci_inputintpolarityhi; + uint32 eci_intmasklo; + uint32 eci_intmaskmi; + uint32 eci_intmaskhi; + uint32 eci_eventlo; + uint32 eci_eventmi; + uint32 eci_eventhi; + uint32 eci_eventmasklo; + uint32 eci_eventmaskmi; + uint32 eci_eventmaskhi; + uint32 PAD[3]; +} eci_prerev35_t; + +typedef struct eci_rev35 { + uint32 eci_outputlo; + uint32 eci_outputhi; + uint32 eci_controllo; + uint32 eci_controlhi; + uint32 eci_inputlo; + uint32 eci_inputhi; + uint32 eci_inputintpolaritylo; + uint32 eci_inputintpolarityhi; + uint32 eci_intmasklo; + uint32 eci_intmaskhi; + uint32 eci_eventlo; + uint32 eci_eventhi; + uint32 eci_eventmasklo; + uint32 eci_eventmaskhi; + uint32 eci_auxtx; + uint32 eci_auxrx; + uint32 eci_datatag; + uint32 eci_uartescvalue; + uint32 eci_autobaudctr; + uint32 eci_uartfifolevel; +} eci_rev35_t; + +typedef struct flash_config { + uint32 PAD[19]; + + uint32 flashstrconfig; +} flash_config_t; + +typedef volatile struct { + uint32 chipid; + uint32 capabilities; + uint32 corecontrol; + uint32 bist; + + + uint32 otpstatus; + uint32 otpcontrol; + uint32 otpprog; + uint32 otplayout; + + + uint32 intstatus; + uint32 intmask; + + + uint32 chipcontrol; + uint32 chipstatus; + + + uint32 jtagcmd; + uint32 jtagir; + uint32 jtagdr; + uint32 jtagctrl; + + + uint32 flashcontrol; + uint32 flashaddress; + uint32 flashdata; + uint32 otplayoutextension; + + + uint32 broadcastaddress; + uint32 broadcastdata; + + + uint32 gpiopullup; + uint32 gpiopulldown; + uint32 gpioin; + uint32 gpioout; + uint32 gpioouten; + uint32 gpiocontrol; + uint32 gpiointpolarity; + uint32 gpiointmask; + + + uint32 gpioevent; + uint32 gpioeventintmask; + + + uint32 watchdog; + + + uint32 gpioeventintpolarity; + + + uint32 gpiotimerval; + uint32 gpiotimeroutmask; + + + uint32 clockcontrol_n; + uint32 clockcontrol_sb; + uint32 clockcontrol_pci; + uint32 clockcontrol_m2; + uint32 clockcontrol_m3; + uint32 clkdiv; + uint32 gpiodebugsel; + uint32 capabilities_ext; + + + uint32 pll_on_delay; + uint32 fref_sel_delay; + uint32 slow_clk_ctl; + uint32 PAD; + + + uint32 system_clk_ctl; + uint32 clkstatestretch; + uint32 PAD[2]; + + + uint32 bp_addrlow; + uint32 bp_addrhigh; + uint32 bp_data; + uint32 PAD; + uint32 bp_indaccess; + + uint32 gsioctrl; + uint32 gsioaddress; + uint32 gsiodata; + + + uint32 clkdiv2; + + uint32 otpcontrol1; + uint32 fabid; + + + uint32 eromptr; + + + uint32 pcmcia_config; + uint32 pcmcia_memwait; + uint32 pcmcia_attrwait; + uint32 pcmcia_iowait; + uint32 ide_config; + uint32 ide_memwait; + uint32 ide_attrwait; + uint32 ide_iowait; + uint32 prog_config; + uint32 prog_waitcount; + uint32 flash_config; + uint32 flash_waitcount; + uint32 SECI_config; + uint32 SECI_status; + uint32 SECI_statusmask; + uint32 SECI_rxnibchanged; + + uint32 PAD[20]; + + + uint32 sromcontrol; + uint32 sromaddress; + uint32 sromdata; + uint32 PAD[1]; + + uint32 nflashctrl; + uint32 nflashconf; + uint32 nflashcoladdr; + uint32 nflashrowaddr; + uint32 nflashdata; + uint32 nflashwaitcnt0; + uint32 PAD[2]; + + uint32 seci_uart_data; + uint32 seci_uart_bauddiv; + uint32 seci_uart_fcr; + uint32 seci_uart_lcr; + uint32 seci_uart_mcr; + uint32 seci_uart_lsr; + uint32 seci_uart_msr; + uint32 seci_uart_baudadj; + + uint32 clk_ctl_st; + uint32 hw_war; + uint32 PAD[70]; + + + uint8 uart0data; + uint8 uart0imr; + uint8 uart0fcr; + uint8 uart0lcr; + uint8 uart0mcr; + uint8 uart0lsr; + uint8 uart0msr; + uint8 uart0scratch; + uint8 PAD[248]; + + uint8 uart1data; + uint8 uart1imr; + uint8 uart1fcr; + uint8 uart1lcr; + uint8 uart1mcr; + uint8 uart1lsr; + uint8 uart1msr; + uint8 uart1scratch; + uint32 PAD[126]; + + + + uint32 pmucontrol; + uint32 pmucapabilities; + uint32 pmustatus; + uint32 res_state; + uint32 res_pending; + uint32 pmutimer; + uint32 min_res_mask; + uint32 max_res_mask; + uint32 res_table_sel; + uint32 res_dep_mask; + uint32 res_updn_timer; + uint32 res_timer; + uint32 clkstretch; + uint32 pmuwatchdog; + uint32 gpiosel; + uint32 gpioenable; + uint32 res_req_timer_sel; + uint32 res_req_timer; + uint32 res_req_mask; + uint32 PAD; + uint32 chipcontrol_addr; + uint32 chipcontrol_data; + uint32 regcontrol_addr; + uint32 regcontrol_data; + uint32 pllcontrol_addr; + uint32 pllcontrol_data; + uint32 pmustrapopt; + uint32 pmu_xtalfreq; + uint32 retention_ctl; + uint32 PAD[3]; + uint32 retention_grpidx; + uint32 retention_grpctl; + uint32 PAD[94]; + uint16 sromotp[512]; +#ifdef NFLASH_SUPPORT + + uint32 nand_revision; + uint32 nand_cmd_start; + uint32 nand_cmd_addr_x; + uint32 nand_cmd_addr; + uint32 nand_cmd_end_addr; + uint32 nand_cs_nand_select; + uint32 nand_cs_nand_xor; + uint32 PAD; + uint32 nand_spare_rd0; + uint32 nand_spare_rd4; + uint32 nand_spare_rd8; + uint32 nand_spare_rd12; + uint32 nand_spare_wr0; + uint32 nand_spare_wr4; + uint32 nand_spare_wr8; + uint32 nand_spare_wr12; + uint32 nand_acc_control; + uint32 PAD; + uint32 nand_config; + uint32 PAD; + uint32 nand_timing_1; + uint32 nand_timing_2; + uint32 nand_semaphore; + uint32 PAD; + uint32 nand_devid; + uint32 nand_devid_x; + uint32 nand_block_lock_status; + uint32 nand_intfc_status; + uint32 nand_ecc_corr_addr_x; + uint32 nand_ecc_corr_addr; + uint32 nand_ecc_unc_addr_x; + uint32 nand_ecc_unc_addr; + uint32 nand_read_error_count; + uint32 nand_corr_stat_threshold; + uint32 PAD[2]; + uint32 nand_read_addr_x; + uint32 nand_read_addr; + uint32 nand_page_program_addr_x; + uint32 nand_page_program_addr; + uint32 nand_copy_back_addr_x; + uint32 nand_copy_back_addr; + uint32 nand_block_erase_addr_x; + uint32 nand_block_erase_addr; + uint32 nand_inv_read_addr_x; + uint32 nand_inv_read_addr; + uint32 PAD[2]; + uint32 nand_blk_wr_protect; + uint32 PAD[3]; + uint32 nand_acc_control_cs1; + uint32 nand_config_cs1; + uint32 nand_timing_1_cs1; + uint32 nand_timing_2_cs1; + uint32 PAD[20]; + uint32 nand_spare_rd16; + uint32 nand_spare_rd20; + uint32 nand_spare_rd24; + uint32 nand_spare_rd28; + uint32 nand_cache_addr; + uint32 nand_cache_data; + uint32 nand_ctrl_config; + uint32 nand_ctrl_status; +#endif + uint32 gci_corecaps0; + uint32 gci_corecaps1; + uint32 gci_corecaps2; + uint32 gci_corectrl; + uint32 gci_corestat; + uint32 PAD[11]; + uint32 gci_indirect_addr; + uint32 PAD[111]; + uint32 gci_chipctrl; +} chipcregs_t; + +#endif + + +#define CC_CHIPID 0 +#define CC_CAPABILITIES 4 +#define CC_CHIPST 0x2c +#define CC_EROMPTR 0xfc + +#define CC_OTPST 0x10 +#define CC_JTAGCMD 0x30 +#define CC_JTAGIR 0x34 +#define CC_JTAGDR 0x38 +#define CC_JTAGCTRL 0x3c +#define CC_GPIOPU 0x58 +#define CC_GPIOPD 0x5c +#define CC_GPIOIN 0x60 +#define CC_GPIOOUT 0x64 +#define CC_GPIOOUTEN 0x68 +#define CC_GPIOCTRL 0x6c +#define CC_GPIOPOL 0x70 +#define CC_GPIOINTM 0x74 +#define CC_WATCHDOG 0x80 +#define CC_CLKC_N 0x90 +#define CC_CLKC_M0 0x94 +#define CC_CLKC_M1 0x98 +#define CC_CLKC_M2 0x9c +#define CC_CLKC_M3 0xa0 +#define CC_CLKDIV 0xa4 +#define CC_SYS_CLK_CTL 0xc0 +#define CC_CLK_CTL_ST SI_CLK_CTL_ST +#define PMU_CTL 0x600 +#define PMU_CAP 0x604 +#define PMU_ST 0x608 +#define PMU_RES_STATE 0x60c +#define PMU_TIMER 0x614 +#define PMU_MIN_RES_MASK 0x618 +#define PMU_MAX_RES_MASK 0x61c +#define CC_CHIPCTL_ADDR 0x650 +#define CC_CHIPCTL_DATA 0x654 +#define PMU_REG_CONTROL_ADDR 0x658 +#define PMU_REG_CONTROL_DATA 0x65C +#define PMU_PLL_CONTROL_ADDR 0x660 +#define PMU_PLL_CONTROL_DATA 0x664 +#define CC_SROM_OTP 0x800 +#define CC_GCI_INDIRECT_ADDR_REG 0xC40 +#define CC_GCI_CHIP_CTRL_REG 0xE00 +#define CC_GCI_CC_OFFSET_2 2 +#define CC_GCI_CC_OFFSET_5 5 + +#ifdef NFLASH_SUPPORT + +#define CC_NAND_REVISION 0xC00 +#define CC_NAND_CMD_START 0xC04 +#define CC_NAND_CMD_ADDR 0xC0C +#define CC_NAND_SPARE_RD_0 0xC20 +#define CC_NAND_SPARE_RD_4 0xC24 +#define CC_NAND_SPARE_RD_8 0xC28 +#define CC_NAND_SPARE_RD_C 0xC2C +#define CC_NAND_CONFIG 0xC48 +#define CC_NAND_DEVID 0xC60 +#define CC_NAND_DEVID_EXT 0xC64 +#define CC_NAND_INTFC_STATUS 0xC6C +#endif + + +#define CID_ID_MASK 0x0000ffff +#define CID_REV_MASK 0x000f0000 +#define CID_REV_SHIFT 16 +#define CID_PKG_MASK 0x00f00000 +#define CID_PKG_SHIFT 20 +#define CID_CC_MASK 0x0f000000 +#define CID_CC_SHIFT 24 +#define CID_TYPE_MASK 0xf0000000 +#define CID_TYPE_SHIFT 28 + + +#define CC_CAP_UARTS_MASK 0x00000003 +#define CC_CAP_MIPSEB 0x00000004 +#define CC_CAP_UCLKSEL 0x00000018 +#define CC_CAP_UINTCLK 0x00000008 +#define CC_CAP_UARTGPIO 0x00000020 +#define CC_CAP_EXTBUS_MASK 0x000000c0 +#define CC_CAP_EXTBUS_NONE 0x00000000 +#define CC_CAP_EXTBUS_FULL 0x00000040 +#define CC_CAP_EXTBUS_PROG 0x00000080 +#define CC_CAP_FLASH_MASK 0x00000700 +#define CC_CAP_PLL_MASK 0x00038000 +#define CC_CAP_PWR_CTL 0x00040000 +#define CC_CAP_OTPSIZE 0x00380000 +#define CC_CAP_OTPSIZE_SHIFT 19 +#define CC_CAP_OTPSIZE_BASE 5 +#define CC_CAP_JTAGP 0x00400000 +#define CC_CAP_ROM 0x00800000 +#define CC_CAP_BKPLN64 0x08000000 +#define CC_CAP_PMU 0x10000000 +#define CC_CAP_ECI 0x20000000 +#define CC_CAP_SROM 0x40000000 +#define CC_CAP_NFLASH 0x80000000 + +#define CC_CAP2_SECI 0x00000001 +#define CC_CAP2_GSIO 0x00000002 + + +#define CC_CAP_EXT_SECI_PRESENT 0x00000001 + + +#define PLL_NONE 0x00000000 +#define PLL_TYPE1 0x00010000 +#define PLL_TYPE2 0x00020000 +#define PLL_TYPE3 0x00030000 +#define PLL_TYPE4 0x00008000 +#define PLL_TYPE5 0x00018000 +#define PLL_TYPE6 0x00028000 +#define PLL_TYPE7 0x00038000 + + +#define ILP_CLOCK 32000 + + +#define ALP_CLOCK 20000000 + + +#define HT_CLOCK 80000000 + + +#define CC_UARTCLKO 0x00000001 +#define CC_SE 0x00000002 +#define CC_ASYNCGPIO 0x00000004 +#define CC_UARTCLKEN 0x00000008 + + +#define CHIPCTRL_4321A0_DEFAULT 0x3a4 +#define CHIPCTRL_4321A1_DEFAULT 0x0a4 +#define CHIPCTRL_4321_PLL_DOWN 0x800000 + + +#define OTPS_OL_MASK 0x000000ff +#define OTPS_OL_MFG 0x00000001 +#define OTPS_OL_OR1 0x00000002 +#define OTPS_OL_OR2 0x00000004 +#define OTPS_OL_GU 0x00000008 +#define OTPS_GUP_MASK 0x00000f00 +#define OTPS_GUP_SHIFT 8 +#define OTPS_GUP_HW 0x00000100 +#define OTPS_GUP_SW 0x00000200 +#define OTPS_GUP_CI 0x00000400 +#define OTPS_GUP_FUSE 0x00000800 +#define OTPS_READY 0x00001000 +#define OTPS_RV(x) (1 << (16 + (x))) +#define OTPS_RV_MASK 0x0fff0000 +#define OTPS_PROGOK 0x40000000 + + +#define OTPC_PROGSEL 0x00000001 +#define OTPC_PCOUNT_MASK 0x0000000e +#define OTPC_PCOUNT_SHIFT 1 +#define OTPC_VSEL_MASK 0x000000f0 +#define OTPC_VSEL_SHIFT 4 +#define OTPC_TMM_MASK 0x00000700 +#define OTPC_TMM_SHIFT 8 +#define OTPC_ODM 0x00000800 +#define OTPC_PROGEN 0x80000000 + + +#define OTPC_40NM_PROGSEL_SHIFT 0 +#define OTPC_40NM_PCOUNT_SHIFT 1 +#define OTPC_40NM_PCOUNT_WR 0xA +#define OTPC_40NM_PCOUNT_V1X 0xB +#define OTPC_40NM_REGCSEL_SHIFT 5 +#define OTPC_40NM_REGCSEL_DEF 0x4 +#define OTPC_40NM_PROGIN_SHIFT 8 +#define OTPC_40NM_R2X_SHIFT 10 +#define OTPC_40NM_ODM_SHIFT 11 +#define OTPC_40NM_DF_SHIFT 15 +#define OTPC_40NM_VSEL_SHIFT 16 +#define OTPC_40NM_VSEL_WR 0xA +#define OTPC_40NM_VSEL_V1X 0xA +#define OTPC_40NM_VSEL_R1X 0x5 +#define OTPC_40NM_COFAIL_SHIFT 30 + +#define OTPC1_CPCSEL_SHIFT 0 +#define OTPC1_CPCSEL_DEF 6 +#define OTPC1_TM_SHIFT 8 +#define OTPC1_TM_WR 0x84 +#define OTPC1_TM_V1X 0x84 +#define OTPC1_TM_R1X 0x4 + + +#define OTPP_COL_MASK 0x000000ff +#define OTPP_COL_SHIFT 0 +#define OTPP_ROW_MASK 0x0000ff00 +#define OTPP_ROW_SHIFT 8 +#define OTPP_OC_MASK 0x0f000000 +#define OTPP_OC_SHIFT 24 +#define OTPP_READERR 0x10000000 +#define OTPP_VALUE_MASK 0x20000000 +#define OTPP_VALUE_SHIFT 29 +#define OTPP_START_BUSY 0x80000000 +#define OTPP_READ 0x40000000 + + +#define OTPL_HWRGN_OFF_MASK 0x00000FFF +#define OTPL_HWRGN_OFF_SHIFT 0 +#define OTPL_WRAP_REVID_MASK 0x00F80000 +#define OTPL_WRAP_REVID_SHIFT 19 +#define OTPL_WRAP_TYPE_MASK 0x00070000 +#define OTPL_WRAP_TYPE_SHIFT 16 +#define OTPL_WRAP_TYPE_65NM 0 +#define OTPL_WRAP_TYPE_40NM 1 + + +#define OTP_CISFORMAT_NEW 0x80000000 + + +#define OTPPOC_READ 0 +#define OTPPOC_BIT_PROG 1 +#define OTPPOC_VERIFY 3 +#define OTPPOC_INIT 4 +#define OTPPOC_SET 5 +#define OTPPOC_RESET 6 +#define OTPPOC_OCST 7 +#define OTPPOC_ROW_LOCK 8 +#define OTPPOC_PRESCN_TEST 9 + + +#define OTPPOC_READ_40NM 0 +#define OTPPOC_PROG_ENABLE_40NM 1 +#define OTPPOC_PROG_DISABLE_40NM 2 +#define OTPPOC_VERIFY_40NM 3 +#define OTPPOC_WORD_VERIFY_1_40NM 4 +#define OTPPOC_ROW_LOCK_40NM 5 +#define OTPPOC_STBY_40NM 6 +#define OTPPOC_WAKEUP_40NM 7 +#define OTPPOC_WORD_VERIFY_0_40NM 8 +#define OTPPOC_PRESCN_TEST_40NM 9 +#define OTPPOC_BIT_PROG_40NM 10 +#define OTPPOC_WORDPROG_40NM 11 +#define OTPPOC_BURNIN_40NM 12 +#define OTPPOC_AUTORELOAD_40NM 13 +#define OTPPOC_OVST_READ_40NM 14 +#define OTPPOC_OVST_PROG_40NM 15 + + +#define OTPLAYOUTEXT_FUSE_MASK 0x3FF + + + +#define JTAGM_CREV_OLD 10 +#define JTAGM_CREV_IRP 22 +#define JTAGM_CREV_RTI 28 + + +#define JCMD_START 0x80000000 +#define JCMD_BUSY 0x80000000 +#define JCMD_STATE_MASK 0x60000000 +#define JCMD_STATE_TLR 0x00000000 +#define JCMD_STATE_PIR 0x20000000 +#define JCMD_STATE_PDR 0x40000000 +#define JCMD_STATE_RTI 0x60000000 +#define JCMD0_ACC_MASK 0x0000f000 +#define JCMD0_ACC_IRDR 0x00000000 +#define JCMD0_ACC_DR 0x00001000 +#define JCMD0_ACC_IR 0x00002000 +#define JCMD0_ACC_RESET 0x00003000 +#define JCMD0_ACC_IRPDR 0x00004000 +#define JCMD0_ACC_PDR 0x00005000 +#define JCMD0_IRW_MASK 0x00000f00 +#define JCMD_ACC_MASK 0x000f0000 +#define JCMD_ACC_IRDR 0x00000000 +#define JCMD_ACC_DR 0x00010000 +#define JCMD_ACC_IR 0x00020000 +#define JCMD_ACC_RESET 0x00030000 +#define JCMD_ACC_IRPDR 0x00040000 +#define JCMD_ACC_PDR 0x00050000 +#define JCMD_ACC_PIR 0x00060000 +#define JCMD_ACC_IRDR_I 0x00070000 +#define JCMD_ACC_DR_I 0x00080000 +#define JCMD_IRW_MASK 0x00001f00 +#define JCMD_IRW_SHIFT 8 +#define JCMD_DRW_MASK 0x0000003f + + +#define JCTRL_FORCE_CLK 4 +#define JCTRL_EXT_EN 2 +#define JCTRL_EN 1 + + +#define CLKD_SFLASH 0x0f000000 +#define CLKD_SFLASH_SHIFT 24 +#define CLKD_OTP 0x000f0000 +#define CLKD_OTP_SHIFT 16 +#define CLKD_JTAG 0x00000f00 +#define CLKD_JTAG_SHIFT 8 +#define CLKD_UART 0x000000ff + +#define CLKD2_SROM 0x00000003 + + +#define CI_GPIO 0x00000001 +#define CI_EI 0x00000002 +#define CI_TEMP 0x00000004 +#define CI_SIRQ 0x00000008 +#define CI_ECI 0x00000010 +#define CI_PMU 0x00000020 +#define CI_UART 0x00000040 +#define CI_WDRESET 0x80000000 + + +#define SCC_SS_MASK 0x00000007 +#define SCC_SS_LPO 0x00000000 +#define SCC_SS_XTAL 0x00000001 +#define SCC_SS_PCI 0x00000002 +#define SCC_LF 0x00000200 +#define SCC_LP 0x00000400 +#define SCC_FS 0x00000800 +#define SCC_IP 0x00001000 +#define SCC_XC 0x00002000 +#define SCC_XP 0x00004000 +#define SCC_CD_MASK 0xffff0000 +#define SCC_CD_SHIFT 16 + + +#define SYCC_IE 0x00000001 +#define SYCC_AE 0x00000002 +#define SYCC_FP 0x00000004 +#define SYCC_AR 0x00000008 +#define SYCC_HR 0x00000010 +#define SYCC_CD_MASK 0xffff0000 +#define SYCC_CD_SHIFT 16 + + +#define BPIA_BYTEEN 0x0000000f +#define BPIA_SZ1 0x00000001 +#define BPIA_SZ2 0x00000003 +#define BPIA_SZ4 0x00000007 +#define BPIA_SZ8 0x0000000f +#define BPIA_WRITE 0x00000100 +#define BPIA_START 0x00000200 +#define BPIA_BUSY 0x00000200 +#define BPIA_ERROR 0x00000400 + + +#define CF_EN 0x00000001 +#define CF_EM_MASK 0x0000000e +#define CF_EM_SHIFT 1 +#define CF_EM_FLASH 0 +#define CF_EM_SYNC 2 +#define CF_EM_PCMCIA 4 +#define CF_DS 0x00000010 +#define CF_BS 0x00000020 +#define CF_CD_MASK 0x000000c0 +#define CF_CD_SHIFT 6 +#define CF_CD_DIV2 0x00000000 +#define CF_CD_DIV3 0x00000040 +#define CF_CD_DIV4 0x00000080 +#define CF_CE 0x00000100 +#define CF_SB 0x00000200 + + +#define PM_W0_MASK 0x0000003f +#define PM_W1_MASK 0x00001f00 +#define PM_W1_SHIFT 8 +#define PM_W2_MASK 0x001f0000 +#define PM_W2_SHIFT 16 +#define PM_W3_MASK 0x1f000000 +#define PM_W3_SHIFT 24 + + +#define PA_W0_MASK 0x0000003f +#define PA_W1_MASK 0x00001f00 +#define PA_W1_SHIFT 8 +#define PA_W2_MASK 0x001f0000 +#define PA_W2_SHIFT 16 +#define PA_W3_MASK 0x1f000000 +#define PA_W3_SHIFT 24 + + +#define PI_W0_MASK 0x0000003f +#define PI_W1_MASK 0x00001f00 +#define PI_W1_SHIFT 8 +#define PI_W2_MASK 0x001f0000 +#define PI_W2_SHIFT 16 +#define PI_W3_MASK 0x1f000000 +#define PI_W3_SHIFT 24 + + +#define PW_W0_MASK 0x0000001f +#define PW_W1_MASK 0x00001f00 +#define PW_W1_SHIFT 8 +#define PW_W2_MASK 0x001f0000 +#define PW_W2_SHIFT 16 +#define PW_W3_MASK 0x1f000000 +#define PW_W3_SHIFT 24 + +#define PW_W0 0x0000000c +#define PW_W1 0x00000a00 +#define PW_W2 0x00020000 +#define PW_W3 0x01000000 + + +#define FW_W0_MASK 0x0000003f +#define FW_W1_MASK 0x00001f00 +#define FW_W1_SHIFT 8 +#define FW_W2_MASK 0x001f0000 +#define FW_W2_SHIFT 16 +#define FW_W3_MASK 0x1f000000 +#define FW_W3_SHIFT 24 + + +#define SRC_START 0x80000000 +#define SRC_BUSY 0x80000000 +#define SRC_OPCODE 0x60000000 +#define SRC_OP_READ 0x00000000 +#define SRC_OP_WRITE 0x20000000 +#define SRC_OP_WRDIS 0x40000000 +#define SRC_OP_WREN 0x60000000 +#define SRC_OTPSEL 0x00000010 +#define SRC_LOCK 0x00000008 +#define SRC_SIZE_MASK 0x00000006 +#define SRC_SIZE_1K 0x00000000 +#define SRC_SIZE_4K 0x00000002 +#define SRC_SIZE_16K 0x00000004 +#define SRC_SIZE_SHIFT 1 +#define SRC_PRESENT 0x00000001 + + +#define PCTL_ILP_DIV_MASK 0xffff0000 +#define PCTL_ILP_DIV_SHIFT 16 +#define PCTL_PLL_PLLCTL_UPD 0x00000400 +#define PCTL_NOILP_ON_WAIT 0x00000200 +#define PCTL_HT_REQ_EN 0x00000100 +#define PCTL_ALP_REQ_EN 0x00000080 +#define PCTL_XTALFREQ_MASK 0x0000007c +#define PCTL_XTALFREQ_SHIFT 2 +#define PCTL_ILP_DIV_EN 0x00000002 +#define PCTL_LPO_SEL 0x00000001 + + +#define CSTRETCH_HT 0xffff0000 +#define CSTRETCH_ALP 0x0000ffff + + +#define GPIO_ONTIME_SHIFT 16 + + +#define CN_N1_MASK 0x3f +#define CN_N2_MASK 0x3f00 +#define CN_N2_SHIFT 8 +#define CN_PLLC_MASK 0xf0000 +#define CN_PLLC_SHIFT 16 + + +#define CC_M1_MASK 0x3f +#define CC_M2_MASK 0x3f00 +#define CC_M2_SHIFT 8 +#define CC_M3_MASK 0x3f0000 +#define CC_M3_SHIFT 16 +#define CC_MC_MASK 0x1f000000 +#define CC_MC_SHIFT 24 + + +#define CC_F6_2 0x02 +#define CC_F6_3 0x03 +#define CC_F6_4 0x05 +#define CC_F6_5 0x09 +#define CC_F6_6 0x11 +#define CC_F6_7 0x21 + +#define CC_F5_BIAS 5 + +#define CC_MC_BYPASS 0x08 +#define CC_MC_M1 0x04 +#define CC_MC_M1M2 0x02 +#define CC_MC_M1M2M3 0x01 +#define CC_MC_M1M3 0x11 + + +#define CC_T2_BIAS 2 +#define CC_T2M2_BIAS 3 + +#define CC_T2MC_M1BYP 1 +#define CC_T2MC_M2BYP 2 +#define CC_T2MC_M3BYP 4 + + +#define CC_T6_MMASK 1 +#define CC_T6_M0 120000000 +#define CC_T6_M1 100000000 +#define SB2MIPS_T6(sb) (2 * (sb)) + + +#define CC_CLOCK_BASE1 24000000 +#define CC_CLOCK_BASE2 12500000 + + +#define CLKC_5350_N 0x0311 +#define CLKC_5350_M 0x04020009 + + +#define FLASH_NONE 0x000 +#define SFLASH_ST 0x100 +#define SFLASH_AT 0x200 +#define NFLASH 0x300 +#define PFLASH 0x700 + + +#define CC_CFG_EN 0x0001 +#define CC_CFG_EM_MASK 0x000e +#define CC_CFG_EM_ASYNC 0x0000 +#define CC_CFG_EM_SYNC 0x0002 +#define CC_CFG_EM_PCMCIA 0x0004 +#define CC_CFG_EM_IDE 0x0006 +#define CC_CFG_DS 0x0010 +#define CC_CFG_CD_MASK 0x00e0 +#define CC_CFG_CE 0x0100 +#define CC_CFG_SB 0x0200 +#define CC_CFG_IS 0x0400 + + +#define CC_EB_BASE 0x1a000000 +#define CC_EB_PCMCIA_MEM 0x1a000000 +#define CC_EB_PCMCIA_IO 0x1a200000 +#define CC_EB_PCMCIA_CFG 0x1a400000 +#define CC_EB_IDE 0x1a800000 +#define CC_EB_PCMCIA1_MEM 0x1a800000 +#define CC_EB_PCMCIA1_IO 0x1aa00000 +#define CC_EB_PCMCIA1_CFG 0x1ac00000 +#define CC_EB_PROGIF 0x1b000000 + + + +#define SFLASH_OPCODE 0x000000ff +#define SFLASH_ACTION 0x00000700 +#define SFLASH_CS_ACTIVE 0x00001000 +#define SFLASH_START 0x80000000 +#define SFLASH_BUSY SFLASH_START + + +#define SFLASH_ACT_OPONLY 0x0000 +#define SFLASH_ACT_OP1D 0x0100 +#define SFLASH_ACT_OP3A 0x0200 +#define SFLASH_ACT_OP3A1D 0x0300 +#define SFLASH_ACT_OP3A4D 0x0400 +#define SFLASH_ACT_OP3A4X4D 0x0500 +#define SFLASH_ACT_OP3A1X4D 0x0700 + + +#define SFLASH_ST_WREN 0x0006 +#define SFLASH_ST_WRDIS 0x0004 +#define SFLASH_ST_RDSR 0x0105 +#define SFLASH_ST_WRSR 0x0101 +#define SFLASH_ST_READ 0x0303 +#define SFLASH_ST_PP 0x0302 +#define SFLASH_ST_SE 0x02d8 +#define SFLASH_ST_BE 0x00c7 +#define SFLASH_ST_DP 0x00b9 +#define SFLASH_ST_RES 0x03ab +#define SFLASH_ST_CSA 0x1000 +#define SFLASH_ST_SSE 0x0220 + +#define SFLASH_MXIC_RDID 0x0390 +#define SFLASH_MXIC_MFID 0xc2 + + +#define SFLASH_ST_WIP 0x01 +#define SFLASH_ST_WEL 0x02 +#define SFLASH_ST_BP_MASK 0x1c +#define SFLASH_ST_BP_SHIFT 2 +#define SFLASH_ST_SRWD 0x80 + + +#define SFLASH_AT_READ 0x07e8 +#define SFLASH_AT_PAGE_READ 0x07d2 +#define SFLASH_AT_BUF1_READ +#define SFLASH_AT_BUF2_READ +#define SFLASH_AT_STATUS 0x01d7 +#define SFLASH_AT_BUF1_WRITE 0x0384 +#define SFLASH_AT_BUF2_WRITE 0x0387 +#define SFLASH_AT_BUF1_ERASE_PROGRAM 0x0283 +#define SFLASH_AT_BUF2_ERASE_PROGRAM 0x0286 +#define SFLASH_AT_BUF1_PROGRAM 0x0288 +#define SFLASH_AT_BUF2_PROGRAM 0x0289 +#define SFLASH_AT_PAGE_ERASE 0x0281 +#define SFLASH_AT_BLOCK_ERASE 0x0250 +#define SFLASH_AT_BUF1_WRITE_ERASE_PROGRAM 0x0382 +#define SFLASH_AT_BUF2_WRITE_ERASE_PROGRAM 0x0385 +#define SFLASH_AT_BUF1_LOAD 0x0253 +#define SFLASH_AT_BUF2_LOAD 0x0255 +#define SFLASH_AT_BUF1_COMPARE 0x0260 +#define SFLASH_AT_BUF2_COMPARE 0x0261 +#define SFLASH_AT_BUF1_REPROGRAM 0x0258 +#define SFLASH_AT_BUF2_REPROGRAM 0x0259 + + +#define SFLASH_AT_READY 0x80 +#define SFLASH_AT_MISMATCH 0x40 +#define SFLASH_AT_ID_MASK 0x38 +#define SFLASH_AT_ID_SHIFT 3 + + +#define GSIO_START 0x80000000 +#define GSIO_BUSY GSIO_START + + + +#define UART_RX 0 +#define UART_TX 0 +#define UART_DLL 0 +#define UART_IER 1 +#define UART_DLM 1 +#define UART_IIR 2 +#define UART_FCR 2 +#define UART_LCR 3 +#define UART_MCR 4 +#define UART_LSR 5 +#define UART_MSR 6 +#define UART_SCR 7 +#define UART_LCR_DLAB 0x80 +#define UART_LCR_WLEN8 0x03 +#define UART_MCR_OUT2 0x08 +#define UART_MCR_LOOP 0x10 +#define UART_LSR_RX_FIFO 0x80 +#define UART_LSR_TDHR 0x40 +#define UART_LSR_THRE 0x20 +#define UART_LSR_BREAK 0x10 +#define UART_LSR_FRAMING 0x08 +#define UART_LSR_PARITY 0x04 +#define UART_LSR_OVERRUN 0x02 +#define UART_LSR_RXRDY 0x01 +#define UART_FCR_FIFO_ENABLE 1 + + +#define UART_IIR_FIFO_MASK 0xc0 +#define UART_IIR_INT_MASK 0xf +#define UART_IIR_MDM_CHG 0x0 +#define UART_IIR_NOINT 0x1 +#define UART_IIR_THRE 0x2 +#define UART_IIR_RCVD_DATA 0x4 +#define UART_IIR_RCVR_STATUS 0x6 +#define UART_IIR_CHAR_TIME 0xc + + +#define UART_IER_EDSSI 8 +#define UART_IER_ELSI 4 +#define UART_IER_ETBEI 2 +#define UART_IER_ERBFI 1 + + +#define PST_EXTLPOAVAIL 0x0100 +#define PST_WDRESET 0x0080 +#define PST_INTPEND 0x0040 +#define PST_SBCLKST 0x0030 +#define PST_SBCLKST_ILP 0x0010 +#define PST_SBCLKST_ALP 0x0020 +#define PST_SBCLKST_HT 0x0030 +#define PST_ALPAVAIL 0x0008 +#define PST_HTAVAIL 0x0004 +#define PST_RESINIT 0x0003 + + +#define PCAP_REV_MASK 0x000000ff +#define PCAP_RC_MASK 0x00001f00 +#define PCAP_RC_SHIFT 8 +#define PCAP_TC_MASK 0x0001e000 +#define PCAP_TC_SHIFT 13 +#define PCAP_PC_MASK 0x001e0000 +#define PCAP_PC_SHIFT 17 +#define PCAP_VC_MASK 0x01e00000 +#define PCAP_VC_SHIFT 21 +#define PCAP_CC_MASK 0x1e000000 +#define PCAP_CC_SHIFT 25 +#define PCAP5_PC_MASK 0x003e0000 +#define PCAP5_PC_SHIFT 17 +#define PCAP5_VC_MASK 0x07c00000 +#define PCAP5_VC_SHIFT 22 +#define PCAP5_CC_MASK 0xf8000000 +#define PCAP5_CC_SHIFT 27 + + + +#define PRRT_TIME_MASK 0x03ff +#define PRRT_INTEN 0x0400 +#define PRRT_REQ_ACTIVE 0x0800 +#define PRRT_ALP_REQ 0x1000 +#define PRRT_HT_REQ 0x2000 +#define PRRT_HQ_REQ 0x4000 + + +#define PMURES_BIT(bit) (1 << (bit)) + + +#define PMURES_MAX_RESNUM 30 + + +#define PMU_CHIPCTL0 0 + + +#define PMU_CC1_CLKREQ_TYPE_SHIFT 19 +#define PMU_CC1_CLKREQ_TYPE_MASK (1 << PMU_CC1_CLKREQ_TYPE_SHIFT) + +#define CLKREQ_TYPE_CONFIG_OPENDRAIN 0 +#define CLKREQ_TYPE_CONFIG_PUSHPULL 1 + + +#define PMU_CHIPCTL1 1 +#define PMU_CC1_RXC_DLL_BYPASS 0x00010000 + +#define PMU_CC1_IF_TYPE_MASK 0x00000030 +#define PMU_CC1_IF_TYPE_RMII 0x00000000 +#define PMU_CC1_IF_TYPE_MII 0x00000010 +#define PMU_CC1_IF_TYPE_RGMII 0x00000020 + +#define PMU_CC1_SW_TYPE_MASK 0x000000c0 +#define PMU_CC1_SW_TYPE_EPHY 0x00000000 +#define PMU_CC1_SW_TYPE_EPHYMII 0x00000040 +#define PMU_CC1_SW_TYPE_EPHYRMII 0x00000080 +#define PMU_CC1_SW_TYPE_RGMII 0x000000c0 + + +#define PMU_CHIPCTL2 2 + + +#define PMU_CHIPCTL3 3 + +#define PMU_CC3_ENABLE_SDIO_WAKEUP_SHIFT 19 +#define PMU_CC3_ENABLE_RF_SHIFT 22 +#define PMU_CC3_RF_DISABLE_IVALUE_SHIFT 23 + + + + + +#define PMU0_PLL0_PLLCTL0 0 +#define PMU0_PLL0_PC0_PDIV_MASK 1 +#define PMU0_PLL0_PC0_PDIV_FREQ 25000 +#define PMU0_PLL0_PC0_DIV_ARM_MASK 0x00000038 +#define PMU0_PLL0_PC0_DIV_ARM_SHIFT 3 +#define PMU0_PLL0_PC0_DIV_ARM_BASE 8 + + +#define PMU0_PLL0_PC0_DIV_ARM_110MHZ 0 +#define PMU0_PLL0_PC0_DIV_ARM_97_7MHZ 1 +#define PMU0_PLL0_PC0_DIV_ARM_88MHZ 2 +#define PMU0_PLL0_PC0_DIV_ARM_80MHZ 3 +#define PMU0_PLL0_PC0_DIV_ARM_73_3MHZ 4 +#define PMU0_PLL0_PC0_DIV_ARM_67_7MHZ 5 +#define PMU0_PLL0_PC0_DIV_ARM_62_9MHZ 6 +#define PMU0_PLL0_PC0_DIV_ARM_58_6MHZ 7 + + +#define PMU0_PLL0_PLLCTL1 1 +#define PMU0_PLL0_PC1_WILD_INT_MASK 0xf0000000 +#define PMU0_PLL0_PC1_WILD_INT_SHIFT 28 +#define PMU0_PLL0_PC1_WILD_FRAC_MASK 0x0fffff00 +#define PMU0_PLL0_PC1_WILD_FRAC_SHIFT 8 +#define PMU0_PLL0_PC1_STOP_MOD 0x00000040 + + +#define PMU0_PLL0_PLLCTL2 2 +#define PMU0_PLL0_PC2_WILD_INT_MASK 0xf +#define PMU0_PLL0_PC2_WILD_INT_SHIFT 4 + + + +#define PMU1_PLL0_PLLCTL0 0 +#define PMU1_PLL0_PC0_P1DIV_MASK 0x00f00000 +#define PMU1_PLL0_PC0_P1DIV_SHIFT 20 +#define PMU1_PLL0_PC0_P2DIV_MASK 0x0f000000 +#define PMU1_PLL0_PC0_P2DIV_SHIFT 24 + + +#define PMU1_PLL0_PLLCTL1 1 +#define PMU1_PLL0_PC1_M1DIV_MASK 0x000000ff +#define PMU1_PLL0_PC1_M1DIV_SHIFT 0 +#define PMU1_PLL0_PC1_M2DIV_MASK 0x0000ff00 +#define PMU1_PLL0_PC1_M2DIV_SHIFT 8 +#define PMU1_PLL0_PC1_M3DIV_MASK 0x00ff0000 +#define PMU1_PLL0_PC1_M3DIV_SHIFT 16 +#define PMU1_PLL0_PC1_M4DIV_MASK 0xff000000 +#define PMU1_PLL0_PC1_M4DIV_SHIFT 24 +#define PMU1_PLL0_PC1_M4DIV_BY_9 9 +#define PMU1_PLL0_PC1_M4DIV_BY_18 0x12 +#define PMU1_PLL0_PC1_M4DIV_BY_36 0x24 + +#define DOT11MAC_880MHZ_CLK_DIVISOR_SHIFT 8 +#define DOT11MAC_880MHZ_CLK_DIVISOR_MASK (0xFF << DOT11MAC_880MHZ_CLK_DIVISOR_SHIFT) +#define DOT11MAC_880MHZ_CLK_DIVISOR_VAL (0xE << DOT11MAC_880MHZ_CLK_DIVISOR_SHIFT) + + +#define PMU1_PLL0_PLLCTL2 2 +#define PMU1_PLL0_PC2_M5DIV_MASK 0x000000ff +#define PMU1_PLL0_PC2_M5DIV_SHIFT 0 +#define PMU1_PLL0_PC2_M5DIV_BY_12 0xc +#define PMU1_PLL0_PC2_M5DIV_BY_18 0x12 +#define PMU1_PLL0_PC2_M5DIV_BY_36 0x24 +#define PMU1_PLL0_PC2_M6DIV_MASK 0x0000ff00 +#define PMU1_PLL0_PC2_M6DIV_SHIFT 8 +#define PMU1_PLL0_PC2_M6DIV_BY_18 0x12 +#define PMU1_PLL0_PC2_M6DIV_BY_36 0x24 +#define PMU1_PLL0_PC2_NDIV_MODE_MASK 0x000e0000 +#define PMU1_PLL0_PC2_NDIV_MODE_SHIFT 17 +#define PMU1_PLL0_PC2_NDIV_MODE_MASH 1 +#define PMU1_PLL0_PC2_NDIV_MODE_MFB 2 +#define PMU1_PLL0_PC2_NDIV_INT_MASK 0x1ff00000 +#define PMU1_PLL0_PC2_NDIV_INT_SHIFT 20 + + +#define PMU1_PLL0_PLLCTL3 3 +#define PMU1_PLL0_PC3_NDIV_FRAC_MASK 0x00ffffff +#define PMU1_PLL0_PC3_NDIV_FRAC_SHIFT 0 + + +#define PMU1_PLL0_PLLCTL4 4 + + +#define PMU1_PLL0_PLLCTL5 5 +#define PMU1_PLL0_PC5_CLK_DRV_MASK 0xffffff00 +#define PMU1_PLL0_PC5_CLK_DRV_SHIFT 8 + + +#define PMU2_PHY_PLL_PLLCTL 4 +#define PMU2_SI_PLL_PLLCTL 10 + + + + +#define PMU2_PLL_PLLCTL0 0 +#define PMU2_PLL_PC0_P1DIV_MASK 0x00f00000 +#define PMU2_PLL_PC0_P1DIV_SHIFT 20 +#define PMU2_PLL_PC0_P2DIV_MASK 0x0f000000 +#define PMU2_PLL_PC0_P2DIV_SHIFT 24 + + +#define PMU2_PLL_PLLCTL1 1 +#define PMU2_PLL_PC1_M1DIV_MASK 0x000000ff +#define PMU2_PLL_PC1_M1DIV_SHIFT 0 +#define PMU2_PLL_PC1_M2DIV_MASK 0x0000ff00 +#define PMU2_PLL_PC1_M2DIV_SHIFT 8 +#define PMU2_PLL_PC1_M3DIV_MASK 0x00ff0000 +#define PMU2_PLL_PC1_M3DIV_SHIFT 16 +#define PMU2_PLL_PC1_M4DIV_MASK 0xff000000 +#define PMU2_PLL_PC1_M4DIV_SHIFT 24 + + +#define PMU2_PLL_PLLCTL2 2 +#define PMU2_PLL_PC2_M5DIV_MASK 0x000000ff +#define PMU2_PLL_PC2_M5DIV_SHIFT 0 +#define PMU2_PLL_PC2_M6DIV_MASK 0x0000ff00 +#define PMU2_PLL_PC2_M6DIV_SHIFT 8 +#define PMU2_PLL_PC2_NDIV_MODE_MASK 0x000e0000 +#define PMU2_PLL_PC2_NDIV_MODE_SHIFT 17 +#define PMU2_PLL_PC2_NDIV_INT_MASK 0x1ff00000 +#define PMU2_PLL_PC2_NDIV_INT_SHIFT 20 + + +#define PMU2_PLL_PLLCTL3 3 +#define PMU2_PLL_PC3_NDIV_FRAC_MASK 0x00ffffff +#define PMU2_PLL_PC3_NDIV_FRAC_SHIFT 0 + + +#define PMU2_PLL_PLLCTL4 4 + + +#define PMU2_PLL_PLLCTL5 5 +#define PMU2_PLL_PC5_CLKDRIVE_CH1_MASK 0x00000f00 +#define PMU2_PLL_PC5_CLKDRIVE_CH1_SHIFT 8 +#define PMU2_PLL_PC5_CLKDRIVE_CH2_MASK 0x0000f000 +#define PMU2_PLL_PC5_CLKDRIVE_CH2_SHIFT 12 +#define PMU2_PLL_PC5_CLKDRIVE_CH3_MASK 0x000f0000 +#define PMU2_PLL_PC5_CLKDRIVE_CH3_SHIFT 16 +#define PMU2_PLL_PC5_CLKDRIVE_CH4_MASK 0x00f00000 +#define PMU2_PLL_PC5_CLKDRIVE_CH4_SHIFT 20 +#define PMU2_PLL_PC5_CLKDRIVE_CH5_MASK 0x0f000000 +#define PMU2_PLL_PC5_CLKDRIVE_CH5_SHIFT 24 +#define PMU2_PLL_PC5_CLKDRIVE_CH6_MASK 0xf0000000 +#define PMU2_PLL_PC5_CLKDRIVE_CH6_SHIFT 28 + + +#define PMU5_PLL_P1P2_OFF 0 +#define PMU5_PLL_P1_MASK 0x0f000000 +#define PMU5_PLL_P1_SHIFT 24 +#define PMU5_PLL_P2_MASK 0x00f00000 +#define PMU5_PLL_P2_SHIFT 20 +#define PMU5_PLL_M14_OFF 1 +#define PMU5_PLL_MDIV_MASK 0x000000ff +#define PMU5_PLL_MDIV_WIDTH 8 +#define PMU5_PLL_NM5_OFF 2 +#define PMU5_PLL_NDIV_MASK 0xfff00000 +#define PMU5_PLL_NDIV_SHIFT 20 +#define PMU5_PLL_NDIV_MODE_MASK 0x000e0000 +#define PMU5_PLL_NDIV_MODE_SHIFT 17 +#define PMU5_PLL_FMAB_OFF 3 +#define PMU5_PLL_MRAT_MASK 0xf0000000 +#define PMU5_PLL_MRAT_SHIFT 28 +#define PMU5_PLL_ABRAT_MASK 0x08000000 +#define PMU5_PLL_ABRAT_SHIFT 27 +#define PMU5_PLL_FDIV_MASK 0x07ffffff +#define PMU5_PLL_PLLCTL_OFF 4 +#define PMU5_PLL_PCHI_OFF 5 +#define PMU5_PLL_PCHI_MASK 0x0000003f + + +#define PMU_XTALFREQ_REG_ILPCTR_MASK 0x00001FFF +#define PMU_XTALFREQ_REG_MEASURE_MASK 0x80000000 +#define PMU_XTALFREQ_REG_MEASURE_SHIFT 31 + + +#define PMU5_MAINPLL_CPU 1 +#define PMU5_MAINPLL_MEM 2 +#define PMU5_MAINPLL_SI 3 + + +#define PMU4706_MAINPLL_PLL0 0 +#define PMU6_4706_PROCPLL_OFF 4 +#define PMU6_4706_PROC_P2DIV_MASK 0x000f0000 +#define PMU6_4706_PROC_P2DIV_SHIFT 16 +#define PMU6_4706_PROC_P1DIV_MASK 0x0000f000 +#define PMU6_4706_PROC_P1DIV_SHIFT 12 +#define PMU6_4706_PROC_NDIV_INT_MASK 0x00000ff8 +#define PMU6_4706_PROC_NDIV_INT_SHIFT 3 +#define PMU6_4706_PROC_NDIV_MODE_MASK 0x00000007 +#define PMU6_4706_PROC_NDIV_MODE_SHIFT 0 + +#define PMU7_PLL_PLLCTL7 7 +#define PMU7_PLL_CTL7_M4DIV_MASK 0xff000000 +#define PMU7_PLL_CTL7_M4DIV_SHIFT 24 +#define PMU7_PLL_CTL7_M4DIV_BY_6 6 +#define PMU7_PLL_CTL7_M4DIV_BY_12 0xc +#define PMU7_PLL_CTL7_M4DIV_BY_24 0x18 +#define PMU7_PLL_PLLCTL8 8 +#define PMU7_PLL_CTL8_M5DIV_MASK 0x000000ff +#define PMU7_PLL_CTL8_M5DIV_SHIFT 0 +#define PMU7_PLL_CTL8_M5DIV_BY_8 8 +#define PMU7_PLL_CTL8_M5DIV_BY_12 0xc +#define PMU7_PLL_CTL8_M5DIV_BY_24 0x18 +#define PMU7_PLL_CTL8_M6DIV_MASK 0x0000ff00 +#define PMU7_PLL_CTL8_M6DIV_SHIFT 8 +#define PMU7_PLL_CTL8_M6DIV_BY_12 0xc +#define PMU7_PLL_CTL8_M6DIV_BY_24 0x18 +#define PMU7_PLL_PLLCTL11 11 +#define PMU7_PLL_PLLCTL11_MASK 0xffffff00 +#define PMU7_PLL_PLLCTL11_VAL 0x22222200 + + +#define PMU15_PLL_PLLCTL0 0 +#define PMU15_PLL_PC0_CLKSEL_MASK 0x00000003 +#define PMU15_PLL_PC0_CLKSEL_SHIFT 0 +#define PMU15_PLL_PC0_FREQTGT_MASK 0x003FFFFC +#define PMU15_PLL_PC0_FREQTGT_SHIFT 2 +#define PMU15_PLL_PC0_PRESCALE_MASK 0x00C00000 +#define PMU15_PLL_PC0_PRESCALE_SHIFT 22 +#define PMU15_PLL_PC0_KPCTRL_MASK 0x07000000 +#define PMU15_PLL_PC0_KPCTRL_SHIFT 24 +#define PMU15_PLL_PC0_FCNTCTRL_MASK 0x38000000 +#define PMU15_PLL_PC0_FCNTCTRL_SHIFT 27 +#define PMU15_PLL_PC0_FDCMODE_MASK 0x40000000 +#define PMU15_PLL_PC0_FDCMODE_SHIFT 30 +#define PMU15_PLL_PC0_CTRLBIAS_MASK 0x80000000 +#define PMU15_PLL_PC0_CTRLBIAS_SHIFT 31 + +#define PMU15_PLL_PLLCTL1 1 +#define PMU15_PLL_PC1_BIAS_CTLM_MASK 0x00000060 +#define PMU15_PLL_PC1_BIAS_CTLM_SHIFT 5 +#define PMU15_PLL_PC1_BIAS_CTLM_RST_MASK 0x00000040 +#define PMU15_PLL_PC1_BIAS_CTLM_RST_SHIFT 6 +#define PMU15_PLL_PC1_BIAS_SS_DIVR_MASK 0x0001FF80 +#define PMU15_PLL_PC1_BIAS_SS_DIVR_SHIFT 7 +#define PMU15_PLL_PC1_BIAS_SS_RSTVAL_MASK 0x03FE0000 +#define PMU15_PLL_PC1_BIAS_SS_RSTVAL_SHIFT 17 +#define PMU15_PLL_PC1_BIAS_INTG_BW_MASK 0x0C000000 +#define PMU15_PLL_PC1_BIAS_INTG_BW_SHIFT 26 +#define PMU15_PLL_PC1_BIAS_INTG_BYP_MASK 0x10000000 +#define PMU15_PLL_PC1_BIAS_INTG_BYP_SHIFT 28 +#define PMU15_PLL_PC1_OPENLP_EN_MASK 0x40000000 +#define PMU15_PLL_PC1_OPENLP_EN_SHIFT 30 + +#define PMU15_PLL_PLLCTL2 2 +#define PMU15_PLL_PC2_CTEN_MASK 0x00000001 +#define PMU15_PLL_PC2_CTEN_SHIFT 0 + +#define PMU15_PLL_PLLCTL3 3 +#define PMU15_PLL_PC3_DITHER_EN_MASK 0x00000001 +#define PMU15_PLL_PC3_DITHER_EN_SHIFT 0 +#define PMU15_PLL_PC3_DCOCTLSP_MASK 0xFE000000 +#define PMU15_PLL_PC3_DCOCTLSP_SHIFT 25 +#define PMU15_PLL_PC3_DCOCTLSP_DIV2EN_MASK 0x01 +#define PMU15_PLL_PC3_DCOCTLSP_DIV2EN_SHIFT 0 +#define PMU15_PLL_PC3_DCOCTLSP_CH0EN_MASK 0x02 +#define PMU15_PLL_PC3_DCOCTLSP_CH0EN_SHIFT 1 +#define PMU15_PLL_PC3_DCOCTLSP_CH1EN_MASK 0x04 +#define PMU15_PLL_PC3_DCOCTLSP_CH1EN_SHIFT 2 +#define PMU15_PLL_PC3_DCOCTLSP_CH0SEL_MASK 0x18 +#define PMU15_PLL_PC3_DCOCTLSP_CH0SEL_SHIFT 3 +#define PMU15_PLL_PC3_DCOCTLSP_CH1SEL_MASK 0x60 +#define PMU15_PLL_PC3_DCOCTLSP_CH1SEL_SHIFT 5 +#define PMU15_PLL_PC3_DCOCTLSP_CHSEL_OUTP_DIV1 0 +#define PMU15_PLL_PC3_DCOCTLSP_CHSEL_OUTP_DIV2 1 +#define PMU15_PLL_PC3_DCOCTLSP_CHSEL_OUTP_DIV3 2 +#define PMU15_PLL_PC3_DCOCTLSP_CHSEL_OUTP_DIV5 3 + +#define PMU15_PLL_PLLCTL4 4 +#define PMU15_PLL_PC4_FLLCLK1_DIV_MASK 0x00000007 +#define PMU15_PLL_PC4_FLLCLK1_DIV_SHIFT 0 +#define PMU15_PLL_PC4_FLLCLK2_DIV_MASK 0x00000038 +#define PMU15_PLL_PC4_FLLCLK2_DIV_SHIFT 3 +#define PMU15_PLL_PC4_FLLCLK3_DIV_MASK 0x000001C0 +#define PMU15_PLL_PC4_FLLCLK3_DIV_SHIFT 6 +#define PMU15_PLL_PC4_DBGMODE_MASK 0x00000E00 +#define PMU15_PLL_PC4_DBGMODE_SHIFT 9 +#define PMU15_PLL_PC4_FLL480_CTLSP_LK_MASK 0x00001000 +#define PMU15_PLL_PC4_FLL480_CTLSP_LK_SHIFT 12 +#define PMU15_PLL_PC4_FLL480_CTLSP_MASK 0x000FE000 +#define PMU15_PLL_PC4_FLL480_CTLSP_SHIFT 13 +#define PMU15_PLL_PC4_DINPOL_MASK 0x00100000 +#define PMU15_PLL_PC4_DINPOL_SHIFT 20 +#define PMU15_PLL_PC4_CLKOUT_PD_MASK 0x00200000 +#define PMU15_PLL_PC4_CLKOUT_PD_SHIFT 21 +#define PMU15_PLL_PC4_CLKDIV2_PD_MASK 0x00400000 +#define PMU15_PLL_PC4_CLKDIV2_PD_SHIFT 22 +#define PMU15_PLL_PC4_CLKDIV4_PD_MASK 0x00800000 +#define PMU15_PLL_PC4_CLKDIV4_PD_SHIFT 23 +#define PMU15_PLL_PC4_CLKDIV8_PD_MASK 0x01000000 +#define PMU15_PLL_PC4_CLKDIV8_PD_SHIFT 24 +#define PMU15_PLL_PC4_CLKDIV16_PD_MASK 0x02000000 +#define PMU15_PLL_PC4_CLKDIV16_PD_SHIFT 25 +#define PMU15_PLL_PC4_TEST_EN_MASK 0x04000000 +#define PMU15_PLL_PC4_TEST_EN_SHIFT 26 + +#define PMU15_PLL_PLLCTL5 5 +#define PMU15_PLL_PC5_FREQTGT_MASK 0x000FFFFF +#define PMU15_PLL_PC5_FREQTGT_SHIFT 0 +#define PMU15_PLL_PC5_DCOCTLSP_MASK 0x07F00000 +#define PMU15_PLL_PC5_DCOCTLSP_SHIFT 20 +#define PMU15_PLL_PC5_PRESCALE_MASK 0x18000000 +#define PMU15_PLL_PC5_PRESCALE_SHIFT 27 + +#define PMU15_PLL_PLLCTL6 6 +#define PMU15_PLL_PC6_FREQTGT_MASK 0x000FFFFF +#define PMU15_PLL_PC6_FREQTGT_SHIFT 0 +#define PMU15_PLL_PC6_DCOCTLSP_MASK 0x07F00000 +#define PMU15_PLL_PC6_DCOCTLSP_SHIFT 20 +#define PMU15_PLL_PC6_PRESCALE_MASK 0x18000000 +#define PMU15_PLL_PC6_PRESCALE_SHIFT 27 + +#define PMU15_FREQTGT_480_DEFAULT 0x19AB1 +#define PMU15_FREQTGT_492_DEFAULT 0x1A4F5 +#define PMU15_ARM_96MHZ 96000000 +#define PMU15_ARM_98MHZ 98400000 +#define PMU15_ARM_97MHZ 97000000 + + +#define PMU17_PLLCTL2_NDIVTYPE_MASK 0x00000070 +#define PMU17_PLLCTL2_NDIVTYPE_SHIFT 4 + +#define PMU17_PLLCTL2_NDIV_MODE_INT 0 +#define PMU17_PLLCTL2_NDIV_MODE_INT1B8 1 +#define PMU17_PLLCTL2_NDIV_MODE_MASH111 2 +#define PMU17_PLLCTL2_NDIV_MODE_MASH111B8 3 + +#define PMU17_PLLCTL0_BBPLL_PWRDWN 0 +#define PMU17_PLLCTL0_BBPLL_DRST 3 +#define PMU17_PLLCTL0_BBPLL_DISBL_CLK 8 + + +#define PMU4716_MAINPLL_PLL0 12 + + +#define PMU5356_MAINPLL_PLL0 0 +#define PMU5357_MAINPLL_PLL0 0 + + +#define RES4716_PROC_PLL_ON 0x00000040 +#define RES4716_PROC_HT_AVAIL 0x00000080 + + +#define CCTRL_471X_I2S_PINS_ENABLE 0x0080 + + + +#define CCTRL_5357_I2S_PINS_ENABLE 0x00040000 +#define CCTRL_5357_I2CSPI_PINS_ENABLE 0x00080000 + + +#define RES5354_EXT_SWITCHER_PWM 0 +#define RES5354_BB_SWITCHER_PWM 1 +#define RES5354_BB_SWITCHER_BURST 2 +#define RES5354_BB_EXT_SWITCHER_BURST 3 +#define RES5354_ILP_REQUEST 4 +#define RES5354_RADIO_SWITCHER_PWM 5 +#define RES5354_RADIO_SWITCHER_BURST 6 +#define RES5354_ROM_SWITCH 7 +#define RES5354_PA_REF_LDO 8 +#define RES5354_RADIO_LDO 9 +#define RES5354_AFE_LDO 10 +#define RES5354_PLL_LDO 11 +#define RES5354_BG_FILTBYP 12 +#define RES5354_TX_FILTBYP 13 +#define RES5354_RX_FILTBYP 14 +#define RES5354_XTAL_PU 15 +#define RES5354_XTAL_EN 16 +#define RES5354_BB_PLL_FILTBYP 17 +#define RES5354_RF_PLL_FILTBYP 18 +#define RES5354_BB_PLL_PU 19 + + +#define CCTRL5357_EXTPA (1<<14) +#define CCTRL5357_ANT_MUX_2o3 (1<<15) +#define CCTRL5357_NFLASH (1<<16) + + +#define CCTRL43217_EXTPA_C0 (1<<13) +#define CCTRL43217_EXTPA_C1 (1<<8) + + +#define RES4328_EXT_SWITCHER_PWM 0 +#define RES4328_BB_SWITCHER_PWM 1 +#define RES4328_BB_SWITCHER_BURST 2 +#define RES4328_BB_EXT_SWITCHER_BURST 3 +#define RES4328_ILP_REQUEST 4 +#define RES4328_RADIO_SWITCHER_PWM 5 +#define RES4328_RADIO_SWITCHER_BURST 6 +#define RES4328_ROM_SWITCH 7 +#define RES4328_PA_REF_LDO 8 +#define RES4328_RADIO_LDO 9 +#define RES4328_AFE_LDO 10 +#define RES4328_PLL_LDO 11 +#define RES4328_BG_FILTBYP 12 +#define RES4328_TX_FILTBYP 13 +#define RES4328_RX_FILTBYP 14 +#define RES4328_XTAL_PU 15 +#define RES4328_XTAL_EN 16 +#define RES4328_BB_PLL_FILTBYP 17 +#define RES4328_RF_PLL_FILTBYP 18 +#define RES4328_BB_PLL_PU 19 + + +#define RES4325_BUCK_BOOST_BURST 0 +#define RES4325_CBUCK_BURST 1 +#define RES4325_CBUCK_PWM 2 +#define RES4325_CLDO_CBUCK_BURST 3 +#define RES4325_CLDO_CBUCK_PWM 4 +#define RES4325_BUCK_BOOST_PWM 5 +#define RES4325_ILP_REQUEST 6 +#define RES4325_ABUCK_BURST 7 +#define RES4325_ABUCK_PWM 8 +#define RES4325_LNLDO1_PU 9 +#define RES4325_OTP_PU 10 +#define RES4325_LNLDO3_PU 11 +#define RES4325_LNLDO4_PU 12 +#define RES4325_XTAL_PU 13 +#define RES4325_ALP_AVAIL 14 +#define RES4325_RX_PWRSW_PU 15 +#define RES4325_TX_PWRSW_PU 16 +#define RES4325_RFPLL_PWRSW_PU 17 +#define RES4325_LOGEN_PWRSW_PU 18 +#define RES4325_AFE_PWRSW_PU 19 +#define RES4325_BBPLL_PWRSW_PU 20 +#define RES4325_HT_AVAIL 21 + + +#define RES4325B0_CBUCK_LPOM 1 +#define RES4325B0_CBUCK_BURST 2 +#define RES4325B0_CBUCK_PWM 3 +#define RES4325B0_CLDO_PU 4 + + +#define RES4325C1_LNLDO2_PU 12 + + +#define CST4325_SPROM_OTP_SEL_MASK 0x00000003 +#define CST4325_DEFCIS_SEL 0 +#define CST4325_SPROM_SEL 1 +#define CST4325_OTP_SEL 2 +#define CST4325_OTP_PWRDN 3 +#define CST4325_SDIO_USB_MODE_MASK 0x00000004 +#define CST4325_SDIO_USB_MODE_SHIFT 2 +#define CST4325_RCAL_VALID_MASK 0x00000008 +#define CST4325_RCAL_VALID_SHIFT 3 +#define CST4325_RCAL_VALUE_MASK 0x000001f0 +#define CST4325_RCAL_VALUE_SHIFT 4 +#define CST4325_PMUTOP_2B_MASK 0x00000200 +#define CST4325_PMUTOP_2B_SHIFT 9 + +#define RES4329_RESERVED0 0 +#define RES4329_CBUCK_LPOM 1 +#define RES4329_CBUCK_BURST 2 +#define RES4329_CBUCK_PWM 3 +#define RES4329_CLDO_PU 4 +#define RES4329_PALDO_PU 5 +#define RES4329_ILP_REQUEST 6 +#define RES4329_RESERVED7 7 +#define RES4329_RESERVED8 8 +#define RES4329_LNLDO1_PU 9 +#define RES4329_OTP_PU 10 +#define RES4329_RESERVED11 11 +#define RES4329_LNLDO2_PU 12 +#define RES4329_XTAL_PU 13 +#define RES4329_ALP_AVAIL 14 +#define RES4329_RX_PWRSW_PU 15 +#define RES4329_TX_PWRSW_PU 16 +#define RES4329_RFPLL_PWRSW_PU 17 +#define RES4329_LOGEN_PWRSW_PU 18 +#define RES4329_AFE_PWRSW_PU 19 +#define RES4329_BBPLL_PWRSW_PU 20 +#define RES4329_HT_AVAIL 21 + +#define CST4329_SPROM_OTP_SEL_MASK 0x00000003 +#define CST4329_DEFCIS_SEL 0 +#define CST4329_SPROM_SEL 1 +#define CST4329_OTP_SEL 2 +#define CST4329_OTP_PWRDN 3 +#define CST4329_SPI_SDIO_MODE_MASK 0x00000004 +#define CST4329_SPI_SDIO_MODE_SHIFT 2 + + +#define CST4312_SPROM_OTP_SEL_MASK 0x00000003 +#define CST4312_DEFCIS_SEL 0 +#define CST4312_SPROM_SEL 1 +#define CST4312_OTP_SEL 2 +#define CST4312_OTP_BAD 3 + + +#define RES4312_SWITCHER_BURST 0 +#define RES4312_SWITCHER_PWM 1 +#define RES4312_PA_REF_LDO 2 +#define RES4312_CORE_LDO_BURST 3 +#define RES4312_CORE_LDO_PWM 4 +#define RES4312_RADIO_LDO 5 +#define RES4312_ILP_REQUEST 6 +#define RES4312_BG_FILTBYP 7 +#define RES4312_TX_FILTBYP 8 +#define RES4312_RX_FILTBYP 9 +#define RES4312_XTAL_PU 10 +#define RES4312_ALP_AVAIL 11 +#define RES4312_BB_PLL_FILTBYP 12 +#define RES4312_RF_PLL_FILTBYP 13 +#define RES4312_HT_AVAIL 14 + + +#define RES4322_RF_LDO 0 +#define RES4322_ILP_REQUEST 1 +#define RES4322_XTAL_PU 2 +#define RES4322_ALP_AVAIL 3 +#define RES4322_SI_PLL_ON 4 +#define RES4322_HT_SI_AVAIL 5 +#define RES4322_PHY_PLL_ON 6 +#define RES4322_HT_PHY_AVAIL 7 +#define RES4322_OTP_PU 8 + + +#define CST4322_XTAL_FREQ_20_40MHZ 0x00000020 +#define CST4322_SPROM_OTP_SEL_MASK 0x000000c0 +#define CST4322_SPROM_OTP_SEL_SHIFT 6 +#define CST4322_NO_SPROM_OTP 0 +#define CST4322_SPROM_PRESENT 1 +#define CST4322_OTP_PRESENT 2 +#define CST4322_PCI_OR_USB 0x00000100 +#define CST4322_BOOT_MASK 0x00000600 +#define CST4322_BOOT_SHIFT 9 +#define CST4322_BOOT_FROM_SRAM 0 +#define CST4322_BOOT_FROM_ROM 1 +#define CST4322_BOOT_FROM_FLASH 2 +#define CST4322_BOOT_FROM_INVALID 3 +#define CST4322_ILP_DIV_EN 0x00000800 +#define CST4322_FLASH_TYPE_MASK 0x00001000 +#define CST4322_FLASH_TYPE_SHIFT 12 +#define CST4322_FLASH_TYPE_SHIFT_ST 0 +#define CST4322_FLASH_TYPE_SHIFT_ATMEL 1 +#define CST4322_ARM_TAP_SEL 0x00002000 +#define CST4322_RES_INIT_MODE_MASK 0x0000c000 +#define CST4322_RES_INIT_MODE_SHIFT 14 +#define CST4322_RES_INIT_MODE_ILPAVAIL 0 +#define CST4322_RES_INIT_MODE_ILPREQ 1 +#define CST4322_RES_INIT_MODE_ALPAVAIL 2 +#define CST4322_RES_INIT_MODE_HTAVAIL 3 +#define CST4322_PCIPLLCLK_GATING 0x00010000 +#define CST4322_CLK_SWITCH_PCI_TO_ALP 0x00020000 +#define CST4322_PCI_CARDBUS_MODE 0x00040000 + + +#define CCTRL43224_GPIO_TOGGLE 0x8000 +#define CCTRL_43224A0_12MA_LED_DRIVE 0x00F000F0 +#define CCTRL_43224B0_12MA_LED_DRIVE 0xF0 + + +#define RES43236_REGULATOR 0 +#define RES43236_ILP_REQUEST 1 +#define RES43236_XTAL_PU 2 +#define RES43236_ALP_AVAIL 3 +#define RES43236_SI_PLL_ON 4 +#define RES43236_HT_SI_AVAIL 5 + + +#define CCTRL43236_BT_COEXIST (1<<0) +#define CCTRL43236_SECI (1<<1) +#define CCTRL43236_EXT_LNA (1<<2) +#define CCTRL43236_ANT_MUX_2o3 (1<<3) +#define CCTRL43236_GSIO (1<<4) + + +#define CST43236_SFLASH_MASK 0x00000040 +#define CST43236_OTP_SEL_MASK 0x00000080 +#define CST43236_OTP_SEL_SHIFT 7 +#define CST43236_HSIC_MASK 0x00000100 +#define CST43236_BP_CLK 0x00000200 +#define CST43236_BOOT_MASK 0x00001800 +#define CST43236_BOOT_SHIFT 11 +#define CST43236_BOOT_FROM_SRAM 0 +#define CST43236_BOOT_FROM_ROM 1 +#define CST43236_BOOT_FROM_FLASH 2 +#define CST43236_BOOT_FROM_INVALID 3 + + +#define RES43237_REGULATOR 0 +#define RES43237_ILP_REQUEST 1 +#define RES43237_XTAL_PU 2 +#define RES43237_ALP_AVAIL 3 +#define RES43237_SI_PLL_ON 4 +#define RES43237_HT_SI_AVAIL 5 + + +#define CCTRL43237_BT_COEXIST (1<<0) +#define CCTRL43237_SECI (1<<1) +#define CCTRL43237_EXT_LNA (1<<2) +#define CCTRL43237_ANT_MUX_2o3 (1<<3) +#define CCTRL43237_GSIO (1<<4) + + +#define CST43237_SFLASH_MASK 0x00000040 +#define CST43237_OTP_SEL_MASK 0x00000080 +#define CST43237_OTP_SEL_SHIFT 7 +#define CST43237_HSIC_MASK 0x00000100 +#define CST43237_BP_CLK 0x00000200 +#define CST43237_BOOT_MASK 0x00001800 +#define CST43237_BOOT_SHIFT 11 +#define CST43237_BOOT_FROM_SRAM 0 +#define CST43237_BOOT_FROM_ROM 1 +#define CST43237_BOOT_FROM_FLASH 2 +#define CST43237_BOOT_FROM_INVALID 3 + + +#define RES43239_OTP_PU 9 +#define RES43239_MACPHY_CLKAVAIL 23 +#define RES43239_HT_AVAIL 24 + + +#define CST43239_SPROM_MASK 0x00000002 +#define CST43239_SFLASH_MASK 0x00000004 +#define CST43239_RES_INIT_MODE_SHIFT 7 +#define CST43239_RES_INIT_MODE_MASK 0x000001f0 +#define CST43239_CHIPMODE_SDIOD(cs) ((cs) & (1 << 15)) +#define CST43239_CHIPMODE_USB20D(cs) (~(cs) & (1 << 15)) +#define CST43239_CHIPMODE_SDIO(cs) (((cs) & (1 << 0)) == 0) +#define CST43239_CHIPMODE_GSPI(cs) (((cs) & (1 << 0)) == (1 << 0)) + + +#define RES4324_OTP_PU 10 +#define RES4324_HT_AVAIL 29 +#define RES4324_MACPHY_CLKAVAIL 30 + + +#define CST4324_SPROM_MASK 0x00000080 +#define CST4324_SFLASH_MASK 0x00400000 +#define CST4324_RES_INIT_MODE_SHIFT 10 +#define CST4324_RES_INIT_MODE_MASK 0x00000c00 +#define CST4324_CHIPMODE_MASK 0x7 +#define CST4324_CHIPMODE_SDIOD(cs) ((~(cs)) & (1 << 2)) +#define CST4324_CHIPMODE_USB20D(cs) (((cs) & CST4324_CHIPMODE_MASK) == 0x6) + + +#define RES4331_REGULATOR 0 +#define RES4331_ILP_REQUEST 1 +#define RES4331_XTAL_PU 2 +#define RES4331_ALP_AVAIL 3 +#define RES4331_SI_PLL_ON 4 +#define RES4331_HT_SI_AVAIL 5 + + +#define CCTRL4331_BT_COEXIST (1<<0) +#define CCTRL4331_SECI (1<<1) +#define CCTRL4331_EXT_LNA_G (1<<2) +#define CCTRL4331_SPROM_GPIO13_15 (1<<3) +#define CCTRL4331_EXTPA_EN (1<<4) +#define CCTRL4331_GPIOCLK_ON_SPROMCS (1<<5) +#define CCTRL4331_PCIE_MDIO_ON_SPROMCS (1<<6) +#define CCTRL4331_EXTPA_ON_GPIO2_5 (1<<7) +#define CCTRL4331_OVR_PIPEAUXCLKEN (1<<8) +#define CCTRL4331_OVR_PIPEAUXPWRDOWN (1<<9) +#define CCTRL4331_PCIE_AUXCLKEN (1<<10) +#define CCTRL4331_PCIE_PIPE_PLLDOWN (1<<11) +#define CCTRL4331_EXTPA_EN2 (1<<12) +#define CCTRL4331_EXT_LNA_A (1<<13) +#define CCTRL4331_BT_SHD0_ON_GPIO4 (1<<16) +#define CCTRL4331_BT_SHD1_ON_GPIO5 (1<<17) +#define CCTRL4331_EXTPA_ANA_EN (1<<24) + + +#define CST4331_XTAL_FREQ 0x00000001 +#define CST4331_SPROM_OTP_SEL_MASK 0x00000006 +#define CST4331_SPROM_OTP_SEL_SHIFT 1 +#define CST4331_SPROM_PRESENT 0x00000002 +#define CST4331_OTP_PRESENT 0x00000004 +#define CST4331_LDO_RF 0x00000008 +#define CST4331_LDO_PAR 0x00000010 + + +#define RES4315_CBUCK_LPOM 1 +#define RES4315_CBUCK_BURST 2 +#define RES4315_CBUCK_PWM 3 +#define RES4315_CLDO_PU 4 +#define RES4315_PALDO_PU 5 +#define RES4315_ILP_REQUEST 6 +#define RES4315_LNLDO1_PU 9 +#define RES4315_OTP_PU 10 +#define RES4315_LNLDO2_PU 12 +#define RES4315_XTAL_PU 13 +#define RES4315_ALP_AVAIL 14 +#define RES4315_RX_PWRSW_PU 15 +#define RES4315_TX_PWRSW_PU 16 +#define RES4315_RFPLL_PWRSW_PU 17 +#define RES4315_LOGEN_PWRSW_PU 18 +#define RES4315_AFE_PWRSW_PU 19 +#define RES4315_BBPLL_PWRSW_PU 20 +#define RES4315_HT_AVAIL 21 + + +#define CST4315_SPROM_OTP_SEL_MASK 0x00000003 +#define CST4315_DEFCIS_SEL 0x00000000 +#define CST4315_SPROM_SEL 0x00000001 +#define CST4315_OTP_SEL 0x00000002 +#define CST4315_OTP_PWRDN 0x00000003 +#define CST4315_SDIO_MODE 0x00000004 +#define CST4315_RCAL_VALID 0x00000008 +#define CST4315_RCAL_VALUE_MASK 0x000001f0 +#define CST4315_RCAL_VALUE_SHIFT 4 +#define CST4315_PALDO_EXTPNP 0x00000200 +#define CST4315_CBUCK_MODE_MASK 0x00000c00 +#define CST4315_CBUCK_MODE_BURST 0x00000400 +#define CST4315_CBUCK_MODE_LPBURST 0x00000c00 + + +#define RES4319_CBUCK_LPOM 1 +#define RES4319_CBUCK_BURST 2 +#define RES4319_CBUCK_PWM 3 +#define RES4319_CLDO_PU 4 +#define RES4319_PALDO_PU 5 +#define RES4319_ILP_REQUEST 6 +#define RES4319_LNLDO1_PU 9 +#define RES4319_OTP_PU 10 +#define RES4319_LNLDO2_PU 12 +#define RES4319_XTAL_PU 13 +#define RES4319_ALP_AVAIL 14 +#define RES4319_RX_PWRSW_PU 15 +#define RES4319_TX_PWRSW_PU 16 +#define RES4319_RFPLL_PWRSW_PU 17 +#define RES4319_LOGEN_PWRSW_PU 18 +#define RES4319_AFE_PWRSW_PU 19 +#define RES4319_BBPLL_PWRSW_PU 20 +#define RES4319_HT_AVAIL 21 + + +#define CST4319_SPI_CPULESSUSB 0x00000001 +#define CST4319_SPI_CLK_POL 0x00000002 +#define CST4319_SPI_CLK_PH 0x00000008 +#define CST4319_SPROM_OTP_SEL_MASK 0x000000c0 +#define CST4319_SPROM_OTP_SEL_SHIFT 6 +#define CST4319_DEFCIS_SEL 0x00000000 +#define CST4319_SPROM_SEL 0x00000040 +#define CST4319_OTP_SEL 0x00000080 +#define CST4319_OTP_PWRDN 0x000000c0 +#define CST4319_SDIO_USB_MODE 0x00000100 +#define CST4319_REMAP_SEL_MASK 0x00000600 +#define CST4319_ILPDIV_EN 0x00000800 +#define CST4319_XTAL_PD_POL 0x00001000 +#define CST4319_LPO_SEL 0x00002000 +#define CST4319_RES_INIT_MODE 0x0000c000 +#define CST4319_PALDO_EXTPNP 0x00010000 +#define CST4319_CBUCK_MODE_MASK 0x00060000 +#define CST4319_CBUCK_MODE_BURST 0x00020000 +#define CST4319_CBUCK_MODE_LPBURST 0x00060000 +#define CST4319_RCAL_VALID 0x01000000 +#define CST4319_RCAL_VALUE_MASK 0x3e000000 +#define CST4319_RCAL_VALUE_SHIFT 25 + +#define PMU1_PLL0_CHIPCTL0 0 +#define PMU1_PLL0_CHIPCTL1 1 +#define PMU1_PLL0_CHIPCTL2 2 +#define CCTL_4319USB_XTAL_SEL_MASK 0x00180000 +#define CCTL_4319USB_XTAL_SEL_SHIFT 19 +#define CCTL_4319USB_48MHZ_PLL_SEL 1 +#define CCTL_4319USB_24MHZ_PLL_SEL 2 + + +#define RES4336_CBUCK_LPOM 0 +#define RES4336_CBUCK_BURST 1 +#define RES4336_CBUCK_LP_PWM 2 +#define RES4336_CBUCK_PWM 3 +#define RES4336_CLDO_PU 4 +#define RES4336_DIS_INT_RESET_PD 5 +#define RES4336_ILP_REQUEST 6 +#define RES4336_LNLDO_PU 7 +#define RES4336_LDO3P3_PU 8 +#define RES4336_OTP_PU 9 +#define RES4336_XTAL_PU 10 +#define RES4336_ALP_AVAIL 11 +#define RES4336_RADIO_PU 12 +#define RES4336_BG_PU 13 +#define RES4336_VREG1p4_PU_PU 14 +#define RES4336_AFE_PWRSW_PU 15 +#define RES4336_RX_PWRSW_PU 16 +#define RES4336_TX_PWRSW_PU 17 +#define RES4336_BB_PWRSW_PU 18 +#define RES4336_SYNTH_PWRSW_PU 19 +#define RES4336_MISC_PWRSW_PU 20 +#define RES4336_LOGEN_PWRSW_PU 21 +#define RES4336_BBPLL_PWRSW_PU 22 +#define RES4336_MACPHY_CLKAVAIL 23 +#define RES4336_HT_AVAIL 24 +#define RES4336_RSVD 25 + + +#define CST4336_SPI_MODE_MASK 0x00000001 +#define CST4336_SPROM_PRESENT 0x00000002 +#define CST4336_OTP_PRESENT 0x00000004 +#define CST4336_ARMREMAP_0 0x00000008 +#define CST4336_ILPDIV_EN_MASK 0x00000010 +#define CST4336_ILPDIV_EN_SHIFT 4 +#define CST4336_XTAL_PD_POL_MASK 0x00000020 +#define CST4336_XTAL_PD_POL_SHIFT 5 +#define CST4336_LPO_SEL_MASK 0x00000040 +#define CST4336_LPO_SEL_SHIFT 6 +#define CST4336_RES_INIT_MODE_MASK 0x00000180 +#define CST4336_RES_INIT_MODE_SHIFT 7 +#define CST4336_CBUCK_MODE_MASK 0x00000600 +#define CST4336_CBUCK_MODE_SHIFT 9 + + +#define PCTL_4336_SERIAL_ENAB (1 << 24) + + +#define RES4330_CBUCK_LPOM 0 +#define RES4330_CBUCK_BURST 1 +#define RES4330_CBUCK_LP_PWM 2 +#define RES4330_CBUCK_PWM 3 +#define RES4330_CLDO_PU 4 +#define RES4330_DIS_INT_RESET_PD 5 +#define RES4330_ILP_REQUEST 6 +#define RES4330_LNLDO_PU 7 +#define RES4330_LDO3P3_PU 8 +#define RES4330_OTP_PU 9 +#define RES4330_XTAL_PU 10 +#define RES4330_ALP_AVAIL 11 +#define RES4330_RADIO_PU 12 +#define RES4330_BG_PU 13 +#define RES4330_VREG1p4_PU_PU 14 +#define RES4330_AFE_PWRSW_PU 15 +#define RES4330_RX_PWRSW_PU 16 +#define RES4330_TX_PWRSW_PU 17 +#define RES4330_BB_PWRSW_PU 18 +#define RES4330_SYNTH_PWRSW_PU 19 +#define RES4330_MISC_PWRSW_PU 20 +#define RES4330_LOGEN_PWRSW_PU 21 +#define RES4330_BBPLL_PWRSW_PU 22 +#define RES4330_MACPHY_CLKAVAIL 23 +#define RES4330_HT_AVAIL 24 +#define RES4330_5gRX_PWRSW_PU 25 +#define RES4330_5gTX_PWRSW_PU 26 +#define RES4330_5g_LOGEN_PWRSW_PU 27 + + +#define CST4330_CHIPMODE_SDIOD(cs) (((cs) & 0x7) < 6) +#define CST4330_CHIPMODE_USB20D(cs) (((cs) & 0x7) >= 6) +#define CST4330_CHIPMODE_SDIO(cs) (((cs) & 0x4) == 0) +#define CST4330_CHIPMODE_GSPI(cs) (((cs) & 0x6) == 4) +#define CST4330_CHIPMODE_USB(cs) (((cs) & 0x7) == 6) +#define CST4330_CHIPMODE_USBDA(cs) (((cs) & 0x7) == 7) +#define CST4330_OTP_PRESENT 0x00000010 +#define CST4330_LPO_AUTODET_EN 0x00000020 +#define CST4330_ARMREMAP_0 0x00000040 +#define CST4330_SPROM_PRESENT 0x00000080 +#define CST4330_ILPDIV_EN 0x00000100 +#define CST4330_LPO_SEL 0x00000200 +#define CST4330_RES_INIT_MODE_SHIFT 10 +#define CST4330_RES_INIT_MODE_MASK 0x00000c00 +#define CST4330_CBUCK_MODE_SHIFT 12 +#define CST4330_CBUCK_MODE_MASK 0x00003000 +#define CST4330_CBUCK_POWER_OK 0x00004000 +#define CST4330_BB_PLL_LOCKED 0x00008000 +#define SOCDEVRAM_BP_ADDR 0x1E000000 +#define SOCDEVRAM_ARM_ADDR 0x00800000 + + +#define PCTL_4330_SERIAL_ENAB (1 << 24) + + +#define CCTRL_4330_GPIO_SEL 0x00000001 +#define CCTRL_4330_ERCX_SEL 0x00000002 +#define CCTRL_4330_SDIO_HOST_WAKE 0x00000004 +#define CCTRL_4330_JTAG_DISABLE 0x00000008 + +#define PMU_VREG0_ADDR 0 +#define PMU_VREG0_DISABLE_PULLD_BT_SHIFT 2 +#define PMU_VREG0_DISABLE_PULLD_WL_SHIFT 3 + + +#define RES4334_LPLDO_PU 0 +#define RES4334_RESET_PULLDN_DIS 1 +#define RES4334_PMU_BG_PU 2 +#define RES4334_HSIC_LDO_PU 3 +#define RES4334_CBUCK_LPOM_PU 4 +#define RES4334_CBUCK_PFM_PU 5 +#define RES4334_CLDO_PU 6 +#define RES4334_LPLDO2_LVM 7 +#define RES4334_LNLDO_PU 8 +#define RES4334_LDO3P3_PU 9 +#define RES4334_OTP_PU 10 +#define RES4334_XTAL_PU 11 +#define RES4334_WL_PWRSW_PU 12 +#define RES4334_LQ_AVAIL 13 +#define RES4334_LOGIC_RET 14 +#define RES4334_MEM_SLEEP 15 +#define RES4334_MACPHY_RET 16 +#define RES4334_WL_CORE_READY 17 +#define RES4334_ILP_REQ 18 +#define RES4334_ALP_AVAIL 19 +#define RES4334_MISC_PWRSW_PU 20 +#define RES4334_SYNTH_PWRSW_PU 21 +#define RES4334_RX_PWRSW_PU 22 +#define RES4334_RADIO_PU 23 +#define RES4334_WL_PMU_PU 24 +#define RES4334_VCO_LDO_PU 25 +#define RES4334_AFE_LDO_PU 26 +#define RES4334_RX_LDO_PU 27 +#define RES4334_TX_LDO_PU 28 +#define RES4334_HT_AVAIL 29 +#define RES4334_MACPHY_CLK_AVAIL 30 + + +#define CST4334_CHIPMODE_MASK 7 +#define CST4334_SDIO_MODE 0x00000000 +#define CST4334_SPI_MODE 0x00000004 +#define CST4334_HSIC_MODE 0x00000006 +#define CST4334_BLUSB_MODE 0x00000007 +#define CST4334_CHIPMODE_HSIC(cs) (((cs) & CST4334_CHIPMODE_MASK) == CST4334_HSIC_MODE) +#define CST4334_OTP_PRESENT 0x00000010 +#define CST4334_LPO_AUTODET_EN 0x00000020 +#define CST4334_ARMREMAP_0 0x00000040 +#define CST4334_SPROM_PRESENT 0x00000080 +#define CST4334_ILPDIV_EN_MASK 0x00000100 +#define CST4334_ILPDIV_EN_SHIFT 8 +#define CST4334_LPO_SEL_MASK 0x00000200 +#define CST4334_LPO_SEL_SHIFT 9 +#define CST4334_RES_INIT_MODE_MASK 0x00000C00 +#define CST4334_RES_INIT_MODE_SHIFT 10 + + +#define PCTL_4334_GPIO3_ENAB (1 << 3) + + +#define CCTRL4334_HSIC_LDO_PU (1 << 23) + + +#define CCTRL1_4324_GPIO_SEL (1 << 0) +#define CCTRL1_4324_SDIO_HOST_WAKE (1 << 2) + + + +#define RES4313_BB_PU_RSRC 0 +#define RES4313_ILP_REQ_RSRC 1 +#define RES4313_XTAL_PU_RSRC 2 +#define RES4313_ALP_AVAIL_RSRC 3 +#define RES4313_RADIO_PU_RSRC 4 +#define RES4313_BG_PU_RSRC 5 +#define RES4313_VREG1P4_PU_RSRC 6 +#define RES4313_AFE_PWRSW_RSRC 7 +#define RES4313_RX_PWRSW_RSRC 8 +#define RES4313_TX_PWRSW_RSRC 9 +#define RES4313_BB_PWRSW_RSRC 10 +#define RES4313_SYNTH_PWRSW_RSRC 11 +#define RES4313_MISC_PWRSW_RSRC 12 +#define RES4313_BB_PLL_PWRSW_RSRC 13 +#define RES4313_HT_AVAIL_RSRC 14 +#define RES4313_MACPHY_CLK_AVAIL_RSRC 15 + + +#define CST4313_SPROM_PRESENT 1 +#define CST4313_OTP_PRESENT 2 +#define CST4313_SPROM_OTP_SEL_MASK 0x00000002 +#define CST4313_SPROM_OTP_SEL_SHIFT 0 + + +#define CCTRL_4313_12MA_LED_DRIVE 0x00000007 + + +#define RES4314_LPLDO_PU 0 +#define RES4314_PMU_SLEEP_DIS 1 +#define RES4314_PMU_BG_PU 2 +#define RES4314_CBUCK_LPOM_PU 3 +#define RES4314_CBUCK_PFM_PU 4 +#define RES4314_CLDO_PU 5 +#define RES4314_LPLDO2_LVM 6 +#define RES4314_WL_PMU_PU 7 +#define RES4314_LNLDO_PU 8 +#define RES4314_LDO3P3_PU 9 +#define RES4314_OTP_PU 10 +#define RES4314_XTAL_PU 11 +#define RES4314_WL_PWRSW_PU 12 +#define RES4314_LQ_AVAIL 13 +#define RES4314_LOGIC_RET 14 +#define RES4314_MEM_SLEEP 15 +#define RES4314_MACPHY_RET 16 +#define RES4314_WL_CORE_READY 17 +#define RES4314_ILP_REQ 18 +#define RES4314_ALP_AVAIL 19 +#define RES4314_MISC_PWRSW_PU 20 +#define RES4314_SYNTH_PWRSW_PU 21 +#define RES4314_RX_PWRSW_PU 22 +#define RES4314_RADIO_PU 23 +#define RES4314_VCO_LDO_PU 24 +#define RES4314_AFE_LDO_PU 25 +#define RES4314_RX_LDO_PU 26 +#define RES4314_TX_LDO_PU 27 +#define RES4314_HT_AVAIL 28 +#define RES4314_MACPHY_CLK_AVAIL 29 + + +#define CST4314_OTP_ENABLED 0x00200000 + + +#define RES43228_NOT_USED 0 +#define RES43228_ILP_REQUEST 1 +#define RES43228_XTAL_PU 2 +#define RES43228_ALP_AVAIL 3 +#define RES43228_PLL_EN 4 +#define RES43228_HT_PHY_AVAIL 5 + + +#define CST43228_ILP_DIV_EN 0x1 +#define CST43228_OTP_PRESENT 0x2 +#define CST43228_SERDES_REFCLK_PADSEL 0x4 +#define CST43228_SDIO_MODE 0x8 +#define CST43228_SDIO_OTP_PRESENT 0x10 +#define CST43228_SDIO_RESET 0x20 + + +#define CST4706_PKG_OPTION (1<<0) +#define CST4706_SFLASH_PRESENT (1<<1) +#define CST4706_SFLASH_TYPE (1<<2) +#define CST4706_MIPS_BENDIAN (1<<3) +#define CST4706_PCIE1_DISABLE (1<<5) + + +#define FLSTRCF4706_MASK 0x000000ff +#define FLSTRCF4706_SF1 0x00000001 +#define FLSTRCF4706_PF1 0x00000002 +#define FLSTRCF4706_SF1_TYPE 0x00000004 +#define FLSTRCF4706_NF1 0x00000008 +#define FLSTRCF4706_1ST_MADDR_SEG_MASK 0x000000f0 +#define FLSTRCF4706_1ST_MADDR_SEG_4MB 0x00000010 +#define FLSTRCF4706_1ST_MADDR_SEG_8MB 0x00000020 +#define FLSTRCF4706_1ST_MADDR_SEG_16MB 0x00000030 +#define FLSTRCF4706_1ST_MADDR_SEG_32MB 0x00000040 +#define FLSTRCF4706_1ST_MADDR_SEG_64MB 0x00000050 +#define FLSTRCF4706_1ST_MADDR_SEG_128MB 0x00000060 +#define FLSTRCF4706_1ST_MADDR_SEG_256MB 0x00000070 + + +#define CCTRL4360_SECI_MODE (1 << 2) +#define CCTRL4360_BTSWCTRL_MODE (1 << 3) +#define CCTRL4360_EXTRA_FEMCTRL_MODE (1 << 8) +#define CCTRL4360_BT_LGCY_MODE (1 << 9) +#define CCTRL4360_CORE2FEMCTRL4_ON (1 << 21) + + +#define RES4360_REGULATOR 0 +#define RES4360_ILP_AVAIL 1 +#define RES4360_ILP_REQ 2 +#define RES4360_XTAL_LDO_PU 3 +#define RES4360_XTAL_PU 4 +#define RES4360_ALP_AVAIL 5 +#define RES4360_BBPLLPWRSW_PU 6 +#define RES4360_HT_AVAIL 7 +#define RES4360_OTP_PU 8 + +#define CST4360_XTAL_40MZ 0x00000001 +#define CST4360_SFLASH 0x00000002 +#define CST4360_SPROM_PRESENT 0x00000004 +#define CST4360_SFLASH_TYPE 0x00000004 +#define CST4360_OTP_ENABLED 0x00000008 +#define CST4360_REMAP_ROM 0x00000010 +#define CST4360_RSRC_INIT_MODE_MASK 0x00000060 +#define CST4360_RSRC_INIT_MODE_SHIFT 5 +#define CST4360_ILP_DIVEN 0x00000080 +#define CST4360_MODE_USB 0x00000100 +#define CST4360_SPROM_SIZE_MASK 0x00000600 +#define CST4360_SPROM_SIZE_SHIFT 9 +#define CST4360_BBPLL_LOCK 0x00000800 +#define CST4360_AVBBPLL_LOCK 0x00001000 +#define CST4360_USBBBPLL_LOCK 0x00002000 + +#define CCTRL_4360_UART_SEL 0x2 + + +#define RES4335_LPLDO_PO 0 +#define RES4335_PMU_BG_PU 1 +#define RES4335_PMU_SLEEP 2 +#define RES4335_RSVD_3 3 +#define RES4335_CBUCK_LPOM_PU 4 +#define RES4335_CBUCK_PFM_PU 5 +#define RES4335_RSVD_6 6 +#define RES4335_RSVD_7 7 +#define RES4335_LNLDO_PU 8 +#define RES4335_XTALLDO_PU 9 +#define RES4335_LDO3P3_PU 10 +#define RES4335_OTP_PU 11 +#define RES4335_XTAL_PU 12 +#define RES4335_SR_CLK_START 13 +#define RES4335_LQ_AVAIL 14 +#define RES4335_LQ_START 15 +#define RES4335_RSVD_16 16 +#define RES4335_WL_CORE_RDY 17 +#define RES4335_ILP_REQ 18 +#define RES4335_ALP_AVAIL 19 +#define RES4335_MINI_PMU 20 +#define RES4335_RADIO_PU 21 +#define RES4335_SR_CLK_STABLE 22 +#define RES4335_SR_SAVE_RESTORE 23 +#define RES4335_SR_PHY_PWRSW 24 +#define RES4335_SR_VDDM_PWRSW 25 +#define RES4335_SR_SUBCORE_PWRSW 26 +#define RES4335_SR_SLEEP 27 +#define RES4335_HT_START 28 +#define RES4335_HT_AVAIL 29 +#define RES4335_MACPHY_CLKAVAIL 30 + + +#define CST4335_SPROM_MASK 0x00000020 +#define CST4335_SFLASH_MASK 0x00000040 +#define CST4335_RES_INIT_MODE_SHIFT 7 +#define CST4335_RES_INIT_MODE_MASK 0x00000180 +#define CST4335_CHIPMODE_MASK 0xF +#define CST4335_CHIPMODE_SDIOD(cs) (((cs) & (1 << 0)) != 0) +#define CST4335_CHIPMODE_GSPI(cs) (((cs) & (1 << 1)) != 0) +#define CST4335_CHIPMODE_USB20D(cs) (((cs) & (1 << 2)) != 0) +#define CST4335_CHIPMODE_PCIE(cs) (((cs) & (1 << 3)) != 0) + + +#define CCTRL1_4335_GPIO_SEL (1 << 0) +#define CCTRL1_4335_SDIO_HOST_WAKE (1 << 2) + + +#define CR4_RAM_BASE (0x180000) + + + + +#define CC_GCI_CHIPCTRL_00 (0) +#define CC_GCI_CHIPCTRL_01 (1) +#define CC_GCI_CHIPCTRL_02 (2) +#define CC_GCI_CHIPCTRL_03 (3) +#define CC_GCI_CHIPCTRL_04 (4) +#define CC_GCI_CHIPCTRL_05 (5) +#define CC_GCI_CHIPCTRL_06 (6) +#define CC_GCI_CHIPCTRL_07 (7) +#define CC_GCI_CHIPCTRL_08 (8) + +#define CC_GCI_NUMCHIPCTRLREGS(cap1) ((cap1 & 0xF00) >> 8) + + +#define CC4335_PIN_GPIO_00 (0) +#define CC4335_PIN_GPIO_01 (1) +#define CC4335_PIN_GPIO_02 (2) +#define CC4335_PIN_GPIO_03 (3) +#define CC4335_PIN_GPIO_04 (4) +#define CC4335_PIN_GPIO_05 (5) +#define CC4335_PIN_GPIO_06 (6) +#define CC4335_PIN_GPIO_07 (7) +#define CC4335_PIN_GPIO_08 (8) +#define CC4335_PIN_GPIO_09 (9) +#define CC4335_PIN_GPIO_10 (10) +#define CC4335_PIN_GPIO_11 (11) +#define CC4335_PIN_GPIO_12 (12) +#define CC4335_PIN_GPIO_13 (13) +#define CC4335_PIN_GPIO_14 (14) +#define CC4335_PIN_GPIO_15 (15) +#define CC4335_PIN_SDIO_CLK (16) +#define CC4335_PIN_SDIO_CMD (17) +#define CC4335_PIN_SDIO_DATA0 (18) +#define CC4335_PIN_SDIO_DATA1 (19) +#define CC4335_PIN_SDIO_DATA2 (20) +#define CC4335_PIN_SDIO_DATA3 (21) +#define CC4335_PIN_RF_SW_CTRL_0 (22) +#define CC4335_PIN_RF_SW_CTRL_1 (23) +#define CC4335_PIN_RF_SW_CTRL_2 (24) +#define CC4335_PIN_RF_SW_CTRL_3 (25) +#define CC4335_PIN_RF_SW_CTRL_4 (26) +#define CC4335_PIN_RF_SW_CTRL_5 (27) +#define CC4335_PIN_RF_SW_CTRL_6 (28) +#define CC4335_PIN_RF_SW_CTRL_7 (29) +#define CC4335_PIN_RF_SW_CTRL_8 (30) +#define CC4335_PIN_RF_SW_CTRL_9 (31) + + +#define CC4335_FNSEL_HWDEF (0) +#define CC4335_FNSEL_SAMEASPIN (1) +#define CC4335_FNSEL_GPIO0 (2) +#define CC4335_FNSEL_GPIO1 (3) +#define CC4335_FNSEL_GCI0 (4) +#define CC4335_FNSEL_GCI1 (5) +#define CC4335_FNSEL_UART (6) +#define CC4335_FNSEL_SFLASH (7) +#define CC4335_FNSEL_SPROM (8) +#define CC4335_FNSEL_MISC0 (9) +#define CC4335_FNSEL_MISC1 (10) +#define CC4335_FNSEL_MISC2 (11) +#define CC4335_FNSEL_IND (12) +#define CC4335_FNSEL_PDN (13) +#define CC4335_FNSEL_PUP (14) +#define CC4335_FNSEL_TRI (15) + + +#define GCIMASK(pos) (((uint32)0xF) << pos) + + +#define GCIPOSVAL(val, pos) ((((uint32)val) << pos) & GCIMASK(pos)) + + +#define MUXENAB4335_UART_MASK (0x0000000f) + + + +#define CHIP_HOSTIF_USB(sih) (si_chip_hostif(sih) & CST4360_MODE_USB) + + +#define PMU_MAX_TRANSITION_DLY 15000 + + +#define PMURES_UP_TRANSITION 2 + + + +#define SECI_MODE_UART 0x0 +#define SECI_MODE_SECI 0x1 +#define SECI_MODE_LEGACY_3WIRE_BT 0x2 +#define SECI_MODE_LEGACY_3WIRE_WLAN 0x3 +#define SECI_MODE_HALF_SECI 0x4 + +#define SECI_RESET (1 << 0) +#define SECI_RESET_BAR_UART (1 << 1) +#define SECI_ENAB_SECI_ECI (1 << 2) +#define SECI_ENAB_SECIOUT_DIS (1 << 3) +#define SECI_MODE_MASK 0x7 +#define SECI_MODE_SHIFT 4 +#define SECI_UPD_SECI (1 << 7) + +#define SECI_SIGNOFF_0 0xDB +#define SECI_SIGNOFF_1 0 + + +#define CLKCTL_STS_SECI_CLK_REQ (1 << 8) +#define CLKCTL_STS_SECI_CLK_AVAIL (1 << 24) + +#define SECI_UART_MSR_CTS_STATE (1 << 0) +#define SECI_UART_MSR_RTS_STATE (1 << 1) +#define SECI_UART_SECI_IN_STATE (1 << 2) +#define SECI_UART_SECI_IN2_STATE (1 << 3) + + +#define SECI_UART_LCR_STOP_BITS (1 << 0) +#define SECI_UART_LCR_PARITY_EN (1 << 1) +#define SECI_UART_LCR_PARITY (1 << 2) +#define SECI_UART_LCR_RX_EN (1 << 3) +#define SECI_UART_LCR_LBRK_CTRL (1 << 4) +#define SECI_UART_LCR_TXO_EN (1 << 5) +#define SECI_UART_LCR_RTSO_EN (1 << 6) +#define SECI_UART_LCR_SLIPMODE_EN (1 << 7) +#define SECI_UART_LCR_RXCRC_CHK (1 << 8) +#define SECI_UART_LCR_TXCRC_INV (1 << 9) +#define SECI_UART_LCR_TXCRC_LSBF (1 << 10) +#define SECI_UART_LCR_TXCRC_EN (1 << 11) + +#define SECI_UART_MCR_TX_EN (1 << 0) +#define SECI_UART_MCR_PRTS (1 << 1) +#define SECI_UART_MCR_SWFLCTRL_EN (1 << 2) +#define SECI_UART_MCR_HIGHRATE_EN (1 << 3) +#define SECI_UART_MCR_LOOPBK_EN (1 << 4) +#define SECI_UART_MCR_AUTO_RTS (1 << 5) +#define SECI_UART_MCR_AUTO_TX_DIS (1 << 6) +#define SECI_UART_MCR_BAUD_ADJ_EN (1 << 7) +#define SECI_UART_MCR_XONOFF_RPT (1 << 9) + + + + +#define ECI_BW_20 0x0 +#define ECI_BW_25 0x1 +#define ECI_BW_30 0x2 +#define ECI_BW_35 0x3 +#define ECI_BW_40 0x4 +#define ECI_BW_45 0x5 +#define ECI_BW_50 0x6 +#define ECI_BW_ALL 0x7 + + +#define WLAN_NUM_ANT1 TXANT_0 +#define WLAN_NUM_ANT2 TXANT_1 + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/sbconfig.h b/drivers/net/wireless/bcmdhd/include/sbconfig.h new file mode 100644 index 00000000..44d68329 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/sbconfig.h @@ -0,0 +1,275 @@ +/* + * Broadcom SiliconBackplane hardware register definitions. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: sbconfig.h 241182 2011-02-17 21:50:03Z $ + */ + +#ifndef _SBCONFIG_H +#define _SBCONFIG_H + + +#ifndef PAD +#define _PADLINE(line) pad ## line +#define _XSTR(line) _PADLINE(line) +#define PAD _XSTR(__LINE__) +#endif + + +#define SB_BUS_SIZE 0x10000 +#define SB_BUS_BASE(b) (SI_ENUM_BASE + (b) * SB_BUS_SIZE) +#define SB_BUS_MAXCORES (SB_BUS_SIZE / SI_CORE_SIZE) + + +#define SBCONFIGOFF 0xf00 +#define SBCONFIGSIZE 256 + +#define SBIPSFLAG 0x08 +#define SBTPSFLAG 0x18 +#define SBTMERRLOGA 0x48 +#define SBTMERRLOG 0x50 +#define SBADMATCH3 0x60 +#define SBADMATCH2 0x68 +#define SBADMATCH1 0x70 +#define SBIMSTATE 0x90 +#define SBINTVEC 0x94 +#define SBTMSTATELOW 0x98 +#define SBTMSTATEHIGH 0x9c +#define SBBWA0 0xa0 +#define SBIMCONFIGLOW 0xa8 +#define SBIMCONFIGHIGH 0xac +#define SBADMATCH0 0xb0 +#define SBTMCONFIGLOW 0xb8 +#define SBTMCONFIGHIGH 0xbc +#define SBBCONFIG 0xc0 +#define SBBSTATE 0xc8 +#define SBACTCNFG 0xd8 +#define SBFLAGST 0xe8 +#define SBIDLOW 0xf8 +#define SBIDHIGH 0xfc + + + +#define SBIMERRLOGA 0xea8 +#define SBIMERRLOG 0xeb0 +#define SBTMPORTCONNID0 0xed8 +#define SBTMPORTLOCK0 0xef8 + +#ifndef _LANGUAGE_ASSEMBLY + +typedef volatile struct _sbconfig { + uint32 PAD[2]; + uint32 sbipsflag; + uint32 PAD[3]; + uint32 sbtpsflag; + uint32 PAD[11]; + uint32 sbtmerrloga; + uint32 PAD; + uint32 sbtmerrlog; + uint32 PAD[3]; + uint32 sbadmatch3; + uint32 PAD; + uint32 sbadmatch2; + uint32 PAD; + uint32 sbadmatch1; + uint32 PAD[7]; + uint32 sbimstate; + uint32 sbintvec; + uint32 sbtmstatelow; + uint32 sbtmstatehigh; + uint32 sbbwa0; + uint32 PAD; + uint32 sbimconfiglow; + uint32 sbimconfighigh; + uint32 sbadmatch0; + uint32 PAD; + uint32 sbtmconfiglow; + uint32 sbtmconfighigh; + uint32 sbbconfig; + uint32 PAD; + uint32 sbbstate; + uint32 PAD[3]; + uint32 sbactcnfg; + uint32 PAD[3]; + uint32 sbflagst; + uint32 PAD[3]; + uint32 sbidlow; + uint32 sbidhigh; +} sbconfig_t; + +#endif + + +#define SBIPS_INT1_MASK 0x3f +#define SBIPS_INT1_SHIFT 0 +#define SBIPS_INT2_MASK 0x3f00 +#define SBIPS_INT2_SHIFT 8 +#define SBIPS_INT3_MASK 0x3f0000 +#define SBIPS_INT3_SHIFT 16 +#define SBIPS_INT4_MASK 0x3f000000 +#define SBIPS_INT4_SHIFT 24 + + +#define SBTPS_NUM0_MASK 0x3f +#define SBTPS_F0EN0 0x40 + + +#define SBTMEL_CM 0x00000007 +#define SBTMEL_CI 0x0000ff00 +#define SBTMEL_EC 0x0f000000 +#define SBTMEL_ME 0x80000000 + + +#define SBIM_PC 0xf +#define SBIM_AP_MASK 0x30 +#define SBIM_AP_BOTH 0x00 +#define SBIM_AP_TS 0x10 +#define SBIM_AP_TK 0x20 +#define SBIM_AP_RSV 0x30 +#define SBIM_IBE 0x20000 +#define SBIM_TO 0x40000 +#define SBIM_BY 0x01800000 +#define SBIM_RJ 0x02000000 + + +#define SBTML_RESET 0x0001 +#define SBTML_REJ_MASK 0x0006 +#define SBTML_REJ 0x0002 +#define SBTML_TMPREJ 0x0004 + +#define SBTML_SICF_SHIFT 16 + + +#define SBTMH_SERR 0x0001 +#define SBTMH_INT 0x0002 +#define SBTMH_BUSY 0x0004 +#define SBTMH_TO 0x0020 + +#define SBTMH_SISF_SHIFT 16 + + +#define SBBWA_TAB0_MASK 0xffff +#define SBBWA_TAB1_MASK 0xffff +#define SBBWA_TAB1_SHIFT 16 + + +#define SBIMCL_STO_MASK 0x7 +#define SBIMCL_RTO_MASK 0x70 +#define SBIMCL_RTO_SHIFT 4 +#define SBIMCL_CID_MASK 0xff0000 +#define SBIMCL_CID_SHIFT 16 + + +#define SBIMCH_IEM_MASK 0xc +#define SBIMCH_TEM_MASK 0x30 +#define SBIMCH_TEM_SHIFT 4 +#define SBIMCH_BEM_MASK 0xc0 +#define SBIMCH_BEM_SHIFT 6 + + +#define SBAM_TYPE_MASK 0x3 +#define SBAM_AD64 0x4 +#define SBAM_ADINT0_MASK 0xf8 +#define SBAM_ADINT0_SHIFT 3 +#define SBAM_ADINT1_MASK 0x1f8 +#define SBAM_ADINT1_SHIFT 3 +#define SBAM_ADINT2_MASK 0x1f8 +#define SBAM_ADINT2_SHIFT 3 +#define SBAM_ADEN 0x400 +#define SBAM_ADNEG 0x800 +#define SBAM_BASE0_MASK 0xffffff00 +#define SBAM_BASE0_SHIFT 8 +#define SBAM_BASE1_MASK 0xfffff000 +#define SBAM_BASE1_SHIFT 12 +#define SBAM_BASE2_MASK 0xffff0000 +#define SBAM_BASE2_SHIFT 16 + + +#define SBTMCL_CD_MASK 0xff +#define SBTMCL_CO_MASK 0xf800 +#define SBTMCL_CO_SHIFT 11 +#define SBTMCL_IF_MASK 0xfc0000 +#define SBTMCL_IF_SHIFT 18 +#define SBTMCL_IM_MASK 0x3000000 +#define SBTMCL_IM_SHIFT 24 + + +#define SBTMCH_BM_MASK 0x3 +#define SBTMCH_RM_MASK 0x3 +#define SBTMCH_RM_SHIFT 2 +#define SBTMCH_SM_MASK 0x30 +#define SBTMCH_SM_SHIFT 4 +#define SBTMCH_EM_MASK 0x300 +#define SBTMCH_EM_SHIFT 8 +#define SBTMCH_IM_MASK 0xc00 +#define SBTMCH_IM_SHIFT 10 + + +#define SBBC_LAT_MASK 0x3 +#define SBBC_MAX0_MASK 0xf0000 +#define SBBC_MAX0_SHIFT 16 +#define SBBC_MAX1_MASK 0xf00000 +#define SBBC_MAX1_SHIFT 20 + + +#define SBBS_SRD 0x1 +#define SBBS_HRD 0x2 + + +#define SBIDL_CS_MASK 0x3 +#define SBIDL_AR_MASK 0x38 +#define SBIDL_AR_SHIFT 3 +#define SBIDL_SYNCH 0x40 +#define SBIDL_INIT 0x80 +#define SBIDL_MINLAT_MASK 0xf00 +#define SBIDL_MINLAT_SHIFT 8 +#define SBIDL_MAXLAT 0xf000 +#define SBIDL_MAXLAT_SHIFT 12 +#define SBIDL_FIRST 0x10000 +#define SBIDL_CW_MASK 0xc0000 +#define SBIDL_CW_SHIFT 18 +#define SBIDL_TP_MASK 0xf00000 +#define SBIDL_TP_SHIFT 20 +#define SBIDL_IP_MASK 0xf000000 +#define SBIDL_IP_SHIFT 24 +#define SBIDL_RV_MASK 0xf0000000 +#define SBIDL_RV_SHIFT 28 +#define SBIDL_RV_2_2 0x00000000 +#define SBIDL_RV_2_3 0x10000000 + + +#define SBIDH_RC_MASK 0x000f +#define SBIDH_RCE_MASK 0x7000 +#define SBIDH_RCE_SHIFT 8 +#define SBCOREREV(sbidh) \ + ((((sbidh) & SBIDH_RCE_MASK) >> SBIDH_RCE_SHIFT) | ((sbidh) & SBIDH_RC_MASK)) +#define SBIDH_CC_MASK 0x8ff0 +#define SBIDH_CC_SHIFT 4 +#define SBIDH_VC_MASK 0xffff0000 +#define SBIDH_VC_SHIFT 16 + +#define SB_COMMIT 0xfd8 + + +#define SB_VEND_BCM 0x4243 + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/sbhnddma.h b/drivers/net/wireless/bcmdhd/include/sbhnddma.h new file mode 100644 index 00000000..da1f1a10 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/sbhnddma.h @@ -0,0 +1,370 @@ +/* + * Generic Broadcom Home Networking Division (HND) DMA engine HW interface + * This supports the following chips: BCM42xx, 44xx, 47xx . + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: sbhnddma.h 309193 2012-01-19 00:03:57Z $ + */ + +#ifndef _sbhnddma_h_ +#define _sbhnddma_h_ + + + + + + + +typedef volatile struct { + uint32 control; + uint32 addr; + uint32 ptr; + uint32 status; +} dma32regs_t; + +typedef volatile struct { + dma32regs_t xmt; + dma32regs_t rcv; +} dma32regp_t; + +typedef volatile struct { + uint32 fifoaddr; + uint32 fifodatalow; + uint32 fifodatahigh; + uint32 pad; +} dma32diag_t; + + +typedef volatile struct { + uint32 ctrl; + uint32 addr; +} dma32dd_t; + + +#define D32RINGALIGN_BITS 12 +#define D32MAXRINGSZ (1 << D32RINGALIGN_BITS) +#define D32RINGALIGN (1 << D32RINGALIGN_BITS) + +#define D32MAXDD (D32MAXRINGSZ / sizeof (dma32dd_t)) + + +#define XC_XE ((uint32)1 << 0) +#define XC_SE ((uint32)1 << 1) +#define XC_LE ((uint32)1 << 2) +#define XC_FL ((uint32)1 << 4) +#define XC_MR_MASK 0x000000C0 +#define XC_MR_SHIFT 6 +#define XC_PD ((uint32)1 << 11) +#define XC_AE ((uint32)3 << 16) +#define XC_AE_SHIFT 16 +#define XC_BL_MASK 0x001C0000 +#define XC_BL_SHIFT 18 +#define XC_PC_MASK 0x00E00000 +#define XC_PC_SHIFT 21 +#define XC_PT_MASK 0x03000000 +#define XC_PT_SHIFT 24 + + +#define DMA_MR_1 0 +#define DMA_MR_2 1 + + + +#define DMA_BL_16 0 +#define DMA_BL_32 1 +#define DMA_BL_64 2 +#define DMA_BL_128 3 +#define DMA_BL_256 4 +#define DMA_BL_512 5 +#define DMA_BL_1024 6 + + +#define DMA_PC_0 0 +#define DMA_PC_4 1 +#define DMA_PC_8 2 +#define DMA_PC_16 3 + + + +#define DMA_PT_1 0 +#define DMA_PT_2 1 +#define DMA_PT_4 2 +#define DMA_PT_8 3 + + +#define XP_LD_MASK 0xfff + + +#define XS_CD_MASK 0x0fff +#define XS_XS_MASK 0xf000 +#define XS_XS_SHIFT 12 +#define XS_XS_DISABLED 0x0000 +#define XS_XS_ACTIVE 0x1000 +#define XS_XS_IDLE 0x2000 +#define XS_XS_STOPPED 0x3000 +#define XS_XS_SUSP 0x4000 +#define XS_XE_MASK 0xf0000 +#define XS_XE_SHIFT 16 +#define XS_XE_NOERR 0x00000 +#define XS_XE_DPE 0x10000 +#define XS_XE_DFU 0x20000 +#define XS_XE_BEBR 0x30000 +#define XS_XE_BEDA 0x40000 +#define XS_AD_MASK 0xfff00000 +#define XS_AD_SHIFT 20 + + +#define RC_RE ((uint32)1 << 0) +#define RC_RO_MASK 0xfe +#define RC_RO_SHIFT 1 +#define RC_FM ((uint32)1 << 8) +#define RC_SH ((uint32)1 << 9) +#define RC_OC ((uint32)1 << 10) +#define RC_PD ((uint32)1 << 11) +#define RC_AE ((uint32)3 << 16) +#define RC_AE_SHIFT 16 +#define RC_BL_MASK 0x001C0000 +#define RC_BL_SHIFT 18 +#define RC_PC_MASK 0x00E00000 +#define RC_PC_SHIFT 21 +#define RC_PT_MASK 0x03000000 +#define RC_PT_SHIFT 24 + + +#define RP_LD_MASK 0xfff + + +#define RS_CD_MASK 0x0fff +#define RS_RS_MASK 0xf000 +#define RS_RS_SHIFT 12 +#define RS_RS_DISABLED 0x0000 +#define RS_RS_ACTIVE 0x1000 +#define RS_RS_IDLE 0x2000 +#define RS_RS_STOPPED 0x3000 +#define RS_RE_MASK 0xf0000 +#define RS_RE_SHIFT 16 +#define RS_RE_NOERR 0x00000 +#define RS_RE_DPE 0x10000 +#define RS_RE_DFO 0x20000 +#define RS_RE_BEBW 0x30000 +#define RS_RE_BEDA 0x40000 +#define RS_AD_MASK 0xfff00000 +#define RS_AD_SHIFT 20 + + +#define FA_OFF_MASK 0xffff +#define FA_SEL_MASK 0xf0000 +#define FA_SEL_SHIFT 16 +#define FA_SEL_XDD 0x00000 +#define FA_SEL_XDP 0x10000 +#define FA_SEL_RDD 0x40000 +#define FA_SEL_RDP 0x50000 +#define FA_SEL_XFD 0x80000 +#define FA_SEL_XFP 0x90000 +#define FA_SEL_RFD 0xc0000 +#define FA_SEL_RFP 0xd0000 +#define FA_SEL_RSD 0xe0000 +#define FA_SEL_RSP 0xf0000 + + +#define CTRL_BC_MASK 0x00001fff +#define CTRL_AE ((uint32)3 << 16) +#define CTRL_AE_SHIFT 16 +#define CTRL_PARITY ((uint32)3 << 18) +#define CTRL_EOT ((uint32)1 << 28) +#define CTRL_IOC ((uint32)1 << 29) +#define CTRL_EOF ((uint32)1 << 30) +#define CTRL_SOF ((uint32)1 << 31) + + +#define CTRL_CORE_MASK 0x0ff00000 + + + + +typedef volatile struct { + uint32 control; + uint32 ptr; + uint32 addrlow; + uint32 addrhigh; + uint32 status0; + uint32 status1; +} dma64regs_t; + +typedef volatile struct { + dma64regs_t tx; + dma64regs_t rx; +} dma64regp_t; + +typedef volatile struct { + uint32 fifoaddr; + uint32 fifodatalow; + uint32 fifodatahigh; + uint32 pad; +} dma64diag_t; + + +typedef volatile struct { + uint32 ctrl1; + uint32 ctrl2; + uint32 addrlow; + uint32 addrhigh; +} dma64dd_t; + + +#define D64RINGALIGN_BITS 13 +#define D64MAXRINGSZ (1 << D64RINGALIGN_BITS) +#define D64RINGALIGN (1 << D64RINGALIGN_BITS) + +#define D64MAXDD (D64MAXRINGSZ / sizeof (dma64dd_t)) + + +#define D64_XC_XE 0x00000001 +#define D64_XC_SE 0x00000002 +#define D64_XC_LE 0x00000004 +#define D64_XC_FL 0x00000010 +#define D64_XC_MR_MASK 0x000000C0 +#define D64_XC_MR_SHIFT 6 +#define D64_XC_PD 0x00000800 +#define D64_XC_AE 0x00030000 +#define D64_XC_AE_SHIFT 16 +#define D64_XC_BL_MASK 0x001C0000 +#define D64_XC_BL_SHIFT 18 +#define D64_XC_PC_MASK 0x00E00000 +#define D64_XC_PC_SHIFT 21 +#define D64_XC_PT_MASK 0x03000000 +#define D64_XC_PT_SHIFT 24 + + +#define D64_XP_LD_MASK 0x00001fff + + +#define D64_XS0_CD_MASK 0x00001fff +#define D64_XS0_XS_MASK 0xf0000000 +#define D64_XS0_XS_SHIFT 28 +#define D64_XS0_XS_DISABLED 0x00000000 +#define D64_XS0_XS_ACTIVE 0x10000000 +#define D64_XS0_XS_IDLE 0x20000000 +#define D64_XS0_XS_STOPPED 0x30000000 +#define D64_XS0_XS_SUSP 0x40000000 + +#define D64_XS1_AD_MASK 0x00001fff +#define D64_XS1_XE_MASK 0xf0000000 +#define D64_XS1_XE_SHIFT 28 +#define D64_XS1_XE_NOERR 0x00000000 +#define D64_XS1_XE_DPE 0x10000000 +#define D64_XS1_XE_DFU 0x20000000 +#define D64_XS1_XE_DTE 0x30000000 +#define D64_XS1_XE_DESRE 0x40000000 +#define D64_XS1_XE_COREE 0x50000000 + + +#define D64_RC_RE 0x00000001 +#define D64_RC_RO_MASK 0x000000fe +#define D64_RC_RO_SHIFT 1 +#define D64_RC_FM 0x00000100 +#define D64_RC_SH 0x00000200 +#define D64_RC_OC 0x00000400 +#define D64_RC_PD 0x00000800 +#define D64_RC_AE 0x00030000 +#define D64_RC_AE_SHIFT 16 +#define D64_RC_BL_MASK 0x001C0000 +#define D64_RC_BL_SHIFT 18 +#define D64_RC_PC_MASK 0x00E00000 +#define D64_RC_PC_SHIFT 21 +#define D64_RC_PT_MASK 0x03000000 +#define D64_RC_PT_SHIFT 24 + + +#define DMA_CTRL_PEN (1 << 0) +#define DMA_CTRL_ROC (1 << 1) +#define DMA_CTRL_RXMULTI (1 << 2) +#define DMA_CTRL_UNFRAMED (1 << 3) +#define DMA_CTRL_USB_BOUNDRY4KB_WAR (1 << 4) +#define DMA_CTRL_DMA_AVOIDANCE_WAR (1 << 5) + + +#define D64_RP_LD_MASK 0x00001fff + + +#define D64_RS0_CD_MASK 0x00001fff +#define D64_RS0_RS_MASK 0xf0000000 +#define D64_RS0_RS_SHIFT 28 +#define D64_RS0_RS_DISABLED 0x00000000 +#define D64_RS0_RS_ACTIVE 0x10000000 +#define D64_RS0_RS_IDLE 0x20000000 +#define D64_RS0_RS_STOPPED 0x30000000 +#define D64_RS0_RS_SUSP 0x40000000 + +#define D64_RS1_AD_MASK 0x0001ffff +#define D64_RS1_RE_MASK 0xf0000000 +#define D64_RS1_RE_SHIFT 28 +#define D64_RS1_RE_NOERR 0x00000000 +#define D64_RS1_RE_DPO 0x10000000 +#define D64_RS1_RE_DFU 0x20000000 +#define D64_RS1_RE_DTE 0x30000000 +#define D64_RS1_RE_DESRE 0x40000000 +#define D64_RS1_RE_COREE 0x50000000 + + +#define D64_FA_OFF_MASK 0xffff +#define D64_FA_SEL_MASK 0xf0000 +#define D64_FA_SEL_SHIFT 16 +#define D64_FA_SEL_XDD 0x00000 +#define D64_FA_SEL_XDP 0x10000 +#define D64_FA_SEL_RDD 0x40000 +#define D64_FA_SEL_RDP 0x50000 +#define D64_FA_SEL_XFD 0x80000 +#define D64_FA_SEL_XFP 0x90000 +#define D64_FA_SEL_RFD 0xc0000 +#define D64_FA_SEL_RFP 0xd0000 +#define D64_FA_SEL_RSD 0xe0000 +#define D64_FA_SEL_RSP 0xf0000 + + +#define D64_CTRL_COREFLAGS 0x0ff00000 +#define D64_CTRL1_EOT ((uint32)1 << 28) +#define D64_CTRL1_IOC ((uint32)1 << 29) +#define D64_CTRL1_EOF ((uint32)1 << 30) +#define D64_CTRL1_SOF ((uint32)1 << 31) + + +#define D64_CTRL2_BC_MASK 0x00007fff +#define D64_CTRL2_AE 0x00030000 +#define D64_CTRL2_AE_SHIFT 16 +#define D64_CTRL2_PARITY 0x00040000 + + +#define D64_CTRL_CORE_MASK 0x0ff00000 + +#define D64_RX_FRM_STS_LEN 0x0000ffff +#define D64_RX_FRM_STS_OVFL 0x00800000 +#define D64_RX_FRM_STS_DSCRCNT 0x0f000000 +#define D64_RX_FRM_STS_DATATYPE 0xf0000000 + + +typedef volatile struct { + uint16 len; + uint16 flags; +} dma_rxh_t; + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/sbpcmcia.h b/drivers/net/wireless/bcmdhd/include/sbpcmcia.h new file mode 100644 index 00000000..6ad98b52 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/sbpcmcia.h @@ -0,0 +1,108 @@ +/* + * BCM43XX Sonics SiliconBackplane PCMCIA core hardware definitions. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: sbpcmcia.h 326494 2012-04-09 13:29:57Z $ + */ + +#ifndef _SBPCMCIA_H +#define _SBPCMCIA_H + + + + +#define PCMCIA_FCR (0x700 / 2) + +#define FCR0_OFF 0 +#define FCR1_OFF (0x40 / 2) +#define FCR2_OFF (0x80 / 2) +#define FCR3_OFF (0xc0 / 2) + +#define PCMCIA_FCR0 (0x700 / 2) +#define PCMCIA_FCR1 (0x740 / 2) +#define PCMCIA_FCR2 (0x780 / 2) +#define PCMCIA_FCR3 (0x7c0 / 2) + + + +#define PCMCIA_COR 0 + +#define COR_RST 0x80 +#define COR_LEV 0x40 +#define COR_IRQEN 0x04 +#define COR_BLREN 0x01 +#define COR_FUNEN 0x01 + + +#define PCICIA_FCSR (2 / 2) +#define PCICIA_PRR (4 / 2) +#define PCICIA_SCR (6 / 2) +#define PCICIA_ESR (8 / 2) + + +#define PCM_MEMOFF 0x0000 +#define F0_MEMOFF 0x1000 +#define F1_MEMOFF 0x2000 +#define F2_MEMOFF 0x3000 +#define F3_MEMOFF 0x4000 + + +#define MEM_ADDR0 (0x728 / 2) +#define MEM_ADDR1 (0x72a / 2) +#define MEM_ADDR2 (0x72c / 2) + + +#define PCMCIA_ADDR0 (0x072e / 2) +#define PCMCIA_ADDR1 (0x0730 / 2) +#define PCMCIA_ADDR2 (0x0732 / 2) + +#define MEM_SEG (0x0734 / 2) +#define SROM_CS (0x0736 / 2) +#define SROM_DATAL (0x0738 / 2) +#define SROM_DATAH (0x073a / 2) +#define SROM_ADDRL (0x073c / 2) +#define SROM_ADDRH (0x073e / 2) +#define SROM_INFO2 (0x0772 / 2) +#define SROM_INFO (0x07be / 2) + + +#define SROM_IDLE 0 +#define SROM_WRITE 1 +#define SROM_READ 2 +#define SROM_WEN 4 +#define SROM_WDS 7 +#define SROM_DONE 8 + + +#define SRI_SZ_MASK 0x03 +#define SRI_BLANK 0x04 +#define SRI_OTP 0x80 + + + +#define SBTML_INT_ACK 0x40000 +#define SBTML_INT_EN 0x20000 + + +#define SBTMH_INT_STATUS 0x40000 + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/sbsdio.h b/drivers/net/wireless/bcmdhd/include/sbsdio.h new file mode 100644 index 00000000..211c4218 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/sbsdio.h @@ -0,0 +1,188 @@ +/* + * SDIO device core hardware definitions. + * sdio is a portion of the pcmcia core in core rev 3 - rev 8 + * + * SDIO core support 1bit, 4 bit SDIO mode as well as SPI mode. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: sbsdio.h 308945 2012-01-18 02:15:27Z $ + */ + +#ifndef _SBSDIO_H +#define _SBSDIO_H + +#define SBSDIO_NUM_FUNCTION 3 /* as of sdiod rev 0, supports 3 functions */ + +/* function 1 miscellaneous registers */ +#define SBSDIO_SPROM_CS 0x10000 /* sprom command and status */ +#define SBSDIO_SPROM_INFO 0x10001 /* sprom info register */ +#define SBSDIO_SPROM_DATA_LOW 0x10002 /* sprom indirect access data byte 0 */ +#define SBSDIO_SPROM_DATA_HIGH 0x10003 /* sprom indirect access data byte 1 */ +#define SBSDIO_SPROM_ADDR_LOW 0x10004 /* sprom indirect access addr byte 0 */ +#define SBSDIO_SPROM_ADDR_HIGH 0x10005 /* sprom indirect access addr byte 0 */ +#define SBSDIO_CHIP_CTRL_DATA 0x10006 /* xtal_pu (gpio) output */ +#define SBSDIO_CHIP_CTRL_EN 0x10007 /* xtal_pu (gpio) enable */ +#define SBSDIO_WATERMARK 0x10008 /* rev < 7, watermark for sdio device */ +#define SBSDIO_DEVICE_CTL 0x10009 /* control busy signal generation */ + +/* registers introduced in rev 8, some content (mask/bits) defs in sbsdpcmdev.h */ +#define SBSDIO_FUNC1_SBADDRLOW 0x1000A /* SB Address Window Low (b15) */ +#define SBSDIO_FUNC1_SBADDRMID 0x1000B /* SB Address Window Mid (b23:b16) */ +#define SBSDIO_FUNC1_SBADDRHIGH 0x1000C /* SB Address Window High (b31:b24) */ +#define SBSDIO_FUNC1_FRAMECTRL 0x1000D /* Frame Control (frame term/abort) */ +#define SBSDIO_FUNC1_CHIPCLKCSR 0x1000E /* ChipClockCSR (ALP/HT ctl/status) */ +#define SBSDIO_FUNC1_SDIOPULLUP 0x1000F /* SdioPullUp (on cmd, d0-d2) */ +#define SBSDIO_FUNC1_WFRAMEBCLO 0x10019 /* Write Frame Byte Count Low */ +#define SBSDIO_FUNC1_WFRAMEBCHI 0x1001A /* Write Frame Byte Count High */ +#define SBSDIO_FUNC1_RFRAMEBCLO 0x1001B /* Read Frame Byte Count Low */ +#define SBSDIO_FUNC1_RFRAMEBCHI 0x1001C /* Read Frame Byte Count High */ +#define SBSDIO_FUNC1_MESBUSYCTRL 0x1001D /* MesBusyCtl at 0x1001D (rev 11) */ + +#define SBSDIO_FUNC1_MISC_REG_START 0x10000 /* f1 misc register start */ +#define SBSDIO_FUNC1_MISC_REG_LIMIT 0x1001C /* f1 misc register end */ + +/* Sdio Core Rev 12 */ +#define SBSDIO_FUNC1_WAKEUPCTRL 0x1001E +#define SBSDIO_FUNC1_WCTRL_ALPWAIT_MASK 0x1 +#define SBSDIO_FUNC1_WCTRL_ALPWAIT_SHIFT 0 +#define SBSDIO_FUNC1_WCTRL_HTWAIT_MASK 0x2 +#define SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT 1 +#define SBSDIO_FUNC1_SLEEPCSR 0x1001F +#define SBSDIO_FUNC1_SLEEPCSR_KSO_MASK 0x1 +#define SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT 0 +#define SBSDIO_FUNC1_SLEEPCSR_KSO_EN 1 +#define SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK 0x2 +#define SBSDIO_FUNC1_SLEEPCSR_DEVON_SHIFT 1 + +/* SBSDIO_SPROM_CS */ +#define SBSDIO_SPROM_IDLE 0 +#define SBSDIO_SPROM_WRITE 1 +#define SBSDIO_SPROM_READ 2 +#define SBSDIO_SPROM_WEN 4 +#define SBSDIO_SPROM_WDS 7 +#define SBSDIO_SPROM_DONE 8 + +/* SBSDIO_SPROM_INFO */ +#define SROM_SZ_MASK 0x03 /* SROM size, 1: 4k, 2: 16k */ +#define SROM_BLANK 0x04 /* depreciated in corerev 6 */ +#define SROM_OTP 0x80 /* OTP present */ + +/* SBSDIO_CHIP_CTRL */ +#define SBSDIO_CHIP_CTRL_XTAL 0x01 /* or'd with onchip xtal_pu, + * 1: power on oscillator + * (for 4318 only) + */ +/* SBSDIO_WATERMARK */ +#define SBSDIO_WATERMARK_MASK 0x7f /* number of words - 1 for sd device + * to wait before sending data to host + */ + +/* SBSDIO_MESBUSYCTRL */ +/* When RX FIFO has less entries than this & MBE is set + * => busy signal is asserted between data blocks. +*/ +#define SBSDIO_MESBUSYCTRL_MASK 0x7f + +/* SBSDIO_DEVICE_CTL */ +#define SBSDIO_DEVCTL_SETBUSY 0x01 /* 1: device will assert busy signal when + * receiving CMD53 + */ +#define SBSDIO_DEVCTL_SPI_INTR_SYNC 0x02 /* 1: assertion of sdio interrupt is + * synchronous to the sdio clock + */ +#define SBSDIO_DEVCTL_CA_INT_ONLY 0x04 /* 1: mask all interrupts to host + * except the chipActive (rev 8) + */ +#define SBSDIO_DEVCTL_PADS_ISO 0x08 /* 1: isolate internal sdio signals, put + * external pads in tri-state; requires + * sdio bus power cycle to clear (rev 9) + */ +#define SBSDIO_DEVCTL_SB_RST_CTL 0x30 /* Force SD->SB reset mapping (rev 11) */ +#define SBSDIO_DEVCTL_RST_CORECTL 0x00 /* Determined by CoreControl bit */ +#define SBSDIO_DEVCTL_RST_BPRESET 0x10 /* Force backplane reset */ +#define SBSDIO_DEVCTL_RST_NOBPRESET 0x20 /* Force no backplane reset */ +#define SBSDIO_DEVCTL_EN_F2_BLK_WATERMARK 0x10 /* Enable function 2 tx for each block */ + + +/* SBSDIO_FUNC1_CHIPCLKCSR */ +#define SBSDIO_FORCE_ALP 0x01 /* Force ALP request to backplane */ +#define SBSDIO_FORCE_HT 0x02 /* Force HT request to backplane */ +#define SBSDIO_FORCE_ILP 0x04 /* Force ILP request to backplane */ +#define SBSDIO_ALP_AVAIL_REQ 0x08 /* Make ALP ready (power up xtal) */ +#define SBSDIO_HT_AVAIL_REQ 0x10 /* Make HT ready (power up PLL) */ +#define SBSDIO_FORCE_HW_CLKREQ_OFF 0x20 /* Squelch clock requests from HW */ +#define SBSDIO_ALP_AVAIL 0x40 /* Status: ALP is ready */ +#define SBSDIO_HT_AVAIL 0x80 /* Status: HT is ready */ +/* In rev8, actual avail bits followed original docs */ +#define SBSDIO_Rev8_HT_AVAIL 0x40 +#define SBSDIO_Rev8_ALP_AVAIL 0x80 +#define SBSDIO_CSR_MASK 0x1F + +#define SBSDIO_AVBITS (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL) +#define SBSDIO_ALPAV(regval) ((regval) & SBSDIO_AVBITS) +#define SBSDIO_HTAV(regval) (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS) +#define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval)) +#define SBSDIO_CLKAV(regval, alponly) (SBSDIO_ALPAV(regval) && \ + (alponly ? 1 : SBSDIO_HTAV(regval))) + +/* SBSDIO_FUNC1_SDIOPULLUP */ +#define SBSDIO_PULLUP_D0 0x01 /* Enable D0/MISO pullup */ +#define SBSDIO_PULLUP_D1 0x02 /* Enable D1/INT# pullup */ +#define SBSDIO_PULLUP_D2 0x04 /* Enable D2 pullup */ +#define SBSDIO_PULLUP_CMD 0x08 /* Enable CMD/MOSI pullup */ +#define SBSDIO_PULLUP_ALL 0x0f /* All valid bits */ + +/* function 1 OCP space */ +#define SBSDIO_SB_OFT_ADDR_MASK 0x07FFF /* sb offset addr is <= 15 bits, 32k */ +#define SBSDIO_SB_OFT_ADDR_LIMIT 0x08000 +#define SBSDIO_SB_ACCESS_2_4B_FLAG 0x08000 /* with b15, maps to 32-bit SB access */ + +/* some duplication with sbsdpcmdev.h here */ +/* valid bits in SBSDIO_FUNC1_SBADDRxxx regs */ +#define SBSDIO_SBADDRLOW_MASK 0x80 /* Valid bits in SBADDRLOW */ +#define SBSDIO_SBADDRMID_MASK 0xff /* Valid bits in SBADDRMID */ +#define SBSDIO_SBADDRHIGH_MASK 0xffU /* Valid bits in SBADDRHIGH */ +#define SBSDIO_SBWINDOW_MASK 0xffff8000 /* Address bits from SBADDR regs */ + +/* direct(mapped) cis space */ +#define SBSDIO_CIS_BASE_COMMON 0x1000 /* MAPPED common CIS address */ +#define SBSDIO_CIS_SIZE_LIMIT 0x200 /* maximum bytes in one CIS */ +#define SBSDIO_OTP_CIS_SIZE_LIMIT 0x078 /* maximum bytes OTP CIS */ + +#define SBSDIO_CIS_OFT_ADDR_MASK 0x1FFFF /* cis offset addr is < 17 bits */ + +#define SBSDIO_CIS_MANFID_TUPLE_LEN 6 /* manfid tuple length, include tuple, + * link bytes + */ + +/* indirect cis access (in sprom) */ +#define SBSDIO_SPROM_CIS_OFFSET 0x8 /* 8 control bytes first, CIS starts from + * 8th byte + */ + +#define SBSDIO_BYTEMODE_DATALEN_MAX 64 /* sdio byte mode: maximum length of one + * data comamnd + */ + +#define SBSDIO_CORE_ADDR_MASK 0x1FFFF /* sdio core function one address mask */ + +#endif /* _SBSDIO_H */ diff --git a/drivers/net/wireless/bcmdhd/include/sbsdpcmdev.h b/drivers/net/wireless/bcmdhd/include/sbsdpcmdev.h new file mode 100644 index 00000000..10c7401a --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/sbsdpcmdev.h @@ -0,0 +1,295 @@ +/* + * Broadcom SiliconBackplane SDIO/PCMCIA hardware-specific + * device core support + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: sbsdpcmdev.h 347614 2012-07-27 10:24:51Z $ + */ + +#ifndef _sbsdpcmdev_h_ +#define _sbsdpcmdev_h_ + +/* cpp contortions to concatenate w/arg prescan */ +#ifndef PAD +#define _PADLINE(line) pad ## line +#define _XSTR(line) _PADLINE(line) +#define PAD _XSTR(__LINE__) +#endif /* PAD */ + + +typedef volatile struct { + dma64regs_t xmt; /* dma tx */ + uint32 PAD[2]; + dma64regs_t rcv; /* dma rx */ + uint32 PAD[2]; +} dma64p_t; + +/* dma64 sdiod corerev >= 1 */ +typedef volatile struct { + dma64p_t dma64regs[2]; + dma64diag_t dmafifo; /* DMA Diagnostic Regs, 0x280-0x28c */ + uint32 PAD[92]; +} sdiodma64_t; + +/* dma32 sdiod corerev == 0 */ +typedef volatile struct { + dma32regp_t dma32regs[2]; /* dma tx & rx, 0x200-0x23c */ + dma32diag_t dmafifo; /* DMA Diagnostic Regs, 0x240-0x24c */ + uint32 PAD[108]; +} sdiodma32_t; + +/* dma32 regs for pcmcia core */ +typedef volatile struct { + dma32regp_t dmaregs; /* DMA Regs, 0x200-0x21c, rev8 */ + dma32diag_t dmafifo; /* DMA Diagnostic Regs, 0x220-0x22c */ + uint32 PAD[116]; +} pcmdma32_t; + +/* core registers */ +typedef volatile struct { + uint32 corecontrol; /* CoreControl, 0x000, rev8 */ + uint32 corestatus; /* CoreStatus, 0x004, rev8 */ + uint32 PAD[1]; + uint32 biststatus; /* BistStatus, 0x00c, rev8 */ + + /* PCMCIA access */ + uint16 pcmciamesportaladdr; /* PcmciaMesPortalAddr, 0x010, rev8 */ + uint16 PAD[1]; + uint16 pcmciamesportalmask; /* PcmciaMesPortalMask, 0x014, rev8 */ + uint16 PAD[1]; + uint16 pcmciawrframebc; /* PcmciaWrFrameBC, 0x018, rev8 */ + uint16 PAD[1]; + uint16 pcmciaunderflowtimer; /* PcmciaUnderflowTimer, 0x01c, rev8 */ + uint16 PAD[1]; + + /* interrupt */ + uint32 intstatus; /* IntStatus, 0x020, rev8 */ + uint32 hostintmask; /* IntHostMask, 0x024, rev8 */ + uint32 intmask; /* IntSbMask, 0x028, rev8 */ + uint32 sbintstatus; /* SBIntStatus, 0x02c, rev8 */ + uint32 sbintmask; /* SBIntMask, 0x030, rev8 */ + uint32 funcintmask; /* SDIO Function Interrupt Mask, SDIO rev4 */ + uint32 PAD[2]; + uint32 tosbmailbox; /* ToSBMailbox, 0x040, rev8 */ + uint32 tohostmailbox; /* ToHostMailbox, 0x044, rev8 */ + uint32 tosbmailboxdata; /* ToSbMailboxData, 0x048, rev8 */ + uint32 tohostmailboxdata; /* ToHostMailboxData, 0x04c, rev8 */ + + /* synchronized access to registers in SDIO clock domain */ + uint32 sdioaccess; /* SdioAccess, 0x050, rev8 */ + uint32 PAD[3]; + + /* PCMCIA frame control */ + uint8 pcmciaframectrl; /* pcmciaFrameCtrl, 0x060, rev8 */ + uint8 PAD[3]; + uint8 pcmciawatermark; /* pcmciaWaterMark, 0x064, rev8 */ + uint8 PAD[155]; + + /* interrupt batching control */ + uint32 intrcvlazy; /* IntRcvLazy, 0x100, rev8 */ + uint32 PAD[3]; + + /* counters */ + uint32 cmd52rd; /* Cmd52RdCount, 0x110, rev8, SDIO: cmd52 reads */ + uint32 cmd52wr; /* Cmd52WrCount, 0x114, rev8, SDIO: cmd52 writes */ + uint32 cmd53rd; /* Cmd53RdCount, 0x118, rev8, SDIO: cmd53 reads */ + uint32 cmd53wr; /* Cmd53WrCount, 0x11c, rev8, SDIO: cmd53 writes */ + uint32 abort; /* AbortCount, 0x120, rev8, SDIO: aborts */ + uint32 datacrcerror; /* DataCrcErrorCount, 0x124, rev8, SDIO: frames w/bad CRC */ + uint32 rdoutofsync; /* RdOutOfSyncCount, 0x128, rev8, SDIO/PCMCIA: Rd Frm OOS */ + uint32 wroutofsync; /* RdOutOfSyncCount, 0x12c, rev8, SDIO/PCMCIA: Wr Frm OOS */ + uint32 writebusy; /* WriteBusyCount, 0x130, rev8, SDIO: dev asserted "busy" */ + uint32 readwait; /* ReadWaitCount, 0x134, rev8, SDIO: read: no data avail */ + uint32 readterm; /* ReadTermCount, 0x138, rev8, SDIO: rd frm terminates */ + uint32 writeterm; /* WriteTermCount, 0x13c, rev8, SDIO: wr frm terminates */ + uint32 PAD[40]; + uint32 clockctlstatus; /* ClockCtlStatus, 0x1e0, rev8 */ + uint32 PAD[7]; + + /* DMA engines */ + volatile union { + pcmdma32_t pcm32; + sdiodma32_t sdiod32; + sdiodma64_t sdiod64; + } dma; + + /* SDIO/PCMCIA CIS region */ + char cis[512]; /* 512 byte CIS, 0x400-0x5ff, rev6 */ + + /* PCMCIA function control registers */ + char pcmciafcr[256]; /* PCMCIA FCR, 0x600-6ff, rev6 */ + uint16 PAD[55]; + + /* PCMCIA backplane access */ + uint16 backplanecsr; /* BackplaneCSR, 0x76E, rev6 */ + uint16 backplaneaddr0; /* BackplaneAddr0, 0x770, rev6 */ + uint16 backplaneaddr1; /* BackplaneAddr1, 0x772, rev6 */ + uint16 backplaneaddr2; /* BackplaneAddr2, 0x774, rev6 */ + uint16 backplaneaddr3; /* BackplaneAddr3, 0x776, rev6 */ + uint16 backplanedata0; /* BackplaneData0, 0x778, rev6 */ + uint16 backplanedata1; /* BackplaneData1, 0x77a, rev6 */ + uint16 backplanedata2; /* BackplaneData2, 0x77c, rev6 */ + uint16 backplanedata3; /* BackplaneData3, 0x77e, rev6 */ + uint16 PAD[31]; + + /* sprom "size" & "blank" info */ + uint16 spromstatus; /* SPROMStatus, 0x7BE, rev2 */ + uint32 PAD[464]; + + /* Sonics SiliconBackplane registers */ + sbconfig_t sbconfig; /* SbConfig Regs, 0xf00-0xfff, rev8 */ +} sdpcmd_regs_t; + +/* corecontrol */ +#define CC_CISRDY (1 << 0) /* CIS Ready */ +#define CC_BPRESEN (1 << 1) /* CCCR RES signal causes backplane reset */ +#define CC_F2RDY (1 << 2) /* set CCCR IOR2 bit */ +#define CC_CLRPADSISO (1 << 3) /* clear SDIO pads isolation bit (rev 11) */ +#define CC_XMTDATAAVAIL_MODE (1 << 4) /* data avail generates an interrupt */ +#define CC_XMTDATAAVAIL_CTRL (1 << 5) /* data avail interrupt ctrl */ + +/* corestatus */ +#define CS_PCMCIAMODE (1 << 0) /* Device Mode; 0=SDIO, 1=PCMCIA */ +#define CS_SMARTDEV (1 << 1) /* 1=smartDev enabled */ +#define CS_F2ENABLED (1 << 2) /* 1=host has enabled the device */ + +#define PCMCIA_MES_PA_MASK 0x7fff /* PCMCIA Message Portal Address Mask */ +#define PCMCIA_MES_PM_MASK 0x7fff /* PCMCIA Message Portal Mask Mask */ +#define PCMCIA_WFBC_MASK 0xffff /* PCMCIA Write Frame Byte Count Mask */ +#define PCMCIA_UT_MASK 0x07ff /* PCMCIA Underflow Timer Mask */ + +/* intstatus */ +#define I_SMB_SW0 (1 << 0) /* To SB Mail S/W interrupt 0 */ +#define I_SMB_SW1 (1 << 1) /* To SB Mail S/W interrupt 1 */ +#define I_SMB_SW2 (1 << 2) /* To SB Mail S/W interrupt 2 */ +#define I_SMB_SW3 (1 << 3) /* To SB Mail S/W interrupt 3 */ +#define I_SMB_SW_MASK 0x0000000f /* To SB Mail S/W interrupts mask */ +#define I_SMB_SW_SHIFT 0 /* To SB Mail S/W interrupts shift */ +#define I_HMB_SW0 (1 << 4) /* To Host Mail S/W interrupt 0 */ +#define I_HMB_SW1 (1 << 5) /* To Host Mail S/W interrupt 1 */ +#define I_HMB_SW2 (1 << 6) /* To Host Mail S/W interrupt 2 */ +#define I_HMB_SW3 (1 << 7) /* To Host Mail S/W interrupt 3 */ +#define I_HMB_SW_MASK 0x000000f0 /* To Host Mail S/W interrupts mask */ +#define I_HMB_SW_SHIFT 4 /* To Host Mail S/W interrupts shift */ +#define I_WR_OOSYNC (1 << 8) /* Write Frame Out Of Sync */ +#define I_RD_OOSYNC (1 << 9) /* Read Frame Out Of Sync */ +#define I_PC (1 << 10) /* descriptor error */ +#define I_PD (1 << 11) /* data error */ +#define I_DE (1 << 12) /* Descriptor protocol Error */ +#define I_RU (1 << 13) /* Receive descriptor Underflow */ +#define I_RO (1 << 14) /* Receive fifo Overflow */ +#define I_XU (1 << 15) /* Transmit fifo Underflow */ +#define I_RI (1 << 16) /* Receive Interrupt */ +#define I_BUSPWR (1 << 17) /* SDIO Bus Power Change (rev 9) */ +#define I_XMTDATA_AVAIL (1 << 23) /* bits in fifo */ +#define I_XI (1 << 24) /* Transmit Interrupt */ +#define I_RF_TERM (1 << 25) /* Read Frame Terminate */ +#define I_WF_TERM (1 << 26) /* Write Frame Terminate */ +#define I_PCMCIA_XU (1 << 27) /* PCMCIA Transmit FIFO Underflow */ +#define I_SBINT (1 << 28) /* sbintstatus Interrupt */ +#define I_CHIPACTIVE (1 << 29) /* chip transitioned from doze to active state */ +#define I_SRESET (1 << 30) /* CCCR RES interrupt */ +#define I_IOE2 (1U << 31) /* CCCR IOE2 Bit Changed */ +#define I_ERRORS (I_PC | I_PD | I_DE | I_RU | I_RO | I_XU) /* DMA Errors */ +#define I_DMA (I_RI | I_XI | I_ERRORS) + +/* sbintstatus */ +#define I_SB_SERR (1 << 8) /* Backplane SError (write) */ +#define I_SB_RESPERR (1 << 9) /* Backplane Response Error (read) */ +#define I_SB_SPROMERR (1 << 10) /* Error accessing the sprom */ + +/* sdioaccess */ +#define SDA_DATA_MASK 0x000000ff /* Read/Write Data Mask */ +#define SDA_ADDR_MASK 0x000fff00 /* Read/Write Address Mask */ +#define SDA_ADDR_SHIFT 8 /* Read/Write Address Shift */ +#define SDA_WRITE 0x01000000 /* Write bit */ +#define SDA_READ 0x00000000 /* Write bit cleared for Read */ +#define SDA_BUSY 0x80000000 /* Busy bit */ + +/* sdioaccess-accessible register address spaces */ +#define SDA_CCCR_SPACE 0x000 /* sdioAccess CCCR register space */ +#define SDA_F1_FBR_SPACE 0x100 /* sdioAccess F1 FBR register space */ +#define SDA_F2_FBR_SPACE 0x200 /* sdioAccess F2 FBR register space */ +#define SDA_F1_REG_SPACE 0x300 /* sdioAccess F1 core-specific register space */ + +/* SDA_F1_REG_SPACE sdioaccess-accessible F1 reg space register offsets */ +#define SDA_CHIPCONTROLDATA 0x006 /* ChipControlData */ +#define SDA_CHIPCONTROLENAB 0x007 /* ChipControlEnable */ +#define SDA_F2WATERMARK 0x008 /* Function 2 Watermark */ +#define SDA_DEVICECONTROL 0x009 /* DeviceControl */ +#define SDA_SBADDRLOW 0x00a /* SbAddrLow */ +#define SDA_SBADDRMID 0x00b /* SbAddrMid */ +#define SDA_SBADDRHIGH 0x00c /* SbAddrHigh */ +#define SDA_FRAMECTRL 0x00d /* FrameCtrl */ +#define SDA_CHIPCLOCKCSR 0x00e /* ChipClockCSR */ +#define SDA_SDIOPULLUP 0x00f /* SdioPullUp */ +#define SDA_SDIOWRFRAMEBCLOW 0x019 /* SdioWrFrameBCLow */ +#define SDA_SDIOWRFRAMEBCHIGH 0x01a /* SdioWrFrameBCHigh */ +#define SDA_SDIORDFRAMEBCLOW 0x01b /* SdioRdFrameBCLow */ +#define SDA_SDIORDFRAMEBCHIGH 0x01c /* SdioRdFrameBCHigh */ + +/* SDA_F2WATERMARK */ +#define SDA_F2WATERMARK_MASK 0x7f /* F2Watermark Mask */ + +/* SDA_SBADDRLOW */ +#define SDA_SBADDRLOW_MASK 0x80 /* SbAddrLow Mask */ + +/* SDA_SBADDRMID */ +#define SDA_SBADDRMID_MASK 0xff /* SbAddrMid Mask */ + +/* SDA_SBADDRHIGH */ +#define SDA_SBADDRHIGH_MASK 0xff /* SbAddrHigh Mask */ + +/* SDA_FRAMECTRL */ +#define SFC_RF_TERM (1 << 0) /* Read Frame Terminate */ +#define SFC_WF_TERM (1 << 1) /* Write Frame Terminate */ +#define SFC_CRC4WOOS (1 << 2) /* HW reports CRC error for write out of sync */ +#define SFC_ABORTALL (1 << 3) /* Abort cancels all in-progress frames */ + +/* pcmciaframectrl */ +#define PFC_RF_TERM (1 << 0) /* Read Frame Terminate */ +#define PFC_WF_TERM (1 << 1) /* Write Frame Terminate */ + +/* intrcvlazy */ +#define IRL_TO_MASK 0x00ffffff /* timeout */ +#define IRL_FC_MASK 0xff000000 /* frame count */ +#define IRL_FC_SHIFT 24 /* frame count */ + +/* rx header */ +typedef volatile struct { + uint16 len; + uint16 flags; +} sdpcmd_rxh_t; + +/* rx header flags */ +#define RXF_CRC 0x0001 /* CRC error detected */ +#define RXF_WOOS 0x0002 /* write frame out of sync */ +#define RXF_WF_TERM 0x0004 /* write frame terminated */ +#define RXF_ABORT 0x0008 /* write frame aborted */ +#define RXF_DISCARD (RXF_CRC | RXF_WOOS | RXF_WF_TERM | RXF_ABORT) /* bad frame */ + +/* HW frame tag */ +#define SDPCM_FRAMETAG_LEN 4 /* HW frametag: 2 bytes len, 2 bytes check val */ + +#define SDPCM_HWEXT_LEN 8 + +#endif /* _sbsdpcmdev_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/sbsocram.h b/drivers/net/wireless/bcmdhd/include/sbsocram.h new file mode 100644 index 00000000..852d1151 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/sbsocram.h @@ -0,0 +1,193 @@ +/* + * BCM47XX Sonics SiliconBackplane embedded ram core + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: sbsocram.h 271781 2011-07-13 20:00:06Z $ + */ + +#ifndef _SBSOCRAM_H +#define _SBSOCRAM_H + +#ifndef _LANGUAGE_ASSEMBLY + + +#ifndef PAD +#define _PADLINE(line) pad ## line +#define _XSTR(line) _PADLINE(line) +#define PAD _XSTR(__LINE__) +#endif + + +typedef volatile struct sbsocramregs { + uint32 coreinfo; + uint32 bwalloc; + uint32 extracoreinfo; + uint32 biststat; + uint32 bankidx; + uint32 standbyctrl; + + uint32 errlogstatus; + uint32 errlogaddr; + + uint32 cambankidx; + uint32 cambankstandbyctrl; + uint32 cambankpatchctrl; + uint32 cambankpatchtblbaseaddr; + uint32 cambankcmdreg; + uint32 cambankdatareg; + uint32 cambankmaskreg; + uint32 PAD[1]; + uint32 bankinfo; + uint32 PAD[15]; + uint32 extmemconfig; + uint32 extmemparitycsr; + uint32 extmemparityerrdata; + uint32 extmemparityerrcnt; + uint32 extmemwrctrlandsize; + uint32 PAD[84]; + uint32 workaround; + uint32 pwrctl; + uint32 PAD[133]; + uint32 sr_control; + uint32 sr_status; + uint32 sr_address; + uint32 sr_data; +} sbsocramregs_t; + +#endif + + +#define SR_COREINFO 0x00 +#define SR_BWALLOC 0x04 +#define SR_BISTSTAT 0x0c +#define SR_BANKINDEX 0x10 +#define SR_BANKSTBYCTL 0x14 +#define SR_PWRCTL 0x1e8 + + +#define SRCI_PT_MASK 0x00070000 +#define SRCI_PT_SHIFT 16 + +#define SRCI_PT_OCP_OCP 0 +#define SRCI_PT_AXI_OCP 1 +#define SRCI_PT_ARM7AHB_OCP 2 +#define SRCI_PT_CM3AHB_OCP 3 +#define SRCI_PT_AXI_AXI 4 +#define SRCI_PT_AHB_AXI 5 + +#define SRCI_LSS_MASK 0x00f00000 +#define SRCI_LSS_SHIFT 20 +#define SRCI_LRS_MASK 0x0f000000 +#define SRCI_LRS_SHIFT 24 + + +#define SRCI_MS0_MASK 0xf +#define SR_MS0_BASE 16 + + +#define SRCI_ROMNB_MASK 0xf000 +#define SRCI_ROMNB_SHIFT 12 +#define SRCI_ROMBSZ_MASK 0xf00 +#define SRCI_ROMBSZ_SHIFT 8 +#define SRCI_SRNB_MASK 0xf0 +#define SRCI_SRNB_SHIFT 4 +#define SRCI_SRBSZ_MASK 0xf +#define SRCI_SRBSZ_SHIFT 0 + +#define SR_BSZ_BASE 14 + + +#define SRSC_SBYOVR_MASK 0x80000000 +#define SRSC_SBYOVR_SHIFT 31 +#define SRSC_SBYOVRVAL_MASK 0x60000000 +#define SRSC_SBYOVRVAL_SHIFT 29 +#define SRSC_SBYEN_MASK 0x01000000 +#define SRSC_SBYEN_SHIFT 24 + + +#define SRPC_PMU_STBYDIS_MASK 0x00000010 +#define SRPC_PMU_STBYDIS_SHIFT 4 +#define SRPC_STBYOVRVAL_MASK 0x00000008 +#define SRPC_STBYOVRVAL_SHIFT 3 +#define SRPC_STBYOVR_MASK 0x00000007 +#define SRPC_STBYOVR_SHIFT 0 + + +#define SRECC_NUM_BANKS_MASK 0x000000F0 +#define SRECC_NUM_BANKS_SHIFT 4 +#define SRECC_BANKSIZE_MASK 0x0000000F +#define SRECC_BANKSIZE_SHIFT 0 + +#define SRECC_BANKSIZE(value) (1 << (value)) + + +#define SRCBPC_PATCHENABLE 0x80000000 + +#define SRP_ADDRESS 0x0001FFFC +#define SRP_VALID 0x8000 + + +#define SRCMD_WRITE 0x00020000 +#define SRCMD_READ 0x00010000 +#define SRCMD_DONE 0x80000000 + +#define SRCMD_DONE_DLY 1000 + + +#define SOCRAM_BANKINFO_SZMASK 0x7f +#define SOCRAM_BANKIDX_ROM_MASK 0x100 + +#define SOCRAM_BANKIDX_MEMTYPE_SHIFT 8 + +#define SOCRAM_MEMTYPE_RAM 0 +#define SOCRAM_MEMTYPE_R0M 1 +#define SOCRAM_MEMTYPE_DEVRAM 2 + +#define SOCRAM_BANKINFO_REG 0x40 +#define SOCRAM_BANKIDX_REG 0x10 +#define SOCRAM_BANKINFO_STDBY_MASK 0x400 +#define SOCRAM_BANKINFO_STDBY_TIMER 0x800 + + +#define SOCRAM_BANKINFO_DEVRAMSEL_SHIFT 13 +#define SOCRAM_BANKINFO_DEVRAMSEL_MASK 0x2000 +#define SOCRAM_BANKINFO_DEVRAMPRO_SHIFT 14 +#define SOCRAM_BANKINFO_DEVRAMPRO_MASK 0x4000 +#define SOCRAM_BANKINFO_SLPSUPP_SHIFT 15 +#define SOCRAM_BANKINFO_SLPSUPP_MASK 0x8000 +#define SOCRAM_BANKINFO_RETNTRAM_SHIFT 16 +#define SOCRAM_BANKINFO_RETNTRAM_MASK 0x00010000 +#define SOCRAM_BANKINFO_PDASZ_SHIFT 17 +#define SOCRAM_BANKINFO_PDASZ_MASK 0x003E0000 +#define SOCRAM_BANKINFO_DEVRAMREMAP_SHIFT 24 +#define SOCRAM_BANKINFO_DEVRAMREMAP_MASK 0x01000000 + + +#define SOCRAM_DEVRAMBANK_MASK 0xF000 +#define SOCRAM_DEVRAMBANK_SHIFT 12 + + +#define SOCRAM_BANKINFO_SZBASE 8192 +#define SOCRAM_BANKSIZE_SHIFT 13 + + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/sdio.h b/drivers/net/wireless/bcmdhd/include/sdio.h new file mode 100644 index 00000000..b8eee1ff --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/sdio.h @@ -0,0 +1,617 @@ +/* + * SDIO spec header file + * Protocol and standard (common) device definitions + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: sdio.h 308973 2012-01-18 04:19:34Z $ + */ + +#ifndef _SDIO_H +#define _SDIO_H + + +/* CCCR structure for function 0 */ +typedef volatile struct { + uint8 cccr_sdio_rev; /* RO, cccr and sdio revision */ + uint8 sd_rev; /* RO, sd spec revision */ + uint8 io_en; /* I/O enable */ + uint8 io_rdy; /* I/O ready reg */ + uint8 intr_ctl; /* Master and per function interrupt enable control */ + uint8 intr_status; /* RO, interrupt pending status */ + uint8 io_abort; /* read/write abort or reset all functions */ + uint8 bus_inter; /* bus interface control */ + uint8 capability; /* RO, card capability */ + + uint8 cis_base_low; /* 0x9 RO, common CIS base address, LSB */ + uint8 cis_base_mid; + uint8 cis_base_high; /* 0xB RO, common CIS base address, MSB */ + + /* suspend/resume registers */ + uint8 bus_suspend; /* 0xC */ + uint8 func_select; /* 0xD */ + uint8 exec_flag; /* 0xE */ + uint8 ready_flag; /* 0xF */ + + uint8 fn0_blk_size[2]; /* 0x10(LSB), 0x11(MSB) */ + + uint8 power_control; /* 0x12 (SDIO version 1.10) */ + + uint8 speed_control; /* 0x13 */ +} sdio_regs_t; + +/* SDIO Device CCCR offsets */ +#define SDIOD_CCCR_REV 0x00 +#define SDIOD_CCCR_SDREV 0x01 +#define SDIOD_CCCR_IOEN 0x02 +#define SDIOD_CCCR_IORDY 0x03 +#define SDIOD_CCCR_INTEN 0x04 +#define SDIOD_CCCR_INTPEND 0x05 +#define SDIOD_CCCR_IOABORT 0x06 +#define SDIOD_CCCR_BICTRL 0x07 +#define SDIOD_CCCR_CAPABLITIES 0x08 +#define SDIOD_CCCR_CISPTR_0 0x09 +#define SDIOD_CCCR_CISPTR_1 0x0A +#define SDIOD_CCCR_CISPTR_2 0x0B +#define SDIOD_CCCR_BUSSUSP 0x0C +#define SDIOD_CCCR_FUNCSEL 0x0D +#define SDIOD_CCCR_EXECFLAGS 0x0E +#define SDIOD_CCCR_RDYFLAGS 0x0F +#define SDIOD_CCCR_BLKSIZE_0 0x10 +#define SDIOD_CCCR_BLKSIZE_1 0x11 +#define SDIOD_CCCR_POWER_CONTROL 0x12 +#define SDIOD_CCCR_SPEED_CONTROL 0x13 +#define SDIOD_CCCR_UHSI_SUPPORT 0x14 +#define SDIOD_CCCR_DRIVER_STRENGTH 0x15 +#define SDIOD_CCCR_INTR_EXTN 0x16 + +/* Broadcom extensions (corerev >= 1) */ +#define SDIOD_CCCR_BRCM_CARDCAP 0xf0 +#define SDIOD_CCCR_BRCM_CARDCAP_CMD14_SUPPORT 0x02 +#define SDIOD_CCCR_BRCM_CARDCAP_CMD14_EXT 0x04 +#define SDIOD_CCCR_BRCM_CARDCAP_CMD_NODEC 0x08 +#define SDIOD_CCCR_BRCM_CARDCTL 0xf1 +#define SDIOD_CCCR_BRCM_SEPINT 0xf2 + +/* cccr_sdio_rev */ +#define SDIO_REV_SDIOID_MASK 0xf0 /* SDIO spec revision number */ +#define SDIO_REV_CCCRID_MASK 0x0f /* CCCR format version number */ + +/* sd_rev */ +#define SD_REV_PHY_MASK 0x0f /* SD format version number */ + +/* io_en */ +#define SDIO_FUNC_ENABLE_1 0x02 /* function 1 I/O enable */ +#define SDIO_FUNC_ENABLE_2 0x04 /* function 2 I/O enable */ + +/* io_rdys */ +#define SDIO_FUNC_READY_1 0x02 /* function 1 I/O ready */ +#define SDIO_FUNC_READY_2 0x04 /* function 2 I/O ready */ + +/* intr_ctl */ +#define INTR_CTL_MASTER_EN 0x1 /* interrupt enable master */ +#define INTR_CTL_FUNC1_EN 0x2 /* interrupt enable for function 1 */ +#define INTR_CTL_FUNC2_EN 0x4 /* interrupt enable for function 2 */ + +/* intr_status */ +#define INTR_STATUS_FUNC1 0x2 /* interrupt pending for function 1 */ +#define INTR_STATUS_FUNC2 0x4 /* interrupt pending for function 2 */ + +/* io_abort */ +#define IO_ABORT_RESET_ALL 0x08 /* I/O card reset */ +#define IO_ABORT_FUNC_MASK 0x07 /* abort selction: function x */ + +/* bus_inter */ +#define BUS_CARD_DETECT_DIS 0x80 /* Card Detect disable */ +#define BUS_SPI_CONT_INTR_CAP 0x40 /* support continuous SPI interrupt */ +#define BUS_SPI_CONT_INTR_EN 0x20 /* continuous SPI interrupt enable */ +#define BUS_SD_DATA_WIDTH_MASK 0x03 /* bus width mask */ +#define BUS_SD_DATA_WIDTH_4BIT 0x02 /* bus width 4-bit mode */ +#define BUS_SD_DATA_WIDTH_1BIT 0x00 /* bus width 1-bit mode */ + +/* capability */ +#define SDIO_CAP_4BLS 0x80 /* 4-bit support for low speed card */ +#define SDIO_CAP_LSC 0x40 /* low speed card */ +#define SDIO_CAP_E4MI 0x20 /* enable interrupt between block of data in 4-bit mode */ +#define SDIO_CAP_S4MI 0x10 /* support interrupt between block of data in 4-bit mode */ +#define SDIO_CAP_SBS 0x08 /* support suspend/resume */ +#define SDIO_CAP_SRW 0x04 /* support read wait */ +#define SDIO_CAP_SMB 0x02 /* support multi-block transfer */ +#define SDIO_CAP_SDC 0x01 /* Support Direct commands during multi-byte transfer */ + +/* power_control */ +#define SDIO_POWER_SMPC 0x01 /* supports master power control (RO) */ +#define SDIO_POWER_EMPC 0x02 /* enable master power control (allow > 200mA) (RW) */ + +/* speed_control (control device entry into high-speed clocking mode) */ +#define SDIO_SPEED_SHS 0x01 /* supports high-speed [clocking] mode (RO) */ +#define SDIO_SPEED_EHS 0x02 /* enable high-speed [clocking] mode (RW) */ + +/* for setting bus speed in card: 0x13h */ +#define SDIO_BUS_SPEED_UHSISEL_M BITFIELD_MASK(3) +#define SDIO_BUS_SPEED_UHSISEL_S 1 + +/* for getting bus speed cap in card: 0x14h */ +#define SDIO_BUS_SPEED_UHSICAP_M BITFIELD_MASK(3) +#define SDIO_BUS_SPEED_UHSICAP_S 0 + +/* for getting driver type CAP in card: 0x15h */ +#define SDIO_BUS_DRVR_TYPE_CAP_M BITFIELD_MASK(3) +#define SDIO_BUS_DRVR_TYPE_CAP_S 0 + +/* for setting driver type selection in card: 0x15h */ +#define SDIO_BUS_DRVR_TYPE_SEL_M BITFIELD_MASK(2) +#define SDIO_BUS_DRVR_TYPE_SEL_S 4 + +/* for getting async int support in card: 0x16h */ +#define SDIO_BUS_ASYNCINT_CAP_M BITFIELD_MASK(1) +#define SDIO_BUS_ASYNCINT_CAP_S 0 + +/* for setting async int selection in card: 0x16h */ +#define SDIO_BUS_ASYNCINT_SEL_M BITFIELD_MASK(1) +#define SDIO_BUS_ASYNCINT_SEL_S 1 + +/* brcm sepint */ +#define SDIO_SEPINT_MASK 0x01 /* route sdpcmdev intr onto separate pad (chip-specific) */ +#define SDIO_SEPINT_OE 0x02 /* 1 asserts output enable for above pad */ +#define SDIO_SEPINT_ACT_HI 0x04 /* use active high interrupt level instead of active low */ + +/* FBR structure for function 1-7, FBR addresses and register offsets */ +typedef volatile struct { + uint8 devctr; /* device interface, CSA control */ + uint8 ext_dev; /* extended standard I/O device type code */ + uint8 pwr_sel; /* power selection support */ + uint8 PAD[6]; /* reserved */ + + uint8 cis_low; /* CIS LSB */ + uint8 cis_mid; + uint8 cis_high; /* CIS MSB */ + uint8 csa_low; /* code storage area, LSB */ + uint8 csa_mid; + uint8 csa_high; /* code storage area, MSB */ + uint8 csa_dat_win; /* data access window to function */ + + uint8 fnx_blk_size[2]; /* block size, little endian */ +} sdio_fbr_t; + +/* Maximum number of I/O funcs */ +#define SDIOD_MAX_FUNCS 8 +#define SDIOD_MAX_IOFUNCS 7 + +/* SDIO Device FBR Start Address */ +#define SDIOD_FBR_STARTADDR 0x100 + +/* SDIO Device FBR Size */ +#define SDIOD_FBR_SIZE 0x100 + +/* Macro to calculate FBR register base */ +#define SDIOD_FBR_BASE(n) ((n) * 0x100) + +/* Function register offsets */ +#define SDIOD_FBR_DEVCTR 0x00 /* basic info for function */ +#define SDIOD_FBR_EXT_DEV 0x01 /* extended I/O device code */ +#define SDIOD_FBR_PWR_SEL 0x02 /* power selection bits */ + +/* SDIO Function CIS ptr offset */ +#define SDIOD_FBR_CISPTR_0 0x09 +#define SDIOD_FBR_CISPTR_1 0x0A +#define SDIOD_FBR_CISPTR_2 0x0B + +/* Code Storage Area pointer */ +#define SDIOD_FBR_CSA_ADDR_0 0x0C +#define SDIOD_FBR_CSA_ADDR_1 0x0D +#define SDIOD_FBR_CSA_ADDR_2 0x0E +#define SDIOD_FBR_CSA_DATA 0x0F + +/* SDIO Function I/O Block Size */ +#define SDIOD_FBR_BLKSIZE_0 0x10 +#define SDIOD_FBR_BLKSIZE_1 0x11 + +/* devctr */ +#define SDIOD_FBR_DEVCTR_DIC 0x0f /* device interface code */ +#define SDIOD_FBR_DECVTR_CSA 0x40 /* CSA support flag */ +#define SDIOD_FBR_DEVCTR_CSA_EN 0x80 /* CSA enabled */ +/* interface codes */ +#define SDIOD_DIC_NONE 0 /* SDIO standard interface is not supported */ +#define SDIOD_DIC_UART 1 +#define SDIOD_DIC_BLUETOOTH_A 2 +#define SDIOD_DIC_BLUETOOTH_B 3 +#define SDIOD_DIC_GPS 4 +#define SDIOD_DIC_CAMERA 5 +#define SDIOD_DIC_PHS 6 +#define SDIOD_DIC_WLAN 7 +#define SDIOD_DIC_EXT 0xf /* extended device interface, read ext_dev register */ + +/* pwr_sel */ +#define SDIOD_PWR_SEL_SPS 0x01 /* supports power selection */ +#define SDIOD_PWR_SEL_EPS 0x02 /* enable power selection (low-current mode) */ + +/* misc defines */ +#define SDIO_FUNC_0 0 +#define SDIO_FUNC_1 1 +#define SDIO_FUNC_2 2 +#define SDIO_FUNC_3 3 +#define SDIO_FUNC_4 4 +#define SDIO_FUNC_5 5 +#define SDIO_FUNC_6 6 +#define SDIO_FUNC_7 7 + +#define SD_CARD_TYPE_UNKNOWN 0 /* bad type or unrecognized */ +#define SD_CARD_TYPE_IO 1 /* IO only card */ +#define SD_CARD_TYPE_MEMORY 2 /* memory only card */ +#define SD_CARD_TYPE_COMBO 3 /* IO and memory combo card */ + +#define SDIO_MAX_BLOCK_SIZE 2048 /* maximum block size for block mode operation */ +#define SDIO_MIN_BLOCK_SIZE 1 /* minimum block size for block mode operation */ + +/* Card registers: status bit position */ +#define CARDREG_STATUS_BIT_OUTOFRANGE 31 +#define CARDREG_STATUS_BIT_COMCRCERROR 23 +#define CARDREG_STATUS_BIT_ILLEGALCOMMAND 22 +#define CARDREG_STATUS_BIT_ERROR 19 +#define CARDREG_STATUS_BIT_IOCURRENTSTATE3 12 +#define CARDREG_STATUS_BIT_IOCURRENTSTATE2 11 +#define CARDREG_STATUS_BIT_IOCURRENTSTATE1 10 +#define CARDREG_STATUS_BIT_IOCURRENTSTATE0 9 +#define CARDREG_STATUS_BIT_FUN_NUM_ERROR 4 + + + +#define SD_CMD_GO_IDLE_STATE 0 /* mandatory for SDIO */ +#define SD_CMD_SEND_OPCOND 1 +#define SD_CMD_MMC_SET_RCA 3 +#define SD_CMD_IO_SEND_OP_COND 5 /* mandatory for SDIO */ +#define SD_CMD_SELECT_DESELECT_CARD 7 +#define SD_CMD_SEND_CSD 9 +#define SD_CMD_SEND_CID 10 +#define SD_CMD_STOP_TRANSMISSION 12 +#define SD_CMD_SEND_STATUS 13 +#define SD_CMD_GO_INACTIVE_STATE 15 +#define SD_CMD_SET_BLOCKLEN 16 +#define SD_CMD_READ_SINGLE_BLOCK 17 +#define SD_CMD_READ_MULTIPLE_BLOCK 18 +#define SD_CMD_WRITE_BLOCK 24 +#define SD_CMD_WRITE_MULTIPLE_BLOCK 25 +#define SD_CMD_PROGRAM_CSD 27 +#define SD_CMD_SET_WRITE_PROT 28 +#define SD_CMD_CLR_WRITE_PROT 29 +#define SD_CMD_SEND_WRITE_PROT 30 +#define SD_CMD_ERASE_WR_BLK_START 32 +#define SD_CMD_ERASE_WR_BLK_END 33 +#define SD_CMD_ERASE 38 +#define SD_CMD_LOCK_UNLOCK 42 +#define SD_CMD_IO_RW_DIRECT 52 /* mandatory for SDIO */ +#define SD_CMD_IO_RW_EXTENDED 53 /* mandatory for SDIO */ +#define SD_CMD_APP_CMD 55 +#define SD_CMD_GEN_CMD 56 +#define SD_CMD_READ_OCR 58 +#define SD_CMD_CRC_ON_OFF 59 /* mandatory for SDIO */ +#define SD_ACMD_SD_STATUS 13 +#define SD_ACMD_SEND_NUM_WR_BLOCKS 22 +#define SD_ACMD_SET_WR_BLOCK_ERASE_CNT 23 +#define SD_ACMD_SD_SEND_OP_COND 41 +#define SD_ACMD_SET_CLR_CARD_DETECT 42 +#define SD_ACMD_SEND_SCR 51 + +/* argument for SD_CMD_IO_RW_DIRECT and SD_CMD_IO_RW_EXTENDED */ +#define SD_IO_OP_READ 0 /* Read_Write: Read */ +#define SD_IO_OP_WRITE 1 /* Read_Write: Write */ +#define SD_IO_RW_NORMAL 0 /* no RAW */ +#define SD_IO_RW_RAW 1 /* RAW */ +#define SD_IO_BYTE_MODE 0 /* Byte Mode */ +#define SD_IO_BLOCK_MODE 1 /* BlockMode */ +#define SD_IO_FIXED_ADDRESS 0 /* fix Address */ +#define SD_IO_INCREMENT_ADDRESS 1 /* IncrementAddress */ + +/* build SD_CMD_IO_RW_DIRECT Argument */ +#define SDIO_IO_RW_DIRECT_ARG(rw, raw, func, addr, data) \ + ((((rw) & 1) << 31) | (((func) & 0x7) << 28) | (((raw) & 1) << 27) | \ + (((addr) & 0x1FFFF) << 9) | ((data) & 0xFF)) + +/* build SD_CMD_IO_RW_EXTENDED Argument */ +#define SDIO_IO_RW_EXTENDED_ARG(rw, blk, func, addr, inc_addr, count) \ + ((((rw) & 1) << 31) | (((func) & 0x7) << 28) | (((blk) & 1) << 27) | \ + (((inc_addr) & 1) << 26) | (((addr) & 0x1FFFF) << 9) | ((count) & 0x1FF)) + +/* SDIO response parameters */ +#define SD_RSP_NO_NONE 0 +#define SD_RSP_NO_1 1 +#define SD_RSP_NO_2 2 +#define SD_RSP_NO_3 3 +#define SD_RSP_NO_4 4 +#define SD_RSP_NO_5 5 +#define SD_RSP_NO_6 6 + + /* Modified R6 response (to CMD3) */ +#define SD_RSP_MR6_COM_CRC_ERROR 0x8000 +#define SD_RSP_MR6_ILLEGAL_COMMAND 0x4000 +#define SD_RSP_MR6_ERROR 0x2000 + + /* Modified R1 in R4 Response (to CMD5) */ +#define SD_RSP_MR1_SBIT 0x80 +#define SD_RSP_MR1_PARAMETER_ERROR 0x40 +#define SD_RSP_MR1_RFU5 0x20 +#define SD_RSP_MR1_FUNC_NUM_ERROR 0x10 +#define SD_RSP_MR1_COM_CRC_ERROR 0x08 +#define SD_RSP_MR1_ILLEGAL_COMMAND 0x04 +#define SD_RSP_MR1_RFU1 0x02 +#define SD_RSP_MR1_IDLE_STATE 0x01 + + /* R5 response (to CMD52 and CMD53) */ +#define SD_RSP_R5_COM_CRC_ERROR 0x80 +#define SD_RSP_R5_ILLEGAL_COMMAND 0x40 +#define SD_RSP_R5_IO_CURRENTSTATE1 0x20 +#define SD_RSP_R5_IO_CURRENTSTATE0 0x10 +#define SD_RSP_R5_ERROR 0x08 +#define SD_RSP_R5_RFU 0x04 +#define SD_RSP_R5_FUNC_NUM_ERROR 0x02 +#define SD_RSP_R5_OUT_OF_RANGE 0x01 + +#define SD_RSP_R5_ERRBITS 0xCB + + +/* ------------------------------------------------ + * SDIO Commands and responses + * + * I/O only commands are: + * CMD0, CMD3, CMD5, CMD7, CMD14, CMD15, CMD52, CMD53 + * ------------------------------------------------ + */ + +/* SDIO Commands */ +#define SDIOH_CMD_0 0 +#define SDIOH_CMD_3 3 +#define SDIOH_CMD_5 5 +#define SDIOH_CMD_7 7 +#define SDIOH_CMD_11 11 +#define SDIOH_CMD_14 14 +#define SDIOH_CMD_15 15 +#define SDIOH_CMD_19 19 +#define SDIOH_CMD_52 52 +#define SDIOH_CMD_53 53 +#define SDIOH_CMD_59 59 + +/* SDIO Command Responses */ +#define SDIOH_RSP_NONE 0 +#define SDIOH_RSP_R1 1 +#define SDIOH_RSP_R2 2 +#define SDIOH_RSP_R3 3 +#define SDIOH_RSP_R4 4 +#define SDIOH_RSP_R5 5 +#define SDIOH_RSP_R6 6 + +/* + * SDIO Response Error flags + */ +#define SDIOH_RSP5_ERROR_FLAGS 0xCB + +/* ------------------------------------------------ + * SDIO Command structures. I/O only commands are: + * + * CMD0, CMD3, CMD5, CMD7, CMD15, CMD52, CMD53 + * ------------------------------------------------ + */ + +#define CMD5_OCR_M BITFIELD_MASK(24) +#define CMD5_OCR_S 0 + +#define CMD5_S18R_M BITFIELD_MASK(1) +#define CMD5_S18R_S 24 + +#define CMD7_RCA_M BITFIELD_MASK(16) +#define CMD7_RCA_S 16 + +#define CMD14_RCA_M BITFIELD_MASK(16) +#define CMD14_RCA_S 16 +#define CMD14_SLEEP_M BITFIELD_MASK(1) +#define CMD14_SLEEP_S 15 + +#define CMD_15_RCA_M BITFIELD_MASK(16) +#define CMD_15_RCA_S 16 + +#define CMD52_DATA_M BITFIELD_MASK(8) /* Bits [7:0] - Write Data/Stuff bits of CMD52 + */ +#define CMD52_DATA_S 0 +#define CMD52_REG_ADDR_M BITFIELD_MASK(17) /* Bits [25:9] - register address */ +#define CMD52_REG_ADDR_S 9 +#define CMD52_RAW_M BITFIELD_MASK(1) /* Bit 27 - Read after Write flag */ +#define CMD52_RAW_S 27 +#define CMD52_FUNCTION_M BITFIELD_MASK(3) /* Bits [30:28] - Function number */ +#define CMD52_FUNCTION_S 28 +#define CMD52_RW_FLAG_M BITFIELD_MASK(1) /* Bit 31 - R/W flag */ +#define CMD52_RW_FLAG_S 31 + + +#define CMD53_BYTE_BLK_CNT_M BITFIELD_MASK(9) /* Bits [8:0] - Byte/Block Count of CMD53 */ +#define CMD53_BYTE_BLK_CNT_S 0 +#define CMD53_REG_ADDR_M BITFIELD_MASK(17) /* Bits [25:9] - register address */ +#define CMD53_REG_ADDR_S 9 +#define CMD53_OP_CODE_M BITFIELD_MASK(1) /* Bit 26 - R/W Operation Code */ +#define CMD53_OP_CODE_S 26 +#define CMD53_BLK_MODE_M BITFIELD_MASK(1) /* Bit 27 - Block Mode */ +#define CMD53_BLK_MODE_S 27 +#define CMD53_FUNCTION_M BITFIELD_MASK(3) /* Bits [30:28] - Function number */ +#define CMD53_FUNCTION_S 28 +#define CMD53_RW_FLAG_M BITFIELD_MASK(1) /* Bit 31 - R/W flag */ +#define CMD53_RW_FLAG_S 31 + +/* ------------------------------------------------------ + * SDIO Command Response structures for SD1 and SD4 modes + * ----------------------------------------------------- + */ +#define RSP4_IO_OCR_M BITFIELD_MASK(24) /* Bits [23:0] - Card's OCR Bits [23:0] */ +#define RSP4_IO_OCR_S 0 + +#define RSP4_S18A_M BITFIELD_MASK(1) /* Bits [23:0] - Card's OCR Bits [23:0] */ +#define RSP4_S18A_S 24 + +#define RSP4_STUFF_M BITFIELD_MASK(3) /* Bits [26:24] - Stuff bits */ +#define RSP4_STUFF_S 24 +#define RSP4_MEM_PRESENT_M BITFIELD_MASK(1) /* Bit 27 - Memory present */ +#define RSP4_MEM_PRESENT_S 27 +#define RSP4_NUM_FUNCS_M BITFIELD_MASK(3) /* Bits [30:28] - Number of I/O funcs */ +#define RSP4_NUM_FUNCS_S 28 +#define RSP4_CARD_READY_M BITFIELD_MASK(1) /* Bit 31 - SDIO card ready */ +#define RSP4_CARD_READY_S 31 + +#define RSP6_STATUS_M BITFIELD_MASK(16) /* Bits [15:0] - Card status bits [19,22,23,12:0] + */ +#define RSP6_STATUS_S 0 +#define RSP6_IO_RCA_M BITFIELD_MASK(16) /* Bits [31:16] - RCA bits[31-16] */ +#define RSP6_IO_RCA_S 16 + +#define RSP1_AKE_SEQ_ERROR_M BITFIELD_MASK(1) /* Bit 3 - Authentication seq error */ +#define RSP1_AKE_SEQ_ERROR_S 3 +#define RSP1_APP_CMD_M BITFIELD_MASK(1) /* Bit 5 - Card expects ACMD */ +#define RSP1_APP_CMD_S 5 +#define RSP1_READY_FOR_DATA_M BITFIELD_MASK(1) /* Bit 8 - Ready for data (buff empty) */ +#define RSP1_READY_FOR_DATA_S 8 +#define RSP1_CURR_STATE_M BITFIELD_MASK(4) /* Bits [12:9] - State of card + * when Cmd was received + */ +#define RSP1_CURR_STATE_S 9 +#define RSP1_EARSE_RESET_M BITFIELD_MASK(1) /* Bit 13 - Erase seq cleared */ +#define RSP1_EARSE_RESET_S 13 +#define RSP1_CARD_ECC_DISABLE_M BITFIELD_MASK(1) /* Bit 14 - Card ECC disabled */ +#define RSP1_CARD_ECC_DISABLE_S 14 +#define RSP1_WP_ERASE_SKIP_M BITFIELD_MASK(1) /* Bit 15 - Partial blocks erased due to W/P */ +#define RSP1_WP_ERASE_SKIP_S 15 +#define RSP1_CID_CSD_OVERW_M BITFIELD_MASK(1) /* Bit 16 - Illegal write to CID or R/O bits + * of CSD + */ +#define RSP1_CID_CSD_OVERW_S 16 +#define RSP1_ERROR_M BITFIELD_MASK(1) /* Bit 19 - General/Unknown error */ +#define RSP1_ERROR_S 19 +#define RSP1_CC_ERROR_M BITFIELD_MASK(1) /* Bit 20 - Internal Card Control error */ +#define RSP1_CC_ERROR_S 20 +#define RSP1_CARD_ECC_FAILED_M BITFIELD_MASK(1) /* Bit 21 - Card internal ECC failed + * to correct data + */ +#define RSP1_CARD_ECC_FAILED_S 21 +#define RSP1_ILLEGAL_CMD_M BITFIELD_MASK(1) /* Bit 22 - Cmd not legal for the card state */ +#define RSP1_ILLEGAL_CMD_S 22 +#define RSP1_COM_CRC_ERROR_M BITFIELD_MASK(1) /* Bit 23 - CRC check of previous command failed + */ +#define RSP1_COM_CRC_ERROR_S 23 +#define RSP1_LOCK_UNLOCK_FAIL_M BITFIELD_MASK(1) /* Bit 24 - Card lock-unlock Cmd Seq error */ +#define RSP1_LOCK_UNLOCK_FAIL_S 24 +#define RSP1_CARD_LOCKED_M BITFIELD_MASK(1) /* Bit 25 - Card locked by the host */ +#define RSP1_CARD_LOCKED_S 25 +#define RSP1_WP_VIOLATION_M BITFIELD_MASK(1) /* Bit 26 - Attempt to program + * write-protected blocks + */ +#define RSP1_WP_VIOLATION_S 26 +#define RSP1_ERASE_PARAM_M BITFIELD_MASK(1) /* Bit 27 - Invalid erase blocks */ +#define RSP1_ERASE_PARAM_S 27 +#define RSP1_ERASE_SEQ_ERR_M BITFIELD_MASK(1) /* Bit 28 - Erase Cmd seq error */ +#define RSP1_ERASE_SEQ_ERR_S 28 +#define RSP1_BLK_LEN_ERR_M BITFIELD_MASK(1) /* Bit 29 - Block length error */ +#define RSP1_BLK_LEN_ERR_S 29 +#define RSP1_ADDR_ERR_M BITFIELD_MASK(1) /* Bit 30 - Misaligned address */ +#define RSP1_ADDR_ERR_S 30 +#define RSP1_OUT_OF_RANGE_M BITFIELD_MASK(1) /* Bit 31 - Cmd arg was out of range */ +#define RSP1_OUT_OF_RANGE_S 31 + + +#define RSP5_DATA_M BITFIELD_MASK(8) /* Bits [0:7] - data */ +#define RSP5_DATA_S 0 +#define RSP5_FLAGS_M BITFIELD_MASK(8) /* Bit [15:8] - Rsp flags */ +#define RSP5_FLAGS_S 8 +#define RSP5_STUFF_M BITFIELD_MASK(16) /* Bits [31:16] - Stuff bits */ +#define RSP5_STUFF_S 16 + +/* ---------------------------------------------- + * SDIO Command Response structures for SPI mode + * ---------------------------------------------- + */ +#define SPIRSP4_IO_OCR_M BITFIELD_MASK(16) /* Bits [15:0] - Card's OCR Bits [23:8] */ +#define SPIRSP4_IO_OCR_S 0 +#define SPIRSP4_STUFF_M BITFIELD_MASK(3) /* Bits [18:16] - Stuff bits */ +#define SPIRSP4_STUFF_S 16 +#define SPIRSP4_MEM_PRESENT_M BITFIELD_MASK(1) /* Bit 19 - Memory present */ +#define SPIRSP4_MEM_PRESENT_S 19 +#define SPIRSP4_NUM_FUNCS_M BITFIELD_MASK(3) /* Bits [22:20] - Number of I/O funcs */ +#define SPIRSP4_NUM_FUNCS_S 20 +#define SPIRSP4_CARD_READY_M BITFIELD_MASK(1) /* Bit 23 - SDIO card ready */ +#define SPIRSP4_CARD_READY_S 23 +#define SPIRSP4_IDLE_STATE_M BITFIELD_MASK(1) /* Bit 24 - idle state */ +#define SPIRSP4_IDLE_STATE_S 24 +#define SPIRSP4_ILLEGAL_CMD_M BITFIELD_MASK(1) /* Bit 26 - Illegal Cmd error */ +#define SPIRSP4_ILLEGAL_CMD_S 26 +#define SPIRSP4_COM_CRC_ERROR_M BITFIELD_MASK(1) /* Bit 27 - COM CRC error */ +#define SPIRSP4_COM_CRC_ERROR_S 27 +#define SPIRSP4_FUNC_NUM_ERROR_M BITFIELD_MASK(1) /* Bit 28 - Function number error + */ +#define SPIRSP4_FUNC_NUM_ERROR_S 28 +#define SPIRSP4_PARAM_ERROR_M BITFIELD_MASK(1) /* Bit 30 - Parameter Error Bit */ +#define SPIRSP4_PARAM_ERROR_S 30 +#define SPIRSP4_START_BIT_M BITFIELD_MASK(1) /* Bit 31 - Start Bit */ +#define SPIRSP4_START_BIT_S 31 + +#define SPIRSP5_DATA_M BITFIELD_MASK(8) /* Bits [23:16] - R/W Data */ +#define SPIRSP5_DATA_S 16 +#define SPIRSP5_IDLE_STATE_M BITFIELD_MASK(1) /* Bit 24 - Idle state */ +#define SPIRSP5_IDLE_STATE_S 24 +#define SPIRSP5_ILLEGAL_CMD_M BITFIELD_MASK(1) /* Bit 26 - Illegal Cmd error */ +#define SPIRSP5_ILLEGAL_CMD_S 26 +#define SPIRSP5_COM_CRC_ERROR_M BITFIELD_MASK(1) /* Bit 27 - COM CRC error */ +#define SPIRSP5_COM_CRC_ERROR_S 27 +#define SPIRSP5_FUNC_NUM_ERROR_M BITFIELD_MASK(1) /* Bit 28 - Function number error + */ +#define SPIRSP5_FUNC_NUM_ERROR_S 28 +#define SPIRSP5_PARAM_ERROR_M BITFIELD_MASK(1) /* Bit 30 - Parameter Error Bit */ +#define SPIRSP5_PARAM_ERROR_S 30 +#define SPIRSP5_START_BIT_M BITFIELD_MASK(1) /* Bit 31 - Start Bit */ +#define SPIRSP5_START_BIT_S 31 + +/* RSP6 card status format; Pg 68 Physical Layer spec v 1.10 */ +#define RSP6STAT_AKE_SEQ_ERROR_M BITFIELD_MASK(1) /* Bit 3 - Authentication seq error + */ +#define RSP6STAT_AKE_SEQ_ERROR_S 3 +#define RSP6STAT_APP_CMD_M BITFIELD_MASK(1) /* Bit 5 - Card expects ACMD */ +#define RSP6STAT_APP_CMD_S 5 +#define RSP6STAT_READY_FOR_DATA_M BITFIELD_MASK(1) /* Bit 8 - Ready for data + * (buff empty) + */ +#define RSP6STAT_READY_FOR_DATA_S 8 +#define RSP6STAT_CURR_STATE_M BITFIELD_MASK(4) /* Bits [12:9] - Card state at + * Cmd reception + */ +#define RSP6STAT_CURR_STATE_S 9 +#define RSP6STAT_ERROR_M BITFIELD_MASK(1) /* Bit 13 - General/Unknown error Bit 19 + */ +#define RSP6STAT_ERROR_S 13 +#define RSP6STAT_ILLEGAL_CMD_M BITFIELD_MASK(1) /* Bit 14 - Illegal cmd for + * card state Bit 22 + */ +#define RSP6STAT_ILLEGAL_CMD_S 14 +#define RSP6STAT_COM_CRC_ERROR_M BITFIELD_MASK(1) /* Bit 15 - CRC previous command + * failed Bit 23 + */ +#define RSP6STAT_COM_CRC_ERROR_S 15 + +#define SDIOH_XFER_TYPE_READ SD_IO_OP_READ +#define SDIOH_XFER_TYPE_WRITE SD_IO_OP_WRITE + +/* command issue options */ +#define CMD_OPTION_DEFAULT 0 +#define CMD_OPTION_TUNING 1 +#endif /* _SDIO_H */ diff --git a/drivers/net/wireless/bcmdhd/include/sdioh.h b/drivers/net/wireless/bcmdhd/include/sdioh.h new file mode 100644 index 00000000..5517a718 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/sdioh.h @@ -0,0 +1,445 @@ +/* + * SDIO Host Controller Spec header file + * Register map and definitions for the Standard Host Controller + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: sdioh.h 347633 2012-07-27 11:02:02Z $ + */ + +#ifndef _SDIOH_H +#define _SDIOH_H + +#define SD_SysAddr 0x000 +#define SD_BlockSize 0x004 +#define SD_BlockCount 0x006 +#define SD_Arg0 0x008 +#define SD_Arg1 0x00A +#define SD_TransferMode 0x00C +#define SD_Command 0x00E +#define SD_Response0 0x010 +#define SD_Response1 0x012 +#define SD_Response2 0x014 +#define SD_Response3 0x016 +#define SD_Response4 0x018 +#define SD_Response5 0x01A +#define SD_Response6 0x01C +#define SD_Response7 0x01E +#define SD_BufferDataPort0 0x020 +#define SD_BufferDataPort1 0x022 +#define SD_PresentState 0x024 +#define SD_HostCntrl 0x028 +#define SD_PwrCntrl 0x029 +#define SD_BlockGapCntrl 0x02A +#define SD_WakeupCntrl 0x02B +#define SD_ClockCntrl 0x02C +#define SD_TimeoutCntrl 0x02E +#define SD_SoftwareReset 0x02F +#define SD_IntrStatus 0x030 +#define SD_ErrorIntrStatus 0x032 +#define SD_IntrStatusEnable 0x034 +#define SD_ErrorIntrStatusEnable 0x036 +#define SD_IntrSignalEnable 0x038 +#define SD_ErrorIntrSignalEnable 0x03A +#define SD_CMD12ErrorStatus 0x03C +#define SD_Capabilities 0x040 +#define SD_Capabilities3 0x044 +#define SD_MaxCurCap 0x048 +#define SD_MaxCurCap_Reserved 0x04C +#define SD_ADMA_ErrStatus 0x054 +#define SD_ADMA_SysAddr 0x58 +#define SD_SlotInterruptStatus 0x0FC +#define SD_HostControllerVersion 0x0FE +#define SD_GPIO_Reg 0x100 +#define SD_GPIO_OE 0x104 +#define SD_GPIO_Enable 0x108 + +/* SD specific registers in PCI config space */ +#define SD_SlotInfo 0x40 + +/* HC 3.0 specific registers and offsets */ +#define SD3_HostCntrl2 0x03E +/* preset regsstart and count */ +#define SD3_PresetValStart 0x060 +#define SD3_PresetValCount 8 +/* preset-indiv regs */ +#define SD3_PresetVal_init 0x060 +#define SD3_PresetVal_default 0x062 +#define SD3_PresetVal_HS 0x064 +#define SD3_PresetVal_SDR12 0x066 +#define SD3_PresetVal_SDR25 0x068 +#define SD3_PresetVal_SDR50 0x06a +#define SD3_PresetVal_SDR104 0x06c +#define SD3_PresetVal_DDR50 0x06e +/* SDIO3.0 Revx specific Registers */ +#define SD3_Tuning_Info_Register 0x0EC +#define SD3_WL_BT_reset_register 0x0F0 + + +/* preset value indices */ +#define SD3_PRESETVAL_INITIAL_IX 0 +#define SD3_PRESETVAL_DESPEED_IX 1 +#define SD3_PRESETVAL_HISPEED_IX 2 +#define SD3_PRESETVAL_SDR12_IX 3 +#define SD3_PRESETVAL_SDR25_IX 4 +#define SD3_PRESETVAL_SDR50_IX 5 +#define SD3_PRESETVAL_SDR104_IX 6 +#define SD3_PRESETVAL_DDR50_IX 7 + +/* SD_Capabilities reg (0x040) */ +#define CAP_TO_CLKFREQ_M BITFIELD_MASK(6) +#define CAP_TO_CLKFREQ_S 0 +#define CAP_TO_CLKUNIT_M BITFIELD_MASK(1) +#define CAP_TO_CLKUNIT_S 7 +/* Note: for sdio-2.0 case, this mask has to be 6 bits, but msb 2 + bits are reserved. going ahead with 8 bits, as it is req for 3.0 +*/ +#define CAP_BASECLK_M BITFIELD_MASK(8) +#define CAP_BASECLK_S 8 +#define CAP_MAXBLOCK_M BITFIELD_MASK(2) +#define CAP_MAXBLOCK_S 16 +#define CAP_ADMA2_M BITFIELD_MASK(1) +#define CAP_ADMA2_S 19 +#define CAP_ADMA1_M BITFIELD_MASK(1) +#define CAP_ADMA1_S 20 +#define CAP_HIGHSPEED_M BITFIELD_MASK(1) +#define CAP_HIGHSPEED_S 21 +#define CAP_DMA_M BITFIELD_MASK(1) +#define CAP_DMA_S 22 +#define CAP_SUSPEND_M BITFIELD_MASK(1) +#define CAP_SUSPEND_S 23 +#define CAP_VOLT_3_3_M BITFIELD_MASK(1) +#define CAP_VOLT_3_3_S 24 +#define CAP_VOLT_3_0_M BITFIELD_MASK(1) +#define CAP_VOLT_3_0_S 25 +#define CAP_VOLT_1_8_M BITFIELD_MASK(1) +#define CAP_VOLT_1_8_S 26 +#define CAP_64BIT_HOST_M BITFIELD_MASK(1) +#define CAP_64BIT_HOST_S 28 + +#define SDIO_OCR_READ_FAIL (2) + + +#define CAP_ASYNCINT_SUP_M BITFIELD_MASK(1) +#define CAP_ASYNCINT_SUP_S 29 + +#define CAP_SLOTTYPE_M BITFIELD_MASK(2) +#define CAP_SLOTTYPE_S 30 + +#define CAP3_MSBits_OFFSET (32) +/* note: following are caps MSB32 bits. + So the bits start from 0, instead of 32. that is why + CAP3_MSBits_OFFSET is subtracted. +*/ +#define CAP3_SDR50_SUP_M BITFIELD_MASK(1) +#define CAP3_SDR50_SUP_S (32 - CAP3_MSBits_OFFSET) + +#define CAP3_SDR104_SUP_M BITFIELD_MASK(1) +#define CAP3_SDR104_SUP_S (33 - CAP3_MSBits_OFFSET) + +#define CAP3_DDR50_SUP_M BITFIELD_MASK(1) +#define CAP3_DDR50_SUP_S (34 - CAP3_MSBits_OFFSET) + +/* for knowing the clk caps in a single read */ +#define CAP3_30CLKCAP_M BITFIELD_MASK(3) +#define CAP3_30CLKCAP_S (32 - CAP3_MSBits_OFFSET) + +#define CAP3_DRIVTYPE_A_M BITFIELD_MASK(1) +#define CAP3_DRIVTYPE_A_S (36 - CAP3_MSBits_OFFSET) + +#define CAP3_DRIVTYPE_C_M BITFIELD_MASK(1) +#define CAP3_DRIVTYPE_C_S (37 - CAP3_MSBits_OFFSET) + +#define CAP3_DRIVTYPE_D_M BITFIELD_MASK(1) +#define CAP3_DRIVTYPE_D_S (38 - CAP3_MSBits_OFFSET) + +#define CAP3_RETUNING_TC_M BITFIELD_MASK(4) +#define CAP3_RETUNING_TC_S (40 - CAP3_MSBits_OFFSET) + +#define CAP3_TUNING_SDR50_M BITFIELD_MASK(1) +#define CAP3_TUNING_SDR50_S (45 - CAP3_MSBits_OFFSET) + +#define CAP3_RETUNING_MODES_M BITFIELD_MASK(2) +#define CAP3_RETUNING_MODES_S (46 - CAP3_MSBits_OFFSET) + +#define CAP3_CLK_MULT_M BITFIELD_MASK(8) +#define CAP3_CLK_MULT_S (48 - CAP3_MSBits_OFFSET) + +#define PRESET_DRIVR_SELECT_M BITFIELD_MASK(2) +#define PRESET_DRIVR_SELECT_S 14 + +#define PRESET_CLK_DIV_M BITFIELD_MASK(10) +#define PRESET_CLK_DIV_S 0 + +/* SD_MaxCurCap reg (0x048) */ +#define CAP_CURR_3_3_M BITFIELD_MASK(8) +#define CAP_CURR_3_3_S 0 +#define CAP_CURR_3_0_M BITFIELD_MASK(8) +#define CAP_CURR_3_0_S 8 +#define CAP_CURR_1_8_M BITFIELD_MASK(8) +#define CAP_CURR_1_8_S 16 + +/* SD_SysAddr: Offset 0x0000, Size 4 bytes */ + +/* SD_BlockSize: Offset 0x004, Size 2 bytes */ +#define BLKSZ_BLKSZ_M BITFIELD_MASK(12) +#define BLKSZ_BLKSZ_S 0 +#define BLKSZ_BNDRY_M BITFIELD_MASK(3) +#define BLKSZ_BNDRY_S 12 + +/* SD_BlockCount: Offset 0x006, size 2 bytes */ + +/* SD_Arg0: Offset 0x008, size = 4 bytes */ +/* SD_TransferMode Offset 0x00C, size = 2 bytes */ +#define XFER_DMA_ENABLE_M BITFIELD_MASK(1) +#define XFER_DMA_ENABLE_S 0 +#define XFER_BLK_COUNT_EN_M BITFIELD_MASK(1) +#define XFER_BLK_COUNT_EN_S 1 +#define XFER_CMD_12_EN_M BITFIELD_MASK(1) +#define XFER_CMD_12_EN_S 2 +#define XFER_DATA_DIRECTION_M BITFIELD_MASK(1) +#define XFER_DATA_DIRECTION_S 4 +#define XFER_MULTI_BLOCK_M BITFIELD_MASK(1) +#define XFER_MULTI_BLOCK_S 5 + +/* SD_Command: Offset 0x00E, size = 2 bytes */ +/* resp_type field */ +#define RESP_TYPE_NONE 0 +#define RESP_TYPE_136 1 +#define RESP_TYPE_48 2 +#define RESP_TYPE_48_BUSY 3 +/* type field */ +#define CMD_TYPE_NORMAL 0 +#define CMD_TYPE_SUSPEND 1 +#define CMD_TYPE_RESUME 2 +#define CMD_TYPE_ABORT 3 + +#define CMD_RESP_TYPE_M BITFIELD_MASK(2) /* Bits [0-1] - Response type */ +#define CMD_RESP_TYPE_S 0 +#define CMD_CRC_EN_M BITFIELD_MASK(1) /* Bit 3 - CRC enable */ +#define CMD_CRC_EN_S 3 +#define CMD_INDEX_EN_M BITFIELD_MASK(1) /* Bit 4 - Enable index checking */ +#define CMD_INDEX_EN_S 4 +#define CMD_DATA_EN_M BITFIELD_MASK(1) /* Bit 5 - Using DAT line */ +#define CMD_DATA_EN_S 5 +#define CMD_TYPE_M BITFIELD_MASK(2) /* Bit [6-7] - Normal, abort, resume, etc + */ +#define CMD_TYPE_S 6 +#define CMD_INDEX_M BITFIELD_MASK(6) /* Bits [8-13] - Command number */ +#define CMD_INDEX_S 8 + +/* SD_BufferDataPort0 : Offset 0x020, size = 2 or 4 bytes */ +/* SD_BufferDataPort1 : Offset 0x022, size = 2 bytes */ +/* SD_PresentState : Offset 0x024, size = 4 bytes */ +#define PRES_CMD_INHIBIT_M BITFIELD_MASK(1) /* Bit 0 May use CMD */ +#define PRES_CMD_INHIBIT_S 0 +#define PRES_DAT_INHIBIT_M BITFIELD_MASK(1) /* Bit 1 May use DAT */ +#define PRES_DAT_INHIBIT_S 1 +#define PRES_DAT_BUSY_M BITFIELD_MASK(1) /* Bit 2 DAT is busy */ +#define PRES_DAT_BUSY_S 2 +#define PRES_PRESENT_RSVD_M BITFIELD_MASK(5) /* Bit [3-7] rsvd */ +#define PRES_PRESENT_RSVD_S 3 +#define PRES_WRITE_ACTIVE_M BITFIELD_MASK(1) /* Bit 8 Write is active */ +#define PRES_WRITE_ACTIVE_S 8 +#define PRES_READ_ACTIVE_M BITFIELD_MASK(1) /* Bit 9 Read is active */ +#define PRES_READ_ACTIVE_S 9 +#define PRES_WRITE_DATA_RDY_M BITFIELD_MASK(1) /* Bit 10 Write buf is avail */ +#define PRES_WRITE_DATA_RDY_S 10 +#define PRES_READ_DATA_RDY_M BITFIELD_MASK(1) /* Bit 11 Read buf data avail */ +#define PRES_READ_DATA_RDY_S 11 +#define PRES_CARD_PRESENT_M BITFIELD_MASK(1) /* Bit 16 Card present - debounced */ +#define PRES_CARD_PRESENT_S 16 +#define PRES_CARD_STABLE_M BITFIELD_MASK(1) /* Bit 17 Debugging */ +#define PRES_CARD_STABLE_S 17 +#define PRES_CARD_PRESENT_RAW_M BITFIELD_MASK(1) /* Bit 18 Not debounced */ +#define PRES_CARD_PRESENT_RAW_S 18 +#define PRES_WRITE_ENABLED_M BITFIELD_MASK(1) /* Bit 19 Write protected? */ +#define PRES_WRITE_ENABLED_S 19 +#define PRES_DAT_SIGNAL_M BITFIELD_MASK(4) /* Bit [20-23] Debugging */ +#define PRES_DAT_SIGNAL_S 20 +#define PRES_CMD_SIGNAL_M BITFIELD_MASK(1) /* Bit 24 Debugging */ +#define PRES_CMD_SIGNAL_S 24 + +/* SD_HostCntrl: Offset 0x028, size = 1 bytes */ +#define HOST_LED_M BITFIELD_MASK(1) /* Bit 0 LED On/Off */ +#define HOST_LED_S 0 +#define HOST_DATA_WIDTH_M BITFIELD_MASK(1) /* Bit 1 4 bit enable */ +#define HOST_DATA_WIDTH_S 1 +#define HOST_HI_SPEED_EN_M BITFIELD_MASK(1) /* Bit 2 High speed vs low speed */ +#define HOST_DMA_SEL_S 3 +#define HOST_DMA_SEL_M BITFIELD_MASK(2) /* Bit 4:3 DMA Select */ +#define HOST_HI_SPEED_EN_S 2 + +/* Host Control2: */ +#define HOSTCtrl2_PRESVAL_EN_M BITFIELD_MASK(1) /* 1 bit */ +#define HOSTCtrl2_PRESVAL_EN_S 15 /* bit# */ + +#define HOSTCtrl2_ASYINT_EN_M BITFIELD_MASK(1) /* 1 bit */ +#define HOSTCtrl2_ASYINT_EN_S 14 /* bit# */ + +#define HOSTCtrl2_SAMPCLK_SEL_M BITFIELD_MASK(1) /* 1 bit */ +#define HOSTCtrl2_SAMPCLK_SEL_S 7 /* bit# */ + +#define HOSTCtrl2_EXEC_TUNING_M BITFIELD_MASK(1) /* 1 bit */ +#define HOSTCtrl2_EXEC_TUNING_S 6 /* bit# */ + +#define HOSTCtrl2_DRIVSTRENGTH_SEL_M BITFIELD_MASK(2) /* 2 bit */ +#define HOSTCtrl2_DRIVSTRENGTH_SEL_S 4 /* bit# */ + +#define HOSTCtrl2_1_8SIG_EN_M BITFIELD_MASK(1) /* 1 bit */ +#define HOSTCtrl2_1_8SIG_EN_S 3 /* bit# */ + +#define HOSTCtrl2_UHSMODE_SEL_M BITFIELD_MASK(3) /* 3 bit */ +#define HOSTCtrl2_UHSMODE_SEL_S 0 /* bit# */ + +#define HOST_CONTR_VER_2 (1) +#define HOST_CONTR_VER_3 (2) + +/* misc defines */ +#define SD1_MODE 0x1 /* SD Host Cntrlr Spec */ +#define SD4_MODE 0x2 /* SD Host Cntrlr Spec */ + +/* SD_PwrCntrl: Offset 0x029, size = 1 bytes */ +#define PWR_BUS_EN_M BITFIELD_MASK(1) /* Bit 0 Power the bus */ +#define PWR_BUS_EN_S 0 +#define PWR_VOLTS_M BITFIELD_MASK(3) /* Bit [1-3] Voltage Select */ +#define PWR_VOLTS_S 1 + +/* SD_SoftwareReset: Offset 0x02F, size = 1 byte */ +#define SW_RESET_ALL_M BITFIELD_MASK(1) /* Bit 0 Reset All */ +#define SW_RESET_ALL_S 0 +#define SW_RESET_CMD_M BITFIELD_MASK(1) /* Bit 1 CMD Line Reset */ +#define SW_RESET_CMD_S 1 +#define SW_RESET_DAT_M BITFIELD_MASK(1) /* Bit 2 DAT Line Reset */ +#define SW_RESET_DAT_S 2 + +/* SD_IntrStatus: Offset 0x030, size = 2 bytes */ +/* Defs also serve SD_IntrStatusEnable and SD_IntrSignalEnable */ +#define INTSTAT_CMD_COMPLETE_M BITFIELD_MASK(1) /* Bit 0 */ +#define INTSTAT_CMD_COMPLETE_S 0 +#define INTSTAT_XFER_COMPLETE_M BITFIELD_MASK(1) +#define INTSTAT_XFER_COMPLETE_S 1 +#define INTSTAT_BLOCK_GAP_EVENT_M BITFIELD_MASK(1) +#define INTSTAT_BLOCK_GAP_EVENT_S 2 +#define INTSTAT_DMA_INT_M BITFIELD_MASK(1) +#define INTSTAT_DMA_INT_S 3 +#define INTSTAT_BUF_WRITE_READY_M BITFIELD_MASK(1) +#define INTSTAT_BUF_WRITE_READY_S 4 +#define INTSTAT_BUF_READ_READY_M BITFIELD_MASK(1) +#define INTSTAT_BUF_READ_READY_S 5 +#define INTSTAT_CARD_INSERTION_M BITFIELD_MASK(1) +#define INTSTAT_CARD_INSERTION_S 6 +#define INTSTAT_CARD_REMOVAL_M BITFIELD_MASK(1) +#define INTSTAT_CARD_REMOVAL_S 7 +#define INTSTAT_CARD_INT_M BITFIELD_MASK(1) +#define INTSTAT_CARD_INT_S 8 +#define INTSTAT_RETUNING_INT_M BITFIELD_MASK(1) /* Bit 12 */ +#define INTSTAT_RETUNING_INT_S 12 +#define INTSTAT_ERROR_INT_M BITFIELD_MASK(1) /* Bit 15 */ +#define INTSTAT_ERROR_INT_S 15 + +/* SD_ErrorIntrStatus: Offset 0x032, size = 2 bytes */ +/* Defs also serve SD_ErrorIntrStatusEnable and SD_ErrorIntrSignalEnable */ +#define ERRINT_CMD_TIMEOUT_M BITFIELD_MASK(1) +#define ERRINT_CMD_TIMEOUT_S 0 +#define ERRINT_CMD_CRC_M BITFIELD_MASK(1) +#define ERRINT_CMD_CRC_S 1 +#define ERRINT_CMD_ENDBIT_M BITFIELD_MASK(1) +#define ERRINT_CMD_ENDBIT_S 2 +#define ERRINT_CMD_INDEX_M BITFIELD_MASK(1) +#define ERRINT_CMD_INDEX_S 3 +#define ERRINT_DATA_TIMEOUT_M BITFIELD_MASK(1) +#define ERRINT_DATA_TIMEOUT_S 4 +#define ERRINT_DATA_CRC_M BITFIELD_MASK(1) +#define ERRINT_DATA_CRC_S 5 +#define ERRINT_DATA_ENDBIT_M BITFIELD_MASK(1) +#define ERRINT_DATA_ENDBIT_S 6 +#define ERRINT_CURRENT_LIMIT_M BITFIELD_MASK(1) +#define ERRINT_CURRENT_LIMIT_S 7 +#define ERRINT_AUTO_CMD12_M BITFIELD_MASK(1) +#define ERRINT_AUTO_CMD12_S 8 +#define ERRINT_VENDOR_M BITFIELD_MASK(4) +#define ERRINT_VENDOR_S 12 +#define ERRINT_ADMA_M BITFIELD_MASK(1) +#define ERRINT_ADMA_S 9 + +/* Also provide definitions in "normal" form to allow combined masks */ +#define ERRINT_CMD_TIMEOUT_BIT 0x0001 +#define ERRINT_CMD_CRC_BIT 0x0002 +#define ERRINT_CMD_ENDBIT_BIT 0x0004 +#define ERRINT_CMD_INDEX_BIT 0x0008 +#define ERRINT_DATA_TIMEOUT_BIT 0x0010 +#define ERRINT_DATA_CRC_BIT 0x0020 +#define ERRINT_DATA_ENDBIT_BIT 0x0040 +#define ERRINT_CURRENT_LIMIT_BIT 0x0080 +#define ERRINT_AUTO_CMD12_BIT 0x0100 +#define ERRINT_ADMA_BIT 0x0200 + +/* Masks to select CMD vs. DATA errors */ +#define ERRINT_CMD_ERRS (ERRINT_CMD_TIMEOUT_BIT | ERRINT_CMD_CRC_BIT |\ + ERRINT_CMD_ENDBIT_BIT | ERRINT_CMD_INDEX_BIT) +#define ERRINT_DATA_ERRS (ERRINT_DATA_TIMEOUT_BIT | ERRINT_DATA_CRC_BIT |\ + ERRINT_DATA_ENDBIT_BIT | ERRINT_ADMA_BIT) +#define ERRINT_TRANSFER_ERRS (ERRINT_CMD_ERRS | ERRINT_DATA_ERRS) + +/* SD_WakeupCntr_BlockGapCntrl : Offset 0x02A , size = bytes */ +/* SD_ClockCntrl : Offset 0x02C , size = bytes */ +/* SD_SoftwareReset_TimeoutCntrl : Offset 0x02E , size = bytes */ +/* SD_IntrStatus : Offset 0x030 , size = bytes */ +/* SD_ErrorIntrStatus : Offset 0x032 , size = bytes */ +/* SD_IntrStatusEnable : Offset 0x034 , size = bytes */ +/* SD_ErrorIntrStatusEnable : Offset 0x036 , size = bytes */ +/* SD_IntrSignalEnable : Offset 0x038 , size = bytes */ +/* SD_ErrorIntrSignalEnable : Offset 0x03A , size = bytes */ +/* SD_CMD12ErrorStatus : Offset 0x03C , size = bytes */ +/* SD_Capabilities : Offset 0x040 , size = bytes */ +/* SD_MaxCurCap : Offset 0x048 , size = bytes */ +/* SD_MaxCurCap_Reserved: Offset 0x04C , size = bytes */ +/* SD_SlotInterruptStatus: Offset 0x0FC , size = bytes */ +/* SD_HostControllerVersion : Offset 0x0FE , size = bytes */ + +/* SDIO Host Control Register DMA Mode Definitions */ +#define SDIOH_SDMA_MODE 0 +#define SDIOH_ADMA1_MODE 1 +#define SDIOH_ADMA2_MODE 2 +#define SDIOH_ADMA2_64_MODE 3 + +#define ADMA2_ATTRIBUTE_VALID (1 << 0) /* ADMA Descriptor line valid */ +#define ADMA2_ATTRIBUTE_END (1 << 1) /* End of Descriptor */ +#define ADMA2_ATTRIBUTE_INT (1 << 2) /* Interrupt when line is done */ +#define ADMA2_ATTRIBUTE_ACT_NOP (0 << 4) /* Skip current line, go to next. */ +#define ADMA2_ATTRIBUTE_ACT_RSV (1 << 4) /* Same as NOP */ +#define ADMA1_ATTRIBUTE_ACT_SET (1 << 4) /* ADMA1 Only - set transfer length */ +#define ADMA2_ATTRIBUTE_ACT_TRAN (2 << 4) /* Transfer Data of one descriptor line. */ +#define ADMA2_ATTRIBUTE_ACT_LINK (3 << 4) /* Link Descriptor */ + +/* ADMA2 Descriptor Table Entry for 32-bit Address */ +typedef struct adma2_dscr_32b { + uint32 len_attr; + uint32 phys_addr; +} adma2_dscr_32b_t; + +/* ADMA1 Descriptor Table Entry */ +typedef struct adma1_dscr { + uint32 phys_addr_attr; +} adma1_dscr_t; + +#endif /* _SDIOH_H */ diff --git a/drivers/net/wireless/bcmdhd/include/sdiovar.h b/drivers/net/wireless/bcmdhd/include/sdiovar.h new file mode 100644 index 00000000..83f82de2 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/sdiovar.h @@ -0,0 +1,58 @@ +/* + * Structure used by apps whose drivers access SDIO drivers. + * Pulled out separately so dhdu and wlu can both use it. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: sdiovar.h 241182 2011-02-17 21:50:03Z $ + */ + +#ifndef _sdiovar_h_ +#define _sdiovar_h_ + +#include + +/* require default structure packing */ +#define BWL_DEFAULT_PACKING +#include + +typedef struct sdreg { + int func; + int offset; + int value; +} sdreg_t; + +/* Common msglevel constants */ +#define SDH_ERROR_VAL 0x0001 /* Error */ +#define SDH_TRACE_VAL 0x0002 /* Trace */ +#define SDH_INFO_VAL 0x0004 /* Info */ +#define SDH_DEBUG_VAL 0x0008 /* Debug */ +#define SDH_DATA_VAL 0x0010 /* Data */ +#define SDH_CTRL_VAL 0x0020 /* Control Regs */ +#define SDH_LOG_VAL 0x0040 /* Enable bcmlog */ +#define SDH_DMA_VAL 0x0080 /* DMA */ + +#define NUM_PREV_TRANSACTIONS 16 + + +#include + +#endif /* _sdiovar_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/siutils.h b/drivers/net/wireless/bcmdhd/include/siutils.h new file mode 100644 index 00000000..a797b3d1 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/siutils.h @@ -0,0 +1,332 @@ +/* + * Misc utility routines for accessing the SOC Interconnects + * of Broadcom HNBU chips. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: siutils.h 347614 2012-07-27 10:24:51Z $ + */ + +#ifndef _siutils_h_ +#define _siutils_h_ + + +struct si_pub { + uint socitype; + + uint bustype; + uint buscoretype; + uint buscorerev; + uint buscoreidx; + int ccrev; + uint32 cccaps; + uint32 cccaps_ext; + int pmurev; + uint32 pmucaps; + uint boardtype; + uint boardrev; + uint boardvendor; + uint boardflags; + uint boardflags2; + uint chip; + uint chiprev; + uint chippkg; + uint32 chipst; + bool issim; + uint socirev; + bool pci_pr32414; + +}; + + +typedef const struct si_pub si_t; + + + +#define SI_OSH NULL + +#define BADIDX (SI_MAXCORES + 1) + + +#define XTAL 0x1 +#define PLL 0x2 + + +#define CLK_FAST 0 +#define CLK_DYNAMIC 2 + + +#define GPIO_DRV_PRIORITY 0 +#define GPIO_APP_PRIORITY 1 +#define GPIO_HI_PRIORITY 2 + + +#define GPIO_PULLUP 0 +#define GPIO_PULLDN 1 + + +#define GPIO_REGEVT 0 +#define GPIO_REGEVT_INTMSK 1 +#define GPIO_REGEVT_INTPOL 2 + + +#define SI_DEVPATH_BUFSZ 16 + + +#define SI_DOATTACH 1 +#define SI_PCIDOWN 2 +#define SI_PCIUP 3 + +#define ISSIM_ENAB(sih) 0 + + +#if defined(BCMPMUCTL) +#define PMUCTL_ENAB(sih) (BCMPMUCTL) +#else +#define PMUCTL_ENAB(sih) ((sih)->cccaps & CC_CAP_PMU) +#endif + + +#if defined(BCMPMUCTL) && BCMPMUCTL +#define CCCTL_ENAB(sih) (0) +#define CCPLL_ENAB(sih) (0) +#else +#define CCCTL_ENAB(sih) ((sih)->cccaps & CC_CAP_PWR_CTL) +#define CCPLL_ENAB(sih) ((sih)->cccaps & CC_CAP_PLL_MASK) +#endif + +typedef void (*gpio_handler_t)(uint32 stat, void *arg); + +#define CC_BTCOEX_EN_MASK 0x01 + +#define GPIO_CTRL_EPA_EN_MASK 0x40 + +#define GPIO_CTRL_5_6_EN_MASK 0x60 +#define GPIO_CTRL_7_6_EN_MASK 0xC0 +#define GPIO_OUT_7_EN_MASK 0x80 + + + +#define SI_CR4_CAP (0x04) +#define SI_CR4_BANKIDX (0x40) +#define SI_CR4_BANKINFO (0x44) + +#define ARMCR4_TCBBNB_MASK 0xf0 +#define ARMCR4_TCBBNB_SHIFT 4 +#define ARMCR4_TCBANB_MASK 0xf +#define ARMCR4_TCBANB_SHIFT 0 + +#define SICF_CPUHALT (0x0020) +#define ARMCR4_BSZ_MASK 0x3f +#define ARMCR4_BSZ_MULT 8192 + + + +extern si_t *si_attach(uint pcidev, osl_t *osh, void *regs, uint bustype, + void *sdh, char **vars, uint *varsz); +extern si_t *si_kattach(osl_t *osh); +extern void si_detach(si_t *sih); +extern bool si_pci_war16165(si_t *sih); + +extern uint si_corelist(si_t *sih, uint coreid[]); +extern uint si_coreid(si_t *sih); +extern uint si_flag(si_t *sih); +extern uint si_intflag(si_t *sih); +extern uint si_coreidx(si_t *sih); +extern uint si_coreunit(si_t *sih); +extern uint si_corevendor(si_t *sih); +extern uint si_corerev(si_t *sih); +extern void *si_osh(si_t *sih); +extern void si_setosh(si_t *sih, osl_t *osh); +extern uint si_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val); +extern void *si_coreregs(si_t *sih); +extern uint si_wrapperreg(si_t *sih, uint32 offset, uint32 mask, uint32 val); +extern uint32 si_core_cflags(si_t *sih, uint32 mask, uint32 val); +extern void si_core_cflags_wo(si_t *sih, uint32 mask, uint32 val); +extern uint32 si_core_sflags(si_t *sih, uint32 mask, uint32 val); +extern bool si_iscoreup(si_t *sih); +extern uint si_findcoreidx(si_t *sih, uint coreid, uint coreunit); +extern void *si_setcoreidx(si_t *sih, uint coreidx); +extern void *si_setcore(si_t *sih, uint coreid, uint coreunit); +extern void *si_switch_core(si_t *sih, uint coreid, uint *origidx, uint *intr_val); +extern void si_restore_core(si_t *sih, uint coreid, uint intr_val); +extern int si_numaddrspaces(si_t *sih); +extern uint32 si_addrspace(si_t *sih, uint asidx); +extern uint32 si_addrspacesize(si_t *sih, uint asidx); +extern void si_coreaddrspaceX(si_t *sih, uint asidx, uint32 *addr, uint32 *size); +extern int si_corebist(si_t *sih); +extern void si_core_reset(si_t *sih, uint32 bits, uint32 resetbits); +extern void si_core_disable(si_t *sih, uint32 bits); +extern uint32 si_clock_rate(uint32 pll_type, uint32 n, uint32 m); +extern bool si_read_pmu_autopll(si_t *sih); +extern uint32 si_clock(si_t *sih); +extern uint32 si_alp_clock(si_t *sih); +extern uint32 si_ilp_clock(si_t *sih); +extern void si_pci_setup(si_t *sih, uint coremask); +extern void si_pcmcia_init(si_t *sih); +extern void si_setint(si_t *sih, int siflag); +extern bool si_backplane64(si_t *sih); +extern void si_register_intr_callback(si_t *sih, void *intrsoff_fn, void *intrsrestore_fn, + void *intrsenabled_fn, void *intr_arg); +extern void si_deregister_intr_callback(si_t *sih); +extern void si_clkctl_init(si_t *sih); +extern uint16 si_clkctl_fast_pwrup_delay(si_t *sih); +extern bool si_clkctl_cc(si_t *sih, uint mode); +extern int si_clkctl_xtal(si_t *sih, uint what, bool on); +extern uint32 si_gpiotimerval(si_t *sih, uint32 mask, uint32 val); +extern void si_btcgpiowar(si_t *sih); +extern bool si_deviceremoved(si_t *sih); +extern uint32 si_socram_size(si_t *sih); +extern uint32 si_socdevram_size(si_t *sih); +extern uint32 si_socram_srmem_size(si_t *sih); +extern void si_socdevram(si_t *sih, bool set, uint8 *ennable, uint8 *protect, uint8 *remap); +extern bool si_socdevram_pkg(si_t *sih); +extern bool si_socdevram_remap_isenb(si_t *sih); +extern uint32 si_socdevram_remap_size(si_t *sih); + +extern void si_watchdog(si_t *sih, uint ticks); +extern void si_watchdog_ms(si_t *sih, uint32 ms); +extern uint32 si_watchdog_msticks(void); +extern void *si_gpiosetcore(si_t *sih); +extern uint32 si_gpiocontrol(si_t *sih, uint32 mask, uint32 val, uint8 priority); +extern uint32 si_gpioouten(si_t *sih, uint32 mask, uint32 val, uint8 priority); +extern uint32 si_gpioout(si_t *sih, uint32 mask, uint32 val, uint8 priority); +extern uint32 si_gpioin(si_t *sih); +extern uint32 si_gpiointpolarity(si_t *sih, uint32 mask, uint32 val, uint8 priority); +extern uint32 si_gpiointmask(si_t *sih, uint32 mask, uint32 val, uint8 priority); +extern uint32 si_gpioled(si_t *sih, uint32 mask, uint32 val); +extern uint32 si_gpioreserve(si_t *sih, uint32 gpio_num, uint8 priority); +extern uint32 si_gpiorelease(si_t *sih, uint32 gpio_num, uint8 priority); +extern uint32 si_gpiopull(si_t *sih, bool updown, uint32 mask, uint32 val); +extern uint32 si_gpioevent(si_t *sih, uint regtype, uint32 mask, uint32 val); +extern uint32 si_gpio_int_enable(si_t *sih, bool enable); + + +extern void *si_gpio_handler_register(si_t *sih, uint32 e, bool lev, gpio_handler_t cb, void *arg); +extern void si_gpio_handler_unregister(si_t *sih, void* gpioh); +extern void si_gpio_handler_process(si_t *sih); + + +extern bool si_pci_pmecap(si_t *sih); +struct osl_info; +extern bool si_pci_fastpmecap(struct osl_info *osh); +extern bool si_pci_pmestat(si_t *sih); +extern void si_pci_pmeclr(si_t *sih); +extern void si_pci_pmeen(si_t *sih); +extern void si_pci_pmestatclr(si_t *sih); +extern uint si_pcie_readreg(void *sih, uint addrtype, uint offset); + +extern void si_sdio_init(si_t *sih); + +extern uint16 si_d11_devid(si_t *sih); +extern int si_corepciid(si_t *sih, uint func, uint16 *pcivendor, uint16 *pcidevice, + uint8 *pciclass, uint8 *pcisubclass, uint8 *pciprogif, uint8 *pciheader); + +#define si_eci(sih) 0 +static INLINE void * si_eci_init(si_t *sih) {return NULL;} +#define si_eci_notify_bt(sih, type, val) (0) +#define si_seci(sih) 0 +#define si_seci_upd(sih, a) do {} while (0) +static INLINE void * si_seci_init(si_t *sih, uint8 use_seci) {return NULL;} +#define si_seci_down(sih) do {} while (0) + + +extern bool si_is_otp_disabled(si_t *sih); +extern bool si_is_otp_powered(si_t *sih); +extern void si_otp_power(si_t *sih, bool on); + + +extern bool si_is_sprom_available(si_t *sih); +extern bool si_is_sprom_enabled(si_t *sih); +extern void si_sprom_enable(si_t *sih, bool enable); + + +extern int si_cis_source(si_t *sih); +#define CIS_DEFAULT 0 +#define CIS_SROM 1 +#define CIS_OTP 2 + + +#define DEFAULT_FAB 0x0 +#define CSM_FAB7 0x1 +#define TSMC_FAB12 0x2 +#define SMIC_FAB4 0x3 +extern int si_otp_fabid(si_t *sih, uint16 *fabid, bool rw); +extern uint16 si_fabid(si_t *sih); + + +extern int si_devpath(si_t *sih, char *path, int size); + +extern char *si_getdevpathvar(si_t *sih, const char *name); +extern int si_getdevpathintvar(si_t *sih, const char *name); +extern char *si_coded_devpathvar(si_t *sih, char *varname, int var_len, const char *name); + + +extern uint8 si_pcieclkreq(si_t *sih, uint32 mask, uint32 val); +extern uint32 si_pcielcreg(si_t *sih, uint32 mask, uint32 val); +extern void si_war42780_clkreq(si_t *sih, bool clkreq); +extern void si_pci_down(si_t *sih); +extern void si_pci_up(si_t *sih); +extern void si_pci_sleep(si_t *sih); +extern void si_pcie_war_ovr_update(si_t *sih, uint8 aspm); +extern void si_pcie_power_save_enable(si_t *sih, bool enable); +extern void si_pcie_extendL1timer(si_t *sih, bool extend); +extern int si_pci_fixcfg(si_t *sih); +extern void si_chippkg_set(si_t *sih, uint); + +extern void si_chipcontrl_btshd0_4331(si_t *sih, bool on); +extern void si_chipcontrl_restore(si_t *sih, uint32 val); +extern uint32 si_chipcontrl_read(si_t *sih); +extern void si_chipcontrl_epa4331(si_t *sih, bool on); +extern void si_chipcontrl_epa4331_wowl(si_t *sih, bool enter_wowl); +extern void si_chipcontrl_srom4360(si_t *sih, bool on); + +extern void si_epa_4313war(si_t *sih); +extern void si_btc_enable_chipcontrol(si_t *sih); + +extern void si_btcombo_p250_4313_war(si_t *sih); +extern void si_btcombo_43228_war(si_t *sih); +extern void si_clk_pmu_htavail_set(si_t *sih, bool set_clear); +extern uint si_pll_reset(si_t *sih); + + +extern bool si_taclear(si_t *sih, bool details); + + + +extern uint32 si_pciereg(si_t *sih, uint32 offset, uint32 mask, uint32 val, uint type); +extern uint32 si_pcieserdesreg(si_t *sih, uint32 mdioslave, uint32 offset, uint32 mask, uint32 val); +extern void si_pcie_set_request_size(si_t *sih, uint16 size); +extern uint16 si_pcie_get_request_size(si_t *sih); +extern uint16 si_pcie_get_ssid(si_t *sih); +extern uint32 si_pcie_get_bar0(si_t *sih); +extern int si_pcie_configspace_cache(si_t *sih); +extern int si_pcie_configspace_restore(si_t *sih); +extern int si_pcie_configspace_get(si_t *sih, uint8 *buf, uint size); + +char *si_getnvramflvar(si_t *sih, const char *name); + + +extern uint32 si_tcm_size(si_t *sih); + +extern int si_set_sromctl(si_t *sih, uint32 value); +extern uint32 si_get_sromctl(si_t *sih); +#endif diff --git a/drivers/net/wireless/bcmdhd/include/trxhdr.h b/drivers/net/wireless/bcmdhd/include/trxhdr.h new file mode 100644 index 00000000..bf92a565 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/trxhdr.h @@ -0,0 +1,53 @@ +/* + * TRX image file header format. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: trxhdr.h 260898 2011-05-20 23:11:12Z $ + */ + +#ifndef _TRX_HDR_H +#define _TRX_HDR_H + +#include + +#define TRX_MAGIC 0x30524448 /* "HDR0" */ +#define TRX_VERSION 1 /* Version 1 */ +#define TRX_MAX_LEN 0x3B0000 /* Max length */ +#define TRX_NO_HEADER 1 /* Do not write TRX header */ +#define TRX_GZ_FILES 0x2 /* Contains up to TRX_MAX_OFFSET individual gzip files */ +#define TRX_EMBED_UCODE 0x8 /* Trx contains embedded ucode image */ +#define TRX_ROMSIM_IMAGE 0x10 /* Trx contains ROM simulation image */ +#define TRX_UNCOMP_IMAGE 0x20 /* Trx contains uncompressed rtecdc.bin image */ +#define TRX_MAX_OFFSET 3 /* Max number of individual files */ + +struct trx_header { + uint32 magic; /* "HDR0" */ + uint32 len; /* Length of file including header */ + uint32 crc32; /* 32-bit CRC from flag_version to end of file */ + uint32 flag_version; /* 0:15 flags, 16:31 version */ + uint32 offsets[TRX_MAX_OFFSET]; /* Offsets of partitions from start of header */ +}; + +/* Compatibility */ +typedef struct trx_header TRXHDR, *PTRXHDR; + +#endif /* _TRX_HDR_H */ diff --git a/drivers/net/wireless/bcmdhd/include/typedefs.h b/drivers/net/wireless/bcmdhd/include/typedefs.h new file mode 100644 index 00000000..4eee5bab --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/typedefs.h @@ -0,0 +1,310 @@ +/* + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * $Id: typedefs.h 286783 2011-09-29 06:18:57Z $ + */ + +#ifndef _TYPEDEFS_H_ +#define _TYPEDEFS_H_ + +#ifdef SITE_TYPEDEFS + + + +#include "site_typedefs.h" + +#else + + + +#ifdef __cplusplus + +#define TYPEDEF_BOOL +#ifndef FALSE +#define FALSE false +#endif +#ifndef TRUE +#define TRUE true +#endif + +#else + + +#endif + +#if defined(__x86_64__) +#define TYPEDEF_UINTPTR +typedef unsigned long long int uintptr; +#endif + + + + + +#if defined(_NEED_SIZE_T_) +typedef long unsigned int size_t; +#endif + + + + +#if defined(__sparc__) +#define TYPEDEF_ULONG +#endif + + + +#if !defined(LINUX_HYBRID) || defined(LINUX_PORT) +#define TYPEDEF_UINT +#ifndef TARGETENV_android +#define TYPEDEF_USHORT +#define TYPEDEF_ULONG +#endif +#ifdef __KERNEL__ +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)) +#define TYPEDEF_BOOL +#endif + +#if (LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 18)) +#include +#ifdef noinline_for_stack +#define TYPEDEF_BOOL +#endif +#endif +#endif +#endif + + + + + +#if defined(__GNUC__) && defined(__STRICT_ANSI__) +#define TYPEDEF_INT64 +#define TYPEDEF_UINT64 +#endif + + +#if defined(__ICL) + +#define TYPEDEF_INT64 + +#if defined(__STDC__) +#define TYPEDEF_UINT64 +#endif + +#endif + +#if !defined(__DJGPP__) + + +#if defined(__KERNEL__) + + +#if !defined(LINUX_HYBRID) || defined(LINUX_PORT) +#include +#endif + +#else + + +#include + +#endif + +#endif + + + + +#define USE_TYPEDEF_DEFAULTS + +#endif + + + + +#ifdef USE_TYPEDEF_DEFAULTS +#undef USE_TYPEDEF_DEFAULTS + +#ifndef TYPEDEF_BOOL +typedef unsigned char bool; +#endif + + + +#ifndef TYPEDEF_UCHAR +typedef unsigned char uchar; +#endif + +#ifndef TYPEDEF_USHORT +typedef unsigned short ushort; +#endif + +#ifndef TYPEDEF_UINT +typedef unsigned int uint; +#endif + +#ifndef TYPEDEF_ULONG +typedef unsigned long ulong; +#endif + + + +#ifndef TYPEDEF_UINT8 +typedef unsigned char uint8; +#endif + +#ifndef TYPEDEF_UINT16 +typedef unsigned short uint16; +#endif + +#ifndef TYPEDEF_UINT32 +typedef unsigned int uint32; +#endif + +#ifndef TYPEDEF_UINT64 +typedef unsigned long long uint64; +#endif + +#ifndef TYPEDEF_UINTPTR +typedef unsigned int uintptr; +#endif + +#ifndef TYPEDEF_INT8 +typedef signed char int8; +#endif + +#ifndef TYPEDEF_INT16 +typedef signed short int16; +#endif + +#ifndef TYPEDEF_INT32 +typedef signed int int32; +#endif + +#ifndef TYPEDEF_INT64 +typedef signed long long int64; +#endif + + + +#ifndef TYPEDEF_FLOAT32 +typedef float float32; +#endif + +#ifndef TYPEDEF_FLOAT64 +typedef double float64; +#endif + + + +#ifndef TYPEDEF_FLOAT_T + +#if defined(FLOAT32) +typedef float32 float_t; +#else +typedef float64 float_t; +#endif + +#endif + + + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef NULL +#define NULL 0 +#endif + +#ifndef OFF +#define OFF 0 +#endif + +#ifndef ON +#define ON 1 +#endif + +#define AUTO (-1) + + + +#ifndef PTRSZ +#define PTRSZ sizeof(char*) +#endif + + + +#if defined(__GNUC__) || defined(__lint) + #define BWL_COMPILER_GNU +#elif defined(__CC_ARM) && __CC_ARM + #define BWL_COMPILER_ARMCC +#else + #error "Unknown compiler!" +#endif + + +#ifndef INLINE + #if defined(BWL_COMPILER_MICROSOFT) + #define INLINE __inline + #elif defined(BWL_COMPILER_GNU) + #define INLINE __inline__ + #elif defined(BWL_COMPILER_ARMCC) + #define INLINE __inline + #else + #define INLINE + #endif +#endif + +#undef TYPEDEF_BOOL +#undef TYPEDEF_UCHAR +#undef TYPEDEF_USHORT +#undef TYPEDEF_UINT +#undef TYPEDEF_ULONG +#undef TYPEDEF_UINT8 +#undef TYPEDEF_UINT16 +#undef TYPEDEF_UINT32 +#undef TYPEDEF_UINT64 +#undef TYPEDEF_UINTPTR +#undef TYPEDEF_INT8 +#undef TYPEDEF_INT16 +#undef TYPEDEF_INT32 +#undef TYPEDEF_INT64 +#undef TYPEDEF_FLOAT32 +#undef TYPEDEF_FLOAT64 +#undef TYPEDEF_FLOAT_T + +#endif + + +#define UNUSED_PARAMETER(x) (void)(x) + + +#define DISCARD_QUAL(ptr, type) ((type *)(uintptr)(ptr)) + + +#include +#endif diff --git a/drivers/net/wireless/bcmdhd/include/wlfc_proto.h b/drivers/net/wireless/bcmdhd/include/wlfc_proto.h new file mode 100644 index 00000000..6b421b5d --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/wlfc_proto.h @@ -0,0 +1,233 @@ +/* +* Copyright (C) 1999-2012, Broadcom Corporation +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed to you +* under the terms of the GNU General Public License version 2 (the "GPL"), +* available at http://www.broadcom.com/licenses/GPLv2.php, with the +* following added to such license: +* +* As a special exception, the copyright holders of this software give you +* permission to link this software with independent modules, and to copy and +* distribute the resulting executable under terms of your choice, provided that +* you also meet, for each linked independent module, the terms and conditions of +* the license of that module. An independent module is a module which is not +* derived from this software. The special exception does not apply to any +* modifications of the software. +* +* Notwithstanding the above, under no circumstances may you combine this +* software in any way with any other Broadcom software provided under a license +* other than the GPL, without Broadcom's express prior written consent. +* $Id: wlfc_proto.h 361006 2012-10-05 07:45:51Z $ +* +*/ +#ifndef __wlfc_proto_definitions_h__ +#define __wlfc_proto_definitions_h__ + + /* Use TLV to convey WLFC information. + --------------------------------------------------------------------------- + | Type | Len | value | Description + --------------------------------------------------------------------------- + | 1 | 1 | (handle) | MAC OPEN + --------------------------------------------------------------------------- + | 2 | 1 | (handle) | MAC CLOSE + --------------------------------------------------------------------------- + | 3 | 2 | (count, handle, prec_bmp)| Set the credit depth for a MAC dstn + --------------------------------------------------------------------------- + | 4 | 4 | see pkttag comments | TXSTATUS + --------------------------------------------------------------------------- + | 5 | 4 | see pkttag comments | PKKTTAG [host->firmware] + --------------------------------------------------------------------------- + | 6 | 8 | (handle, ifid, MAC) | MAC ADD + --------------------------------------------------------------------------- + | 7 | 8 | (handle, ifid, MAC) | MAC DEL + --------------------------------------------------------------------------- + | 8 | 1 | (rssi) | RSSI - RSSI value for the packet. + --------------------------------------------------------------------------- + | 9 | 1 | (interface ID) | Interface OPEN + --------------------------------------------------------------------------- + | 10 | 1 | (interface ID) | Interface CLOSE + --------------------------------------------------------------------------- + | 11 | 8 | fifo credit returns map | FIFO credits back to the host + | | | | + | | | | -------------------------------------- + | | | | | ac0 | ac1 | ac2 | ac3 | bcmc | atim | + | | | | -------------------------------------- + | | | | + --------------------------------------------------------------------------- + | 12 | 2 | MAC handle, | Host provides a bitmap of pending + | | | AC[0-3] traffic bitmap | unicast traffic for MAC-handle dstn. + | | | | [host->firmware] + --------------------------------------------------------------------------- + | 13 | 3 | (count, handle, prec_bmp)| One time request for packet to a specific + | | | | MAC destination. + --------------------------------------------------------------------------- + | 15 | 1 | interface ID | NIC period start + --------------------------------------------------------------------------- + | 16 | 1 | interface ID | NIC period end + --------------------------------------------------------------------------- + | 17 | 3 | (ifid, txs) | Action frame tx status + --------------------------------------------------------------------------- + | 255 | N/A | N/A | FILLER - This is a special type + | | | | that has no length or value. + | | | | Typically used for padding. + --------------------------------------------------------------------------- + */ + +#define WLFC_CTL_TYPE_MAC_OPEN 1 +#define WLFC_CTL_TYPE_MAC_CLOSE 2 +#define WLFC_CTL_TYPE_MAC_REQUEST_CREDIT 3 +#define WLFC_CTL_TYPE_TXSTATUS 4 +#define WLFC_CTL_TYPE_PKTTAG 5 + +#define WLFC_CTL_TYPE_MACDESC_ADD 6 +#define WLFC_CTL_TYPE_MACDESC_DEL 7 +#define WLFC_CTL_TYPE_RSSI 8 + +#define WLFC_CTL_TYPE_INTERFACE_OPEN 9 +#define WLFC_CTL_TYPE_INTERFACE_CLOSE 10 + +#define WLFC_CTL_TYPE_FIFO_CREDITBACK 11 + +#define WLFC_CTL_TYPE_PENDING_TRAFFIC_BMP 12 +#define WLFC_CTL_TYPE_MAC_REQUEST_PACKET 13 +#define WLFC_CTL_TYPE_HOST_REORDER_RXPKTS 14 + +#define WLFC_CTL_TYPE_NIC_PRD_START 15 +#define WLFC_CTL_TYPE_NIC_PRD_END 16 +#define WLFC_CTL_TYPE_AF_TXS 17 +#define WLFC_CTL_TYPE_TRANS_ID 18 +#define WLFC_CTL_TYPE_COMP_TXSTATUS 19 + +#define WLFC_CTL_TYPE_FILLER 255 + +#define WLFC_CTL_VALUE_LEN_MACDESC 8 /* handle, interface, MAC */ + +#define WLFC_CTL_VALUE_LEN_MAC 1 /* MAC-handle */ +#define WLFC_CTL_VALUE_LEN_RSSI 1 + +#define WLFC_CTL_VALUE_LEN_INTERFACE 1 +#define WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP 2 + +#define WLFC_CTL_VALUE_LEN_TXSTATUS 4 +#define WLFC_CTL_VALUE_LEN_PKTTAG 4 + +/* enough space to host all 4 ACs, bc/mc and atim fifo credit */ +#define WLFC_CTL_VALUE_LEN_FIFO_CREDITBACK 6 + +#define WLFC_CTL_VALUE_LEN_REQUEST_CREDIT 3 /* credit, MAC-handle, prec_bitmap */ +#define WLFC_CTL_VALUE_LEN_REQUEST_PACKET 3 /* credit, MAC-handle, prec_bitmap */ + +#define WLFC_CTL_VALUE_LEN_NIC_PRD_START 1 +#define WLFC_CTL_VALUE_LEN_NIC_PRD_END 1 +#define WLFC_CTL_VALUE_LEN_AF_TXS 3 + + +#define WLFC_PKTID_GEN_MASK 0x80000000 +#define WLFC_PKTID_GEN_SHIFT 31 + +#define WLFC_PKTID_GEN(x) (((x) & WLFC_PKTID_GEN_MASK) >> WLFC_PKTID_GEN_SHIFT) +#define WLFC_PKTID_SETGEN(x, gen) (x) = ((x) & ~WLFC_PKTID_GEN_MASK) | \ + (((gen) << WLFC_PKTID_GEN_SHIFT) & WLFC_PKTID_GEN_MASK) + +#define WLFC_PKTFLAG_PKTFROMHOST 0x01 +#define WLFC_PKTFLAG_PKT_REQUESTED 0x02 + +#define WL_TXSTATUS_FLAGS_MASK 0xf /* allow 4 bits only */ +#define WL_TXSTATUS_FLAGS_SHIFT 27 + +#define WL_TXSTATUS_SET_FLAGS(x, flags) ((x) = \ + ((x) & ~(WL_TXSTATUS_FLAGS_MASK << WL_TXSTATUS_FLAGS_SHIFT)) | \ + (((flags) & WL_TXSTATUS_FLAGS_MASK) << WL_TXSTATUS_FLAGS_SHIFT)) +#define WL_TXSTATUS_GET_FLAGS(x) (((x) >> WL_TXSTATUS_FLAGS_SHIFT) & \ + WL_TXSTATUS_FLAGS_MASK) + +#define WL_TXSTATUS_FIFO_MASK 0x7 /* allow 3 bits for FIFO ID */ +#define WL_TXSTATUS_FIFO_SHIFT 24 + +#define WL_TXSTATUS_SET_FIFO(x, flags) ((x) = \ + ((x) & ~(WL_TXSTATUS_FIFO_MASK << WL_TXSTATUS_FIFO_SHIFT)) | \ + (((flags) & WL_TXSTATUS_FIFO_MASK) << WL_TXSTATUS_FIFO_SHIFT)) +#define WL_TXSTATUS_GET_FIFO(x) (((x) >> WL_TXSTATUS_FIFO_SHIFT) & WL_TXSTATUS_FIFO_MASK) + +#define WL_TXSTATUS_PKTID_MASK 0xffffff /* allow 24 bits */ +#define WL_TXSTATUS_SET_PKTID(x, num) ((x) = \ + ((x) & ~WL_TXSTATUS_PKTID_MASK) | (num)) +#define WL_TXSTATUS_GET_PKTID(x) ((x) & WL_TXSTATUS_PKTID_MASK) + +/* 32 STA should be enough??, 6 bits; Must be power of 2 */ +#define WLFC_MAC_DESC_TABLE_SIZE 32 +#define WLFC_MAX_IFNUM 16 +#define WLFC_MAC_DESC_ID_INVALID 0xff + +/* b[7:5] -reuse guard, b[4:0] -value */ +#define WLFC_MAC_DESC_GET_LOOKUP_INDEX(x) ((x) & 0x1f) + +#define WLFC_PKTFLAG_SET_PKTREQUESTED(x) (x) |= \ + (WLFC_PKTFLAG_PKT_REQUESTED << WL_TXSTATUS_FLAGS_SHIFT) + +#define WLFC_PKTFLAG_CLR_PKTREQUESTED(x) (x) &= \ + ~(WLFC_PKTFLAG_PKT_REQUESTED << WL_TXSTATUS_FLAGS_SHIFT) + +#define WL_TXSTATUS_GENERATION_MASK 1 +#define WL_TXSTATUS_GENERATION_SHIFT 31 + +#define WLFC_PKTFLAG_SET_GENERATION(x, gen) ((x) = \ + ((x) & ~(WL_TXSTATUS_GENERATION_MASK << WL_TXSTATUS_GENERATION_SHIFT)) | \ + (((gen) & WL_TXSTATUS_GENERATION_MASK) << WL_TXSTATUS_GENERATION_SHIFT)) + +#define WLFC_PKTFLAG_GENERATION(x) (((x) >> WL_TXSTATUS_GENERATION_SHIFT) & \ + WL_TXSTATUS_GENERATION_MASK) + +#define WLFC_MAX_PENDING_DATALEN 120 + +/* host is free to discard the packet */ +#define WLFC_CTL_PKTFLAG_DISCARD 0 +/* D11 suppressed a packet */ +#define WLFC_CTL_PKTFLAG_D11SUPPRESS 1 +/* WL firmware suppressed a packet because MAC is + already in PSMode (short time window) +*/ +#define WLFC_CTL_PKTFLAG_WLSUPPRESS 2 +/* Firmware tossed this packet */ +#define WLFC_CTL_PKTFLAG_TOSSED_BYWLC 3 + +#define WLFC_D11_STATUS_INTERPRET(txs) \ + (((txs)->status.suppr_ind != 0) ? WLFC_CTL_PKTFLAG_D11SUPPRESS : WLFC_CTL_PKTFLAG_DISCARD) + +#ifdef PROP_TXSTATUS_DEBUG +#define WLFC_DBGMESG(x) printf x +/* wlfc-breadcrumb */ +#define WLFC_BREADCRUMB(x) do {if ((x) == NULL) \ + {printf("WLFC: %s():%d:caller:%p\n", \ + __FUNCTION__, __LINE__, __builtin_return_address(0));}} while (0) +#define WLFC_PRINTMAC(banner, ea) do {printf("%s MAC: [%02x:%02x:%02x:%02x:%02x:%02x]\n", \ + banner, ea[0], ea[1], ea[2], ea[3], ea[4], ea[5]); } while (0) +#define WLFC_WHEREIS(s) printf("WLFC: at %s():%d, %s\n", __FUNCTION__, __LINE__, (s)) +#else +#define WLFC_DBGMESG(x) +#define WLFC_BREADCRUMB(x) +#define WLFC_PRINTMAC(banner, ea) +#define WLFC_WHEREIS(s) +#endif + +/* AMPDU host reorder packet flags */ +#define WLHOST_REORDERDATA_MAXFLOWS 256 +#define WLHOST_REORDERDATA_LEN 10 +#define WLHOST_REORDERDATA_TOTLEN (WLHOST_REORDERDATA_LEN + 1 + 1) /* +tag +len */ + +#define WLHOST_REORDERDATA_FLOWID_OFFSET 0 +#define WLHOST_REORDERDATA_MAXIDX_OFFSET 2 +#define WLHOST_REORDERDATA_FLAGS_OFFSET 4 +#define WLHOST_REORDERDATA_CURIDX_OFFSET 6 +#define WLHOST_REORDERDATA_EXPIDX_OFFSET 8 + +#define WLHOST_REORDERDATA_DEL_FLOW 0x01 +#define WLHOST_REORDERDATA_FLUSH_ALL 0x02 +#define WLHOST_REORDERDATA_CURIDX_VALID 0x04 +#define WLHOST_REORDERDATA_EXPIDX_VALID 0x08 +#define WLHOST_REORDERDATA_NEW_HOLE 0x10 +/* transaction id data len byte 0: rsvd, byte 1: seqnumber, byte 2-5 will be used for timestampe */ +#define WLFC_CTL_TRANS_ID_LEN 6 + +#endif /* __wlfc_proto_definitions_h__ */ diff --git a/drivers/net/wireless/bcmdhd/include/wlioctl.h b/drivers/net/wireless/bcmdhd/include/wlioctl.h new file mode 100644 index 00000000..c8c19950 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/wlioctl.h @@ -0,0 +1,5052 @@ +/* + * Custom OID/ioctl definitions for + * Broadcom 802.11abg Networking Device Driver + * + * Definitions subject to change without notice. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wlioctl.h 366141 2012-11-01 01:55:06Z $ + */ + +#ifndef _wlioctl_h_ +#define _wlioctl_h_ + +#include +#include +#include +#include +#include +#include +#include + +#ifndef LINUX_POSTMOGRIFY_REMOVAL +#include +#include +#endif /* LINUX_POSTMOGRIFY_REMOVAL */ + +/* LINUX_POSTMOGRIFY_REMOVAL: undefined during compile phase, so its + * a no-op for most cases. For hybrid and other open source releases, + * its defined during a second pass and mogrified out for distribution. + */ + + +#ifndef LINUX_POSTMOGRIFY_REMOVAL + +#ifndef INTF_NAME_SIZ +#define INTF_NAME_SIZ 16 +#endif + +/* Used to send ioctls over the transport pipe */ +typedef struct remote_ioctl { + cdc_ioctl_t msg; + uint data_len; + char intf_name[INTF_NAME_SIZ]; +} rem_ioctl_t; +#define REMOTE_SIZE sizeof(rem_ioctl_t) + +#define ACTION_FRAME_SIZE 1800 + +typedef struct wl_action_frame { + struct ether_addr da; + uint16 len; + uint32 packetId; + uint8 data[ACTION_FRAME_SIZE]; +} wl_action_frame_t; + +#define WL_WIFI_ACTION_FRAME_SIZE sizeof(struct wl_action_frame) + +typedef struct ssid_info +{ + uint8 ssid_len; /* the length of SSID */ + uint8 ssid[32]; /* SSID string */ +} ssid_info_t; + +typedef struct wl_af_params { + uint32 channel; + int32 dwell_time; + struct ether_addr BSSID; + wl_action_frame_t action_frame; +} wl_af_params_t; + +#define WL_WIFI_AF_PARAMS_SIZE sizeof(struct wl_af_params) + +#define MFP_TEST_FLAG_NORMAL 0 +#define MFP_TEST_FLAG_ANY_KEY 1 +typedef struct wl_sa_query { + uint32 flag; + uint8 action; + uint16 id; + struct ether_addr da; +} wl_sa_query_t; + +#endif /* LINUX_POSTMOGRIFY_REMOVAL */ + +/* require default structure packing */ +#define BWL_DEFAULT_PACKING +#include + + +#ifndef LINUX_POSTMOGRIFY_REMOVAL +/* Legacy structure to help keep backward compatible wl tool and tray app */ + +#define LEGACY_WL_BSS_INFO_VERSION 107 /* older version of wl_bss_info struct */ + +typedef struct wl_bss_info_107 { + uint32 version; /* version field */ + uint32 length; /* byte length of data in this record, + * starting at version and including IEs + */ + struct ether_addr BSSID; + uint16 beacon_period; /* units are Kusec */ + uint16 capability; /* Capability information */ + uint8 SSID_len; + uint8 SSID[32]; + struct { + uint count; /* # rates in this set */ + uint8 rates[16]; /* rates in 500kbps units w/hi bit set if basic */ + } rateset; /* supported rates */ + uint8 channel; /* Channel no. */ + uint16 atim_window; /* units are Kusec */ + uint8 dtim_period; /* DTIM period */ + int16 RSSI; /* receive signal strength (in dBm) */ + int8 phy_noise; /* noise (in dBm) */ + uint32 ie_length; /* byte length of Information Elements */ + /* variable length Information Elements */ +} wl_bss_info_107_t; + +/* + * Per-BSS information structure. + */ + +#define LEGACY2_WL_BSS_INFO_VERSION 108 /* old version of wl_bss_info struct */ + +/* BSS info structure + * Applications MUST CHECK ie_offset field and length field to access IEs and + * next bss_info structure in a vector (in wl_scan_results_t) + */ +typedef struct wl_bss_info_108 { + uint32 version; /* version field */ + uint32 length; /* byte length of data in this record, + * starting at version and including IEs + */ + struct ether_addr BSSID; + uint16 beacon_period; /* units are Kusec */ + uint16 capability; /* Capability information */ + uint8 SSID_len; + uint8 SSID[32]; + struct { + uint count; /* # rates in this set */ + uint8 rates[16]; /* rates in 500kbps units w/hi bit set if basic */ + } rateset; /* supported rates */ + chanspec_t chanspec; /* chanspec for bss */ + uint16 atim_window; /* units are Kusec */ + uint8 dtim_period; /* DTIM period */ + int16 RSSI; /* receive signal strength (in dBm) */ + int8 phy_noise; /* noise (in dBm) */ + + uint8 n_cap; /* BSS is 802.11N Capable */ + uint32 nbss_cap; /* 802.11N BSS Capabilities (based on HT_CAP_*) */ + uint8 ctl_ch; /* 802.11N BSS control channel number */ + uint32 reserved32[1]; /* Reserved for expansion of BSS properties */ + uint8 flags; /* flags */ + uint8 reserved[3]; /* Reserved for expansion of BSS properties */ + uint8 basic_mcs[MCSSET_LEN]; /* 802.11N BSS required MCS set */ + + uint16 ie_offset; /* offset at which IEs start, from beginning */ + uint32 ie_length; /* byte length of Information Elements */ + /* Add new fields here */ + /* variable length Information Elements */ +} wl_bss_info_108_t; + +#endif /* LINUX_POSTMOGRIFY_REMOVAL */ + +#define WL_BSS_INFO_VERSION 109 /* current version of wl_bss_info struct */ + +/* BSS info structure + * Applications MUST CHECK ie_offset field and length field to access IEs and + * next bss_info structure in a vector (in wl_scan_results_t) + */ +typedef struct wl_bss_info { + uint32 version; /* version field */ + uint32 length; /* byte length of data in this record, + * starting at version and including IEs + */ + struct ether_addr BSSID; + uint16 beacon_period; /* units are Kusec */ + uint16 capability; /* Capability information */ + uint8 SSID_len; + uint8 SSID[32]; + struct { + uint count; /* # rates in this set */ + uint8 rates[16]; /* rates in 500kbps units w/hi bit set if basic */ + } rateset; /* supported rates */ + chanspec_t chanspec; /* chanspec for bss */ + uint16 atim_window; /* units are Kusec */ + uint8 dtim_period; /* DTIM period */ + int16 RSSI; /* receive signal strength (in dBm) */ + int8 phy_noise; /* noise (in dBm) */ + + uint8 n_cap; /* BSS is 802.11N Capable */ + uint32 nbss_cap; /* 802.11N+AC BSS Capabilities */ + uint8 ctl_ch; /* 802.11N BSS control channel number */ + uint8 padding1[3]; /* explicit struct alignment padding */ + uint16 vht_rxmcsmap; /* VHT rx mcs map */ + uint16 vht_txmcsmap; /* VHT tx mcs map */ + uint8 flags; /* flags */ + uint8 vht_cap; /* BSS is vht capable */ + uint8 reserved[2]; /* Reserved for expansion of BSS properties */ + uint8 basic_mcs[MCSSET_LEN]; /* 802.11N BSS required MCS set */ + + uint16 ie_offset; /* offset at which IEs start, from beginning */ + uint32 ie_length; /* byte length of Information Elements */ + int16 SNR; /* average SNR of during frame reception */ + /* Add new fields here */ + /* variable length Information Elements */ +} wl_bss_info_t; + +/* bss_info_cap_t flags */ +#define WL_BSS_FLAGS_FROM_BEACON 0x01 /* bss_info derived from beacon */ +#define WL_BSS_FLAGS_FROM_CACHE 0x02 /* bss_info collected from cache */ +#define WL_BSS_FLAGS_RSSI_ONCHANNEL 0x04 /* rssi info was received on channel (vs offchannel) */ + +/* bssinfo flag for nbss_cap */ +#define VHT_BI_SGI_80MHZ 0x00000100 + +#ifndef LINUX_POSTMOGRIFY_REMOVAL + +typedef struct wl_bsscfg { + uint32 wsec; + uint32 WPA_auth; + uint32 wsec_index; + uint32 associated; + uint32 BSS; + uint32 phytest_on; + struct ether_addr prev_BSSID; + struct ether_addr BSSID; + uint32 targetbss_wpa2_flags; + uint32 assoc_type; + uint32 assoc_state; +} wl_bsscfg_t; + +typedef struct wl_bss_config { + uint32 atim_window; + uint32 beacon_period; + uint32 chanspec; +} wl_bss_config_t; + +#define DLOAD_HANDLER_VER 1 /* Downloader version */ +#define DLOAD_FLAG_VER_MASK 0xf000 /* Downloader version mask */ +#define DLOAD_FLAG_VER_SHIFT 12 /* Downloader version shift */ + +#define DL_CRC_NOT_INUSE 0x0001 + +/* generic download types & flags */ +enum { + DL_TYPE_UCODE = 1, + DL_TYPE_CLM = 2 +}; + +/* ucode type values */ +enum { + UCODE_FW, + INIT_VALS, + BS_INIT_VALS +}; + +struct wl_dload_data { + uint16 flag; + uint16 dload_type; + uint32 len; + uint32 crc; + uint8 data[1]; +}; +typedef struct wl_dload_data wl_dload_data_t; + +struct wl_ucode_info { + uint32 ucode_type; + uint32 num_chunks; + uint32 chunk_len; + uint32 chunk_num; + uint8 data_chunk[1]; +}; +typedef struct wl_ucode_info wl_ucode_info_t; + +struct wl_clm_dload_info { + uint32 ds_id; + uint32 clm_total_len; + uint32 num_chunks; + uint32 chunk_len; + uint32 chunk_offset; + uint8 data_chunk[1]; +}; +typedef struct wl_clm_dload_info wl_clm_dload_info_t; + +#endif /* LINUX_POSTMOGRIFY_REMOVAL */ + +typedef struct wlc_ssid { + uint32 SSID_len; + uchar SSID[32]; +} wlc_ssid_t; + +#ifndef LINUX_POSTMOGRIFY_REMOVAL + +#define MAX_PREFERRED_AP_NUM 5 +typedef struct wlc_fastssidinfo { + uint32 SSID_channel[MAX_PREFERRED_AP_NUM]; + wlc_ssid_t SSID_info[MAX_PREFERRED_AP_NUM]; +} wlc_fastssidinfo_t; + +typedef BWL_PRE_PACKED_STRUCT struct wnm_url { + uint8 len; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT wnm_url_t; + +typedef struct chan_scandata { + uint8 txpower; + uint8 pad; + chanspec_t channel; /* Channel num, bw, ctrl_sb and band */ + uint32 channel_mintime; + uint32 channel_maxtime; +} chan_scandata_t; + +typedef enum wl_scan_type { + EXTDSCAN_FOREGROUND_SCAN, + EXTDSCAN_BACKGROUND_SCAN, + EXTDSCAN_FORCEDBACKGROUND_SCAN +} wl_scan_type_t; + +#define WLC_EXTDSCAN_MAX_SSID 5 + +typedef struct wl_extdscan_params { + int8 nprobes; /* 0, passive, otherwise active */ + int8 split_scan; /* split scan */ + int8 band; /* band */ + int8 pad; + wlc_ssid_t ssid[WLC_EXTDSCAN_MAX_SSID]; /* ssid list */ + uint32 tx_rate; /* in 500ksec units */ + wl_scan_type_t scan_type; /* enum */ + int32 channel_num; + chan_scandata_t channel_list[1]; /* list of chandata structs */ +} wl_extdscan_params_t; + +#define WL_EXTDSCAN_PARAMS_FIXED_SIZE (sizeof(wl_extdscan_params_t) - sizeof(chan_scandata_t)) + +#define WL_BSSTYPE_INFRA 1 +#define WL_BSSTYPE_INDEP 0 +#define WL_BSSTYPE_ANY 2 + +/* Bitmask for scan_type */ +#define WL_SCANFLAGS_PASSIVE 0x01 /* force passive scan */ +#define WL_SCANFLAGS_RESERVED 0x02 /* Reserved */ +#define WL_SCANFLAGS_PROHIBITED 0x04 /* allow scanning prohibited channels */ + +#define WL_SCAN_PARAMS_SSID_MAX 10 + +typedef struct wl_scan_params { + wlc_ssid_t ssid; /* default: {0, ""} */ + struct ether_addr bssid; /* default: bcast */ + int8 bss_type; /* default: any, + * DOT11_BSSTYPE_ANY/INFRASTRUCTURE/INDEPENDENT + */ + uint8 scan_type; /* flags, 0 use default */ + int32 nprobes; /* -1 use default, number of probes per channel */ + int32 active_time; /* -1 use default, dwell time per channel for + * active scanning + */ + int32 passive_time; /* -1 use default, dwell time per channel + * for passive scanning + */ + int32 home_time; /* -1 use default, dwell time for the home channel + * between channel scans + */ + int32 channel_num; /* count of channels and ssids that follow + * + * low half is count of channels in channel_list, 0 + * means default (use all available channels) + * + * high half is entries in wlc_ssid_t array that + * follows channel_list, aligned for int32 (4 bytes) + * meaning an odd channel count implies a 2-byte pad + * between end of channel_list and first ssid + * + * if ssid count is zero, single ssid in the fixed + * parameter portion is assumed, otherwise ssid in + * the fixed portion is ignored + */ + uint16 channel_list[1]; /* list of chanspecs */ +} wl_scan_params_t; + +/* size of wl_scan_params not including variable length array */ +#define WL_SCAN_PARAMS_FIXED_SIZE 64 + +/* masks for channel and ssid count */ +#define WL_SCAN_PARAMS_COUNT_MASK 0x0000ffff +#define WL_SCAN_PARAMS_NSSID_SHIFT 16 + +#define WL_SCAN_ACTION_START 1 +#define WL_SCAN_ACTION_CONTINUE 2 +#define WL_SCAN_ACTION_ABORT 3 + +#define ISCAN_REQ_VERSION 1 + +/* incremental scan struct */ +typedef struct wl_iscan_params { + uint32 version; + uint16 action; + uint16 scan_duration; + wl_scan_params_t params; +} wl_iscan_params_t; + +/* 3 fields + size of wl_scan_params, not including variable length array */ +#define WL_ISCAN_PARAMS_FIXED_SIZE (OFFSETOF(wl_iscan_params_t, params) + sizeof(wlc_ssid_t)) +#endif /* LINUX_POSTMOGRIFY_REMOVAL */ + +typedef struct wl_scan_results { + uint32 buflen; + uint32 version; + uint32 count; + wl_bss_info_t bss_info[1]; +} wl_scan_results_t; + +#ifndef LINUX_POSTMOGRIFY_REMOVAL +/* size of wl_scan_results not including variable length array */ +#define WL_SCAN_RESULTS_FIXED_SIZE (sizeof(wl_scan_results_t) - sizeof(wl_bss_info_t)) + +/* wl_iscan_results status values */ +#define WL_SCAN_RESULTS_SUCCESS 0 +#define WL_SCAN_RESULTS_PARTIAL 1 +#define WL_SCAN_RESULTS_PENDING 2 +#define WL_SCAN_RESULTS_ABORTED 3 +#define WL_SCAN_RESULTS_NO_MEM 4 + +/* Used in EXT_STA */ +#define DNGL_RXCTXT_SIZE 45 + + +#define ESCAN_REQ_VERSION 1 + +typedef struct wl_escan_params { + uint32 version; + uint16 action; + uint16 sync_id; + wl_scan_params_t params; +} wl_escan_params_t; + +#define WL_ESCAN_PARAMS_FIXED_SIZE (OFFSETOF(wl_escan_params_t, params) + sizeof(wlc_ssid_t)) + +typedef struct wl_escan_result { + uint32 buflen; + uint32 version; + uint16 sync_id; + uint16 bss_count; + wl_bss_info_t bss_info[1]; +} wl_escan_result_t; + +#define WL_ESCAN_RESULTS_FIXED_SIZE (sizeof(wl_escan_result_t) - sizeof(wl_bss_info_t)) + +/* incremental scan results struct */ +typedef struct wl_iscan_results { + uint32 status; + wl_scan_results_t results; +} wl_iscan_results_t; + +/* size of wl_iscan_results not including variable length array */ +#define WL_ISCAN_RESULTS_FIXED_SIZE \ + (WL_SCAN_RESULTS_FIXED_SIZE + OFFSETOF(wl_iscan_results_t, results)) + +typedef struct wl_probe_params { + wlc_ssid_t ssid; + struct ether_addr bssid; + struct ether_addr mac; +} wl_probe_params_t; +#endif /* LINUX_POSTMOGRIFY_REMOVAL */ + +#define WL_MAXRATES_IN_SET 16 /* max # of rates in a rateset */ +typedef struct wl_rateset { + uint32 count; /* # rates in this set */ + uint8 rates[WL_MAXRATES_IN_SET]; /* rates in 500kbps units w/hi bit set if basic */ +} wl_rateset_t; + +typedef struct wl_rateset_args { + uint32 count; /* # rates in this set */ + uint8 rates[WL_MAXRATES_IN_SET]; /* rates in 500kbps units w/hi bit set if basic */ + uint8 mcs[MCSSET_LEN]; /* supported mcs index bit map */ +} wl_rateset_args_t; + +/* uint32 list */ +typedef struct wl_uint32_list { + /* in - # of elements, out - # of entries */ + uint32 count; + /* variable length uint32 list */ + uint32 element[1]; +} wl_uint32_list_t; + +/* used for association with a specific BSSID and chanspec list */ +typedef struct wl_assoc_params { + struct ether_addr bssid; /* 00:00:00:00:00:00: broadcast scan */ + uint16 bssid_cnt; /* 0: use chanspec_num, and the single bssid, + * otherwise count of chanspecs in chanspec_list + * AND paired bssids following chanspec_list + */ + int32 chanspec_num; /* 0: all available channels, + * otherwise count of chanspecs in chanspec_list + */ + chanspec_t chanspec_list[1]; /* list of chanspecs */ +} wl_assoc_params_t; +#define WL_ASSOC_PARAMS_FIXED_SIZE OFFSETOF(wl_assoc_params_t, chanspec_list) + +/* used for reassociation/roam to a specific BSSID and channel */ +typedef wl_assoc_params_t wl_reassoc_params_t; +#define WL_REASSOC_PARAMS_FIXED_SIZE WL_ASSOC_PARAMS_FIXED_SIZE + +/* used for association to a specific BSSID and channel */ +typedef wl_assoc_params_t wl_join_assoc_params_t; +#define WL_JOIN_ASSOC_PARAMS_FIXED_SIZE WL_ASSOC_PARAMS_FIXED_SIZE + +/* used for join with or without a specific bssid and channel list */ +typedef struct wl_join_params { + wlc_ssid_t ssid; + wl_assoc_params_t params; /* optional field, but it must include the fixed portion + * of the wl_assoc_params_t struct when it does present. + */ +} wl_join_params_t; + +#ifndef LINUX_POSTMOGRIFY_REMOVAL +#define WL_JOIN_PARAMS_FIXED_SIZE (OFFSETOF(wl_join_params_t, params) + \ + WL_ASSOC_PARAMS_FIXED_SIZE) +/* scan params for extended join */ +typedef struct wl_join_scan_params { + uint8 scan_type; /* 0 use default, active or passive scan */ + int32 nprobes; /* -1 use default, number of probes per channel */ + int32 active_time; /* -1 use default, dwell time per channel for + * active scanning + */ + int32 passive_time; /* -1 use default, dwell time per channel + * for passive scanning + */ + int32 home_time; /* -1 use default, dwell time for the home channel + * between channel scans + */ +} wl_join_scan_params_t; + +/* extended join params */ +typedef struct wl_extjoin_params { + wlc_ssid_t ssid; /* {0, ""}: wildcard scan */ + wl_join_scan_params_t scan; + wl_join_assoc_params_t assoc; /* optional field, but it must include the fixed portion + * of the wl_join_assoc_params_t struct when it does + * present. + */ +} wl_extjoin_params_t; +#define WL_EXTJOIN_PARAMS_FIXED_SIZE (OFFSETOF(wl_extjoin_params_t, assoc) + \ + WL_JOIN_ASSOC_PARAMS_FIXED_SIZE) + +/* All builds use the new 11ac ratespec/chanspec */ +#undef D11AC_IOTYPES +#define D11AC_IOTYPES + +#ifndef D11AC_IOTYPES + +/* defines used by the nrate iovar */ +#define NRATE_MCS_INUSE 0x00000080 /* MSC in use,indicates b0-6 holds an mcs */ +#define NRATE_RATE_MASK 0x0000007f /* rate/mcs value */ +#define NRATE_STF_MASK 0x0000ff00 /* stf mode mask: siso, cdd, stbc, sdm */ +#define NRATE_STF_SHIFT 8 /* stf mode shift */ +#define NRATE_OVERRIDE 0x80000000 /* bit indicates override both rate & mode */ +#define NRATE_OVERRIDE_MCS_ONLY 0x40000000 /* bit indicate to override mcs only */ +#define NRATE_SGI_MASK 0x00800000 /* sgi mode */ +#define NRATE_SGI_SHIFT 23 /* sgi mode */ +#define NRATE_LDPC_CODING 0x00400000 /* bit indicates adv coding in use */ +#define NRATE_LDPC_SHIFT 22 /* ldpc shift */ + +#define NRATE_STF_SISO 0 /* stf mode SISO */ +#define NRATE_STF_CDD 1 /* stf mode CDD */ +#define NRATE_STF_STBC 2 /* stf mode STBC */ +#define NRATE_STF_SDM 3 /* stf mode SDM */ + +#else /* D11AC_IOTYPES */ + +/* WL_RSPEC defines for rate information */ +#define WL_RSPEC_RATE_MASK 0x000000FF /* rate or HT MCS value */ +#define WL_RSPEC_VHT_MCS_MASK 0x0000000F /* VHT MCS value */ +#define WL_RSPEC_VHT_NSS_MASK 0x000000F0 /* VHT Nss value */ +#define WL_RSPEC_VHT_NSS_SHIFT 4 /* VHT Nss value shift */ +#define WL_RSPEC_TXEXP_MASK 0x00000300 +#define WL_RSPEC_TXEXP_SHIFT 8 +#define WL_RSPEC_BW_MASK 0x00070000 /* bandwidth mask */ +#define WL_RSPEC_BW_SHIFT 16 /* bandwidth shift */ +#define WL_RSPEC_STBC 0x00100000 /* STBC encoding, Nsts = 2 x Nss */ +#define WL_RSPEC_LDPC 0x00400000 /* bit indicates adv coding in use */ +#define WL_RSPEC_SGI 0x00800000 /* Short GI mode */ +#define WL_RSPEC_ENCODING_MASK 0x03000000 /* Encoding of Rate/MCS field */ +#define WL_RSPEC_OVERRIDE_RATE 0x40000000 /* bit indicate to override mcs only */ +#define WL_RSPEC_OVERRIDE_MODE 0x80000000 /* bit indicates override both rate & mode */ + +/* WL_RSPEC_ENCODING field defs */ +#define WL_RSPEC_ENCODE_RATE 0x00000000 /* Legacy rate is stored in RSPEC_RATE_MASK */ +#define WL_RSPEC_ENCODE_HT 0x01000000 /* HT MCS is stored in RSPEC_RATE_MASK */ +#define WL_RSPEC_ENCODE_VHT 0x02000000 /* VHT MCS and Nss is stored in RSPEC_RATE_MASK */ + +/* WL_RSPEC_BW field defs */ +#define WL_RSPEC_BW_UNSPECIFIED 0 +#define WL_RSPEC_BW_20MHZ 0x00010000 +#define WL_RSPEC_BW_40MHZ 0x00020000 +#define WL_RSPEC_BW_80MHZ 0x00030000 +#define WL_RSPEC_BW_160MHZ 0x00040000 + +/* Legacy defines for the nrate iovar */ +#define OLD_NRATE_MCS_INUSE 0x00000080 /* MSC in use,indicates b0-6 holds an mcs */ +#define OLD_NRATE_RATE_MASK 0x0000007f /* rate/mcs value */ +#define OLD_NRATE_STF_MASK 0x0000ff00 /* stf mode mask: siso, cdd, stbc, sdm */ +#define OLD_NRATE_STF_SHIFT 8 /* stf mode shift */ +#define OLD_NRATE_OVERRIDE 0x80000000 /* bit indicates override both rate & mode */ +#define OLD_NRATE_OVERRIDE_MCS_ONLY 0x40000000 /* bit indicate to override mcs only */ +#define OLD_NRATE_SGI 0x00800000 /* sgi mode */ +#define OLD_NRATE_LDPC_CODING 0x00400000 /* bit indicates adv coding in use */ + +#define OLD_NRATE_STF_SISO 0 /* stf mode SISO */ +#define OLD_NRATE_STF_CDD 1 /* stf mode CDD */ +#define OLD_NRATE_STF_STBC 2 /* stf mode STBC */ +#define OLD_NRATE_STF_SDM 3 /* stf mode SDM */ + +#endif /* D11AC_IOTYPES */ + +#define ANTENNA_NUM_1 1 /* total number of antennas to be used */ +#define ANTENNA_NUM_2 2 +#define ANTENNA_NUM_3 3 +#define ANTENNA_NUM_4 4 + +#define ANT_SELCFG_AUTO 0x80 /* bit indicates antenna sel AUTO */ +#define ANT_SELCFG_MASK 0x33 /* antenna configuration mask */ +#define ANT_SELCFG_MAX 4 /* max number of antenna configurations */ +#define ANT_SELCFG_TX_UNICAST 0 /* unicast tx antenna configuration */ +#define ANT_SELCFG_RX_UNICAST 1 /* unicast rx antenna configuration */ +#define ANT_SELCFG_TX_DEF 2 /* default tx antenna configuration */ +#define ANT_SELCFG_RX_DEF 3 /* default rx antenna configuration */ + +#define MAX_STREAMS_SUPPORTED 4 /* max number of streams supported */ + +typedef struct { + uint8 ant_config[ANT_SELCFG_MAX]; /* antenna configuration */ + uint8 num_antcfg; /* number of available antenna configurations */ +} wlc_antselcfg_t; + +#define HIGHEST_SINGLE_STREAM_MCS 7 /* MCS values greater than this enable multiple streams */ + +#define MAX_CCA_CHANNELS 38 /* Max number of 20 Mhz wide channels */ +#define MAX_CCA_SECS 60 /* CCA keeps this many seconds history */ + +#define IBSS_MED 15 /* Mediom in-bss congestion percentage */ +#define IBSS_HI 25 /* Hi in-bss congestion percentage */ +#define OBSS_MED 12 +#define OBSS_HI 25 +#define INTERFER_MED 5 +#define INTERFER_HI 10 + +#define CCA_FLAG_2G_ONLY 0x01 /* Return a channel from 2.4 Ghz band */ +#define CCA_FLAG_5G_ONLY 0x02 /* Return a channel from 2.4 Ghz band */ +#define CCA_FLAG_IGNORE_DURATION 0x04 /* Ignore dwell time for each channel */ +#define CCA_FLAGS_PREFER_1_6_11 0x10 +#define CCA_FLAG_IGNORE_INTERFER 0x20 /* do not exlude channel based on interfer level */ + +#define CCA_ERRNO_BAND 1 /* After filtering for band pref, no choices left */ +#define CCA_ERRNO_DURATION 2 /* After filtering for duration, no choices left */ +#define CCA_ERRNO_PREF_CHAN 3 /* After filtering for chan pref, no choices left */ +#define CCA_ERRNO_INTERFER 4 /* After filtering for interference, no choices left */ +#define CCA_ERRNO_TOO_FEW 5 /* Only 1 channel was input */ + +typedef struct { + uint32 duration; /* millisecs spent sampling this channel */ + uint32 congest_ibss; /* millisecs in our bss (presumably this traffic will */ + /* move if cur bss moves channels) */ + uint32 congest_obss; /* traffic not in our bss */ + uint32 interference; /* millisecs detecting a non 802.11 interferer. */ + uint32 timestamp; /* second timestamp */ +} cca_congest_t; + +typedef struct { + chanspec_t chanspec; /* Which channel? */ + uint8 num_secs; /* How many secs worth of data */ + cca_congest_t secs[1]; /* Data */ +} cca_congest_channel_req_t; + +/* interference source detection and identification mode */ +#define ITFR_MODE_DISABLE 0 /* disable feature */ +#define ITFR_MODE_MANUAL_ENABLE 1 /* enable manual detection */ +#define ITFR_MODE_AUTO_ENABLE 2 /* enable auto detection */ + +/* interference sources */ +enum interference_source { + ITFR_NONE = 0, /* interference */ + ITFR_PHONE, /* wireless phone */ + ITFR_VIDEO_CAMERA, /* wireless video camera */ + ITFR_MICROWAVE_OVEN, /* microwave oven */ + ITFR_BABY_MONITOR, /* wireless baby monitor */ + ITFR_BLUETOOTH, /* bluetooth */ + ITFR_VIDEO_CAMERA_OR_BABY_MONITOR, /* wireless camera or baby monitor */ + ITFR_BLUETOOTH_OR_BABY_MONITOR, /* bluetooth or baby monitor */ + ITFR_VIDEO_CAMERA_OR_PHONE, /* video camera or phone */ + ITFR_UNIDENTIFIED /* interference from unidentified source */ +}; + +/* structure for interference source report */ +typedef struct { + uint32 flags; /* flags. bit definitions below */ + uint32 source; /* last detected interference source */ + uint32 timestamp; /* second timestamp on interferenced flag change */ +} interference_source_rep_t; + +/* bit definitions for flags in interference source report */ +#define ITFR_INTERFERENCED 1 /* interference detected */ +#define ITFR_HOME_CHANNEL 2 /* home channel has interference */ +#define ITFR_NOISY_ENVIRONMENT 4 /* noisy environemnt so feature stopped */ + +#endif /* LINUX_POSTMOGRIFY_REMOVAL */ + +#define WLC_CNTRY_BUF_SZ 4 /* Country string is 3 bytes + NUL */ + +#ifndef LINUX_POSTMOGRIFY_REMOVAL + +typedef struct wl_country { + char country_abbrev[WLC_CNTRY_BUF_SZ]; /* nul-terminated country code used in + * the Country IE + */ + int32 rev; /* revision specifier for ccode + * on set, -1 indicates unspecified. + * on get, rev >= 0 + */ + char ccode[WLC_CNTRY_BUF_SZ]; /* nul-terminated built-in country code. + * variable length, but fixed size in + * struct allows simple allocation for + * expected country strings <= 3 chars. + */ +} wl_country_t; + +typedef struct wl_channels_in_country { + uint32 buflen; + uint32 band; + char country_abbrev[WLC_CNTRY_BUF_SZ]; + uint32 count; + uint32 channel[1]; +} wl_channels_in_country_t; + +typedef struct wl_country_list { + uint32 buflen; + uint32 band_set; + uint32 band; + uint32 count; + char country_abbrev[1]; +} wl_country_list_t; + +#define WL_NUM_RPI_BINS 8 +#define WL_RM_TYPE_BASIC 1 +#define WL_RM_TYPE_CCA 2 +#define WL_RM_TYPE_RPI 3 + +#define WL_RM_FLAG_PARALLEL (1<<0) + +#define WL_RM_FLAG_LATE (1<<1) +#define WL_RM_FLAG_INCAPABLE (1<<2) +#define WL_RM_FLAG_REFUSED (1<<3) + +typedef struct wl_rm_req_elt { + int8 type; + int8 flags; + chanspec_t chanspec; + uint32 token; /* token for this measurement */ + uint32 tsf_h; /* TSF high 32-bits of Measurement start time */ + uint32 tsf_l; /* TSF low 32-bits */ + uint32 dur; /* TUs */ +} wl_rm_req_elt_t; + +typedef struct wl_rm_req { + uint32 token; /* overall measurement set token */ + uint32 count; /* number of measurement requests */ + void *cb; /* completion callback function: may be NULL */ + void *cb_arg; /* arg to completion callback function */ + wl_rm_req_elt_t req[1]; /* variable length block of requests */ +} wl_rm_req_t; +#define WL_RM_REQ_FIXED_LEN OFFSETOF(wl_rm_req_t, req) + +typedef struct wl_rm_rep_elt { + int8 type; + int8 flags; + chanspec_t chanspec; + uint32 token; /* token for this measurement */ + uint32 tsf_h; /* TSF high 32-bits of Measurement start time */ + uint32 tsf_l; /* TSF low 32-bits */ + uint32 dur; /* TUs */ + uint32 len; /* byte length of data block */ + uint8 data[1]; /* variable length data block */ +} wl_rm_rep_elt_t; +#define WL_RM_REP_ELT_FIXED_LEN 24 /* length excluding data block */ + +#define WL_RPI_REP_BIN_NUM 8 +typedef struct wl_rm_rpi_rep { + uint8 rpi[WL_RPI_REP_BIN_NUM]; + int8 rpi_max[WL_RPI_REP_BIN_NUM]; +} wl_rm_rpi_rep_t; + +typedef struct wl_rm_rep { + uint32 token; /* overall measurement set token */ + uint32 len; /* length of measurement report block */ + wl_rm_rep_elt_t rep[1]; /* variable length block of reports */ +} wl_rm_rep_t; +#define WL_RM_REP_FIXED_LEN 8 + + +typedef enum sup_auth_status { + /* Basic supplicant authentication states */ + WLC_SUP_DISCONNECTED = 0, + WLC_SUP_CONNECTING, + WLC_SUP_IDREQUIRED, + WLC_SUP_AUTHENTICATING, + WLC_SUP_AUTHENTICATED, + WLC_SUP_KEYXCHANGE, + WLC_SUP_KEYED, + WLC_SUP_TIMEOUT, + WLC_SUP_LAST_BASIC_STATE, + + /* Extended supplicant authentication states */ + /* Waiting to receive handshake msg M1 */ + WLC_SUP_KEYXCHANGE_WAIT_M1 = WLC_SUP_AUTHENTICATED, + /* Preparing to send handshake msg M2 */ + WLC_SUP_KEYXCHANGE_PREP_M2 = WLC_SUP_KEYXCHANGE, + /* Waiting to receive handshake msg M3 */ + WLC_SUP_KEYXCHANGE_WAIT_M3 = WLC_SUP_LAST_BASIC_STATE, + WLC_SUP_KEYXCHANGE_PREP_M4, /* Preparing to send handshake msg M4 */ + WLC_SUP_KEYXCHANGE_WAIT_G1, /* Waiting to receive handshake msg G1 */ + WLC_SUP_KEYXCHANGE_PREP_G2 /* Preparing to send handshake msg G2 */ +} sup_auth_status_t; +#endif /* LINUX_POSTMOGRIFY_REMOVAL */ + +/* Enumerate crypto algorithms */ +#define CRYPTO_ALGO_OFF 0 +#define CRYPTO_ALGO_WEP1 1 +#define CRYPTO_ALGO_TKIP 2 +#define CRYPTO_ALGO_WEP128 3 +#define CRYPTO_ALGO_AES_CCM 4 +#define CRYPTO_ALGO_AES_OCB_MSDU 5 +#define CRYPTO_ALGO_AES_OCB_MPDU 6 +#if !defined(BCMEXTCCX) +#define CRYPTO_ALGO_NALG 7 +#else +#define CRYPTO_ALGO_CKIP 7 +#define CRYPTO_ALGO_CKIP_MMH 8 +#define CRYPTO_ALGO_WEP_MMH 9 +#define CRYPTO_ALGO_NALG 10 +#endif +#define CRYPTO_ALGO_PMK 12 /* for 802.1x supp to set PMK before 4-way */ + +#define WSEC_GEN_MIC_ERROR 0x0001 +#define WSEC_GEN_REPLAY 0x0002 +#define WSEC_GEN_ICV_ERROR 0x0004 +#define WSEC_GEN_MFP_ACT_ERROR 0x0008 +#define WSEC_GEN_MFP_DISASSOC_ERROR 0x0010 +#define WSEC_GEN_MFP_DEAUTH_ERROR 0x0020 + +#define WL_SOFT_KEY (1 << 0) /* Indicates this key is using soft encrypt */ +#define WL_PRIMARY_KEY (1 << 1) /* Indicates this key is the primary (ie tx) key */ +#if defined(BCMEXTCCX) +#define WL_CKIP_KP (1 << 4) /* CMIC */ +#define WL_CKIP_MMH (1 << 5) /* CKIP */ +#else +#define WL_KF_RES_4 (1 << 4) /* Reserved for backward compat */ +#define WL_KF_RES_5 (1 << 5) /* Reserved for backward compat */ +#endif +#define WL_IBSS_PEER_GROUP_KEY (1 << 6) /* Indicates a group key for a IBSS PEER */ + +typedef struct wl_wsec_key { + uint32 index; /* key index */ + uint32 len; /* key length */ + uint8 data[DOT11_MAX_KEY_SIZE]; /* key data */ + uint32 pad_1[18]; + uint32 algo; /* CRYPTO_ALGO_AES_CCM, CRYPTO_ALGO_WEP128, etc */ + uint32 flags; /* misc flags */ + uint32 pad_2[2]; + int pad_3; + int iv_initialized; /* has IV been initialized already? */ + int pad_4; + /* Rx IV */ + struct { + uint32 hi; /* upper 32 bits of IV */ + uint16 lo; /* lower 16 bits of IV */ + } rxiv; + uint32 pad_5[2]; + struct ether_addr ea; /* per station */ +} wl_wsec_key_t; + +#define WSEC_MIN_PSK_LEN 8 +#define WSEC_MAX_PSK_LEN 64 + +/* Flag for key material needing passhash'ing */ +#define WSEC_PASSPHRASE (1<<0) + +/* receptacle for WLC_SET_WSEC_PMK parameter */ +typedef struct { + ushort key_len; /* octets in key material */ + ushort flags; /* key handling qualification */ + uint8 key[WSEC_MAX_PSK_LEN]; /* PMK material */ +} wsec_pmk_t; + +/* wireless security bitvec */ +#define WEP_ENABLED 0x0001 +#define TKIP_ENABLED 0x0002 +#define AES_ENABLED 0x0004 +#define WSEC_SWFLAG 0x0008 +#define SES_OW_ENABLED 0x0040 /* to go into transition mode without setting wep */ + +/* wsec macros for operating on the above definitions */ +#define WSEC_WEP_ENABLED(wsec) ((wsec) & WEP_ENABLED) +#define WSEC_TKIP_ENABLED(wsec) ((wsec) & TKIP_ENABLED) +#define WSEC_AES_ENABLED(wsec) ((wsec) & AES_ENABLED) + +#define WSEC_ENABLED(wsec) ((wsec) & (WEP_ENABLED | TKIP_ENABLED | AES_ENABLED)) +#define WSEC_SES_OW_ENABLED(wsec) ((wsec) & SES_OW_ENABLED) + +#ifdef MFP +#define MFP_CAPABLE 0x0200 +#define MFP_REQUIRED 0x0400 +#define MFP_SHA256 0x0800 /* a special configuration for STA for WIFI test tool */ +#endif /* MFP */ + +/* WPA authentication mode bitvec */ +#define WPA_AUTH_DISABLED 0x0000 /* Legacy (i.e., non-WPA) */ +#define WPA_AUTH_NONE 0x0001 /* none (IBSS) */ +#define WPA_AUTH_UNSPECIFIED 0x0002 /* over 802.1x */ +#define WPA_AUTH_PSK 0x0004 /* Pre-shared key */ +#if defined(BCMEXTCCX) +#define WPA_AUTH_CCKM 0x0008 /* CCKM */ +#define WPA2_AUTH_CCKM 0x0010 /* CCKM2 */ +#endif +/* #define WPA_AUTH_8021X 0x0020 */ /* 802.1x, reserved */ +#define WPA2_AUTH_UNSPECIFIED 0x0040 /* over 802.1x */ +#define WPA2_AUTH_PSK 0x0080 /* Pre-shared key */ +#define BRCM_AUTH_PSK 0x0100 /* BRCM specific PSK */ +#define BRCM_AUTH_DPT 0x0200 /* DPT PSK without group keys */ +#define WPA2_AUTH_MFP 0x1000 /* MFP (11w) in contrast to CCX */ +#define WPA2_AUTH_TPK 0x2000 /* TDLS Peer Key */ +#define WPA2_AUTH_FT 0x4000 /* Fast Transition. */ +#define WPA_AUTH_PFN_ANY 0xffffffff /* for PFN, match only ssid */ + +/* pmkid */ +#define MAXPMKID 16 + +typedef struct _pmkid { + struct ether_addr BSSID; + uint8 PMKID[WPA2_PMKID_LEN]; +} pmkid_t; + +typedef struct _pmkid_list { + uint32 npmkid; + pmkid_t pmkid[1]; +} pmkid_list_t; + +typedef struct _pmkid_cand { + struct ether_addr BSSID; + uint8 preauth; +} pmkid_cand_t; + +typedef struct _pmkid_cand_list { + uint32 npmkid_cand; + pmkid_cand_t pmkid_cand[1]; +} pmkid_cand_list_t; + +#ifndef LINUX_POSTMOGRIFY_REMOVAL +typedef struct wl_assoc_info { + uint32 req_len; + uint32 resp_len; + uint32 flags; + struct dot11_assoc_req req; + struct ether_addr reassoc_bssid; /* used in reassoc's */ + struct dot11_assoc_resp resp; +} wl_assoc_info_t; + +/* flags */ +#define WLC_ASSOC_REQ_IS_REASSOC 0x01 /* assoc req was actually a reassoc */ + +typedef struct wl_led_info { + uint32 index; /* led index */ + uint32 behavior; + uint8 activehi; +} wl_led_info_t; + + +/* srom read/write struct passed through ioctl */ +typedef struct { + uint byteoff; /* byte offset */ + uint nbytes; /* number of bytes */ + uint16 buf[1]; +} srom_rw_t; + +/* similar cis (srom or otp) struct [iovar: may not be aligned] */ +typedef struct { + uint32 source; /* cis source */ + uint32 byteoff; /* byte offset */ + uint32 nbytes; /* number of bytes */ + /* data follows here */ +} cis_rw_t; + +#define WLC_CIS_DEFAULT 0 /* built-in default */ +#define WLC_CIS_SROM 1 /* source is sprom */ +#define WLC_CIS_OTP 2 /* source is otp */ + +/* R_REG and W_REG struct passed through ioctl */ +typedef struct { + uint32 byteoff; /* byte offset of the field in d11regs_t */ + uint32 val; /* read/write value of the field */ + uint32 size; /* sizeof the field */ + uint band; /* band (optional) */ +} rw_reg_t; + +/* Structure used by GET/SET_ATTEN ioctls - it controls power in b/g-band */ +/* PCL - Power Control Loop */ +/* current gain setting is replaced by user input */ +#define WL_ATTEN_APP_INPUT_PCL_OFF 0 /* turn off PCL, apply supplied input */ +#define WL_ATTEN_PCL_ON 1 /* turn on PCL */ +/* current gain setting is maintained */ +#define WL_ATTEN_PCL_OFF 2 /* turn off PCL. */ + +typedef struct { + uint16 auto_ctrl; /* WL_ATTEN_XX */ + uint16 bb; /* Baseband attenuation */ + uint16 radio; /* Radio attenuation */ + uint16 txctl1; /* Radio TX_CTL1 value */ +} atten_t; + +/* Per-AC retry parameters */ +struct wme_tx_params_s { + uint8 short_retry; + uint8 short_fallback; + uint8 long_retry; + uint8 long_fallback; + uint16 max_rate; /* In units of 512 Kbps */ +}; + +typedef struct wme_tx_params_s wme_tx_params_t; + +#define WL_WME_TX_PARAMS_IO_BYTES (sizeof(wme_tx_params_t) * AC_COUNT) + +/* defines used by poweridx iovar - it controls power in a-band */ +/* current gain setting is maintained */ +#define WL_PWRIDX_PCL_OFF -2 /* turn off PCL. */ +#define WL_PWRIDX_PCL_ON -1 /* turn on PCL */ +#define WL_PWRIDX_LOWER_LIMIT -2 /* lower limit */ +#define WL_PWRIDX_UPPER_LIMIT 63 /* upper limit */ +/* value >= 0 causes + * - input to be set to that value + * - PCL to be off + */ + +/* Used to get specific link/ac parameters */ +typedef struct { + int ac; + uint8 val; + struct ether_addr ea; +} link_val_t; + +#define BCM_MAC_STATUS_INDICATION (0x40010200L) + +typedef struct { + uint16 ver; /* version of this struct */ + uint16 len; /* length in bytes of this structure */ + uint16 cap; /* sta's advertised capabilities */ + uint32 flags; /* flags defined below */ + uint32 idle; /* time since data pkt rx'd from sta */ + struct ether_addr ea; /* Station address */ + wl_rateset_t rateset; /* rateset in use */ + uint32 in; /* seconds elapsed since associated */ + uint32 listen_interval_inms; /* Min Listen interval in ms for this STA */ + uint32 tx_pkts; /* # of packets transmitted */ + uint32 tx_failures; /* # of packets failed */ + uint32 rx_ucast_pkts; /* # of unicast packets received */ + uint32 rx_mcast_pkts; /* # of multicast packets received */ + uint32 tx_rate; /* Rate of last successful tx frame */ + uint32 rx_rate; /* Rate of last successful rx frame */ + uint32 rx_decrypt_succeeds; /* # of packet decrypted successfully */ + uint32 rx_decrypt_failures; /* # of packet decrypted unsuccessfully */ +} sta_info_t; + +#define WL_OLD_STAINFO_SIZE OFFSETOF(sta_info_t, tx_pkts) + +#define WL_STA_VER 3 + +/* Flags for sta_info_t indicating properties of STA */ +#define WL_STA_BRCM 0x1 /* Running a Broadcom driver */ +#define WL_STA_WME 0x2 /* WMM association */ +#define WL_STA_UNUSED 0x4 +#define WL_STA_AUTHE 0x8 /* Authenticated */ +#define WL_STA_ASSOC 0x10 /* Associated */ +#define WL_STA_AUTHO 0x20 /* Authorized */ +#define WL_STA_WDS 0x40 /* Wireless Distribution System */ +#define WL_STA_WDS_LINKUP 0x80 /* WDS traffic/probes flowing properly */ +#define WL_STA_PS 0x100 /* STA is in power save mode from AP's viewpoint */ +#define WL_STA_APSD_BE 0x200 /* APSD delv/trigger for AC_BE is default enabled */ +#define WL_STA_APSD_BK 0x400 /* APSD delv/trigger for AC_BK is default enabled */ +#define WL_STA_APSD_VI 0x800 /* APSD delv/trigger for AC_VI is default enabled */ +#define WL_STA_APSD_VO 0x1000 /* APSD delv/trigger for AC_VO is default enabled */ +#define WL_STA_N_CAP 0x2000 /* STA 802.11n capable */ +#define WL_STA_SCBSTATS 0x4000 /* Per STA debug stats */ + +#define WL_WDS_LINKUP WL_STA_WDS_LINKUP /* deprecated */ + +/* Values for TX Filter override mode */ +#define WLC_TXFILTER_OVERRIDE_DISABLED 0 +#define WLC_TXFILTER_OVERRIDE_ENABLED 1 + +#endif /* LINUX_POSTMOGRIFY_REMOVAL */ + +/* Used to get specific STA parameters */ +typedef struct { + uint32 val; + struct ether_addr ea; +} scb_val_t; + +/* Used by iovar versions of some ioctls, i.e. WLC_SCB_AUTHORIZE et al */ +typedef struct { + uint32 code; + scb_val_t ioctl_args; +} authops_t; + +/* channel encoding */ +typedef struct channel_info { + int hw_channel; + int target_channel; + int scan_channel; +} channel_info_t; + +/* For ioctls that take a list of MAC addresses */ +struct maclist { + uint count; /* number of MAC addresses */ + struct ether_addr ea[1]; /* variable length array of MAC addresses */ +}; + +#ifndef LINUX_POSTMOGRIFY_REMOVAL +/* get pkt count struct passed through ioctl */ +typedef struct get_pktcnt { + uint rx_good_pkt; + uint rx_bad_pkt; + uint tx_good_pkt; + uint tx_bad_pkt; + uint rx_ocast_good_pkt; /* unicast packets destined for others */ +} get_pktcnt_t; + +/* NINTENDO2 */ +#define LQ_IDX_MIN 0 +#define LQ_IDX_MAX 1 +#define LQ_IDX_AVG 2 +#define LQ_IDX_SUM 2 +#define LQ_IDX_LAST 3 +#define LQ_STOP_MONITOR 0 +#define LQ_START_MONITOR 1 + +/* Get averages RSSI, Rx PHY rate and SNR values */ +typedef struct { + int rssi[LQ_IDX_LAST]; /* Array to keep min, max, avg rssi */ + int snr[LQ_IDX_LAST]; /* Array to keep min, max, avg snr */ + int isvalid; /* Flag indicating whether above data is valid */ +} wl_lq_t; /* Link Quality */ + +typedef enum wl_wakeup_reason_type { + LCD_ON = 1, + LCD_OFF, + DRC1_WAKE, + DRC2_WAKE, + REASON_LAST +} wl_wr_type_t; + +typedef struct { +/* Unique filter id */ + uint32 id; + +/* stores the reason for the last wake up */ + uint8 reason; +} wl_wr_t; + +/* Get MAC specific rate histogram command */ +typedef struct { + struct ether_addr ea; /* MAC Address */ + uint8 ac_cat; /* Access Category */ + uint8 num_pkts; /* Number of packet entries to be averaged */ +} wl_mac_ratehisto_cmd_t; /* MAC Specific Rate Histogram command */ + +/* Get MAC rate histogram response */ +typedef struct { + uint32 rate[WLC_MAXRATE + 1]; /* Rates */ + uint32 mcs[WL_RATESET_SZ_HT_MCS * WL_TX_CHAINS_MAX]; /* MCS counts */ + uint32 vht[WL_RATESET_SZ_VHT_MCS][WL_TX_CHAINS_MAX]; /* VHT counts */ + uint32 tsf_timer[2][2]; /* Start and End time for 8bytes value */ +} wl_mac_ratehisto_res_t; /* MAC Specific Rate Histogram Response */ + +/* Values for TX Filter override mode */ +#define WLC_TXFILTER_OVERRIDE_DISABLED 0 +#define WLC_TXFILTER_OVERRIDE_ENABLED 1 + +#define WL_IOCTL_ACTION_GET 0x0 +#define WL_IOCTL_ACTION_SET 0x1 +#define WL_IOCTL_ACTION_OVL_IDX_MASK 0x1e +#define WL_IOCTL_ACTION_OVL_RSV 0x20 +#define WL_IOCTL_ACTION_OVL 0x40 +#define WL_IOCTL_ACTION_MASK 0x7e +#define WL_IOCTL_ACTION_OVL_SHIFT 1 + +#endif /* LINUX_POSTMOGRIFY_REMOVAL */ + +/* Linux network driver ioctl encoding */ +typedef struct wl_ioctl { + uint cmd; /* common ioctl definition */ + void *buf; /* pointer to user buffer */ + uint len; /* length of user buffer */ + uint8 set; /* 1=set IOCTL; 0=query IOCTL */ + uint used; /* bytes read or written (optional) */ + uint needed; /* bytes needed (optional) */ +} wl_ioctl_t; + +#ifndef LINUX_POSTMOGRIFY_REMOVAL + +/* reference to wl_ioctl_t struct used by usermode driver */ +#define ioctl_subtype set /* subtype param */ +#define ioctl_pid used /* pid param */ +#define ioctl_status needed /* status param */ + +/* + * Structure for passing hardware and software + * revision info up from the driver. + */ +typedef struct wlc_rev_info { + uint vendorid; /* PCI vendor id */ + uint deviceid; /* device id of chip */ + uint radiorev; /* radio revision */ + uint chiprev; /* chip revision */ + uint corerev; /* core revision */ + uint boardid; /* board identifier (usu. PCI sub-device id) */ + uint boardvendor; /* board vendor (usu. PCI sub-vendor id) */ + uint boardrev; /* board revision */ + uint driverrev; /* driver version */ + uint ucoderev; /* microcode version */ + uint bus; /* bus type */ + uint chipnum; /* chip number */ + uint phytype; /* phy type */ + uint phyrev; /* phy revision */ + uint anarev; /* anacore rev */ + uint chippkg; /* chip package info */ +} wlc_rev_info_t; + +#define WL_REV_INFO_LEGACY_LENGTH 48 + +#define WL_BRAND_MAX 10 +typedef struct wl_instance_info { + uint instance; + char brand[WL_BRAND_MAX]; +} wl_instance_info_t; + +/* structure to change size of tx fifo */ +typedef struct wl_txfifo_sz { + uint16 magic; + uint16 fifo; + uint16 size; +} wl_txfifo_sz_t; +/* magic pattern used for mismatch driver and wl */ +#define WL_TXFIFO_SZ_MAGIC 0xa5a5 + +/* Transfer info about an IOVar from the driver */ +/* Max supported IOV name size in bytes, + 1 for nul termination */ +#define WLC_IOV_NAME_LEN 30 +typedef struct wlc_iov_trx_s { + uint8 module; + uint8 type; + char name[WLC_IOV_NAME_LEN]; +} wlc_iov_trx_t; + +/* check this magic number */ +#define WLC_IOCTL_MAGIC 0x14e46c77 + +/* bump this number if you change the ioctl interface */ +#ifdef D11AC_IOTYPES +#define WLC_IOCTL_VERSION 2 +#define WLC_IOCTL_VERSION_LEGACY_IOTYPES 1 +#else +#define WLC_IOCTL_VERSION 1 +#endif /* D11AC_IOTYPES */ +#endif /* LINUX_POSTMOGRIFY_REMOVAL */ + +#define WLC_IOCTL_MAXLEN 8192 /* max length ioctl buffer required */ +#define WLC_IOCTL_SMLEN 256 /* "small" length ioctl buffer required */ +#define WLC_IOCTL_MEDLEN 1536 /* "med" length ioctl buffer required */ +#if defined(LCNCONF) || defined(LCN40CONF) +#define WLC_SAMPLECOLLECT_MAXLEN 8192 /* Max Sample Collect buffer */ +#else +#define WLC_SAMPLECOLLECT_MAXLEN 10240 /* Max Sample Collect buffer for two cores */ +#endif + +/* common ioctl definitions */ +#define WLC_GET_MAGIC 0 +#define WLC_GET_VERSION 1 +#define WLC_UP 2 +#define WLC_DOWN 3 +#define WLC_GET_LOOP 4 +#define WLC_SET_LOOP 5 +#define WLC_DUMP 6 +#define WLC_GET_MSGLEVEL 7 +#define WLC_SET_MSGLEVEL 8 +#define WLC_GET_PROMISC 9 +#define WLC_SET_PROMISC 10 +/* #define WLC_OVERLAY_IOCTL 11 */ /* not supported */ +#define WLC_GET_RATE 12 +#define WLC_GET_MAX_RATE 13 +#define WLC_GET_INSTANCE 14 +/* #define WLC_GET_FRAG 15 */ /* no longer supported */ +/* #define WLC_SET_FRAG 16 */ /* no longer supported */ +/* #define WLC_GET_RTS 17 */ /* no longer supported */ +/* #define WLC_SET_RTS 18 */ /* no longer supported */ +#define WLC_GET_INFRA 19 +#define WLC_SET_INFRA 20 +#define WLC_GET_AUTH 21 +#define WLC_SET_AUTH 22 +#define WLC_GET_BSSID 23 +#define WLC_SET_BSSID 24 +#define WLC_GET_SSID 25 +#define WLC_SET_SSID 26 +#define WLC_RESTART 27 +#define WLC_TERMINATED 28 +/* #define WLC_DUMP_SCB 28 */ /* no longer supported */ +#define WLC_GET_CHANNEL 29 +#define WLC_SET_CHANNEL 30 +#define WLC_GET_SRL 31 +#define WLC_SET_SRL 32 +#define WLC_GET_LRL 33 +#define WLC_SET_LRL 34 +#define WLC_GET_PLCPHDR 35 +#define WLC_SET_PLCPHDR 36 +#define WLC_GET_RADIO 37 +#define WLC_SET_RADIO 38 +#define WLC_GET_PHYTYPE 39 +#define WLC_DUMP_RATE 40 +#define WLC_SET_RATE_PARAMS 41 +#define WLC_GET_FIXRATE 42 +#define WLC_SET_FIXRATE 43 +/* #define WLC_GET_WEP 42 */ /* no longer supported */ +/* #define WLC_SET_WEP 43 */ /* no longer supported */ +#define WLC_GET_KEY 44 +#define WLC_SET_KEY 45 +#define WLC_GET_REGULATORY 46 +#define WLC_SET_REGULATORY 47 +#define WLC_GET_PASSIVE_SCAN 48 +#define WLC_SET_PASSIVE_SCAN 49 +#define WLC_SCAN 50 +#define WLC_SCAN_RESULTS 51 +#define WLC_DISASSOC 52 +#define WLC_REASSOC 53 +#define WLC_GET_ROAM_TRIGGER 54 +#define WLC_SET_ROAM_TRIGGER 55 +#define WLC_GET_ROAM_DELTA 56 +#define WLC_SET_ROAM_DELTA 57 +#define WLC_GET_ROAM_SCAN_PERIOD 58 +#define WLC_SET_ROAM_SCAN_PERIOD 59 +#define WLC_EVM 60 /* diag */ +#define WLC_GET_TXANT 61 +#define WLC_SET_TXANT 62 +#define WLC_GET_ANTDIV 63 +#define WLC_SET_ANTDIV 64 +/* #define WLC_GET_TXPWR 65 */ /* no longer supported */ +/* #define WLC_SET_TXPWR 66 */ /* no longer supported */ +#define WLC_GET_CLOSED 67 +#define WLC_SET_CLOSED 68 +#define WLC_GET_MACLIST 69 +#define WLC_SET_MACLIST 70 +#define WLC_GET_RATESET 71 +#define WLC_SET_RATESET 72 +/* #define WLC_GET_LOCALE 73 */ /* no longer supported */ +#define WLC_LONGTRAIN 74 +#define WLC_GET_BCNPRD 75 +#define WLC_SET_BCNPRD 76 +#define WLC_GET_DTIMPRD 77 +#define WLC_SET_DTIMPRD 78 +#define WLC_GET_SROM 79 +#define WLC_SET_SROM 80 +#define WLC_GET_WEP_RESTRICT 81 +#define WLC_SET_WEP_RESTRICT 82 +#define WLC_GET_COUNTRY 83 +#define WLC_SET_COUNTRY 84 +#define WLC_GET_PM 85 +#define WLC_SET_PM 86 +#define WLC_GET_WAKE 87 +#define WLC_SET_WAKE 88 +/* #define WLC_GET_D11CNTS 89 */ /* -> "counters" iovar */ +#define WLC_GET_FORCELINK 90 /* ndis only */ +#define WLC_SET_FORCELINK 91 /* ndis only */ +#define WLC_FREQ_ACCURACY 92 /* diag */ +#define WLC_CARRIER_SUPPRESS 93 /* diag */ +#define WLC_GET_PHYREG 94 +#define WLC_SET_PHYREG 95 +#define WLC_GET_RADIOREG 96 +#define WLC_SET_RADIOREG 97 +#define WLC_GET_REVINFO 98 +#define WLC_GET_UCANTDIV 99 +#define WLC_SET_UCANTDIV 100 +#define WLC_R_REG 101 +#define WLC_W_REG 102 +/* #define WLC_DIAG_LOOPBACK 103 old tray diag */ +/* #define WLC_RESET_D11CNTS 104 */ /* -> "reset_d11cnts" iovar */ +#define WLC_GET_MACMODE 105 +#define WLC_SET_MACMODE 106 +#define WLC_GET_MONITOR 107 +#define WLC_SET_MONITOR 108 +#define WLC_GET_GMODE 109 +#define WLC_SET_GMODE 110 +#define WLC_GET_LEGACY_ERP 111 +#define WLC_SET_LEGACY_ERP 112 +#define WLC_GET_RX_ANT 113 +#define WLC_GET_CURR_RATESET 114 /* current rateset */ +#define WLC_GET_SCANSUPPRESS 115 +#define WLC_SET_SCANSUPPRESS 116 +#define WLC_GET_AP 117 +#define WLC_SET_AP 118 +#define WLC_GET_EAP_RESTRICT 119 +#define WLC_SET_EAP_RESTRICT 120 +#define WLC_SCB_AUTHORIZE 121 +#define WLC_SCB_DEAUTHORIZE 122 +#define WLC_GET_WDSLIST 123 +#define WLC_SET_WDSLIST 124 +#define WLC_GET_ATIM 125 +#define WLC_SET_ATIM 126 +#define WLC_GET_RSSI 127 +#define WLC_GET_PHYANTDIV 128 +#define WLC_SET_PHYANTDIV 129 +#define WLC_AP_RX_ONLY 130 +#define WLC_GET_TX_PATH_PWR 131 +#define WLC_SET_TX_PATH_PWR 132 +#define WLC_GET_WSEC 133 +#define WLC_SET_WSEC 134 +#define WLC_GET_PHY_NOISE 135 +#define WLC_GET_BSS_INFO 136 +#define WLC_GET_PKTCNTS 137 +#define WLC_GET_LAZYWDS 138 +#define WLC_SET_LAZYWDS 139 +#define WLC_GET_BANDLIST 140 + +#ifndef LINUX_POSTMOGRIFY_REMOVAL +#define WLC_GET_BAND 141 +#define WLC_SET_BAND 142 +#define WLC_SCB_DEAUTHENTICATE 143 +#define WLC_GET_SHORTSLOT 144 +#define WLC_GET_SHORTSLOT_OVERRIDE 145 +#define WLC_SET_SHORTSLOT_OVERRIDE 146 +#define WLC_GET_SHORTSLOT_RESTRICT 147 +#define WLC_SET_SHORTSLOT_RESTRICT 148 +#define WLC_GET_GMODE_PROTECTION 149 +#define WLC_GET_GMODE_PROTECTION_OVERRIDE 150 +#define WLC_SET_GMODE_PROTECTION_OVERRIDE 151 +#define WLC_UPGRADE 152 +/* #define WLC_GET_MRATE 153 */ /* no longer supported */ +/* #define WLC_SET_MRATE 154 */ /* no longer supported */ +#define WLC_GET_IGNORE_BCNS 155 +#define WLC_SET_IGNORE_BCNS 156 +#define WLC_GET_SCB_TIMEOUT 157 +#define WLC_SET_SCB_TIMEOUT 158 +#define WLC_GET_ASSOCLIST 159 +#define WLC_GET_CLK 160 +#define WLC_SET_CLK 161 +#define WLC_GET_UP 162 +#define WLC_OUT 163 +#define WLC_GET_WPA_AUTH 164 +#define WLC_SET_WPA_AUTH 165 +#define WLC_GET_UCFLAGS 166 +#define WLC_SET_UCFLAGS 167 +#define WLC_GET_PWRIDX 168 +#define WLC_SET_PWRIDX 169 +#define WLC_GET_TSSI 170 +#define WLC_GET_SUP_RATESET_OVERRIDE 171 +#define WLC_SET_SUP_RATESET_OVERRIDE 172 +/* #define WLC_SET_FAST_TIMER 173 */ /* no longer supported */ +/* #define WLC_GET_FAST_TIMER 174 */ /* no longer supported */ +/* #define WLC_SET_SLOW_TIMER 175 */ /* no longer supported */ +/* #define WLC_GET_SLOW_TIMER 176 */ /* no longer supported */ +/* #define WLC_DUMP_PHYREGS 177 */ /* no longer supported */ +#define WLC_GET_PROTECTION_CONTROL 178 +#define WLC_SET_PROTECTION_CONTROL 179 +#endif /* LINUX_POSTMOGRIFY_REMOVAL */ +#define WLC_GET_PHYLIST 180 +#ifndef LINUX_POSTMOGRIFY_REMOVAL +#define WLC_ENCRYPT_STRENGTH 181 /* ndis only */ +#define WLC_DECRYPT_STATUS 182 /* ndis only */ +#define WLC_GET_KEY_SEQ 183 +#define WLC_GET_SCAN_CHANNEL_TIME 184 +#define WLC_SET_SCAN_CHANNEL_TIME 185 +#define WLC_GET_SCAN_UNASSOC_TIME 186 +#define WLC_SET_SCAN_UNASSOC_TIME 187 +#define WLC_GET_SCAN_HOME_TIME 188 +#define WLC_SET_SCAN_HOME_TIME 189 +#define WLC_GET_SCAN_NPROBES 190 +#define WLC_SET_SCAN_NPROBES 191 +#define WLC_GET_PRB_RESP_TIMEOUT 192 +#define WLC_SET_PRB_RESP_TIMEOUT 193 +#define WLC_GET_ATTEN 194 +#define WLC_SET_ATTEN 195 +#define WLC_GET_SHMEM 196 /* diag */ +#define WLC_SET_SHMEM 197 /* diag */ +/* #define WLC_GET_GMODE_PROTECTION_CTS 198 */ /* no longer supported */ +/* #define WLC_SET_GMODE_PROTECTION_CTS 199 */ /* no longer supported */ +#define WLC_SET_WSEC_TEST 200 +#endif /* LINUX_POSTMOGRIFY_REMOVAL */ +#define WLC_SCB_DEAUTHENTICATE_FOR_REASON 201 +#ifndef LINUX_POSTMOGRIFY_REMOVAL +#define WLC_TKIP_COUNTERMEASURES 202 +#define WLC_GET_PIOMODE 203 +#define WLC_SET_PIOMODE 204 +#define WLC_SET_ASSOC_PREFER 205 +#define WLC_GET_ASSOC_PREFER 206 +#define WLC_SET_ROAM_PREFER 207 +#define WLC_GET_ROAM_PREFER 208 +#define WLC_SET_LED 209 +#define WLC_GET_LED 210 +#define WLC_GET_INTERFERENCE_MODE 211 +#define WLC_SET_INTERFERENCE_MODE 212 +#define WLC_GET_CHANNEL_QA 213 +#define WLC_START_CHANNEL_QA 214 +#define WLC_GET_CHANNEL_SEL 215 +#define WLC_START_CHANNEL_SEL 216 +#endif /* LINUX_POSTMOGRIFY_REMOVAL */ +#define WLC_GET_VALID_CHANNELS 217 +#define WLC_GET_FAKEFRAG 218 +#define WLC_SET_FAKEFRAG 219 +#define WLC_GET_PWROUT_PERCENTAGE 220 +#define WLC_SET_PWROUT_PERCENTAGE 221 +#define WLC_SET_BAD_FRAME_PREEMPT 222 +#define WLC_GET_BAD_FRAME_PREEMPT 223 +#define WLC_SET_LEAP_LIST 224 +#define WLC_GET_LEAP_LIST 225 +#define WLC_GET_CWMIN 226 +#define WLC_SET_CWMIN 227 +#define WLC_GET_CWMAX 228 +#define WLC_SET_CWMAX 229 +#define WLC_GET_WET 230 +#define WLC_SET_WET 231 +#define WLC_GET_PUB 232 +/* #define WLC_SET_GLACIAL_TIMER 233 */ /* no longer supported */ +/* #define WLC_GET_GLACIAL_TIMER 234 */ /* no longer supported */ +#define WLC_GET_KEY_PRIMARY 235 +#define WLC_SET_KEY_PRIMARY 236 + +#ifndef LINUX_POSTMOGRIFY_REMOVAL + +/* #define WLC_DUMP_RADIOREGS 237 */ /* no longer supported */ +#define WLC_GET_ACI_ARGS 238 +#define WLC_SET_ACI_ARGS 239 +#define WLC_UNSET_CALLBACK 240 +#define WLC_SET_CALLBACK 241 +#define WLC_GET_RADAR 242 +#define WLC_SET_RADAR 243 +#define WLC_SET_SPECT_MANAGMENT 244 +#define WLC_GET_SPECT_MANAGMENT 245 +#define WLC_WDS_GET_REMOTE_HWADDR 246 /* handled in wl_linux.c/wl_vx.c */ +#define WLC_WDS_GET_WPA_SUP 247 +#define WLC_SET_CS_SCAN_TIMER 248 +#define WLC_GET_CS_SCAN_TIMER 249 +#define WLC_MEASURE_REQUEST 250 +#define WLC_INIT 251 +#define WLC_SEND_QUIET 252 +#define WLC_KEEPALIVE 253 +#define WLC_SEND_PWR_CONSTRAINT 254 +#define WLC_UPGRADE_STATUS 255 +#define WLC_CURRENT_PWR 256 +#define WLC_GET_SCAN_PASSIVE_TIME 257 +#define WLC_SET_SCAN_PASSIVE_TIME 258 +#define WLC_LEGACY_LINK_BEHAVIOR 259 +#define WLC_GET_CHANNELS_IN_COUNTRY 260 +#define WLC_GET_COUNTRY_LIST 261 +#endif /* LINUX_POSTMOGRIFY_REMOVAL */ +#define WLC_GET_VAR 262 /* get value of named variable */ +#define WLC_SET_VAR 263 /* set named variable to value */ +#ifndef LINUX_POSTMOGRIFY_REMOVAL +#define WLC_NVRAM_GET 264 /* deprecated */ +#define WLC_NVRAM_SET 265 +#define WLC_NVRAM_DUMP 266 +#define WLC_REBOOT 267 +#define WLC_SET_WSEC_PMK 268 +#define WLC_GET_AUTH_MODE 269 +#define WLC_SET_AUTH_MODE 270 +#define WLC_GET_WAKEENTRY 271 +#define WLC_SET_WAKEENTRY 272 +#define WLC_NDCONFIG_ITEM 273 /* currently handled in wl_oid.c */ +#define WLC_NVOTPW 274 +#define WLC_OTPW 275 +#define WLC_IOV_BLOCK_GET 276 +#define WLC_IOV_MODULES_GET 277 +#define WLC_SOFT_RESET 278 +#define WLC_GET_ALLOW_MODE 279 +#define WLC_SET_ALLOW_MODE 280 +#define WLC_GET_DESIRED_BSSID 281 +#define WLC_SET_DESIRED_BSSID 282 +#define WLC_DISASSOC_MYAP 283 +#define WLC_GET_NBANDS 284 /* for Dongle EXT_STA support */ +#define WLC_GET_BANDSTATES 285 /* for Dongle EXT_STA support */ +#define WLC_GET_WLC_BSS_INFO 286 /* for Dongle EXT_STA support */ +#define WLC_GET_ASSOC_INFO 287 /* for Dongle EXT_STA support */ +#define WLC_GET_OID_PHY 288 /* for Dongle EXT_STA support */ +#define WLC_SET_OID_PHY 289 /* for Dongle EXT_STA support */ +#define WLC_SET_ASSOC_TIME 290 /* for Dongle EXT_STA support */ +#define WLC_GET_DESIRED_SSID 291 /* for Dongle EXT_STA support */ +#define WLC_GET_CHANSPEC 292 /* for Dongle EXT_STA support */ +#define WLC_GET_ASSOC_STATE 293 /* for Dongle EXT_STA support */ +#define WLC_SET_PHY_STATE 294 /* for Dongle EXT_STA support */ +#define WLC_GET_SCAN_PENDING 295 /* for Dongle EXT_STA support */ +#define WLC_GET_SCANREQ_PENDING 296 /* for Dongle EXT_STA support */ +#define WLC_GET_PREV_ROAM_REASON 297 /* for Dongle EXT_STA support */ +#define WLC_SET_PREV_ROAM_REASON 298 /* for Dongle EXT_STA support */ +#define WLC_GET_BANDSTATES_PI 299 /* for Dongle EXT_STA support */ +#define WLC_GET_PHY_STATE 300 /* for Dongle EXT_STA support */ +#define WLC_GET_BSS_WPA_RSN 301 /* for Dongle EXT_STA support */ +#define WLC_GET_BSS_WPA2_RSN 302 /* for Dongle EXT_STA support */ +#define WLC_GET_BSS_BCN_TS 303 /* for Dongle EXT_STA support */ +#define WLC_GET_INT_DISASSOC 304 /* for Dongle EXT_STA support */ +#define WLC_SET_NUM_PEERS 305 /* for Dongle EXT_STA support */ +#define WLC_GET_NUM_BSS 306 /* for Dongle EXT_STA support */ +#define WLC_PHY_SAMPLE_COLLECT 307 /* phy sample collect mode */ +/* #define WLC_UM_PRIV 308 */ /* Deprecated: usermode driver */ +#define WLC_GET_CMD 309 +/* #define WLC_LAST 310 */ /* Never used - can be reused */ +#define WLC_SET_INTERFERENCE_OVERRIDE_MODE 311 /* set inter mode override */ +#define WLC_GET_INTERFERENCE_OVERRIDE_MODE 312 /* get inter mode override */ +/* #define WLC_GET_WAI_RESTRICT 313 */ /* for WAPI, deprecated use iovar instead */ +/* #define WLC_SET_WAI_RESTRICT 314 */ /* for WAPI, deprecated use iovar instead */ +/* #define WLC_SET_WAI_REKEY 315 */ /* for WAPI, deprecated use iovar instead */ +#define WLC_SET_NAT_CONFIG 316 /* for configuring NAT filter driver */ +#define WLC_GET_NAT_STATE 317 +#define WLC_LAST 318 + +#ifndef EPICTRL_COOKIE +#define EPICTRL_COOKIE 0xABADCEDE +#endif + +/* vx wlc ioctl's offset */ +#define CMN_IOCTL_OFF 0x180 + +/* + * custom OID support + * + * 0xFF - implementation specific OID + * 0xE4 - first byte of Broadcom PCI vendor ID + * 0x14 - second byte of Broadcom PCI vendor ID + * 0xXX - the custom OID number + */ + +/* begin 0x1f values beyond the start of the ET driver range. */ +#define WL_OID_BASE 0xFFE41420 + +/* NDIS overrides */ +#define OID_WL_GETINSTANCE (WL_OID_BASE + WLC_GET_INSTANCE) +#define OID_WL_GET_FORCELINK (WL_OID_BASE + WLC_GET_FORCELINK) +#define OID_WL_SET_FORCELINK (WL_OID_BASE + WLC_SET_FORCELINK) +#define OID_WL_ENCRYPT_STRENGTH (WL_OID_BASE + WLC_ENCRYPT_STRENGTH) +#define OID_WL_DECRYPT_STATUS (WL_OID_BASE + WLC_DECRYPT_STATUS) +#define OID_LEGACY_LINK_BEHAVIOR (WL_OID_BASE + WLC_LEGACY_LINK_BEHAVIOR) +#define OID_WL_NDCONFIG_ITEM (WL_OID_BASE + WLC_NDCONFIG_ITEM) + +/* EXT_STA Dongle suuport */ +#define OID_STA_CHANSPEC (WL_OID_BASE + WLC_GET_CHANSPEC) +#define OID_STA_NBANDS (WL_OID_BASE + WLC_GET_NBANDS) +#define OID_STA_GET_PHY (WL_OID_BASE + WLC_GET_OID_PHY) +#define OID_STA_SET_PHY (WL_OID_BASE + WLC_SET_OID_PHY) +#define OID_STA_ASSOC_TIME (WL_OID_BASE + WLC_SET_ASSOC_TIME) +#define OID_STA_DESIRED_SSID (WL_OID_BASE + WLC_GET_DESIRED_SSID) +#define OID_STA_SET_PHY_STATE (WL_OID_BASE + WLC_SET_PHY_STATE) +#define OID_STA_SCAN_PENDING (WL_OID_BASE + WLC_GET_SCAN_PENDING) +#define OID_STA_SCANREQ_PENDING (WL_OID_BASE + WLC_GET_SCANREQ_PENDING) +#define OID_STA_GET_ROAM_REASON (WL_OID_BASE + WLC_GET_PREV_ROAM_REASON) +#define OID_STA_SET_ROAM_REASON (WL_OID_BASE + WLC_SET_PREV_ROAM_REASON) +#define OID_STA_GET_PHY_STATE (WL_OID_BASE + WLC_GET_PHY_STATE) +#define OID_STA_INT_DISASSOC (WL_OID_BASE + WLC_GET_INT_DISASSOC) +#define OID_STA_SET_NUM_PEERS (WL_OID_BASE + WLC_SET_NUM_PEERS) +#define OID_STA_GET_NUM_BSS (WL_OID_BASE + WLC_GET_NUM_BSS) + +/* NAT filter driver support */ +#define OID_NAT_SET_CONFIG (WL_OID_BASE + WLC_SET_NAT_CONFIG) +#define OID_NAT_GET_STATE (WL_OID_BASE + WLC_GET_NAT_STATE) + +#define WL_DECRYPT_STATUS_SUCCESS 1 +#define WL_DECRYPT_STATUS_FAILURE 2 +#define WL_DECRYPT_STATUS_UNKNOWN 3 + +/* allows user-mode app to poll the status of USB image upgrade */ +#define WLC_UPGRADE_SUCCESS 0 +#define WLC_UPGRADE_PENDING 1 + +#ifdef CONFIG_USBRNDIS_RETAIL +/* struct passed in for WLC_NDCONFIG_ITEM */ +typedef struct { + char *name; + void *param; +} ndconfig_item_t; +#endif + + +/* WLC_GET_AUTH, WLC_SET_AUTH values */ +#define WL_AUTH_OPEN_SYSTEM 0 /* d11 open authentication */ +#define WL_AUTH_SHARED_KEY 1 /* d11 shared authentication */ +#ifdef BCM4330_CHIP +#define WL_AUTH_OPEN_SHARED 2 /* try open, then shared if open failed w/rc 13 */ +#else +/* BCM4334(Phoenex branch) value changed to 3 */ +#define WL_AUTH_OPEN_SHARED 3 /* try open, then shared if open failed w/rc 13 */ +#endif +#endif /* LINUX_POSTMOGRIFY_REMOVAL */ + +/* Bit masks for radio disabled status - returned by WL_GET_RADIO */ +#define WL_RADIO_SW_DISABLE (1<<0) +#define WL_RADIO_HW_DISABLE (1<<1) +#define WL_RADIO_MPC_DISABLE (1<<2) +#define WL_RADIO_COUNTRY_DISABLE (1<<3) /* some countries don't support any channel */ + +#define WL_SPURAVOID_OFF 0 +#define WL_SPURAVOID_ON1 1 +#define WL_SPURAVOID_ON2 2 + +/* Override bit for WLC_SET_TXPWR. if set, ignore other level limits */ +#define WL_TXPWR_OVERRIDE (1U<<31) +#define WL_TXPWR_NEG (1U<<30) + +#ifndef LINUX_POSTMOGRIFY_REMOVAL +#define WL_PHY_PAVARS_LEN 32 /* Phy type, Band range, chain, a1[0], b0[0], b1[0] ... */ + +#define WL_PHY_PAVAR_VER 1 /* pavars version */ + +typedef struct wl_po { + uint16 phy_type; /* Phy type */ + uint16 band; + uint16 cckpo; + uint32 ofdmpo; + uint16 mcspo[8]; +} wl_po_t; + +/* a large TX Power as an init value to factor out of MIN() calculations, + * keep low enough to fit in an int8, units are .25 dBm + */ +#define WLC_TXPWR_MAX (127) /* ~32 dBm = 1,500 mW */ + +/* "diag" iovar argument and error code */ +#define WL_DIAG_INTERRUPT 1 /* d11 loopback interrupt test */ +#define WL_DIAG_LOOPBACK 2 /* d11 loopback data test */ +#define WL_DIAG_MEMORY 3 /* d11 memory test */ +#define WL_DIAG_LED 4 /* LED test */ +#define WL_DIAG_REG 5 /* d11/phy register test */ +#define WL_DIAG_SROM 6 /* srom read/crc test */ +#define WL_DIAG_DMA 7 /* DMA test */ +#define WL_DIAG_LOOPBACK_EXT 8 /* enhenced d11 loopback data test */ + +#define WL_DIAGERR_SUCCESS 0 +#define WL_DIAGERR_FAIL_TO_RUN 1 /* unable to run requested diag */ +#define WL_DIAGERR_NOT_SUPPORTED 2 /* diag requested is not supported */ +#define WL_DIAGERR_INTERRUPT_FAIL 3 /* loopback interrupt test failed */ +#define WL_DIAGERR_LOOPBACK_FAIL 4 /* loopback data test failed */ +#define WL_DIAGERR_SROM_FAIL 5 /* srom read failed */ +#define WL_DIAGERR_SROM_BADCRC 6 /* srom crc failed */ +#define WL_DIAGERR_REG_FAIL 7 /* d11/phy register test failed */ +#define WL_DIAGERR_MEMORY_FAIL 8 /* d11 memory test failed */ +#define WL_DIAGERR_NOMEM 9 /* diag test failed due to no memory */ +#define WL_DIAGERR_DMA_FAIL 10 /* DMA test failed */ + +#define WL_DIAGERR_MEMORY_TIMEOUT 11 /* d11 memory test didn't finish in time */ +#define WL_DIAGERR_MEMORY_BADPATTERN 12 /* d11 memory test result in bad pattern */ + +/* band types */ +#define WLC_BAND_AUTO 0 /* auto-select */ +#define WLC_BAND_5G 1 /* 5 Ghz */ +#define WLC_BAND_2G 2 /* 2.4 Ghz */ +#define WLC_BAND_ALL 3 /* all bands */ + +/* band range returned by band_range iovar */ +#define WL_CHAN_FREQ_RANGE_2G 0 +#define WL_CHAN_FREQ_RANGE_5GL 1 +#define WL_CHAN_FREQ_RANGE_5GM 2 +#define WL_CHAN_FREQ_RANGE_5GH 3 + +#define WL_CHAN_FREQ_RANGE_5G_BAND0 1 +#define WL_CHAN_FREQ_RANGE_5G_BAND1 2 +#define WL_CHAN_FREQ_RANGE_5G_BAND2 3 +#define WL_CHAN_FREQ_RANGE_5G_BAND3 4 + +#define WL_CHAN_FREQ_RANGE_5G_4BAND 5 +#endif /* LINUX_POSTMOGRIFY_REMOVAL */ + +/* phy types (returned by WLC_GET_PHYTPE) */ +#define WLC_PHY_TYPE_A 0 +#define WLC_PHY_TYPE_B 1 +#define WLC_PHY_TYPE_G 2 +#define WLC_PHY_TYPE_N 4 +#define WLC_PHY_TYPE_LP 5 +#define WLC_PHY_TYPE_SSN 6 +#define WLC_PHY_TYPE_HT 7 +#define WLC_PHY_TYPE_LCN 8 +#define WLC_PHY_TYPE_LCN40 10 +#define WLC_PHY_TYPE_AC 11 +#define WLC_PHY_TYPE_NULL 0xf + +/* Values for PM */ +#define PM_OFF 0 +#define PM_MAX 1 +#define PM_FAST 2 +#define PM_FORCE_OFF 3 /* use this bit to force PM off even bt is active */ + +#ifndef LINUX_POSTMOGRIFY_REMOVAL +/* MAC list modes */ +#define WLC_MACMODE_DISABLED 0 /* MAC list disabled */ +#define WLC_MACMODE_DENY 1 /* Deny specified (i.e. allow unspecified) */ +#define WLC_MACMODE_ALLOW 2 /* Allow specified (i.e. deny unspecified) */ + +/* + * 54g modes (basic bits may still be overridden) + * + * GMODE_LEGACY_B Rateset: 1b, 2b, 5.5, 11 + * Preamble: Long + * Shortslot: Off + * GMODE_AUTO Rateset: 1b, 2b, 5.5b, 11b, 18, 24, 36, 54 + * Extended Rateset: 6, 9, 12, 48 + * Preamble: Long + * Shortslot: Auto + * GMODE_ONLY Rateset: 1b, 2b, 5.5b, 11b, 18, 24b, 36, 54 + * Extended Rateset: 6b, 9, 12b, 48 + * Preamble: Short required + * Shortslot: Auto + * GMODE_B_DEFERRED Rateset: 1b, 2b, 5.5b, 11b, 18, 24, 36, 54 + * Extended Rateset: 6, 9, 12, 48 + * Preamble: Long + * Shortslot: On + * GMODE_PERFORMANCE Rateset: 1b, 2b, 5.5b, 6b, 9, 11b, 12b, 18, 24b, 36, 48, 54 + * Preamble: Short required + * Shortslot: On and required + * GMODE_LRS Rateset: 1b, 2b, 5.5b, 11b + * Extended Rateset: 6, 9, 12, 18, 24, 36, 48, 54 + * Preamble: Long + * Shortslot: Auto + */ +#define GMODE_LEGACY_B 0 +#define GMODE_AUTO 1 +#define GMODE_ONLY 2 +#define GMODE_B_DEFERRED 3 +#define GMODE_PERFORMANCE 4 +#define GMODE_LRS 5 +#define GMODE_MAX 6 + +/* values for PLCPHdr_override */ +#define WLC_PLCP_AUTO -1 +#define WLC_PLCP_SHORT 0 +#define WLC_PLCP_LONG 1 + +/* values for g_protection_override and n_protection_override */ +#define WLC_PROTECTION_AUTO -1 +#define WLC_PROTECTION_OFF 0 +#define WLC_PROTECTION_ON 1 +#define WLC_PROTECTION_MMHDR_ONLY 2 +#define WLC_PROTECTION_CTS_ONLY 3 + +/* values for g_protection_control and n_protection_control */ +#define WLC_PROTECTION_CTL_OFF 0 +#define WLC_PROTECTION_CTL_LOCAL 1 +#define WLC_PROTECTION_CTL_OVERLAP 2 + +/* values for n_protection */ +#define WLC_N_PROTECTION_OFF 0 +#define WLC_N_PROTECTION_OPTIONAL 1 +#define WLC_N_PROTECTION_20IN40 2 +#define WLC_N_PROTECTION_MIXEDMODE 3 + +/* values for n_preamble_type */ +#define WLC_N_PREAMBLE_MIXEDMODE 0 +#define WLC_N_PREAMBLE_GF 1 +#define WLC_N_PREAMBLE_GF_BRCM 2 + +/* values for band specific 40MHz capabilities (deprecated) */ +#define WLC_N_BW_20ALL 0 +#define WLC_N_BW_40ALL 1 +#define WLC_N_BW_20IN2G_40IN5G 2 + +#define WLC_BW_20MHZ_BIT (1<<0) +#define WLC_BW_40MHZ_BIT (1<<1) +#define WLC_BW_80MHZ_BIT (1<<2) + +/* Bandwidth capabilities */ +#define WLC_BW_CAP_20MHZ (WLC_BW_20MHZ_BIT) +#define WLC_BW_CAP_40MHZ (WLC_BW_40MHZ_BIT|WLC_BW_20MHZ_BIT) +#define WLC_BW_CAP_80MHZ (WLC_BW_80MHZ_BIT|WLC_BW_40MHZ_BIT|WLC_BW_20MHZ_BIT) +#define WLC_BW_CAP_UNRESTRICTED 0xFF + +#define WL_BW_CAP_20MHZ(bw_cap) (((bw_cap) & WLC_BW_20MHZ_BIT) ? TRUE : FALSE) +#define WL_BW_CAP_40MHZ(bw_cap) (((bw_cap) & WLC_BW_40MHZ_BIT) ? TRUE : FALSE) +#define WL_BW_CAP_80MHZ(bw_cap) (((bw_cap) & WLC_BW_80MHZ_BIT) ? TRUE : FALSE) + +/* values to force tx/rx chain */ +#define WLC_N_TXRX_CHAIN0 0 +#define WLC_N_TXRX_CHAIN1 1 + +/* bitflags for SGI support (sgi_rx iovar) */ +#define WLC_N_SGI_20 0x01 +#define WLC_N_SGI_40 0x02 +#define WLC_VHT_SGI_80 0x04 + +/* when sgi_tx==WLC_SGI_ALL, bypass rate selection, enable sgi for all mcs */ +#define WLC_SGI_ALL 0x02 + +#define LISTEN_INTERVAL 10 +/* interference mitigation options */ +#define INTERFERE_OVRRIDE_OFF -1 /* interference override off */ +#define INTERFERE_NONE 0 /* off */ +#define NON_WLAN 1 /* foreign/non 802.11 interference, no auto detect */ +#define WLAN_MANUAL 2 /* ACI: no auto detection */ +#define WLAN_AUTO 3 /* ACI: auto detect */ +#define WLAN_AUTO_W_NOISE 4 /* ACI: auto - detect and non 802.11 interference */ +#define AUTO_ACTIVE (1 << 7) /* Auto is currently active */ + +/* AP environment */ +#define AP_ENV_DETECT_NOT_USED 0 /* We aren't using AP environment detection */ +#define AP_ENV_DENSE 1 /* "Corporate" or other AP dense environment */ +#define AP_ENV_SPARSE 2 /* "Home" or other sparse environment */ +#define AP_ENV_INDETERMINATE 3 /* AP environment hasn't been identified */ + +typedef struct wl_aci_args { + int enter_aci_thresh; /* Trigger level to start detecting ACI */ + int exit_aci_thresh; /* Trigger level to exit ACI mode */ + int usec_spin; /* microsecs to delay between rssi samples */ + int glitch_delay; /* interval between ACI scans when glitch count is consistently high */ + uint16 nphy_adcpwr_enter_thresh; /* ADC power to enter ACI mitigation mode */ + uint16 nphy_adcpwr_exit_thresh; /* ADC power to exit ACI mitigation mode */ + uint16 nphy_repeat_ctr; /* Number of tries per channel to compute power */ + uint16 nphy_num_samples; /* Number of samples to compute power on one channel */ + uint16 nphy_undetect_window_sz; /* num of undetects to exit ACI Mitigation mode */ + uint16 nphy_b_energy_lo_aci; /* low ACI power energy threshold for bphy */ + uint16 nphy_b_energy_md_aci; /* mid ACI power energy threshold for bphy */ + uint16 nphy_b_energy_hi_aci; /* high ACI power energy threshold for bphy */ + uint16 nphy_noise_noassoc_glitch_th_up; /* wl interference 4 */ + uint16 nphy_noise_noassoc_glitch_th_dn; + uint16 nphy_noise_assoc_glitch_th_up; + uint16 nphy_noise_assoc_glitch_th_dn; + uint16 nphy_noise_assoc_aci_glitch_th_up; + uint16 nphy_noise_assoc_aci_glitch_th_dn; + uint16 nphy_noise_assoc_enter_th; + uint16 nphy_noise_noassoc_enter_th; + uint16 nphy_noise_assoc_rx_glitch_badplcp_enter_th; + uint16 nphy_noise_noassoc_crsidx_incr; + uint16 nphy_noise_assoc_crsidx_incr; + uint16 nphy_noise_crsidx_decr; +} wl_aci_args_t; + +#define TRIGGER_NOW 0 +#define TRIGGER_CRS 0x01 +#define TRIGGER_CRSDEASSERT 0x02 +#define TRIGGER_GOODFCS 0x04 +#define TRIGGER_BADFCS 0x08 +#define TRIGGER_BADPLCP 0x10 +#define TRIGGER_CRSGLITCH 0x20 +#define WL_ACI_ARGS_LEGACY_LENGTH 16 /* bytes of pre NPHY aci args */ +#define WL_SAMPLECOLLECT_T_VERSION 2 /* version of wl_samplecollect_args_t struct */ +typedef struct wl_samplecollect_args { + /* version 0 fields */ + uint8 coll_us; + int cores; + /* add'l version 1 fields */ + uint16 version; /* see definition of WL_SAMPLECOLLECT_T_VERSION */ + uint16 length; /* length of entire structure */ + int8 trigger; + uint16 timeout; + uint16 mode; + uint32 pre_dur; + uint32 post_dur; + uint8 gpio_sel; + bool downsamp; + bool be_deaf; + bool agc; /* loop from init gain and going down */ + bool filter; /* override high pass corners to lowest */ + /* add'l version 2 fields */ + uint8 trigger_state; + uint8 module_sel1; + uint8 module_sel2; + uint16 nsamps; +} wl_samplecollect_args_t; + +#define WL_SAMPLEDATA_HEADER_TYPE 1 +#define WL_SAMPLEDATA_HEADER_SIZE 80 /* sample collect header size (bytes) */ +#define WL_SAMPLEDATA_TYPE 2 +#define WL_SAMPLEDATA_SEQ 0xff /* sequence # */ +#define WL_SAMPLEDATA_MORE_DATA 0x100 /* more data mask */ +#define WL_SAMPLEDATA_T_VERSION 1 /* version of wl_samplecollect_args_t struct */ +/* version for unpacked sample data, int16 {(I,Q),Core(0..N)} */ +#define WL_SAMPLEDATA_T_VERSION_SPEC_AN 2 + +typedef struct wl_sampledata { + uint16 version; /* structure version */ + uint16 size; /* size of structure */ + uint16 tag; /* Header/Data */ + uint16 length; /* data length */ + uint32 flag; /* bit def */ +} wl_sampledata_t; + +/* wl_radar_args_t */ +typedef struct { + int npulses; /* required number of pulses at n * t_int */ + int ncontig; /* required number of pulses at t_int */ + int min_pw; /* minimum pulse width (20 MHz clocks) */ + int max_pw; /* maximum pulse width (20 MHz clocks) */ + uint16 thresh0; /* Radar detection, thresh 0 */ + uint16 thresh1; /* Radar detection, thresh 1 */ + uint16 blank; /* Radar detection, blank control */ + uint16 fmdemodcfg; /* Radar detection, fmdemod config */ + int npulses_lp; /* Radar detection, minimum long pulses */ + int min_pw_lp; /* Minimum pulsewidth for long pulses */ + int max_pw_lp; /* Maximum pulsewidth for long pulses */ + int min_fm_lp; /* Minimum fm for long pulses */ + int max_span_lp; /* Maximum deltat for long pulses */ + int min_deltat; /* Minimum spacing between pulses */ + int max_deltat; /* Maximum spacing between pulses */ + uint16 autocorr; /* Radar detection, autocorr on or off */ + uint16 st_level_time; /* Radar detection, start_timing level */ + uint16 t2_min; /* minimum clocks needed to remain in state 2 */ + uint32 version; /* version */ + uint32 fra_pulse_err; /* sample error margin for detecting French radar pulsed */ + int npulses_fra; /* Radar detection, minimum French pulses set */ + int npulses_stg2; /* Radar detection, minimum staggered-2 pulses set */ + int npulses_stg3; /* Radar detection, minimum staggered-3 pulses set */ + uint16 percal_mask; /* defines which period cal is masked from radar detection */ + int quant; /* quantization resolution to pulse positions */ + uint32 min_burst_intv_lp; /* minimum burst to burst interval for bin3 radar */ + uint32 max_burst_intv_lp; /* maximum burst to burst interval for bin3 radar */ + int nskip_rst_lp; /* number of skipped pulses before resetting lp buffer */ + int max_pw_tol; /* maximum tollerance allowed in detected pulse width for radar detection */ + uint16 feature_mask; /* 16-bit mask to specify enabled features */ +} wl_radar_args_t; + +#define WL_RADAR_ARGS_VERSION 2 + +typedef struct { + uint32 version; /* version */ + uint16 thresh0_20_lo; /* Radar detection, thresh 0 (range 5250-5350MHz) for BW 20MHz */ + uint16 thresh1_20_lo; /* Radar detection, thresh 1 (range 5250-5350MHz) for BW 20MHz */ + uint16 thresh0_40_lo; /* Radar detection, thresh 0 (range 5250-5350MHz) for BW 40MHz */ + uint16 thresh1_40_lo; /* Radar detection, thresh 1 (range 5250-5350MHz) for BW 40MHz */ + uint16 thresh0_80_lo; /* Radar detection, thresh 0 (range 5250-5350MHz) for BW 80MHz */ + uint16 thresh1_80_lo; /* Radar detection, thresh 1 (range 5250-5350MHz) for BW 80MHz */ + uint16 thresh0_160_lo; /* Radar detection, thresh 0 (range 5250-5350MHz) for BW 160MHz */ + uint16 thresh1_160_lo; /* Radar detection, thresh 1 (range 5250-5350MHz) for BW 160MHz */ + uint16 thresh0_20_hi; /* Radar detection, thresh 0 (range 5470-5725MHz) for BW 20MHz */ + uint16 thresh1_20_hi; /* Radar detection, thresh 1 (range 5470-5725MHz) for BW 20MHz */ + uint16 thresh0_40_hi; /* Radar detection, thresh 0 (range 5470-5725MHz) for BW 40MHz */ + uint16 thresh1_40_hi; /* Radar detection, thresh 1 (range 5470-5725MHz) for BW 40MHz */ + uint16 thresh0_80_hi; /* Radar detection, thresh 0 (range 5470-5725MHz) for BW 80MHz */ + uint16 thresh1_80_hi; /* Radar detection, thresh 1 (range 5470-5725MHz) for BW 80MHz */ + uint16 thresh0_160_hi; /* Radar detection, thresh 0 (range 5470-5725MHz) for BW 160MHz */ + uint16 thresh1_160_hi; /* Radar detection, thresh 1 (range 5470-5725MHz) for BW 160MHz */ +} wl_radar_thr_t; + +#define WL_RADAR_THR_VERSION 2 +#define WL_THRESHOLD_LO_BAND 70 /* range from 5250MHz - 5350MHz */ + +/* radar iovar SET defines */ +#define WL_RADAR_DETECTOR_OFF 0 /* radar detector off */ +#define WL_RADAR_DETECTOR_ON 1 /* radar detector on */ +#define WL_RADAR_SIMULATED 2 /* force radar detector to declare + * detection once + */ +#define WL_RSSI_ANT_VERSION 1 /* current version of wl_rssi_ant_t */ +#define WL_ANT_RX_MAX 2 /* max 2 receive antennas */ +#define WL_ANT_HT_RX_MAX 3 /* max 3 receive antennas/cores */ +#define WL_ANT_IDX_1 0 /* antenna index 1 */ +#define WL_ANT_IDX_2 1 /* antenna index 2 */ + +#ifndef WL_RSSI_ANT_MAX +#define WL_RSSI_ANT_MAX 4 /* max possible rx antennas */ +#elif WL_RSSI_ANT_MAX != 4 +#error "WL_RSSI_ANT_MAX does not match" +#endif + +/* RSSI per antenna */ +typedef struct { + uint32 version; /* version field */ + uint32 count; /* number of valid antenna rssi */ + int8 rssi_ant[WL_RSSI_ANT_MAX]; /* rssi per antenna */ +} wl_rssi_ant_t; + +/* dfs_status iovar-related defines */ + +/* cac - channel availability check, + * ism - in-service monitoring + * csa - channel switching announcement + */ + +/* cac state values */ +#define WL_DFS_CACSTATE_IDLE 0 /* state for operating in non-radar channel */ +#define WL_DFS_CACSTATE_PREISM_CAC 1 /* CAC in progress */ +#define WL_DFS_CACSTATE_ISM 2 /* ISM in progress */ +#define WL_DFS_CACSTATE_CSA 3 /* csa */ +#define WL_DFS_CACSTATE_POSTISM_CAC 4 /* ISM CAC */ +#define WL_DFS_CACSTATE_PREISM_OOC 5 /* PREISM OOC */ +#define WL_DFS_CACSTATE_POSTISM_OOC 6 /* POSTISM OOC */ +#define WL_DFS_CACSTATES 7 /* this many states exist */ + +/* data structure used in 'dfs_status' wl interface, which is used to query dfs status */ +typedef struct { + uint state; /* noted by WL_DFS_CACSTATE_XX. */ + uint duration; /* time spent in ms in state. */ + /* as dfs enters ISM state, it removes the operational channel from quiet channel + * list and notes the channel in channel_cleared. set to 0 if no channel is cleared + */ + chanspec_t chanspec_cleared; + /* chanspec cleared used to be a uint, add another to uint16 to maintain size */ + uint16 pad; +} wl_dfs_status_t; + +#define NUM_PWRCTRL_RATES 12 + +typedef struct { + uint8 txpwr_band_max[NUM_PWRCTRL_RATES]; /* User set target */ + uint8 txpwr_limit[NUM_PWRCTRL_RATES]; /* reg and local power limit */ + uint8 txpwr_local_max; /* local max according to the AP */ + uint8 txpwr_local_constraint; /* local constraint according to the AP */ + uint8 txpwr_chan_reg_max; /* Regulatory max for this channel */ + uint8 txpwr_target[2][NUM_PWRCTRL_RATES]; /* Latest target for 2.4 and 5 Ghz */ + uint8 txpwr_est_Pout[2]; /* Latest estimate for 2.4 and 5 Ghz */ + uint8 txpwr_opo[NUM_PWRCTRL_RATES]; /* On G phy, OFDM power offset */ + uint8 txpwr_bphy_cck_max[NUM_PWRCTRL_RATES]; /* Max CCK power for this band (SROM) */ + uint8 txpwr_bphy_ofdm_max; /* Max OFDM power for this band (SROM) */ + uint8 txpwr_aphy_max[NUM_PWRCTRL_RATES]; /* Max power for A band (SROM) */ + int8 txpwr_antgain[2]; /* Ant gain for each band - from SROM */ + uint8 txpwr_est_Pout_gofdm; /* Pwr estimate for 2.4 OFDM */ +} tx_power_legacy_t; + +#define WL_TX_POWER_RATES_LEGACY 45 +#define WL_TX_POWER_MCS20_FIRST 12 +#define WL_TX_POWER_MCS20_NUM 16 +#define WL_TX_POWER_MCS40_FIRST 28 +#define WL_TX_POWER_MCS40_NUM 17 + +typedef struct { + uint32 flags; + chanspec_t chanspec; /* txpwr report for this channel */ + chanspec_t local_chanspec; /* channel on which we are associated */ + uint8 local_max; /* local max according to the AP */ + uint8 local_constraint; /* local constraint according to the AP */ + int8 antgain[2]; /* Ant gain for each band - from SROM */ + uint8 rf_cores; /* count of RF Cores being reported */ + uint8 est_Pout[4]; /* Latest tx power out estimate per RF + * chain without adjustment + */ + uint8 est_Pout_cck; /* Latest CCK tx power out estimate */ + uint8 user_limit[WL_TX_POWER_RATES_LEGACY]; /* User limit */ + uint8 reg_limit[WL_TX_POWER_RATES_LEGACY]; /* Regulatory power limit */ + uint8 board_limit[WL_TX_POWER_RATES_LEGACY]; /* Max power board can support (SROM) */ + uint8 target[WL_TX_POWER_RATES_LEGACY]; /* Latest target power */ +} tx_power_legacy2_t; + +/* TX Power index defines */ +#define WL_NUM_RATES_CCK 4 /* 1, 2, 5.5, 11 Mbps */ +#define WL_NUM_RATES_OFDM 8 /* 6, 9, 12, 18, 24, 36, 48, 54 Mbps SISO/CDD */ +#define WL_NUM_RATES_MCS_1STREAM 8 /* MCS 0-7 1-stream rates - SISO/CDD/STBC/MCS */ +#define WL_NUM_RATES_EXTRA_VHT 2 /* Additional VHT 11AC rates */ +#define WL_NUM_RATES_VHT 10 +#define WL_NUM_RATES_MCS32 1 + +#define WLC_NUM_RATES_CCK WL_NUM_RATES_CCK +#define WLC_NUM_RATES_OFDM WL_NUM_RATES_OFDM +#define WLC_NUM_RATES_MCS_1_STREAM WL_NUM_RATES_MCS_1STREAM +#define WLC_NUM_RATES_MCS_2_STREAM WL_NUM_RATES_MCS_1STREAM +#define WLC_NUM_RATES_MCS32 WL_NUM_RATES_MCS32 +#define WL_TX_POWER_CCK_NUM WL_NUM_RATES_CCK +#define WL_TX_POWER_OFDM_NUM WL_NUM_RATES_OFDM +#define WL_TX_POWER_MCS_1_STREAM_NUM WL_NUM_RATES_MCS_1STREAM +#define WL_TX_POWER_MCS_2_STREAM_NUM WL_NUM_RATES_MCS_1STREAM +#define WL_TX_POWER_MCS_32_NUM WL_NUM_RATES_MCS32 + +#define WL_NUM_2x2_ELEMENTS 4 +#define WL_NUM_3x3_ELEMENTS 6 + +typedef struct txppr { + /* start of 20MHz tx power limits */ + uint8 b20_1x1dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ + uint8 b20_1x1ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM transmission */ + uint8 b20_1x1mcs0[WL_NUM_RATES_MCS_1STREAM]; /* SISO MCS 0-7 */ + + uint8 b20_1x2dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ + uint8 b20_1x2cdd_ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM CDD transmission */ + uint8 b20_1x2cdd_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* CDD MCS 0-7 */ + uint8 b20_2x2stbc_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* STBC MCS 0-7 */ + uint8 b20_2x2sdm_mcs8[WL_NUM_RATES_MCS_1STREAM]; /* MCS 8-15 */ + + uint8 b20_1x3dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ + uint8 b20_1x3cdd_ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM CDD transmission */ + uint8 b20_1x3cdd_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* 1 Nsts to 3 Tx Chain */ + uint8 b20_2x3stbc_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* STBC MCS 0-7 */ + uint8 b20_2x3sdm_mcs8[WL_NUM_RATES_MCS_1STREAM]; /* 2 Nsts to 3 Tx Chain */ + uint8 b20_3x3sdm_mcs16[WL_NUM_RATES_MCS_1STREAM]; /* 3 Nsts to 3 Tx Chain */ + + uint8 b20_1x1vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1 */ + uint8 b20_1x2cdd_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_CDD1 */ + uint8 b20_2x2stbc_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_STBC */ + uint8 b20_2x2sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS2 */ + uint8 b20_1x3cdd_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_CDD2 */ + uint8 b20_2x3stbc_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_STBC_SPEXP1 */ + uint8 b20_2x3sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS2_SPEXP1 */ + uint8 b20_3x3sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS3 */ + + /* start of 40MHz tx power limits */ + uint8 b40_dummy1x1dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ + uint8 b40_1x1ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM transmission */ + uint8 b40_1x1mcs0[WL_NUM_RATES_MCS_1STREAM]; /* SISO MCS 0-7 */ + + uint8 b40_dummy1x2dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ + uint8 b40_1x2cdd_ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM CDD transmission */ + uint8 b40_1x2cdd_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* CDD MCS 0-7 */ + uint8 b40_2x2stbc_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* STBC MCS 0-7 */ + uint8 b40_2x2sdm_mcs8[WL_NUM_RATES_MCS_1STREAM]; /* MCS 8-15 */ + + uint8 b40_dummy1x3dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ + uint8 b40_1x3cdd_ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM CDD transmission */ + uint8 b40_1x3cdd_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* 1 Nsts to 3 Tx Chain */ + uint8 b40_2x3stbc_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* STBC MCS 0-7 */ + uint8 b40_2x3sdm_mcs8[WL_NUM_RATES_MCS_1STREAM]; /* 2 Nsts to 3 Tx Chain */ + uint8 b40_3x3sdm_mcs16[WL_NUM_RATES_MCS_1STREAM]; /* 3 Nsts to 3 Tx Chain */ + + uint8 b40_1x1vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1 */ + uint8 b40_1x2cdd_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_CDD1 */ + uint8 b40_2x2stbc_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_STBC */ + uint8 b40_2x2sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS2 */ + uint8 b40_1x3cdd_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_CDD2 */ + uint8 b40_2x3stbc_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_STBC_SPEXP1 */ + uint8 b40_2x3sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS2_SPEXP1 */ + uint8 b40_3x3sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS3 */ + + /* start of 20in40MHz tx power limits */ + uint8 b20in40_1x1dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ + uint8 b20in40_1x1ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM transmission */ + uint8 b20in40_1x1mcs0[WL_NUM_RATES_MCS_1STREAM]; /* SISO MCS 0-7 */ + + uint8 b20in40_1x2dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ + uint8 b20in40_1x2cdd_ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM CDD transmission */ + uint8 b20in40_1x2cdd_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* CDD MCS 0-7 */ + uint8 b20in40_2x2stbc_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* STBC MCS 0-7 */ + uint8 b20in40_2x2sdm_mcs8[WL_NUM_RATES_MCS_1STREAM]; /* MCS 8-15 */ + + uint8 b20in40_1x3dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ + uint8 b20in40_1x3cdd_ofdm[WL_NUM_RATES_OFDM]; /* 20 in 40 MHz Legacy OFDM CDD */ + uint8 b20in40_1x3cdd_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* 1 Nsts to 3 Tx Chain */ + uint8 b20in40_2x3stbc_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* STBC MCS 0-7 */ + uint8 b20in40_2x3sdm_mcs8[WL_NUM_RATES_MCS_1STREAM]; /* 2 Nsts to 3 Tx Chain */ + uint8 b20in40_3x3sdm_mcs16[WL_NUM_RATES_MCS_1STREAM]; /* 3 Nsts to 3 Tx Chain */ + + uint8 b20in40_1x1vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1 */ + uint8 b20in40_1x2cdd_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_CDD1 */ + uint8 b20in40_2x2stbc_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_STBC */ + uint8 b20in40_2x2sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS2 */ + uint8 b20in40_1x3cdd_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_CDD2 */ + uint8 b20in40_2x3stbc_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_STBC_SPEXP1 */ + uint8 b20in40_2x3sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS2_SPEXP1 */ + uint8 b20in40_3x3sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS3 */ + + /* start of 80MHz tx power limits */ + uint8 b80_dummy1x1dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ + uint8 b80_1x1ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM transmission */ + uint8 b80_1x1mcs0[WL_NUM_RATES_MCS_1STREAM]; /* SISO MCS 0-7 */ + + uint8 b80_dummy1x2dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ + uint8 b80_1x2cdd_ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM CDD transmission */ + uint8 b80_1x2cdd_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* CDD MCS 0-7 */ + uint8 b80_2x2stbc_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* STBC MCS 0-7 */ + uint8 b80_2x2sdm_mcs8[WL_NUM_RATES_MCS_1STREAM]; /* MCS 8-15 */ + + uint8 b80_dummy1x3dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ + uint8 b80_1x3cdd_ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM CDD transmission */ + uint8 b80_1x3cdd_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* 1 Nsts to 3 Tx Chain */ + uint8 b80_2x3stbc_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* STBC MCS 0-7 */ + uint8 b80_2x3sdm_mcs8[WL_NUM_RATES_MCS_1STREAM]; /* 2 Nsts to 3 Tx Chain */ + uint8 b80_3x3sdm_mcs16[WL_NUM_RATES_MCS_1STREAM]; /* 3 Nsts to 3 Tx Chain */ + + uint8 b80_1x1vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1 */ + uint8 b80_1x2cdd_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_CDD1 */ + uint8 b80_2x2stbc_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_STBC */ + uint8 b80_2x2sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS2 */ + uint8 b80_1x3cdd_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_CDD2 */ + uint8 b80_2x3stbc_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_STBC_SPEXP1 */ + uint8 b80_2x3sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS2_SPEXP1 */ + uint8 b80_3x3sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS3 */ + + /* start of 20in80MHz tx power limits */ + uint8 b20in80_1x1dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ + uint8 b20in80_1x1ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM transmission */ + uint8 b20in80_1x1mcs0[WL_NUM_RATES_MCS_1STREAM]; /* SISO MCS 0-7 */ + + uint8 b20in80_1x2dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ + uint8 b20in80_1x2cdd_ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM CDD transmission */ + uint8 b20in80_1x2cdd_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* CDD MCS 0-7 */ + uint8 b20in80_2x2stbc_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* STBC MCS 0-7 */ + uint8 b20in80_2x2sdm_mcs8[WL_NUM_RATES_MCS_1STREAM]; /* MCS 8-15 */ + + uint8 b20in80_1x3dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ + uint8 b20in80_1x3cdd_ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM CDD transmission */ + uint8 b20in80_1x3cdd_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* 1 Nsts to 3 Tx Chain */ + uint8 b20in80_2x3stbc_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* STBC MCS 0-7 */ + uint8 b20in80_2x3sdm_mcs8[WL_NUM_RATES_MCS_1STREAM]; /* 2 Nsts to 3 Tx Chain */ + uint8 b20in80_3x3sdm_mcs16[WL_NUM_RATES_MCS_1STREAM]; /* 3 Nsts to 3 Tx Chain */ + + uint8 b20in80_1x1vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1 */ + uint8 b20in80_1x2cdd_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_CDD1 */ + uint8 b20in80_2x2stbc_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_STBC */ + uint8 b20in80_2x2sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS2 */ + uint8 b20in80_1x3cdd_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_CDD2 */ + uint8 b20in80_2x3stbc_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_STBC_SPEXP1 */ + uint8 b20in80_2x3sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS2_SPEXP1 */ + uint8 b20in80_3x3sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS3 */ + + /* start of 40in80MHz tx power limits */ + uint8 b40in80_dummy1x1dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ + uint8 b40in80_1x1ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM transmission */ + uint8 b40in80_1x1mcs0[WL_NUM_RATES_MCS_1STREAM]; /* SISO MCS 0-7 */ + + uint8 b40in80_dummy1x2dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ + uint8 b40in80_1x2cdd_ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM CDD transmission */ + uint8 b40in80_1x2cdd_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* CDD MCS 0-7 */ + uint8 b40in80_2x2stbc_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* STBC MCS 0-7 */ + uint8 b40in80_2x2sdm_mcs8[WL_NUM_RATES_MCS_1STREAM]; /* MCS 8-15 */ + + uint8 b40in80_dummy1x3dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ + uint8 b40in80_1x3cdd_ofdm[WL_NUM_RATES_OFDM]; /* MHz Legacy OFDM CDD */ + uint8 b40in80_1x3cdd_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* 1 Nsts to 3 Tx Chain */ + uint8 b40in80_2x3stbc_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* STBC MCS 0-7 */ + uint8 b40in80_2x3sdm_mcs8[WL_NUM_RATES_MCS_1STREAM]; /* 2 Nsts to 3 Tx Chain */ + uint8 b40in80_3x3sdm_mcs16[WL_NUM_RATES_MCS_1STREAM]; /* 3 Nsts to 3 Tx Chain */ + + uint8 b40in80_1x1vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1 */ + uint8 b40in80_1x2cdd_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_CDD1 */ + uint8 b40in80_2x2stbc_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_STBC */ + uint8 b40in80_2x2sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS2 */ + uint8 b40in80_1x3cdd_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_CDD2 */ + uint8 b40in80_2x3stbc_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_STBC_SPEXP1 */ + uint8 b40in80_2x3sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS2_SPEXP1 */ + uint8 b40in80_3x3sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS3 */ + + uint8 mcs32; /* C_CHECK - THIS NEEDS TO BE REMOVED THROUGHOUT THE CODE */ +} txppr_t; + +/* 20MHz */ +#define WL_TX_POWER_CCK_FIRST OFFSETOF(txppr_t, b20_1x1dsss) +#define WL_TX_POWER_OFDM20_FIRST OFFSETOF(txppr_t, b20_1x1ofdm) +#define WL_TX_POWER_MCS20_SISO_FIRST OFFSETOF(txppr_t, b20_1x1mcs0) +#define WL_TX_POWER_20_S1x1_FIRST OFFSETOF(txppr_t, b20_1x1mcs0) + +#define WL_TX_POWER_CCK_CDD_S1x2_FIRST OFFSETOF(txppr_t, b20_1x2dsss) +#define WL_TX_POWER_OFDM20_CDD_FIRST OFFSETOF(txppr_t, b20_1x2cdd_ofdm) +#define WL_TX_POWER_MCS20_CDD_FIRST OFFSETOF(txppr_t, b20_1x2cdd_mcs0) +#define WL_TX_POWER_20_S1x2_FIRST OFFSETOF(txppr_t, b20_1x2cdd_mcs0) +#define WL_TX_POWER_MCS20_STBC_FIRST OFFSETOF(txppr_t, b20_2x2stbc_mcs0) +#define WL_TX_POWER_MCS20_SDM_FIRST OFFSETOF(txppr_t, b20_2x2sdm_mcs8) +#define WL_TX_POWER_20_S2x2_FIRST OFFSETOF(txppr_t, b20_2x2sdm_mcs8) + +#define WL_TX_POWER_CCK_CDD_S1x3_FIRST OFFSETOF(txppr_t, b20_1x3dsss) +#define WL_TX_POWER_OFDM20_CDD_S1x3_FIRST OFFSETOF(txppr_t, b20_1x3cdd_ofdm) +#define WL_TX_POWER_20_S1x3_FIRST OFFSETOF(txppr_t, b20_1x3cdd_mcs0) +#define WL_TX_POWER_20_STBC_S2x3_FIRST OFFSETOF(txppr_t, b20_2x3stbc_mcs0) +#define WL_TX_POWER_20_S2x3_FIRST OFFSETOF(txppr_t, b20_2x3sdm_mcs8) +#define WL_TX_POWER_20_S3x3_FIRST OFFSETOF(txppr_t, b20_3x3sdm_mcs16) + +#define WL_TX_POWER_20_S1X1_VHT OFFSETOF(txppr_t, b20_1x1vht) +#define WL_TX_POWER_20_S1X2_CDD_VHT OFFSETOF(txppr_t, b20_1x2cdd_vht) +#define WL_TX_POWER_20_S2X2_STBC_VHT OFFSETOF(txppr_t, b20_2x2stbc_vht) +#define WL_TX_POWER_20_S2X2_VHT OFFSETOF(txppr_t, b20_2x2sdm_vht) +#define WL_TX_POWER_20_S1X3_CDD_VHT OFFSETOF(txppr_t, b20_1x3cdd_vht) +#define WL_TX_POWER_20_S2X3_STBC_VHT OFFSETOF(txppr_t, b20_2x3stbc_vht) +#define WL_TX_POWER_20_S2X3_VHT OFFSETOF(txppr_t, b20_2x3sdm_vht) +#define WL_TX_POWER_20_S3X3_VHT OFFSETOF(txppr_t, b20_3x3sdm_vht) + +/* 40MHz */ +#define WL_TX_POWER_40_DUMMY_CCK_FIRST OFFSETOF(txppr_t, b40_dummy1x1dsss) +#define WL_TX_POWER_OFDM40_FIRST OFFSETOF(txppr_t, b40_1x1ofdm) +#define WL_TX_POWER_MCS40_SISO_FIRST OFFSETOF(txppr_t, b40_1x1mcs0) +#define WL_TX_POWER_40_S1x1_FIRST OFFSETOF(txppr_t, b40_1x1mcs0) + +#define WL_TX_POWER_40_DUMMY_CCK_CDD_S1x2_FIRST OFFSETOF(txppr_t, b40_dummy1x2dsss) +#define WL_TX_POWER_OFDM40_CDD_FIRST OFFSETOF(txppr_t, b40_1x2cdd_ofdm) +#define WL_TX_POWER_MCS40_CDD_FIRST OFFSETOF(txppr_t, b40_1x2cdd_mcs0) +#define WL_TX_POWER_40_S1x2_FIRST OFFSETOF(txppr_t, b40_1x2cdd_mcs0) +#define WL_TX_POWER_MCS40_STBC_FIRST OFFSETOF(txppr_t, b40_2x2stbc_mcs0) +#define WL_TX_POWER_MCS40_SDM_FIRST OFFSETOF(txppr_t, b40_2x2sdm_mcs8) +#define WL_TX_POWER_40_S2x2_FIRST OFFSETOF(txppr_t, b40_2x2sdm_mcs8) + +#define WL_TX_POWER_40_DUMMY_CCK_CDD_S1x3_FIRST OFFSETOF(txppr_t, b40_dummy1x3dsss) +#define WL_TX_POWER_OFDM40_CDD_S1x3_FIRST OFFSETOF(txppr_t, b40_1x3cdd_ofdm) +#define WL_TX_POWER_40_S1x3_FIRST OFFSETOF(txppr_t, b40_1x3cdd_mcs0) +#define WL_TX_POWER_40_STBC_S2x3_FIRST OFFSETOF(txppr_t, b40_2x3stbc_mcs0) +#define WL_TX_POWER_40_S2x3_FIRST OFFSETOF(txppr_t, b40_2x3sdm_mcs8) +#define WL_TX_POWER_40_S3x3_FIRST OFFSETOF(txppr_t, b40_3x3sdm_mcs16) + +#define WL_TX_POWER_40_S1X1_VHT OFFSETOF(txppr_t, b40_1x1vht) +#define WL_TX_POWER_40_S1X2_CDD_VHT OFFSETOF(txppr_t, b40_1x2cdd_vht) +#define WL_TX_POWER_40_S2X2_STBC_VHT OFFSETOF(txppr_t, b40_2x2stbc_vht) +#define WL_TX_POWER_40_S2X2_VHT OFFSETOF(txppr_t, b40_2x2sdm_vht) +#define WL_TX_POWER_40_S1X3_CDD_VHT OFFSETOF(txppr_t, b40_1x3cdd_vht) +#define WL_TX_POWER_40_S2X3_STBC_VHT OFFSETOF(txppr_t, b40_2x3stbc_vht) +#define WL_TX_POWER_40_S2X3_VHT OFFSETOF(txppr_t, b40_2x3sdm_vht) +#define WL_TX_POWER_40_S3X3_VHT OFFSETOF(txppr_t, b40_3x3sdm_vht) + +/* 20 in 40MHz */ +#define WL_TX_POWER_20UL_CCK_FIRST OFFSETOF(txppr_t, b20in40_1x1dsss) +#define WL_TX_POWER_20UL_OFDM_FIRST OFFSETOF(txppr_t, b20in40_1x1ofdm) +#define WL_TX_POWER_20UL_S1x1_FIRST OFFSETOF(txppr_t, b20in40_1x1mcs0) + +#define WL_TX_POWER_CCK_20U_CDD_S1x2_FIRST OFFSETOF(txppr_t, b20in40_1x2dsss) +#define WL_TX_POWER_20UL_OFDM_CDD_FIRST OFFSETOF(txppr_t, b20in40_1x2cdd_ofdm) +#define WL_TX_POWER_20UL_S1x2_FIRST OFFSETOF(txppr_t, b20in40_1x2cdd_mcs0) +#define WL_TX_POWER_20UL_STBC_S2x2_FIRST OFFSETOF(txppr_t, b20in40_2x2stbc_mcs0) +#define WL_TX_POWER_20UL_S2x2_FIRST OFFSETOF(txppr_t, b20in40_2x2sdm_mcs8) + +#define WL_TX_POWER_CCK_20U_CDD_S1x3_FIRST OFFSETOF(txppr_t, b20in40_1x3dsss) +#define WL_TX_POWER_20UL_OFDM_CDD_S1x3_FIRST OFFSETOF(txppr_t, b20in40_1x3cdd_ofdm) +#define WL_TX_POWER_20UL_S1x3_FIRST OFFSETOF(txppr_t, b20in40_1x3cdd_mcs0) +#define WL_TX_POWER_20UL_STBC_S2x3_FIRST OFFSETOF(txppr_t, b20in40_2x3stbc_mcs0) +#define WL_TX_POWER_20UL_S2x3_FIRST OFFSETOF(txppr_t, b20in40_2x3sdm_mcs8) +#define WL_TX_POWER_20UL_S3x3_FIRST OFFSETOF(txppr_t, b20in40_3x3sdm_mcs16) + +#define WL_TX_POWER_20UL_S1X1_VHT OFFSETOF(txppr_t, b20in40_1x1vht) +#define WL_TX_POWER_20UL_S1X2_CDD_VHT OFFSETOF(txppr_t, b20in40_1x2cdd_vht) +#define WL_TX_POWER_20UL_S2X2_STBC_VHT OFFSETOF(txppr_t, b20in40_2x2stbc_vht) +#define WL_TX_POWER_20UL_S2X2_VHT OFFSETOF(txppr_t, b20in40_2x2sdm_vht) +#define WL_TX_POWER_20UL_S1X3_CDD_VHT OFFSETOF(txppr_t, b20in40_1x3cdd_vht) +#define WL_TX_POWER_20UL_S2X3_STBC_VHT OFFSETOF(txppr_t, b20in40_2x3stbc_vht) +#define WL_TX_POWER_20UL_S2X3_VHT OFFSETOF(txppr_t, b20in40_2x3sdm_vht) +#define WL_TX_POWER_20UL_S3X3_VHT OFFSETOF(txppr_t, b20in40_3x3sdm_vht) + +/* 80MHz */ +#define WL_TX_POWER_80_DUMMY_CCK_FIRST OFFSETOF(txppr_t, b80_dummy1x1dsss) +#define WL_TX_POWER_OFDM80_FIRST OFFSETOF(txppr_t, b80_1x1ofdm) +#define WL_TX_POWER_MCS80_SISO_FIRST OFFSETOF(txppr_t, b80_1x1mcs0) +#define WL_TX_POWER_80_S1x1_FIRST OFFSETOF(txppr_t, b80_1x1mcs0) + +#define WL_TX_POWER_80_DUMMY_CCK_CDD_S1x2_FIRST OFFSETOF(txppr_t, b80_dummy1x2dsss) +#define WL_TX_POWER_OFDM80_CDD_FIRST OFFSETOF(txppr_t, b80_1x2cdd_ofdm) +#define WL_TX_POWER_MCS80_CDD_FIRST OFFSETOF(txppr_t, b80_1x2cdd_mcs0) +#define WL_TX_POWER_80_S1x2_FIRST OFFSETOF(txppr_t, b80_1x2cdd_mcs0) +#define WL_TX_POWER_MCS80_STBC_FIRST OFFSETOF(txppr_t, b80_2x2stbc_mcs0) +#define WL_TX_POWER_MCS80_SDM_FIRST OFFSETOF(txppr_t, b80_2x2sdm_mcs8) +#define WL_TX_POWER_80_S2x2_FIRST OFFSETOF(txppr_t, b80_2x2sdm_mcs8) + +#define WL_TX_POWER_80_DUMMY_CCK_CDD_S1x3_FIRST OFFSETOF(txppr_t, b80_dummy1x3dsss) +#define WL_TX_POWER_OFDM80_CDD_S1x3_FIRST OFFSETOF(txppr_t, b80_1x3cdd_ofdm) +#define WL_TX_POWER_80_S1x3_FIRST OFFSETOF(txppr_t, b80_1x3cdd_mcs0) +#define WL_TX_POWER_80_STBC_S2x3_FIRST OFFSETOF(txppr_t, b80_2x3stbc_mcs0) +#define WL_TX_POWER_80_S2x3_FIRST OFFSETOF(txppr_t, b80_2x3sdm_mcs8) +#define WL_TX_POWER_80_S3x3_FIRST OFFSETOF(txppr_t, b80_3x3sdm_mcs16) + +#define WL_TX_POWER_80_S1X1_VHT OFFSETOF(txppr_t, b80_1x1vht) +#define WL_TX_POWER_80_S1X2_CDD_VHT OFFSETOF(txppr_t, b80_1x2cdd_vht) +#define WL_TX_POWER_80_S2X2_STBC_VHT OFFSETOF(txppr_t, b80_2x2stbc_vht) +#define WL_TX_POWER_80_S2X2_VHT OFFSETOF(txppr_t, b80_2x2sdm_vht) +#define WL_TX_POWER_80_S1X3_CDD_VHT OFFSETOF(txppr_t, b80_1x3cdd_vht) +#define WL_TX_POWER_80_S2X3_STBC_VHT OFFSETOF(txppr_t, b80_2x3stbc_vht) +#define WL_TX_POWER_80_S2X3_VHT OFFSETOF(txppr_t, b80_2x3sdm_vht) +#define WL_TX_POWER_80_S3X3_VHT OFFSETOF(txppr_t, b80_3x3sdm_vht) + +/* 20 in 80MHz */ +#define WL_TX_POWER_20UUL_CCK_FIRST OFFSETOF(txppr_t, b20in80_1x1dsss) +#define WL_TX_POWER_20UUL_OFDM_FIRST OFFSETOF(txppr_t, b20in80_1x1ofdm) +#define WL_TX_POWER_20UUL_S1x1_FIRST OFFSETOF(txppr_t, b20in80_1x1mcs0) + +#define WL_TX_POWER_CCK_20UU_CDD_S1x2_FIRST OFFSETOF(txppr_t, b20in80_1x2dsss) +#define WL_TX_POWER_20UUL_OFDM_CDD_FIRST OFFSETOF(txppr_t, b20in80_1x2cdd_ofdm) +#define WL_TX_POWER_20UUL_S1x2_FIRST OFFSETOF(txppr_t, b20in80_1x2cdd_mcs0) +#define WL_TX_POWER_20UUL_STBC_S2x2_FIRST OFFSETOF(txppr_t, b20in80_2x2stbc_mcs0) +#define WL_TX_POWER_20UUL_S2x2_FIRST OFFSETOF(txppr_t, b20in80_2x2sdm_mcs8) + +#define WL_TX_POWER_CCK_20UU_CDD_S1x3_FIRST OFFSETOF(txppr_t, b20in80_1x3dsss) +#define WL_TX_POWER_20UUL_OFDM_CDD_S1x3_FIRST OFFSETOF(txppr_t, b20in80_1x3cdd_ofdm) +#define WL_TX_POWER_20UUL_S1x3_FIRST OFFSETOF(txppr_t, b20in80_1x3cdd_mcs0) +#define WL_TX_POWER_20UUL_STBC_S2x3_FIRST OFFSETOF(txppr_t, b20in80_2x3stbc_mcs0) +#define WL_TX_POWER_20UUL_S2x3_FIRST OFFSETOF(txppr_t, b20in80_2x3sdm_mcs8) +#define WL_TX_POWER_20UUL_S3x3_FIRST OFFSETOF(txppr_t, b20in80_3x3sdm_mcs16) + +#define WL_TX_POWER_20UUL_S1X1_VHT OFFSETOF(txppr_t, b20in80_1x1vht) +#define WL_TX_POWER_20UUL_S1X2_CDD_VHT OFFSETOF(txppr_t, b20in80_1x2cdd_vht) +#define WL_TX_POWER_20UUL_S2X2_STBC_VHT OFFSETOF(txppr_t, b20in80_2x2stbc_vht) +#define WL_TX_POWER_20UUL_S2X2_VHT OFFSETOF(txppr_t, b20in80_2x2sdm_vht) +#define WL_TX_POWER_20UUL_S1X3_CDD_VHT OFFSETOF(txppr_t, b20in80_1x3cdd_vht) +#define WL_TX_POWER_20UUL_S2X3_STBC_VHT OFFSETOF(txppr_t, b20in80_2x3stbc_vht) +#define WL_TX_POWER_20UUL_S2X3_VHT OFFSETOF(txppr_t, b20in80_2x3sdm_vht) +#define WL_TX_POWER_20UUL_S3X3_VHT OFFSETOF(txppr_t, b20in80_3x3sdm_vht) + +/* 40 in 80MHz */ +#define WL_TX_POWER_40UUL_DUMMY_CCK_FIRST OFFSETOF(txppr_t, b40in80_dummy1x1dsss) +#define WL_TX_POWER_40UUL_OFDM_FIRST OFFSETOF(txppr_t, b40in80_1x1ofdm) +#define WL_TX_POWER_40UUL_S1x1_FIRST OFFSETOF(txppr_t, b40in80_1x1mcs0) + +#define WL_TX_POWER_CCK_40UU_DUMMY_CDD_S1x2_FIRST OFFSETOF(txppr_t, b40in80_dummy1x2dsss) +#define WL_TX_POWER_40UUL_OFDM_CDD_FIRST OFFSETOF(txppr_t, b40in80_1x2cdd_ofdm) +#define WL_TX_POWER_40UUL_S1x2_FIRST OFFSETOF(txppr_t, b40in80_1x2cdd_mcs0) +#define WL_TX_POWER_40UUL_STBC_S2x2_FIRST OFFSETOF(txppr_t, b40in80_2x2stbc_mcs0) +#define WL_TX_POWER_40UUL_S2x2_FIRST OFFSETOF(txppr_t, b40in80_2x2sdm_mcs8) + +#define WL_TX_POWER_CCK_40UU_DUMMY_CDD_S1x3_FIRST OFFSETOF(txppr_t, b40in80_dummy1x3dsss) +#define WL_TX_POWER_40UUL_OFDM_CDD_S1x3_FIRST OFFSETOF(txppr_t, b40in80_1x3cdd_ofdm) +#define WL_TX_POWER_40UUL_S1x3_FIRST OFFSETOF(txppr_t, b40in80_1x3cdd_mcs0) +#define WL_TX_POWER_40UUL_STBC_S2x3_FIRST OFFSETOF(txppr_t, b40in80_2x3stbc_mcs0) +#define WL_TX_POWER_40UUL_S2x3_FIRST OFFSETOF(txppr_t, b40in80_2x3sdm_mcs8) +#define WL_TX_POWER_40UUL_S3x3_FIRST OFFSETOF(txppr_t, b40in80_3x3sdm_mcs16) + +#define WL_TX_POWER_40UUL_S1X1_VHT OFFSETOF(txppr_t, b40in80_1x1vht) +#define WL_TX_POWER_40UUL_S1X2_CDD_VHT OFFSETOF(txppr_t, b40in80_1x2cdd_vht) +#define WL_TX_POWER_40UUL_S2X2_STBC_VHT OFFSETOF(txppr_t, b40in80_2x2stbc_vht) +#define WL_TX_POWER_40UUL_S2X2_VHT OFFSETOF(txppr_t, b40in80_2x2sdm_vht) +#define WL_TX_POWER_40UUL_S1X3_CDD_VHT OFFSETOF(txppr_t, b40in80_1x3cdd_vht) +#define WL_TX_POWER_40UUL_S2X3_STBC_VHT OFFSETOF(txppr_t, b40in80_2x3stbc_vht) +#define WL_TX_POWER_40UUL_S2X3_VHT OFFSETOF(txppr_t, b40in80_2x3sdm_vht) +#define WL_TX_POWER_40UUL_S3X3_VHT OFFSETOF(txppr_t, b40in80_3x3sdm_vht) + +#define WL_TX_POWER_MCS_32 OFFSETOF(txppr_t, mcs32) /* C_CHECK remove later */ + +#define WL_TX_POWER_RATES sizeof(struct txppr) + +/* sslpnphy specifics */ +#define WL_TX_POWER_MCS20_SISO_FIRST_SSN WL_TX_POWER_MCS20_SISO_FIRST +#define WL_TX_POWER_MCS40_SISO_FIRST_SSN WL_TX_POWER_MCS40_SISO_FIRST + +/* tx_power_t.flags bits */ +#define WL_TX_POWER_F_ENABLED 1 +#define WL_TX_POWER_F_HW 2 +#define WL_TX_POWER_F_MIMO 4 +#define WL_TX_POWER_F_SISO 8 +#define WL_TX_POWER_F_HT 0x10 + +typedef struct { + uint16 ver; /* version of this struct */ + uint16 len; /* length in bytes of this structure */ + uint32 flags; + chanspec_t chanspec; /* txpwr report for this channel */ + chanspec_t local_chanspec; /* channel on which we are associated */ + uint8 ppr[WL_TX_POWER_RATES]; /* Latest target power */ +} wl_txppr_t; + +#define WL_TXPPR_VERSION 0 +#define WL_TXPPR_LENGTH (sizeof(wl_txppr_t)) +#define TX_POWER_T_VERSION 43 + +/* Defines used with channel_bandwidth for curpower */ +#define WL_BW_20MHZ 0 +#define WL_BW_40MHZ 1 +#define WL_BW_80MHZ 2 + +/* tx_power_t.flags bits */ +#ifdef PPR_API +#define WL_TX_POWER2_F_ENABLED 1 +#define WL_TX_POWER2_F_HW 2 +#define WL_TX_POWER2_F_MIMO 4 +#define WL_TX_POWER2_F_SISO 8 +#define WL_TX_POWER2_F_HT 0x10 +#else +#define WL_TX_POWER_F_ENABLED 1 +#define WL_TX_POWER_F_HW 2 +#define WL_TX_POWER_F_MIMO 4 +#define WL_TX_POWER_F_SISO 8 +#define WL_TX_POWER_F_HT 0x10 +#endif +typedef struct { + uint32 flags; + chanspec_t chanspec; /* txpwr report for this channel */ + chanspec_t local_chanspec; /* channel on which we are associated */ + uint8 local_max; /* local max according to the AP */ + uint8 local_constraint; /* local constraint according to the AP */ + int8 antgain[2]; /* Ant gain for each band - from SROM */ + uint8 rf_cores; /* count of RF Cores being reported */ + uint8 est_Pout[4]; /* Latest tx power out estimate per RF chain */ + uint8 est_Pout_act[4]; /* Latest tx power out estimate per RF chain + * without adjustment + */ + uint8 est_Pout_cck; /* Latest CCK tx power out estimate */ + uint8 tx_power_max[4]; /* Maximum target power among all rates */ + uint tx_power_max_rate_ind[4]; /* Index of the rate with the max target power */ + uint8 user_limit[WL_TX_POWER_RATES]; /* User limit */ + int8 board_limit[WL_TX_POWER_RATES]; /* Max power board can support (SROM) */ + int8 target[WL_TX_POWER_RATES]; /* Latest target power */ + int8 clm_limits[WL_NUMRATES]; /* regulatory limits - 20, 40 or 80MHz */ + int8 clm_limits_subchan1[WL_NUMRATES]; /* regulatory limits - 20in40 or 40in80 */ + int8 clm_limits_subchan2[WL_NUMRATES]; /* regulatory limits - 20in80MHz */ + int8 sar; /* SAR limit for display by wl executable */ + int8 channel_bandwidth; /* 20, 40 or 80 MHz bandwidth? */ + uint8 version; /* Version of the data format wlu <--> driver */ + uint8 display_core; /* Displayed curpower core */ +#ifdef PPR_API +} tx_power_new_t; +#else +} tx_power_t; +#endif + +typedef struct tx_inst_power { + uint8 txpwr_est_Pout[2]; /* Latest estimate for 2.4 and 5 Ghz */ + uint8 txpwr_est_Pout_gofdm; /* Pwr estimate for 2.4 OFDM */ +} tx_inst_power_t; + + +typedef struct { + uint32 flags; + chanspec_t chanspec; /* txpwr report for this channel */ + chanspec_t local_chanspec; /* channel on which we are associated */ + uint8 local_max; /* local max according to the AP */ + uint8 local_constraint; /* local constraint according to the AP */ + int8 antgain[2]; /* Ant gain for each band - from SROM */ + uint8 rf_cores; /* count of RF Cores being reported */ + uint8 est_Pout[4]; /* Latest tx power out estimate per RF chain */ + uint8 est_Pout_act[4]; /* Latest tx power out estimate per RF chain + * without adjustment + */ + uint8 est_Pout_cck; /* Latest CCK tx power out estimate */ + uint8 tx_power_max[4]; /* Maximum target power among all rates */ + uint tx_power_max_rate_ind[4]; /* Index of the rate with the max target power */ + txppr_t user_limit; /* User limit */ + txppr_t reg_limit; /* Regulatory power limit */ + txppr_t board_limit; /* Max power board can support (SROM) */ + txppr_t target; /* Latest target power */ +} wl_txpwr_t; + +#define WL_NUM_TXCHAIN_MAX 4 +typedef struct wl_txchain_pwr_offsets { + int8 offset[WL_NUM_TXCHAIN_MAX]; /* quarter dBm signed offset for each chain */ +} wl_txchain_pwr_offsets_t; + +/* 802.11h measurement types */ +#define WLC_MEASURE_TPC 1 +#define WLC_MEASURE_CHANNEL_BASIC 2 +#define WLC_MEASURE_CHANNEL_CCA 3 +#define WLC_MEASURE_CHANNEL_RPI 4 + +/* regulatory enforcement levels */ +#define SPECT_MNGMT_OFF 0 /* both 11h and 11d disabled */ +#define SPECT_MNGMT_LOOSE_11H 1 /* allow non-11h APs in scan lists */ +#define SPECT_MNGMT_STRICT_11H 2 /* prune out non-11h APs from scan list */ +#define SPECT_MNGMT_STRICT_11D 3 /* switch to 802.11D mode */ +/* SPECT_MNGMT_LOOSE_11H_D - same as SPECT_MNGMT_LOOSE with the exception that Country IE + * adoption is done regardless of capability spectrum_management + */ +#define SPECT_MNGMT_LOOSE_11H_D 4 /* operation defined above */ + +#define WL_CHAN_VALID_HW (1 << 0) /* valid with current HW */ +#define WL_CHAN_VALID_SW (1 << 1) /* valid with current country setting */ +#define WL_CHAN_BAND_5G (1 << 2) /* 5GHz-band channel */ +#define WL_CHAN_RADAR (1 << 3) /* radar sensitive channel */ +#define WL_CHAN_INACTIVE (1 << 4) /* temporarily inactive due to radar */ +#define WL_CHAN_PASSIVE (1 << 5) /* channel is in passive mode */ +#define WL_CHAN_RESTRICTED (1 << 6) /* restricted use channel */ + +/* BTC mode used by "btc_mode" iovar */ +#define WL_BTC_DISABLE 0 /* disable BT coexistence */ +#define WL_BTC_FULLTDM 1 /* full TDM COEX */ +#define WL_BTC_ENABLE 1 /* full TDM COEX to maintain backward compatiblity */ +#define WL_BTC_PREMPT 2 /* full TDM COEX with preemption */ +#define WL_BTC_LITE 3 /* light weight coex for large isolation platform */ +#define WL_BTC_PARALLEL 4 /* BT and WLAN run in parallel with separate antenna */ +#define WL_BTC_HYBRID 5 /* hybrid coex, only ack is allowed to transmit in BT slot */ +#define WL_BTC_DEFAULT 8 /* set the default mode for the device */ +#define WL_INF_BTC_DISABLE 0 +#define WL_INF_BTC_ENABLE 1 +#define WL_INF_BTC_AUTO 3 + +/* BTC wire used by "btc_wire" iovar */ +#define WL_BTC_DEFWIRE 0 /* use default wire setting */ +#define WL_BTC_2WIRE 2 /* use 2-wire BTC */ +#define WL_BTC_3WIRE 3 /* use 3-wire BTC */ +#define WL_BTC_4WIRE 4 /* use 4-wire BTC */ + +/* BTC flags: BTC configuration that can be set by host */ +#define WL_BTC_FLAG_PREMPT (1 << 0) +#define WL_BTC_FLAG_BT_DEF (1 << 1) +#define WL_BTC_FLAG_ACTIVE_PROT (1 << 2) +#define WL_BTC_FLAG_SIM_RSP (1 << 3) +#define WL_BTC_FLAG_PS_PROTECT (1 << 4) +#define WL_BTC_FLAG_SIM_TX_LP (1 << 5) +#define WL_BTC_FLAG_ECI (1 << 6) +#define WL_BTC_FLAG_LIGHT (1 << 7) +#define WL_BTC_FLAG_PARALLEL (1 << 8) + +/* Message levels */ +#define WL_ERROR_VAL 0x00000001 +#define WL_TRACE_VAL 0x00000002 +#define WL_PRHDRS_VAL 0x00000004 +#define WL_PRPKT_VAL 0x00000008 +#define WL_INFORM_VAL 0x00000010 +#define WL_TMP_VAL 0x00000020 +#define WL_OID_VAL 0x00000040 +#define WL_RATE_VAL 0x00000080 +#define WL_ASSOC_VAL 0x00000100 +#define WL_PRUSR_VAL 0x00000200 +#define WL_PS_VAL 0x00000400 +#define WL_TXPWR_VAL 0x00000800 /* retired in TOT on 6/10/2009 */ +#define WL_PORT_VAL 0x00001000 +#define WL_DUAL_VAL 0x00002000 +#define WL_WSEC_VAL 0x00004000 +#define WL_WSEC_DUMP_VAL 0x00008000 +#define WL_LOG_VAL 0x00010000 +#define WL_NRSSI_VAL 0x00020000 /* retired in TOT on 6/10/2009 */ +#define WL_LOFT_VAL 0x00040000 /* retired in TOT on 6/10/2009 */ +#define WL_REGULATORY_VAL 0x00080000 +#define WL_PHYCAL_VAL 0x00100000 /* retired in TOT on 6/10/2009 */ +#define WL_RADAR_VAL 0x00200000 /* retired in TOT on 6/10/2009 */ +#define WL_MPC_VAL 0x00400000 +#define WL_APSTA_VAL 0x00800000 +#define WL_DFS_VAL 0x01000000 +#define WL_BA_VAL 0x02000000 /* retired in TOT on 6/14/2010 */ +#define WL_ACI_VAL 0x04000000 +#define WL_MBSS_VAL 0x04000000 +#define WL_CAC_VAL 0x08000000 +#define WL_AMSDU_VAL 0x10000000 +#define WL_AMPDU_VAL 0x20000000 +#define WL_FFPLD_VAL 0x40000000 + +/* wl_msg_level is full. For new bits take the next one and AND with + * wl_msg_level2 in wl_dbg.h + */ +#define WL_DPT_VAL 0x00000001 +#define WL_SCAN_VAL 0x00000002 +#define WL_WOWL_VAL 0x00000004 +#define WL_COEX_VAL 0x00000008 +#define WL_RTDC_VAL 0x00000010 +#define WL_PROTO_VAL 0x00000020 +#define WL_BTA_VAL 0x00000040 +#define WL_CHANINT_VAL 0x00000080 +#define WL_THERMAL_VAL 0x00000100 /* retired in TOT on 6/10/2009 */ +#define WL_P2P_VAL 0x00000200 +#define WL_ITFR_VAL 0x00000400 +#define WL_MCHAN_VAL 0x00000800 +#define WL_TDLS_VAL 0x00001000 +#define WL_MCNX_VAL 0x00002000 +#define WL_PROT_VAL 0x00004000 +#define WL_PSTA_VAL 0x00008000 +#define WL_TBTT_VAL 0x00010000 +#define WL_NIC_VAL 0x00020000 +#define WL_PWRSEL_VAL 0x00040000 +/* use top-bit for WL_TIME_STAMP_VAL because this is a modifier + * rather than a message-type of its own + */ +#define WL_TIMESTAMP_VAL 0x80000000 + +/* max # of leds supported by GPIO (gpio pin# == led index#) */ +#define WL_LED_NUMGPIO 32 /* gpio 0-31 */ + +/* led per-pin behaviors */ +#define WL_LED_OFF 0 /* always off */ +#define WL_LED_ON 1 /* always on */ +#define WL_LED_ACTIVITY 2 /* activity */ +#define WL_LED_RADIO 3 /* radio enabled */ +#define WL_LED_ARADIO 4 /* 5 Ghz radio enabled */ +#define WL_LED_BRADIO 5 /* 2.4Ghz radio enabled */ +#define WL_LED_BGMODE 6 /* on if gmode, off if bmode */ +#define WL_LED_WI1 7 +#define WL_LED_WI2 8 +#define WL_LED_WI3 9 +#define WL_LED_ASSOC 10 /* associated state indicator */ +#define WL_LED_INACTIVE 11 /* null behavior (clears default behavior) */ +#define WL_LED_ASSOCACT 12 /* on when associated; blink fast for activity */ +#define WL_LED_WI4 13 +#define WL_LED_WI5 14 +#define WL_LED_BLINKSLOW 15 /* blink slow */ +#define WL_LED_BLINKMED 16 /* blink med */ +#define WL_LED_BLINKFAST 17 /* blink fast */ +#define WL_LED_BLINKCUSTOM 18 /* blink custom */ +#define WL_LED_BLINKPERIODIC 19 /* blink periodic (custom 1000ms / off 400ms) */ +#define WL_LED_ASSOC_WITH_SEC 20 /* when connected with security */ + /* keep on for 300 sec */ +#define WL_LED_START_OFF 21 /* off upon boot, could be turned on later */ +#define WL_LED_NUMBEHAVIOR 22 + +/* led behavior numeric value format */ +#define WL_LED_BEH_MASK 0x7f /* behavior mask */ +#define WL_LED_AL_MASK 0x80 /* activelow (polarity) bit */ + +/* maximum channels returned by the get valid channels iovar */ +#define WL_NUMCHANNELS 64 + +/* max number of chanspecs (used by the iovar to calc. buf space) */ +#define WL_NUMCHANSPECS 110 + +/* WDS link local endpoint WPA role */ +#define WL_WDS_WPA_ROLE_AUTH 0 /* authenticator */ +#define WL_WDS_WPA_ROLE_SUP 1 /* supplicant */ +#define WL_WDS_WPA_ROLE_AUTO 255 /* auto, based on mac addr value */ + +/* number of bytes needed to define a 128-bit mask for MAC event reporting */ +#define WL_EVENTING_MASK_LEN 16 + +/* + * Join preference iovar value is an array of tuples. Each tuple has a one-byte type, + * a one-byte length, and a variable length value. RSSI type tuple must be present + * in the array. + * + * Types are defined in "join preference types" section. + * + * Length is the value size in octets. It is reserved for WL_JOIN_PREF_WPA type tuple + * and must be set to zero. + * + * Values are defined below. + * + * 1. RSSI - 2 octets + * offset 0: reserved + * offset 1: reserved + * + * 2. WPA - 2 + 12 * n octets (n is # tuples defined below) + * offset 0: reserved + * offset 1: # of tuples + * offset 2: tuple 1 + * offset 14: tuple 2 + * ... + * offset 2 + 12 * (n - 1) octets: tuple n + * + * struct wpa_cfg_tuple { + * uint8 akm[DOT11_OUI_LEN+1]; akm suite + * uint8 ucipher[DOT11_OUI_LEN+1]; unicast cipher suite + * uint8 mcipher[DOT11_OUI_LEN+1]; multicast cipher suite + * }; + * + * multicast cipher suite can be specified as a specific cipher suite or WL_WPA_ACP_MCS_ANY. + * + * 3. BAND - 2 octets + * offset 0: reserved + * offset 1: see "band preference" and "band types" + * + * 4. BAND RSSI - 2 octets + * offset 0: band types + * offset 1: +ve RSSI boost balue in dB + */ + +/* join preference types */ +#define WL_JOIN_PREF_RSSI 1 /* by RSSI */ +#define WL_JOIN_PREF_WPA 2 /* by akm and ciphers */ +#define WL_JOIN_PREF_BAND 3 /* by 802.11 band */ +#define WL_JOIN_PREF_RSSI_DELTA 4 /* by 802.11 band only if RSSI delta condition matches */ +#define WL_JOIN_PREF_TRANS_PREF 5 /* defined by requesting AP */ + +/* band preference */ +#define WLJP_BAND_ASSOC_PREF 255 /* use what WLC_SET_ASSOC_PREFER ioctl specifies */ + +/* any multicast cipher suite */ +#define WL_WPA_ACP_MCS_ANY "\x00\x00\x00\x00" + +struct tsinfo_arg { + uint8 octets[3]; +}; +#endif /* LINUX_POSTMOGRIFY_REMOVAL */ + +#define NFIFO 6 /* # tx/rx fifopairs */ + +#define WL_CNT_T_VERSION 8 /* current version of wl_cnt_t struct */ + +typedef struct { + uint16 version; /* see definition of WL_CNT_T_VERSION */ + uint16 length; /* length of entire structure */ + + /* transmit stat counters */ + uint32 txframe; /* tx data frames */ + uint32 txbyte; /* tx data bytes */ + uint32 txretrans; /* tx mac retransmits */ + uint32 txerror; /* tx data errors (derived: sum of others) */ + uint32 txctl; /* tx management frames */ + uint32 txprshort; /* tx short preamble frames */ + uint32 txserr; /* tx status errors */ + uint32 txnobuf; /* tx out of buffers errors */ + uint32 txnoassoc; /* tx discard because we're not associated */ + uint32 txrunt; /* tx runt frames */ + uint32 txchit; /* tx header cache hit (fastpath) */ + uint32 txcmiss; /* tx header cache miss (slowpath) */ + + /* transmit chip error counters */ + uint32 txuflo; /* tx fifo underflows */ + uint32 txphyerr; /* tx phy errors (indicated in tx status) */ + uint32 txphycrs; + + /* receive stat counters */ + uint32 rxframe; /* rx data frames */ + uint32 rxbyte; /* rx data bytes */ + uint32 rxerror; /* rx data errors (derived: sum of others) */ + uint32 rxctl; /* rx management frames */ + uint32 rxnobuf; /* rx out of buffers errors */ + uint32 rxnondata; /* rx non data frames in the data channel errors */ + uint32 rxbadds; /* rx bad DS errors */ + uint32 rxbadcm; /* rx bad control or management frames */ + uint32 rxfragerr; /* rx fragmentation errors */ + uint32 rxrunt; /* rx runt frames */ + uint32 rxgiant; /* rx giant frames */ + uint32 rxnoscb; /* rx no scb error */ + uint32 rxbadproto; /* rx invalid frames */ + uint32 rxbadsrcmac; /* rx frames with Invalid Src Mac */ + uint32 rxbadda; /* rx frames tossed for invalid da */ + uint32 rxfilter; /* rx frames filtered out */ + + /* receive chip error counters */ + uint32 rxoflo; /* rx fifo overflow errors */ + uint32 rxuflo[NFIFO]; /* rx dma descriptor underflow errors */ + + uint32 d11cnt_txrts_off; /* d11cnt txrts value when reset d11cnt */ + uint32 d11cnt_rxcrc_off; /* d11cnt rxcrc value when reset d11cnt */ + uint32 d11cnt_txnocts_off; /* d11cnt txnocts value when reset d11cnt */ + + /* misc counters */ + uint32 dmade; /* tx/rx dma descriptor errors */ + uint32 dmada; /* tx/rx dma data errors */ + uint32 dmape; /* tx/rx dma descriptor protocol errors */ + uint32 reset; /* reset count */ + uint32 tbtt; /* cnts the TBTT int's */ + uint32 txdmawar; + uint32 pkt_callback_reg_fail; /* callbacks register failure */ + + /* MAC counters: 32-bit version of d11.h's macstat_t */ + uint32 txallfrm; /* total number of frames sent, incl. Data, ACK, RTS, CTS, + * Control Management (includes retransmissions) + */ + uint32 txrtsfrm; /* number of RTS sent out by the MAC */ + uint32 txctsfrm; /* number of CTS sent out by the MAC */ + uint32 txackfrm; /* number of ACK frames sent out */ + uint32 txdnlfrm; /* Not used */ + uint32 txbcnfrm; /* beacons transmitted */ + uint32 txfunfl[8]; /* per-fifo tx underflows */ + uint32 txtplunfl; /* Template underflows (mac was too slow to transmit ACK/CTS + * or BCN) + */ + uint32 txphyerror; /* Transmit phy error, type of error is reported in tx-status for + * driver enqueued frames + */ + uint32 rxfrmtoolong; /* Received frame longer than legal limit (2346 bytes) */ + uint32 rxfrmtooshrt; /* Received frame did not contain enough bytes for its frame type */ + uint32 rxinvmachdr; /* Either the protocol version != 0 or frame type not + * data/control/management + */ + uint32 rxbadfcs; /* number of frames for which the CRC check failed in the MAC */ + uint32 rxbadplcp; /* parity check of the PLCP header failed */ + uint32 rxcrsglitch; /* PHY was able to correlate the preamble but not the header */ + uint32 rxstrt; /* Number of received frames with a good PLCP + * (i.e. passing parity check) + */ + uint32 rxdfrmucastmbss; /* Number of received DATA frames with good FCS and matching RA */ + uint32 rxmfrmucastmbss; /* number of received mgmt frames with good FCS and matching RA */ + uint32 rxcfrmucast; /* number of received CNTRL frames with good FCS and matching RA */ + uint32 rxrtsucast; /* number of unicast RTS addressed to the MAC (good FCS) */ + uint32 rxctsucast; /* number of unicast CTS addressed to the MAC (good FCS) */ + uint32 rxackucast; /* number of ucast ACKS received (good FCS) */ + uint32 rxdfrmocast; /* number of received DATA frames (good FCS and not matching RA) */ + uint32 rxmfrmocast; /* number of received MGMT frames (good FCS and not matching RA) */ + uint32 rxcfrmocast; /* number of received CNTRL frame (good FCS and not matching RA) */ + uint32 rxrtsocast; /* number of received RTS not addressed to the MAC */ + uint32 rxctsocast; /* number of received CTS not addressed to the MAC */ + uint32 rxdfrmmcast; /* number of RX Data multicast frames received by the MAC */ + uint32 rxmfrmmcast; /* number of RX Management multicast frames received by the MAC */ + uint32 rxcfrmmcast; /* number of RX Control multicast frames received by the MAC + * (unlikely to see these) + */ + uint32 rxbeaconmbss; /* beacons received from member of BSS */ + uint32 rxdfrmucastobss; /* number of unicast frames addressed to the MAC from + * other BSS (WDS FRAME) + */ + uint32 rxbeaconobss; /* beacons received from other BSS */ + uint32 rxrsptmout; /* Number of response timeouts for transmitted frames + * expecting a response + */ + uint32 bcntxcancl; /* transmit beacons canceled due to receipt of beacon (IBSS) */ + uint32 rxf0ovfl; /* Number of receive fifo 0 overflows */ + uint32 rxf1ovfl; /* Number of receive fifo 1 overflows (obsolete) */ + uint32 rxf2ovfl; /* Number of receive fifo 2 overflows (obsolete) */ + uint32 txsfovfl; /* Number of transmit status fifo overflows (obsolete) */ + uint32 pmqovfl; /* Number of PMQ overflows */ + uint32 rxcgprqfrm; /* Number of received Probe requests that made it into + * the PRQ fifo + */ + uint32 rxcgprsqovfl; /* Rx Probe Request Que overflow in the AP */ + uint32 txcgprsfail; /* Tx Probe Response Fail. AP sent probe response but did + * not get ACK + */ + uint32 txcgprssuc; /* Tx Probe Response Success (ACK was received) */ + uint32 prs_timeout; /* Number of probe requests that were dropped from the PRQ + * fifo because a probe response could not be sent out within + * the time limit defined in M_PRS_MAXTIME + */ + uint32 rxnack; /* obsolete */ + uint32 frmscons; /* obsolete */ + uint32 txnack; /* obsolete */ + uint32 txglitch_nack; /* obsolete */ + uint32 txburst; /* obsolete */ + + /* 802.11 MIB counters, pp. 614 of 802.11 reaff doc. */ + uint32 txfrag; /* dot11TransmittedFragmentCount */ + uint32 txmulti; /* dot11MulticastTransmittedFrameCount */ + uint32 txfail; /* dot11FailedCount */ + uint32 txretry; /* dot11RetryCount */ + uint32 txretrie; /* dot11MultipleRetryCount */ + uint32 rxdup; /* dot11FrameduplicateCount */ + uint32 txrts; /* dot11RTSSuccessCount */ + uint32 txnocts; /* dot11RTSFailureCount */ + uint32 txnoack; /* dot11ACKFailureCount */ + uint32 rxfrag; /* dot11ReceivedFragmentCount */ + uint32 rxmulti; /* dot11MulticastReceivedFrameCount */ + uint32 rxcrc; /* dot11FCSErrorCount */ + uint32 txfrmsnt; /* dot11TransmittedFrameCount (bogus MIB?) */ + uint32 rxundec; /* dot11WEPUndecryptableCount */ + + /* WPA2 counters (see rxundec for DecryptFailureCount) */ + uint32 tkipmicfaill; /* TKIPLocalMICFailures */ + uint32 tkipcntrmsr; /* TKIPCounterMeasuresInvoked */ + uint32 tkipreplay; /* TKIPReplays */ + uint32 ccmpfmterr; /* CCMPFormatErrors */ + uint32 ccmpreplay; /* CCMPReplays */ + uint32 ccmpundec; /* CCMPDecryptErrors */ + uint32 fourwayfail; /* FourWayHandshakeFailures */ + uint32 wepundec; /* dot11WEPUndecryptableCount */ + uint32 wepicverr; /* dot11WEPICVErrorCount */ + uint32 decsuccess; /* DecryptSuccessCount */ + uint32 tkipicverr; /* TKIPICVErrorCount */ + uint32 wepexcluded; /* dot11WEPExcludedCount */ + + uint32 txchanrej; /* Tx frames suppressed due to channel rejection */ + uint32 psmwds; /* Count PSM watchdogs */ + uint32 phywatchdog; /* Count Phy watchdogs (triggered by ucode) */ + + /* MBSS counters, AP only */ + uint32 prq_entries_handled; /* PRQ entries read in */ + uint32 prq_undirected_entries; /* which were bcast bss & ssid */ + uint32 prq_bad_entries; /* which could not be translated to info */ + uint32 atim_suppress_count; /* TX suppressions on ATIM fifo */ + uint32 bcn_template_not_ready; /* Template marked in use on send bcn ... */ + uint32 bcn_template_not_ready_done; /* ...but "DMA done" interrupt rcvd */ + uint32 late_tbtt_dpc; /* TBTT DPC did not happen in time */ + + /* per-rate receive stat counters */ + uint32 rx1mbps; /* packets rx at 1Mbps */ + uint32 rx2mbps; /* packets rx at 2Mbps */ + uint32 rx5mbps5; /* packets rx at 5.5Mbps */ + uint32 rx6mbps; /* packets rx at 6Mbps */ + uint32 rx9mbps; /* packets rx at 9Mbps */ + uint32 rx11mbps; /* packets rx at 11Mbps */ + uint32 rx12mbps; /* packets rx at 12Mbps */ + uint32 rx18mbps; /* packets rx at 18Mbps */ + uint32 rx24mbps; /* packets rx at 24Mbps */ + uint32 rx36mbps; /* packets rx at 36Mbps */ + uint32 rx48mbps; /* packets rx at 48Mbps */ + uint32 rx54mbps; /* packets rx at 54Mbps */ + uint32 rx108mbps; /* packets rx at 108mbps */ + uint32 rx162mbps; /* packets rx at 162mbps */ + uint32 rx216mbps; /* packets rx at 216 mbps */ + uint32 rx270mbps; /* packets rx at 270 mbps */ + uint32 rx324mbps; /* packets rx at 324 mbps */ + uint32 rx378mbps; /* packets rx at 378 mbps */ + uint32 rx432mbps; /* packets rx at 432 mbps */ + uint32 rx486mbps; /* packets rx at 486 mbps */ + uint32 rx540mbps; /* packets rx at 540 mbps */ + + /* pkteng rx frame stats */ + uint32 pktengrxducast; /* unicast frames rxed by the pkteng code */ + uint32 pktengrxdmcast; /* multicast frames rxed by the pkteng code */ + + uint32 rfdisable; /* count of radio disables */ + uint32 bphy_rxcrsglitch; /* PHY count of bphy glitches */ + + uint32 txexptime; /* Tx frames suppressed due to timer expiration */ + + uint32 txmpdu_sgi; /* count for sgi transmit */ + uint32 rxmpdu_sgi; /* count for sgi received */ + uint32 txmpdu_stbc; /* count for stbc transmit */ + uint32 rxmpdu_stbc; /* count for stbc received */ + + uint32 rxundec_mcst; /* dot11WEPUndecryptableCount */ + + /* WPA2 counters (see rxundec for DecryptFailureCount) */ + uint32 tkipmicfaill_mcst; /* TKIPLocalMICFailures */ + uint32 tkipcntrmsr_mcst; /* TKIPCounterMeasuresInvoked */ + uint32 tkipreplay_mcst; /* TKIPReplays */ + uint32 ccmpfmterr_mcst; /* CCMPFormatErrors */ + uint32 ccmpreplay_mcst; /* CCMPReplays */ + uint32 ccmpundec_mcst; /* CCMPDecryptErrors */ + uint32 fourwayfail_mcst; /* FourWayHandshakeFailures */ + uint32 wepundec_mcst; /* dot11WEPUndecryptableCount */ + uint32 wepicverr_mcst; /* dot11WEPICVErrorCount */ + uint32 decsuccess_mcst; /* DecryptSuccessCount */ + uint32 tkipicverr_mcst; /* TKIPICVErrorCount */ + uint32 wepexcluded_mcst; /* dot11WEPExcludedCount */ + + uint32 dma_hang; /* count for dma hang */ + uint32 reinit; /* count for reinit */ + + uint32 pstatxucast; /* count of ucast frames xmitted on all psta assoc */ + uint32 pstatxnoassoc; /* count of txnoassoc frames xmitted on all psta assoc */ + uint32 pstarxucast; /* count of ucast frames received on all psta assoc */ + uint32 pstarxbcmc; /* count of bcmc frames received on all psta */ + uint32 pstatxbcmc; /* count of bcmc frames transmitted on all psta */ + + uint32 cso_passthrough; /* hw cso required but passthrough */ +} wl_cnt_t; + +#ifndef LINUX_POSTMOGRIFY_REMOVAL +typedef struct { + uint16 version; /* see definition of WL_CNT_T_VERSION */ + uint16 length; /* length of entire structure */ + + /* transmit stat counters */ + uint32 txframe; /* tx data frames */ + uint32 txbyte; /* tx data bytes */ + uint32 txretrans; /* tx mac retransmits */ + uint32 txerror; /* tx data errors (derived: sum of others) */ + uint32 txctl; /* tx management frames */ + uint32 txprshort; /* tx short preamble frames */ + uint32 txserr; /* tx status errors */ + uint32 txnobuf; /* tx out of buffers errors */ + uint32 txnoassoc; /* tx discard because we're not associated */ + uint32 txrunt; /* tx runt frames */ + uint32 txchit; /* tx header cache hit (fastpath) */ + uint32 txcmiss; /* tx header cache miss (slowpath) */ + + /* transmit chip error counters */ + uint32 txuflo; /* tx fifo underflows */ + uint32 txphyerr; /* tx phy errors (indicated in tx status) */ + uint32 txphycrs; + + /* receive stat counters */ + uint32 rxframe; /* rx data frames */ + uint32 rxbyte; /* rx data bytes */ + uint32 rxerror; /* rx data errors (derived: sum of others) */ + uint32 rxctl; /* rx management frames */ + uint32 rxnobuf; /* rx out of buffers errors */ + uint32 rxnondata; /* rx non data frames in the data channel errors */ + uint32 rxbadds; /* rx bad DS errors */ + uint32 rxbadcm; /* rx bad control or management frames */ + uint32 rxfragerr; /* rx fragmentation errors */ + uint32 rxrunt; /* rx runt frames */ + uint32 rxgiant; /* rx giant frames */ + uint32 rxnoscb; /* rx no scb error */ + uint32 rxbadproto; /* rx invalid frames */ + uint32 rxbadsrcmac; /* rx frames with Invalid Src Mac */ + uint32 rxbadda; /* rx frames tossed for invalid da */ + uint32 rxfilter; /* rx frames filtered out */ + + /* receive chip error counters */ + uint32 rxoflo; /* rx fifo overflow errors */ + uint32 rxuflo[NFIFO]; /* rx dma descriptor underflow errors */ + + uint32 d11cnt_txrts_off; /* d11cnt txrts value when reset d11cnt */ + uint32 d11cnt_rxcrc_off; /* d11cnt rxcrc value when reset d11cnt */ + uint32 d11cnt_txnocts_off; /* d11cnt txnocts value when reset d11cnt */ + + /* misc counters */ + uint32 dmade; /* tx/rx dma descriptor errors */ + uint32 dmada; /* tx/rx dma data errors */ + uint32 dmape; /* tx/rx dma descriptor protocol errors */ + uint32 reset; /* reset count */ + uint32 tbtt; /* cnts the TBTT int's */ + uint32 txdmawar; + uint32 pkt_callback_reg_fail; /* callbacks register failure */ + + /* MAC counters: 32-bit version of d11.h's macstat_t */ + uint32 txallfrm; /* total number of frames sent, incl. Data, ACK, RTS, CTS, + * Control Management (includes retransmissions) + */ + uint32 txrtsfrm; /* number of RTS sent out by the MAC */ + uint32 txctsfrm; /* number of CTS sent out by the MAC */ + uint32 txackfrm; /* number of ACK frames sent out */ + uint32 txdnlfrm; /* Not used */ + uint32 txbcnfrm; /* beacons transmitted */ + uint32 txfunfl[8]; /* per-fifo tx underflows */ + uint32 txtplunfl; /* Template underflows (mac was too slow to transmit ACK/CTS + * or BCN) + */ + uint32 txphyerror; /* Transmit phy error, type of error is reported in tx-status for + * driver enqueued frames + */ + uint32 rxfrmtoolong; /* Received frame longer than legal limit (2346 bytes) */ + uint32 rxfrmtooshrt; /* Received frame did not contain enough bytes for its frame type */ + uint32 rxinvmachdr; /* Either the protocol version != 0 or frame type not + * data/control/management + */ + uint32 rxbadfcs; /* number of frames for which the CRC check failed in the MAC */ + uint32 rxbadplcp; /* parity check of the PLCP header failed */ + uint32 rxcrsglitch; /* PHY was able to correlate the preamble but not the header */ + uint32 rxstrt; /* Number of received frames with a good PLCP + * (i.e. passing parity check) + */ + uint32 rxdfrmucastmbss; /* Number of received DATA frames with good FCS and matching RA */ + uint32 rxmfrmucastmbss; /* number of received mgmt frames with good FCS and matching RA */ + uint32 rxcfrmucast; /* number of received CNTRL frames with good FCS and matching RA */ + uint32 rxrtsucast; /* number of unicast RTS addressed to the MAC (good FCS) */ + uint32 rxctsucast; /* number of unicast CTS addressed to the MAC (good FCS) */ + uint32 rxackucast; /* number of ucast ACKS received (good FCS) */ + uint32 rxdfrmocast; /* number of received DATA frames (good FCS and not matching RA) */ + uint32 rxmfrmocast; /* number of received MGMT frames (good FCS and not matching RA) */ + uint32 rxcfrmocast; /* number of received CNTRL frame (good FCS and not matching RA) */ + uint32 rxrtsocast; /* number of received RTS not addressed to the MAC */ + uint32 rxctsocast; /* number of received CTS not addressed to the MAC */ + uint32 rxdfrmmcast; /* number of RX Data multicast frames received by the MAC */ + uint32 rxmfrmmcast; /* number of RX Management multicast frames received by the MAC */ + uint32 rxcfrmmcast; /* number of RX Control multicast frames received by the MAC + * (unlikely to see these) + */ + uint32 rxbeaconmbss; /* beacons received from member of BSS */ + uint32 rxdfrmucastobss; /* number of unicast frames addressed to the MAC from + * other BSS (WDS FRAME) + */ + uint32 rxbeaconobss; /* beacons received from other BSS */ + uint32 rxrsptmout; /* Number of response timeouts for transmitted frames + * expecting a response + */ + uint32 bcntxcancl; /* transmit beacons canceled due to receipt of beacon (IBSS) */ + uint32 rxf0ovfl; /* Number of receive fifo 0 overflows */ + uint32 rxf1ovfl; /* Number of receive fifo 1 overflows (obsolete) */ + uint32 rxf2ovfl; /* Number of receive fifo 2 overflows (obsolete) */ + uint32 txsfovfl; /* Number of transmit status fifo overflows (obsolete) */ + uint32 pmqovfl; /* Number of PMQ overflows */ + uint32 rxcgprqfrm; /* Number of received Probe requests that made it into + * the PRQ fifo + */ + uint32 rxcgprsqovfl; /* Rx Probe Request Que overflow in the AP */ + uint32 txcgprsfail; /* Tx Probe Response Fail. AP sent probe response but did + * not get ACK + */ + uint32 txcgprssuc; /* Tx Probe Response Success (ACK was received) */ + uint32 prs_timeout; /* Number of probe requests that were dropped from the PRQ + * fifo because a probe response could not be sent out within + * the time limit defined in M_PRS_MAXTIME + */ + uint32 rxnack; + uint32 frmscons; + uint32 txnack; + uint32 txglitch_nack; /* obsolete */ + uint32 txburst; /* obsolete */ + + /* 802.11 MIB counters, pp. 614 of 802.11 reaff doc. */ + uint32 txfrag; /* dot11TransmittedFragmentCount */ + uint32 txmulti; /* dot11MulticastTransmittedFrameCount */ + uint32 txfail; /* dot11FailedCount */ + uint32 txretry; /* dot11RetryCount */ + uint32 txretrie; /* dot11MultipleRetryCount */ + uint32 rxdup; /* dot11FrameduplicateCount */ + uint32 txrts; /* dot11RTSSuccessCount */ + uint32 txnocts; /* dot11RTSFailureCount */ + uint32 txnoack; /* dot11ACKFailureCount */ + uint32 rxfrag; /* dot11ReceivedFragmentCount */ + uint32 rxmulti; /* dot11MulticastReceivedFrameCount */ + uint32 rxcrc; /* dot11FCSErrorCount */ + uint32 txfrmsnt; /* dot11TransmittedFrameCount (bogus MIB?) */ + uint32 rxundec; /* dot11WEPUndecryptableCount */ + + /* WPA2 counters (see rxundec for DecryptFailureCount) */ + uint32 tkipmicfaill; /* TKIPLocalMICFailures */ + uint32 tkipcntrmsr; /* TKIPCounterMeasuresInvoked */ + uint32 tkipreplay; /* TKIPReplays */ + uint32 ccmpfmterr; /* CCMPFormatErrors */ + uint32 ccmpreplay; /* CCMPReplays */ + uint32 ccmpundec; /* CCMPDecryptErrors */ + uint32 fourwayfail; /* FourWayHandshakeFailures */ + uint32 wepundec; /* dot11WEPUndecryptableCount */ + uint32 wepicverr; /* dot11WEPICVErrorCount */ + uint32 decsuccess; /* DecryptSuccessCount */ + uint32 tkipicverr; /* TKIPICVErrorCount */ + uint32 wepexcluded; /* dot11WEPExcludedCount */ + + uint32 rxundec_mcst; /* dot11WEPUndecryptableCount */ + + /* WPA2 counters (see rxundec for DecryptFailureCount) */ + uint32 tkipmicfaill_mcst; /* TKIPLocalMICFailures */ + uint32 tkipcntrmsr_mcst; /* TKIPCounterMeasuresInvoked */ + uint32 tkipreplay_mcst; /* TKIPReplays */ + uint32 ccmpfmterr_mcst; /* CCMPFormatErrors */ + uint32 ccmpreplay_mcst; /* CCMPReplays */ + uint32 ccmpundec_mcst; /* CCMPDecryptErrors */ + uint32 fourwayfail_mcst; /* FourWayHandshakeFailures */ + uint32 wepundec_mcst; /* dot11WEPUndecryptableCount */ + uint32 wepicverr_mcst; /* dot11WEPICVErrorCount */ + uint32 decsuccess_mcst; /* DecryptSuccessCount */ + uint32 tkipicverr_mcst; /* TKIPICVErrorCount */ + uint32 wepexcluded_mcst; /* dot11WEPExcludedCount */ + + uint32 txchanrej; /* Tx frames suppressed due to channel rejection */ + uint32 txexptime; /* Tx frames suppressed due to timer expiration */ + uint32 psmwds; /* Count PSM watchdogs */ + uint32 phywatchdog; /* Count Phy watchdogs (triggered by ucode) */ + + /* MBSS counters, AP only */ + uint32 prq_entries_handled; /* PRQ entries read in */ + uint32 prq_undirected_entries; /* which were bcast bss & ssid */ + uint32 prq_bad_entries; /* which could not be translated to info */ + uint32 atim_suppress_count; /* TX suppressions on ATIM fifo */ + uint32 bcn_template_not_ready; /* Template marked in use on send bcn ... */ + uint32 bcn_template_not_ready_done; /* ...but "DMA done" interrupt rcvd */ + uint32 late_tbtt_dpc; /* TBTT DPC did not happen in time */ + + /* per-rate receive stat counters */ + uint32 rx1mbps; /* packets rx at 1Mbps */ + uint32 rx2mbps; /* packets rx at 2Mbps */ + uint32 rx5mbps5; /* packets rx at 5.5Mbps */ + uint32 rx6mbps; /* packets rx at 6Mbps */ + uint32 rx9mbps; /* packets rx at 9Mbps */ + uint32 rx11mbps; /* packets rx at 11Mbps */ + uint32 rx12mbps; /* packets rx at 12Mbps */ + uint32 rx18mbps; /* packets rx at 18Mbps */ + uint32 rx24mbps; /* packets rx at 24Mbps */ + uint32 rx36mbps; /* packets rx at 36Mbps */ + uint32 rx48mbps; /* packets rx at 48Mbps */ + uint32 rx54mbps; /* packets rx at 54Mbps */ + uint32 rx108mbps; /* packets rx at 108mbps */ + uint32 rx162mbps; /* packets rx at 162mbps */ + uint32 rx216mbps; /* packets rx at 216 mbps */ + uint32 rx270mbps; /* packets rx at 270 mbps */ + uint32 rx324mbps; /* packets rx at 324 mbps */ + uint32 rx378mbps; /* packets rx at 378 mbps */ + uint32 rx432mbps; /* packets rx at 432 mbps */ + uint32 rx486mbps; /* packets rx at 486 mbps */ + uint32 rx540mbps; /* packets rx at 540 mbps */ + + /* pkteng rx frame stats */ + uint32 pktengrxducast; /* unicast frames rxed by the pkteng code */ + uint32 pktengrxdmcast; /* multicast frames rxed by the pkteng code */ + + uint32 rfdisable; /* count of radio disables */ + uint32 bphy_rxcrsglitch; /* PHY count of bphy glitches */ + + uint32 txmpdu_sgi; /* count for sgi transmit */ + uint32 rxmpdu_sgi; /* count for sgi received */ + uint32 txmpdu_stbc; /* count for stbc transmit */ + uint32 rxmpdu_stbc; /* count for stbc received */ +} wl_cnt_ver_six_t; + +#define WL_DELTA_STATS_T_VERSION 1 /* current version of wl_delta_stats_t struct */ + +typedef struct { + uint16 version; /* see definition of WL_DELTA_STATS_T_VERSION */ + uint16 length; /* length of entire structure */ + + /* transmit stat counters */ + uint32 txframe; /* tx data frames */ + uint32 txbyte; /* tx data bytes */ + uint32 txretrans; /* tx mac retransmits */ + uint32 txfail; /* tx failures */ + + /* receive stat counters */ + uint32 rxframe; /* rx data frames */ + uint32 rxbyte; /* rx data bytes */ + + /* per-rate receive stat counters */ + uint32 rx1mbps; /* packets rx at 1Mbps */ + uint32 rx2mbps; /* packets rx at 2Mbps */ + uint32 rx5mbps5; /* packets rx at 5.5Mbps */ + uint32 rx6mbps; /* packets rx at 6Mbps */ + uint32 rx9mbps; /* packets rx at 9Mbps */ + uint32 rx11mbps; /* packets rx at 11Mbps */ + uint32 rx12mbps; /* packets rx at 12Mbps */ + uint32 rx18mbps; /* packets rx at 18Mbps */ + uint32 rx24mbps; /* packets rx at 24Mbps */ + uint32 rx36mbps; /* packets rx at 36Mbps */ + uint32 rx48mbps; /* packets rx at 48Mbps */ + uint32 rx54mbps; /* packets rx at 54Mbps */ + uint32 rx108mbps; /* packets rx at 108mbps */ + uint32 rx162mbps; /* packets rx at 162mbps */ + uint32 rx216mbps; /* packets rx at 216 mbps */ + uint32 rx270mbps; /* packets rx at 270 mbps */ + uint32 rx324mbps; /* packets rx at 324 mbps */ + uint32 rx378mbps; /* packets rx at 378 mbps */ + uint32 rx432mbps; /* packets rx at 432 mbps */ + uint32 rx486mbps; /* packets rx at 486 mbps */ + uint32 rx540mbps; /* packets rx at 540 mbps */ +} wl_delta_stats_t; +#endif /* LINUX_POSTMOGRIFY_REMOVAL */ + +#define WL_WME_CNT_VERSION 1 /* current version of wl_wme_cnt_t */ + +typedef struct { + uint32 packets; + uint32 bytes; +} wl_traffic_stats_t; + +typedef struct { + uint16 version; /* see definition of WL_WME_CNT_VERSION */ + uint16 length; /* length of entire structure */ + + wl_traffic_stats_t tx[AC_COUNT]; /* Packets transmitted */ + wl_traffic_stats_t tx_failed[AC_COUNT]; /* Packets dropped or failed to transmit */ + wl_traffic_stats_t rx[AC_COUNT]; /* Packets received */ + wl_traffic_stats_t rx_failed[AC_COUNT]; /* Packets failed to receive */ + + wl_traffic_stats_t forward[AC_COUNT]; /* Packets forwarded by AP */ + + wl_traffic_stats_t tx_expired[AC_COUNT]; /* packets dropped due to lifetime expiry */ + +} wl_wme_cnt_t; + +#ifndef LINUX_POSTMOGRIFY_REMOVAL +struct wl_msglevel2 { + uint32 low; + uint32 high; +}; + +typedef struct wl_mkeep_alive_pkt { + uint16 version; /* Version for mkeep_alive */ + uint16 length; /* length of fixed parameters in the structure */ + uint32 period_msec; + uint16 len_bytes; + uint8 keep_alive_id; /* 0 - 3 for N = 4 */ + uint8 data[1]; +} wl_mkeep_alive_pkt_t; + +#define WL_MKEEP_ALIVE_VERSION 1 +#define WL_MKEEP_ALIVE_FIXED_LEN OFFSETOF(wl_mkeep_alive_pkt_t, data) +#define WL_MKEEP_ALIVE_PRECISION 500 + +#ifdef WLBA + +#define WLC_BA_CNT_VERSION 1 /* current version of wlc_ba_cnt_t */ + +/* block ack related stats */ +typedef struct wlc_ba_cnt { + uint16 version; /* WLC_BA_CNT_VERSION */ + uint16 length; /* length of entire structure */ + + /* transmit stat counters */ + uint32 txpdu; /* pdus sent */ + uint32 txsdu; /* sdus sent */ + uint32 txfc; /* tx side flow controlled packets */ + uint32 txfci; /* tx side flow control initiated */ + uint32 txretrans; /* retransmitted pdus */ + uint32 txbatimer; /* ba resend due to timer */ + uint32 txdrop; /* dropped packets */ + uint32 txaddbareq; /* addba req sent */ + uint32 txaddbaresp; /* addba resp sent */ + uint32 txdelba; /* delba sent */ + uint32 txba; /* ba sent */ + uint32 txbar; /* bar sent */ + uint32 txpad[4]; /* future */ + + /* receive side counters */ + uint32 rxpdu; /* pdus recd */ + uint32 rxqed; /* pdus buffered before sending up */ + uint32 rxdup; /* duplicate pdus */ + uint32 rxnobuf; /* pdus discarded due to no buf */ + uint32 rxaddbareq; /* addba req recd */ + uint32 rxaddbaresp; /* addba resp recd */ + uint32 rxdelba; /* delba recd */ + uint32 rxba; /* ba recd */ + uint32 rxbar; /* bar recd */ + uint32 rxinvba; /* invalid ba recd */ + uint32 rxbaholes; /* ba recd with holes */ + uint32 rxunexp; /* unexpected packets */ + uint32 rxpad[4]; /* future */ +} wlc_ba_cnt_t; +#endif /* WLBA */ + +/* structure for per-tid ampdu control */ +struct ampdu_tid_control { + uint8 tid; /* tid */ + uint8 enable; /* enable/disable */ +}; + +/* structure for identifying ea/tid for sending addba/delba */ +struct ampdu_ea_tid { + struct ether_addr ea; /* Station address */ + uint8 tid; /* tid */ +}; +/* structure for identifying retry/tid for retry_limit_tid/rr_retry_limit_tid */ +struct ampdu_retry_tid { + uint8 tid; /* tid */ + uint8 retry; /* retry value */ +}; + +/* Different discovery modes for dpt */ +#define DPT_DISCOVERY_MANUAL 0x01 /* manual discovery mode */ +#define DPT_DISCOVERY_AUTO 0x02 /* auto discovery mode */ +#define DPT_DISCOVERY_SCAN 0x04 /* scan-based discovery mode */ + +/* different path selection values */ +#define DPT_PATHSEL_AUTO 0 /* auto mode for path selection */ +#define DPT_PATHSEL_DIRECT 1 /* always use direct DPT path */ +#define DPT_PATHSEL_APPATH 2 /* always use AP path */ + +/* different ops for deny list */ +#define DPT_DENY_LIST_ADD 1 /* add to dpt deny list */ +#define DPT_DENY_LIST_REMOVE 2 /* remove from dpt deny list */ + +/* different ops for manual end point */ +#define DPT_MANUAL_EP_CREATE 1 /* create manual dpt endpoint */ +#define DPT_MANUAL_EP_MODIFY 2 /* modify manual dpt endpoint */ +#define DPT_MANUAL_EP_DELETE 3 /* delete manual dpt endpoint */ + +/* structure for dpt iovars */ +typedef struct dpt_iovar { + struct ether_addr ea; /* Station address */ + uint8 mode; /* mode: depends on iovar */ + uint32 pad; /* future */ +} dpt_iovar_t; + +/* flags to indicate DPT status */ +#define DPT_STATUS_ACTIVE 0x01 /* link active (though may be suspended) */ +#define DPT_STATUS_AES 0x02 /* link secured through AES encryption */ +#define DPT_STATUS_FAILED 0x04 /* DPT link failed */ + +#define DPT_FNAME_LEN 48 /* Max length of friendly name */ + +typedef struct dpt_status { + uint8 status; /* flags to indicate status */ + uint8 fnlen; /* length of friendly name */ + uchar name[DPT_FNAME_LEN]; /* friendly name */ + uint32 rssi; /* RSSI of the link */ + sta_info_t sta; /* sta info */ +} dpt_status_t; + +/* structure for dpt list */ +typedef struct dpt_list { + uint32 num; /* number of entries in struct */ + dpt_status_t status[1]; /* per station info */ +} dpt_list_t; + +/* structure for dpt friendly name */ +typedef struct dpt_fname { + uint8 len; /* length of friendly name */ + uchar name[DPT_FNAME_LEN]; /* friendly name */ +} dpt_fname_t; + +#define BDD_FNAME_LEN 32 /* Max length of friendly name */ +typedef struct bdd_fname { + uint8 len; /* length of friendly name */ + uchar name[BDD_FNAME_LEN]; /* friendly name */ +} bdd_fname_t; + +/* structure for addts arguments */ +/* For ioctls that take a list of TSPEC */ +struct tslist { + int count; /* number of tspecs */ + struct tsinfo_arg tsinfo[1]; /* variable length array of tsinfo */ +}; + +#ifdef WLTDLS +/* different ops for manual end point */ +#define TDLS_MANUAL_EP_CREATE 1 /* create manual dpt endpoint */ +#define TDLS_MANUAL_EP_MODIFY 2 /* modify manual dpt endpoint */ +#define TDLS_MANUAL_EP_DELETE 3 /* delete manual dpt endpoint */ +#define TDLS_MANUAL_EP_PM 4 /* put dpt endpoint in PM mode */ +#define TDLS_MANUAL_EP_WAKE 5 /* wake up dpt endpoint from PM */ +#define TDLS_MANUAL_EP_DISCOVERY 6 /* discover if endpoint is TDLS capable */ +#define TDLS_MANUAL_EP_CHSW 7 /* channel switch */ + +/* structure for tdls iovars */ +typedef struct tdls_iovar { + struct ether_addr ea; /* Station address */ + uint8 mode; /* mode: depends on iovar */ + chanspec_t chanspec; + uint32 pad; /* future */ +} tdls_iovar_t; + +/* modes */ +#define TDLS_WFD_IE_TX 0 +#define TDLS_WFD_IE_RX 1 +#define TDLS_WFD_IE_SIZE 255 +/* structure for tdls wfd ie */ +typedef struct tdls_wfd_ie_iovar { + struct ether_addr ea; /* Station address */ + uint8 mode; + uint8 length; + uint8 data[TDLS_WFD_IE_SIZE]; +} tdls_wfd_ie_iovar_t; +#endif /* WLTDLS */ + +/* structure for addts/delts arguments */ +typedef struct tspec_arg { + uint16 version; /* see definition of TSPEC_ARG_VERSION */ + uint16 length; /* length of entire structure */ + uint flag; /* bit field */ + /* TSPEC Arguments */ + struct tsinfo_arg tsinfo; /* TS Info bit field */ + uint16 nom_msdu_size; /* (Nominal or fixed) MSDU Size (bytes) */ + uint16 max_msdu_size; /* Maximum MSDU Size (bytes) */ + uint min_srv_interval; /* Minimum Service Interval (us) */ + uint max_srv_interval; /* Maximum Service Interval (us) */ + uint inactivity_interval; /* Inactivity Interval (us) */ + uint suspension_interval; /* Suspension Interval (us) */ + uint srv_start_time; /* Service Start Time (us) */ + uint min_data_rate; /* Minimum Data Rate (bps) */ + uint mean_data_rate; /* Mean Data Rate (bps) */ + uint peak_data_rate; /* Peak Data Rate (bps) */ + uint max_burst_size; /* Maximum Burst Size (bytes) */ + uint delay_bound; /* Delay Bound (us) */ + uint min_phy_rate; /* Minimum PHY Rate (bps) */ + uint16 surplus_bw; /* Surplus Bandwidth Allowance (range 1.0 to 8.0) */ + uint16 medium_time; /* Medium Time (32 us/s periods) */ + uint8 dialog_token; /* dialog token */ +} tspec_arg_t; + +/* tspec arg for desired station */ +typedef struct tspec_per_sta_arg { + struct ether_addr ea; + struct tspec_arg ts; +} tspec_per_sta_arg_t; + +/* structure for max bandwidth for each access category */ +typedef struct wme_max_bandwidth { + uint32 ac[AC_COUNT]; /* max bandwidth for each access category */ +} wme_max_bandwidth_t; + +#define WL_WME_MBW_PARAMS_IO_BYTES (sizeof(wme_max_bandwidth_t)) + +/* current version of wl_tspec_arg_t struct */ +#define TSPEC_ARG_VERSION 2 /* current version of wl_tspec_arg_t struct */ +#define TSPEC_ARG_LENGTH 55 /* argument length from tsinfo to medium_time */ +#define TSPEC_DEFAULT_DIALOG_TOKEN 42 /* default dialog token */ +#define TSPEC_DEFAULT_SBW_FACTOR 0x3000 /* default surplus bw */ + + +#define WL_WOWL_KEEPALIVE_MAX_PACKET_SIZE 80 +#define WLC_WOWL_MAX_KEEPALIVE 2 + +/* define for flag */ +#define TSPEC_PENDING 0 /* TSPEC pending */ +#define TSPEC_ACCEPTED 1 /* TSPEC accepted */ +#define TSPEC_REJECTED 2 /* TSPEC rejected */ +#define TSPEC_UNKNOWN 3 /* TSPEC unknown */ +#define TSPEC_STATUS_MASK 7 /* TSPEC status mask */ + + +/* Software feature flag defines used by wlfeatureflag */ +#ifdef WLAFTERBURNER +#define WL_SWFL_ABBFL 0x0001 /* Allow Afterburner on systems w/o hardware BFL */ +#define WL_SWFL_ABENCORE 0x0002 /* Allow AB on non-4318E chips */ +#endif /* WLAFTERBURNER */ +#define WL_SWFL_NOHWRADIO 0x0004 +#define WL_SWFL_FLOWCONTROL 0x0008 /* Enable backpressure to OS stack */ +#define WL_SWFL_WLBSSSORT 0x0010 /* Per-port supports sorting of BSS */ + +#define WL_LIFETIME_MAX 0xFFFF /* Max value in ms */ + +/* Packet lifetime configuration per ac */ +typedef struct wl_lifetime { + uint32 ac; /* access class */ + uint32 lifetime; /* Packet lifetime value in ms */ +} wl_lifetime_t; + +/* Channel Switch Announcement param */ +typedef struct wl_chan_switch { + uint8 mode; /* value 0 or 1 */ + uint8 count; /* count # of beacons before switching */ + chanspec_t chspec; /* chanspec */ + uint8 reg; /* regulatory class */ +} wl_chan_switch_t; + +/* Roaming trigger definitions for WLC_SET_ROAM_TRIGGER. + * + * (-100 < value < 0) value is used directly as a roaming trigger in dBm + * (0 <= value) value specifies a logical roaming trigger level from + * the list below + * + * WLC_GET_ROAM_TRIGGER always returns roaming trigger value in dBm, never + * the logical roam trigger value. + */ +#define WLC_ROAM_TRIGGER_DEFAULT 0 /* default roaming trigger */ +#define WLC_ROAM_TRIGGER_BANDWIDTH 1 /* optimize for bandwidth roaming trigger */ +#define WLC_ROAM_TRIGGER_DISTANCE 2 /* optimize for distance roaming trigger */ +#define WLC_ROAM_TRIGGER_AUTO 3 /* auto-detect environment */ +#define WLC_ROAM_TRIGGER_MAX_VALUE 3 /* max. valid value */ + +#define WLC_ROAM_NEVER_ROAM_TRIGGER (-100) /* Avoid Roaming by setting a large value */ + +/* Preferred Network Offload (PNO, formerly PFN) defines */ +#define WPA_AUTH_PFN_ANY 0xffffffff /* for PFN, match only ssid */ + +enum { + PFN_LIST_ORDER, + PFN_RSSI +}; + +enum { + DISABLE, + ENABLE +}; + +enum { + OFF_ADAPT, + SMART_ADAPT, + STRICT_ADAPT, + SLOW_ADAPT +}; + +#define SORT_CRITERIA_BIT 0 +#define AUTO_NET_SWITCH_BIT 1 +#define ENABLE_BKGRD_SCAN_BIT 2 +#define IMMEDIATE_SCAN_BIT 3 +#define AUTO_CONNECT_BIT 4 +#define ENABLE_BD_SCAN_BIT 5 +#define ENABLE_ADAPTSCAN_BIT 6 +#define IMMEDIATE_EVENT_BIT 8 +#define SUPPRESS_SSID_BIT 9 +#define ENABLE_NET_OFFLOAD_BIT 10 + +#define SORT_CRITERIA_MASK 0x0001 +#define AUTO_NET_SWITCH_MASK 0x0002 +#define ENABLE_BKGRD_SCAN_MASK 0x0004 +#define IMMEDIATE_SCAN_MASK 0x0008 +#define AUTO_CONNECT_MASK 0x0010 + +#define ENABLE_BD_SCAN_MASK 0x0020 +#define ENABLE_ADAPTSCAN_MASK 0x00c0 +#define IMMEDIATE_EVENT_MASK 0x0100 +#define SUPPRESS_SSID_MASK 0x0200 +#define ENABLE_NET_OFFLOAD_MASK 0x0400 + +#define PFN_VERSION 2 +#define PFN_SCANRESULT_VERSION 1 +#define MAX_PFN_LIST_COUNT 16 + +#define PFN_COMPLETE 1 +#define PFN_INCOMPLETE 0 + +#define DEFAULT_BESTN 2 +#define DEFAULT_MSCAN 0 +#define DEFAULT_REPEAT 10 +#define DEFAULT_EXP 2 + +/* PFN network info structure */ +typedef struct wl_pfn_subnet_info { + struct ether_addr BSSID; + uint8 channel; /* channel number only */ + uint8 SSID_len; + uint8 SSID[32]; +} wl_pfn_subnet_info_t; + +typedef struct wl_pfn_net_info { + wl_pfn_subnet_info_t pfnsubnet; + int16 RSSI; /* receive signal strength (in dBm) */ + uint16 timestamp; /* age in seconds */ +} wl_pfn_net_info_t; + +typedef struct wl_pfn_scanresults { + uint32 version; + uint32 status; + uint32 count; + wl_pfn_net_info_t netinfo[1]; +} wl_pfn_scanresults_t; + +/* PFN data structure */ +typedef struct wl_pfn_param { + int32 version; /* PNO parameters version */ + int32 scan_freq; /* Scan frequency */ + int32 lost_network_timeout; /* Timeout in sec. to declare + * discovered network as lost + */ + int16 flags; /* Bit field to control features + * of PFN such as sort criteria auto + * enable switch and background scan + */ + int16 rssi_margin; /* Margin to avoid jitter for choosing a + * PFN based on RSSI sort criteria + */ + uint8 bestn; /* number of best networks in each scan */ + uint8 mscan; /* number of scans recorded */ + uint8 repeat; /* Minimum number of scan intervals + *before scan frequency changes in adaptive scan + */ + uint8 exp; /* Exponent of 2 for maximum scan interval */ + int32 slow_freq; /* slow scan period */ +} wl_pfn_param_t; + +typedef struct wl_pfn_bssid { + struct ether_addr macaddr; + /* Bit4: suppress_lost, Bit3: suppress_found */ + uint16 flags; +} wl_pfn_bssid_t; +#define WL_PFN_SUPPRESSFOUND_MASK 0x08 +#define WL_PFN_SUPPRESSLOST_MASK 0x10 + +typedef struct wl_pfn_cfg { + uint32 reporttype; + int32 channel_num; + uint16 channel_list[WL_NUMCHANNELS]; +} wl_pfn_cfg_t; +#define WL_PFN_REPORT_ALLNET 0 +#define WL_PFN_REPORT_SSIDNET 1 +#define WL_PFN_REPORT_BSSIDNET 2 + +typedef struct wl_pfn { + wlc_ssid_t ssid; /* ssid name and its length */ + int32 flags; /* bit2: hidden */ + int32 infra; /* BSS Vs IBSS */ + int32 auth; /* Open Vs Closed */ + int32 wpa_auth; /* WPA type */ + int32 wsec; /* wsec value */ +} wl_pfn_t; +#define WL_PFN_HIDDEN_BIT 2 +#define PNO_SCAN_MAX_FW 508*1000 /* max time scan time in msec */ +#define PNO_SCAN_MAX_FW_SEC PNO_SCAN_MAX_FW/1000 /* max time scan time in SEC */ +#define PNO_SCAN_MIN_FW_SEC 10 /* min time scan time in SEC */ +#define WL_PFN_HIDDEN_MASK 0x4 + +#endif /* LINUX_POSTMOGRIFY_REMOVAL */ + +/* TCP Checksum Offload defines */ +#define TOE_TX_CSUM_OL 0x00000001 +#define TOE_RX_CSUM_OL 0x00000002 + +#ifndef LINUX_POSTMOGRIFY_REMOVAL +/* TCP Checksum Offload error injection for testing */ +#define TOE_ERRTEST_TX_CSUM 0x00000001 +#define TOE_ERRTEST_RX_CSUM 0x00000002 +#define TOE_ERRTEST_RX_CSUM2 0x00000004 + +struct toe_ol_stats_t { + /* Num of tx packets that don't need to be checksummed */ + uint32 tx_summed; + + /* Num of tx packets where checksum is filled by offload engine */ + uint32 tx_iph_fill; + uint32 tx_tcp_fill; + uint32 tx_udp_fill; + uint32 tx_icmp_fill; + + /* Num of rx packets where toe finds out if checksum is good or bad */ + uint32 rx_iph_good; + uint32 rx_iph_bad; + uint32 rx_tcp_good; + uint32 rx_tcp_bad; + uint32 rx_udp_good; + uint32 rx_udp_bad; + uint32 rx_icmp_good; + uint32 rx_icmp_bad; + + /* Num of tx packets in which csum error is injected */ + uint32 tx_tcp_errinj; + uint32 tx_udp_errinj; + uint32 tx_icmp_errinj; + + /* Num of rx packets in which csum error is injected */ + uint32 rx_tcp_errinj; + uint32 rx_udp_errinj; + uint32 rx_icmp_errinj; +}; + +/* ARP Offload feature flags for arp_ol iovar */ +#define ARP_OL_AGENT 0x00000001 +#define ARP_OL_SNOOP 0x00000002 +#define ARP_OL_HOST_AUTO_REPLY 0x00000004 +#define ARP_OL_PEER_AUTO_REPLY 0x00000008 + +/* ARP Offload error injection */ +#define ARP_ERRTEST_REPLY_PEER 0x1 +#define ARP_ERRTEST_REPLY_HOST 0x2 + +#define ARP_MULTIHOMING_MAX 8 /* Maximum local host IP addresses */ +#define ND_MULTIHOMING_MAX 8 /* Maximum local host IP addresses */ + +/* Arp offload statistic counts */ +struct arp_ol_stats_t { + uint32 host_ip_entries; /* Host IP table addresses (more than one if multihomed) */ + uint32 host_ip_overflow; /* Host IP table additions skipped due to overflow */ + + uint32 arp_table_entries; /* ARP table entries */ + uint32 arp_table_overflow; /* ARP table additions skipped due to overflow */ + + uint32 host_request; /* ARP requests from host */ + uint32 host_reply; /* ARP replies from host */ + uint32 host_service; /* ARP requests from host serviced by ARP Agent */ + + uint32 peer_request; /* ARP requests received from network */ + uint32 peer_request_drop; /* ARP requests from network that were dropped */ + uint32 peer_reply; /* ARP replies received from network */ + uint32 peer_reply_drop; /* ARP replies from network that were dropped */ + uint32 peer_service; /* ARP request from host serviced by ARP Agent */ +}; + +/* NS offload statistic counts */ +struct nd_ol_stats_t { + uint32 host_ip_entries; /* Host IP table addresses (more than one if multihomed) */ + uint32 host_ip_overflow; /* Host IP table additions skipped due to overflow */ + uint32 peer_request; /* NS requests received from network */ + uint32 peer_request_drop; /* NS requests from network that were dropped */ + uint32 peer_reply_drop; /* NA replies from network that were dropped */ + uint32 peer_service; /* NS request from host serviced by firmware */ +}; + +/* + * Keep-alive packet offloading. + */ + +/* NAT keep-alive packets format: specifies the re-transmission period, the packet + * length, and packet contents. + */ +typedef struct wl_keep_alive_pkt { + uint32 period_msec; /* Retransmission period (0 to disable packet re-transmits) */ + uint16 len_bytes; /* Size of packet to transmit (0 to disable packet re-transmits) */ + uint8 data[1]; /* Variable length packet to transmit. Contents should include + * entire ethernet packet (enet header, IP header, UDP header, + * and UDP payload) in network byte order. + */ +} wl_keep_alive_pkt_t; + +#define WL_KEEP_ALIVE_FIXED_LEN OFFSETOF(wl_keep_alive_pkt_t, data) + +/* + * Dongle pattern matching filter. + */ + +/* Packet filter types. Currently, only pattern matching is supported. */ +typedef enum wl_pkt_filter_type { + WL_PKT_FILTER_TYPE_PATTERN_MATCH /* Pattern matching filter */ +} wl_pkt_filter_type_t; + +#define WL_PKT_FILTER_TYPE wl_pkt_filter_type_t + +/* Pattern matching filter. Specifies an offset within received packets to + * start matching, the pattern to match, the size of the pattern, and a bitmask + * that indicates which bits within the pattern should be matched. + */ +typedef struct wl_pkt_filter_pattern { + uint32 offset; /* Offset within received packet to start pattern matching. + * Offset '0' is the first byte of the ethernet header. + */ + uint32 size_bytes; /* Size of the pattern. Bitmask must be the same size. */ + uint8 mask_and_pattern[1]; /* Variable length mask and pattern data. mask starts + * at offset 0. Pattern immediately follows mask. + */ +} wl_pkt_filter_pattern_t; + +/* IOVAR "pkt_filter_add" parameter. Used to install packet filters. */ +typedef struct wl_pkt_filter { + uint32 id; /* Unique filter id, specified by app. */ + uint32 type; /* Filter type (WL_PKT_FILTER_TYPE_xxx). */ + uint32 negate_match; /* Negate the result of filter matches */ + union { /* Filter definitions */ + wl_pkt_filter_pattern_t pattern; /* Pattern matching filter */ + } u; +} wl_pkt_filter_t; + +#define WL_PKT_FILTER_FIXED_LEN OFFSETOF(wl_pkt_filter_t, u) +#define WL_PKT_FILTER_PATTERN_FIXED_LEN OFFSETOF(wl_pkt_filter_pattern_t, mask_and_pattern) + +/* IOVAR "pkt_filter_enable" parameter. */ +typedef struct wl_pkt_filter_enable { + uint32 id; /* Unique filter id */ + uint32 enable; /* Enable/disable bool */ +} wl_pkt_filter_enable_t; + +/* IOVAR "pkt_filter_list" parameter. Used to retrieve a list of installed filters. */ +typedef struct wl_pkt_filter_list { + uint32 num; /* Number of installed packet filters */ + wl_pkt_filter_t filter[1]; /* Variable array of packet filters. */ +} wl_pkt_filter_list_t; + +#define WL_PKT_FILTER_LIST_FIXED_LEN OFFSETOF(wl_pkt_filter_list_t, filter) + +/* IOVAR "pkt_filter_stats" parameter. Used to retrieve debug statistics. */ +typedef struct wl_pkt_filter_stats { + uint32 num_pkts_matched; /* # filter matches for specified filter id */ + uint32 num_pkts_forwarded; /* # packets fwded from dongle to host for all filters */ + uint32 num_pkts_discarded; /* # packets discarded by dongle for all filters */ +} wl_pkt_filter_stats_t; + +/* Sequential Commands ioctl */ +typedef struct wl_seq_cmd_ioctl { + uint32 cmd; /* common ioctl definition */ + uint32 len; /* length of user buffer */ +} wl_seq_cmd_ioctl_t; + +#define WL_SEQ_CMD_ALIGN_BYTES 4 + +/* These are the set of get IOCTLs that should be allowed when using + * IOCTL sequence commands. These are issued implicitly by wl.exe each time + * it is invoked. We never want to buffer these, or else wl.exe will stop working. + */ +#define WL_SEQ_CMDS_GET_IOCTL_FILTER(cmd) \ + (((cmd) == WLC_GET_MAGIC) || \ + ((cmd) == WLC_GET_VERSION) || \ + ((cmd) == WLC_GET_AP) || \ + ((cmd) == WLC_GET_INSTANCE)) + +/* + * Packet engine interface + */ + +#define WL_PKTENG_PER_TX_START 0x01 +#define WL_PKTENG_PER_TX_STOP 0x02 +#define WL_PKTENG_PER_RX_START 0x04 +#define WL_PKTENG_PER_RX_WITH_ACK_START 0x05 +#define WL_PKTENG_PER_TX_WITH_ACK_START 0x06 +#define WL_PKTENG_PER_RX_STOP 0x08 +#define WL_PKTENG_PER_MASK 0xff + +#define WL_PKTENG_SYNCHRONOUS 0x100 /* synchronous flag */ + +typedef struct wl_pkteng { + uint32 flags; + uint32 delay; /* Inter-packet delay */ + uint32 nframes; /* Number of frames */ + uint32 length; /* Packet length */ + uint8 seqno; /* Enable/disable sequence no. */ + struct ether_addr dest; /* Destination address */ + struct ether_addr src; /* Source address */ +} wl_pkteng_t; + +#define NUM_80211b_RATES 4 +#define NUM_80211ag_RATES 8 +#define NUM_80211n_RATES 32 +#define NUM_80211_RATES (NUM_80211b_RATES+NUM_80211ag_RATES+NUM_80211n_RATES) +typedef struct wl_pkteng_stats { + uint32 lostfrmcnt; /* RX PER test: no of frames lost (skip seqno) */ + int32 rssi; /* RSSI */ + int32 snr; /* signal to noise ratio */ + uint16 rxpktcnt[NUM_80211_RATES+1]; +} wl_pkteng_stats_t; + + +#define WL_WOWL_MAGIC (1 << 0) /* Wakeup on Magic packet */ +#define WL_WOWL_NET (1 << 1) /* Wakeup on Netpattern */ +#define WL_WOWL_DIS (1 << 2) /* Wakeup on loss-of-link due to Disassoc/Deauth */ +#define WL_WOWL_RETR (1 << 3) /* Wakeup on retrograde TSF */ +#define WL_WOWL_BCN (1 << 4) /* Wakeup on loss of beacon */ +#define WL_WOWL_TST (1 << 5) /* Wakeup after test */ +#define WL_WOWL_M1 (1 << 6) /* Wakeup after PTK refresh */ +#define WL_WOWL_EAPID (1 << 7) /* Wakeup after receipt of EAP-Identity Req */ +#define WL_WOWL_PME_GPIO (1 << 8) /* Wakeind via PME(0) or GPIO(1) */ +#define WL_WOWL_NEEDTKIP1 (1 << 9) /* need tkip phase 1 key to be updated by the driver */ +#define WL_WOWL_GTK_FAILURE (1 << 10) /* enable wakeup if GTK fails */ +#define WL_WOWL_EXTMAGPAT (1 << 11) /* support extended magic packets */ +#define WL_WOWL_ARPOFFLOAD (1 << 12) /* support ARP/NS/keepalive offloading */ +#define WL_WOWL_WPA2 (1 << 13) /* read protocol version for EAPOL frames */ +#define WL_WOWL_KEYROT (1 << 14) /* If the bit is set, use key rotaton */ +#define WL_WOWL_BCAST (1 << 15) /* If the bit is set, frm received was bcast frame */ + +#define MAGIC_PKT_MINLEN 102 /* Magic pkt min length is 6 * 0xFF + 16 * ETHER_ADDR_LEN */ + +#define WOWL_PATTEN_TYPE_ARP (1 << 0) /* ARP offload Pattern */ +#define WOWL_PATTEN_TYPE_NA (1 << 1) /* NA offload Pattern */ + +typedef struct { + uint32 masksize; /* Size of the mask in #of bytes */ + uint32 offset; /* Offset to start looking for the packet in # of bytes */ + uint32 patternoffset; /* Offset of start of pattern in the structure */ + uint32 patternsize; /* Size of the pattern itself in #of bytes */ + uint32 id; /* id */ + uint32 reasonsize; /* Size of the wakeup reason code */ + uint32 flags; /* Flags to tell the pattern type and other properties */ + /* Mask follows the structure above */ + /* Pattern follows the mask is at 'patternoffset' from the start */ +} wl_wowl_pattern_t; + +typedef struct { + uint count; + wl_wowl_pattern_t pattern[1]; +} wl_wowl_pattern_list_t; + +typedef struct { + uint8 pci_wakeind; /* Whether PCI PMECSR PMEStatus bit was set */ + uint16 ucode_wakeind; /* What wakeup-event indication was set by ucode */ +} wl_wowl_wakeind_t; + + +/* per AC rate control related data structure */ +typedef struct wl_txrate_class { + uint8 init_rate; + uint8 min_rate; + uint8 max_rate; +} wl_txrate_class_t; + + + +/* Overlap BSS Scan parameters default, minimum, maximum */ +#define WLC_OBSS_SCAN_PASSIVE_DWELL_DEFAULT 20 /* unit TU */ +#define WLC_OBSS_SCAN_PASSIVE_DWELL_MIN 5 /* unit TU */ +#define WLC_OBSS_SCAN_PASSIVE_DWELL_MAX 1000 /* unit TU */ +#define WLC_OBSS_SCAN_ACTIVE_DWELL_DEFAULT 10 /* unit TU */ +#define WLC_OBSS_SCAN_ACTIVE_DWELL_MIN 10 /* unit TU */ +#define WLC_OBSS_SCAN_ACTIVE_DWELL_MAX 1000 /* unit TU */ +#define WLC_OBSS_SCAN_WIDTHSCAN_INTERVAL_DEFAULT 300 /* unit Sec */ +#define WLC_OBSS_SCAN_WIDTHSCAN_INTERVAL_MIN 10 /* unit Sec */ +#define WLC_OBSS_SCAN_WIDTHSCAN_INTERVAL_MAX 900 /* unit Sec */ +#define WLC_OBSS_SCAN_CHANWIDTH_TRANSITION_DLY_DEFAULT 5 +#define WLC_OBSS_SCAN_CHANWIDTH_TRANSITION_DLY_MIN 5 +#define WLC_OBSS_SCAN_CHANWIDTH_TRANSITION_DLY_MAX 100 +#define WLC_OBSS_SCAN_PASSIVE_TOTAL_PER_CHANNEL_DEFAULT 200 /* unit TU */ +#define WLC_OBSS_SCAN_PASSIVE_TOTAL_PER_CHANNEL_MIN 200 /* unit TU */ +#define WLC_OBSS_SCAN_PASSIVE_TOTAL_PER_CHANNEL_MAX 10000 /* unit TU */ +#define WLC_OBSS_SCAN_ACTIVE_TOTAL_PER_CHANNEL_DEFAULT 20 /* unit TU */ +#define WLC_OBSS_SCAN_ACTIVE_TOTAL_PER_CHANNEL_MIN 20 /* unit TU */ +#define WLC_OBSS_SCAN_ACTIVE_TOTAL_PER_CHANNEL_MAX 10000 /* unit TU */ +#define WLC_OBSS_SCAN_ACTIVITY_THRESHOLD_DEFAULT 25 /* unit percent */ +#define WLC_OBSS_SCAN_ACTIVITY_THRESHOLD_MIN 0 /* unit percent */ +#define WLC_OBSS_SCAN_ACTIVITY_THRESHOLD_MAX 100 /* unit percent */ + +/* structure for Overlap BSS scan arguments */ +typedef struct wl_obss_scan_arg { + int16 passive_dwell; + int16 active_dwell; + int16 bss_widthscan_interval; + int16 passive_total; + int16 active_total; + int16 chanwidth_transition_delay; + int16 activity_threshold; +} wl_obss_scan_arg_t; + +#define WL_OBSS_SCAN_PARAM_LEN sizeof(wl_obss_scan_arg_t) +#define WL_MIN_NUM_OBSS_SCAN_ARG 7 /* minimum number of arguments required for OBSS Scan */ + +#define WL_COEX_INFO_MASK 0x07 +#define WL_COEX_INFO_REQ 0x01 +#define WL_COEX_40MHZ_INTOLERANT 0x02 +#define WL_COEX_WIDTH20 0x04 + +#define WLC_RSSI_INVALID 0 /* invalid RSSI value */ + +#define MAX_RSSI_LEVELS 8 + +/* RSSI event notification configuration. */ +typedef struct wl_rssi_event { + uint32 rate_limit_msec; /* # of events posted to application will be limited to + * one per specified period (0 to disable rate limit). + */ + uint8 num_rssi_levels; /* Number of entries in rssi_levels[] below */ + int8 rssi_levels[MAX_RSSI_LEVELS]; /* Variable number of RSSI levels. An event + * will be posted each time the RSSI of received + * beacons/packets crosses a level. + */ +} wl_rssi_event_t; + +typedef struct wl_action_obss_coex_req { + uint8 info; + uint8 num; + uint8 ch_list[1]; +} wl_action_obss_coex_req_t; + + +/* IOVar parameter block for small MAC address array with type indicator */ +#define WL_IOV_MAC_PARAM_LEN 4 + +#define WL_IOV_PKTQ_LOG_PRECS 16 + +typedef struct { + uint32 num_addrs; + char addr_type[WL_IOV_MAC_PARAM_LEN]; + struct ether_addr ea[WL_IOV_MAC_PARAM_LEN]; +} wl_iov_mac_params_t; + + +/* Parameter block for PKTQ_LOG statistics */ +typedef struct { + uint32 requested; /* packets requested to be stored */ + uint32 stored; /* packets stored */ + uint32 saved; /* packets saved, + because a lowest priority queue has given away one packet + */ + uint32 selfsaved; /* packets saved, + because an older packet from the same queue has been dropped + */ + uint32 full_dropped; /* packets dropped, + because pktq is full with higher precedence packets + */ + uint32 dropped; /* packets dropped because pktq per that precedence is full */ + uint32 sacrificed; /* packets dropped, + in order to save one from a queue of a highest priority + */ + uint32 busy; /* packets droped because of hardware/transmission error */ + uint32 retry; /* packets re-sent because they were not received */ + uint32 ps_retry; /* packets retried again prior to moving power save mode */ + uint32 retry_drop; /* packets finally dropped after retry limit */ + uint32 max_avail; /* the high-water mark of the queue capacity for packets - + goes to zero as queue fills + */ + uint32 max_used; /* the high-water mark of the queue utilisation for packets - + increases with use ('inverse' of max_avail) + */ + uint32 queue_capacity; /* the maximum capacity of the queue */ +} pktq_log_counters_v01_t; + +#define sacrified sacrificed + +typedef struct { + uint8 num_prec[WL_IOV_MAC_PARAM_LEN]; + pktq_log_counters_v01_t counters[WL_IOV_MAC_PARAM_LEN][WL_IOV_PKTQ_LOG_PRECS]; + char headings[1]; +} pktq_log_format_v01_t; + + +typedef struct { + uint32 version; + wl_iov_mac_params_t params; + union { + pktq_log_format_v01_t v01; + } pktq_log; +} wl_iov_pktq_log_t; + + +/* **** EXTLOG **** */ +#define EXTLOG_CUR_VER 0x0100 + +#define MAX_ARGSTR_LEN 18 /* At least big enough for storing ETHER_ADDR_STR_LEN */ + +/* log modules (bitmap) */ +#define LOG_MODULE_COMMON 0x0001 +#define LOG_MODULE_ASSOC 0x0002 +#define LOG_MODULE_EVENT 0x0004 +#define LOG_MODULE_MAX 3 /* Update when adding module */ + +/* log levels */ +#define WL_LOG_LEVEL_DISABLE 0 +#define WL_LOG_LEVEL_ERR 1 +#define WL_LOG_LEVEL_WARN 2 +#define WL_LOG_LEVEL_INFO 3 +#define WL_LOG_LEVEL_MAX WL_LOG_LEVEL_INFO /* Update when adding level */ + +/* flag */ +#define LOG_FLAG_EVENT 1 + +/* log arg_type */ +#define LOG_ARGTYPE_NULL 0 +#define LOG_ARGTYPE_STR 1 /* %s */ +#define LOG_ARGTYPE_INT 2 /* %d */ +#define LOG_ARGTYPE_INT_STR 3 /* %d...%s */ +#define LOG_ARGTYPE_STR_INT 4 /* %s...%d */ + +typedef struct wlc_extlog_cfg { + int max_number; + uint16 module; /* bitmap */ + uint8 level; + uint8 flag; + uint16 version; +} wlc_extlog_cfg_t; + +typedef struct log_record { + uint32 time; + uint16 module; + uint16 id; + uint8 level; + uint8 sub_unit; + uint8 seq_num; + int32 arg; + char str[MAX_ARGSTR_LEN]; +} log_record_t; + +typedef struct wlc_extlog_req { + uint32 from_last; + uint32 num; +} wlc_extlog_req_t; + +typedef struct wlc_extlog_results { + uint16 version; + uint16 record_len; + uint32 num; + log_record_t logs[1]; +} wlc_extlog_results_t; + +typedef struct log_idstr { + uint16 id; + uint16 flag; + uint8 arg_type; + const char *fmt_str; +} log_idstr_t; + +#define FMTSTRF_USER 1 + +/* flat ID definitions + * New definitions HAVE TO BE ADDED at the end of the table. Otherwise, it will + * affect backward compatibility with pre-existing apps + */ +typedef enum { + FMTSTR_DRIVER_UP_ID = 0, + FMTSTR_DRIVER_DOWN_ID = 1, + FMTSTR_SUSPEND_MAC_FAIL_ID = 2, + FMTSTR_NO_PROGRESS_ID = 3, + FMTSTR_RFDISABLE_ID = 4, + FMTSTR_REG_PRINT_ID = 5, + FMTSTR_EXPTIME_ID = 6, + FMTSTR_JOIN_START_ID = 7, + FMTSTR_JOIN_COMPLETE_ID = 8, + FMTSTR_NO_NETWORKS_ID = 9, + FMTSTR_SECURITY_MISMATCH_ID = 10, + FMTSTR_RATE_MISMATCH_ID = 11, + FMTSTR_AP_PRUNED_ID = 12, + FMTSTR_KEY_INSERTED_ID = 13, + FMTSTR_DEAUTH_ID = 14, + FMTSTR_DISASSOC_ID = 15, + FMTSTR_LINK_UP_ID = 16, + FMTSTR_LINK_DOWN_ID = 17, + FMTSTR_RADIO_HW_OFF_ID = 18, + FMTSTR_RADIO_HW_ON_ID = 19, + FMTSTR_EVENT_DESC_ID = 20, + FMTSTR_PNP_SET_POWER_ID = 21, + FMTSTR_RADIO_SW_OFF_ID = 22, + FMTSTR_RADIO_SW_ON_ID = 23, + FMTSTR_PWD_MISMATCH_ID = 24, + FMTSTR_FATAL_ERROR_ID = 25, + FMTSTR_AUTH_FAIL_ID = 26, + FMTSTR_ASSOC_FAIL_ID = 27, + FMTSTR_IBSS_FAIL_ID = 28, + FMTSTR_EXTAP_FAIL_ID = 29, + FMTSTR_MAX_ID +} log_fmtstr_id_t; + +#ifdef DONGLEOVERLAYS +typedef struct { + uint32 flags_idx; /* lower 8 bits: overlay index; upper 24 bits: flags */ + uint32 offset; /* offset into overlay region to write code */ + uint32 len; /* overlay code len */ + /* overlay code follows this struct */ +} wl_ioctl_overlay_t; + +#define OVERLAY_IDX_MASK 0x000000ff +#define OVERLAY_IDX_SHIFT 0 +#define OVERLAY_FLAGS_MASK 0xffffff00 +#define OVERLAY_FLAGS_SHIFT 8 +/* overlay written to device memory immediately after loading the base image */ +#define OVERLAY_FLAG_POSTLOAD 0x100 +/* defer overlay download until the device responds w/WLC_E_OVL_DOWNLOAD event */ +#define OVERLAY_FLAG_DEFER_DL 0x200 +/* overlay downloaded prior to the host going to sleep */ +#define OVERLAY_FLAG_PRESLEEP 0x400 + +#define OVERLAY_DOWNLOAD_CHUNKSIZE 1024 +#endif /* DONGLEOVERLAYS */ + +#endif /* LINUX_POSTMOGRIFY_REMOVAL */ + +/* no default structure packing */ +#include + +/* require strict packing */ +#include + +#ifndef LINUX_POSTMOGRIFY_REMOVAL + +/* Structures and constants used for "vndr_ie" IOVar interface */ +#define VNDR_IE_CMD_LEN 4 /* length of the set command string: + * "add", "del" (+ NUL) + */ + +/* 802.11 Mgmt Packet flags */ +#define VNDR_IE_BEACON_FLAG 0x1 +#define VNDR_IE_PRBRSP_FLAG 0x2 +#define VNDR_IE_ASSOCRSP_FLAG 0x4 +#define VNDR_IE_AUTHRSP_FLAG 0x8 +#define VNDR_IE_PRBREQ_FLAG 0x10 +#define VNDR_IE_ASSOCREQ_FLAG 0x20 +#define VNDR_IE_IWAPID_FLAG 0x40 /* vendor IE in IW advertisement protocol ID field */ +#define VNDR_IE_CUSTOM_FLAG 0x100 /* allow custom IE id */ + +#define VNDR_IE_INFO_HDR_LEN (sizeof(uint32)) + +typedef BWL_PRE_PACKED_STRUCT struct { + uint32 pktflag; /* bitmask indicating which packet(s) contain this IE */ + vndr_ie_t vndr_ie_data; /* vendor IE data */ +} BWL_POST_PACKED_STRUCT vndr_ie_info_t; + +typedef BWL_PRE_PACKED_STRUCT struct { + int iecount; /* number of entries in the vndr_ie_list[] array */ + vndr_ie_info_t vndr_ie_list[1]; /* variable size list of vndr_ie_info_t structs */ +} BWL_POST_PACKED_STRUCT vndr_ie_buf_t; + +typedef BWL_PRE_PACKED_STRUCT struct { + char cmd[VNDR_IE_CMD_LEN]; /* vndr_ie IOVar set command : "add", "del" + NUL */ + vndr_ie_buf_t vndr_ie_buffer; /* buffer containing Vendor IE list information */ +} BWL_POST_PACKED_STRUCT vndr_ie_setbuf_t; + +/* tag_ID/length/value_buffer tuple */ +typedef BWL_PRE_PACKED_STRUCT struct { + uint8 id; + uint8 len; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT tlv_t; + +typedef BWL_PRE_PACKED_STRUCT struct { + uint32 pktflag; /* bitmask indicating which packet(s) contain this IE */ + tlv_t ie_data; /* IE data */ +} BWL_POST_PACKED_STRUCT ie_info_t; + +typedef BWL_PRE_PACKED_STRUCT struct { + int iecount; /* number of entries in the ie_list[] array */ + ie_info_t ie_list[1]; /* variable size list of ie_info_t structs */ +} BWL_POST_PACKED_STRUCT ie_buf_t; + +typedef BWL_PRE_PACKED_STRUCT struct { + char cmd[VNDR_IE_CMD_LEN]; /* ie IOVar set command : "add" + NUL */ + ie_buf_t ie_buffer; /* buffer containing IE list information */ +} BWL_POST_PACKED_STRUCT ie_setbuf_t; + +typedef BWL_PRE_PACKED_STRUCT struct { + uint32 pktflag; /* bitmask indicating which packet(s) contain this IE */ + uint8 id; /* IE type */ +} BWL_POST_PACKED_STRUCT ie_getbuf_t; + +/* structures used to define format of wps ie data from probe requests */ +/* passed up to applications via iovar "prbreq_wpsie" */ +typedef BWL_PRE_PACKED_STRUCT struct sta_prbreq_wps_ie_hdr { + struct ether_addr staAddr; + uint16 ieLen; +} BWL_POST_PACKED_STRUCT sta_prbreq_wps_ie_hdr_t; + +typedef BWL_PRE_PACKED_STRUCT struct sta_prbreq_wps_ie_data { + sta_prbreq_wps_ie_hdr_t hdr; + uint8 ieData[1]; +} BWL_POST_PACKED_STRUCT sta_prbreq_wps_ie_data_t; + +typedef BWL_PRE_PACKED_STRUCT struct sta_prbreq_wps_ie_list { + uint32 totLen; + uint8 ieDataList[1]; +} BWL_POST_PACKED_STRUCT sta_prbreq_wps_ie_list_t; + + +#ifdef WLMEDIA_TXFAILEVENT +typedef BWL_PRE_PACKED_STRUCT struct { + char dest[ETHER_ADDR_LEN]; /* destination MAC */ + uint8 prio; /* Packet Priority */ + uint8 flags; /* Flags */ + uint32 tsf_l; /* TSF timer low */ + uint32 tsf_h; /* TSF timer high */ + uint16 rates; /* Main Rates */ + uint16 txstatus; /* TX Status */ +} BWL_POST_PACKED_STRUCT txfailinfo_t; +#endif /* WLMEDIA_TXFAILEVENT */ + +#endif /* LINUX_POSTMOGRIFY_REMOVAL */ + +/* no strict structure packing */ +#include + +#ifndef LINUX_POSTMOGRIFY_REMOVAL +/* Global ASSERT Logging */ +#define ASSERTLOG_CUR_VER 0x0100 +#define MAX_ASSRTSTR_LEN 64 + +typedef struct assert_record { + uint32 time; + uint8 seq_num; + char str[MAX_ASSRTSTR_LEN]; +} assert_record_t; + +typedef struct assertlog_results { + uint16 version; + uint16 record_len; + uint32 num; + assert_record_t logs[1]; +} assertlog_results_t; + +#define LOGRRC_FIX_LEN 8 +#define IOBUF_ALLOWED_NUM_OF_LOGREC(type, len) ((len - LOGRRC_FIX_LEN)/sizeof(type)) + + +/* channel interference measurement (chanim) related defines */ + +/* chanim mode */ +#define CHANIM_DISABLE 0 /* disabled */ +#define CHANIM_DETECT 1 /* detection only */ +#define CHANIM_EXT 2 /* external state machine */ +#define CHANIM_ACT 3 /* full internal state machine, detect + act */ +#define CHANIM_MODE_MAX 4 + +/* define for apcs reason code */ +#define APCS_INIT 0 +#define APCS_IOCTL 1 +#define APCS_CHANIM 2 +#define APCS_CSTIMER 3 +#define APCS_BTA 4 + +/* number of ACS record entries */ +#define CHANIM_ACS_RECORD 10 + +/* CHANIM */ +#define CCASTATS_TXDUR 0 +#define CCASTATS_INBSS 1 +#define CCASTATS_OBSS 2 +#define CCASTATS_NOCTG 3 +#define CCASTATS_NOPKT 4 +#define CCASTATS_DOZE 5 +#define CCASTATS_TXOP 6 +#define CCASTATS_GDTXDUR 7 +#define CCASTATS_BDTXDUR 8 +#define CCASTATS_MAX 9 + +/* chanim acs record */ +typedef struct { + bool valid; + uint8 trigger; + chanspec_t selected_chspc; + int8 bgnoise; + uint32 glitch_cnt; + uint8 ccastats; + uint timestamp; +} chanim_acs_record_t; + +typedef struct { + chanim_acs_record_t acs_record[CHANIM_ACS_RECORD]; + uint8 count; + uint timestamp; +} wl_acs_record_t; + +typedef struct chanim_stats { + uint32 glitchcnt; /* normalized as per second count */ + uint32 badplcp; /* normalized as per second count */ + uint8 ccastats[CCASTATS_MAX]; /* normalized as 0-255 */ + int8 bgnoise; /* background noise level (in dBm) */ + chanspec_t chanspec; + uint32 timestamp; +} chanim_stats_t; + +#define WL_CHANIM_STATS_VERSION 1 +#define WL_CHANIM_COUNT_ALL 0xff +#define WL_CHANIM_COUNT_ONE 0x1 + +typedef struct { + uint32 buflen; + uint32 version; + uint32 count; + chanim_stats_t stats[1]; +} wl_chanim_stats_t; + +#define WL_CHANIM_STATS_FIXED_LEN OFFSETOF(wl_chanim_stats_t, stats) + +/* Noise measurement metrics. */ +#define NOISE_MEASURE_KNOISE 0x1 + +/* scb probe parameter */ +typedef struct { + uint32 scb_timeout; + uint32 scb_activity_time; + uint32 scb_max_probe; +} wl_scb_probe_t; + +/* ap tpc modes */ +#define AP_TPC_OFF 0 +#define AP_TPC_BSS_PWR 1 /* BSS power control */ +#define AP_TPC_AP_PWR 2 /* AP power control */ +#define AP_TPC_AP_BSS_PWR 3 /* Both AP and BSS power control */ +#define AP_TPC_MAX_LINK_MARGIN 127 + +/* ap tpc modes */ +#define AP_TPC_OFF 0 +#define AP_TPC_BSS_PWR 1 /* BSS power control */ +#define AP_TPC_AP_PWR 2 /* AP power control */ +#define AP_TPC_AP_BSS_PWR 3 /* Both AP and BSS power control */ +#define AP_TPC_MAX_LINK_MARGIN 127 + +/* structure/defines for selective mgmt frame (smf) stats support */ + +#define SMFS_VERSION 1 +/* selected mgmt frame (smf) stats element */ +typedef struct wl_smfs_elem { + uint32 count; + uint16 code; /* SC or RC code */ +} wl_smfs_elem_t; + +typedef struct wl_smf_stats { + uint32 version; + uint16 length; /* reserved for future usage */ + uint8 type; + uint8 codetype; + uint32 ignored_cnt; + uint32 malformed_cnt; + uint32 count_total; /* count included the interested group */ + wl_smfs_elem_t elem[1]; +} wl_smf_stats_t; + +#define WL_SMFSTATS_FIXED_LEN OFFSETOF(wl_smf_stats_t, elem); + +enum { + SMFS_CODETYPE_SC, + SMFS_CODETYPE_RC +}; + +/* reuse two number in the sc/rc space */ +#define SMFS_CODE_MALFORMED 0xFFFE +#define SMFS_CODE_IGNORED 0xFFFD + +typedef enum smfs_type { + SMFS_TYPE_AUTH, + SMFS_TYPE_ASSOC, + SMFS_TYPE_REASSOC, + SMFS_TYPE_DISASSOC_TX, + SMFS_TYPE_DISASSOC_RX, + SMFS_TYPE_DEAUTH_TX, + SMFS_TYPE_DEAUTH_RX, + SMFS_TYPE_MAX +} smfs_type_t; + +#ifdef PHYMON + +#define PHYMON_VERSION 1 + +typedef struct wl_phycal_core_state { + /* Tx IQ/LO calibration coeffs */ + int16 tx_iqlocal_a; + int16 tx_iqlocal_b; + int8 tx_iqlocal_ci; + int8 tx_iqlocal_cq; + int8 tx_iqlocal_di; + int8 tx_iqlocal_dq; + int8 tx_iqlocal_ei; + int8 tx_iqlocal_eq; + int8 tx_iqlocal_fi; + int8 tx_iqlocal_fq; + + /* Rx IQ calibration coeffs */ + int16 rx_iqcal_a; + int16 rx_iqcal_b; + + uint8 tx_iqlocal_pwridx; /* Tx Power Index for Tx IQ/LO calibration */ + uint32 papd_epsilon_table[64]; /* PAPD epsilon table */ + int16 papd_epsilon_offset; /* PAPD epsilon offset */ + uint8 curr_tx_pwrindex; /* Tx power index */ + int8 idle_tssi; /* Idle TSSI */ + int8 est_tx_pwr; /* Estimated Tx Power (dB) */ + int8 est_rx_pwr; /* Estimated Rx Power (dB) from RSSI */ + uint16 rx_gaininfo; /* Rx gain applied on last Rx pkt */ + uint16 init_gaincode; /* initgain required for ACI */ + int8 estirr_tx; + int8 estirr_rx; + +} wl_phycal_core_state_t; + +typedef struct wl_phycal_state { + int version; + int8 num_phy_cores; /* number of cores */ + int8 curr_temperature; /* on-chip temperature sensor reading */ + chanspec_t chspec; /* channspec for this state */ + bool aci_state; /* ACI state: ON/OFF */ + uint16 crsminpower; /* crsminpower required for ACI */ + uint16 crsminpowerl; /* crsminpowerl required for ACI */ + uint16 crsminpoweru; /* crsminpoweru required for ACI */ + wl_phycal_core_state_t phycal_core[1]; +} wl_phycal_state_t; + +#define WL_PHYCAL_STAT_FIXED_LEN OFFSETOF(wl_phycal_state_t, phycal_core) +#endif /* PHYMON */ + +/* discovery state */ +typedef struct wl_p2p_disc_st { + uint8 state; /* see state */ + chanspec_t chspec; /* valid in listen state */ + uint16 dwell; /* valid in listen state, in ms */ +} wl_p2p_disc_st_t; + +/* state */ +#define WL_P2P_DISC_ST_SCAN 0 +#define WL_P2P_DISC_ST_LISTEN 1 +#define WL_P2P_DISC_ST_SEARCH 2 + +/* scan request */ +typedef struct wl_p2p_scan { + uint8 type; /* 'S' for WLC_SCAN, 'E' for "escan" */ + uint8 reserved[3]; + /* scan or escan parms... */ +} wl_p2p_scan_t; + +/* i/f request */ +typedef struct wl_p2p_if { + struct ether_addr addr; + uint8 type; /* see i/f type */ + chanspec_t chspec; /* for p2p_ifadd GO */ +} wl_p2p_if_t; + +/* i/f type */ +#define WL_P2P_IF_CLIENT 0 +#define WL_P2P_IF_GO 1 +#define WL_P2P_IF_DYNBCN_GO 2 +#define WL_P2P_IF_DEV 3 + +/* i/f query */ +typedef struct wl_p2p_ifq { + uint bsscfgidx; + char ifname[BCM_MSG_IFNAME_MAX]; +} wl_p2p_ifq_t; + +/* OppPS & CTWindow */ +typedef struct wl_p2p_ops { + uint8 ops; /* 0: disable 1: enable */ + uint8 ctw; /* >= 10 */ +} wl_p2p_ops_t; + +/* absence and presence request */ +typedef struct wl_p2p_sched_desc { + uint32 start; + uint32 interval; + uint32 duration; + uint32 count; /* see count */ +} wl_p2p_sched_desc_t; + +/* count */ +#define WL_P2P_SCHED_RSVD 0 +#define WL_P2P_SCHED_REPEAT 255 /* anything > 255 will be treated as 255 */ + +typedef struct wl_p2p_sched { + uint8 type; /* see schedule type */ + uint8 action; /* see schedule action */ + uint8 option; /* see schedule option */ + wl_p2p_sched_desc_t desc[1]; +} wl_p2p_sched_t; +#define WL_P2P_SCHED_FIXED_LEN 3 + +/* schedule type */ +#define WL_P2P_SCHED_TYPE_ABS 0 /* Scheduled Absence */ +#define WL_P2P_SCHED_TYPE_REQ_ABS 1 /* Requested Absence */ + +/* schedule action during absence periods (for WL_P2P_SCHED_ABS type) */ +#define WL_P2P_SCHED_ACTION_NONE 0 /* no action */ +#define WL_P2P_SCHED_ACTION_DOZE 1 /* doze */ +/* schedule option - WL_P2P_SCHED_TYPE_REQ_ABS */ +#define WL_P2P_SCHED_ACTION_GOOFF 2 /* turn off GO beacon/prbrsp functions */ +/* schedule option - WL_P2P_SCHED_TYPE_XXX */ +#define WL_P2P_SCHED_ACTION_RESET 255 /* reset */ + +/* schedule option - WL_P2P_SCHED_TYPE_ABS */ +#define WL_P2P_SCHED_OPTION_NORMAL 0 /* normal start/interval/duration/count */ +#define WL_P2P_SCHED_OPTION_BCNPCT 1 /* percentage of beacon interval */ +/* schedule option - WL_P2P_SCHED_TYPE_REQ_ABS */ +#define WL_P2P_SCHED_OPTION_TSFOFS 2 /* normal start/internal/duration/count with + * start being an offset of the 'current' TSF + */ + +/* feature flags */ +#define WL_P2P_FEAT_GO_CSA (1 << 0) /* GO moves with the STA using CSA method */ +#define WL_P2P_FEAT_GO_NOLEGACY (1 << 1) /* GO does not probe respond to non-p2p probe + * requests + */ +#define WL_P2P_FEAT_RESTRICT_DEV_RESP (1 << 2) /* Restrict p2p dev interface from responding */ + +#ifdef WLNIC +/* nic_cnx iovar */ +typedef struct wl_nic_cnx { + uint8 opcode; + struct ether_addr addr; + /* the following are valid for WL_NIC_CNX_CONN */ + uint8 SSID_len; + uint8 SSID[32]; + struct ether_addr abssid; + uint8 join_period; +} wl_nic_cnx_t; + +/* opcode */ +#define WL_NIC_CNX_ADD 0 /* add NIC connection */ +#define WL_NIC_CNX_DEL 1 /* delete NIC connection */ +#define WL_NIC_CNX_IDX 2 /* query NIC connection index */ +#define WL_NIC_CNX_CONN 3 /* join/create network */ +#define WL_NIC_CNX_DIS 4 /* disconnect from network */ + +/* nic_cfg iovar */ +typedef struct wl_nic_cfg { + uint8 version; + uint8 beacon_mode; + uint16 beacon_interval; + uint8 diluted_beacon_period; + uint8 repeat_EQC; + uint8 scan_length; + uint8 scan_interval; + uint8 scan_probability; + uint8 awake_window_length; + int8 TSF_correction; + uint8 ASID; + uint8 channel_usage_mode; +} wl_nic_cfg_t; + +/* version */ +#define WL_NIC_CFG_VER 1 + +/* beacon_mode */ +#define WL_NIC_BCN_NORM 0 +#define WL_NIC_BCN_DILUTED 1 + +/* channel_usage_mode */ +#define WL_NIC_CHAN_STATIC 0 +#define WL_NIC_CHAN_CYCLE 1 + +/* nic_cfg iovar */ +typedef struct wl_nic_frm { + uint8 type; + struct ether_addr da; + uint8 body[1]; +} wl_nic_frm_t; + +/* type */ +#define WL_NIC_FRM_MYNET 1 +#define WL_NIC_FRM_ACTION 2 + +/* i/f query */ +typedef struct wl_nic_ifq { + uint bsscfgidx; + char ifname[BCM_MSG_IFNAME_MAX]; +} wl_nic_ifq_t; + +/* data mode */ +/* nic_dm iovar */ +typedef struct wl_nic_dm { + uint8 enab; + chanspec_t chspec; +} wl_nic_dm_t; +#endif /* WLNIC */ + +/* RFAWARE def */ +#define BCM_ACTION_RFAWARE 0x77 +#define BCM_ACTION_RFAWARE_DCS 0x01 + +/* DCS reason code define */ +#define BCM_DCS_IOVAR 0x1 +#define BCM_DCS_UNKNOWN 0xFF + +typedef struct wl_bcmdcs_data { + uint reason; + chanspec_t chspec; +} wl_bcmdcs_data_t; + +/* n-mode support capability */ +/* 2x2 includes both 1x1 & 2x2 devices + * reserved #define 2 for future when we want to separate 1x1 & 2x2 and + * control it independently + */ +#define WL_11N_2x2 1 +#define WL_11N_3x3 3 +#define WL_11N_4x4 4 + +/* define 11n feature disable flags */ +#define WLFEATURE_DISABLE_11N 0x00000001 +#define WLFEATURE_DISABLE_11N_STBC_TX 0x00000002 +#define WLFEATURE_DISABLE_11N_STBC_RX 0x00000004 +#define WLFEATURE_DISABLE_11N_SGI_TX 0x00000008 +#define WLFEATURE_DISABLE_11N_SGI_RX 0x00000010 +#define WLFEATURE_DISABLE_11N_AMPDU_TX 0x00000020 +#define WLFEATURE_DISABLE_11N_AMPDU_RX 0x00000040 +#define WLFEATURE_DISABLE_11N_GF 0x00000080 + +/* Proxy STA modes */ +#define PSTA_MODE_DISABLED 0 +#define PSTA_MODE_PROXY 1 +#define PSTA_MODE_REPEATER 2 + + +/* NAT configuration */ +typedef struct { + uint32 ipaddr; /* interface ip address */ + uint32 ipaddr_mask; /* interface ip address mask */ + uint32 ipaddr_gateway; /* gateway ip address */ + uint8 mac_gateway[6]; /* gateway mac address */ + uint32 ipaddr_dns; /* DNS server ip address, valid only for public if */ + uint8 mac_dns[6]; /* DNS server mac address, valid only for public if */ + uint8 GUID[38]; /* interface GUID */ +} nat_if_info_t; + +typedef struct { + uint op; /* operation code */ + bool pub_if; /* set for public if, clear for private if */ + nat_if_info_t if_info; /* interface info */ +} nat_cfg_t; + +/* op code in nat_cfg */ +#define NAT_OP_ENABLE 1 /* enable NAT on given interface */ +#define NAT_OP_DISABLE 2 /* disable NAT on given interface */ +#define NAT_OP_DISABLE_ALL 3 /* disable NAT on all interfaces */ + +/* NAT state */ +#define NAT_STATE_ENABLED 1 /* NAT is enabled */ +#define NAT_STATE_DISABLED 2 /* NAT is disabled */ + +typedef struct { + int state; /* NAT state returned */ +} nat_state_t; + +#ifdef PROP_TXSTATUS +/* Bit definitions for tlv iovar */ +/* + * enable RSSI signals: + * WLFC_CTL_TYPE_RSSI + */ +#define WLFC_FLAGS_RSSI_SIGNALS 0x0001 + +/* enable (if/mac_open, if/mac_close,, mac_add, mac_del) signals: + * + * WLFC_CTL_TYPE_MAC_OPEN + * WLFC_CTL_TYPE_MAC_CLOSE + * + * WLFC_CTL_TYPE_INTERFACE_OPEN + * WLFC_CTL_TYPE_INTERFACE_CLOSE + * + * WLFC_CTL_TYPE_MACDESC_ADD + * WLFC_CTL_TYPE_MACDESC_DEL + * + */ +#define WLFC_FLAGS_XONXOFF_SIGNALS 0x0002 + +/* enable (status, fifo_credit, mac_credit) signals + * WLFC_CTL_TYPE_MAC_REQUEST_CREDIT + * WLFC_CTL_TYPE_TXSTATUS + * WLFC_CTL_TYPE_FIFO_CREDITBACK + */ +#define WLFC_FLAGS_CREDIT_STATUS_SIGNALS 0x0004 + +#define WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE 0x0008 +#define WLFC_FLAGS_PSQ_GENERATIONFSM_ENABLE 0x0010 +#define WLFC_FLAGS_PSQ_ZERO_BUFFER_ENABLE 0x0020 +#define WLFC_FLAGS_HOST_RXRERODER_ACTIVE 0x0040 +#endif /* PROP_TXSTATUS */ + +#define BTA_STATE_LOG_SZ 64 + +/* BTAMP Statemachine states */ +enum { + HCIReset = 1, + HCIReadLocalAMPInfo, + HCIReadLocalAMPASSOC, + HCIWriteRemoteAMPASSOC, + HCICreatePhysicalLink, + HCIAcceptPhysicalLinkRequest, + HCIDisconnectPhysicalLink, + HCICreateLogicalLink, + HCIAcceptLogicalLink, + HCIDisconnectLogicalLink, + HCILogicalLinkCancel, + HCIAmpStateChange, + HCIWriteLogicalLinkAcceptTimeout +}; + +typedef struct flush_txfifo { + uint32 txfifobmp; + uint32 hwtxfifoflush; + struct ether_addr ea; +} flush_txfifo_t; + +#define CHANNEL_5G_LOW_START 36 /* 5G low (36..48) CDD enable/disable bit mask */ +#define CHANNEL_5G_MID_START 52 /* 5G mid (52..64) CDD enable/disable bit mask */ +#define CHANNEL_5G_HIGH_START 100 /* 5G high (100..140) CDD enable/disable bit mask */ +#define CHANNEL_5G_UPPER_START 149 /* 5G upper (149..161) CDD enable/disable bit mask */ + +enum { + SPATIAL_MODE_2G_IDX = 0, + SPATIAL_MODE_5G_LOW_IDX, + SPATIAL_MODE_5G_MID_IDX, + SPATIAL_MODE_5G_HIGH_IDX, + SPATIAL_MODE_5G_UPPER_IDX, + SPATIAL_MODE_MAX_IDX +}; + +/* IOVAR "mempool" parameter. Used to retrieve a list of memory pool statistics. */ +typedef struct wl_mempool_stats { + int num; /* Number of memory pools */ + bcm_mp_stats_t s[1]; /* Variable array of memory pool stats. */ +} wl_mempool_stats_t; + + +/* D0 Coalescing */ +#define IPV4_ARP_FILTER 0x0001 +#define IPV4_NETBT_FILTER 0x0002 +#define IPV4_LLMNR_FILTER 0x0004 +#define IPV4_SSDP_FILTER 0x0008 +#define IPV4_WSD_FILTER 0x0010 +#define IPV6_NETBT_FILTER 0x0200 +#define IPV6_LLMNR_FILTER 0x0400 +#define IPV6_SSDP_FILTER 0x0800 +#define IPV6_WSD_FILTER 0x1000 + +/* Network Offload Engine */ +#define NWOE_OL_ENABLE 0x00000001 + +typedef struct { + uint32 ipaddr; + uint32 ipaddr_netmask; + uint32 ipaddr_gateway; +} nwoe_ifconfig_t; + +/* + * Traffic management structures/defines. + */ + +/* Traffic management bandwidth parameters */ +#define TRF_MGMT_MAX_PRIORITIES 3 + +#define TRF_MGMT_FLAG_ADD_DSCP 0x0001 /* Add DSCP to IP TOS field */ +#define TRF_MGMT_FLAG_DISABLE_SHAPING 0x0002 /* Only support traffic clasification */ +#define TRF_MGMT_FLAG_DISABLE_PRIORITY_TAGGING 0x0004 /* Don't override packet's priority */ + +/* Traffic management priority classes */ +typedef enum trf_mgmt_priority_class { + trf_mgmt_priority_low = 0, /* Maps to 802.1p BO */ + trf_mgmt_priority_medium = 1, /* Maps to 802.1p BE */ + trf_mgmt_priority_high = 2, /* Maps to 802.1p VI */ + trf_mgmt_priority_invalid = (trf_mgmt_priority_high + 1) +} trf_mgmt_priority_class_t; + +/* Traffic management configuration parameters */ +typedef struct trf_mgmt_config { + uint32 trf_mgmt_enabled; /* 0 - disabled, 1 - enabled */ + uint32 flags; /* See TRF_MGMT_FLAG_xxx defines */ + uint32 host_ip_addr; /* My IP address to determine subnet */ + uint32 host_subnet_mask; /* My subnet mask */ + uint32 downlink_bandwidth; /* In units of kbps */ + uint32 uplink_bandwidth; /* In units of kbps */ + uint32 min_tx_bandwidth[TRF_MGMT_MAX_PRIORITIES]; /* Minimum guaranteed tx bandwidth */ + uint32 min_rx_bandwidth[TRF_MGMT_MAX_PRIORITIES]; /* Minimum guaranteed rx bandwidth */ +} trf_mgmt_config_t; + +/* Traffic management filter */ +typedef struct trf_mgmt_filter { + struct ether_addr dst_ether_addr; /* His L2 address */ + uint32 dst_ip_addr; /* His IP address */ + uint16 dst_port; /* His L4 port */ + uint16 src_port; /* My L4 port */ + uint16 prot; /* L4 protocol (only TCP or UDP) */ + uint16 flags; /* TBD. For now, this must be zero. */ + trf_mgmt_priority_class_t priority; /* Priority for filtered packets */ +} trf_mgmt_filter_t; + +/* Traffic management filter list (variable length) */ +typedef struct trf_mgmt_filter_list { + uint32 num_filters; + trf_mgmt_filter_t filter[1]; +} trf_mgmt_filter_list_t; + +/* Traffic management global info used for all queues */ +typedef struct trf_mgmt_global_info { + uint32 maximum_bytes_per_second; + uint32 maximum_bytes_per_sampling_period; + uint32 total_bytes_consumed_per_second; + uint32 total_bytes_consumed_per_sampling_period; + uint32 total_unused_bytes_per_sampling_period; +} trf_mgmt_global_info_t; + +/* Traffic management shaping info per priority queue */ +typedef struct trf_mgmt_shaping_info { + uint32 gauranteed_bandwidth_percentage; + uint32 guaranteed_bytes_per_second; + uint32 guaranteed_bytes_per_sampling_period; + uint32 num_bytes_produced_per_second; + uint32 num_bytes_consumed_per_second; + uint32 num_queued_packets; /* Number of packets in queue */ + uint32 num_queued_bytes; /* Number of bytes in queue */ +} trf_mgmt_shaping_info_t; + +/* Traffic management shaping info array */ +typedef struct trf_mgmt_shaping_info_array { + trf_mgmt_global_info_t tx_global_shaping_info; + trf_mgmt_shaping_info_t tx_queue_shaping_info[TRF_MGMT_MAX_PRIORITIES]; + trf_mgmt_global_info_t rx_global_shaping_info; + trf_mgmt_shaping_info_t rx_queue_shaping_info[TRF_MGMT_MAX_PRIORITIES]; +} trf_mgmt_shaping_info_array_t; + + +/* Traffic management statistical counters */ +typedef struct trf_mgmt_stats { + uint32 num_processed_packets; /* Number of packets processed */ + uint32 num_processed_bytes; /* Number of bytes processed */ + uint32 num_discarded_packets; /* Number of packets discarded from queue */ +} trf_mgmt_stats_t; + +/* Traffic management statisics array */ +typedef struct trf_mgmt_stats_array { + trf_mgmt_stats_t tx_queue_stats[TRF_MGMT_MAX_PRIORITIES]; + trf_mgmt_stats_t rx_queue_stats[TRF_MGMT_MAX_PRIORITIES]; +} trf_mgmt_stats_array_t; + +typedef struct powersel_params { + /* LPC Params exposed via IOVAR */ + int32 tp_ratio_thresh; /* Throughput ratio threshold */ + uint8 rate_stab_thresh; /* Thresh for rate stability based on nupd */ + uint8 pwr_stab_thresh; /* Number of successes before power step down */ + uint8 pwr_sel_exp_time; /* Time lapse for expiry of database */ +} powersel_params_t; + +#endif /* LINUX_POSTMOGRIFY_REMOVAL */ +#endif /* _wlioctl_h_ */ diff --git a/drivers/net/wireless/bcmdhd/linux_osl.c b/drivers/net/wireless/bcmdhd/linux_osl.c new file mode 100644 index 00000000..93990d9b --- /dev/null +++ b/drivers/net/wireless/bcmdhd/linux_osl.c @@ -0,0 +1,1126 @@ +/* + * Linux OS Independent Layer + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: linux_osl.c 373382 2012-12-07 07:59:52Z $ + */ + +#define LINUX_PORT + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include + +#define PCI_CFG_RETRY 10 + +#define OS_HANDLE_MAGIC 0x1234abcd /* Magic # to recognize osh */ +#define BCM_MEM_FILENAME_LEN 24 /* Mem. filename length */ + +#ifdef CONFIG_DHD_USE_STATIC_BUF +#define DHD_SKB_HDRSIZE 336 +#define DHD_SKB_1PAGE_BUFSIZE ((PAGE_SIZE*1)-DHD_SKB_HDRSIZE) +#define DHD_SKB_2PAGE_BUFSIZE ((PAGE_SIZE*2)-DHD_SKB_HDRSIZE) +#define DHD_SKB_4PAGE_BUFSIZE ((PAGE_SIZE*4)-DHD_SKB_HDRSIZE) + +#define STATIC_BUF_MAX_NUM 16 +#define STATIC_BUF_SIZE (PAGE_SIZE*2) +#define STATIC_BUF_TOTAL_LEN (STATIC_BUF_MAX_NUM * STATIC_BUF_SIZE) + +typedef struct bcm_static_buf { + struct semaphore static_sem; + unsigned char *buf_ptr; + unsigned char buf_use[STATIC_BUF_MAX_NUM]; +} bcm_static_buf_t; + +static bcm_static_buf_t *bcm_static_buf = 0; + +#define STATIC_PKT_MAX_NUM 8 +#if defined(ENHANCED_STATIC_BUF) +#define STATIC_PKT_4PAGE_NUM 1 +#define DHD_SKB_MAX_BUFSIZE DHD_SKB_4PAGE_BUFSIZE +#else +#define STATIC_PKT_4PAGE_NUM 0 +#define DHD_SKB_MAX_BUFSIZE DHD_SKB_2PAGE_BUFSIZE +#endif + +typedef struct bcm_static_pkt { + struct sk_buff *skb_4k[STATIC_PKT_MAX_NUM]; + struct sk_buff *skb_8k[STATIC_PKT_MAX_NUM]; +#ifdef ENHANCED_STATIC_BUF + struct sk_buff *skb_16k; +#endif + struct semaphore osl_pkt_sem; + unsigned char pkt_use[STATIC_PKT_MAX_NUM * 2 + STATIC_PKT_4PAGE_NUM]; +} bcm_static_pkt_t; + +static bcm_static_pkt_t *bcm_static_skb = 0; +#endif + +typedef struct bcm_mem_link { + struct bcm_mem_link *prev; + struct bcm_mem_link *next; + uint size; + int line; + void *osh; + char file[BCM_MEM_FILENAME_LEN]; +} bcm_mem_link_t; + +struct osl_info { + osl_pubinfo_t pub; +#ifdef CTFPOOL + ctfpool_t *ctfpool; +#endif + uint magic; + void *pdev; + atomic_t malloced; + uint failed; + uint bustype; + bcm_mem_link_t *dbgmem_list; + spinlock_t dbgmem_lock; + spinlock_t pktalloc_lock; +}; + + + + +uint32 g_assert_type = FALSE; + +static int16 linuxbcmerrormap[] = +{ 0, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -E2BIG, + -E2BIG, + -EBUSY, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -EFAULT, + -ENOMEM, + -EOPNOTSUPP, + -EMSGSIZE, + -EINVAL, + -EPERM, + -ENOMEM, + -EINVAL, + -ERANGE, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -EIO, + -ENODEV, + -EINVAL, + -EIO, + -EIO, + -ENODEV, + -EINVAL, + -ENODATA, + + + +#if BCME_LAST != -42 +#error "You need to add a OS error translation in the linuxbcmerrormap \ + for new error code defined in bcmutils.h" +#endif +}; + + +int +osl_error(int bcmerror) +{ + if (bcmerror > 0) + bcmerror = 0; + else if (bcmerror < BCME_LAST) + bcmerror = BCME_ERROR; + + + return linuxbcmerrormap[-bcmerror]; +} + +extern uint8* dhd_os_prealloc(void *osh, int section, int size); + +osl_t * +osl_attach(void *pdev, uint bustype, bool pkttag) +{ + osl_t *osh; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + gfp_t flags; + + flags = (in_atomic() || in_interrupt()) ? GFP_ATOMIC : GFP_KERNEL; + osh = kmalloc(sizeof(osl_t), flags); +#else + osh = kmalloc(sizeof(osl_t), GFP_ATOMIC); +#endif + + ASSERT(osh); + + bzero(osh, sizeof(osl_t)); + + + ASSERT(ABS(BCME_LAST) == (ARRAYSIZE(linuxbcmerrormap) - 1)); + + osh->magic = OS_HANDLE_MAGIC; + atomic_set(&osh->malloced, 0); + osh->failed = 0; + osh->dbgmem_list = NULL; + spin_lock_init(&(osh->dbgmem_lock)); + osh->pdev = pdev; + osh->pub.pkttag = pkttag; + osh->bustype = bustype; + + switch (bustype) { + case PCI_BUS: + case SI_BUS: + case PCMCIA_BUS: + osh->pub.mmbus = TRUE; + break; + case JTAG_BUS: + case SDIO_BUS: + case USB_BUS: + case SPI_BUS: + case RPC_BUS: + osh->pub.mmbus = FALSE; + break; + default: + ASSERT(FALSE); + break; + } + +#if defined(CONFIG_DHD_USE_STATIC_BUF) + if (!bcm_static_buf) { + if (!(bcm_static_buf = (bcm_static_buf_t *)dhd_os_prealloc(osh, 3, STATIC_BUF_SIZE+ + STATIC_BUF_TOTAL_LEN))) { + printk("can not alloc static buf!\n"); + } + else + printk("alloc static buf at %x!\n", (unsigned int)bcm_static_buf); + + + sema_init(&bcm_static_buf->static_sem, 1); + + bcm_static_buf->buf_ptr = (unsigned char *)bcm_static_buf + STATIC_BUF_SIZE; + } + + if (!bcm_static_skb) { + int i; + void *skb_buff_ptr = 0; + bcm_static_skb = (bcm_static_pkt_t *)((char *)bcm_static_buf + 2048); + skb_buff_ptr = dhd_os_prealloc(osh, 4, 0); + + bcopy(skb_buff_ptr, bcm_static_skb, sizeof(struct sk_buff *)* + (STATIC_PKT_MAX_NUM * 2 + STATIC_PKT_4PAGE_NUM)); + for (i = 0; i < (STATIC_PKT_MAX_NUM * 2 + STATIC_PKT_4PAGE_NUM); i++) + bcm_static_skb->pkt_use[i] = 0; + + sema_init(&bcm_static_skb->osl_pkt_sem, 1); + } +#endif + + spin_lock_init(&(osh->pktalloc_lock)); + + return osh; +} + +void +osl_detach(osl_t *osh) +{ + if (osh == NULL) + return; + +#ifdef CONFIG_DHD_USE_STATIC_BUF + if (bcm_static_buf) { + bcm_static_buf = 0; + } + if (bcm_static_skb) { + bcm_static_skb = 0; + } +#endif + + ASSERT(osh->magic == OS_HANDLE_MAGIC); + kfree(osh); +} + +static struct sk_buff *osl_alloc_skb(unsigned int len) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) + gfp_t flags = (in_atomic() || in_interrupt()) ? GFP_ATOMIC : GFP_KERNEL; + + return __dev_alloc_skb(len, flags); +#else + return dev_alloc_skb(len); +#endif +} + +#ifdef CTFPOOL + +#ifdef CTFPOOL_SPINLOCK +#define CTFPOOL_LOCK(ctfpool, flags) spin_lock_irqsave(&(ctfpool)->lock, flags) +#define CTFPOOL_UNLOCK(ctfpool, flags) spin_unlock_irqrestore(&(ctfpool)->lock, flags) +#else +#define CTFPOOL_LOCK(ctfpool, flags) spin_lock_bh(&(ctfpool)->lock) +#define CTFPOOL_UNLOCK(ctfpool, flags) spin_unlock_bh(&(ctfpool)->lock) +#endif + +void * +osl_ctfpool_add(osl_t *osh) +{ + struct sk_buff *skb; +#ifdef CTFPOOL_SPINLOCK + unsigned long flags; +#endif + + if ((osh == NULL) || (osh->ctfpool == NULL)) + return NULL; + + CTFPOOL_LOCK(osh->ctfpool, flags); + ASSERT(osh->ctfpool->curr_obj <= osh->ctfpool->max_obj); + + + if (osh->ctfpool->curr_obj == osh->ctfpool->max_obj) { + CTFPOOL_UNLOCK(osh->ctfpool, flags); + return NULL; + } + + + skb = osl_alloc_skb(osh->ctfpool->obj_size); + if (skb == NULL) { + printf("%s: skb alloc of len %d failed\n", __FUNCTION__, + osh->ctfpool->obj_size); + CTFPOOL_UNLOCK(osh->ctfpool, flags); + return NULL; + } + + + skb->next = (struct sk_buff *)osh->ctfpool->head; + osh->ctfpool->head = skb; + osh->ctfpool->fast_frees++; + osh->ctfpool->curr_obj++; + + + CTFPOOLPTR(osh, skb) = (void *)osh->ctfpool; + + + PKTFAST(osh, skb) = FASTBUF; + + CTFPOOL_UNLOCK(osh->ctfpool, flags); + + return skb; +} + + +void +osl_ctfpool_replenish(osl_t *osh, uint thresh) +{ + if ((osh == NULL) || (osh->ctfpool == NULL)) + return; + + + while ((osh->ctfpool->refills > 0) && (thresh--)) { + osl_ctfpool_add(osh); + osh->ctfpool->refills--; + } +} + + +int32 +osl_ctfpool_init(osl_t *osh, uint numobj, uint size) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + gfp_t flags; + + flags = (in_atomic() || in_interrupt()) ? GFP_ATOMIC : GFP_KERNEL; + osh->ctfpool = kmalloc(sizeof(ctfpool_t), flags); +#else + osh->ctfpool = kmalloc(sizeof(ctfpool_t), GFP_ATOMIC); +#endif + ASSERT(osh->ctfpool); + bzero(osh->ctfpool, sizeof(ctfpool_t)); + + osh->ctfpool->max_obj = numobj; + osh->ctfpool->obj_size = size; + + spin_lock_init(&osh->ctfpool->lock); + + while (numobj--) { + if (!osl_ctfpool_add(osh)) + return -1; + osh->ctfpool->fast_frees--; + } + + return 0; +} + + +void +osl_ctfpool_cleanup(osl_t *osh) +{ + struct sk_buff *skb, *nskb; +#ifdef CTFPOOL_SPINLOCK + unsigned long flags; +#endif + + if ((osh == NULL) || (osh->ctfpool == NULL)) + return; + + CTFPOOL_LOCK(osh->ctfpool, flags); + + skb = osh->ctfpool->head; + + while (skb != NULL) { + nskb = skb->next; + dev_kfree_skb(skb); + skb = nskb; + osh->ctfpool->curr_obj--; + } + + ASSERT(osh->ctfpool->curr_obj == 0); + osh->ctfpool->head = NULL; + CTFPOOL_UNLOCK(osh->ctfpool, flags); + + kfree(osh->ctfpool); + osh->ctfpool = NULL; +} + +void +osl_ctfpool_stats(osl_t *osh, void *b) +{ + struct bcmstrbuf *bb; + + if ((osh == NULL) || (osh->ctfpool == NULL)) + return; + +#ifdef CONFIG_DHD_USE_STATIC_BUF + if (bcm_static_buf) { + bcm_static_buf = 0; + } + if (bcm_static_skb) { + bcm_static_skb = 0; + } +#endif + + bb = b; + + ASSERT((osh != NULL) && (bb != NULL)); + + bcm_bprintf(bb, "max_obj %d obj_size %d curr_obj %d refills %d\n", + osh->ctfpool->max_obj, osh->ctfpool->obj_size, + osh->ctfpool->curr_obj, osh->ctfpool->refills); + bcm_bprintf(bb, "fast_allocs %d fast_frees %d slow_allocs %d\n", + osh->ctfpool->fast_allocs, osh->ctfpool->fast_frees, + osh->ctfpool->slow_allocs); +} + +static inline struct sk_buff * +osl_pktfastget(osl_t *osh, uint len) +{ + struct sk_buff *skb; +#ifdef CTFPOOL_SPINLOCK + unsigned long flags; +#endif + + + if (osh->ctfpool == NULL) + return NULL; + + CTFPOOL_LOCK(osh->ctfpool, flags); + if (osh->ctfpool->head == NULL) { + ASSERT(osh->ctfpool->curr_obj == 0); + osh->ctfpool->slow_allocs++; + CTFPOOL_UNLOCK(osh->ctfpool, flags); + return NULL; + } + + ASSERT(len <= osh->ctfpool->obj_size); + + + skb = (struct sk_buff *)osh->ctfpool->head; + osh->ctfpool->head = (void *)skb->next; + + osh->ctfpool->fast_allocs++; + osh->ctfpool->curr_obj--; + ASSERT(CTFPOOLHEAD(osh, skb) == (struct sock *)osh->ctfpool->head); + CTFPOOL_UNLOCK(osh->ctfpool, flags); + + + skb->next = skb->prev = NULL; + skb->data = skb->head + 16; + skb->tail = skb->head + 16; + + skb->len = 0; + skb->cloned = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) + skb->list = NULL; +#endif + atomic_set(&skb->users, 1); + + return skb; +} +#endif + +struct sk_buff * BCMFASTPATH +osl_pkt_tonative(osl_t *osh, void *pkt) +{ +#ifndef WL_UMK + struct sk_buff *nskb; + unsigned long flags; +#endif + + if (osh->pub.pkttag) + bzero((void*)((struct sk_buff *)pkt)->cb, OSL_PKTTAG_SZ); + +#ifndef WL_UMK + + for (nskb = (struct sk_buff *)pkt; nskb; nskb = nskb->next) { + spin_lock_irqsave(&osh->pktalloc_lock, flags); + osh->pub.pktalloced--; + spin_unlock_irqrestore(&osh->pktalloc_lock, flags); + } +#endif + return (struct sk_buff *)pkt; +} + + +void * BCMFASTPATH +osl_pkt_frmnative(osl_t *osh, void *pkt) +{ +#ifndef WL_UMK + struct sk_buff *nskb; + unsigned long flags; +#endif + + if (osh->pub.pkttag) + bzero((void*)((struct sk_buff *)pkt)->cb, OSL_PKTTAG_SZ); + +#ifndef WL_UMK + + for (nskb = (struct sk_buff *)pkt; nskb; nskb = nskb->next) { + spin_lock_irqsave(&osh->pktalloc_lock, flags); + osh->pub.pktalloced++; + spin_unlock_irqrestore(&osh->pktalloc_lock, flags); + } +#endif + return (void *)pkt; +} + + +void * BCMFASTPATH +osl_pktget(osl_t *osh, uint len) +{ + struct sk_buff *skb; + unsigned long flags; + +#ifdef CTFPOOL + + skb = osl_pktfastget(osh, len); + if ((skb != NULL) || ((skb = osl_alloc_skb(len)) != NULL)) { +#else + if ((skb = osl_alloc_skb(len))) { +#endif + skb_put(skb, len); + skb->priority = 0; + + + spin_lock_irqsave(&osh->pktalloc_lock, flags); + osh->pub.pktalloced++; + spin_unlock_irqrestore(&osh->pktalloc_lock, flags); + } + + return ((void*) skb); +} + +#ifdef CTFPOOL +static inline void +osl_pktfastfree(osl_t *osh, struct sk_buff *skb) +{ + ctfpool_t *ctfpool; +#ifdef CTFPOOL_SPINLOCK + unsigned long flags; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14) + skb->tstamp.tv.sec = 0; +#else + skb->stamp.tv_sec = 0; +#endif + + + skb->dev = NULL; + skb->dst = NULL; + memset(skb->cb, 0, sizeof(skb->cb)); + skb->ip_summed = 0; + skb->destructor = NULL; + + ctfpool = (ctfpool_t *)CTFPOOLPTR(osh, skb); + ASSERT(ctfpool != NULL); + + + CTFPOOL_LOCK(ctfpool, flags); + skb->next = (struct sk_buff *)ctfpool->head; + ctfpool->head = (void *)skb; + + ctfpool->fast_frees++; + ctfpool->curr_obj++; + + ASSERT(ctfpool->curr_obj <= ctfpool->max_obj); + CTFPOOL_UNLOCK(ctfpool, flags); +} +#endif + + +void BCMFASTPATH +osl_pktfree(osl_t *osh, void *p, bool send) +{ + struct sk_buff *skb, *nskb; + unsigned long flags; + + skb = (struct sk_buff*) p; + + if (send && osh->pub.tx_fn) + osh->pub.tx_fn(osh->pub.tx_ctx, p, 0); + + PKTDBG_TRACE(osh, (void *) skb, PKTLIST_PKTFREE); + + + while (skb) { + nskb = skb->next; + skb->next = NULL; + + + +#ifdef CTFPOOL + if ((PKTISFAST(osh, skb)) && (atomic_read(&skb->users) == 1)) + osl_pktfastfree(osh, skb); + else { +#else + { +#endif + + if (skb->destructor) + + dev_kfree_skb_any(skb); + else + + dev_kfree_skb(skb); + } + spin_lock_irqsave(&osh->pktalloc_lock, flags); + osh->pub.pktalloced--; + spin_unlock_irqrestore(&osh->pktalloc_lock, flags); + skb = nskb; + } +} + +#ifdef CONFIG_DHD_USE_STATIC_BUF +void* +osl_pktget_static(osl_t *osh, uint len) +{ + int i = 0; + struct sk_buff *skb; + + + if (len > DHD_SKB_MAX_BUFSIZE) { + printk("osl_pktget_static: Do we really need this big skb??" + " len=%d\n", len); + return osl_pktget(osh, len); + } + + down(&bcm_static_skb->osl_pkt_sem); + + if (len <= DHD_SKB_1PAGE_BUFSIZE) { + for (i = 0; i < STATIC_PKT_MAX_NUM; i++) { + if (bcm_static_skb->pkt_use[i] == 0) + break; + } + + if (i != STATIC_PKT_MAX_NUM) { + bcm_static_skb->pkt_use[i] = 1; + + skb = bcm_static_skb->skb_4k[i]; + skb->tail = skb->data + len; + skb->len = len; + + up(&bcm_static_skb->osl_pkt_sem); + return skb; + } + } + + if (len <= DHD_SKB_2PAGE_BUFSIZE) { + + for (i = 0; i < STATIC_PKT_MAX_NUM; i++) { + if (bcm_static_skb->pkt_use[i + STATIC_PKT_MAX_NUM] + == 0) + break; + } + + if (i != STATIC_PKT_MAX_NUM) { + bcm_static_skb->pkt_use[i + STATIC_PKT_MAX_NUM] = 1; + skb = bcm_static_skb->skb_8k[i]; + skb->tail = skb->data + len; + skb->len = len; + + up(&bcm_static_skb->osl_pkt_sem); + return skb; + } + } + +#if defined(ENHANCED_STATIC_BUF) + if (bcm_static_skb->pkt_use[STATIC_PKT_MAX_NUM * 2] == 0) { + bcm_static_skb->pkt_use[STATIC_PKT_MAX_NUM * 2] = 1; + + skb = bcm_static_skb->skb_16k; + skb->tail = skb->data + len; + skb->len = len; + + up(&bcm_static_skb->osl_pkt_sem); + return skb; + } +#endif + + up(&bcm_static_skb->osl_pkt_sem); + printk("osl_pktget_static: all static pkt in use!\n"); + return osl_pktget(osh, len); +} + +void +osl_pktfree_static(osl_t *osh, void *p, bool send) +{ + int i; + if (!bcm_static_skb) { + osl_pktfree(osh, p, send); + return; + } + + down(&bcm_static_skb->osl_pkt_sem); + for (i = 0; i < STATIC_PKT_MAX_NUM; i++) { + if (p == bcm_static_skb->skb_4k[i]) { + bcm_static_skb->pkt_use[i] = 0; + up(&bcm_static_skb->osl_pkt_sem); + return; + } + } + + for (i = 0; i < STATIC_PKT_MAX_NUM; i++) { + if (p == bcm_static_skb->skb_8k[i]) { + bcm_static_skb->pkt_use[i + STATIC_PKT_MAX_NUM] = 0; + up(&bcm_static_skb->osl_pkt_sem); + return; + } + } +#ifdef ENHANCED_STATIC_BUF + if (p == bcm_static_skb->skb_16k) { + bcm_static_skb->pkt_use[STATIC_PKT_MAX_NUM*2] = 0; + up(&bcm_static_skb->osl_pkt_sem); + return; + } +#endif + up(&bcm_static_skb->osl_pkt_sem); + + osl_pktfree(osh, p, send); + return; +} +#endif + +uint32 +osl_pci_read_config(osl_t *osh, uint offset, uint size) +{ + uint val = 0; + uint retry = PCI_CFG_RETRY; + + ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); + + + ASSERT(size == 4); + + do { + pci_read_config_dword(osh->pdev, offset, &val); + if (val != 0xffffffff) + break; + } while (retry--); + + + return (val); +} + +void +osl_pci_write_config(osl_t *osh, uint offset, uint size, uint val) +{ + uint retry = PCI_CFG_RETRY; + + ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); + + + ASSERT(size == 4); + + do { + pci_write_config_dword(osh->pdev, offset, val); + if (offset != PCI_BAR0_WIN) + break; + if (osl_pci_read_config(osh, offset, size) == val) + break; + } while (retry--); + +} + + +uint +osl_pci_bus(osl_t *osh) +{ + ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev); + + return ((struct pci_dev *)osh->pdev)->bus->number; +} + + +uint +osl_pci_slot(osl_t *osh) +{ + ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev); + + return PCI_SLOT(((struct pci_dev *)osh->pdev)->devfn); +} + + +struct pci_dev * +osl_pci_device(osl_t *osh) +{ + ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev); + + return osh->pdev; +} + +static void +osl_pcmcia_attr(osl_t *osh, uint offset, char *buf, int size, bool write) +{ +} + +void +osl_pcmcia_read_attr(osl_t *osh, uint offset, void *buf, int size) +{ + osl_pcmcia_attr(osh, offset, (char *) buf, size, FALSE); +} + +void +osl_pcmcia_write_attr(osl_t *osh, uint offset, void *buf, int size) +{ + osl_pcmcia_attr(osh, offset, (char *) buf, size, TRUE); +} + +void * +osl_malloc(osl_t *osh, uint size) +{ + void *addr; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + gfp_t flags; +#endif + + + if (osh) + ASSERT(osh->magic == OS_HANDLE_MAGIC); + +#ifdef CONFIG_DHD_USE_STATIC_BUF + if (bcm_static_buf) + { + int i = 0; + if ((size >= PAGE_SIZE)&&(size <= STATIC_BUF_SIZE)) + { + down(&bcm_static_buf->static_sem); + + for (i = 0; i < STATIC_BUF_MAX_NUM; i++) + { + if (bcm_static_buf->buf_use[i] == 0) + break; + } + + if (i == STATIC_BUF_MAX_NUM) + { + up(&bcm_static_buf->static_sem); + printk("all static buff in use!\n"); + goto original; + } + + bcm_static_buf->buf_use[i] = 1; + up(&bcm_static_buf->static_sem); + + bzero(bcm_static_buf->buf_ptr+STATIC_BUF_SIZE*i, size); + if (osh) + atomic_add(size, &osh->malloced); + + return ((void *)(bcm_static_buf->buf_ptr+STATIC_BUF_SIZE*i)); + } + } +original: +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + flags = (in_atomic() || in_interrupt()) ? GFP_ATOMIC : GFP_KERNEL; + if ((addr = kmalloc(size, flags)) == NULL) { +#else + if ((addr = kmalloc(size, GFP_ATOMIC)) == NULL) { +#endif + if (osh) + osh->failed++; + return (NULL); + } + if (osh) + atomic_add(size, &osh->malloced); + + return (addr); +} + +void +osl_mfree(osl_t *osh, void *addr, uint size) +{ +#ifdef CONFIG_DHD_USE_STATIC_BUF + if (bcm_static_buf) + { + if ((addr > (void *)bcm_static_buf) && ((unsigned char *)addr + <= ((unsigned char *)bcm_static_buf + STATIC_BUF_TOTAL_LEN))) + { + int buf_idx = 0; + + buf_idx = ((unsigned char *)addr - bcm_static_buf->buf_ptr)/STATIC_BUF_SIZE; + + down(&bcm_static_buf->static_sem); + bcm_static_buf->buf_use[buf_idx] = 0; + up(&bcm_static_buf->static_sem); + + if (osh) { + ASSERT(osh->magic == OS_HANDLE_MAGIC); + atomic_sub(size, &osh->malloced); + } + return; + } + } +#endif + if (osh) { + ASSERT(osh->magic == OS_HANDLE_MAGIC); + atomic_sub(size, &osh->malloced); + } + kfree(addr); +} + +uint +osl_malloced(osl_t *osh) +{ + ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); + return (atomic_read(&osh->malloced)); +} + +uint +osl_malloc_failed(osl_t *osh) +{ + ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); + return (osh->failed); +} + + +uint +osl_dma_consistent_align(void) +{ + return (PAGE_SIZE); +} + +void* +osl_dma_alloc_consistent(osl_t *osh, uint size, uint16 align_bits, uint *alloced, ulong *pap) +{ + uint16 align = (1 << align_bits); + ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); + + if (!ISALIGNED(DMA_CONSISTENT_ALIGN, align)) + size += align; + *alloced = size; + + return (pci_alloc_consistent(osh->pdev, size, (dma_addr_t*)pap)); +} + +void +osl_dma_free_consistent(osl_t *osh, void *va, uint size, ulong pa) +{ + ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); + + pci_free_consistent(osh->pdev, size, va, (dma_addr_t)pa); +} + +uint BCMFASTPATH +osl_dma_map(osl_t *osh, void *va, uint size, int direction) +{ + int dir; + + ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); + dir = (direction == DMA_TX)? PCI_DMA_TODEVICE: PCI_DMA_FROMDEVICE; + return (pci_map_single(osh->pdev, va, size, dir)); +} + +void BCMFASTPATH +osl_dma_unmap(osl_t *osh, uint pa, uint size, int direction) +{ + int dir; + + ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); + dir = (direction == DMA_TX)? PCI_DMA_TODEVICE: PCI_DMA_FROMDEVICE; + pci_unmap_single(osh->pdev, (uint32)pa, size, dir); +} + +#if defined(BCMASSERT_LOG) +void +osl_assert(const char *exp, const char *file, int line) +{ + char tempbuf[256]; + const char *basename; + + basename = strrchr(file, '/'); + /* skip the '/' */ + if (basename) + basename++; + + if (!basename) + basename = file; + + snprintf(tempbuf, 64, "\"%s\": file \"%s\", line %d\n", + exp, basename, line); + + printk("%s", tempbuf); + + +} +#endif + +void +osl_delay(uint usec) +{ + uint d; + + while (usec > 0) { + d = MIN(usec, 1000); + udelay(d); + usec -= d; + } +} + + + +void * +osl_pktdup(osl_t *osh, void *skb) +{ + void * p; + unsigned long irqflags; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + gfp_t flags; +#endif + + + PKTCTFMAP(osh, skb); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + flags = (in_atomic() || in_interrupt()) ? GFP_ATOMIC : GFP_KERNEL; + if ((p = skb_clone((struct sk_buff *)skb, flags)) == NULL) +#else + if ((p = skb_clone((struct sk_buff*)skb, GFP_ATOMIC)) == NULL) +#endif + return NULL; + +#ifdef CTFPOOL + if (PKTISFAST(osh, skb)) { + ctfpool_t *ctfpool; + + + ctfpool = (ctfpool_t *)CTFPOOLPTR(osh, skb); + ASSERT(ctfpool != NULL); + PKTCLRFAST(osh, p); + PKTCLRFAST(osh, skb); + ctfpool->refills++; + } +#endif + + + if (osh->pub.pkttag) + bzero((void*)((struct sk_buff *)p)->cb, OSL_PKTTAG_SZ); + + + spin_lock_irqsave(&osh->pktalloc_lock, irqflags); + osh->pub.pktalloced++; + spin_unlock_irqrestore(&osh->pktalloc_lock, irqflags); + return (p); +} + + + + + + + +void * +osl_os_open_image(char *filename) +{ + struct file *fp; + + fp = filp_open(filename, O_RDONLY, 0); + + if (IS_ERR(fp)) + fp = NULL; + + return fp; +} + +int +osl_os_get_image_block(char *buf, int len, void *image) +{ + struct file *fp = (struct file *)image; + int rdlen; + + if (!image) + return 0; + + rdlen = kernel_read(fp, fp->f_pos, buf, len); + if (rdlen > 0) + fp->f_pos += rdlen; + + return rdlen; +} + +void +osl_os_close_image(void *image) +{ + if (image) + filp_close((struct file *)image, NULL); +} diff --git a/drivers/net/wireless/bcmdhd/sbutils.c b/drivers/net/wireless/bcmdhd/sbutils.c new file mode 100644 index 00000000..68cfcb27 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/sbutils.c @@ -0,0 +1,1001 @@ +/* + * Misc utility routines for accessing chip-specific features + * of the SiliconBackplane-based Broadcom chips. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: sbutils.c 310902 2012-01-26 19:45:33Z $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "siutils_priv.h" + + +/* local prototypes */ +static uint _sb_coreidx(si_info_t *sii, uint32 sba); +static uint _sb_scan(si_info_t *sii, uint32 sba, void *regs, uint bus, uint32 sbba, + uint ncores); +static uint32 _sb_coresba(si_info_t *sii); +static void *_sb_setcoreidx(si_info_t *sii, uint coreidx); + +#define SET_SBREG(sii, r, mask, val) \ + W_SBREG((sii), (r), ((R_SBREG((sii), (r)) & ~(mask)) | (val))) +#define REGS2SB(va) (sbconfig_t*) ((int8*)(va) + SBCONFIGOFF) + +/* sonicsrev */ +#define SONICS_2_2 (SBIDL_RV_2_2 >> SBIDL_RV_SHIFT) +#define SONICS_2_3 (SBIDL_RV_2_3 >> SBIDL_RV_SHIFT) + +#define R_SBREG(sii, sbr) sb_read_sbreg((sii), (sbr)) +#define W_SBREG(sii, sbr, v) sb_write_sbreg((sii), (sbr), (v)) +#define AND_SBREG(sii, sbr, v) W_SBREG((sii), (sbr), (R_SBREG((sii), (sbr)) & (v))) +#define OR_SBREG(sii, sbr, v) W_SBREG((sii), (sbr), (R_SBREG((sii), (sbr)) | (v))) + +static uint32 +sb_read_sbreg(si_info_t *sii, volatile uint32 *sbr) +{ + uint8 tmp; + uint32 val, intr_val = 0; + + + /* + * compact flash only has 11 bits address, while we needs 12 bits address. + * MEM_SEG will be OR'd with other 11 bits address in hardware, + * so we program MEM_SEG with 12th bit when necessary(access sb regsiters). + * For normal PCMCIA bus(CFTable_regwinsz > 2k), do nothing special + */ + if (PCMCIA(sii)) { + INTR_OFF(sii, intr_val); + tmp = 1; + OSL_PCMCIA_WRITE_ATTR(sii->osh, MEM_SEG, &tmp, 1); + sbr = (volatile uint32 *)((uintptr)sbr & ~(1 << 11)); /* mask out bit 11 */ + } + + val = R_REG(sii->osh, sbr); + + if (PCMCIA(sii)) { + tmp = 0; + OSL_PCMCIA_WRITE_ATTR(sii->osh, MEM_SEG, &tmp, 1); + INTR_RESTORE(sii, intr_val); + } + + return (val); +} + +static void +sb_write_sbreg(si_info_t *sii, volatile uint32 *sbr, uint32 v) +{ + uint8 tmp; + volatile uint32 dummy; + uint32 intr_val = 0; + + + /* + * compact flash only has 11 bits address, while we needs 12 bits address. + * MEM_SEG will be OR'd with other 11 bits address in hardware, + * so we program MEM_SEG with 12th bit when necessary(access sb regsiters). + * For normal PCMCIA bus(CFTable_regwinsz > 2k), do nothing special + */ + if (PCMCIA(sii)) { + INTR_OFF(sii, intr_val); + tmp = 1; + OSL_PCMCIA_WRITE_ATTR(sii->osh, MEM_SEG, &tmp, 1); + sbr = (volatile uint32 *)((uintptr)sbr & ~(1 << 11)); /* mask out bit 11 */ + } + + if (BUSTYPE(sii->pub.bustype) == PCMCIA_BUS) { + dummy = R_REG(sii->osh, sbr); + BCM_REFERENCE(dummy); + W_REG(sii->osh, (volatile uint16 *)sbr, (uint16)(v & 0xffff)); + dummy = R_REG(sii->osh, sbr); + BCM_REFERENCE(dummy); + W_REG(sii->osh, ((volatile uint16 *)sbr + 1), (uint16)((v >> 16) & 0xffff)); + } else + W_REG(sii->osh, sbr, v); + + if (PCMCIA(sii)) { + tmp = 0; + OSL_PCMCIA_WRITE_ATTR(sii->osh, MEM_SEG, &tmp, 1); + INTR_RESTORE(sii, intr_val); + } +} + +uint +sb_coreid(si_t *sih) +{ + si_info_t *sii; + sbconfig_t *sb; + + sii = SI_INFO(sih); + sb = REGS2SB(sii->curmap); + + return ((R_SBREG(sii, &sb->sbidhigh) & SBIDH_CC_MASK) >> SBIDH_CC_SHIFT); +} + +uint +sb_intflag(si_t *sih) +{ + si_info_t *sii; + void *corereg; + sbconfig_t *sb; + uint origidx, intflag, intr_val = 0; + + sii = SI_INFO(sih); + + INTR_OFF(sii, intr_val); + origidx = si_coreidx(sih); + corereg = si_setcore(sih, CC_CORE_ID, 0); + ASSERT(corereg != NULL); + sb = REGS2SB(corereg); + intflag = R_SBREG(sii, &sb->sbflagst); + sb_setcoreidx(sih, origidx); + INTR_RESTORE(sii, intr_val); + + return intflag; +} + +uint +sb_flag(si_t *sih) +{ + si_info_t *sii; + sbconfig_t *sb; + + sii = SI_INFO(sih); + sb = REGS2SB(sii->curmap); + + return R_SBREG(sii, &sb->sbtpsflag) & SBTPS_NUM0_MASK; +} + +void +sb_setint(si_t *sih, int siflag) +{ + si_info_t *sii; + sbconfig_t *sb; + uint32 vec; + + sii = SI_INFO(sih); + sb = REGS2SB(sii->curmap); + + if (siflag == -1) + vec = 0; + else + vec = 1 << siflag; + W_SBREG(sii, &sb->sbintvec, vec); +} + +/* return core index of the core with address 'sba' */ +static uint +_sb_coreidx(si_info_t *sii, uint32 sba) +{ + uint i; + + for (i = 0; i < sii->numcores; i ++) + if (sba == sii->coresba[i]) + return i; + return BADIDX; +} + +/* return core address of the current core */ +static uint32 +_sb_coresba(si_info_t *sii) +{ + uint32 sbaddr; + + + switch (BUSTYPE(sii->pub.bustype)) { + case SI_BUS: { + sbconfig_t *sb = REGS2SB(sii->curmap); + sbaddr = sb_base(R_SBREG(sii, &sb->sbadmatch0)); + break; + } + + case PCI_BUS: + sbaddr = OSL_PCI_READ_CONFIG(sii->osh, PCI_BAR0_WIN, sizeof(uint32)); + break; + + case PCMCIA_BUS: { + uint8 tmp = 0; + OSL_PCMCIA_READ_ATTR(sii->osh, PCMCIA_ADDR0, &tmp, 1); + sbaddr = (uint32)tmp << 12; + OSL_PCMCIA_READ_ATTR(sii->osh, PCMCIA_ADDR1, &tmp, 1); + sbaddr |= (uint32)tmp << 16; + OSL_PCMCIA_READ_ATTR(sii->osh, PCMCIA_ADDR2, &tmp, 1); + sbaddr |= (uint32)tmp << 24; + break; + } + + case SPI_BUS: + case SDIO_BUS: + sbaddr = (uint32)(uintptr)sii->curmap; + break; + + + default: + sbaddr = BADCOREADDR; + break; + } + + return sbaddr; +} + +uint +sb_corevendor(si_t *sih) +{ + si_info_t *sii; + sbconfig_t *sb; + + sii = SI_INFO(sih); + sb = REGS2SB(sii->curmap); + + return ((R_SBREG(sii, &sb->sbidhigh) & SBIDH_VC_MASK) >> SBIDH_VC_SHIFT); +} + +uint +sb_corerev(si_t *sih) +{ + si_info_t *sii; + sbconfig_t *sb; + uint sbidh; + + sii = SI_INFO(sih); + sb = REGS2SB(sii->curmap); + sbidh = R_SBREG(sii, &sb->sbidhigh); + + return (SBCOREREV(sbidh)); +} + +/* set core-specific control flags */ +void +sb_core_cflags_wo(si_t *sih, uint32 mask, uint32 val) +{ + si_info_t *sii; + sbconfig_t *sb; + uint32 w; + + sii = SI_INFO(sih); + sb = REGS2SB(sii->curmap); + + ASSERT((val & ~mask) == 0); + + /* mask and set */ + w = (R_SBREG(sii, &sb->sbtmstatelow) & ~(mask << SBTML_SICF_SHIFT)) | + (val << SBTML_SICF_SHIFT); + W_SBREG(sii, &sb->sbtmstatelow, w); +} + +/* set/clear core-specific control flags */ +uint32 +sb_core_cflags(si_t *sih, uint32 mask, uint32 val) +{ + si_info_t *sii; + sbconfig_t *sb; + uint32 w; + + sii = SI_INFO(sih); + sb = REGS2SB(sii->curmap); + + ASSERT((val & ~mask) == 0); + + /* mask and set */ + if (mask || val) { + w = (R_SBREG(sii, &sb->sbtmstatelow) & ~(mask << SBTML_SICF_SHIFT)) | + (val << SBTML_SICF_SHIFT); + W_SBREG(sii, &sb->sbtmstatelow, w); + } + + /* return the new value + * for write operation, the following readback ensures the completion of write opration. + */ + return (R_SBREG(sii, &sb->sbtmstatelow) >> SBTML_SICF_SHIFT); +} + +/* set/clear core-specific status flags */ +uint32 +sb_core_sflags(si_t *sih, uint32 mask, uint32 val) +{ + si_info_t *sii; + sbconfig_t *sb; + uint32 w; + + sii = SI_INFO(sih); + sb = REGS2SB(sii->curmap); + + ASSERT((val & ~mask) == 0); + ASSERT((mask & ~SISF_CORE_BITS) == 0); + + /* mask and set */ + if (mask || val) { + w = (R_SBREG(sii, &sb->sbtmstatehigh) & ~(mask << SBTMH_SISF_SHIFT)) | + (val << SBTMH_SISF_SHIFT); + W_SBREG(sii, &sb->sbtmstatehigh, w); + } + + /* return the new value */ + return (R_SBREG(sii, &sb->sbtmstatehigh) >> SBTMH_SISF_SHIFT); +} + +bool +sb_iscoreup(si_t *sih) +{ + si_info_t *sii; + sbconfig_t *sb; + + sii = SI_INFO(sih); + sb = REGS2SB(sii->curmap); + + return ((R_SBREG(sii, &sb->sbtmstatelow) & + (SBTML_RESET | SBTML_REJ_MASK | (SICF_CLOCK_EN << SBTML_SICF_SHIFT))) == + (SICF_CLOCK_EN << SBTML_SICF_SHIFT)); +} + +/* + * Switch to 'coreidx', issue a single arbitrary 32bit register mask&set operation, + * switch back to the original core, and return the new value. + * + * When using the silicon backplane, no fidleing with interrupts or core switches are needed. + * + * Also, when using pci/pcie, we can optimize away the core switching for pci registers + * and (on newer pci cores) chipcommon registers. + */ +uint +sb_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val) +{ + uint origidx = 0; + uint32 *r = NULL; + uint w; + uint intr_val = 0; + bool fast = FALSE; + si_info_t *sii; + + sii = SI_INFO(sih); + + ASSERT(GOODIDX(coreidx)); + ASSERT(regoff < SI_CORE_SIZE); + ASSERT((val & ~mask) == 0); + + if (coreidx >= SI_MAXCORES) + return 0; + + if (BUSTYPE(sii->pub.bustype) == SI_BUS) { + /* If internal bus, we can always get at everything */ + fast = TRUE; + /* map if does not exist */ + if (!sii->regs[coreidx]) { + sii->regs[coreidx] = REG_MAP(sii->coresba[coreidx], + SI_CORE_SIZE); + ASSERT(GOODREGS(sii->regs[coreidx])); + } + r = (uint32 *)((uchar *)sii->regs[coreidx] + regoff); + } else if (BUSTYPE(sii->pub.bustype) == PCI_BUS) { + /* If pci/pcie, we can get at pci/pcie regs and on newer cores to chipc */ + + if ((sii->coreid[coreidx] == CC_CORE_ID) && SI_FAST(sii)) { + /* Chipc registers are mapped at 12KB */ + + fast = TRUE; + r = (uint32 *)((char *)sii->curmap + PCI_16KB0_CCREGS_OFFSET + regoff); + } else if (sii->pub.buscoreidx == coreidx) { + /* pci registers are at either in the last 2KB of an 8KB window + * or, in pcie and pci rev 13 at 8KB + */ + fast = TRUE; + if (SI_FAST(sii)) + r = (uint32 *)((char *)sii->curmap + + PCI_16KB0_PCIREGS_OFFSET + regoff); + else + r = (uint32 *)((char *)sii->curmap + + ((regoff >= SBCONFIGOFF) ? + PCI_BAR0_PCISBR_OFFSET : PCI_BAR0_PCIREGS_OFFSET) + + regoff); + } + } + + if (!fast) { + INTR_OFF(sii, intr_val); + + /* save current core index */ + origidx = si_coreidx(&sii->pub); + + /* switch core */ + r = (uint32*) ((uchar*)sb_setcoreidx(&sii->pub, coreidx) + regoff); + } + ASSERT(r != NULL); + + /* mask and set */ + if (mask || val) { + if (regoff >= SBCONFIGOFF) { + w = (R_SBREG(sii, r) & ~mask) | val; + W_SBREG(sii, r, w); + } else { + w = (R_REG(sii->osh, r) & ~mask) | val; + W_REG(sii->osh, r, w); + } + } + + /* readback */ + if (regoff >= SBCONFIGOFF) + w = R_SBREG(sii, r); + else { + if ((CHIPID(sii->pub.chip) == BCM5354_CHIP_ID) && + (coreidx == SI_CC_IDX) && + (regoff == OFFSETOF(chipcregs_t, watchdog))) { + w = val; + } else + w = R_REG(sii->osh, r); + } + + if (!fast) { + /* restore core index */ + if (origidx != coreidx) + sb_setcoreidx(&sii->pub, origidx); + + INTR_RESTORE(sii, intr_val); + } + + return (w); +} + +/* Scan the enumeration space to find all cores starting from the given + * bus 'sbba'. Append coreid and other info to the lists in 'si'. 'sba' + * is the default core address at chip POR time and 'regs' is the virtual + * address that the default core is mapped at. 'ncores' is the number of + * cores expected on bus 'sbba'. It returns the total number of cores + * starting from bus 'sbba', inclusive. + */ +#define SB_MAXBUSES 2 +static uint +_sb_scan(si_info_t *sii, uint32 sba, void *regs, uint bus, uint32 sbba, uint numcores) +{ + uint next; + uint ncc = 0; + uint i; + + if (bus >= SB_MAXBUSES) { + SI_ERROR(("_sb_scan: bus 0x%08x at level %d is too deep to scan\n", sbba, bus)); + return 0; + } + SI_MSG(("_sb_scan: scan bus 0x%08x assume %u cores\n", sbba, numcores)); + + /* Scan all cores on the bus starting from core 0. + * Core addresses must be contiguous on each bus. + */ + for (i = 0, next = sii->numcores; i < numcores && next < SB_BUS_MAXCORES; i++, next++) { + sii->coresba[next] = sbba + (i * SI_CORE_SIZE); + + /* keep and reuse the initial register mapping */ + if ((BUSTYPE(sii->pub.bustype) == SI_BUS) && (sii->coresba[next] == sba)) { + SI_VMSG(("_sb_scan: reuse mapped regs %p for core %u\n", regs, next)); + sii->regs[next] = regs; + } + + /* change core to 'next' and read its coreid */ + sii->curmap = _sb_setcoreidx(sii, next); + sii->curidx = next; + + sii->coreid[next] = sb_coreid(&sii->pub); + + /* core specific processing... */ + /* chipc provides # cores */ + if (sii->coreid[next] == CC_CORE_ID) { + chipcregs_t *cc = (chipcregs_t *)sii->curmap; + uint32 ccrev = sb_corerev(&sii->pub); + + /* determine numcores - this is the total # cores in the chip */ + if (((ccrev == 4) || (ccrev >= 6))) + numcores = (R_REG(sii->osh, &cc->chipid) & CID_CC_MASK) >> + CID_CC_SHIFT; + else { + /* Older chips */ + uint chip = CHIPID(sii->pub.chip); + + if (chip == BCM4306_CHIP_ID) /* < 4306c0 */ + numcores = 6; + else if (chip == BCM4704_CHIP_ID) + numcores = 9; + else if (chip == BCM5365_CHIP_ID) + numcores = 7; + else { + SI_ERROR(("sb_chip2numcores: unsupported chip 0x%x\n", + chip)); + ASSERT(0); + numcores = 1; + } + } + SI_VMSG(("_sb_scan: there are %u cores in the chip %s\n", numcores, + sii->pub.issim ? "QT" : "")); + } + /* scan bridged SB(s) and add results to the end of the list */ + else if (sii->coreid[next] == OCP_CORE_ID) { + sbconfig_t *sb = REGS2SB(sii->curmap); + uint32 nsbba = R_SBREG(sii, &sb->sbadmatch1); + uint nsbcc; + + sii->numcores = next + 1; + + if ((nsbba & 0xfff00000) != SI_ENUM_BASE) + continue; + nsbba &= 0xfffff000; + if (_sb_coreidx(sii, nsbba) != BADIDX) + continue; + + nsbcc = (R_SBREG(sii, &sb->sbtmstatehigh) & 0x000f0000) >> 16; + nsbcc = _sb_scan(sii, sba, regs, bus + 1, nsbba, nsbcc); + if (sbba == SI_ENUM_BASE) + numcores -= nsbcc; + ncc += nsbcc; + } + } + + SI_MSG(("_sb_scan: found %u cores on bus 0x%08x\n", i, sbba)); + + sii->numcores = i + ncc; + return sii->numcores; +} + +/* scan the sb enumerated space to identify all cores */ +void +sb_scan(si_t *sih, void *regs, uint devid) +{ + si_info_t *sii; + uint32 origsba; + sbconfig_t *sb; + + sii = SI_INFO(sih); + sb = REGS2SB(sii->curmap); + + sii->pub.socirev = (R_SBREG(sii, &sb->sbidlow) & SBIDL_RV_MASK) >> SBIDL_RV_SHIFT; + + /* Save the current core info and validate it later till we know + * for sure what is good and what is bad. + */ + origsba = _sb_coresba(sii); + + /* scan all SB(s) starting from SI_ENUM_BASE */ + sii->numcores = _sb_scan(sii, origsba, regs, 0, SI_ENUM_BASE, 1); +} + +/* + * This function changes logical "focus" to the indicated core; + * must be called with interrupts off. + * Moreover, callers should keep interrupts off during switching out of and back to d11 core + */ +void * +sb_setcoreidx(si_t *sih, uint coreidx) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + + if (coreidx >= sii->numcores) + return (NULL); + + /* + * If the user has provided an interrupt mask enabled function, + * then assert interrupts are disabled before switching the core. + */ + ASSERT((sii->intrsenabled_fn == NULL) || !(*(sii)->intrsenabled_fn)((sii)->intr_arg)); + + sii->curmap = _sb_setcoreidx(sii, coreidx); + sii->curidx = coreidx; + + return (sii->curmap); +} + +/* This function changes the logical "focus" to the indicated core. + * Return the current core's virtual address. + */ +static void * +_sb_setcoreidx(si_info_t *sii, uint coreidx) +{ + uint32 sbaddr = sii->coresba[coreidx]; + void *regs; + + switch (BUSTYPE(sii->pub.bustype)) { + case SI_BUS: + /* map new one */ + if (!sii->regs[coreidx]) { + sii->regs[coreidx] = REG_MAP(sbaddr, SI_CORE_SIZE); + ASSERT(GOODREGS(sii->regs[coreidx])); + } + regs = sii->regs[coreidx]; + break; + + case PCI_BUS: + /* point bar0 window */ + OSL_PCI_WRITE_CONFIG(sii->osh, PCI_BAR0_WIN, 4, sbaddr); + regs = sii->curmap; + break; + + case PCMCIA_BUS: { + uint8 tmp = (sbaddr >> 12) & 0x0f; + OSL_PCMCIA_WRITE_ATTR(sii->osh, PCMCIA_ADDR0, &tmp, 1); + tmp = (sbaddr >> 16) & 0xff; + OSL_PCMCIA_WRITE_ATTR(sii->osh, PCMCIA_ADDR1, &tmp, 1); + tmp = (sbaddr >> 24) & 0xff; + OSL_PCMCIA_WRITE_ATTR(sii->osh, PCMCIA_ADDR2, &tmp, 1); + regs = sii->curmap; + break; + } + case SPI_BUS: + case SDIO_BUS: + /* map new one */ + if (!sii->regs[coreidx]) { + sii->regs[coreidx] = (void *)(uintptr)sbaddr; + ASSERT(GOODREGS(sii->regs[coreidx])); + } + regs = sii->regs[coreidx]; + break; + + + default: + ASSERT(0); + regs = NULL; + break; + } + + return regs; +} + +/* Return the address of sbadmatch0/1/2/3 register */ +static volatile uint32 * +sb_admatch(si_info_t *sii, uint asidx) +{ + sbconfig_t *sb; + volatile uint32 *addrm; + + sb = REGS2SB(sii->curmap); + + switch (asidx) { + case 0: + addrm = &sb->sbadmatch0; + break; + + case 1: + addrm = &sb->sbadmatch1; + break; + + case 2: + addrm = &sb->sbadmatch2; + break; + + case 3: + addrm = &sb->sbadmatch3; + break; + + default: + SI_ERROR(("%s: Address space index (%d) out of range\n", __FUNCTION__, asidx)); + return 0; + } + + return (addrm); +} + +/* Return the number of address spaces in current core */ +int +sb_numaddrspaces(si_t *sih) +{ + si_info_t *sii; + sbconfig_t *sb; + + sii = SI_INFO(sih); + sb = REGS2SB(sii->curmap); + + /* + 1 because of enumeration space */ + return ((R_SBREG(sii, &sb->sbidlow) & SBIDL_AR_MASK) >> SBIDL_AR_SHIFT) + 1; +} + +/* Return the address of the nth address space in the current core */ +uint32 +sb_addrspace(si_t *sih, uint asidx) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + + return (sb_base(R_SBREG(sii, sb_admatch(sii, asidx)))); +} + +/* Return the size of the nth address space in the current core */ +uint32 +sb_addrspacesize(si_t *sih, uint asidx) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + + return (sb_size(R_SBREG(sii, sb_admatch(sii, asidx)))); +} + + +/* do buffered registers update */ +void +sb_commit(si_t *sih) +{ + si_info_t *sii; + uint origidx; + uint intr_val = 0; + + sii = SI_INFO(sih); + + origidx = sii->curidx; + ASSERT(GOODIDX(origidx)); + + INTR_OFF(sii, intr_val); + + /* switch over to chipcommon core if there is one, else use pci */ + if (sii->pub.ccrev != NOREV) { + chipcregs_t *ccregs = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); + ASSERT(ccregs != NULL); + + /* do the buffer registers update */ + W_REG(sii->osh, &ccregs->broadcastaddress, SB_COMMIT); + W_REG(sii->osh, &ccregs->broadcastdata, 0x0); + } else + ASSERT(0); + + /* restore core index */ + sb_setcoreidx(sih, origidx); + INTR_RESTORE(sii, intr_val); +} + +void +sb_core_disable(si_t *sih, uint32 bits) +{ + si_info_t *sii; + volatile uint32 dummy; + sbconfig_t *sb; + + sii = SI_INFO(sih); + + ASSERT(GOODREGS(sii->curmap)); + sb = REGS2SB(sii->curmap); + + /* if core is already in reset, just return */ + if (R_SBREG(sii, &sb->sbtmstatelow) & SBTML_RESET) + return; + + /* if clocks are not enabled, put into reset and return */ + if ((R_SBREG(sii, &sb->sbtmstatelow) & (SICF_CLOCK_EN << SBTML_SICF_SHIFT)) == 0) + goto disable; + + /* set target reject and spin until busy is clear (preserve core-specific bits) */ + OR_SBREG(sii, &sb->sbtmstatelow, SBTML_REJ); + dummy = R_SBREG(sii, &sb->sbtmstatelow); + BCM_REFERENCE(dummy); + OSL_DELAY(1); + SPINWAIT((R_SBREG(sii, &sb->sbtmstatehigh) & SBTMH_BUSY), 100000); + if (R_SBREG(sii, &sb->sbtmstatehigh) & SBTMH_BUSY) + SI_ERROR(("%s: target state still busy\n", __FUNCTION__)); + + if (R_SBREG(sii, &sb->sbidlow) & SBIDL_INIT) { + OR_SBREG(sii, &sb->sbimstate, SBIM_RJ); + dummy = R_SBREG(sii, &sb->sbimstate); + BCM_REFERENCE(dummy); + OSL_DELAY(1); + SPINWAIT((R_SBREG(sii, &sb->sbimstate) & SBIM_BY), 100000); + } + + /* set reset and reject while enabling the clocks */ + W_SBREG(sii, &sb->sbtmstatelow, + (((bits | SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT) | + SBTML_REJ | SBTML_RESET)); + dummy = R_SBREG(sii, &sb->sbtmstatelow); + BCM_REFERENCE(dummy); + OSL_DELAY(10); + + /* don't forget to clear the initiator reject bit */ + if (R_SBREG(sii, &sb->sbidlow) & SBIDL_INIT) + AND_SBREG(sii, &sb->sbimstate, ~SBIM_RJ); + +disable: + /* leave reset and reject asserted */ + W_SBREG(sii, &sb->sbtmstatelow, ((bits << SBTML_SICF_SHIFT) | SBTML_REJ | SBTML_RESET)); + OSL_DELAY(1); +} + +/* reset and re-enable a core + * inputs: + * bits - core specific bits that are set during and after reset sequence + * resetbits - core specific bits that are set only during reset sequence + */ +void +sb_core_reset(si_t *sih, uint32 bits, uint32 resetbits) +{ + si_info_t *sii; + sbconfig_t *sb; + volatile uint32 dummy; + + sii = SI_INFO(sih); + ASSERT(GOODREGS(sii->curmap)); + sb = REGS2SB(sii->curmap); + + /* + * Must do the disable sequence first to work for arbitrary current core state. + */ + sb_core_disable(sih, (bits | resetbits)); + + /* + * Now do the initialization sequence. + */ + + /* set reset while enabling the clock and forcing them on throughout the core */ + W_SBREG(sii, &sb->sbtmstatelow, + (((bits | resetbits | SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT) | + SBTML_RESET)); + dummy = R_SBREG(sii, &sb->sbtmstatelow); + BCM_REFERENCE(dummy); + OSL_DELAY(1); + + if (R_SBREG(sii, &sb->sbtmstatehigh) & SBTMH_SERR) { + W_SBREG(sii, &sb->sbtmstatehigh, 0); + } + if ((dummy = R_SBREG(sii, &sb->sbimstate)) & (SBIM_IBE | SBIM_TO)) { + AND_SBREG(sii, &sb->sbimstate, ~(SBIM_IBE | SBIM_TO)); + } + + /* clear reset and allow it to propagate throughout the core */ + W_SBREG(sii, &sb->sbtmstatelow, + ((bits | resetbits | SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT)); + dummy = R_SBREG(sii, &sb->sbtmstatelow); + BCM_REFERENCE(dummy); + OSL_DELAY(1); + + /* leave clock enabled */ + W_SBREG(sii, &sb->sbtmstatelow, ((bits | SICF_CLOCK_EN) << SBTML_SICF_SHIFT)); + dummy = R_SBREG(sii, &sb->sbtmstatelow); + BCM_REFERENCE(dummy); + OSL_DELAY(1); +} + +/* + * Set the initiator timeout for the "master core". + * The master core is defined to be the core in control + * of the chip and so it issues accesses to non-memory + * locations (Because of dma *any* core can access memeory). + * + * The routine uses the bus to decide who is the master: + * SI_BUS => mips + * JTAG_BUS => chipc + * PCI_BUS => pci or pcie + * PCMCIA_BUS => pcmcia + * SDIO_BUS => pcmcia + * + * This routine exists so callers can disable initiator + * timeouts so accesses to very slow devices like otp + * won't cause an abort. The routine allows arbitrary + * settings of the service and request timeouts, though. + * + * Returns the timeout state before changing it or -1 + * on error. + */ + +#define TO_MASK (SBIMCL_RTO_MASK | SBIMCL_STO_MASK) + +uint32 +sb_set_initiator_to(si_t *sih, uint32 to, uint idx) +{ + si_info_t *sii; + uint origidx; + uint intr_val = 0; + uint32 tmp, ret = 0xffffffff; + sbconfig_t *sb; + + sii = SI_INFO(sih); + + if ((to & ~TO_MASK) != 0) + return ret; + + /* Figure out the master core */ + if (idx == BADIDX) { + switch (BUSTYPE(sii->pub.bustype)) { + case PCI_BUS: + idx = sii->pub.buscoreidx; + break; + case JTAG_BUS: + idx = SI_CC_IDX; + break; + case PCMCIA_BUS: + case SDIO_BUS: + idx = si_findcoreidx(sih, PCMCIA_CORE_ID, 0); + break; + case SI_BUS: + idx = si_findcoreidx(sih, MIPS33_CORE_ID, 0); + break; + default: + ASSERT(0); + } + if (idx == BADIDX) + return ret; + } + + INTR_OFF(sii, intr_val); + origidx = si_coreidx(sih); + + sb = REGS2SB(sb_setcoreidx(sih, idx)); + + tmp = R_SBREG(sii, &sb->sbimconfiglow); + ret = tmp & TO_MASK; + W_SBREG(sii, &sb->sbimconfiglow, (tmp & ~TO_MASK) | to); + + sb_commit(sih); + sb_setcoreidx(sih, origidx); + INTR_RESTORE(sii, intr_val); + return ret; +} + +uint32 +sb_base(uint32 admatch) +{ + uint32 base; + uint type; + + type = admatch & SBAM_TYPE_MASK; + ASSERT(type < 3); + + base = 0; + + if (type == 0) { + base = admatch & SBAM_BASE0_MASK; + } else if (type == 1) { + ASSERT(!(admatch & SBAM_ADNEG)); /* neg not supported */ + base = admatch & SBAM_BASE1_MASK; + } else if (type == 2) { + ASSERT(!(admatch & SBAM_ADNEG)); /* neg not supported */ + base = admatch & SBAM_BASE2_MASK; + } + + return (base); +} + +uint32 +sb_size(uint32 admatch) +{ + uint32 size; + uint type; + + type = admatch & SBAM_TYPE_MASK; + ASSERT(type < 3); + + size = 0; + + if (type == 0) { + size = 1 << (((admatch & SBAM_ADINT0_MASK) >> SBAM_ADINT0_SHIFT) + 1); + } else if (type == 1) { + ASSERT(!(admatch & SBAM_ADNEG)); /* neg not supported */ + size = 1 << (((admatch & SBAM_ADINT1_MASK) >> SBAM_ADINT1_SHIFT) + 1); + } else if (type == 2) { + ASSERT(!(admatch & SBAM_ADNEG)); /* neg not supported */ + size = 1 << (((admatch & SBAM_ADINT2_MASK) >> SBAM_ADINT2_SHIFT) + 1); + } + + return (size); +} diff --git a/drivers/net/wireless/bcmdhd/siutils.c b/drivers/net/wireless/bcmdhd/siutils.c new file mode 100644 index 00000000..fef3cbd2 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/siutils.c @@ -0,0 +1,2466 @@ +/* + * Misc utility routines for accessing chip-specific features + * of the SiliconBackplane-based Broadcom chips. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: siutils.c 347632 2012-07-27 11:00:35Z $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "siutils_priv.h" + +/* local prototypes */ +static si_info_t *si_doattach(si_info_t *sii, uint devid, osl_t *osh, void *regs, + uint bustype, void *sdh, char **vars, uint *varsz); +static bool si_buscore_prep(si_info_t *sii, uint bustype, uint devid, void *sdh); +static bool si_buscore_setup(si_info_t *sii, chipcregs_t *cc, uint bustype, uint32 savewin, + uint *origidx, void *regs); + + + +/* global variable to indicate reservation/release of gpio's */ +static uint32 si_gpioreservation = 0; + +/* global flag to prevent shared resources from being initialized multiple times in si_attach() */ + +int do_4360_pcie2_war = 0; + +/* + * Allocate a si handle. + * devid - pci device id (used to determine chip#) + * osh - opaque OS handle + * regs - virtual address of initial core registers + * bustype - pci/pcmcia/sb/sdio/etc + * vars - pointer to a pointer area for "environment" variables + * varsz - pointer to int to return the size of the vars + */ +si_t * +si_attach(uint devid, osl_t *osh, void *regs, + uint bustype, void *sdh, char **vars, uint *varsz) +{ + si_info_t *sii; + + /* alloc si_info_t */ + if ((sii = MALLOC(osh, sizeof (si_info_t))) == NULL) { + SI_ERROR(("si_attach: malloc failed! malloced %d bytes\n", MALLOCED(osh))); + return (NULL); + } + + if (si_doattach(sii, devid, osh, regs, bustype, sdh, vars, varsz) == NULL) { + MFREE(osh, sii, sizeof(si_info_t)); + return (NULL); + } + sii->vars = vars ? *vars : NULL; + sii->varsz = varsz ? *varsz : 0; + + return (si_t *)sii; +} + +/* global kernel resource */ +static si_info_t ksii; + +static uint32 wd_msticks; /* watchdog timer ticks normalized to ms */ + +/* generic kernel variant of si_attach() */ +si_t * +si_kattach(osl_t *osh) +{ + static bool ksii_attached = FALSE; + + if (!ksii_attached) { + void *regs; + regs = REG_MAP(SI_ENUM_BASE, SI_CORE_SIZE); + + if (si_doattach(&ksii, BCM4710_DEVICE_ID, osh, regs, + SI_BUS, NULL, + osh != SI_OSH ? &ksii.vars : NULL, + osh != SI_OSH ? &ksii.varsz : NULL) == NULL) { + SI_ERROR(("si_kattach: si_doattach failed\n")); + REG_UNMAP(regs); + return NULL; + } + REG_UNMAP(regs); + + /* save ticks normalized to ms for si_watchdog_ms() */ + if (PMUCTL_ENAB(&ksii.pub)) { + /* based on 32KHz ILP clock */ + wd_msticks = 32; + } else { + wd_msticks = ALP_CLOCK / 1000; + } + + ksii_attached = TRUE; + SI_MSG(("si_kattach done. ccrev = %d, wd_msticks = %d\n", + ksii.pub.ccrev, wd_msticks)); + } + + return &ksii.pub; +} + + +static bool +si_buscore_prep(si_info_t *sii, uint bustype, uint devid, void *sdh) +{ + /* need to set memseg flag for CF card first before any sb registers access */ + if (BUSTYPE(bustype) == PCMCIA_BUS) + sii->memseg = TRUE; + + + if (BUSTYPE(bustype) == SDIO_BUS) { + int err; + uint8 clkset; + + /* Try forcing SDIO core to do ALPAvail request only */ + clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ; + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); + if (!err) { + uint8 clkval; + + /* If register supported, wait for ALPAvail and then force ALP */ + clkval = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, NULL); + if ((clkval & ~SBSDIO_AVBITS) == clkset) { + SPINWAIT(((clkval = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, + SBSDIO_FUNC1_CHIPCLKCSR, NULL)), !SBSDIO_ALPAV(clkval)), + PMU_MAX_TRANSITION_DLY); + if (!SBSDIO_ALPAV(clkval)) { + SI_ERROR(("timeout on ALPAV wait, clkval 0x%02x\n", + clkval)); + return FALSE; + } + clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP; + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, + clkset, &err); + OSL_DELAY(65); + } + } + + /* Also, disable the extra SDIO pull-ups */ + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL); + } + + + return TRUE; +} + +static bool +si_buscore_setup(si_info_t *sii, chipcregs_t *cc, uint bustype, uint32 savewin, + uint *origidx, void *regs) +{ + bool pci, pcie, pcie_gen2 = FALSE; + uint i; + uint pciidx, pcieidx, pcirev, pcierev; + + cc = si_setcoreidx(&sii->pub, SI_CC_IDX); + ASSERT((uintptr)cc); + + /* get chipcommon rev */ + sii->pub.ccrev = (int)si_corerev(&sii->pub); + + /* get chipcommon chipstatus */ + if (sii->pub.ccrev >= 11) + sii->pub.chipst = R_REG(sii->osh, &cc->chipstatus); + + /* get chipcommon capabilites */ + sii->pub.cccaps = R_REG(sii->osh, &cc->capabilities); + /* get chipcommon extended capabilities */ + + if (sii->pub.ccrev >= 35) + sii->pub.cccaps_ext = R_REG(sii->osh, &cc->capabilities_ext); + + /* get pmu rev and caps */ + if (sii->pub.cccaps & CC_CAP_PMU) { + sii->pub.pmucaps = R_REG(sii->osh, &cc->pmucapabilities); + sii->pub.pmurev = sii->pub.pmucaps & PCAP_REV_MASK; + } + + SI_MSG(("Chipc: rev %d, caps 0x%x, chipst 0x%x pmurev %d, pmucaps 0x%x\n", + sii->pub.ccrev, sii->pub.cccaps, sii->pub.chipst, sii->pub.pmurev, + sii->pub.pmucaps)); + + /* figure out bus/orignal core idx */ + sii->pub.buscoretype = NODEV_CORE_ID; + sii->pub.buscorerev = (uint)NOREV; + sii->pub.buscoreidx = BADIDX; + + pci = pcie = FALSE; + pcirev = pcierev = (uint)NOREV; + pciidx = pcieidx = BADIDX; + + for (i = 0; i < sii->numcores; i++) { + uint cid, crev; + + si_setcoreidx(&sii->pub, i); + cid = si_coreid(&sii->pub); + crev = si_corerev(&sii->pub); + + /* Display cores found */ + SI_VMSG(("CORE[%d]: id 0x%x rev %d base 0x%x regs 0x%p\n", + i, cid, crev, sii->coresba[i], sii->regs[i])); + + if (BUSTYPE(bustype) == PCI_BUS) { + if (cid == PCI_CORE_ID) { + pciidx = i; + pcirev = crev; + pci = TRUE; + } else if ((cid == PCIE_CORE_ID) || (cid == PCIE2_CORE_ID)) { + pcieidx = i; + pcierev = crev; + pcie = TRUE; + if (cid == PCIE2_CORE_ID) + pcie_gen2 = TRUE; + } + } else if ((BUSTYPE(bustype) == PCMCIA_BUS) && + (cid == PCMCIA_CORE_ID)) { + sii->pub.buscorerev = crev; + sii->pub.buscoretype = cid; + sii->pub.buscoreidx = i; + } + else if (((BUSTYPE(bustype) == SDIO_BUS) || + (BUSTYPE(bustype) == SPI_BUS)) && + ((cid == PCMCIA_CORE_ID) || + (cid == SDIOD_CORE_ID))) { + sii->pub.buscorerev = crev; + sii->pub.buscoretype = cid; + sii->pub.buscoreidx = i; + } + + /* find the core idx before entering this func. */ + if ((savewin && (savewin == sii->coresba[i])) || + (regs == sii->regs[i])) + *origidx = i; + } + + if (pci) { + sii->pub.buscoretype = PCI_CORE_ID; + sii->pub.buscorerev = pcirev; + sii->pub.buscoreidx = pciidx; + } else if (pcie) { + if (pcie_gen2) + sii->pub.buscoretype = PCIE2_CORE_ID; + else + sii->pub.buscoretype = PCIE_CORE_ID; + sii->pub.buscorerev = pcierev; + sii->pub.buscoreidx = pcieidx; + } + + SI_VMSG(("Buscore id/type/rev %d/0x%x/%d\n", sii->pub.buscoreidx, sii->pub.buscoretype, + sii->pub.buscorerev)); + + if (BUSTYPE(sii->pub.bustype) == SI_BUS && (CHIPID(sii->pub.chip) == BCM4712_CHIP_ID) && + (sii->pub.chippkg != BCM4712LARGE_PKG_ID) && (CHIPREV(sii->pub.chiprev) <= 3)) + OR_REG(sii->osh, &cc->slow_clk_ctl, SCC_SS_XTAL); + + + /* Make sure any on-chip ARM is off (in case strapping is wrong), or downloaded code was + * already running. + */ + if ((BUSTYPE(bustype) == SDIO_BUS) || (BUSTYPE(bustype) == SPI_BUS)) { + if (si_setcore(&sii->pub, ARM7S_CORE_ID, 0) || + si_setcore(&sii->pub, ARMCM3_CORE_ID, 0)) + si_core_disable(&sii->pub, 0); + } + + /* return to the original core */ + si_setcoreidx(&sii->pub, *origidx); + + return TRUE; +} + + + + +static si_info_t * +si_doattach(si_info_t *sii, uint devid, osl_t *osh, void *regs, + uint bustype, void *sdh, char **vars, uint *varsz) +{ + struct si_pub *sih = &sii->pub; + uint32 w, savewin; + chipcregs_t *cc; + char *pvars = NULL; + uint origidx; + + ASSERT(GOODREGS(regs)); + + bzero((uchar*)sii, sizeof(si_info_t)); + + savewin = 0; + + sih->buscoreidx = BADIDX; + + sii->curmap = regs; + sii->sdh = sdh; + sii->osh = osh; + + + + /* find Chipcommon address */ + if (bustype == PCI_BUS) { + savewin = OSL_PCI_READ_CONFIG(sii->osh, PCI_BAR0_WIN, sizeof(uint32)); + if (!GOODCOREADDR(savewin, SI_ENUM_BASE)) + savewin = SI_ENUM_BASE; + OSL_PCI_WRITE_CONFIG(sii->osh, PCI_BAR0_WIN, 4, SI_ENUM_BASE); + if (!regs) + return NULL; + cc = (chipcregs_t *)regs; + } else if ((bustype == SDIO_BUS) || (bustype == SPI_BUS)) { + cc = (chipcregs_t *)sii->curmap; + } else { + cc = (chipcregs_t *)REG_MAP(SI_ENUM_BASE, SI_CORE_SIZE); + } + + sih->bustype = bustype; + if (bustype != BUSTYPE(bustype)) { + SI_ERROR(("si_doattach: bus type %d does not match configured bus type %d\n", + bustype, BUSTYPE(bustype))); + return NULL; + } + + /* bus/core/clk setup for register access */ + if (!si_buscore_prep(sii, bustype, devid, sdh)) { + SI_ERROR(("si_doattach: si_core_clk_prep failed %d\n", bustype)); + return NULL; + } + + /* ChipID recognition. + * We assume we can read chipid at offset 0 from the regs arg. + * If we add other chiptypes (or if we need to support old sdio hosts w/o chipcommon), + * some way of recognizing them needs to be added here. + */ + if (!cc) { + SI_ERROR(("%s: chipcommon register space is null \n", __FUNCTION__)); + return NULL; + } + w = R_REG(osh, &cc->chipid); + sih->socitype = (w & CID_TYPE_MASK) >> CID_TYPE_SHIFT; + /* Might as wll fill in chip id rev & pkg */ + sih->chip = w & CID_ID_MASK; + sih->chiprev = (w & CID_REV_MASK) >> CID_REV_SHIFT; + sih->chippkg = (w & CID_PKG_MASK) >> CID_PKG_SHIFT; + + if ((CHIPID(sih->chip) == BCM4329_CHIP_ID) && (sih->chiprev == 0) && + (sih->chippkg != BCM4329_289PIN_PKG_ID)) { + sih->chippkg = BCM4329_182PIN_PKG_ID; + } + sih->issim = IS_SIM(sih->chippkg); + + /* scan for cores */ + if (CHIPTYPE(sii->pub.socitype) == SOCI_SB) { + SI_MSG(("Found chip type SB (0x%08x)\n", w)); + sb_scan(&sii->pub, regs, devid); + } else if (CHIPTYPE(sii->pub.socitype) == SOCI_AI) { + SI_MSG(("Found chip type AI (0x%08x)\n", w)); + /* pass chipc address instead of original core base */ + ai_scan(&sii->pub, (void *)(uintptr)cc, devid); + } else if (CHIPTYPE(sii->pub.socitype) == SOCI_UBUS) { + SI_MSG(("Found chip type UBUS (0x%08x), chip id = 0x%4x\n", w, sih->chip)); + /* pass chipc address instead of original core base */ + ub_scan(&sii->pub, (void *)(uintptr)cc, devid); + } else { + SI_ERROR(("Found chip of unknown type (0x%08x)\n", w)); + return NULL; + } + /* no cores found, bail out */ + if (sii->numcores == 0) { + SI_ERROR(("si_doattach: could not find any cores\n")); + return NULL; + } + /* bus/core/clk setup */ + origidx = SI_CC_IDX; + if (!si_buscore_setup(sii, cc, bustype, savewin, &origidx, regs)) { + SI_ERROR(("si_doattach: si_buscore_setup failed\n")); + goto exit; + } + + if (CHIPID(sih->chip) == BCM4322_CHIP_ID && (((sih->chipst & CST4322_SPROM_OTP_SEL_MASK) + >> CST4322_SPROM_OTP_SEL_SHIFT) == (CST4322_OTP_PRESENT | + CST4322_SPROM_PRESENT))) { + SI_ERROR(("%s: Invalid setting: both SPROM and OTP strapped.\n", __FUNCTION__)); + return NULL; + } + + /* assume current core is CC */ + if ((sii->pub.ccrev == 0x25) && ((CHIPID(sih->chip) == BCM43236_CHIP_ID || + CHIPID(sih->chip) == BCM43235_CHIP_ID || + CHIPID(sih->chip) == BCM43234_CHIP_ID || + CHIPID(sih->chip) == BCM43238_CHIP_ID) && + (CHIPREV(sii->pub.chiprev) <= 2))) { + + if ((cc->chipstatus & CST43236_BP_CLK) != 0) { + uint clkdiv; + clkdiv = R_REG(osh, &cc->clkdiv); + /* otp_clk_div is even number, 120/14 < 9mhz */ + clkdiv = (clkdiv & ~CLKD_OTP) | (14 << CLKD_OTP_SHIFT); + W_REG(osh, &cc->clkdiv, clkdiv); + SI_ERROR(("%s: set clkdiv to %x\n", __FUNCTION__, clkdiv)); + } + OSL_DELAY(10); + } + + if (bustype == PCI_BUS) { + + } + + pvars = NULL; + BCM_REFERENCE(pvars); + + + + if (sii->pub.ccrev >= 20) { + uint32 gpiopullup = 0, gpiopulldown = 0; + cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); + ASSERT(cc != NULL); + + /* 4314/43142 has pin muxing, don't clear gpio bits */ + if ((CHIPID(sih->chip) == BCM4314_CHIP_ID) || + (CHIPID(sih->chip) == BCM43142_CHIP_ID)) { + gpiopullup |= 0x402e0; + gpiopulldown |= 0x20500; + } + + W_REG(osh, &cc->gpiopullup, gpiopullup); + W_REG(osh, &cc->gpiopulldown, gpiopulldown); + si_setcoreidx(sih, origidx); + } + + + /* clear any previous epidiag-induced target abort */ + ASSERT(!si_taclear(sih, FALSE)); + + return (sii); + +exit: + + return NULL; +} + +/* may be called with core in reset */ +void +si_detach(si_t *sih) +{ + si_info_t *sii; + uint idx; + + + sii = SI_INFO(sih); + + if (sii == NULL) + return; + + if (BUSTYPE(sih->bustype) == SI_BUS) + for (idx = 0; idx < SI_MAXCORES; idx++) + if (sii->regs[idx]) { + REG_UNMAP(sii->regs[idx]); + sii->regs[idx] = NULL; + } + + + +#if !defined(BCMBUSTYPE) || (BCMBUSTYPE == SI_BUS) + if (sii != &ksii) +#endif /* !BCMBUSTYPE || (BCMBUSTYPE == SI_BUS) */ + MFREE(sii->osh, sii, sizeof(si_info_t)); +} + +void * +si_osh(si_t *sih) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + return sii->osh; +} + +void +si_setosh(si_t *sih, osl_t *osh) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + if (sii->osh != NULL) { + SI_ERROR(("osh is already set....\n")); + ASSERT(!sii->osh); + } + sii->osh = osh; +} + +/* register driver interrupt disabling and restoring callback functions */ +void +si_register_intr_callback(si_t *sih, void *intrsoff_fn, void *intrsrestore_fn, + void *intrsenabled_fn, void *intr_arg) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + sii->intr_arg = intr_arg; + sii->intrsoff_fn = (si_intrsoff_t)intrsoff_fn; + sii->intrsrestore_fn = (si_intrsrestore_t)intrsrestore_fn; + sii->intrsenabled_fn = (si_intrsenabled_t)intrsenabled_fn; + /* save current core id. when this function called, the current core + * must be the core which provides driver functions(il, et, wl, etc.) + */ + sii->dev_coreid = sii->coreid[sii->curidx]; +} + +void +si_deregister_intr_callback(si_t *sih) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + sii->intrsoff_fn = NULL; +} + +uint +si_intflag(si_t *sih) +{ + si_info_t *sii = SI_INFO(sih); + + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_intflag(sih); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return R_REG(sii->osh, ((uint32 *)(uintptr) + (sii->oob_router + OOB_STATUSA))); + else { + ASSERT(0); + return 0; + } +} + +uint +si_flag(si_t *sih) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_flag(sih); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return ai_flag(sih); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + return ub_flag(sih); + else { + ASSERT(0); + return 0; + } +} + +void +si_setint(si_t *sih, int siflag) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + sb_setint(sih, siflag); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + ai_setint(sih, siflag); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + ub_setint(sih, siflag); + else + ASSERT(0); +} + +uint +si_coreid(si_t *sih) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + return sii->coreid[sii->curidx]; +} + +uint +si_coreidx(si_t *sih) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + return sii->curidx; +} + +/* return the core-type instantiation # of the current core */ +uint +si_coreunit(si_t *sih) +{ + si_info_t *sii; + uint idx; + uint coreid; + uint coreunit; + uint i; + + sii = SI_INFO(sih); + coreunit = 0; + + idx = sii->curidx; + + ASSERT(GOODREGS(sii->curmap)); + coreid = si_coreid(sih); + + /* count the cores of our type */ + for (i = 0; i < idx; i++) + if (sii->coreid[i] == coreid) + coreunit++; + + return (coreunit); +} + +uint +si_corevendor(si_t *sih) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_corevendor(sih); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return ai_corevendor(sih); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + return ub_corevendor(sih); + else { + ASSERT(0); + return 0; + } +} + +bool +si_backplane64(si_t *sih) +{ + return ((sih->cccaps & CC_CAP_BKPLN64) != 0); +} + +uint +si_corerev(si_t *sih) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_corerev(sih); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return ai_corerev(sih); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + return ub_corerev(sih); + else { + ASSERT(0); + return 0; + } +} + +/* return index of coreid or BADIDX if not found */ +uint +si_findcoreidx(si_t *sih, uint coreid, uint coreunit) +{ + si_info_t *sii; + uint found; + uint i; + + sii = SI_INFO(sih); + + found = 0; + + for (i = 0; i < sii->numcores; i++) + if (sii->coreid[i] == coreid) { + if (found == coreunit) + return (i); + found++; + } + + return (BADIDX); +} + +/* return list of found cores */ +uint +si_corelist(si_t *sih, uint coreid[]) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + + bcopy((uchar*)sii->coreid, (uchar*)coreid, (sii->numcores * sizeof(uint))); + return (sii->numcores); +} + +/* return current register mapping */ +void * +si_coreregs(si_t *sih) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + ASSERT(GOODREGS(sii->curmap)); + + return (sii->curmap); +} + +/* + * This function changes logical "focus" to the indicated core; + * must be called with interrupts off. + * Moreover, callers should keep interrupts off during switching out of and back to d11 core + */ +void * +si_setcore(si_t *sih, uint coreid, uint coreunit) +{ + uint idx; + + idx = si_findcoreidx(sih, coreid, coreunit); + if (!GOODIDX(idx)) + return (NULL); + + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_setcoreidx(sih, idx); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return ai_setcoreidx(sih, idx); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + return ub_setcoreidx(sih, idx); + else { + ASSERT(0); + return NULL; + } +} + +void * +si_setcoreidx(si_t *sih, uint coreidx) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_setcoreidx(sih, coreidx); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return ai_setcoreidx(sih, coreidx); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + return ub_setcoreidx(sih, coreidx); + else { + ASSERT(0); + return NULL; + } +} + +/* Turn off interrupt as required by sb_setcore, before switch core */ +void * +si_switch_core(si_t *sih, uint coreid, uint *origidx, uint *intr_val) +{ + void *cc; + si_info_t *sii; + + sii = SI_INFO(sih); + + if (SI_FAST(sii)) { + /* Overloading the origidx variable to remember the coreid, + * this works because the core ids cannot be confused with + * core indices. + */ + *origidx = coreid; + if (coreid == CC_CORE_ID) + return (void *)CCREGS_FAST(sii); + else if (coreid == sih->buscoretype) + return (void *)PCIEREGS(sii); + } + INTR_OFF(sii, *intr_val); + *origidx = sii->curidx; + cc = si_setcore(sih, coreid, 0); + ASSERT(cc != NULL); + + return cc; +} + +/* restore coreidx and restore interrupt */ +void +si_restore_core(si_t *sih, uint coreid, uint intr_val) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + if (SI_FAST(sii) && ((coreid == CC_CORE_ID) || (coreid == sih->buscoretype))) + return; + + si_setcoreidx(sih, coreid); + INTR_RESTORE(sii, intr_val); +} + +int +si_numaddrspaces(si_t *sih) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_numaddrspaces(sih); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return ai_numaddrspaces(sih); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + return ub_numaddrspaces(sih); + else { + ASSERT(0); + return 0; + } +} + +uint32 +si_addrspace(si_t *sih, uint asidx) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_addrspace(sih, asidx); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return ai_addrspace(sih, asidx); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + return ub_addrspace(sih, asidx); + else { + ASSERT(0); + return 0; + } +} + +uint32 +si_addrspacesize(si_t *sih, uint asidx) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_addrspacesize(sih, asidx); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return ai_addrspacesize(sih, asidx); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + return ub_addrspacesize(sih, asidx); + else { + ASSERT(0); + return 0; + } +} + +void +si_coreaddrspaceX(si_t *sih, uint asidx, uint32 *addr, uint32 *size) +{ + /* Only supported for SOCI_AI */ + if (CHIPTYPE(sih->socitype) == SOCI_AI) + ai_coreaddrspaceX(sih, asidx, addr, size); + else + *size = 0; +} + +uint32 +si_core_cflags(si_t *sih, uint32 mask, uint32 val) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_core_cflags(sih, mask, val); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return ai_core_cflags(sih, mask, val); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + return ub_core_cflags(sih, mask, val); + else { + ASSERT(0); + return 0; + } +} + +void +si_core_cflags_wo(si_t *sih, uint32 mask, uint32 val) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + sb_core_cflags_wo(sih, mask, val); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + ai_core_cflags_wo(sih, mask, val); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + ub_core_cflags_wo(sih, mask, val); + else + ASSERT(0); +} + +uint32 +si_core_sflags(si_t *sih, uint32 mask, uint32 val) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_core_sflags(sih, mask, val); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return ai_core_sflags(sih, mask, val); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + return ub_core_sflags(sih, mask, val); + else { + ASSERT(0); + return 0; + } +} + +bool +si_iscoreup(si_t *sih) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_iscoreup(sih); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return ai_iscoreup(sih); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + return ub_iscoreup(sih); + else { + ASSERT(0); + return FALSE; + } +} + +uint +si_wrapperreg(si_t *sih, uint32 offset, uint32 mask, uint32 val) +{ + /* only for AI back plane chips */ + if (CHIPTYPE(sih->socitype) == SOCI_AI) + return (ai_wrap_reg(sih, offset, mask, val)); + return 0; +} + +uint +si_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_corereg(sih, coreidx, regoff, mask, val); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return ai_corereg(sih, coreidx, regoff, mask, val); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + return ub_corereg(sih, coreidx, regoff, mask, val); + else { + ASSERT(0); + return 0; + } +} + +void +si_core_disable(si_t *sih, uint32 bits) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + sb_core_disable(sih, bits); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + ai_core_disable(sih, bits); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + ub_core_disable(sih, bits); +} + +void +si_core_reset(si_t *sih, uint32 bits, uint32 resetbits) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + sb_core_reset(sih, bits, resetbits); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + ai_core_reset(sih, bits, resetbits); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + ub_core_reset(sih, bits, resetbits); +} + +/* Run bist on current core. Caller needs to take care of core-specific bist hazards */ +int +si_corebist(si_t *sih) +{ + uint32 cflags; + int result = 0; + + /* Read core control flags */ + cflags = si_core_cflags(sih, 0, 0); + + /* Set bist & fgc */ + si_core_cflags(sih, ~0, (SICF_BIST_EN | SICF_FGC)); + + /* Wait for bist done */ + SPINWAIT(((si_core_sflags(sih, 0, 0) & SISF_BIST_DONE) == 0), 100000); + + if (si_core_sflags(sih, 0, 0) & SISF_BIST_ERROR) + result = BCME_ERROR; + + /* Reset core control flags */ + si_core_cflags(sih, 0xffff, cflags); + + return result; +} + +static uint32 +factor6(uint32 x) +{ + switch (x) { + case CC_F6_2: return 2; + case CC_F6_3: return 3; + case CC_F6_4: return 4; + case CC_F6_5: return 5; + case CC_F6_6: return 6; + case CC_F6_7: return 7; + default: return 0; + } +} + +/* calculate the speed the SI would run at given a set of clockcontrol values */ +uint32 +si_clock_rate(uint32 pll_type, uint32 n, uint32 m) +{ + uint32 n1, n2, clock, m1, m2, m3, mc; + + n1 = n & CN_N1_MASK; + n2 = (n & CN_N2_MASK) >> CN_N2_SHIFT; + + if (pll_type == PLL_TYPE6) { + if (m & CC_T6_MMASK) + return CC_T6_M1; + else + return CC_T6_M0; + } else if ((pll_type == PLL_TYPE1) || + (pll_type == PLL_TYPE3) || + (pll_type == PLL_TYPE4) || + (pll_type == PLL_TYPE7)) { + n1 = factor6(n1); + n2 += CC_F5_BIAS; + } else if (pll_type == PLL_TYPE2) { + n1 += CC_T2_BIAS; + n2 += CC_T2_BIAS; + ASSERT((n1 >= 2) && (n1 <= 7)); + ASSERT((n2 >= 5) && (n2 <= 23)); + } else if (pll_type == PLL_TYPE5) { + return (100000000); + } else + ASSERT(0); + /* PLL types 3 and 7 use BASE2 (25Mhz) */ + if ((pll_type == PLL_TYPE3) || + (pll_type == PLL_TYPE7)) { + clock = CC_CLOCK_BASE2 * n1 * n2; + } else + clock = CC_CLOCK_BASE1 * n1 * n2; + + if (clock == 0) + return 0; + + m1 = m & CC_M1_MASK; + m2 = (m & CC_M2_MASK) >> CC_M2_SHIFT; + m3 = (m & CC_M3_MASK) >> CC_M3_SHIFT; + mc = (m & CC_MC_MASK) >> CC_MC_SHIFT; + + if ((pll_type == PLL_TYPE1) || + (pll_type == PLL_TYPE3) || + (pll_type == PLL_TYPE4) || + (pll_type == PLL_TYPE7)) { + m1 = factor6(m1); + if ((pll_type == PLL_TYPE1) || (pll_type == PLL_TYPE3)) + m2 += CC_F5_BIAS; + else + m2 = factor6(m2); + m3 = factor6(m3); + + switch (mc) { + case CC_MC_BYPASS: return (clock); + case CC_MC_M1: return (clock / m1); + case CC_MC_M1M2: return (clock / (m1 * m2)); + case CC_MC_M1M2M3: return (clock / (m1 * m2 * m3)); + case CC_MC_M1M3: return (clock / (m1 * m3)); + default: return (0); + } + } else { + ASSERT(pll_type == PLL_TYPE2); + + m1 += CC_T2_BIAS; + m2 += CC_T2M2_BIAS; + m3 += CC_T2_BIAS; + ASSERT((m1 >= 2) && (m1 <= 7)); + ASSERT((m2 >= 3) && (m2 <= 10)); + ASSERT((m3 >= 2) && (m3 <= 7)); + + if ((mc & CC_T2MC_M1BYP) == 0) + clock /= m1; + if ((mc & CC_T2MC_M2BYP) == 0) + clock /= m2; + if ((mc & CC_T2MC_M3BYP) == 0) + clock /= m3; + + return (clock); + } +} + + +/* set chip watchdog reset timer to fire in 'ticks' */ +void +si_watchdog(si_t *sih, uint ticks) +{ + uint nb, maxt; + + if (PMUCTL_ENAB(sih)) { + + if ((CHIPID(sih->chip) == BCM4319_CHIP_ID) && + (CHIPREV(sih->chiprev) == 0) && (ticks != 0)) { + si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, clk_ctl_st), ~0, 0x2); + si_setcore(sih, USB20D_CORE_ID, 0); + si_core_disable(sih, 1); + si_setcore(sih, CC_CORE_ID, 0); + } + + nb = (sih->ccrev < 26) ? 16 : ((sih->ccrev >= 37) ? 32 : 24); + /* The mips compiler uses the sllv instruction, + * so we specially handle the 32-bit case. + */ + if (nb == 32) + maxt = 0xffffffff; + else + maxt = ((1 << nb) - 1); + + if (ticks == 1) + ticks = 2; + else if (ticks > maxt) + ticks = maxt; + + si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, pmuwatchdog), ~0, ticks); + } else { + maxt = (1 << 28) - 1; + if (ticks > maxt) + ticks = maxt; + + si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, watchdog), ~0, ticks); + } +} + +/* trigger watchdog reset after ms milliseconds */ +void +si_watchdog_ms(si_t *sih, uint32 ms) +{ + si_watchdog(sih, wd_msticks * ms); +} + +uint32 si_watchdog_msticks(void) +{ + return wd_msticks; +} + +bool +si_taclear(si_t *sih, bool details) +{ + return FALSE; +} + + + +/* return the slow clock source - LPO, XTAL, or PCI */ +static uint +si_slowclk_src(si_info_t *sii) +{ + chipcregs_t *cc; + + ASSERT(SI_FAST(sii) || si_coreid(&sii->pub) == CC_CORE_ID); + + if (sii->pub.ccrev < 6) { + if ((BUSTYPE(sii->pub.bustype) == PCI_BUS) && + (OSL_PCI_READ_CONFIG(sii->osh, PCI_GPIO_OUT, sizeof(uint32)) & + PCI_CFG_GPIO_SCS)) + return (SCC_SS_PCI); + else + return (SCC_SS_XTAL); + } else if (sii->pub.ccrev < 10) { + cc = (chipcregs_t *)si_setcoreidx(&sii->pub, sii->curidx); + return (R_REG(sii->osh, &cc->slow_clk_ctl) & SCC_SS_MASK); + } else /* Insta-clock */ + return (SCC_SS_XTAL); +} + +/* return the ILP (slowclock) min or max frequency */ +static uint +si_slowclk_freq(si_info_t *sii, bool max_freq, chipcregs_t *cc) +{ + uint32 slowclk; + uint div; + + ASSERT(SI_FAST(sii) || si_coreid(&sii->pub) == CC_CORE_ID); + + /* shouldn't be here unless we've established the chip has dynamic clk control */ + ASSERT(R_REG(sii->osh, &cc->capabilities) & CC_CAP_PWR_CTL); + + slowclk = si_slowclk_src(sii); + if (sii->pub.ccrev < 6) { + if (slowclk == SCC_SS_PCI) + return (max_freq ? (PCIMAXFREQ / 64) : (PCIMINFREQ / 64)); + else + return (max_freq ? (XTALMAXFREQ / 32) : (XTALMINFREQ / 32)); + } else if (sii->pub.ccrev < 10) { + div = 4 * + (((R_REG(sii->osh, &cc->slow_clk_ctl) & SCC_CD_MASK) >> SCC_CD_SHIFT) + 1); + if (slowclk == SCC_SS_LPO) + return (max_freq ? LPOMAXFREQ : LPOMINFREQ); + else if (slowclk == SCC_SS_XTAL) + return (max_freq ? (XTALMAXFREQ / div) : (XTALMINFREQ / div)); + else if (slowclk == SCC_SS_PCI) + return (max_freq ? (PCIMAXFREQ / div) : (PCIMINFREQ / div)); + else + ASSERT(0); + } else { + /* Chipc rev 10 is InstaClock */ + div = R_REG(sii->osh, &cc->system_clk_ctl) >> SYCC_CD_SHIFT; + div = 4 * (div + 1); + return (max_freq ? XTALMAXFREQ : (XTALMINFREQ / div)); + } + return (0); +} + +static void +si_clkctl_setdelay(si_info_t *sii, void *chipcregs) +{ + chipcregs_t *cc = (chipcregs_t *)chipcregs; + uint slowmaxfreq, pll_delay, slowclk; + uint pll_on_delay, fref_sel_delay; + + pll_delay = PLL_DELAY; + + /* If the slow clock is not sourced by the xtal then add the xtal_on_delay + * since the xtal will also be powered down by dynamic clk control logic. + */ + + slowclk = si_slowclk_src(sii); + if (slowclk != SCC_SS_XTAL) + pll_delay += XTAL_ON_DELAY; + + /* Starting with 4318 it is ILP that is used for the delays */ + slowmaxfreq = si_slowclk_freq(sii, (sii->pub.ccrev >= 10) ? FALSE : TRUE, cc); + + pll_on_delay = ((slowmaxfreq * pll_delay) + 999999) / 1000000; + fref_sel_delay = ((slowmaxfreq * FREF_DELAY) + 999999) / 1000000; + + W_REG(sii->osh, &cc->pll_on_delay, pll_on_delay); + W_REG(sii->osh, &cc->fref_sel_delay, fref_sel_delay); +} + +/* initialize power control delay registers */ +void +si_clkctl_init(si_t *sih) +{ + si_info_t *sii; + uint origidx = 0; + chipcregs_t *cc; + bool fast; + + if (!CCCTL_ENAB(sih)) + return; + + sii = SI_INFO(sih); + fast = SI_FAST(sii); + if (!fast) { + origidx = sii->curidx; + if ((cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0)) == NULL) + return; + } else if ((cc = (chipcregs_t *)CCREGS_FAST(sii)) == NULL) + return; + ASSERT(cc != NULL); + + /* set all Instaclk chip ILP to 1 MHz */ + if (sih->ccrev >= 10) + SET_REG(sii->osh, &cc->system_clk_ctl, SYCC_CD_MASK, + (ILP_DIV_1MHZ << SYCC_CD_SHIFT)); + + si_clkctl_setdelay(sii, (void *)(uintptr)cc); + + if (!fast) + si_setcoreidx(sih, origidx); +} + + +/* change logical "focus" to the gpio core for optimized access */ +void * +si_gpiosetcore(si_t *sih) +{ + return (si_setcoreidx(sih, SI_CC_IDX)); +} + +/* + * mask & set gpiocontrol bits. + * If a gpiocontrol bit is set to 0, chipcommon controls the corresponding GPIO pin. + * If a gpiocontrol bit is set to 1, the GPIO pin is no longer a GPIO and becomes dedicated + * to some chip-specific purpose. + */ +uint32 +si_gpiocontrol(si_t *sih, uint32 mask, uint32 val, uint8 priority) +{ + uint regoff; + + regoff = 0; + + /* gpios could be shared on router platforms + * ignore reservation if it's high priority (e.g., test apps) + */ + if ((priority != GPIO_HI_PRIORITY) && + (BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) { + mask = priority ? (si_gpioreservation & mask) : + ((si_gpioreservation | mask) & ~(si_gpioreservation)); + val &= mask; + } + + regoff = OFFSETOF(chipcregs_t, gpiocontrol); + return (si_corereg(sih, SI_CC_IDX, regoff, mask, val)); +} + +/* mask&set gpio output enable bits */ +uint32 +si_gpioouten(si_t *sih, uint32 mask, uint32 val, uint8 priority) +{ + uint regoff; + + regoff = 0; + + /* gpios could be shared on router platforms + * ignore reservation if it's high priority (e.g., test apps) + */ + if ((priority != GPIO_HI_PRIORITY) && + (BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) { + mask = priority ? (si_gpioreservation & mask) : + ((si_gpioreservation | mask) & ~(si_gpioreservation)); + val &= mask; + } + + regoff = OFFSETOF(chipcregs_t, gpioouten); + return (si_corereg(sih, SI_CC_IDX, regoff, mask, val)); +} + +/* mask&set gpio output bits */ +uint32 +si_gpioout(si_t *sih, uint32 mask, uint32 val, uint8 priority) +{ + uint regoff; + + regoff = 0; + + /* gpios could be shared on router platforms + * ignore reservation if it's high priority (e.g., test apps) + */ + if ((priority != GPIO_HI_PRIORITY) && + (BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) { + mask = priority ? (si_gpioreservation & mask) : + ((si_gpioreservation | mask) & ~(si_gpioreservation)); + val &= mask; + } + + regoff = OFFSETOF(chipcregs_t, gpioout); + return (si_corereg(sih, SI_CC_IDX, regoff, mask, val)); +} + +/* reserve one gpio */ +uint32 +si_gpioreserve(si_t *sih, uint32 gpio_bitmask, uint8 priority) +{ + /* only cores on SI_BUS share GPIO's and only applcation users need to + * reserve/release GPIO + */ + if ((BUSTYPE(sih->bustype) != SI_BUS) || (!priority)) { + ASSERT((BUSTYPE(sih->bustype) == SI_BUS) && (priority)); + return 0xffffffff; + } + /* make sure only one bit is set */ + if ((!gpio_bitmask) || ((gpio_bitmask) & (gpio_bitmask - 1))) { + ASSERT((gpio_bitmask) && !((gpio_bitmask) & (gpio_bitmask - 1))); + return 0xffffffff; + } + + /* already reserved */ + if (si_gpioreservation & gpio_bitmask) + return 0xffffffff; + /* set reservation */ + si_gpioreservation |= gpio_bitmask; + + return si_gpioreservation; +} + +/* release one gpio */ +/* + * releasing the gpio doesn't change the current value on the GPIO last write value + * persists till some one overwrites it + */ + +uint32 +si_gpiorelease(si_t *sih, uint32 gpio_bitmask, uint8 priority) +{ + /* only cores on SI_BUS share GPIO's and only applcation users need to + * reserve/release GPIO + */ + if ((BUSTYPE(sih->bustype) != SI_BUS) || (!priority)) { + ASSERT((BUSTYPE(sih->bustype) == SI_BUS) && (priority)); + return 0xffffffff; + } + /* make sure only one bit is set */ + if ((!gpio_bitmask) || ((gpio_bitmask) & (gpio_bitmask - 1))) { + ASSERT((gpio_bitmask) && !((gpio_bitmask) & (gpio_bitmask - 1))); + return 0xffffffff; + } + + /* already released */ + if (!(si_gpioreservation & gpio_bitmask)) + return 0xffffffff; + + /* clear reservation */ + si_gpioreservation &= ~gpio_bitmask; + + return si_gpioreservation; +} + +/* return the current gpioin register value */ +uint32 +si_gpioin(si_t *sih) +{ + uint regoff; + + regoff = OFFSETOF(chipcregs_t, gpioin); + return (si_corereg(sih, SI_CC_IDX, regoff, 0, 0)); +} + +/* mask&set gpio interrupt polarity bits */ +uint32 +si_gpiointpolarity(si_t *sih, uint32 mask, uint32 val, uint8 priority) +{ + uint regoff; + + /* gpios could be shared on router platforms */ + if ((BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) { + mask = priority ? (si_gpioreservation & mask) : + ((si_gpioreservation | mask) & ~(si_gpioreservation)); + val &= mask; + } + + regoff = OFFSETOF(chipcregs_t, gpiointpolarity); + return (si_corereg(sih, SI_CC_IDX, regoff, mask, val)); +} + +/* mask&set gpio interrupt mask bits */ +uint32 +si_gpiointmask(si_t *sih, uint32 mask, uint32 val, uint8 priority) +{ + uint regoff; + + /* gpios could be shared on router platforms */ + if ((BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) { + mask = priority ? (si_gpioreservation & mask) : + ((si_gpioreservation | mask) & ~(si_gpioreservation)); + val &= mask; + } + + regoff = OFFSETOF(chipcregs_t, gpiointmask); + return (si_corereg(sih, SI_CC_IDX, regoff, mask, val)); +} + +/* assign the gpio to an led */ +uint32 +si_gpioled(si_t *sih, uint32 mask, uint32 val) +{ + if (sih->ccrev < 16) + return 0xffffffff; + + /* gpio led powersave reg */ + return (si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, gpiotimeroutmask), mask, val)); +} + +/* mask&set gpio timer val */ +uint32 +si_gpiotimerval(si_t *sih, uint32 mask, uint32 gpiotimerval) +{ + if (sih->ccrev < 16) + return 0xffffffff; + + return (si_corereg(sih, SI_CC_IDX, + OFFSETOF(chipcregs_t, gpiotimerval), mask, gpiotimerval)); +} + +uint32 +si_gpiopull(si_t *sih, bool updown, uint32 mask, uint32 val) +{ + uint offs; + + if (sih->ccrev < 20) + return 0xffffffff; + + offs = (updown ? OFFSETOF(chipcregs_t, gpiopulldown) : OFFSETOF(chipcregs_t, gpiopullup)); + return (si_corereg(sih, SI_CC_IDX, offs, mask, val)); +} + +uint32 +si_gpioevent(si_t *sih, uint regtype, uint32 mask, uint32 val) +{ + uint offs; + + if (sih->ccrev < 11) + return 0xffffffff; + + if (regtype == GPIO_REGEVT) + offs = OFFSETOF(chipcregs_t, gpioevent); + else if (regtype == GPIO_REGEVT_INTMSK) + offs = OFFSETOF(chipcregs_t, gpioeventintmask); + else if (regtype == GPIO_REGEVT_INTPOL) + offs = OFFSETOF(chipcregs_t, gpioeventintpolarity); + else + return 0xffffffff; + + return (si_corereg(sih, SI_CC_IDX, offs, mask, val)); +} + +void * +si_gpio_handler_register(si_t *sih, uint32 event, + bool level, gpio_handler_t cb, void *arg) +{ + si_info_t *sii; + gpioh_item_t *gi; + + ASSERT(event); + ASSERT(cb != NULL); + + sii = SI_INFO(sih); + if (sih->ccrev < 11) + return NULL; + + if ((gi = MALLOC(sii->osh, sizeof(gpioh_item_t))) == NULL) + return NULL; + + bzero(gi, sizeof(gpioh_item_t)); + gi->event = event; + gi->handler = cb; + gi->arg = arg; + gi->level = level; + + gi->next = sii->gpioh_head; + sii->gpioh_head = gi; + + return (void *)(gi); +} + +void +si_gpio_handler_unregister(si_t *sih, void *gpioh) +{ + si_info_t *sii; + gpioh_item_t *p, *n; + + sii = SI_INFO(sih); + if (sih->ccrev < 11) + return; + + ASSERT(sii->gpioh_head != NULL); + if ((void*)sii->gpioh_head == gpioh) { + sii->gpioh_head = sii->gpioh_head->next; + MFREE(sii->osh, gpioh, sizeof(gpioh_item_t)); + return; + } else { + p = sii->gpioh_head; + n = p->next; + while (n) { + if ((void*)n == gpioh) { + p->next = n->next; + MFREE(sii->osh, gpioh, sizeof(gpioh_item_t)); + return; + } + p = n; + n = n->next; + } + } + + ASSERT(0); /* Not found in list */ +} + +void +si_gpio_handler_process(si_t *sih) +{ + si_info_t *sii; + gpioh_item_t *h; + uint32 level = si_gpioin(sih); + uint32 levelp = si_gpiointpolarity(sih, 0, 0, 0); + uint32 edge = si_gpioevent(sih, GPIO_REGEVT, 0, 0); + uint32 edgep = si_gpioevent(sih, GPIO_REGEVT_INTPOL, 0, 0); + + sii = SI_INFO(sih); + for (h = sii->gpioh_head; h != NULL; h = h->next) { + if (h->handler) { + uint32 status = (h->level ? level : edge) & h->event; + uint32 polarity = (h->level ? levelp : edgep) & h->event; + + /* polarity bitval is opposite of status bitval */ + if (status ^ polarity) + h->handler(status, h->arg); + } + } + + si_gpioevent(sih, GPIO_REGEVT, edge, edge); /* clear edge-trigger status */ +} + +uint32 +si_gpio_int_enable(si_t *sih, bool enable) +{ + uint offs; + + if (sih->ccrev < 11) + return 0xffffffff; + + offs = OFFSETOF(chipcregs_t, intmask); + return (si_corereg(sih, SI_CC_IDX, offs, CI_GPIO, (enable ? CI_GPIO : 0))); +} + + +/* Return the size of the specified SOCRAM bank */ +static uint +socram_banksize(si_info_t *sii, sbsocramregs_t *regs, uint8 idx, uint8 mem_type) +{ + uint banksize, bankinfo; + uint bankidx = idx | (mem_type << SOCRAM_BANKIDX_MEMTYPE_SHIFT); + + ASSERT(mem_type <= SOCRAM_MEMTYPE_DEVRAM); + + W_REG(sii->osh, ®s->bankidx, bankidx); + bankinfo = R_REG(sii->osh, ®s->bankinfo); + banksize = SOCRAM_BANKINFO_SZBASE * ((bankinfo & SOCRAM_BANKINFO_SZMASK) + 1); + return banksize; +} + +void +si_socdevram(si_t *sih, bool set, uint8 *enable, uint8 *protect, uint8 *remap) +{ + si_info_t *sii; + uint origidx; + uint intr_val = 0; + sbsocramregs_t *regs; + bool wasup; + uint corerev; + + sii = SI_INFO(sih); + + /* Block ints and save current core */ + INTR_OFF(sii, intr_val); + origidx = si_coreidx(sih); + + if (!set) + *enable = *protect = *remap = 0; + + /* Switch to SOCRAM core */ + if (!(regs = si_setcore(sih, SOCRAM_CORE_ID, 0))) + goto done; + + /* Get info for determining size */ + if (!(wasup = si_iscoreup(sih))) + si_core_reset(sih, 0, 0); + + corerev = si_corerev(sih); + if (corerev >= 10) { + uint32 extcinfo; + uint8 nb; + uint8 i; + uint32 bankidx, bankinfo; + + extcinfo = R_REG(sii->osh, ®s->extracoreinfo); + nb = ((extcinfo & SOCRAM_DEVRAMBANK_MASK) >> SOCRAM_DEVRAMBANK_SHIFT); + for (i = 0; i < nb; i++) { + bankidx = i | (SOCRAM_MEMTYPE_DEVRAM << SOCRAM_BANKIDX_MEMTYPE_SHIFT); + W_REG(sii->osh, ®s->bankidx, bankidx); + bankinfo = R_REG(sii->osh, ®s->bankinfo); + if (set) { + bankinfo &= ~SOCRAM_BANKINFO_DEVRAMSEL_MASK; + bankinfo &= ~SOCRAM_BANKINFO_DEVRAMPRO_MASK; + bankinfo &= ~SOCRAM_BANKINFO_DEVRAMREMAP_MASK; + if (*enable) { + bankinfo |= (1 << SOCRAM_BANKINFO_DEVRAMSEL_SHIFT); + if (*protect) + bankinfo |= (1 << SOCRAM_BANKINFO_DEVRAMPRO_SHIFT); + if ((corerev >= 16) && *remap) + bankinfo |= + (1 << SOCRAM_BANKINFO_DEVRAMREMAP_SHIFT); + } + W_REG(sii->osh, ®s->bankinfo, bankinfo); + } + else if (i == 0) { + if (bankinfo & SOCRAM_BANKINFO_DEVRAMSEL_MASK) { + *enable = 1; + if (bankinfo & SOCRAM_BANKINFO_DEVRAMPRO_MASK) + *protect = 1; + if (bankinfo & SOCRAM_BANKINFO_DEVRAMREMAP_MASK) + *remap = 1; + } + } + } + } + + /* Return to previous state and core */ + if (!wasup) + si_core_disable(sih, 0); + si_setcoreidx(sih, origidx); + +done: + INTR_RESTORE(sii, intr_val); +} + +bool +si_socdevram_remap_isenb(si_t *sih) +{ + si_info_t *sii; + uint origidx; + uint intr_val = 0; + sbsocramregs_t *regs; + bool wasup, remap = FALSE; + uint corerev; + uint32 extcinfo; + uint8 nb; + uint8 i; + uint32 bankidx, bankinfo; + + sii = SI_INFO(sih); + + /* Block ints and save current core */ + INTR_OFF(sii, intr_val); + origidx = si_coreidx(sih); + + /* Switch to SOCRAM core */ + if (!(regs = si_setcore(sih, SOCRAM_CORE_ID, 0))) + goto done; + + /* Get info for determining size */ + if (!(wasup = si_iscoreup(sih))) + si_core_reset(sih, 0, 0); + + corerev = si_corerev(sih); + if (corerev >= 16) { + extcinfo = R_REG(sii->osh, ®s->extracoreinfo); + nb = ((extcinfo & SOCRAM_DEVRAMBANK_MASK) >> SOCRAM_DEVRAMBANK_SHIFT); + for (i = 0; i < nb; i++) { + bankidx = i | (SOCRAM_MEMTYPE_DEVRAM << SOCRAM_BANKIDX_MEMTYPE_SHIFT); + W_REG(sii->osh, ®s->bankidx, bankidx); + bankinfo = R_REG(sii->osh, ®s->bankinfo); + if (bankinfo & SOCRAM_BANKINFO_DEVRAMREMAP_MASK) { + remap = TRUE; + break; + } + } + } + + /* Return to previous state and core */ + if (!wasup) + si_core_disable(sih, 0); + si_setcoreidx(sih, origidx); + +done: + INTR_RESTORE(sii, intr_val); + return remap; +} + +bool +si_socdevram_pkg(si_t *sih) +{ + if (si_socdevram_size(sih) > 0) + return TRUE; + else + return FALSE; +} + +uint32 +si_socdevram_size(si_t *sih) +{ + si_info_t *sii; + uint origidx; + uint intr_val = 0; + uint32 memsize = 0; + sbsocramregs_t *regs; + bool wasup; + uint corerev; + + sii = SI_INFO(sih); + + /* Block ints and save current core */ + INTR_OFF(sii, intr_val); + origidx = si_coreidx(sih); + + /* Switch to SOCRAM core */ + if (!(regs = si_setcore(sih, SOCRAM_CORE_ID, 0))) + goto done; + + /* Get info for determining size */ + if (!(wasup = si_iscoreup(sih))) + si_core_reset(sih, 0, 0); + + corerev = si_corerev(sih); + if (corerev >= 10) { + uint32 extcinfo; + uint8 nb; + uint8 i; + + extcinfo = R_REG(sii->osh, ®s->extracoreinfo); + nb = (((extcinfo & SOCRAM_DEVRAMBANK_MASK) >> SOCRAM_DEVRAMBANK_SHIFT)); + for (i = 0; i < nb; i++) + memsize += socram_banksize(sii, regs, i, SOCRAM_MEMTYPE_DEVRAM); + } + + /* Return to previous state and core */ + if (!wasup) + si_core_disable(sih, 0); + si_setcoreidx(sih, origidx); + +done: + INTR_RESTORE(sii, intr_val); + + return memsize; +} + +uint32 +si_socdevram_remap_size(si_t *sih) +{ + si_info_t *sii; + uint origidx; + uint intr_val = 0; + uint32 memsize = 0, banksz; + sbsocramregs_t *regs; + bool wasup; + uint corerev; + uint32 extcinfo; + uint8 nb; + uint8 i; + uint32 bankidx, bankinfo; + + sii = SI_INFO(sih); + + /* Block ints and save current core */ + INTR_OFF(sii, intr_val); + origidx = si_coreidx(sih); + + /* Switch to SOCRAM core */ + if (!(regs = si_setcore(sih, SOCRAM_CORE_ID, 0))) + goto done; + + /* Get info for determining size */ + if (!(wasup = si_iscoreup(sih))) + si_core_reset(sih, 0, 0); + + corerev = si_corerev(sih); + if (corerev >= 16) { + extcinfo = R_REG(sii->osh, ®s->extracoreinfo); + nb = (((extcinfo & SOCRAM_DEVRAMBANK_MASK) >> SOCRAM_DEVRAMBANK_SHIFT)); + + /* + * FIX: A0 Issue: Max addressable is 512KB, instead 640KB + * Only four banks are accessible to ARM + */ + if ((corerev == 16) && (nb == 5)) + nb = 4; + + for (i = 0; i < nb; i++) { + bankidx = i | (SOCRAM_MEMTYPE_DEVRAM << SOCRAM_BANKIDX_MEMTYPE_SHIFT); + W_REG(sii->osh, ®s->bankidx, bankidx); + bankinfo = R_REG(sii->osh, ®s->bankinfo); + if (bankinfo & SOCRAM_BANKINFO_DEVRAMREMAP_MASK) { + banksz = socram_banksize(sii, regs, i, SOCRAM_MEMTYPE_DEVRAM); + memsize += banksz; + } else { + /* Account only consecutive banks for now */ + break; + } + } + } + + /* Return to previous state and core */ + if (!wasup) + si_core_disable(sih, 0); + si_setcoreidx(sih, origidx); + +done: + INTR_RESTORE(sii, intr_val); + + return memsize; +} + +/* Return the RAM size of the SOCRAM core */ +uint32 +si_socram_size(si_t *sih) +{ + si_info_t *sii; + uint origidx; + uint intr_val = 0; + + sbsocramregs_t *regs; + bool wasup; + uint corerev; + uint32 coreinfo; + uint memsize = 0; + + sii = SI_INFO(sih); + + /* Block ints and save current core */ + INTR_OFF(sii, intr_val); + origidx = si_coreidx(sih); + + /* Switch to SOCRAM core */ + if (!(regs = si_setcore(sih, SOCRAM_CORE_ID, 0))) + goto done; + + /* Get info for determining size */ + if (!(wasup = si_iscoreup(sih))) + si_core_reset(sih, 0, 0); + corerev = si_corerev(sih); + coreinfo = R_REG(sii->osh, ®s->coreinfo); + + /* Calculate size from coreinfo based on rev */ + if (corerev == 0) + memsize = 1 << (16 + (coreinfo & SRCI_MS0_MASK)); + else if (corerev < 3) { + memsize = 1 << (SR_BSZ_BASE + (coreinfo & SRCI_SRBSZ_MASK)); + memsize *= (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; + } else if ((corerev <= 7) || (corerev == 12)) { + uint nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; + uint bsz = (coreinfo & SRCI_SRBSZ_MASK); + uint lss = (coreinfo & SRCI_LSS_MASK) >> SRCI_LSS_SHIFT; + if (lss != 0) + nb --; + memsize = nb * (1 << (bsz + SR_BSZ_BASE)); + if (lss != 0) + memsize += (1 << ((lss - 1) + SR_BSZ_BASE)); + } else { + uint8 i; + uint nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; + for (i = 0; i < nb; i++) + memsize += socram_banksize(sii, regs, i, SOCRAM_MEMTYPE_RAM); + } + + /* Return to previous state and core */ + if (!wasup) + si_core_disable(sih, 0); + si_setcoreidx(sih, origidx); + +done: + INTR_RESTORE(sii, intr_val); + + return memsize; +} + + +/* Return the TCM-RAM size of the ARMCR4 core. */ +uint32 +si_tcm_size(si_t *sih) +{ + si_info_t *sii; + uint origidx; + uint intr_val = 0; + uint8 *regs; + bool wasup; + uint32 corecap; + uint memsize = 0; + uint32 nab = 0; + uint32 nbb = 0; + uint32 totb = 0; + uint32 bxinfo = 0; + uint32 idx = 0; + uint32 *arm_cap_reg; + uint32 *arm_bidx; + uint32 *arm_binfo; + + sii = SI_INFO(sih); + + /* Block ints and save current core */ + INTR_OFF(sii, intr_val); + origidx = si_coreidx(sih); + + /* Switch to CR4 core */ + if (!(regs = si_setcore(sih, ARMCR4_CORE_ID, 0))) + goto done; + + /* Get info for determining size. If in reset, come out of reset, + * but remain in halt + */ + if (!(wasup = si_iscoreup(sih))) + si_core_reset(sih, SICF_CPUHALT, SICF_CPUHALT); + + arm_cap_reg = (uint32 *)(regs + SI_CR4_CAP); + corecap = R_REG(sii->osh, arm_cap_reg); + + nab = (corecap & ARMCR4_TCBANB_MASK) >> ARMCR4_TCBANB_SHIFT; + nbb = (corecap & ARMCR4_TCBBNB_MASK) >> ARMCR4_TCBBNB_SHIFT; + totb = nab + nbb; + + arm_bidx = (uint32 *)(regs + SI_CR4_BANKIDX); + arm_binfo = (uint32 *)(regs + SI_CR4_BANKINFO); + for (idx = 0; idx < totb; idx++) { + W_REG(sii->osh, arm_bidx, idx); + + bxinfo = R_REG(sii->osh, arm_binfo); + memsize += ((bxinfo & ARMCR4_BSZ_MASK) + 1) * ARMCR4_BSZ_MULT; + } + + /* Return to previous state and core */ + if (!wasup) + si_core_disable(sih, 0); + si_setcoreidx(sih, origidx); + +done: + INTR_RESTORE(sii, intr_val); + + return memsize; +} + +uint32 +si_socram_srmem_size(si_t *sih) +{ + si_info_t *sii; + uint origidx; + uint intr_val = 0; + + sbsocramregs_t *regs; + bool wasup; + uint corerev; + uint32 coreinfo; + uint memsize = 0; + + if ((CHIPID(sih->chip) == BCM4334_CHIP_ID) && (CHIPREV(sih->chiprev) < 2)) { + return (32 * 1024); + } + + sii = SI_INFO(sih); + + /* Block ints and save current core */ + INTR_OFF(sii, intr_val); + origidx = si_coreidx(sih); + + /* Switch to SOCRAM core */ + if (!(regs = si_setcore(sih, SOCRAM_CORE_ID, 0))) + goto done; + + /* Get info for determining size */ + if (!(wasup = si_iscoreup(sih))) + si_core_reset(sih, 0, 0); + corerev = si_corerev(sih); + coreinfo = R_REG(sii->osh, ®s->coreinfo); + + /* Calculate size from coreinfo based on rev */ + if (corerev >= 16) { + uint8 i; + uint nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; + for (i = 0; i < nb; i++) { + W_REG(sii->osh, ®s->bankidx, i); + if (R_REG(sii->osh, ®s->bankinfo) & SOCRAM_BANKINFO_RETNTRAM_MASK) + memsize += socram_banksize(sii, regs, i, SOCRAM_MEMTYPE_RAM); + } + } + + /* Return to previous state and core */ + if (!wasup) + si_core_disable(sih, 0); + si_setcoreidx(sih, origidx); + +done: + INTR_RESTORE(sii, intr_val); + + return memsize; +} + + +void +si_btcgpiowar(si_t *sih) +{ + si_info_t *sii; + uint origidx; + uint intr_val = 0; + chipcregs_t *cc; + + sii = SI_INFO(sih); + + /* Make sure that there is ChipCommon core present && + * UART_TX is strapped to 1 + */ + if (!(sih->cccaps & CC_CAP_UARTGPIO)) + return; + + /* si_corereg cannot be used as we have to guarantee 8-bit read/writes */ + INTR_OFF(sii, intr_val); + + origidx = si_coreidx(sih); + + cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); + ASSERT(cc != NULL); + + W_REG(sii->osh, &cc->uart0mcr, R_REG(sii->osh, &cc->uart0mcr) | 0x04); + + /* restore the original index */ + si_setcoreidx(sih, origidx); + + INTR_RESTORE(sii, intr_val); +} + +void +si_chipcontrl_btshd0_4331(si_t *sih, bool on) +{ + si_info_t *sii; + chipcregs_t *cc; + uint origidx; + uint32 val; + uint intr_val = 0; + + sii = SI_INFO(sih); + + INTR_OFF(sii, intr_val); + + origidx = si_coreidx(sih); + + cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); + + val = R_REG(sii->osh, &cc->chipcontrol); + + /* bt_shd0 controls are same for 4331 chiprevs 0 and 1, packages 12x9 and 12x12 */ + if (on) { + /* Enable bt_shd0 on gpio4: */ + val |= (CCTRL4331_BT_SHD0_ON_GPIO4); + W_REG(sii->osh, &cc->chipcontrol, val); + } else { + val &= ~(CCTRL4331_BT_SHD0_ON_GPIO4); + W_REG(sii->osh, &cc->chipcontrol, val); + } + + /* restore the original index */ + si_setcoreidx(sih, origidx); + + INTR_RESTORE(sii, intr_val); +} + +void +si_chipcontrl_restore(si_t *sih, uint32 val) +{ + si_info_t *sii; + chipcregs_t *cc; + uint origidx; + + sii = SI_INFO(sih); + origidx = si_coreidx(sih); + cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); + W_REG(sii->osh, &cc->chipcontrol, val); + si_setcoreidx(sih, origidx); +} + +uint32 +si_chipcontrl_read(si_t *sih) +{ + si_info_t *sii; + chipcregs_t *cc; + uint origidx; + uint32 val; + + sii = SI_INFO(sih); + origidx = si_coreidx(sih); + cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); + val = R_REG(sii->osh, &cc->chipcontrol); + si_setcoreidx(sih, origidx); + return val; +} + +void +si_chipcontrl_epa4331(si_t *sih, bool on) +{ + si_info_t *sii; + chipcregs_t *cc; + uint origidx; + uint32 val; + + sii = SI_INFO(sih); + origidx = si_coreidx(sih); + + cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); + + val = R_REG(sii->osh, &cc->chipcontrol); + + if (on) { + if (sih->chippkg == 9 || sih->chippkg == 0xb) { + val |= (CCTRL4331_EXTPA_EN | CCTRL4331_EXTPA_ON_GPIO2_5); + /* Ext PA Controls for 4331 12x9 Package */ + W_REG(sii->osh, &cc->chipcontrol, val); + } else { + /* Ext PA Controls for 4331 12x12 Package */ + if (sih->chiprev > 0) { + W_REG(sii->osh, &cc->chipcontrol, val | + (CCTRL4331_EXTPA_EN) | (CCTRL4331_EXTPA_EN2)); + } else { + W_REG(sii->osh, &cc->chipcontrol, val | (CCTRL4331_EXTPA_EN)); + } + } + } else { + val &= ~(CCTRL4331_EXTPA_EN | CCTRL4331_EXTPA_EN2 | CCTRL4331_EXTPA_ON_GPIO2_5); + W_REG(sii->osh, &cc->chipcontrol, val); + } + + si_setcoreidx(sih, origidx); +} + +/* switch muxed pins, on: SROM, off: FEMCTRL */ +void +si_chipcontrl_srom4360(si_t *sih, bool on) +{ + si_info_t *sii; + chipcregs_t *cc; + uint origidx; + uint32 val; + + sii = SI_INFO(sih); + origidx = si_coreidx(sih); + + cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); + + val = R_REG(sii->osh, &cc->chipcontrol); + + if (on) { + val &= ~(CCTRL4360_SECI_MODE | + CCTRL4360_BTSWCTRL_MODE | + CCTRL4360_EXTRA_FEMCTRL_MODE | + CCTRL4360_BT_LGCY_MODE | + CCTRL4360_CORE2FEMCTRL4_ON); + + W_REG(sii->osh, &cc->chipcontrol, val); + } else { + } + + si_setcoreidx(sih, origidx); +} + +void +si_chipcontrl_epa4331_wowl(si_t *sih, bool enter_wowl) +{ + si_info_t *sii; + chipcregs_t *cc; + uint origidx; + uint32 val; + bool sel_chip; + + sel_chip = (CHIPID(sih->chip) == BCM4331_CHIP_ID) || + (CHIPID(sih->chip) == BCM43431_CHIP_ID); + sel_chip &= ((sih->chippkg == 9 || sih->chippkg == 0xb)); + + if (!sel_chip) + return; + + sii = SI_INFO(sih); + origidx = si_coreidx(sih); + + cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); + + val = R_REG(sii->osh, &cc->chipcontrol); + + if (enter_wowl) { + val |= CCTRL4331_EXTPA_EN; + W_REG(sii->osh, &cc->chipcontrol, val); + } else { + val |= (CCTRL4331_EXTPA_EN | CCTRL4331_EXTPA_ON_GPIO2_5); + W_REG(sii->osh, &cc->chipcontrol, val); + } + si_setcoreidx(sih, origidx); +} + +uint +si_pll_reset(si_t *sih) +{ + uint err = 0; + + return (err); +} + +/* Enable BT-COEX & Ex-PA for 4313 */ +void +si_epa_4313war(si_t *sih) +{ + si_info_t *sii; + chipcregs_t *cc; + uint origidx; + + sii = SI_INFO(sih); + origidx = si_coreidx(sih); + + cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); + + /* EPA Fix */ + W_REG(sii->osh, &cc->gpiocontrol, + R_REG(sii->osh, &cc->gpiocontrol) | GPIO_CTRL_EPA_EN_MASK); + + si_setcoreidx(sih, origidx); +} + +void +si_clk_pmu_htavail_set(si_t *sih, bool set_clear) +{ +} + +/* WL/BT control for 4313 btcombo boards >= P250 */ +void +si_btcombo_p250_4313_war(si_t *sih) +{ + si_info_t *sii; + chipcregs_t *cc; + uint origidx; + + sii = SI_INFO(sih); + origidx = si_coreidx(sih); + + cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); + W_REG(sii->osh, &cc->gpiocontrol, + R_REG(sii->osh, &cc->gpiocontrol) | GPIO_CTRL_5_6_EN_MASK); + + W_REG(sii->osh, &cc->gpioouten, + R_REG(sii->osh, &cc->gpioouten) | GPIO_CTRL_5_6_EN_MASK); + + si_setcoreidx(sih, origidx); +} +void +si_btc_enable_chipcontrol(si_t *sih) +{ + si_info_t *sii; + chipcregs_t *cc; + uint origidx; + + sii = SI_INFO(sih); + origidx = si_coreidx(sih); + + cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); + + /* BT fix */ + W_REG(sii->osh, &cc->chipcontrol, + R_REG(sii->osh, &cc->chipcontrol) | CC_BTCOEX_EN_MASK); + + si_setcoreidx(sih, origidx); +} +void +si_btcombo_43228_war(si_t *sih) +{ + si_info_t *sii; + chipcregs_t *cc; + uint origidx; + + sii = SI_INFO(sih); + origidx = si_coreidx(sih); + + cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); + + W_REG(sii->osh, &cc->gpioouten, GPIO_CTRL_7_6_EN_MASK); + W_REG(sii->osh, &cc->gpioout, GPIO_OUT_7_EN_MASK); + + si_setcoreidx(sih, origidx); +} + +/* check if the device is removed */ +bool +si_deviceremoved(si_t *sih) +{ + uint32 w; + si_info_t *sii; + + sii = SI_INFO(sih); + + switch (BUSTYPE(sih->bustype)) { + case PCI_BUS: + ASSERT(sii->osh != NULL); + w = OSL_PCI_READ_CONFIG(sii->osh, PCI_CFG_VID, sizeof(uint32)); + if ((w & 0xFFFF) != VENDOR_BROADCOM) + return TRUE; + break; + } + return FALSE; +} + +bool +si_is_sprom_available(si_t *sih) +{ + if (sih->ccrev >= 31) { + si_info_t *sii; + uint origidx; + chipcregs_t *cc; + uint32 sromctrl; + + if ((sih->cccaps & CC_CAP_SROM) == 0) + return FALSE; + + sii = SI_INFO(sih); + origidx = sii->curidx; + cc = si_setcoreidx(sih, SI_CC_IDX); + sromctrl = R_REG(sii->osh, &cc->sromcontrol); + si_setcoreidx(sih, origidx); + return (sromctrl & SRC_PRESENT); + } + + switch (CHIPID(sih->chip)) { + case BCM4312_CHIP_ID: + return ((sih->chipst & CST4312_SPROM_OTP_SEL_MASK) != CST4312_OTP_SEL); + case BCM4325_CHIP_ID: + return (sih->chipst & CST4325_SPROM_SEL) != 0; + case BCM4322_CHIP_ID: case BCM43221_CHIP_ID: case BCM43231_CHIP_ID: + case BCM43222_CHIP_ID: case BCM43111_CHIP_ID: case BCM43112_CHIP_ID: + case BCM4342_CHIP_ID: { + uint32 spromotp; + spromotp = (sih->chipst & CST4322_SPROM_OTP_SEL_MASK) >> + CST4322_SPROM_OTP_SEL_SHIFT; + return (spromotp & CST4322_SPROM_PRESENT) != 0; + } + case BCM4329_CHIP_ID: + return (sih->chipst & CST4329_SPROM_SEL) != 0; + case BCM4315_CHIP_ID: + return (sih->chipst & CST4315_SPROM_SEL) != 0; + case BCM4319_CHIP_ID: + return (sih->chipst & CST4319_SPROM_SEL) != 0; + case BCM4336_CHIP_ID: + case BCM43362_CHIP_ID: + return (sih->chipst & CST4336_SPROM_PRESENT) != 0; + case BCM4330_CHIP_ID: + return (sih->chipst & CST4330_SPROM_PRESENT) != 0; + case BCM4313_CHIP_ID: + return (sih->chipst & CST4313_SPROM_PRESENT) != 0; + case BCM4331_CHIP_ID: + case BCM43431_CHIP_ID: + return (sih->chipst & CST4331_SPROM_PRESENT) != 0; + case BCM43239_CHIP_ID: + return ((sih->chipst & CST43239_SPROM_MASK) && + !(sih->chipst & CST43239_SFLASH_MASK)); + case BCM4324_CHIP_ID: + return ((sih->chipst & CST4324_SPROM_MASK) && + !(sih->chipst & CST4324_SFLASH_MASK)); + case BCM4335_CHIP_ID: + return ((sih->chipst & CST4335_SPROM_MASK) && + !(sih->chipst & CST4335_SFLASH_MASK)); + case BCM43131_CHIP_ID: + case BCM43217_CHIP_ID: + case BCM43227_CHIP_ID: + case BCM43228_CHIP_ID: + case BCM43428_CHIP_ID: + return (sih->chipst & CST43228_OTP_PRESENT) != CST43228_OTP_PRESENT; + default: + return TRUE; + } +} + + +uint32 si_get_sromctl(si_t *sih) +{ + chipcregs_t *cc; + uint origidx; + uint32 sromctl; + osl_t *osh; + + osh = si_osh(sih); + origidx = si_coreidx(sih); + cc = si_setcoreidx(sih, SI_CC_IDX); + ASSERT((uintptr)cc); + + sromctl = R_REG(osh, &cc->sromcontrol); + + /* return to the original core */ + si_setcoreidx(sih, origidx); + return sromctl; +} + +int si_set_sromctl(si_t *sih, uint32 value) +{ + chipcregs_t *cc; + uint origidx; + osl_t *osh; + + osh = si_osh(sih); + origidx = si_coreidx(sih); + cc = si_setcoreidx(sih, SI_CC_IDX); + ASSERT((uintptr)cc); + + /* get chipcommon rev */ + if (si_corerev(sih) < 32) + return BCME_UNSUPPORTED; + + W_REG(osh, &cc->sromcontrol, value); + + /* return to the original core */ + si_setcoreidx(sih, origidx); + return BCME_OK; + +} diff --git a/drivers/net/wireless/bcmdhd/siutils_priv.h b/drivers/net/wireless/bcmdhd/siutils_priv.h new file mode 100644 index 00000000..9a3270f7 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/siutils_priv.h @@ -0,0 +1,246 @@ +/* + * Include file private to the SOC Interconnect support files. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: siutils_priv.h 309193 2012-01-19 00:03:57Z $ + */ + +#ifndef _siutils_priv_h_ +#define _siutils_priv_h_ + +#define SI_ERROR(args) + +#define SI_MSG(args) + +#ifdef BCMDBG_SI +#define SI_VMSG(args) printf args +#else +#define SI_VMSG(args) +#endif + +#define IS_SIM(chippkg) ((chippkg == HDLSIM_PKG_ID) || (chippkg == HWSIM_PKG_ID)) + +typedef uint32 (*si_intrsoff_t)(void *intr_arg); +typedef void (*si_intrsrestore_t)(void *intr_arg, uint32 arg); +typedef bool (*si_intrsenabled_t)(void *intr_arg); + +typedef struct gpioh_item { + void *arg; + bool level; + gpio_handler_t handler; + uint32 event; + struct gpioh_item *next; +} gpioh_item_t; + +/* misc si info needed by some of the routines */ +typedef struct si_info { + struct si_pub pub; /* back plane public state (must be first field) */ + + void *osh; /* osl os handle */ + void *sdh; /* bcmsdh handle */ + + uint dev_coreid; /* the core provides driver functions */ + void *intr_arg; /* interrupt callback function arg */ + si_intrsoff_t intrsoff_fn; /* turns chip interrupts off */ + si_intrsrestore_t intrsrestore_fn; /* restore chip interrupts */ + si_intrsenabled_t intrsenabled_fn; /* check if interrupts are enabled */ + + void *pch; /* PCI/E core handle */ + + gpioh_item_t *gpioh_head; /* GPIO event handlers list */ + + bool memseg; /* flag to toggle MEM_SEG register */ + + char *vars; + uint varsz; + + void *curmap; /* current regs va */ + void *regs[SI_MAXCORES]; /* other regs va */ + + uint curidx; /* current core index */ + uint numcores; /* # discovered cores */ + uint coreid[SI_MAXCORES]; /* id of each core */ + uint32 coresba[SI_MAXCORES]; /* backplane address of each core */ + void *regs2[SI_MAXCORES]; /* va of each core second register set (usbh20) */ + uint32 coresba2[SI_MAXCORES]; /* address of each core second register set (usbh20) */ + uint32 coresba_size[SI_MAXCORES]; /* backplane address space size */ + uint32 coresba2_size[SI_MAXCORES]; /* second address space size */ + + void *curwrap; /* current wrapper va */ + void *wrappers[SI_MAXCORES]; /* other cores wrapper va */ + uint32 wrapba[SI_MAXCORES]; /* address of controlling wrapper */ + + uint32 cia[SI_MAXCORES]; /* erom cia entry for each core */ + uint32 cib[SI_MAXCORES]; /* erom cia entry for each core */ + uint32 oob_router; /* oob router registers for axi */ +} si_info_t; + +#define SI_INFO(sih) (si_info_t *)(uintptr)sih + +#define GOODCOREADDR(x, b) (((x) >= (b)) && ((x) < ((b) + SI_MAXCORES * SI_CORE_SIZE)) && \ + ISALIGNED((x), SI_CORE_SIZE)) +#define GOODREGS(regs) ((regs) != NULL && ISALIGNED((uintptr)(regs), SI_CORE_SIZE)) +#define BADCOREADDR 0 +#define GOODIDX(idx) (((uint)idx) < SI_MAXCORES) +#define NOREV -1 /* Invalid rev */ + +#define PCI(si) ((BUSTYPE((si)->pub.bustype) == PCI_BUS) && \ + ((si)->pub.buscoretype == PCI_CORE_ID)) + +#define PCIE_GEN1(si) ((BUSTYPE((si)->pub.bustype) == PCI_BUS) && \ + ((si)->pub.buscoretype == PCIE_CORE_ID)) + +#define PCIE_GEN2(si) ((BUSTYPE((si)->pub.bustype) == PCI_BUS) && \ + ((si)->pub.buscoretype == PCIE2_CORE_ID)) + +#define PCIE(si) (PCIE_GEN1(si) || PCIE_GEN2(si)) + +#define PCMCIA(si) ((BUSTYPE((si)->pub.bustype) == PCMCIA_BUS) && ((si)->memseg == TRUE)) + +/* Newer chips can access PCI/PCIE and CC core without requiring to change + * PCI BAR0 WIN + */ +#define SI_FAST(si) (PCIE(si) || (PCI(si) && ((si)->pub.buscorerev >= 13))) + +#define PCIEREGS(si) (((char *)((si)->curmap) + PCI_16KB0_PCIREGS_OFFSET)) +#define CCREGS_FAST(si) (((char *)((si)->curmap) + PCI_16KB0_CCREGS_OFFSET)) + +/* + * Macros to disable/restore function core(D11, ENET, ILINE20, etc) interrupts before/ + * after core switching to avoid invalid register accesss inside ISR. + */ +#define INTR_OFF(si, intr_val) \ + if ((si)->intrsoff_fn && (si)->coreid[(si)->curidx] == (si)->dev_coreid) { \ + intr_val = (*(si)->intrsoff_fn)((si)->intr_arg); } +#define INTR_RESTORE(si, intr_val) \ + if ((si)->intrsrestore_fn && (si)->coreid[(si)->curidx] == (si)->dev_coreid) { \ + (*(si)->intrsrestore_fn)((si)->intr_arg, intr_val); } + +/* dynamic clock control defines */ +#define LPOMINFREQ 25000 /* low power oscillator min */ +#define LPOMAXFREQ 43000 /* low power oscillator max */ +#define XTALMINFREQ 19800000 /* 20 MHz - 1% */ +#define XTALMAXFREQ 20200000 /* 20 MHz + 1% */ +#define PCIMINFREQ 25000000 /* 25 MHz */ +#define PCIMAXFREQ 34000000 /* 33 MHz + fudge */ + +#define ILP_DIV_5MHZ 0 /* ILP = 5 MHz */ +#define ILP_DIV_1MHZ 4 /* ILP = 1 MHz */ + +#define PCI_FORCEHT(si) \ + (((PCIE_GEN1(si)) && (si->pub.chip == BCM4311_CHIP_ID) && ((si->pub.chiprev <= 1))) || \ + ((PCI(si) || PCIE_GEN1(si)) && (si->pub.chip == BCM4321_CHIP_ID)) || \ + (PCIE_GEN1(si) && (si->pub.chip == BCM4716_CHIP_ID)) || \ + (PCIE_GEN1(si) && (si->pub.chip == BCM4748_CHIP_ID))) + +/* GPIO Based LED powersave defines */ +#define DEFAULT_GPIO_ONTIME 10 /* Default: 10% on */ +#define DEFAULT_GPIO_OFFTIME 90 /* Default: 10% on */ + +#ifndef DEFAULT_GPIOTIMERVAL +#define DEFAULT_GPIOTIMERVAL ((DEFAULT_GPIO_ONTIME << GPIO_ONTIME_SHIFT) | DEFAULT_GPIO_OFFTIME) +#endif + +/* Silicon Backplane externs */ +extern void sb_scan(si_t *sih, void *regs, uint devid); +extern uint sb_coreid(si_t *sih); +extern uint sb_intflag(si_t *sih); +extern uint sb_flag(si_t *sih); +extern void sb_setint(si_t *sih, int siflag); +extern uint sb_corevendor(si_t *sih); +extern uint sb_corerev(si_t *sih); +extern uint sb_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val); +extern bool sb_iscoreup(si_t *sih); +extern void *sb_setcoreidx(si_t *sih, uint coreidx); +extern uint32 sb_core_cflags(si_t *sih, uint32 mask, uint32 val); +extern void sb_core_cflags_wo(si_t *sih, uint32 mask, uint32 val); +extern uint32 sb_core_sflags(si_t *sih, uint32 mask, uint32 val); +extern void sb_commit(si_t *sih); +extern uint32 sb_base(uint32 admatch); +extern uint32 sb_size(uint32 admatch); +extern void sb_core_reset(si_t *sih, uint32 bits, uint32 resetbits); +extern void sb_core_disable(si_t *sih, uint32 bits); +extern uint32 sb_addrspace(si_t *sih, uint asidx); +extern uint32 sb_addrspacesize(si_t *sih, uint asidx); +extern int sb_numaddrspaces(si_t *sih); + +extern uint32 sb_set_initiator_to(si_t *sih, uint32 to, uint idx); + +extern bool sb_taclear(si_t *sih, bool details); + + +/* Wake-on-wireless-LAN (WOWL) */ +extern bool sb_pci_pmecap(si_t *sih); +struct osl_info; +extern bool sb_pci_fastpmecap(struct osl_info *osh); +extern bool sb_pci_pmeclr(si_t *sih); +extern void sb_pci_pmeen(si_t *sih); +extern uint sb_pcie_readreg(void *sih, uint addrtype, uint offset); + +/* AMBA Interconnect exported externs */ +extern si_t *ai_attach(uint pcidev, osl_t *osh, void *regs, uint bustype, + void *sdh, char **vars, uint *varsz); +extern si_t *ai_kattach(osl_t *osh); +extern void ai_scan(si_t *sih, void *regs, uint devid); + +extern uint ai_flag(si_t *sih); +extern void ai_setint(si_t *sih, int siflag); +extern uint ai_coreidx(si_t *sih); +extern uint ai_corevendor(si_t *sih); +extern uint ai_corerev(si_t *sih); +extern bool ai_iscoreup(si_t *sih); +extern void *ai_setcoreidx(si_t *sih, uint coreidx); +extern uint32 ai_core_cflags(si_t *sih, uint32 mask, uint32 val); +extern void ai_core_cflags_wo(si_t *sih, uint32 mask, uint32 val); +extern uint32 ai_core_sflags(si_t *sih, uint32 mask, uint32 val); +extern uint ai_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val); +extern void ai_core_reset(si_t *sih, uint32 bits, uint32 resetbits); +extern void ai_core_disable(si_t *sih, uint32 bits); +extern int ai_numaddrspaces(si_t *sih); +extern uint32 ai_addrspace(si_t *sih, uint asidx); +extern uint32 ai_addrspacesize(si_t *sih, uint asidx); +extern void ai_coreaddrspaceX(si_t *sih, uint asidx, uint32 *addr, uint32 *size); +extern uint ai_wrap_reg(si_t *sih, uint32 offset, uint32 mask, uint32 val); + + + +#define ub_scan(a, b, c) do {} while (0) +#define ub_flag(a) (0) +#define ub_setint(a, b) do {} while (0) +#define ub_coreidx(a) (0) +#define ub_corevendor(a) (0) +#define ub_corerev(a) (0) +#define ub_iscoreup(a) (0) +#define ub_setcoreidx(a, b) (0) +#define ub_core_cflags(a, b, c) (0) +#define ub_core_cflags_wo(a, b, c) do {} while (0) +#define ub_core_sflags(a, b, c) (0) +#define ub_corereg(a, b, c, d, e) (0) +#define ub_core_reset(a, b, c) do {} while (0) +#define ub_core_disable(a, b) do {} while (0) +#define ub_numaddrspaces(a) (0) +#define ub_addrspace(a, b) (0) +#define ub_addrspacesize(a, b) (0) +#define ub_view(a, b) do {} while (0) +#define ub_dumpregs(a, b) do {} while (0) + +#endif /* _siutils_priv_h_ */ diff --git a/drivers/net/wireless/bcmdhd/uamp_api.h b/drivers/net/wireless/bcmdhd/uamp_api.h new file mode 100644 index 00000000..673dce08 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/uamp_api.h @@ -0,0 +1,176 @@ +/* + * Name: uamp_api.h + * + * Description: Universal AMP API + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: uamp_api.h 294267 2011-11-04 23:41:52Z $ + * + */ +#ifndef UAMP_API_H +#define UAMP_API_H + + +#include "typedefs.h" + + +/***************************************************************************** +** Constant and Type Definitions +****************************************************************************** +*/ + +#define BT_API + +/* Types. */ +typedef bool BOOLEAN; +typedef uint8 UINT8; +typedef uint16 UINT16; + + +/* UAMP identifiers */ +#define UAMP_ID_1 1 +#define UAMP_ID_2 2 +typedef UINT8 tUAMP_ID; + +/* UAMP event ids (used by UAMP_CBACK) */ +#define UAMP_EVT_RX_READY 0 /* Data from AMP controller is ready to be read */ +#define UAMP_EVT_CTLR_REMOVED 1 /* Controller removed */ +#define UAMP_EVT_CTLR_READY 2 /* Controller added/ready */ +typedef UINT8 tUAMP_EVT; + + +/* UAMP Channels */ +#define UAMP_CH_HCI_CMD 0 /* HCI Command channel */ +#define UAMP_CH_HCI_EVT 1 /* HCI Event channel */ +#define UAMP_CH_HCI_DATA 2 /* HCI ACL Data channel */ +typedef UINT8 tUAMP_CH; + +/* tUAMP_EVT_DATA: union for event-specific data, used by UAMP_CBACK */ +typedef union { + tUAMP_CH channel; /* UAMP_EVT_RX_READY: channel for which rx occured */ +} tUAMP_EVT_DATA; + + +/***************************************************************************** +** +** Function: UAMP_CBACK +** +** Description: Callback for events. Register callback using UAMP_Init. +** +** Parameters amp_id: AMP device identifier that generated the event +** amp_evt: event id +** p_amp_evt_data: pointer to event-specific data +** +****************************************************************************** +*/ +typedef void (*tUAMP_CBACK)(tUAMP_ID amp_id, tUAMP_EVT amp_evt, tUAMP_EVT_DATA *p_amp_evt_data); + +/***************************************************************************** +** external function declarations +****************************************************************************** +*/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************************************************************** +** +** Function: UAMP_Init +** +** Description: Initialize UAMP driver +** +** Parameters p_cback: Callback function for UAMP event notification +** +****************************************************************************** +*/ +BT_API BOOLEAN UAMP_Init(tUAMP_CBACK p_cback); + + +/***************************************************************************** +** +** Function: UAMP_Open +** +** Description: Open connection to local AMP device. +** +** Parameters app_id: Application specific AMP identifer. This value +** will be included in AMP messages sent to the +** BTU task, to identify source of the message +** +****************************************************************************** +*/ +BT_API BOOLEAN UAMP_Open(tUAMP_ID amp_id); + +/***************************************************************************** +** +** Function: UAMP_Close +** +** Description: Close connection to local AMP device. +** +** Parameters app_id: Application specific AMP identifer. +** +****************************************************************************** +*/ +BT_API void UAMP_Close(tUAMP_ID amp_id); + + +/***************************************************************************** +** +** Function: UAMP_Write +** +** Description: Send buffer to AMP device. Frees GKI buffer when done. +** +** +** Parameters: app_id: AMP identifer. +** p_buf: pointer to buffer to write +** num_bytes: number of bytes to write +** channel: UAMP_CH_HCI_ACL, or UAMP_CH_HCI_CMD +** +** Returns: number of bytes written +** +****************************************************************************** +*/ +BT_API UINT16 UAMP_Write(tUAMP_ID amp_id, UINT8 *p_buf, UINT16 num_bytes, tUAMP_CH channel); + +/***************************************************************************** +** +** Function: UAMP_Read +** +** Description: Read incoming data from AMP. Call after receiving a +** UAMP_EVT_RX_READY callback event. +** +** Parameters: app_id: AMP identifer. +** p_buf: pointer to buffer for holding incoming AMP data +** buf_size: size of p_buf +** channel: UAMP_CH_HCI_ACL, or UAMP_CH_HCI_EVT +** +** Returns: number of bytes read +** +****************************************************************************** +*/ +BT_API UINT16 UAMP_Read(tUAMP_ID amp_id, UINT8 *p_buf, UINT16 buf_size, tUAMP_CH channel); + +#ifdef __cplusplus +} +#endif + +#endif /* UAMP_API_H */ diff --git a/drivers/net/wireless/bcmdhd/wl_android.c b/drivers/net/wireless/bcmdhd/wl_android.c new file mode 100644 index 00000000..26f88e24 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wl_android.c @@ -0,0 +1,908 @@ +/* + * Linux cfg80211 driver - Android related functions + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_android.c 379859 2013-01-19 13:16:55Z $ + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef WL_CFG80211 +#include +#endif +#if defined(CONFIG_WIFI_CONTROL_FUNC) +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) +#include +#else +#include +#endif +#endif /* CONFIG_WIFI_CONTROL_FUNC */ + +/* + * Android private command strings, PLEASE define new private commands here + * so they can be updated easily in the future (if needed) + */ + +#define CMD_START "START" +#define CMD_STOP "STOP" +#define CMD_SCAN_ACTIVE "SCAN-ACTIVE" +#define CMD_SCAN_PASSIVE "SCAN-PASSIVE" +#define CMD_RSSI "RSSI" +#define CMD_LINKSPEED "LINKSPEED" +#define CMD_RXFILTER_START "RXFILTER-START" +#define CMD_RXFILTER_STOP "RXFILTER-STOP" +#define CMD_RXFILTER_ADD "RXFILTER-ADD" +#define CMD_RXFILTER_REMOVE "RXFILTER-REMOVE" +#define CMD_BTCOEXSCAN_START "BTCOEXSCAN-START" +#define CMD_BTCOEXSCAN_STOP "BTCOEXSCAN-STOP" +#define CMD_BTCOEXMODE "BTCOEXMODE" +#define CMD_SETSUSPENDOPT "SETSUSPENDOPT" +#define CMD_SETSUSPENDMODE "SETSUSPENDMODE" +#define CMD_P2P_DEV_ADDR "P2P_DEV_ADDR" +#define CMD_SETFWPATH "SETFWPATH" +#define CMD_SETBAND "SETBAND" +#define CMD_GETBAND "GETBAND" +#define CMD_COUNTRY "COUNTRY" +#define CMD_P2P_SET_NOA "P2P_SET_NOA" +#if !defined WL_ENABLE_P2P_IF +#define CMD_P2P_GET_NOA "P2P_GET_NOA" +#endif +#define CMD_P2P_SET_PS "P2P_SET_PS" +#define CMD_SET_AP_WPS_P2P_IE "SET_AP_WPS_P2P_IE" + + +#ifdef PNO_SUPPORT +#define CMD_PNOSSIDCLR_SET "PNOSSIDCLR" +#define CMD_PNOSETUP_SET "PNOSETUP " +#define CMD_PNOENABLE_SET "PNOFORCE" +#define CMD_PNODEBUG_SET "PNODEBUG" + +#define PNO_TLV_PREFIX 'S' +#define PNO_TLV_VERSION '1' +#define PNO_TLV_SUBVERSION '2' +#define PNO_TLV_RESERVED '0' +#define PNO_TLV_TYPE_SSID_IE 'S' +#define PNO_TLV_TYPE_TIME 'T' +#define PNO_TLV_FREQ_REPEAT 'R' +#define PNO_TLV_FREQ_EXPO_MAX 'M' + +typedef struct cmd_tlv { + char prefix; + char version; + char subver; + char reserved; +} cmd_tlv_t; +#endif /* PNO_SUPPORT */ + +typedef struct android_wifi_priv_cmd { + char *buf; + int used_len; + int total_len; +} android_wifi_priv_cmd; + +/** + * Extern function declarations (TODO: move them to dhd_linux.h) + */ +void dhd_customer_gpio_wlan_ctrl(int onoff); +int dhd_dev_reset(struct net_device *dev, uint8 flag); +int dhd_dev_init_ioctl(struct net_device *dev); +#ifdef WL_CFG80211 +int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr); +int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command); +#else +int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr) +{ return 0; } +int wl_cfg80211_set_p2p_noa(struct net_device *net, char* buf, int len) +{ return 0; } +int wl_cfg80211_get_p2p_noa(struct net_device *net, char* buf, int len) +{ return 0; } +int wl_cfg80211_set_p2p_ps(struct net_device *net, char* buf, int len) +{ return 0; } +#endif /* WL_CFG80211 */ +extern int dhd_os_check_if_up(void *dhdp); +extern void *bcmsdh_get_drvdata(void); +#if defined(PROP_TXSTATUS) && !defined(PROP_TXSTATUS_VSDB) +extern int dhd_wlfc_init(dhd_pub_t *dhd); +extern void dhd_wlfc_deinit(dhd_pub_t *dhd); +#endif + +extern bool ap_fw_loaded; +#if defined(CUSTOMER_HW2) +extern char iface_name[IFNAMSIZ]; +#endif + +#define WIFI_TURNOFF_DELAY 0 +/** + * Local (static) functions and variables + */ + +/* Initialize g_wifi_on to 1 so dhd_bus_start will be called for the first + * time (only) in dhd_open, subsequential wifi on will be handled by + * wl_android_wifi_on + */ +static int g_wifi_on = TRUE; + +/** + * Local (static) function definitions + */ +static int wl_android_get_link_speed(struct net_device *net, char *command, int total_len) +{ + int link_speed; + int bytes_written; + int error; + + error = wldev_get_link_speed(net, &link_speed); + if (error) + return -1; + + /* Convert Kbps to Android Mbps */ + link_speed = link_speed / 1000; + bytes_written = snprintf(command, total_len, "LinkSpeed %d", link_speed); + DHD_INFO(("%s: command result is %s\n", __FUNCTION__, command)); + return bytes_written; +} + +static int wl_android_get_rssi(struct net_device *net, char *command, int total_len) +{ + wlc_ssid_t ssid = {0}; + int rssi; + int bytes_written = 0; + int error; + + error = wldev_get_rssi(net, &rssi); + if (error) + return -1; + + error = wldev_get_ssid(net, &ssid); + if (error) + return -1; + if ((ssid.SSID_len == 0) || (ssid.SSID_len > DOT11_MAX_SSID_LEN)) { + DHD_ERROR(("%s: wldev_get_ssid failed\n", __FUNCTION__)); + } else { + memcpy(command, ssid.SSID, ssid.SSID_len); + bytes_written = ssid.SSID_len; + } + bytes_written += snprintf(&command[bytes_written], total_len, " rssi %d", rssi); + DHD_INFO(("%s: command result is %s (%d)\n", __FUNCTION__, command, bytes_written)); + return bytes_written; +} + +static int wl_android_set_suspendopt(struct net_device *dev, char *command, int total_len) +{ + int suspend_flag; + int ret_now; + int ret = 0; + + suspend_flag = *(command + strlen(CMD_SETSUSPENDOPT) + 1) - '0'; + + if (suspend_flag != 0) + suspend_flag = 1; + ret_now = net_os_set_suspend_disable(dev, suspend_flag); + + if (ret_now != suspend_flag) { + if (!(ret = net_os_set_suspend(dev, ret_now, 1))) + DHD_INFO(("%s: Suspend Flag %d -> %d\n", + __FUNCTION__, ret_now, suspend_flag)); + else + DHD_ERROR(("%s: failed %d\n", __FUNCTION__, ret)); + } + return ret; +} + +static int wl_android_set_suspendmode(struct net_device *dev, char *command, int total_len) +{ + int ret = 0; + +#if !defined(CONFIG_HAS_EARLYSUSPEND) || !defined(DHD_USE_EARLYSUSPEND) + int suspend_flag; + + suspend_flag = *(command + strlen(CMD_SETSUSPENDMODE) + 1) - '0'; + + if (suspend_flag != 0) + suspend_flag = 1; + + if (!(ret = net_os_set_suspend(dev, suspend_flag, 0))) + DHD_INFO(("%s: Suspend Mode %d\n", __FUNCTION__, suspend_flag)); + else + DHD_ERROR(("%s: failed %d\n", __FUNCTION__, ret)); +#endif + return ret; +} + +static int wl_android_get_band(struct net_device *dev, char *command, int total_len) +{ + uint band; + int bytes_written; + int error; + + error = wldev_get_band(dev, &band); + if (error) + return -1; + bytes_written = snprintf(command, total_len, "Band %d", band); + return bytes_written; +} + +#if defined(PNO_SUPPORT) && !defined(WL_SCHED_SCAN) +static int wl_android_set_pno_setup(struct net_device *dev, char *command, int total_len) +{ + wlc_ssid_t ssids_local[MAX_PFN_LIST_COUNT]; + int res = -1; + int nssid = 0; + cmd_tlv_t *cmd_tlv_temp; + char *str_ptr; + int tlv_size_left; + int pno_time = 0; + int pno_repeat = 0; + int pno_freq_expo_max = 0; + +#ifdef PNO_SET_DEBUG + int i; + char pno_in_example[] = { + 'P', 'N', 'O', 'S', 'E', 'T', 'U', 'P', ' ', + 'S', '1', '2', '0', + 'S', + 0x05, + 'd', 'l', 'i', 'n', 'k', + 'S', + 0x04, + 'G', 'O', 'O', 'G', + 'T', + '0', 'B', + 'R', + '2', + 'M', + '2', + 0x00 + }; +#endif /* PNO_SET_DEBUG */ + + DHD_INFO(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); + + if (total_len < (strlen(CMD_PNOSETUP_SET) + sizeof(cmd_tlv_t))) { + DHD_ERROR(("%s argument=%d less min size\n", __FUNCTION__, total_len)); + goto exit_proc; + } + + +#ifdef PNO_SET_DEBUG + memcpy(command, pno_in_example, sizeof(pno_in_example)); + for (i = 0; i < sizeof(pno_in_example); i++) + printf("%02X ", command[i]); + printf("\n"); + total_len = sizeof(pno_in_example); +#endif + + str_ptr = command + strlen(CMD_PNOSETUP_SET); + tlv_size_left = total_len - strlen(CMD_PNOSETUP_SET); + + cmd_tlv_temp = (cmd_tlv_t *)str_ptr; + memset(ssids_local, 0, sizeof(ssids_local)); + + if ((cmd_tlv_temp->prefix == PNO_TLV_PREFIX) && + (cmd_tlv_temp->version == PNO_TLV_VERSION) && + (cmd_tlv_temp->subver == PNO_TLV_SUBVERSION)) { + + str_ptr += sizeof(cmd_tlv_t); + tlv_size_left -= sizeof(cmd_tlv_t); + + if ((nssid = wl_iw_parse_ssid_list_tlv(&str_ptr, ssids_local, + MAX_PFN_LIST_COUNT, &tlv_size_left)) <= 0) { + DHD_ERROR(("SSID is not presented or corrupted ret=%d\n", nssid)); + goto exit_proc; + } else { + if ((str_ptr[0] != PNO_TLV_TYPE_TIME) || (tlv_size_left <= 1)) { + DHD_ERROR(("%s scan duration corrupted field size %d\n", + __FUNCTION__, tlv_size_left)); + goto exit_proc; + } + str_ptr++; + pno_time = simple_strtoul(str_ptr, &str_ptr, 16); + DHD_INFO(("%s: pno_time=%d\n", __FUNCTION__, pno_time)); + + if (str_ptr[0] != 0) { + if ((str_ptr[0] != PNO_TLV_FREQ_REPEAT)) { + DHD_ERROR(("%s pno repeat : corrupted field\n", + __FUNCTION__)); + goto exit_proc; + } + str_ptr++; + pno_repeat = simple_strtoul(str_ptr, &str_ptr, 16); + DHD_INFO(("%s :got pno_repeat=%d\n", __FUNCTION__, pno_repeat)); + if (str_ptr[0] != PNO_TLV_FREQ_EXPO_MAX) { + DHD_ERROR(("%s FREQ_EXPO_MAX corrupted field size\n", + __FUNCTION__)); + goto exit_proc; + } + str_ptr++; + pno_freq_expo_max = simple_strtoul(str_ptr, &str_ptr, 16); + DHD_INFO(("%s: pno_freq_expo_max=%d\n", + __FUNCTION__, pno_freq_expo_max)); + } + } + } else { + DHD_ERROR(("%s get wrong TLV command\n", __FUNCTION__)); + goto exit_proc; + } + + res = dhd_dev_pno_set(dev, ssids_local, nssid, pno_time, pno_repeat, pno_freq_expo_max); + +exit_proc: + return res; +} +#endif /* PNO_SUPPORT */ + +static int wl_android_get_p2p_dev_addr(struct net_device *ndev, char *command, int total_len) +{ + int ret; + int bytes_written = 0; + + ret = wl_cfg80211_get_p2p_dev_addr(ndev, (struct ether_addr*)command); + if (ret) + return 0; + bytes_written = sizeof(struct ether_addr); + return bytes_written; +} + +/** + * Global function definitions (declared in wl_android.h) + */ + +int wl_android_wifi_on(struct net_device *dev) +{ + int ret = 0; + int retry = POWERUP_MAX_RETRY; + + printk("%s in\n", __FUNCTION__); + if (!dev) { + DHD_ERROR(("%s: dev is null\n", __FUNCTION__)); + return -EINVAL; + } + + dhd_net_if_lock(dev); + if (!g_wifi_on) { + do { + dhd_customer_gpio_wlan_ctrl(WLAN_RESET_ON); + ret = sdioh_start(NULL, 0); + if (ret == 0) + break; + DHD_ERROR(("\nfailed to power up wifi chip, retry again (%d left) **\n\n", + retry+1)); + dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); + } while (retry-- >= 0); + if (ret != 0) { + DHD_ERROR(("\nfailed to power up wifi chip, max retry reached **\n\n")); + goto exit; + } + ret = dhd_dev_reset(dev, FALSE); + sdioh_start(NULL, 1); + if (!ret) { + if (dhd_dev_init_ioctl(dev) < 0) + ret = -EFAULT; + } +#if defined(PROP_TXSTATUS) && !defined(PROP_TXSTATUS_VSDB) + dhd_wlfc_init(bcmsdh_get_drvdata()); +#endif + g_wifi_on = TRUE; + } + +exit: + dhd_net_if_unlock(dev); + + return ret; +} + +int wl_android_wifi_off(struct net_device *dev) +{ + int ret = 0; + + printk("%s in\n", __FUNCTION__); + if (!dev) { + DHD_TRACE(("%s: dev is null\n", __FUNCTION__)); + return -EINVAL; + } + + dhd_net_if_lock(dev); + if (g_wifi_on) { +#if defined(PROP_TXSTATUS) && !defined(PROP_TXSTATUS_VSDB) + dhd_wlfc_deinit(bcmsdh_get_drvdata()); +#endif + ret = dhd_dev_reset(dev, TRUE); + sdioh_stop(NULL); + dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); + g_wifi_on = FALSE; + } + dhd_net_if_unlock(dev); + + return ret; +} + +static int wl_android_set_fwpath(struct net_device *net, char *command, int total_len) +{ + if ((strlen(command) - strlen(CMD_SETFWPATH)) > MOD_PARAM_PATHLEN) + return -1; + bcm_strncpy_s(fw_path, sizeof(fw_path), + command + strlen(CMD_SETFWPATH) + 1, MOD_PARAM_PATHLEN - 1); + if (strstr(fw_path, "apsta") != NULL) { + DHD_INFO(("GOT APSTA FIRMWARE\n")); + ap_fw_loaded = TRUE; + } else { + DHD_INFO(("GOT STA FIRMWARE\n")); + ap_fw_loaded = FALSE; + } + return 0; +} + +int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) +{ +#define PRIVATE_COMMAND_MAX_LEN 8192 + int ret = 0; + char *command = NULL; + int bytes_written = 0; + android_wifi_priv_cmd priv_cmd; + + net_os_wake_lock(net); + + if (!ifr->ifr_data) { + ret = -EINVAL; + goto exit; + } + if (copy_from_user(&priv_cmd, ifr->ifr_data, sizeof(android_wifi_priv_cmd))) { + ret = -EFAULT; + goto exit; + } + if (priv_cmd.total_len > PRIVATE_COMMAND_MAX_LEN) + { + DHD_ERROR(("%s: too long priavte command\n", __FUNCTION__)); + ret = -EINVAL; + } + command = kmalloc(priv_cmd.total_len, GFP_KERNEL); + if (!command) + { + DHD_ERROR(("%s: failed to allocate memory\n", __FUNCTION__)); + ret = -ENOMEM; + goto exit; + } + if (copy_from_user(command, priv_cmd.buf, priv_cmd.total_len)) { + ret = -EFAULT; + goto exit; + } + + DHD_INFO(("%s: Android private cmd \"%s\" on %s\n", __FUNCTION__, command, ifr->ifr_name)); + + if (strnicmp(command, CMD_START, strlen(CMD_START)) == 0) { + DHD_INFO(("%s, Received regular START command\n", __FUNCTION__)); + bytes_written = wl_android_wifi_on(net); + } + else if (strnicmp(command, CMD_SETFWPATH, strlen(CMD_SETFWPATH)) == 0) { + bytes_written = wl_android_set_fwpath(net, command, priv_cmd.total_len); + } + + if (!g_wifi_on) { + DHD_ERROR(("%s: Ignore private cmd \"%s\" - iface %s is down\n", + __FUNCTION__, command, ifr->ifr_name)); + ret = 0; + goto exit; + } + + if (strnicmp(command, CMD_STOP, strlen(CMD_STOP)) == 0) { + bytes_written = wl_android_wifi_off(net); + } + else if (strnicmp(command, CMD_SCAN_ACTIVE, strlen(CMD_SCAN_ACTIVE)) == 0) { + /* TBD: SCAN-ACTIVE */ + } + else if (strnicmp(command, CMD_SCAN_PASSIVE, strlen(CMD_SCAN_PASSIVE)) == 0) { + /* TBD: SCAN-PASSIVE */ + } + else if (strnicmp(command, CMD_RSSI, strlen(CMD_RSSI)) == 0) { + bytes_written = wl_android_get_rssi(net, command, priv_cmd.total_len); + } + else if (strnicmp(command, CMD_LINKSPEED, strlen(CMD_LINKSPEED)) == 0) { + bytes_written = wl_android_get_link_speed(net, command, priv_cmd.total_len); + } +#ifdef PKT_FILTER_SUPPORT + else if (strnicmp(command, CMD_RXFILTER_START, strlen(CMD_RXFILTER_START)) == 0) { + bytes_written = net_os_enable_packet_filter(net, 1); + } + else if (strnicmp(command, CMD_RXFILTER_STOP, strlen(CMD_RXFILTER_STOP)) == 0) { + bytes_written = net_os_enable_packet_filter(net, 0); + } + else if (strnicmp(command, CMD_RXFILTER_ADD, strlen(CMD_RXFILTER_ADD)) == 0) { + int filter_num = *(command + strlen(CMD_RXFILTER_ADD) + 1) - '0'; + bytes_written = net_os_rxfilter_add_remove(net, TRUE, filter_num); + } + else if (strnicmp(command, CMD_RXFILTER_REMOVE, strlen(CMD_RXFILTER_REMOVE)) == 0) { + int filter_num = *(command + strlen(CMD_RXFILTER_REMOVE) + 1) - '0'; + bytes_written = net_os_rxfilter_add_remove(net, FALSE, filter_num); + } +#endif /* PKT_FILTER_SUPPORT */ + else if (strnicmp(command, CMD_BTCOEXSCAN_START, strlen(CMD_BTCOEXSCAN_START)) == 0) { + /* TBD: BTCOEXSCAN-START */ + } + else if (strnicmp(command, CMD_BTCOEXSCAN_STOP, strlen(CMD_BTCOEXSCAN_STOP)) == 0) { + /* TBD: BTCOEXSCAN-STOP */ + } + else if (strnicmp(command, CMD_BTCOEXMODE, strlen(CMD_BTCOEXMODE)) == 0) { +#ifdef WL_CFG80211 + bytes_written = wl_cfg80211_set_btcoex_dhcp(net, command); +#else +#ifdef PKT_FILTER_SUPPORT + uint mode = *(command + strlen(CMD_BTCOEXMODE) + 1) - '0'; + + if (mode == 1) + net_os_enable_packet_filter(net, 0); /* DHCP starts */ + else + net_os_enable_packet_filter(net, 1); /* DHCP ends */ +#endif /* PKT_FILTER_SUPPORT */ +#endif /* WL_CFG80211 */ + } + else if (strnicmp(command, CMD_SETSUSPENDOPT, strlen(CMD_SETSUSPENDOPT)) == 0) { + bytes_written = wl_android_set_suspendopt(net, command, priv_cmd.total_len); + } + else if (strnicmp(command, CMD_SETSUSPENDMODE, strlen(CMD_SETSUSPENDMODE)) == 0) { + bytes_written = wl_android_set_suspendmode(net, command, priv_cmd.total_len); + } + else if (strnicmp(command, CMD_SETBAND, strlen(CMD_SETBAND)) == 0) { + uint band = *(command + strlen(CMD_SETBAND) + 1) - '0'; +#ifdef WL_HOST_BAND_MGMT + if (wl_cfg80211_set_band(net, band) < 0) { + bytes_written = -1; + goto exit; + } + if (band == WLC_BAND_AUTO) + bytes_written = wldev_set_band(net, band); +#else + bytes_written = wldev_set_band(net, band); +#endif /* WL_HOST_BAND_MGMT */ + } + else if (strnicmp(command, CMD_GETBAND, strlen(CMD_GETBAND)) == 0) { + bytes_written = wl_android_get_band(net, command, priv_cmd.total_len); + } +#ifdef WL_CFG80211 + else if (strnicmp(command, CMD_COUNTRY, strlen(CMD_COUNTRY)) == 0) { + char *country_code = command + strlen(CMD_COUNTRY) + 1; + bytes_written = wldev_set_country(net, country_code, true, true); + } +#endif /* WL_CFG80211 */ +#if defined(PNO_SUPPORT) && !defined(WL_SCHED_SCAN) + else if (strnicmp(command, CMD_PNOSSIDCLR_SET, strlen(CMD_PNOSSIDCLR_SET)) == 0) { + bytes_written = dhd_dev_pno_reset(net); + } + else if (strnicmp(command, CMD_PNOSETUP_SET, strlen(CMD_PNOSETUP_SET)) == 0) { + bytes_written = wl_android_set_pno_setup(net, command, priv_cmd.total_len); + } + else if (strnicmp(command, CMD_PNOENABLE_SET, strlen(CMD_PNOENABLE_SET)) == 0) { + uint pfn_enabled = *(command + strlen(CMD_PNOENABLE_SET) + 1) - '0'; + bytes_written = dhd_dev_pno_enable(net, pfn_enabled); + } +#endif /* PNO_SUPPORT && !WL_SCHED_SCAN */ + else if (strnicmp(command, CMD_P2P_DEV_ADDR, strlen(CMD_P2P_DEV_ADDR)) == 0) { + bytes_written = wl_android_get_p2p_dev_addr(net, command, priv_cmd.total_len); + } + else if (strnicmp(command, CMD_P2P_SET_NOA, strlen(CMD_P2P_SET_NOA)) == 0) { + int skip = strlen(CMD_P2P_SET_NOA) + 1; + bytes_written = wl_cfg80211_set_p2p_noa(net, command + skip, + priv_cmd.total_len - skip); + } +#if !defined WL_ENABLE_P2P_IF + else if (strnicmp(command, CMD_P2P_GET_NOA, strlen(CMD_P2P_GET_NOA)) == 0) { + bytes_written = wl_cfg80211_get_p2p_noa(net, command, priv_cmd.total_len); + } +#endif /* SUPPORT_GET_NOA */ + else if (strnicmp(command, CMD_P2P_SET_PS, strlen(CMD_P2P_SET_PS)) == 0) { + int skip = strlen(CMD_P2P_SET_PS) + 1; + bytes_written = wl_cfg80211_set_p2p_ps(net, command + skip, + priv_cmd.total_len - skip); + } +#ifdef WL_CFG80211 + else if (strnicmp(command, CMD_SET_AP_WPS_P2P_IE, + strlen(CMD_SET_AP_WPS_P2P_IE)) == 0) { + int skip = strlen(CMD_SET_AP_WPS_P2P_IE) + 3; + bytes_written = wl_cfg80211_set_wps_p2p_ie(net, command + skip, + priv_cmd.total_len - skip, *(command + skip - 2) - '0'); + } +#endif /* WL_CFG80211 */ + else { + DHD_ERROR(("Unknown PRIVATE command %s - ignored\n", command)); + snprintf(command, 3, "OK"); + bytes_written = strlen("OK"); + } + + if (bytes_written >= 0) { + if ((bytes_written == 0) && (priv_cmd.total_len > 0)) + command[0] = '\0'; + if (bytes_written >= priv_cmd.total_len) { + DHD_ERROR(("%s: bytes_written = %d\n", __FUNCTION__, bytes_written)); + bytes_written = priv_cmd.total_len; + } else { + bytes_written++; + } + priv_cmd.used_len = bytes_written; + if (copy_to_user(priv_cmd.buf, command, bytes_written)) { + DHD_ERROR(("%s: failed to copy data to user buffer\n", __FUNCTION__)); + ret = -EFAULT; + } + } + else { + ret = bytes_written; + } + +exit: + net_os_wake_unlock(net); + if (command) { + kfree(command); + } + + return ret; +} + +int wl_android_init(void) +{ + int ret = 0; + + dhd_msg_level |= DHD_ERROR_VAL; +#ifdef ENABLE_INSMOD_NO_FW_LOAD + dhd_download_fw_on_driverload = FALSE; +#endif /* ENABLE_INSMOD_NO_FW_LOAD */ +#if defined(CUSTOMER_HW2) + if (!iface_name[0]) { + memset(iface_name, 0, IFNAMSIZ); + bcm_strncpy_s(iface_name, IFNAMSIZ, "wlan", IFNAMSIZ); + } +#endif + return ret; +} + +int wl_android_exit(void) +{ + int ret = 0; + + return ret; +} + +void wl_android_post_init(void) +{ + if (!dhd_download_fw_on_driverload) { + /* Call customer gpio to turn off power with WL_REG_ON signal */ + dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); + g_wifi_on = 0; + } +} +/** + * Functions for Android WiFi card detection + */ +#if defined(CONFIG_WIFI_CONTROL_FUNC) + +static int g_wifidev_registered = 0; +static struct semaphore wifi_control_sem; +static struct wifi_platform_data *wifi_control_data = NULL; +static struct resource *wifi_irqres = NULL; + +static int wifi_add_dev(void); +static void wifi_del_dev(void); + +int wl_android_wifictrl_func_add(void) +{ + int ret = 0; + sema_init(&wifi_control_sem, 0); + + ret = wifi_add_dev(); + if (ret) { + DHD_ERROR(("%s: platform_driver_register failed\n", __FUNCTION__)); + return ret; + } + g_wifidev_registered = 1; + + /* Waiting callback after platform_driver_register is done or exit with error */ + if (down_timeout(&wifi_control_sem, msecs_to_jiffies(1000)) != 0) { + ret = -EINVAL; + DHD_ERROR(("%s: platform_driver_register timeout\n", __FUNCTION__)); + } + + return ret; +} + +void wl_android_wifictrl_func_del(void) +{ + if (g_wifidev_registered) + { + wifi_del_dev(); + g_wifidev_registered = 0; + } +} + +void* wl_android_prealloc(int section, unsigned long size) +{ + void *alloc_ptr = NULL; + if (wifi_control_data && wifi_control_data->mem_prealloc) { + alloc_ptr = wifi_control_data->mem_prealloc(section, size); + if (alloc_ptr) { + DHD_INFO(("success alloc section %d\n", section)); + if (size != 0L) + bzero(alloc_ptr, size); + return alloc_ptr; + } + } + + DHD_ERROR(("can't alloc section %d\n", section)); + return NULL; +} + +int wifi_get_irq_number(unsigned long *irq_flags_ptr) +{ + if (wifi_irqres) { + *irq_flags_ptr = wifi_irqres->flags & IRQF_TRIGGER_MASK; + return (int)wifi_irqres->start; + } +#ifdef CUSTOM_OOB_GPIO_NUM + return CUSTOM_OOB_GPIO_NUM; +#else + return -1; +#endif +} + +int wifi_set_power(int on, unsigned long msec) +{ + DHD_ERROR(("%s = %d\n", __FUNCTION__, on)); + if (wifi_control_data && wifi_control_data->set_power) { + wifi_control_data->set_power(on); + } + if (msec) + msleep(msec); + return 0; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) +int wifi_get_mac_addr(unsigned char *buf) +{ + DHD_ERROR(("%s\n", __FUNCTION__)); + if (!buf) + return -EINVAL; + if (wifi_control_data && wifi_control_data->get_mac_addr) { + return wifi_control_data->get_mac_addr(buf); + } + return -EOPNOTSUPP; +} +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) +void *wifi_get_country_code(char *ccode) +{ + DHD_TRACE(("%s\n", __FUNCTION__)); + if (!ccode) + return NULL; + if (wifi_control_data && wifi_control_data->get_country_code) { + return wifi_control_data->get_country_code(ccode); + } + return NULL; +} +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) */ + +static int wifi_set_carddetect(int on) +{ + DHD_ERROR(("%s = %d\n", __FUNCTION__, on)); + if (wifi_control_data && wifi_control_data->set_carddetect) { + wifi_control_data->set_carddetect(on); + } + return 0; +} + +static int wifi_probe(struct platform_device *pdev) +{ + struct wifi_platform_data *wifi_ctrl = + (struct wifi_platform_data *)(pdev->dev.platform_data); + + wifi_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "bcmdhd_wlan_irq"); + if (wifi_irqres == NULL) + wifi_irqres = platform_get_resource_byname(pdev, + IORESOURCE_IRQ, "bcm4329_wlan_irq"); + wifi_control_data = wifi_ctrl; + wifi_set_power(1, 0); /* Power On */ + wifi_set_carddetect(1); /* CardDetect (0->1) */ + + up(&wifi_control_sem); + return 0; +} + +static int wifi_remove(struct platform_device *pdev) +{ + struct wifi_platform_data *wifi_ctrl = + (struct wifi_platform_data *)(pdev->dev.platform_data); + + DHD_ERROR(("## %s\n", __FUNCTION__)); + wifi_control_data = wifi_ctrl; + + wifi_set_power(0, WIFI_TURNOFF_DELAY); /* Power Off */ + wifi_set_carddetect(0); /* CardDetect (1->0) */ + + up(&wifi_control_sem); + return 0; +} + +static int wifi_suspend(struct platform_device *pdev, pm_message_t state) +{ + DHD_TRACE(("##> %s\n", __FUNCTION__)); +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY) && 1 + bcmsdh_oob_intr_set(0); +#endif /* (OOB_INTR_ONLY) */ + return 0; +} + +static int wifi_resume(struct platform_device *pdev) +{ + DHD_TRACE(("##> %s\n", __FUNCTION__)); +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY) && 1 + if (dhd_os_check_if_up(bcmsdh_get_drvdata())) + bcmsdh_oob_intr_set(1); +#endif /* (OOB_INTR_ONLY) */ + return 0; +} + +static struct platform_driver wifi_device = { + .probe = wifi_probe, + .remove = wifi_remove, + .suspend = wifi_suspend, + .resume = wifi_resume, + .driver = { + .name = "bcmdhd_wlan", + } +}; + +static struct platform_driver wifi_device_legacy = { + .probe = wifi_probe, + .remove = wifi_remove, + .suspend = wifi_suspend, + .resume = wifi_resume, + .driver = { + .name = "bcm4329_wlan", + } +}; + +static int wifi_add_dev(void) +{ + int ret; + DHD_TRACE(("## Calling platform_driver_register\n")); + ret = platform_driver_register(&wifi_device); + if (ret) + return ret; + + ret = platform_driver_register(&wifi_device_legacy); + return ret; +} + +static void wifi_del_dev(void) +{ + DHD_TRACE(("## Unregister platform_driver_register\n")); + platform_driver_unregister(&wifi_device); + platform_driver_unregister(&wifi_device_legacy); +} +#endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */ diff --git a/drivers/net/wireless/bcmdhd/wl_android.h b/drivers/net/wireless/bcmdhd/wl_android.h new file mode 100644 index 00000000..583a1673 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wl_android.h @@ -0,0 +1,57 @@ +/* + * Linux cfg80211 driver - Android related functions + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_android.h 307885 2012-01-12 23:30:48Z $ + */ + +#include +#include +#include + +/** + * Android platform dependent functions, feel free to add Android specific functions here + * (save the macros in dhd). Please do NOT declare functions that are NOT exposed to dhd + * or cfg, define them as static in wl_android.c + */ + +/** + * wl_android_init will be called from module init function (dhd_module_init now), similarly + * wl_android_exit will be called from module exit function (dhd_module_cleanup now) + */ +int wl_android_init(void); +int wl_android_exit(void); +void wl_android_post_init(void); +int wl_android_wifi_on(struct net_device *dev); +int wl_android_wifi_off(struct net_device *dev); +int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd); + +#if defined(CONFIG_WIFI_CONTROL_FUNC) +int wl_android_wifictrl_func_add(void); +void wl_android_wifictrl_func_del(void); +void* wl_android_prealloc(int section, unsigned long size); + +int wifi_get_irq_number(unsigned long *irq_flags_ptr); +int wifi_set_power(int on, unsigned long msec); +int wifi_get_mac_addr(unsigned char *buf); +void *wifi_get_country_code(char *ccode); +#endif /* CONFIG_WIFI_CONTROL_FUNC */ diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c new file mode 100644 index 00000000..193dbe05 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -0,0 +1,9750 @@ +/* + * Linux cfg80211 driver + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_cfg80211.c 381665 2013-01-29 02:34:22Z $ + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef PROP_TXSTATUS +#include +#endif + + +#define IW_WSEC_ENABLED(wsec) ((wsec) & (WEP_ENABLED | TKIP_ENABLED | AES_ENABLED)) + +static struct device *cfg80211_parent_dev = NULL; +struct wl_priv *wlcfg_drv_priv = NULL; +u32 wl_dbg_level = WL_DBG_ERR; + +#define MAX_WAIT_TIME 1500 + +#ifdef VSDB +/* sleep time to keep STA's connecting or connection for continuous af tx or finding a peer */ +#define DEFAULT_SLEEP_TIME_VSDB 200 +#define OFF_CHAN_TIME_THRESHOLD_MS 200 + +/* if sta is connected or connecting, sleep for a while before retry af tx or finding a peer */ +#define WL_AF_TX_KEEP_PRI_CONNECTION_VSDB(wl) \ + do { \ + if (wl_get_drv_status(wl, CONNECTED, wl_to_prmry_ndev(wl)) || \ + wl_get_drv_status(wl, CONNECTING, wl_to_prmry_ndev(wl))) { \ + msleep(DEFAULT_SLEEP_TIME_VSDB); \ + } \ + } while (0) +#else /* VSDB */ +/* if not VSDB, do nothing */ +#define WL_AF_TX_KEEP_PRI_CONNECTION_VSDB(wl) +#endif /* VSDB */ + +#ifdef WL_CFG80211_SYNC_GON +#define WL_DRV_STATUS_SENDING_AF_FRM_EXT(wl) \ + (wl_get_drv_status_all(wl, SENDING_ACT_FRM) || \ + wl_get_drv_status_all(wl, WAITING_NEXT_ACT_FRM_LISTEN)) +#else +#define WL_DRV_STATUS_SENDING_AF_FRM_EXT(wl) wl_get_drv_status_all(wl, SENDING_ACT_FRM) +#endif /* WL_CFG80211_SYNC_GON */ + +#define WL_CHANSPEC_CTL_SB_NONE WL_CHANSPEC_CTL_SB_LLL + + +#define DNGL_FUNC(func, parameters) func parameters; +#define COEX_DHCP + +#define WLAN_EID_SSID 0 +#define CH_MIN_5G_CHANNEL 34 +#define CH_MIN_2G_CHANNEL 1 + +/* This is to override regulatory domains defined in cfg80211 module (reg.c) + * By default world regulatory domain defined in reg.c puts the flags NL80211_RRF_PASSIVE_SCAN + * and NL80211_RRF_NO_IBSS for 5GHz channels (for 36..48 and 149..165). + * With respect to these flags, wpa_supplicant doesn't start p2p operations on 5GHz channels. + * All the chnages in world regulatory domain are to be done here. + */ +static const struct ieee80211_regdomain brcm_regdom = { + .n_reg_rules = 4, + .alpha2 = "99", + .reg_rules = { + /* IEEE 802.11b/g, channels 1..11 */ + REG_RULE(2412-10, 2472+10, 40, 6, 20, 0), + /* If any */ + /* IEEE 802.11 channel 14 - Only JP enables + * this and for 802.11b only + */ + REG_RULE(2484-10, 2484+10, 20, 6, 20, 0), + /* IEEE 802.11a, channel 36..64 */ + REG_RULE(5150-10, 5350+10, 40, 6, 20, 0), + /* IEEE 802.11a, channel 100..165 */ + REG_RULE(5470-10, 5850+10, 40, 6, 20, 0), } +}; + + +/* Data Element Definitions */ +#define WPS_ID_CONFIG_METHODS 0x1008 +#define WPS_ID_REQ_TYPE 0x103A +#define WPS_ID_DEVICE_NAME 0x1011 +#define WPS_ID_VERSION 0x104A +#define WPS_ID_DEVICE_PWD_ID 0x1012 +#define WPS_ID_REQ_DEV_TYPE 0x106A +#define WPS_ID_SELECTED_REGISTRAR_CONFIG_METHODS 0x1053 +#define WPS_ID_PRIM_DEV_TYPE 0x1054 + +/* Device Password ID */ +#define DEV_PW_DEFAULT 0x0000 +#define DEV_PW_USER_SPECIFIED 0x0001, +#define DEV_PW_MACHINE_SPECIFIED 0x0002 +#define DEV_PW_REKEY 0x0003 +#define DEV_PW_PUSHBUTTON 0x0004 +#define DEV_PW_REGISTRAR_SPECIFIED 0x0005 + +/* Config Methods */ +#define WPS_CONFIG_USBA 0x0001 +#define WPS_CONFIG_ETHERNET 0x0002 +#define WPS_CONFIG_LABEL 0x0004 +#define WPS_CONFIG_DISPLAY 0x0008 +#define WPS_CONFIG_EXT_NFC_TOKEN 0x0010 +#define WPS_CONFIG_INT_NFC_TOKEN 0x0020 +#define WPS_CONFIG_NFC_INTERFACE 0x0040 +#define WPS_CONFIG_PUSHBUTTON 0x0080 +#define WPS_CONFIG_KEYPAD 0x0100 +#define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280 +#define WPS_CONFIG_PHY_PUSHBUTTON 0x0480 +#define WPS_CONFIG_VIRT_DISPLAY 0x2008 +#define WPS_CONFIG_PHY_DISPLAY 0x4008 + +#define PM_BLOCK 1 +#define PM_ENABLE 0 + +#ifndef RSSI_OFFSET +#define RSSI_OFFSET 0 +#endif +/* + * cfg80211_ops api/callback list + */ +static s32 wl_frame_get_mgmt(u16 fc, const struct ether_addr *da, + const struct ether_addr *sa, const struct ether_addr *bssid, + u8 **pheader, u32 *body_len, u8 *pbody); +static s32 __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_scan_request *request, + struct cfg80211_ssid *this_ssid); +static s32 wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_scan_request *request); +static s32 wl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed); +static s32 wl_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ibss_params *params); +static s32 wl_cfg80211_leave_ibss(struct wiphy *wiphy, + struct net_device *dev); +static s32 wl_cfg80211_get_station(struct wiphy *wiphy, + struct net_device *dev, u8 *mac, + struct station_info *sinfo); +static s32 wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, + struct net_device *dev, bool enabled, + s32 timeout); +static int wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme); +static s32 wl_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, + u16 reason_code); +static s32 wl_cfg80211_set_tx_power(struct wiphy *wiphy, + enum nl80211_tx_power_setting type, + s32 dbm); +static s32 wl_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm); +static s32 wl_cfg80211_config_default_key(struct wiphy *wiphy, + struct net_device *dev, + u8 key_idx, bool unicast, bool multicast); +static s32 wl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, bool pairwise, const u8 *mac_addr, + struct key_params *params); +static s32 wl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, bool pairwise, const u8 *mac_addr); +static s32 wl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, bool pairwise, const u8 *mac_addr, + void *cookie, void (*callback) (void *cookie, + struct key_params *params)); +static s32 wl_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, + struct net_device *dev, u8 key_idx); +static s32 wl_cfg80211_resume(struct wiphy *wiphy); +static s32 wl_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, + struct net_device *dev, u64 cookie); +static s32 wl_cfg80211_del_station(struct wiphy *wiphy, + struct net_device *ndev, u8* mac_addr); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39) +static s32 wl_cfg80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow); +#else +static s32 wl_cfg80211_suspend(struct wiphy *wiphy); +#endif +static s32 wl_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_pmksa *pmksa); +static s32 wl_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_pmksa *pmksa); +static s32 wl_cfg80211_flush_pmksa(struct wiphy *wiphy, + struct net_device *dev); +static s32 wl_notify_escan_complete(struct wl_priv *wl, + struct net_device *ndev, bool aborted, bool fw_abort); +/* + * event & event Q handlers for cfg80211 interfaces + */ +static s32 wl_create_event_handler(struct wl_priv *wl); +static void wl_destroy_event_handler(struct wl_priv *wl); +static s32 wl_event_handler(void *data); +static void wl_init_eq(struct wl_priv *wl); +static void wl_flush_eq(struct wl_priv *wl); +static unsigned long wl_lock_eq(struct wl_priv *wl); +static void wl_unlock_eq(struct wl_priv *wl, unsigned long flags); +static void wl_init_eq_lock(struct wl_priv *wl); +static void wl_init_event_handler(struct wl_priv *wl); +static struct wl_event_q *wl_deq_event(struct wl_priv *wl); +static s32 wl_enq_event(struct wl_priv *wl, struct net_device *ndev, u32 type, + const wl_event_msg_t *msg, void *data); +static void wl_put_event(struct wl_event_q *e); +static void wl_wakeup_event(struct wl_priv *wl); +static s32 wl_notify_connect_status_ap(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data); +static s32 wl_notify_connect_status(struct wl_priv *wl, + struct net_device *ndev, + const wl_event_msg_t *e, void *data); +static s32 wl_notify_roaming_status(struct wl_priv *wl, + struct net_device *ndev, + const wl_event_msg_t *e, void *data); +static s32 wl_notify_scan_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data); +static s32 wl_bss_connect_done(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data, bool completed); +static s32 wl_bss_roaming_done(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data); +static s32 wl_notify_mic_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data); +#ifdef WL_SCHED_SCAN +static s32 +wl_notify_sched_scan_results(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data); +#endif /* WL_SCHED_SCAN */ +#ifdef PNO_SUPPORT +static s32 wl_notify_pfn_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data); +#endif /* PNO_SUPPORT */ +static s32 wl_notifier_change_state(struct wl_priv *wl, struct net_info *_net_info, + enum wl_status state, bool set); +/* + * register/deregister parent device + */ +static void wl_cfg80211_clear_parent_dev(void); + +/* + * ioctl utilites + */ + +/* + * cfg80211 set_wiphy_params utilities + */ +static s32 wl_set_frag(struct net_device *dev, u32 frag_threshold); +static s32 wl_set_rts(struct net_device *dev, u32 frag_threshold); +static s32 wl_set_retry(struct net_device *dev, u32 retry, bool l); + +/* + * wl profile utilities + */ +static s32 wl_update_prof(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data, s32 item); +static void *wl_read_prof(struct wl_priv *wl, struct net_device *ndev, s32 item); +static void wl_init_prof(struct wl_priv *wl, struct net_device *ndev); + +/* + * cfg80211 connect utilites + */ +static s32 wl_set_wpa_version(struct net_device *dev, + struct cfg80211_connect_params *sme); +static s32 wl_set_auth_type(struct net_device *dev, + struct cfg80211_connect_params *sme); +static s32 wl_set_set_cipher(struct net_device *dev, + struct cfg80211_connect_params *sme); +static s32 wl_set_key_mgmt(struct net_device *dev, + struct cfg80211_connect_params *sme); +static s32 wl_set_set_sharedkey(struct net_device *dev, + struct cfg80211_connect_params *sme); +static s32 wl_get_assoc_ies(struct wl_priv *wl, struct net_device *ndev); +static void wl_ch_to_chanspec(int ch, + struct wl_join_params *join_params, size_t *join_params_size); + +/* + * information element utilities + */ +static void wl_rst_ie(struct wl_priv *wl); +static __used s32 wl_add_ie(struct wl_priv *wl, u8 t, u8 l, u8 *v); +static s32 wl_mrg_ie(struct wl_priv *wl, u8 *ie_stream, u16 ie_size); +static s32 wl_cp_ie(struct wl_priv *wl, u8 *dst, u16 dst_size); +static u32 wl_get_ielen(struct wl_priv *wl); + + +static s32 wl_setup_wiphy(struct wireless_dev *wdev, struct device *dev); +static void wl_free_wdev(struct wl_priv *wl); +#ifdef CONFIG_CFG80211_INTERNAL_REGDB +static int +wl_cfg80211_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request); +#endif /* CONFIG_CFG80211_INTERNAL_REGDB */ + +static s32 wl_inform_bss(struct wl_priv *wl); +static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi, u8 is_roam_done); +static s32 wl_update_bss_info(struct wl_priv *wl, struct net_device *ndev, u8 is_roam_done); +static chanspec_t wl_cfg80211_get_shared_freq(struct wiphy *wiphy); +s32 wl_cfg80211_channel_to_freq(u32 channel); + +#if defined(DHCP_SCAN_SUPPRESS) +static void wl_cfg80211_work_handler(struct work_struct *work); +static void wl_cfg80211_scan_supp_timerfunc(ulong data); +#endif /* DHCP_SCAN_SUPPRESS */ + +static s32 wl_add_keyext(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, const u8 *mac_addr, + struct key_params *params); +/* + * key indianess swap utilities + */ +static void swap_key_from_BE(struct wl_wsec_key *key); +static void swap_key_to_BE(struct wl_wsec_key *key); + +/* + * wl_priv memory init/deinit utilities + */ +static s32 wl_init_priv_mem(struct wl_priv *wl); +static void wl_deinit_priv_mem(struct wl_priv *wl); + +static void wl_delay(u32 ms); + +/* + * ibss mode utilities + */ +static bool wl_is_ibssmode(struct wl_priv *wl, struct net_device *ndev); +static __used bool wl_is_ibssstarter(struct wl_priv *wl); + +/* + * link up/down , default configuration utilities + */ +static s32 __wl_cfg80211_up(struct wl_priv *wl); +static s32 __wl_cfg80211_down(struct wl_priv *wl); +static bool wl_is_linkdown(struct wl_priv *wl, const wl_event_msg_t *e); +static bool wl_is_linkup(struct wl_priv *wl, const wl_event_msg_t *e, struct net_device *ndev); +static bool wl_is_nonetwork(struct wl_priv *wl, const wl_event_msg_t *e); +static void wl_link_up(struct wl_priv *wl); +static void wl_link_down(struct wl_priv *wl); +static s32 wl_config_ifmode(struct wl_priv *wl, struct net_device *ndev, s32 iftype); +static void wl_init_conf(struct wl_conf *conf); + +/* + * iscan handler + */ +static void wl_iscan_timer(unsigned long data); +static void wl_term_iscan(struct wl_priv *wl); +static s32 wl_init_scan(struct wl_priv *wl); +static s32 wl_iscan_thread(void *data); +static s32 wl_run_iscan(struct wl_iscan_ctrl *iscan, struct cfg80211_scan_request *request, + u16 action); +static s32 wl_do_iscan(struct wl_priv *wl, struct cfg80211_scan_request *request); +static s32 wl_wakeup_iscan(struct wl_iscan_ctrl *iscan); +static s32 wl_invoke_iscan(struct wl_priv *wl); +static s32 wl_get_iscan_results(struct wl_iscan_ctrl *iscan, u32 *status, + struct wl_scan_results **bss_list); +static void wl_notify_iscan_complete(struct wl_iscan_ctrl *iscan, bool aborted); +static void wl_init_iscan_handler(struct wl_iscan_ctrl *iscan); +static s32 wl_iscan_done(struct wl_priv *wl); +static s32 wl_iscan_pending(struct wl_priv *wl); +static s32 wl_iscan_inprogress(struct wl_priv *wl); +static s32 wl_iscan_aborted(struct wl_priv *wl); + +/* + * find most significant bit set + */ +static __used u32 wl_find_msb(u16 bit16); + +/* + * rfkill support + */ +static int wl_setup_rfkill(struct wl_priv *wl, bool setup); +static int wl_rfkill_set(void *data, bool blocked); + +static wl_scan_params_t *wl_cfg80211_scan_alloc_params(int channel, + int nprobes, int *out_params_size); +static void get_primary_mac(struct wl_priv *wl, struct ether_addr *mac); + +/* + * Some external functions, TODO: move them to dhd_linux.h + */ +int dhd_add_monitor(char *name, struct net_device **new_ndev); +int dhd_del_monitor(struct net_device *ndev); +int dhd_monitor_init(void *dhd_pub); +int dhd_monitor_uninit(void); +int dhd_start_xmit(struct sk_buff *skb, struct net_device *net); + + + +#define CHECK_SYS_UP(wlpriv) \ +do { \ + struct net_device *ndev = wl_to_prmry_ndev(wlpriv); \ + if (unlikely(!wl_get_drv_status(wlpriv, READY, ndev))) { \ + WL_INFO(("device is not ready\n")); \ + return -EIO; \ + } \ +} while (0) + + +#define IS_WPA_AKM(akm) ((akm) == RSN_AKM_NONE || \ + (akm) == RSN_AKM_UNSPECIFIED || \ + (akm) == RSN_AKM_PSK) + + +extern int dhd_wait_pend8021x(struct net_device *dev); +#ifdef PROP_TXSTATUS_VSDB +extern int disable_proptx; +extern int dhd_wlfc_init(dhd_pub_t *dhd); +extern void dhd_wlfc_deinit(dhd_pub_t *dhd); +#endif /* PROP_TXSTATUS_VSDB */ + +#if (WL_DBG_LEVEL > 0) +#define WL_DBG_ESTR_MAX 50 +static s8 wl_dbg_estr[][WL_DBG_ESTR_MAX] = { + "SET_SSID", "JOIN", "START", "AUTH", "AUTH_IND", + "DEAUTH", "DEAUTH_IND", "ASSOC", "ASSOC_IND", "REASSOC", + "REASSOC_IND", "DISASSOC", "DISASSOC_IND", "QUIET_START", "QUIET_END", + "BEACON_RX", "LINK", "MIC_ERROR", "NDIS_LINK", "ROAM", + "TXFAIL", "PMKID_CACHE", "RETROGRADE_TSF", "PRUNE", "AUTOAUTH", + "EAPOL_MSG", "SCAN_COMPLETE", "ADDTS_IND", "DELTS_IND", "BCNSENT_IND", + "BCNRX_MSG", "BCNLOST_MSG", "ROAM_PREP", "PFN_NET_FOUND", + "PFN_NET_LOST", + "RESET_COMPLETE", "JOIN_START", "ROAM_START", "ASSOC_START", + "IBSS_ASSOC", + "RADIO", "PSM_WATCHDOG", "WLC_E_CCX_ASSOC_START", "WLC_E_CCX_ASSOC_ABORT", + "PROBREQ_MSG", + "SCAN_CONFIRM_IND", "PSK_SUP", "COUNTRY_CODE_CHANGED", + "EXCEEDED_MEDIUM_TIME", "ICV_ERROR", + "UNICAST_DECODE_ERROR", "MULTICAST_DECODE_ERROR", "TRACE", + "WLC_E_BTA_HCI_EVENT", "IF", "WLC_E_P2P_DISC_LISTEN_COMPLETE", + "RSSI", "PFN_SCAN_COMPLETE", "WLC_E_EXTLOG_MSG", + "ACTION_FRAME", "ACTION_FRAME_COMPLETE", "WLC_E_PRE_ASSOC_IND", + "WLC_E_PRE_REASSOC_IND", "WLC_E_CHANNEL_ADOPTED", "WLC_E_AP_STARTED", + "WLC_E_DFS_AP_STOP", "WLC_E_DFS_AP_RESUME", "WLC_E_WAI_STA_EVENT", + "WLC_E_WAI_MSG", "WLC_E_ESCAN_RESULT", "WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE", + "WLC_E_PROBRESP_MSG", "WLC_E_P2P_PROBREQ_MSG", "WLC_E_DCS_REQUEST", "WLC_E_FIFO_CREDIT_MAP", + "WLC_E_ACTION_FRAME_RX", "WLC_E_WAKE_EVENT", "WLC_E_RM_COMPLETE" +}; +#endif /* WL_DBG_LEVEL */ + +#define CHAN2G(_channel, _freq, _flags) { \ + .band = IEEE80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +#define CHAN5G(_channel, _flags) { \ + .band = IEEE80211_BAND_5GHZ, \ + .center_freq = 5000 + (5 * (_channel)), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2) +#define RATETAB_ENT(_rateid, _flags) \ + { \ + .bitrate = RATE_TO_BASE100KBPS(_rateid), \ + .hw_value = (_rateid), \ + .flags = (_flags), \ + } + +static struct ieee80211_rate __wl_rates[] = { + RATETAB_ENT(WLC_RATE_1M, 0), + RATETAB_ENT(WLC_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(WLC_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(WLC_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(WLC_RATE_6M, 0), + RATETAB_ENT(WLC_RATE_9M, 0), + RATETAB_ENT(WLC_RATE_12M, 0), + RATETAB_ENT(WLC_RATE_18M, 0), + RATETAB_ENT(WLC_RATE_24M, 0), + RATETAB_ENT(WLC_RATE_36M, 0), + RATETAB_ENT(WLC_RATE_48M, 0), + RATETAB_ENT(WLC_RATE_54M, 0) +}; + +#define wl_a_rates (__wl_rates + 4) +#define wl_a_rates_size 8 +#define wl_g_rates (__wl_rates + 0) +#define wl_g_rates_size 12 + +static struct ieee80211_channel __wl_2ghz_channels[] = { + CHAN2G(1, 2412, 0), + CHAN2G(2, 2417, 0), + CHAN2G(3, 2422, 0), + CHAN2G(4, 2427, 0), + CHAN2G(5, 2432, 0), + CHAN2G(6, 2437, 0), + CHAN2G(7, 2442, 0), + CHAN2G(8, 2447, 0), + CHAN2G(9, 2452, 0), + CHAN2G(10, 2457, 0), + CHAN2G(11, 2462, 0), + CHAN2G(12, 2467, 0), + CHAN2G(13, 2472, 0), + CHAN2G(14, 2484, 0) +}; + +static struct ieee80211_channel __wl_5ghz_a_channels[] = { + CHAN5G(34, 0), CHAN5G(36, 0), + CHAN5G(38, 0), CHAN5G(40, 0), + CHAN5G(42, 0), CHAN5G(44, 0), + CHAN5G(46, 0), CHAN5G(48, 0), + CHAN5G(52, 0), CHAN5G(56, 0), + CHAN5G(60, 0), CHAN5G(64, 0), + CHAN5G(100, 0), CHAN5G(104, 0), + CHAN5G(108, 0), CHAN5G(112, 0), + CHAN5G(116, 0), CHAN5G(120, 0), + CHAN5G(124, 0), CHAN5G(128, 0), + CHAN5G(132, 0), CHAN5G(136, 0), + CHAN5G(140, 0), CHAN5G(149, 0), + CHAN5G(153, 0), CHAN5G(157, 0), + CHAN5G(161, 0), CHAN5G(165, 0) +}; + +static struct ieee80211_supported_band __wl_band_2ghz = { + .band = IEEE80211_BAND_2GHZ, + .channels = __wl_2ghz_channels, + .n_channels = ARRAY_SIZE(__wl_2ghz_channels), + .bitrates = wl_g_rates, + .n_bitrates = wl_g_rates_size +}; + +static struct ieee80211_supported_band __wl_band_5ghz_a = { + .band = IEEE80211_BAND_5GHZ, + .channels = __wl_5ghz_a_channels, + .n_channels = ARRAY_SIZE(__wl_5ghz_a_channels), + .bitrates = wl_a_rates, + .n_bitrates = wl_a_rates_size +}; + +static const u32 __wl_cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, + WLAN_CIPHER_SUITE_AES_CMAC, +}; + + +/* IOCtl version read from targeted driver */ +static int ioctl_version; + +/* Return a new chanspec given a legacy chanspec + * Returns INVCHANSPEC on error + */ +static chanspec_t +wl_chspec_from_legacy(chanspec_t legacy_chspec) +{ + chanspec_t chspec; + + /* get the channel number */ + chspec = LCHSPEC_CHANNEL(legacy_chspec); + + /* convert the band */ + if (LCHSPEC_IS2G(legacy_chspec)) { + chspec |= WL_CHANSPEC_BAND_2G; + } else { + chspec |= WL_CHANSPEC_BAND_5G; + } + + /* convert the bw and sideband */ + if (LCHSPEC_IS20(legacy_chspec)) { + chspec |= WL_CHANSPEC_BW_20; + } else { + chspec |= WL_CHANSPEC_BW_40; + if (LCHSPEC_CTL_SB(legacy_chspec) == WL_LCHANSPEC_CTL_SB_LOWER) { + chspec |= WL_CHANSPEC_CTL_SB_L; + } else { + chspec |= WL_CHANSPEC_CTL_SB_U; + } + } + + if (wf_chspec_malformed(chspec)) { + WL_ERR(("wl_chspec_from_legacy: output chanspec (0x%04X) malformed\n", + chspec)); + return INVCHANSPEC; + } + + return chspec; +} + +/* Return a legacy chanspec given a new chanspec + * Returns INVCHANSPEC on error + */ +static chanspec_t +wl_chspec_to_legacy(chanspec_t chspec) +{ + chanspec_t lchspec; + + if (wf_chspec_malformed(chspec)) { + WL_ERR(("wl_chspec_to_legacy: input chanspec (0x%04X) malformed\n", + chspec)); + return INVCHANSPEC; + } + + /* get the channel number */ + lchspec = CHSPEC_CHANNEL(chspec); + + /* convert the band */ + if (CHSPEC_IS2G(chspec)) { + lchspec |= WL_LCHANSPEC_BAND_2G; + } else { + lchspec |= WL_LCHANSPEC_BAND_5G; + } + + /* convert the bw and sideband */ + if (CHSPEC_IS20(chspec)) { + lchspec |= WL_LCHANSPEC_BW_20; + lchspec |= WL_LCHANSPEC_CTL_SB_NONE; + } else if (CHSPEC_IS40(chspec)) { + lchspec |= WL_LCHANSPEC_BW_40; + if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_L) { + lchspec |= WL_LCHANSPEC_CTL_SB_LOWER; + } else { + lchspec |= WL_LCHANSPEC_CTL_SB_UPPER; + } + } else { + /* cannot express the bandwidth */ + char chanbuf[CHANSPEC_STR_LEN]; + WL_ERR(( + "wl_chspec_to_legacy: unable to convert chanspec %s (0x%04X) " + "to pre-11ac format\n", + wf_chspec_ntoa(chspec, chanbuf), chspec)); + return INVCHANSPEC; + } + + return lchspec; +} + +/* given a chanspec value, do the endian and chanspec version conversion to + * a chanspec_t value + * Returns INVCHANSPEC on error + */ +static chanspec_t +wl_chspec_host_to_driver(chanspec_t chanspec) +{ + if (ioctl_version == 1) { + chanspec = wl_chspec_to_legacy(chanspec); + if (chanspec == INVCHANSPEC) { + return chanspec; + } + } + chanspec = htodchanspec(chanspec); + + return chanspec; +} + +/* given a channel value, do the endian and chanspec version conversion to + * a chanspec_t value + * Returns INVCHANSPEC on error + */ +chanspec_t +wl_ch_host_to_driver(u16 channel) +{ + + chanspec_t chanspec; + + chanspec = channel & WL_CHANSPEC_CHAN_MASK; + + if (channel <= CH_MAX_2G_CHANNEL) + chanspec |= WL_CHANSPEC_BAND_2G; + else + chanspec |= WL_CHANSPEC_BAND_5G; + + chanspec |= WL_CHANSPEC_BW_20; + chanspec |= WL_CHANSPEC_CTL_SB_NONE; + + return wl_chspec_host_to_driver(chanspec); +} + +/* given a chanspec value from the driver, do the endian and chanspec version conversion to + * a chanspec_t value + * Returns INVCHANSPEC on error + */ +static chanspec_t +wl_chspec_driver_to_host(chanspec_t chanspec) +{ + chanspec = dtohchanspec(chanspec); + if (ioctl_version == 1) { + chanspec = wl_chspec_from_legacy(chanspec); + } + + return chanspec; +} + +/* There isn't a lot of sense in it, but you can transmit anything you like */ +static const struct ieee80211_txrx_stypes +wl_cfg80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = { + [NL80211_IFTYPE_ADHOC] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) + }, + [NL80211_IFTYPE_STATION] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_AP] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | + BIT(IEEE80211_STYPE_DISASSOC >> 4) | + BIT(IEEE80211_STYPE_AUTH >> 4) | + BIT(IEEE80211_STYPE_DEAUTH >> 4) | + BIT(IEEE80211_STYPE_ACTION >> 4) + }, + [NL80211_IFTYPE_AP_VLAN] = { + /* copy AP */ + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | + BIT(IEEE80211_STYPE_DISASSOC >> 4) | + BIT(IEEE80211_STYPE_AUTH >> 4) | + BIT(IEEE80211_STYPE_DEAUTH >> 4) | + BIT(IEEE80211_STYPE_ACTION >> 4) + }, + [NL80211_IFTYPE_P2P_CLIENT] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_P2P_GO] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | + BIT(IEEE80211_STYPE_DISASSOC >> 4) | + BIT(IEEE80211_STYPE_AUTH >> 4) | + BIT(IEEE80211_STYPE_DEAUTH >> 4) | + BIT(IEEE80211_STYPE_ACTION >> 4) + } +}; + +static void swap_key_from_BE(struct wl_wsec_key *key) +{ + key->index = htod32(key->index); + key->len = htod32(key->len); + key->algo = htod32(key->algo); + key->flags = htod32(key->flags); + key->rxiv.hi = htod32(key->rxiv.hi); + key->rxiv.lo = htod16(key->rxiv.lo); + key->iv_initialized = htod32(key->iv_initialized); +} + +static void swap_key_to_BE(struct wl_wsec_key *key) +{ + key->index = dtoh32(key->index); + key->len = dtoh32(key->len); + key->algo = dtoh32(key->algo); + key->flags = dtoh32(key->flags); + key->rxiv.hi = dtoh32(key->rxiv.hi); + key->rxiv.lo = dtoh16(key->rxiv.lo); + key->iv_initialized = dtoh32(key->iv_initialized); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) +/* For debug: Dump the contents of the encoded wps ie buffe */ +static void +wl_validate_wps_ie(char *wps_ie, s32 wps_ie_len, bool *pbc) +{ + #define WPS_IE_FIXED_LEN 6 + u16 len; + u8 *subel = NULL; + u16 subelt_id; + u16 subelt_len; + u16 val; + u8 *valptr = (uint8*) &val; + if (wps_ie == NULL || wps_ie_len < WPS_IE_FIXED_LEN) { + WL_ERR(("invalid argument : NULL\n")); + return; + } + len = (u16)wps_ie[TLV_LEN_OFF]; + + if (len > wps_ie_len) { + WL_ERR(("invalid length len %d, wps ie len %d\n", len, wps_ie_len)); + return; + } + WL_DBG(("wps_ie len=%d\n", len)); + len -= 4; /* for the WPS IE's OUI, oui_type fields */ + subel = wps_ie + WPS_IE_FIXED_LEN; + while (len >= 4) { /* must have attr id, attr len fields */ + valptr[0] = *subel++; + valptr[1] = *subel++; + subelt_id = HTON16(val); + + valptr[0] = *subel++; + valptr[1] = *subel++; + subelt_len = HTON16(val); + + len -= 4; /* for the attr id, attr len fields */ + len -= subelt_len; /* for the remaining fields in this attribute */ + WL_DBG((" subel=%p, subelt_id=0x%x subelt_len=%u\n", + subel, subelt_id, subelt_len)); + + if (subelt_id == WPS_ID_VERSION) { + WL_DBG((" attr WPS_ID_VERSION: %u\n", *subel)); + } else if (subelt_id == WPS_ID_REQ_TYPE) { + WL_DBG((" attr WPS_ID_REQ_TYPE: %u\n", *subel)); + } else if (subelt_id == WPS_ID_CONFIG_METHODS) { + valptr[0] = *subel; + valptr[1] = *(subel + 1); + WL_DBG((" attr WPS_ID_CONFIG_METHODS: %x\n", HTON16(val))); + } else if (subelt_id == WPS_ID_DEVICE_NAME) { + char devname[100]; + memcpy(devname, subel, subelt_len); + devname[subelt_len] = '\0'; + WL_DBG((" attr WPS_ID_DEVICE_NAME: %s (len %u)\n", + devname, subelt_len)); + } else if (subelt_id == WPS_ID_DEVICE_PWD_ID) { + valptr[0] = *subel; + valptr[1] = *(subel + 1); + WL_DBG((" attr WPS_ID_DEVICE_PWD_ID: %u\n", HTON16(val))); + *pbc = (HTON16(val) == DEV_PW_PUSHBUTTON) ? true : false; + } else if (subelt_id == WPS_ID_PRIM_DEV_TYPE) { + valptr[0] = *subel; + valptr[1] = *(subel + 1); + WL_DBG((" attr WPS_ID_PRIM_DEV_TYPE: cat=%u \n", HTON16(val))); + valptr[0] = *(subel + 6); + valptr[1] = *(subel + 7); + WL_DBG((" attr WPS_ID_PRIM_DEV_TYPE: subcat=%u\n", HTON16(val))); + } else if (subelt_id == WPS_ID_REQ_DEV_TYPE) { + valptr[0] = *subel; + valptr[1] = *(subel + 1); + WL_DBG((" attr WPS_ID_REQ_DEV_TYPE: cat=%u\n", HTON16(val))); + valptr[0] = *(subel + 6); + valptr[1] = *(subel + 7); + WL_DBG((" attr WPS_ID_REQ_DEV_TYPE: subcat=%u\n", HTON16(val))); + } else if (subelt_id == WPS_ID_SELECTED_REGISTRAR_CONFIG_METHODS) { + valptr[0] = *subel; + valptr[1] = *(subel + 1); + WL_DBG((" attr WPS_ID_SELECTED_REGISTRAR_CONFIG_METHODS" + ": cat=%u\n", HTON16(val))); + } else { + WL_DBG((" unknown attr 0x%x\n", subelt_id)); + } + + subel += subelt_len; + } +} +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) */ + +static chanspec_t wl_cfg80211_get_shared_freq(struct wiphy *wiphy) +{ + chanspec_t chspec; + int err = 0; + struct wl_priv *wl = wiphy_priv(wiphy); + struct net_device *dev = wl_to_prmry_ndev(wl); + struct ether_addr bssid; + struct wl_bss_info *bss = NULL; + + if ((err = wldev_ioctl(dev, WLC_GET_BSSID, &bssid, sizeof(bssid), false))) { + /* STA interface is not associated. So start the new interface on a temp + * channel . Later proper channel will be applied by the above framework + * via set_channel (cfg80211 API). + */ + WL_DBG(("Not associated. Return a temp channel. \n")); + return wl_ch_host_to_driver(WL_P2P_TEMP_CHAN); + } + + + *(u32 *) wl->extra_buf = htod32(WL_EXTRA_BUF_MAX); + if ((err = wldev_ioctl(dev, WLC_GET_BSS_INFO, wl->extra_buf, + WL_EXTRA_BUF_MAX, false))) { + WL_ERR(("Failed to get associated bss info, use temp channel \n")); + chspec = wl_ch_host_to_driver(WL_P2P_TEMP_CHAN); + } + else { + bss = (struct wl_bss_info *) (wl->extra_buf + 4); + chspec = bss->chanspec; + WL_DBG(("Valid BSS Found. chanspec:%d \n", chspec)); + } + return chspec; +} + +static struct net_device* wl_cfg80211_add_monitor_if(char *name) +{ +#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) + WL_INFO(("wl_cfg80211_add_monitor_if: No more support monitor interface\n")); + return ERR_PTR(-EOPNOTSUPP); +#else + struct net_device* ndev = NULL; + + dhd_add_monitor(name, &ndev); + WL_INFO(("wl_cfg80211_add_monitor_if net device returned: 0x%p\n", ndev)); + return ndev; +#endif /* defined(WLP2P) && defined(WL_ENABLE_P2P_IF) */ +} + +static struct net_device * +wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + s32 err; + s32 timeout = -1; + s32 wlif_type = -1; + s32 mode = 0; + s32 val = 0; + s32 dhd_mode = 0; + chanspec_t chspec; + struct wl_priv *wl = wiphy_priv(wiphy); + struct net_device *_ndev; + struct ether_addr primary_mac; + int (*net_attach)(void *dhdp, int ifidx); + bool rollback_lock = false; +#ifdef PROP_TXSTATUS_VSDB + s32 up = 1; + dhd_pub_t *dhd; +#endif /* PROP_TXSTATUS_VSDB */ + + if (!wl) + return ERR_PTR(-EINVAL); + +#ifdef PROP_TXSTATUS_VSDB + dhd = (dhd_pub_t *)(wl->pub); +#endif /* PROP_TXSTATUS_VSDB */ + + + /* Use primary I/F for sending cmds down to firmware */ + _ndev = wl_to_prmry_ndev(wl); + + WL_DBG(("if name: %s, type: %d\n", name, type)); + switch (type) { + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_WDS: + case NL80211_IFTYPE_MESH_POINT: + WL_ERR(("Unsupported interface type\n")); + mode = WL_MODE_IBSS; + return NULL; + case NL80211_IFTYPE_MONITOR: + return wl_cfg80211_add_monitor_if(name); + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_STATION: + wlif_type = WL_P2P_IF_CLIENT; + mode = WL_MODE_BSS; + break; + case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_AP: + wlif_type = WL_P2P_IF_GO; + mode = WL_MODE_AP; + break; + default: + WL_ERR(("Unsupported interface type\n")); + return NULL; + break; + } + + if (!name) { + WL_ERR(("name is NULL\n")); + return NULL; + } + if (wl->p2p_supported && (wlif_type != -1)) { + if (wl_get_p2p_status(wl, IF_DELETING)) { + /* wait till IF_DEL is complete + * release the lock for the unregister to proceed + */ + if (rtnl_is_locked()) { + rtnl_unlock(); + rollback_lock = true; + } + WL_INFO(("%s: Released the lock and wait till IF_DEL is complete\n", + __func__)); + timeout = wait_event_interruptible_timeout(wl->netif_change_event, + (wl_get_p2p_status(wl, IF_DELETING) == false), + msecs_to_jiffies(MAX_WAIT_TIME)); + + /* put back the rtnl_lock again */ + if (rollback_lock) { + rtnl_lock(); + rollback_lock = false; + } + if (timeout > 0) { + WL_ERR(("IF DEL is Success\n")); + + } else { + WL_ERR(("timeount < 0, return -EAGAIN\n")); + return ERR_PTR(-EAGAIN); + } + /* It should be now be safe to put this check here since we are sure + * by now netdev_notifier (unregister) would have been called + */ + if (wl->iface_cnt == IFACE_MAX_CNT) + return ERR_PTR(-ENOMEM); + } + +#ifdef PROP_TXSTATUS_VSDB + if (!dhd) + return ERR_PTR(-ENODEV); +#endif /* PROP_TXSTATUS_VSDB */ + if (!wl->p2p) + return ERR_PTR(-ENODEV); + + if (wl->p2p && !wl->p2p->on && strstr(name, WL_P2P_INTERFACE_PREFIX)) { + p2p_on(wl) = true; + wl_cfgp2p_set_firm_p2p(wl); + wl_cfgp2p_init_discovery(wl); + get_primary_mac(wl, &primary_mac); + wl_cfgp2p_generate_bss_mac(&primary_mac, + &wl->p2p->dev_addr, &wl->p2p->int_addr); + } + + memset(wl->p2p->vir_ifname, 0, IFNAMSIZ); + strncpy(wl->p2p->vir_ifname, name, IFNAMSIZ - 1); + + wl_notify_escan_complete(wl, _ndev, true, true); +#ifdef PROP_TXSTATUS_VSDB + if (!wl->wlfc_on && !disable_proptx) { + dhd->wlfc_enabled = true; + dhd_wlfc_init(dhd); + err = wldev_ioctl(_ndev, WLC_UP, &up, sizeof(s32), true); + if (err < 0) + WL_ERR(("WLC_UP return err:%d\n", err)); + wl->wlfc_on = true; + } +#endif /* PROP_TXSTATUS_VSDB */ + + /* In concurrency case, STA may be already associated in a particular channel. + * so retrieve the current channel of primary interface and then start the virtual + * interface on that. + */ + chspec = wl_cfg80211_get_shared_freq(wiphy); + + /* For P2P mode, use P2P-specific driver features to create the + * bss: "wl p2p_ifadd" + */ + wl_set_p2p_status(wl, IF_ADD); + if (wlif_type == WL_P2P_IF_GO) + wldev_iovar_setint(_ndev, "mpc", 0); + err = wl_cfgp2p_ifadd(wl, &wl->p2p->int_addr, htod32(wlif_type), chspec); + + if (unlikely(err)) { + WL_ERR((" virtual iface add failed (%d) \n", err)); + return ERR_PTR(-ENOMEM); + } + + timeout = wait_event_interruptible_timeout(wl->netif_change_event, + (wl_get_p2p_status(wl, IF_ADD) == false), + msecs_to_jiffies(MAX_WAIT_TIME)); + if (timeout > 0 && (!wl_get_p2p_status(wl, IF_ADD))) { + + struct wireless_dev *vwdev; + vwdev = kzalloc(sizeof(*vwdev), GFP_KERNEL); + if (unlikely(!vwdev)) { + WL_ERR(("Could not allocate wireless device\n")); + return ERR_PTR(-ENOMEM); + } + vwdev->wiphy = wl->wdev->wiphy; + WL_INFO((" virtual interface(%s) is created memalloc done \n", + wl->p2p->vir_ifname)); + vwdev->iftype = type; + _ndev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION); + _ndev->ieee80211_ptr = vwdev; + SET_NETDEV_DEV(_ndev, wiphy_dev(vwdev->wiphy)); + vwdev->netdev = _ndev; + wl_set_drv_status(wl, READY, _ndev); + wl->p2p->vif_created = true; + wl_set_mode_by_netdev(wl, _ndev, mode); + net_attach = wl_to_p2p_bss_private(wl, P2PAPI_BSSCFG_CONNECTION); + if (rtnl_is_locked()) { + rtnl_unlock(); + rollback_lock = true; + } + if (net_attach && !net_attach(wl->pub, _ndev->ifindex)) { + wl_alloc_netinfo(wl, _ndev, vwdev, mode, PM_ENABLE); + val = 1; + /* Disable firmware roaming for P2P interface */ + wldev_iovar_setint(_ndev, "roam_off", val); + WL_ERR((" virtual interface(%s) is " + "created net attach done\n", wl->p2p->vir_ifname)); + if (mode == WL_MODE_AP) + wl_set_drv_status(wl, CONNECTED, _ndev); + if (type == NL80211_IFTYPE_P2P_CLIENT) + dhd_mode = DHD_FLAG_P2P_GC_MODE; + else if (type == NL80211_IFTYPE_P2P_GO) + dhd_mode = DHD_FLAG_P2P_GO_MODE; + DNGL_FUNC(dhd_cfg80211_set_p2p_info, (wl, dhd_mode)); + } else { + /* put back the rtnl_lock again */ + if (rollback_lock) + rtnl_lock(); + goto fail; + } + /* put back the rtnl_lock again */ + if (rollback_lock) + rtnl_lock(); + return _ndev; + + } else { + wl_clr_p2p_status(wl, IF_ADD); + WL_ERR((" virtual interface(%s) is not created \n", wl->p2p->vir_ifname)); + memset(wl->p2p->vir_ifname, '\0', IFNAMSIZ); + wl->p2p->vif_created = false; +#ifdef PROP_TXSTATUS_VSDB + if (dhd->wlfc_enabled && wl->wlfc_on) { + dhd->wlfc_enabled = false; + dhd_wlfc_deinit(dhd); + wl->wlfc_on = false; + } +#endif /* PROP_TXSTATUS_VSDB */ + } + } +fail: + if (wlif_type == WL_P2P_IF_GO) + wldev_iovar_setint(_ndev, "mpc", 1); + return ERR_PTR(-ENODEV); +} + +static s32 +wl_cfg80211_del_virtual_iface(struct wiphy *wiphy, struct net_device *dev) +{ + struct ether_addr p2p_mac; + struct wl_priv *wl = wiphy_priv(wiphy); + s32 timeout = -1; + s32 ret = 0; + WL_DBG(("Enter\n")); + + if (wl->p2p_net == dev) { + /* Since there is no ifidx corresponding to p2p0, cmds to + * firmware should be routed through primary I/F + */ + dev = wl_to_prmry_ndev(wl); + } + + if (wl->p2p_supported) { + memcpy(p2p_mac.octet, wl->p2p->int_addr.octet, ETHER_ADDR_LEN); + + /* Clear GO_NEG_PHASE bit to take care of GO-NEG-FAIL cases + */ + WL_DBG(("P2P: GO_NEG_PHASE status cleared ")); + wl_clr_p2p_status(wl, GO_NEG_PHASE); + if (wl->p2p->vif_created) { + if (wl_get_drv_status(wl, SCANNING, dev)) { + wl_notify_escan_complete(wl, dev, true, true); + } + wldev_iovar_setint(dev, "mpc", 1); + + /* for GC */ + if (wl_get_drv_status(wl, DISCONNECTING, dev) && + (wl_get_mode_by_netdev(wl, dev) != WL_MODE_AP)) { + WL_ERR(("Wait for Link Down event for GC !\n")); + wait_for_completion_timeout + (&wl->iface_disable, msecs_to_jiffies(500)); + } + wl_set_p2p_status(wl, IF_DELETING); + DNGL_FUNC(dhd_cfg80211_clean_p2p_info, (wl)); + + /* for GO */ + if (wl_get_mode_by_netdev(wl, dev) == WL_MODE_AP) { + wl_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, false); + /* disable interface before bsscfg free */ + ret = wl_cfgp2p_ifdisable(wl, &p2p_mac); + /* if fw doesn't support "ifdis", + do not wait for link down of ap mode + */ + if (ret == 0) { + WL_ERR(("Wait for Link Down event for GO !!!\n")); + wait_for_completion_timeout(&wl->iface_disable, + msecs_to_jiffies(500)); + } else { + msleep(300); + } + } + wl_cfgp2p_clear_management_ie(wl, wl_cfgp2p_find_idx(wl, dev)); + /* delete interface after link down */ + ret = wl_cfgp2p_ifdel(wl, &p2p_mac); + /* Firmware could not delete the interface so we will not get WLC_E_IF + * event for cleaning the dhd virtual nw interace + * So lets do it here. Failures from fw will ensure the application to do + * ifconfig down and up sequnce, which will reload the fw + * however we should cleanup the linux network virtual interfaces + */ + /* Request framework to RESET and clean up */ + if (ret) { + struct net_device *ndev = wl_to_prmry_ndev(wl); + WL_ERR(("Firmware returned an error (%d) from p2p_ifdel" + "HANG Notification sent to %s\n", ret, ndev->name)); + net_os_send_hang_message(ndev); + } + /* Wait for IF_DEL operation to be finished in firmware */ + timeout = wait_event_interruptible_timeout(wl->netif_change_event, + (wl->p2p->vif_created == false), + msecs_to_jiffies(MAX_WAIT_TIME)); + if (timeout > 0 && (wl->p2p->vif_created == false)) { + WL_DBG(("IFDEL operation done\n")); + } else { + WL_ERR(("IFDEL didn't complete properly\n")); + } + ret = dhd_del_monitor(dev); + } + } + return ret; +} + +static s32 +wl_cfg80211_change_virtual_iface(struct wiphy *wiphy, struct net_device *ndev, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + s32 ap = 0; + s32 infra = 0; + s32 wlif_type; + s32 mode = 0; + chanspec_t chspec; + struct wl_priv *wl = wiphy_priv(wiphy); + dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); + WL_DBG(("Enter type %d\n", type)); + switch (type) { + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_WDS: + case NL80211_IFTYPE_MESH_POINT: + ap = 1; + WL_ERR(("type (%d) : currently we do not support this type\n", + type)); + break; + case NL80211_IFTYPE_ADHOC: + mode = WL_MODE_IBSS; + break; + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + mode = WL_MODE_BSS; + infra = 1; + break; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_P2P_GO: + mode = WL_MODE_AP; + ap = 1; + break; + default: + return -EINVAL; + } + if (!dhd) + return -EINVAL; + if (ap) { + wl_set_mode_by_netdev(wl, ndev, mode); + if (wl->p2p_supported && wl->p2p->vif_created) { + WL_DBG(("p2p_vif_created (%d) p2p_on (%d)\n", wl->p2p->vif_created, + p2p_on(wl))); + wldev_iovar_setint(ndev, "mpc", 0); + wl_notify_escan_complete(wl, ndev, true, true); + + /* In concurrency case, STA may be already associated in a particular + * channel. so retrieve the current channel of primary interface and + * then start the virtual interface on that. + */ + chspec = wl_cfg80211_get_shared_freq(wiphy); + + wlif_type = WL_P2P_IF_GO; + WL_ERR(("%s : ap (%d), infra (%d), iftype: (%d)\n", + ndev->name, ap, infra, type)); + wl_set_p2p_status(wl, IF_CHANGING); + wl_clr_p2p_status(wl, IF_CHANGED); + wl_cfgp2p_ifchange(wl, &wl->p2p->int_addr, htod32(wlif_type), chspec); + wait_event_interruptible_timeout(wl->netif_change_event, + (wl_get_p2p_status(wl, IF_CHANGED) == true), + msecs_to_jiffies(MAX_WAIT_TIME)); + wl_set_mode_by_netdev(wl, ndev, mode); + dhd->op_mode &= ~DHD_FLAG_P2P_GC_MODE; + dhd->op_mode |= DHD_FLAG_P2P_GO_MODE; + wl_clr_p2p_status(wl, IF_CHANGING); + wl_clr_p2p_status(wl, IF_CHANGED); + if (mode == WL_MODE_AP) + wl_set_drv_status(wl, CONNECTED, ndev); + } else if (ndev == wl_to_prmry_ndev(wl) && + !wl_get_drv_status(wl, AP_CREATED, ndev)) { + wl_set_drv_status(wl, AP_CREATING, ndev); + if (!wl->ap_info && + !(wl->ap_info = kzalloc(sizeof(struct ap_info), GFP_KERNEL))) { + WL_ERR(("struct ap_saved_ie allocation failed\n")); + return -ENOMEM; + } + } else { + WL_ERR(("Cannot change the interface for GO or SOFTAP\n")); + return -EINVAL; + } + } else { + WL_DBG(("Change_virtual_iface for transition from GO/AP to client/STA")); + } + + ndev->ieee80211_ptr->iftype = type; + return 0; +} + +s32 +wl_cfg80211_notify_ifadd(struct net_device *ndev, s32 idx, s32 bssidx, + void* _net_attach) +{ + struct wl_priv *wl = wlcfg_drv_priv; + s32 ret = BCME_OK; + WL_DBG(("Enter")); + if (!ndev) { + WL_ERR(("net is NULL\n")); + return 0; + } + if (wl->p2p_supported && wl_get_p2p_status(wl, IF_ADD)) { + WL_DBG(("IF_ADD event called from dongle, old interface name: %s," + "new name: %s\n", ndev->name, wl->p2p->vir_ifname)); + /* Assign the net device to CONNECT BSSCFG */ + strncpy(ndev->name, wl->p2p->vir_ifname, IFNAMSIZ - 1); + wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION) = ndev; + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_CONNECTION) = bssidx; + wl_to_p2p_bss_private(wl, P2PAPI_BSSCFG_CONNECTION) = _net_attach; + ndev->ifindex = idx; + wl_clr_p2p_status(wl, IF_ADD); + + wake_up_interruptible(&wl->netif_change_event); + } else { + ret = BCME_NOTREADY; + } + return ret; +} + +s32 +wl_cfg80211_notify_ifdel(void) +{ + struct wl_priv *wl = wlcfg_drv_priv; + + WL_DBG(("Enter \n")); + wl_clr_p2p_status(wl, IF_DELETING); + wake_up_interruptible(&wl->netif_change_event); + return 0; +} + +s32 +wl_cfg80211_ifdel_ops(struct net_device *ndev) +{ + struct wl_priv *wl = wlcfg_drv_priv; + bool rollback_lock = false; + s32 index = 0; +#ifdef PROP_TXSTATUS_VSDB + dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); +#endif /* PROP_TXSTATUS_VSDB */ + if (!ndev || (strlen(ndev->name) == 0)) { + WL_ERR(("net is NULL\n")); + return 0; + } + + if (p2p_is_on(wl) && wl->p2p->vif_created && + wl_get_p2p_status(wl, IF_DELETING)) { + if (wl->scan_request && + (wl->escan_info.ndev == ndev)) { + /* Abort any pending scan requests */ + wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; + if (!rtnl_is_locked()) { + rtnl_lock(); + rollback_lock = true; + } + WL_DBG(("ESCAN COMPLETED\n")); + wl_notify_escan_complete(wl, ndev, true, false); + if (rollback_lock) + rtnl_unlock(); + } + WL_ERR(("IF_DEL event called from dongle, net %x, vif name: %s\n", + (unsigned int)ndev, wl->p2p->vir_ifname)); + + memset(wl->p2p->vir_ifname, '\0', IFNAMSIZ); + index = wl_cfgp2p_find_idx(wl, ndev); + wl_to_p2p_bss_ndev(wl, index) = NULL; + wl_to_p2p_bss_bssidx(wl, index) = WL_INVALID; + wl->p2p->vif_created = false; + + WL_DBG(("index : %d\n", index)); +#ifdef PROP_TXSTATUS_VSDB + if (dhd->wlfc_enabled && wl->wlfc_on) { + dhd->wlfc_enabled = false; + dhd_wlfc_deinit(dhd); + wl->wlfc_on = false; + } +#endif /* PROP_TXSTATUS_VSDB */ + wl_clr_drv_status(wl, CONNECTED, ndev); + } + /* Wake up any waiting thread */ + wake_up_interruptible(&wl->netif_change_event); + + return 0; +} + +s32 +wl_cfg80211_is_progress_ifadd(void) +{ + s32 is_progress = 0; + struct wl_priv *wl = wlcfg_drv_priv; + if (wl_get_p2p_status(wl, IF_ADD)) + is_progress = 1; + return is_progress; +} + +s32 +wl_cfg80211_is_progress_ifchange(void) +{ + s32 is_progress = 0; + struct wl_priv *wl = wlcfg_drv_priv; + if (wl_get_p2p_status(wl, IF_CHANGING)) + is_progress = 1; + return is_progress; +} + + +s32 +wl_cfg80211_notify_ifchange(void) +{ + struct wl_priv *wl = wlcfg_drv_priv; + if (wl_get_p2p_status(wl, IF_CHANGING)) { + wl_set_p2p_status(wl, IF_CHANGED); + wake_up_interruptible(&wl->netif_change_event); + } + return 0; +} + +/* Find listen channel */ +static s32 wl_find_listen_channel(struct wl_priv *wl, + u8 *ie, u32 ie_len) +{ + wifi_p2p_ie_t *p2p_ie; + u8 *end, *pos; + s32 listen_channel; + + p2p_ie = wl_cfgp2p_find_p2pie(ie, ie_len); + + if (p2p_ie == NULL) + return 0; + + pos = p2p_ie->subelts; + end = p2p_ie->subelts + (p2p_ie->len - 4); + + CFGP2P_DBG((" found p2p ie ! lenth %d \n", + p2p_ie->len)); + + while (pos < end) { + uint16 attr_len; + if (pos + 2 >= end) { + CFGP2P_DBG((" -- Invalid P2P attribute")); + return 0; + } + attr_len = ((uint16) (((pos + 1)[1] << 8) | (pos + 1)[0])); + + if (pos + 3 + attr_len > end) { + CFGP2P_DBG(("P2P: Attribute underflow " + "(len=%u left=%d)", + attr_len, (int) (end - pos - 3))); + return 0; + } + + /* if Listen Channel att id is 6 and the vailue is valid, + * return the listen channel + */ + if (pos[0] == 6) { + /* listen channel subel length format + * 1(id) + 2(len) + 3(country) + 1(op. class) + 1(chan num) + */ + listen_channel = pos[1 + 2 + 3 + 1]; + + if (listen_channel == SOCIAL_CHAN_1 || + listen_channel == SOCIAL_CHAN_2 || + listen_channel == SOCIAL_CHAN_3) { + CFGP2P_DBG((" Found my Listen Channel %d \n", listen_channel)); + return listen_channel; + } + } + pos += 3 + attr_len; + } + return 0; +} + +static void wl_scan_prep(struct wl_scan_params *params, struct cfg80211_scan_request *request) +{ + u32 n_ssids; + u32 n_channels; + u16 channel; + chanspec_t chanspec; + s32 i = 0, j = 0, offset; + char *ptr; + wlc_ssid_t ssid; + struct wl_priv *wl = wlcfg_drv_priv; + + memcpy(¶ms->bssid, ðer_bcast, ETHER_ADDR_LEN); + params->bss_type = DOT11_BSSTYPE_ANY; + params->scan_type = 0; + params->nprobes = -1; + params->active_time = -1; + params->passive_time = -1; + params->home_time = -1; + params->channel_num = 0; + memset(¶ms->ssid, 0, sizeof(wlc_ssid_t)); + + WL_SCAN(("Preparing Scan request\n")); + WL_SCAN(("nprobes=%d\n", params->nprobes)); + WL_SCAN(("active_time=%d\n", params->active_time)); + WL_SCAN(("passive_time=%d\n", params->passive_time)); + WL_SCAN(("home_time=%d\n", params->home_time)); + WL_SCAN(("scan_type=%d\n", params->scan_type)); + + params->nprobes = htod32(params->nprobes); + params->active_time = htod32(params->active_time); + params->passive_time = htod32(params->passive_time); + params->home_time = htod32(params->home_time); + + /* if request is null just exit so it will be all channel broadcast scan */ + if (!request) + return; + + n_ssids = request->n_ssids; + n_channels = request->n_channels; + + /* Copy channel array if applicable */ + WL_SCAN(("### List of channelspecs to scan ###\n")); + if (n_channels > 0) { + for (i = 0; i < n_channels; i++) { + chanspec = 0; + channel = ieee80211_frequency_to_channel(request->channels[i]->center_freq); + /* SKIP DFS channels for Secondary interface */ + if ((wl->escan_info.ndev != wl_to_prmry_ndev(wl)) && + (request->channels[i]->flags & + (IEEE80211_CHAN_RADAR | IEEE80211_CHAN_PASSIVE_SCAN))) + continue; + + if (request->channels[i]->band == IEEE80211_BAND_2GHZ) { +#ifdef WL_HOST_BAND_MGMT + if (wl->curr_band == WLC_BAND_5G) { + WL_DBG(("In 5G only mode, omit 2G channel:%d\n", channel)); + continue; + } +#endif /* WL_HOST_BAND_MGMT */ + chanspec |= WL_CHANSPEC_BAND_2G; + } else { +#ifdef WL_HOST_BAND_MGMT + if (wl->curr_band == WLC_BAND_2G) { + WL_DBG(("In 2G only mode, omit 5G channel:%d\n", channel)); + continue; + } +#endif /* WL_HOST_BAND_MGMT */ + chanspec |= WL_CHANSPEC_BAND_5G; + } + + chanspec |= WL_CHANSPEC_BW_20; + chanspec |= WL_CHANSPEC_CTL_SB_NONE; + + params->channel_list[j] = channel; + params->channel_list[j] &= WL_CHANSPEC_CHAN_MASK; + params->channel_list[j] |= chanspec; + WL_SCAN(("Chan : %d, Channel spec: %x \n", + channel, params->channel_list[j])); + params->channel_list[j] = wl_chspec_host_to_driver(params->channel_list[j]); + j++; + } + } else { + WL_SCAN(("Scanning all channels\n")); + } + n_channels = j; + /* Copy ssid array if applicable */ + WL_SCAN(("### List of SSIDs to scan ###\n")); + if (n_ssids > 0) { + offset = offsetof(wl_scan_params_t, channel_list) + n_channels * sizeof(u16); + offset = roundup(offset, sizeof(u32)); + ptr = (char*)params + offset; + for (i = 0; i < n_ssids; i++) { + memset(&ssid, 0, sizeof(wlc_ssid_t)); + ssid.SSID_len = request->ssids[i].ssid_len; + memcpy(ssid.SSID, request->ssids[i].ssid, ssid.SSID_len); + if (!ssid.SSID_len) + WL_SCAN(("%d: Broadcast scan\n", i)); + else + WL_SCAN(("%d: scan for %s size =%d\n", i, + ssid.SSID, ssid.SSID_len)); + memcpy(ptr, &ssid, sizeof(wlc_ssid_t)); + ptr += sizeof(wlc_ssid_t); + } + } else { + WL_SCAN(("Broadcast scan\n")); + } + /* Adding mask to channel numbers */ + params->channel_num = + htod32((n_ssids << WL_SCAN_PARAMS_NSSID_SHIFT) | + (n_channels & WL_SCAN_PARAMS_COUNT_MASK)); + + if (n_channels == 1 && wl_get_drv_status_all(wl, CONNECTED)) { + params->active_time = WL_SCAN_CONNECT_DWELL_TIME_MS; + } +} + +static s32 +wl_run_iscan(struct wl_iscan_ctrl *iscan, struct cfg80211_scan_request *request, u16 action) +{ + u32 n_channels; + u32 n_ssids; + s32 params_size = + (WL_SCAN_PARAMS_FIXED_SIZE + offsetof(wl_iscan_params_t, params)); + struct wl_iscan_params *params = NULL; + s32 err = 0; + + if (request != NULL) { + n_channels = request->n_channels; + n_ssids = request->n_ssids; + /* Allocate space for populating ssids in wl_iscan_params struct */ + if (n_channels % 2) + /* If n_channels is odd, add a padd of u16 */ + params_size += sizeof(u16) * (n_channels + 1); + else + params_size += sizeof(u16) * n_channels; + + /* Allocate space for populating ssids in wl_iscan_params struct */ + params_size += sizeof(struct wlc_ssid) * n_ssids; + } + params = (struct wl_iscan_params *)kzalloc(params_size, GFP_KERNEL); + if (!params) { + err = -ENOMEM; + goto done; + } + wl_scan_prep(¶ms->params, request); + + params->version = htod32(ISCAN_REQ_VERSION); + params->action = htod16(action); + params->scan_duration = htod16(0); + + if (params_size + sizeof("iscan") >= WLC_IOCTL_MEDLEN) { + WL_ERR(("ioctl buffer length is not sufficient\n")); + err = -ENOMEM; + goto done; + } + err = wldev_iovar_setbuf(iscan->dev, "iscan", params, params_size, + iscan->ioctl_buf, WLC_IOCTL_MEDLEN, NULL); + if (unlikely(err)) { + if (err == -EBUSY) { + WL_ERR(("system busy : iscan canceled\n")); + } else { + WL_ERR(("error (%d)\n", err)); + } + } + +done: + if (params) + kfree(params); + return err; +} + +static s32 wl_do_iscan(struct wl_priv *wl, struct cfg80211_scan_request *request) +{ + struct wl_iscan_ctrl *iscan = wl_to_iscan(wl); + struct net_device *ndev = wl_to_prmry_ndev(wl); + s32 passive_scan; + s32 err = 0; + + iscan->state = WL_ISCAN_STATE_SCANING; + + passive_scan = wl->active_scan ? 0 : 1; + err = wldev_ioctl(ndev, WLC_SET_PASSIVE_SCAN, + &passive_scan, sizeof(passive_scan), true); + if (unlikely(err)) { + WL_DBG(("error (%d)\n", err)); + return err; + } + wl->iscan_kickstart = true; + wl_run_iscan(iscan, request, WL_SCAN_ACTION_START); + mod_timer(&iscan->timer, jiffies + msecs_to_jiffies(iscan->timer_ms)); + iscan->timer_on = 1; + + return err; +} + +static s32 +wl_get_valid_channels(struct net_device *ndev, u8 *valid_chan_list, s32 size) +{ + wl_uint32_list_t *list; + s32 err = BCME_OK; + if (valid_chan_list == NULL || size <= 0) + return -ENOMEM; + + memset(valid_chan_list, 0, size); + list = (wl_uint32_list_t *)(void *) valid_chan_list; + list->count = htod32(WL_NUMCHANNELS); + err = wldev_ioctl(ndev, WLC_GET_VALID_CHANNELS, valid_chan_list, size, false); + if (err != 0) { + WL_ERR(("get channels failed with %d\n", err)); + } + + return err; +} + +static s32 +wl_run_escan(struct wl_priv *wl, struct net_device *ndev, + struct cfg80211_scan_request *request, uint16 action) +{ + s32 err = BCME_OK; + u32 n_channels; + u32 n_ssids; + s32 params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_escan_params_t, params)); + wl_escan_params_t *params = NULL; + u8 chan_buf[sizeof(u32)*(WL_NUMCHANNELS + 1)]; + u32 num_chans = 0; + s32 channel; + s32 n_valid_chan; + s32 search_state = WL_P2P_DISC_ST_SCAN; + u32 i, j, n_nodfs = 0; + u16 *default_chan_list = NULL; + wl_uint32_list_t *list; + struct net_device *dev = NULL; + + WL_DBG(("Enter \n")); + + if (!wl) { + err = -EINVAL; + goto exit; + } + if (!wl->p2p_supported || !p2p_scan(wl)) { + /* LEGACY SCAN TRIGGER */ + WL_SCAN((" LEGACY E-SCAN START\n")); + + /* if scan request is not empty parse scan request paramters */ + if (request != NULL) { + n_channels = request->n_channels; + n_ssids = request->n_ssids; + /* Allocate space for populating ssids in wl_iscan_params struct */ + if (n_channels % 2) + /* If n_channels is odd, add a padd of u16 */ + params_size += sizeof(u16) * (n_channels + 1); + else + params_size += sizeof(u16) * n_channels; + + /* Allocate space for populating ssids in wl_iscan_params struct */ + params_size += sizeof(struct wlc_ssid) * n_ssids; + } + params = (wl_escan_params_t *) kzalloc(params_size, GFP_KERNEL); + if (params == NULL) { + err = -ENOMEM; + goto exit; + } + + wl_scan_prep(¶ms->params, request); + + params->version = htod32(ESCAN_REQ_VERSION); + params->action = htod16(action); + params->sync_id = htod16(0x1234); + if (params_size + sizeof("escan") >= WLC_IOCTL_MEDLEN) { + WL_ERR(("ioctl buffer length not sufficient\n")); + kfree(params); + err = -ENOMEM; + goto exit; + } + err = wldev_iovar_setbuf(ndev, "escan", params, params_size, + wl->escan_ioctl_buf, WLC_IOCTL_MEDLEN, NULL); + if (unlikely(err)) { + if (err == BCME_EPERM) + /* Scan Not permitted at this point of time */ + WL_DBG((" Escan not permitted at this time (%d)\n", err)); + else + WL_ERR((" Escan set error (%d)\n", err)); + } + kfree(params); + } + else if (p2p_is_on(wl) && p2p_scan(wl)) { + /* P2P SCAN TRIGGER */ + s32 _freq = 0; + n_nodfs = 0; + if (request && request->n_channels) { + num_chans = request->n_channels; + WL_SCAN((" chann number : %d\n", num_chans)); + default_chan_list = kzalloc(num_chans * sizeof(*default_chan_list), + GFP_KERNEL); + if (default_chan_list == NULL) { + WL_ERR(("channel list allocation failed \n")); + err = -ENOMEM; + goto exit; + } + if (!wl_get_valid_channels(ndev, chan_buf, sizeof(chan_buf))) { + list = (wl_uint32_list_t *) chan_buf; + n_valid_chan = dtoh32(list->count); + for (i = 0; i < num_chans; i++) + { +#ifdef WL_HOST_BAND_MGMT + int channel_band = 0; +#endif /* WL_HOST_BAND_MGMT */ + _freq = request->channels[i]->center_freq; + channel = ieee80211_frequency_to_channel(_freq); +#ifdef WL_HOST_BAND_MGMT + channel_band = (channel > CH_MAX_2G_CHANNEL) ? + WLC_BAND_5G : WLC_BAND_2G; + if ((wl->curr_band != WLC_BAND_AUTO) && + (wl->curr_band != channel_band) && + !IS_P2P_SOCIAL_CHANNEL(channel)) + continue; +#endif /* WL_HOST_BAND_MGMT */ + + /* ignore DFS channels */ + if (request->channels[i]->flags & + (IEEE80211_CHAN_RADAR + | IEEE80211_CHAN_PASSIVE_SCAN)) + continue; + + for (j = 0; j < n_valid_chan; j++) { + /* allows only supported channel on + * current reguatory + */ + if (channel == (dtoh32(list->element[j]))) + default_chan_list[n_nodfs++] = + channel; + } + + } + } + if (num_chans == 3 && ( + (default_chan_list[0] == SOCIAL_CHAN_1) && + (default_chan_list[1] == SOCIAL_CHAN_2) && + (default_chan_list[2] == SOCIAL_CHAN_3))) { + /* SOCIAL CHANNELS 1, 6, 11 */ + search_state = WL_P2P_DISC_ST_SEARCH; + WL_INFO(("P2P SEARCH PHASE START \n")); + } else if ((dev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION)) && + (wl_get_mode_by_netdev(wl, dev) == WL_MODE_AP)) { + /* If you are already a GO, then do SEARCH only */ + WL_INFO(("Already a GO. Do SEARCH Only")); + search_state = WL_P2P_DISC_ST_SEARCH; + num_chans = n_nodfs; + + } else { + WL_INFO(("P2P SCAN STATE START \n")); + num_chans = n_nodfs; + } + + } + err = wl_cfgp2p_escan(wl, ndev, wl->active_scan, num_chans, default_chan_list, + search_state, action, + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE), NULL); + kfree(default_chan_list); + } +exit: + if (unlikely(err)) { + /* Don't print Error incase of Scan suppress */ + if ((err == BCME_EPERM) && wl->scan_suppressed) + WL_DBG(("Escan failed: Scan Suppressed \n")); + else + WL_ERR(("error (%d)\n", err)); + } + return err; +} + + +static s32 +wl_do_escan(struct wl_priv *wl, struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_scan_request *request) +{ + s32 err = BCME_OK; + s32 passive_scan; + wl_scan_results_t *results; + WL_SCAN(("Enter \n")); + mutex_lock(&wl->usr_sync); + results = (wl_scan_results_t *) wl->escan_info.escan_buf; + results->version = 0; + results->count = 0; + results->buflen = WL_SCAN_RESULTS_FIXED_SIZE; + + wl->escan_info.ndev = ndev; + wl->escan_info.wiphy = wiphy; + wl->escan_info.escan_state = WL_ESCAN_STATE_SCANING; + passive_scan = wl->active_scan ? 0 : 1; + err = wldev_ioctl(ndev, WLC_SET_PASSIVE_SCAN, + &passive_scan, sizeof(passive_scan), true); + if (unlikely(err)) { + WL_ERR(("error (%d)\n", err)); + goto exit; + } + + err = wl_run_escan(wl, ndev, request, WL_SCAN_ACTION_START); +exit: + mutex_unlock(&wl->usr_sync); + return err; +} + +static s32 +__wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_scan_request *request, + struct cfg80211_ssid *this_ssid) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + struct cfg80211_ssid *ssids; + struct wl_scan_req *sr = wl_to_sr(wl); + struct ether_addr primary_mac; + s32 passive_scan; + bool iscan_req; + bool escan_req = false; + bool p2p_ssid; + s32 err = 0; + s32 bssidx = -1; + s32 i; + + unsigned long flags; + static s32 busy_count = 0; + + /* If scan req comes for p2p0, send it over primary I/F + * Scan results will be delivered corresponding to cfg80211_scan_request + */ + if (ndev == wl->p2p_net) { + ndev = wl_to_prmry_ndev(wl); + } + + if (WL_DRV_STATUS_SENDING_AF_FRM_EXT(wl)) { + WL_ERR(("Sending Action Frames. Try it again.\n")); + return -EAGAIN; + } + + WL_DBG(("Enter wiphy (%p)\n", wiphy)); + if (wl_get_drv_status_all(wl, SCANNING)) { + if (wl->scan_request == NULL) { + wl_clr_drv_status_all(wl, SCANNING); + WL_DBG(("<<<<<<<<<<>>>>>>>>>>\n")); + } else { + WL_ERR(("Scanning already\n")); + return -EAGAIN; + } + } + if (wl_get_drv_status(wl, SCAN_ABORTING, ndev)) { + WL_ERR(("Scanning being aborted\n")); + return -EAGAIN; + } + if (request && request->n_ssids > WL_SCAN_PARAMS_SSID_MAX) { + WL_ERR(("request null or n_ssids > WL_SCAN_PARAMS_SSID_MAX\n")); + return -EOPNOTSUPP; + } +#ifdef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST + if (wl_get_drv_status_all(wl, REMAINING_ON_CHANNEL)) { + WL_DBG(("Remain_on_channel bit is set, somehow it didn't get cleared\n")); + wl_notify_escan_complete(wl, ndev, true, true); + } +#endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ + + /* Arm scan timeout timer */ + mod_timer(&wl->scan_timeout, jiffies + msecs_to_jiffies(WL_SCAN_TIMER_INTERVAL_MS)); + iscan_req = false; + if (request) { /* scan bss */ + ssids = request->ssids; + if (wl->iscan_on && (!ssids || !ssids->ssid_len || request->n_ssids != 1)) { + iscan_req = true; + } else if (wl->escan_on) { + escan_req = true; + p2p_ssid = false; + for (i = 0; i < request->n_ssids; i++) { + if (ssids[i].ssid_len && + IS_P2P_SSID(ssids[i].ssid, ssids[i].ssid_len)) { + p2p_ssid = true; + break; + } + } + if (p2p_ssid) { + if (wl->p2p_supported) { + /* p2p scan trigger */ + if (p2p_on(wl) == false) { + /* p2p on at the first time */ + p2p_on(wl) = true; + wl_cfgp2p_set_firm_p2p(wl); + get_primary_mac(wl, &primary_mac); + wl_cfgp2p_generate_bss_mac(&primary_mac, + &wl->p2p->dev_addr, &wl->p2p->int_addr); + } + wl_clr_p2p_status(wl, GO_NEG_PHASE); + WL_DBG(("P2P: GO_NEG_PHASE status cleared \n")); + p2p_scan(wl) = true; + } + } else { + /* legacy scan trigger + * So, we have to disable p2p discovery if p2p discovery is on + */ + if (wl->p2p_supported) { + p2p_scan(wl) = false; + /* If Netdevice is not equals to primary and p2p is on + * , we will do p2p scan using P2PAPI_BSSCFG_DEVICE. + */ + + if (p2p_scan(wl) == false) { + if (wl_get_p2p_status(wl, DISCOVERY_ON)) { + err = wl_cfgp2p_discover_enable_search(wl, + false); + if (unlikely(err)) { + goto scan_out; + } + + } + } + } + if (!wl->p2p_supported || !p2p_scan(wl)) { + bssidx = wl_cfgp2p_find_idx(wl, ndev); + err = wl_cfgp2p_set_management_ie(wl, ndev, bssidx, + VNDR_IE_PRBREQ_FLAG, (u8 *)request->ie, + request->ie_len); + + if (unlikely(err)) { + goto scan_out; + } + + } + } + } + } else { /* scan in ibss */ + /* we don't do iscan in ibss */ + ssids = this_ssid; + } + wl->scan_request = request; + wl_set_drv_status(wl, SCANNING, ndev); + if (iscan_req) { + err = wl_do_iscan(wl, request); + if (likely(!err)) + goto scan_success; + else + goto scan_out; + } else if (escan_req) { + if (wl->p2p_supported) { + if (p2p_on(wl) && p2p_scan(wl)) { + + /* find my listen channel */ + wl->afx_hdl->my_listen_chan = + wl_find_listen_channel(wl, (u8 *)request->ie, + request->ie_len); + err = wl_cfgp2p_enable_discovery(wl, ndev, + request->ie, request->ie_len); + + if (unlikely(err)) { + goto scan_out; + } + } + } + err = wl_do_escan(wl, wiphy, ndev, request); + if (likely(!err)) + goto scan_success; + else + goto scan_out; + + + } else { + memset(&sr->ssid, 0, sizeof(sr->ssid)); + sr->ssid.SSID_len = + min_t(u8, sizeof(sr->ssid.SSID), ssids->ssid_len); + if (sr->ssid.SSID_len) { + memcpy(sr->ssid.SSID, ssids->ssid, sr->ssid.SSID_len); + sr->ssid.SSID_len = htod32(sr->ssid.SSID_len); + WL_SCAN(("Specific scan ssid=\"%s\" len=%d\n", + sr->ssid.SSID, sr->ssid.SSID_len)); + } else { + WL_SCAN(("Broadcast scan\n")); + } + WL_SCAN(("sr->ssid.SSID_len (%d)\n", sr->ssid.SSID_len)); + passive_scan = wl->active_scan ? 0 : 1; + err = wldev_ioctl(ndev, WLC_SET_PASSIVE_SCAN, + &passive_scan, sizeof(passive_scan), true); + if (unlikely(err)) { + WL_SCAN(("WLC_SET_PASSIVE_SCAN error (%d)\n", err)); + goto scan_out; + } + err = wldev_ioctl(ndev, WLC_SCAN, &sr->ssid, + sizeof(sr->ssid), false); + if (err) { + if (err == -EBUSY) { + WL_ERR(("system busy : scan for \"%s\" " + "canceled\n", sr->ssid.SSID)); + } else { + WL_ERR(("WLC_SCAN error (%d)\n", err)); + } + goto scan_out; + } + } + +scan_success: + + busy_count = 0; + + return 0; + +scan_out: + + if (err == BCME_BUSY || err == BCME_NOTREADY) { + WL_ERR(("Scan err = (%d), busy?%d", err, -EBUSY)); + err = -EBUSY; + } + +#define SCAN_EBUSY_RETRY_LIMIT 10 + if (err == -EBUSY) { + if (busy_count++ > SCAN_EBUSY_RETRY_LIMIT) { + struct ether_addr bssid; + s32 ret = 0; + busy_count = 0; + WL_ERR(("Unusual continuous EBUSY error, %d %d %d %d %d %d %d %d %d\n", + wl_get_drv_status(wl, SCANNING, ndev), + wl_get_drv_status(wl, SCAN_ABORTING, ndev), + wl_get_drv_status(wl, CONNECTING, ndev), + wl_get_drv_status(wl, CONNECTED, ndev), + wl_get_drv_status(wl, DISCONNECTING, ndev), + wl_get_drv_status(wl, AP_CREATING, ndev), + wl_get_drv_status(wl, AP_CREATED, ndev), + wl_get_drv_status(wl, SENDING_ACT_FRM, ndev), + wl_get_drv_status(wl, SENDING_ACT_FRM, ndev))); + + bzero(&bssid, sizeof(bssid)); + if ((ret = wldev_ioctl(ndev, WLC_GET_BSSID, + &bssid, ETHER_ADDR_LEN, false)) == 0) + WL_ERR(("FW is connected with " MACDBG "/n", + MAC2STRDBG(bssid.octet))); + else + WL_ERR(("GET BSSID failed with %d\n", ret)); + + wl_cfg80211_disconnect(wiphy, ndev, DOT11_RC_DISASSOC_LEAVING); + } + } else { + busy_count = 0; + } + wl_clr_drv_status(wl, SCANNING, ndev); + if (timer_pending(&wl->scan_timeout)) + del_timer_sync(&wl->scan_timeout); + spin_lock_irqsave(&wl->cfgdrv_lock, flags); + wl->scan_request = NULL; + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); + return err; +} + +static s32 +wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_scan_request *request) +{ + s32 err = 0; + struct wl_priv *wl = wiphy_priv(wiphy); + + WL_DBG(("Enter \n")); + CHECK_SYS_UP(wl); + + err = __wl_cfg80211_scan(wiphy, ndev, request, NULL); + if (unlikely(err)) { + if ((err == BCME_EPERM) && wl->scan_suppressed) + WL_DBG(("scan not permitted at this time (%d)\n", err)); + else + WL_ERR(("scan error (%d)\n", err)); + return err; + } + + return err; +} + +static s32 wl_set_rts(struct net_device *dev, u32 rts_threshold) +{ + s32 err = 0; + + err = wldev_iovar_setint(dev, "rtsthresh", rts_threshold); + if (unlikely(err)) { + WL_ERR(("Error (%d)\n", err)); + return err; + } + return err; +} + +static s32 wl_set_frag(struct net_device *dev, u32 frag_threshold) +{ + s32 err = 0; + + err = wldev_iovar_setint_bsscfg(dev, "fragthresh", frag_threshold, 0); + if (unlikely(err)) { + WL_ERR(("Error (%d)\n", err)); + return err; + } + return err; +} + +static s32 wl_set_retry(struct net_device *dev, u32 retry, bool l) +{ + s32 err = 0; + u32 cmd = (l ? WLC_SET_LRL : WLC_SET_SRL); + + retry = htod32(retry); + err = wldev_ioctl(dev, cmd, &retry, sizeof(retry), true); + if (unlikely(err)) { + WL_ERR(("cmd (%d) , error (%d)\n", cmd, err)); + return err; + } + return err; +} + +static s32 wl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) +{ + struct wl_priv *wl = (struct wl_priv *)wiphy_priv(wiphy); + struct net_device *ndev = wl_to_prmry_ndev(wl); + s32 err = 0; + + CHECK_SYS_UP(wl); + WL_DBG(("Enter\n")); + if (changed & WIPHY_PARAM_RTS_THRESHOLD && + (wl->conf->rts_threshold != wiphy->rts_threshold)) { + wl->conf->rts_threshold = wiphy->rts_threshold; + err = wl_set_rts(ndev, wl->conf->rts_threshold); + if (!err) + return err; + } + if (changed & WIPHY_PARAM_FRAG_THRESHOLD && + (wl->conf->frag_threshold != wiphy->frag_threshold)) { + wl->conf->frag_threshold = wiphy->frag_threshold; + err = wl_set_frag(ndev, wl->conf->frag_threshold); + if (!err) + return err; + } + if (changed & WIPHY_PARAM_RETRY_LONG && + (wl->conf->retry_long != wiphy->retry_long)) { + wl->conf->retry_long = wiphy->retry_long; + err = wl_set_retry(ndev, wl->conf->retry_long, true); + if (!err) + return err; + } + if (changed & WIPHY_PARAM_RETRY_SHORT && + (wl->conf->retry_short != wiphy->retry_short)) { + wl->conf->retry_short = wiphy->retry_short; + err = wl_set_retry(ndev, wl->conf->retry_short, false); + if (!err) { + return err; + } + } + + return err; +} + +static s32 +wl_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ibss_params *params) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + struct cfg80211_bss *bss; + struct ieee80211_channel *chan; + struct wl_join_params join_params; + struct cfg80211_ssid ssid; + s32 scan_retry = 0; + s32 err = 0; + bool rollback_lock = false; + + WL_TRACE(("In\n")); + CHECK_SYS_UP(wl); + if (params->bssid) { + WL_ERR(("Invalid bssid\n")); + return -EOPNOTSUPP; + } + bss = cfg80211_get_ibss(wiphy, NULL, params->ssid, params->ssid_len); + if (!bss) { + memcpy(ssid.ssid, params->ssid, params->ssid_len); + ssid.ssid_len = params->ssid_len; + do { + if (unlikely + (__wl_cfg80211_scan(wiphy, dev, NULL, &ssid) == + -EBUSY)) { + wl_delay(150); + } else { + break; + } + } while (++scan_retry < WL_SCAN_RETRY_MAX); + /* to allow scan_inform to propagate to cfg80211 plane */ + if (rtnl_is_locked()) { + rtnl_unlock(); + rollback_lock = true; + } + + /* wait 4 secons till scan done.... */ + schedule_timeout_interruptible(msecs_to_jiffies(4000)); + if (rollback_lock) + rtnl_lock(); + bss = cfg80211_get_ibss(wiphy, NULL, + params->ssid, params->ssid_len); + } + if (bss) { + wl->ibss_starter = false; + WL_DBG(("Found IBSS\n")); + } else { + wl->ibss_starter = true; + } + chan = params->channel; + if (chan) + wl->channel = ieee80211_frequency_to_channel(chan->center_freq); + /* + * Join with specific BSSID and cached SSID + * If SSID is zero join based on BSSID only + */ + memset(&join_params, 0, sizeof(join_params)); + memcpy((void *)join_params.ssid.SSID, (void *)params->ssid, + params->ssid_len); + join_params.ssid.SSID_len = htod32(params->ssid_len); + if (params->bssid) + memcpy(&join_params.params.bssid, params->bssid, + ETHER_ADDR_LEN); + else + memset(&join_params.params.bssid, 0, ETHER_ADDR_LEN); + + err = wldev_ioctl(dev, WLC_SET_SSID, &join_params, + sizeof(join_params), true); + if (unlikely(err)) { + WL_ERR(("Error (%d)\n", err)); + return err; + } + return err; +} + +static s32 wl_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + s32 err = 0; + + CHECK_SYS_UP(wl); + wl_link_down(wl); + + return err; +} + +static s32 +wl_set_wpa_version(struct net_device *dev, struct cfg80211_connect_params *sme) +{ + struct wl_priv *wl = wlcfg_drv_priv; + struct wl_security *sec; + s32 val = 0; + s32 err = 0; + s32 bssidx = wl_cfgp2p_find_idx(wl, dev); + + if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) + val = WPA_AUTH_PSK | + WPA_AUTH_UNSPECIFIED; + else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) + val = WPA2_AUTH_PSK| + WPA2_AUTH_UNSPECIFIED; + else + val = WPA_AUTH_DISABLED; + + if (is_wps_conn(sme)) + val = WPA_AUTH_DISABLED; + + WL_DBG(("setting wpa_auth to 0x%0x\n", val)); + err = wldev_iovar_setint_bsscfg(dev, "wpa_auth", val, bssidx); + if (unlikely(err)) { + WL_ERR(("set wpa_auth failed (%d)\n", err)); + return err; + } + sec = wl_read_prof(wl, dev, WL_PROF_SEC); + sec->wpa_versions = sme->crypto.wpa_versions; + return err; +} + + +static s32 +wl_set_auth_type(struct net_device *dev, struct cfg80211_connect_params *sme) +{ + struct wl_priv *wl = wlcfg_drv_priv; + struct wl_security *sec; + s32 val = 0; + s32 err = 0; + s32 bssidx = wl_cfgp2p_find_idx(wl, dev); + switch (sme->auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + val = WL_AUTH_OPEN_SYSTEM; + WL_DBG(("open system\n")); + break; + case NL80211_AUTHTYPE_SHARED_KEY: + val = WL_AUTH_SHARED_KEY; + WL_DBG(("shared key\n")); + break; + case NL80211_AUTHTYPE_AUTOMATIC: + val = WL_AUTH_OPEN_SHARED; + WL_DBG(("automatic\n")); + break; + default: + val = WL_AUTH_OPEN_SHARED; + WL_ERR(("invalid auth type (%d)\n", sme->auth_type)); + break; + } + + err = wldev_iovar_setint_bsscfg(dev, "auth", val, bssidx); + if (unlikely(err)) { + WL_ERR(("set auth failed (%d)\n", err)); + return err; + } + sec = wl_read_prof(wl, dev, WL_PROF_SEC); + sec->auth_type = sme->auth_type; + return err; +} + +static s32 +wl_set_set_cipher(struct net_device *dev, struct cfg80211_connect_params *sme) +{ + struct wl_priv *wl = wlcfg_drv_priv; + struct wl_security *sec; + s32 pval = 0; + s32 gval = 0; + s32 err = 0; + s32 bssidx = wl_cfgp2p_find_idx(wl, dev); + + if (sme->crypto.n_ciphers_pairwise) { + switch (sme->crypto.ciphers_pairwise[0]) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + pval = WEP_ENABLED; + break; + case WLAN_CIPHER_SUITE_TKIP: + pval = TKIP_ENABLED; + break; + case WLAN_CIPHER_SUITE_CCMP: + pval = AES_ENABLED; + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + pval = AES_ENABLED; + break; + default: + WL_ERR(("invalid cipher pairwise (%d)\n", + sme->crypto.ciphers_pairwise[0])); + return -EINVAL; + } + } + if (sme->crypto.cipher_group) { + switch (sme->crypto.cipher_group) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + gval = WEP_ENABLED; + break; + case WLAN_CIPHER_SUITE_TKIP: + gval = TKIP_ENABLED; + break; + case WLAN_CIPHER_SUITE_CCMP: + gval = AES_ENABLED; + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + gval = AES_ENABLED; + break; + default: + WL_ERR(("invalid cipher group (%d)\n", + sme->crypto.cipher_group)); + return -EINVAL; + } + } + + WL_DBG(("pval (%d) gval (%d)\n", pval, gval)); + + if (is_wps_conn(sme)) { + if (sme->privacy) + err = wldev_iovar_setint_bsscfg(dev, "wsec", 4, bssidx); + else + /* WPS-2.0 allows no security */ + err = wldev_iovar_setint_bsscfg(dev, "wsec", 0, bssidx); + } else { + WL_DBG((" NO, is_wps_conn, Set pval | gval to WSEC")); + err = wldev_iovar_setint_bsscfg(dev, "wsec", + pval | gval, bssidx); + } + if (unlikely(err)) { + WL_ERR(("error (%d)\n", err)); + return err; + } + + sec = wl_read_prof(wl, dev, WL_PROF_SEC); + sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0]; + sec->cipher_group = sme->crypto.cipher_group; + + return err; +} + +static s32 +wl_set_key_mgmt(struct net_device *dev, struct cfg80211_connect_params *sme) +{ + struct wl_priv *wl = wlcfg_drv_priv; + struct wl_security *sec; + s32 val = 0; + s32 err = 0; + s32 bssidx = wl_cfgp2p_find_idx(wl, dev); + + if (sme->crypto.n_akm_suites) { + err = wldev_iovar_getint(dev, "wpa_auth", &val); + if (unlikely(err)) { + WL_ERR(("could not get wpa_auth (%d)\n", err)); + return err; + } + if (val & (WPA_AUTH_PSK | + WPA_AUTH_UNSPECIFIED)) { + switch (sme->crypto.akm_suites[0]) { + case WLAN_AKM_SUITE_8021X: + val = WPA_AUTH_UNSPECIFIED; + break; + case WLAN_AKM_SUITE_PSK: + val = WPA_AUTH_PSK; + break; + default: + WL_ERR(("invalid cipher group (%d)\n", + sme->crypto.cipher_group)); + return -EINVAL; + } + } else if (val & (WPA2_AUTH_PSK | + WPA2_AUTH_UNSPECIFIED)) { + switch (sme->crypto.akm_suites[0]) { + case WLAN_AKM_SUITE_8021X: + val = WPA2_AUTH_UNSPECIFIED; + break; + case WLAN_AKM_SUITE_PSK: + val = WPA2_AUTH_PSK; + break; + default: + WL_ERR(("invalid cipher group (%d)\n", + sme->crypto.cipher_group)); + return -EINVAL; + } + } + WL_DBG(("setting wpa_auth to %d\n", val)); + + err = wldev_iovar_setint_bsscfg(dev, "wpa_auth", val, bssidx); + if (unlikely(err)) { + WL_ERR(("could not set wpa_auth (%d)\n", err)); + return err; + } + } + sec = wl_read_prof(wl, dev, WL_PROF_SEC); + sec->wpa_auth = sme->crypto.akm_suites[0]; + + return err; +} + +static s32 +wl_set_set_sharedkey(struct net_device *dev, + struct cfg80211_connect_params *sme) +{ + struct wl_priv *wl = wlcfg_drv_priv; + struct wl_security *sec; + struct wl_wsec_key key; + s32 val; + s32 err = 0; + s32 bssidx = wl_cfgp2p_find_idx(wl, dev); + + WL_DBG(("key len (%d)\n", sme->key_len)); + if (sme->key_len) { + sec = wl_read_prof(wl, dev, WL_PROF_SEC); + WL_DBG(("wpa_versions 0x%x cipher_pairwise 0x%x\n", + sec->wpa_versions, sec->cipher_pairwise)); + if (!(sec->wpa_versions & (NL80211_WPA_VERSION_1 | + NL80211_WPA_VERSION_2)) && + (sec->cipher_pairwise & (WLAN_CIPHER_SUITE_WEP40 | + WLAN_CIPHER_SUITE_WEP104))) + { + memset(&key, 0, sizeof(key)); + key.len = (u32) sme->key_len; + key.index = (u32) sme->key_idx; + if (unlikely(key.len > sizeof(key.data))) { + WL_ERR(("Too long key length (%u)\n", key.len)); + return -EINVAL; + } + memcpy(key.data, sme->key, key.len); + key.flags = WL_PRIMARY_KEY; + switch (sec->cipher_pairwise) { + case WLAN_CIPHER_SUITE_WEP40: + key.algo = CRYPTO_ALGO_WEP1; + break; + case WLAN_CIPHER_SUITE_WEP104: + key.algo = CRYPTO_ALGO_WEP128; + break; + default: + WL_ERR(("Invalid algorithm (%d)\n", + sme->crypto.ciphers_pairwise[0])); + return -EINVAL; + } + /* Set the new key/index */ + WL_DBG(("key length (%d) key index (%d) algo (%d)\n", + key.len, key.index, key.algo)); + WL_DBG(("key \"%s\"\n", key.data)); + swap_key_from_BE(&key); + err = wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), + wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); + if (unlikely(err)) { + WL_ERR(("WLC_SET_KEY error (%d)\n", err)); + return err; + } + if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) { + WL_DBG(("set auth_type to shared key\n")); + val = WL_AUTH_SHARED_KEY; /* shared key */ + err = wldev_iovar_setint_bsscfg(dev, "auth", val, bssidx); + if (unlikely(err)) { + WL_ERR(("set auth failed (%d)\n", err)); + return err; + } + } + } + } + return err; +} + +#ifdef ESCAN_RESULT_PATCH +static u8 connect_req_bssid[6]; +static u8 broad_bssid[6]; +#endif + + +static s32 +wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + struct ieee80211_channel *chan = sme->channel; + wl_extjoin_params_t *ext_join_params; + struct wl_join_params join_params; + size_t join_params_size; + s32 err = 0; + wpa_ie_fixed_t *wpa_ie; + bcm_tlv_t *wpa2_ie; + u8* wpaie = 0; + u32 wpaie_len = 0; + u32 chan_cnt = 0; + struct ether_addr bssid; + int ret; + + WL_DBG(("In\n")); + + if (unlikely(!sme->ssid)) { + WL_ERR(("Invalid ssid\n")); + return -EOPNOTSUPP; + } + + CHECK_SYS_UP(wl); + + /* + * Cancel ongoing scan to sync up with sme state machine of cfg80211. + */ +#if !defined(ESCAN_RESULT_PATCH) + if (wl->scan_request) { + wl_notify_escan_complete(wl, dev, true, true); + } +#endif +#ifdef ESCAN_RESULT_PATCH + if (sme->bssid) { + memcpy(connect_req_bssid, sme->bssid, ETHER_ADDR_LEN); + } + else { + bzero(connect_req_bssid, ETHER_ADDR_LEN); + } + bzero(broad_bssid, ETHER_ADDR_LEN); +#endif + + bzero(&bssid, sizeof(bssid)); + if (!wl_get_drv_status(wl, CONNECTED, dev)&& + (ret = wldev_ioctl(dev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, false)) == 0) { + if (!ETHER_ISNULLADDR(&bssid)) { + scb_val_t scbval; + wl_set_drv_status(wl, DISCONNECTING, dev); + scbval.val = DOT11_RC_DISASSOC_LEAVING; + memcpy(&scbval.ea, &bssid, ETHER_ADDR_LEN); + scbval.val = htod32(scbval.val); + + WL_DBG(("drv status CONNECTED is not set, but connected in FW!" MACDBG "/n", + MAC2STRDBG(bssid.octet))); + err = wldev_ioctl(dev, WLC_DISASSOC, &scbval, + sizeof(scb_val_t), true); + if (unlikely(err)) { + wl_clr_drv_status(wl, DISCONNECTING, dev); + WL_ERR(("error (%d)\n", err)); + return err; + } + while (wl_get_drv_status(wl, DISCONNECTING, dev)) { + WL_ERR(("Waiting for disconnection terminated.\n")); + msleep(20); + } + } else + WL_DBG(("Currently not associated!\n")); + } + + /* Clean BSSID */ + bzero(&bssid, sizeof(bssid)); + if (!wl_get_drv_status(wl, DISCONNECTING, dev)) + wl_update_prof(wl, dev, NULL, (void *)&bssid, WL_PROF_BSSID); + + if (p2p_is_on(wl) && (dev != wl_to_prmry_ndev(wl))) { + /* we only allow to connect using virtual interface in case of P2P */ + wl_cfgp2p_set_management_ie(wl, dev, wl_cfgp2p_find_idx(wl, dev), + VNDR_IE_ASSOCREQ_FLAG, sme->ie, sme->ie_len); + } else if (dev == wl_to_prmry_ndev(wl)) { + /* find the RSN_IE */ + if ((wpa2_ie = bcm_parse_tlvs((u8 *)sme->ie, sme->ie_len, + DOT11_MNG_RSN_ID)) != NULL) { + WL_DBG((" WPA2 IE is found\n")); + } + /* find the WPA_IE */ + if ((wpa_ie = wl_cfgp2p_find_wpaie((u8 *)sme->ie, + sme->ie_len)) != NULL) { + WL_DBG((" WPA IE is found\n")); + } + if (wpa_ie != NULL || wpa2_ie != NULL) { + wpaie = (wpa_ie != NULL) ? (u8 *)wpa_ie : (u8 *)wpa2_ie; + wpaie_len = (wpa_ie != NULL) ? wpa_ie->length : wpa2_ie->len; + wpaie_len += WPA_RSN_IE_TAG_FIXED_LEN; + wldev_iovar_setbuf(dev, "wpaie", wpaie, wpaie_len, + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); + } else { + wldev_iovar_setbuf(dev, "wpaie", NULL, 0, + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); + } + + err = wl_cfgp2p_set_management_ie(wl, dev, wl_cfgp2p_find_idx(wl, dev), + VNDR_IE_ASSOCREQ_FLAG, (u8 *)sme->ie, sme->ie_len); + if (unlikely(err)) { + return err; + } + } + + if (chan) { + wl->channel = ieee80211_frequency_to_channel(chan->center_freq); + chan_cnt = 1; + WL_DBG(("channel (%d), center_req (%d), %d channels\n", wl->channel, + chan->center_freq, chan_cnt)); + } else + wl->channel = 0; + WL_DBG(("ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len)); + WL_DBG(("3. set wapi version \n")); + err = wl_set_wpa_version(dev, sme); + if (unlikely(err)) { + WL_ERR(("Invalid wpa_version\n")); + return err; + } + err = wl_set_auth_type(dev, sme); + if (unlikely(err)) { + WL_ERR(("Invalid auth type\n")); + return err; + } + + err = wl_set_set_cipher(dev, sme); + if (unlikely(err)) { + WL_ERR(("Invalid ciper\n")); + return err; + } + + err = wl_set_key_mgmt(dev, sme); + if (unlikely(err)) { + WL_ERR(("Invalid key mgmt\n")); + return err; + } + + err = wl_set_set_sharedkey(dev, sme); + if (unlikely(err)) { + WL_ERR(("Invalid shared key\n")); + return err; + } + + /* + * Join with specific BSSID and cached SSID + * If SSID is zero join based on BSSID only + */ + join_params_size = WL_EXTJOIN_PARAMS_FIXED_SIZE + + chan_cnt * sizeof(chanspec_t); + ext_join_params = (wl_extjoin_params_t*)kzalloc(join_params_size, GFP_KERNEL); + if (ext_join_params == NULL) { + err = -ENOMEM; + wl_clr_drv_status(wl, CONNECTING, dev); + goto exit; + } + ext_join_params->ssid.SSID_len = min(sizeof(ext_join_params->ssid.SSID), sme->ssid_len); + memcpy(&ext_join_params->ssid.SSID, sme->ssid, ext_join_params->ssid.SSID_len); + wl_update_prof(wl, dev, NULL, &ext_join_params->ssid, WL_PROF_SSID); + ext_join_params->ssid.SSID_len = htod32(ext_join_params->ssid.SSID_len); + /* increate dwell time to receive probe response or detect Beacon + * from target AP at a noisy air only during connect command + */ + ext_join_params->scan.active_time = WL_SCAN_JOIN_ACTIVE_DWELL_TIME_MS; + ext_join_params->scan.passive_time = WL_SCAN_JOIN_PASSIVE_DWELL_TIME_MS; + /* Set up join scan parameters */ + ext_join_params->scan.scan_type = -1; + ext_join_params->scan.nprobes + = (ext_join_params->scan.active_time/WL_SCAN_JOIN_PROBE_INTERVAL_MS); + ext_join_params->scan.home_time = -1; + + if (sme->bssid) + memcpy(&ext_join_params->assoc.bssid, sme->bssid, ETH_ALEN); + else + memcpy(&ext_join_params->assoc.bssid, ðer_bcast, ETH_ALEN); + ext_join_params->assoc.chanspec_num = chan_cnt; + if (chan_cnt) { + u16 channel, band, bw, ctl_sb; + chanspec_t chspec; + channel = wl->channel; + band = (channel <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G + : WL_CHANSPEC_BAND_5G; + bw = WL_CHANSPEC_BW_20; + ctl_sb = WL_CHANSPEC_CTL_SB_NONE; + chspec = (channel | band | bw | ctl_sb); + ext_join_params->assoc.chanspec_list[0] &= WL_CHANSPEC_CHAN_MASK; + ext_join_params->assoc.chanspec_list[0] |= chspec; + ext_join_params->assoc.chanspec_list[0] = + wl_chspec_host_to_driver(ext_join_params->assoc.chanspec_list[0]); + } + ext_join_params->assoc.chanspec_num = htod32(ext_join_params->assoc.chanspec_num); + if (ext_join_params->ssid.SSID_len < IEEE80211_MAX_SSID_LEN) { + WL_INFO(("ssid \"%s\", len (%d)\n", ext_join_params->ssid.SSID, + ext_join_params->ssid.SSID_len)); + } + wl_set_drv_status(wl, CONNECTING, dev); + err = wldev_iovar_setbuf_bsscfg(dev, "join", ext_join_params, join_params_size, + wl->ioctl_buf, WLC_IOCTL_MAXLEN, wl_cfgp2p_find_idx(wl, dev), &wl->ioctl_buf_sync); + kfree(ext_join_params); + if (err) { + wl_clr_drv_status(wl, CONNECTING, dev); + if (err == BCME_UNSUPPORTED) { + WL_DBG(("join iovar is not supported\n")); + goto set_ssid; + } else + WL_ERR(("error (%d)\n", err)); + } else + goto exit; + +set_ssid: + memset(&join_params, 0, sizeof(join_params)); + join_params_size = sizeof(join_params.ssid); + + join_params.ssid.SSID_len = min(sizeof(join_params.ssid.SSID), sme->ssid_len); + memcpy(&join_params.ssid.SSID, sme->ssid, join_params.ssid.SSID_len); + join_params.ssid.SSID_len = htod32(join_params.ssid.SSID_len); + wl_update_prof(wl, dev, NULL, &join_params.ssid, WL_PROF_SSID); + if (sme->bssid) + memcpy(&join_params.params.bssid, sme->bssid, ETH_ALEN); + else + memcpy(&join_params.params.bssid, ðer_bcast, ETH_ALEN); + + wl_ch_to_chanspec(wl->channel, &join_params, &join_params_size); + WL_DBG(("join_param_size %d\n", join_params_size)); + + if (join_params.ssid.SSID_len < IEEE80211_MAX_SSID_LEN) { + WL_INFO(("ssid \"%s\", len (%d)\n", join_params.ssid.SSID, + join_params.ssid.SSID_len)); + } + wl_set_drv_status(wl, CONNECTING, dev); + err = wldev_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size, true); + if (err) { + WL_ERR(("error (%d)\n", err)); + wl_clr_drv_status(wl, CONNECTING, dev); + } +exit: + return err; +} + +static s32 +wl_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, + u16 reason_code) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + scb_val_t scbval; + bool act = false; + s32 err = 0; + u8 *curbssid; + WL_ERR(("Reason %d\n", reason_code)); + CHECK_SYS_UP(wl); + act = *(bool *) wl_read_prof(wl, dev, WL_PROF_ACT); + curbssid = wl_read_prof(wl, dev, WL_PROF_BSSID); + if (act) { + /* + * Cancel ongoing scan to sync up with sme state machine of cfg80211. + */ +#if !defined(ESCAN_RESULT_PATCH) + /* Let scan aborted by F/W */ + if (wl->scan_request) { + wl_notify_escan_complete(wl, dev, true, true); + } +#endif /* ESCAN_RESULT_PATCH */ + wl_set_drv_status(wl, DISCONNECTING, dev); + scbval.val = reason_code; + memcpy(&scbval.ea, curbssid, ETHER_ADDR_LEN); + scbval.val = htod32(scbval.val); + err = wldev_ioctl(dev, WLC_DISASSOC, &scbval, + sizeof(scb_val_t), true); + if (unlikely(err)) { + wl_clr_drv_status(wl, DISCONNECTING, dev); + WL_ERR(("error (%d)\n", err)); + return err; + } + } + + return err; +} + +static s32 +wl_cfg80211_set_tx_power(struct wiphy *wiphy, + enum nl80211_tx_power_setting type, s32 dbm) +{ + + struct wl_priv *wl = wiphy_priv(wiphy); + struct net_device *ndev = wl_to_prmry_ndev(wl); + u16 txpwrmw; + s32 err = 0; + s32 disable = 0; + + CHECK_SYS_UP(wl); + switch (type) { + case NL80211_TX_POWER_AUTOMATIC: + break; + case NL80211_TX_POWER_LIMITED: + if (dbm < 0) { + WL_ERR(("TX_POWER_LIMITTED - dbm is negative\n")); + return -EINVAL; + } + break; + case NL80211_TX_POWER_FIXED: + if (dbm < 0) { + WL_ERR(("TX_POWER_FIXED - dbm is negative..\n")); + return -EINVAL; + } + break; + } + /* Make sure radio is off or on as far as software is concerned */ + disable = WL_RADIO_SW_DISABLE << 16; + disable = htod32(disable); + err = wldev_ioctl(ndev, WLC_SET_RADIO, &disable, sizeof(disable), true); + if (unlikely(err)) { + WL_ERR(("WLC_SET_RADIO error (%d)\n", err)); + return err; + } + + if (dbm > 0xffff) + txpwrmw = 0xffff; + else + txpwrmw = (u16) dbm; + err = wldev_iovar_setint(ndev, "qtxpower", + (s32) (bcm_mw_to_qdbm(txpwrmw))); + if (unlikely(err)) { + WL_ERR(("qtxpower error (%d)\n", err)); + return err; + } + wl->conf->tx_power = dbm; + + return err; +} + +static s32 wl_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + struct net_device *ndev = wl_to_prmry_ndev(wl); + s32 txpwrdbm; + u8 result; + s32 err = 0; + + CHECK_SYS_UP(wl); + err = wldev_iovar_getint(ndev, "qtxpower", &txpwrdbm); + if (unlikely(err)) { + WL_ERR(("error (%d)\n", err)); + return err; + } + result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE); + *dbm = (s32) bcm_qdbm_to_mw(result); + + return err; +} + +static s32 +wl_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, bool unicast, bool multicast) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + u32 index; + s32 wsec; + s32 err = 0; + s32 bssidx = wl_cfgp2p_find_idx(wl, dev); + + WL_DBG(("key index (%d)\n", key_idx)); + CHECK_SYS_UP(wl); + err = wldev_iovar_getint_bsscfg(dev, "wsec", &wsec, bssidx); + if (unlikely(err)) { + WL_ERR(("WLC_GET_WSEC error (%d)\n", err)); + return err; + } + if (wsec & WEP_ENABLED) { + /* Just select a new current key */ + index = (u32) key_idx; + index = htod32(index); + err = wldev_ioctl(dev, WLC_SET_KEY_PRIMARY, &index, + sizeof(index), true); + if (unlikely(err)) { + WL_ERR(("error (%d)\n", err)); + } + } + return err; +} + +static s32 +wl_add_keyext(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, const u8 *mac_addr, struct key_params *params) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + struct wl_wsec_key key; + s32 err = 0; + s32 bssidx = wl_cfgp2p_find_idx(wl, dev); + s32 mode = wl_get_mode_by_netdev(wl, dev); + memset(&key, 0, sizeof(key)); + key.index = (u32) key_idx; + + if (!ETHER_ISMULTI(mac_addr)) + memcpy((char *)&key.ea, (void *)mac_addr, ETHER_ADDR_LEN); + key.len = (u32) params->key_len; + + /* check for key index change */ + if (key.len == 0) { + /* key delete */ + swap_key_from_BE(&key); + err = wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), + wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); + if (unlikely(err)) { + WL_ERR(("key delete error (%d)\n", err)); + return err; + } + } else { + if (key.len > sizeof(key.data)) { + WL_ERR(("Invalid key length (%d)\n", key.len)); + return -EINVAL; + } + WL_DBG(("Setting the key index %d\n", key.index)); + memcpy(key.data, params->key, key.len); + + if ((mode == WL_MODE_BSS) && + (params->cipher == WLAN_CIPHER_SUITE_TKIP)) { + u8 keybuf[8]; + memcpy(keybuf, &key.data[24], sizeof(keybuf)); + memcpy(&key.data[24], &key.data[16], sizeof(keybuf)); + memcpy(&key.data[16], keybuf, sizeof(keybuf)); + } + + /* if IW_ENCODE_EXT_RX_SEQ_VALID set */ + if (params->seq && params->seq_len == 6) { + /* rx iv */ + u8 *ivptr; + ivptr = (u8 *) params->seq; + key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) | + (ivptr[3] << 8) | ivptr[2]; + key.rxiv.lo = (ivptr[1] << 8) | ivptr[0]; + key.iv_initialized = true; + } + + switch (params->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + key.algo = CRYPTO_ALGO_WEP1; + WL_DBG(("WLAN_CIPHER_SUITE_WEP40\n")); + break; + case WLAN_CIPHER_SUITE_WEP104: + key.algo = CRYPTO_ALGO_WEP128; + WL_DBG(("WLAN_CIPHER_SUITE_WEP104\n")); + break; + case WLAN_CIPHER_SUITE_TKIP: + key.algo = CRYPTO_ALGO_TKIP; + WL_DBG(("WLAN_CIPHER_SUITE_TKIP\n")); + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + key.algo = CRYPTO_ALGO_AES_CCM; + WL_DBG(("WLAN_CIPHER_SUITE_AES_CMAC\n")); + break; + case WLAN_CIPHER_SUITE_CCMP: + key.algo = CRYPTO_ALGO_AES_CCM; + WL_DBG(("WLAN_CIPHER_SUITE_CCMP\n")); + break; + default: + WL_ERR(("Invalid cipher (0x%x)\n", params->cipher)); + return -EINVAL; + } + swap_key_from_BE(&key); + /* need to guarantee EAPOL 4/4 send out before set key */ + dhd_wait_pend8021x(dev); + err = wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), + wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); + if (unlikely(err)) { + WL_ERR(("WLC_SET_KEY error (%d)\n", err)); + return err; + } + } + return err; +} + +static s32 +wl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, bool pairwise, const u8 *mac_addr, + struct key_params *params) +{ + struct wl_wsec_key key; + s32 val = 0; + s32 wsec = 0; + s32 err = 0; + u8 keybuf[8]; + s32 bssidx = 0; + struct wl_priv *wl = wiphy_priv(wiphy); + s32 mode = wl_get_mode_by_netdev(wl, dev); + WL_DBG(("key index (%d)\n", key_idx)); + CHECK_SYS_UP(wl); + + bssidx = wl_cfgp2p_find_idx(wl, dev); + + if (mac_addr && + ((params->cipher != WLAN_CIPHER_SUITE_WEP40) && + (params->cipher != WLAN_CIPHER_SUITE_WEP104))) { + wl_add_keyext(wiphy, dev, key_idx, mac_addr, params); + goto exit; + } + memset(&key, 0, sizeof(key)); + + key.len = (u32) params->key_len; + key.index = (u32) key_idx; + + if (unlikely(key.len > sizeof(key.data))) { + WL_ERR(("Too long key length (%u)\n", key.len)); + return -EINVAL; + } + memcpy(key.data, params->key, key.len); + + key.flags = WL_PRIMARY_KEY; + switch (params->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + key.algo = CRYPTO_ALGO_WEP1; + val = WEP_ENABLED; + WL_DBG(("WLAN_CIPHER_SUITE_WEP40\n")); + break; + case WLAN_CIPHER_SUITE_WEP104: + key.algo = CRYPTO_ALGO_WEP128; + val = WEP_ENABLED; + WL_DBG(("WLAN_CIPHER_SUITE_WEP104\n")); + break; + case WLAN_CIPHER_SUITE_TKIP: + key.algo = CRYPTO_ALGO_TKIP; + val = TKIP_ENABLED; + /* wpa_supplicant switches the third and fourth quarters of the TKIP key */ + if (mode == WL_MODE_BSS) { + bcopy(&key.data[24], keybuf, sizeof(keybuf)); + bcopy(&key.data[16], &key.data[24], sizeof(keybuf)); + bcopy(keybuf, &key.data[16], sizeof(keybuf)); + } + WL_DBG(("WLAN_CIPHER_SUITE_TKIP\n")); + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + key.algo = CRYPTO_ALGO_AES_CCM; + val = AES_ENABLED; + WL_DBG(("WLAN_CIPHER_SUITE_AES_CMAC\n")); + break; + case WLAN_CIPHER_SUITE_CCMP: + key.algo = CRYPTO_ALGO_AES_CCM; + val = AES_ENABLED; + WL_DBG(("WLAN_CIPHER_SUITE_CCMP\n")); + break; + default: + WL_ERR(("Invalid cipher (0x%x)\n", params->cipher)); + return -EINVAL; + } + + /* Set the new key/index */ + swap_key_from_BE(&key); + err = wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), wl->ioctl_buf, + WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); + if (unlikely(err)) { + WL_ERR(("WLC_SET_KEY error (%d)\n", err)); + return err; + } + +exit: + err = wldev_iovar_getint_bsscfg(dev, "wsec", &wsec, bssidx); + if (unlikely(err)) { + WL_ERR(("get wsec error (%d)\n", err)); + return err; + } + + wsec |= val; + err = wldev_iovar_setint_bsscfg(dev, "wsec", wsec, bssidx); + if (unlikely(err)) { + WL_ERR(("set wsec error (%d)\n", err)); + return err; + } + + return err; +} + +static s32 +wl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, bool pairwise, const u8 *mac_addr) +{ + struct wl_wsec_key key; + struct wl_priv *wl = wiphy_priv(wiphy); + s32 err = 0; + s32 bssidx = wl_cfgp2p_find_idx(wl, dev); + + WL_DBG(("Enter\n")); +#ifndef IEEE80211W + if ((key_idx >= DOT11_MAX_DEFAULT_KEYS) && (key_idx < DOT11_MAX_DEFAULT_KEYS+2)) + return -EINVAL; +#endif + CHECK_SYS_UP(wl); + memset(&key, 0, sizeof(key)); + + key.flags = WL_PRIMARY_KEY; + key.algo = CRYPTO_ALGO_OFF; + key.index = (u32) key_idx; + + WL_DBG(("key index (%d)\n", key_idx)); + /* Set the new key/index */ + swap_key_from_BE(&key); + err = wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), wl->ioctl_buf, + WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); + if (unlikely(err)) { + if (err == -EINVAL) { + if (key.index >= DOT11_MAX_DEFAULT_KEYS) { + /* we ignore this key index in this case */ + WL_DBG(("invalid key index (%d)\n", key_idx)); + } + } else { + WL_ERR(("WLC_SET_KEY error (%d)\n", err)); + } + return err; + } + return err; +} + +static s32 +wl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie, + void (*callback) (void *cookie, struct key_params * params)) +{ + struct key_params params; + struct wl_wsec_key key; + struct wl_priv *wl = wiphy_priv(wiphy); + struct wl_security *sec; + s32 wsec; + s32 err = 0; + s32 bssidx = wl_cfgp2p_find_idx(wl, dev); + + WL_DBG(("key index (%d)\n", key_idx)); + CHECK_SYS_UP(wl); + memset(&key, 0, sizeof(key)); + key.index = key_idx; + swap_key_to_BE(&key); + memset(¶ms, 0, sizeof(params)); + params.key_len = (u8) min_t(u8, DOT11_MAX_KEY_SIZE, key.len); + memcpy(params.key, key.data, params.key_len); + + wldev_iovar_getint_bsscfg(dev, "wsec", &wsec, bssidx); + if (unlikely(err)) { + WL_ERR(("WLC_GET_WSEC error (%d)\n", err)); + return err; + } + switch (wsec & ~SES_OW_ENABLED) { + case WEP_ENABLED: + sec = wl_read_prof(wl, dev, WL_PROF_SEC); + if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) { + params.cipher = WLAN_CIPHER_SUITE_WEP40; + WL_DBG(("WLAN_CIPHER_SUITE_WEP40\n")); + } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) { + params.cipher = WLAN_CIPHER_SUITE_WEP104; + WL_DBG(("WLAN_CIPHER_SUITE_WEP104\n")); + } + break; + case TKIP_ENABLED: + params.cipher = WLAN_CIPHER_SUITE_TKIP; + WL_DBG(("WLAN_CIPHER_SUITE_TKIP\n")); + break; + case AES_ENABLED: + params.cipher = WLAN_CIPHER_SUITE_AES_CMAC; + WL_DBG(("WLAN_CIPHER_SUITE_AES_CMAC\n")); + break; + default: + WL_ERR(("Invalid algo (0x%x)\n", wsec)); + return -EINVAL; + } + + callback(cookie, ¶ms); + return err; +} + +static s32 +wl_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, + struct net_device *dev, u8 key_idx) +{ + WL_INFO(("Not supported\n")); + return -EOPNOTSUPP; +} + +static s32 +wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, + u8 *mac, struct station_info *sinfo) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + scb_val_t scb_val; + s32 rssi; + s32 rate; + s32 err = 0; + sta_info_t *sta; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + s8 eabuf[ETHER_ADDR_STR_LEN]; +#endif + dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); + CHECK_SYS_UP(wl); + if (wl_get_mode_by_netdev(wl, dev) == WL_MODE_AP) { + err = wldev_iovar_getbuf(dev, "sta_info", (struct ether_addr *)mac, + ETHER_ADDR_LEN, wl->ioctl_buf, WLC_IOCTL_SMLEN, &wl->ioctl_buf_sync); + if (err < 0) { + WL_ERR(("GET STA INFO failed, %d\n", err)); + return err; + } + sinfo->filled = STATION_INFO_INACTIVE_TIME; + sta = (sta_info_t *)wl->ioctl_buf; + sta->len = dtoh16(sta->len); + sta->cap = dtoh16(sta->cap); + sta->flags = dtoh32(sta->flags); + sta->idle = dtoh32(sta->idle); + sta->in = dtoh32(sta->in); + sinfo->inactive_time = sta->idle * 1000; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + if (sta->flags & WL_STA_ASSOC) { + sinfo->filled |= STATION_INFO_CONNECTED_TIME; + sinfo->connected_time = sta->in; + } + WL_INFO(("STA %s : idle time : %d sec, connected time :%d ms\n", + bcm_ether_ntoa((const struct ether_addr *)mac, eabuf), sinfo->inactive_time, + sta->idle * 1000)); +#endif + } else if (wl_get_mode_by_netdev(wl, dev) == WL_MODE_BSS) { + get_pktcnt_t pktcnt; + u8 *curmacp = wl_read_prof(wl, dev, WL_PROF_BSSID); + if (!wl_get_drv_status(wl, CONNECTED, dev) || + (dhd_is_associated(dhd, NULL, &err) == FALSE)) { + WL_ERR(("NOT assoc\n")); + if (err == -ERESTARTSYS) + return err; + err = -ENODEV; + return err; + } + if (memcmp(mac, curmacp, ETHER_ADDR_LEN)) { + WL_ERR(("Wrong Mac address: "MACDBG" != "MACDBG"\n", + MAC2STRDBG(mac), MAC2STRDBG(curmacp))); + } + + /* Report the current tx rate */ + err = wldev_ioctl(dev, WLC_GET_RATE, &rate, sizeof(rate), false); + if (err) { + WL_ERR(("Could not get rate (%d)\n", err)); + } else { + rate = dtoh32(rate); + sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->txrate.legacy = rate * 5; + WL_DBG(("Rate %d Mbps\n", (rate / 2))); + } + + memset(&scb_val, 0, sizeof(scb_val)); + scb_val.val = 0; + err = wldev_ioctl(dev, WLC_GET_RSSI, &scb_val, + sizeof(scb_val_t), false); + if (err) { + WL_ERR(("Could not get rssi (%d)\n", err)); + goto get_station_err; + } + rssi = dtoh32(scb_val.val) + RSSI_OFFSET; + sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->signal = rssi; + WL_DBG(("RSSI %d dBm\n", rssi)); + err = wldev_ioctl(dev, WLC_GET_PKTCNTS, &pktcnt, + sizeof(pktcnt), false); + if (!err) { + sinfo->filled |= (STATION_INFO_RX_PACKETS | + STATION_INFO_RX_DROP_MISC | + STATION_INFO_TX_PACKETS | + STATION_INFO_TX_FAILED); + sinfo->rx_packets = pktcnt.rx_good_pkt; + sinfo->rx_dropped_misc = pktcnt.rx_bad_pkt; + sinfo->tx_packets = pktcnt.tx_good_pkt; + sinfo->tx_failed = pktcnt.tx_bad_pkt; + } +get_station_err: + if (err && (err != -ERESTARTSYS)) { + /* Disconnect due to zero BSSID or error to get RSSI */ + WL_ERR(("force cfg80211_disconnected\n")); + wl_clr_drv_status(wl, CONNECTED, dev); + cfg80211_disconnected(dev, 0, NULL, 0, GFP_KERNEL); + wl_link_down(wl); + } + } + + return err; +} + +/* Function to update sta power save mode for Kernel wifi stack */ +int wl_cfg80211_update_power_mode(struct net_device *dev) +{ + int pm = -1; + int err; + + err = wldev_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm), false); + if (err || (pm == -1)) { + WL_ERR(("error (%d)\n", err)); + } else { + pm = (pm == PM_OFF) ? false : true; + WL_DBG(("%s: %d\n", __func__, pm)); + if (dev->ieee80211_ptr) + dev->ieee80211_ptr->ps = pm; + } + return err; +} + +static s32 +wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, + bool enabled, s32 timeout) +{ + s32 pm; + s32 err = 0; + struct wl_priv *wl = wiphy_priv(wiphy); + struct net_info *_net_info = wl_get_netinfo_by_netdev(wl, dev); +#if !defined(SUPPORT_PM2_ONLY) + dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); +#endif + + CHECK_SYS_UP(wl); + + if (wl->p2p_net == dev || _net_info == NULL) { + return err; + } + WL_DBG(("%s: Enter power save enabled %d\n", dev->name, enabled)); + +#if !defined(SUPPORT_PM2_ONLY) + /* android has special hooks to change pm when kernel suspended */ + pm = enabled ? ((dhd->in_suspend) ? PM_MAX : PM_FAST) : PM_OFF; +#else + pm = enabled ? PM_FAST : PM_OFF; +#endif /* SUPPORT_PM2_ONLY */ + + if (_net_info->pm_block || wl->vsdb_mode) { + /* Do not enable the power save if it is p2p interface or vsdb mode is set */ + WL_DBG(("%s:Do not enable the power save for pm_block %d or vsdb_mode %d\n", + dev->name, _net_info->pm_block, wl->vsdb_mode)); + pm = PM_OFF; + } + pm = htod32(pm); + WL_DBG(("%s:power save %s\n", dev->name, (pm ? "enabled" : "disabled"))); + err = wldev_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm), true); + if (unlikely(err)) { + if (err == -ENODEV) + WL_DBG(("net_device is not ready yet\n")); + else + WL_ERR(("error (%d)\n", err)); + return err; + } + return err; +} + +static __used u32 wl_find_msb(u16 bit16) +{ + u32 ret = 0; + + if (bit16 & 0xff00) { + ret += 8; + bit16 >>= 8; + } + + if (bit16 & 0xf0) { + ret += 4; + bit16 >>= 4; + } + + if (bit16 & 0xc) { + ret += 2; + bit16 >>= 2; + } + + if (bit16 & 2) + ret += bit16 & 2; + else if (bit16) + ret += bit16; + + return ret; +} + +static s32 wl_cfg80211_resume(struct wiphy *wiphy) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + struct net_device *ndev = wl_to_prmry_ndev(wl); + s32 err = 0; + + if (unlikely(!wl_get_drv_status(wl, READY, ndev))) { + WL_INFO(("device is not ready\n")); + return 0; + } + + wl_invoke_iscan(wl); + + return err; +} + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39) +static s32 wl_cfg80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow) +#else +static s32 wl_cfg80211_suspend(struct wiphy *wiphy) +#endif +{ +#ifdef DHD_CLEAR_ON_SUSPEND + struct wl_priv *wl = wiphy_priv(wiphy); + struct net_info *iter, *next; + struct net_device *ndev = wl_to_prmry_ndev(wl); + unsigned long flags; + if (unlikely(!wl_get_drv_status(wl, READY, ndev))) { + WL_INFO(("device is not ready : status (%d)\n", + (int)wl->status)); + return 0; + } + for_each_ndev(wl, iter, next) + wl_set_drv_status(wl, SCAN_ABORTING, iter->ndev); + wl_term_iscan(wl); + spin_lock_irqsave(&wl->cfgdrv_lock, flags); + if (wl->scan_request) { + cfg80211_scan_done(wl->scan_request, true); + wl->scan_request = NULL; + } + for_each_ndev(wl, iter, next) { + wl_clr_drv_status(wl, SCANNING, iter->ndev); + wl_clr_drv_status(wl, SCAN_ABORTING, iter->ndev); + } + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); + for_each_ndev(wl, iter, next) { + if (wl_get_drv_status(wl, CONNECTING, iter->ndev)) { + wl_bss_connect_done(wl, iter->ndev, NULL, NULL, false); + } + } +#endif /* DHD_CLEAR_ON_SUSPEND */ + return 0; +} + +static s32 +wl_update_pmklist(struct net_device *dev, struct wl_pmk_list *pmk_list, + s32 err) +{ + int i, j; + struct wl_priv *wl = wlcfg_drv_priv; + struct net_device *primary_dev = wl_to_prmry_ndev(wl); + + if (!pmk_list) { + printk("pmk_list is NULL\n"); + return -EINVAL; + } + /* pmk list is supported only for STA interface i.e. primary interface + * Refer code wlc_bsscfg.c->wlc_bsscfg_sta_init + */ + if (primary_dev != dev) { + WL_INFO(("Not supporting Flushing pmklist on virtual" + " interfaces than primary interface\n")); + return err; + } + + WL_DBG(("No of elements %d\n", pmk_list->pmkids.npmkid)); + for (i = 0; i < pmk_list->pmkids.npmkid; i++) { + WL_DBG(("PMKID[%d]: %pM =\n", i, + &pmk_list->pmkids.pmkid[i].BSSID)); + for (j = 0; j < WPA2_PMKID_LEN; j++) { + WL_DBG(("%02x\n", pmk_list->pmkids.pmkid[i].PMKID[j])); + } + } + if (likely(!err)) { + err = wldev_iovar_setbuf(dev, "pmkid_info", (char *)pmk_list, + sizeof(*pmk_list), wl->ioctl_buf, WLC_IOCTL_MAXLEN, NULL); + } + + return err; +} + +static s32 +wl_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_pmksa *pmksa) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + s32 err = 0; + int i; + + CHECK_SYS_UP(wl); + for (i = 0; i < wl->pmk_list->pmkids.npmkid; i++) + if (!memcmp(pmksa->bssid, &wl->pmk_list->pmkids.pmkid[i].BSSID, + ETHER_ADDR_LEN)) + break; + if (i < WL_NUM_PMKIDS_MAX) { + memcpy(&wl->pmk_list->pmkids.pmkid[i].BSSID, pmksa->bssid, + ETHER_ADDR_LEN); + memcpy(&wl->pmk_list->pmkids.pmkid[i].PMKID, pmksa->pmkid, + WPA2_PMKID_LEN); + if (i == wl->pmk_list->pmkids.npmkid) + wl->pmk_list->pmkids.npmkid++; + } else { + err = -EINVAL; + } + WL_DBG(("set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n", + &wl->pmk_list->pmkids.pmkid[wl->pmk_list->pmkids.npmkid - 1].BSSID)); + for (i = 0; i < WPA2_PMKID_LEN; i++) { + WL_DBG(("%02x\n", + wl->pmk_list->pmkids.pmkid[wl->pmk_list->pmkids.npmkid - 1]. + PMKID[i])); + } + + err = wl_update_pmklist(dev, wl->pmk_list, err); + + return err; +} + +static s32 +wl_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_pmksa *pmksa) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + struct _pmkid_list pmkid; + s32 err = 0; + int i; + + CHECK_SYS_UP(wl); + memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETHER_ADDR_LEN); + memcpy(pmkid.pmkid[0].PMKID, pmksa->pmkid, WPA2_PMKID_LEN); + + WL_DBG(("del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n", + &pmkid.pmkid[0].BSSID)); + for (i = 0; i < WPA2_PMKID_LEN; i++) { + WL_DBG(("%02x\n", pmkid.pmkid[0].PMKID[i])); + } + + for (i = 0; i < wl->pmk_list->pmkids.npmkid; i++) + if (!memcmp + (pmksa->bssid, &wl->pmk_list->pmkids.pmkid[i].BSSID, + ETHER_ADDR_LEN)) + break; + + if ((wl->pmk_list->pmkids.npmkid > 0) && + (i < wl->pmk_list->pmkids.npmkid)) { + memset(&wl->pmk_list->pmkids.pmkid[i], 0, sizeof(pmkid_t)); + for (; i < (wl->pmk_list->pmkids.npmkid - 1); i++) { + memcpy(&wl->pmk_list->pmkids.pmkid[i].BSSID, + &wl->pmk_list->pmkids.pmkid[i + 1].BSSID, + ETHER_ADDR_LEN); + memcpy(&wl->pmk_list->pmkids.pmkid[i].PMKID, + &wl->pmk_list->pmkids.pmkid[i + 1].PMKID, + WPA2_PMKID_LEN); + } + wl->pmk_list->pmkids.npmkid--; + } else { + err = -EINVAL; + } + + err = wl_update_pmklist(dev, wl->pmk_list, err); + + return err; + +} + +static s32 +wl_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *dev) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + s32 err = 0; + CHECK_SYS_UP(wl); + memset(wl->pmk_list, 0, sizeof(*wl->pmk_list)); + err = wl_update_pmklist(dev, wl->pmk_list, err); + return err; + +} + +static wl_scan_params_t * +wl_cfg80211_scan_alloc_params(int channel, int nprobes, int *out_params_size) +{ + wl_scan_params_t *params; + int params_size; + int num_chans; + + *out_params_size = 0; + + /* Our scan params only need space for 1 channel and 0 ssids */ + params_size = WL_SCAN_PARAMS_FIXED_SIZE + 1 * sizeof(uint16); + params = (wl_scan_params_t*) kzalloc(params_size, GFP_KERNEL); + if (params == NULL) { + WL_ERR(("%s: mem alloc failed (%d bytes)\n", __func__, params_size)); + return params; + } + memset(params, 0, params_size); + params->nprobes = nprobes; + + num_chans = (channel == 0) ? 0 : 1; + + memcpy(¶ms->bssid, ðer_bcast, ETHER_ADDR_LEN); + params->bss_type = DOT11_BSSTYPE_ANY; + params->scan_type = DOT11_SCANTYPE_ACTIVE; + params->nprobes = htod32(1); + params->active_time = htod32(-1); + params->passive_time = htod32(-1); + params->home_time = htod32(10); + if (channel == -1) + params->channel_list[0] = htodchanspec(channel); + else + params->channel_list[0] = wl_ch_host_to_driver(channel); + + /* Our scan params have 1 channel and 0 ssids */ + params->channel_num = htod32((0 << WL_SCAN_PARAMS_NSSID_SHIFT) | + (num_chans & WL_SCAN_PARAMS_COUNT_MASK)); + + *out_params_size = params_size; /* rtn size to the caller */ + return params; +} + +static s32 +wl_cfg80211_remain_on_channel(struct wiphy *wiphy, struct net_device *dev, + struct ieee80211_channel * channel, + enum nl80211_channel_type channel_type, + unsigned int duration, u64 *cookie) +{ + s32 target_channel; + u32 id; + struct ether_addr primary_mac; + struct net_device *ndev = NULL; + + s32 err = BCME_OK; + struct wl_priv *wl = wiphy_priv(wiphy); + + WL_DBG(("Enter, ifindex: %d, channel: %d, duration ms (%d) SCANNING ?? %s \n", + dev->ifindex, ieee80211_frequency_to_channel(channel->center_freq), + duration, (wl_get_drv_status(wl, SCANNING, ndev)) ? "YES":"NO")); + + if (wl->p2p_net == dev) { + ndev = wl_to_prmry_ndev(wl); + } else { + ndev = dev; + } + + if (!wl->p2p) { + WL_ERR(("wl->p2p is not initialized\n")); + err = BCME_ERROR; + goto exit; + } + +#ifndef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST + if (wl_get_drv_status(wl, SCANNING, ndev)) { + wl_notify_escan_complete(wl, ndev, true, true); + } +#endif /* not WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ + + target_channel = ieee80211_frequency_to_channel(channel->center_freq); + memcpy(&wl->remain_on_chan, channel, sizeof(struct ieee80211_channel)); + wl->remain_on_chan_type = channel_type; + id = ++wl->last_roc_id; + if (id == 0) + id = ++wl->last_roc_id; + *cookie = id; + +#ifdef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST + if (wl_get_drv_status(wl, SCANNING, ndev)) { + struct timer_list *_timer; + WL_DBG(("scan is running. go to fake listen state\n")); + + wl_set_drv_status(wl, FAKE_REMAINING_ON_CHANNEL, ndev); + + if (timer_pending(&wl->p2p->listen_timer)) { + WL_DBG(("cancel current listen timer \n")); + del_timer_sync(&wl->p2p->listen_timer); + } + + _timer = &wl->p2p->listen_timer; + wl_clr_p2p_status(wl, LISTEN_EXPIRED); + + INIT_TIMER(_timer, wl_cfgp2p_listen_expired, duration, 0); + + err = BCME_OK; + goto exit; + } +#endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ + +#ifdef WL_CFG80211_SYNC_GON + if (wl_get_drv_status_all(wl, WAITING_NEXT_ACT_FRM_LISTEN)) { + /* do not enter listen mode again if we are in listen mode already for next af. + * remain on channel completion will be returned by waiting next af completion. + */ +#ifdef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST + wl_set_drv_status(wl, FAKE_REMAINING_ON_CHANNEL, ndev); +#else + wl_set_drv_status(wl, REMAINING_ON_CHANNEL, ndev); +#endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ + goto exit; + } +#endif /* WL_CFG80211_SYNC_GON */ + if (wl->p2p && !wl->p2p->on) { + /* In case of p2p_listen command, supplicant send remain_on_channel + * without turning on P2P + */ + get_primary_mac(wl, &primary_mac); + wl_cfgp2p_generate_bss_mac(&primary_mac, &wl->p2p->dev_addr, &wl->p2p->int_addr); + p2p_on(wl) = true; + } + + if (p2p_is_on(wl)) { + err = wl_cfgp2p_enable_discovery(wl, ndev, NULL, 0); + if (unlikely(err)) { + goto exit; + } +#ifndef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST + wl_set_drv_status(wl, REMAINING_ON_CHANNEL, ndev); +#endif /* not WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ + err = wl_cfgp2p_discover_listen(wl, target_channel, duration); + +#ifdef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST + if (err == BCME_OK) { + wl_set_drv_status(wl, REMAINING_ON_CHANNEL, ndev); + } else { + /* if failed, firmware may be internal scanning state. + * so other scan request shall not abort it + */ + wl_set_drv_status(wl, FAKE_REMAINING_ON_CHANNEL, ndev); + } +#endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ + /* WAR: set err = ok to prevent cookie mismatch in wpa_supplicant + * and expire timer will send a completion to the upper layer + */ + err = BCME_OK; + } + +exit: + if (err == BCME_OK) { + WL_INFO(("Success\n")); + cfg80211_ready_on_channel(dev, *cookie, channel, + channel_type, duration, GFP_KERNEL); + } else { + WL_ERR(("Fail to Set (err=%d cookie:%llu)\n", err, *cookie)); + } + return err; +} + +static s32 +wl_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, struct net_device *dev, + u64 cookie) +{ + s32 err = 0; + WL_DBG((" enter ) netdev_ifidx: %d \n", dev->ifindex)); + return err; +} + +static void +wl_cfg80211_afx_handler(struct work_struct *work) +{ + struct afx_hdl *afx_instance; + struct wl_priv *wl = wlcfg_drv_priv; + s32 ret = BCME_OK; + + afx_instance = container_of(work, struct afx_hdl, work); + if (afx_instance != NULL && wl->afx_hdl->is_active) { + if (wl->afx_hdl->is_listen && wl->afx_hdl->my_listen_chan) { + ret = wl_cfgp2p_discover_listen(wl, wl->afx_hdl->my_listen_chan, + (100 * (1 + (random32() % 3)))); /* 100ms ~ 300ms */ + } else { + ret = wl_cfgp2p_act_frm_search(wl, wl->afx_hdl->dev, + wl->afx_hdl->bssidx, wl->afx_hdl->peer_listen_chan, + &wl->afx_hdl->tx_dst_addr); + } + if (unlikely(ret != BCME_OK)) { + WL_ERR(("ERROR occurred! returned value is (%d)\n", ret)); + if (wl_get_drv_status_all(wl, FINDING_COMMON_CHANNEL)) + complete(&wl->act_frm_scan); + } + } +} + +static s32 +wl_cfg80211_af_searching_channel(struct wl_priv *wl, struct net_device *dev) +{ + u32 max_retry = WL_CHANNEL_SYNC_RETRY; + + if (dev == NULL) + return -1; + + WL_DBG((" enter ) \n")); + + wl_set_drv_status(wl, FINDING_COMMON_CHANNEL, dev); + wl->afx_hdl->is_active = TRUE; + + /* Loop to wait until we find a peer's channel or the + * pending action frame tx is cancelled. + */ + while ((wl->afx_hdl->retry < max_retry) && + (wl->afx_hdl->peer_chan == WL_INVALID)) { + wl->afx_hdl->is_listen = FALSE; + wl_set_drv_status(wl, SCANNING, dev); + WL_DBG(("Scheduling the action frame for sending.. retry %d\n", + wl->afx_hdl->retry)); + /* search peer on peer's listen channel */ + schedule_work(&wl->afx_hdl->work); + wait_for_completion_timeout(&wl->act_frm_scan, + msecs_to_jiffies(MAX_WAIT_TIME)); + + if ((wl->afx_hdl->peer_chan != WL_INVALID) || + !(wl_get_drv_status(wl, FINDING_COMMON_CHANNEL, dev))) + break; + + if (wl->afx_hdl->my_listen_chan) { + WL_DBG(("Scheduling Listen peer in my listen channel = %d\n", + wl->afx_hdl->my_listen_chan)); + /* listen on my listen channel */ + wl->afx_hdl->is_listen = TRUE; + schedule_work(&wl->afx_hdl->work); + wait_for_completion_timeout(&wl->act_frm_scan, + msecs_to_jiffies(MAX_WAIT_TIME)); + } + if ((wl->afx_hdl->peer_chan != WL_INVALID) || + !(wl_get_drv_status(wl, FINDING_COMMON_CHANNEL, dev))) + break; + + wl->afx_hdl->retry++; + + WL_AF_TX_KEEP_PRI_CONNECTION_VSDB(wl); + } + + wl->afx_hdl->is_active = FALSE; + + wl_clr_drv_status(wl, SCANNING, dev); + wl_clr_drv_status(wl, FINDING_COMMON_CHANNEL, dev); + + return (wl->afx_hdl->peer_chan); +} + +struct p2p_config_af_params { + s32 max_tx_retry; /* max tx retry count if tx no ack */ + /* To make sure to send successfully action frame, we have to turn off mpc + * 0: off, 1: on, (-1): do nothing + */ + s32 mpc_onoff; +#ifdef WL_CFG80211_SYNC_GON + bool extra_listen; +#endif + bool search_channel; /* 1: search peer's channel to send af */ +}; + +static s32 +wl_cfg80211_config_p2p_pub_af_tx(struct wiphy *wiphy, + wl_action_frame_t *action_frame, wl_af_params_t *af_params, + struct p2p_config_af_params *config_af_params) +{ + s32 err = BCME_OK; + struct wl_priv *wl = wiphy_priv(wiphy); + wifi_p2p_pub_act_frame_t *act_frm = + (wifi_p2p_pub_act_frame_t *) (action_frame->data); + + /* initialize default value */ +#ifdef WL_CFG80211_SYNC_GON + config_af_params->extra_listen = true; +#endif + config_af_params->search_channel = false; + config_af_params->max_tx_retry = WL_AF_TX_MAX_RETRY; + config_af_params->mpc_onoff = -1; + + switch (act_frm->subtype) { + case P2P_PAF_GON_REQ: { + WL_DBG(("P2P: GO_NEG_PHASE status set \n")); + wl_set_p2p_status(wl, GO_NEG_PHASE); + + config_af_params->mpc_onoff = 0; + config_af_params->search_channel = true; + wl->next_af_subtype = act_frm->subtype + 1; + + /* increase dwell time to wait for RESP frame */ + af_params->dwell_time = WL_MED_DWELL_TIME; + + break; + } + case P2P_PAF_GON_RSP: { + wl->next_af_subtype = act_frm->subtype + 1; + /* increase dwell time to wait for CONF frame */ + af_params->dwell_time = WL_MED_DWELL_TIME; + break; + } + case P2P_PAF_GON_CONF: { + /* If we reached till GO Neg confirmation reset the filter */ + WL_DBG(("P2P: GO_NEG_PHASE status cleared \n")); + wl_clr_p2p_status(wl, GO_NEG_PHASE); + + /* turn on mpc again if go nego is done */ + config_af_params->mpc_onoff = 1; + + /* minimize dwell time */ + af_params->dwell_time = WL_MIN_DWELL_TIME; + +#ifdef WL_CFG80211_SYNC_GON + config_af_params->extra_listen = false; +#endif /* WL_CFG80211_SYNC_GON */ + break; + } + case P2P_PAF_INVITE_REQ: { + config_af_params->search_channel = true; + wl->next_af_subtype = act_frm->subtype + 1; + + /* increase dwell time */ + af_params->dwell_time = WL_MED_DWELL_TIME; + break; + } + case P2P_PAF_INVITE_RSP: + /* minimize dwell time */ + af_params->dwell_time = WL_MIN_DWELL_TIME; +#ifdef WL_CFG80211_SYNC_GON + config_af_params->extra_listen = false; +#endif /* WL_CFG80211_SYNC_GON */ + break; + case P2P_PAF_DEVDIS_REQ: { + config_af_params->search_channel = true; + + wl->next_af_subtype = act_frm->subtype + 1; + /* maximize dwell time to wait for RESP frame */ + af_params->dwell_time = WL_LONG_DWELL_TIME; + break; + } + case P2P_PAF_DEVDIS_RSP: + /* minimize dwell time */ + af_params->dwell_time = WL_MIN_DWELL_TIME; +#ifdef WL_CFG80211_SYNC_GON + config_af_params->extra_listen = false; +#endif /* WL_CFG80211_SYNC_GON */ + break; + case P2P_PAF_PROVDIS_REQ: { + if (IS_PROV_DISC_WITHOUT_GROUP_ID(&act_frm->elts[0], + action_frame->len)) { + config_af_params->search_channel = true; + } + + config_af_params->mpc_onoff = 0; + wl->next_af_subtype = act_frm->subtype + 1; + /* increase dwell time to wait for RESP frame */ + af_params->dwell_time = WL_MED_DWELL_TIME; + break; + } + case P2P_PAF_PROVDIS_RSP: { + wl->next_af_subtype = P2P_PAF_GON_REQ; + /* increase dwell time to MED level */ + af_params->dwell_time = WL_MED_DWELL_TIME; +#ifdef WL_CFG80211_SYNC_GON + config_af_params->extra_listen = false; +#endif /* WL_CFG80211_SYNC_GON */ + break; + } + default: + WL_DBG(("Unknown p2p pub act frame subtype: %d\n", + act_frm->subtype)); + err = BCME_BADARG; + } + return err; +} + + +static bool +wl_cfg80211_send_action_frame(struct wiphy *wiphy, struct net_device *dev, + struct net_device *ndev, wl_af_params_t *af_params, + wl_action_frame_t *action_frame, u16 action_frame_len, s32 bssidx) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + bool ack = false; + u8 category, action; + s32 tx_retry; + struct p2p_config_af_params config_af_params; +#ifdef VSDB + ulong off_chan_started_jiffies = 0; +#endif + dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); + + wl_cfgp2p_print_actframe(true, action_frame->data, action_frame->len); + + category = action_frame->data[DOT11_ACTION_CAT_OFF]; + action = action_frame->data[DOT11_ACTION_ACT_OFF]; + + /* initialize variables */ + tx_retry = 0; + wl->next_af_subtype = P2P_PAF_SUBTYPE_INVALID; + config_af_params.max_tx_retry = WL_AF_TX_MAX_RETRY; + config_af_params.mpc_onoff = -1; + config_af_params.search_channel = false; +#ifdef WL_CFG80211_SYNC_GON + config_af_params.extra_listen = false; +#endif + + /* config parameters */ + /* Public Action Frame Process - DOT11_ACTION_CAT_PUBLIC */ + if (category == DOT11_ACTION_CAT_PUBLIC) { + if ((action == P2P_PUB_AF_ACTION) && + (action_frame_len >= sizeof(wifi_p2p_pub_act_frame_t))) { + /* p2p public action frame process */ + if (BCME_OK != wl_cfg80211_config_p2p_pub_af_tx(wiphy, + action_frame, af_params, &config_af_params)) { + WL_DBG(("Unknown subtype.\n")); + } + + } else if (action_frame_len >= sizeof(wifi_p2psd_gas_pub_act_frame_t)) { + /* service discovery process */ + if (action == P2PSD_ACTION_ID_GAS_IREQ || + action == P2PSD_ACTION_ID_GAS_CREQ) { + /* configure service discovery query frame */ + + config_af_params.search_channel = true; + + /* save next af suptype to cancel remained dwell time */ + wl->next_af_subtype = action + 1; + + af_params->dwell_time = WL_MED_DWELL_TIME; + } else if (action == P2PSD_ACTION_ID_GAS_IRESP || + action == P2PSD_ACTION_ID_GAS_CRESP) { + /* configure service discovery response frame */ + af_params->dwell_time = WL_MIN_DWELL_TIME; + } else { + WL_DBG(("Unknown action type: %d\n", action)); + } + } else { + WL_DBG(("Unknown Frame: category 0x%x, action 0x%x, length %d\n", + category, action, action_frame_len)); + } + } else if (category == P2P_AF_CATEGORY) { + /* do not configure anything. it will be sent with a default configuration */ + } else { + WL_DBG(("Unknown Frame: category 0x%x, action 0x%x\n", + category, action)); + if (dhd->op_mode & DHD_FLAG_HOSTAP_MODE) { + wl_clr_drv_status(wl, SENDING_ACT_FRM, dev); + return false; + } + } + + /* To make sure to send successfully action frame, we have to turn off mpc */ + if (config_af_params.mpc_onoff == 0) { + wldev_iovar_setint(dev, "mpc", 0); + } + + /* validate channel and p2p ies */ + if (config_af_params.search_channel && IS_P2P_SOCIAL(af_params->channel) && + wl_to_p2p_bss_saved_ie(wl, P2PAPI_BSSCFG_DEVICE).p2p_probe_req_ie_len) { + config_af_params.search_channel = true; + } else { + config_af_params.search_channel = false; + } + +#ifdef VSDB + /* if connecting on primary iface, sleep for a while before sending af tx for VSDB */ + if (wl_get_drv_status(wl, CONNECTING, wl_to_prmry_ndev(wl))) { + msleep(50); + } +#endif + + /* if scan is ongoing, abort current scan. */ + if (wl_get_drv_status_all(wl, SCANNING)) { + wl_notify_escan_complete(wl, ndev, true, true); + } + + /* set status and destination address before sending af */ + if (wl->next_af_subtype != P2P_PAF_SUBTYPE_INVALID) { + /* set this status to cancel the remained dwell time in rx process */ + wl_set_drv_status(wl, WAITING_NEXT_ACT_FRM, dev); + } + wl_set_drv_status(wl, SENDING_ACT_FRM, dev); + memcpy(wl->afx_hdl->tx_dst_addr.octet, + af_params->action_frame.da.octet, + sizeof(wl->afx_hdl->tx_dst_addr.octet)); + + /* save af_params for rx process */ + wl->afx_hdl->pending_tx_act_frm = af_params; + + /* search peer's channel */ + if (config_af_params.search_channel) { + /* initialize afx_hdl */ + wl->afx_hdl->bssidx = wl_cfgp2p_find_idx(wl, dev); + wl->afx_hdl->dev = dev; + wl->afx_hdl->retry = 0; + wl->afx_hdl->peer_chan = WL_INVALID; + + if (wl_cfg80211_af_searching_channel(wl, dev) == WL_INVALID) { + WL_ERR(("couldn't find peer's channel.\n")); + goto exit; + } + + wl_clr_drv_status(wl, SCANNING, wl->afx_hdl->dev); + /* + * Abort scan even for VSDB scenarios. Scan gets aborted in firmware + * but after the check of piggyback algorithm. + * To take care of current piggback algo, lets abort the scan here itself. + */ + wl_notify_escan_complete(wl, dev, true, true); + /* Suspend P2P discovery's search-listen to prevent it from + * starting a scan or changing the channel. + */ + wl_cfgp2p_discover_enable_search(wl, false); + + /* update channel */ + af_params->channel = wl->afx_hdl->peer_chan; + } + +#ifdef VSDB + off_chan_started_jiffies = jiffies; +#endif /* VSDB */ + + /* Now send a tx action frame */ + ack = wl_cfgp2p_tx_action_frame(wl, dev, af_params, bssidx) ? false : true; + + /* if failed, retry it. tx_retry_max value is configure by .... */ + while ((ack == false) && (tx_retry++ < config_af_params.max_tx_retry)) { +#ifdef VSDB + if (af_params->channel) { + if (jiffies_to_msecs(jiffies - off_chan_started_jiffies) > + OFF_CHAN_TIME_THRESHOLD_MS) { + WL_AF_TX_KEEP_PRI_CONNECTION_VSDB(wl); + off_chan_started_jiffies = jiffies; + } + } +#endif /* VSDB */ + ack = wl_cfgp2p_tx_action_frame(wl, dev, af_params, bssidx) ? + false : true; + } + if (ack == false) { + WL_ERR(("Failed to send Action Frame(retry %d)\n", tx_retry)); + } +exit: + /* Clear SENDING_ACT_FRM after all sending af is done */ + wl_clr_drv_status(wl, SENDING_ACT_FRM, dev); + +#ifdef WL_CFG80211_SYNC_GON + /* WAR: sometimes dongle does not keep the dwell time of 'actframe'. + * if we coundn't get the next action response frame and dongle does not keep + * the dwell time, go to listen state again to get next action response frame. + */ + if (ack && config_af_params.extra_listen && + wl_get_drv_status_all(wl, WAITING_NEXT_ACT_FRM) && + wl->af_sent_channel == wl->afx_hdl->my_listen_chan) { + s32 extar_listen_time; + + extar_listen_time = af_params->dwell_time - + jiffies_to_msecs(jiffies - wl->af_tx_sent_jiffies); + + if (extar_listen_time > 50) { + wl_set_drv_status(wl, WAITING_NEXT_ACT_FRM_LISTEN, dev); + WL_DBG(("Wait more time! actual af time:%d," + "calculated extar listen:%d\n", + af_params->dwell_time, extar_listen_time)); + if (wl_cfgp2p_discover_listen(wl, wl->af_sent_channel, + extar_listen_time + 100) == BCME_OK) { + wait_for_completion_timeout(&wl->wait_next_af, + msecs_to_jiffies(extar_listen_time + 100 + 300)); + } + wl_clr_drv_status(wl, WAITING_NEXT_ACT_FRM_LISTEN, dev); + } + } +#endif /* WL_CFG80211_SYNC_GON */ + wl_clr_drv_status(wl, WAITING_NEXT_ACT_FRM, dev); + + if (wl->afx_hdl->pending_tx_act_frm) + wl->afx_hdl->pending_tx_act_frm = NULL; + + WL_INFO(("-- sending Action Frame is %s, listen chan: %d\n", + (ack) ? "Succeeded!!":"Failed!!", wl->afx_hdl->my_listen_chan)); + + + /* if all done, turn mpc on again */ + if (config_af_params.mpc_onoff == 1) { + wldev_iovar_setint(dev, "mpc", 1); + } + + return ack; +} + +#define MAX_NUM_OF_ASSOCIATED_DEV 64 +static s32 +wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *ndev, + struct ieee80211_channel *channel, bool offchan, + enum nl80211_channel_type channel_type, + bool channel_type_valid, unsigned int wait, + const u8* buf, size_t len, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + bool no_cck, +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) + bool dont_wait_for_ack, +#endif + u64 *cookie) +{ + wl_action_frame_t *action_frame; + wl_af_params_t *af_params; + scb_val_t scb_val; + const struct ieee80211_mgmt *mgmt; + struct wl_priv *wl = wiphy_priv(wiphy); + struct net_device *dev = NULL; + s32 err = BCME_OK; + s32 bssidx = 0; + u32 id; + bool ack = false; + s8 eabuf[ETHER_ADDR_STR_LEN]; + + WL_DBG(("Enter \n")); + + if (ndev == wl->p2p_net) { + dev = wl_to_prmry_ndev(wl); + } else { + /* If TX req is for any valid ifidx. Use as is */ + dev = ndev; + } + + /* find bssidx based on ndev */ + bssidx = wl_cfgp2p_find_idx(wl, dev); + if (bssidx == -1) { + + WL_ERR(("Can not find the bssidx for dev( %p )\n", dev)); + return -ENODEV; + } + if (p2p_is_on(wl)) { + /* Suspend P2P discovery search-listen to prevent it from changing the + * channel. + */ + if ((err = wl_cfgp2p_discover_enable_search(wl, false)) < 0) { + WL_ERR(("Can not disable discovery mode\n")); + return -EFAULT; + } + } + *cookie = 0; + id = wl->send_action_id++; + if (id == 0) + id = wl->send_action_id++; + *cookie = id; + mgmt = (const struct ieee80211_mgmt *)buf; + if (ieee80211_is_mgmt(mgmt->frame_control)) { + if (ieee80211_is_probe_resp(mgmt->frame_control)) { + s32 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN; + s32 ie_len = len - ie_offset; + if (dev == wl_to_prmry_ndev(wl)) + bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE); + wl_cfgp2p_set_management_ie(wl, dev, bssidx, + VNDR_IE_PRBRSP_FLAG, (u8 *)(buf + ie_offset), ie_len); + cfg80211_mgmt_tx_status(ndev, *cookie, buf, len, true, GFP_KERNEL); + goto exit; + } else if (ieee80211_is_disassoc(mgmt->frame_control) || + ieee80211_is_deauth(mgmt->frame_control)) { + char mac_buf[MAX_NUM_OF_ASSOCIATED_DEV * + sizeof(struct ether_addr) + sizeof(uint)] = {0}; + int num_associated = 0; + struct maclist *assoc_maclist = (struct maclist *)mac_buf; + if (!bcmp((const uint8 *)BSSID_BROADCAST, + (const struct ether_addr *)mgmt->da, ETHER_ADDR_LEN)) { + assoc_maclist->count = MAX_NUM_OF_ASSOCIATED_DEV; + err = wldev_ioctl(ndev, WLC_GET_ASSOCLIST, + assoc_maclist, sizeof(mac_buf), false); + if (err < 0) + WL_ERR(("WLC_GET_ASSOCLIST error %d\n", err)); + else + num_associated = assoc_maclist->count; + } + memcpy(scb_val.ea.octet, mgmt->da, ETH_ALEN); + scb_val.val = mgmt->u.disassoc.reason_code; + err = wldev_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scb_val, + sizeof(scb_val_t), true); + if (err < 0) + WL_ERR(("WLC_SCB_DEAUTHENTICATE_FOR_REASON error %d\n", err)); + WL_DBG(("Disconnect STA : %s scb_val.val %d\n", + bcm_ether_ntoa((const struct ether_addr *)mgmt->da, eabuf), + scb_val.val)); + if (num_associated) { + wl_delay(400); + } + cfg80211_mgmt_tx_status(ndev, *cookie, buf, len, true, GFP_KERNEL); + goto exit; + + } else if (ieee80211_is_action(mgmt->frame_control)) { + /* Abort the dwell time of any previous off-channel + * action frame that may be still in effect. Sending + * off-channel action frames relies on the driver's + * scan engine. If a previous off-channel action frame + * tx is still in progress (including the dwell time), + * then this new action frame will not be sent out. + */ +/* Do not abort scan for VSDB. Scan will be aborted in firmware if necessary. + * And previous off-channel action frame must be ended before new af tx. + */ +#ifndef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST + wl_notify_escan_complete(wl, dev, true, true); +#endif /* not WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ + } + + } else { + WL_ERR(("Driver only allows MGMT packet type\n")); + goto exit; + } + + af_params = (wl_af_params_t *) kzalloc(WL_WIFI_AF_PARAMS_SIZE, GFP_KERNEL); + + if (af_params == NULL) + { + WL_ERR(("unable to allocate frame\n")); + return -ENOMEM; + } + + action_frame = &af_params->action_frame; + + /* Add the packet Id */ + action_frame->packetId = *cookie; + WL_DBG(("action frame %d\n", action_frame->packetId)); + /* Add BSSID */ + memcpy(&action_frame->da, &mgmt->da[0], ETHER_ADDR_LEN); + memcpy(&af_params->BSSID, &mgmt->bssid[0], ETHER_ADDR_LEN); + + /* Add the length exepted for 802.11 header */ + action_frame->len = len - DOT11_MGMT_HDR_LEN; + WL_DBG(("action_frame->len: %d\n", action_frame->len)); + + /* Add the channel */ + af_params->channel = + ieee80211_frequency_to_channel(channel->center_freq); + + /* Save listen_chan for searching common channel */ + wl->afx_hdl->peer_listen_chan = af_params->channel; + WL_DBG(("channel from upper layer %d\n", wl->afx_hdl->peer_listen_chan)); + + /* Add the default dwell time + * Dwell time to stay off-channel to wait for a response action frame + * after transmitting an GO Negotiation action frame + */ + af_params->dwell_time = WL_DWELL_TIME; + + memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN], action_frame->len); + + ack = wl_cfg80211_send_action_frame(wiphy, dev, ndev, af_params, + action_frame, action_frame->len, bssidx); + + cfg80211_mgmt_tx_status(ndev, *cookie, buf, len, ack, GFP_KERNEL); + + kfree(af_params); +exit: + return err; +} + + +static void +wl_cfg80211_mgmt_frame_register(struct wiphy *wiphy, struct net_device *dev, + u16 frame_type, bool reg) +{ + + WL_DBG(("%s: frame_type: %x, reg: %d\n", __func__, frame_type, reg)); + + if (frame_type != (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ)) + return; + + return; +} + + +static s32 +wl_cfg80211_change_bss(struct wiphy *wiphy, + struct net_device *dev, + struct bss_parameters *params) +{ + if (params->use_cts_prot >= 0) { + } + + if (params->use_short_preamble >= 0) { + } + + if (params->use_short_slot_time >= 0) { + } + + if (params->basic_rates) { + } + + if (params->ap_isolate >= 0) { + } + + if (params->ht_opmode >= 0) { + } + + return 0; +} + +static s32 +wl_cfg80211_set_channel(struct wiphy *wiphy, struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) +{ + s32 _chan; + chanspec_t chspec = 0; + chanspec_t fw_chspec = 0; + u32 bw = WL_CHANSPEC_BW_20; + + s32 err = BCME_OK; + s32 bw_cap = 0; + struct { + u32 band; + u32 bw_cap; + } param = {0, 0}; + struct wl_priv *wl = wiphy_priv(wiphy); + + if (wl->p2p_net == dev) { + dev = wl_to_prmry_ndev(wl); + } + _chan = ieee80211_frequency_to_channel(chan->center_freq); + WL_ERR(("netdev_ifidx(%d), chan_type(%d) target channel(%d) \n", + dev->ifindex, channel_type, _chan)); + + + if (chan->band == IEEE80211_BAND_5GHZ) { + param.band = WLC_BAND_5G; + err = wldev_iovar_getbuf(dev, "bw_cap", ¶m, sizeof(param), + wl->ioctl_buf, WLC_IOCTL_SMLEN, &wl->ioctl_buf_sync); + if (err) { + if (err != BCME_UNSUPPORTED) { + WL_ERR(("bw_cap failed, %d\n", err)); + return err; + } else { + err = wldev_iovar_getint(dev, "mimo_bw_cap", &bw_cap); + if (err) { + WL_ERR(("error get mimo_bw_cap (%d)\n", err)); + } + if (bw_cap != WLC_N_BW_20ALL) + bw = WL_CHANSPEC_BW_40; + } + } else { + if (WL_BW_CAP_80MHZ(wl->ioctl_buf[0])) + bw = WL_CHANSPEC_BW_80; + else if (WL_BW_CAP_40MHZ(wl->ioctl_buf[0])) + bw = WL_CHANSPEC_BW_40; + else + bw = WL_CHANSPEC_BW_20; + + } + + } else if (chan->band == IEEE80211_BAND_2GHZ) + bw = WL_CHANSPEC_BW_20; +set_channel: + chspec = wf_channel2chspec(_chan, bw); + if (wf_chspec_valid(chspec)) { + fw_chspec = wl_chspec_host_to_driver(chspec); + if (fw_chspec != INVCHANSPEC) { + if ((err = wldev_iovar_setint(dev, "chanspec", + fw_chspec)) == BCME_BADCHAN) { + if (bw == WL_CHANSPEC_BW_80) + goto change_bw; + err = wldev_ioctl(dev, WLC_SET_CHANNEL, + &_chan, sizeof(_chan), true); + if (err < 0) { + WL_ERR(("WLC_SET_CHANNEL error %d" + "chip may not be supporting this channel\n", err)); + } + } else if (err) { + WL_ERR(("failed to set chanspec error %d\n", err)); + } + } else { + WL_ERR(("failed to convert host chanspec to fw chanspec\n")); + err = BCME_ERROR; + } + } else { +change_bw: + if (bw == WL_CHANSPEC_BW_80) + bw = WL_CHANSPEC_BW_40; + else if (bw == WL_CHANSPEC_BW_40) + bw = WL_CHANSPEC_BW_20; + else + bw = 0; + if (bw) + goto set_channel; + WL_ERR(("Invalid chanspec 0x%x\n", chspec)); + err = BCME_ERROR; + } + return err; +} + +static s32 +wl_validate_opensecurity(struct net_device *dev, s32 bssidx) +{ + s32 err = BCME_OK; + + /* set auth */ + err = wldev_iovar_setint_bsscfg(dev, "auth", 0, bssidx); + if (err < 0) { + WL_ERR(("auth error %d\n", err)); + return BCME_ERROR; + } + /* set wsec */ + err = wldev_iovar_setint_bsscfg(dev, "wsec", 0, bssidx); + if (err < 0) { + WL_ERR(("wsec error %d\n", err)); + return BCME_ERROR; + } + /* set upper-layer auth */ + err = wldev_iovar_setint_bsscfg(dev, "wpa_auth", WPA_AUTH_NONE, bssidx); + if (err < 0) { + WL_ERR(("wpa_auth error %d\n", err)); + return BCME_ERROR; + } + + return 0; +} + +static s32 +wl_validate_wpa2ie(struct net_device *dev, bcm_tlv_t *wpa2ie, s32 bssidx) +{ + s32 len = 0; + s32 err = BCME_OK; + u16 auth = 0; /* d11 open authentication */ + u32 wsec; + u32 pval = 0; + u32 gval = 0; + u32 wpa_auth = 0; + wpa_suite_mcast_t *mcast; + wpa_suite_ucast_t *ucast; + wpa_suite_auth_key_mgmt_t *mgmt; + + u16 suite_count; + u8 rsn_cap[2]; + u32 wme_bss_disable; + + if (wpa2ie == NULL) + goto exit; + + WL_DBG(("Enter \n")); + len = wpa2ie->len; + /* check the mcast cipher */ + mcast = (wpa_suite_mcast_t *)&wpa2ie->data[WPA2_VERSION_LEN]; + switch (mcast->type) { + case WPA_CIPHER_NONE: + gval = 0; + break; + case WPA_CIPHER_WEP_40: + case WPA_CIPHER_WEP_104: + gval = WEP_ENABLED; + break; + case WPA_CIPHER_TKIP: + gval = TKIP_ENABLED; + break; + case WPA_CIPHER_AES_CCM: + gval = AES_ENABLED; + break; + default: + WL_ERR(("No Security Info\n")); + break; + } + if ((len -= WPA_SUITE_LEN) <= 0) + return BCME_BADLEN; + + /* check the unicast cipher */ + ucast = (wpa_suite_ucast_t *)&mcast[1]; + suite_count = ltoh16_ua(&ucast->count); + switch (ucast->list[0].type) { + case WPA_CIPHER_NONE: + pval = 0; + break; + case WPA_CIPHER_WEP_40: + case WPA_CIPHER_WEP_104: + pval = WEP_ENABLED; + break; + case WPA_CIPHER_TKIP: + pval = TKIP_ENABLED; + break; + case WPA_CIPHER_AES_CCM: + pval = AES_ENABLED; + break; + default: + WL_ERR(("No Security Info\n")); + } + if ((len -= (WPA_IE_SUITE_COUNT_LEN + (WPA_SUITE_LEN * suite_count))) <= 0) + return BCME_BADLEN; + + /* FOR WPS , set SEC_OW_ENABLED */ + wsec = (pval | gval | SES_OW_ENABLED); + /* check the AKM */ + mgmt = (wpa_suite_auth_key_mgmt_t *)&ucast->list[suite_count]; + suite_count = ltoh16_ua(&mgmt->count); + switch (mgmt->list[0].type) { + case RSN_AKM_NONE: + wpa_auth = WPA_AUTH_NONE; + break; + case RSN_AKM_UNSPECIFIED: + wpa_auth = WPA2_AUTH_UNSPECIFIED; + break; + case RSN_AKM_PSK: + wpa_auth = WPA2_AUTH_PSK; + break; + default: + WL_ERR(("No Key Mgmt Info\n")); + } + + if ((len -= (WPA_IE_SUITE_COUNT_LEN + (WPA_SUITE_LEN * suite_count))) >= RSN_CAP_LEN) { + rsn_cap[0] = *(u8 *)&mgmt->list[suite_count]; + rsn_cap[1] = *((u8 *)&mgmt->list[suite_count] + 1); + + if (rsn_cap[0] & (RSN_CAP_16_REPLAY_CNTRS << RSN_CAP_PTK_REPLAY_CNTR_SHIFT)) { + wme_bss_disable = 0; + } else { + wme_bss_disable = 1; + } + + /* set wme_bss_disable to sync RSN Capabilities */ + err = wldev_iovar_setint_bsscfg(dev, "wme_bss_disable", wme_bss_disable, bssidx); + if (err < 0) { + WL_ERR(("wme_bss_disable error %d\n", err)); + return BCME_ERROR; + } + } else { + WL_DBG(("There is no RSN Capabilities. remained len %d\n", len)); + } + + /* set auth */ + err = wldev_iovar_setint_bsscfg(dev, "auth", auth, bssidx); + if (err < 0) { + WL_ERR(("auth error %d\n", err)); + return BCME_ERROR; + } + /* set wsec */ + err = wldev_iovar_setint_bsscfg(dev, "wsec", wsec, bssidx); + if (err < 0) { + WL_ERR(("wsec error %d\n", err)); + return BCME_ERROR; + } + /* set upper-layer auth */ + err = wldev_iovar_setint_bsscfg(dev, "wpa_auth", wpa_auth, bssidx); + if (err < 0) { + WL_ERR(("wpa_auth error %d\n", err)); + return BCME_ERROR; + } +exit: + return 0; +} + +static s32 +wl_validate_wpaie(struct net_device *dev, wpa_ie_fixed_t *wpaie, s32 bssidx) +{ + wpa_suite_mcast_t *mcast; + wpa_suite_ucast_t *ucast; + wpa_suite_auth_key_mgmt_t *mgmt; + u16 auth = 0; /* d11 open authentication */ + u16 count; + s32 err = BCME_OK; + s32 len = 0; + u32 i; + u32 wsec; + u32 pval = 0; + u32 gval = 0; + u32 wpa_auth = 0; + u32 tmp = 0; + + if (wpaie == NULL) + goto exit; + WL_DBG(("Enter \n")); + len = wpaie->length; /* value length */ + len -= WPA_IE_TAG_FIXED_LEN; + /* check for multicast cipher suite */ + if (len < WPA_SUITE_LEN) { + WL_INFO(("no multicast cipher suite\n")); + goto exit; + } + + /* pick up multicast cipher */ + mcast = (wpa_suite_mcast_t *)&wpaie[1]; + len -= WPA_SUITE_LEN; + if (!bcmp(mcast->oui, WPA_OUI, WPA_OUI_LEN)) { + if (IS_WPA_CIPHER(mcast->type)) { + tmp = 0; + switch (mcast->type) { + case WPA_CIPHER_NONE: + tmp = 0; + break; + case WPA_CIPHER_WEP_40: + case WPA_CIPHER_WEP_104: + tmp = WEP_ENABLED; + break; + case WPA_CIPHER_TKIP: + tmp = TKIP_ENABLED; + break; + case WPA_CIPHER_AES_CCM: + tmp = AES_ENABLED; + break; + default: + WL_ERR(("No Security Info\n")); + } + gval |= tmp; + } + } + /* Check for unicast suite(s) */ + if (len < WPA_IE_SUITE_COUNT_LEN) { + WL_INFO(("no unicast suite\n")); + goto exit; + } + /* walk thru unicast cipher list and pick up what we recognize */ + ucast = (wpa_suite_ucast_t *)&mcast[1]; + count = ltoh16_ua(&ucast->count); + len -= WPA_IE_SUITE_COUNT_LEN; + for (i = 0; i < count && len >= WPA_SUITE_LEN; + i++, len -= WPA_SUITE_LEN) { + if (!bcmp(ucast->list[i].oui, WPA_OUI, WPA_OUI_LEN)) { + if (IS_WPA_CIPHER(ucast->list[i].type)) { + tmp = 0; + switch (ucast->list[i].type) { + case WPA_CIPHER_NONE: + tmp = 0; + break; + case WPA_CIPHER_WEP_40: + case WPA_CIPHER_WEP_104: + tmp = WEP_ENABLED; + break; + case WPA_CIPHER_TKIP: + tmp = TKIP_ENABLED; + break; + case WPA_CIPHER_AES_CCM: + tmp = AES_ENABLED; + break; + default: + WL_ERR(("No Security Info\n")); + } + pval |= tmp; + } + } + } + len -= (count - i) * WPA_SUITE_LEN; + /* Check for auth key management suite(s) */ + if (len < WPA_IE_SUITE_COUNT_LEN) { + WL_INFO((" no auth key mgmt suite\n")); + goto exit; + } + /* walk thru auth management suite list and pick up what we recognize */ + mgmt = (wpa_suite_auth_key_mgmt_t *)&ucast->list[count]; + count = ltoh16_ua(&mgmt->count); + len -= WPA_IE_SUITE_COUNT_LEN; + for (i = 0; i < count && len >= WPA_SUITE_LEN; + i++, len -= WPA_SUITE_LEN) { + if (!bcmp(mgmt->list[i].oui, WPA_OUI, WPA_OUI_LEN)) { + if (IS_WPA_AKM(mgmt->list[i].type)) { + tmp = 0; + switch (mgmt->list[i].type) { + case RSN_AKM_NONE: + tmp = WPA_AUTH_NONE; + break; + case RSN_AKM_UNSPECIFIED: + tmp = WPA_AUTH_UNSPECIFIED; + break; + case RSN_AKM_PSK: + tmp = WPA_AUTH_PSK; + break; + default: + WL_ERR(("No Key Mgmt Info\n")); + } + wpa_auth |= tmp; + } + } + + } + /* FOR WPS , set SEC_OW_ENABLED */ + wsec = (pval | gval | SES_OW_ENABLED); + /* set auth */ + err = wldev_iovar_setint_bsscfg(dev, "auth", auth, bssidx); + if (err < 0) { + WL_ERR(("auth error %d\n", err)); + return BCME_ERROR; + } + /* set wsec */ + err = wldev_iovar_setint_bsscfg(dev, "wsec", wsec, bssidx); + if (err < 0) { + WL_ERR(("wsec error %d\n", err)); + return BCME_ERROR; + } + /* set upper-layer auth */ + err = wldev_iovar_setint_bsscfg(dev, "wpa_auth", wpa_auth, bssidx); + if (err < 0) { + WL_ERR(("wpa_auth error %d\n", err)); + return BCME_ERROR; + } +exit: + return 0; +} + +static s32 +wl_cfg80211_bcn_validate_sec( + struct net_device *dev, + struct parsed_ies *ies, + u32 dev_role, + s32 bssidx) +{ + struct wl_priv *wl = wlcfg_drv_priv; + + if (dev_role == NL80211_IFTYPE_P2P_GO && (ies->wpa2_ie)) { + /* For P2P GO, the sec type is WPA2-PSK */ + WL_DBG(("P2P GO: validating wpa2_ie")); + if (wl_validate_wpa2ie(dev, ies->wpa2_ie, bssidx) < 0) + return BCME_ERROR; + + } else if (dev_role == NL80211_IFTYPE_AP) { + + WL_DBG(("SoftAP: validating security")); + /* If wpa2_ie or wpa_ie is present validate it */ + if ((ies->wpa2_ie || ies->wpa_ie) && + ((wl_validate_wpa2ie(dev, ies->wpa2_ie, bssidx) < 0 || + wl_validate_wpaie(dev, ies->wpa_ie, bssidx) < 0))) { + wl->ap_info->security_mode = false; + return BCME_ERROR; + } + + wl->ap_info->security_mode = true; + if (wl->ap_info->rsn_ie) { + kfree(wl->ap_info->rsn_ie); + wl->ap_info->rsn_ie = NULL; + } + if (wl->ap_info->wpa_ie) { + kfree(wl->ap_info->wpa_ie); + wl->ap_info->wpa_ie = NULL; + } + if (wl->ap_info->wps_ie) { + kfree(wl->ap_info->wps_ie); + wl->ap_info->wps_ie = NULL; + } + if (ies->wpa_ie != NULL) { + /* WPAIE */ + wl->ap_info->rsn_ie = NULL; + wl->ap_info->wpa_ie = kmemdup(ies->wpa_ie, + ies->wpa_ie->length + WPA_RSN_IE_TAG_FIXED_LEN, + GFP_KERNEL); + } else if (ies->wpa2_ie != NULL) { + /* RSNIE */ + wl->ap_info->wpa_ie = NULL; + wl->ap_info->rsn_ie = kmemdup(ies->wpa2_ie, + ies->wpa2_ie->len + WPA_RSN_IE_TAG_FIXED_LEN, + GFP_KERNEL); + } + + if (!ies->wpa2_ie && !ies->wpa_ie) { + wl_validate_opensecurity(dev, bssidx); + wl->ap_info->security_mode = false; + } + + if (ies->wps_ie) { + wl->ap_info->wps_ie = kmemdup(ies->wps_ie, ies->wps_ie_len, GFP_KERNEL); + } + } + + return 0; + +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) +static s32 wl_cfg80211_bcn_set_params( + struct cfg80211_ap_settings *info, + struct net_device *dev, + u32 dev_role, s32 bssidx) +{ + struct wl_priv *wl = wlcfg_drv_priv; + s32 err = BCME_OK; + + WL_DBG(("interval (%d) \ndtim_period (%d) \n", + info->beacon_interval, info->dtim_period)); + + if (info->beacon_interval) { + if ((err = wldev_ioctl(dev, WLC_SET_BCNPRD, + &info->beacon_interval, sizeof(s32), true)) < 0) { + WL_ERR(("Beacon Interval Set Error, %d\n", err)); + return err; + } + } + + if (info->dtim_period) { + if ((err = wldev_ioctl(dev, WLC_SET_DTIMPRD, + &info->dtim_period, sizeof(s32), true)) < 0) { + WL_ERR(("DTIM Interval Set Error, %d\n", err)); + return err; + } + } + + if ((info->ssid) && (info->ssid_len > 0) && + (info->ssid_len <= 32)) { + WL_DBG(("SSID (%s) len:%d \n", info->ssid, info->ssid_len)); + if (dev_role == NL80211_IFTYPE_AP) { + /* Store the hostapd SSID */ + memset(wl->hostapd_ssid.SSID, 0x00, 32); + memcpy(wl->hostapd_ssid.SSID, info->ssid, info->ssid_len); + wl->hostapd_ssid.SSID_len = info->ssid_len; + } else { + /* P2P GO */ + memset(wl->p2p->ssid.SSID, 0x00, 32); + memcpy(wl->p2p->ssid.SSID, info->ssid, info->ssid_len); + wl->p2p->ssid.SSID_len = info->ssid_len; + } + } + + if (info->hidden_ssid) { + if ((err = wldev_iovar_setint(dev, "closednet", 1)) < 0) + WL_ERR(("failed to set hidden : %d\n", err)); + WL_DBG(("hidden_ssid_enum_val: %d \n", info->hidden_ssid)); + } + + return err; +} +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) */ + +static s32 +wl_cfg80211_parse_ies(u8 *ptr, u32 len, struct parsed_ies *ies) +{ + s32 err = BCME_OK; + + memset(ies, 0, sizeof(struct parsed_ies)); + + /* find the WPSIE */ + if ((ies->wps_ie = wl_cfgp2p_find_wpsie(ptr, len)) != NULL) { + WL_DBG(("WPSIE in beacon \n")); + ies->wps_ie_len = ies->wps_ie->length + WPA_RSN_IE_TAG_FIXED_LEN; + } else { + WL_ERR(("No WPSIE in beacon \n")); + } + + /* find the RSN_IE */ + if ((ies->wpa2_ie = bcm_parse_tlvs(ptr, len, + DOT11_MNG_RSN_ID)) != NULL) { + WL_DBG((" WPA2 IE found\n")); + ies->wpa2_ie_len = ies->wpa2_ie->len; + } + + /* find the WPA_IE */ + if ((ies->wpa_ie = wl_cfgp2p_find_wpaie(ptr, len)) != NULL) { + WL_DBG((" WPA found\n")); + ies->wpa_ie_len = ies->wpa_ie->length; + } + + return err; + +} + +static s32 +wl_cfg80211_bcn_bringup_ap( + struct net_device *dev, + struct parsed_ies *ies, + u32 dev_role, s32 bssidx) +{ + struct wl_priv *wl = wlcfg_drv_priv; + struct wl_join_params join_params; + bool is_bssup = false; + s32 infra = 1; + s32 join_params_size = 0; + s32 ap = 1; + s32 err = BCME_OK; + + WL_DBG(("Enter dev_role: %d\n", dev_role)); + + /* Common code for SoftAP and P2P GO */ + wldev_iovar_setint(dev, "mpc", 0); + + if (dev_role == NL80211_IFTYPE_P2P_GO) { + is_bssup = wl_cfgp2p_bss_isup(dev, bssidx); + if (!is_bssup && (ies->wpa2_ie != NULL)) { + + err = wldev_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(s32), true); + if (err < 0) { + WL_ERR(("SET INFRA error %d\n", err)); + goto exit; + } + + err = wldev_iovar_setbuf_bsscfg(dev, "ssid", &wl->p2p->ssid, + sizeof(wl->p2p->ssid), wl->ioctl_buf, WLC_IOCTL_MAXLEN, + bssidx, &wl->ioctl_buf_sync); + if (err < 0) { + WL_ERR(("GO SSID setting error %d\n", err)); + goto exit; + } + + if ((err = wl_cfgp2p_bss(wl, dev, bssidx, 1)) < 0) { + WL_ERR(("GO Bring up error %d\n", err)); + goto exit; + } + } else + WL_DBG(("Bss is already up\n")); + } else if ((dev_role == NL80211_IFTYPE_AP) && + (wl_get_drv_status(wl, AP_CREATING, dev))) { + /* Device role SoftAP */ + err = wldev_ioctl(dev, WLC_DOWN, &ap, sizeof(s32), true); + if (err < 0) { + WL_ERR(("WLC_DOWN error %d\n", err)); + goto exit; + } + err = wldev_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(s32), true); + if (err < 0) { + WL_ERR(("SET INFRA error %d\n", err)); + goto exit; + } + if ((err = wldev_ioctl(dev, WLC_SET_AP, &ap, sizeof(s32), true)) < 0) { + WL_ERR(("setting AP mode failed %d \n", err)); + goto exit; + } + + err = wldev_ioctl(dev, WLC_UP, &ap, sizeof(s32), true); + if (unlikely(err)) { + WL_ERR(("WLC_UP error (%d)\n", err)); + goto exit; + } + + memset(&join_params, 0, sizeof(join_params)); + /* join parameters starts with ssid */ + join_params_size = sizeof(join_params.ssid); + memcpy(join_params.ssid.SSID, wl->hostapd_ssid.SSID, + wl->hostapd_ssid.SSID_len); + join_params.ssid.SSID_len = htod32(wl->hostapd_ssid.SSID_len); + + /* create softap */ + if ((err = wldev_ioctl(dev, WLC_SET_SSID, &join_params, + join_params_size, true)) == 0) { + WL_DBG(("SoftAP set SSID (%s) success\n", join_params.ssid.SSID)); + wl_clr_drv_status(wl, AP_CREATING, dev); + wl_set_drv_status(wl, AP_CREATED, dev); + } + } + + +exit: + return err; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) +s32 +wl_cfg80211_parse_ap_ies( + struct net_device *dev, + struct cfg80211_beacon_data *info, + struct parsed_ies *ies) +{ + struct parsed_ies prb_ies; + s32 err = BCME_OK; + + /* Parse Beacon IEs */ + if (wl_cfg80211_parse_ies((u8 *)info->tail, + info->tail_len, ies) < 0) { + WL_ERR(("Beacon get IEs failed \n")); + err = -EINVAL; + goto fail; + } + + /* Parse Probe Response IEs */ + if (wl_cfg80211_parse_ies((u8 *)info->proberesp_ies, + info->proberesp_ies_len, &prb_ies) < 0) { + WL_ERR(("PROBE RESP get IEs failed \n")); + err = -EINVAL; + } + +fail: + + return err; +} + +s32 +wl_cfg80211_set_ies( + struct net_device *dev, + struct cfg80211_beacon_data *info, + s32 bssidx) +{ + struct wl_priv *wl = wlcfg_drv_priv; + s32 err = BCME_OK; + + /* Set Beacon IEs to FW */ + if ((err = wl_cfgp2p_set_management_ie(wl, dev, bssidx, + VNDR_IE_BEACON_FLAG, (u8 *)info->tail, + info->tail_len)) < 0) { + WL_ERR(("Set Beacon IE Failed \n")); + } else { + WL_DBG(("Applied Vndr IEs for Beacon \n")); + } + + /* Set Probe Response IEs to FW */ + if ((err = wl_cfgp2p_set_management_ie(wl, dev, bssidx, + VNDR_IE_PRBRSP_FLAG, (u8 *)info->proberesp_ies, + info->proberesp_ies_len)) < 0) { + WL_ERR(("Set Probe Resp IE Failed \n")); + } else { + WL_DBG(("Applied Vndr IEs for Probe Resp \n")); + } + + return err; +} +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) */ + +static s32 wl_cfg80211_hostapd_sec( + struct net_device *dev, + struct parsed_ies *ies, + s32 bssidx) +{ + bool update_bss = 0; + struct wl_priv *wl = wlcfg_drv_priv; + + + if (ies->wps_ie) { + if (wl->ap_info->wps_ie && + memcmp(wl->ap_info->wps_ie, ies->wps_ie, ies->wps_ie_len)) { + WL_DBG((" WPS IE is changed\n")); + kfree(wl->ap_info->wps_ie); + wl->ap_info->wps_ie = kmemdup(ies->wps_ie, ies->wps_ie_len, GFP_KERNEL); + } else if (wl->ap_info->wps_ie == NULL) { + WL_DBG((" WPS IE is added\n")); + wl->ap_info->wps_ie = kmemdup(ies->wps_ie, ies->wps_ie_len, GFP_KERNEL); + } + if ((ies->wpa_ie != NULL || ies->wpa2_ie != NULL)) { + if (!wl->ap_info->security_mode) { + /* change from open mode to security mode */ + update_bss = true; + if (ies->wpa_ie != NULL) { + wl->ap_info->wpa_ie = kmemdup(ies->wpa_ie, + ies->wpa_ie->length + WPA_RSN_IE_TAG_FIXED_LEN, + GFP_KERNEL); + } else { + wl->ap_info->rsn_ie = kmemdup(ies->wpa2_ie, + ies->wpa2_ie->len + WPA_RSN_IE_TAG_FIXED_LEN, + GFP_KERNEL); + } + } else if (wl->ap_info->wpa_ie) { + /* change from WPA2 mode to WPA mode */ + if (ies->wpa_ie != NULL) { + update_bss = true; + kfree(wl->ap_info->rsn_ie); + wl->ap_info->rsn_ie = NULL; + wl->ap_info->wpa_ie = kmemdup(ies->wpa_ie, + ies->wpa_ie->length + WPA_RSN_IE_TAG_FIXED_LEN, + GFP_KERNEL); + } else if (memcmp(wl->ap_info->rsn_ie, + ies->wpa2_ie, ies->wpa2_ie->len + + WPA_RSN_IE_TAG_FIXED_LEN)) { + update_bss = true; + kfree(wl->ap_info->rsn_ie); + wl->ap_info->rsn_ie = kmemdup(ies->wpa2_ie, + ies->wpa2_ie->len + WPA_RSN_IE_TAG_FIXED_LEN, + GFP_KERNEL); + wl->ap_info->wpa_ie = NULL; + } + } + if (update_bss) { + wl->ap_info->security_mode = true; + wl_cfgp2p_bss(wl, dev, bssidx, 0); + if (wl_validate_wpa2ie(dev, ies->wpa2_ie, bssidx) < 0 || + wl_validate_wpaie(dev, ies->wpa_ie, bssidx) < 0) { + return BCME_ERROR; + } + wl_cfgp2p_bss(wl, dev, bssidx, 1); + } + } + } else { + WL_ERR(("No WPSIE in beacon \n")); + } + return 0; +} + +static s32 +wl_cfg80211_del_station( + struct wiphy *wiphy, + struct net_device *ndev, + u8* mac_addr) +{ + struct net_device *dev; + struct wl_priv *wl = wiphy_priv(wiphy); + scb_val_t scb_val; + s8 eabuf[ETHER_ADDR_STR_LEN]; + char mac_buf[MAX_NUM_OF_ASSOCIATED_DEV * + sizeof(struct ether_addr) + sizeof(uint)] = {0}; + struct maclist *assoc_maclist = (struct maclist *)mac_buf; + int num_associated = 0, err; + + WL_DBG(("Entry\n")); + if (mac_addr == NULL) { + WL_DBG(("mac_addr is NULL ignore it\n")); + return 0; + } + + if (ndev == wl->p2p_net) { + dev = wl_to_prmry_ndev(wl); + } else { + dev = ndev; + } + + if (p2p_is_on(wl)) { + /* Suspend P2P discovery search-listen to prevent it from changing the + * channel. + */ + if ((wl_cfgp2p_discover_enable_search(wl, false)) < 0) { + WL_ERR(("Can not disable discovery mode\n")); + return -EFAULT; + } + } + + assoc_maclist->count = MAX_NUM_OF_ASSOCIATED_DEV; + err = wldev_ioctl(ndev, WLC_GET_ASSOCLIST, + assoc_maclist, sizeof(mac_buf), false); + if (err < 0) + WL_ERR(("WLC_GET_ASSOCLIST error %d\n", err)); + else + num_associated = assoc_maclist->count; + + memcpy(scb_val.ea.octet, mac_addr, ETHER_ADDR_LEN); + scb_val.val = DOT11_RC_DEAUTH_LEAVING; + err = wldev_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scb_val, + sizeof(scb_val_t), true); + if (err < 0) + WL_ERR(("WLC_SCB_DEAUTHENTICATE_FOR_REASON err %d\n", err)); + WL_DBG(("Disconnect STA : %s scb_val.val %d\n", + bcm_ether_ntoa((const struct ether_addr *)mac_addr, eabuf), + scb_val.val)); + if (num_associated) + wl_delay(400); + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) +static s32 +wl_cfg80211_start_ap( + struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_ap_settings *info) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + s32 err = BCME_OK; + struct parsed_ies ies; + s32 bssidx = 0; + u32 dev_role = 0; + + WL_DBG(("Enter \n")); + if (dev == wl_to_prmry_ndev(wl)) { + WL_DBG(("Start AP req on primary iface: Softap\n")); + dev_role = NL80211_IFTYPE_AP; + } else if (dev == wl->p2p_net) { + /* Group Add request on p2p0 */ + WL_DBG(("Start AP req on P2P iface: GO\n")); + dev = wl_to_prmry_ndev(wl); + dev_role = NL80211_IFTYPE_P2P_GO; + } + + bssidx = wl_cfgp2p_find_idx(wl, dev); + if (p2p_is_on(wl) && + (bssidx == wl_to_p2p_bss_bssidx(wl, + P2PAPI_BSSCFG_CONNECTION))) { + dev_role = NL80211_IFTYPE_P2P_GO; + WL_DBG(("Start AP req on P2P connection iface\n")); + } + + if ((err = wl_cfg80211_bcn_set_params(info, dev, + dev_role, bssidx)) < 0) { + WL_ERR(("Beacon params set failed \n")); + goto fail; + } + + /* Parse IEs */ + if ((err = wl_cfg80211_parse_ap_ies(dev, &info->beacon, &ies) < 0)) { + WL_ERR(("Set IEs failed \n")); + goto fail; + } + + if ((wl_cfg80211_bcn_validate_sec(dev, &ies, + dev_role, bssidx)) < 0) + { + WL_ERR(("Beacon set security failed \n")); + goto fail; + } + + if ((err = wl_cfg80211_bcn_bringup_ap(dev, &ies, + dev_role, bssidx)) < 0) { + WL_ERR(("Beacon bring up AP/GO failed \n")); + goto fail; + } + + WL_DBG(("** AP/GO Created **\n")); + + /* Set IEs to FW */ + if ((err = wl_cfg80211_set_ies(dev, &info->beacon, bssidx) < 0)) + WL_ERR(("Set IEs failed \n")); + +fail: + if (err) { + WL_ERR(("ADD/SET beacon failed\n")); + wldev_iovar_setint(dev, "mpc", 1); + } + + return err; +} + +static s32 +wl_cfg80211_stop_ap( + struct wiphy *wiphy, + struct net_device *dev) +{ + int err = 0; + u32 dev_role = 0; + int infra = 0; + int ap = 0; + s32 bssidx = 0; + struct wl_priv *wl = wiphy_priv(wiphy); + + WL_DBG(("Enter \n")); + if (dev == wl_to_prmry_ndev(wl)) { + dev_role = NL80211_IFTYPE_AP; + } else if (dev == wl->p2p_net) { + /* Group Add request on p2p0 */ + dev = wl_to_prmry_ndev(wl); + dev_role = NL80211_IFTYPE_P2P_GO; + } + bssidx = wl_cfgp2p_find_idx(wl, dev); + if (p2p_is_on(wl) && + (bssidx == wl_to_p2p_bss_bssidx(wl, + P2PAPI_BSSCFG_CONNECTION))) { + dev_role = NL80211_IFTYPE_P2P_GO; + } + + if (dev_role == NL80211_IFTYPE_AP) { + /* SoftAp on primary Interface. + * Shut down AP and turn on MPC + */ + err = wldev_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(s32), true); + if (err < 0) { + WL_ERR(("SET INFRA error %d\n", err)); + err = -ENOTSUPP; + goto exit; + } + if ((err = wldev_ioctl(dev, WLC_SET_AP, &ap, sizeof(s32), true)) < 0) { + WL_ERR(("setting AP mode failed %d \n", err)); + err = -ENOTSUPP; + goto exit; + } + + err = wldev_ioctl(dev, WLC_UP, &ap, sizeof(s32), true); + if (unlikely(err)) { + WL_ERR(("WLC_UP error (%d)\n", err)); + err = -EINVAL; + goto exit; + } + + wl_clr_drv_status(wl, AP_CREATED, dev); + /* Turn on the MPC */ + wldev_iovar_setint(dev, "mpc", 1); + } else { + WL_DBG(("Stopping P2P GO \n")); + } + +exit: + return err; +} + +static s32 +wl_cfg80211_change_beacon( + struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_beacon_data *info) +{ + s32 err = BCME_OK; + struct wl_priv *wl = wiphy_priv(wiphy); + struct parsed_ies ies; + u32 dev_role = 0; + s32 bssidx = 0; + + WL_DBG(("Enter \n")); + + if (dev == wl_to_prmry_ndev(wl)) { + dev_role = NL80211_IFTYPE_AP; + } else if (dev == wl->p2p_net) { + /* Group Add request on p2p0 */ + dev = wl_to_prmry_ndev(wl); + dev_role = NL80211_IFTYPE_P2P_GO; + } + + bssidx = wl_cfgp2p_find_idx(wl, dev); + if (p2p_is_on(wl) && + (bssidx == wl_to_p2p_bss_bssidx(wl, + P2PAPI_BSSCFG_CONNECTION))) { + dev_role = NL80211_IFTYPE_P2P_GO; + } + + /* Parse IEs */ + if ((err = wl_cfg80211_parse_ap_ies(dev, info, &ies) < 0)) { + WL_ERR(("Parse IEs failed \n")); + goto fail; + } + + /* Set IEs to FW */ + if ((err = wl_cfg80211_set_ies(dev, info, bssidx) < 0)) { + WL_ERR(("Set IEs failed \n")); + goto fail; + } + + if (dev_role == NL80211_IFTYPE_AP) { + if (wl_cfg80211_hostapd_sec(dev, &ies, bssidx) < 0) { + WL_ERR(("Hostapd update sec failed \n")); + err = -EINVAL; + goto fail; + } + } + +fail: + return err; +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) */ +static s32 +wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev, + struct beacon_parameters *info) +{ + s32 err = BCME_OK; + struct wl_priv *wl = wiphy_priv(wiphy); + s32 ie_offset = 0; + s32 bssidx = 0; + u32 dev_role = NL80211_IFTYPE_AP; + struct parsed_ies ies; + bcm_tlv_t *ssid_ie; + bool pbc = 0; + + WL_DBG(("interval (%d) dtim_period (%d) head_len (%d) tail_len (%d)\n", + info->interval, info->dtim_period, info->head_len, info->tail_len)); + + if (dev == wl_to_prmry_ndev(wl)) { + dev_role = NL80211_IFTYPE_AP; + } else if (dev == wl->p2p_net) { + /* Group Add request on p2p0 */ + dev = wl_to_prmry_ndev(wl); + dev_role = NL80211_IFTYPE_P2P_GO; + } + + bssidx = wl_cfgp2p_find_idx(wl, dev); + if (p2p_is_on(wl) && + (bssidx == wl_to_p2p_bss_bssidx(wl, + P2PAPI_BSSCFG_CONNECTION))) { + dev_role = NL80211_IFTYPE_P2P_GO; + } + + ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN; + /* find the SSID */ + if ((ssid_ie = bcm_parse_tlvs((u8 *)&info->head[ie_offset], + info->head_len - ie_offset, + DOT11_MNG_SSID_ID)) != NULL) { + if (dev_role == NL80211_IFTYPE_AP) { + /* Store the hostapd SSID */ + memset(&wl->hostapd_ssid.SSID[0], 0x00, 32); + memcpy(&wl->hostapd_ssid.SSID[0], ssid_ie->data, ssid_ie->len); + wl->hostapd_ssid.SSID_len = ssid_ie->len; + } else { + /* P2P GO */ + memset(&wl->p2p->ssid.SSID[0], 0x00, 32); + memcpy(wl->p2p->ssid.SSID, ssid_ie->data, ssid_ie->len); + wl->p2p->ssid.SSID_len = ssid_ie->len; + } + } + + if (wl_cfg80211_parse_ies((u8 *)info->tail, + info->tail_len, &ies) < 0) { + WL_ERR(("Beacon get IEs failed \n")); + err = -EINVAL; + goto fail; + } + + if (wl_cfgp2p_set_management_ie(wl, dev, bssidx, + VNDR_IE_BEACON_FLAG, (u8 *)info->tail, + info->tail_len) < 0) { + WL_ERR(("Beacon set IEs failed \n")); + goto fail; + } else { + WL_DBG(("Applied Vndr IEs for Beacon \n")); + } + if (!wl_cfgp2p_bss_isup(dev, bssidx) && + (wl_cfg80211_bcn_validate_sec(dev, &ies, dev_role, bssidx) < 0)) + { + WL_ERR(("Beacon set security failed \n")); + goto fail; + } + + /* Set BI and DTIM period */ + if (info->interval) { + if ((err = wldev_ioctl(dev, WLC_SET_BCNPRD, + &info->interval, sizeof(s32), true)) < 0) { + WL_ERR(("Beacon Interval Set Error, %d\n", err)); + return err; + } + } + if (info->dtim_period) { + if ((err = wldev_ioctl(dev, WLC_SET_DTIMPRD, + &info->dtim_period, sizeof(s32), true)) < 0) { + WL_ERR(("DTIM Interval Set Error, %d\n", err)); + return err; + } + } + + if (wl_cfg80211_bcn_bringup_ap(dev, &ies, dev_role, bssidx) < 0) { + WL_ERR(("Beacon bring up AP/GO failed \n")); + goto fail; + } + + if (wl_get_drv_status(wl, AP_CREATED, dev)) { + /* Soft AP already running. Update changed params */ + if (wl_cfg80211_hostapd_sec(dev, &ies, bssidx) < 0) { + WL_ERR(("Hostapd update sec failed \n")); + err = -EINVAL; + goto fail; + } + } + + /* Enable Probe Req filter */ + if (((dev_role == NL80211_IFTYPE_P2P_GO) || + (dev_role == NL80211_IFTYPE_AP)) && (ies.wps_ie != NULL)) { + wl_validate_wps_ie((char *) ies.wps_ie, ies.wps_ie_len, &pbc); + if (pbc) + wl_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, true); + } + + WL_DBG(("** ADD/SET beacon done **\n")); + +fail: + if (err) { + WL_ERR(("ADD/SET beacon failed\n")); + wldev_iovar_setint(dev, "mpc", 1); + } + return err; + +} +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) */ + +#ifdef WL_SCHED_SCAN +#define PNO_TIME 30 +#define PNO_REPEAT 4 +#define PNO_FREQ_EXPO_MAX 2 +int wl_cfg80211_sched_scan_start(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_sched_scan_request *request) +{ + ushort pno_time = PNO_TIME; + int pno_repeat = PNO_REPEAT; + int pno_freq_expo_max = PNO_FREQ_EXPO_MAX; + wlc_ssid_t ssids_local[MAX_PFN_LIST_COUNT]; + struct wl_priv *wl = wiphy_priv(wiphy); + struct cfg80211_ssid *ssid = NULL; + int ssid_count = 0; + int i; + int ret = 0; + + WL_DBG(("Enter \n")); + WL_PNO((">>> SCHED SCAN START\n")); + WL_PNO(("Enter n_match_sets:%d n_ssids:%d \n", + request->n_match_sets, request->n_ssids)); + WL_PNO(("ssids:%d pno_time:%d pno_repeat:%d pno_freq:%d \n", + request->n_ssids, pno_time, pno_repeat, pno_freq_expo_max)); + + + if (!request || !request->n_ssids || !request->n_match_sets) { + WL_ERR(("Invalid sched scan req!! n_ssids:%d \n", request->n_ssids)); + return -EINVAL; + } + + memset(&ssids_local, 0, sizeof(ssids_local)); + + if (request->n_match_sets > 0) { + for (i = 0; i < request->n_match_sets; i++) { + ssid = &request->match_sets[i].ssid; + memcpy(ssids_local[i].SSID, ssid->ssid, ssid->ssid_len); + ssids_local[i].SSID_len = ssid->ssid_len; + WL_PNO((">>> PNO filter set for ssid (%s) \n", ssid->ssid)); + ssid_count++; + } + } + + if (request->n_ssids > 0) { + for (i = 0; i < request->n_ssids; i++) { + /* Active scan req for ssids */ + WL_PNO((">>> Active scan req for ssid (%s) \n", request->ssids[i].ssid)); + + /* match_set ssids is a supert set of n_ssid list, so we need + * not add these set seperately + */ + } + } + + if (ssid_count) { + if ((ret = dhd_dev_pno_set(dev, ssids_local, request->n_match_sets, + pno_time, pno_repeat, pno_freq_expo_max)) < 0) { + WL_ERR(("PNO setup failed!! ret=%d \n", ret)); + return -EINVAL; + } + + /* Enable the PNO */ + if (dhd_dev_pno_enable(dev, 1) < 0) { + WL_ERR(("PNO enable failed!! ret=%d \n", ret)); + return -EINVAL; + } + wl->sched_scan_req = request; + } else { + return -EINVAL; + } + + return 0; +} + +int wl_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + + WL_DBG(("Enter \n")); + WL_PNO((">>> SCHED SCAN STOP\n")); + + if (dhd_dev_pno_enable(dev, 0) < 0) + WL_ERR(("PNO disable failed")); + + if (dhd_dev_pno_reset(dev) < 0) + WL_ERR(("PNO reset failed")); + + if (wl->scan_request && wl->sched_scan_running) { + WL_PNO((">>> Sched scan running. Aborting it..\n")); + wl_notify_escan_complete(wl, dev, true, true); + } + + wl->sched_scan_req = NULL; + wl->sched_scan_running = FALSE; + + return 0; +} +#endif /* WL_SCHED_SCAN */ + +static struct cfg80211_ops wl_cfg80211_ops = { + .add_virtual_intf = wl_cfg80211_add_virtual_iface, + .del_virtual_intf = wl_cfg80211_del_virtual_iface, + .change_virtual_intf = wl_cfg80211_change_virtual_iface, + .scan = wl_cfg80211_scan, + .set_wiphy_params = wl_cfg80211_set_wiphy_params, + .join_ibss = wl_cfg80211_join_ibss, + .leave_ibss = wl_cfg80211_leave_ibss, + .get_station = wl_cfg80211_get_station, + .set_tx_power = wl_cfg80211_set_tx_power, + .get_tx_power = wl_cfg80211_get_tx_power, + .add_key = wl_cfg80211_add_key, + .del_key = wl_cfg80211_del_key, + .get_key = wl_cfg80211_get_key, + .set_default_key = wl_cfg80211_config_default_key, + .set_default_mgmt_key = wl_cfg80211_config_default_mgmt_key, + .set_power_mgmt = wl_cfg80211_set_power_mgmt, + .connect = wl_cfg80211_connect, + .disconnect = wl_cfg80211_disconnect, + .suspend = wl_cfg80211_suspend, + .resume = wl_cfg80211_resume, + .set_pmksa = wl_cfg80211_set_pmksa, + .del_pmksa = wl_cfg80211_del_pmksa, + .flush_pmksa = wl_cfg80211_flush_pmksa, + .remain_on_channel = wl_cfg80211_remain_on_channel, + .cancel_remain_on_channel = wl_cfg80211_cancel_remain_on_channel, + .mgmt_tx = wl_cfg80211_mgmt_tx, + .mgmt_frame_register = wl_cfg80211_mgmt_frame_register, + .change_bss = wl_cfg80211_change_bss, + .set_channel = wl_cfg80211_set_channel, +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) + .set_beacon = wl_cfg80211_add_set_beacon, + .add_beacon = wl_cfg80211_add_set_beacon, +#else + .change_beacon = wl_cfg80211_change_beacon, + .start_ap = wl_cfg80211_start_ap, + .stop_ap = wl_cfg80211_stop_ap, +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) */ +#ifdef WL_SCHED_SCAN + .sched_scan_start = wl_cfg80211_sched_scan_start, + .sched_scan_stop = wl_cfg80211_sched_scan_stop, +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) */ + .del_station = wl_cfg80211_del_station, + .mgmt_tx_cancel_wait = wl_cfg80211_mgmt_tx_cancel_wait, +}; + +s32 wl_mode_to_nl80211_iftype(s32 mode) +{ + s32 err = 0; + + switch (mode) { + case WL_MODE_BSS: + return NL80211_IFTYPE_STATION; + case WL_MODE_IBSS: + return NL80211_IFTYPE_ADHOC; + case WL_MODE_AP: + return NL80211_IFTYPE_AP; + default: + return NL80211_IFTYPE_UNSPECIFIED; + } + + return err; +} + +/* Kernel Network Support->Wireless->Regulatory rules database + options should be enabled and regulatory CRDA regdb table populated in Kernel + for proper country reg notification +*/ +#ifdef CONFIG_CFG80211_INTERNAL_REGDB +static int +wl_cfg80211_reg_notifier( + struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct wl_priv *wl = (struct wl_priv *)wiphy_priv(wiphy); + int ret = 0; + + if (!request || !wl) { + WL_ERR(("Invalid arg\n")); + return -EINVAL; + } + + WL_DBG(("ccode: %c%c Initiator: %d\n", + request->alpha2[0], request->alpha2[1], request->initiator)); + + /* We support only REGDOM_SET_BY_USER and by + NL80211_REGDOM_SET_BY_COUNTRY_IE (11d) right now + */ + if ((request->initiator != NL80211_REGDOM_SET_BY_USER) && + (request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE)) { + WL_ERR(("reg_notifier for intiator:%d not supported : set default\n", + request->initiator)); + /* in case of no supported country by regdb + lets driver setup platform default Locale + */ + } + + WL_ERR(("Set country code %c%c from %s\n", + request->alpha2[0], request->alpha2[1], + ((request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) ? " 11d AP" : "User"))); + + if ((ret = wldev_set_country(wl_to_prmry_ndev(wl), request->alpha2, + false, (request->initiator == NL80211_REGDOM_SET_BY_USER ? true : false))) < 0) { + WL_ERR(("set country Failed :%d\n", ret)); + } + + return ret; +} +#endif /* CONFIG_CFG80211_INTERNAL_REGDB */ + +static s32 wl_setup_wiphy(struct wireless_dev *wdev, struct device *sdiofunc_dev) +{ + s32 err = 0; + wdev->wiphy = + wiphy_new(&wl_cfg80211_ops, sizeof(struct wl_priv)); + if (unlikely(!wdev->wiphy)) { + WL_ERR(("Couldn not allocate wiphy device\n")); + err = -ENOMEM; + return err; + } + set_wiphy_dev(wdev->wiphy, sdiofunc_dev); + wdev->wiphy->max_scan_ie_len = WL_SCAN_IE_LEN_MAX; + /* Report how many SSIDs Driver can support per Scan request */ + wdev->wiphy->max_scan_ssids = WL_SCAN_PARAMS_SSID_MAX; + wdev->wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX; +#ifdef WL_SCHED_SCAN + wdev->wiphy->max_sched_scan_ssids = MAX_PFN_LIST_COUNT; + wdev->wiphy->max_match_sets = MAX_PFN_LIST_COUNT; + wdev->wiphy->max_sched_scan_ie_len = WL_SCAN_IE_LEN_MAX; + wdev->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; +#endif /* WL_SCHED_SCAN */ + wdev->wiphy->interface_modes = + BIT(NL80211_IFTYPE_STATION) +#if !(defined(WLP2P) && defined(WL_ENABLE_P2P_IF)) + | BIT(NL80211_IFTYPE_MONITOR) +#endif + | BIT(NL80211_IFTYPE_AP); + + wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; + + wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + wdev->wiphy->cipher_suites = __wl_cipher_suites; + wdev->wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites); + wdev->wiphy->max_remain_on_channel_duration = 5000; + wdev->wiphy->mgmt_stypes = wl_cfg80211_default_mgmt_stypes; +#ifndef WL_POWERSAVE_DISABLED + wdev->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; +#else + wdev->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; +#endif /* !WL_POWERSAVE_DISABLED */ + wdev->wiphy->flags |= WIPHY_FLAG_NETNS_OK | + WIPHY_FLAG_4ADDR_AP | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39) + WIPHY_FLAG_SUPPORTS_SEPARATE_DEFAULT_KEYS | +#endif + WIPHY_FLAG_4ADDR_STATION; + /* If driver advertises FW_ROAM, the supplicant wouldn't + * send the BSSID & Freq in the connect command allowing the + * the driver to choose the AP to connect to. But unless we + * support ROAM_CACHE in firware this will delay the ASSOC as + * as the FW need to do a full scan before attempting to connect + * So that feature will just increase assoc. The better approach + * to let Supplicant to provide channel info and FW letter may roam + * if needed so DON'T advertise that featur eto Supplicant. + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + /* wdev->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; */ +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) + wdev->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | + WIPHY_FLAG_OFFCHAN_TX; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + /* From 3.4 kernel ownards AP_SME flag can be advertised + * to remove the patch from supplicant + */ + wdev->wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME; +#endif + +#ifdef CONFIG_CFG80211_INTERNAL_REGDB + wdev->wiphy->reg_notifier = wl_cfg80211_reg_notifier; +#endif /* CONFIG_CFG80211_INTERNAL_REGDB */ + + WL_DBG(("Registering custom regulatory)\n")); + wdev->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; + wiphy_apply_custom_regulatory(wdev->wiphy, &brcm_regdom); + /* Now we can register wiphy with cfg80211 module */ + err = wiphy_register(wdev->wiphy); + if (unlikely(err < 0)) { + WL_ERR(("Couldn not register wiphy device (%d)\n", err)); + wiphy_free(wdev->wiphy); + } + return err; +} + +static void wl_free_wdev(struct wl_priv *wl) +{ + struct wireless_dev *wdev = wl->wdev; + struct wiphy *wiphy; + if (!wdev) { + WL_ERR(("wdev is invalid\n")); + return; + } + wiphy = wdev->wiphy; + wiphy_unregister(wdev->wiphy); + wdev->wiphy->dev.parent = NULL; + + wl_delete_all_netinfo(wl); + wiphy_free(wiphy); + /* PLEASE do NOT call any function after wiphy_free, the driver's private structure "wl", + * which is the private part of wiphy, has been freed in wiphy_free !!!!!!!!!!! + */ +} + +static s32 wl_inform_bss(struct wl_priv *wl) +{ + struct wl_scan_results *bss_list; + struct wl_bss_info *bi = NULL; /* must be initialized */ + s32 err = 0; + s32 i; + + bss_list = wl->bss_list; + WL_DBG(("scanned AP count (%d)\n", bss_list->count)); + bi = next_bss(bss_list, bi); + for_each_bss(bss_list, bi, i) { + err = wl_inform_single_bss(wl, bi, 0); + if (unlikely(err)) + break; + } + return err; +} + +static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi, u8 is_roam_done) +{ + struct wiphy *wiphy = wl_to_wiphy(wl); + struct ieee80211_mgmt *mgmt; + struct ieee80211_channel *channel; + struct ieee80211_supported_band *band; + struct wl_cfg80211_bss_info *notif_bss_info; + struct wl_scan_req *sr = wl_to_sr(wl); + struct beacon_proberesp *beacon_proberesp; + struct cfg80211_bss *cbss = NULL; + s32 mgmt_type; + s32 signal; + u32 freq; + s32 err = 0; + gfp_t aflags; + u8 *ie_offset = NULL; + + if (unlikely(dtoh32(bi->length) > WL_BSS_INFO_MAX)) { + WL_DBG(("Beacon is larger than buffer. Discarding\n")); + return err; + } + aflags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; + notif_bss_info = kzalloc(sizeof(*notif_bss_info) + sizeof(*mgmt) + - sizeof(u8) + WL_BSS_INFO_MAX, aflags); + if (unlikely(!notif_bss_info)) { + WL_ERR(("notif_bss_info alloc failed\n")); + return -ENOMEM; + } + mgmt = (struct ieee80211_mgmt *)notif_bss_info->frame_buf; + notif_bss_info->channel = + bi->ctl_ch ? bi->ctl_ch : CHSPEC_CHANNEL(wl_chspec_driver_to_host(bi->chanspec)); + + if (notif_bss_info->channel <= CH_MAX_2G_CHANNEL) + band = wiphy->bands[IEEE80211_BAND_2GHZ]; + else + band = wiphy->bands[IEEE80211_BAND_5GHZ]; + if (!band) { + WL_ERR(("No valid band")); + kfree(notif_bss_info); + return -EINVAL; + } + notif_bss_info->rssi = dtoh16(bi->RSSI) + RSSI_OFFSET; + memcpy(mgmt->bssid, &bi->BSSID, ETHER_ADDR_LEN); + mgmt_type = wl->active_scan ? + IEEE80211_STYPE_PROBE_RESP : IEEE80211_STYPE_BEACON; + if (!memcmp(bi->SSID, sr->ssid.SSID, bi->SSID_len)) { + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | mgmt_type); + } + beacon_proberesp = wl->active_scan ? + (struct beacon_proberesp *)&mgmt->u.probe_resp : + (struct beacon_proberesp *)&mgmt->u.beacon; + beacon_proberesp->timestamp = 0; + beacon_proberesp->beacon_int = cpu_to_le16(bi->beacon_period); + beacon_proberesp->capab_info = cpu_to_le16(bi->capability); + wl_rst_ie(wl); + + ie_offset = ((u8 *) bi) + bi->ie_offset; + + if (is_roam_done && ((int)(*(ie_offset)) == WLAN_EID_SSID && + ((int)(*(ie_offset+1)) == 0 || (int)(*(ie_offset+2)) == 0))) { + u8 *ie_new_offset = NULL; + uint8 ie_new_length; + + WL_ERR(("WAR trace: Changing the SSID Info, from beacon %d\n", + bi->flags & WL_BSS_FLAGS_FROM_BEACON)); + + ie_new_offset = (u8 *)kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); + if (ie_new_offset) { + *(ie_new_offset) = WLAN_EID_SSID; + *(ie_new_offset+1) = bi->SSID_len; + memcpy(ie_new_offset+2, bi->SSID, bi->SSID_len); + ie_new_length = bi->ie_length - *(ie_offset+1) + bi->SSID_len; + + /* Copy the remaining IE apart from SSID IE from bi */ + memcpy(ie_new_offset+2 + bi->SSID_len, + ie_offset+2 + *(ie_offset+1), + bi->ie_length - 2 - *(ie_offset+1)); + wl_mrg_ie(wl, ie_new_offset, ie_new_length); + kfree(ie_new_offset); + } else { + wl_mrg_ie(wl, ((u8 *) bi) + bi->ie_offset, bi->ie_length); + } + } else { + wl_mrg_ie(wl, ((u8 *) bi) + bi->ie_offset, bi->ie_length); + } + + wl_cp_ie(wl, beacon_proberesp->variable, WL_BSS_INFO_MAX - + offsetof(struct wl_cfg80211_bss_info, frame_buf)); + notif_bss_info->frame_len = offsetof(struct ieee80211_mgmt, + u.beacon.variable) + wl_get_ielen(wl); +#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) + freq = ieee80211_channel_to_frequency(notif_bss_info->channel); + (void)band->band; +#else + freq = ieee80211_channel_to_frequency(notif_bss_info->channel, band->band); +#endif + if (freq == 0) { + WL_ERR(("Invalid channel, fail to chcnage channel to freq\n")); + kfree(notif_bss_info); + return -EINVAL; + } + channel = ieee80211_get_channel(wiphy, freq); + if (unlikely(!channel)) { + WL_ERR(("ieee80211_get_channel error\n")); + kfree(notif_bss_info); + return -EINVAL; + } + WL_DBG(("SSID : \"%s\", rssi %d, channel %d, capability : 0x04%x, bssid %pM" + "mgmt_type %d frame_len %d\n", bi->SSID, + notif_bss_info->rssi, notif_bss_info->channel, + mgmt->u.beacon.capab_info, &bi->BSSID, mgmt_type, + notif_bss_info->frame_len)); + + signal = notif_bss_info->rssi * 100; + if (!mgmt->u.probe_resp.timestamp) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + struct timespec ts; + get_monotonic_boottime(&ts); + mgmt->u.probe_resp.timestamp = ((u64)ts.tv_sec*1000000) + + ts.tv_nsec / 1000; +#else + struct timeval tv; + do_gettimeofday(&tv); + mgmt->u.probe_resp.timestamp = ((u64)tv.tv_sec*1000000) + + tv.tv_usec; +#endif + } + + cbss = cfg80211_inform_bss_frame(wiphy, channel, mgmt, + le16_to_cpu(notif_bss_info->frame_len), signal, aflags); + if (unlikely(!cbss)) { + WL_ERR(("cfg80211_inform_bss_frame error\n")); + kfree(notif_bss_info); + return -EINVAL; + } + + cfg80211_put_bss(cbss); + kfree(notif_bss_info); + return err; +} + +static bool wl_is_linkup(struct wl_priv *wl, const wl_event_msg_t *e, struct net_device *ndev) +{ + u32 event = ntoh32(e->event_type); + u32 status = ntoh32(e->status); + u16 flags = ntoh16(e->flags); + + WL_DBG(("event %d, status %d flags %x\n", event, status, flags)); + if (event == WLC_E_SET_SSID) { + if (status == WLC_E_STATUS_SUCCESS) { + if (!wl_is_ibssmode(wl, ndev)) + return true; + } + } else if (event == WLC_E_LINK) { + if (flags & WLC_EVENT_MSG_LINK) + return true; + } + + WL_DBG(("wl_is_linkup false\n")); + return false; +} + +static bool wl_is_linkdown(struct wl_priv *wl, const wl_event_msg_t *e) +{ + u32 event = ntoh32(e->event_type); + u16 flags = ntoh16(e->flags); + + if (event == WLC_E_DEAUTH_IND || + event == WLC_E_DISASSOC_IND || + event == WLC_E_DISASSOC || + event == WLC_E_DEAUTH) { +#if (WL_DBG_LEVEL > 0) + WL_ERR(("Link down Reason : WLC_E_%s\n", wl_dbg_estr[event])); +#endif /* (WL_DBG_LEVEL > 0) */ + return true; + } else if (event == WLC_E_LINK) { + if (!(flags & WLC_EVENT_MSG_LINK)) { +#if (WL_DBG_LEVEL > 0) + WL_ERR(("Link down Reason : WLC_E_%s\n", wl_dbg_estr[event])); +#endif /* (WL_DBG_LEVEL > 0) */ + return true; + } + } + + return false; +} + +static bool wl_is_nonetwork(struct wl_priv *wl, const wl_event_msg_t *e) +{ + u32 event = ntoh32(e->event_type); + u32 status = ntoh32(e->status); + + if (event == WLC_E_LINK && status == WLC_E_STATUS_NO_NETWORKS) + return true; + if (event == WLC_E_SET_SSID && status != WLC_E_STATUS_SUCCESS) + return true; + + return false; +} + +/* The mainline kernel >= 3.2.0 has support for indicating new/del station + * to AP/P2P GO via events. If this change is backported to kernel for which + * this driver is being built, then define WL_CFG80211_STA_EVENT. You + * should use this new/del sta event mechanism for BRCM supplicant >= 22. + */ +static s32 +wl_notify_connect_status_ap(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + s32 err = 0; + u32 event = ntoh32(e->event_type); + u32 reason = ntoh32(e->reason); + u32 len = ntoh32(e->datalen); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) && !defined(WL_CFG80211_STA_EVENT) + bool isfree = false; + u8 *mgmt_frame; + u8 bsscfgidx = e->bsscfgidx; + s32 freq; + s32 channel; + u8 *body = NULL; + u16 fc = 0; + + struct ieee80211_supported_band *band; + struct ether_addr da; + struct ether_addr bssid; + struct wiphy *wiphy = wl_to_wiphy(wl); + channel_info_t ci; +#else + struct station_info sinfo; +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) && !WL_CFG80211_STA_EVENT */ + + WL_DBG(("event %d status %d reason %d\n", event, ntoh32(e->status), reason)); + /* if link down, bsscfg is disabled. */ + if (event == WLC_E_LINK && reason == WLC_E_LINK_BSSCFG_DIS && + wl_get_p2p_status(wl, IF_DELETING) && (ndev != wl_to_prmry_ndev(wl))) { + wl_add_remove_eventmsg(ndev, WLC_E_PROBREQ_MSG, false); + WL_INFO(("AP mode link down !! \n")); + complete(&wl->iface_disable); + return 0; + } + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) && !defined(WL_CFG80211_STA_EVENT) + WL_DBG(("Enter \n")); + if (!len && (event == WLC_E_DEAUTH)) { + len = 2; /* reason code field */ + data = &reason; + } + if (len) { + body = kzalloc(len, GFP_KERNEL); + + if (body == NULL) { + WL_ERR(("wl_notify_connect_status: Failed to allocate body\n")); + return WL_INVALID; + } + } + memset(&bssid, 0, ETHER_ADDR_LEN); + WL_DBG(("Enter event %d ndev %p\n", event, ndev)); + if (wl_get_mode_by_netdev(wl, ndev) == WL_INVALID) { + kfree(body); + return WL_INVALID; + } + if (len) + memcpy(body, data, len); + + wldev_iovar_getbuf_bsscfg(ndev, "cur_etheraddr", + NULL, 0, wl->ioctl_buf, WLC_IOCTL_SMLEN, bsscfgidx, &wl->ioctl_buf_sync); + memcpy(da.octet, wl->ioctl_buf, ETHER_ADDR_LEN); + err = wldev_ioctl(ndev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, false); + switch (event) { + case WLC_E_ASSOC_IND: + fc = FC_ASSOC_REQ; + break; + case WLC_E_REASSOC_IND: + fc = FC_REASSOC_REQ; + break; + case WLC_E_DISASSOC_IND: + fc = FC_DISASSOC; + break; + case WLC_E_DEAUTH_IND: + fc = FC_DISASSOC; + break; + case WLC_E_DEAUTH: + fc = FC_DISASSOC; + break; + default: + fc = 0; + goto exit; + } + if ((err = wldev_ioctl(ndev, WLC_GET_CHANNEL, &ci, sizeof(ci), false))) { + kfree(body); + return err; + } + + channel = dtoh32(ci.hw_channel); + if (channel <= CH_MAX_2G_CHANNEL) + band = wiphy->bands[IEEE80211_BAND_2GHZ]; + else + band = wiphy->bands[IEEE80211_BAND_5GHZ]; + if (!band) { + WL_ERR(("No valid band")); + if (body) + kfree(body); + return -EINVAL; + } +#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) + freq = ieee80211_channel_to_frequency(channel); + (void)band->band; +#else + freq = ieee80211_channel_to_frequency(channel, band->band); +#endif + + err = wl_frame_get_mgmt(fc, &da, &e->addr, &bssid, + &mgmt_frame, &len, body); + if (err < 0) + goto exit; + isfree = true; + + if (event == WLC_E_ASSOC_IND && reason == DOT11_SC_SUCCESS) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + cfg80211_rx_mgmt(ndev, freq, 0, mgmt_frame, len, GFP_ATOMIC); +#else + cfg80211_rx_mgmt(ndev, freq, mgmt_frame, len, GFP_ATOMIC); +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) */ + } else if (event == WLC_E_DISASSOC_IND) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + cfg80211_rx_mgmt(ndev, freq, 0, mgmt_frame, len, GFP_ATOMIC); +#else + cfg80211_rx_mgmt(ndev, freq, mgmt_frame, len, GFP_ATOMIC); +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) */ + } else if ((event == WLC_E_DEAUTH_IND) || (event == WLC_E_DEAUTH)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + cfg80211_rx_mgmt(ndev, freq, 0, mgmt_frame, len, GFP_ATOMIC); +#else + cfg80211_rx_mgmt(ndev, freq, mgmt_frame, len, GFP_ATOMIC); +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) */ + } + +exit: + if (isfree) + kfree(mgmt_frame); + if (body) + kfree(body); + return err; +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) && !WL_CFG80211_STA_EVENT */ + sinfo.filled = 0; + if (((event == WLC_E_ASSOC_IND) || (event == WLC_E_REASSOC_IND)) && + reason == DOT11_SC_SUCCESS) { + sinfo.filled = STATION_INFO_ASSOC_REQ_IES; + if (!data) { + WL_ERR(("No IEs present in ASSOC/REASSOC_IND")); + return -EINVAL; + } + sinfo.assoc_req_ies = data; + sinfo.assoc_req_ies_len = len; + cfg80211_new_sta(ndev, e->addr.octet, &sinfo, GFP_ATOMIC); + } else if (event == WLC_E_DISASSOC_IND) { + cfg80211_del_sta(ndev, e->addr.octet, GFP_ATOMIC); + } else if ((event == WLC_E_DEAUTH_IND) || (event == WLC_E_DEAUTH)) { + cfg80211_del_sta(ndev, e->addr.octet, GFP_ATOMIC); + } +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) && !WL_CFG80211_STA_EVENT */ + return err; +} + +static s32 +wl_get_auth_assoc_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e) +{ + u32 reason = ntoh32(e->reason); + u32 event = ntoh32(e->event_type); + struct wl_security *sec = wl_read_prof(wl, ndev, WL_PROF_SEC); + WL_DBG(("event type : %d, reason : %d\n", event, reason)); + if (sec) { + switch (event) { + case WLC_E_ASSOC: + case WLC_E_AUTH: + sec->auth_assoc_res_status = reason; + default: + break; + } + } else + WL_ERR(("sec is NULL\n")); + return 0; +} + +static s32 +wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + bool act; + s32 err = 0; + u32 event = ntoh32(e->event_type); + + if (wl_get_mode_by_netdev(wl, ndev) == WL_MODE_AP) { + wl_notify_connect_status_ap(wl, ndev, e, data); + } else { + WL_DBG(("wl_notify_connect_status : event %d status : %d ndev %p\n", + ntoh32(e->event_type), ntoh32(e->status), ndev)); + if (event == WLC_E_ASSOC || event == WLC_E_AUTH) { + wl_get_auth_assoc_status(wl, ndev, e); + return err; + } + if (wl_is_linkup(wl, e, ndev)) { + wl_link_up(wl); + act = true; + if (wl_is_ibssmode(wl, ndev)) { + printk("cfg80211_ibss_joined\n"); + cfg80211_ibss_joined(ndev, (s8 *)&e->addr, + GFP_KERNEL); + WL_DBG(("joined in IBSS network\n")); + } else { + if (!wl_get_drv_status(wl, DISCONNECTING, ndev)) { + printk("wl_bss_connect_done succeeded with " MACDBG "\n", + MAC2STRDBG((u8*)(&e->addr))); + wl_bss_connect_done(wl, ndev, e, data, true); + WL_DBG(("joined in BSS network \"%s\"\n", + ((struct wlc_ssid *) + wl_read_prof(wl, ndev, WL_PROF_SSID))->SSID)); + } + } + wl_update_prof(wl, ndev, e, &act, WL_PROF_ACT); + wl_update_prof(wl, ndev, NULL, (void *)&e->addr, WL_PROF_BSSID); + + } else if (wl_is_linkdown(wl, e)) { + if (wl->scan_request) { + if (wl->escan_on) { + wl_notify_escan_complete(wl, ndev, true, true); + } else { + del_timer_sync(&wl->scan_timeout); + wl_iscan_aborted(wl); + } + } + if (wl_get_drv_status(wl, CONNECTED, ndev)) { + scb_val_t scbval; + u8 *curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); + s32 reason = 0; + if (event == WLC_E_DEAUTH_IND || event == WLC_E_DISASSOC_IND) + reason = ntoh32(e->reason); + /* WLAN_REASON_UNSPECIFIED is used for hang up event in Android */ + reason = (reason == WLAN_REASON_UNSPECIFIED)? 0 : reason; + + printk("link down if %s may call cfg80211_disconnected. " + "event : %d, reason=%d from " MACDBG "\n", + ndev->name, event, ntoh32(e->reason), + MAC2STRDBG((u8*)(&e->addr))); + if (memcmp(curbssid, &e->addr, ETHER_ADDR_LEN) != 0) { + WL_ERR(("BSSID of event is not the connected BSSID" + "(ignore it) cur: " MACDBG " event: " MACDBG"\n", + MAC2STRDBG(curbssid), MAC2STRDBG((u8*)(&e->addr)))); + return 0; + } + wl_clr_drv_status(wl, CONNECTED, ndev); + if (! wl_get_drv_status(wl, DISCONNECTING, ndev)) { + /* To make sure disconnect, explictly send dissassoc + * for BSSID 00:00:00:00:00:00 issue + */ + scbval.val = WLAN_REASON_DEAUTH_LEAVING; + + memcpy(&scbval.ea, curbssid, ETHER_ADDR_LEN); + scbval.val = htod32(scbval.val); + err = wldev_ioctl(ndev, WLC_DISASSOC, &scbval, + sizeof(scb_val_t), true); + if (err < 0) { + WL_ERR(("WLC_DISASSOC error %d\n", err)); + err = 0; + } + cfg80211_disconnected(ndev, reason, NULL, 0, GFP_KERNEL); + wl_link_down(wl); + wl_init_prof(wl, ndev); + } + } + else if (wl_get_drv_status(wl, CONNECTING, ndev)) { + printk("link down, during connecting\n"); +#ifdef ESCAN_RESULT_PATCH + if ((memcmp(connect_req_bssid, broad_bssid, ETHER_ADDR_LEN) == 0) || + (memcmp(&e->addr, broad_bssid, ETHER_ADDR_LEN) == 0) || + (memcmp(&e->addr, connect_req_bssid, ETHER_ADDR_LEN) == 0)) + /* In case this event comes while associating another AP */ +#endif /* ESCAN_RESULT_PATCH */ + wl_bss_connect_done(wl, ndev, e, data, false); + } + wl_clr_drv_status(wl, DISCONNECTING, ndev); + + /* if link down, bsscfg is diabled */ + if (ndev != wl_to_prmry_ndev(wl)) + complete(&wl->iface_disable); + + } else if (wl_is_nonetwork(wl, e)) { + printk("connect failed event=%d e->status %d e->reason %d \n", + event, (int)ntoh32(e->status), (int)ntoh32(e->reason)); + /* Clean up any pending scan request */ + if (wl->scan_request) { + if (wl->escan_on) { + wl_notify_escan_complete(wl, ndev, true, true); + } else { + del_timer_sync(&wl->scan_timeout); + wl_iscan_aborted(wl); + } + } + if (wl_get_drv_status(wl, CONNECTING, ndev)) + wl_bss_connect_done(wl, ndev, e, data, false); + } else { + printk("%s nothing\n", __FUNCTION__); + } + } + return err; +} + +static s32 +wl_notify_roaming_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + bool act; + s32 err = 0; + u32 event = be32_to_cpu(e->event_type); + u32 status = be32_to_cpu(e->status); + WL_DBG(("Enter \n")); + if (event == WLC_E_ROAM && status == WLC_E_STATUS_SUCCESS) { + if (wl_get_drv_status(wl, CONNECTED, ndev)) + wl_bss_roaming_done(wl, ndev, e, data); + else + wl_bss_connect_done(wl, ndev, e, data, true); + act = true; + wl_update_prof(wl, ndev, e, &act, WL_PROF_ACT); + wl_update_prof(wl, ndev, NULL, (void *)&e->addr, WL_PROF_BSSID); + } + return err; +} + +static s32 wl_get_assoc_ies(struct wl_priv *wl, struct net_device *ndev) +{ + wl_assoc_info_t assoc_info; + struct wl_connect_info *conn_info = wl_to_conn(wl); + s32 err = 0; + + WL_DBG(("Enter \n")); + err = wldev_iovar_getbuf(ndev, "assoc_info", NULL, 0, wl->extra_buf, + WL_ASSOC_INFO_MAX, NULL); + if (unlikely(err)) { + WL_ERR(("could not get assoc info (%d)\n", err)); + return err; + } + memcpy(&assoc_info, wl->extra_buf, sizeof(wl_assoc_info_t)); + assoc_info.req_len = htod32(assoc_info.req_len); + assoc_info.resp_len = htod32(assoc_info.resp_len); + assoc_info.flags = htod32(assoc_info.flags); + if (conn_info->req_ie_len) { + conn_info->req_ie_len = 0; + bzero(conn_info->req_ie, sizeof(conn_info->req_ie)); + } + if (conn_info->resp_ie_len) { + conn_info->resp_ie_len = 0; + bzero(conn_info->resp_ie, sizeof(conn_info->resp_ie)); + } + if (assoc_info.req_len) { + err = wldev_iovar_getbuf(ndev, "assoc_req_ies", NULL, 0, wl->extra_buf, + WL_ASSOC_INFO_MAX, NULL); + if (unlikely(err)) { + WL_ERR(("could not get assoc req (%d)\n", err)); + return err; + } + conn_info->req_ie_len = assoc_info.req_len - sizeof(struct dot11_assoc_req); + if (assoc_info.flags & WLC_ASSOC_REQ_IS_REASSOC) { + conn_info->req_ie_len -= ETHER_ADDR_LEN; + } + if (conn_info->req_ie_len <= MAX_REQ_LINE) + memcpy(conn_info->req_ie, wl->extra_buf, conn_info->req_ie_len); + else { + WL_ERR(("%s IE size %d above max %d size \n", + __FUNCTION__, conn_info->req_ie_len, MAX_REQ_LINE)); + return err; + } + } else { + conn_info->req_ie_len = 0; + } + if (assoc_info.resp_len) { + err = wldev_iovar_getbuf(ndev, "assoc_resp_ies", NULL, 0, wl->extra_buf, + WL_ASSOC_INFO_MAX, NULL); + if (unlikely(err)) { + WL_ERR(("could not get assoc resp (%d)\n", err)); + return err; + } + conn_info->resp_ie_len = assoc_info.resp_len -sizeof(struct dot11_assoc_resp); + if (conn_info->resp_ie_len <= MAX_REQ_LINE) + memcpy(conn_info->resp_ie, wl->extra_buf, conn_info->resp_ie_len); + else { + WL_ERR(("%s IE size %d above max %d size \n", + __FUNCTION__, conn_info->resp_ie_len, MAX_REQ_LINE)); + return err; + } + } else { + conn_info->resp_ie_len = 0; + } + WL_DBG(("req len (%d) resp len (%d)\n", conn_info->req_ie_len, + conn_info->resp_ie_len)); + + return err; +} + +static void wl_ch_to_chanspec(int ch, struct wl_join_params *join_params, + size_t *join_params_size) +{ + chanspec_t chanspec = 0; + if (ch != 0) { + join_params->params.chanspec_num = 1; + join_params->params.chanspec_list[0] = ch; + + if (join_params->params.chanspec_list[0] <= CH_MAX_2G_CHANNEL) + chanspec |= WL_CHANSPEC_BAND_2G; + else + chanspec |= WL_CHANSPEC_BAND_5G; + + chanspec |= WL_CHANSPEC_BW_20; + chanspec |= WL_CHANSPEC_CTL_SB_NONE; + + *join_params_size += WL_ASSOC_PARAMS_FIXED_SIZE + + join_params->params.chanspec_num * sizeof(chanspec_t); + + join_params->params.chanspec_list[0] &= WL_CHANSPEC_CHAN_MASK; + join_params->params.chanspec_list[0] |= chanspec; + join_params->params.chanspec_list[0] = + wl_chspec_host_to_driver(join_params->params.chanspec_list[0]); + + join_params->params.chanspec_num = + htod32(join_params->params.chanspec_num); + WL_DBG(("join_params->params.chanspec_list[0]= %X, %d channels\n", + join_params->params.chanspec_list[0], + join_params->params.chanspec_num)); + } +} + +static s32 wl_update_bss_info(struct wl_priv *wl, struct net_device *ndev, u8 is_roam_done) +{ + struct cfg80211_bss *bss; + struct wl_bss_info *bi; + struct wlc_ssid *ssid; + struct bcm_tlv *tim; + s32 beacon_interval; + s32 dtim_period; + size_t ie_len; + u8 *ie; + u8 *ssidie; + u8 *curbssid; + s32 err = 0; + struct wiphy *wiphy; + + wiphy = wl_to_wiphy(wl); + + if (wl_is_ibssmode(wl, ndev)) + return err; + + ssid = (struct wlc_ssid *)wl_read_prof(wl, ndev, WL_PROF_SSID); + curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); + bss = cfg80211_get_bss(wiphy, NULL, curbssid, + ssid->SSID, ssid->SSID_len, WLAN_CAPABILITY_ESS, + WLAN_CAPABILITY_ESS); + + mutex_lock(&wl->usr_sync); + if (!bss) { + WL_DBG(("Could not find the AP\n")); + *(u32 *) wl->extra_buf = htod32(WL_EXTRA_BUF_MAX); + err = wldev_ioctl(ndev, WLC_GET_BSS_INFO, + wl->extra_buf, WL_EXTRA_BUF_MAX, false); + if (unlikely(err)) { + WL_ERR(("Could not get bss info %d\n", err)); + goto update_bss_info_out; + } + bi = (struct wl_bss_info *)(wl->extra_buf + 4); + if (memcmp(bi->BSSID.octet, curbssid, ETHER_ADDR_LEN)) { + err = -EIO; + goto update_bss_info_out; + } + + ie = ((u8 *)bi) + bi->ie_offset; + ie_len = bi->ie_length; + ssidie = (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ie, ie_len); + if (ssidie && ssidie[1] == bi->SSID_len && !ssidie[2] && bi->SSID[0]) + memcpy(ssidie + 2, bi->SSID, bi->SSID_len); + + err = wl_inform_single_bss(wl, bi, is_roam_done); + if (unlikely(err)) + goto update_bss_info_out; + + ie = ((u8 *)bi) + bi->ie_offset; + ie_len = bi->ie_length; + beacon_interval = cpu_to_le16(bi->beacon_period); + } else { + WL_DBG(("Found the AP in the list - BSSID %pM\n", bss->bssid)); + ie = bss->information_elements; + ie_len = bss->len_information_elements; + beacon_interval = bss->beacon_interval; + cfg80211_put_bss(bss); + } + + tim = bcm_parse_tlvs(ie, ie_len, WLAN_EID_TIM); + if (tim) { + dtim_period = tim->data[1]; + } else { + /* + * active scan was done so we could not get dtim + * information out of probe response. + * so we speficially query dtim information. + */ + err = wldev_ioctl(ndev, WLC_GET_DTIMPRD, + &dtim_period, sizeof(dtim_period), false); + if (unlikely(err)) { + WL_ERR(("WLC_GET_DTIMPRD error (%d)\n", err)); + goto update_bss_info_out; + } + } + + wl_update_prof(wl, ndev, NULL, &beacon_interval, WL_PROF_BEACONINT); + wl_update_prof(wl, ndev, NULL, &dtim_period, WL_PROF_DTIMPERIOD); + +update_bss_info_out: + mutex_unlock(&wl->usr_sync); + return err; +} + +static s32 +wl_bss_roaming_done(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + struct wl_connect_info *conn_info = wl_to_conn(wl); + s32 err = 0; + u8 *curbssid; + + wl_get_assoc_ies(wl, ndev); + wl_update_prof(wl, ndev, NULL, (void *)(e->addr.octet), WL_PROF_BSSID); + curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); + wl_update_bss_info(wl, ndev, 1); + wl_update_pmklist(ndev, wl->pmk_list, err); + printk("wl_bss_roaming_done succeeded to " MACDBG "\n", + MAC2STRDBG((u8*)(&e->addr))); + + cfg80211_roamed(ndev, + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) + NULL, /* struct cfg80211_bss *bss */ +#elif LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39) + NULL, +#endif + curbssid, + conn_info->req_ie, conn_info->req_ie_len, + conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL); + WL_DBG(("Report roaming result\n")); + + wl_set_drv_status(wl, CONNECTED, ndev); + + return err; +} + +static s32 +wl_bss_connect_done(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data, bool completed) +{ + struct wl_connect_info *conn_info = wl_to_conn(wl); + struct wl_security *sec = wl_read_prof(wl, ndev, WL_PROF_SEC); + s32 err = 0; + u8 *curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); + if (!sec) { + WL_ERR(("sec is NULL\n")); + return -ENODEV; + } + WL_DBG((" enter\n")); +#ifdef ESCAN_RESULT_PATCH + if (wl_get_drv_status(wl, CONNECTED, ndev)) { + if (memcmp(curbssid, connect_req_bssid, ETHER_ADDR_LEN) == 0) { + WL_DBG((" Connected event of connected device e=%d s=%d, ignore it\n", + ntoh32(e->event_type), ntoh32(e->status))); + return err; + } + } + if (memcmp(curbssid, broad_bssid, ETHER_ADDR_LEN) == 0 && + memcmp(broad_bssid, connect_req_bssid, ETHER_ADDR_LEN) != 0) { + WL_DBG(("copy bssid\n")); + memcpy(curbssid, connect_req_bssid, ETHER_ADDR_LEN); + } + +#else + if (wl->scan_request) { + wl_notify_escan_complete(wl, ndev, true, true); + } +#endif /* ESCAN_RESULT_PATCH */ + if (wl_get_drv_status(wl, CONNECTING, ndev)) { + wl_clr_drv_status(wl, CONNECTING, ndev); + if (completed) { + wl_get_assoc_ies(wl, ndev); + wl_update_prof(wl, ndev, NULL, (void *)(e->addr.octet), WL_PROF_BSSID); + curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); + wl_update_bss_info(wl, ndev, 0); + wl_update_pmklist(ndev, wl->pmk_list, err); + wl_set_drv_status(wl, CONNECTED, ndev); + } + cfg80211_connect_result(ndev, + curbssid, + conn_info->req_ie, + conn_info->req_ie_len, + conn_info->resp_ie, + conn_info->resp_ie_len, + completed ? WLAN_STATUS_SUCCESS : + (sec->auth_assoc_res_status) ? + sec->auth_assoc_res_status : + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); + if (completed) + WL_INFO(("Report connect result - connection succeeded\n")); + else + WL_ERR(("Report connect result - connection failed\n")); + } + return err; +} + +static s32 +wl_notify_mic_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + u16 flags = ntoh16(e->flags); + enum nl80211_key_type key_type; + + mutex_lock(&wl->usr_sync); + if (flags & WLC_EVENT_MSG_GROUP) + key_type = NL80211_KEYTYPE_GROUP; + else + key_type = NL80211_KEYTYPE_PAIRWISE; + + cfg80211_michael_mic_failure(ndev, (u8 *)&e->addr, key_type, -1, + NULL, GFP_KERNEL); + mutex_unlock(&wl->usr_sync); + + return 0; +} + +#ifdef PNO_SUPPORT +static s32 +wl_notify_pfn_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + WL_ERR((">>> PNO Event\n")); + +#ifndef WL_SCHED_SCAN + mutex_lock(&wl->usr_sync); + /* TODO: Use cfg80211_sched_scan_results(wiphy); */ + cfg80211_disconnected(ndev, 0, NULL, 0, GFP_KERNEL); + mutex_unlock(&wl->usr_sync); +#else + /* If cfg80211 scheduled scan is supported, report the pno results via sched + * scan results + */ + wl_notify_sched_scan_results(wl, ndev, e, data); +#endif /* WL_SCHED_SCAN */ + return 0; +} +#endif /* PNO_SUPPORT */ + +static s32 +wl_notify_scan_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + struct channel_info channel_inform; + struct wl_scan_results *bss_list; + u32 len = WL_SCAN_BUF_MAX; + s32 err = 0; + unsigned long flags; + + WL_DBG(("Enter \n")); + if (!wl_get_drv_status(wl, SCANNING, ndev)) { + WL_ERR(("scan is not ready \n")); + return err; + } + if (wl->iscan_on && wl->iscan_kickstart) + return wl_wakeup_iscan(wl_to_iscan(wl)); + + mutex_lock(&wl->usr_sync); + wl_clr_drv_status(wl, SCANNING, ndev); + err = wldev_ioctl(ndev, WLC_GET_CHANNEL, &channel_inform, + sizeof(channel_inform), false); + if (unlikely(err)) { + WL_ERR(("scan busy (%d)\n", err)); + goto scan_done_out; + } + channel_inform.scan_channel = dtoh32(channel_inform.scan_channel); + if (unlikely(channel_inform.scan_channel)) { + + WL_DBG(("channel_inform.scan_channel (%d)\n", + channel_inform.scan_channel)); + } + wl->bss_list = wl->scan_results; + bss_list = wl->bss_list; + memset(bss_list, 0, len); + bss_list->buflen = htod32(len); + err = wldev_ioctl(ndev, WLC_SCAN_RESULTS, bss_list, len, false); + if (unlikely(err) && unlikely(!wl->scan_suppressed)) { + WL_ERR(("%s Scan_results error (%d)\n", ndev->name, err)); + err = -EINVAL; + goto scan_done_out; + } + bss_list->buflen = dtoh32(bss_list->buflen); + bss_list->version = dtoh32(bss_list->version); + bss_list->count = dtoh32(bss_list->count); + + err = wl_inform_bss(wl); + +scan_done_out: + del_timer_sync(&wl->scan_timeout); + spin_lock_irqsave(&wl->cfgdrv_lock, flags); + if (wl->scan_request) { + cfg80211_scan_done(wl->scan_request, false); + wl->scan_request = NULL; + } + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); + WL_DBG(("cfg80211_scan_done\n")); + mutex_unlock(&wl->usr_sync); + return err; +} +static s32 +wl_frame_get_mgmt(u16 fc, const struct ether_addr *da, + const struct ether_addr *sa, const struct ether_addr *bssid, + u8 **pheader, u32 *body_len, u8 *pbody) +{ + struct dot11_management_header *hdr; + u32 totlen = 0; + s32 err = 0; + u8 *offset; + u32 prebody_len = *body_len; + switch (fc) { + case FC_ASSOC_REQ: + /* capability , listen interval */ + totlen = DOT11_ASSOC_REQ_FIXED_LEN; + *body_len += DOT11_ASSOC_REQ_FIXED_LEN; + break; + + case FC_REASSOC_REQ: + /* capability, listen inteval, ap address */ + totlen = DOT11_REASSOC_REQ_FIXED_LEN; + *body_len += DOT11_REASSOC_REQ_FIXED_LEN; + break; + } + totlen += DOT11_MGMT_HDR_LEN + prebody_len; + *pheader = kzalloc(totlen, GFP_KERNEL); + if (*pheader == NULL) { + WL_ERR(("memory alloc failed \n")); + return -ENOMEM; + } + hdr = (struct dot11_management_header *) (*pheader); + hdr->fc = htol16(fc); + hdr->durid = 0; + hdr->seq = 0; + offset = (u8*)(hdr + 1) + (totlen - DOT11_MGMT_HDR_LEN - prebody_len); + bcopy((const char*)da, (u8*)&hdr->da, ETHER_ADDR_LEN); + bcopy((const char*)sa, (u8*)&hdr->sa, ETHER_ADDR_LEN); + bcopy((const char*)bssid, (u8*)&hdr->bssid, ETHER_ADDR_LEN); + if ((pbody != NULL) && prebody_len) + bcopy((const char*)pbody, offset, prebody_len); + *body_len = totlen; + return err; +} + + +void +wl_stop_wait_next_action_frame(struct wl_priv *wl, struct net_device *ndev) +{ + if (wl_get_drv_status_all(wl, SENDING_ACT_FRM) && + (wl_get_p2p_status(wl, ACTION_TX_COMPLETED) || + wl_get_p2p_status(wl, ACTION_TX_NOACK))) { + WL_DBG(("*** Wake UP ** abort actframe iovar\n")); + /* if channel is not zero, "actfame" uses off channel scan. + * So abort scan for off channel completion. + */ + if (wl->af_sent_channel) + /* wl_cfg80211_scan_abort(wl, ndev); */ + wl_notify_escan_complete(wl, + (ndev == wl->p2p_net) ? wl_to_prmry_ndev(wl) : ndev, true, true); + } +#ifdef WL_CFG80211_SYNC_GON + else if (wl_get_drv_status_all(wl, WAITING_NEXT_ACT_FRM_LISTEN)) { + WL_DBG(("*** Wake UP ** abort listen for next af frame\n")); + /* So abort scan to cancel listen */ + wl_notify_escan_complete(wl, + (ndev == wl->p2p_net) ? wl_to_prmry_ndev(wl) : ndev, true, true); + } +#endif /* WL_CFG80211_SYNC_GON */ +} + +static s32 +wl_notify_rx_mgmt_frame(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + struct ieee80211_supported_band *band; + struct wiphy *wiphy = wl_to_wiphy(wl); + struct ether_addr da; + struct ether_addr bssid; + bool isfree = false; + s32 err = 0; + s32 freq; + struct net_device *dev = NULL; + wifi_p2p_pub_act_frame_t *act_frm = NULL; + wifi_p2p_action_frame_t *p2p_act_frm = NULL; + wifi_p2psd_gas_pub_act_frame_t *sd_act_frm = NULL; + wl_event_rx_frame_data_t *rxframe = + (wl_event_rx_frame_data_t*)data; + u32 event = ntoh32(e->event_type); + u8 *mgmt_frame; + u8 bsscfgidx = e->bsscfgidx; + u32 mgmt_frame_len = ntoh32(e->datalen) - sizeof(wl_event_rx_frame_data_t); + u16 channel = ((ntoh16(rxframe->channel) & WL_CHANSPEC_CHAN_MASK)); + + memset(&bssid, 0, ETHER_ADDR_LEN); + + if (wl->p2p_net == ndev) { + dev = wl_to_prmry_ndev(wl); + } else { + dev = ndev; + } + + if (channel <= CH_MAX_2G_CHANNEL) + band = wiphy->bands[IEEE80211_BAND_2GHZ]; + else + band = wiphy->bands[IEEE80211_BAND_5GHZ]; + if (!band) { + WL_ERR(("No valid band")); + return -EINVAL; + } +#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) + freq = ieee80211_channel_to_frequency(channel); + (void)band->band; +#else + freq = ieee80211_channel_to_frequency(channel, band->band); +#endif + if (event == WLC_E_ACTION_FRAME_RX) { + wldev_iovar_getbuf_bsscfg(dev, "cur_etheraddr", + NULL, 0, wl->ioctl_buf, WLC_IOCTL_SMLEN, bsscfgidx, &wl->ioctl_buf_sync); + + err = wldev_ioctl(dev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, false); + if (err < 0) + WL_ERR(("WLC_GET_BSSID error %d\n", err)); + memcpy(da.octet, wl->ioctl_buf, ETHER_ADDR_LEN); + err = wl_frame_get_mgmt(FC_ACTION, &da, &e->addr, &bssid, + &mgmt_frame, &mgmt_frame_len, + (u8 *)((wl_event_rx_frame_data_t *)rxframe + 1)); + if (err < 0) { + WL_ERR(("%s: Error in receiving action frame len %d channel %d freq %d\n", + __func__, mgmt_frame_len, channel, freq)); + goto exit; + } + isfree = true; + if (wl_cfgp2p_is_pub_action(&mgmt_frame[DOT11_MGMT_HDR_LEN], + mgmt_frame_len - DOT11_MGMT_HDR_LEN)) { + act_frm = (wifi_p2p_pub_act_frame_t *) + (&mgmt_frame[DOT11_MGMT_HDR_LEN]); + } else if (wl_cfgp2p_is_p2p_action(&mgmt_frame[DOT11_MGMT_HDR_LEN], + mgmt_frame_len - DOT11_MGMT_HDR_LEN)) { + p2p_act_frm = (wifi_p2p_action_frame_t *) + (&mgmt_frame[DOT11_MGMT_HDR_LEN]); + (void) p2p_act_frm; + } else if (wl_cfgp2p_is_gas_action(&mgmt_frame[DOT11_MGMT_HDR_LEN], + mgmt_frame_len - DOT11_MGMT_HDR_LEN)) { + sd_act_frm = (wifi_p2psd_gas_pub_act_frame_t *) + (&mgmt_frame[DOT11_MGMT_HDR_LEN]); + if (sd_act_frm && wl_get_drv_status_all(wl, WAITING_NEXT_ACT_FRM)) { + if (wl->next_af_subtype == sd_act_frm->action) { + WL_DBG(("We got a right next frame of SD!(%d)\n", + sd_act_frm->action)); + wl_clr_drv_status(wl, WAITING_NEXT_ACT_FRM, + (ndev == wl->p2p_net) ? + wl_to_prmry_ndev(wl) : ndev); + + /* Stop waiting for next AF. */ + wl_stop_wait_next_action_frame(wl, ndev); + } + } + (void) sd_act_frm; + } else { + /* + * if we got normal action frame and ndev is p2p0, + * we have to change ndev from p2p0 to wlan0 + */ + if (wl->p2p_net == ndev) + ndev = wl_to_prmry_ndev(wl); + } + + if (act_frm) { + + if (wl_get_drv_status_all(wl, WAITING_NEXT_ACT_FRM)) { + if (wl->next_af_subtype == act_frm->subtype) { + WL_DBG(("We got a right next frame!(%d)\n", + act_frm->subtype)); + wl_clr_drv_status(wl, WAITING_NEXT_ACT_FRM, + (ndev == wl->p2p_net) ? + wl_to_prmry_ndev(wl) : ndev); + + /* Stop waiting for next AF. */ + wl_stop_wait_next_action_frame(wl, ndev); + } + } + } + + wl_cfgp2p_print_actframe(false, &mgmt_frame[DOT11_MGMT_HDR_LEN], + mgmt_frame_len - DOT11_MGMT_HDR_LEN); + /* + * After complete GO Negotiation, roll back to mpc mode + */ + if (act_frm && ((act_frm->subtype == P2P_PAF_GON_CONF) || + (act_frm->subtype == P2P_PAF_PROVDIS_RSP))) { + wldev_iovar_setint(dev, "mpc", 1); + } + if (act_frm && (act_frm->subtype == P2P_PAF_GON_CONF)) { + WL_DBG(("P2P: GO_NEG_PHASE status cleared \n")); + wl_clr_p2p_status(wl, GO_NEG_PHASE); + } + } else { + mgmt_frame = (u8 *)((wl_event_rx_frame_data_t *)rxframe + 1); + + /* wpa supplicant use probe request event for restarting another GON Req. + * but it makes GON Req repetition. + * so if src addr of prb req is same as my target device, + * do not send probe request event during sending action frame. + */ + if (event == WLC_E_P2P_PROBREQ_MSG) { + WL_DBG((" Event %s\n", (event == WLC_E_P2P_PROBREQ_MSG) ? + "WLC_E_P2P_PROBREQ_MSG":"WLC_E_PROBREQ_MSG")); + + + /* Filter any P2P probe reqs arriving during the + * GO-NEG Phase + */ + if (wl->p2p && + wl_get_p2p_status(wl, GO_NEG_PHASE)) { + WL_DBG(("Filtering P2P probe_req while " + "being in GO-Neg state\n")); + return 0; + } + } + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + cfg80211_rx_mgmt(ndev, freq, 0, mgmt_frame, mgmt_frame_len, GFP_ATOMIC); +#else + cfg80211_rx_mgmt(ndev, freq, mgmt_frame, mgmt_frame_len, GFP_ATOMIC); +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) */ + + WL_DBG(("%s: mgmt_frame_len (%d) , e->datalen (%d), channel (%d), freq (%d)\n", __func__, + mgmt_frame_len, ntoh32(e->datalen), channel, freq)); +exit: + if (isfree) + kfree(mgmt_frame); + return 0; +} + +#ifdef WL_SCHED_SCAN +/* If target scan is not reliable, set the below define to "1" to do a + * full escan + */ +#define FULL_ESCAN_ON_PFN_NET_FOUND 0 +static s32 +wl_notify_sched_scan_results(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + wl_pfn_net_info_t *netinfo, *pnetinfo; + struct cfg80211_scan_request request; + struct wiphy *wiphy = wl_to_wiphy(wl); + int err = 0; + struct cfg80211_ssid ssid[MAX_PFN_LIST_COUNT]; + struct ieee80211_channel *channel = NULL; + int channel_req = 0; + int band = 0; + struct wl_pfn_scanresults *pfn_result = (struct wl_pfn_scanresults *)data; + + WL_DBG(("Enter\n")); + + if (e->event_type == WLC_E_PFN_NET_LOST) { + WL_PNO(("PFN NET LOST event. Do Nothing \n")); + return 0; + } + WL_PNO((">>> PFN NET FOUND event. count:%d \n", pfn_result->count)); + if (pfn_result->count > 0) { + int i; + + memset(&request, 0x00, sizeof(struct cfg80211_scan_request)); + memset(&ssid, 0x00, sizeof(ssid)); + request.wiphy = wiphy; + + pnetinfo = (wl_pfn_net_info_t *)(data + sizeof(wl_pfn_scanresults_t) + - sizeof(wl_pfn_net_info_t)); + channel = (struct ieee80211_channel *)kzalloc( + (sizeof(struct ieee80211_channel) * MAX_PFN_LIST_COUNT), + GFP_KERNEL); + if (!channel) { + WL_ERR(("No memory")); + err = -ENOMEM; + goto out_err; + } + + for (i = 0; i < pfn_result->count; i++) { + netinfo = &pnetinfo[i]; + if (!netinfo) { + WL_ERR(("Invalid netinfo ptr. index:%d", i)); + err = -EINVAL; + goto out_err; + } + WL_PNO((">>> SSID:%s Channel:%d \n", + netinfo->pfnsubnet.SSID, netinfo->pfnsubnet.channel)); + /* PFN result doesn't have all the info which are required by the supplicant + * (For e.g IEs) Do a target Escan so that sched scan results are reported + * via wl_inform_single_bss in the required format. Escan does require the + * scan request in the form of cfg80211_scan_request. For timebeing, create + * cfg80211_scan_request one out of the received PNO event. + */ + memcpy(ssid[i].ssid, netinfo->pfnsubnet.SSID, + netinfo->pfnsubnet.SSID_len); + ssid[i].ssid_len = netinfo->pfnsubnet.SSID_len; + request.n_ssids++; + + channel_req = netinfo->pfnsubnet.channel; + band = (channel_req <= CH_MAX_2G_CHANNEL) ? NL80211_BAND_2GHZ + : NL80211_BAND_5GHZ; + channel[i].center_freq = ieee80211_channel_to_frequency(channel_req, band); + channel[i].band = band; + channel[i].flags |= IEEE80211_CHAN_NO_HT40; + request.channels[i] = &channel[i]; + request.n_channels++; + } + + /* assign parsed ssid array */ + if (request.n_ssids) + request.ssids = &ssid[0]; + + if (wl_get_drv_status_all(wl, SCANNING)) { + /* Abort any on-going scan */ + wl_notify_escan_complete(wl, ndev, true, true); + } + + if (wl_get_p2p_status(wl, DISCOVERY_ON)) { + WL_PNO((">>> P2P discovery was ON. Disabling it\n")); + err = wl_cfgp2p_discover_enable_search(wl, false); + if (unlikely(err)) { + wl_clr_drv_status(wl, SCANNING, ndev); + goto out_err; + } + } + + wl_set_drv_status(wl, SCANNING, ndev); +#if FULL_ESCAN_ON_PFN_NET_FOUND + WL_PNO((">>> Doing Full ESCAN on PNO event\n")); + err = wl_do_escan(wl, wiphy, ndev, NULL); +#else + WL_PNO((">>> Doing targeted ESCAN on PNO event\n")); + err = wl_do_escan(wl, wiphy, ndev, &request); +#endif + if (err) { + wl_clr_drv_status(wl, SCANNING, ndev); + goto out_err; + } + wl->sched_scan_running = TRUE; + } + else { + WL_ERR(("FALSE PNO Event. (pfn_count == 0) \n")); + } +out_err: + if (channel) + kfree(channel); + return err; +} +#endif /* WL_SCHED_SCAN */ + +static void wl_init_conf(struct wl_conf *conf) +{ + WL_DBG(("Enter \n")); + conf->frag_threshold = (u32)-1; + conf->rts_threshold = (u32)-1; + conf->retry_short = (u32)-1; + conf->retry_long = (u32)-1; + conf->tx_power = -1; +} + +static void wl_init_prof(struct wl_priv *wl, struct net_device *ndev) +{ + unsigned long flags; + struct wl_profile *profile = wl_get_profile_by_netdev(wl, ndev); + + spin_lock_irqsave(&wl->cfgdrv_lock, flags); + memset(profile, 0, sizeof(struct wl_profile)); + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); +} + +static void wl_init_event_handler(struct wl_priv *wl) +{ + memset(wl->evt_handler, 0, sizeof(wl->evt_handler)); + + wl->evt_handler[WLC_E_SCAN_COMPLETE] = wl_notify_scan_status; + wl->evt_handler[WLC_E_AUTH] = wl_notify_connect_status; + wl->evt_handler[WLC_E_ASSOC] = wl_notify_connect_status; + wl->evt_handler[WLC_E_LINK] = wl_notify_connect_status; + wl->evt_handler[WLC_E_DEAUTH_IND] = wl_notify_connect_status; + wl->evt_handler[WLC_E_DEAUTH] = wl_notify_connect_status; + wl->evt_handler[WLC_E_DISASSOC_IND] = wl_notify_connect_status; + wl->evt_handler[WLC_E_ASSOC_IND] = wl_notify_connect_status; + wl->evt_handler[WLC_E_REASSOC_IND] = wl_notify_connect_status; + wl->evt_handler[WLC_E_ROAM] = wl_notify_roaming_status; + wl->evt_handler[WLC_E_MIC_ERROR] = wl_notify_mic_status; + wl->evt_handler[WLC_E_SET_SSID] = wl_notify_connect_status; + wl->evt_handler[WLC_E_ACTION_FRAME_RX] = wl_notify_rx_mgmt_frame; + wl->evt_handler[WLC_E_PROBREQ_MSG] = wl_notify_rx_mgmt_frame; + wl->evt_handler[WLC_E_P2P_PROBREQ_MSG] = wl_notify_rx_mgmt_frame; + wl->evt_handler[WLC_E_P2P_DISC_LISTEN_COMPLETE] = wl_cfgp2p_listen_complete; + wl->evt_handler[WLC_E_ACTION_FRAME_COMPLETE] = wl_cfgp2p_action_tx_complete; + wl->evt_handler[WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE] = wl_cfgp2p_action_tx_complete; +#ifdef PNO_SUPPORT + wl->evt_handler[WLC_E_PFN_NET_FOUND] = wl_notify_pfn_status; +#endif /* PNO_SUPPORT */ +} + +static s32 wl_init_priv_mem(struct wl_priv *wl) +{ + WL_DBG(("Enter \n")); + wl->scan_results = (void *)kzalloc(WL_SCAN_BUF_MAX, GFP_KERNEL); + if (unlikely(!wl->scan_results)) { + WL_ERR(("Scan results alloc failed\n")); + goto init_priv_mem_out; + } + wl->conf = (void *)kzalloc(sizeof(*wl->conf), GFP_KERNEL); + if (unlikely(!wl->conf)) { + WL_ERR(("wl_conf alloc failed\n")); + goto init_priv_mem_out; + } + wl->scan_req_int = + (void *)kzalloc(sizeof(*wl->scan_req_int), GFP_KERNEL); + if (unlikely(!wl->scan_req_int)) { + WL_ERR(("Scan req alloc failed\n")); + goto init_priv_mem_out; + } + wl->ioctl_buf = (void *)kzalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL); + if (unlikely(!wl->ioctl_buf)) { + WL_ERR(("Ioctl buf alloc failed\n")); + goto init_priv_mem_out; + } + wl->escan_ioctl_buf = (void *)kzalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL); + if (unlikely(!wl->escan_ioctl_buf)) { + WL_ERR(("Ioctl buf alloc failed\n")); + goto init_priv_mem_out; + } + wl->extra_buf = (void *)kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL); + if (unlikely(!wl->extra_buf)) { + WL_ERR(("Extra buf alloc failed\n")); + goto init_priv_mem_out; + } + wl->iscan = (void *)kzalloc(sizeof(*wl->iscan), GFP_KERNEL); + if (unlikely(!wl->iscan)) { + WL_ERR(("Iscan buf alloc failed\n")); + goto init_priv_mem_out; + } + wl->pmk_list = (void *)kzalloc(sizeof(*wl->pmk_list), GFP_KERNEL); + if (unlikely(!wl->pmk_list)) { + WL_ERR(("pmk list alloc failed\n")); + goto init_priv_mem_out; + } + wl->sta_info = (void *)kzalloc(sizeof(*wl->sta_info), GFP_KERNEL); + if (unlikely(!wl->sta_info)) { + WL_ERR(("sta info alloc failed\n")); + goto init_priv_mem_out; + } + +#if defined(STATIC_WL_PRIV_STRUCT) + wl->conn_info = (void *)kzalloc(sizeof(*wl->conn_info), GFP_KERNEL); + if (unlikely(!wl->conn_info)) { + WL_ERR(("wl->conn_info alloc failed\n")); + goto init_priv_mem_out; + } + wl->ie = (void *)kzalloc(sizeof(*wl->ie), GFP_KERNEL); + if (unlikely(!wl->ie)) { + WL_ERR(("wl->ie alloc failed\n")); + goto init_priv_mem_out; + } + wl->escan_info.escan_buf = dhd_os_prealloc(NULL, DHD_PREALLOC_WIPHY_ESCAN0, 0); + bzero(wl->escan_info.escan_buf, ESCAN_BUF_SIZE); +#endif /* STATIC_WL_PRIV_STRUCT */ + wl->afx_hdl = (void *)kzalloc(sizeof(*wl->afx_hdl), GFP_KERNEL); + if (unlikely(!wl->afx_hdl)) { + WL_ERR(("afx hdl alloc failed\n")); + goto init_priv_mem_out; + } else { + init_completion(&wl->act_frm_scan); + init_completion(&wl->wait_next_af); + + INIT_WORK(&wl->afx_hdl->work, wl_cfg80211_afx_handler); + } + return 0; + +init_priv_mem_out: + wl_deinit_priv_mem(wl); + + return -ENOMEM; +} + +static void wl_deinit_priv_mem(struct wl_priv *wl) +{ + kfree(wl->scan_results); + wl->scan_results = NULL; + kfree(wl->conf); + wl->conf = NULL; + kfree(wl->scan_req_int); + wl->scan_req_int = NULL; + kfree(wl->ioctl_buf); + wl->ioctl_buf = NULL; + kfree(wl->escan_ioctl_buf); + wl->escan_ioctl_buf = NULL; + kfree(wl->extra_buf); + wl->extra_buf = NULL; + kfree(wl->iscan); + wl->iscan = NULL; + kfree(wl->pmk_list); + wl->pmk_list = NULL; + kfree(wl->sta_info); + wl->sta_info = NULL; +#if defined(STATIC_WL_PRIV_STRUCT) + kfree(wl->conn_info); + wl->conn_info = NULL; + kfree(wl->ie); + wl->ie = NULL; + wl->escan_info.escan_buf = NULL; +#endif /* STATIC_WL_PRIV_STRUCT */ + if (wl->afx_hdl) { + cancel_work_sync(&wl->afx_hdl->work); + kfree(wl->afx_hdl); + wl->afx_hdl = NULL; + } + + if (wl->ap_info) { + kfree(wl->ap_info->wpa_ie); + kfree(wl->ap_info->rsn_ie); + kfree(wl->ap_info->wps_ie); + kfree(wl->ap_info); + wl->ap_info = NULL; + } +} + +static s32 wl_create_event_handler(struct wl_priv *wl) +{ + int ret = 0; + WL_DBG(("Enter \n")); + + /* Do not use DHD in cfg driver */ + wl->event_tsk.thr_pid = -1; + PROC_START(wl_event_handler, wl, &wl->event_tsk, 0); + if (wl->event_tsk.thr_pid < 0) + ret = -ENOMEM; + return ret; +} + +static void wl_destroy_event_handler(struct wl_priv *wl) +{ + if (wl->event_tsk.thr_pid >= 0) + PROC_STOP(&wl->event_tsk); +} + +static void wl_term_iscan(struct wl_priv *wl) +{ + struct wl_iscan_ctrl *iscan = wl_to_iscan(wl); + WL_TRACE(("In\n")); + if (wl->iscan_on && iscan->tsk) { + iscan->state = WL_ISCAN_STATE_IDLE; + WL_INFO(("SIGTERM\n")); + send_sig(SIGTERM, iscan->tsk, 1); + WL_DBG(("kthread_stop\n")); + kthread_stop(iscan->tsk); + iscan->tsk = NULL; + } +} + +static void wl_notify_iscan_complete(struct wl_iscan_ctrl *iscan, bool aborted) +{ + struct wl_priv *wl = iscan_to_wl(iscan); + struct net_device *ndev = wl_to_prmry_ndev(wl); + unsigned long flags; + + WL_DBG(("Enter \n")); + if (!wl_get_drv_status(wl, SCANNING, ndev)) { + wl_clr_drv_status(wl, SCANNING, ndev); + WL_ERR(("Scan complete while device not scanning\n")); + return; + } + spin_lock_irqsave(&wl->cfgdrv_lock, flags); + wl_clr_drv_status(wl, SCANNING, ndev); + if (likely(wl->scan_request)) { + cfg80211_scan_done(wl->scan_request, aborted); + wl->scan_request = NULL; + } + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); + wl->iscan_kickstart = false; +} + +static s32 wl_wakeup_iscan(struct wl_iscan_ctrl *iscan) +{ + if (likely(iscan->state != WL_ISCAN_STATE_IDLE)) { + WL_DBG(("wake up iscan\n")); + up(&iscan->sync); + return 0; + } + + return -EIO; +} + +static s32 +wl_get_iscan_results(struct wl_iscan_ctrl *iscan, u32 *status, + struct wl_scan_results **bss_list) +{ + struct wl_iscan_results list; + struct wl_scan_results *results; + struct wl_iscan_results *list_buf; + s32 err = 0; + + WL_DBG(("Enter \n")); + memset(iscan->scan_buf, 0, WL_ISCAN_BUF_MAX); + list_buf = (struct wl_iscan_results *)iscan->scan_buf; + results = &list_buf->results; + results->buflen = WL_ISCAN_RESULTS_FIXED_SIZE; + results->version = 0; + results->count = 0; + + memset(&list, 0, sizeof(list)); + list.results.buflen = htod32(WL_ISCAN_BUF_MAX); + err = wldev_iovar_getbuf(iscan->dev, "iscanresults", &list, + WL_ISCAN_RESULTS_FIXED_SIZE, iscan->scan_buf, + WL_ISCAN_BUF_MAX, NULL); + if (unlikely(err)) { + WL_ERR(("error (%d)\n", err)); + return err; + } + results->buflen = dtoh32(results->buflen); + results->version = dtoh32(results->version); + results->count = dtoh32(results->count); + WL_DBG(("results->count = %d\n", results->count)); + WL_DBG(("results->buflen = %d\n", results->buflen)); + *status = dtoh32(list_buf->status); + *bss_list = results; + + return err; +} + +static s32 wl_iscan_done(struct wl_priv *wl) +{ + struct wl_iscan_ctrl *iscan = wl->iscan; + s32 err = 0; + + iscan->state = WL_ISCAN_STATE_IDLE; + mutex_lock(&wl->usr_sync); + wl_inform_bss(wl); + wl_notify_iscan_complete(iscan, false); + mutex_unlock(&wl->usr_sync); + + return err; +} + +static s32 wl_iscan_pending(struct wl_priv *wl) +{ + struct wl_iscan_ctrl *iscan = wl->iscan; + s32 err = 0; + + /* Reschedule the timer */ + mod_timer(&iscan->timer, jiffies + msecs_to_jiffies(iscan->timer_ms)); + iscan->timer_on = 1; + + return err; +} + +static s32 wl_iscan_inprogress(struct wl_priv *wl) +{ + struct wl_iscan_ctrl *iscan = wl->iscan; + s32 err = 0; + + mutex_lock(&wl->usr_sync); + wl_inform_bss(wl); + wl_run_iscan(iscan, NULL, WL_SCAN_ACTION_CONTINUE); + mutex_unlock(&wl->usr_sync); + /* Reschedule the timer */ + mod_timer(&iscan->timer, jiffies + msecs_to_jiffies(iscan->timer_ms)); + iscan->timer_on = 1; + + return err; +} + +static s32 wl_iscan_aborted(struct wl_priv *wl) +{ + struct wl_iscan_ctrl *iscan = wl->iscan; + s32 err = 0; + + iscan->state = WL_ISCAN_STATE_IDLE; + mutex_lock(&wl->usr_sync); + wl_notify_iscan_complete(iscan, true); + mutex_unlock(&wl->usr_sync); + + return err; +} + +static s32 wl_iscan_thread(void *data) +{ + struct wl_iscan_ctrl *iscan = (struct wl_iscan_ctrl *)data; + struct wl_priv *wl = iscan_to_wl(iscan); + u32 status; + int err = 0; + + allow_signal(SIGTERM); + status = WL_SCAN_RESULTS_PARTIAL; + while (likely(!down_interruptible(&iscan->sync))) { + if (kthread_should_stop()) + break; + if (iscan->timer_on) { + del_timer_sync(&iscan->timer); + iscan->timer_on = 0; + } + mutex_lock(&wl->usr_sync); + err = wl_get_iscan_results(iscan, &status, &wl->bss_list); + if (unlikely(err)) { + status = WL_SCAN_RESULTS_ABORTED; + WL_ERR(("Abort iscan\n")); + } + mutex_unlock(&wl->usr_sync); + iscan->iscan_handler[status] (wl); + } + if (iscan->timer_on) { + del_timer_sync(&iscan->timer); + iscan->timer_on = 0; + } + WL_DBG(("%s was terminated\n", __func__)); + + return 0; +} + +static void wl_scan_timeout(unsigned long data) +{ + struct wl_priv *wl = (struct wl_priv *)data; + + if (wl->scan_request) { + WL_ERR(("timer expired\n")); + if (wl->escan_on) + wl_notify_escan_complete(wl, wl->escan_info.ndev, true, true); + else + wl_notify_iscan_complete(wl_to_iscan(wl), true); + } +} +static void wl_iscan_timer(unsigned long data) +{ + struct wl_iscan_ctrl *iscan = (struct wl_iscan_ctrl *)data; + + if (iscan) { + iscan->timer_on = 0; + WL_DBG(("timer expired\n")); + wl_wakeup_iscan(iscan); + } +} + +static s32 wl_invoke_iscan(struct wl_priv *wl) +{ + struct wl_iscan_ctrl *iscan = wl_to_iscan(wl); + int err = 0; + + if (wl->iscan_on && !iscan->tsk) { + iscan->state = WL_ISCAN_STATE_IDLE; + sema_init(&iscan->sync, 0); + iscan->tsk = kthread_run(wl_iscan_thread, iscan, "wl_iscan"); + if (IS_ERR(iscan->tsk)) { + WL_ERR(("Could not create iscan thread\n")); + iscan->tsk = NULL; + return -ENOMEM; + } + } + + return err; +} + +static void wl_init_iscan_handler(struct wl_iscan_ctrl *iscan) +{ + memset(iscan->iscan_handler, 0, sizeof(iscan->iscan_handler)); + iscan->iscan_handler[WL_SCAN_RESULTS_SUCCESS] = wl_iscan_done; + iscan->iscan_handler[WL_SCAN_RESULTS_PARTIAL] = wl_iscan_inprogress; + iscan->iscan_handler[WL_SCAN_RESULTS_PENDING] = wl_iscan_pending; + iscan->iscan_handler[WL_SCAN_RESULTS_ABORTED] = wl_iscan_aborted; + iscan->iscan_handler[WL_SCAN_RESULTS_NO_MEM] = wl_iscan_aborted; +} + +static s32 +wl_cfg80211_netdev_notifier_call(struct notifier_block * nb, + unsigned long state, + void *ndev) +{ + struct net_device *dev = ndev; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wl_priv *wl = wlcfg_drv_priv; + int refcnt = 0; + + WL_DBG(("Enter \n")); + if (!wdev || !wl || dev == wl_to_prmry_ndev(wl)) + return NOTIFY_DONE; + switch (state) { + case NETDEV_DOWN: + while (work_pending(&wdev->cleanup_work) && refcnt < 100) { + if (refcnt%5 == 0) + WL_ERR(("%s : [NETDEV_DOWN] work_pending (%d th)\n", + __FUNCTION__, refcnt)); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(100); + set_current_state(TASK_RUNNING); + refcnt++; + } + break; + + case NETDEV_UNREGISTER: + /* after calling list_del_rcu(&wdev->list) */ + wl_dealloc_netinfo(wl, ndev); + break; + case NETDEV_GOING_DOWN: + /* At NETDEV_DOWN state, wdev_cleanup_work work will be called. + * In front of door, the function checks + * whether current scan is working or not. + * If the scanning is still working, wdev_cleanup_work call WARN_ON and + * make the scan done forcibly. + */ + if (wl_get_drv_status(wl, SCANNING, dev)) { + if (wl->escan_on) { + wl_notify_escan_complete(wl, dev, true, true); + } + } + break; + } + return NOTIFY_DONE; +} +static struct notifier_block wl_cfg80211_netdev_notifier = { + .notifier_call = wl_cfg80211_netdev_notifier_call, +}; + +static s32 wl_notify_escan_complete(struct wl_priv *wl, + struct net_device *ndev, + bool aborted, bool fw_abort) +{ + wl_scan_params_t *params = NULL; + s32 params_size = 0; + s32 err = BCME_OK; + unsigned long flags; + struct net_device *dev; + + WL_DBG(("Enter \n")); + + if (wl->escan_info.ndev != ndev) + { + WL_ERR(("ndev is different %p %p\n", wl->escan_info.ndev, ndev)); + return err; + } + + if (wl->scan_request) { + if (wl->scan_request->dev == wl->p2p_net) + dev = wl_to_prmry_ndev(wl); + else + dev = wl->scan_request->dev; + } + else { + WL_DBG(("wl->scan_request is NULL may be internal scan." + "doing scan_abort for ndev %p primary %p p2p_net %p", + ndev, wl_to_prmry_ndev(wl), wl->p2p_net)); + dev = ndev; + } + if (fw_abort && !in_atomic()) { + /* Our scan params only need space for 1 channel and 0 ssids */ + params = wl_cfg80211_scan_alloc_params(-1, 0, ¶ms_size); + if (params == NULL) { + WL_ERR(("scan params allocation failed \n")); + err = -ENOMEM; + } else { + /* Do a scan abort to stop the driver's scan engine */ + err = wldev_ioctl(dev, WLC_SCAN, params, params_size, true); + if (err < 0) { + WL_ERR(("scan abort failed \n")); + } + } + } + if (timer_pending(&wl->scan_timeout)) + del_timer_sync(&wl->scan_timeout); +#if defined(ESCAN_RESULT_PATCH) + if (likely(wl->scan_request)) { + wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; + wl_inform_bss(wl); + } +#endif /* ESCAN_RESULT_PATCH */ + spin_lock_irqsave(&wl->cfgdrv_lock, flags); +#ifdef WL_SCHED_SCAN + if (wl->sched_scan_req && !wl->scan_request) { + WL_PNO((">>> REPORTING SCHED SCAN RESULTS \n")); + if (aborted) + cfg80211_sched_scan_stopped(wl->sched_scan_req->wiphy); + else + cfg80211_sched_scan_results(wl->sched_scan_req->wiphy); + wl->sched_scan_running = FALSE; + wl->sched_scan_req = NULL; + } +#endif /* WL_SCHED_SCAN */ + if (likely(wl->scan_request)) { + cfg80211_scan_done(wl->scan_request, aborted); + wl->scan_request = NULL; + } + if (p2p_is_on(wl)) + wl_clr_p2p_status(wl, SCANNING); + wl_clr_drv_status(wl, SCANNING, dev); + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); + if (params) + kfree(params); + + return err; +} + +static s32 wl_escan_handler(struct wl_priv *wl, + struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + s32 err = BCME_OK; + s32 status = ntoh32(e->status); + wl_bss_info_t *bi; + wl_escan_result_t *escan_result; + wl_bss_info_t *bss = NULL; + wl_scan_results_t *list; + wifi_p2p_ie_t * p2p_ie; + u32 bi_length; + u32 i; + u8 *p2p_dev_addr = NULL; + + WL_DBG((" enter event type : %d, status : %d \n", + ntoh32(e->event_type), ntoh32(e->status))); + + mutex_lock(&wl->usr_sync); + /* P2P SCAN is coming from primary interface */ + if (wl_get_p2p_status(wl, SCANNING)) { + if (wl_get_drv_status_all(wl, SENDING_ACT_FRM)) + ndev = wl->afx_hdl->dev; + else + ndev = wl->escan_info.ndev; + + } + if (!ndev || !wl->escan_on || + (!wl_get_drv_status(wl, SCANNING, ndev) && + !wl->sched_scan_running)) { + WL_ERR(("escan is not ready ndev %p wl->escan_on %d drv_status 0x%x\n", + ndev, wl->escan_on, wl_get_drv_status(wl, SCANNING, ndev))); + goto exit; + } + if (status == WLC_E_STATUS_PARTIAL) { + WL_INFO(("WLC_E_STATUS_PARTIAL \n")); + escan_result = (wl_escan_result_t *) data; + if (!escan_result) { + WL_ERR(("Invalid escan result (NULL pointer)\n")); + goto exit; + } + if (dtoh16(escan_result->bss_count) != 1) { + WL_ERR(("Invalid bss_count %d: ignoring\n", escan_result->bss_count)); + goto exit; + } + bi = escan_result->bss_info; + if (!bi) { + WL_ERR(("Invalid escan bss info (NULL pointer)\n")); + goto exit; + } + bi_length = dtoh32(bi->length); + if (bi_length != (dtoh32(escan_result->buflen) - WL_ESCAN_RESULTS_FIXED_SIZE)) { + WL_ERR(("Invalid bss_info length %d: ignoring\n", bi_length)); + goto exit; + } + + if (!(wl_to_wiphy(wl)->interface_modes & BIT(NL80211_IFTYPE_ADHOC))) { + if (dtoh16(bi->capability) & DOT11_CAP_IBSS) { + WL_DBG(("Ignoring IBSS result\n")); + goto exit; + } + } + + if (wl_get_drv_status_all(wl, FINDING_COMMON_CHANNEL)) { + p2p_dev_addr = wl_cfgp2p_retreive_p2p_dev_addr(bi, bi_length); + if (p2p_dev_addr && !memcmp(p2p_dev_addr, + wl->afx_hdl->tx_dst_addr.octet, ETHER_ADDR_LEN)) { + s32 channel = wf_chspec_ctlchan( + wl_chspec_driver_to_host(bi->chanspec)); + WL_DBG(("ACTION FRAME SCAN : Peer " MACDBG " found, channel : %d\n", + MAC2STRDBG(wl->afx_hdl->tx_dst_addr.octet), channel)); + wl_clr_p2p_status(wl, SCANNING); + wl->afx_hdl->peer_chan = channel; + complete(&wl->act_frm_scan); + goto exit; + } + + } else { + int cur_len = 0; + list = (wl_scan_results_t *)wl->escan_info.escan_buf; +#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) + if (wl->p2p_net && wl->scan_request && + wl->scan_request->dev == wl->p2p_net) { +#else + if (p2p_is_on(wl) && p2p_scan(wl)) { +#endif +#ifdef WL_HOST_BAND_MGMT + s32 channel = 0; + s32 channel_band = 0; +#endif /* WL_HOST_BAND_MGMT */ + /* p2p scan && allow only probe response */ + if (bi->flags & WL_BSS_FLAGS_FROM_BEACON) + goto exit; + if ((p2p_ie = wl_cfgp2p_find_p2pie(((u8 *) bi) + bi->ie_offset, + bi->ie_length)) == NULL) { + WL_ERR(("Couldn't find P2PIE in probe" + " response/beacon\n")); + goto exit; + } +#ifdef WL_HOST_BAND_MGMT + channel = CHSPEC_CHANNEL(wl_chspec_driver_to_host(bi->chanspec)); + channel_band = (channel > CH_MAX_2G_CHANNEL) ? + WLC_BAND_5G : WLC_BAND_2G; + + + if ((wl->curr_band == WLC_BAND_5G) && + (channel_band == WLC_BAND_2G)) { + /* Avoid sending the GO results in band conflict */ + if (wl_cfgp2p_retreive_p2pattrib(p2p_ie, + P2P_SEID_GROUP_ID) != NULL) + goto exit; + } +#endif /* WL_HOST_BAND_MGMT */ + } + for (i = 0; i < list->count; i++) { + bss = bss ? (wl_bss_info_t *)((uintptr)bss + dtoh32(bss->length)) + : list->bss_info; + + if (!bcmp(&bi->BSSID, &bss->BSSID, ETHER_ADDR_LEN) && + (CHSPEC_BAND(wl_chspec_driver_to_host(bi->chanspec)) + == CHSPEC_BAND(wl_chspec_driver_to_host(bss->chanspec))) && + bi->SSID_len == bss->SSID_len && + !bcmp(bi->SSID, bss->SSID, bi->SSID_len)) { + + /* do not allow beacon data to update + *the data recd from a probe response + */ + if (!(bss->flags & WL_BSS_FLAGS_FROM_BEACON) && + (bi->flags & WL_BSS_FLAGS_FROM_BEACON)) + goto exit; + + WL_DBG(("%s("MACDBG"), i=%d prev: RSSI %d" + " flags 0x%x, new: RSSI %d flags 0x%x\n", + bss->SSID, MAC2STRDBG(bi->BSSID.octet), i, + bss->RSSI, bss->flags, bi->RSSI, bi->flags)); + + if ((bss->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL) == + (bi->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL)) { + /* preserve max RSSI if the measurements are + * both on-channel or both off-channel + */ + WL_SCAN(("%s("MACDBG"), same onchan" + ", RSSI: prev %d new %d\n", + bss->SSID, MAC2STRDBG(bi->BSSID.octet), + bss->RSSI, bi->RSSI)); + bi->RSSI = MAX(bss->RSSI, bi->RSSI); + } else if ((bss->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL) && + (bi->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL) == 0) { + /* preserve the on-channel rssi measurement + * if the new measurement is off channel + */ + WL_SCAN(("%s("MACDBG"), prev onchan" + ", RSSI: prev %d new %d\n", + bss->SSID, MAC2STRDBG(bi->BSSID.octet), + bss->RSSI, bi->RSSI)); + bi->RSSI = bss->RSSI; + bi->flags |= WL_BSS_FLAGS_RSSI_ONCHANNEL; + } + if (dtoh32(bss->length) != bi_length) { + u32 prev_len = dtoh32(bss->length); + + WL_SCAN(("bss info replacement" + " is occured(bcast:%d->probresp%d)\n", + bss->ie_length, bi->ie_length)); + WL_DBG(("%s("MACDBG"), replacement!(%d -> %d)\n", + bss->SSID, MAC2STRDBG(bi->BSSID.octet), + prev_len, bi_length)); + + if (list->buflen - prev_len + bi_length + > ESCAN_BUF_SIZE) { + WL_ERR(("Buffer is too small: keep the" + " previous result of this AP\n")); + /* Only update RSSI */ + bss->RSSI = bi->RSSI; + bss->flags |= (bi->flags + & WL_BSS_FLAGS_RSSI_ONCHANNEL); + goto exit; + } + + if (i < list->count - 1) { + /* memory copy required by this case only */ + memmove((u8 *)bss + bi_length, + (u8 *)bss + prev_len, + list->buflen - cur_len - prev_len); + } + list->buflen -= prev_len; + list->buflen += bi_length; + } + list->version = dtoh32(bi->version); + memcpy((u8 *)bss, (u8 *)bi, bi_length); + goto exit; + } + cur_len += dtoh32(bss->length); + } + if (bi_length > ESCAN_BUF_SIZE - list->buflen) { + WL_ERR(("Buffer is too small: ignoring\n")); + goto exit; + } + memcpy(&(wl->escan_info.escan_buf[list->buflen]), bi, bi_length); + list->version = dtoh32(bi->version); + list->buflen += bi_length; + list->count++; + } + + } + else if (status == WLC_E_STATUS_SUCCESS) { + wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; + if (wl_get_drv_status_all(wl, FINDING_COMMON_CHANNEL)) { + WL_INFO(("ACTION FRAME SCAN DONE\n")); + wl_clr_p2p_status(wl, SCANNING); + wl_clr_drv_status(wl, SCANNING, wl->afx_hdl->dev); + if (wl->afx_hdl->peer_chan == WL_INVALID) + complete(&wl->act_frm_scan); + } else if ((likely(wl->scan_request)) || (wl->sched_scan_running)) { + WL_INFO(("ESCAN COMPLETED\n")); + wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; + wl_inform_bss(wl); + wl_notify_escan_complete(wl, ndev, false, false); + } + } + else if (status == WLC_E_STATUS_ABORT) { + wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; + if (wl_get_drv_status_all(wl, FINDING_COMMON_CHANNEL)) { + WL_INFO(("ACTION FRAME SCAN DONE\n")); + wl_clr_drv_status(wl, SCANNING, wl->afx_hdl->dev); + wl_clr_p2p_status(wl, SCANNING); + if (wl->afx_hdl->peer_chan == WL_INVALID) + complete(&wl->act_frm_scan); + } else if ((likely(wl->scan_request)) || (wl->sched_scan_running)) { + WL_INFO(("ESCAN ABORTED\n")); + wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; + wl_inform_bss(wl); + wl_notify_escan_complete(wl, ndev, true, false); + } + } + else if (status == WLC_E_STATUS_NEWSCAN) + { + escan_result = (wl_escan_result_t *) data; + WL_ERR(("WLC_E_STATUS_NEWSCAN : scan_request[%p]\n", wl->scan_request)); + WL_ERR(("sync_id[%d], bss_count[%d]\n", escan_result->sync_id, + escan_result->bss_count)); + } else { + WL_ERR(("unexpected Escan Event %d : abort\n", status)); + wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; + if (wl_get_drv_status_all(wl, FINDING_COMMON_CHANNEL)) { + WL_INFO(("ACTION FRAME SCAN DONE\n")); + wl_clr_p2p_status(wl, SCANNING); + wl_clr_drv_status(wl, SCANNING, wl->afx_hdl->dev); + if (wl->afx_hdl->peer_chan == WL_INVALID) + complete(&wl->act_frm_scan); + } else if ((likely(wl->scan_request)) || (wl->sched_scan_running)) { + wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; + wl_inform_bss(wl); + wl_notify_escan_complete(wl, ndev, true, false); + } + } +exit: + mutex_unlock(&wl->usr_sync); + return err; +} +static void wl_cfg80211_concurrent_roam(struct wl_priv *wl, int enable) +{ + u32 connected_cnt = wl_get_drv_status_all(wl, CONNECTED); + struct net_info *iter, *next; + int err; + + if (!wl->roamoff_on_concurrent) + return; + if (enable && connected_cnt > 1) { + for_each_ndev(wl, iter, next) { + /* Save the current roam setting */ + if ((err = wldev_iovar_getint(iter->ndev, "roam_off", + (s32 *)&iter->roam_off)) != BCME_OK) { + WL_ERR(("%s:Failed to get current roam setting err %d\n", + iter->ndev->name, err)); + continue; + } + if ((err = wldev_iovar_setint(iter->ndev, "roam_off", 1)) != BCME_OK) { + WL_ERR((" %s:failed to set roam_off : %d\n", + iter->ndev->name, err)); + } + } + } + else if (!enable) { + for_each_ndev(wl, iter, next) { + if (iter->roam_off != WL_INVALID) { + if ((err = wldev_iovar_setint(iter->ndev, "roam_off", + iter->roam_off)) == BCME_OK) + iter->roam_off = WL_INVALID; + else { + WL_ERR((" %s:failed to set roam_off : %d\n", + iter->ndev->name, err)); + } + } + } + } + return; +} + +static void wl_cfg80211_determine_vsdb_mode(struct wl_priv *wl) +{ + struct net_info *iter, *next; + u32 chan = 0; + u32 chanspec = 0; + u32 prev_chan = 0; + u32 connected_cnt = wl_get_drv_status_all(wl, CONNECTED); + wl->vsdb_mode = false; + + if (connected_cnt <= 1) { + return; + } + for_each_ndev(wl, iter, next) { + chanspec = 0; + chan = 0; + if (wl_get_drv_status(wl, CONNECTED, iter->ndev)) { + if (wldev_iovar_getint(iter->ndev, "chanspec", + (s32 *)&chanspec) == BCME_OK) { + chan = CHSPEC_CHANNEL(chanspec); + if (CHSPEC_IS40(chanspec)) { + if (CHSPEC_SB_UPPER(chanspec)) + chan += CH_10MHZ_APART; + else + chan -= CH_10MHZ_APART; + } + wl_update_prof(wl, iter->ndev, NULL, + &chan, WL_PROF_CHAN); + } + if (!prev_chan && chan) + prev_chan = chan; + else if (prev_chan && (prev_chan != chan)) + wl->vsdb_mode = true; + } + } + return; +} +static s32 wl_notifier_change_state(struct wl_priv *wl, struct net_info *_net_info, + enum wl_status state, bool set) +{ + s32 pm = PM_FAST; + s32 err = BCME_OK; + u32 chan = 0; + struct net_info *iter, *next; + struct net_device *primary_dev = wl_to_prmry_ndev(wl); + WL_DBG(("Enter state %d set %d _net_info->pm_restore %d iface %s\n", + state, set, _net_info->pm_restore, _net_info->ndev->name)); + + if (state != WL_STATUS_CONNECTED) + return 0; + + if (set) { + wl_cfg80211_concurrent_roam(wl, 1); + + if (wl_get_mode_by_netdev(wl, _net_info->ndev) == WL_MODE_AP) { + pm = PM_OFF; + WL_DBG(("%s:AP power save %s\n", _net_info->ndev->name, + pm ? "enabled" : "disabled")); + if ((err = wldev_ioctl(_net_info->ndev, WLC_SET_PM, + &pm, sizeof(pm), true)) != 0) { + if (err == -ENODEV) + WL_DBG(("%s:net_device is not ready\n", + _net_info->ndev->name)); + else + WL_ERR(("%s:error (%d)\n", _net_info->ndev->name, err)); + } + if (wl_add_remove_eventmsg(primary_dev, WLC_E_P2P_PROBREQ_MSG, false)) + WL_ERR((" failed to unset WLC_E_P2P_PROPREQ_MSG\n")); + return 0; + } + wl_cfg80211_determine_vsdb_mode(wl); + pm = PM_OFF; + for_each_ndev(wl, iter, next) { + if ((!wl->vsdb_mode) && (iter->ndev != _net_info->ndev)) { + /* Do not touch the other interfaces power save + * if we are not in vsdb mode + */ + continue; + } + /* Save the current power mode */ + iter->pm_restore = true; + err = wldev_ioctl(iter->ndev, WLC_GET_PM, &iter->pm, + sizeof(iter->pm), false); + WL_DBG(("%s:power save %s\n", iter->ndev->name, + iter->pm ? "enabled" : "disabled")); + if ((err = wldev_ioctl(iter->ndev, WLC_SET_PM, &pm, + sizeof(pm), true)) != 0) { + if (err == -ENODEV) + WL_DBG(("%s:netdev not ready\n", iter->ndev->name)); + else + WL_ERR(("%s:error (%d)\n", iter->ndev->name, err)); + iter->ndev->ieee80211_ptr->ps = pm ? true: false; + } + } + } + else { /* clear */ + chan = 0; + /* clear chan information when the net device is disconnected */ + wl_update_prof(wl, _net_info->ndev, NULL, &chan, WL_PROF_CHAN); + wl_cfg80211_determine_vsdb_mode(wl); + for_each_ndev(wl, iter, next) { + if (iter->pm_restore) { + WL_DBG(("%s:restoring power save %s\n", + iter->ndev->name, (iter->pm ? "enabled" : "disabled"))); + err = wldev_ioctl(iter->ndev, + WLC_SET_PM, &iter->pm, sizeof(iter->pm), true); + if (unlikely(err)) { + if (err == -ENODEV) + WL_DBG(("%s:netdev not ready\n", iter->ndev->name)); + else + WL_ERR(("%s:error(%d)\n", iter->ndev->name, err)); + break; + } + iter->pm_restore = 0; + } + } + wl_cfg80211_concurrent_roam(wl, 0); + } + return err; +} + +static s32 wl_init_scan(struct wl_priv *wl) +{ + struct wl_iscan_ctrl *iscan = wl_to_iscan(wl); + int err = 0; + + if (wl->iscan_on) { + iscan->dev = wl_to_prmry_ndev(wl); + iscan->state = WL_ISCAN_STATE_IDLE; + wl_init_iscan_handler(iscan); + iscan->timer_ms = WL_ISCAN_TIMER_INTERVAL_MS; + init_timer(&iscan->timer); + iscan->timer.data = (unsigned long) iscan; + iscan->timer.function = wl_iscan_timer; + sema_init(&iscan->sync, 0); + iscan->tsk = kthread_run(wl_iscan_thread, iscan, "wl_iscan"); + if (IS_ERR(iscan->tsk)) { + WL_ERR(("Could not create iscan thread\n")); + iscan->tsk = NULL; + return -ENOMEM; + } + iscan->data = wl; + } else if (wl->escan_on) { + wl->evt_handler[WLC_E_ESCAN_RESULT] = wl_escan_handler; + wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; + } + /* Init scan_timeout timer */ + init_timer(&wl->scan_timeout); + wl->scan_timeout.data = (unsigned long) wl; + wl->scan_timeout.function = wl_scan_timeout; + + return err; +} + +static s32 wl_init_priv(struct wl_priv *wl) +{ + struct wiphy *wiphy = wl_to_wiphy(wl); + struct net_device *ndev = wl_to_prmry_ndev(wl); + s32 err = 0; + + wl->scan_request = NULL; + wl->pwr_save = !!(wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT); + wl->iscan_on = false; + wl->escan_on = true; + wl->roam_on = false; + wl->iscan_kickstart = false; + wl->active_scan = true; + wl->rf_blocked = false; + wl->vsdb_mode = false; + wl->wlfc_on = false; + wl->roamoff_on_concurrent = true; + /* register interested state */ + set_bit(WL_STATUS_CONNECTED, &wl->interrested_state); + spin_lock_init(&wl->cfgdrv_lock); + mutex_init(&wl->ioctl_buf_sync); + init_waitqueue_head(&wl->netif_change_event); + init_completion(&wl->send_af_done); + init_completion(&wl->iface_disable); + wl_init_eq(wl); + err = wl_init_priv_mem(wl); + if (err) + return err; + if (wl_create_event_handler(wl)) + return -ENOMEM; + wl_init_event_handler(wl); + mutex_init(&wl->usr_sync); + mutex_init(&wl->event_sync); + err = wl_init_scan(wl); + if (err) + return err; + wl_init_conf(wl->conf); + wl_init_prof(wl, ndev); + wl_link_down(wl); + DNGL_FUNC(dhd_cfg80211_init, (wl)); + + return err; +} + +static void wl_deinit_priv(struct wl_priv *wl) +{ + DNGL_FUNC(dhd_cfg80211_deinit, (wl)); + wl_destroy_event_handler(wl); + wl_flush_eq(wl); + wl_link_down(wl); + del_timer_sync(&wl->scan_timeout); + wl_term_iscan(wl); + wl_deinit_priv_mem(wl); + unregister_netdevice_notifier(&wl_cfg80211_netdev_notifier); +} + +#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) +static s32 wl_cfg80211_attach_p2p(void) +{ + struct wl_priv *wl = wlcfg_drv_priv; + + WL_TRACE(("Enter \n")); + + if (wl_cfgp2p_register_ndev(wl) < 0) { + WL_ERR(("%s: P2P attach failed. \n", __func__)); + return -ENODEV; + } + + return 0; +} + +static s32 wl_cfg80211_detach_p2p(void) +{ + struct wl_priv *wl = wlcfg_drv_priv; + struct wireless_dev *wdev = wl->p2p_wdev; + + WL_DBG(("Enter \n")); + if (!wdev || !wl) { + WL_ERR(("Invalid Ptr\n")); + return -EINVAL; + } + + wl_cfgp2p_unregister_ndev(wl); + + wl->p2p_wdev = NULL; + wl->p2p_net = NULL; + WL_DBG(("Freeing 0x%08x \n", (unsigned int)wdev)); + kfree(wdev); + + return 0; +} +#endif /* defined(WLP2P) && defined(WL_ENABLE_P2P_IF) */ + +s32 wl_cfg80211_attach_post(struct net_device *ndev) +{ + struct wl_priv * wl = NULL; + s32 err = 0; + WL_TRACE(("In\n")); + if (unlikely(!ndev)) { + WL_ERR(("ndev is invaild\n")); + return -ENODEV; + } + wl = wlcfg_drv_priv; + if (unlikely(!wl)) { + WL_ERR(("wl is invaild\n")); + return -EINVAL; + } + if (!wl_get_drv_status(wl, READY, ndev)) { + if (wl->wdev && + wl_cfgp2p_supported(wl, ndev)) { +#if !defined(WL_ENABLE_P2P_IF) + wl->wdev->wiphy->interface_modes |= + (BIT(NL80211_IFTYPE_P2P_CLIENT)| + BIT(NL80211_IFTYPE_P2P_GO)); +#endif + if ((err = wl_cfgp2p_init_priv(wl)) != 0) + goto fail; + +#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) + if (wl->p2p_net) { + /* Update MAC addr for p2p0 interface here. */ + memcpy(wl->p2p_net->dev_addr, ndev->dev_addr, ETH_ALEN); + wl->p2p_net->dev_addr[0] |= 0x02; + WL_ERR(("%s: p2p_dev_addr="MACDBG "\n", + wl->p2p_net->name, + MAC2STRDBG(wl->p2p_net->dev_addr))); + } else { + WL_ERR(("p2p_net not yet populated." + " Couldn't update the MAC Address for p2p0 \n")); + return -ENODEV; + } +#endif /* defined(WLP2P) && (WL_ENABLE_P2P_IF) */ + + wl->p2p_supported = true; + } + } + wl_set_drv_status(wl, READY, ndev); +fail: + return err; +} + +s32 wl_cfg80211_attach(struct net_device *ndev, void *data) +{ + struct wireless_dev *wdev; + struct wl_priv *wl; + s32 err = 0; + struct device *dev; + + WL_TRACE(("In\n")); + if (!ndev) { + WL_ERR(("ndev is invaild\n")); + return -ENODEV; + } + WL_DBG(("func %p\n", wl_cfg80211_get_parent_dev())); + dev = wl_cfg80211_get_parent_dev(); + + wdev = kzalloc(sizeof(*wdev), GFP_KERNEL); + if (unlikely(!wdev)) { + WL_ERR(("Could not allocate wireless device\n")); + return -ENOMEM; + } + err = wl_setup_wiphy(wdev, dev); + if (unlikely(err)) { + kfree(wdev); + return -ENOMEM; + } + wdev->iftype = wl_mode_to_nl80211_iftype(WL_MODE_BSS); + wl = (struct wl_priv *)wiphy_priv(wdev->wiphy); + wl->wdev = wdev; + wl->pub = data; + INIT_LIST_HEAD(&wl->net_list); + ndev->ieee80211_ptr = wdev; + SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); + wdev->netdev = ndev; + wl->state_notifier = wl_notifier_change_state; + err = wl_alloc_netinfo(wl, ndev, wdev, WL_MODE_BSS, PM_ENABLE); + if (err) { + WL_ERR(("Failed to alloc net_info (%d)\n", err)); + goto cfg80211_attach_out; + } + err = wl_init_priv(wl); + if (err) { + WL_ERR(("Failed to init iwm_priv (%d)\n", err)); + goto cfg80211_attach_out; + } + + err = wl_setup_rfkill(wl, TRUE); + if (err) { + WL_ERR(("Failed to setup rfkill %d\n", err)); + goto cfg80211_attach_out; + } + err = register_netdevice_notifier(&wl_cfg80211_netdev_notifier); + if (err) { + WL_ERR(("Failed to register notifierl %d\n", err)); + goto cfg80211_attach_out; + } +#if defined(COEX_DHCP) + err = wl_cfg80211_btcoex_init(wl); + if (err) + goto cfg80211_attach_out; +#endif + + wlcfg_drv_priv = wl; + +#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) + err = wl_cfg80211_attach_p2p(); + if (err) + goto cfg80211_attach_out; +#endif + + return err; + +cfg80211_attach_out: + wl_setup_rfkill(wl, FALSE); + wl_free_wdev(wl); + return err; +} + +void wl_cfg80211_detach(void *para) +{ + struct wl_priv *wl; + + (void)para; + wl = wlcfg_drv_priv; + + WL_TRACE(("In\n")); + +#if defined(COEX_DHCP) + wl_cfg80211_btcoex_deinit(wl); +#endif + + wl_setup_rfkill(wl, FALSE); + if (wl->p2p_supported) { + if (timer_pending(&wl->p2p->listen_timer)) + del_timer_sync(&wl->p2p->listen_timer); + wl_cfgp2p_deinit_priv(wl); + } + +#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) + wl_cfg80211_detach_p2p(); +#endif + wl_deinit_priv(wl); + wlcfg_drv_priv = NULL; + wl_cfg80211_clear_parent_dev(); + wl_free_wdev(wl); + /* PLEASE do NOT call any function after wl_free_wdev, the driver's private structure "wl", + * which is the private part of wiphy, has been freed in wl_free_wdev !!!!!!!!!!! + */ +} + +static void wl_wakeup_event(struct wl_priv *wl) +{ + if (wl->event_tsk.thr_pid >= 0) { + DHD_OS_WAKE_LOCK(wl->pub); + up(&wl->event_tsk.sema); + } +} + +static int wl_is_p2p_event(struct wl_event_q *e) +{ + switch (e->etype) { + /* We have to seperate out the P2P events received + * on primary interface so that it can be send up + * via p2p0 interface. + */ + case WLC_E_P2P_PROBREQ_MSG: + case WLC_E_P2P_DISC_LISTEN_COMPLETE: + case WLC_E_ACTION_FRAME_RX: + case WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE: + case WLC_E_ACTION_FRAME_COMPLETE: + + if (e->emsg.ifidx != 0) { + WL_TRACE(("P2P Event on Virtual I/F (ifidx:%d) \n", + e->emsg.ifidx)); + /* We are only bothered about the P2P events received + * on primary interface. For rest of them return false + * so that it is sent over the interface corresponding + * to the ifidx. + */ + return FALSE; + } else { + WL_TRACE(("P2P Event on Primary I/F (ifidx:%d)." + " Sent it to p2p0 \n", e->emsg.ifidx)); + return TRUE; + } + break; + + default: + WL_TRACE(("NON-P2P Event %d on ifidx (ifidx:%d) \n", + e->etype, e->emsg.ifidx)); + return FALSE; + } +} + +static s32 wl_event_handler(void *data) +{ + struct net_device *netdev; + struct wl_priv *wl = NULL; + struct wl_event_q *e; + tsk_ctl_t *tsk = (tsk_ctl_t *)data; + + wl = (struct wl_priv *)tsk->parent; + DAEMONIZE("dhd_cfg80211_event"); + complete(&tsk->completed); + + while (down_interruptible (&tsk->sema) == 0) { + SMP_RD_BARRIER_DEPENDS(); + if (tsk->terminated) + break; + while ((e = wl_deq_event(wl))) { + WL_DBG(("event type (%d), if idx: %d\n", e->etype, e->emsg.ifidx)); + /* All P2P device address related events comes on primary interface since + * there is no corresponding bsscfg for P2P interface. Map it to p2p0 + * interface. + */ + if ((wl_is_p2p_event(e) == TRUE) && (wl->p2p_net)) { + netdev = wl->p2p_net; + } else { + netdev = dhd_idx2net((struct dhd_pub *)(wl->pub), e->emsg.ifidx); + } + if (!netdev) + netdev = wl_to_prmry_ndev(wl); + if (e->etype < WLC_E_LAST && wl->evt_handler[e->etype]) { + wl->evt_handler[e->etype] (wl, netdev, &e->emsg, e->edata); + } else { + WL_DBG(("Unknown Event (%d): ignoring\n", e->etype)); + } + wl_put_event(e); + } + DHD_OS_WAKE_UNLOCK(wl->pub); + } + WL_ERR(("%s was terminated\n", __func__)); + complete_and_exit(&tsk->completed, 0); + return 0; +} + +void +wl_cfg80211_event(struct net_device *ndev, const wl_event_msg_t * e, void *data) +{ + u32 event_type = ntoh32(e->event_type); + struct wl_priv *wl = wlcfg_drv_priv; + +#if (WL_DBG_LEVEL > 0) + s8 *estr = (event_type <= sizeof(wl_dbg_estr) / WL_DBG_ESTR_MAX - 1) ? + wl_dbg_estr[event_type] : (s8 *) "Unknown"; + WL_DBG(("event_type (%d):" "WLC_E_" "%s\n", event_type, estr)); +#endif /* (WL_DBG_LEVEL > 0) */ + + if (event_type == WLC_E_PFN_NET_FOUND) { + WL_DBG((" PNOEVENT: PNO_NET_FOUND\n")); + } + else if (event_type == WLC_E_PFN_NET_LOST) { + WL_DBG((" PNOEVENT: PNO_NET_LOST\n")); + } + + if (likely(!wl_enq_event(wl, ndev, event_type, e, data))) + wl_wakeup_event(wl); +} + +static void wl_init_eq(struct wl_priv *wl) +{ + wl_init_eq_lock(wl); + INIT_LIST_HEAD(&wl->eq_list); +} + +static void wl_flush_eq(struct wl_priv *wl) +{ + struct wl_event_q *e; + unsigned long flags; + + flags = wl_lock_eq(wl); + while (!list_empty(&wl->eq_list)) { + e = list_first_entry(&wl->eq_list, struct wl_event_q, eq_list); + list_del(&e->eq_list); + kfree(e); + } + wl_unlock_eq(wl, flags); +} + +/* +* retrieve first queued event from head +*/ + +static struct wl_event_q *wl_deq_event(struct wl_priv *wl) +{ + struct wl_event_q *e = NULL; + unsigned long flags; + + flags = wl_lock_eq(wl); + if (likely(!list_empty(&wl->eq_list))) { + e = list_first_entry(&wl->eq_list, struct wl_event_q, eq_list); + list_del(&e->eq_list); + } + wl_unlock_eq(wl, flags); + + return e; +} + +/* + * push event to tail of the queue + */ + +static s32 +wl_enq_event(struct wl_priv *wl, struct net_device *ndev, u32 event, const wl_event_msg_t *msg, + void *data) +{ + struct wl_event_q *e; + s32 err = 0; + uint32 evtq_size; + uint32 data_len; + unsigned long flags; + gfp_t aflags; + + data_len = 0; + if (data) + data_len = ntoh32(msg->datalen); + evtq_size = sizeof(struct wl_event_q) + data_len; + aflags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; + e = kzalloc(evtq_size, aflags); + if (unlikely(!e)) { + WL_ERR(("event alloc failed\n")); + return -ENOMEM; + } + e->etype = event; + memcpy(&e->emsg, msg, sizeof(wl_event_msg_t)); + if (data) + memcpy(e->edata, data, data_len); + flags = wl_lock_eq(wl); + list_add_tail(&e->eq_list, &wl->eq_list); + wl_unlock_eq(wl, flags); + + return err; +} + +static void wl_put_event(struct wl_event_q *e) +{ + kfree(e); +} + +static s32 wl_config_ifmode(struct wl_priv *wl, struct net_device *ndev, s32 iftype) +{ + s32 infra = 0; + s32 err = 0; + s32 mode = 0; + switch (iftype) { + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_WDS: + WL_ERR(("type (%d) : currently we do not support this mode\n", + iftype)); + err = -EINVAL; + return err; + case NL80211_IFTYPE_ADHOC: + mode = WL_MODE_IBSS; + break; + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + mode = WL_MODE_BSS; + infra = 1; + break; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + mode = WL_MODE_AP; + infra = 1; + break; + default: + err = -EINVAL; + WL_ERR(("invalid type (%d)\n", iftype)); + return err; + } + infra = htod32(infra); + err = wldev_ioctl(ndev, WLC_SET_INFRA, &infra, sizeof(infra), true); + if (unlikely(err)) { + WL_ERR(("WLC_SET_INFRA error (%d)\n", err)); + return err; + } + + wl_set_mode_by_netdev(wl, ndev, mode); + + return 0; +} + +void wl_cfg80211_add_to_eventbuffer(struct wl_eventmsg_buf *ev, u16 event, bool set) +{ + if (!ev || (event > WLC_E_LAST)) + return; + + if (ev->num < MAX_EVENT_BUF_NUM) { + ev->event[ev->num].type = event; + ev->event[ev->num].set = set; + ev->num++; + } else { + WL_ERR(("evenbuffer doesn't support > %u events. Update" + " the define MAX_EVENT_BUF_NUM \n", MAX_EVENT_BUF_NUM)); + ASSERT(0); + } +} + +s32 wl_cfg80211_apply_eventbuffer( + struct net_device *ndev, + struct wl_priv *wl, + wl_eventmsg_buf_t *ev) +{ + char eventmask[WL_EVENTING_MASK_LEN]; + int i, ret = 0; + s8 iovbuf[WL_EVENTING_MASK_LEN + 12]; + + if (!ev || (!ev->num)) + return -EINVAL; + + mutex_lock(&wl->event_sync); + + /* Read event_msgs mask */ + bcm_mkiovar("event_msgs", NULL, 0, iovbuf, + sizeof(iovbuf)); + ret = wldev_ioctl(ndev, WLC_GET_VAR, iovbuf, sizeof(iovbuf), false); + if (unlikely(ret)) { + WL_ERR(("Get event_msgs error (%d)\n", ret)); + goto exit; + } + memcpy(eventmask, iovbuf, WL_EVENTING_MASK_LEN); + + /* apply the set bits */ + for (i = 0; i < ev->num; i++) { + if (ev->event[i].set) + setbit(eventmask, ev->event[i].type); + else + clrbit(eventmask, ev->event[i].type); + } + + /* Write updated Event mask */ + bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, + sizeof(iovbuf)); + ret = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); + if (unlikely(ret)) { + WL_ERR(("Set event_msgs error (%d)\n", ret)); + } + +exit: + mutex_unlock(&wl->event_sync); + return ret; +} + +s32 wl_add_remove_eventmsg(struct net_device *ndev, u16 event, bool add) +{ + s8 iovbuf[WL_EVENTING_MASK_LEN + 12]; + s8 eventmask[WL_EVENTING_MASK_LEN]; + s32 err = 0; + struct wl_priv *wl = wlcfg_drv_priv; + + if (!ndev || !wl) + return -ENODEV; + + mutex_lock(&wl->event_sync); + + /* Setup event_msgs */ + bcm_mkiovar("event_msgs", NULL, 0, iovbuf, + sizeof(iovbuf)); + err = wldev_ioctl(ndev, WLC_GET_VAR, iovbuf, sizeof(iovbuf), false); + if (unlikely(err)) { + WL_ERR(("Get event_msgs error (%d)\n", err)); + goto eventmsg_out; + } + memcpy(eventmask, iovbuf, WL_EVENTING_MASK_LEN); + if (add) { + setbit(eventmask, event); + } else { + clrbit(eventmask, event); + } + bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, + sizeof(iovbuf)); + err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); + if (unlikely(err)) { + WL_ERR(("Set event_msgs error (%d)\n", err)); + goto eventmsg_out; + } + +eventmsg_out: + mutex_unlock(&wl->event_sync); + return err; +} + +static int wl_construct_reginfo(struct wl_priv *wl, s32 bw_cap) +{ + struct net_device *dev = wl_to_prmry_ndev(wl); + struct ieee80211_channel *band_chan_arr = NULL; + wl_uint32_list_t *list; + u32 i, j, index, n_2g, n_5g, band, channel, array_size; + u32 *n_cnt = NULL; + chanspec_t c = 0; + s32 err = BCME_OK; + bool update; + bool ht40_allowed; + u8 *pbuf = NULL; + +#define LOCAL_BUF_LEN 1024 + pbuf = kzalloc(LOCAL_BUF_LEN, GFP_KERNEL); + + if (pbuf == NULL) { + WL_ERR(("failed to allocate local buf\n")); + return -ENOMEM; + } + list = (wl_uint32_list_t *)(void *) pbuf; + list->count = htod32(WL_NUMCHANSPECS); + + + err = wldev_iovar_getbuf_bsscfg(dev, "chanspecs", NULL, + 0, pbuf, LOCAL_BUF_LEN, 0, &wl->ioctl_buf_sync); + if (err != 0) { + WL_ERR(("get chanspecs failed with %d\n", err)); + kfree(pbuf); + return err; + } +#undef LOCAL_BUF_LEN + + list = (wl_uint32_list_t *)(void *)pbuf; + band = array_size = n_2g = n_5g = 0; + for (i = 0; i < dtoh32(list->count); i++) { + index = 0; + update = false; + ht40_allowed = false; + c = (chanspec_t)dtoh32(list->element[i]); + c = wl_chspec_driver_to_host(c); + channel = CHSPEC_CHANNEL(c); + if (CHSPEC_IS40(c)) { + if (CHSPEC_SB_UPPER(c)) + channel += CH_10MHZ_APART; + else + channel -= CH_10MHZ_APART; + } else if (CHSPEC_IS80(c)) { + WL_DBG(("HT80 center channel : %d\n", channel)); + continue; + } + if (CHSPEC_IS2G(c) && (channel >= CH_MIN_2G_CHANNEL) && + (channel <= CH_MAX_2G_CHANNEL)) { + band_chan_arr = __wl_2ghz_channels; + array_size = ARRAYSIZE(__wl_2ghz_channels); + n_cnt = &n_2g; + band = IEEE80211_BAND_2GHZ; + ht40_allowed = (bw_cap == WLC_N_BW_40ALL)? true : false; + } else if (CHSPEC_IS5G(c) && channel >= CH_MIN_5G_CHANNEL) { + band_chan_arr = __wl_5ghz_a_channels; + array_size = ARRAYSIZE(__wl_5ghz_a_channels); + n_cnt = &n_5g; + band = IEEE80211_BAND_5GHZ; + ht40_allowed = (bw_cap == WLC_N_BW_20ALL)? false : true; + } else { + WL_ERR(("Invalid channel Sepc. 0x%x.\n", c)); + continue; + } + if (!ht40_allowed && CHSPEC_IS40(c)) + continue; + for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) { + if (band_chan_arr[j].hw_value == channel) { + update = true; + break; + } + } + if (update) + index = j; + else + index = *n_cnt; + if (index < array_size) { +#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) + band_chan_arr[index].center_freq = + ieee80211_channel_to_frequency(channel); +#else + band_chan_arr[index].center_freq = + ieee80211_channel_to_frequency(channel, band); +#endif + band_chan_arr[index].hw_value = channel; + + if (CHSPEC_IS40(c) && ht40_allowed) { + /* assuming the order is HT20, HT40 Upper, + HT40 lower from chanspecs + */ + u32 ht40_flag = band_chan_arr[index].flags & IEEE80211_CHAN_NO_HT40; + if (CHSPEC_SB_UPPER(c)) { + if (ht40_flag == IEEE80211_CHAN_NO_HT40) + band_chan_arr[index].flags &= + ~IEEE80211_CHAN_NO_HT40; + band_chan_arr[index].flags |= IEEE80211_CHAN_NO_HT40PLUS; + } else { + /* It should be one of + IEEE80211_CHAN_NO_HT40 or IEEE80211_CHAN_NO_HT40PLUS + */ + band_chan_arr[index].flags &= ~IEEE80211_CHAN_NO_HT40; + if (ht40_flag == IEEE80211_CHAN_NO_HT40) + band_chan_arr[index].flags |= + IEEE80211_CHAN_NO_HT40MINUS; + } + } else { + band_chan_arr[index].flags = IEEE80211_CHAN_NO_HT40; + if (band == IEEE80211_BAND_2GHZ) + channel |= WL_CHANSPEC_BAND_2G; + else + channel |= WL_CHANSPEC_BAND_5G; + channel |= WL_CHANSPEC_BW_20; + channel = wl_chspec_host_to_driver(channel); + err = wldev_iovar_getint(dev, "per_chan_info", &channel); + if (!err) { + if (channel & WL_CHAN_RADAR) + band_chan_arr[index].flags |= + (IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IBSS); + if (channel & WL_CHAN_PASSIVE) + band_chan_arr[index].flags |= + IEEE80211_CHAN_PASSIVE_SCAN; + } + } + if (!update) + (*n_cnt)++; + } + + } + __wl_band_2ghz.n_channels = n_2g; + __wl_band_5ghz_a.n_channels = n_5g; + kfree(pbuf); + return err; +} + +s32 wl_update_wiphybands(struct wl_priv *wl, bool notify) +{ + struct wiphy *wiphy; + struct net_device *dev; + u32 bandlist[3]; + u32 nband = 0; + u32 i = 0; + s32 err = 0; + s32 index = 0; + s32 nmode = 0; + bool rollback_lock = false; + s32 bw_cap = 0; + s32 cur_band = -1; + struct ieee80211_supported_band *bands[IEEE80211_NUM_BANDS] = {NULL, }; + + if (wl == NULL) { + wl = wlcfg_drv_priv; + mutex_lock(&wl->usr_sync); + rollback_lock = true; + } + dev = wl_to_prmry_ndev(wl); + + memset(bandlist, 0, sizeof(bandlist)); + err = wldev_ioctl(dev, WLC_GET_BANDLIST, bandlist, + sizeof(bandlist), false); + if (unlikely(err)) { + WL_ERR(("error read bandlist (%d)\n", err)); + goto end_bands; + } + + wiphy = wl_to_wiphy(wl); + wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; + wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; + + err = wldev_ioctl(dev, WLC_GET_BAND, &cur_band, + sizeof(s32), false); + if (unlikely(err)) { + WL_ERR(("error (%d)\n", err)); + goto end_bands; + } + + err = wldev_iovar_getint(dev, "nmode", &nmode); + if (unlikely(err)) { + WL_ERR(("error reading nmode (%d)\n", err)); + } else { + /* For nmodeonly check bw cap */ + err = wldev_iovar_getint(dev, "mimo_bw_cap", &bw_cap); + if (unlikely(err)) { + WL_ERR(("error get mimo_bw_cap (%d)\n", err)); + } + } + + err = wl_construct_reginfo(wl, bw_cap); + if (err) { + WL_ERR(("wl_construct_reginfo() fails err=%d\n", err)); + if (err != BCME_UNSUPPORTED) + goto end_bands; + /* Ignore error if "chanspecs" command is not supported */ + err = 0; + } + + nband = bandlist[0]; + + for (i = 1; i <= nband && i < ARRAYSIZE(bandlist); i++) { + index = -1; + if (bandlist[i] == WLC_BAND_5G && __wl_band_5ghz_a.n_channels > 0) { + bands[IEEE80211_BAND_5GHZ] = + &__wl_band_5ghz_a; + index = IEEE80211_BAND_5GHZ; + if (bw_cap == WLC_N_BW_40ALL || bw_cap == WLC_N_BW_20IN2G_40IN5G) + bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + } + else if (bandlist[i] == WLC_BAND_2G && __wl_band_2ghz.n_channels > 0) { + bands[IEEE80211_BAND_2GHZ] = + &__wl_band_2ghz; + index = IEEE80211_BAND_2GHZ; + if (bw_cap == WLC_N_BW_40ALL) + bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + } + + if ((index >= 0) && nmode) { + bands[index]->ht_cap.cap |= + (IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_DSSSCCK40); + bands[index]->ht_cap.ht_supported = TRUE; + bands[index]->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + bands[index]->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; + /* An HT shall support all EQM rates for one spatial stream */ + bands[index]->ht_cap.mcs.rx_mask[0] = 0xff; + } + + } + + wiphy->bands[IEEE80211_BAND_2GHZ] = bands[IEEE80211_BAND_2GHZ]; + wiphy->bands[IEEE80211_BAND_5GHZ] = bands[IEEE80211_BAND_5GHZ]; + + if (notify) + wiphy_apply_custom_regulatory(wiphy, &brcm_regdom); + +end_bands: + if (rollback_lock) + mutex_unlock(&wl->usr_sync); + return err; +} + +static s32 __wl_cfg80211_up(struct wl_priv *wl) +{ + s32 err = 0; + struct net_device *ndev = wl_to_prmry_ndev(wl); + struct wireless_dev *wdev = ndev->ieee80211_ptr; + + WL_DBG(("In\n")); + + err = dhd_config_dongle(wl, false); + if (unlikely(err)) + return err; + + err = wl_config_ifmode(wl, ndev, wdev->iftype); + if (unlikely(err && err != -EINPROGRESS)) { + WL_ERR(("wl_config_ifmode failed\n")); + } + err = wl_update_wiphybands(wl, true); + if (unlikely(err)) { + WL_ERR(("wl_update_wiphybands failed\n")); + } + + err = dhd_monitor_init(wl->pub); + err = wl_invoke_iscan(wl); + +#ifdef WL_HOST_BAND_MGMT + /* By default the curr_band is initialized to BAND_AUTO */ + if (wl_cfg80211_set_band(ndev, WLC_BAND_AUTO) < 0) { + WL_ERR(("roam_band set failed\n")); + err = -1; + } +#endif /* WL_HOST_BAND_MGMT */ + +#if defined(DHCP_SCAN_SUPPRESS) + /* wlan scan_supp timer and work thread info */ + init_timer(&wl->scan_supp_timer); + wl->scan_supp_timer.data = (ulong)wl; + wl->scan_supp_timer.function = wl_cfg80211_scan_supp_timerfunc; + INIT_WORK(&wl->wlan_work, wl_cfg80211_work_handler); +#endif /* DHCP_SCAN_SUPPRESS */ + + wl_set_drv_status(wl, READY, ndev); + return err; +} + +static s32 __wl_cfg80211_down(struct wl_priv *wl) +{ + s32 err = 0; + unsigned long flags; + struct net_info *iter, *next; + struct net_device *ndev = wl_to_prmry_ndev(wl); + struct net_device *p2p_net = wl->p2p_net; + u32 bssidx = wl_cfgp2p_find_idx(wl, ndev); + WL_DBG(("In\n")); + +#if defined(DHCP_SCAN_SUPPRESS) + /* Force clear of scan_suppress */ + if (wl->scan_suppressed) + wl_cfg80211_scan_suppress(ndev, 0); + if (timer_pending(&wl->scan_supp_timer)) + del_timer_sync(&wl->scan_supp_timer); + cancel_work_sync(&wl->wlan_work); +#endif /* DHCP_SCAN_SUPPRESS */ + + /* If BSS is operational (e.g SoftAp), bring it down */ + if (wl_cfgp2p_bss_isup(ndev, bssidx)) { + if (wl_cfgp2p_bss(wl, ndev, bssidx, 0) < 0) + WL_ERR(("BSS down failed \n")); + } + + /* Check if cfg80211 interface is already down */ + if (!wl_get_drv_status(wl, READY, ndev)) + return err; /* it is even not ready */ + + for_each_ndev(wl, iter, next) + wl_set_drv_status(wl, SCAN_ABORTING, iter->ndev); + + wl_term_iscan(wl); + spin_lock_irqsave(&wl->cfgdrv_lock, flags); + if (wl->scan_request) { + cfg80211_scan_done(wl->scan_request, true); + wl->scan_request = NULL; + } + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); + + for_each_ndev(wl, iter, next) { + wl_clr_drv_status(wl, READY, iter->ndev); + wl_clr_drv_status(wl, SCANNING, iter->ndev); + wl_clr_drv_status(wl, SCAN_ABORTING, iter->ndev); + wl_clr_drv_status(wl, CONNECTING, iter->ndev); + wl_clr_drv_status(wl, CONNECTED, iter->ndev); + wl_clr_drv_status(wl, DISCONNECTING, iter->ndev); + wl_clr_drv_status(wl, AP_CREATED, iter->ndev); + wl_clr_drv_status(wl, AP_CREATING, iter->ndev); + } + wl_to_prmry_ndev(wl)->ieee80211_ptr->iftype = + NL80211_IFTYPE_STATION; + if (p2p_net) + dev_close(p2p_net); + DNGL_FUNC(dhd_cfg80211_down, (wl)); + wl_flush_eq(wl); + wl_link_down(wl); + if (wl->p2p_supported) + wl_cfgp2p_down(wl); + dhd_monitor_uninit(); + + return err; +} + +s32 wl_cfg80211_up(void *para) +{ + struct wl_priv *wl; + s32 err = 0; + int val = 1; + dhd_pub_t *dhd; + + (void)para; + WL_DBG(("In\n")); + wl = wlcfg_drv_priv; + + if ((err = wldev_ioctl(wl_to_prmry_ndev(wl), WLC_GET_VERSION, &val, + sizeof(int), false) < 0)) { + WL_ERR(("WLC_GET_VERSION failed, err=%d\n", err)); + return err; + } + val = dtoh32(val); + if (val != WLC_IOCTL_VERSION && val != 1) { + WL_ERR(("Version mismatch, please upgrade. Got %d, expected %d or 1\n", + val, WLC_IOCTL_VERSION)); + return BCME_VERSION; + } + ioctl_version = val; + WL_TRACE(("WLC_GET_VERSION=%d\n", ioctl_version)); + + mutex_lock(&wl->usr_sync); + dhd = (dhd_pub_t *)(wl->pub); + if (!(dhd->op_mode & DHD_FLAG_HOSTAP_MODE)) { + err = wl_cfg80211_attach_post(wl_to_prmry_ndev(wl)); + if (unlikely(err)) + return err; + } + err = __wl_cfg80211_up(wl); + if (unlikely(err)) + WL_ERR(("__wl_cfg80211_up failed\n")); + mutex_unlock(&wl->usr_sync); + return err; +} + +/* Private Event to Supplicant with indication that chip hangs */ +int wl_cfg80211_hang(struct net_device *dev, u16 reason) +{ + struct wl_priv *wl; + wl = wlcfg_drv_priv; + + WL_ERR(("In : chip crash eventing\n")); + cfg80211_disconnected(dev, reason, NULL, 0, GFP_KERNEL); + if (wl != NULL) { + wl_link_down(wl); + } + return 0; +} + +s32 wl_cfg80211_down(void *para) +{ + struct wl_priv *wl; + s32 err = 0; + + (void)para; + WL_DBG(("In\n")); + wl = wlcfg_drv_priv; + mutex_lock(&wl->usr_sync); + err = __wl_cfg80211_down(wl); + mutex_unlock(&wl->usr_sync); + + return err; +} + +static void *wl_read_prof(struct wl_priv *wl, struct net_device *ndev, s32 item) +{ + unsigned long flags; + void *rptr = NULL; + struct wl_profile *profile = wl_get_profile_by_netdev(wl, ndev); + + if (!profile) + return NULL; + spin_lock_irqsave(&wl->cfgdrv_lock, flags); + switch (item) { + case WL_PROF_SEC: + rptr = &profile->sec; + break; + case WL_PROF_ACT: + rptr = &profile->active; + break; + case WL_PROF_BSSID: + rptr = profile->bssid; + break; + case WL_PROF_SSID: + rptr = &profile->ssid; + break; + case WL_PROF_CHAN: + rptr = &profile->channel; + break; + } + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); + if (!rptr) + WL_ERR(("invalid item (%d)\n", item)); + return rptr; +} + +static s32 +wl_update_prof(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data, s32 item) +{ + s32 err = 0; + struct wlc_ssid *ssid; + unsigned long flags; + struct wl_profile *profile = wl_get_profile_by_netdev(wl, ndev); + + if (!profile) + return WL_INVALID; + spin_lock_irqsave(&wl->cfgdrv_lock, flags); + switch (item) { + case WL_PROF_SSID: + ssid = (wlc_ssid_t *) data; + memset(profile->ssid.SSID, 0, + sizeof(profile->ssid.SSID)); + memcpy(profile->ssid.SSID, ssid->SSID, ssid->SSID_len); + profile->ssid.SSID_len = ssid->SSID_len; + break; + case WL_PROF_BSSID: + if (data) + memcpy(profile->bssid, data, ETHER_ADDR_LEN); + else + memset(profile->bssid, 0, ETHER_ADDR_LEN); + break; + case WL_PROF_SEC: + memcpy(&profile->sec, data, sizeof(profile->sec)); + break; + case WL_PROF_ACT: + profile->active = *(bool *)data; + break; + case WL_PROF_BEACONINT: + profile->beacon_interval = *(u16 *)data; + break; + case WL_PROF_DTIMPERIOD: + profile->dtim_period = *(u8 *)data; + break; + case WL_PROF_CHAN: + profile->channel = *(u32*)data; + break; + default: + err = -EOPNOTSUPP; + break; + } + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); + + if (err == EOPNOTSUPP) + WL_ERR(("unsupported item (%d)\n", item)); + + return err; +} + +void wl_cfg80211_dbg_level(u32 level) +{ + /* + * prohibit to change debug level + * by insmod parameter. + * eventually debug level will be configured + * in compile time by using CONFIG_XXX + */ + /* wl_dbg_level = level; */ +} + +static bool wl_is_ibssmode(struct wl_priv *wl, struct net_device *ndev) +{ + return wl_get_mode_by_netdev(wl, ndev) == WL_MODE_IBSS; +} + +static __used bool wl_is_ibssstarter(struct wl_priv *wl) +{ + return wl->ibss_starter; +} + +static void wl_rst_ie(struct wl_priv *wl) +{ + struct wl_ie *ie = wl_to_ie(wl); + + ie->offset = 0; +} + +static __used s32 wl_add_ie(struct wl_priv *wl, u8 t, u8 l, u8 *v) +{ + struct wl_ie *ie = wl_to_ie(wl); + s32 err = 0; + + if (unlikely(ie->offset + l + 2 > WL_TLV_INFO_MAX)) { + WL_ERR(("ei crosses buffer boundary\n")); + return -ENOSPC; + } + ie->buf[ie->offset] = t; + ie->buf[ie->offset + 1] = l; + memcpy(&ie->buf[ie->offset + 2], v, l); + ie->offset += l + 2; + + return err; +} + +static s32 wl_mrg_ie(struct wl_priv *wl, u8 *ie_stream, u16 ie_size) +{ + struct wl_ie *ie = wl_to_ie(wl); + s32 err = 0; + + if (unlikely(ie->offset + ie_size > WL_TLV_INFO_MAX)) { + WL_ERR(("ei_stream crosses buffer boundary\n")); + return -ENOSPC; + } + memcpy(&ie->buf[ie->offset], ie_stream, ie_size); + ie->offset += ie_size; + + return err; +} + +static s32 wl_cp_ie(struct wl_priv *wl, u8 *dst, u16 dst_size) +{ + struct wl_ie *ie = wl_to_ie(wl); + s32 err = 0; + + if (unlikely(ie->offset > dst_size)) { + WL_ERR(("dst_size is not enough\n")); + return -ENOSPC; + } + memcpy(dst, &ie->buf[0], ie->offset); + + return err; +} + +static u32 wl_get_ielen(struct wl_priv *wl) +{ + struct wl_ie *ie = wl_to_ie(wl); + + return ie->offset; +} + +static void wl_link_up(struct wl_priv *wl) +{ + wl->link_up = true; +} + +static void wl_link_down(struct wl_priv *wl) +{ + struct wl_connect_info *conn_info = wl_to_conn(wl); + + WL_DBG(("In\n")); + wl->link_up = false; + conn_info->req_ie_len = 0; + conn_info->resp_ie_len = 0; +} + +static unsigned long wl_lock_eq(struct wl_priv *wl) +{ + unsigned long flags; + + spin_lock_irqsave(&wl->eq_lock, flags); + return flags; +} + +static void wl_unlock_eq(struct wl_priv *wl, unsigned long flags) +{ + spin_unlock_irqrestore(&wl->eq_lock, flags); +} + +static void wl_init_eq_lock(struct wl_priv *wl) +{ + spin_lock_init(&wl->eq_lock); +} + +static void wl_delay(u32 ms) +{ + if (in_atomic() || (ms < jiffies_to_msecs(1))) { + mdelay(ms); + } else { + msleep(ms); + } +} + +s32 wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr) +{ + struct wl_priv *wl = wlcfg_drv_priv; + struct ether_addr p2pif_addr; + struct ether_addr primary_mac; + if (!wl->p2p) + return -1; + if (!p2p_is_on(wl)) { + get_primary_mac(wl, &primary_mac); + wl_cfgp2p_generate_bss_mac(&primary_mac, p2pdev_addr, &p2pif_addr); + } else { + memcpy(p2pdev_addr->octet, + wl->p2p->dev_addr.octet, ETHER_ADDR_LEN); + } + + + return 0; +} +s32 wl_cfg80211_set_p2p_noa(struct net_device *net, char* buf, int len) +{ + struct wl_priv *wl; + + wl = wlcfg_drv_priv; + + return wl_cfgp2p_set_p2p_noa(wl, net, buf, len); +} + +s32 wl_cfg80211_get_p2p_noa(struct net_device *net, char* buf, int len) +{ + struct wl_priv *wl; + wl = wlcfg_drv_priv; + + return wl_cfgp2p_get_p2p_noa(wl, net, buf, len); +} + +s32 wl_cfg80211_set_p2p_ps(struct net_device *net, char* buf, int len) +{ + struct wl_priv *wl; + wl = wlcfg_drv_priv; + + return wl_cfgp2p_set_p2p_ps(wl, net, buf, len); +} + +s32 wl_cfg80211_channel_to_freq(u32 channel) +{ + int freq = 0; + +#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) + freq = ieee80211_channel_to_frequency(channel); +#else + { + u16 band = 0; + if (channel <= CH_MAX_2G_CHANNEL) + band = IEEE80211_BAND_2GHZ; + else + band = IEEE80211_BAND_5GHZ; + freq = ieee80211_channel_to_frequency(channel, band); + } +#endif + return freq; +} + +s32 wl_cfg80211_set_wps_p2p_ie(struct net_device *net, char *buf, int len, + enum wl_management_type type) +{ + struct wl_priv *wl; + struct net_device *ndev = NULL; + struct ether_addr primary_mac; + s32 ret = 0; + s32 bssidx = 0; + s32 pktflag = 0; + wl = wlcfg_drv_priv; + + if (wl_get_drv_status(wl, AP_CREATING, net) || + wl_get_drv_status(wl, AP_CREATED, net)) { + ndev = net; + bssidx = 0; + } else if (wl->p2p) { + if (net == wl->p2p_net) { + net = wl_to_prmry_ndev(wl); + } + if (!wl->p2p->on) { + get_primary_mac(wl, &primary_mac); + wl_cfgp2p_generate_bss_mac(&primary_mac, &wl->p2p->dev_addr, + &wl->p2p->int_addr); + /* In case of p2p_listen command, supplicant send remain_on_channel + * without turning on P2P + */ + + p2p_on(wl) = true; + ret = wl_cfgp2p_enable_discovery(wl, net, NULL, 0); + + if (unlikely(ret)) { + goto exit; + } + } + if (net != wl_to_prmry_ndev(wl)) { + if (wl_get_mode_by_netdev(wl, net) == WL_MODE_AP) { + ndev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION); + bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_CONNECTION); + } + } else { + ndev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_PRIMARY); + bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE); + } + } + if (ndev != NULL) { + switch (type) { + case WL_BEACON: + pktflag = VNDR_IE_BEACON_FLAG; + break; + case WL_PROBE_RESP: + pktflag = VNDR_IE_PRBRSP_FLAG; + break; + case WL_ASSOC_RESP: + pktflag = VNDR_IE_ASSOCRSP_FLAG; + break; + } + if (pktflag) + ret = wl_cfgp2p_set_management_ie(wl, ndev, bssidx, pktflag, buf, len); + } +exit: + return ret; +} + +static const struct rfkill_ops wl_rfkill_ops = { + .set_block = wl_rfkill_set +}; + +static int wl_rfkill_set(void *data, bool blocked) +{ + struct wl_priv *wl = (struct wl_priv *)data; + + WL_DBG(("Enter \n")); + WL_DBG(("RF %s\n", blocked ? "blocked" : "unblocked")); + + if (!wl) + return -EINVAL; + + wl->rf_blocked = blocked; + + return 0; +} + +static int wl_setup_rfkill(struct wl_priv *wl, bool setup) +{ + s32 err = 0; + + WL_DBG(("Enter \n")); + if (!wl) + return -EINVAL; + if (setup) { + wl->rfkill = rfkill_alloc("brcmfmac-wifi", + wl_cfg80211_get_parent_dev(), + RFKILL_TYPE_WLAN, &wl_rfkill_ops, (void *)wl); + + if (!wl->rfkill) { + err = -ENOMEM; + goto err_out; + } + + err = rfkill_register(wl->rfkill); + + if (err) + rfkill_destroy(wl->rfkill); + } else { + if (!wl->rfkill) { + err = -ENOMEM; + goto err_out; + } + + rfkill_unregister(wl->rfkill); + rfkill_destroy(wl->rfkill); + } + +err_out: + return err; +} + +struct device *wl_cfg80211_get_parent_dev(void) +{ + return cfg80211_parent_dev; +} + +void wl_cfg80211_set_parent_dev(void *dev) +{ + cfg80211_parent_dev = dev; +} + +static void wl_cfg80211_clear_parent_dev(void) +{ + cfg80211_parent_dev = NULL; +} + +static void get_primary_mac(struct wl_priv *wl, struct ether_addr *mac) +{ + wldev_iovar_getbuf_bsscfg(wl_to_prmry_ndev(wl), "cur_etheraddr", NULL, + 0, wl->ioctl_buf, WLC_IOCTL_SMLEN, 0, &wl->ioctl_buf_sync); + memcpy(mac->octet, wl->ioctl_buf, ETHER_ADDR_LEN); +} + +int wl_cfg80211_do_driver_init(struct net_device *net) +{ + struct wl_priv *wl = *(struct wl_priv **)netdev_priv(net); + + if (!wl || !wl->wdev) + return -EINVAL; + + if (dhd_do_driver_init(wl->wdev->netdev) < 0) + return -1; + + return 0; +} + +void wl_cfg80211_enable_trace(bool set, u32 level) +{ + if (set) + wl_dbg_level = level & WL_DBG_LEVEL; + else + wl_dbg_level |= (WL_DBG_LEVEL & level); +} + +static s32 +wl_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, + struct net_device *dev, u64 cookie) +{ + /* CFG80211 checks for tx_cancel_wait callback when ATTR_DURATION + * is passed with CMD_FRAME. This callback is supposed to cancel + * the OFFCHANNEL Wait. Since we are already taking care of that + * with the tx_mgmt logic, do nothing here. + */ + + return 0; +} + +#ifdef WL_HOST_BAND_MGMT +s32 +wl_cfg80211_set_band(struct net_device *ndev, int band) +{ + struct wl_priv *wl = wlcfg_drv_priv; + int ret = 0; + char ioctl_buf[50]; + + if ((band < WLC_BAND_AUTO) || (band > WLC_BAND_2G)) { + WL_ERR(("Invalid band\n")); + return -EINVAL; + } + + if ((ret = wldev_iovar_setbuf(ndev, "roam_band", &band, + sizeof(int), ioctl_buf, sizeof(ioctl_buf), NULL)) < 0) { + WL_ERR(("seting roam_band failed code=%d\n", ret)); + return ret; + } + + WL_DBG(("Setting band to %d\n", band)); + wl->curr_band = band; + + return 0; +} +#endif /* WL_HOST_BAND_MGMT */ + +#if defined(DHCP_SCAN_SUPPRESS) +static void wl_cfg80211_scan_supp_timerfunc(ulong data) +{ + struct wl_priv *wl = (struct wl_priv *)data; + + WL_DBG(("Enter \n")); + schedule_work(&wl->wlan_work); +} + +static void wl_cfg80211_work_handler(struct work_struct *work) +{ + struct wl_priv *wl = wlcfg_drv_priv; + + wl = container_of(work, struct wl_priv, wlan_work); + + if (!wl) { + WL_ERR(("wl_priv ptr NULL\n")); + return; + } + + if (wl->scan_suppressed) { + /* There is pending scan_suppress. Clean it */ + WL_ERR(("Clean up from timer after %d msec\n", WL_SCAN_SUPPRESS_TIMEOUT)); + wl_cfg80211_scan_suppress(wl_to_prmry_ndev(wl), 0); + } +} + +int wl_cfg80211_scan_suppress(struct net_device *dev, int suppress) +{ + struct wl_priv *wl = wlcfg_drv_priv; + int ret = 0; + + if (!dev || !wl || ((suppress != 0) && (suppress != 1))) + return -EINVAL; + + if (suppress == wl->scan_suppressed) { + WL_DBG(("No change in scan_suppress state. Ignoring cmd..\n")); + return 0; + } + + if (timer_pending(&wl->scan_supp_timer)) + del_timer_sync(&wl->scan_supp_timer); + + if ((ret = wldev_ioctl(dev, WLC_SET_SCANSUPPRESS, + &suppress, sizeof(int), true)) < 0) { + WL_ERR(("Scan suppress setting failed ret:%d \n", ret)); + } else { + WL_DBG(("Scan suppress %s \n", suppress ? "Enabled" : "Disabled")); + wl->scan_suppressed = suppress; + } + + /* If scan_suppress is set, Start a timer to monitor it (just incase) */ + if (wl->scan_suppressed) { + if (ret) { + WL_ERR(("Retry scan_suppress reset at a later time \n")); + mod_timer(&wl->scan_supp_timer, + jiffies + msecs_to_jiffies(WL_SCAN_SUPPRESS_RETRY)); + } else { + WL_DBG(("Start wlan_timer to clear of scan_suppress \n")); + mod_timer(&wl->scan_supp_timer, + jiffies + msecs_to_jiffies(WL_SCAN_SUPPRESS_TIMEOUT)); + } + } + + return ret; +} +#endif /* DHCP_SCAN_SUPPRESS */ diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.h b/drivers/net/wireless/bcmdhd/wl_cfg80211.h new file mode 100644 index 00000000..fc300899 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.h @@ -0,0 +1,850 @@ +/* + * Linux cfg80211 driver + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_cfg80211.h 378667 2013-01-14 10:11:50Z $ + */ + +#ifndef _wl_cfg80211_h_ +#define _wl_cfg80211_h_ + +#include +#include +#include +#include +#include +#include +#include + +#include + +struct wl_conf; +struct wl_iface; +struct wl_priv; +struct wl_security; +struct wl_ibss; + + +#define htod32(i) i +#define htod16(i) i +#define dtoh32(i) i +#define dtoh16(i) i +#define htodchanspec(i) i +#define dtohchanspec(i) i + +#define WL_DBG_NONE 0 +#define WL_DBG_P2P_ACTION (1 << 5) +#define WL_DBG_TRACE (1 << 4) +#define WL_DBG_SCAN (1 << 3) +#define WL_DBG_DBG (1 << 2) +#define WL_DBG_INFO (1 << 1) +#define WL_DBG_ERR (1 << 0) + +/* 0 invalidates all debug messages. default is 1 */ +#define WL_DBG_LEVEL 0xFF + +#define CFG80211_ERROR_TEXT "CFG80211-ERROR) " + +#if defined(DHD_DEBUG) +#define WL_ERR(args) \ +do { \ + if (wl_dbg_level & WL_DBG_ERR) { \ + printk(KERN_INFO CFG80211_ERROR_TEXT "%s : ", __func__); \ + printk args; \ + } \ +} while (0) +#else /* defined(DHD_DEBUG) */ +#define WL_ERR(args) \ +do { \ + if ((wl_dbg_level & WL_DBG_ERR) && net_ratelimit()) { \ + printk(KERN_INFO CFG80211_ERROR_TEXT "%s : ", __func__); \ + printk args; \ + } \ +} while (0) +#endif /* defined(DHD_DEBUG) */ + +#ifdef WL_INFO +#undef WL_INFO +#endif +#define WL_INFO(args) \ +do { \ + if (wl_dbg_level & WL_DBG_INFO) { \ + printk(KERN_INFO "CFG80211-INFO) %s : ", __func__); \ + printk args; \ + } \ +} while (0) +#ifdef WL_SCAN +#undef WL_SCAN +#endif +#define WL_SCAN(args) \ +do { \ + if (wl_dbg_level & WL_DBG_SCAN) { \ + printk(KERN_INFO "CFG80211-SCAN) %s :", __func__); \ + printk args; \ + } \ +} while (0) +#ifdef WL_TRACE +#undef WL_TRACE +#endif +#define WL_TRACE(args) \ +do { \ + if (wl_dbg_level & WL_DBG_TRACE) { \ + printk(KERN_INFO "CFG80211-TRACE) %s :", __func__); \ + printk args; \ + } \ +} while (0) +#ifdef WL_TRACE_HW4 +#undef WL_TRACE_HW4 +#endif +#define WL_TRACE_HW4 WL_TRACE +#if (WL_DBG_LEVEL > 0) +#define WL_DBG(args) \ +do { \ + if (wl_dbg_level & WL_DBG_DBG) { \ + printk(KERN_DEBUG "CFG80211-DEBUG) %s :", __func__); \ + printk args; \ + } \ +} while (0) +#else /* !(WL_DBG_LEVEL > 0) */ +#define WL_DBG(args) +#endif /* (WL_DBG_LEVEL > 0) */ +#define WL_PNO(x) +#define WL_SD(x) + + +#define WL_SCAN_RETRY_MAX 3 +#define WL_NUM_PMKIDS_MAX MAXPMKID +#define WL_SCAN_BUF_MAX (1024 * 8) +#define WL_TLV_INFO_MAX 1500 +#define WL_SCAN_IE_LEN_MAX 2048 +#define WL_BSS_INFO_MAX 2048 +#define WL_ASSOC_INFO_MAX 512 +#define WL_IOCTL_LEN_MAX 1024 +#define WL_EXTRA_BUF_MAX 2048 +#define WL_ISCAN_BUF_MAX 2048 +#define WL_ISCAN_TIMER_INTERVAL_MS 3000 +#define WL_SCAN_ERSULTS_LAST (WL_SCAN_RESULTS_NO_MEM+1) +#define WL_AP_MAX 256 +#define WL_FILE_NAME_MAX 256 +#define WL_DWELL_TIME 200 +#define WL_MED_DWELL_TIME 400 +#define WL_MIN_DWELL_TIME 100 +#define WL_LONG_DWELL_TIME 1000 +#define IFACE_MAX_CNT 2 +#define WL_SCAN_CONNECT_DWELL_TIME_MS 200 +#define WL_SCAN_JOIN_PROBE_INTERVAL_MS 20 +#define WL_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320 +#define WL_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400 +#define WL_AF_TX_MAX_RETRY 5 + +#define WL_SCAN_TIMER_INTERVAL_MS 8000 /* Scan timeout */ +#define WL_CHANNEL_SYNC_RETRY 5 +#define WL_INVALID -1 + +/* Bring down SCB Timeout to 20secs from 60secs default */ +#ifndef WL_SCB_TIMEOUT +#define WL_SCB_TIMEOUT 20 +#endif + +/* SCAN_SUPPRESS timer values in ms */ +#define WL_SCAN_SUPPRESS_TIMEOUT 31000 /* default Framwork DHCP timeout is 30 sec */ +#define WL_SCAN_SUPPRESS_RETRY 3000 + +/* driver status */ +enum wl_status { + WL_STATUS_READY = 0, + WL_STATUS_SCANNING, + WL_STATUS_SCAN_ABORTING, + WL_STATUS_CONNECTING, + WL_STATUS_CONNECTED, + WL_STATUS_DISCONNECTING, + WL_STATUS_AP_CREATING, + WL_STATUS_AP_CREATED, + /* whole sending action frame procedure: + * includes a) 'finding common channel' for public action request frame + * and b) 'sending af via 'actframe' iovar' + */ + WL_STATUS_SENDING_ACT_FRM, + /* find a peer to go to a common channel before sending public action req frame */ + WL_STATUS_FINDING_COMMON_CHANNEL, + /* waiting for next af to sync time of supplicant. + * it includes SENDING_ACT_FRM and WAITING_NEXT_ACT_FRM_LISTEN + */ + WL_STATUS_WAITING_NEXT_ACT_FRM, +#ifdef WL_CFG80211_SYNC_GON + /* go to listen state to wait for next af after SENDING_ACT_FRM */ + WL_STATUS_WAITING_NEXT_ACT_FRM_LISTEN, +#endif /* WL_CFG80211_SYNC_GON */ + /* it will be set when upper layer requests listen and succeed in setting listen mode. + * if set, other scan request can abort current listen state + */ + WL_STATUS_REMAINING_ON_CHANNEL, +#ifdef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST + /* it's fake listen state to keep current scan state. + * it will be set when upper layer requests listen but scan is running. then just run + * a expire timer without actual listen state. + * if set, other scan request does not need to abort scan. + */ + WL_STATUS_FAKE_REMAINING_ON_CHANNEL +#endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ +}; + +/* wi-fi mode */ +enum wl_mode { + WL_MODE_BSS, + WL_MODE_IBSS, + WL_MODE_AP +}; + +/* driver profile list */ +enum wl_prof_list { + WL_PROF_MODE, + WL_PROF_SSID, + WL_PROF_SEC, + WL_PROF_IBSS, + WL_PROF_BAND, + WL_PROF_CHAN, + WL_PROF_BSSID, + WL_PROF_ACT, + WL_PROF_BEACONINT, + WL_PROF_DTIMPERIOD +}; + +/* driver iscan state */ +enum wl_iscan_state { + WL_ISCAN_STATE_IDLE, + WL_ISCAN_STATE_SCANING +}; + +/* donlge escan state */ +enum wl_escan_state { + WL_ESCAN_STATE_IDLE, + WL_ESCAN_STATE_SCANING +}; +/* fw downloading status */ +enum wl_fw_status { + WL_FW_LOADING_DONE, + WL_NVRAM_LOADING_DONE +}; + +enum wl_management_type { + WL_BEACON = 0x1, + WL_PROBE_RESP = 0x2, + WL_ASSOC_RESP = 0x4 +}; +/* beacon / probe_response */ +struct beacon_proberesp { + __le64 timestamp; + __le16 beacon_int; + __le16 capab_info; + u8 variable[0]; +} __attribute__ ((packed)); + +/* driver configuration */ +struct wl_conf { + u32 frag_threshold; + u32 rts_threshold; + u32 retry_short; + u32 retry_long; + s32 tx_power; + struct ieee80211_channel channel; +}; + +typedef s32(*EVENT_HANDLER) (struct wl_priv *wl, + struct net_device *ndev, const wl_event_msg_t *e, void *data); + +/* bss inform structure for cfg80211 interface */ +struct wl_cfg80211_bss_info { + u16 band; + u16 channel; + s16 rssi; + u16 frame_len; + u8 frame_buf[1]; +}; + +/* basic structure of scan request */ +struct wl_scan_req { + struct wlc_ssid ssid; +}; + +/* basic structure of information element */ +struct wl_ie { + u16 offset; + u8 buf[WL_TLV_INFO_MAX]; +}; + +/* event queue for cfg80211 main event */ +struct wl_event_q { + struct list_head eq_list; + u32 etype; + wl_event_msg_t emsg; + s8 edata[1]; +}; + +/* security information with currently associated ap */ +struct wl_security { + u32 wpa_versions; + u32 auth_type; + u32 cipher_pairwise; + u32 cipher_group; + u32 wpa_auth; + u32 auth_assoc_res_status; +}; + +/* ibss information for currently joined ibss network */ +struct wl_ibss { + u8 beacon_interval; /* in millisecond */ + u8 atim; /* in millisecond */ + s8 join_only; + u8 band; + u8 channel; +}; + +/* wl driver profile */ +struct wl_profile { + u32 mode; + s32 band; + u32 channel; + struct wlc_ssid ssid; + struct wl_security sec; + struct wl_ibss ibss; + u8 bssid[ETHER_ADDR_LEN]; + u16 beacon_interval; + u8 dtim_period; + bool active; +}; + +struct net_info { + struct net_device *ndev; + struct wireless_dev *wdev; + struct wl_profile profile; + s32 mode; + s32 roam_off; + unsigned long sme_state; + bool pm_restore; + bool pm_block; + s32 pm; + struct list_head list; /* list of all net_info structure */ +}; +typedef s32(*ISCAN_HANDLER) (struct wl_priv *wl); + +/* iscan controller */ +struct wl_iscan_ctrl { + struct net_device *dev; + struct timer_list timer; + u32 timer_ms; + u32 timer_on; + s32 state; + struct task_struct *tsk; + struct semaphore sync; + ISCAN_HANDLER iscan_handler[WL_SCAN_ERSULTS_LAST]; + void *data; + s8 ioctl_buf[WLC_IOCTL_SMLEN]; + s8 scan_buf[WL_ISCAN_BUF_MAX]; +}; + +/* association inform */ +#define MAX_REQ_LINE 1024 +struct wl_connect_info { + u8 req_ie[MAX_REQ_LINE]; + s32 req_ie_len; + u8 resp_ie[MAX_REQ_LINE]; + s32 resp_ie_len; +}; + +/* firmware /nvram downloading controller */ +struct wl_fw_ctrl { + const struct firmware *fw_entry; + unsigned long status; + u32 ptr; + s8 fw_name[WL_FILE_NAME_MAX]; + s8 nvram_name[WL_FILE_NAME_MAX]; +}; + +/* assoc ie length */ +struct wl_assoc_ielen { + u32 req_len; + u32 resp_len; +}; + +/* wpa2 pmk list */ +struct wl_pmk_list { + pmkid_list_t pmkids; + pmkid_t foo[MAXPMKID - 1]; +}; + + +#define ESCAN_BUF_SIZE (64 * 1024) + +struct escan_info { + u32 escan_state; +#if defined(STATIC_WL_PRIV_STRUCT) +#ifndef CONFIG_DHD_USE_STATIC_BUF +#error STATIC_WL_PRIV_STRUCT should be used with CONFIG_DHD_USE_STATIC_BUF +#endif + u8 *escan_buf; +#else + u8 escan_buf[ESCAN_BUF_SIZE]; +#endif /* STATIC_WL_PRIV_STRUCT */ + struct wiphy *wiphy; + struct net_device *ndev; +}; + +struct ap_info { +/* Structure to hold WPS, WPA IEs for a AP */ + u8 probe_res_ie[VNDR_IES_MAX_BUF_LEN]; + u8 beacon_ie[VNDR_IES_MAX_BUF_LEN]; + u32 probe_res_ie_len; + u32 beacon_ie_len; + u8 *wpa_ie; + u8 *rsn_ie; + u8 *wps_ie; + bool security_mode; +}; +struct btcoex_info { + struct timer_list timer; + u32 timer_ms; + u32 timer_on; + u32 ts_dhcp_start; /* ms ts ecord time stats */ + u32 ts_dhcp_ok; /* ms ts ecord time stats */ + bool dhcp_done; /* flag, indicates that host done with + * dhcp before t1/t2 expiration + */ + s32 bt_state; + struct work_struct work; + struct net_device *dev; +}; + +struct sta_info { + /* Structure to hold WPS IE for a STA */ + u8 probe_req_ie[VNDR_IES_BUF_LEN]; + u8 assoc_req_ie[VNDR_IES_BUF_LEN]; + u32 probe_req_ie_len; + u32 assoc_req_ie_len; +}; + +struct afx_hdl { + wl_af_params_t *pending_tx_act_frm; + struct ether_addr tx_dst_addr; + struct net_device *dev; + struct work_struct work; + u32 bssidx; + u32 retry; + s32 peer_chan; + s32 peer_listen_chan; /* search channel: configured by upper layer */ + s32 my_listen_chan; /* listen chanel: extract it from prb req or gon req */ + bool is_listen; + bool ack_recv; + bool is_active; +}; + +struct parsed_ies { + wpa_ie_fixed_t *wps_ie; + u32 wps_ie_len; + wpa_ie_fixed_t *wpa_ie; + u32 wpa_ie_len; + bcm_tlv_t *wpa2_ie; + u32 wpa2_ie_len; +}; + + +#define MAX_EVENT_BUF_NUM 16 +typedef struct wl_eventmsg_buf { + u16 num; + struct { + u16 type; + bool set; + } event [MAX_EVENT_BUF_NUM]; +} wl_eventmsg_buf_t; + +/* private data of cfg80211 interface */ +struct wl_priv { + struct wireless_dev *wdev; /* representing wl cfg80211 device */ + + struct wireless_dev *p2p_wdev; /* representing wl cfg80211 device for P2P */ + struct net_device *p2p_net; /* reference to p2p0 interface */ + + struct wl_conf *conf; + struct cfg80211_scan_request *scan_request; /* scan request object */ + EVENT_HANDLER evt_handler[WLC_E_LAST]; + struct list_head eq_list; /* used for event queue */ + struct list_head net_list; /* used for struct net_info */ + spinlock_t eq_lock; /* for event queue synchronization */ + spinlock_t cfgdrv_lock; /* to protect scan status (and others if needed) */ + struct completion act_frm_scan; + struct completion iface_disable; + struct completion wait_next_af; + struct mutex usr_sync; /* maily for up/down synchronization */ + struct wl_scan_results *bss_list; + struct wl_scan_results *scan_results; + + /* scan request object for internal purpose */ + struct wl_scan_req *scan_req_int; + /* information element object for internal purpose */ +#if defined(STATIC_WL_PRIV_STRUCT) + struct wl_ie *ie; +#else + struct wl_ie ie; +#endif + struct wl_iscan_ctrl *iscan; /* iscan controller */ + + /* association information container */ +#if defined(STATIC_WL_PRIV_STRUCT) + struct wl_connect_info *conn_info; +#else + struct wl_connect_info conn_info; +#endif + + struct wl_pmk_list *pmk_list; /* wpa2 pmk list */ + tsk_ctl_t event_tsk; /* task of main event handler thread */ + void *pub; + u32 iface_cnt; + u32 channel; /* current channel */ + u32 af_sent_channel; /* channel action frame is sent */ + /* next af subtype to cancel the remained dwell time in rx process */ + u8 next_af_subtype; +#ifdef WL_CFG80211_SYNC_GON + ulong af_tx_sent_jiffies; +#endif /* WL_CFG80211_SYNC_GON */ + bool iscan_on; /* iscan on/off switch */ + bool iscan_kickstart; /* indicate iscan already started */ + bool escan_on; /* escan on/off switch */ + struct escan_info escan_info; /* escan information */ + bool active_scan; /* current scan mode */ + bool ibss_starter; /* indicates this sta is ibss starter */ + bool link_up; /* link/connection up flag */ + + /* indicate whether chip to support power save mode */ + bool pwr_save; + bool roam_on; /* on/off switch for self-roaming */ + bool scan_tried; /* indicates if first scan attempted */ + bool wlfc_on; + bool vsdb_mode; + bool roamoff_on_concurrent; + u8 *ioctl_buf; /* ioctl buffer */ + struct mutex ioctl_buf_sync; + u8 *escan_ioctl_buf; + u8 *extra_buf; /* maily to grab assoc information */ + struct dentry *debugfsdir; + struct rfkill *rfkill; + bool rf_blocked; + struct ieee80211_channel remain_on_chan; + enum nl80211_channel_type remain_on_chan_type; + u64 send_action_id; + u64 last_roc_id; + wait_queue_head_t netif_change_event; + struct completion send_af_done; + struct afx_hdl *afx_hdl; + struct ap_info *ap_info; + struct sta_info *sta_info; + struct p2p_info *p2p; + bool p2p_supported; + struct btcoex_info *btcoex_info; + struct timer_list scan_timeout; /* Timer for catch scan event timeout */ + s32(*state_notifier) (struct wl_priv *wl, + struct net_info *_net_info, enum wl_status state, bool set); + unsigned long interrested_state; + wlc_ssid_t hostapd_ssid; + bool sched_scan_running; /* scheduled scan req status */ +#ifdef WL_SCHED_SCAN + struct cfg80211_sched_scan_request *sched_scan_req; /* scheduled scan req */ +#endif /* WL_SCHED_SCAN */ +#ifdef WL_HOST_BAND_MGMT + u8 curr_band; +#endif /* WL_HOST_BAND_MGMT */ + bool scan_suppressed; + struct timer_list scan_supp_timer; + struct work_struct wlan_work; + struct mutex event_sync; /* maily for up/down synchronization */ +}; + + +static inline struct wl_bss_info *next_bss(struct wl_scan_results *list, struct wl_bss_info *bss) +{ + return bss = bss ? + (struct wl_bss_info *)((uintptr) bss + dtoh32(bss->length)) : list->bss_info; +} +static inline s32 +wl_alloc_netinfo(struct wl_priv *wl, struct net_device *ndev, + struct wireless_dev * wdev, s32 mode, bool pm_block) +{ + struct net_info *_net_info; + s32 err = 0; + if (wl->iface_cnt == IFACE_MAX_CNT) + return -ENOMEM; + _net_info = kzalloc(sizeof(struct net_info), GFP_KERNEL); + if (!_net_info) + err = -ENOMEM; + else { + _net_info->mode = mode; + _net_info->ndev = ndev; + _net_info->wdev = wdev; + _net_info->pm_restore = 0; + _net_info->pm = 0; + _net_info->pm_block = pm_block; + _net_info->roam_off = WL_INVALID; + wl->iface_cnt++; + list_add(&_net_info->list, &wl->net_list); + } + return err; +} +static inline void +wl_dealloc_netinfo(struct wl_priv *wl, struct net_device *ndev) +{ + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (ndev && (_net_info->ndev == ndev)) { + list_del(&_net_info->list); + wl->iface_cnt--; + if (_net_info->wdev) { + kfree(_net_info->wdev); + ndev->ieee80211_ptr = NULL; + } + kfree(_net_info); + } + } + +} +static inline void +wl_delete_all_netinfo(struct wl_priv *wl) +{ + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + list_del(&_net_info->list); + if (_net_info->wdev) + kfree(_net_info->wdev); + kfree(_net_info); + } + wl->iface_cnt = 0; +} +static inline u32 +wl_get_status_all(struct wl_priv *wl, s32 status) + +{ + struct net_info *_net_info, *next; + u32 cnt = 0; + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (_net_info->ndev && + test_bit(status, &_net_info->sme_state)) + cnt++; + } + return cnt; +} +static inline void +wl_set_status_all(struct wl_priv *wl, s32 status, u32 op) +{ + struct net_info *_net_info, *next; + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + switch (op) { + case 1: + return; /* set all status is not allowed */ + case 2: + clear_bit(status, &_net_info->sme_state); + if (wl->state_notifier && + test_bit(status, &(wl->interrested_state))) + wl->state_notifier(wl, _net_info, status, false); + break; + case 4: + return; /* change all status is not allowed */ + default: + return; /* unknown operation */ + } + } +} +static inline void +wl_set_status_by_netdev(struct wl_priv *wl, s32 status, + struct net_device *ndev, u32 op) +{ + + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (ndev && (_net_info->ndev == ndev)) { + switch (op) { + case 1: + set_bit(status, &_net_info->sme_state); + if (wl->state_notifier && + test_bit(status, &(wl->interrested_state))) + wl->state_notifier(wl, _net_info, status, true); + break; + case 2: + clear_bit(status, &_net_info->sme_state); + if (wl->state_notifier && + test_bit(status, &(wl->interrested_state))) + wl->state_notifier(wl, _net_info, status, false); + break; + case 4: + change_bit(status, &_net_info->sme_state); + break; + } + } + + } + +} + +static inline u32 +wl_get_status_by_netdev(struct wl_priv *wl, s32 status, + struct net_device *ndev) +{ + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (ndev && (_net_info->ndev == ndev)) + return test_bit(status, &_net_info->sme_state); + } + return 0; +} + +static inline s32 +wl_get_mode_by_netdev(struct wl_priv *wl, struct net_device *ndev) +{ + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (ndev && (_net_info->ndev == ndev)) + return _net_info->mode; + } + return -1; +} + + +static inline void +wl_set_mode_by_netdev(struct wl_priv *wl, struct net_device *ndev, + s32 mode) +{ + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (ndev && (_net_info->ndev == ndev)) + _net_info->mode = mode; + } +} +static inline struct wl_profile * +wl_get_profile_by_netdev(struct wl_priv *wl, struct net_device *ndev) +{ + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (ndev && (_net_info->ndev == ndev)) + return &_net_info->profile; + } + return NULL; +} +static inline struct net_info * +wl_get_netinfo_by_netdev(struct wl_priv *wl, struct net_device *ndev) +{ + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (ndev && (_net_info->ndev == ndev)) + return _net_info; + } + return NULL; +} +#define wl_to_wiphy(w) (w->wdev->wiphy) +#define wl_to_prmry_ndev(w) (w->wdev->netdev) +#define ndev_to_wl(n) (wdev_to_wl(n->ieee80211_ptr)) +#define wl_to_sr(w) (w->scan_req_int) +#if defined(STATIC_WL_PRIV_STRUCT) +#define wl_to_ie(w) (w->ie) +#define wl_to_conn(w) (w->conn_info) +#else +#define wl_to_ie(w) (&w->ie) +#define wl_to_conn(w) (&w->conn_info) +#endif +#define iscan_to_wl(i) ((struct wl_priv *)(i->data)) +#define wl_to_iscan(w) (w->iscan) +#define wiphy_from_scan(w) (w->escan_info.wiphy) +#define wl_get_drv_status_all(wl, stat) \ + (wl_get_status_all(wl, WL_STATUS_ ## stat)) +#define wl_get_drv_status(wl, stat, ndev) \ + (wl_get_status_by_netdev(wl, WL_STATUS_ ## stat, ndev)) +#define wl_set_drv_status(wl, stat, ndev) \ + (wl_set_status_by_netdev(wl, WL_STATUS_ ## stat, ndev, 1)) +#define wl_clr_drv_status(wl, stat, ndev) \ + (wl_set_status_by_netdev(wl, WL_STATUS_ ## stat, ndev, 2)) +#define wl_clr_drv_status_all(wl, stat) \ + (wl_set_status_all(wl, WL_STATUS_ ## stat, 2)) +#define wl_chg_drv_status(wl, stat, ndev) \ + (wl_set_status_by_netdev(wl, WL_STATUS_ ## stat, ndev, 4)) + +#define for_each_bss(list, bss, __i) \ + for (__i = 0; __i < list->count && __i < WL_AP_MAX; __i++, bss = next_bss(list, bss)) + +#define for_each_ndev(wl, iter, next) \ + list_for_each_entry_safe(iter, next, &wl->net_list, list) + + +/* In case of WPS from wpa_supplicant, pairwise siute and group suite is 0. + * In addtion to that, wpa_version is WPA_VERSION_1 + */ +#define is_wps_conn(_sme) \ + ((wl_cfgp2p_find_wpsie((u8 *)_sme->ie, _sme->ie_len) != NULL) && \ + (!_sme->crypto.n_ciphers_pairwise) && \ + (!_sme->crypto.cipher_group)) +extern s32 wl_cfg80211_attach(struct net_device *ndev, void *data); +extern s32 wl_cfg80211_attach_post(struct net_device *ndev); +extern void wl_cfg80211_detach(void *para); + +extern void wl_cfg80211_event(struct net_device *ndev, const wl_event_msg_t *e, + void *data); +void wl_cfg80211_set_parent_dev(void *dev); +struct device *wl_cfg80211_get_parent_dev(void); + +extern s32 wl_cfg80211_up(void *para); +extern s32 wl_cfg80211_down(void *para); +extern s32 wl_cfg80211_notify_ifadd(struct net_device *ndev, s32 idx, s32 bssidx, + void* _net_attach); +extern s32 wl_cfg80211_ifdel_ops(struct net_device *net); +extern s32 wl_cfg80211_notify_ifdel(void); +extern s32 wl_cfg80211_is_progress_ifadd(void); +extern s32 wl_cfg80211_is_progress_ifchange(void); +extern s32 wl_cfg80211_is_progress_ifadd(void); +extern s32 wl_cfg80211_notify_ifchange(void); +extern void wl_cfg80211_dbg_level(u32 level); +extern s32 wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr); +extern s32 wl_cfg80211_set_p2p_noa(struct net_device *net, char* buf, int len); +extern s32 wl_cfg80211_get_p2p_noa(struct net_device *net, char* buf, int len); +extern s32 wl_cfg80211_set_wps_p2p_ie(struct net_device *net, char *buf, int len, + enum wl_management_type type); +extern s32 wl_cfg80211_set_p2p_ps(struct net_device *net, char* buf, int len); +extern int wl_cfg80211_hang(struct net_device *dev, u16 reason); +extern s32 wl_mode_to_nl80211_iftype(s32 mode); +int wl_cfg80211_do_driver_init(struct net_device *net); +void wl_cfg80211_enable_trace(bool set, u32 level); +extern s32 wl_update_wiphybands(struct wl_priv *wl, bool notify); +extern s32 wl_cfg80211_if_is_group_owner(void); +extern chanspec_t wl_ch_host_to_driver(u16 channel); +extern s32 wl_add_remove_eventmsg(struct net_device *ndev, u16 event, bool add); +extern void wl_stop_wait_next_action_frame(struct wl_priv *wl, struct net_device *ndev); +extern s32 wl_cfg80211_set_band(struct net_device *ndev, int band); +extern int wl_cfg80211_update_power_mode(struct net_device *dev); +#if defined(DHCP_SCAN_SUPPRESS) +extern int wl_cfg80211_scan_suppress(struct net_device *dev, int suppress); +#endif +extern void wl_cfg80211_add_to_eventbuffer(wl_eventmsg_buf_t *ev, u16 event, bool set); +extern s32 wl_cfg80211_apply_eventbuffer(struct net_device *ndev, + struct wl_priv *wl, wl_eventmsg_buf_t *ev); +#endif /* _wl_cfg80211_h_ */ diff --git a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c new file mode 100644 index 00000000..c3144ca2 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c @@ -0,0 +1,2339 @@ +/* + * Linux cfgp2p driver + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_cfgp2p.c 376685 2013-01-02 06:28:45Z $ + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +static s8 scanparambuf[WLC_IOCTL_SMLEN]; +static s8 g_mgmt_ie_buf[2048]; +static bool +wl_cfgp2p_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len, const u8 *oui, u32 oui_len, u8 type); + +static u32 +wl_cfgp2p_vndr_ie(struct wl_priv *wl, u8 *iebuf, s32 bssidx, s32 pktflag, + s8 *oui, s32 ie_id, s8 *data, s32 datalen, const s8* add_del_cmd); + +static int wl_cfgp2p_start_xmit(struct sk_buff *skb, struct net_device *ndev); +static int wl_cfgp2p_do_ioctl(struct net_device *net, struct ifreq *ifr, int cmd); +static int wl_cfgp2p_if_open(struct net_device *net); +static int wl_cfgp2p_if_stop(struct net_device *net); +static s32 wl_cfgp2p_cancel_listen(struct wl_priv *wl, struct net_device *ndev, + bool notify); + +static const struct net_device_ops wl_cfgp2p_if_ops = { + .ndo_open = wl_cfgp2p_if_open, + .ndo_stop = wl_cfgp2p_if_stop, + .ndo_do_ioctl = wl_cfgp2p_do_ioctl, + .ndo_start_xmit = wl_cfgp2p_start_xmit, +}; + +bool wl_cfgp2p_is_pub_action(void *frame, u32 frame_len) +{ + wifi_p2p_pub_act_frame_t *pact_frm; + + if (frame == NULL) + return false; + pact_frm = (wifi_p2p_pub_act_frame_t *)frame; + if (frame_len < sizeof(wifi_p2p_pub_act_frame_t) -1) + return false; + + if (pact_frm->category == P2P_PUB_AF_CATEGORY && + pact_frm->action == P2P_PUB_AF_ACTION && + pact_frm->oui_type == P2P_VER && + memcmp(pact_frm->oui, P2P_OUI, sizeof(pact_frm->oui)) == 0) { + return true; + } + + return false; +} + +bool wl_cfgp2p_is_p2p_action(void *frame, u32 frame_len) +{ + wifi_p2p_action_frame_t *act_frm; + + if (frame == NULL) + return false; + act_frm = (wifi_p2p_action_frame_t *)frame; + if (frame_len < sizeof(wifi_p2p_action_frame_t) -1) + return false; + + if (act_frm->category == P2P_AF_CATEGORY && + act_frm->type == P2P_VER && + memcmp(act_frm->OUI, P2P_OUI, DOT11_OUI_LEN) == 0) { + return true; + } + + return false; +} +bool wl_cfgp2p_is_gas_action(void *frame, u32 frame_len) +{ + + wifi_p2psd_gas_pub_act_frame_t *sd_act_frm; + + if (frame == NULL) + return false; + + sd_act_frm = (wifi_p2psd_gas_pub_act_frame_t *)frame; + if (frame_len < sizeof(wifi_p2psd_gas_pub_act_frame_t) - 1) + return false; + if (sd_act_frm->category != P2PSD_ACTION_CATEGORY) + return false; + + if (sd_act_frm->action == P2PSD_ACTION_ID_GAS_IREQ || + sd_act_frm->action == P2PSD_ACTION_ID_GAS_IRESP || + sd_act_frm->action == P2PSD_ACTION_ID_GAS_CREQ || + sd_act_frm->action == P2PSD_ACTION_ID_GAS_CRESP) + return true; + else + return false; + +} +void wl_cfgp2p_print_actframe(bool tx, void *frame, u32 frame_len) +{ + wifi_p2p_pub_act_frame_t *pact_frm; + wifi_p2p_action_frame_t *act_frm; + wifi_p2psd_gas_pub_act_frame_t *sd_act_frm; + if (!frame || frame_len <= 2) + return; + + if (wl_cfgp2p_is_pub_action(frame, frame_len)) { + pact_frm = (wifi_p2p_pub_act_frame_t *)frame; + switch (pact_frm->subtype) { + case P2P_PAF_GON_REQ: + CFGP2P_ACTION(("%s P2P Group Owner Negotiation Req Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_GON_RSP: + CFGP2P_ACTION(("%s P2P Group Owner Negotiation Rsp Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_GON_CONF: + CFGP2P_ACTION(("%s P2P Group Owner Negotiation Confirm Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_INVITE_REQ: + CFGP2P_ACTION(("%s P2P Invitation Request Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_INVITE_RSP: + CFGP2P_ACTION(("%s P2P Invitation Response Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_DEVDIS_REQ: + CFGP2P_ACTION(("%s P2P Device Discoverability Request Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_DEVDIS_RSP: + CFGP2P_ACTION(("%s P2P Device Discoverability Response Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_PROVDIS_REQ: + CFGP2P_ACTION(("%s P2P Provision Discovery Request Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_PROVDIS_RSP: + CFGP2P_ACTION(("%s P2P Provision Discovery Response Frame\n", + (tx)? "TX": "RX")); + break; + default: + CFGP2P_ACTION(("%s Unknown P2P Public Action Frame\n", + (tx)? "TX": "RX")); + + } + + } else if (wl_cfgp2p_is_p2p_action(frame, frame_len)) { + act_frm = (wifi_p2p_action_frame_t *)frame; + switch (act_frm->subtype) { + case P2P_AF_NOTICE_OF_ABSENCE: + CFGP2P_ACTION(("%s P2P Notice of Absence Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_AF_PRESENCE_REQ: + CFGP2P_ACTION(("%s P2P Presence Request Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_AF_PRESENCE_RSP: + CFGP2P_ACTION(("%s P2P Presence Response Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_AF_GO_DISC_REQ: + CFGP2P_ACTION(("%s P2P Discoverability Request Frame\n", + (tx)? "TX": "RX")); + break; + default: + CFGP2P_ACTION(("%s Unknown P2P Action Frame\n", + (tx)? "TX": "RX")); + } + + } else if (wl_cfgp2p_is_gas_action(frame, frame_len)) { + sd_act_frm = (wifi_p2psd_gas_pub_act_frame_t *)frame; + switch (sd_act_frm->action) { + case P2PSD_ACTION_ID_GAS_IREQ: + CFGP2P_ACTION(("%s P2P GAS Initial Request\n", + (tx)? "TX" : "RX")); + break; + case P2PSD_ACTION_ID_GAS_IRESP: + CFGP2P_ACTION(("%s P2P GAS Initial Response\n", + (tx)? "TX" : "RX")); + break; + case P2PSD_ACTION_ID_GAS_CREQ: + CFGP2P_ACTION(("%s P2P GAS Comback Request\n", + (tx)? "TX" : "RX")); + break; + case P2PSD_ACTION_ID_GAS_CRESP: + CFGP2P_ACTION(("%s P2P GAS Comback Response\n", + (tx)? "TX" : "RX")); + break; + default: + CFGP2P_ACTION(("%s Unknown P2P GAS Frame\n", + (tx)? "TX" : "RX")); + } + + + } +} + +/* + * Initialize variables related to P2P + * + */ +s32 +wl_cfgp2p_init_priv(struct wl_priv *wl) +{ + if (!(wl->p2p = kzalloc(sizeof(struct p2p_info), GFP_KERNEL))) { + CFGP2P_ERR(("struct p2p_info allocation failed\n")); + return -ENOMEM; + } +#define INIT_IE(IE_TYPE, BSS_TYPE) \ + do { \ + memset(wl_to_p2p_bss_saved_ie(wl, BSS_TYPE).p2p_ ## IE_TYPE ## _ie, 0, \ + sizeof(wl_to_p2p_bss_saved_ie(wl, BSS_TYPE).p2p_ ## IE_TYPE ## _ie)); \ + wl_to_p2p_bss_saved_ie(wl, BSS_TYPE).p2p_ ## IE_TYPE ## _ie_len = 0; \ + } while (0); + + INIT_IE(probe_req, P2PAPI_BSSCFG_PRIMARY); + INIT_IE(probe_res, P2PAPI_BSSCFG_PRIMARY); + INIT_IE(assoc_req, P2PAPI_BSSCFG_PRIMARY); + INIT_IE(assoc_res, P2PAPI_BSSCFG_PRIMARY); + INIT_IE(beacon, P2PAPI_BSSCFG_PRIMARY); + INIT_IE(probe_req, P2PAPI_BSSCFG_DEVICE); + INIT_IE(probe_res, P2PAPI_BSSCFG_DEVICE); + INIT_IE(assoc_req, P2PAPI_BSSCFG_DEVICE); + INIT_IE(assoc_res, P2PAPI_BSSCFG_DEVICE); + INIT_IE(beacon, P2PAPI_BSSCFG_DEVICE); + INIT_IE(probe_req, P2PAPI_BSSCFG_CONNECTION); + INIT_IE(probe_res, P2PAPI_BSSCFG_CONNECTION); + INIT_IE(assoc_req, P2PAPI_BSSCFG_CONNECTION); + INIT_IE(assoc_res, P2PAPI_BSSCFG_CONNECTION); + INIT_IE(beacon, P2PAPI_BSSCFG_CONNECTION); +#undef INIT_IE + wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_PRIMARY) = wl_to_prmry_ndev(wl); + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_PRIMARY) = 0; + wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE) = NULL; + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) = 0; + wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION) = NULL; + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_CONNECTION) = 0; + return BCME_OK; + +} +/* + * Deinitialize variables related to P2P + * + */ +void +wl_cfgp2p_deinit_priv(struct wl_priv *wl) +{ + CFGP2P_DBG(("In\n")); + if (wl->p2p) { + kfree(wl->p2p); + wl->p2p = NULL; + } + wl->p2p_supported = 0; +} +/* + * Set P2P functions into firmware + */ +s32 +wl_cfgp2p_set_firm_p2p(struct wl_priv *wl) +{ + struct net_device *ndev = wl_to_prmry_ndev(wl); + struct ether_addr null_eth_addr = { { 0, 0, 0, 0, 0, 0 } }; + s32 ret = BCME_OK; + s32 val = 0; + /* Do we have to check whether APSTA is enabled or not ? */ + wldev_iovar_getint(ndev, "apsta", &val); + if (val == 0) { + val = 1; + ret = wldev_ioctl(ndev, WLC_DOWN, &val, sizeof(s32), true); + if (ret < 0) { + CFGP2P_ERR(("WLC_DOWN error %d\n", ret)); + return ret; + } + wldev_iovar_setint(ndev, "apsta", val); + ret = wldev_ioctl(ndev, WLC_UP, &val, sizeof(s32), true); + if (ret < 0) { + CFGP2P_ERR(("WLC_UP error %d\n", ret)); + return ret; + } + } + + /* In case of COB type, firmware has default mac address + * After Initializing firmware, we have to set current mac address to + * firmware for P2P device address + */ + ret = wldev_iovar_setbuf_bsscfg(ndev, "p2p_da_override", &null_eth_addr, + sizeof(null_eth_addr), wl->ioctl_buf, WLC_IOCTL_MAXLEN, 0, &wl->ioctl_buf_sync); + if (ret && ret != BCME_UNSUPPORTED) { + CFGP2P_ERR(("failed to update device address ret %d\n", ret)); + } + return ret; +} + +/* Create a new P2P BSS. + * Parameters: + * @mac : MAC address of the BSS to create + * @if_type : interface type: WL_P2P_IF_GO or WL_P2P_IF_CLIENT + * @chspec : chspec to use if creating a GO BSS. + * Returns 0 if success. + */ +s32 +wl_cfgp2p_ifadd(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, + chanspec_t chspec) +{ + wl_p2p_if_t ifreq; + s32 err; + u32 scb_timeout = WL_SCB_TIMEOUT; + struct net_device *ndev = wl_to_prmry_ndev(wl); + + ifreq.type = if_type; + ifreq.chspec = chspec; + memcpy(ifreq.addr.octet, mac->octet, sizeof(ifreq.addr.octet)); + + CFGP2P_DBG(("---wl p2p_ifadd "MACDBG" %s %u\n", + MAC2STRDBG(ifreq.addr.octet), + (if_type == WL_P2P_IF_GO) ? "go" : "client", + (chspec & WL_CHANSPEC_CHAN_MASK) >> WL_CHANSPEC_CHAN_SHIFT)); + + err = wldev_iovar_setbuf(ndev, "p2p_ifadd", &ifreq, sizeof(ifreq), + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); + + if (unlikely(err < 0)) + printk("'wl p2p_ifadd' error %d\n", err); + else if (if_type == WL_P2P_IF_GO) { + err = wldev_ioctl(ndev, WLC_SET_SCB_TIMEOUT, &scb_timeout, sizeof(u32), true); + if (unlikely(err < 0)) + printk("'wl scb_timeout' error %d\n", err); + } + return err; +} + +/* Disable a P2P BSS. + * Parameters: + * @mac : MAC address of the BSS to create + * Returns 0 if success. + */ +s32 +wl_cfgp2p_ifdisable(struct wl_priv *wl, struct ether_addr *mac) +{ + s32 ret; + struct net_device *netdev = wl_to_prmry_ndev(wl); + + CFGP2P_INFO(("------primary idx %d : wl p2p_ifdis "MACDBG"\n", + netdev->ifindex, MAC2STRDBG(mac->octet))); + ret = wldev_iovar_setbuf(netdev, "p2p_ifdis", mac, sizeof(*mac), + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); + if (unlikely(ret < 0)) { + printk("'wl p2p_ifdis' error %d\n", ret); + } + return ret; +} + +/* Delete a P2P BSS. + * Parameters: + * @mac : MAC address of the BSS to create + * Returns 0 if success. + */ +s32 +wl_cfgp2p_ifdel(struct wl_priv *wl, struct ether_addr *mac) +{ + s32 ret; + struct net_device *netdev = wl_to_prmry_ndev(wl); + + CFGP2P_INFO(("------primary idx %d : wl p2p_ifdel "MACDBG"\n", + netdev->ifindex, MAC2STRDBG(mac->octet))); + ret = wldev_iovar_setbuf(netdev, "p2p_ifdel", mac, sizeof(*mac), + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); + if (unlikely(ret < 0)) { + printk("'wl p2p_ifdel' error %d\n", ret); + } + return ret; +} + +/* Change a P2P Role. + * Parameters: + * @mac : MAC address of the BSS to change a role + * Returns 0 if success. + */ +s32 +wl_cfgp2p_ifchange(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, + chanspec_t chspec) +{ + wl_p2p_if_t ifreq; + s32 err; + u32 scb_timeout = WL_SCB_TIMEOUT; + + struct net_device *netdev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION); + + ifreq.type = if_type; + ifreq.chspec = chspec; + memcpy(ifreq.addr.octet, mac->octet, sizeof(ifreq.addr.octet)); + + CFGP2P_INFO(("---wl p2p_ifchange "MACDBG" %s %u" + " chanspec 0x%04x\n", MAC2STRDBG(ifreq.addr.octet), + (if_type == WL_P2P_IF_GO) ? "go" : "client", + (chspec & WL_CHANSPEC_CHAN_MASK) >> WL_CHANSPEC_CHAN_SHIFT, + ifreq.chspec)); + + err = wldev_iovar_setbuf(netdev, "p2p_ifupd", &ifreq, sizeof(ifreq), + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); + + if (unlikely(err < 0)) { + printk("'wl p2p_ifupd' error %d\n", err); + } else if (if_type == WL_P2P_IF_GO) { + err = wldev_ioctl(netdev, WLC_SET_SCB_TIMEOUT, &scb_timeout, sizeof(u32), true); + if (unlikely(err < 0)) + printk("'wl scb_timeout' error %d\n", err); + } + return err; +} + + +/* Get the index of a created P2P BSS. + * Parameters: + * @mac : MAC address of the created BSS + * @index : output: index of created BSS + * Returns 0 if success. + */ +s32 +wl_cfgp2p_ifidx(struct wl_priv *wl, struct ether_addr *mac, s32 *index) +{ + s32 ret; + u8 getbuf[64]; + struct net_device *dev = wl_to_prmry_ndev(wl); + + CFGP2P_INFO(("---wl p2p_if "MACDBG"\n", MAC2STRDBG(mac->octet))); + + ret = wldev_iovar_getbuf_bsscfg(dev, "p2p_if", mac, sizeof(*mac), getbuf, + sizeof(getbuf), wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_PRIMARY), NULL); + + if (ret == 0) { + memcpy(index, getbuf, sizeof(s32)); + CFGP2P_INFO(("---wl p2p_if ==> %d\n", *index)); + } + + return ret; +} + +static s32 +wl_cfgp2p_set_discovery(struct wl_priv *wl, s32 on) +{ + s32 ret = BCME_OK; + struct net_device *ndev = wl_to_prmry_ndev(wl); + CFGP2P_DBG(("enter\n")); + + ret = wldev_iovar_setint(ndev, "p2p_disc", on); + + if (unlikely(ret < 0)) { + CFGP2P_ERR(("p2p_disc %d error %d\n", on, ret)); + } + + return ret; +} + +/* Set the WL driver's P2P mode. + * Parameters : + * @mode : is one of WL_P2P_DISC_ST_{SCAN,LISTEN,SEARCH}. + * @channel : the channel to listen + * @listen_ms : the time (milli seconds) to wait + * @bssidx : bss index for BSSCFG + * Returns 0 if success + */ + +s32 +wl_cfgp2p_set_p2p_mode(struct wl_priv *wl, u8 mode, u32 channel, u16 listen_ms, int bssidx) +{ + wl_p2p_disc_st_t discovery_mode; + s32 ret; + struct net_device *dev; + CFGP2P_DBG(("enter\n")); + + if (unlikely(bssidx == WL_INVALID || bssidx >= P2PAPI_BSSCFG_MAX)) { + CFGP2P_ERR((" %d index out of range\n", bssidx)); + return -1; + } + + dev = wl_to_p2p_bss_ndev(wl, bssidx); + if (unlikely(dev == NULL)) { + CFGP2P_ERR(("bssidx %d is not assigned\n", bssidx)); + return BCME_NOTFOUND; + } + + /* Put the WL driver into P2P Listen Mode to respond to P2P probe reqs */ + discovery_mode.state = mode; + discovery_mode.chspec = wl_ch_host_to_driver(channel); + discovery_mode.dwell = listen_ms; + ret = wldev_iovar_setbuf_bsscfg(dev, "p2p_state", &discovery_mode, + sizeof(discovery_mode), wl->ioctl_buf, WLC_IOCTL_MAXLEN, + bssidx, &wl->ioctl_buf_sync); + + return ret; +} + +/* Get the index of the P2P Discovery BSS */ +static s32 +wl_cfgp2p_get_disc_idx(struct wl_priv *wl, s32 *index) +{ + s32 ret; + struct net_device *dev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_PRIMARY); + + ret = wldev_iovar_getint(dev, "p2p_dev", index); + CFGP2P_INFO(("p2p_dev bsscfg_idx=%d ret=%d\n", *index, ret)); + + if (unlikely(ret < 0)) { + CFGP2P_ERR(("'p2p_dev' error %d\n", ret)); + return ret; + } + return ret; +} + +s32 +wl_cfgp2p_init_discovery(struct wl_priv *wl) +{ + + s32 index = 0; + s32 ret = BCME_OK; + + CFGP2P_DBG(("enter\n")); + + if (wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) != 0) { + CFGP2P_ERR(("do nothing, already initialized\n")); + return ret; + } + + ret = wl_cfgp2p_set_discovery(wl, 1); + if (ret < 0) { + CFGP2P_ERR(("set discover error\n")); + return ret; + } + /* Enable P2P Discovery in the WL Driver */ + ret = wl_cfgp2p_get_disc_idx(wl, &index); + + if (ret < 0) { + return ret; + } + wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE) = + wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_PRIMARY); + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) = index; + + /* Set the initial discovery state to SCAN */ + ret = wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); + + if (unlikely(ret != 0)) { + CFGP2P_ERR(("unable to set WL_P2P_DISC_ST_SCAN\n")); + wl_cfgp2p_set_discovery(wl, 0); + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) = 0; + wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE) = NULL; + return 0; + } + return ret; +} + +/* Deinitialize P2P Discovery + * Parameters : + * @wl : wl_private data + * Returns 0 if succes + */ +static s32 +wl_cfgp2p_deinit_discovery(struct wl_priv *wl) +{ + s32 ret = BCME_OK; + CFGP2P_DBG(("enter\n")); + + if (wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) == 0) { + CFGP2P_ERR(("do nothing, not initialized\n")); + return -1; + } + /* Set the discovery state to SCAN */ + ret = wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); + /* Disable P2P discovery in the WL driver (deletes the discovery BSSCFG) */ + ret = wl_cfgp2p_set_discovery(wl, 0); + + /* Clear our saved WPS and P2P IEs for the discovery BSS. The driver + * deleted these IEs when wl_cfgp2p_set_discovery() deleted the discovery + * BSS. + */ + + /* Clear the saved bsscfg index of the discovery BSSCFG to indicate we + * have no discovery BSS. + */ + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) = WL_INVALID; + wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE) = NULL; + + return ret; + +} +/* Enable P2P Discovery + * Parameters: + * @wl : wl_private data + * @ie : probe request ie (WPS IE + P2P IE) + * @ie_len : probe request ie length + * Returns 0 if success. + */ +s32 +wl_cfgp2p_enable_discovery(struct wl_priv *wl, struct net_device *dev, + const u8 *ie, u32 ie_len) +{ + s32 ret = BCME_OK; + s32 bssidx = (wl_to_prmry_ndev(wl) == dev) ? + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) : wl_cfgp2p_find_idx(wl, dev); + if (wl_get_p2p_status(wl, DISCOVERY_ON)) { + CFGP2P_INFO((" DISCOVERY is already initialized, we have nothing to do\n")); + goto set_ie; + } + + wl_set_p2p_status(wl, DISCOVERY_ON); + + CFGP2P_DBG(("enter\n")); + + ret = wl_cfgp2p_init_discovery(wl); + if (unlikely(ret < 0)) { + CFGP2P_ERR((" init discovery error %d\n", ret)); + goto exit; + } + /* Set wsec to any non-zero value in the discovery bsscfg to ensure our + * P2P probe responses have the privacy bit set in the 802.11 WPA IE. + * Some peer devices may not initiate WPS with us if this bit is not set. + */ + ret = wldev_iovar_setint_bsscfg(wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE), + "wsec", AES_ENABLED, wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); + if (unlikely(ret < 0)) { + CFGP2P_ERR((" wsec error %d\n", ret)); + } +set_ie: + if (ie_len) { + ret = wl_cfgp2p_set_management_ie(wl, dev, + bssidx, + VNDR_IE_PRBREQ_FLAG, ie, ie_len); + + if (unlikely(ret < 0)) { + CFGP2P_ERR(("set probreq ie occurs error %d\n", ret)); + goto exit; + } + } +exit: + return ret; +} + +/* Disable P2P Discovery + * Parameters: + * @wl : wl_private_data + * Returns 0 if success. + */ +s32 +wl_cfgp2p_disable_discovery(struct wl_priv *wl) +{ + s32 ret = BCME_OK; + CFGP2P_DBG((" enter\n")); + wl_clr_p2p_status(wl, DISCOVERY_ON); + + if (wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) == 0) { + CFGP2P_ERR((" do nothing, not initialized\n")); + goto exit; + } + + ret = wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); + + if (unlikely(ret < 0)) { + + CFGP2P_ERR(("unable to set WL_P2P_DISC_ST_SCAN\n")); + } + /* Do a scan abort to stop the driver's scan engine in case it is still + * waiting out an action frame tx dwell time. + */ + wl_clr_p2p_status(wl, DISCOVERY_ON); + ret = wl_cfgp2p_deinit_discovery(wl); + +exit: + return ret; +} + +s32 +wl_cfgp2p_escan(struct wl_priv *wl, struct net_device *dev, u16 active, + u32 num_chans, u16 *channels, + s32 search_state, u16 action, u32 bssidx, struct ether_addr *tx_dst_addr) +{ + s32 ret = BCME_OK; + s32 memsize; + s32 eparams_size; + u32 i; + s8 *memblk; + wl_p2p_scan_t *p2p_params; + wl_escan_params_t *eparams; + wlc_ssid_t ssid; + /* Scan parameters */ +#define P2PAPI_SCAN_NPROBES 1 +#define P2PAPI_SCAN_DWELL_TIME_MS 80 +#define P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS 40 +#define P2PAPI_SCAN_HOME_TIME_MS 60 +#define P2PAPI_SCAN_NPROBS_TIME_MS 30 +#define P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS 100 + + struct net_device *pri_dev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_PRIMARY); + /* Allocate scan params which need space for 3 channels and 0 ssids */ + eparams_size = (WL_SCAN_PARAMS_FIXED_SIZE + + OFFSETOF(wl_escan_params_t, params)) + + num_chans * sizeof(eparams->params.channel_list[0]); + + memsize = sizeof(wl_p2p_scan_t) + eparams_size; + memblk = scanparambuf; + if (memsize > sizeof(scanparambuf)) { + CFGP2P_ERR((" scanpar buf too small (%u > %u)\n", + memsize, sizeof(scanparambuf))); + return -1; + } + memset(memblk, 0, memsize); + memset(wl->ioctl_buf, 0, WLC_IOCTL_MAXLEN); + if (search_state == WL_P2P_DISC_ST_SEARCH) { + /* + * If we in SEARCH STATE, we don't need to set SSID explictly + * because dongle use P2P WILDCARD internally by default + */ + wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SEARCH, 0, 0, bssidx); + ssid.SSID_len = htod32(0); + + } else if (search_state == WL_P2P_DISC_ST_SCAN) { + /* SCAN STATE 802.11 SCAN + * WFD Supplicant has p2p_find command with (type=progressive, type= full) + * So if P2P_find command with type=progressive, + * we have to set ssid to P2P WILDCARD because + * we just do broadcast scan unless setting SSID + */ + strncpy(ssid.SSID, WL_P2P_WILDCARD_SSID, sizeof(ssid.SSID) - 1); + ssid.SSID[sizeof(ssid.SSID) - 1] = 0; + ssid.SSID_len = htod32(WL_P2P_WILDCARD_SSID_LEN); + wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, bssidx); + } + else { + CFGP2P_ERR((" invalid search state %d\n", search_state)); + return -1; + } + + + /* Fill in the P2P scan structure at the start of the iovar param block */ + p2p_params = (wl_p2p_scan_t*) memblk; + p2p_params->type = 'E'; + /* Fill in the Scan structure that follows the P2P scan structure */ + eparams = (wl_escan_params_t*) (p2p_params + 1); + eparams->params.bss_type = DOT11_BSSTYPE_ANY; + if (active) + eparams->params.scan_type = DOT11_SCANTYPE_ACTIVE; + else + eparams->params.scan_type = DOT11_SCANTYPE_PASSIVE; + + if (tx_dst_addr == NULL) + memcpy(&eparams->params.bssid, ðer_bcast, ETHER_ADDR_LEN); + else + memcpy(&eparams->params.bssid, tx_dst_addr, ETHER_ADDR_LEN); + + if (ssid.SSID_len) + memcpy(&eparams->params.ssid, &ssid, sizeof(wlc_ssid_t)); + + eparams->params.home_time = htod32(P2PAPI_SCAN_HOME_TIME_MS); + + /* SOCIAL_CHAN_CNT + 1 takes care of the Progressive scan supported by + * the supplicant + */ + if ((num_chans == SOCIAL_CHAN_CNT) || (num_chans == SOCIAL_CHAN_CNT + 1)) + eparams->params.active_time = htod32(P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS); + else if (num_chans == AF_PEER_SEARCH_CNT) + eparams->params.active_time = htod32(P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS); + else if (wl_get_drv_status_all(wl, CONNECTED)) + eparams->params.active_time = -1; + else + eparams->params.active_time = htod32(P2PAPI_SCAN_DWELL_TIME_MS); + eparams->params.nprobes = htod32((eparams->params.active_time / + P2PAPI_SCAN_NPROBS_TIME_MS)); + + /* Override scan params to find a peer for a connection */ + if (num_chans == 1) { + eparams->params.active_time = htod32(WL_SCAN_CONNECT_DWELL_TIME_MS); + eparams->params.nprobes = htod32(eparams->params.active_time / + WL_SCAN_JOIN_PROBE_INTERVAL_MS); + } + + if (eparams->params.nprobes <= 0) + eparams->params.nprobes = 1; + CFGP2P_DBG(("nprobes # %d, active_time %d\n", + eparams->params.nprobes, eparams->params.active_time)); + eparams->params.passive_time = htod32(-1); + eparams->params.channel_num = htod32((0 << WL_SCAN_PARAMS_NSSID_SHIFT) | + (num_chans & WL_SCAN_PARAMS_COUNT_MASK)); + + for (i = 0; i < num_chans; i++) { + eparams->params.channel_list[i] = wl_ch_host_to_driver(channels[i]); + } + eparams->version = htod32(ESCAN_REQ_VERSION); + eparams->action = htod16(action); + eparams->sync_id = htod16(0x1234); + CFGP2P_INFO(("SCAN CHANNELS : ")); + + for (i = 0; i < num_chans; i++) { + if (i == 0) CFGP2P_INFO(("%d", channels[i])); + else CFGP2P_INFO((",%d", channels[i])); + } + + CFGP2P_INFO(("\n")); + + ret = wldev_iovar_setbuf_bsscfg(pri_dev, "p2p_scan", + memblk, memsize, wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); + if (ret == BCME_OK) + wl_set_p2p_status(wl, SCANNING); + return ret; +} + +/* search function to reach at common channel to send action frame + * Parameters: + * @wl : wl_private data + * @ndev : net device for bssidx + * @bssidx : bssidx for BSS + * Returns 0 if success. + */ +s32 +wl_cfgp2p_act_frm_search(struct wl_priv *wl, struct net_device *ndev, + s32 bssidx, s32 channel, struct ether_addr *tx_dst_addr) +{ + s32 ret = 0; + u32 chan_cnt = 0; + u16 *default_chan_list = NULL; + if (!p2p_is_on(wl) || ndev == NULL || bssidx == WL_INVALID) + return -BCME_ERROR; + CFGP2P_ERR((" Enter\n")); + if (bssidx == P2PAPI_BSSCFG_PRIMARY) + bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE); + if (channel) + chan_cnt = AF_PEER_SEARCH_CNT; + else + chan_cnt = SOCIAL_CHAN_CNT; + default_chan_list = kzalloc(chan_cnt * sizeof(*default_chan_list), GFP_KERNEL); + if (default_chan_list == NULL) { + CFGP2P_ERR(("channel list allocation failed \n")); + ret = -ENOMEM; + goto exit; + } + if (channel) { + u32 i; + /* insert same channel to the chan_list */ + for (i = 0; i < chan_cnt; i++) { + default_chan_list[i] = channel; + } + } else { + default_chan_list[0] = SOCIAL_CHAN_1; + default_chan_list[1] = SOCIAL_CHAN_2; + default_chan_list[2] = SOCIAL_CHAN_3; + } + ret = wl_cfgp2p_escan(wl, ndev, true, chan_cnt, + default_chan_list, WL_P2P_DISC_ST_SEARCH, + WL_SCAN_ACTION_START, bssidx, tx_dst_addr); + kfree(default_chan_list); +exit: + return ret; +} + +/* Check whether pointed-to IE looks like WPA. */ +#define wl_cfgp2p_is_wpa_ie(ie, tlvs, len) wl_cfgp2p_has_ie(ie, tlvs, len, \ + (const uint8 *)WPS_OUI, WPS_OUI_LEN, WPA_OUI_TYPE) +/* Check whether pointed-to IE looks like WPS. */ +#define wl_cfgp2p_is_wps_ie(ie, tlvs, len) wl_cfgp2p_has_ie(ie, tlvs, len, \ + (const uint8 *)WPS_OUI, WPS_OUI_LEN, WPS_OUI_TYPE) +/* Check whether the given IE looks like WFA P2P IE. */ +#define wl_cfgp2p_is_p2p_ie(ie, tlvs, len) wl_cfgp2p_has_ie(ie, tlvs, len, \ + (const uint8 *)WFA_OUI, WFA_OUI_LEN, WFA_OUI_TYPE_P2P) +/* Check whether the given IE looks like WFA WFDisplay IE. */ +#define WFA_OUI_TYPE_WFD 0x0a /* WiFi Display OUI TYPE */ +#define wl_cfgp2p_is_wfd_ie(ie, tlvs, len) wl_cfgp2p_has_ie(ie, tlvs, len, \ + (const uint8 *)WFA_OUI, WFA_OUI_LEN, WFA_OUI_TYPE_WFD) + +static s32 +wl_cfgp2p_parse_vndr_ies(u8 *parse, u32 len, + struct parsed_vndr_ies *vndr_ies) +{ + s32 err = BCME_OK; + vndr_ie_t *vndrie; + bcm_tlv_t *ie; + struct parsed_vndr_ie_info *parsed_info; + u32 count = 0; + s32 remained_len; + + remained_len = (s32)len; + memset(vndr_ies, 0, sizeof(*vndr_ies)); + + WL_INFO(("---> len %d\n", len)); + ie = (bcm_tlv_t *) parse; + if (!bcm_valid_tlv(ie, remained_len)) + ie = NULL; + while (ie) { + if (count >= MAX_VNDR_IE_NUMBER) + break; + if (ie->id == DOT11_MNG_VS_ID) { + vndrie = (vndr_ie_t *) ie; + /* len should be bigger than OUI length + one data length at least */ + if (vndrie->len < (VNDR_IE_MIN_LEN + 1)) { + CFGP2P_ERR(("%s: invalid vndr ie. length is too small %d\n", + __FUNCTION__, vndrie->len)); + goto end; + } + /* if wpa or wme ie, do not add ie */ + if (!bcmp(vndrie->oui, (u8*)WPA_OUI, WPA_OUI_LEN) && + ((vndrie->data[0] == WPA_OUI_TYPE) || + (vndrie->data[0] == WME_OUI_TYPE))) { + CFGP2P_DBG(("Found WPA/WME oui. Do not add it\n")); + goto end; + } + + parsed_info = &vndr_ies->ie_info[count++]; + + /* save vndr ie information */ + parsed_info->ie_ptr = (char *)vndrie; + parsed_info->ie_len = (vndrie->len + TLV_HDR_LEN); + memcpy(&parsed_info->vndrie, vndrie, sizeof(vndr_ie_t)); + + vndr_ies->count = count; + + CFGP2P_DBG(("\t ** OUI %02x %02x %02x, type 0x%02x \n", + parsed_info->vndrie.oui[0], parsed_info->vndrie.oui[1], + parsed_info->vndrie.oui[2], parsed_info->vndrie.data[0])); + } +end: + ie = bcm_next_tlv(ie, &remained_len); + } + return err; +} + + +/* Delete and Set a management vndr ie to firmware + * Parameters: + * @wl : wl_private data + * @ndev : net device for bssidx + * @bssidx : bssidx for BSS + * @pktflag : packet flag for IE (VNDR_IE_PRBREQ_FLAG,VNDR_IE_PRBRSP_FLAG, VNDR_IE_ASSOCRSP_FLAG, + * VNDR_IE_ASSOCREQ_FLAG) + * @ie : VNDR IE (such as P2P IE , WPS IE) + * @ie_len : VNDR IE Length + * Returns 0 if success. + */ + +s32 +wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, + s32 pktflag, const u8 *vndr_ie, u32 vndr_ie_len) +{ + s32 ret = BCME_OK; + u8 *curr_ie_buf = NULL; + u8 *mgmt_ie_buf = NULL; + u32 mgmt_ie_buf_len = 0; + u32 *mgmt_ie_len = 0; + u32 del_add_ie_buf_len = 0; + u32 total_ie_buf_len = 0; + u32 parsed_ie_buf_len = 0; + struct parsed_vndr_ies old_vndr_ies; + struct parsed_vndr_ies new_vndr_ies; + s32 i; + u8 *ptr; + s32 remained_buf_len; + +#define IE_TYPE(type, bsstype) (wl_to_p2p_bss_saved_ie(wl, bsstype).p2p_ ## type ## _ie) +#define IE_TYPE_LEN(type, bsstype) (wl_to_p2p_bss_saved_ie(wl, bsstype).p2p_ ## type ## _ie_len) + memset(g_mgmt_ie_buf, 0, sizeof(g_mgmt_ie_buf)); + curr_ie_buf = g_mgmt_ie_buf; + CFGP2P_DBG((" bssidx %d, pktflag : 0x%02X\n", bssidx, pktflag)); + if (wl->p2p != NULL) { + switch (pktflag) { + case VNDR_IE_PRBREQ_FLAG : + mgmt_ie_buf = IE_TYPE(probe_req, bssidx); + mgmt_ie_len = &IE_TYPE_LEN(probe_req, bssidx); + mgmt_ie_buf_len = sizeof(IE_TYPE(probe_req, bssidx)); + break; + case VNDR_IE_PRBRSP_FLAG : + mgmt_ie_buf = IE_TYPE(probe_res, bssidx); + mgmt_ie_len = &IE_TYPE_LEN(probe_res, bssidx); + mgmt_ie_buf_len = sizeof(IE_TYPE(probe_res, bssidx)); + break; + case VNDR_IE_ASSOCREQ_FLAG : + mgmt_ie_buf = IE_TYPE(assoc_req, bssidx); + mgmt_ie_len = &IE_TYPE_LEN(assoc_req, bssidx); + mgmt_ie_buf_len = sizeof(IE_TYPE(assoc_req, bssidx)); + break; + case VNDR_IE_ASSOCRSP_FLAG : + mgmt_ie_buf = IE_TYPE(assoc_res, bssidx); + mgmt_ie_len = &IE_TYPE_LEN(assoc_res, bssidx); + mgmt_ie_buf_len = sizeof(IE_TYPE(assoc_res, bssidx)); + break; + case VNDR_IE_BEACON_FLAG : + mgmt_ie_buf = IE_TYPE(beacon, bssidx); + mgmt_ie_len = &IE_TYPE_LEN(beacon, bssidx); + mgmt_ie_buf_len = sizeof(IE_TYPE(beacon, bssidx)); + break; + default: + mgmt_ie_buf = NULL; + mgmt_ie_len = NULL; + CFGP2P_ERR(("not suitable type\n")); + return -1; + } + } else if (wl_get_mode_by_netdev(wl, ndev) == WL_MODE_AP) { + switch (pktflag) { + case VNDR_IE_PRBRSP_FLAG : + mgmt_ie_buf = wl->ap_info->probe_res_ie; + mgmt_ie_len = &wl->ap_info->probe_res_ie_len; + mgmt_ie_buf_len = sizeof(wl->ap_info->probe_res_ie); + break; + case VNDR_IE_BEACON_FLAG : + mgmt_ie_buf = wl->ap_info->beacon_ie; + mgmt_ie_len = &wl->ap_info->beacon_ie_len; + mgmt_ie_buf_len = sizeof(wl->ap_info->beacon_ie); + break; + default: + mgmt_ie_buf = NULL; + mgmt_ie_len = NULL; + CFGP2P_ERR(("not suitable type\n")); + return -1; + } + bssidx = 0; + } else if (wl_get_mode_by_netdev(wl, ndev) == WL_MODE_BSS) { + switch (pktflag) { + case VNDR_IE_PRBREQ_FLAG : + mgmt_ie_buf = wl->sta_info->probe_req_ie; + mgmt_ie_len = &wl->sta_info->probe_req_ie_len; + mgmt_ie_buf_len = sizeof(wl->sta_info->probe_req_ie); + break; + case VNDR_IE_ASSOCREQ_FLAG : + mgmt_ie_buf = wl->sta_info->assoc_req_ie; + mgmt_ie_len = &wl->sta_info->assoc_req_ie_len; + mgmt_ie_buf_len = sizeof(wl->sta_info->assoc_req_ie); + break; + default: + mgmt_ie_buf = NULL; + mgmt_ie_len = NULL; + CFGP2P_ERR(("not suitable type\n")); + return -1; + } + bssidx = 0; + } else { + CFGP2P_ERR(("not suitable type\n")); + return -1; + } + + if (vndr_ie_len > mgmt_ie_buf_len) { + CFGP2P_ERR(("extra IE size too big\n")); + ret = -ENOMEM; + } else { + /* parse and save new vndr_ie in curr_ie_buff before comparing it */ + if (vndr_ie && vndr_ie_len && curr_ie_buf) { + ptr = curr_ie_buf; + + wl_cfgp2p_parse_vndr_ies((u8*)vndr_ie, + vndr_ie_len, &new_vndr_ies); + + for (i = 0; i < new_vndr_ies.count; i++) { + struct parsed_vndr_ie_info *vndrie_info = + &new_vndr_ies.ie_info[i]; + + memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr, + vndrie_info->ie_len); + parsed_ie_buf_len += vndrie_info->ie_len; + } + } + + if (mgmt_ie_buf != NULL) { + if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) && + (memcmp(mgmt_ie_buf, curr_ie_buf, parsed_ie_buf_len) == 0)) { + CFGP2P_INFO(("Previous mgmt IE is equals to current IE")); + goto exit; + } + + /* parse old vndr_ie */ + wl_cfgp2p_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, + &old_vndr_ies); + + /* make a command to delete old ie */ + for (i = 0; i < old_vndr_ies.count; i++) { + struct parsed_vndr_ie_info *vndrie_info = + &old_vndr_ies.ie_info[i]; + + CFGP2P_INFO(("DELETED ID : %d, Len: %d , OUI:%02x:%02x:%02x\n", + vndrie_info->vndrie.id, vndrie_info->vndrie.len, + vndrie_info->vndrie.oui[0], vndrie_info->vndrie.oui[1], + vndrie_info->vndrie.oui[2])); + + del_add_ie_buf_len = wl_cfgp2p_vndr_ie(wl, curr_ie_buf, + bssidx, pktflag, vndrie_info->vndrie.oui, + vndrie_info->vndrie.id, + vndrie_info->ie_ptr + VNDR_IE_FIXED_LEN, + vndrie_info->ie_len - VNDR_IE_FIXED_LEN, + "del"); + + curr_ie_buf += del_add_ie_buf_len; + total_ie_buf_len += del_add_ie_buf_len; + } + } + + *mgmt_ie_len = 0; + /* Add if there is any extra IE */ + if (mgmt_ie_buf && parsed_ie_buf_len) { + ptr = mgmt_ie_buf; + + remained_buf_len = mgmt_ie_buf_len; + + /* make a command to add new ie */ + for (i = 0; i < new_vndr_ies.count; i++) { + struct parsed_vndr_ie_info *vndrie_info = + &new_vndr_ies.ie_info[i]; + + CFGP2P_INFO(("ADDED ID : %d, Len: %d(%d), OUI:%02x:%02x:%02x\n", + vndrie_info->vndrie.id, vndrie_info->vndrie.len, + vndrie_info->ie_len - 2, + vndrie_info->vndrie.oui[0], vndrie_info->vndrie.oui[1], + vndrie_info->vndrie.oui[2])); + + del_add_ie_buf_len = wl_cfgp2p_vndr_ie(wl, curr_ie_buf, + bssidx, pktflag, vndrie_info->vndrie.oui, + vndrie_info->vndrie.id, + vndrie_info->ie_ptr + VNDR_IE_FIXED_LEN, + vndrie_info->ie_len - VNDR_IE_FIXED_LEN, + "add"); + + /* verify remained buf size before copy data */ + if (remained_buf_len >= vndrie_info->ie_len) { + remained_buf_len -= vndrie_info->ie_len; + } else { + CFGP2P_ERR(("no space in mgmt_ie_buf: pktflag = %d, " + "found vndr ies # = %d(cur %d), remained len %d, " + "cur mgmt_ie_len %d, new ie len = %d\n", + pktflag, new_vndr_ies.count, i, remained_buf_len, + *mgmt_ie_len, vndrie_info->ie_len)); + break; + } + + /* save the parsed IE in wl struct */ + memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr, + vndrie_info->ie_len); + *mgmt_ie_len += vndrie_info->ie_len; + + curr_ie_buf += del_add_ie_buf_len; + total_ie_buf_len += del_add_ie_buf_len; + } + } + if (total_ie_buf_len) { + ret = wldev_iovar_setbuf_bsscfg(ndev, "vndr_ie", g_mgmt_ie_buf, + total_ie_buf_len, wl->ioctl_buf, WLC_IOCTL_MAXLEN, + bssidx, &wl->ioctl_buf_sync); + if (ret) + CFGP2P_ERR(("vndr ie set error : %d\n", ret)); + } + } +#undef IE_TYPE +#undef IE_TYPE_LEN +exit: + return ret; +} + +/* Clear the manament IE buffer of BSSCFG + * Parameters: + * @wl : wl_private data + * @bssidx : bssidx for BSS + * + * Returns 0 if success. + */ +s32 +wl_cfgp2p_clear_management_ie(struct wl_priv *wl, s32 bssidx) +{ + s32 vndrie_flag[] = {VNDR_IE_BEACON_FLAG, VNDR_IE_PRBRSP_FLAG, VNDR_IE_ASSOCRSP_FLAG, + VNDR_IE_PRBREQ_FLAG, VNDR_IE_ASSOCREQ_FLAG}; + s32 index = -1; + struct net_device *ndev = wl_cfgp2p_find_ndev(wl, bssidx); +#define INIT_IE(IE_TYPE, BSS_TYPE) \ + do { \ + memset(wl_to_p2p_bss_saved_ie(wl, BSS_TYPE).p2p_ ## IE_TYPE ## _ie, 0, \ + sizeof(wl_to_p2p_bss_saved_ie(wl, BSS_TYPE).p2p_ ## IE_TYPE ## _ie)); \ + wl_to_p2p_bss_saved_ie(wl, BSS_TYPE).p2p_ ## IE_TYPE ## _ie_len = 0; \ + } while (0); + + if (bssidx < 0 || ndev == NULL) { + CFGP2P_ERR(("invalid %s\n", (bssidx < 0) ? "bssidx" : "ndev")); + return BCME_BADARG; + } + for (index = 0; index < ARRAYSIZE(vndrie_flag); index++) { + /* clean up vndr ies in dongle */ + wl_cfgp2p_set_management_ie(wl, ndev, bssidx, vndrie_flag[index], NULL, 0); + } + INIT_IE(probe_req, bssidx); + INIT_IE(probe_res, bssidx); + INIT_IE(assoc_req, bssidx); + INIT_IE(assoc_res, bssidx); + INIT_IE(beacon, bssidx); + return BCME_OK; +} + + +/* Is any of the tlvs the expected entry? If + * not update the tlvs buffer pointer/length. + */ +static bool +wl_cfgp2p_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len, const u8 *oui, u32 oui_len, u8 type) +{ + /* If the contents match the OUI and the type */ + if (ie[TLV_LEN_OFF] >= oui_len + 1 && + !bcmp(&ie[TLV_BODY_OFF], oui, oui_len) && + type == ie[TLV_BODY_OFF + oui_len]) { + return TRUE; + } + + if (tlvs == NULL) + return FALSE; + /* point to the next ie */ + ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN; + /* calculate the length of the rest of the buffer */ + *tlvs_len -= (int)(ie - *tlvs); + /* update the pointer to the start of the buffer */ + *tlvs = ie; + + return FALSE; +} + +wpa_ie_fixed_t * +wl_cfgp2p_find_wpaie(u8 *parse, u32 len) +{ + bcm_tlv_t *ie; + + while ((ie = bcm_parse_tlvs(parse, (u32)len, DOT11_MNG_VS_ID))) { + if (wl_cfgp2p_is_wpa_ie((u8*)ie, &parse, &len)) { + return (wpa_ie_fixed_t *)ie; + } + } + return NULL; +} + +wpa_ie_fixed_t * +wl_cfgp2p_find_wpsie(u8 *parse, u32 len) +{ + bcm_tlv_t *ie; + + while ((ie = bcm_parse_tlvs(parse, (u32)len, DOT11_MNG_VS_ID))) { + if (wl_cfgp2p_is_wps_ie((u8*)ie, &parse, &len)) { + return (wpa_ie_fixed_t *)ie; + } + } + return NULL; +} + +wifi_p2p_ie_t * +wl_cfgp2p_find_p2pie(u8 *parse, u32 len) +{ + bcm_tlv_t *ie; + + while ((ie = bcm_parse_tlvs(parse, (int)len, DOT11_MNG_VS_ID))) { + if (wl_cfgp2p_is_p2p_ie((uint8*)ie, &parse, &len)) { + return (wifi_p2p_ie_t *)ie; + } + } + return NULL; +} + +wifi_wfd_ie_t * +wl_cfgp2p_find_wfdie(u8 *parse, u32 len) +{ + bcm_tlv_t *ie; + + while ((ie = bcm_parse_tlvs(parse, (int)len, DOT11_MNG_VS_ID))) { + if (wl_cfgp2p_is_wfd_ie((uint8*)ie, &parse, &len)) { + return (wifi_wfd_ie_t *)ie; + } + } + return NULL; +} +static u32 +wl_cfgp2p_vndr_ie(struct wl_priv *wl, u8 *iebuf, s32 bssidx, s32 pktflag, + s8 *oui, s32 ie_id, s8 *data, s32 datalen, const s8* add_del_cmd) +{ + vndr_ie_setbuf_t hdr; /* aligned temporary vndr_ie buffer header */ + s32 iecount; + u32 data_offset; + + /* Validate the pktflag parameter */ + if ((pktflag & ~(VNDR_IE_BEACON_FLAG | VNDR_IE_PRBRSP_FLAG | + VNDR_IE_ASSOCRSP_FLAG | VNDR_IE_AUTHRSP_FLAG | + VNDR_IE_PRBREQ_FLAG | VNDR_IE_ASSOCREQ_FLAG))) { + CFGP2P_ERR(("p2pwl_vndr_ie: Invalid packet flag 0x%x\n", pktflag)); + return -1; + } + + /* Copy the vndr_ie SET command ("add"/"del") to the buffer */ + strncpy(hdr.cmd, add_del_cmd, VNDR_IE_CMD_LEN - 1); + hdr.cmd[VNDR_IE_CMD_LEN - 1] = '\0'; + + /* Set the IE count - the buffer contains only 1 IE */ + iecount = htod32(1); + memcpy((void *)&hdr.vndr_ie_buffer.iecount, &iecount, sizeof(s32)); + + /* Copy packet flags that indicate which packets will contain this IE */ + pktflag = htod32(pktflag); + memcpy((void *)&hdr.vndr_ie_buffer.vndr_ie_list[0].pktflag, &pktflag, + sizeof(u32)); + + /* Add the IE ID to the buffer */ + hdr.vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.id = ie_id; + + /* Add the IE length to the buffer */ + hdr.vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.len = + (uint8) VNDR_IE_MIN_LEN + datalen; + + /* Add the IE OUI to the buffer */ + hdr.vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.oui[0] = oui[0]; + hdr.vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.oui[1] = oui[1]; + hdr.vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.oui[2] = oui[2]; + + /* Copy the aligned temporary vndr_ie buffer header to the IE buffer */ + memcpy(iebuf, &hdr, sizeof(hdr) - 1); + + /* Copy the IE data to the IE buffer */ + data_offset = + (u8*)&hdr.vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.data[0] - + (u8*)&hdr; + memcpy(iebuf + data_offset, data, datalen); + return data_offset + datalen; + +} + +/* + * Search the bssidx based on dev argument + * Parameters: + * @wl : wl_private data + * @ndev : net device to search bssidx + * Returns bssidx for ndev + */ +s32 +wl_cfgp2p_find_idx(struct wl_priv *wl, struct net_device *ndev) +{ + u32 i; + s32 index = -1; + + if (ndev == NULL) { + CFGP2P_ERR((" ndev is NULL\n")); + goto exit; + } + if (!wl->p2p_supported) { + return P2PAPI_BSSCFG_PRIMARY; + } + for (i = 0; i < P2PAPI_BSSCFG_MAX; i++) { + if (ndev == wl_to_p2p_bss_ndev(wl, i)) { + index = wl_to_p2p_bss_bssidx(wl, i); + break; + } + } + if (index == -1) + return P2PAPI_BSSCFG_PRIMARY; +exit: + return index; +} + +struct net_device * +wl_cfgp2p_find_ndev(struct wl_priv *wl, s32 bssidx) +{ + u32 i; + struct net_device *ndev = NULL; + if (bssidx < 0) { + CFGP2P_ERR((" bsscfg idx is invalid\n")); + goto exit; + } + + for (i = 0; i < P2PAPI_BSSCFG_MAX; i++) { + if (bssidx == wl_to_p2p_bss_bssidx(wl, i)) { + ndev = wl_to_p2p_bss_ndev(wl, i); + break; + } + } + +exit: + return ndev; +} + +/* + * Callback function for WLC_E_P2P_DISC_LISTEN_COMPLETE + */ +s32 +wl_cfgp2p_listen_complete(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + s32 ret = BCME_OK; + struct net_device *netdev; + if (!wl || !wl->p2p) + return BCME_ERROR; + if (wl->p2p_net == ndev) { + netdev = wl_to_prmry_ndev(wl); + } else { + netdev = ndev; + } + CFGP2P_DBG((" Enter\n")); + if (wl_get_p2p_status(wl, LISTEN_EXPIRED) == 0) { + wl_set_p2p_status(wl, LISTEN_EXPIRED); + if (timer_pending(&wl->p2p->listen_timer)) { + del_timer_sync(&wl->p2p->listen_timer); + } + + if (wl->afx_hdl->is_listen == TRUE && + wl_get_drv_status_all(wl, FINDING_COMMON_CHANNEL)) { + WL_DBG(("Listen DONE for action frame\n")); + complete(&wl->act_frm_scan); + } +#ifdef WL_CFG80211_SYNC_GON + else if (wl_get_drv_status_all(wl, WAITING_NEXT_ACT_FRM_LISTEN)) { + wl_clr_drv_status(wl, WAITING_NEXT_ACT_FRM_LISTEN, netdev); + WL_DBG(("Listen DONE and wake up wait_next_af !!(%d)\n", + jiffies_to_msecs(jiffies - wl->af_tx_sent_jiffies))); + + if (wl_get_drv_status_all(wl, WAITING_NEXT_ACT_FRM)) + wl_clr_drv_status(wl, WAITING_NEXT_ACT_FRM, netdev); + + complete(&wl->wait_next_af); + } +#endif /* WL_CFG80211_SYNC_GON */ + +#ifndef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST + if (wl_get_drv_status_all(wl, REMAINING_ON_CHANNEL)) { +#else + if (wl_get_drv_status_all(wl, REMAINING_ON_CHANNEL) || + wl_get_drv_status_all(wl, FAKE_REMAINING_ON_CHANNEL)) { +#endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ + WL_DBG(("Listen DONE for ramain on channel expired\n")); + wl_clr_drv_status(wl, REMAINING_ON_CHANNEL, netdev); +#ifdef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST + wl_clr_drv_status(wl, FAKE_REMAINING_ON_CHANNEL, netdev); +#endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ + if (ndev && (ndev->ieee80211_ptr != NULL)) { + cfg80211_remain_on_channel_expired(ndev, wl->last_roc_id, + &wl->remain_on_chan, wl->remain_on_chan_type, GFP_KERNEL); + } + } + if (wl_add_remove_eventmsg(wl_to_prmry_ndev(wl), + WLC_E_P2P_PROBREQ_MSG, false) != BCME_OK) { + CFGP2P_ERR((" failed to unset WLC_E_P2P_PROPREQ_MSG\n")); + } + } else + wl_clr_p2p_status(wl, LISTEN_EXPIRED); + + return ret; + +} + +/* + * Timer expire callback function for LISTEN + * We can't report cfg80211_remain_on_channel_expired from Timer ISR context, + * so lets do it from thread context. + */ +void +wl_cfgp2p_listen_expired(unsigned long data) +{ + wl_event_msg_t msg; + struct wl_priv *wl = (struct wl_priv *) data; + CFGP2P_DBG((" Enter\n")); + bzero(&msg, sizeof(wl_event_msg_t)); + msg.event_type = hton32(WLC_E_P2P_DISC_LISTEN_COMPLETE); + wl_cfg80211_event(wl->p2p_net ? wl->p2p_net : + wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE), &msg, NULL); +} +/* + * Routine for cancelling the P2P LISTEN + */ +static s32 +wl_cfgp2p_cancel_listen(struct wl_priv *wl, struct net_device *ndev, + bool notify) +{ + WL_DBG(("Enter \n")); + /* Irrespective of whether timer is running or not, reset + * the LISTEN state. + */ + if (timer_pending(&wl->p2p->listen_timer)) { + del_timer_sync(&wl->p2p->listen_timer); + if (notify) + if (ndev && ndev->ieee80211_ptr) { + cfg80211_remain_on_channel_expired(ndev, wl->last_roc_id, + &wl->remain_on_chan, wl->remain_on_chan_type, + GFP_KERNEL); + } + } + return 0; +} +/* + * Do a P2P Listen on the given channel for the given duration. + * A listen consists of sitting idle and responding to P2P probe requests + * with a P2P probe response. + * + * This fn assumes dongle p2p device discovery is already enabled. + * Parameters : + * @wl : wl_private data + * @channel : channel to listen + * @duration_ms : the time (milli seconds) to wait + */ +s32 +wl_cfgp2p_discover_listen(struct wl_priv *wl, s32 channel, u32 duration_ms) +{ +#define EXTRA_DELAY_TIME 100 + s32 ret = BCME_OK; + struct timer_list *_timer; + s32 extra_delay; + struct net_device *netdev = wl_to_prmry_ndev(wl); + + CFGP2P_DBG((" Enter Listen Channel : %d, Duration : %d\n", channel, duration_ms)); + if (unlikely(wl_get_p2p_status(wl, DISCOVERY_ON) == 0)) { + + CFGP2P_ERR((" Discovery is not set, so we have noting to do\n")); + + ret = BCME_NOTREADY; + goto exit; + } + if (timer_pending(&wl->p2p->listen_timer)) { + CFGP2P_DBG(("previous LISTEN is not completed yet\n")); + goto exit; + + } +#ifndef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST + else + wl_clr_p2p_status(wl, LISTEN_EXPIRED); +#endif /* not WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ + if (wl_add_remove_eventmsg(netdev, WLC_E_P2P_PROBREQ_MSG, true) != BCME_OK) { + CFGP2P_ERR((" failed to set WLC_E_P2P_PROPREQ_MSG\n")); + } + + ret = wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_LISTEN, channel, (u16) duration_ms, + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); + _timer = &wl->p2p->listen_timer; + + /* We will wait to receive WLC_E_P2P_DISC_LISTEN_COMPLETE from dongle , + * otherwise we will wait up to duration_ms + 100ms + duration / 10 + */ + if (ret == BCME_OK) { + extra_delay = EXTRA_DELAY_TIME + (duration_ms / 10); + } else { + /* if failed to set listen, it doesn't need to wait whole duration. */ + duration_ms = 100 + duration_ms / 20; + extra_delay = 0; + } + + INIT_TIMER(_timer, wl_cfgp2p_listen_expired, duration_ms, extra_delay); +#ifdef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST + wl_clr_p2p_status(wl, LISTEN_EXPIRED); +#endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ + +#undef EXTRA_DELAY_TIME +exit: + return ret; +} + + +s32 +wl_cfgp2p_discover_enable_search(struct wl_priv *wl, u8 enable) +{ + s32 ret = BCME_OK; + CFGP2P_DBG((" Enter\n")); + if (!wl_get_p2p_status(wl, DISCOVERY_ON)) { + + CFGP2P_DBG((" do nothing, discovery is off\n")); + return ret; + } + if (wl_get_p2p_status(wl, SEARCH_ENABLED) == enable) { + CFGP2P_DBG(("already : %d\n", enable)); + return ret; + } + + wl_chg_p2p_status(wl, SEARCH_ENABLED); + /* When disabling Search, reset the WL driver's p2p discovery state to + * WL_P2P_DISC_ST_SCAN. + */ + if (!enable) { + wl_clr_p2p_status(wl, SCANNING); + ret = wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); + } + + return ret; +} + +/* + * Callback function for WLC_E_ACTION_FRAME_COMPLETE, WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE + */ +s32 +wl_cfgp2p_action_tx_complete(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + s32 ret = BCME_OK; + u32 event_type = ntoh32(e->event_type); + u32 status = ntoh32(e->status); + CFGP2P_DBG((" Enter\n")); + if (event_type == WLC_E_ACTION_FRAME_COMPLETE) { + + CFGP2P_INFO((" WLC_E_ACTION_FRAME_COMPLETE is received : %d\n", status)); + if (status == WLC_E_STATUS_SUCCESS) { + wl_set_p2p_status(wl, ACTION_TX_COMPLETED); + CFGP2P_DBG(("WLC_E_ACTION_FRAME_COMPLETE : ACK\n")); + } + else { + wl_set_p2p_status(wl, ACTION_TX_NOACK); + CFGP2P_INFO(("WLC_E_ACTION_FRAME_COMPLETE : NO ACK\n")); + wl_stop_wait_next_action_frame(wl, ndev); + } + } else { + CFGP2P_INFO((" WLC_E_ACTION_FRAME_OFFCHAN_COMPLETE is received," + "status : %d\n", status)); + + if (wl_get_drv_status_all(wl, SENDING_ACT_FRM)) + complete(&wl->send_af_done); + } + return ret; +} +/* Send an action frame immediately without doing channel synchronization. + * + * This function does not wait for a completion event before returning. + * The WLC_E_ACTION_FRAME_COMPLETE event will be received when the action + * frame is transmitted. + * The WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE event will be received when an + * 802.11 ack has been received for the sent action frame. + */ +s32 +wl_cfgp2p_tx_action_frame(struct wl_priv *wl, struct net_device *dev, + wl_af_params_t *af_params, s32 bssidx) +{ + s32 ret = BCME_OK; + s32 timeout = 0; + wl_eventmsg_buf_t buf; + + + CFGP2P_INFO(("\n")); + CFGP2P_INFO(("channel : %u , dwell time : %u\n", + af_params->channel, af_params->dwell_time)); + + wl_clr_p2p_status(wl, ACTION_TX_COMPLETED); + wl_clr_p2p_status(wl, ACTION_TX_NOACK); + + bzero(&buf, sizeof(wl_eventmsg_buf_t)); + wl_cfg80211_add_to_eventbuffer(&buf, WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE, true); + wl_cfg80211_add_to_eventbuffer(&buf, WLC_E_ACTION_FRAME_COMPLETE, true); + if ((ret = wl_cfg80211_apply_eventbuffer(wl_to_prmry_ndev(wl), wl, &buf)) < 0) + return ret; + +#define MAX_WAIT_TIME 2000 + if (bssidx == P2PAPI_BSSCFG_PRIMARY) + bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE); + + wl->af_sent_channel = af_params->channel; +#ifdef WL_CFG80211_SYNC_GON + wl->af_tx_sent_jiffies = jiffies; +#endif /* WL_CFG80211_SYNC_GON */ + + ret = wldev_iovar_setbuf_bsscfg(dev, "actframe", af_params, sizeof(*af_params), + wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); + + if (ret < 0) { + CFGP2P_ERR((" sending action frame is failed\n")); + goto exit; + } + + timeout = wait_for_completion_timeout(&wl->send_af_done, msecs_to_jiffies(MAX_WAIT_TIME)); + + if (timeout > 0 && wl_get_p2p_status(wl, ACTION_TX_COMPLETED)) { + CFGP2P_INFO(("tx action frame operation is completed\n")); + ret = BCME_OK; + } else { + ret = BCME_ERROR; + CFGP2P_INFO(("tx action frame operation is failed\n")); + } + /* clear status bit for action tx */ + wl_clr_p2p_status(wl, ACTION_TX_COMPLETED); + wl_clr_p2p_status(wl, ACTION_TX_NOACK); + +exit: + CFGP2P_INFO((" via act frame iovar : status = %d\n", ret)); + + bzero(&buf, sizeof(wl_eventmsg_buf_t)); + wl_cfg80211_add_to_eventbuffer(&buf, WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE, false); + wl_cfg80211_add_to_eventbuffer(&buf, WLC_E_ACTION_FRAME_COMPLETE, false); + if ((ret = wl_cfg80211_apply_eventbuffer(wl_to_prmry_ndev(wl), wl, &buf)) < 0) + WL_ERR(("TX frame events revert back failed \n")); + +#undef MAX_WAIT_TIME + return ret; +} + +/* Generate our P2P Device Address and P2P Interface Address from our primary + * MAC address. + */ +void +wl_cfgp2p_generate_bss_mac(struct ether_addr *primary_addr, + struct ether_addr *out_dev_addr, struct ether_addr *out_int_addr) +{ + memset(out_dev_addr, 0, sizeof(*out_dev_addr)); + memset(out_int_addr, 0, sizeof(*out_int_addr)); + + /* Generate the P2P Device Address. This consists of the device's + * primary MAC address with the locally administered bit set. + */ + memcpy(out_dev_addr, primary_addr, sizeof(*out_dev_addr)); + out_dev_addr->octet[0] |= 0x02; + + /* Generate the P2P Interface Address. If the discovery and connection + * BSSCFGs need to simultaneously co-exist, then this address must be + * different from the P2P Device Address. + */ + memcpy(out_int_addr, out_dev_addr, sizeof(*out_int_addr)); + out_int_addr->octet[4] ^= 0x80; + +} + +/* P2P IF Address change to Virtual Interface MAC Address */ +void +wl_cfg80211_change_ifaddr(u8* buf, struct ether_addr *p2p_int_addr, u8 element_id) +{ + wifi_p2p_ie_t *ie = (wifi_p2p_ie_t*) buf; + u16 len = ie->len; + u8 *subel; + u8 subelt_id; + u16 subelt_len; + CFGP2P_DBG((" Enter\n")); + + /* Point subel to the P2P IE's subelt field. + * Subtract the preceding fields (id, len, OUI, oui_type) from the length. + */ + subel = ie->subelts; + len -= 4; /* exclude OUI + OUI_TYPE */ + + while (len >= 3) { + /* attribute id */ + subelt_id = *subel; + subel += 1; + len -= 1; + + /* 2-byte little endian */ + subelt_len = *subel++; + subelt_len |= *subel++ << 8; + + len -= 2; + len -= subelt_len; /* for the remaining subelt fields */ + + if (subelt_id == element_id) { + if (subelt_id == P2P_SEID_INTINTADDR) { + memcpy(subel, p2p_int_addr->octet, ETHER_ADDR_LEN); + CFGP2P_INFO(("Intended P2P Interface Address ATTR FOUND\n")); + } else if (subelt_id == P2P_SEID_DEV_ID) { + memcpy(subel, p2p_int_addr->octet, ETHER_ADDR_LEN); + CFGP2P_INFO(("Device ID ATTR FOUND\n")); + } else if (subelt_id == P2P_SEID_DEV_INFO) { + memcpy(subel, p2p_int_addr->octet, ETHER_ADDR_LEN); + CFGP2P_INFO(("Device INFO ATTR FOUND\n")); + } else if (subelt_id == P2P_SEID_GROUP_ID) { + memcpy(subel, p2p_int_addr->octet, ETHER_ADDR_LEN); + CFGP2P_INFO(("GROUP ID ATTR FOUND\n")); + } return; + } else { + CFGP2P_DBG(("OTHER id : %d\n", subelt_id)); + } + subel += subelt_len; + } +} +/* + * Check if a BSS is up. + * This is a common implementation called by most OSL implementations of + * p2posl_bss_isup(). DO NOT call this function directly from the + * common code -- call p2posl_bss_isup() instead to allow the OSL to + * override the common implementation if necessary. + */ +bool +wl_cfgp2p_bss_isup(struct net_device *ndev, int bsscfg_idx) +{ + s32 result, val; + bool isup = false; + s8 getbuf[64]; + + /* Check if the BSS is up */ + *(int*)getbuf = -1; + result = wldev_iovar_getbuf_bsscfg(ndev, "bss", &bsscfg_idx, + sizeof(bsscfg_idx), getbuf, sizeof(getbuf), 0, NULL); + if (result != 0) { + CFGP2P_ERR(("'wl bss -C %d' failed: %d\n", bsscfg_idx, result)); + CFGP2P_ERR(("NOTE: this ioctl error is normal " + "when the BSS has not been created yet.\n")); + } else { + val = *(int*)getbuf; + val = dtoh32(val); + CFGP2P_INFO(("---wl bss -C %d ==> %d\n", bsscfg_idx, val)); + isup = (val ? TRUE : FALSE); + } + return isup; +} + + +/* Bring up or down a BSS */ +s32 +wl_cfgp2p_bss(struct wl_priv *wl, struct net_device *ndev, s32 bsscfg_idx, s32 up) +{ + s32 ret = BCME_OK; + s32 val = up ? 1 : 0; + + struct { + s32 cfg; + s32 val; + } bss_setbuf; + + bss_setbuf.cfg = htod32(bsscfg_idx); + bss_setbuf.val = htod32(val); + CFGP2P_INFO(("---wl bss -C %d %s\n", bsscfg_idx, up ? "up" : "down")); + ret = wldev_iovar_setbuf(ndev, "bss", &bss_setbuf, sizeof(bss_setbuf), + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); + + if (ret != 0) { + CFGP2P_ERR(("'bss %d' failed with %d\n", up, ret)); + } + + return ret; +} + +/* Check if 'p2p' is supported in the driver */ +s32 +wl_cfgp2p_supported(struct wl_priv *wl, struct net_device *ndev) +{ + s32 ret = BCME_OK; + s32 p2p_supported = 0; + ret = wldev_iovar_getint(ndev, "p2p", + &p2p_supported); + if (ret < 0) { + CFGP2P_ERR(("wl p2p error %d\n", ret)); + return 0; + } + if (p2p_supported == 1) { + CFGP2P_INFO(("p2p is supported\n")); + } else { + CFGP2P_INFO(("p2p is unsupported\n")); + p2p_supported = 0; + } + return p2p_supported; +} + +/* Cleanup P2P resources */ +s32 +wl_cfgp2p_down(struct wl_priv *wl) +{ + s32 i = 0, index = -1; + wl_cfgp2p_cancel_listen(wl, + wl->p2p_net ? wl->p2p_net : wl_to_prmry_ndev(wl), TRUE); + for (i = 0; i < P2PAPI_BSSCFG_MAX; i++) { + index = wl_to_p2p_bss_bssidx(wl, i); + if (index != WL_INVALID) + wl_cfgp2p_clear_management_ie(wl, index); + } + wl_cfgp2p_deinit_priv(wl); + return 0; +} + +s32 +wl_cfgp2p_set_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf, int len) +{ + s32 ret = -1; + int count, start, duration; + wl_p2p_sched_t dongle_noa; + + CFGP2P_DBG((" Enter\n")); + + memset(&dongle_noa, 0, sizeof(dongle_noa)); + + if (wl->p2p && wl->p2p->vif_created) { + + wl->p2p->noa.desc[0].start = 0; + + sscanf(buf, "%10d %10d %10d", &count, &start, &duration); + CFGP2P_DBG(("set_p2p_noa count %d start %d duration %d\n", + count, start, duration)); + if (count != -1) + wl->p2p->noa.desc[0].count = count; + + /* supplicant gives interval as start */ + if (start != -1) + wl->p2p->noa.desc[0].interval = start; + + if (duration != -1) + wl->p2p->noa.desc[0].duration = duration; + + if (wl->p2p->noa.desc[0].count != 255) { + wl->p2p->noa.desc[0].start = 200; + dongle_noa.type = WL_P2P_SCHED_TYPE_REQ_ABS; + dongle_noa.action = WL_P2P_SCHED_ACTION_GOOFF; + dongle_noa.option = WL_P2P_SCHED_OPTION_TSFOFS; + } + else { + /* Continuous NoA interval. */ + dongle_noa.action = WL_P2P_SCHED_ACTION_NONE; + dongle_noa.type = WL_P2P_SCHED_TYPE_ABS; + if ((wl->p2p->noa.desc[0].interval == 102) || + (wl->p2p->noa.desc[0].interval == 100)) { + wl->p2p->noa.desc[0].start = 100 - + wl->p2p->noa.desc[0].duration; + dongle_noa.option = WL_P2P_SCHED_OPTION_BCNPCT; + } + else { + dongle_noa.option = WL_P2P_SCHED_OPTION_NORMAL; + } + } + /* Put the noa descriptor in dongle format for dongle */ + dongle_noa.desc[0].count = htod32(wl->p2p->noa.desc[0].count); + if (dongle_noa.option == WL_P2P_SCHED_OPTION_BCNPCT) { + dongle_noa.desc[0].start = htod32(wl->p2p->noa.desc[0].start); + dongle_noa.desc[0].duration = htod32(wl->p2p->noa.desc[0].duration); + } + else { + dongle_noa.desc[0].start = htod32(wl->p2p->noa.desc[0].start*1000); + dongle_noa.desc[0].duration = htod32(wl->p2p->noa.desc[0].duration*1000); + } + dongle_noa.desc[0].interval = htod32(wl->p2p->noa.desc[0].interval*1000); + + ret = wldev_iovar_setbuf(wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION), + "p2p_noa", &dongle_noa, sizeof(dongle_noa), wl->ioctl_buf, WLC_IOCTL_MAXLEN, + &wl->ioctl_buf_sync); + + if (ret < 0) { + CFGP2P_ERR(("fw set p2p_noa failed %d\n", ret)); + } + } + else { + CFGP2P_ERR(("ERROR: set_noa in non-p2p mode\n")); + } + return ret; +} +s32 +wl_cfgp2p_get_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf, int buf_len) +{ + + wifi_p2p_noa_desc_t *noa_desc; + int len = 0, i; + char _buf[200]; + + CFGP2P_DBG((" Enter\n")); + buf[0] = '\0'; + if (wl->p2p && wl->p2p->vif_created) { + if (wl->p2p->noa.desc[0].count || wl->p2p->ops.ops) { + _buf[0] = 1; /* noa index */ + _buf[1] = (wl->p2p->ops.ops ? 0x80: 0) | + (wl->p2p->ops.ctw & 0x7f); /* ops + ctw */ + len += 2; + if (wl->p2p->noa.desc[0].count) { + noa_desc = (wifi_p2p_noa_desc_t*)&_buf[len]; + noa_desc->cnt_type = wl->p2p->noa.desc[0].count; + noa_desc->duration = wl->p2p->noa.desc[0].duration; + noa_desc->interval = wl->p2p->noa.desc[0].interval; + noa_desc->start = wl->p2p->noa.desc[0].start; + len += sizeof(wifi_p2p_noa_desc_t); + } + if (buf_len <= len * 2) { + CFGP2P_ERR(("ERROR: buf_len %d in not enough for" + "returning noa in string format\n", buf_len)); + return -1; + } + /* We have to convert the buffer data into ASCII strings */ + for (i = 0; i < len; i++) { + snprintf(buf, 3, "%02x", _buf[i]); + buf += 2; + } + buf[i*2] = '\0'; + } + } + else { + CFGP2P_ERR(("ERROR: get_noa in non-p2p mode\n")); + return -1; + } + return len * 2; +} +s32 +wl_cfgp2p_set_p2p_ps(struct wl_priv *wl, struct net_device *ndev, char* buf, int len) +{ + int ps, ctw; + int ret = -1; + s32 legacy_ps; + + CFGP2P_DBG((" Enter\n")); + if (wl->p2p && wl->p2p->vif_created) { + sscanf(buf, "%10d %10d %10d", &legacy_ps, &ps, &ctw); + CFGP2P_DBG((" Enter legacy_ps %d ps %d ctw %d\n", legacy_ps, ps, ctw)); + if (ctw != -1) { + wl->p2p->ops.ctw = ctw; + ret = 0; + } + if (ps != -1) { + wl->p2p->ops.ops = ps; + ret = wldev_iovar_setbuf(wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION), + "p2p_ops", &wl->p2p->ops, sizeof(wl->p2p->ops), + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); + if (ret < 0) { + CFGP2P_ERR(("fw set p2p_ops failed %d\n", ret)); + } + } + + if ((legacy_ps != -1) && ((legacy_ps == PM_MAX) || (legacy_ps == PM_OFF))) { +#if defined(SUPPORT_PM2_ONLY) + if (legacy_ps == PM_MAX) + legacy_ps = PM_FAST; +#endif /* SUPPORT_PM2_ONLY */ + + ret = wldev_ioctl(wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION), + WLC_SET_PM, &legacy_ps, sizeof(legacy_ps), true); + if (unlikely(ret)) { + CFGP2P_ERR(("error (%d)\n", ret)); + } else { + wl_cfg80211_update_power_mode(ndev); + } + } + else + CFGP2P_ERR(("ilegal setting\n")); + } + else { + CFGP2P_ERR(("ERROR: set_p2p_ps in non-p2p mode\n")); + ret = -1; + } + return ret; +} + +u8 * +wl_cfgp2p_retreive_p2pattrib(void *buf, u8 element_id) +{ + wifi_p2p_ie_t *ie = NULL; + u16 len = 0; + u8 *subel; + u8 subelt_id; + u16 subelt_len; + + if (!buf) { + WL_ERR(("P2P IE not present")); + return 0; + } + + ie = (wifi_p2p_ie_t*) buf; + len = ie->len; + + /* Point subel to the P2P IE's subelt field. + * Subtract the preceding fields (id, len, OUI, oui_type) from the length. + */ + subel = ie->subelts; + len -= 4; /* exclude OUI + OUI_TYPE */ + + while (len >= 3) { + /* attribute id */ + subelt_id = *subel; + subel += 1; + len -= 1; + + /* 2-byte little endian */ + subelt_len = *subel++; + subelt_len |= *subel++ << 8; + + len -= 2; + len -= subelt_len; /* for the remaining subelt fields */ + + if (subelt_id == element_id) { + /* This will point to start of subelement attrib after + * attribute id & len + */ + return subel; + } + + /* Go to next subelement */ + subel += subelt_len; + } + + /* Not Found */ + return NULL; +} + +#define P2P_GROUP_CAPAB_GO_BIT 0x01 +u8 * +wl_cfgp2p_retreive_p2p_dev_addr(wl_bss_info_t *bi, u32 bi_length) +{ + wifi_p2p_ie_t * p2p_ie = NULL; + u8 *capability = NULL; + bool p2p_go = 0; + u8 *ptr = NULL; + + if (!(p2p_ie = wl_cfgp2p_find_p2pie(((u8 *) bi) + bi->ie_offset, bi->ie_length))) { + WL_ERR(("P2P IE not found")); + return NULL; + } + + if (!(capability = wl_cfgp2p_retreive_p2pattrib(p2p_ie, P2P_SEID_P2P_INFO))) { + WL_ERR(("P2P Capability attribute not found")); + return NULL; + } + + /* Check Group capability for Group Owner bit */ + p2p_go = capability[1] & P2P_GROUP_CAPAB_GO_BIT; + if (!p2p_go) { + return bi->BSSID.octet; + } + + /* In probe responses, DEVICE INFO attribute will be present */ + if (!(ptr = wl_cfgp2p_retreive_p2pattrib(p2p_ie, P2P_SEID_DEV_INFO))) { + /* If DEVICE_INFO is not found, this might be a beacon frame. + * check for DEVICE_ID in the beacon frame. + */ + ptr = wl_cfgp2p_retreive_p2pattrib(p2p_ie, P2P_SEID_DEV_ID); + } + + if (!ptr) + WL_ERR((" Both DEVICE_ID & DEVICE_INFO attribute not present in P2P IE ")); + + return ptr; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) +static void +wl_cfgp2p_ethtool_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) +{ + snprintf(info->driver, sizeof(info->driver), "p2p"); + snprintf(info->version, sizeof(info->version), "%lu", (unsigned long)(0)); +} + +struct ethtool_ops cfgp2p_ethtool_ops = { + .get_drvinfo = wl_cfgp2p_ethtool_get_drvinfo +}; +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */ + +s32 +wl_cfgp2p_register_ndev(struct wl_priv *wl) +{ + int ret = 0; + struct net_device* net = NULL; + struct wireless_dev *wdev = NULL; + uint8 temp_addr[ETHER_ADDR_LEN] = { 0x00, 0x90, 0x4c, 0x33, 0x22, 0x11 }; + + if (wl->p2p_net) { + CFGP2P_ERR(("p2p_net defined already.\n")); + return -EINVAL; + } + + /* Allocate etherdev, including space for private structure */ + if (!(net = alloc_etherdev(sizeof(struct wl_priv *)))) { + CFGP2P_ERR(("%s: OOM - alloc_etherdev\n", __FUNCTION__)); + return -ENODEV; + } + + wdev = kzalloc(sizeof(*wdev), GFP_KERNEL); + if (unlikely(!wdev)) { + WL_ERR(("Could not allocate wireless device\n")); + free_netdev(net); + return -ENOMEM; + } + + strncpy(net->name, "p2p%d", sizeof(net->name) - 1); + net->name[IFNAMSIZ - 1] = '\0'; + + /* Copy the reference to wl_priv */ + memcpy((void *)netdev_priv(net), &wl, sizeof(struct wl_priv *)); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) + ASSERT(!net->open); + net->do_ioctl = wl_cfgp2p_do_ioctl; + net->hard_start_xmit = wl_cfgp2p_start_xmit; + net->open = wl_cfgp2p_if_open; + net->stop = wl_cfgp2p_if_stop; +#else + ASSERT(!net->netdev_ops); + net->netdev_ops = &wl_cfgp2p_if_ops; +#endif + + /* Register with a dummy MAC addr */ + memcpy(net->dev_addr, temp_addr, ETHER_ADDR_LEN); + + wdev->wiphy = wl->wdev->wiphy; + + wdev->iftype = wl_mode_to_nl80211_iftype(WL_MODE_BSS); + + net->ieee80211_ptr = wdev; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) + net->ethtool_ops = &cfgp2p_ethtool_ops; +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */ + + SET_NETDEV_DEV(net, wiphy_dev(wdev->wiphy)); + + /* Associate p2p0 network interface with new wdev */ + wdev->netdev = net; + + ret = register_netdev(net); + if (ret) { + CFGP2P_ERR((" register_netdevice failed (%d)\n", ret)); + free_netdev(net); + kfree(wdev); + return -ENODEV; + } + + /* store p2p net ptr for further reference. Note that iflist won't have this + * entry as there corresponding firmware interface is a "Hidden" interface. + */ + wl->p2p_wdev = wdev; + wl->p2p_net = net; + + printk("%s: P2P Interface Registered\n", net->name); + + return ret; +} + +s32 +wl_cfgp2p_unregister_ndev(struct wl_priv *wl) +{ + + if (!wl || !wl->p2p_net) { + CFGP2P_ERR(("Invalid Ptr\n")); + return -EINVAL; + } + + unregister_netdev(wl->p2p_net); + free_netdev(wl->p2p_net); + + return 0; +} +static int wl_cfgp2p_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + if (skb) + { + CFGP2P_DBG(("(%s) is not used for data operations.Droping the packet.\n", + ndev->name)); + dev_kfree_skb_any(skb); + } + + return 0; +} + +static int wl_cfgp2p_do_ioctl(struct net_device *net, struct ifreq *ifr, int cmd) +{ + int ret = 0; + struct wl_priv *wl = *(struct wl_priv **)netdev_priv(net); + struct net_device *ndev = wl_to_prmry_ndev(wl); + + /* There is no ifidx corresponding to p2p0 in our firmware. So we should + * not Handle any IOCTL cmds on p2p0 other than ANDROID PRIVATE CMDs. + * For Android PRIV CMD handling map it to primary I/F + */ + if (cmd == SIOCDEVPRIVATE+1) { + ret = wl_android_priv_cmd(ndev, ifr, cmd); + + } else { + CFGP2P_ERR(("%s: IOCTL req 0x%x on p2p0 I/F. Ignoring. \n", + __FUNCTION__, cmd)); + return -1; + } + + return ret; +} + +static int wl_cfgp2p_if_open(struct net_device *net) +{ + extern struct wl_priv *wlcfg_drv_priv; + struct wireless_dev *wdev = net->ieee80211_ptr; + struct wl_priv *wl = NULL; + wl = wlcfg_drv_priv; + if (!wdev || !wl || !wl->p2p) + return -EINVAL; + WL_TRACE(("Enter\n")); + /* If suppose F/W download (ifconfig wlan0 up) hasn't been done by now, + * do it here. This will make sure that in concurrent mode, supplicant + * is not dependent on a particular order of interface initialization. + * i.e you may give wpa_supp -iwlan0 -N -ip2p0 or wpa_supp -ip2p0 -N + * -iwlan0. + */ + wdev->wiphy->interface_modes |= (BIT(NL80211_IFTYPE_P2P_CLIENT) + | BIT(NL80211_IFTYPE_P2P_GO)); + wl_cfg80211_do_driver_init(net); + + return 0; +} + +static int wl_cfgp2p_if_stop(struct net_device *net) +{ + extern struct wl_priv *wlcfg_drv_priv; + struct wl_priv *wl = NULL; + unsigned long flags; + struct wireless_dev *wdev = net->ieee80211_ptr; + int clear_flag = 0; + if (!wdev) + return -EINVAL; + + WL_TRACE(("Enter\n")); + wl = wlcfg_drv_priv; + if (!wl) + return -EINVAL; + spin_lock_irqsave(&wl->cfgdrv_lock, flags); + if (wl->scan_request && wl->scan_request->dev == net) { + cfg80211_scan_done(wl->scan_request, true); + wl->scan_request = NULL; + clear_flag = 1; + } + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); + if (clear_flag) + wl_clr_drv_status(wl, SCANNING, net); + wdev->wiphy->interface_modes = (wdev->wiphy->interface_modes) + & (~(BIT(NL80211_IFTYPE_P2P_CLIENT)| + BIT(NL80211_IFTYPE_P2P_GO))); + return 0; +} + +bool wl_cfgp2p_is_ifops(const struct net_device_ops *if_ops) +{ + return (if_ops == &wl_cfgp2p_if_ops); +} diff --git a/drivers/net/wireless/bcmdhd/wl_cfgp2p.h b/drivers/net/wireless/bcmdhd/wl_cfgp2p.h new file mode 100644 index 00000000..4c64db61 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.h @@ -0,0 +1,342 @@ +/* + * Linux cfgp2p driver + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_cfgp2p.h 376685 2013-01-02 06:28:45Z $ + */ +#ifndef _wl_cfgp2p_h_ +#define _wl_cfgp2p_h_ +#include +#include + +struct wl_priv; +extern u32 wl_dbg_level; + +typedef struct wifi_p2p_ie wifi_wfd_ie_t; +/* Enumeration of the usages of the BSSCFGs used by the P2P Library. Do not + * confuse this with a bsscfg index. This value is an index into the + * saved_ie[] array of structures which in turn contains a bsscfg index field. + */ +typedef enum { + P2PAPI_BSSCFG_PRIMARY, /* maps to driver's primary bsscfg */ + P2PAPI_BSSCFG_DEVICE, /* maps to driver's P2P device discovery bsscfg */ + P2PAPI_BSSCFG_CONNECTION, /* maps to driver's P2P connection bsscfg */ + P2PAPI_BSSCFG_MAX +} p2p_bsscfg_type_t; + +/* vendor ies max buffer length for probe response or beacon */ +#define VNDR_IES_MAX_BUF_LEN 1400 +/* normal vendor ies buffer length */ +#define VNDR_IES_BUF_LEN 512 + +/* Structure to hold all saved P2P and WPS IEs for a BSSCFG */ +struct p2p_saved_ie { + u8 p2p_probe_req_ie[VNDR_IES_BUF_LEN]; + u8 p2p_probe_res_ie[VNDR_IES_MAX_BUF_LEN]; + u8 p2p_assoc_req_ie[VNDR_IES_BUF_LEN]; + u8 p2p_assoc_res_ie[VNDR_IES_BUF_LEN]; + u8 p2p_beacon_ie[VNDR_IES_MAX_BUF_LEN]; + u32 p2p_probe_req_ie_len; + u32 p2p_probe_res_ie_len; + u32 p2p_assoc_req_ie_len; + u32 p2p_assoc_res_ie_len; + u32 p2p_beacon_ie_len; +}; + +struct p2p_bss { + u32 bssidx; + struct net_device *dev; + struct p2p_saved_ie saved_ie; + void *private_data; +}; + +struct p2p_info { + bool on; /* p2p on/off switch */ + bool scan; + bool vif_created; + s8 vir_ifname[IFNAMSIZ]; + unsigned long status; + struct ether_addr dev_addr; + struct ether_addr int_addr; + struct p2p_bss bss_idx[P2PAPI_BSSCFG_MAX]; + struct timer_list listen_timer; + wl_p2p_sched_t noa; + wl_p2p_ops_t ops; + wlc_ssid_t ssid; +}; + +#define MAX_VNDR_IE_NUMBER 5 + +struct parsed_vndr_ie_info { + char *ie_ptr; + u32 ie_len; /* total length including id & length field */ + vndr_ie_t vndrie; +}; + +struct parsed_vndr_ies { + u32 count; + struct parsed_vndr_ie_info ie_info[MAX_VNDR_IE_NUMBER]; +}; + +/* dongle status */ +enum wl_cfgp2p_status { + WLP2P_STATUS_DISCOVERY_ON = 0, + WLP2P_STATUS_SEARCH_ENABLED, + WLP2P_STATUS_IF_ADD, + WLP2P_STATUS_IF_DEL, + WLP2P_STATUS_IF_DELETING, + WLP2P_STATUS_IF_CHANGING, + WLP2P_STATUS_IF_CHANGED, + WLP2P_STATUS_LISTEN_EXPIRED, + WLP2P_STATUS_ACTION_TX_COMPLETED, + WLP2P_STATUS_ACTION_TX_NOACK, + WLP2P_STATUS_SCANNING, + WLP2P_STATUS_GO_NEG_PHASE, + WLP2P_STATUS_DISC_IN_PROGRESS +}; + + +#define wl_to_p2p_bss_ndev(wl, type) ((wl)->p2p->bss_idx[type].dev) +#define wl_to_p2p_bss_bssidx(wl, type) ((wl)->p2p->bss_idx[type].bssidx) +#define wl_to_p2p_bss_saved_ie(wl, type) ((wl)->p2p->bss_idx[type].saved_ie) +#define wl_to_p2p_bss_private(wl, type) ((wl)->p2p->bss_idx[type].private_data) +#define wl_to_p2p_bss(wl, type) ((wl)->p2p->bss_idx[type]) +#define wl_get_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? 0 : test_bit(WLP2P_STATUS_ ## stat, \ + &(wl)->p2p->status)) +#define wl_set_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? 0 : set_bit(WLP2P_STATUS_ ## stat, \ + &(wl)->p2p->status)) +#define wl_clr_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? 0 : clear_bit(WLP2P_STATUS_ ## stat, \ + &(wl)->p2p->status)) +#define wl_chg_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? 0:change_bit(WLP2P_STATUS_ ## stat, \ + &(wl)->p2p->status)) +#define p2p_on(wl) ((wl)->p2p->on) +#define p2p_scan(wl) ((wl)->p2p->scan) +#define p2p_is_on(wl) ((wl)->p2p && (wl)->p2p->on) + +/* dword align allocation */ +#define WLC_IOCTL_MAXLEN 8192 + +#define CFGP2P_ERROR_TEXT "CFGP2P-ERROR) " + +#define CFGP2P_ERR(args) \ + do { \ + if (wl_dbg_level & WL_DBG_ERR) { \ + printk(KERN_INFO CFGP2P_ERROR_TEXT "%s : ", __func__); \ + printk args; \ + } \ + } while (0) +#define CFGP2P_INFO(args) \ + do { \ + if (wl_dbg_level & WL_DBG_INFO) { \ + printk(KERN_INFO "CFGP2P-INFO) %s : ", __func__); \ + printk args; \ + } \ + } while (0) +#define CFGP2P_DBG(args) \ + do { \ + if (wl_dbg_level & WL_DBG_DBG) { \ + printk(KERN_DEBUG "CFGP2P-DEBUG) %s :", __func__); \ + printk args; \ + } \ + } while (0) + +#define CFGP2P_ACTION(args) \ + do { \ + if (wl_dbg_level & WL_DBG_P2P_ACTION) { \ + printk(KERN_DEBUG "CFGP2P-ACTION) %s :", __func__); \ + printk args; \ + } \ + } while (0) +#define INIT_TIMER(timer, func, duration, extra_delay) \ + do { \ + init_timer(timer); \ + timer->function = func; \ + timer->expires = jiffies + msecs_to_jiffies(duration + extra_delay); \ + timer->data = (unsigned long) wl; \ + add_timer(timer); \ + } while (0); +extern void +wl_cfgp2p_listen_expired(unsigned long data); +extern bool +wl_cfgp2p_is_pub_action(void *frame, u32 frame_len); +extern bool +wl_cfgp2p_is_p2p_action(void *frame, u32 frame_len); +extern bool +wl_cfgp2p_is_gas_action(void *frame, u32 frame_len); +extern void +wl_cfgp2p_print_actframe(bool tx, void *frame, u32 frame_len); +extern s32 +wl_cfgp2p_init_priv(struct wl_priv *wl); +extern void +wl_cfgp2p_deinit_priv(struct wl_priv *wl); +extern s32 +wl_cfgp2p_set_firm_p2p(struct wl_priv *wl); +extern s32 +wl_cfgp2p_set_p2p_mode(struct wl_priv *wl, u8 mode, + u32 channel, u16 listen_ms, int bssidx); +extern s32 +wl_cfgp2p_ifadd(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, + chanspec_t chspec); +extern s32 +wl_cfgp2p_ifdisable(struct wl_priv *wl, struct ether_addr *mac); +extern s32 +wl_cfgp2p_ifdel(struct wl_priv *wl, struct ether_addr *mac); +extern s32 +wl_cfgp2p_ifchange(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, chanspec_t chspec); + +extern s32 +wl_cfgp2p_ifidx(struct wl_priv *wl, struct ether_addr *mac, s32 *index); + +extern s32 +wl_cfgp2p_init_discovery(struct wl_priv *wl); +extern s32 +wl_cfgp2p_enable_discovery(struct wl_priv *wl, struct net_device *dev, const u8 *ie, u32 ie_len); +extern s32 +wl_cfgp2p_disable_discovery(struct wl_priv *wl); +extern s32 +wl_cfgp2p_escan(struct wl_priv *wl, struct net_device *dev, u16 active, u32 num_chans, + u16 *channels, + s32 search_state, u16 action, u32 bssidx, struct ether_addr *tx_dst_addr); + +extern s32 +wl_cfgp2p_act_frm_search(struct wl_priv *wl, struct net_device *ndev, + s32 bssidx, s32 channel, struct ether_addr *tx_dst_addr); + +extern wpa_ie_fixed_t * +wl_cfgp2p_find_wpaie(u8 *parse, u32 len); + +extern wpa_ie_fixed_t * +wl_cfgp2p_find_wpsie(u8 *parse, u32 len); + +extern wifi_p2p_ie_t * +wl_cfgp2p_find_p2pie(u8 *parse, u32 len); + +extern wifi_wfd_ie_t * +wl_cfgp2p_find_wfdie(u8 *parse, u32 len); +extern s32 +wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, + s32 pktflag, const u8 *vndr_ie, u32 vndr_ie_len); +extern s32 +wl_cfgp2p_clear_management_ie(struct wl_priv *wl, s32 bssidx); + +extern s32 +wl_cfgp2p_find_idx(struct wl_priv *wl, struct net_device *ndev); +extern struct net_device * +wl_cfgp2p_find_ndev(struct wl_priv *wl, s32 bssidx); + + +extern s32 +wl_cfgp2p_listen_complete(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data); +extern s32 +wl_cfgp2p_discover_listen(struct wl_priv *wl, s32 channel, u32 duration_ms); + +extern s32 +wl_cfgp2p_discover_enable_search(struct wl_priv *wl, u8 enable); + +extern s32 +wl_cfgp2p_action_tx_complete(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data); +extern s32 +wl_cfgp2p_tx_action_frame(struct wl_priv *wl, struct net_device *dev, + wl_af_params_t *af_params, s32 bssidx); + +extern void +wl_cfgp2p_generate_bss_mac(struct ether_addr *primary_addr, struct ether_addr *out_dev_addr, + struct ether_addr *out_int_addr); + +extern void +wl_cfg80211_change_ifaddr(u8* buf, struct ether_addr *p2p_int_addr, u8 element_id); +extern bool +wl_cfgp2p_bss_isup(struct net_device *ndev, int bsscfg_idx); + +extern s32 +wl_cfgp2p_bss(struct wl_priv *wl, struct net_device *ndev, s32 bsscfg_idx, s32 up); + + +extern s32 +wl_cfgp2p_supported(struct wl_priv *wl, struct net_device *ndev); + +extern s32 +wl_cfgp2p_down(struct wl_priv *wl); + +extern s32 +wl_cfgp2p_set_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf, int len); + +extern s32 +wl_cfgp2p_get_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf, int len); + +extern s32 +wl_cfgp2p_set_p2p_ps(struct wl_priv *wl, struct net_device *ndev, char* buf, int len); + +extern u8 * +wl_cfgp2p_retreive_p2pattrib(void *buf, u8 element_id); + +extern u8 * +wl_cfgp2p_retreive_p2p_dev_addr(wl_bss_info_t *bi, u32 bi_length); + +extern s32 +wl_cfgp2p_register_ndev(struct wl_priv *wl); + +extern s32 +wl_cfgp2p_unregister_ndev(struct wl_priv *wl); + +extern bool +wl_cfgp2p_is_ifops(const struct net_device_ops *if_ops); + +/* WiFi Direct */ +#define SOCIAL_CHAN_1 1 +#define SOCIAL_CHAN_2 6 +#define SOCIAL_CHAN_3 11 +#define IS_P2P_SOCIAL_CHANNEL(channel) ((channel == SOCIAL_CHAN_1) || \ + (channel == SOCIAL_CHAN_2) || \ + (channel == SOCIAL_CHAN_3)) +#define SOCIAL_CHAN_CNT 3 +#define AF_PEER_SEARCH_CNT 2 +#define WL_P2P_WILDCARD_SSID "DIRECT-" +#define WL_P2P_WILDCARD_SSID_LEN 7 +#define WL_P2P_INTERFACE_PREFIX "p2p" +#define WL_P2P_TEMP_CHAN 11 + +/* If the provision discovery is for JOIN operations, + * then we need not do an internal scan to find GO. + */ +#define IS_PROV_DISC_WITHOUT_GROUP_ID(p2p_ie, len) \ + (wl_cfgp2p_retreive_p2pattrib(p2p_ie, P2P_SEID_GROUP_ID) == NULL) + +#define IS_GAS_REQ(frame, len) (wl_cfgp2p_is_gas_action(frame, len) && \ + ((frame->action == P2PSD_ACTION_ID_GAS_IREQ) || \ + (frame->action == P2PSD_ACTION_ID_GAS_CREQ))) +#define IS_P2P_PUB_ACT_REQ(frame, p2p_ie, len) \ + (wl_cfgp2p_is_pub_action(frame, len) && \ + ((frame->subtype == P2P_PAF_GON_REQ) || \ + (frame->subtype == P2P_PAF_INVITE_REQ) || \ + ((frame->subtype == P2P_PAF_PROVDIS_REQ) && \ + IS_PROV_DISC_WITHOUT_GROUP_ID(p2p_ie, len)))) +#define IS_P2P_PUB_ACT_RSP_SUBTYPE(subtype) ((subtype == P2P_PAF_GON_RSP) || \ + ((subtype == P2P_PAF_GON_CONF) || \ + (subtype == P2P_PAF_INVITE_RSP) || \ + (subtype == P2P_PAF_PROVDIS_RSP))) +#define IS_P2P_SOCIAL(ch) ((ch == SOCIAL_CHAN_1) || (ch == SOCIAL_CHAN_2) || (ch == SOCIAL_CHAN_3)) +#define IS_P2P_SSID(ssid, len) (!memcmp(ssid, WL_P2P_WILDCARD_SSID, WL_P2P_WILDCARD_SSID_LEN) && \ + (len == WL_P2P_WILDCARD_SSID_LEN)) +#endif /* _wl_cfgp2p_h_ */ diff --git a/drivers/net/wireless/bcmdhd/wl_dbg.h b/drivers/net/wireless/bcmdhd/wl_dbg.h new file mode 100644 index 00000000..b5e70807 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wl_dbg.h @@ -0,0 +1,63 @@ +/* + * Minimal debug/trace/assert driver definitions for + * Broadcom 802.11 Networking Adapter. + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_dbg.h 326635 2012-04-10 03:15:29Z $ + */ + + +#ifndef _wl_dbg_h_ +#define _wl_dbg_h_ + + +extern uint32 wl_msg_level; +extern uint32 wl_msg_level2; + +#define WL_TIMESTAMP() + +#if 0 && (VERSION_MAJOR > 9) +#include +#define WL_PRINT(args) do { printf args; IO8Log args; } while (0) +#else +#define WL_PRINT(args) do { WL_TIMESTAMP(); printf args; } while (0) +#endif + + + +#define WL_NONE(args) + +#define WL_ERROR(args) +#define WL_TRACE(args) +#define WL_APSTA_UPDN(args) +#define WL_APSTA_RX(args) +#ifdef WLMSG_WSEC +#define WL_WSEC(args) WL_PRINT(args) +#define WL_WSEC_DUMP(args) WL_PRINT(args) +#else +#define WL_WSEC(args) +#define WL_WSEC_DUMP(args) +#endif + +extern uint32 wl_msg_level; +extern uint32 wl_msg_level2; +#endif diff --git a/drivers/net/wireless/bcmdhd/wl_iw.c b/drivers/net/wireless/bcmdhd/wl_iw.c new file mode 100644 index 00000000..c9b24ebc --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wl_iw.c @@ -0,0 +1,3589 @@ +/* + * Linux Wireless Extensions support + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_iw.c 347632 2012-07-27 11:00:35Z $ + */ + +#if defined(USE_IW) +#define LINUX_PORT + +#include +#include +#include + +#include +#include +#include + +#include +#include + + +typedef const struct si_pub si_t; +#include + + +#include +#include + + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) +#include +#endif +#if defined(SOFTAP) +struct net_device *ap_net_dev = NULL; +tsk_ctl_t ap_eth_ctl; +#endif + +extern bool wl_iw_conn_status_str(uint32 event_type, uint32 status, + uint32 reason, char* stringBuf, uint buflen); + +uint wl_msg_level = WL_ERROR_VAL; + +#define MAX_WLIW_IOCTL_LEN 1024 + + +#define htod32(i) i +#define htod16(i) i +#define dtoh32(i) i +#define dtoh16(i) i +#define htodchanspec(i) i +#define dtohchanspec(i) i + +extern struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev); +extern int dhd_wait_pend8021x(struct net_device *dev); + +#if WIRELESS_EXT < 19 +#define IW_IOCTL_IDX(cmd) ((cmd) - SIOCIWFIRST) +#define IW_EVENT_IDX(cmd) ((cmd) - IWEVFIRST) +#endif + + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) +#define DAEMONIZE(a) daemonize(a); \ + allow_signal(SIGKILL); \ + allow_signal(SIGTERM); +#else +#define RAISE_RX_SOFTIRQ() \ + cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ) +#define DAEMONIZE(a) daemonize(); \ + do { if (a) \ + strncpy(current->comm, a, MIN(sizeof(current->comm), (strlen(a) + 1))); \ + } while (0); +#endif + +#define ISCAN_STATE_IDLE 0 +#define ISCAN_STATE_SCANING 1 + + +#define WLC_IW_ISCAN_MAXLEN 2048 +typedef struct iscan_buf { + struct iscan_buf * next; + char iscan_buf[WLC_IW_ISCAN_MAXLEN]; +} iscan_buf_t; + +typedef struct iscan_info { + struct net_device *dev; + struct timer_list timer; + uint32 timer_ms; + uint32 timer_on; + int iscan_state; + iscan_buf_t * list_hdr; + iscan_buf_t * list_cur; + + + long sysioc_pid; + struct semaphore sysioc_sem; + struct completion sysioc_exited; + + + char ioctlbuf[WLC_IOCTL_SMLEN]; +} iscan_info_t; +iscan_info_t *g_iscan = NULL; +static void wl_iw_timerfunc(ulong data); +static void wl_iw_set_event_mask(struct net_device *dev); +static int wl_iw_iscan(iscan_info_t *iscan, wlc_ssid_t *ssid, uint16 action); + + +typedef struct priv_link { + wl_iw_t *wliw; +} priv_link_t; + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)) +#define WL_DEV_LINK(dev) (priv_link_t*)(dev->priv) +#else +#define WL_DEV_LINK(dev) (priv_link_t*)netdev_priv(dev) +#endif + + +#define IW_DEV_IF(dev) ((wl_iw_t*)(WL_DEV_LINK(dev))->wliw) + +static void swap_key_from_BE( + wl_wsec_key_t *key +) +{ + key->index = htod32(key->index); + key->len = htod32(key->len); + key->algo = htod32(key->algo); + key->flags = htod32(key->flags); + key->rxiv.hi = htod32(key->rxiv.hi); + key->rxiv.lo = htod16(key->rxiv.lo); + key->iv_initialized = htod32(key->iv_initialized); +} + +static void swap_key_to_BE( + wl_wsec_key_t *key +) +{ + key->index = dtoh32(key->index); + key->len = dtoh32(key->len); + key->algo = dtoh32(key->algo); + key->flags = dtoh32(key->flags); + key->rxiv.hi = dtoh32(key->rxiv.hi); + key->rxiv.lo = dtoh16(key->rxiv.lo); + key->iv_initialized = dtoh32(key->iv_initialized); +} + +static int +dev_wlc_ioctl( + struct net_device *dev, + int cmd, + void *arg, + int len +) +{ + struct ifreq ifr; + wl_ioctl_t ioc; + mm_segment_t fs; + int ret; + + memset(&ioc, 0, sizeof(ioc)); + ioc.cmd = cmd; + ioc.buf = arg; + ioc.len = len; + + strcpy(ifr.ifr_name, dev->name); + ifr.ifr_data = (caddr_t) &ioc; + +#ifndef LINUX_HYBRID + + dev_open(dev); +#endif + + fs = get_fs(); + set_fs(get_ds()); +#if defined(WL_USE_NETDEV_OPS) + ret = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, SIOCDEVPRIVATE); +#else + ret = dev->do_ioctl(dev, &ifr, SIOCDEVPRIVATE); +#endif + set_fs(fs); + + return ret; +} + + + +static int +dev_wlc_intvar_set( + struct net_device *dev, + char *name, + int val) +{ + char buf[WLC_IOCTL_SMLEN]; + uint len; + + val = htod32(val); + len = bcm_mkiovar(name, (char *)(&val), sizeof(val), buf, sizeof(buf)); + ASSERT(len); + + return (dev_wlc_ioctl(dev, WLC_SET_VAR, buf, len)); +} + +static int +dev_iw_iovar_setbuf( + struct net_device *dev, + char *iovar, + void *param, + int paramlen, + void *bufptr, + int buflen) +{ + int iolen; + + iolen = bcm_mkiovar(iovar, param, paramlen, bufptr, buflen); + ASSERT(iolen); + BCM_REFERENCE(iolen); + + return (dev_wlc_ioctl(dev, WLC_SET_VAR, bufptr, iolen)); +} + +static int +dev_iw_iovar_getbuf( + struct net_device *dev, + char *iovar, + void *param, + int paramlen, + void *bufptr, + int buflen) +{ + int iolen; + + iolen = bcm_mkiovar(iovar, param, paramlen, bufptr, buflen); + ASSERT(iolen); + BCM_REFERENCE(iolen); + + return (dev_wlc_ioctl(dev, WLC_GET_VAR, bufptr, buflen)); +} + +#if WIRELESS_EXT > 17 +static int +dev_wlc_bufvar_set( + struct net_device *dev, + char *name, + char *buf, int len) +{ + char *ioctlbuf; + uint buflen; + int error; + + ioctlbuf = kmalloc(MAX_WLIW_IOCTL_LEN, GFP_KERNEL); + if (!ioctlbuf) + return -ENOMEM; + + buflen = bcm_mkiovar(name, buf, len, ioctlbuf, MAX_WLIW_IOCTL_LEN); + ASSERT(buflen); + error = dev_wlc_ioctl(dev, WLC_SET_VAR, ioctlbuf, buflen); + + kfree(ioctlbuf); + return error; +} +#endif + + + +static int +dev_wlc_bufvar_get( + struct net_device *dev, + char *name, + char *buf, int buflen) +{ + char *ioctlbuf; + int error; + + uint len; + + ioctlbuf = kmalloc(MAX_WLIW_IOCTL_LEN, GFP_KERNEL); + if (!ioctlbuf) + return -ENOMEM; + len = bcm_mkiovar(name, NULL, 0, ioctlbuf, MAX_WLIW_IOCTL_LEN); + ASSERT(len); + BCM_REFERENCE(len); + error = dev_wlc_ioctl(dev, WLC_GET_VAR, (void *)ioctlbuf, MAX_WLIW_IOCTL_LEN); + if (!error) + bcopy(ioctlbuf, buf, buflen); + + kfree(ioctlbuf); + return (error); +} + + + +static int +dev_wlc_intvar_get( + struct net_device *dev, + char *name, + int *retval) +{ + union { + char buf[WLC_IOCTL_SMLEN]; + int val; + } var; + int error; + + uint len; + uint data_null; + + len = bcm_mkiovar(name, (char *)(&data_null), 0, (char *)(&var), sizeof(var.buf)); + ASSERT(len); + error = dev_wlc_ioctl(dev, WLC_GET_VAR, (void *)&var, len); + + *retval = dtoh32(var.val); + + return (error); +} + + +#if WIRELESS_EXT < 13 +struct iw_request_info +{ + __u16 cmd; + __u16 flags; +}; + +typedef int (*iw_handler)(struct net_device *dev, struct iw_request_info *info, + void *wrqu, char *extra); +#endif + +#if WIRELESS_EXT > 12 +static int +wl_iw_set_leddc( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int dc = *(int *)extra; + int error; + + error = dev_wlc_intvar_set(dev, "leddc", dc); + return error; +} + +static int +wl_iw_set_vlanmode( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int mode = *(int *)extra; + int error; + + mode = htod32(mode); + error = dev_wlc_intvar_set(dev, "vlan_mode", mode); + return error; +} + +static int +wl_iw_set_pm( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int pm = *(int *)extra; + int error; + + pm = htod32(pm); + error = dev_wlc_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm)); + return error; +} +#endif + +int +wl_iw_send_priv_event( + struct net_device *dev, + char *flag +) +{ + union iwreq_data wrqu; + char extra[IW_CUSTOM_MAX + 1]; + int cmd; + + cmd = IWEVCUSTOM; + memset(&wrqu, 0, sizeof(wrqu)); + if (strlen(flag) > sizeof(extra)) + return -1; + + strcpy(extra, flag); + wrqu.data.length = strlen(extra); + wireless_send_event(dev, cmd, &wrqu, extra); + WL_TRACE(("Send IWEVCUSTOM Event as %s\n", extra)); + + return 0; +} + +static int +wl_iw_config_commit( + struct net_device *dev, + struct iw_request_info *info, + void *zwrq, + char *extra +) +{ + wlc_ssid_t ssid; + int error; + struct sockaddr bssid; + + WL_TRACE(("%s: SIOCSIWCOMMIT\n", dev->name)); + + if ((error = dev_wlc_ioctl(dev, WLC_GET_SSID, &ssid, sizeof(ssid)))) + return error; + + ssid.SSID_len = dtoh32(ssid.SSID_len); + + if (!ssid.SSID_len) + return 0; + + bzero(&bssid, sizeof(struct sockaddr)); + if ((error = dev_wlc_ioctl(dev, WLC_REASSOC, &bssid, ETHER_ADDR_LEN))) { + WL_ERROR(("%s: WLC_REASSOC failed (%d)\n", __FUNCTION__, error)); + return error; + } + + return 0; +} + +static int +wl_iw_get_name( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *cwrq, + char *extra +) +{ + int phytype, err; + uint band[3]; + char cap[5]; + + WL_TRACE(("%s: SIOCGIWNAME\n", dev->name)); + + cap[0] = 0; + if ((err = dev_wlc_ioctl(dev, WLC_GET_PHYTYPE, &phytype, sizeof(phytype))) < 0) + goto done; + if ((err = dev_wlc_ioctl(dev, WLC_GET_BANDLIST, band, sizeof(band))) < 0) + goto done; + + band[0] = dtoh32(band[0]); + switch (phytype) { + case WLC_PHY_TYPE_A: + strcpy(cap, "a"); + break; + case WLC_PHY_TYPE_B: + strcpy(cap, "b"); + break; + case WLC_PHY_TYPE_LP: + case WLC_PHY_TYPE_G: + if (band[0] >= 2) + strcpy(cap, "abg"); + else + strcpy(cap, "bg"); + break; + case WLC_PHY_TYPE_N: + if (band[0] >= 2) + strcpy(cap, "abgn"); + else + strcpy(cap, "bgn"); + break; + } +done: + snprintf(cwrq->name, IFNAMSIZ, "IEEE 802.11%s", cap); + return 0; +} + +static int +wl_iw_set_freq( + struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *fwrq, + char *extra +) +{ + int error, chan; + uint sf = 0; + + WL_TRACE(("%s: SIOCSIWFREQ\n", dev->name)); + + + if (fwrq->e == 0 && fwrq->m < MAXCHANNEL) { + chan = fwrq->m; + } + + + else { + + if (fwrq->e >= 6) { + fwrq->e -= 6; + while (fwrq->e--) + fwrq->m *= 10; + } else if (fwrq->e < 6) { + while (fwrq->e++ < 6) + fwrq->m /= 10; + } + + if (fwrq->m > 4000 && fwrq->m < 5000) + sf = WF_CHAN_FACTOR_4_G; + + chan = wf_mhz2channel(fwrq->m, sf); + } + chan = htod32(chan); + if ((error = dev_wlc_ioctl(dev, WLC_SET_CHANNEL, &chan, sizeof(chan)))) + return error; + + + return -EINPROGRESS; +} + +static int +wl_iw_get_freq( + struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *fwrq, + char *extra +) +{ + channel_info_t ci; + int error; + + WL_TRACE(("%s: SIOCGIWFREQ\n", dev->name)); + + if ((error = dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(ci)))) + return error; + + + fwrq->m = dtoh32(ci.hw_channel); + fwrq->e = dtoh32(0); + return 0; +} + +static int +wl_iw_set_mode( + struct net_device *dev, + struct iw_request_info *info, + __u32 *uwrq, + char *extra +) +{ + int infra = 0, ap = 0, error = 0; + + WL_TRACE(("%s: SIOCSIWMODE\n", dev->name)); + + switch (*uwrq) { + case IW_MODE_MASTER: + infra = ap = 1; + break; + case IW_MODE_ADHOC: + case IW_MODE_AUTO: + break; + case IW_MODE_INFRA: + infra = 1; + break; + default: + return -EINVAL; + } + infra = htod32(infra); + ap = htod32(ap); + + if ((error = dev_wlc_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(infra))) || + (error = dev_wlc_ioctl(dev, WLC_SET_AP, &ap, sizeof(ap)))) + return error; + + + return -EINPROGRESS; +} + +static int +wl_iw_get_mode( + struct net_device *dev, + struct iw_request_info *info, + __u32 *uwrq, + char *extra +) +{ + int error, infra = 0, ap = 0; + + WL_TRACE(("%s: SIOCGIWMODE\n", dev->name)); + + if ((error = dev_wlc_ioctl(dev, WLC_GET_INFRA, &infra, sizeof(infra))) || + (error = dev_wlc_ioctl(dev, WLC_GET_AP, &ap, sizeof(ap)))) + return error; + + infra = dtoh32(infra); + ap = dtoh32(ap); + *uwrq = infra ? ap ? IW_MODE_MASTER : IW_MODE_INFRA : IW_MODE_ADHOC; + + return 0; +} + +static int +wl_iw_get_range( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + struct iw_range *range = (struct iw_range *) extra; + static int channels[MAXCHANNEL+1]; + wl_uint32_list_t *list = (wl_uint32_list_t *) channels; + wl_rateset_t rateset; + int error, i, k; + uint sf, ch; + + int phytype; + int bw_cap = 0, sgi_tx = 0, nmode = 0; + channel_info_t ci; + uint8 nrate_list2copy = 0; + uint16 nrate_list[4][8] = { {13, 26, 39, 52, 78, 104, 117, 130}, + {14, 29, 43, 58, 87, 116, 130, 144}, + {27, 54, 81, 108, 162, 216, 243, 270}, + {30, 60, 90, 120, 180, 240, 270, 300}}; + + WL_TRACE(("%s: SIOCGIWRANGE\n", dev->name)); + + if (!extra) + return -EINVAL; + + dwrq->length = sizeof(struct iw_range); + memset(range, 0, sizeof(*range)); + + + range->min_nwid = range->max_nwid = 0; + + + list->count = htod32(MAXCHANNEL); + if ((error = dev_wlc_ioctl(dev, WLC_GET_VALID_CHANNELS, channels, sizeof(channels)))) + return error; + for (i = 0; i < dtoh32(list->count) && i < IW_MAX_FREQUENCIES; i++) { + range->freq[i].i = dtoh32(list->element[i]); + + ch = dtoh32(list->element[i]); + if (ch <= CH_MAX_2G_CHANNEL) + sf = WF_CHAN_FACTOR_2_4_G; + else + sf = WF_CHAN_FACTOR_5_G; + + range->freq[i].m = wf_channel2mhz(ch, sf); + range->freq[i].e = 6; + } + range->num_frequency = range->num_channels = i; + + + range->max_qual.qual = 5; + + range->max_qual.level = 0x100 - 200; + + range->max_qual.noise = 0x100 - 200; + + range->sensitivity = 65535; + +#if WIRELESS_EXT > 11 + + range->avg_qual.qual = 3; + + range->avg_qual.level = 0x100 + WL_IW_RSSI_GOOD; + + range->avg_qual.noise = 0x100 - 75; +#endif + + + if ((error = dev_wlc_ioctl(dev, WLC_GET_CURR_RATESET, &rateset, sizeof(rateset)))) + return error; + rateset.count = dtoh32(rateset.count); + range->num_bitrates = rateset.count; + for (i = 0; i < rateset.count && i < IW_MAX_BITRATES; i++) + range->bitrate[i] = (rateset.rates[i] & 0x7f) * 500000; + dev_wlc_intvar_get(dev, "nmode", &nmode); + if ((error = dev_wlc_ioctl(dev, WLC_GET_PHYTYPE, &phytype, sizeof(phytype)))) + return error; + + if (nmode == 1 && ((phytype == WLC_PHY_TYPE_SSN) || (phytype == WLC_PHY_TYPE_LCN) || + (phytype == WLC_PHY_TYPE_LCN40))) { + dev_wlc_intvar_get(dev, "mimo_bw_cap", &bw_cap); + dev_wlc_intvar_get(dev, "sgi_tx", &sgi_tx); + dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(channel_info_t)); + ci.hw_channel = dtoh32(ci.hw_channel); + + if (bw_cap == 0 || + (bw_cap == 2 && ci.hw_channel <= 14)) { + if (sgi_tx == 0) + nrate_list2copy = 0; + else + nrate_list2copy = 1; + } + if (bw_cap == 1 || + (bw_cap == 2 && ci.hw_channel >= 36)) { + if (sgi_tx == 0) + nrate_list2copy = 2; + else + nrate_list2copy = 3; + } + range->num_bitrates += 8; + for (k = 0; i < range->num_bitrates; k++, i++) { + + range->bitrate[i] = (nrate_list[nrate_list2copy][k]) * 500000; + } + } + + + if ((error = dev_wlc_ioctl(dev, WLC_GET_PHYTYPE, &i, sizeof(i)))) + return error; + i = dtoh32(i); + if (i == WLC_PHY_TYPE_A) + range->throughput = 24000000; + else + range->throughput = 1500000; + + + range->min_rts = 0; + range->max_rts = 2347; + range->min_frag = 256; + range->max_frag = 2346; + + range->max_encoding_tokens = DOT11_MAX_DEFAULT_KEYS; + range->num_encoding_sizes = 4; + range->encoding_size[0] = WEP1_KEY_SIZE; + range->encoding_size[1] = WEP128_KEY_SIZE; +#if WIRELESS_EXT > 17 + range->encoding_size[2] = TKIP_KEY_SIZE; +#else + range->encoding_size[2] = 0; +#endif + range->encoding_size[3] = AES_KEY_SIZE; + + + range->min_pmp = 0; + range->max_pmp = 0; + range->min_pmt = 0; + range->max_pmt = 0; + range->pmp_flags = 0; + range->pm_capa = 0; + + + range->num_txpower = 2; + range->txpower[0] = 1; + range->txpower[1] = 255; + range->txpower_capa = IW_TXPOW_MWATT; + +#if WIRELESS_EXT > 10 + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 19; + + + range->retry_capa = IW_RETRY_LIMIT; + range->retry_flags = IW_RETRY_LIMIT; + range->r_time_flags = 0; + + range->min_retry = 1; + range->max_retry = 255; + + range->min_r_time = 0; + range->max_r_time = 0; +#endif + +#if WIRELESS_EXT > 17 + range->enc_capa = IW_ENC_CAPA_WPA; + range->enc_capa |= IW_ENC_CAPA_CIPHER_TKIP; + range->enc_capa |= IW_ENC_CAPA_CIPHER_CCMP; + range->enc_capa |= IW_ENC_CAPA_WPA2; +#if (defined(BCMSUP_PSK) && defined(WLFBT)) + + range->enc_capa |= IW_ENC_CAPA_4WAY_HANDSHAKE; +#endif + + + IW_EVENT_CAPA_SET_KERNEL(range->event_capa); + + IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP); + IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); + IW_EVENT_CAPA_SET(range->event_capa, IWEVTXDROP); + IW_EVENT_CAPA_SET(range->event_capa, IWEVMICHAELMICFAILURE); + IW_EVENT_CAPA_SET(range->event_capa, IWEVASSOCREQIE); + IW_EVENT_CAPA_SET(range->event_capa, IWEVASSOCRESPIE); + IW_EVENT_CAPA_SET(range->event_capa, IWEVPMKIDCAND); + +#if WIRELESS_EXT >= 22 && defined(IW_SCAN_CAPA_ESSID) + + range->scan_capa = IW_SCAN_CAPA_ESSID; +#endif +#endif + + return 0; +} + +static int +rssi_to_qual(int rssi) +{ + if (rssi <= WL_IW_RSSI_NO_SIGNAL) + return 0; + else if (rssi <= WL_IW_RSSI_VERY_LOW) + return 1; + else if (rssi <= WL_IW_RSSI_LOW) + return 2; + else if (rssi <= WL_IW_RSSI_GOOD) + return 3; + else if (rssi <= WL_IW_RSSI_VERY_GOOD) + return 4; + else + return 5; +} + +static int +wl_iw_set_spy( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + wl_iw_t *iw = IW_DEV_IF(dev); + struct sockaddr *addr = (struct sockaddr *) extra; + int i; + + WL_TRACE(("%s: SIOCSIWSPY\n", dev->name)); + + if (!extra) + return -EINVAL; + + iw->spy_num = MIN(ARRAYSIZE(iw->spy_addr), dwrq->length); + for (i = 0; i < iw->spy_num; i++) + memcpy(&iw->spy_addr[i], addr[i].sa_data, ETHER_ADDR_LEN); + memset(iw->spy_qual, 0, sizeof(iw->spy_qual)); + + return 0; +} + +static int +wl_iw_get_spy( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + wl_iw_t *iw = IW_DEV_IF(dev); + struct sockaddr *addr = (struct sockaddr *) extra; + struct iw_quality *qual = (struct iw_quality *) &addr[iw->spy_num]; + int i; + + WL_TRACE(("%s: SIOCGIWSPY\n", dev->name)); + + if (!extra) + return -EINVAL; + + dwrq->length = iw->spy_num; + for (i = 0; i < iw->spy_num; i++) { + memcpy(addr[i].sa_data, &iw->spy_addr[i], ETHER_ADDR_LEN); + addr[i].sa_family = AF_UNIX; + memcpy(&qual[i], &iw->spy_qual[i], sizeof(struct iw_quality)); + iw->spy_qual[i].updated = 0; + } + + return 0; +} + +static int +wl_iw_set_wap( + struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *awrq, + char *extra +) +{ + int error = -EINVAL; + + WL_TRACE(("%s: SIOCSIWAP\n", dev->name)); + + if (awrq->sa_family != ARPHRD_ETHER) { + WL_ERROR(("%s: Invalid Header...sa_family\n", __FUNCTION__)); + return -EINVAL; + } + + + if (ETHER_ISBCAST(awrq->sa_data) || ETHER_ISNULLADDR(awrq->sa_data)) { + scb_val_t scbval; + bzero(&scbval, sizeof(scb_val_t)); + if ((error = dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t)))) { + WL_ERROR(("%s: WLC_DISASSOC failed (%d).\n", __FUNCTION__, error)); + } + return 0; + } + + + if ((error = dev_wlc_ioctl(dev, WLC_REASSOC, awrq->sa_data, ETHER_ADDR_LEN))) { + WL_ERROR(("%s: WLC_REASSOC failed (%d).\n", __FUNCTION__, error)); + return error; + } + + return 0; +} + +static int +wl_iw_get_wap( + struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *awrq, + char *extra +) +{ + WL_TRACE(("%s: SIOCGIWAP\n", dev->name)); + + awrq->sa_family = ARPHRD_ETHER; + memset(awrq->sa_data, 0, ETHER_ADDR_LEN); + + + (void) dev_wlc_ioctl(dev, WLC_GET_BSSID, awrq->sa_data, ETHER_ADDR_LEN); + + return 0; +} + +#if WIRELESS_EXT > 17 +static int +wl_iw_mlme( + struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *awrq, + char *extra +) +{ + struct iw_mlme *mlme; + scb_val_t scbval; + int error = -EINVAL; + + WL_TRACE(("%s: SIOCSIWMLME\n", dev->name)); + + mlme = (struct iw_mlme *)extra; + if (mlme == NULL) { + WL_ERROR(("Invalid ioctl data.\n")); + return error; + } + + scbval.val = mlme->reason_code; + bcopy(&mlme->addr.sa_data, &scbval.ea, ETHER_ADDR_LEN); + + if (mlme->cmd == IW_MLME_DISASSOC) { + scbval.val = htod32(scbval.val); + error = dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t)); + } + else if (mlme->cmd == IW_MLME_DEAUTH) { + scbval.val = htod32(scbval.val); + error = dev_wlc_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scbval, + sizeof(scb_val_t)); + } + else { + WL_ERROR(("%s: Invalid ioctl data.\n", __FUNCTION__)); + return error; + } + + return error; +} +#endif + +static int +wl_iw_get_aplist( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + wl_scan_results_t *list; + struct sockaddr *addr = (struct sockaddr *) extra; + struct iw_quality qual[IW_MAX_AP]; + wl_bss_info_t *bi = NULL; + int error, i; + uint buflen = dwrq->length; + + WL_TRACE(("%s: SIOCGIWAPLIST\n", dev->name)); + + if (!extra) + return -EINVAL; + + + list = kmalloc(buflen, GFP_KERNEL); + if (!list) + return -ENOMEM; + memset(list, 0, buflen); + list->buflen = htod32(buflen); + if ((error = dev_wlc_ioctl(dev, WLC_SCAN_RESULTS, list, buflen))) { + WL_ERROR(("%d: Scan results error %d\n", __LINE__, error)); + kfree(list); + return error; + } + list->buflen = dtoh32(list->buflen); + list->version = dtoh32(list->version); + list->count = dtoh32(list->count); + ASSERT(list->version == WL_BSS_INFO_VERSION); + + for (i = 0, dwrq->length = 0; i < list->count && dwrq->length < IW_MAX_AP; i++) { + bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info; + ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list + + buflen)); + + + if (!(dtoh16(bi->capability) & DOT11_CAP_ESS)) + continue; + + + memcpy(addr[dwrq->length].sa_data, &bi->BSSID, ETHER_ADDR_LEN); + addr[dwrq->length].sa_family = ARPHRD_ETHER; + qual[dwrq->length].qual = rssi_to_qual(dtoh16(bi->RSSI)); + qual[dwrq->length].level = 0x100 + dtoh16(bi->RSSI); + qual[dwrq->length].noise = 0x100 + bi->phy_noise; + + +#if WIRELESS_EXT > 18 + qual[dwrq->length].updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; +#else + qual[dwrq->length].updated = 7; +#endif + + dwrq->length++; + } + + kfree(list); + + if (dwrq->length) { + memcpy(&addr[dwrq->length], qual, sizeof(struct iw_quality) * dwrq->length); + + dwrq->flags = 1; + } + + return 0; +} + +static int +wl_iw_iscan_get_aplist( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + wl_scan_results_t *list; + iscan_buf_t * buf; + iscan_info_t *iscan = g_iscan; + + struct sockaddr *addr = (struct sockaddr *) extra; + struct iw_quality qual[IW_MAX_AP]; + wl_bss_info_t *bi = NULL; + int i; + + WL_TRACE(("%s: SIOCGIWAPLIST\n", dev->name)); + + if (!extra) + return -EINVAL; + + if ((!iscan) || (iscan->sysioc_pid < 0)) { + return wl_iw_get_aplist(dev, info, dwrq, extra); + } + + buf = iscan->list_hdr; + + while (buf) { + list = &((wl_iscan_results_t*)buf->iscan_buf)->results; + ASSERT(list->version == WL_BSS_INFO_VERSION); + + bi = NULL; + for (i = 0, dwrq->length = 0; i < list->count && dwrq->length < IW_MAX_AP; i++) { + bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info; + ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list + + WLC_IW_ISCAN_MAXLEN)); + + + if (!(dtoh16(bi->capability) & DOT11_CAP_ESS)) + continue; + + + memcpy(addr[dwrq->length].sa_data, &bi->BSSID, ETHER_ADDR_LEN); + addr[dwrq->length].sa_family = ARPHRD_ETHER; + qual[dwrq->length].qual = rssi_to_qual(dtoh16(bi->RSSI)); + qual[dwrq->length].level = 0x100 + dtoh16(bi->RSSI); + qual[dwrq->length].noise = 0x100 + bi->phy_noise; + + +#if WIRELESS_EXT > 18 + qual[dwrq->length].updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; +#else + qual[dwrq->length].updated = 7; +#endif + + dwrq->length++; + } + buf = buf->next; + } + if (dwrq->length) { + memcpy(&addr[dwrq->length], qual, sizeof(struct iw_quality) * dwrq->length); + + dwrq->flags = 1; + } + + return 0; +} + +#if WIRELESS_EXT > 13 +static int +wl_iw_set_scan( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + wlc_ssid_t ssid; + + WL_TRACE(("%s: SIOCSIWSCAN\n", dev->name)); + + + memset(&ssid, 0, sizeof(ssid)); + +#if WIRELESS_EXT > 17 + + if (wrqu->data.length == sizeof(struct iw_scan_req)) { + if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { + struct iw_scan_req *req = (struct iw_scan_req *)extra; + ssid.SSID_len = MIN(sizeof(ssid.SSID), req->essid_len); + memcpy(ssid.SSID, req->essid, ssid.SSID_len); + ssid.SSID_len = htod32(ssid.SSID_len); + } + } +#endif + + (void) dev_wlc_ioctl(dev, WLC_SCAN, &ssid, sizeof(ssid)); + + return 0; +} + +static int +wl_iw_iscan_set_scan( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + wlc_ssid_t ssid; + iscan_info_t *iscan = g_iscan; + + WL_TRACE(("%s: SIOCSIWSCAN\n", dev->name)); + + + if ((!iscan) || (iscan->sysioc_pid < 0)) { + return wl_iw_set_scan(dev, info, wrqu, extra); + } + if (iscan->iscan_state == ISCAN_STATE_SCANING) { + return 0; + } + + + memset(&ssid, 0, sizeof(ssid)); + +#if WIRELESS_EXT > 17 + + if (wrqu->data.length == sizeof(struct iw_scan_req)) { + if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { + struct iw_scan_req *req = (struct iw_scan_req *)extra; + ssid.SSID_len = MIN(sizeof(ssid.SSID), req->essid_len); + memcpy(ssid.SSID, req->essid, ssid.SSID_len); + ssid.SSID_len = htod32(ssid.SSID_len); + } + } +#endif + + iscan->list_cur = iscan->list_hdr; + iscan->iscan_state = ISCAN_STATE_SCANING; + + + wl_iw_set_event_mask(dev); + wl_iw_iscan(iscan, &ssid, WL_SCAN_ACTION_START); + + iscan->timer.expires = jiffies + msecs_to_jiffies(iscan->timer_ms); + add_timer(&iscan->timer); + iscan->timer_on = 1; + + return 0; +} + +#if WIRELESS_EXT > 17 +static bool +ie_is_wpa_ie(uint8 **wpaie, uint8 **tlvs, int *tlvs_len) +{ + + + uint8 *ie = *wpaie; + + + if ((ie[1] >= 6) && + !bcmp((const void *)&ie[2], (const void *)(WPA_OUI "\x01"), 4)) { + return TRUE; + } + + + ie += ie[1] + 2; + + *tlvs_len -= (int)(ie - *tlvs); + + *tlvs = ie; + return FALSE; +} + +static bool +ie_is_wps_ie(uint8 **wpsie, uint8 **tlvs, int *tlvs_len) +{ + + + uint8 *ie = *wpsie; + + + if ((ie[1] >= 4) && + !bcmp((const void *)&ie[2], (const void *)(WPA_OUI "\x04"), 4)) { + return TRUE; + } + + + ie += ie[1] + 2; + + *tlvs_len -= (int)(ie - *tlvs); + + *tlvs = ie; + return FALSE; +} +#endif + + +static int +wl_iw_handle_scanresults_ies(char **event_p, char *end, + struct iw_request_info *info, wl_bss_info_t *bi) +{ +#if WIRELESS_EXT > 17 + struct iw_event iwe; + char *event; + + event = *event_p; + if (bi->ie_length) { + + bcm_tlv_t *ie; + uint8 *ptr = ((uint8 *)bi) + sizeof(wl_bss_info_t); + int ptr_len = bi->ie_length; + + if ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_RSN_ID))) { + iwe.cmd = IWEVGENIE; + iwe.u.data.length = ie->len + 2; + event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie); + } + ptr = ((uint8 *)bi) + sizeof(wl_bss_info_t); + +#if defined(WLFBT) + if ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_MDIE_ID))) { + iwe.cmd = IWEVGENIE; + iwe.u.data.length = ie->len + 2; + event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie); + } + ptr = ((uint8 *)bi) + sizeof(wl_bss_info_t); +#endif + + while ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_WPA_ID))) { + + if (ie_is_wps_ie(((uint8 **)&ie), &ptr, &ptr_len)) { + iwe.cmd = IWEVGENIE; + iwe.u.data.length = ie->len + 2; + event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie); + break; + } + } + + ptr = ((uint8 *)bi) + sizeof(wl_bss_info_t); + ptr_len = bi->ie_length; + while ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_WPA_ID))) { + if (ie_is_wpa_ie(((uint8 **)&ie), &ptr, &ptr_len)) { + iwe.cmd = IWEVGENIE; + iwe.u.data.length = ie->len + 2; + event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie); + break; + } + } + + *event_p = event; + } + +#endif + return 0; +} +static int +wl_iw_get_scan( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + channel_info_t ci; + wl_scan_results_t *list; + struct iw_event iwe; + wl_bss_info_t *bi = NULL; + int error, i, j; + char *event = extra, *end = extra + dwrq->length, *value; + uint buflen = dwrq->length; + + WL_TRACE(("%s: SIOCGIWSCAN\n", dev->name)); + + if (!extra) + return -EINVAL; + + + if ((error = dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(ci)))) + return error; + ci.scan_channel = dtoh32(ci.scan_channel); + if (ci.scan_channel) + return -EAGAIN; + + + list = kmalloc(buflen, GFP_KERNEL); + if (!list) + return -ENOMEM; + memset(list, 0, buflen); + list->buflen = htod32(buflen); + if ((error = dev_wlc_ioctl(dev, WLC_SCAN_RESULTS, list, buflen))) { + kfree(list); + return error; + } + list->buflen = dtoh32(list->buflen); + list->version = dtoh32(list->version); + list->count = dtoh32(list->count); + + ASSERT(list->version == WL_BSS_INFO_VERSION); + + for (i = 0; i < list->count && i < IW_MAX_AP; i++) { + bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info; + ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list + + buflen)); + + + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, &bi->BSSID, ETHER_ADDR_LEN); + event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_ADDR_LEN); + + + iwe.u.data.length = dtoh32(bi->SSID_len); + iwe.cmd = SIOCGIWESSID; + iwe.u.data.flags = 1; + event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, bi->SSID); + + + if (dtoh16(bi->capability) & (DOT11_CAP_ESS | DOT11_CAP_IBSS)) { + iwe.cmd = SIOCGIWMODE; + if (dtoh16(bi->capability) & DOT11_CAP_ESS) + iwe.u.mode = IW_MODE_INFRA; + else + iwe.u.mode = IW_MODE_ADHOC; + event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_UINT_LEN); + } + + + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = wf_channel2mhz(CHSPEC_CHANNEL(bi->chanspec), + CHSPEC_CHANNEL(bi->chanspec) <= CH_MAX_2G_CHANNEL ? + WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G); + iwe.u.freq.e = 6; + event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_FREQ_LEN); + + + iwe.cmd = IWEVQUAL; + iwe.u.qual.qual = rssi_to_qual(dtoh16(bi->RSSI)); + iwe.u.qual.level = 0x100 + dtoh16(bi->RSSI); + iwe.u.qual.noise = 0x100 + bi->phy_noise; + event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_QUAL_LEN); + + + wl_iw_handle_scanresults_ies(&event, end, info, bi); + + + iwe.cmd = SIOCGIWENCODE; + if (dtoh16(bi->capability) & DOT11_CAP_PRIVACY) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + iwe.u.data.length = 0; + event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)event); + + + if (bi->rateset.count) { + value = event + IW_EV_LCP_LEN; + iwe.cmd = SIOCGIWRATE; + + iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; + for (j = 0; j < bi->rateset.count && j < IW_MAX_BITRATES; j++) { + iwe.u.bitrate.value = (bi->rateset.rates[j] & 0x7f) * 500000; + value = IWE_STREAM_ADD_VALUE(info, event, value, end, &iwe, + IW_EV_PARAM_LEN); + } + event = value; + } + } + + kfree(list); + + dwrq->length = event - extra; + dwrq->flags = 0; + + return 0; +} + +static int +wl_iw_iscan_get_scan( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + wl_scan_results_t *list; + struct iw_event iwe; + wl_bss_info_t *bi = NULL; + int ii, j; + int apcnt; + char *event = extra, *end = extra + dwrq->length, *value; + iscan_info_t *iscan = g_iscan; + iscan_buf_t * p_buf; + + WL_TRACE(("%s: SIOCGIWSCAN\n", dev->name)); + + if (!extra) + return -EINVAL; + + + if ((!iscan) || (iscan->sysioc_pid < 0)) { + return wl_iw_get_scan(dev, info, dwrq, extra); + } + + + if (iscan->iscan_state == ISCAN_STATE_SCANING) + return -EAGAIN; + + apcnt = 0; + p_buf = iscan->list_hdr; + + while (p_buf != iscan->list_cur) { + list = &((wl_iscan_results_t*)p_buf->iscan_buf)->results; + + if (list->version != WL_BSS_INFO_VERSION) { + WL_ERROR(("list->version %d != WL_BSS_INFO_VERSION\n", list->version)); + } + + bi = NULL; + for (ii = 0; ii < list->count && apcnt < IW_MAX_AP; apcnt++, ii++) { + bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info; + ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list + + WLC_IW_ISCAN_MAXLEN)); + + + if (event + ETHER_ADDR_LEN + bi->SSID_len + IW_EV_UINT_LEN + IW_EV_FREQ_LEN + + IW_EV_QUAL_LEN >= end) + return -E2BIG; + + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, &bi->BSSID, ETHER_ADDR_LEN); + event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_ADDR_LEN); + + + iwe.u.data.length = dtoh32(bi->SSID_len); + iwe.cmd = SIOCGIWESSID; + iwe.u.data.flags = 1; + event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, bi->SSID); + + + if (dtoh16(bi->capability) & (DOT11_CAP_ESS | DOT11_CAP_IBSS)) { + iwe.cmd = SIOCGIWMODE; + if (dtoh16(bi->capability) & DOT11_CAP_ESS) + iwe.u.mode = IW_MODE_INFRA; + else + iwe.u.mode = IW_MODE_ADHOC; + event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_UINT_LEN); + } + + + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = wf_channel2mhz(CHSPEC_CHANNEL(bi->chanspec), + CHSPEC_CHANNEL(bi->chanspec) <= CH_MAX_2G_CHANNEL ? + WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G); + iwe.u.freq.e = 6; + event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_FREQ_LEN); + + + iwe.cmd = IWEVQUAL; + iwe.u.qual.qual = rssi_to_qual(dtoh16(bi->RSSI)); + iwe.u.qual.level = 0x100 + dtoh16(bi->RSSI); + iwe.u.qual.noise = 0x100 + bi->phy_noise; + event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_QUAL_LEN); + + + wl_iw_handle_scanresults_ies(&event, end, info, bi); + + + iwe.cmd = SIOCGIWENCODE; + if (dtoh16(bi->capability) & DOT11_CAP_PRIVACY) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + iwe.u.data.length = 0; + event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)event); + + + if (bi->rateset.count <= sizeof(bi->rateset.rates)) { + if (event + IW_MAX_BITRATES*IW_EV_PARAM_LEN >= end) + return -E2BIG; + + value = event + IW_EV_LCP_LEN; + iwe.cmd = SIOCGIWRATE; + + iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; + for (j = 0; j < bi->rateset.count && j < IW_MAX_BITRATES; j++) { + iwe.u.bitrate.value = (bi->rateset.rates[j] & 0x7f) * 500000; + value = IWE_STREAM_ADD_VALUE(info, event, value, end, &iwe, + IW_EV_PARAM_LEN); + } + event = value; + } + } + p_buf = p_buf->next; + } + + dwrq->length = event - extra; + dwrq->flags = 0; + + return 0; +} + +#endif + + +static int +wl_iw_set_essid( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + wlc_ssid_t ssid; + int error; + + WL_TRACE(("%s: SIOCSIWESSID\n", dev->name)); + + + memset(&ssid, 0, sizeof(ssid)); + if (dwrq->length && extra) { +#if WIRELESS_EXT > 20 + ssid.SSID_len = MIN(sizeof(ssid.SSID), dwrq->length); +#else + ssid.SSID_len = MIN(sizeof(ssid.SSID), dwrq->length-1); +#endif + memcpy(ssid.SSID, extra, ssid.SSID_len); + ssid.SSID_len = htod32(ssid.SSID_len); + + if ((error = dev_wlc_ioctl(dev, WLC_SET_SSID, &ssid, sizeof(ssid)))) + return error; + } + + else { + scb_val_t scbval; + bzero(&scbval, sizeof(scb_val_t)); + if ((error = dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t)))) + return error; + } + return 0; +} + +static int +wl_iw_get_essid( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + wlc_ssid_t ssid; + int error; + + WL_TRACE(("%s: SIOCGIWESSID\n", dev->name)); + + if (!extra) + return -EINVAL; + + if ((error = dev_wlc_ioctl(dev, WLC_GET_SSID, &ssid, sizeof(ssid)))) { + WL_ERROR(("Error getting the SSID\n")); + return error; + } + + ssid.SSID_len = dtoh32(ssid.SSID_len); + + + memcpy(extra, ssid.SSID, ssid.SSID_len); + + dwrq->length = ssid.SSID_len; + + dwrq->flags = 1; + + return 0; +} + +static int +wl_iw_set_nick( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + wl_iw_t *iw = IW_DEV_IF(dev); + WL_TRACE(("%s: SIOCSIWNICKN\n", dev->name)); + + if (!extra) + return -EINVAL; + + + if (dwrq->length > sizeof(iw->nickname)) + return -E2BIG; + + memcpy(iw->nickname, extra, dwrq->length); + iw->nickname[dwrq->length - 1] = '\0'; + + return 0; +} + +static int +wl_iw_get_nick( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + wl_iw_t *iw = IW_DEV_IF(dev); + WL_TRACE(("%s: SIOCGIWNICKN\n", dev->name)); + + if (!extra) + return -EINVAL; + + strcpy(extra, iw->nickname); + dwrq->length = strlen(extra) + 1; + + return 0; +} + +static int wl_iw_set_rate( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + wl_rateset_t rateset; + int error, rate, i, error_bg, error_a; + + WL_TRACE(("%s: SIOCSIWRATE\n", dev->name)); + + + if ((error = dev_wlc_ioctl(dev, WLC_GET_CURR_RATESET, &rateset, sizeof(rateset)))) + return error; + + rateset.count = dtoh32(rateset.count); + + if (vwrq->value < 0) { + + rate = rateset.rates[rateset.count - 1] & 0x7f; + } else if (vwrq->value < rateset.count) { + + rate = rateset.rates[vwrq->value] & 0x7f; + } else { + + rate = vwrq->value / 500000; + } + + if (vwrq->fixed) { + + error_bg = dev_wlc_intvar_set(dev, "bg_rate", rate); + error_a = dev_wlc_intvar_set(dev, "a_rate", rate); + + if (error_bg && error_a) + return (error_bg | error_a); + } else { + + + error_bg = dev_wlc_intvar_set(dev, "bg_rate", 0); + + error_a = dev_wlc_intvar_set(dev, "a_rate", 0); + + if (error_bg && error_a) + return (error_bg | error_a); + + + for (i = 0; i < rateset.count; i++) + if ((rateset.rates[i] & 0x7f) > rate) + break; + rateset.count = htod32(i); + + + if ((error = dev_wlc_ioctl(dev, WLC_SET_RATESET, &rateset, sizeof(rateset)))) + return error; + } + + return 0; +} + +static int wl_iw_get_rate( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error, rate; + + WL_TRACE(("%s: SIOCGIWRATE\n", dev->name)); + + + if ((error = dev_wlc_ioctl(dev, WLC_GET_RATE, &rate, sizeof(rate)))) + return error; + rate = dtoh32(rate); + vwrq->value = rate * 500000; + + return 0; +} + +static int +wl_iw_set_rts( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error, rts; + + WL_TRACE(("%s: SIOCSIWRTS\n", dev->name)); + + if (vwrq->disabled) + rts = DOT11_DEFAULT_RTS_LEN; + else if (vwrq->value < 0 || vwrq->value > DOT11_DEFAULT_RTS_LEN) + return -EINVAL; + else + rts = vwrq->value; + + if ((error = dev_wlc_intvar_set(dev, "rtsthresh", rts))) + return error; + + return 0; +} + +static int +wl_iw_get_rts( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error, rts; + + WL_TRACE(("%s: SIOCGIWRTS\n", dev->name)); + + if ((error = dev_wlc_intvar_get(dev, "rtsthresh", &rts))) + return error; + + vwrq->value = rts; + vwrq->disabled = (rts >= DOT11_DEFAULT_RTS_LEN); + vwrq->fixed = 1; + + return 0; +} + +static int +wl_iw_set_frag( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error, frag; + + WL_TRACE(("%s: SIOCSIWFRAG\n", dev->name)); + + if (vwrq->disabled) + frag = DOT11_DEFAULT_FRAG_LEN; + else if (vwrq->value < 0 || vwrq->value > DOT11_DEFAULT_FRAG_LEN) + return -EINVAL; + else + frag = vwrq->value; + + if ((error = dev_wlc_intvar_set(dev, "fragthresh", frag))) + return error; + + return 0; +} + +static int +wl_iw_get_frag( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error, fragthreshold; + + WL_TRACE(("%s: SIOCGIWFRAG\n", dev->name)); + + if ((error = dev_wlc_intvar_get(dev, "fragthresh", &fragthreshold))) + return error; + + vwrq->value = fragthreshold; + vwrq->disabled = (fragthreshold >= DOT11_DEFAULT_FRAG_LEN); + vwrq->fixed = 1; + + return 0; +} + +static int +wl_iw_set_txpow( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error, disable; + uint16 txpwrmw; + WL_TRACE(("%s: SIOCSIWTXPOW\n", dev->name)); + + + disable = vwrq->disabled ? WL_RADIO_SW_DISABLE : 0; + disable += WL_RADIO_SW_DISABLE << 16; + + disable = htod32(disable); + if ((error = dev_wlc_ioctl(dev, WLC_SET_RADIO, &disable, sizeof(disable)))) + return error; + + + if (disable & WL_RADIO_SW_DISABLE) + return 0; + + + if (!(vwrq->flags & IW_TXPOW_MWATT)) + return -EINVAL; + + + if (vwrq->value < 0) + return 0; + + if (vwrq->value > 0xffff) txpwrmw = 0xffff; + else txpwrmw = (uint16)vwrq->value; + + + error = dev_wlc_intvar_set(dev, "qtxpower", (int)(bcm_mw_to_qdbm(txpwrmw))); + return error; +} + +static int +wl_iw_get_txpow( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error, disable, txpwrdbm; + uint8 result; + + WL_TRACE(("%s: SIOCGIWTXPOW\n", dev->name)); + + if ((error = dev_wlc_ioctl(dev, WLC_GET_RADIO, &disable, sizeof(disable))) || + (error = dev_wlc_intvar_get(dev, "qtxpower", &txpwrdbm))) + return error; + + disable = dtoh32(disable); + result = (uint8)(txpwrdbm & ~WL_TXPWR_OVERRIDE); + vwrq->value = (int32)bcm_qdbm_to_mw(result); + vwrq->fixed = 0; + vwrq->disabled = (disable & (WL_RADIO_SW_DISABLE | WL_RADIO_HW_DISABLE)) ? 1 : 0; + vwrq->flags = IW_TXPOW_MWATT; + + return 0; +} + +#if WIRELESS_EXT > 10 +static int +wl_iw_set_retry( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error, lrl, srl; + + WL_TRACE(("%s: SIOCSIWRETRY\n", dev->name)); + + + if (vwrq->disabled || (vwrq->flags & IW_RETRY_LIFETIME)) + return -EINVAL; + + + if (vwrq->flags & IW_RETRY_LIMIT) { + +#if WIRELESS_EXT > 20 + if ((vwrq->flags & IW_RETRY_LONG) ||(vwrq->flags & IW_RETRY_MAX) || + !((vwrq->flags & IW_RETRY_SHORT) || (vwrq->flags & IW_RETRY_MIN))) { +#else + if ((vwrq->flags & IW_RETRY_MAX) || !(vwrq->flags & IW_RETRY_MIN)) { +#endif + + lrl = htod32(vwrq->value); + if ((error = dev_wlc_ioctl(dev, WLC_SET_LRL, &lrl, sizeof(lrl)))) + return error; + } + +#if WIRELESS_EXT > 20 + if ((vwrq->flags & IW_RETRY_SHORT) ||(vwrq->flags & IW_RETRY_MIN) || + !((vwrq->flags & IW_RETRY_LONG) || (vwrq->flags & IW_RETRY_MAX))) { +#else + if ((vwrq->flags & IW_RETRY_MIN) || !(vwrq->flags & IW_RETRY_MAX)) { +#endif + + srl = htod32(vwrq->value); + if ((error = dev_wlc_ioctl(dev, WLC_SET_SRL, &srl, sizeof(srl)))) + return error; + } + } + + return 0; +} + +static int +wl_iw_get_retry( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error, lrl, srl; + + WL_TRACE(("%s: SIOCGIWRETRY\n", dev->name)); + + vwrq->disabled = 0; + + + if ((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) + return -EINVAL; + + + if ((error = dev_wlc_ioctl(dev, WLC_GET_LRL, &lrl, sizeof(lrl))) || + (error = dev_wlc_ioctl(dev, WLC_GET_SRL, &srl, sizeof(srl)))) + return error; + + lrl = dtoh32(lrl); + srl = dtoh32(srl); + + + if (vwrq->flags & IW_RETRY_MAX) { + vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX; + vwrq->value = lrl; + } else { + vwrq->flags = IW_RETRY_LIMIT; + vwrq->value = srl; + if (srl != lrl) + vwrq->flags |= IW_RETRY_MIN; + } + + return 0; +} +#endif + +static int +wl_iw_set_encode( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + wl_wsec_key_t key; + int error, val, wsec; + + WL_TRACE(("%s: SIOCSIWENCODE\n", dev->name)); + + memset(&key, 0, sizeof(key)); + + if ((dwrq->flags & IW_ENCODE_INDEX) == 0) { + + for (key.index = 0; key.index < DOT11_MAX_DEFAULT_KEYS; key.index++) { + val = htod32(key.index); + if ((error = dev_wlc_ioctl(dev, WLC_GET_KEY_PRIMARY, &val, sizeof(val)))) + return error; + val = dtoh32(val); + if (val) + break; + } + + if (key.index == DOT11_MAX_DEFAULT_KEYS) + key.index = 0; + } else { + key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + if (key.index >= DOT11_MAX_DEFAULT_KEYS) + return -EINVAL; + } + + + wsec = (dwrq->flags & IW_ENCODE_DISABLED) ? 0 : WEP_ENABLED; + + if ((error = dev_wlc_intvar_set(dev, "wsec", wsec))) + return error; + + + if (!extra || !dwrq->length || (dwrq->flags & IW_ENCODE_NOKEY)) { + + val = htod32(key.index); + if ((error = dev_wlc_ioctl(dev, WLC_SET_KEY_PRIMARY, &val, sizeof(val)))) + return error; + } else { + key.len = dwrq->length; + + if (dwrq->length > sizeof(key.data)) + return -EINVAL; + + memcpy(key.data, extra, dwrq->length); + + key.flags = WL_PRIMARY_KEY; + switch (key.len) { + case WEP1_KEY_SIZE: + key.algo = CRYPTO_ALGO_WEP1; + break; + case WEP128_KEY_SIZE: + key.algo = CRYPTO_ALGO_WEP128; + break; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14) + case TKIP_KEY_SIZE: + key.algo = CRYPTO_ALGO_TKIP; + break; +#endif + case AES_KEY_SIZE: + key.algo = CRYPTO_ALGO_AES_CCM; + break; + default: + return -EINVAL; + } + + + swap_key_from_BE(&key); + if ((error = dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key)))) + return error; + } + + + val = (dwrq->flags & IW_ENCODE_RESTRICTED) ? 1 : 0; + val = htod32(val); + if ((error = dev_wlc_ioctl(dev, WLC_SET_AUTH, &val, sizeof(val)))) + return error; + + return 0; +} + +static int +wl_iw_get_encode( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + wl_wsec_key_t key; + int error, val, wsec, auth; + + WL_TRACE(("%s: SIOCGIWENCODE\n", dev->name)); + + + bzero(&key, sizeof(wl_wsec_key_t)); + + if ((dwrq->flags & IW_ENCODE_INDEX) == 0) { + + for (key.index = 0; key.index < DOT11_MAX_DEFAULT_KEYS; key.index++) { + val = key.index; + if ((error = dev_wlc_ioctl(dev, WLC_GET_KEY_PRIMARY, &val, sizeof(val)))) + return error; + val = dtoh32(val); + if (val) + break; + } + } else + key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + + if (key.index >= DOT11_MAX_DEFAULT_KEYS) + key.index = 0; + + + + if ((error = dev_wlc_ioctl(dev, WLC_GET_WSEC, &wsec, sizeof(wsec))) || + (error = dev_wlc_ioctl(dev, WLC_GET_AUTH, &auth, sizeof(auth)))) + return error; + + swap_key_to_BE(&key); + + wsec = dtoh32(wsec); + auth = dtoh32(auth); + + dwrq->length = MIN(IW_ENCODING_TOKEN_MAX, key.len); + + + dwrq->flags = key.index + 1; + if (!(wsec & (WEP_ENABLED | TKIP_ENABLED | AES_ENABLED))) { + + dwrq->flags |= IW_ENCODE_DISABLED; + } + if (auth) { + + dwrq->flags |= IW_ENCODE_RESTRICTED; + } + + + if (dwrq->length && extra) + memcpy(extra, key.data, dwrq->length); + + return 0; +} + +static int +wl_iw_set_power( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error, pm; + + WL_TRACE(("%s: SIOCSIWPOWER\n", dev->name)); + + pm = vwrq->disabled ? PM_OFF : PM_MAX; + + pm = htod32(pm); + if ((error = dev_wlc_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm)))) + return error; + + return 0; +} + +static int +wl_iw_get_power( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error, pm; + + WL_TRACE(("%s: SIOCGIWPOWER\n", dev->name)); + + if ((error = dev_wlc_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm)))) + return error; + + pm = dtoh32(pm); + vwrq->disabled = pm ? 0 : 1; + vwrq->flags = IW_POWER_ALL_R; + + return 0; +} + +#if WIRELESS_EXT > 17 +static int +wl_iw_set_wpaie( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *iwp, + char *extra +) +{ + dev_wlc_bufvar_set(dev, "wpaie", extra, iwp->length); + + return 0; +} + +static int +wl_iw_get_wpaie( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *iwp, + char *extra +) +{ + WL_TRACE(("%s: SIOCGIWGENIE\n", dev->name)); + iwp->length = 64; + dev_wlc_bufvar_get(dev, "wpaie", extra, iwp->length); + return 0; +} + +static int +wl_iw_set_encodeext( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + wl_wsec_key_t key; + int error; + struct iw_encode_ext *iwe; + + WL_TRACE(("%s: SIOCSIWENCODEEXT\n", dev->name)); + + memset(&key, 0, sizeof(key)); + iwe = (struct iw_encode_ext *)extra; + + + if (dwrq->flags & IW_ENCODE_DISABLED) { + + } + + + key.index = 0; + if (dwrq->flags & IW_ENCODE_INDEX) + key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + + key.len = iwe->key_len; + + + if (!ETHER_ISMULTI(iwe->addr.sa_data)) + bcopy((void *)&iwe->addr.sa_data, (char *)&key.ea, ETHER_ADDR_LEN); + + + if (key.len == 0) { + if (iwe->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { + WL_WSEC(("Changing the the primary Key to %d\n", key.index)); + + key.index = htod32(key.index); + error = dev_wlc_ioctl(dev, WLC_SET_KEY_PRIMARY, + &key.index, sizeof(key.index)); + if (error) + return error; + } + + else { + swap_key_from_BE(&key); + error = dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key)); + if (error) + return error; + } + } +#if (defined(BCMSUP_PSK) && defined(WLFBT)) + + else if (iwe->alg == IW_ENCODE_ALG_PMK) { + int j; + wsec_pmk_t pmk; + char keystring[WSEC_MAX_PSK_LEN + 1]; + char* charptr = keystring; + uint len; + + + for (j = 0; j < (WSEC_MAX_PSK_LEN / 2); j++) { + sprintf(charptr, "%02x", iwe->key[j]); + charptr += 2; + } + len = strlen(keystring); + pmk.key_len = htod16(len); + bcopy(keystring, pmk.key, len); + pmk.flags = htod16(WSEC_PASSPHRASE); + + error = dev_wlc_ioctl(dev, WLC_SET_WSEC_PMK, &pmk, sizeof(pmk)); + if (error) + return error; + } +#endif + + else { + if (iwe->key_len > sizeof(key.data)) + return -EINVAL; + + WL_WSEC(("Setting the key index %d\n", key.index)); + if (iwe->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { + WL_WSEC(("key is a Primary Key\n")); + key.flags = WL_PRIMARY_KEY; + } + + bcopy((void *)iwe->key, key.data, iwe->key_len); + + if (iwe->alg == IW_ENCODE_ALG_TKIP) { + uint8 keybuf[8]; + bcopy(&key.data[24], keybuf, sizeof(keybuf)); + bcopy(&key.data[16], &key.data[24], sizeof(keybuf)); + bcopy(keybuf, &key.data[16], sizeof(keybuf)); + } + + + if (iwe->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) { + uchar *ivptr; + ivptr = (uchar *)iwe->rx_seq; + key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) | + (ivptr[3] << 8) | ivptr[2]; + key.rxiv.lo = (ivptr[1] << 8) | ivptr[0]; + key.iv_initialized = TRUE; + } + + switch (iwe->alg) { + case IW_ENCODE_ALG_NONE: + key.algo = CRYPTO_ALGO_OFF; + break; + case IW_ENCODE_ALG_WEP: + if (iwe->key_len == WEP1_KEY_SIZE) + key.algo = CRYPTO_ALGO_WEP1; + else + key.algo = CRYPTO_ALGO_WEP128; + break; + case IW_ENCODE_ALG_TKIP: + key.algo = CRYPTO_ALGO_TKIP; + break; + case IW_ENCODE_ALG_CCMP: + key.algo = CRYPTO_ALGO_AES_CCM; + break; + default: + break; + } + swap_key_from_BE(&key); + + dhd_wait_pend8021x(dev); + + error = dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key)); + if (error) + return error; + } + return 0; +} + + +#if WIRELESS_EXT > 17 +struct { + pmkid_list_t pmkids; + pmkid_t foo[MAXPMKID-1]; +} pmkid_list; +static int +wl_iw_set_pmksa( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + struct iw_pmksa *iwpmksa; + uint i; + char eabuf[ETHER_ADDR_STR_LEN]; + pmkid_t * pmkid_array = pmkid_list.pmkids.pmkid; + + WL_TRACE(("%s: SIOCSIWPMKSA\n", dev->name)); + iwpmksa = (struct iw_pmksa *)extra; + bzero((char *)eabuf, ETHER_ADDR_STR_LEN); + if (iwpmksa->cmd == IW_PMKSA_FLUSH) { + WL_TRACE(("wl_iw_set_pmksa - IW_PMKSA_FLUSH\n")); + bzero((char *)&pmkid_list, sizeof(pmkid_list)); + } + if (iwpmksa->cmd == IW_PMKSA_REMOVE) { + pmkid_list_t pmkid, *pmkidptr; + pmkidptr = &pmkid; + bcopy(&iwpmksa->bssid.sa_data[0], &pmkidptr->pmkid[0].BSSID, ETHER_ADDR_LEN); + bcopy(&iwpmksa->pmkid[0], &pmkidptr->pmkid[0].PMKID, WPA2_PMKID_LEN); + { + uint j; + WL_TRACE(("wl_iw_set_pmksa,IW_PMKSA_REMOVE - PMKID: %s = ", + bcm_ether_ntoa(&pmkidptr->pmkid[0].BSSID, + eabuf))); + for (j = 0; j < WPA2_PMKID_LEN; j++) + WL_TRACE(("%02x ", pmkidptr->pmkid[0].PMKID[j])); + WL_TRACE(("\n")); + } + for (i = 0; i < pmkid_list.pmkids.npmkid; i++) + if (!bcmp(&iwpmksa->bssid.sa_data[0], &pmkid_array[i].BSSID, + ETHER_ADDR_LEN)) + break; + for (; i < pmkid_list.pmkids.npmkid; i++) { + bcopy(&pmkid_array[i+1].BSSID, + &pmkid_array[i].BSSID, + ETHER_ADDR_LEN); + bcopy(&pmkid_array[i+1].PMKID, + &pmkid_array[i].PMKID, + WPA2_PMKID_LEN); + } + pmkid_list.pmkids.npmkid--; + } + if (iwpmksa->cmd == IW_PMKSA_ADD) { + bcopy(&iwpmksa->bssid.sa_data[0], + &pmkid_array[pmkid_list.pmkids.npmkid].BSSID, + ETHER_ADDR_LEN); + bcopy(&iwpmksa->pmkid[0], &pmkid_array[pmkid_list.pmkids.npmkid].PMKID, + WPA2_PMKID_LEN); + { + uint j; + uint k; + k = pmkid_list.pmkids.npmkid; + BCM_REFERENCE(k); + WL_TRACE(("wl_iw_set_pmksa,IW_PMKSA_ADD - PMKID: %s = ", + bcm_ether_ntoa(&pmkid_array[k].BSSID, + eabuf))); + for (j = 0; j < WPA2_PMKID_LEN; j++) + WL_TRACE(("%02x ", pmkid_array[k].PMKID[j])); + WL_TRACE(("\n")); + } + pmkid_list.pmkids.npmkid++; + } + WL_TRACE(("PRINTING pmkid LIST - No of elements %d\n", pmkid_list.pmkids.npmkid)); + for (i = 0; i < pmkid_list.pmkids.npmkid; i++) { + uint j; + WL_TRACE(("PMKID[%d]: %s = ", i, + bcm_ether_ntoa(&pmkid_array[i].BSSID, + eabuf))); + for (j = 0; j < WPA2_PMKID_LEN; j++) + WL_TRACE(("%02x ", pmkid_array[i].PMKID[j])); + printf("\n"); + } + WL_TRACE(("\n")); + dev_wlc_bufvar_set(dev, "pmkid_info", (char *)&pmkid_list, sizeof(pmkid_list)); + return 0; +} +#endif + +static int +wl_iw_get_encodeext( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + WL_TRACE(("%s: SIOCGIWENCODEEXT\n", dev->name)); + return 0; +} + +static int +wl_iw_set_wpaauth( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error = 0; + int paramid; + int paramval; + uint32 cipher_combined; + int val = 0; + wl_iw_t *iw = IW_DEV_IF(dev); + + WL_TRACE(("%s: SIOCSIWAUTH\n", dev->name)); + + paramid = vwrq->flags & IW_AUTH_INDEX; + paramval = vwrq->value; + + WL_TRACE(("%s: SIOCSIWAUTH, paramid = 0x%0x, paramval = 0x%0x\n", + dev->name, paramid, paramval)); + + switch (paramid) { + + case IW_AUTH_WPA_VERSION: + + if (paramval & IW_AUTH_WPA_VERSION_DISABLED) + val = WPA_AUTH_DISABLED; + else if (paramval & (IW_AUTH_WPA_VERSION_WPA)) + val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED; + else if (paramval & IW_AUTH_WPA_VERSION_WPA2) + val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED; + WL_TRACE(("%s: %d: setting wpa_auth to 0x%0x\n", __FUNCTION__, __LINE__, val)); + if ((error = dev_wlc_intvar_set(dev, "wpa_auth", val))) + return error; + break; + + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + + if (paramid == IW_AUTH_CIPHER_PAIRWISE) { + iw->pwsec = paramval; + } + else { + iw->gwsec = paramval; + } + + if ((error = dev_wlc_intvar_get(dev, "wsec", &val))) + return error; + + cipher_combined = iw->gwsec | iw->pwsec; + val &= ~(WEP_ENABLED | TKIP_ENABLED | AES_ENABLED); + if (cipher_combined & (IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104)) + val |= WEP_ENABLED; + if (cipher_combined & IW_AUTH_CIPHER_TKIP) + val |= TKIP_ENABLED; + if (cipher_combined & IW_AUTH_CIPHER_CCMP) + val |= AES_ENABLED; + + if (iw->privacy_invoked && !val) { + WL_WSEC(("%s: %s: 'Privacy invoked' TRUE but clearing wsec, assuming " + "we're a WPS enrollee\n", dev->name, __FUNCTION__)); + if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", TRUE))) { + WL_WSEC(("Failed to set iovar is_WPS_enrollee\n")); + return error; + } + } else if (val) { + if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", FALSE))) { + WL_WSEC(("Failed to clear iovar is_WPS_enrollee\n")); + return error; + } + } + + if ((error = dev_wlc_intvar_set(dev, "wsec", val))) + return error; +#ifdef WLFBT + if ((paramid == IW_AUTH_CIPHER_PAIRWISE) && (val | AES_ENABLED)) { + if ((error = dev_wlc_intvar_set(dev, "sup_wpa", 1))) + return error; + } + else if (val == 0) { + if ((error = dev_wlc_intvar_set(dev, "sup_wpa", 0))) + return error; + } +#endif + break; + + case IW_AUTH_KEY_MGMT: + if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val))) + return error; + + if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) { + if (paramval & IW_AUTH_KEY_MGMT_PSK) + val = WPA_AUTH_PSK; + else + val = WPA_AUTH_UNSPECIFIED; + } + else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) { + if (paramval & IW_AUTH_KEY_MGMT_PSK) + val = WPA2_AUTH_PSK; + else + val = WPA2_AUTH_UNSPECIFIED; + } + WL_TRACE(("%s: %d: setting wpa_auth to %d\n", __FUNCTION__, __LINE__, val)); + if ((error = dev_wlc_intvar_set(dev, "wpa_auth", val))) + return error; + break; + + case IW_AUTH_TKIP_COUNTERMEASURES: + dev_wlc_bufvar_set(dev, "tkip_countermeasures", (char *)¶mval, 1); + break; + + case IW_AUTH_80211_AUTH_ALG: + + WL_ERROR(("Setting the D11auth %d\n", paramval)); + if (paramval & IW_AUTH_ALG_OPEN_SYSTEM) + val = 0; + else if (paramval & IW_AUTH_ALG_SHARED_KEY) + val = 1; + else + error = 1; + if (!error && (error = dev_wlc_intvar_set(dev, "auth", val))) + return error; + break; + + case IW_AUTH_WPA_ENABLED: + if (paramval == 0) { + val = 0; + WL_TRACE(("%s: %d: setting wpa_auth to %d\n", __FUNCTION__, __LINE__, val)); + error = dev_wlc_intvar_set(dev, "wpa_auth", val); + return error; + } + else { + + } + break; + + case IW_AUTH_DROP_UNENCRYPTED: + dev_wlc_bufvar_set(dev, "wsec_restrict", (char *)¶mval, 1); + break; + + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + dev_wlc_bufvar_set(dev, "rx_unencrypted_eapol", (char *)¶mval, 1); + break; + +#if WIRELESS_EXT > 17 + + case IW_AUTH_ROAMING_CONTROL: + WL_TRACE(("%s: IW_AUTH_ROAMING_CONTROL\n", __FUNCTION__)); + + break; + + case IW_AUTH_PRIVACY_INVOKED: { + int wsec; + + if (paramval == 0) { + iw->privacy_invoked = FALSE; + if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", FALSE))) { + WL_WSEC(("Failed to clear iovar is_WPS_enrollee\n")); + return error; + } + } else { + iw->privacy_invoked = TRUE; + if ((error = dev_wlc_intvar_get(dev, "wsec", &wsec))) + return error; + + if (!WSEC_ENABLED(wsec)) { + + if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", TRUE))) { + WL_WSEC(("Failed to set iovar is_WPS_enrollee\n")); + return error; + } + } else { + if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", FALSE))) { + WL_WSEC(("Failed to clear iovar is_WPS_enrollee\n")); + return error; + } + } + } + break; + } + + +#endif + + + default: + break; + } + return 0; +} +#define VAL_PSK(_val) (((_val) & WPA_AUTH_PSK) || ((_val) & WPA2_AUTH_PSK)) + +static int +wl_iw_get_wpaauth( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error; + int paramid; + int paramval = 0; + int val; + wl_iw_t *iw = IW_DEV_IF(dev); + + WL_TRACE(("%s: SIOCGIWAUTH\n", dev->name)); + + paramid = vwrq->flags & IW_AUTH_INDEX; + + switch (paramid) { + case IW_AUTH_WPA_VERSION: + + if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val))) + return error; + if (val & (WPA_AUTH_NONE | WPA_AUTH_DISABLED)) + paramval = IW_AUTH_WPA_VERSION_DISABLED; + else if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) + paramval = IW_AUTH_WPA_VERSION_WPA; + else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) + paramval = IW_AUTH_WPA_VERSION_WPA2; + break; + + case IW_AUTH_CIPHER_PAIRWISE: + paramval = iw->pwsec; + break; + + case IW_AUTH_CIPHER_GROUP: + paramval = iw->gwsec; + break; + + case IW_AUTH_KEY_MGMT: + + if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val))) + return error; + if (VAL_PSK(val)) + paramval = IW_AUTH_KEY_MGMT_PSK; + else + paramval = IW_AUTH_KEY_MGMT_802_1X; + + break; + case IW_AUTH_TKIP_COUNTERMEASURES: + dev_wlc_bufvar_get(dev, "tkip_countermeasures", (char *)¶mval, 1); + break; + + case IW_AUTH_DROP_UNENCRYPTED: + dev_wlc_bufvar_get(dev, "wsec_restrict", (char *)¶mval, 1); + break; + + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + dev_wlc_bufvar_get(dev, "rx_unencrypted_eapol", (char *)¶mval, 1); + break; + + case IW_AUTH_80211_AUTH_ALG: + + if ((error = dev_wlc_intvar_get(dev, "auth", &val))) + return error; + if (!val) + paramval = IW_AUTH_ALG_OPEN_SYSTEM; + else + paramval = IW_AUTH_ALG_SHARED_KEY; + break; + case IW_AUTH_WPA_ENABLED: + if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val))) + return error; + if (val) + paramval = TRUE; + else + paramval = FALSE; + break; + +#if WIRELESS_EXT > 17 + + case IW_AUTH_ROAMING_CONTROL: + WL_ERROR(("%s: IW_AUTH_ROAMING_CONTROL\n", __FUNCTION__)); + + break; + + case IW_AUTH_PRIVACY_INVOKED: + paramval = iw->privacy_invoked; + break; + +#endif + } + vwrq->value = paramval; + return 0; +} +#endif + +static const iw_handler wl_iw_handler[] = +{ + (iw_handler) wl_iw_config_commit, + (iw_handler) wl_iw_get_name, + (iw_handler) NULL, + (iw_handler) NULL, + (iw_handler) wl_iw_set_freq, + (iw_handler) wl_iw_get_freq, + (iw_handler) wl_iw_set_mode, + (iw_handler) wl_iw_get_mode, + (iw_handler) NULL, + (iw_handler) NULL, + (iw_handler) NULL, + (iw_handler) wl_iw_get_range, + (iw_handler) NULL, + (iw_handler) NULL, + (iw_handler) NULL, + (iw_handler) NULL, + (iw_handler) wl_iw_set_spy, + (iw_handler) wl_iw_get_spy, + (iw_handler) NULL, + (iw_handler) NULL, + (iw_handler) wl_iw_set_wap, + (iw_handler) wl_iw_get_wap, +#if WIRELESS_EXT > 17 + (iw_handler) wl_iw_mlme, +#else + (iw_handler) NULL, +#endif + (iw_handler) wl_iw_iscan_get_aplist, +#if WIRELESS_EXT > 13 + (iw_handler) wl_iw_iscan_set_scan, + (iw_handler) wl_iw_iscan_get_scan, +#else + (iw_handler) NULL, + (iw_handler) NULL, +#endif + (iw_handler) wl_iw_set_essid, + (iw_handler) wl_iw_get_essid, + (iw_handler) wl_iw_set_nick, + (iw_handler) wl_iw_get_nick, + (iw_handler) NULL, + (iw_handler) NULL, + (iw_handler) wl_iw_set_rate, + (iw_handler) wl_iw_get_rate, + (iw_handler) wl_iw_set_rts, + (iw_handler) wl_iw_get_rts, + (iw_handler) wl_iw_set_frag, + (iw_handler) wl_iw_get_frag, + (iw_handler) wl_iw_set_txpow, + (iw_handler) wl_iw_get_txpow, +#if WIRELESS_EXT > 10 + (iw_handler) wl_iw_set_retry, + (iw_handler) wl_iw_get_retry, +#endif + (iw_handler) wl_iw_set_encode, + (iw_handler) wl_iw_get_encode, + (iw_handler) wl_iw_set_power, + (iw_handler) wl_iw_get_power, +#if WIRELESS_EXT > 17 + (iw_handler) NULL, + (iw_handler) NULL, + (iw_handler) wl_iw_set_wpaie, + (iw_handler) wl_iw_get_wpaie, + (iw_handler) wl_iw_set_wpaauth, + (iw_handler) wl_iw_get_wpaauth, + (iw_handler) wl_iw_set_encodeext, + (iw_handler) wl_iw_get_encodeext, + (iw_handler) wl_iw_set_pmksa, +#endif +}; + +#if WIRELESS_EXT > 12 +enum { + WL_IW_SET_LEDDC = SIOCIWFIRSTPRIV, + WL_IW_SET_VLANMODE, + WL_IW_SET_PM +}; + +static iw_handler wl_iw_priv_handler[] = { + wl_iw_set_leddc, + wl_iw_set_vlanmode, + wl_iw_set_pm +}; + +static struct iw_priv_args wl_iw_priv_args[] = { + { + WL_IW_SET_LEDDC, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "set_leddc" + }, + { + WL_IW_SET_VLANMODE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "set_vlanmode" + }, + { + WL_IW_SET_PM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "set_pm" + } +}; + +const struct iw_handler_def wl_iw_handler_def = +{ + .num_standard = ARRAYSIZE(wl_iw_handler), + .num_private = ARRAY_SIZE(wl_iw_priv_handler), + .num_private_args = ARRAY_SIZE(wl_iw_priv_args), + .standard = (iw_handler *) wl_iw_handler, + .private = wl_iw_priv_handler, + .private_args = wl_iw_priv_args, +#if WIRELESS_EXT >= 19 + get_wireless_stats: dhd_get_wireless_stats, +#endif + }; +#endif + +int +wl_iw_ioctl( + struct net_device *dev, + struct ifreq *rq, + int cmd +) +{ + struct iwreq *wrq = (struct iwreq *) rq; + struct iw_request_info info; + iw_handler handler; + char *extra = NULL; + size_t token_size = 1; + int max_tokens = 0, ret = 0; + + if (cmd < SIOCIWFIRST || + IW_IOCTL_IDX(cmd) >= ARRAYSIZE(wl_iw_handler) || + !(handler = wl_iw_handler[IW_IOCTL_IDX(cmd)])) + return -EOPNOTSUPP; + + switch (cmd) { + + case SIOCSIWESSID: + case SIOCGIWESSID: + case SIOCSIWNICKN: + case SIOCGIWNICKN: + max_tokens = IW_ESSID_MAX_SIZE + 1; + break; + + case SIOCSIWENCODE: + case SIOCGIWENCODE: +#if WIRELESS_EXT > 17 + case SIOCSIWENCODEEXT: + case SIOCGIWENCODEEXT: +#endif + max_tokens = IW_ENCODING_TOKEN_MAX; + break; + + case SIOCGIWRANGE: + max_tokens = sizeof(struct iw_range); + break; + + case SIOCGIWAPLIST: + token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality); + max_tokens = IW_MAX_AP; + break; + +#if WIRELESS_EXT > 13 + case SIOCGIWSCAN: + if (g_iscan) + max_tokens = wrq->u.data.length; + else + max_tokens = IW_SCAN_MAX_DATA; + break; +#endif + + case SIOCSIWSPY: + token_size = sizeof(struct sockaddr); + max_tokens = IW_MAX_SPY; + break; + + case SIOCGIWSPY: + token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality); + max_tokens = IW_MAX_SPY; + break; + default: + break; + } + + if (max_tokens && wrq->u.data.pointer) { + if (wrq->u.data.length > max_tokens) + return -E2BIG; + + if (!(extra = kmalloc(max_tokens * token_size, GFP_KERNEL))) + return -ENOMEM; + + if (copy_from_user(extra, wrq->u.data.pointer, wrq->u.data.length * token_size)) { + kfree(extra); + return -EFAULT; + } + } + + info.cmd = cmd; + info.flags = 0; + + ret = handler(dev, &info, &wrq->u, extra); + + if (extra) { + if (copy_to_user(wrq->u.data.pointer, extra, wrq->u.data.length * token_size)) { + kfree(extra); + return -EFAULT; + } + + kfree(extra); + } + + return ret; +} + + +bool +wl_iw_conn_status_str(uint32 event_type, uint32 status, uint32 reason, + char* stringBuf, uint buflen) +{ + typedef struct conn_fail_event_map_t { + uint32 inEvent; + uint32 inStatus; + uint32 inReason; + const char* outName; + const char* outCause; + } conn_fail_event_map_t; + + +# define WL_IW_DONT_CARE 9999 + const conn_fail_event_map_t event_map [] = { + + + {WLC_E_SET_SSID, WLC_E_STATUS_SUCCESS, WL_IW_DONT_CARE, + "Conn", "Success"}, + {WLC_E_SET_SSID, WLC_E_STATUS_NO_NETWORKS, WL_IW_DONT_CARE, + "Conn", "NoNetworks"}, + {WLC_E_SET_SSID, WLC_E_STATUS_FAIL, WL_IW_DONT_CARE, + "Conn", "ConfigMismatch"}, + {WLC_E_PRUNE, WL_IW_DONT_CARE, WLC_E_PRUNE_ENCR_MISMATCH, + "Conn", "EncrypMismatch"}, + {WLC_E_PRUNE, WL_IW_DONT_CARE, WLC_E_RSN_MISMATCH, + "Conn", "RsnMismatch"}, + {WLC_E_AUTH, WLC_E_STATUS_TIMEOUT, WL_IW_DONT_CARE, + "Conn", "AuthTimeout"}, + {WLC_E_AUTH, WLC_E_STATUS_FAIL, WL_IW_DONT_CARE, + "Conn", "AuthFail"}, + {WLC_E_AUTH, WLC_E_STATUS_NO_ACK, WL_IW_DONT_CARE, + "Conn", "AuthNoAck"}, + {WLC_E_REASSOC, WLC_E_STATUS_FAIL, WL_IW_DONT_CARE, + "Conn", "ReassocFail"}, + {WLC_E_REASSOC, WLC_E_STATUS_TIMEOUT, WL_IW_DONT_CARE, + "Conn", "ReassocTimeout"}, + {WLC_E_REASSOC, WLC_E_STATUS_ABORT, WL_IW_DONT_CARE, + "Conn", "ReassocAbort"}, + {WLC_E_PSK_SUP, WLC_SUP_KEYED, WL_IW_DONT_CARE, + "Sup", "ConnSuccess"}, + {WLC_E_PSK_SUP, WL_IW_DONT_CARE, WL_IW_DONT_CARE, + "Sup", "WpaHandshakeFail"}, + {WLC_E_DEAUTH_IND, WL_IW_DONT_CARE, WL_IW_DONT_CARE, + "Conn", "Deauth"}, + {WLC_E_DISASSOC_IND, WL_IW_DONT_CARE, WL_IW_DONT_CARE, + "Conn", "DisassocInd"}, + {WLC_E_DISASSOC, WL_IW_DONT_CARE, WL_IW_DONT_CARE, + "Conn", "Disassoc"} + }; + + const char* name = ""; + const char* cause = NULL; + int i; + + + for (i = 0; i < sizeof(event_map)/sizeof(event_map[0]); i++) { + const conn_fail_event_map_t* row = &event_map[i]; + if (row->inEvent == event_type && + (row->inStatus == status || row->inStatus == WL_IW_DONT_CARE) && + (row->inReason == reason || row->inReason == WL_IW_DONT_CARE)) { + name = row->outName; + cause = row->outCause; + break; + } + } + + + if (cause) { + memset(stringBuf, 0, buflen); + snprintf(stringBuf, buflen, "%s %s %02d %02d", + name, cause, status, reason); + WL_TRACE(("Connection status: %s\n", stringBuf)); + return TRUE; + } else { + return FALSE; + } +} + +#if (WIRELESS_EXT > 14) + +static bool +wl_iw_check_conn_fail(wl_event_msg_t *e, char* stringBuf, uint buflen) +{ + uint32 event = ntoh32(e->event_type); + uint32 status = ntoh32(e->status); + uint32 reason = ntoh32(e->reason); + + if (wl_iw_conn_status_str(event, status, reason, stringBuf, buflen)) { + return TRUE; + } else + { + return FALSE; + } +} +#endif + +#ifndef IW_CUSTOM_MAX +#define IW_CUSTOM_MAX 256 +#endif + +void +wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) +{ +#if WIRELESS_EXT > 13 + union iwreq_data wrqu; + char extra[IW_CUSTOM_MAX + 1]; + int cmd = 0; + uint32 event_type = ntoh32(e->event_type); + uint16 flags = ntoh16(e->flags); + uint32 datalen = ntoh32(e->datalen); + uint32 status = ntoh32(e->status); + + memset(&wrqu, 0, sizeof(wrqu)); + memset(extra, 0, sizeof(extra)); + + memcpy(wrqu.addr.sa_data, &e->addr, ETHER_ADDR_LEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + + switch (event_type) { + case WLC_E_TXFAIL: + cmd = IWEVTXDROP; + break; +#if WIRELESS_EXT > 14 + case WLC_E_JOIN: + case WLC_E_ASSOC_IND: + case WLC_E_REASSOC_IND: + cmd = IWEVREGISTERED; + break; + case WLC_E_DEAUTH_IND: + case WLC_E_DISASSOC_IND: + cmd = SIOCGIWAP; + wrqu.data.length = strlen(extra); + bzero(wrqu.addr.sa_data, ETHER_ADDR_LEN); + bzero(&extra, ETHER_ADDR_LEN); + break; + + case WLC_E_LINK: + case WLC_E_NDIS_LINK: + cmd = SIOCGIWAP; + wrqu.data.length = strlen(extra); + if (!(flags & WLC_EVENT_MSG_LINK)) { + bzero(wrqu.addr.sa_data, ETHER_ADDR_LEN); + bzero(&extra, ETHER_ADDR_LEN); + } + break; + case WLC_E_ACTION_FRAME: + cmd = IWEVCUSTOM; + if (datalen + 1 <= sizeof(extra)) { + wrqu.data.length = datalen + 1; + extra[0] = WLC_E_ACTION_FRAME; + memcpy(&extra[1], data, datalen); + WL_TRACE(("WLC_E_ACTION_FRAME len %d \n", wrqu.data.length)); + } + break; + + case WLC_E_ACTION_FRAME_COMPLETE: + cmd = IWEVCUSTOM; + if (sizeof(status) + 1 <= sizeof(extra)) { + wrqu.data.length = sizeof(status) + 1; + extra[0] = WLC_E_ACTION_FRAME_COMPLETE; + memcpy(&extra[1], &status, sizeof(status)); + WL_TRACE(("wl_iw_event status %d \n", status)); + } + break; +#endif +#if WIRELESS_EXT > 17 + case WLC_E_MIC_ERROR: { + struct iw_michaelmicfailure *micerrevt = (struct iw_michaelmicfailure *)&extra; + cmd = IWEVMICHAELMICFAILURE; + wrqu.data.length = sizeof(struct iw_michaelmicfailure); + if (flags & WLC_EVENT_MSG_GROUP) + micerrevt->flags |= IW_MICFAILURE_GROUP; + else + micerrevt->flags |= IW_MICFAILURE_PAIRWISE; + memcpy(micerrevt->src_addr.sa_data, &e->addr, ETHER_ADDR_LEN); + micerrevt->src_addr.sa_family = ARPHRD_ETHER; + + break; + } + + case WLC_E_ASSOC_REQ_IE: + cmd = IWEVASSOCREQIE; + wrqu.data.length = datalen; + if (datalen < sizeof(extra)) + memcpy(extra, data, datalen); + break; + + case WLC_E_ASSOC_RESP_IE: + cmd = IWEVASSOCRESPIE; + wrqu.data.length = datalen; + if (datalen < sizeof(extra)) + memcpy(extra, data, datalen); + break; + + case WLC_E_PMKID_CACHE: { + struct iw_pmkid_cand *iwpmkidcand = (struct iw_pmkid_cand *)&extra; + pmkid_cand_list_t *pmkcandlist; + pmkid_cand_t *pmkidcand; + int count; + + if (data == NULL) + break; + + cmd = IWEVPMKIDCAND; + pmkcandlist = data; + count = ntoh32_ua((uint8 *)&pmkcandlist->npmkid_cand); + wrqu.data.length = sizeof(struct iw_pmkid_cand); + pmkidcand = pmkcandlist->pmkid_cand; + while (count) { + bzero(iwpmkidcand, sizeof(struct iw_pmkid_cand)); + if (pmkidcand->preauth) + iwpmkidcand->flags |= IW_PMKID_CAND_PREAUTH; + bcopy(&pmkidcand->BSSID, &iwpmkidcand->bssid.sa_data, + ETHER_ADDR_LEN); + wireless_send_event(dev, cmd, &wrqu, extra); + pmkidcand++; + count--; + } + break; + } +#endif + + case WLC_E_SCAN_COMPLETE: +#if WIRELESS_EXT > 14 + cmd = SIOCGIWSCAN; +#endif + WL_TRACE(("event WLC_E_SCAN_COMPLETE\n")); + if ((g_iscan) && (g_iscan->sysioc_pid >= 0) && + (g_iscan->iscan_state != ISCAN_STATE_IDLE)) + up(&g_iscan->sysioc_sem); + break; + + default: + + break; + } + + if (cmd) { + if (cmd == SIOCGIWSCAN) + wireless_send_event(dev, cmd, &wrqu, NULL); + else + wireless_send_event(dev, cmd, &wrqu, extra); + } + +#if WIRELESS_EXT > 14 + + memset(extra, 0, sizeof(extra)); + if (wl_iw_check_conn_fail(e, extra, sizeof(extra))) { + cmd = IWEVCUSTOM; + wrqu.data.length = strlen(extra); + wireless_send_event(dev, cmd, &wrqu, extra); + } +#endif + +#endif +} + +int wl_iw_get_wireless_stats(struct net_device *dev, struct iw_statistics *wstats) +{ + int res = 0; + wl_cnt_t cnt; + int phy_noise; + int rssi; + scb_val_t scb_val; + + phy_noise = 0; + if ((res = dev_wlc_ioctl(dev, WLC_GET_PHY_NOISE, &phy_noise, sizeof(phy_noise)))) + goto done; + + phy_noise = dtoh32(phy_noise); + WL_TRACE(("wl_iw_get_wireless_stats phy noise=%d\n *****", phy_noise)); + + scb_val.val = 0; + if ((res = dev_wlc_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t)))) + goto done; + + rssi = dtoh32(scb_val.val); + WL_TRACE(("wl_iw_get_wireless_stats rssi=%d ****** \n", rssi)); + if (rssi <= WL_IW_RSSI_NO_SIGNAL) + wstats->qual.qual = 0; + else if (rssi <= WL_IW_RSSI_VERY_LOW) + wstats->qual.qual = 1; + else if (rssi <= WL_IW_RSSI_LOW) + wstats->qual.qual = 2; + else if (rssi <= WL_IW_RSSI_GOOD) + wstats->qual.qual = 3; + else if (rssi <= WL_IW_RSSI_VERY_GOOD) + wstats->qual.qual = 4; + else + wstats->qual.qual = 5; + + + wstats->qual.level = 0x100 + rssi; + wstats->qual.noise = 0x100 + phy_noise; +#if WIRELESS_EXT > 18 + wstats->qual.updated |= (IW_QUAL_ALL_UPDATED | IW_QUAL_DBM); +#else + wstats->qual.updated |= 7; +#endif + +#if WIRELESS_EXT > 11 + WL_TRACE(("wl_iw_get_wireless_stats counters=%d\n *****", (int)sizeof(wl_cnt_t))); + + memset(&cnt, 0, sizeof(wl_cnt_t)); + res = dev_wlc_bufvar_get(dev, "counters", (char *)&cnt, sizeof(wl_cnt_t)); + if (res) + { + WL_ERROR(("wl_iw_get_wireless_stats counters failed error=%d ****** \n", res)); + goto done; + } + + cnt.version = dtoh16(cnt.version); + if (cnt.version != WL_CNT_T_VERSION) { + WL_TRACE(("\tIncorrect version of counters struct: expected %d; got %d\n", + WL_CNT_T_VERSION, cnt.version)); + goto done; + } + + wstats->discard.nwid = 0; + wstats->discard.code = dtoh32(cnt.rxundec); + wstats->discard.fragment = dtoh32(cnt.rxfragerr); + wstats->discard.retries = dtoh32(cnt.txfail); + wstats->discard.misc = dtoh32(cnt.rxrunt) + dtoh32(cnt.rxgiant); + wstats->miss.beacon = 0; + + WL_TRACE(("wl_iw_get_wireless_stats counters txframe=%d txbyte=%d\n", + dtoh32(cnt.txframe), dtoh32(cnt.txbyte))); + WL_TRACE(("wl_iw_get_wireless_stats counters rxfrmtoolong=%d\n", dtoh32(cnt.rxfrmtoolong))); + WL_TRACE(("wl_iw_get_wireless_stats counters rxbadplcp=%d\n", dtoh32(cnt.rxbadplcp))); + WL_TRACE(("wl_iw_get_wireless_stats counters rxundec=%d\n", dtoh32(cnt.rxundec))); + WL_TRACE(("wl_iw_get_wireless_stats counters rxfragerr=%d\n", dtoh32(cnt.rxfragerr))); + WL_TRACE(("wl_iw_get_wireless_stats counters txfail=%d\n", dtoh32(cnt.txfail))); + WL_TRACE(("wl_iw_get_wireless_stats counters rxrunt=%d\n", dtoh32(cnt.rxrunt))); + WL_TRACE(("wl_iw_get_wireless_stats counters rxgiant=%d\n", dtoh32(cnt.rxgiant))); + +#endif + +done: + return res; +} + +static void +wl_iw_timerfunc(ulong data) +{ + iscan_info_t *iscan = (iscan_info_t *)data; + iscan->timer_on = 0; + if (iscan->iscan_state != ISCAN_STATE_IDLE) { + WL_TRACE(("timer trigger\n")); + up(&iscan->sysioc_sem); + } +} + +static void +wl_iw_set_event_mask(struct net_device *dev) +{ + char eventmask[WL_EVENTING_MASK_LEN]; + char iovbuf[WL_EVENTING_MASK_LEN + 12]; + + dev_iw_iovar_getbuf(dev, "event_msgs", "", 0, iovbuf, sizeof(iovbuf)); + bcopy(iovbuf, eventmask, WL_EVENTING_MASK_LEN); + setbit(eventmask, WLC_E_SCAN_COMPLETE); + dev_iw_iovar_setbuf(dev, "event_msgs", eventmask, WL_EVENTING_MASK_LEN, + iovbuf, sizeof(iovbuf)); + +} + +static int +wl_iw_iscan_prep(wl_scan_params_t *params, wlc_ssid_t *ssid) +{ + int err = 0; + + memcpy(¶ms->bssid, ðer_bcast, ETHER_ADDR_LEN); + params->bss_type = DOT11_BSSTYPE_ANY; + params->scan_type = 0; + params->nprobes = -1; + params->active_time = -1; + params->passive_time = -1; + params->home_time = -1; + params->channel_num = 0; + + params->nprobes = htod32(params->nprobes); + params->active_time = htod32(params->active_time); + params->passive_time = htod32(params->passive_time); + params->home_time = htod32(params->home_time); + if (ssid && ssid->SSID_len) + memcpy(¶ms->ssid, ssid, sizeof(wlc_ssid_t)); + + return err; +} + +static int +wl_iw_iscan(iscan_info_t *iscan, wlc_ssid_t *ssid, uint16 action) +{ + int params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_iscan_params_t, params)); + wl_iscan_params_t *params; + int err = 0; + + if (ssid && ssid->SSID_len) { + params_size += sizeof(wlc_ssid_t); + } + params = (wl_iscan_params_t*)kmalloc(params_size, GFP_KERNEL); + if (params == NULL) { + return -ENOMEM; + } + memset(params, 0, params_size); + ASSERT(params_size < WLC_IOCTL_SMLEN); + + err = wl_iw_iscan_prep(¶ms->params, ssid); + + if (!err) { + params->version = htod32(ISCAN_REQ_VERSION); + params->action = htod16(action); + params->scan_duration = htod16(0); + + + (void) dev_iw_iovar_setbuf(iscan->dev, "iscan", params, params_size, + iscan->ioctlbuf, WLC_IOCTL_SMLEN); + } + + kfree(params); + return err; +} + +static uint32 +wl_iw_iscan_get(iscan_info_t *iscan) +{ + iscan_buf_t * buf; + iscan_buf_t * ptr; + wl_iscan_results_t * list_buf; + wl_iscan_results_t list; + wl_scan_results_t *results; + uint32 status; + + + if (iscan->list_cur) { + buf = iscan->list_cur; + iscan->list_cur = buf->next; + } + else { + buf = kmalloc(sizeof(iscan_buf_t), GFP_KERNEL); + if (!buf) + return WL_SCAN_RESULTS_ABORTED; + buf->next = NULL; + if (!iscan->list_hdr) + iscan->list_hdr = buf; + else { + ptr = iscan->list_hdr; + while (ptr->next) { + ptr = ptr->next; + } + ptr->next = buf; + } + } + memset(buf->iscan_buf, 0, WLC_IW_ISCAN_MAXLEN); + list_buf = (wl_iscan_results_t*)buf->iscan_buf; + results = &list_buf->results; + results->buflen = WL_ISCAN_RESULTS_FIXED_SIZE; + results->version = 0; + results->count = 0; + + memset(&list, 0, sizeof(list)); + list.results.buflen = htod32(WLC_IW_ISCAN_MAXLEN); + (void) dev_iw_iovar_getbuf( + iscan->dev, + "iscanresults", + &list, + WL_ISCAN_RESULTS_FIXED_SIZE, + buf->iscan_buf, + WLC_IW_ISCAN_MAXLEN); + results->buflen = dtoh32(results->buflen); + results->version = dtoh32(results->version); + results->count = dtoh32(results->count); + WL_TRACE(("results->count = %d\n", results->count)); + + WL_TRACE(("results->buflen = %d\n", results->buflen)); + status = dtoh32(list_buf->status); + return status; +} + +static void wl_iw_send_scan_complete(iscan_info_t *iscan) +{ + union iwreq_data wrqu; + + memset(&wrqu, 0, sizeof(wrqu)); + + + wireless_send_event(iscan->dev, SIOCGIWSCAN, &wrqu, NULL); +} + +static int +_iscan_sysioc_thread(void *data) +{ + uint32 status; + iscan_info_t *iscan = (iscan_info_t *)data; + + DAEMONIZE("iscan_sysioc"); + + status = WL_SCAN_RESULTS_PARTIAL; + while (down_interruptible(&iscan->sysioc_sem) == 0) { + if (iscan->timer_on) { + del_timer(&iscan->timer); + iscan->timer_on = 0; + } + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + rtnl_lock(); +#endif + status = wl_iw_iscan_get(iscan); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + rtnl_unlock(); +#endif + + switch (status) { + case WL_SCAN_RESULTS_PARTIAL: + WL_TRACE(("iscanresults incomplete\n")); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + rtnl_lock(); +#endif + + wl_iw_iscan(iscan, NULL, WL_SCAN_ACTION_CONTINUE); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + rtnl_unlock(); +#endif + + iscan->timer.expires = jiffies + msecs_to_jiffies(iscan->timer_ms); + add_timer(&iscan->timer); + iscan->timer_on = 1; + break; + case WL_SCAN_RESULTS_SUCCESS: + WL_TRACE(("iscanresults complete\n")); + iscan->iscan_state = ISCAN_STATE_IDLE; + wl_iw_send_scan_complete(iscan); + break; + case WL_SCAN_RESULTS_PENDING: + WL_TRACE(("iscanresults pending\n")); + + iscan->timer.expires = jiffies + msecs_to_jiffies(iscan->timer_ms); + add_timer(&iscan->timer); + iscan->timer_on = 1; + break; + case WL_SCAN_RESULTS_ABORTED: + WL_TRACE(("iscanresults aborted\n")); + iscan->iscan_state = ISCAN_STATE_IDLE; + wl_iw_send_scan_complete(iscan); + break; + default: + WL_TRACE(("iscanresults returned unknown status %d\n", status)); + break; + } + } + complete_and_exit(&iscan->sysioc_exited, 0); +} + +int +wl_iw_attach(struct net_device *dev, void * dhdp) +{ + iscan_info_t *iscan = NULL; + + if (!dev) + return 0; + + iscan = kmalloc(sizeof(iscan_info_t), GFP_KERNEL); + if (!iscan) + return -ENOMEM; + memset(iscan, 0, sizeof(iscan_info_t)); + iscan->sysioc_pid = -1; + + g_iscan = iscan; + iscan->dev = dev; + iscan->iscan_state = ISCAN_STATE_IDLE; + + + + iscan->timer_ms = 2000; + init_timer(&iscan->timer); + iscan->timer.data = (ulong)iscan; + iscan->timer.function = wl_iw_timerfunc; + + sema_init(&iscan->sysioc_sem, 0); + init_completion(&iscan->sysioc_exited); + iscan->sysioc_pid = kernel_thread(_iscan_sysioc_thread, iscan, 0); + if (iscan->sysioc_pid < 0) + return -ENOMEM; + return 0; +} + +void wl_iw_detach(void) +{ + iscan_buf_t *buf; + iscan_info_t *iscan = g_iscan; + if (!iscan) + return; + if (iscan->sysioc_pid >= 0) { + KILL_PROC(iscan->sysioc_pid, SIGTERM); + wait_for_completion(&iscan->sysioc_exited); + } + + while (iscan->list_hdr) { + buf = iscan->list_hdr->next; + kfree(iscan->list_hdr); + iscan->list_hdr = buf; + } + kfree(iscan); + g_iscan = NULL; +} + +#endif diff --git a/drivers/net/wireless/bcmdhd/wl_iw.h b/drivers/net/wireless/bcmdhd/wl_iw.h new file mode 100644 index 00000000..2afb5a68 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wl_iw.h @@ -0,0 +1,161 @@ +/* + * Linux Wireless Extensions support + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_iw.h 291086 2011-10-21 01:17:24Z $ + */ + +#ifndef _wl_iw_h_ +#define _wl_iw_h_ + +#include + +#include +#include +#include + +#define WL_SCAN_PARAMS_SSID_MAX 10 +#define GET_SSID "SSID=" +#define GET_CHANNEL "CH=" +#define GET_NPROBE "NPROBE=" +#define GET_ACTIVE_ASSOC_DWELL "ACTIVE=" +#define GET_PASSIVE_ASSOC_DWELL "PASSIVE=" +#define GET_HOME_DWELL "HOME=" +#define GET_SCAN_TYPE "TYPE=" + +#define BAND_GET_CMD "GETBAND" +#define BAND_SET_CMD "SETBAND" +#define DTIM_SKIP_GET_CMD "DTIMSKIPGET" +#define DTIM_SKIP_SET_CMD "DTIMSKIPSET" +#define SETSUSPEND_CMD "SETSUSPENDOPT" +#define PNOSSIDCLR_SET_CMD "PNOSSIDCLR" + +#define PNOSETUP_SET_CMD "PNOSETUP " +#define PNOENABLE_SET_CMD "PNOFORCE" +#define PNODEBUG_SET_CMD "PNODEBUG" +#define TXPOWER_SET_CMD "TXPOWER" + +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" + + +typedef struct wl_iw_extra_params { + int target_channel; +} wl_iw_extra_params_t; + +struct cntry_locales_custom { + char iso_abbrev[WLC_CNTRY_BUF_SZ]; + char custom_locale[WLC_CNTRY_BUF_SZ]; + int32 custom_locale_rev; +}; + + +#define WL_IW_RSSI_MINVAL -200 +#define WL_IW_RSSI_NO_SIGNAL -91 +#define WL_IW_RSSI_VERY_LOW -80 +#define WL_IW_RSSI_LOW -70 +#define WL_IW_RSSI_GOOD -68 +#define WL_IW_RSSI_VERY_GOOD -58 +#define WL_IW_RSSI_EXCELLENT -57 +#define WL_IW_RSSI_INVALID 0 +#define MAX_WX_STRING 80 +#define SSID_FMT_BUF_LEN ((4 * 32) + 1) +#define isprint(c) bcm_isprint(c) +#define WL_IW_SET_ACTIVE_SCAN (SIOCIWFIRSTPRIV+1) +#define WL_IW_GET_RSSI (SIOCIWFIRSTPRIV+3) +#define WL_IW_SET_PASSIVE_SCAN (SIOCIWFIRSTPRIV+5) +#define WL_IW_GET_LINK_SPEED (SIOCIWFIRSTPRIV+7) +#define WL_IW_GET_CURR_MACADDR (SIOCIWFIRSTPRIV+9) +#define WL_IW_SET_STOP (SIOCIWFIRSTPRIV+11) +#define WL_IW_SET_START (SIOCIWFIRSTPRIV+13) + +#define G_SCAN_RESULTS 8*1024 +#define WE_ADD_EVENT_FIX 0x80 +#define G_WLAN_SET_ON 0 +#define G_WLAN_SET_OFF 1 + + +typedef struct wl_iw { + char nickname[IW_ESSID_MAX_SIZE]; + + struct iw_statistics wstats; + + int spy_num; + uint32 pwsec; + uint32 gwsec; + bool privacy_invoked; + struct ether_addr spy_addr[IW_MAX_SPY]; + struct iw_quality spy_qual[IW_MAX_SPY]; + void *wlinfo; +} wl_iw_t; + +struct wl_ctrl { + struct timer_list *timer; + struct net_device *dev; + long sysioc_pid; + struct semaphore sysioc_sem; + struct completion sysioc_exited; +}; + + +#if WIRELESS_EXT > 12 +#include +extern const struct iw_handler_def wl_iw_handler_def; +#endif + +extern int wl_iw_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +extern void wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data); +extern int wl_iw_get_wireless_stats(struct net_device *dev, struct iw_statistics *wstats); +int wl_iw_attach(struct net_device *dev, void * dhdp); +int wl_iw_send_priv_event(struct net_device *dev, char *flag); + +void wl_iw_detach(void); + +#define CSCAN_COMMAND "CSCAN " +#define CSCAN_TLV_PREFIX 'S' +#define CSCAN_TLV_VERSION 1 +#define CSCAN_TLV_SUBVERSION 0 +#define CSCAN_TLV_TYPE_SSID_IE 'S' +#define CSCAN_TLV_TYPE_CHANNEL_IE 'C' +#define CSCAN_TLV_TYPE_NPROBE_IE 'N' +#define CSCAN_TLV_TYPE_ACTIVE_IE 'A' +#define CSCAN_TLV_TYPE_PASSIVE_IE 'P' +#define CSCAN_TLV_TYPE_HOME_IE 'H' +#define CSCAN_TLV_TYPE_STYPE_IE 'T' + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) +#define IWE_STREAM_ADD_EVENT(info, stream, ends, iwe, extra) \ + iwe_stream_add_event(info, stream, ends, iwe, extra) +#define IWE_STREAM_ADD_VALUE(info, event, value, ends, iwe, event_len) \ + iwe_stream_add_value(info, event, value, ends, iwe, event_len) +#define IWE_STREAM_ADD_POINT(info, stream, ends, iwe, extra) \ + iwe_stream_add_point(info, stream, ends, iwe, extra) +#else +#define IWE_STREAM_ADD_EVENT(info, stream, ends, iwe, extra) \ + iwe_stream_add_event(stream, ends, iwe, extra) +#define IWE_STREAM_ADD_VALUE(info, event, value, ends, iwe, event_len) \ + iwe_stream_add_value(event, value, ends, iwe, event_len) +#define IWE_STREAM_ADD_POINT(info, stream, ends, iwe, extra) \ + iwe_stream_add_point(stream, ends, iwe, extra) +#endif + +#endif diff --git a/drivers/net/wireless/bcmdhd/wl_linux_mon.c b/drivers/net/wireless/bcmdhd/wl_linux_mon.c new file mode 100644 index 00000000..af258632 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wl_linux_mon.c @@ -0,0 +1,422 @@ +/* + * Broadcom Dongle Host Driver (DHD), Linux monitor network interface + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhd_linux_mon.c 280623 2011-08-30 14:49:39Z $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +typedef enum monitor_states +{ + MONITOR_STATE_DEINIT = 0x0, + MONITOR_STATE_INIT = 0x1, + MONITOR_STATE_INTERFACE_ADDED = 0x2, + MONITOR_STATE_INTERFACE_DELETED = 0x4 +} monitor_states_t; +int dhd_add_monitor(char *name, struct net_device **new_ndev); +extern int dhd_start_xmit(struct sk_buff *skb, struct net_device *net); +int dhd_del_monitor(struct net_device *ndev); +int dhd_monitor_init(void *dhd_pub); +int dhd_monitor_uninit(void); + +/** + * Local declarations and defintions (not exposed) + */ +#ifndef DHD_MAX_IFS +#define DHD_MAX_IFS 16 +#endif +#define MON_PRINT(format, ...) printk("DHD-MON: %s " format, __func__, ##__VA_ARGS__) +#define MON_TRACE MON_PRINT + +typedef struct monitor_interface { + int radiotap_enabled; + struct net_device* real_ndev; /* The real interface that the monitor is on */ + struct net_device* mon_ndev; +} monitor_interface; + +typedef struct dhd_linux_monitor { + void *dhd_pub; + monitor_states_t monitor_state; + monitor_interface mon_if[DHD_MAX_IFS]; + struct mutex lock; /* lock to protect mon_if */ +} dhd_linux_monitor_t; + +static dhd_linux_monitor_t g_monitor; + +static struct net_device* lookup_real_netdev(char *name); +static monitor_interface* ndev_to_monif(struct net_device *ndev); +static int dhd_mon_if_open(struct net_device *ndev); +static int dhd_mon_if_stop(struct net_device *ndev); +static int dhd_mon_if_subif_start_xmit(struct sk_buff *skb, struct net_device *ndev); +static void dhd_mon_if_set_multicast_list(struct net_device *ndev); +static int dhd_mon_if_change_mac(struct net_device *ndev, void *addr); + +static const struct net_device_ops dhd_mon_if_ops = { + .ndo_open = dhd_mon_if_open, + .ndo_stop = dhd_mon_if_stop, + .ndo_start_xmit = dhd_mon_if_subif_start_xmit, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)) + .ndo_set_rx_mode = dhd_mon_if_set_multicast_list, +#else + .ndo_set_multicast_list = dhd_mon_if_set_multicast_list, +#endif + .ndo_set_mac_address = dhd_mon_if_change_mac, +}; + +/** + * Local static function defintions + */ + +/* Look up dhd's net device table to find a match (e.g. interface "eth0" is a match for "mon.eth0" + * "p2p-eth0-0" is a match for "mon.p2p-eth0-0") + */ +static struct net_device* lookup_real_netdev(char *name) +{ + struct net_device *ndev_found = NULL; + + int i; + int len = 0; + int last_name_len = 0; + struct net_device *ndev; + + /* We need to find interface "p2p-p2p-0" corresponding to monitor interface "mon-p2p-0", + * Once mon iface name reaches IFNAMSIZ, it is reset to p2p0-0 and corresponding mon + * iface would be mon-p2p0-0. + */ + for (i = 0; i < DHD_MAX_IFS; i++) { + ndev = dhd_idx2net(g_monitor.dhd_pub, i); + + /* Skip "p2p" and look for "-p2p0-x" in monitor interface name. If it + * it matches, then this netdev is the corresponding real_netdev. + */ + if (ndev && strstr(ndev->name, "p2p-p2p0")) { + len = strlen("p2p"); + } else { + /* if p2p- is not present, then the IFNAMSIZ have reached and name + * would have got reset. In this casse,look for p2p0-x in mon-p2p0-x + */ + len = 0; + } + if (ndev && strstr(name, (ndev->name + len))) { + if (strlen(ndev->name) > last_name_len) { + ndev_found = ndev; + last_name_len = strlen(ndev->name); + } + } + } + + return ndev_found; +} + +static monitor_interface* ndev_to_monif(struct net_device *ndev) +{ + int i; + + for (i = 0; i < DHD_MAX_IFS; i++) { + if (g_monitor.mon_if[i].mon_ndev == ndev) + return &g_monitor.mon_if[i]; + } + + return NULL; +} + +static int dhd_mon_if_open(struct net_device *ndev) +{ + int ret = 0; + + MON_PRINT("enter\n"); + return ret; +} + +static int dhd_mon_if_stop(struct net_device *ndev) +{ + int ret = 0; + + MON_PRINT("enter\n"); + return ret; +} + +static int dhd_mon_if_subif_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + int ret = 0; + int rtap_len; + int qos_len = 0; + int dot11_hdr_len = 24; + int snap_len = 6; + unsigned char *pdata; + unsigned short frame_ctl; + unsigned char src_mac_addr[6]; + unsigned char dst_mac_addr[6]; + struct ieee80211_hdr *dot11_hdr; + struct ieee80211_radiotap_header *rtap_hdr; + monitor_interface* mon_if; + + MON_PRINT("enter\n"); + + mon_if = ndev_to_monif(ndev); + if (mon_if == NULL || mon_if->real_ndev == NULL) { + MON_PRINT(" cannot find matched net dev, skip the packet\n"); + goto fail; + } + + if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header))) + goto fail; + + rtap_hdr = (struct ieee80211_radiotap_header *)skb->data; + if (unlikely(rtap_hdr->it_version)) + goto fail; + + rtap_len = ieee80211_get_radiotap_len(skb->data); + if (unlikely(skb->len < rtap_len)) + goto fail; + + MON_PRINT("radiotap len (should be 14): %d\n", rtap_len); + + /* Skip the ratio tap header */ + skb_pull(skb, rtap_len); + + dot11_hdr = (struct ieee80211_hdr *)skb->data; + frame_ctl = le16_to_cpu(dot11_hdr->frame_control); + /* Check if the QoS bit is set */ + if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) { + /* Check if this ia a Wireless Distribution System (WDS) frame + * which has 4 MAC addresses + */ + if (dot11_hdr->frame_control & 0x0080) + qos_len = 2; + if ((dot11_hdr->frame_control & 0x0300) == 0x0300) + dot11_hdr_len += 6; + + memcpy(dst_mac_addr, dot11_hdr->addr1, sizeof(dst_mac_addr)); + memcpy(src_mac_addr, dot11_hdr->addr2, sizeof(src_mac_addr)); + + /* Skip the 802.11 header, QoS (if any) and SNAP, but leave spaces for + * for two MAC addresses + */ + skb_pull(skb, dot11_hdr_len + qos_len + snap_len - sizeof(src_mac_addr) * 2); + pdata = (unsigned char*)skb->data; + memcpy(pdata, dst_mac_addr, sizeof(dst_mac_addr)); + memcpy(pdata + sizeof(dst_mac_addr), src_mac_addr, sizeof(src_mac_addr)); + PKTSETPRIO(skb, 0); + + MON_PRINT("if name: %s, matched if name %s\n", ndev->name, mon_if->real_ndev->name); + + /* Use the real net device to transmit the packet */ + ret = dhd_start_xmit(skb, mon_if->real_ndev); + + return ret; + } +fail: + dev_kfree_skb(skb); + return 0; +} + +static void dhd_mon_if_set_multicast_list(struct net_device *ndev) +{ + monitor_interface* mon_if; + + mon_if = ndev_to_monif(ndev); + if (mon_if == NULL || mon_if->real_ndev == NULL) { + MON_PRINT(" cannot find matched net dev, skip the packet\n"); + } else { + MON_PRINT("enter, if name: %s, matched if name %s\n", + ndev->name, mon_if->real_ndev->name); + } +} + +static int dhd_mon_if_change_mac(struct net_device *ndev, void *addr) +{ + int ret = 0; + monitor_interface* mon_if; + + mon_if = ndev_to_monif(ndev); + if (mon_if == NULL || mon_if->real_ndev == NULL) { + MON_PRINT(" cannot find matched net dev, skip the packet\n"); + } else { + MON_PRINT("enter, if name: %s, matched if name %s\n", + ndev->name, mon_if->real_ndev->name); + } + return ret; +} + +/** + * Global function definitions (declared in dhd_linux_mon.h) + */ + +int dhd_add_monitor(char *name, struct net_device **new_ndev) +{ + int i; + int idx = -1; + int ret = 0; + struct net_device* ndev = NULL; + dhd_linux_monitor_t **dhd_mon; + + mutex_lock(&g_monitor.lock); + + MON_TRACE("enter, if name: %s\n", name); + if (!name || !new_ndev) { + MON_PRINT("invalid parameters\n"); + ret = -EINVAL; + goto out; + } + + /* + * Find a vacancy + */ + for (i = 0; i < DHD_MAX_IFS; i++) + if (g_monitor.mon_if[i].mon_ndev == NULL) { + idx = i; + break; + } + if (idx == -1) { + MON_PRINT("exceeds maximum interfaces\n"); + ret = -EFAULT; + goto out; + } + + ndev = alloc_etherdev(sizeof(dhd_linux_monitor_t*)); + if (!ndev) { + MON_PRINT("failed to allocate memory\n"); + ret = -ENOMEM; + goto out; + } + + ndev->type = ARPHRD_IEEE80211_RADIOTAP; + strncpy(ndev->name, name, IFNAMSIZ); + ndev->name[IFNAMSIZ - 1] = 0; + ndev->netdev_ops = &dhd_mon_if_ops; + + ret = register_netdevice(ndev); + if (ret) { + MON_PRINT(" register_netdevice failed (%d)\n", ret); + goto out; + } + + *new_ndev = ndev; + g_monitor.mon_if[idx].radiotap_enabled = TRUE; + g_monitor.mon_if[idx].mon_ndev = ndev; + g_monitor.mon_if[idx].real_ndev = lookup_real_netdev(name); + dhd_mon = (dhd_linux_monitor_t **)netdev_priv(ndev); + *dhd_mon = &g_monitor; + g_monitor.monitor_state = MONITOR_STATE_INTERFACE_ADDED; + MON_PRINT("net device returned: 0x%p\n", ndev); + MON_PRINT("found a matched net device, name %s\n", g_monitor.mon_if[idx].real_ndev->name); + +out: + if (ret && ndev) + free_netdev(ndev); + + mutex_unlock(&g_monitor.lock); + return ret; + +} + +int dhd_del_monitor(struct net_device *ndev) +{ + int i; + bool rollback_lock = false; + if (!ndev) + return -EINVAL; + mutex_lock(&g_monitor.lock); + for (i = 0; i < DHD_MAX_IFS; i++) { + if (g_monitor.mon_if[i].mon_ndev == ndev || + g_monitor.mon_if[i].real_ndev == ndev) { + g_monitor.mon_if[i].real_ndev = NULL; + if (rtnl_is_locked()) { + rtnl_unlock(); + rollback_lock = true; + } + unregister_netdev(g_monitor.mon_if[i].mon_ndev); + free_netdev(g_monitor.mon_if[i].mon_ndev); + g_monitor.mon_if[i].mon_ndev = NULL; + g_monitor.monitor_state = MONITOR_STATE_INTERFACE_DELETED; + break; + } + } + if (rollback_lock) { + rtnl_lock(); + rollback_lock = false; + } + + if (g_monitor.monitor_state != + MONITOR_STATE_INTERFACE_DELETED) + MON_PRINT("interface not found in monitor IF array, is this a monitor IF? 0x%p\n", + ndev); + mutex_unlock(&g_monitor.lock); + + return 0; +} + +int dhd_monitor_init(void *dhd_pub) +{ + if (g_monitor.monitor_state == MONITOR_STATE_DEINIT) { + g_monitor.dhd_pub = dhd_pub; + mutex_init(&g_monitor.lock); + g_monitor.monitor_state = MONITOR_STATE_INIT; + } + return 0; +} + +int dhd_monitor_uninit(void) +{ + int i; + struct net_device *ndev; + bool rollback_lock = false; + mutex_lock(&g_monitor.lock); + if (g_monitor.monitor_state != MONITOR_STATE_DEINIT) { + for (i = 0; i < DHD_MAX_IFS; i++) { + ndev = g_monitor.mon_if[i].mon_ndev; + if (ndev) { + if (rtnl_is_locked()) { + rtnl_unlock(); + rollback_lock = true; + } + unregister_netdev(ndev); + free_netdev(ndev); + g_monitor.mon_if[i].real_ndev = NULL; + g_monitor.mon_if[i].mon_ndev = NULL; + if (rollback_lock) { + rtnl_lock(); + rollback_lock = false; + } + } + } + g_monitor.monitor_state = MONITOR_STATE_DEINIT; + } + mutex_unlock(&g_monitor.lock); + return 0; +} diff --git a/drivers/net/wireless/bcmdhd/wldev_common.c b/drivers/net/wireless/bcmdhd/wldev_common.c new file mode 100644 index 00000000..18008e0b --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wldev_common.c @@ -0,0 +1,384 @@ +/* + * Common function shared by Linux WEXT, cfg80211 and p2p drivers + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wldev_common.c,v 1.1.4.1.2.14 2011-02-09 01:40:07 $ + */ + +#include +#include +#include +#include + +#include +#include + +#define htod32(i) i +#define htod16(i) i +#define dtoh32(i) i +#define dtoh16(i) i +#define htodchanspec(i) i +#define dtohchanspec(i) i + +#define WLDEV_ERROR(args) \ + do { \ + printk(KERN_ERR "WLDEV-ERROR) %s : ", __func__); \ + printk args; \ + } while (0) + +extern int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd); + +s32 wldev_ioctl( + struct net_device *dev, u32 cmd, void *arg, u32 len, u32 set) +{ + s32 ret = 0; + struct wl_ioctl ioc; + + + memset(&ioc, 0, sizeof(ioc)); + ioc.cmd = cmd; + ioc.buf = arg; + ioc.len = len; + ioc.set = set; + + ret = dhd_ioctl_entry_local(dev, &ioc, cmd); + + return ret; +} + +/* Format a iovar buffer, not bsscfg indexed. The bsscfg index will be + * taken care of in dhd_ioctl_entry. Internal use only, not exposed to + * wl_iw, wl_cfg80211 and wl_cfgp2p + */ +static s32 wldev_mkiovar( + s8 *iovar_name, s8 *param, s32 paramlen, + s8 *iovar_buf, u32 buflen) +{ + s32 iolen = 0; + + iolen = bcm_mkiovar(iovar_name, param, paramlen, iovar_buf, buflen); + return iolen; +} + +s32 wldev_iovar_getbuf( + struct net_device *dev, s8 *iovar_name, + void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync) +{ + s32 ret = 0; + if (buf_sync) { + mutex_lock(buf_sync); + } + wldev_mkiovar(iovar_name, param, paramlen, buf, buflen); + ret = wldev_ioctl(dev, WLC_GET_VAR, buf, buflen, FALSE); + if (buf_sync) + mutex_unlock(buf_sync); + return ret; +} + + +s32 wldev_iovar_setbuf( + struct net_device *dev, s8 *iovar_name, + void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync) +{ + s32 ret = 0; + s32 iovar_len; + if (buf_sync) { + mutex_lock(buf_sync); + } + iovar_len = wldev_mkiovar(iovar_name, param, paramlen, buf, buflen); + if (iovar_len > 0) + ret = wldev_ioctl(dev, WLC_SET_VAR, buf, iovar_len, TRUE); + else + ret = BCME_BUFTOOSHORT; + if (buf_sync) + mutex_unlock(buf_sync); + return ret; +} + +s32 wldev_iovar_setint( + struct net_device *dev, s8 *iovar, s32 val) +{ + s8 iovar_buf[WLC_IOCTL_SMLEN]; + + val = htod32(val); + memset(iovar_buf, 0, sizeof(iovar_buf)); + return wldev_iovar_setbuf(dev, iovar, &val, sizeof(val), iovar_buf, + sizeof(iovar_buf), NULL); +} + + +s32 wldev_iovar_getint( + struct net_device *dev, s8 *iovar, s32 *pval) +{ + s8 iovar_buf[WLC_IOCTL_SMLEN]; + s32 err; + + memset(iovar_buf, 0, sizeof(iovar_buf)); + err = wldev_iovar_getbuf(dev, iovar, pval, sizeof(*pval), iovar_buf, + sizeof(iovar_buf), NULL); + if (err == 0) + { + memcpy(pval, iovar_buf, sizeof(*pval)); + *pval = dtoh32(*pval); + } + return err; +} + +/** Format a bsscfg indexed iovar buffer. The bsscfg index will be + * taken care of in dhd_ioctl_entry. Internal use only, not exposed to + * wl_iw, wl_cfg80211 and wl_cfgp2p + */ +s32 wldev_mkiovar_bsscfg( + const s8 *iovar_name, s8 *param, s32 paramlen, + s8 *iovar_buf, s32 buflen, s32 bssidx) +{ + const s8 *prefix = "bsscfg:"; + s8 *p; + u32 prefixlen; + u32 namelen; + u32 iolen; + + if (bssidx == 0) { + return wldev_mkiovar((s8*)iovar_name, (s8 *)param, paramlen, + (s8 *) iovar_buf, buflen); + } + + prefixlen = (u32) strlen(prefix); /* lengh of bsscfg prefix */ + namelen = (u32) strlen(iovar_name) + 1; /* lengh of iovar name + null */ + iolen = prefixlen + namelen + sizeof(u32) + paramlen; + + if (buflen < 0 || iolen > (u32)buflen) + { + WLDEV_ERROR(("%s: buffer is too short\n", __FUNCTION__)); + return BCME_BUFTOOSHORT; + } + + p = (s8 *)iovar_buf; + + /* copy prefix, no null */ + memcpy(p, prefix, prefixlen); + p += prefixlen; + + /* copy iovar name including null */ + memcpy(p, iovar_name, namelen); + p += namelen; + + /* bss config index as first param */ + bssidx = htod32(bssidx); + memcpy(p, &bssidx, sizeof(u32)); + p += sizeof(u32); + + /* parameter buffer follows */ + if (paramlen) + memcpy(p, param, paramlen); + + return iolen; + +} + +s32 wldev_iovar_getbuf_bsscfg( + struct net_device *dev, s8 *iovar_name, + void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync) +{ + s32 ret = 0; + if (buf_sync) { + mutex_lock(buf_sync); + } + + wldev_mkiovar_bsscfg(iovar_name, param, paramlen, buf, buflen, bsscfg_idx); + ret = wldev_ioctl(dev, WLC_GET_VAR, buf, buflen, FALSE); + if (buf_sync) { + mutex_unlock(buf_sync); + } + return ret; + +} + +s32 wldev_iovar_setbuf_bsscfg( + struct net_device *dev, s8 *iovar_name, + void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync) +{ + s32 ret = 0; + s32 iovar_len; + if (buf_sync) { + mutex_lock(buf_sync); + } + iovar_len = wldev_mkiovar_bsscfg(iovar_name, param, paramlen, buf, buflen, bsscfg_idx); + if (iovar_len > 0) + ret = wldev_ioctl(dev, WLC_SET_VAR, buf, iovar_len, TRUE); + else { + ret = BCME_BUFTOOSHORT; + } + + if (buf_sync) { + mutex_unlock(buf_sync); + } + return ret; +} + +s32 wldev_iovar_setint_bsscfg( + struct net_device *dev, s8 *iovar, s32 val, s32 bssidx) +{ + s8 iovar_buf[WLC_IOCTL_SMLEN]; + + val = htod32(val); + memset(iovar_buf, 0, sizeof(iovar_buf)); + return wldev_iovar_setbuf_bsscfg(dev, iovar, &val, sizeof(val), iovar_buf, + sizeof(iovar_buf), bssidx, NULL); +} + + +s32 wldev_iovar_getint_bsscfg( + struct net_device *dev, s8 *iovar, s32 *pval, s32 bssidx) +{ + s8 iovar_buf[WLC_IOCTL_SMLEN]; + s32 err; + + memset(iovar_buf, 0, sizeof(iovar_buf)); + err = wldev_iovar_getbuf_bsscfg(dev, iovar, pval, sizeof(*pval), iovar_buf, + sizeof(iovar_buf), bssidx, NULL); + if (err == 0) + { + memcpy(pval, iovar_buf, sizeof(*pval)); + *pval = dtoh32(*pval); + } + return err; +} + +int wldev_get_link_speed( + struct net_device *dev, int *plink_speed) +{ + int error; + + if (!plink_speed) + return -ENOMEM; + error = wldev_ioctl(dev, WLC_GET_RATE, plink_speed, sizeof(int), 0); + if (unlikely(error)) + return error; + + /* Convert internal 500Kbps to Kbps */ + *plink_speed *= 500; + return error; +} + +int wldev_get_rssi( + struct net_device *dev, int *prssi) +{ + scb_val_t scb_val; + int error; + + if (!prssi) + return -ENOMEM; + bzero(&scb_val, sizeof(scb_val_t)); + + error = wldev_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t), 0); + if (unlikely(error)) + return error; + + *prssi = dtoh32(scb_val.val); + return error; +} + +int wldev_get_ssid( + struct net_device *dev, wlc_ssid_t *pssid) +{ + int error; + + if (!pssid) + return -ENOMEM; + error = wldev_ioctl(dev, WLC_GET_SSID, pssid, sizeof(wlc_ssid_t), 0); + if (unlikely(error)) + return error; + pssid->SSID_len = dtoh32(pssid->SSID_len); + return error; +} + +int wldev_get_band( + struct net_device *dev, uint *pband) +{ + int error; + + error = wldev_ioctl(dev, WLC_GET_BAND, pband, sizeof(uint), 0); + return error; +} + +int wldev_set_band( + struct net_device *dev, uint band) +{ + int error = -1; + + if ((band == WLC_BAND_AUTO) || (band == WLC_BAND_5G) || (band == WLC_BAND_2G)) { + error = wldev_ioctl(dev, WLC_SET_BAND, &band, sizeof(band), true); + if (!error) + dhd_bus_band_set(dev, band); + } + return error; +} + +int wldev_set_country( + struct net_device *dev, char *country_code, bool notify, bool user_enforced) +{ + int error = -1; + wl_country_t cspec = {{0}, 0, {0}}; + scb_val_t scbval; + char smbuf[WLC_IOCTL_SMLEN]; + + if (!country_code) + return error; + + bzero(&scbval, sizeof(scb_val_t)); + error = wldev_iovar_getbuf(dev, "country", NULL, 0, &cspec, sizeof(cspec), NULL); + if (error < 0) { + WLDEV_ERROR(("%s: get country failed = %d\n", __FUNCTION__, error)); + return error; + } + + if ((error < 0) || + (strncmp(country_code, cspec.ccode, WLC_CNTRY_BUF_SZ) != 0)) { + + if (user_enforced) { + bzero(&scbval, sizeof(scb_val_t)); + error = wldev_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t), true); + if (error < 0) { + WLDEV_ERROR(("%s: set country failed due to Disassoc error %d\n", + __FUNCTION__, error)); + return error; + } + } + + cspec.rev = -1; + memcpy(cspec.country_abbrev, country_code, WLC_CNTRY_BUF_SZ); + memcpy(cspec.ccode, country_code, WLC_CNTRY_BUF_SZ); + get_customized_country_code((char *)&cspec.country_abbrev, &cspec); + error = wldev_iovar_setbuf(dev, "country", &cspec, sizeof(cspec), + smbuf, sizeof(smbuf), NULL); + if (error < 0) { + WLDEV_ERROR(("%s: set country for %s as %s rev %d failed\n", + __FUNCTION__, country_code, cspec.ccode, cspec.rev)); + return error; + } + dhd_bus_country_set(dev, &cspec, notify); + WLDEV_ERROR(("%s: set country for %s as %s rev %d\n", + __FUNCTION__, country_code, cspec.ccode, cspec.rev)); + } + return 0; +} diff --git a/drivers/net/wireless/bcmdhd/wldev_common.h b/drivers/net/wireless/bcmdhd/wldev_common.h new file mode 100644 index 00000000..e63620fe --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wldev_common.h @@ -0,0 +1,112 @@ +/* + * Common function shared by Linux WEXT, cfg80211 and p2p drivers + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wldev_common.h,v 1.1.4.1.2.14 2011-02-09 01:40:07 $ + */ +#ifndef __WLDEV_COMMON_H__ +#define __WLDEV_COMMON_H__ + +#include + +/* wl_dev_ioctl - get/set IOCTLs, will call net_device's do_ioctl (or + * netdev_ops->ndo_do_ioctl in new kernels) + * @dev: the net_device handle + */ +s32 wldev_ioctl( + struct net_device *dev, u32 cmd, void *arg, u32 len, u32 set); + +/** Retrieve named IOVARs, this function calls wl_dev_ioctl with + * WLC_GET_VAR IOCTL code + */ +s32 wldev_iovar_getbuf( + struct net_device *dev, s8 *iovar_name, + void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync); + +/** Set named IOVARs, this function calls wl_dev_ioctl with + * WLC_SET_VAR IOCTL code + */ +s32 wldev_iovar_setbuf( + struct net_device *dev, s8 *iovar_name, + void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync); + +s32 wldev_iovar_setint( + struct net_device *dev, s8 *iovar, s32 val); + +s32 wldev_iovar_getint( + struct net_device *dev, s8 *iovar, s32 *pval); + +/** The following function can be implemented if there is a need for bsscfg + * indexed IOVARs + */ + +s32 wldev_mkiovar_bsscfg( + const s8 *iovar_name, s8 *param, s32 paramlen, + s8 *iovar_buf, s32 buflen, s32 bssidx); + +/** Retrieve named and bsscfg indexed IOVARs, this function calls wl_dev_ioctl with + * WLC_GET_VAR IOCTL code + */ +s32 wldev_iovar_getbuf_bsscfg( + struct net_device *dev, s8 *iovar_name, void *param, s32 paramlen, + void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync); + +/** Set named and bsscfg indexed IOVARs, this function calls wl_dev_ioctl with + * WLC_SET_VAR IOCTL code + */ +s32 wldev_iovar_setbuf_bsscfg( + struct net_device *dev, s8 *iovar_name, void *param, s32 paramlen, + void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync); + +s32 wldev_iovar_getint_bsscfg( + struct net_device *dev, s8 *iovar, s32 *pval, s32 bssidx); + +s32 wldev_iovar_setint_bsscfg( + struct net_device *dev, s8 *iovar, s32 val, s32 bssidx); + +extern void get_customized_country_code(char *country_iso_code, wl_country_t *cspec); +extern void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec, bool notify); +extern void dhd_bus_band_set(struct net_device *dev, uint band); +extern int wldev_set_country(struct net_device *dev, char *country_code, bool notify, + bool user_enforced); +extern int net_os_wake_lock(struct net_device *dev); +extern int net_os_wake_unlock(struct net_device *dev); +extern int net_os_wake_lock_timeout(struct net_device *dev); +extern int net_os_wake_lock_timeout_enable(struct net_device *dev, int val); +extern int net_os_set_dtim_skip(struct net_device *dev, int val); +extern int net_os_set_suspend_disable(struct net_device *dev, int val); +extern int net_os_set_suspend(struct net_device *dev, int val, int force); +extern int wl_iw_parse_ssid_list_tlv(char** list_str, wlc_ssid_t* ssid, + int max, int *bytes_left); + +/* Get the link speed from dongle, speed is in kpbs */ +int wldev_get_link_speed(struct net_device *dev, int *plink_speed); + +int wldev_get_rssi(struct net_device *dev, int *prssi); + +int wldev_get_ssid(struct net_device *dev, wlc_ssid_t *pssid); + +int wldev_get_band(struct net_device *dev, uint *pband); + +int wldev_set_band(struct net_device *dev, uint band); + +#endif /* __WLDEV_COMMON_H__ */ diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 99dc29f2..a86498ba 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -190,6 +190,16 @@ config BATTERY_MAX17042 multi-function devices that include fuel gauages that are compatible with MAX17042. +config BATTERY_ANDROID + tristate "Battery driver for Android" + help + Say Y to enable generic support for battery charging according + to common Android policies. + This driver adds periodic battery level and health monitoring, + kernel log reporting and other debugging features, common board + battery file glue logic for battery/case temperature sensors, + etc. + config BATTERY_Z2 tristate "Z2 battery driver" depends on I2C && MACH_ZIPIT2 @@ -307,4 +317,12 @@ config AB8500_BATTERY_THERM_ON_BATCTRL help Say Y to enable battery temperature measurements using thermistor connected on BATCTRL ADC. + +config BATTERY_AK + tristate "Anyka Battery driver support" + depends on ARCH_AK39 + help + Say Y to enable support for the anyka battery from SOCLE. + + endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile index b6b24341..07e3946a 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -44,3 +44,5 @@ obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o +obj-$(CONFIG_BATTERY_ANDROID) += android_battery.o +obj-$(CONFIG_BATTERY_AK) += plat-anyka/ diff --git a/drivers/power/android_battery.c b/drivers/power/android_battery.c new file mode 100644 index 00000000..ee437d08 --- /dev/null +++ b/drivers/power/android_battery.c @@ -0,0 +1,692 @@ +/* + * android_battery.c + * Android Battery Driver + * + * Copyright (C) 2012 Google, Inc. + * Copyright (C) 2012 Samsung Electronics + * + * Based on work by himihee.seo@samsung.com, ms925.kim@samsung.com, and + * joshua.chang@samsung.com. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FAST_POLL (1 * 60) +#define SLOW_POLL (10 * 60) + +struct android_bat_data { + struct android_bat_platform_data *pdata; + struct android_bat_callbacks callbacks; + + struct device *dev; + + struct power_supply psy_bat; + + struct wake_lock monitor_wake_lock; + struct wake_lock charger_wake_lock; + + int charge_source; + + int batt_temp; + int batt_current; + unsigned int batt_health; + unsigned int batt_vcell; + unsigned int batt_soc; + unsigned int charging_status; + bool recharging; + unsigned long charging_start_time; + + struct workqueue_struct *monitor_wqueue; + struct work_struct monitor_work; + struct work_struct charger_work; + + struct alarm monitor_alarm; + ktime_t last_poll; + + struct dentry *debugfs_entry; +}; + +static enum power_supply_property android_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CURRENT_NOW, +}; + +static DEFINE_MUTEX(android_bat_state_lock); + +static void android_bat_update_data(struct android_bat_data *battery); +static int android_bat_enable_charging(struct android_bat_data *battery, + bool enable); + +static char *charge_source_str(int charge_source) +{ + switch (charge_source) { + case CHARGE_SOURCE_NONE: + return "none"; + case CHARGE_SOURCE_AC: + return "ac"; + case CHARGE_SOURCE_USB: + return "usb"; + default: + break; + } + + return "?"; +} + +static int android_bat_get_property(struct power_supply *ps, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct android_bat_data *battery = + container_of(ps, struct android_bat_data, psy_bat); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = battery->charging_status; + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = battery->batt_health; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = battery->batt_temp; + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + android_bat_update_data(battery); + val->intval = battery->batt_vcell; + if (val->intval == -1) + return -EINVAL; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = battery->batt_soc; + if (val->intval == -1) + return -EINVAL; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + android_bat_update_data(battery); + val->intval = battery->batt_current; + break; + default: + return -EINVAL; + } + return 0; +} + +static void android_bat_get_temp(struct android_bat_data *battery) +{ + int batt_temp = 42; /* 4.2C */ + int health = battery->batt_health; + + if (battery->pdata->get_temperature) + battery->pdata->get_temperature(&batt_temp); + + if (battery->charge_source != CHARGE_SOURCE_NONE) { + if (batt_temp >= battery->pdata->temp_high_threshold) { + if (health != POWER_SUPPLY_HEALTH_OVERHEAT && + health != POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) { + pr_info("battery overheat (%d>=%d), " \ + "charging unavailable\n", + batt_temp, + battery->pdata->temp_high_threshold); + battery->batt_health = + POWER_SUPPLY_HEALTH_OVERHEAT; + } + } else if (batt_temp <= battery->pdata->temp_high_recovery && + batt_temp >= battery->pdata->temp_low_recovery) { + if (health == POWER_SUPPLY_HEALTH_OVERHEAT || + health == POWER_SUPPLY_HEALTH_COLD) { + pr_info("battery recovery (%d,%d~%d)," \ + "charging available\n", + batt_temp, + battery->pdata->temp_low_recovery, + battery->pdata->temp_high_recovery); + battery->batt_health = + POWER_SUPPLY_HEALTH_GOOD; + } + } else if (batt_temp <= battery->pdata->temp_low_threshold) { + if (health != POWER_SUPPLY_HEALTH_COLD && + health != POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) { + pr_info("battery cold (%d <= %d)," \ + "charging unavailable\n", + batt_temp, + battery->pdata->temp_low_threshold); + battery->batt_health = + POWER_SUPPLY_HEALTH_COLD; + } + } + } + + battery->batt_temp = batt_temp; +} + +/* + * android_bat_state_lock not held, may call back into + * android_bat_charge_source_changed. Gathering data here can be + * non-atomic; updating our state based on the data may need to be + * atomic. + */ + +static void android_bat_update_data(struct android_bat_data *battery) +{ + int ret; + int v; + + if (battery->pdata->poll_charge_source) + battery->charge_source = battery->pdata->poll_charge_source(); + + if (battery->pdata->get_voltage_now) { + ret = battery->pdata->get_voltage_now(); + battery->batt_vcell = ret >= 0 ? ret : 4242000; + } + + if (battery->pdata->get_capacity) { + ret = battery->pdata->get_capacity(); + battery->batt_soc = ret >= 0 ? ret : 42; + } + + if (battery->pdata->get_current_now) { + ret = battery->pdata->get_current_now(&v); + + if (!ret) + battery->batt_current = v; + } + + android_bat_get_temp(battery); +} + +static void android_bat_set_charge_time(struct android_bat_data *battery, + bool enable) +{ + if (enable && !battery->charging_start_time) { + struct timespec cur_time; + + get_monotonic_boottime(&cur_time); + /* record start time for charge timeout timer */ + battery->charging_start_time = cur_time.tv_sec; + } else if (!enable) { + /* clear charge timeout timer */ + battery->charging_start_time = 0; + } +} + +static int android_bat_enable_charging(struct android_bat_data *battery, + bool enable) +{ + if (enable && (battery->batt_health != POWER_SUPPLY_HEALTH_GOOD)) { + battery->charging_status = + POWER_SUPPLY_STATUS_NOT_CHARGING; + return -EPERM; + } + + if (enable) { + if (battery->pdata && battery->pdata->set_charging_current) + battery->pdata->set_charging_current + (battery->charge_source); + } + + if (battery->pdata && battery->pdata->set_charging_enable) + battery->pdata->set_charging_enable(enable); + + android_bat_set_charge_time(battery, enable); + pr_info("battery: enable=%d charger: %s\n", enable, + charge_source_str(battery->charge_source)); + return 0; +} + +static bool android_bat_charge_timeout(struct android_bat_data *battery, + unsigned long timeout) +{ + struct timespec cur_time; + + if (!battery->charging_start_time) + return 0; + + get_monotonic_boottime(&cur_time); + pr_debug("%s: Start time: %ld, End time: %ld, current time: %ld\n", + __func__, battery->charging_start_time, + battery->charging_start_time + timeout, + cur_time.tv_sec); + return cur_time.tv_sec >= battery->charging_start_time + timeout; +} + +static void android_bat_charging_timer(struct android_bat_data *battery) +{ + if (!battery->charging_start_time && + battery->charging_status == POWER_SUPPLY_STATUS_CHARGING) { + android_bat_enable_charging(battery, true); + battery->recharging = true; + pr_debug("%s: charge status charging but timer is expired\n", + __func__); + } else if (battery->charging_start_time == 0) { + pr_debug("%s: charging_start_time never initialized\n", + __func__); + return; + } + + if (android_bat_charge_timeout( + battery, + battery->recharging ? battery->pdata->recharging_time : + battery->pdata->full_charging_time)) { + android_bat_enable_charging(battery, false); + if (battery->batt_vcell > + battery->pdata->recharging_voltage && + battery->batt_soc == 100) + battery->charging_status = + POWER_SUPPLY_STATUS_FULL; + battery->recharging = false; + battery->charging_start_time = 0; + pr_info("battery: charging timer expired\n"); + } + + return; +} + +static void android_bat_charge_source_changed(struct android_bat_callbacks *ptr, + int charge_source) +{ + struct android_bat_data *battery = + container_of(ptr, struct android_bat_data, callbacks); + + wake_lock(&battery->charger_wake_lock); + mutex_lock(&android_bat_state_lock); + battery->charge_source = charge_source; + + pr_info("battery: charge source type was changed: %s\n", + charge_source_str(battery->charge_source)); + + mutex_unlock(&android_bat_state_lock); + queue_work(battery->monitor_wqueue, &battery->charger_work); +} + +static void android_bat_set_full_status(struct android_bat_callbacks *ptr) +{ + struct android_bat_data *battery = + container_of(ptr, struct android_bat_data, callbacks); + + mutex_lock(&android_bat_state_lock); + pr_info("battery: battery full\n"); + battery->charging_status = POWER_SUPPLY_STATUS_FULL; + android_bat_enable_charging(battery, false); + battery->recharging = false; + mutex_unlock(&android_bat_state_lock); + power_supply_changed(&battery->psy_bat); +} + +static void android_bat_charger_work(struct work_struct *work) +{ + struct android_bat_data *battery = + container_of(work, struct android_bat_data, charger_work); + + mutex_lock(&android_bat_state_lock); + + switch (battery->charge_source) { + case CHARGE_SOURCE_NONE: + battery->charging_status = POWER_SUPPLY_STATUS_DISCHARGING; + android_bat_enable_charging(battery, false); + battery->batt_health = POWER_SUPPLY_HEALTH_GOOD; + battery->recharging = false; + battery->charging_start_time = 0; + break; + case CHARGE_SOURCE_USB: + case CHARGE_SOURCE_AC: + /* + * If charging status indicates a charger was already + * connected prior to this and the status is something + * other than charging ("full" or "not-charging"), leave + * the status alone. + */ + if (battery->charging_status == + POWER_SUPPLY_STATUS_DISCHARGING || + battery->charging_status == POWER_SUPPLY_STATUS_UNKNOWN) + battery->charging_status = POWER_SUPPLY_STATUS_CHARGING; + + /* + * Don't re-enable charging if the battery is full and we + * are not actively re-charging it, or if "not-charging" + * status is set. + */ + if (!((battery->charging_status == POWER_SUPPLY_STATUS_FULL + && !battery->recharging) || battery->charging_status == + POWER_SUPPLY_STATUS_NOT_CHARGING)) + android_bat_enable_charging(battery, true); + + break; + default: + pr_err("%s: Invalid charger type\n", __func__); + break; + } + + mutex_unlock(&android_bat_state_lock); + wake_lock_timeout(&battery->charger_wake_lock, HZ * 2); + power_supply_changed(&battery->psy_bat); +} + + +static void android_bat_monitor_set_alarm(struct android_bat_data *battery, + int seconds) +{ + alarm_start(&battery->monitor_alarm, + ktime_add(battery->last_poll, ktime_set(seconds, 0))); +} + +static void android_bat_monitor_work(struct work_struct *work) +{ + struct android_bat_data *battery = + container_of(work, struct android_bat_data, monitor_work); + struct timespec cur_time; + + wake_lock(&battery->monitor_wake_lock); + android_bat_update_data(battery); + mutex_lock(&android_bat_state_lock); + + switch (battery->charging_status) { + case POWER_SUPPLY_STATUS_FULL: + if (battery->batt_vcell < battery->pdata->recharging_voltage && + !battery->recharging) { + battery->recharging = true; + android_bat_enable_charging(battery, true); + pr_info("battery: start recharging, v=%d\n", + battery->batt_vcell/1000); + } + break; + case POWER_SUPPLY_STATUS_DISCHARGING: + break; + case POWER_SUPPLY_STATUS_CHARGING: + switch (battery->batt_health) { + case POWER_SUPPLY_HEALTH_OVERHEAT: + case POWER_SUPPLY_HEALTH_COLD: + case POWER_SUPPLY_HEALTH_OVERVOLTAGE: + case POWER_SUPPLY_HEALTH_DEAD: + case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE: + battery->charging_status = + POWER_SUPPLY_STATUS_NOT_CHARGING; + android_bat_enable_charging(battery, false); + + pr_info("battery: Not charging, health=%d\n", + battery->batt_health); + break; + default: + break; + } + break; + case POWER_SUPPLY_STATUS_NOT_CHARGING: + if (battery->batt_health == POWER_SUPPLY_HEALTH_GOOD) { + pr_info("battery: battery health recovered\n"); + if (battery->charge_source != CHARGE_SOURCE_NONE) { + android_bat_enable_charging(battery, true); + battery->charging_status + = POWER_SUPPLY_STATUS_CHARGING; + } else { + battery->charging_status + = POWER_SUPPLY_STATUS_DISCHARGING; + } + } + break; + default: + pr_err("%s: Undefined battery status: %d\n", __func__, + battery->charging_status); + break; + } + + android_bat_charging_timer(battery); + get_monotonic_boottime(&cur_time); + pr_info("battery: l=%d v=%d c=%d temp=%s%ld.%ld h=%d st=%d%s ct=%lu type=%s\n", + battery->batt_soc, battery->batt_vcell/1000, + battery->batt_current, battery->batt_temp < 0 ? "-" : "", + abs(battery->batt_temp / 10), abs(battery->batt_temp % 10), + battery->batt_health, battery->charging_status, + battery->recharging ? "r" : "", + battery->charging_start_time ? + cur_time.tv_sec - battery->charging_start_time : 0, + charge_source_str(battery->charge_source)); + mutex_unlock(&android_bat_state_lock); + power_supply_changed(&battery->psy_bat); + battery->last_poll = ktime_get_boottime(); + android_bat_monitor_set_alarm(battery, FAST_POLL); + wake_unlock(&battery->monitor_wake_lock); + return; +} + +static enum alarmtimer_restart android_bat_monitor_alarm( + struct alarm *alarm, ktime_t now) +{ + struct android_bat_data *battery = + container_of(alarm, struct android_bat_data, monitor_alarm); + + wake_lock(&battery->monitor_wake_lock); + queue_work(battery->monitor_wqueue, &battery->monitor_work); + return ALARMTIMER_NORESTART; +} + +static int android_power_debug_dump(struct seq_file *s, void *unused) +{ + struct android_bat_data *battery = s->private; + struct timespec cur_time; + + android_bat_update_data(battery); + get_monotonic_boottime(&cur_time); + mutex_lock(&android_bat_state_lock); + seq_printf(s, "l=%d v=%d c=%d temp=%s%ld.%ld h=%d st=%d%s ct=%lu type=%s\n", + battery->batt_soc, battery->batt_vcell/1000, + battery->batt_current, battery->batt_temp < 0 ? "-" : "", + abs(battery->batt_temp / 10), abs(battery->batt_temp % 10), + battery->batt_health, battery->charging_status, + battery->recharging ? "r" : "", + battery->charging_start_time ? + cur_time.tv_sec - battery->charging_start_time : 0, + charge_source_str(battery->charge_source)); + mutex_unlock(&android_bat_state_lock); + return 0; +} + +static int android_power_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, android_power_debug_dump, inode->i_private); +} + +static const struct file_operations android_power_debug_fops = { + .open = android_power_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static __devinit int android_bat_probe(struct platform_device *pdev) +{ + struct android_bat_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct android_bat_data *battery; + int ret = 0; + + dev_info(&pdev->dev, "Android Battery Driver\n"); + battery = kzalloc(sizeof(*battery), GFP_KERNEL); + if (!battery) + return -ENOMEM; + + battery->pdata = pdata; + if (!battery->pdata) { + pr_err("%s : No platform data\n", __func__); + ret = -EINVAL; + goto err_pdata; + } + + battery->dev = &pdev->dev; + platform_set_drvdata(pdev, battery); + battery->batt_health = POWER_SUPPLY_HEALTH_GOOD; + + battery->psy_bat.name = "android-battery", + battery->psy_bat.type = POWER_SUPPLY_TYPE_BATTERY, + battery->psy_bat.properties = android_battery_props, + battery->psy_bat.num_properties = ARRAY_SIZE(android_battery_props), + battery->psy_bat.get_property = android_bat_get_property, + + battery->batt_vcell = -1; + battery->batt_soc = -1; + + wake_lock_init(&battery->monitor_wake_lock, WAKE_LOCK_SUSPEND, + "android-battery-monitor"); + wake_lock_init(&battery->charger_wake_lock, WAKE_LOCK_SUSPEND, + "android-chargerdetect"); + + ret = power_supply_register(&pdev->dev, &battery->psy_bat); + if (ret) { + dev_err(battery->dev, "%s: failed to register psy_bat\n", + __func__); + goto err_psy_bat_reg; + } + + battery->monitor_wqueue = + alloc_workqueue(dev_name(&pdev->dev), WQ_FREEZABLE, 1); + if (!battery->monitor_wqueue) { + dev_err(battery->dev, "%s: fail to create workqueue\n", + __func__); + goto err_wq; + } + + INIT_WORK(&battery->monitor_work, android_bat_monitor_work); + INIT_WORK(&battery->charger_work, android_bat_charger_work); + + battery->callbacks.charge_source_changed = + android_bat_charge_source_changed; + battery->callbacks.battery_set_full = + android_bat_set_full_status; + if (battery->pdata && battery->pdata->register_callbacks) + battery->pdata->register_callbacks(&battery->callbacks); + + /* get initial charger status */ + if (battery->pdata->poll_charge_source) + battery->charge_source = battery->pdata->poll_charge_source(); + + wake_lock(&battery->charger_wake_lock); + queue_work(battery->monitor_wqueue, &battery->charger_work); + + wake_lock(&battery->monitor_wake_lock); + battery->last_poll = ktime_get_boottime(); + alarm_init(&battery->monitor_alarm, ALARM_BOOTTIME, + android_bat_monitor_alarm); + queue_work(battery->monitor_wqueue, &battery->monitor_work); + + battery->debugfs_entry = + debugfs_create_file("android-power", S_IRUGO, NULL, + battery, &android_power_debug_fops); + if (!battery->debugfs_entry) + pr_err("failed to create android-power debugfs entry\n"); + + return 0; + +err_wq: + power_supply_unregister(&battery->psy_bat); +err_psy_bat_reg: + wake_lock_destroy(&battery->monitor_wake_lock); + wake_lock_destroy(&battery->charger_wake_lock); +err_pdata: + kfree(battery); + + return ret; +} + +static int __devexit android_bat_remove(struct platform_device *pdev) +{ + struct android_bat_data *battery = platform_get_drvdata(pdev); + + alarm_cancel(&battery->monitor_alarm); + flush_workqueue(battery->monitor_wqueue); + destroy_workqueue(battery->monitor_wqueue); + power_supply_unregister(&battery->psy_bat); + wake_lock_destroy(&battery->monitor_wake_lock); + wake_lock_destroy(&battery->charger_wake_lock); + debugfs_remove(battery->debugfs_entry); + kfree(battery); + return 0; +} + +static int android_bat_suspend(struct device *dev) +{ + struct android_bat_data *battery = dev_get_drvdata(dev); + + cancel_work_sync(&battery->monitor_work); + android_bat_monitor_set_alarm( + battery, + battery->charge_source == CHARGE_SOURCE_NONE ? + SLOW_POLL : FAST_POLL); + return 0; +} + +static void android_bat_resume(struct device *dev) +{ + struct android_bat_data *battery = dev_get_drvdata(dev); + + android_bat_monitor_set_alarm(battery, FAST_POLL); + return; +} + +static const struct dev_pm_ops android_bat_pm_ops = { + .prepare = android_bat_suspend, + .complete = android_bat_resume, +}; + +static struct platform_driver android_bat_driver = { + .driver = { + .name = "android-battery", + .owner = THIS_MODULE, + .pm = &android_bat_pm_ops, + }, + .probe = android_bat_probe, + .remove = __devexit_p(android_bat_remove), +}; + +static int __init android_bat_init(void) +{ + return platform_driver_register(&android_bat_driver); +} + +static void __exit android_bat_exit(void) +{ + platform_driver_unregister(&android_bat_driver); +} + +late_initcall(android_bat_init); +module_exit(android_bat_exit); + +MODULE_DESCRIPTION("Android battery driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/plat-anyka/Makefile b/drivers/power/plat-anyka/Makefile new file mode 100644 index 00000000..b8f84617 --- /dev/null +++ b/drivers/power/plat-anyka/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for RTC class/drivers. +# + +obj-$(CONFIG_BATTERY_AK) += battery.o diff --git a/drivers/power/plat-anyka/battery.c b/drivers/power/plat-anyka/battery.c new file mode 100644 index 00000000..bbff3dc5 --- /dev/null +++ b/drivers/power/plat-anyka/battery.c @@ -0,0 +1,1436 @@ +/* + * @file battery.c + * @battery driver for ak chip + * @Copyright (C) 2010 Anyka (Guangzhou) Microelectronics Technology Co + * @author gao_wangsheng + * @date 2011-04 + * @version 2.0 +*/ + +/* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define BATTERY_GET_VOLTAGE + +/* BATTERY DEFINE*/ +#define UPDATE_DISCHARGE_DELAY (HZ * 6) /* delay 6s */ +#define UPDATE_CHARGE_DELAY (HZ * 60) /* delay 60s */ +#define UPDATE_VOLTAGE_DELAY (HZ * 1) /* delay 1s */ +#define CHECK_PDOWN_DELAY 50 /* delay 50ms */ +#define CHECK_READ_CNT 10 +#define CHECK_PDOWN_CNT 5 +#define CHARGE_FULL 1 +#define CHARGE_NOT_FULL 0 + +#define PK(fmt...) //printk(fmt) // debug and read ad4 voltage +#define PK_SAMPLE(fmt...) //printk(fmt) //debug read voltage sample +#define PK_CHARGE(fmt...) //printk(fmt) //debug charge ac and usb in +#define PK_DISCHARGE(fmt...) //printk(fmt) //debug discharge +#define PK_PDOWN(fmt...) //printk(fmt) //debug check power down +#define get_bat_platform_data(x) ((x)->bat_ps.dev->parent->platform_data) + +struct ak_bat{ + struct power_supply bat_ps; + struct power_supply ac_ps; + struct power_supply usb_ps; + struct read_voltage_sample rd_voltage; + struct mutex bat_lock; + struct timer_list timer; + int status; + int ac_online; + int usb_online; + int voltage; + int capacity; + int pdown_count; + int read_count; + atomic_t full_flag; +}; + +static struct delayed_work charge_work; +static struct delayed_work discharge_work; +static struct delayed_work voltage_work; +static struct delayed_work usbirq_work; +static struct delayed_work acirq_work; +static struct delayed_work pdown_work; +static struct delayed_work resume_work; +static struct ak_bat bat_device; + +static struct notifier_block ac_detect_nb; + + +static void ak_bat_update(struct ak_bat *battery); + +/** +* @Copyright (C) Anyka 2012 +* @brief set battery full flag +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *battery +* @return void +*/ +static void bat_charge_full_timer(unsigned long bat_dev) +{ + struct ak_bat *battery = (void *)bat_dev; + + if (POWER_SUPPLY_STATUS_CHARGING == battery->status) + { + atomic_set(&battery->full_flag, CHARGE_FULL); + } +} + +/** +* @Copyright (C) Anyka 2012 +* @brief check capacity +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *battery +* @return void +*/ +static void bat_check_charge_capacity(struct ak_bat *battery) +{ + struct ak_bat_mach_info *info = get_bat_platform_data(battery); + unsigned int delay = info->bat_mach_info.full_delay * 60 * 1000; //minute to ms + + if (POWER_SUPPLY_STATUS_CHARGING != battery->status) + { + return; + } + + if (atomic_read(&battery->full_flag) == CHARGE_FULL + && battery->capacity != info->bat_mach_info.full_capacity) + { + battery->capacity = info->bat_mach_info.full_capacity; + } + + if (atomic_read(&battery->full_flag) == CHARGE_NOT_FULL + && battery->capacity == info->bat_mach_info.full_capacity) + { + battery->capacity = info->bat_mach_info.full_capacity - 1; + + if (!timer_pending(&battery->timer)) + { + mod_timer(&battery->timer,jiffies + msecs_to_jiffies(delay)); + } + } +} + +/** +* @Copyright (C) Anyka 2012 +* @brief read battery voltage +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *info +* @return voltage +*/ +int ak_bat_read_voltage(struct ak_bat_mach_info *info) +{ + int voltage; + + PK("\n###############%s; \n",__func__); + + // read voltage sample + PK("############adc1_read_bat:\n"); + voltage = (int)adc1_read_bat(); + PK("ad4_voltage = %d:\n",voltage); +#if 0 + voltage = voltage + * (info->bat_adc.up_resistance + info->bat_adc.dw_resistance) + / info->bat_adc.dw_resistance; + PK("bat_voltage = %d:\n",voltage); + + voltage += info->bat_adc.voltage_correct; // correct battery voltage + PK("correct_voltage = %d:\n",voltage); +#endif + return voltage; + +} +EXPORT_SYMBOL(ak_bat_read_voltage); + +/** +* @Copyright (C) Anyka 2012 +* @brief read and set gpio interrupt +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] gpio pin +* @return void +*/ +static void bat_set_int_inverse(unsigned int pin) +{ + if (ak_gpio_getpin(pin) == AK_GPIO_OUT_HIGH) + { + ak_gpio_intpol(pin, AK_GPIO_INT_LOWLEVEL); + } + else + { + ak_gpio_intpol(pin, AK_GPIO_INT_HIGHLEVEL); + } +} + +/** +* @Copyright (C) Anyka 2012 +* @brief notify ac and usb state who interest in +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *battery +* @return void +*/ +static void bat_notify_ac_usb_plug(struct ak_bat *battery) +{ + struct ak_bat_mach_info *info = get_bat_platform_data(battery); + + PK_CHARGE("##################%s:\n", __func__); + + if (info->usb_gpio.pindata.pin >= 0) + { + // usb and ac pin all exist + if (battery->usb_online) + { + power_notifier_call_chain(POWER_EVENT_AC_PLUGIN, NULL); + } + else + { + power_notifier_call_chain(POWER_EVENT_AC_PLUGOUT, NULL); + } + } + + else + { + // only ac exist + if (battery->ac_online) + { + power_notifier_call_chain(POWER_EVENT_AC_PLUGIN, NULL); + } + else + { + power_notifier_call_chain(POWER_EVENT_AC_PLUGOUT, NULL); + } + } +} + +/** +* @Copyright (C) Anyka 2012 +* @brief poweroff system when voltage down to setting level +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *battery +* @return void +*/ +static void bat_check_voltage_poweroff(struct ak_bat *battery ) +{ + int voltage; + struct ak_bat_mach_info *info = get_bat_platform_data(battery); + + voltage = ak_bat_read_voltage(info); + if (voltage <= info->bat_mach_info.min_voltage) + { + PK_PDOWN("voltage=%d <= pdown_voltage=%d\n",voltage, + info->bat_mach_info.min_voltage); + schedule_delayed_work(&pdown_work,0); + } +} + +/** +* @Copyright (C) Anyka 2012 +* @brief switch work depend on status +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *battery +* @return void +*/ +static void switch_charge_discharge_work(struct ak_bat *battery) +{ + if (POWER_SUPPLY_STATUS_CHARGING == battery->status) + { + cancel_delayed_work_sync(&discharge_work); + schedule_delayed_work(&charge_work,UPDATE_CHARGE_DELAY); + } + else + { + cancel_delayed_work_sync(&charge_work); + del_timer_sync(&battery->timer); + schedule_delayed_work(&discharge_work,UPDATE_DISCHARGE_DELAY); + } +} + + +/** +* @Copyright (C) Anyka 2012 +* @brief return battery property to user space +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *psy... +* @return int +*/ +static int bat_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct ak_bat *battery = container_of(psy, struct ak_bat, bat_ps); + struct ak_bat_mach_info *info = get_bat_platform_data(battery); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = battery->status; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = battery->voltage; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = info->bat_mach_info.max_voltage; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + val->intval = info->bat_mach_info.min_voltage; + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = 25; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = battery->capacity; // real capacity + break; +#if 0 + case POWER_SUPPLY_PROP_POWEROFF_CAP: + val->intval = info->bat_mach_info.poweroff_cap; //power off capacity value + break; + case POWER_SUPPLY_PROP_LOW_CAP: + val->intval = info->bat_mach_info.low_cap; //low warring capacity + break; + case POWER_SUPPLY_PROP_RECOVER_CAP: + val->intval = info->bat_mach_info.recover_cap; //recover capacity limit + break; + case POWER_SUPPLY_PROP_POWER_ON_VOLTAGE: + val->intval = info->bat_mach_info.power_on_voltage; //power on voltage + break; + case POWER_SUPPLY_PROP_CPOWER_ON_VOLTAGE: + val->intval = info->bat_mach_info.cpower_on_voltage; //low warring capacity + break; +#endif + default: + return -EINVAL; + } + return 0; +} + + +/** +* @Copyright (C) Anyka 2012 +* @brief return usb property +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *psy... +* @return void +*/ +static int usb_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + struct ak_bat *battery = container_of(psy, struct ak_bat, usb_ps); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = battery->usb_online; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +/** +* @Copyright (C) Anyka 2012 +* @brief return ac property +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *psy... +* @return void +*/ +static int ac_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + struct ak_bat *battery = container_of(psy, struct ak_bat, ac_ps); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = battery->ac_online; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + + +static void ak_battery_external_power_changed(struct power_supply *bat_ps) +{ + // NULL +} + + +/** +* @Copyright (C) Anyka 2012 +* @brief power manage function +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *dev +* @return int +*/ +#ifdef CONFIG_PM +static int ak_battery_suspend(struct platform_device *dev, pm_message_t state) +{ + flush_scheduled_work(); + del_timer_sync(&bat_device.timer); + atomic_set(&bat_device.full_flag, CHARGE_NOT_FULL); + cancel_delayed_work_sync(&charge_work); + cancel_delayed_work_sync(&discharge_work); + cancel_delayed_work_sync(&voltage_work); + cancel_delayed_work_sync(&usbirq_work); + cancel_delayed_work_sync(&acirq_work); + cancel_delayed_work_sync(&pdown_work); + cancel_delayed_work_sync(&resume_work); + + return 0; +} + +static int ak_battery_resume(struct platform_device *dev) +{ + + schedule_delayed_work(&resume_work,msecs_to_jiffies(1000)); + return 0; +} +#else +#define ak_battery_suspend NULL +#define ak_battery_resume NULL +#endif + +/** +* @Copyright (C) Anyka 2012 +* @brief caculate capacity from discharge voltage +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *battery... +* @return void +*/ +static void get_capacity_from_voltage(struct ak_bat *battery, + struct ak_bat_mach_info *info,int voltage) +{ + int vtg = voltage; + int min_voltage; + int max_voltage; + + min_voltage = (POWER_SUPPLY_STATUS_CHARGING == battery->status) + ? info->bat_mach_info.charge_min_voltage + : info->bat_mach_info.min_voltage; + max_voltage = (POWER_SUPPLY_STATUS_DISCHARGING == battery->status + && atomic_read(&battery->full_flag) == CHARGE_FULL) + ? info->bat_mach_info.full_voltage + : info->bat_mach_info.max_voltage; + + if (vtg < min_voltage) + { + vtg = min_voltage; + } + + if (vtg > max_voltage) + { + vtg = max_voltage; + } + + + battery->capacity = (vtg - min_voltage) * info->bat_mach_info.full_capacity + / (max_voltage - min_voltage); + +} + +/** +* @Copyright (C) Anyka 2012 +* @brief init sample value +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *battery +* @return void +*/ +static void ak_init_sample_value(struct ak_bat *battery) +{ + struct read_voltage_sample *rd_voltage = &(battery->rd_voltage); + + rd_voltage->index = 0; + rd_voltage->sum = 0; + rd_voltage->max = 0; + rd_voltage->min = rd_voltage->design_max * 2; +} + +/** +* @Copyright (C) Anyka 2012 +* @brief caculate voltage +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *battery,*info +* @return void +*/ +static void ak_update_voltage(struct ak_bat *battery, + struct ak_bat_mach_info *info) +{ + int temp; + struct read_voltage_sample *rd_voltage = &(battery->rd_voltage); + + PK_SAMPLE("\n###############%s; \n",__func__); + + // if voltage_index in correct range + if ((rd_voltage->index >= rd_voltage->sample) + || (rd_voltage->index < 0)) + { + PK_SAMPLE("%s:error voltage sample index\n",__func__); + ak_init_sample_value(battery); + } + + // read voltage sample + temp = ak_bat_read_voltage(info); + + // save voltage max and min + if (temp > rd_voltage->max) + { + rd_voltage->max = temp; + } + + if (temp < rd_voltage->min) + { + rd_voltage->min = temp; + } + + PK_SAMPLE("read_voltage=%d\n",temp); + PK_SAMPLE("voltage_max=%d; voltage_min=%d;\n",rd_voltage->max,rd_voltage->min); + PK_SAMPLE("voltage_index=%d; voltage_sum=%d;\n",rd_voltage->index,rd_voltage->sum); + + rd_voltage->sum += temp; + + if (++rd_voltage->index == rd_voltage->sample) + { + rd_voltage->sum -= rd_voltage->min + rd_voltage->max; + rd_voltage->index -= 2; + battery->voltage =rd_voltage->sum / rd_voltage->index; + + PK_SAMPLE("=========bat_data.voltage = %d;=======\n",battery->voltage); + ak_init_sample_value(battery); + } +} + +/** +* @Copyright (C) Anyka 2012 +* @brief wrap for caculating voltage +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *battery +* @return void +*/ +static void bat_update_voltage(struct ak_bat *battery) +{ + struct ak_bat_mach_info *info = get_bat_platform_data(battery); + + mutex_lock(&battery->bat_lock); + ak_update_voltage(battery,info); + mutex_unlock(&battery->bat_lock); +} + +static void update_voltage_immediately(struct ak_bat *battery, + struct ak_bat_mach_info *info) +{ + int read_cnt; + + ak_init_sample_value(battery); + + for (read_cnt = 0; read_cnt < battery->rd_voltage.sample; read_cnt++) + { + ak_update_voltage(battery,info); + } +} + +/** +* @Copyright (C) Anyka 2012 +* @brief debug message +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *battery +* @return void +*/ +static void bat_print_battery_info(struct ak_bat *battery) +{ + struct ak_bat_mach_info *info = get_bat_platform_data(battery); + + printk("battery:cap=%d; vol=%d; status=%s; full=%d; poweroff_cap=%d; low_cap=%d.\n", + battery->capacity, + battery->voltage, + (battery->status == POWER_SUPPLY_STATUS_CHARGING) ? "charge" : "discharge", + ak_gpio_getpin(info->full_gpio.pindata.pin), + info->bat_mach_info.poweroff_cap, + info->bat_mach_info.low_cap); +} + +/** +* @Copyright (C) Anyka 2012 +* @brief check capacity flag +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *battery +* @return void +*/ +static void check_cap_full_flag(struct ak_bat *battery) +{ + struct ak_bat_mach_info *info = get_bat_platform_data(battery); + + if (atomic_read(&battery->full_flag) == CHARGE_FULL) + { + if (battery->voltage < info->bat_mach_info.full_voltage) + { + atomic_set(&battery->full_flag, CHARGE_NOT_FULL); + } + } + else + { + if (battery->capacity == info->bat_mach_info.full_capacity) + { + atomic_set(&battery->full_flag, CHARGE_FULL); + } + } +} + +/** +* @Copyright (C) Anyka 2012 +* @brief caculate discharge capacity from discharge voltage +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *battery +* @return void +*/ +static void update_discharge_capacity(struct ak_bat *battery) +{ + struct ak_bat_mach_info *info = get_bat_platform_data(battery); + + int old_capacity = battery->capacity; + + PK_DISCHARGE("###########%s:\n",__func__); + get_capacity_from_voltage(battery,info,battery->voltage); + check_cap_full_flag(battery); + + if (old_capacity != battery->capacity) + { + power_supply_changed(&battery->bat_ps); + } + + PK_DISCHARGE("voltage = %d;capacity = %d;\n", battery->voltage, battery->capacity); +} + +/** +* @Copyright (C) Anyka 2012 +* @brief caculate capacity from charge voltage +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *battery +* @return void +*/ +static void update_charge_capacity(struct ak_bat *battery) +{ + struct ak_bat_mach_info *info = get_bat_platform_data(battery); + + int old_capacity = battery->capacity; + + get_capacity_from_voltage(battery,info,battery->voltage); + bat_check_charge_capacity(battery); + + if (battery->capacity != old_capacity) + { + power_supply_changed(&battery->bat_ps); + } + + PK_CHARGE("voltage = %d; capacity = %d;\n", + battery->voltage,battery->capacity); +} + +/** +* @Copyright (C) Anyka 2012 +* @brief caculate ac and usb status +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *battery, *info +* @return void +*/ +static void ak_get_bat_status(struct ak_bat *battery + ,struct ak_bat_mach_info *info) +{ + if (info->ac_gpio.is_detect_mode == BAT_CHARGE_GPIO_DETECT) { + if (info->ac_gpio.pindata.pin >= 0) + { + battery->ac_online = + (ak_gpio_getpin(info->ac_gpio.pindata.pin) == info->ac_gpio.active)? 1 : 0; + } + else + { + battery->ac_online = 0; + } + } + + if (info->usb_gpio.pindata.pin >= 0) + { + battery->usb_online = + (ak_gpio_getpin(info->usb_gpio.pindata.pin)== info->usb_gpio.active)? 1 : 0; + } + else + { + battery->usb_online = 0; + } + + // if (battery->ac_online || battery->usb_online) + if (battery->ac_online) + { + battery->status = POWER_SUPPLY_STATUS_CHARGING; + } + else + { + battery->status = POWER_SUPPLY_STATUS_DISCHARGING; + } + +} + + +/** +* @Copyright (C) Anyka 2012 +* @brief quickly power off system +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *battery +* @return void +*/ +static void check_machine_power_off(struct ak_bat *battery) +{ + struct ak_bat_mach_info *info = get_bat_platform_data(battery); + + if ((poweroff_enable == info->bat_mach_info.power_off) + && (POWER_SUPPLY_STATUS_DISCHARGING == battery->status) + && (battery->voltage <= info->bat_mach_info.min_voltage)) + { + printk("battery:voltage=%d,machine power off immediately!\n",battery->voltage); + machine_power_off(); + } +} + +/** +* @Copyright (C) Anyka 2012 +* @brief ac irq work +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *work +* @return void +*/ +static void bat_acirq_work(struct work_struct *work) +{ + struct ak_bat *battery = &bat_device; + + PK_CHARGE("##################%s:\n", __func__); + ak_bat_update(battery); + check_machine_power_off(battery); +} + +/** +* @Copyright (C) Anyka 2012 +* @brief ac irq handler +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] irq, *battery +* @return irqreturn_t +*/ +static irqreturn_t akbat_ac_irqhandler(int irq, void *handle) +{ + struct ak_bat *battery = (struct ak_bat *)handle; + struct ak_bat_mach_info *info = get_bat_platform_data(battery); + + PK_CHARGE("##################%s:\n", __func__); + disable_irq_nosync(irq); + bat_set_int_inverse(ak_irq_to_gpio(irq)); + schedule_delayed_work(&acirq_work,msecs_to_jiffies(info->ac_gpio.delay)); + enable_irq(irq); + return IRQ_HANDLED; +} + +static int ac_detect_plugin(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct ak_bat *battery = &bat_device; + + if (val == ADDETECT_AC_PLUGIN) { + battery->ac_online = 1; + } + else if (val == ADDETECT_AC_PLUGOUT) { + battery->ac_online = 0; + } + + ak_bat_update(battery); + return 0; +} + +/** +* @Copyright (C) Anyka 2012 +* @brief usb irq handler +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *battery, irq +* @return irqreturn_t +*/ +static irqreturn_t akbat_usb_irqhandler(int irq, void *handle) +{ + struct ak_bat *battery = (struct ak_bat *)handle; + struct ak_bat_mach_info *info = get_bat_platform_data(battery); + + PK_CHARGE("##################%s:\n", __func__); + disable_irq_nosync(irq); + bat_set_int_inverse(ak_irq_to_gpio(irq)); + schedule_delayed_work(&usbirq_work,msecs_to_jiffies(info->usb_gpio.delay)); + enable_irq(irq); + return IRQ_HANDLED; +} + + +/** +* @Copyright (C) Anyka 2012 +* @brief update battery info +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *battery +* @return void +*/ +static void ak_bat_update(struct ak_bat *battery) +{ + struct ak_bat_mach_info *info = get_bat_platform_data(battery); + + PK_CHARGE("##################%s:\n", __func__); + mutex_lock(&battery->bat_lock); + + ak_get_bat_status(battery,info); + update_voltage_immediately(battery,info); + get_capacity_from_voltage(battery,info,battery->voltage); + bat_check_charge_capacity(battery); + power_supply_changed(&battery->bat_ps); + power_supply_changed(&battery->ac_ps); + power_supply_changed(&battery->usb_ps); + + switch_charge_discharge_work(battery); + bat_notify_ac_usb_plug(battery); + + mutex_unlock(&battery->bat_lock); +} + +/** +* @Copyright (C) Anyka 2012 +* @brief update charge info +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *battery +* @return void +*/ +static void ak_bat_update_charge(struct ak_bat *battery) +{ + PK_CHARGE("##################%s:\n", __func__); + mutex_lock(&battery->bat_lock); + update_charge_capacity(battery); + mutex_unlock(&battery->bat_lock); +} + +/** +* @Copyright (C) Anyka 2012 +* @brief update discharge info +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *battery +* @return void +*/ +static void ak_bat_update_discharge(struct ak_bat *battery) +{ + PK_DISCHARGE("##################%s:\n", __func__); + mutex_lock(&battery->bat_lock); + update_discharge_capacity(battery); + mutex_unlock(&battery->bat_lock); +} + +/** +* @Copyright (C) Anyka 2012 +* @brief charge work +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *work +* @return void +*/ +static void bat_charge_work(struct work_struct *work) +{ + ak_bat_update_charge(&bat_device); + schedule_delayed_work(&charge_work,UPDATE_CHARGE_DELAY); +// schedule_delayed_work(&charge_work,HZ*6); +} + +/** +* @Copyright (C) Anyka 2012 +* @brief discharge work +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *battery +* @return void +*/ +static void bat_discharge_work(struct work_struct *work) +{ + ak_bat_update_discharge(&bat_device); + schedule_delayed_work(&discharge_work,UPDATE_DISCHARGE_DELAY); +} + +/** +* @Copyright (C) Anyka 2012 +* @brief voltage work +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *work +* @return void +*/ +static void bat_voltage_work(struct work_struct *work) +{ +#ifdef BATTERY_GET_VOLTAGE + // 5 seconds to print battery info + static int print_time = 10; + if (--print_time < 0) { + print_time = 5; + bat_print_battery_info(&bat_device); + } +#endif + bat_check_voltage_poweroff(&bat_device); + bat_update_voltage(&bat_device); + schedule_delayed_work(&voltage_work,UPDATE_VOLTAGE_DELAY); +} + +/** +* @Copyright (C) Anyka 2012 +* @brief usb irq work +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *work +* @return void +*/ +static void bat_usbirq_work(struct work_struct *work) +{ + struct ak_bat *battery = &bat_device; + + PK_CHARGE("##################%s:\n", __func__); + ak_bat_update (battery); +} + +/** +* @Copyright (C) Anyka 2012 +* @brief power down work +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *work +* @return void +*/ +static void bat_pdown_work(struct work_struct *work) +{ + int voltage; + struct ak_bat *battery = &bat_device; + struct ak_bat_mach_info *info = get_bat_platform_data(battery); + + PK_PDOWN("#############%s:\n",__func__); + if (battery->status != POWER_SUPPLY_STATUS_DISCHARGING) + { + return; + } + + if (battery->read_count++ < CHECK_READ_CNT) + { + PK_PDOWN("read_count=%d\n",battery->read_count); + voltage = ak_bat_read_voltage(info); + PK_PDOWN("voltage=%d\n",voltage); + + if (voltage <= info->bat_mach_info.min_voltage) + { + battery->pdown_count++; + } + + schedule_delayed_work(&pdown_work,msecs_to_jiffies(CHECK_PDOWN_DELAY)); + } + else + { + PK_PDOWN("pdown_count=%d\n",battery->pdown_count); + + if (battery->pdown_count >= CHECK_PDOWN_CNT) + { + mutex_lock(&battery->bat_lock); + printk(KERN_WARNING "Ak_battery:capacity=%d => 0!\n",battery->capacity); + battery->capacity = 0; + power_supply_changed(&battery->bat_ps); + cancel_delayed_work_sync(&voltage_work); + cancel_delayed_work_sync(&charge_work); + cancel_delayed_work_sync(&discharge_work); + mutex_unlock(&battery->bat_lock); + } + + battery->read_count = 0; + battery->pdown_count = 0; + } + +} + +/** +* @Copyright (C) Anyka 2012 +* @brief resume work +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *work +* @return void +*/ +static void bat_resume_work(struct work_struct *work) +{ + ak_bat_update(&bat_device); + schedule_delayed_work(&voltage_work,UPDATE_VOLTAGE_DELAY); +} + +static int battery_reboot_notify(struct notifier_block *nb, unsigned long code, + void *unused) +{ + switch (code) { + case SYS_DOWN: + case SYS_HALT: + case SYS_POWER_OFF: + printk("%s:code=%ld, cap=%d, vol=%d, status=%s.\n", + __func__, + code, + bat_device.capacity, + bat_device.voltage, + bat_device.status==POWER_SUPPLY_STATUS_CHARGING ? "charging":"discharging"); + break; + } + + return NOTIFY_DONE; +} + +/** +* @Copyright (C) Anyka 2012 +* @brief power supply property +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] void +* @return void +*/ +static enum power_supply_property bat_power_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CAPACITY, +#if 0 + POWER_SUPPLY_PROP_POWEROFF_CAP, + POWER_SUPPLY_PROP_LOW_CAP, + POWER_SUPPLY_PROP_RECOVER_CAP, + POWER_SUPPLY_PROP_POWER_ON_VOLTAGE, + POWER_SUPPLY_PROP_CPOWER_ON_VOLTAGE, +#endif +}; + +static enum power_supply_property usb_power_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static enum power_supply_property ac_power_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +/** +* @Copyright (C) Anyka 2012 +* @brief battery device +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] void +* @return void +*/ +static struct ak_bat bat_device = { + .bat_ps= { + .name = "battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = bat_power_props, + .num_properties = ARRAY_SIZE(bat_power_props), + .get_property = bat_get_property, + .external_power_changed = ak_battery_external_power_changed, + .use_for_apm = 1, + }, + + .ac_ps = { + .name = "ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = ac_power_props, + .num_properties = ARRAY_SIZE(ac_power_props), + .get_property = ac_get_property, + }, + + .usb_ps = { + .name = "usb", + .type = POWER_SUPPLY_TYPE_USB, + .properties = usb_power_props, + .num_properties = ARRAY_SIZE(usb_power_props), + .get_property = usb_get_property, + }, + + .status = POWER_SUPPLY_STATUS_CHARGING, + .ac_online = 0, + .usb_online = 0, + .read_count = 0, + .pdown_count = 0, + .full_flag = ATOMIC_INIT(CHARGE_NOT_FULL), +}; + + +static struct notifier_block battery_reboot_nb = { + .notifier_call = battery_reboot_notify, + .next = NULL, + .priority = INT_MIN +}; + + +/** +* @Copyright (C) Anyka 2012 +* @brief probe battery +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *dev +* @return fail or not +*/ +static int __devinit ak_battery_probe(struct platform_device *dev) +{ + int ret = 0; + struct ak_bat_mach_info *info; + + info = (struct ak_bat_mach_info *)dev->dev.platform_data; + if (!info) + { + printk(KERN_ERR "%s:no platform data for battery\n",__func__); + ret = -EINVAL; + goto nodata_out; + } + + /* initialize hardware */ + mutex_init(&bat_device.bat_lock); + bat_device.rd_voltage.sample = info->bat_mach_info.voltage_sample; + bat_device.rd_voltage.design_max = info->bat_mach_info.max_voltage; + init_timer(&bat_device.timer); + bat_device.timer.function = bat_charge_full_timer; + bat_device.timer.data = (unsigned long) &bat_device; + + INIT_DELAYED_WORK(&charge_work,bat_charge_work); + INIT_DELAYED_WORK(&discharge_work,bat_discharge_work); + INIT_DELAYED_WORK(&voltage_work,bat_voltage_work); + INIT_DELAYED_WORK(&usbirq_work,bat_usbirq_work); + INIT_DELAYED_WORK(&pdown_work,bat_pdown_work); + INIT_DELAYED_WORK(&resume_work,bat_resume_work); + + ret = power_supply_register(&dev->dev, &bat_device.bat_ps); + if (ret != 0) + { + goto cancel_out; + } + + ret = power_supply_register(&dev->dev,&bat_device.usb_ps); + if (ret != 0) + { + goto free_bat_ps_out; + } + + ret = power_supply_register(&dev->dev,&bat_device.ac_ps); + if (ret != 0) + { + goto free_usb_ps_out; + } + + // use for charge full state + if (info->full_gpio.pindata.pin >= 0) + { + info->gpio_init(&info->full_gpio.pindata); + } + + if (info->ac_gpio.is_detect_mode == BAT_CHARGE_GPIO_DETECT) { + INIT_DELAYED_WORK(&acirq_work,bat_acirq_work); + // use for ac charge in irq + if (info->ac_gpio.irq >= 0) + { + info->gpio_init(&info->ac_gpio.pindata); + bat_set_int_inverse(info->ac_gpio.pindata.pin); + if (request_irq(info->ac_gpio.irq,akbat_ac_irqhandler,0,"ac_charge",&bat_device)) + { + printk(KERN_ERR "%s:Could not allocate IRQ %d\n", __func__,info->ac_gpio.irq); + ret = -EIO; + goto free_ac_ps_out; + } + } + } else if (info->ac_gpio.is_detect_mode == BAT_CHARGE_ADC_DETECT) { + memset(&ac_detect_nb, 0, sizeof(ac_detect_nb)); + ac_detect_nb.notifier_call = ac_detect_plugin; + addetect_register_client(&ac_detect_nb); + } + + // use for usb charge in irq + if (info->usb_gpio.irq >= 0) + { + info->gpio_init(&info->usb_gpio.pindata); + mdelay(100); + bat_set_int_inverse(info->usb_gpio.pindata.pin); + if (request_irq(info->usb_gpio.irq,akbat_usb_irqhandler,0,"usb_charge",&bat_device)) + { + printk(KERN_ERR "%s:Could not allocate IRQ %d\n", __func__,info->usb_gpio.irq); + ret = -EIO; + goto free_acirq_out; + } + } + + ak_bat_update(&bat_device); + schedule_delayed_work(&voltage_work,UPDATE_VOLTAGE_DELAY); + register_reboot_notifier(&battery_reboot_nb); + + bat_print_battery_info(&bat_device); + printk("AK Battery initialized\n"); + return ret; + +free_acirq_out: + if (info->ac_gpio.irq > 0) + { + disable_irq(info->ac_gpio.irq); + free_irq(info->ac_gpio.irq, dev); + } +free_ac_ps_out: + power_supply_unregister(&bat_device.ac_ps); +free_usb_ps_out: + power_supply_unregister(&bat_device.usb_ps); +free_bat_ps_out: + power_supply_unregister(&bat_device.bat_ps); +cancel_out: + del_timer_sync(&bat_device.timer); + cancel_delayed_work_sync(&charge_work); + cancel_delayed_work_sync(&discharge_work); + cancel_delayed_work_sync(&voltage_work); + cancel_delayed_work_sync(&usbirq_work); + cancel_delayed_work_sync(&acirq_work); + cancel_delayed_work_sync(&pdown_work); + cancel_delayed_work_sync(&resume_work); + +nodata_out: + printk(KERN_ERR "###########%s:ERR out##########\n",__func__); + return ret; +} + +/** +* @Copyright (C) Anyka 2012 +* @brief remove battery +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] *dev +* @return fail or not +*/ +static int __devexit ak_battery_remove(struct platform_device *dev) +{ + struct ak_bat_mach_info *info; + + info = (struct ak_bat_mach_info *)dev->dev.platform_data; + if (!info) { + printk(KERN_ERR "no platform data for battery\n"); + return -EINVAL; + } + +#ifdef CONFIG_AC_GPIO_DETECT + if (info->ac_gpio.irq > 0) + { + disable_irq(info->ac_gpio.irq); + free_irq(info->ac_gpio.irq, &bat_device); + } +#elif defined(CONFIG_AC_AD_DETECT) + addetect_unregister_client(&ac_detect_nb); +#endif + + if (info->usb_gpio.irq > 0) + { + disable_irq(info->usb_gpio.irq); + free_irq(info->usb_gpio.irq, &bat_device); + } + + del_timer_sync(&bat_device.timer); + cancel_delayed_work_sync(&charge_work); + cancel_delayed_work_sync(&discharge_work); + cancel_delayed_work_sync(&voltage_work); + cancel_delayed_work_sync(&usbirq_work); + cancel_delayed_work_sync(&acirq_work); + cancel_delayed_work_sync(&pdown_work); + cancel_delayed_work_sync(&resume_work); + + power_supply_unregister(&bat_device.bat_ps); + power_supply_unregister(&bat_device.ac_ps); + power_supply_unregister(&bat_device.usb_ps); + + unregister_reboot_notifier(&battery_reboot_nb); + return 0; +} + +static struct platform_driver ak_battery_driver = { + .driver = { + .name = "battery", + }, + .probe = ak_battery_probe, + .remove = __devexit_p(ak_battery_remove), + .suspend = ak_battery_suspend, + .resume = ak_battery_resume, +}; + +/** +* @Copyright (C) Anyka 2012 +* @brief init battery +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] void +* @return fail or not +*/ +static int __init ak_battery_init(void) +{ + return platform_driver_register(&ak_battery_driver); +} + +/** +* @Copyright (C) Anyka 2012 +* @brief exit battery +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-11-2 +* @param[out] void +* @param[in] void +* @return void +*/ +static void __exit ak_battery_exit(void) +{ + platform_driver_unregister(&ak_battery_driver); +} + +module_init(ak_battery_init); +module_exit(ak_battery_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("gao_wangsheng "); +MODULE_DESCRIPTION("ak battery driver"); diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 6ad61272..773fca15 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -41,23 +41,40 @@ static int __power_supply_changed_work(struct device *dev, void *data) static void power_supply_changed_work(struct work_struct *work) { + unsigned long flags; struct power_supply *psy = container_of(work, struct power_supply, changed_work); dev_dbg(psy->dev, "%s\n", __func__); - class_for_each_device(power_supply_class, NULL, psy, - __power_supply_changed_work); + spin_lock_irqsave(&psy->changed_lock, flags); + if (psy->changed) { + psy->changed = false; + spin_unlock_irqrestore(&psy->changed_lock, flags); - power_supply_update_leds(psy); + class_for_each_device(power_supply_class, NULL, psy, + __power_supply_changed_work); - kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); + power_supply_update_leds(psy); + + kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); + spin_lock_irqsave(&psy->changed_lock, flags); + } + if (!psy->changed) + pm_relax(psy->dev); + spin_unlock_irqrestore(&psy->changed_lock, flags); } void power_supply_changed(struct power_supply *psy) { + unsigned long flags; + dev_dbg(psy->dev, "%s\n", __func__); + spin_lock_irqsave(&psy->changed_lock, flags); + psy->changed = true; + pm_stay_awake(psy->dev); + spin_unlock_irqrestore(&psy->changed_lock, flags); schedule_work(&psy->changed_work); } EXPORT_SYMBOL_GPL(power_supply_changed); @@ -197,6 +214,11 @@ int power_supply_register(struct device *parent, struct power_supply *psy) if (rc) goto device_add_failed; + spin_lock_init(&psy->changed_lock); + rc = device_init_wakeup(dev, true); + if (rc) + goto wakeup_init_failed; + rc = power_supply_create_triggers(psy); if (rc) goto create_triggers_failed; @@ -206,6 +228,7 @@ int power_supply_register(struct device *parent, struct power_supply *psy) goto success; create_triggers_failed: +wakeup_init_failed: device_del(dev); kobject_set_name_failed: device_add_failed: diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 4368e7d6..b20acfa9 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -174,6 +174,10 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(time_to_full_avg), POWER_SUPPLY_ATTR(type), POWER_SUPPLY_ATTR(scope), + /* Local extensions */ + POWER_SUPPLY_ATTR(usb_hc), + POWER_SUPPLY_ATTR(usb_otg), + POWER_SUPPLY_ATTR(charge_enabled), /* Properties of type `const char *' */ POWER_SUPPLY_ATTR(model_name), POWER_SUPPLY_ATTR(manufacturer), diff --git a/drivers/power/smb347-charger.c b/drivers/power/smb347-charger.c index ce1694d1..fb56ec75 100644 --- a/drivers/power/smb347-charger.c +++ b/drivers/power/smb347-charger.c @@ -22,6 +22,7 @@ #include #include #include +#include /* * Configuration registers. These are mirrored to volatile RAM and can be @@ -38,14 +39,20 @@ #define CFG_CURRENT_LIMIT_DC_MASK 0xf0 #define CFG_CURRENT_LIMIT_DC_SHIFT 4 #define CFG_CURRENT_LIMIT_USB_MASK 0x0f +#define CFG_VARIOUS_FUNCTION 0x02 +#define CFG_INPUT_SOURCE_PRIORITY BIT(2) #define CFG_FLOAT_VOLTAGE 0x03 #define CFG_FLOAT_VOLTAGE_THRESHOLD_MASK 0xc0 +#define CFG_FLOAT_VOLTAGE_MASK 0x3F #define CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT 6 +#define CFG_CHARGE_CONTROL 0x04 +#define CFG_AUTOMATIC_RECHARGE_DISABLE BIT(7) #define CFG_STAT 0x05 #define CFG_STAT_DISABLED BIT(5) #define CFG_STAT_ACTIVE_HIGH BIT(7) #define CFG_PIN 0x06 #define CFG_PIN_EN_CTRL_MASK 0x60 +#define CFG_PIN_USB_MODE_CTRL BIT(4) #define CFG_PIN_EN_CTRL_ACTIVE_HIGH 0x40 #define CFG_PIN_EN_CTRL_ACTIVE_LOW 0x60 #define CFG_PIN_EN_APSD_IRQ BIT(1) @@ -85,8 +92,12 @@ #define CMD_A 0x30 #define CMD_A_CHG_ENABLED BIT(1) #define CMD_A_SUSPEND_ENABLED BIT(2) +#define CMD_A_OTG_ENABLE BIT(4) #define CMD_A_ALLOW_WRITE BIT(7) #define CMD_B 0x31 +#define CMD_B_POR BIT(7) +#define CMD_B_USB59_MODE BIT(1) +#define CMD_B_HC_MODE BIT(0) #define CMD_C 0x33 /* Interrupt Status registers */ @@ -108,6 +119,7 @@ #define STAT_B 0x3c #define STAT_C 0x3d #define STAT_C_CHG_ENABLED BIT(0) +#define STAT_C_CHG_STATUS BIT(5) #define STAT_C_CHG_MASK 0x06 #define STAT_C_CHG_SHIFT 1 #define STAT_C_CHARGER_ERROR BIT(6) @@ -135,6 +147,11 @@ struct smb347_charger { bool mains_online; bool usb_online; bool charging_enabled; + unsigned int mains_current_limit; + bool usb_hc_mode; + bool usb_otg_enabled; + bool is_fully_charged; + int en_gpio; struct dentry *dentry; const struct smb347_charger_platform_data *pdata; }; @@ -315,9 +332,17 @@ static int smb347_charging_set(struct smb347_charger *smb, bool enable) { int ret = 0; + if (enable && !smb->charging_enabled) + smb->is_fully_charged = false; + if (smb->pdata->enable_control != SMB347_CHG_ENABLE_SW) { - dev_dbg(&smb->client->dev, - "charging enable/disable in SW disabled\n"); + smb->charging_enabled = enable; + + if (smb->en_gpio) + gpio_set_value( + smb->en_gpio, + (smb->pdata->enable_control == + SMB347_CHG_ENABLE_PIN_ACTIVE_LOW) ^ enable); return 0; } @@ -424,9 +449,9 @@ static int smb347_set_current_limits(struct smb347_charger *smb) if (ret < 0) return ret; - if (smb->pdata->mains_current_limit) { + if (smb->mains_current_limit) { val = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl), - smb->pdata->mains_current_limit); + smb->mains_current_limit); if (val < 0) return val; @@ -473,6 +498,7 @@ static int smb347_set_voltage_limits(struct smb347_charger *smb) val = clamp_val(val, 3500000, 4500000) - 3500000; val /= 20000; + ret &= ~CFG_FLOAT_VOLTAGE_MASK; ret |= val; } @@ -662,6 +688,168 @@ static int smb347_set_writable(struct smb347_charger *smb, bool writable) return smb347_write(smb, CMD_A, ret); } +static int smb347_irq_set(struct smb347_charger *smb, bool enable) +{ + int ret; + + ret = smb347_set_writable(smb, true); + if (ret < 0) + return ret; + + /* + * Enable/disable interrupts for: + * - under voltage + * - termination current reached + * - charger error + */ + if (enable) { + ret = smb347_write(smb, CFG_FAULT_IRQ, CFG_FAULT_IRQ_DCIN_UV); + if (ret < 0) + goto fail; + + ret = smb347_write(smb, CFG_STATUS_IRQ, + CFG_STATUS_IRQ_TERMINATION_OR_TAPER); + if (ret < 0) + goto fail; + + ret = smb347_read(smb, CFG_PIN); + if (ret < 0) + goto fail; + + ret |= CFG_PIN_EN_CHARGER_ERROR; + + ret = smb347_write(smb, CFG_PIN, ret); + } else { + ret = smb347_write(smb, CFG_FAULT_IRQ, 0); + if (ret < 0) + goto fail; + + ret = smb347_write(smb, CFG_STATUS_IRQ, 0); + if (ret < 0) + goto fail; + + ret = smb347_read(smb, CFG_PIN); + if (ret < 0) + goto fail; + + ret &= ~CFG_PIN_EN_CHARGER_ERROR; + + ret = smb347_write(smb, CFG_PIN, ret); + } + +fail: + smb347_set_writable(smb, false); + return ret; +} + +static inline int smb347_irq_enable(struct smb347_charger *smb) +{ + return smb347_irq_set(smb, true); +} + +static inline int smb347_irq_disable(struct smb347_charger *smb) +{ + return smb347_irq_set(smb, false); +} + +static irqreturn_t smb347_interrupt(int irq, void *data) +{ + struct smb347_charger *smb = data; + int stat_c, t; + u8 irqstat[6]; + irqreturn_t ret = IRQ_NONE; + + t = i2c_smbus_read_i2c_block_data(smb->client, IRQSTAT_A, 6, irqstat); + if (t < 0) { + dev_warn(&smb->client->dev, + "reading IRQSTAT registers failed\n"); + return IRQ_NONE; + } + + stat_c = smb347_read(smb, STAT_C); + if (stat_c < 0) { + dev_warn(&smb->client->dev, "reading STAT_C failed\n"); + return IRQ_NONE; + } + + pr_debug("%s: stat c=%x irq a=%x b=%x c=%x d=%x e=%x f=%x\n", + __func__, stat_c, irqstat[0], irqstat[1], irqstat[2], + irqstat[3], irqstat[4], irqstat[5]); + + /* + * If we get charger error we report the error back to user and + * disable charging. + */ + if (stat_c & STAT_C_CHARGER_ERROR) { + dev_err(&smb->client->dev, + "error in charger, disabling charging\n"); + + smb347_charging_disable(smb); + power_supply_changed(&smb->battery); + + ret = IRQ_HANDLED; + } else if (((stat_c & STAT_C_CHG_STATUS) || + (irqstat[2] & (IRQSTAT_C_TERMINATION_IRQ | + IRQSTAT_C_TERMINATION_STAT))) && + !smb->is_fully_charged) { + dev_info(&smb->client->dev, "charge terminated\n"); + smb->is_fully_charged = true; + smb347_charging_disable(smb); + power_supply_changed(&smb->battery); + ret = IRQ_HANDLED; + } + + if (irqstat[2] & IRQSTAT_C_TAPER_IRQ) + ret = IRQ_HANDLED; + + /* + * If we got an under voltage interrupt it means that AC/USB input + * was disconnected. + */ + if (irqstat[4] & (IRQSTAT_E_USBIN_UV_IRQ | IRQSTAT_E_DCIN_UV_IRQ)) + ret = IRQ_HANDLED; + + if (smb347_update_status(smb) > 0) { + smb347_update_online(smb); + power_supply_changed(&smb->mains); + power_supply_changed(&smb->usb); + ret = IRQ_HANDLED; + } + + return ret; +} + +static int smb347_irq_init(struct smb347_charger *smb) +{ + const struct smb347_charger_platform_data *pdata = smb->pdata; + int ret, irq = gpio_to_irq(pdata->irq_gpio); + + ret = gpio_request_one(pdata->irq_gpio, GPIOF_IN, smb->client->name); + if (ret < 0) + goto fail; + + ret = request_threaded_irq(irq, NULL, smb347_interrupt, + pdata->disable_stat_interrupts ? + IRQF_TRIGGER_RISING | IRQF_ONESHOT : + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + smb->client->name, smb); + if (ret < 0) + goto fail_gpio; + + ret = enable_irq_wake(irq); + if (ret) + pr_err("%s: failed to enable wake on irq %d\n", __func__, irq); + + smb->client->irq = irq; + return 0; + +fail_gpio: + gpio_free(pdata->irq_gpio); +fail: + smb->client->irq = 0; + return ret; +} + static int smb347_hw_init(struct smb347_charger *smb) { int ret; @@ -686,9 +874,12 @@ static int smb347_hw_init(struct smb347_charger *smb) if (ret < 0) goto fail; +// HACK for Manta pre-alpha 0.2, TH_BATTERY not connected properly +#if 0 // HACK ret = smb347_set_temp_limits(smb); if (ret < 0) goto fail; +#endif // HACK /* If USB charging is disabled we put the USB in suspend mode */ if (!smb->pdata->use_usb) { @@ -719,6 +910,19 @@ static int smb347_hw_init(struct smb347_charger *smb) if (ret < 0) goto fail; + /* If configured by platform data, disable AUTOMATIC RECHARGE */ + if (smb->pdata->disable_automatic_recharge) { + ret = smb347_read(smb, CFG_CHARGE_CONTROL); + if (ret < 0) + goto fail; + + ret |= CFG_AUTOMATIC_RECHARGE_DISABLE; + + ret = smb347_write(smb, CFG_CHARGE_CONTROL, ret); + if (ret < 0) + goto fail; + } + ret = smb347_read(smb, CFG_PIN); if (ret < 0) goto fail; @@ -728,7 +932,7 @@ static int smb347_hw_init(struct smb347_charger *smb) * command register unless pin control is specified in the platform * data. */ - ret &= ~CFG_PIN_EN_CTRL_MASK; + ret &= ~(CFG_PIN_EN_CTRL_MASK | CFG_PIN_USB_MODE_CTRL); switch (smb->pdata->enable_control) { case SMB347_CHG_ENABLE_SW: @@ -742,6 +946,9 @@ static int smb347_hw_init(struct smb347_charger *smb) break; } + if (smb->pdata->usb_mode_pin_ctrl) + ret |= CFG_PIN_USB_MODE_CTRL; + /* Disable Automatic Power Source Detection (APSD) interrupt. */ ret &= ~CFG_PIN_EN_APSD_IRQ; @@ -755,123 +962,27 @@ static int smb347_hw_init(struct smb347_charger *smb) ret = smb347_update_online(smb); -fail: - smb347_set_writable(smb, false); - return ret; -} - -static irqreturn_t smb347_interrupt(int irq, void *data) -{ - struct smb347_charger *smb = data; - int stat_c, irqstat_e, irqstat_c; - irqreturn_t ret = IRQ_NONE; - - stat_c = smb347_read(smb, STAT_C); - if (stat_c < 0) { - dev_warn(&smb->client->dev, "reading STAT_C failed\n"); - return IRQ_NONE; - } - - irqstat_c = smb347_read(smb, IRQSTAT_C); - if (irqstat_c < 0) { - dev_warn(&smb->client->dev, "reading IRQSTAT_C failed\n"); - return IRQ_NONE; - } - - irqstat_e = smb347_read(smb, IRQSTAT_E); - if (irqstat_e < 0) { - dev_warn(&smb->client->dev, "reading IRQSTAT_E failed\n"); - return IRQ_NONE; - } - - /* - * If we get charger error we report the error back to user and - * disable charging. - */ - if (stat_c & STAT_C_CHARGER_ERROR) { - dev_err(&smb->client->dev, - "error in charger, disabling charging\n"); - - smb347_charging_disable(smb); - power_supply_changed(&smb->battery); - - ret = IRQ_HANDLED; - } - - /* - * If we reached the termination current the battery is charged and - * we can update the status now. Charging is automatically - * disabled by the hardware. - */ - if (irqstat_c & (IRQSTAT_C_TERMINATION_IRQ | IRQSTAT_C_TAPER_IRQ)) { - if (irqstat_c & IRQSTAT_C_TERMINATION_STAT) - power_supply_changed(&smb->battery); - ret = IRQ_HANDLED; - } - - /* - * If we got an under voltage interrupt it means that AC/USB input - * was connected or disconnected. - */ - if (irqstat_e & (IRQSTAT_E_USBIN_UV_IRQ | IRQSTAT_E_DCIN_UV_IRQ)) { - if (smb347_update_status(smb) > 0) { - smb347_update_online(smb); - power_supply_changed(&smb->mains); - power_supply_changed(&smb->usb); - } - ret = IRQ_HANDLED; - } - - return ret; -} - -static int smb347_irq_set(struct smb347_charger *smb, bool enable) -{ - int ret; - - ret = smb347_set_writable(smb, true); - if (ret < 0) - return ret; - - /* - * Enable/disable interrupts for: - * - under voltage - * - termination current reached - * - charger error - */ - if (enable) { - ret = smb347_write(smb, CFG_FAULT_IRQ, CFG_FAULT_IRQ_DCIN_UV); - if (ret < 0) - goto fail; - - ret = smb347_write(smb, CFG_STATUS_IRQ, - CFG_STATUS_IRQ_TERMINATION_OR_TAPER); - if (ret < 0) - goto fail; - - ret = smb347_read(smb, CFG_PIN); + if ((smb->pdata->irq_gpio >= 0) && + !smb->pdata->disable_stat_interrupts) { + /* + * Configure the STAT output to be suitable for interrupts: + * disable all other output (except interrupts) and make it + * active low. + */ + ret = smb347_read(smb, CFG_STAT); if (ret < 0) goto fail; - ret |= CFG_PIN_EN_CHARGER_ERROR; + ret &= ~CFG_STAT_ACTIVE_HIGH; + ret |= CFG_STAT_DISABLED; - ret = smb347_write(smb, CFG_PIN, ret); - } else { - ret = smb347_write(smb, CFG_FAULT_IRQ, 0); + ret = smb347_write(smb, CFG_STAT, ret); if (ret < 0) goto fail; - ret = smb347_write(smb, CFG_STATUS_IRQ, 0); - if (ret < 0) - goto fail; - - ret = smb347_read(smb, CFG_PIN); + ret = smb347_irq_enable(smb); if (ret < 0) goto fail; - - ret &= ~CFG_PIN_EN_CHARGER_ERROR; - - ret = smb347_write(smb, CFG_PIN, ret); } fail: @@ -879,85 +990,90 @@ fail: return ret; } -static inline int smb347_irq_enable(struct smb347_charger *smb) +static int smb347_mains_get_property(struct power_supply *psy, + enum power_supply_property prop, + union power_supply_propval *val) { - return smb347_irq_set(smb, true); -} + struct smb347_charger *smb = + container_of(psy, struct smb347_charger, mains); -static inline int smb347_irq_disable(struct smb347_charger *smb) -{ - return smb347_irq_set(smb, false); + switch (prop) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = smb->mains_online; + return 0; + + case POWER_SUPPLY_PROP_CURRENT_MAX: + val->intval = smb->mains_current_limit; + return 0; + + default: + return -EINVAL; + } + return -EINVAL; } -static int smb347_irq_init(struct smb347_charger *smb) +static int smb347_mains_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) { - const struct smb347_charger_platform_data *pdata = smb->pdata; - int ret, irq = gpio_to_irq(pdata->irq_gpio); + struct smb347_charger *smb = + container_of(psy, struct smb347_charger, mains); + int ret; + bool oldval; - ret = gpio_request_one(pdata->irq_gpio, GPIOF_IN, smb->client->name); - if (ret < 0) - goto fail; + switch (prop) { + case POWER_SUPPLY_PROP_ONLINE: + oldval = smb->mains_online; - ret = request_threaded_irq(irq, NULL, smb347_interrupt, - IRQF_TRIGGER_FALLING, smb->client->name, - smb); - if (ret < 0) - goto fail_gpio; + smb->mains_online = val->intval; - ret = smb347_set_writable(smb, true); - if (ret < 0) - goto fail_irq; + smb347_set_writable(smb, true); - /* - * Configure the STAT output to be suitable for interrupts: disable - * all other output (except interrupts) and make it active low. - */ - ret = smb347_read(smb, CFG_STAT); - if (ret < 0) - goto fail_readonly; + ret = smb347_read(smb, CMD_A); + if (ret < 0) + return -EINVAL; - ret &= ~CFG_STAT_ACTIVE_HIGH; - ret |= CFG_STAT_DISABLED; + ret &= ~CMD_A_SUSPEND_ENABLED; + if (val->intval) + ret |= CMD_A_SUSPEND_ENABLED; - ret = smb347_write(smb, CFG_STAT, ret); - if (ret < 0) - goto fail_readonly; + ret = smb347_write(smb, CMD_A, ret); - ret = smb347_irq_enable(smb); - if (ret < 0) - goto fail_readonly; + smb347_hw_init(smb); - smb347_set_writable(smb, false); - smb->client->irq = irq; - return 0; + smb347_set_writable(smb, false); -fail_readonly: - smb347_set_writable(smb, false); -fail_irq: - free_irq(irq, smb); -fail_gpio: - gpio_free(pdata->irq_gpio); -fail: - smb->client->irq = 0; - return ret; + if (smb->mains_online != oldval) + power_supply_changed(psy); + return 0; + case POWER_SUPPLY_PROP_CURRENT_MAX: + smb->mains_current_limit = val->intval; + smb347_hw_init(smb); + return 0; + + default: + return -EINVAL; + } + + return -EINVAL; } -static int smb347_mains_get_property(struct power_supply *psy, - enum power_supply_property prop, - union power_supply_propval *val) +static int smb347_mains_property_is_writeable(struct power_supply *psy, + enum power_supply_property prop) { - struct smb347_charger *smb = - container_of(psy, struct smb347_charger, mains); - - if (prop == POWER_SUPPLY_PROP_ONLINE) { - val->intval = smb->mains_online; - return 0; + switch (prop) { + case POWER_SUPPLY_PROP_CURRENT_MAX: + return 1; + default: + break; } - return -EINVAL; + + return 0; } static enum power_supply_property smb347_mains_properties[] = { POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_CURRENT_MAX, }; static int smb347_usb_get_property(struct power_supply *psy, @@ -967,15 +1083,94 @@ static int smb347_usb_get_property(struct power_supply *psy, struct smb347_charger *smb = container_of(psy, struct smb347_charger, usb); - if (prop == POWER_SUPPLY_PROP_ONLINE) { + switch (prop) { + case POWER_SUPPLY_PROP_ONLINE: val->intval = smb->usb_online; return 0; + + case POWER_SUPPLY_PROP_USB_HC: + val->intval = smb->usb_hc_mode; + return 0; + + case POWER_SUPPLY_PROP_USB_OTG: + val->intval = smb->usb_otg_enabled; + return 0; + + default: + break; } return -EINVAL; } +static int smb347_usb_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + int ret = -EINVAL; + struct smb347_charger *smb = + container_of(psy, struct smb347_charger, usb); + bool oldval; + + switch (prop) { + case POWER_SUPPLY_PROP_ONLINE: + oldval = smb->usb_online; + smb->usb_online = val->intval; + + if (smb->usb_online != oldval) + power_supply_changed(psy); + ret = 0; + break; + case POWER_SUPPLY_PROP_USB_HC: + smb347_set_writable(smb, true); + ret = smb347_write(smb, CMD_B, val->intval ? + CMD_B_HC_MODE : CMD_B_USB59_MODE); + smb347_set_writable(smb, false); + smb->usb_hc_mode = val->intval; + break; + + case POWER_SUPPLY_PROP_USB_OTG: + ret = smb347_read(smb, CMD_A); + + if (ret < 0) + return ret; + + if (val->intval) + ret |= CMD_A_OTG_ENABLE; + else + ret &= ~CMD_A_OTG_ENABLE; + + ret = smb347_write(smb, CMD_A, ret); + + if (ret >= 0) + smb->usb_otg_enabled = val->intval; + + break; + + default: + break; + } + + return ret; +} + +static int smb347_usb_property_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + switch (prop) { + case POWER_SUPPLY_PROP_USB_HC: + case POWER_SUPPLY_PROP_USB_OTG: + return 1; + default: + break; + } + + return 0; +} + static enum power_supply_property smb347_usb_properties[] = { POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_USB_HC, + POWER_SUPPLY_PROP_USB_OTG, }; static int smb347_battery_get_property(struct power_supply *psy, @@ -991,16 +1186,25 @@ static int smb347_battery_get_property(struct power_supply *psy, if (ret < 0) return ret; + if (ret > 0) { + smb347_update_online(smb); + power_supply_changed(&smb->mains); + power_supply_changed(&smb->usb); + } + switch (prop) { case POWER_SUPPLY_PROP_STATUS: if (!smb347_is_online(smb)) { + smb->is_fully_charged = false; val->intval = POWER_SUPPLY_STATUS_DISCHARGING; break; } if (smb347_charging_status(smb)) val->intval = POWER_SUPPLY_STATUS_CHARGING; else - val->intval = POWER_SUPPLY_STATUS_FULL; + val->intval = smb->is_fully_charged ? + POWER_SUPPLY_STATUS_FULL : + POWER_SUPPLY_STATUS_NOT_CHARGING; break; case POWER_SUPPLY_PROP_CHARGE_TYPE: @@ -1078,6 +1282,10 @@ static int smb347_battery_get_property(struct power_supply *psy, val->intval = pdata->battery_info.charge_full_design; break; + case POWER_SUPPLY_PROP_CHARGE_ENABLED: + val->intval = smb->charging_enabled; + break; + case POWER_SUPPLY_PROP_MODEL_NAME: val->strval = pdata->battery_info.name; break; @@ -1089,6 +1297,39 @@ static int smb347_battery_get_property(struct power_supply *psy, return 0; } +static int smb347_battery_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + int ret = -EINVAL; + struct smb347_charger *smb = + container_of(psy, struct smb347_charger, battery); + + switch (prop) { + case POWER_SUPPLY_PROP_CHARGE_ENABLED: + ret = smb347_charging_set(smb, val->intval); + break; + + default: + break; + } + + return ret; +} + +static int smb347_battery_property_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + switch (prop) { + case POWER_SUPPLY_PROP_CHARGE_ENABLED: + return 1; + default: + break; + } + + return 0; +} + static enum power_supply_property smb347_battery_properties[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_CHARGE_TYPE, @@ -1098,6 +1339,7 @@ static enum power_supply_property smb347_battery_properties[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_ENABLED, POWER_SUPPLY_PROP_MODEL_NAME, }; @@ -1181,6 +1423,33 @@ static int smb347_probe(struct i2c_client *client, smb->client = client; smb->pdata = pdata; + smb->mains_current_limit = smb->pdata->mains_current_limit; + + if (pdata->en_gpio) { + ret = gpio_request_one( + pdata->en_gpio, + smb->pdata->enable_control == + SMB347_CHG_ENABLE_PIN_ACTIVE_LOW ? + GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, + smb->client->name); + if (ret < 0) + dev_warn(dev, "failed to claim EN GPIO: %d\n", ret); + else + smb->en_gpio = pdata->en_gpio; + } + + ret = smb347_write(smb, CMD_B, CMD_B_POR); + if (ret < 0) + return ret; + + msleep(20); + + ret = smb347_read(smb, CMD_B); + if (ret < 0) { + dev_err(dev, "failed read after reset\n"); + return ret; + } + ret = smb347_hw_init(smb); if (ret < 0) return ret; @@ -1188,6 +1457,8 @@ static int smb347_probe(struct i2c_client *client, smb->mains.name = "smb347-mains"; smb->mains.type = POWER_SUPPLY_TYPE_MAINS; smb->mains.get_property = smb347_mains_get_property; + smb->mains.set_property = smb347_mains_set_property; + smb->mains.property_is_writeable = smb347_mains_property_is_writeable; smb->mains.properties = smb347_mains_properties; smb->mains.num_properties = ARRAY_SIZE(smb347_mains_properties); smb->mains.supplied_to = battery; @@ -1196,6 +1467,8 @@ static int smb347_probe(struct i2c_client *client, smb->usb.name = "smb347-usb"; smb->usb.type = POWER_SUPPLY_TYPE_USB; smb->usb.get_property = smb347_usb_get_property; + smb->usb.set_property = smb347_usb_set_property; + smb->usb.property_is_writeable = smb347_usb_property_is_writeable; smb->usb.properties = smb347_usb_properties; smb->usb.num_properties = ARRAY_SIZE(smb347_usb_properties); smb->usb.supplied_to = battery; @@ -1204,9 +1477,17 @@ static int smb347_probe(struct i2c_client *client, smb->battery.name = "smb347-battery"; smb->battery.type = POWER_SUPPLY_TYPE_BATTERY; smb->battery.get_property = smb347_battery_get_property; + smb->battery.set_property = smb347_battery_set_property; + smb->battery.property_is_writeable = smb347_battery_property_is_writeable; smb->battery.properties = smb347_battery_properties; smb->battery.num_properties = ARRAY_SIZE(smb347_battery_properties); + if (smb->pdata->supplied_to) { + smb->battery.supplied_to = smb->pdata->supplied_to; + smb->battery.num_supplicants = smb->pdata->num_supplicants; + smb->battery.external_power_changed = power_supply_changed; + } + ret = power_supply_register(dev, &smb->mains); if (ret < 0) return ret; @@ -1250,6 +1531,7 @@ static int smb347_remove(struct i2c_client *client) if (client->irq) { smb347_irq_disable(smb); + disable_irq_wake(client->irq); free_irq(client->irq, smb); gpio_free(smb->pdata->irq_gpio); } @@ -1260,6 +1542,29 @@ static int smb347_remove(struct i2c_client *client) return 0; } +static int smb347_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (client->irq) + disable_irq(client->irq); + return 0; +} + +static int smb347_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (client->irq) + enable_irq(client->irq); + return 0; +} + +static const struct dev_pm_ops smb347_pm_ops = { + .suspend = smb347_suspend, + .resume = smb347_resume, +}; + static const struct i2c_device_id smb347_id[] = { { "smb347", 0 }, { } @@ -1269,6 +1574,7 @@ MODULE_DEVICE_TABLE(i2c, smb347_id); static struct i2c_driver smb347_driver = { .driver = { .name = "smb347", + .pm = &smb347_pm_ops, }, .probe = smb347_probe, .remove = __devexit_p(smb347_remove), diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 8c8377d5..22e6d064 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1087,4 +1087,12 @@ config RTC_DRV_LOONGSON1 This driver can also be built as a module. If so, the module will be called rtc-ls1x. +config RTC_DRV_AK + tristate "AK RTC" + depends on ARCH_AK39 + help + If you say Y here you will get support for the onboard AK RTC. + +source "drivers/rtc/plat-anyka/Kconfig" + endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 727ae778..aaa613a2 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -111,3 +111,4 @@ obj-$(CONFIG_RTC_DRV_VT8500) += rtc-vt8500.o obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o +obj-$(CONFIG_RTC_DRV_AK) += plat-anyka/ diff --git a/drivers/rtc/plat-anyka/Kconfig b/drivers/rtc/plat-anyka/Kconfig new file mode 100644 index 00000000..bd84b499 --- /dev/null +++ b/drivers/rtc/plat-anyka/Kconfig @@ -0,0 +1,23 @@ +menuconfig AK39_RTC_SELECT_CLOCK + bool "ANYKA ak39 rtc select clock support" + help + Enable this to be able to choose the drivers for using the internal or external CLOCK. + +if AK39_RTC_SELECT_CLOCK + + +config AK39_RTC_INTERNAL_RC_OSC + bool "RTC_INTERNAL_RC_OSC" + depends on ARCH_AK39 + help + Say Y here if you are using the RTC_INTERNAL_RC_OSC + +config AK39_RTC_EXTERNAL_XTAL + bool "RTC_EXTERNAL_XTAL" + depends on ARCH_AK39 + help + Say Y here if you are using the RTC_EXTERNAL_XTAL + +endif + +#AK39_RTC_SELECT_CLOCK \ No newline at end of file diff --git a/drivers/rtc/plat-anyka/Makefile b/drivers/rtc/plat-anyka/Makefile new file mode 100644 index 00000000..b18e7080 --- /dev/null +++ b/drivers/rtc/plat-anyka/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for RTC class/drivers. +# + +obj-$(CONFIG_RTC_DRV_AK) += rtc.o diff --git a/drivers/rtc/plat-anyka/rtc.c b/drivers/rtc/plat-anyka/rtc.c new file mode 100644 index 00000000..bcd945e5 --- /dev/null +++ b/drivers/rtc/plat-anyka/rtc.c @@ -0,0 +1,782 @@ +/** +* @file drivers/rtc/rtc-ak.c +* @brief +* Copyright C 2011 Anyka CO.,LTD + +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* @author zhou wenyong +* @date 2011-08-29 +* @note +* +*/ +/* + * drivers/rtc/rtc-ak.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * 2011-3-22 for AK zwy + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + + + +//#define RTC_DEBUG +#define REG32(_reg_) (*(volatile unsigned long *)(_reg_)) + + +#undef PDEBUG /* undef it, just in case */ +#ifdef RTC_DEBUG +#ifdef __KERNEL__ +/* This one if debugging is on, and kernel space */ +#define PDEBUG(fmt, args...) printk( KERN_INFO fmt,## args) +#else +/* This one for user space */ +#define PDEBUG(fmt, args...) fprintf(stderr, "%s %d: "fmt,__FILE__, __LINE__, ## args) +#endif +#else +#define PDEBUG(fmt, args...) /* not debugging: nothing */ +#endif + + +static int ak_rtc_wakeup_enable(int en); +static void ak_rtc_alarm_enable(void ); +static void ak_rtc_alarm_disable(void ); + +//static volatile unsigned int adjust_time = 0; +//struct aktimer *ak_osc_adjust_timer; +//#define TIMER_ENABLE (0x1<<13) +#if 0 +/** +* @brief To clear alarm interrupt status. +* @author zhou wenyong +* @date 2011-08-29 +* @param[in] void +* @return void +*/ +static inline void ak_rtc_alarm_int_clear(void) +{ + unsigned int regval; + + PDEBUG("Entering %s\n", __FUNCTION__); + regval = ak_rtc_read(AK_RTC_SETTING); + regval |= (1<<0); + + ak_rtc_write(AK_RTC_SETTING, regval); + +} +#endif + +/** +* @brief ak_rtc_wakeup_enable +* @author zhou wenyong +* @date 2011-08-29 +* @param[in] en +* @return int +*/ +static int ak_rtc_wakeup_enable(int en) +{ + unsigned int val; + + PDEBUG("%s(): Entering...\n", __func__); + PDEBUG("%s: %s\n", __FUNCTION__, en ? "enable" : "disable"); + + /* read rtc wakeup setting and clear status */ + val = REG32(OTHER_WAKEUP_CTRL); + val &= ~RTC_WAKEUP_EN; + REG32(OTHER_WAKEUP_CTRL) = val; + + + if (en) + { + /* refer to PG 3.6.1.2*/ + + /* 1st: set bit[9:5] of Other Source Wake-up Control Register to 0X1F */ + val = REG32(OTHER_WAKEUP_CTRL); + val |= (0x1F<<7); + REG32(OTHER_WAKEUP_CTRL) = val; + + /* 2nd: make sure that Other Source Wake-up Status Register is 0x00000000 */ + while (REG32(OTHER_WAKEUP_STAT) & 0x1F) + ; + + /* 3rd: clear bit[9:5] of Other Source Wake-up Control Register to 0x0 */ + val = REG32(OTHER_WAKEUP_CTRL); + val &= ~(1<<7); + val |= RTC_WAKEUP_EN; + REG32(OTHER_WAKEUP_CTRL) = val; + } + else + { + /* disable wakeup signal */ + /* val |= RTC_WAKEUP_SIGNAL_CLEAR; */ + /* ak_rtc_write(AK_WDT_TIMER1, val); */ + } + return 0; + +} + + +/** +* @brief ak_rtc_gettime +* @author zhou wenyong +* @date 2011-08-29 +* @param[out] *dev +* @param[out] *tm +* @return int +*/ +static int ak_rtc_gettime(struct device *dev, struct rtc_time *tm) +{ + unsigned long rtcset; + unsigned long rtc_time1; + unsigned long rtc_time2; + unsigned long rtc_time3; + unsigned long timeout; + timeout = 500; + + PDEBUG("Entering %s\n", __FUNCTION__); + rtcset = ak_rtc_read(AK_RTC_SETTING); + rtcset |= RTC_SETTING_REAL_TIME_RE; + ak_rtc_write(AK_RTC_SETTING, rtcset); + + while ((ak_rtc_read(AK_RTC_SETTING) & RTC_SETTING_REAL_TIME_RE) && timeout) { + timeout--; + msleep(10); + if(0 == timeout ) + printk("ak_rtc_gettime timeout\n"); + } + + rtc_time1 = ak_rtc_read(AK_RTC_REAL_TIME1); + rtc_time2 = ak_rtc_read(AK_RTC_REAL_TIME2); + rtc_time3 = ak_rtc_read(AK_RTC_REAL_TIME3); + + tm->tm_year = ((rtc_time3 >> 4) & 0x7F) - EPOCH_START_YEAR + RTC_START_YEAR; + tm->tm_mon = (rtc_time3 & 0xF) - 1; + tm->tm_mday = (rtc_time2 >> 5) & 0x1F; + tm->tm_hour = rtc_time2 & 0x1F; + tm->tm_min = (rtc_time1 >> 6) & 0x3F; + tm->tm_sec = rtc_time1 & 0x3F; + tm->tm_wday = (rtc_time2 >> 10) & 0x7; + tm->tm_isdst = -1; + if ((tm->tm_year < (RTC_START_YEAR - EPOCH_START_YEAR)) + || (tm->tm_year > (RTC_START_YEAR - EPOCH_START_YEAR + RTC_YEAR_COUNT))) { + printk("%s(): -----RTC Year must between (%d ~ %d), now tm->tm_year:%d--------------\n", + __func__, RTC_START_YEAR, (RTC_START_YEAR + RTC_YEAR_COUNT), tm->tm_year); + + tm->tm_year = 2011 - EPOCH_START_YEAR ; + tm->tm_mon = 0; + tm->tm_mday = 1; + tm->tm_hour = 0; + tm->tm_min = 0; + tm->tm_sec = 0; + tm->tm_wday = 6; + + } +#if 0 + printk("Current RTC date/time is %d-%d-%d, %02d:%02d:%02d.\n", + tm->tm_year+1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); +#endif + return 0; +} + + +/** +* @brief ak_rtc_settime +* @author zhou wenyong +* @date 2011-08-29 +* @param[out] *dev +* @param[out] *tm +* @return int +*/ +static int ak_rtc_settime(struct device *dev, struct rtc_time *tm) +{ + unsigned long regval; + unsigned long timeout; + + timeout = 500; + printk(KERN_ERR "%s in\n", __func__); + + PDEBUG("Entering %s\n", __FUNCTION__); + printk("%s: %d-%d-%d %d:%d:%d\n", __func__, tm->tm_year, tm->tm_mon + 1, + tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + if ((tm->tm_year < (RTC_START_YEAR - EPOCH_START_YEAR)) + || (tm->tm_year > (RTC_START_YEAR - EPOCH_START_YEAR + RTC_YEAR_COUNT))) { + printk("%s(): RTC Year must between (%d ~ %d)\n", + __func__, RTC_START_YEAR, (RTC_START_YEAR + RTC_YEAR_COUNT)); + return -1; + } + + ak_rtc_write(AK_RTC_REAL_TIME1, (tm->tm_min << 6) | tm->tm_sec); + ak_rtc_write(AK_RTC_REAL_TIME2, (tm->tm_wday << 10) | (tm->tm_mday << 5) | tm->tm_hour); + ak_rtc_write(AK_RTC_REAL_TIME3, ((tm->tm_year + EPOCH_START_YEAR - RTC_START_YEAR) << 4) | (tm->tm_mon + 1)); + + regval = ak_rtc_read(AK_RTC_SETTING); + regval |= RTC_SETTING_REAL_TIME_WR; + ak_rtc_write(AK_RTC_SETTING, regval); + + while ((ak_rtc_read(AK_RTC_SETTING) & RTC_SETTING_REAL_TIME_WR) && timeout) { + timeout--; + msleep(10); + if(0 == timeout ) + printk("ak_rtc_settime timeout\n"); + } + + printk(KERN_ERR "%s out\n", __func__); + return 0; +} +#if 0 +/** +* @brief ak_rtc_getalarm +* @author zhou wenyong +* @date 2011-08-29 +* @param[out] *dev +* @param[out] *wkalrm +* @return int +*/ +static int ak_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + unsigned long rtc_alarm1; + unsigned long rtc_alarm2; + unsigned long rtc_alarm3; + + PDEBUG("Entering %s\n", __FUNCTION__); + + rtc_alarm1 = ak_rtc_read(AK_RTC_ALARM_TIME1); + rtc_alarm2 = ak_rtc_read(AK_RTC_ALARM_TIME2); + rtc_alarm3 = ak_rtc_read(AK_RTC_ALARM_TIME3); + + PDEBUG("%x %x %x\n", rtc_alarm1, rtc_alarm2, rtc_alarm3); + + wkalrm->time.tm_sec = rtc_alarm1 & 0x3F; + wkalrm->time.tm_min = (rtc_alarm1 >> 6) & 0x3F; + wkalrm->time.tm_hour = rtc_alarm2 & 0x1F; + wkalrm->time.tm_mday = (rtc_alarm2 >> 5) & 0x1F; + wkalrm->time.tm_mon = (rtc_alarm3 & 0xF) - 1; + wkalrm->time.tm_year = ((rtc_alarm3 >> 4) & 0x7F) - EPOCH_START_YEAR + RTC_START_YEAR; + wkalrm->time.tm_isdst = -1; + + PDEBUG("%d-%d-%d %d:%d:%d",wkalrm->time.tm_year,wkalrm->time.tm_mon + 1,wkalrm->time.tm_mday, + wkalrm->time.tm_hour,wkalrm->time.tm_min ,wkalrm->time.tm_sec); + return 0; +} + +/** +* @brief ak_rtc_setalarm +* @author zhou wenyong +* @date 2011-08-29 +* @param[out] *dev +* @param[out] *wkalrm +* @return int +*/ +static int ak_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + unsigned long rtc_alarm1; + unsigned long rtc_alarm2; + unsigned long rtc_alarm3; + + PDEBUG("Entering %s\n", __FUNCTION__); + rtc_alarm1 = ak_rtc_read(AK_RTC_ALARM_TIME1); + rtc_alarm2 = ak_rtc_read(AK_RTC_ALARM_TIME2); + rtc_alarm3 = ak_rtc_read(AK_RTC_ALARM_TIME3); + + PDEBUG("%u %u %u\n", rtc_alarm1, rtc_alarm2, rtc_alarm3); + PDEBUG("%d-%d-%d %d:%d:%d",wkalrm->time.tm_year,wkalrm->time.tm_mon + 1,wkalrm->time.tm_mday, + wkalrm->time.tm_hour,wkalrm->time.tm_min ,wkalrm->time.tm_sec); + + rtc_alarm1 &= ~(0xFFF); + rtc_alarm2 &= ~(0x3FF); + rtc_alarm3 &= ~(0x7FF); + + + rtc_alarm1 |= ((wkalrm->time.tm_min << 6) + wkalrm->time.tm_sec); + rtc_alarm2 |= ((wkalrm->time.tm_mday << 5) + wkalrm->time.tm_hour); + rtc_alarm3 |= (((wkalrm->time.tm_year + EPOCH_START_YEAR - RTC_START_YEAR) << 4) + (wkalrm->time.tm_mon + 1)); + + ak_rtc_write(AK_RTC_ALARM_TIME1, rtc_alarm1); + ak_rtc_write(AK_RTC_ALARM_TIME2, rtc_alarm2); + ak_rtc_write(AK_RTC_ALARM_TIME3, rtc_alarm3); + + PDEBUG("%u %u %u\n", rtc_alarm1, rtc_alarm2, rtc_alarm3); + + if (wkalrm->enabled == 1) + { + ak_rtc_wakeup_enable(1); + ak_rtc_alarm_enable(); + } + else + { + ak_rtc_alarm_disable(); + ak_rtc_wakeup_enable(0); + } + + return 0; +} +#endif +/** +* @brief ak_rtc_alarm_enable +* @author zhou wenyong +* @date 2011-08-29 +* @param[in] void +* @return void +*/ +static void ak_rtc_alarm_enable(void ) +{ + unsigned long rtc_alarm1; + unsigned long rtc_alarm2; + unsigned long rtc_alarm3; + + rtc_alarm1 = ak_rtc_read(AK_RTC_ALARM_TIME1); + rtc_alarm2 = ak_rtc_read(AK_RTC_ALARM_TIME2); + rtc_alarm3 = ak_rtc_read(AK_RTC_ALARM_TIME3); + + rtc_alarm1 |= (1<<13); + rtc_alarm2 |= (1<<13); + rtc_alarm3 |= (1<<13); + + ak_rtc_write(AK_RTC_ALARM_TIME1, rtc_alarm1); + ak_rtc_write(AK_RTC_ALARM_TIME2, rtc_alarm2); + ak_rtc_write(AK_RTC_ALARM_TIME3, rtc_alarm3); +} + +/** +* @brief ak_rtc_alarm_disable +* @author zhou wenyong +* @date 2011-08-29 +* @param[in] void +* @return void +*/ +static void ak_rtc_alarm_disable(void ) +{ + unsigned long rtc_alarm1; + unsigned long rtc_alarm2; + unsigned long rtc_alarm3; + + rtc_alarm1 = ak_rtc_read(AK_RTC_ALARM_TIME1); + rtc_alarm2 = ak_rtc_read(AK_RTC_ALARM_TIME2); + rtc_alarm3 = ak_rtc_read(AK_RTC_ALARM_TIME3); + + rtc_alarm1 &= ~(1<<13); + rtc_alarm2 &= ~(1<<13); + rtc_alarm3 &= ~(1<<13); + + ak_rtc_write(AK_RTC_ALARM_TIME1, rtc_alarm1); + ak_rtc_write(AK_RTC_ALARM_TIME2, rtc_alarm2); + ak_rtc_write(AK_RTC_ALARM_TIME3, rtc_alarm3); +} + +static void ak_osc_source_select(int osc_source) +{ + unsigned long val; + + /* select RTC 32K Adjust Register 1 */ + val = ak_rtc_read(AK_RTC_SETTING); + val |= (1 << 13); + val &= ~(1 << 10); + ak_rtc_write(AK_RTC_SETTING, val); + + switch(osc_source) + { + /* To Use Internal RC OSC */ + case RTC_INTERNAL_RC_OSC: + val = ak_rtc_read(AK_WDT_RTC_TIMER_CONF); + val |= (1 << 13); + ak_rtc_write(AK_WDT_RTC_TIMER_CONF,val); + printk("<0>""We're Using Internal RC OSC.\n"); + break; + + /* To Use External 32K XTAL */ + case RTC_EXTERNAL_XTAL: + val = ak_rtc_read(AK_WDT_RTC_TIMER_CONF); + val &= ~(1 << 13); + ak_rtc_write(AK_WDT_RTC_TIMER_CONF,val); + printk("<0>""We're Using External 32K XTAL.\n"); + break; + + default: + printk("Error RTC OSC SOURCE select operation.\n"); + break; + } + +} + +#ifdef CONFIG_AK39_RTC_INTERNAL_RC_OSC + +/** +* @brief To clear RTC timer interrupt status +* @author zhang zhipeng +* @date 2017-10-18 +* @param[in] void +* @return void +*/ +static inline void ak_rtc_timer_int_clear(void) +{ + unsigned int regval; + + PDEBUG("Entering %s\n", __FUNCTION__); + regval = ak_rtc_read(AK_RTC_SETTING); + regval |= (1<<1); + + ak_rtc_write(AK_RTC_SETTING, regval); + +} + +static void ak_osc_source_adjust_enable(void) +{ + unsigned long val; + //unsigned long regval; + + /* select RTC 32K Adjust Register 2 */ + val = ak_rtc_read(AK_RTC_SETTING); + val |= (1UL << 13); + val |= (1UL << 10); + ak_rtc_write(AK_RTC_SETTING, val); + + /* To Enable time adjustment */ + val = ak_rtc_read(AK_WDT_RTC_TIMER_CONF); + val |= (1UL << 13); + ak_rtc_write(AK_WDT_RTC_TIMER_CONF,val); + +} +struct delayed_work ak_osc_adjust_work; +static void ak_osc_adjust_handler(struct work_struct *work) +{ + printk("ak_osc_source_adjust_enable\n"); + ak_osc_source_adjust_enable(); + + schedule_delayed_work(&ak_osc_adjust_work, 120*HZ); +} +#endif + +/** +* @brief ak_rtc_ioctl +* @author zhou wenyong +* @date 2011-08-29 +* @param[out] *dev +* @param[in] cmd +* @param[in] arg +* @return int +*/ +static int ak_rtc_ioctl(struct device *dev, + unsigned int cmd, unsigned long arg) +{ + //PDEBUG("Entering %s: cmd %d\n", __FUNCTION__, cmd); + unsigned int ret = -ENOIOCTLCMD; + + switch (cmd) { + case RTC_AIE_OFF: + PDEBUG("RTC_AIE_OFF\n"); + ak_rtc_alarm_disable(); + ak_rtc_wakeup_enable(0); + + ret = 0; + break; + case RTC_AIE_ON: + PDEBUG("RTC_AIE_ON\n"); + ak_rtc_wakeup_enable(1); + ak_rtc_alarm_enable(); + ret = 0; + break; + + case RTC_UIE_ON: + case RTC_UIE_OFF: + ret = -ENOTTY; + break; + case RTC_RD_TIME: + PDEBUG("Read Time\n"); + break; + case RTC_SET_TIME: + PDEBUG("Set Time\n"); + break; + case RTC_ALM_SET: + PDEBUG("Set alarm\n"); + break; + case RTC_ALM_READ: + PDEBUG("Read alarm\n"); + break; + case RTC_WKALM_SET: + PDEBUG("Set alarm\n"); + break; + case RTC_WKALM_RD: + PDEBUG("Read alarm\n"); + break; + default: + return -ENOTTY; + + } + + return ret; +} + +/** +* @brief ak_rtc_proc +* @author zhou wenyong +* @date 2011-08-29 +* @param[out] *dev +* @param[out] *seq +* @return int +*/ +static int ak_rtc_proc(struct device *dev, struct seq_file *seq) +{ + PDEBUG("Entering %s\n", __FUNCTION__); + return 0; +} +#if 0 +/** +* @brief ak_rtc_alarmirq +* @author zhou wenyong +* @date 2011-08-29 +* @param[in] irq +* @param[out] *id +* @return irqreturn_t +*/ +static irqreturn_t ak_rtc_alarmirq(int irq, void *id) +{ + struct rtc_device *rdev = id; + PDEBUG("%s(): Entering...\n", __func__); + ak_rtc_alarm_int_clear(); + rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF); + + return IRQ_HANDLED; +} +#endif +/** +* @brief ak_rtc_open +* @author zhou wenyong +* @date 2011-08-29 +* @param[out] *dev +* @return int +*/ +static int ak_rtc_open(struct device *dev) +{ + PDEBUG("Entering %s\n", __FUNCTION__); + return 0; +} + +/** +* @brief ak_rtc_release +* @author zhou wenyong +* @date 2011-08-29 +* @param[out] *dev +* @return void +*/ +static void ak_rtc_release(struct device *dev) +{ + PDEBUG("Entering %s\n", __FUNCTION__); + +} + +static const struct rtc_class_ops ak_rtc_ops = { + .open = ak_rtc_open, + .release = ak_rtc_release, + .ioctl = ak_rtc_ioctl, + .read_time = ak_rtc_gettime, + .set_time = ak_rtc_settime, + .read_alarm = NULL, + //.read_alarm = ak_rtc_getalarm, + //.set_alarm = ak_rtc_setalarm, + .set_alarm = NULL, + .proc = ak_rtc_proc, +}; + +/** +* @brief ak_rtc_remove +* @author zhou wenyong +* @date 2011-08-29 +* @param[out] *dev +* @return int +*/ +static int ak_rtc_remove(struct platform_device *dev) +{ + struct rtc_device *rtc = platform_get_drvdata(dev); + PDEBUG("Entering %s\n", __FUNCTION__); + +#ifdef CONFIG_AK39_RTC_INTERNAL_RC_OSC + //free_irq(IRQ_RTC_ALARM, rtc); + cancel_delayed_work_sync(&ak_osc_adjust_work); +#endif + + platform_set_drvdata(dev, NULL); + rtc_device_unregister(rtc); + + return 0; +} + +/** +* @brief ak_rtc_probe +* @author zhou wenyong +* @date 2011-08-29 +* @param[out] *pdev +* @return int +*/ +static int ak_rtc_probe(struct platform_device *pdev) +{ + struct rtc_device *rtc; + /* struct resource *res; */ + int ret = 0; + + rtc = rtc_device_register("ak-rtc", &pdev->dev, &ak_rtc_ops, + THIS_MODULE); + + if (IS_ERR(rtc)) { + dev_err(&pdev->dev, "cannot attach rtc\n"); + ret = PTR_ERR(rtc); + return -1; + } + + rtc->max_user_freq = 128; + + platform_set_drvdata(pdev, rtc); + + ak_rtc_wakeup_enable(0); + +#ifdef CONFIG_AK39_RTC_INTERNAL_RC_OSC + INIT_DELAYED_WORK(&ak_osc_adjust_work, ak_osc_adjust_handler); + schedule_delayed_work(&ak_osc_adjust_work, 120*HZ); +#endif + return ret; + +} + +#ifdef CONFIG_PM + +//static struct timespec ak_rtc_delta; +/* RTC Power management control */ +/** +* @brief ak_rtc_suspend +* @author zhou wenyong +* @date 2011-08-29 +* @param[out] *pdev +* @param[in] state +* @return int +*/ +static int ak_rtc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct rtc_time tm; + struct timespec time; + + time.tv_nsec = 0; + + /* calculate time delta for suspend */ + ak_rtc_gettime(&pdev->dev, &tm); + rtc_tm_to_time(&tm, &time.tv_sec); +// save_time_delta(&ak_rtc_delta, &time); +#ifdef CONFIG_AK39_RTC_INTERNAL_RC_OSC + cancel_delayed_work_sync(&ak_osc_adjust_work); +#endif + return 0; +} + +/** +* @brief ak_rtc_resume +* @author zhou wenyong +* @date 2011-08-29 +* @param[out] *pdev +* @return int +*/ +static int ak_rtc_resume(struct platform_device *pdev) +{ + struct rtc_time tm; + struct timespec time; + + time.tv_nsec = 0; + ak_rtc_gettime(&pdev->dev, &tm); + rtc_tm_to_time(&tm, &time.tv_sec); +// restore_time_delta(&ak_rtc_delta, &time); +#ifdef CONFIG_AK39_RTC_INTERNAL_RC_OSC + INIT_DELAYED_WORK(&ak_osc_adjust_work, ak_osc_adjust_handler); +#endif + + return 0; +} +#else +#define ak_rtc_suspend NULL +#define ak_rtc_resume NULL +#endif + +static struct platform_driver ak_rtcdrv = { + .probe = ak_rtc_probe, + .remove = ak_rtc_remove, + .suspend = ak_rtc_suspend, + .resume = ak_rtc_resume, + .driver = { + .name = "ak-rtc", + .owner = THIS_MODULE, + }, +}; + +/** +* @brief ak_rtc_init +* @author zhou wenyong +* @date 2011-08-29 +* @param[in] void +* @return int __init +*/ +static int __init ak_rtc_init(void) +{ + PDEBUG("RTC Init...\n"); + ak_rtc_power(RTC_ON); + + if (test_rtc_inter_reg() < 0) { + printk("Board has not RTC support.exit %s\n", __func__); + ak_rtc_power(RTC_OFF); + return -ENODEV; + } + +#ifdef CONFIG_AK39_RTC_INTERNAL_RC_OSC + ak_osc_source_select(RTC_INTERNAL_RC_OSC); + ak_osc_source_adjust_enable(); +#endif + +#ifdef CONFIG_AK39_RTC_EXTERNAL_XTAL + ak_osc_source_select(RTC_EXTERNAL_XTAL); +#endif + + printk("AK RTC, (c) 2010 ANYKA \n"); + return platform_driver_register(&ak_rtcdrv); +} + +/** +* @brief ak_rtc_exit +* @author zhou wenyong +* @date 2011-08-29 +* @param[in] void +* @return void __exit +*/ +static void __exit ak_rtc_exit(void) +{ + /* + When RTC is powered off, this bit(AK_RTC_CONF [24] ) should be set to 0 + */ + ak_rtc_power(RTC_OFF); + platform_driver_unregister(&ak_rtcdrv); +} + +module_init(ak_rtc_init); +module_exit(ak_rtc_exit); + +MODULE_DESCRIPTION("ANYKA AK RTC Driver"); +MODULE_AUTHOR("anyka"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ak-rtc"); diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 29684c81..362baecf 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -265,7 +265,7 @@ config SCSI_SCAN_ASYNC config SCSI_WAIT_SCAN tristate # No prompt here, this is an invisible symbol. - default m + default n depends on SCSI depends on MODULES # scsi_wait_scan is a loadable module which waits until all the async scans are diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 00c02403..0706d7a0 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -403,6 +403,14 @@ config SPI_NUC900 help SPI driver for Nuvoton NUC900 series ARM SoCs +config SPI_ANYKA + tristate "ANYKA SPI controller" + depends on SPI_MASTER && (ARCH_AK98 || ARCH_AK39) + select SPI_BITBANG + help + SPI driver for anyka ARM SoCs + + # # Add new SPI master controllers in alphabetical order above this line # @@ -454,6 +462,13 @@ config SPI_TLE62X0 endif # SPI_MASTER +# (slave support would go here) +config SPI_ANYKA_SLAVE + tristate "ANYKA SPI slave controller " + depends on (ARCH_AK98 || ARCH_AK39) && !SPI_ANYKA + help + This is the SPI slave controller driver for anyka platform + # (slave support would go here) endif # SPI diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 9d75d219..f88966f0 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -62,4 +62,6 @@ obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o obj-$(CONFIG_SPI_TXX9) += spi-txx9.o obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o +obj-$(CONFIG_SPI_ANYKA) += spi-anyka.o +obj-$(CONFIG_SPI_ANYKA_SLAVE) += spi-anyka-slave.o diff --git a/drivers/spi/spi-anyka-slave.c b/drivers/spi/spi-anyka-slave.c new file mode 100644 index 00000000..ec17db3c --- /dev/null +++ b/drivers/spi/spi-anyka-slave.c @@ -0,0 +1,1050 @@ +/* + * spi_anyka_slave.c - Anyka SPI slave controller driver + * based on spi_anyka.c + * + * Copyright (C) Anyka 2012 + * Wangsheng Gao + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +/* + * Note: + * + * Supports interrupt programmed transfers. + * + */ + + /* + * Usage: + * 1. set one device as master: + * 1. register spidev board info in platform file; + * 2. select spidev and anyka spi controler in menuconfig. + * 2. set one device as slave: select anyka spi slave controler in menuconfig + * 3. run make command in Documentation/spi/ directory + * 4. copy Documentation/spi/spi_master_test and Documentation/spi/spi_slave_test to Your_rootfs_dir + * 5. firstly run spi_slave_test in slave and then run spi_master_test in master + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/** +* @brief slave data: devices, lock, wait_queue, io resource... +* @author gao wangsheng +* @date 2012-10-16 +* @param[out] void +* @param[in] void +* @param[in] void +* @return void +*/ +struct spi_anyka_slave { + /* Driver model hookup */ + struct platform_device *pdev; + struct ak_spi_info *pdata; + struct cdev cdev; + struct fasync_struct *async_queue; /* asynchronous readers */ + + struct clk *clk; + + /* Lock */ + struct mutex slave_lock; + spinlock_t regs_lock; + + /* wait queue */ + wait_queue_head_t readq; + wait_queue_head_t writeq; + + /* read and write buffer */ + char *rdbuf, *rdend; + char *recvp, *readp; + int rdsize; + char *wrbuf, *wrend; + char *sentp, *writep; + int wrsize; + + /* Spi controler register addresses */ + void *ioarea; + void *regs; + int irq; + + /* flag */ + u8 mode; + u8 bits_per_word; + u32 max_speed_hz; + int openers; +}; + +static struct class *slave_class; +static int slave_major; +static int slave_minor = 0; +#define SLAVE_MAX_MINOR (8) +#define DFT_DIV (255) +#define DFT_CON AK_SPICON_EN +#define BUF_LEN (512) +#define DCNT (0xffff) +#define SET_CLK (1 << 0) +#define SET_MODE (1 << 1) +#define SPI_MODE_MASK (SPI_CPOL | SPI_CPHA ) +#define SLAVE_MASK (AK_SPISTA_RXHFULL|AK_SPIINT_TIMEOUT |AK_SPIINT_RXFULL) + +#define sdbug(fmt...) //printk(fmt) + +static int akspi_slave_fasync(int fd, struct file *filp, int mode); + + +/** +* @Copyright (C) Anyka 2012 +* @brief How much space is free? +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-10-23 +* @param[out] void +* @param[in] *slave +* @return free space in buffer +*/ +static int akspi_slave_space_free(struct spi_anyka_slave *slave) +{ + if (slave->readp == slave->recvp){ + return slave->rdsize - 1; + } + return ((slave->readp + slave->rdsize - slave->recvp) % slave->rdsize) - 1; +} + +/** +* @Copyright (C) Anyka 2012 +* @brief store data into buffer +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-10-23 +* @param[out] slave->recvp +* @param[in] *slave +* @param[in] *buf +* @param[in] count +* @return How much data has been stored +*/ +static int akspi_slave_reveive_data(struct spi_anyka_slave *slave, + char *buf, size_t count) +{ +// sdbug("%s\n", __func__); + + /* ok, space is there, accept something */ + count = min(count, (size_t)akspi_slave_space_free(slave)); + if (slave->recvp >= slave->readp){ + /* to end-of-buf */ + count = min(count, (size_t)(slave->rdend - slave->recvp)); + } + else { + /* the write pointer has wrapped, fill up to rp-1 */ + count = min(count, (size_t)(slave->readp - slave->recvp - 1)); + } + + memcpy(slave->recvp, buf, count); + slave->recvp += count; + if (slave->recvp == slave->rdend){ + slave->recvp = slave->rdbuf; /* wrapped */ + } + + return count; +} + +/** +* @Copyright (C) Anyka 2012 +* @brief Enable or disable irq +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-10-23 +* @param[out] void +* @param[in] *slave +* @param[in] is enable? +* @return void +*/ +static void akspi_slave_set_irq(struct spi_anyka_slave *slave, int enable) +{ + u32 spiint = SLAVE_MASK; + if (enable) + iowrite32(spiint, slave->regs + AK_SPIINT); + else + iowrite32(0, slave->regs + AK_SPIINT); +} + +/** +* @Copyright (C) Anyka 2012 +* @brief receive data int interupt +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-10-23 +* @param[out] void +* @param[in] *slave +* @param[in] irq +* @return irqreturn_t +*/ +static irqreturn_t akspi_slave_int(int irq, void *dev_id) +{ + struct spi_anyka_slave *slave = (struct spi_anyka_slave *)dev_id; + u32 status, val; + u16 count; + int i; + + spin_lock(&slave->regs_lock); + status = ioread32(slave->regs + AK_SPISTA); + sdbug("%s: status=0x%08x\n", __func__, status); + + if (((status & AK_SPISTA_TIMEOUT) == AK_SPISTA_TIMEOUT) + && ((status & AK_SPISTA_RXEMP) != AK_SPISTA_RXEMP)) + { + char temp; + u16 cnt =0; + + count = ioread32(slave->regs + AK_SPICNT); + cnt = (DCNT - count)%4; + val = ioread32(slave->regs + AK_SPIIN); + sdbug("%s: AK_SPISTA_TIMEOUT, val=0x%08x, cnt=%u\n", __func__, val, cnt); + + for (i=0; i> i*8) & 0xff; + akspi_slave_reveive_data(slave, &temp, 1); + } + + iowrite32(DCNT, slave->regs + AK_SPICNT); + } + else if ((status & AK_SPISTA_RXFULL) == AK_SPISTA_RXFULL) + { + val = ioread32(slave->regs + AK_SPIIN); + sdbug("%s: AK_SPISTA_RXFULL, val=0x%08x\n", __func__, val); + akspi_slave_reveive_data(slave, (char *)&val, 4); + val = ioread32(slave->regs + AK_SPIIN); + sdbug("%s: AK_SPISTA_RXFULL, val=0x%08x\n", __func__, val); + akspi_slave_reveive_data(slave, (char *)&val, 4); + } + + else if ((status & AK_SPISTA_RXHFULL) == AK_SPISTA_RXHFULL) + { + val = ioread32(slave->regs + AK_SPIIN); + sdbug("%s: AK_SPISTA_RXHFULL, val=0x%08x\n", __func__, val); + akspi_slave_reveive_data(slave, (char *)&val, 4); + } + + wake_up_interruptible(&slave->readq); + if (slave->async_queue){ + kill_fasync(&slave->async_queue, SIGIO, POLL_IN); + } + spin_unlock(&slave->regs_lock); + return IRQ_HANDLED; +} + +/** +* @Copyright (C) Anyka 2012 +* @brief prepare receive data from master +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-10-23 +* @param[out] void +* @param[in] *slave +* @return void +*/ +static void akspi_slave_prepare_read(struct spi_anyka_slave *slave) +{ + u32 val; + unsigned long flags; + + spin_lock_irqsave(&slave->regs_lock, flags); + + val = ioread32(slave->regs + AK_SPICON); + val &= ~(AK_SPICON_ARRM); + val |= AK_SPICON_TGDM; + iowrite32(val, slave->regs + AK_SPICON); + iowrite32(DCNT, slave->regs + AK_SPICNT); + akspi_slave_set_irq(slave, 1); + + spin_unlock_irqrestore(&slave->regs_lock, flags); +} + +/** +* @Copyright (C) Anyka 2012 +* @brief open slave device +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-10-23 +* @param[out] void +* @param[in] *node +* @param[in] *filp +* @return fail or not +*/ +static int akspi_slave_open(struct inode *node, struct file *filp) +{ + struct spi_anyka_slave *slave = container_of(node->i_cdev, struct spi_anyka_slave, cdev); + + if (mutex_lock_interruptible(&slave->slave_lock)){ + return -ERESTARTSYS; + } + + nonseekable_open(node, filp); + + if (!slave->rdbuf){ + /* Alloc memory for receive data */ + slave->rdbuf = kzalloc(BUF_LEN, GFP_KERNEL); + if (!slave->rdbuf){ + mutex_unlock(&slave->slave_lock); + printk("%s: %d\n", __func__, __LINE__); + return -ENOMEM; + } + akspi_slave_prepare_read(slave); + } + + slave->rdsize = BUF_LEN; + slave->rdend = slave->rdbuf + slave->rdsize; + slave->readp = slave->recvp = slave->rdbuf; /* rd and wr from the beginning */ + slave->openers++; + filp->private_data = slave; + mutex_unlock(&slave->slave_lock); + sdbug("%s: openers=%d\n", __func__, slave->openers); + return 0; +} + +/** +* @Copyright (C) Anyka 2012 +* @brief relese slave device +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-10-23 +* @param[out] void +* @param[in] *node +* @param[in] *filp +* @return fail or not +*/ +static int akspi_slave_release(struct inode *node, struct file *filp) +{ + struct spi_anyka_slave *slave = container_of(node->i_cdev, struct spi_anyka_slave, cdev); + + akspi_slave_fasync(-1, filp, 0); + mutex_lock(&slave->slave_lock); + + slave->openers--; + if (!slave->openers){ + /* close spi controler */ + akspi_slave_set_irq(slave, 0); + kfree(slave->rdbuf); + slave->rdbuf = NULL; + } + + filp->private_data = NULL; + mutex_unlock(&slave->slave_lock); + sdbug("%s: openers=%d\n", __func__, slave->openers); + return 0; +} + +/** +* @Copyright (C) Anyka 2012 +* @brief setup slave controler +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-10-23 +* @param[out] void +* @param[in] *slave +* @param[in] *void +* @return fail or not +*/ +static int akspi_slave_setmode(struct spi_anyka_slave *slave) +{ + u16 spicon; + + sdbug("%s: set mode-------------.\n", __func__); + spin_lock(&slave->regs_lock); + spicon = ioread32(slave->regs + AK_SPICON); + + if (slave->mode & SPI_CPHA) + spicon |= AK_SPICON_CPHA; + else + spicon &= ~AK_SPICON_CPHA; + if (slave->mode & SPI_CPOL) + spicon |= AK_SPICON_CPOL; + else + spicon &= ~AK_SPICON_CPOL; + + iowrite32(spicon, slave->regs + AK_SPICON); + spin_unlock(&slave->regs_lock); + return 0; +} + +/** +* @Copyright (C) Anyka 2012 +* @brief setup slave controler +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-10-23 +* @param[out] void +* @param[in] *slave +* @param[in] *void +* @return fail or not +*/ +static int akspi_slave_setclk(struct spi_anyka_slave *slave) +{ + unsigned int div; + unsigned int hz = slave->max_speed_hz; + unsigned long clk = ak_get_asic_clk(); + u16 spicon; + + sdbug("%s: set clk-------------.\n", __func__); + spin_lock(&slave->regs_lock); + spicon = ioread32(slave->regs + AK_SPICON); + + div = clk / (hz*2) - 1; + if (div > 255){ + div = 255; + } + else if (div < 3){ + div = 3; + } + + spicon &=~(0xff << 8); + spicon |= div << 8; + iowrite32(spicon, slave->regs + AK_SPICON); + spin_unlock(&slave->regs_lock); + return 0; +} + + +/** +* @Copyright (C) Anyka 2012 +* @brief ioctl slave device +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-10-23 +* @param[out] void +* @param[in] *node +* @param[in] *filp, cmd, arg +* @return fail or not +*/ +static long akspi_slave_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int retval = 0; + int err = 0; + u32 tmp; + struct spi_anyka_slave *slave; + + sdbug("%s: cmd=%u\n", __func__, cmd); + + /* Check type and command number */ + if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC) + return -ENOTTY; + + /* Check access direction once here; don't repeat below. + * IOC_DIR is from the user perspective, while access_ok is + * from the kernel perspective; so they look reversed. + */ + if (_IOC_DIR(cmd) & _IOC_READ){ + err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); + } + + if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE){ + err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); + } + + if (err){ + return -EFAULT; + } + + slave = filp->private_data; + if (!slave){ + return -ESHUTDOWN; + } + + mutex_lock(&slave->slave_lock); + + switch (cmd){ + /* read requests */ + case SPI_IOC_RD_MODE: + retval = __put_user(slave->mode & SPI_MODE_MASK, (__u8 __user *)arg); + break; + case SPI_IOC_RD_LSB_FIRST: + retval = __put_user((slave->mode & SPI_LSB_FIRST) ? 1 : 0, (__u8 __user *)arg); + break; + case SPI_IOC_RD_BITS_PER_WORD: + retval = __put_user(slave->bits_per_word, (__u8 __user *)arg); + break; + case SPI_IOC_RD_MAX_SPEED_HZ: + retval = __put_user(slave->max_speed_hz, (__u32 __user *)arg); + break; + + /* write requests */ + case SPI_IOC_WR_MODE: + retval = __get_user(tmp, (u8 __user *)arg); + if (retval == 0) { + u8 save = slave->mode; + + if (tmp & ~SPI_MODE_MASK) { + retval = -EINVAL; + break; + } + + tmp |= slave->mode & ~SPI_MODE_MASK; + slave->mode = (u8)tmp; + retval = akspi_slave_setmode(slave); + if (retval < 0) + slave->mode = save; + else + dev_dbg(&slave->pdev->dev, "spi mode %02x\n", tmp); + } + break; + case SPI_IOC_WR_LSB_FIRST: + retval = -EINVAL; + break; + case SPI_IOC_WR_BITS_PER_WORD: + retval = -EINVAL; + break; + case SPI_IOC_WR_MAX_SPEED_HZ: + retval = __get_user(tmp, (__u32 __user *)arg); + if (retval == 0) { + u32 save = slave->max_speed_hz; + + slave->max_speed_hz = tmp; + retval = akspi_slave_setclk(slave); + if (retval < 0) + slave->max_speed_hz = save; + else + dev_dbg(&slave->pdev->dev, "%d Hz (max)\n", tmp); + } + break; + + default: + retval = -EINVAL; + break; + } + + mutex_unlock(&slave->slave_lock); + return retval; + } + +/** +* @Copyright (C) Anyka 2012 +* @brief read slave device +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-10-23 +* @param[out] void +* @param[in] *buf +* @param[in] *filp +* @return read data count +*/ +static ssize_t akspi_slave_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) +{ + struct spi_anyka_slave *slave = filp->private_data; + + if (mutex_lock_interruptible(&slave->slave_lock)){ + return -ERESTARTSYS; + } + + sdbug("%s: %d\n", __func__, __LINE__); + while (slave->recvp == slave->readp){ + mutex_unlock(&slave->slave_lock); + if (filp->f_flags & O_NONBLOCK){ + return -EAGAIN; + } + if (wait_event_interruptible(slave->readq, (slave->recvp != slave->readp))){ + return -ERESTARTSYS; + } + if (mutex_lock_interruptible(&slave->slave_lock)){ + return -ERESTARTSYS; + } + sdbug("%s: %d\n", __func__, __LINE__); + } + sdbug("%s: %d\n", __func__, __LINE__); + + if (slave->recvp > slave->readp) { + /* return the data */ + count = min(count, (size_t)(slave->recvp - slave->readp)); + } + else { + /* the write pointer has wrapped, return data up to end */ + count = min (count, (size_t)(slave->rdend - slave->readp)); + } + + if (copy_to_user(buf, slave->readp, count)) { + mutex_unlock(&slave->slave_lock); + return -EFAULT; + } + + slave->readp += count; + if (slave->readp == slave->rdend){ + slave->readp = slave->rdbuf; + } + + mutex_unlock(&slave->slave_lock); + sdbug("%s: %d\n", __func__, __LINE__); + return count; +} + +/** +* @Copyright (C) Anyka 2012 +* @brief write slave device +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-10-23 +* @param[out] void +* @param[in] *buf +* @param[in] *filp, count, pos +* @return write data count +*/ +static ssize_t akspi_slave_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) +{ + struct spi_anyka_slave *slave = filp->private_data; + + sdbug("%s: %d\n", __func__, __LINE__); + + return count; + + if (mutex_lock_interruptible(&slave->slave_lock)){ + return -ERESTARTSYS; + } + + while (slave->sentp != slave->writep){ + mutex_unlock(&slave->slave_lock); + if (filp->f_flags & O_NONBLOCK){ + return -EAGAIN; + } + if (wait_event_interruptible(slave->writeq, slave->sentp != slave->writep)){ + return -ERESTARTSYS; + } + if (mutex_lock_interruptible(&slave->slave_lock)){ + return -ERESTARTSYS; + } + } + + /* spi slave has something to write */ + + mutex_unlock(&slave->slave_lock); + sdbug("%s: %d\n", __func__, __LINE__); + return count; +} + +/** +* @Copyright (C) Anyka 2012 +* @brief poll slave device +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-10-23 +* @param[out] void +* @param[in] *table +* @param[in] *filp +* @return fail or not +*/ +unsigned int akspi_slave_poll (struct file *filp, struct poll_table_struct *table) +{ + struct spi_anyka_slave *slave = filp->private_data; + int mask = 0; + + sdbug("%s: %d\n", __func__, __LINE__); + mutex_lock(&slave->slave_lock); + poll_wait(filp, &slave->readq, table); + poll_wait(filp, &slave->writeq, table); + + if (slave->recvp != slave->readp){ + mask |= POLLIN | POLLRDNORM; /* readable */ + } + + if (slave->sentp != slave->writep){ + mask |= POLLOUT | POLLWRNORM; /* writable */ + } + mutex_unlock(&slave->slave_lock); + sdbug("%s: %d\n", __func__, __LINE__); + return mask; +} + +/** +* @Copyright (C) Anyka 2012 +* @brief fasync slave device +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-10-23 +* @param[out] void +* @param[in] *node +* @param[in] *filp, fd, mode +* @return fail or not +*/ +static int akspi_slave_fasync(int fd, struct file *filp, int mode) +{ + struct spi_anyka_slave *slave = filp->private_data; + + return fasync_helper(fd, filp, mode, &slave->async_queue); +} + +/** +* @Copyright (C) Anyka 2012 +* @brief initialize slave device +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-10-23 +* @param[out] void +* @param[in] *slave +* @param[in] void +* @return void +*/ +static void akspi_slave_initial_setup(struct spi_anyka_slave *slave) +{ + u32 value = 0; + + sdbug("%s: %d\n", __func__, __LINE__); + clk_enable(slave->clk); + + spin_lock(&slave->regs_lock); + value = DFT_CON ; + iowrite32(value, slave->regs + AK_SPICON); + spin_unlock(&slave->regs_lock); + akspi_slave_setclk(slave); + akspi_slave_setmode(slave); + sdbug("value=%08x, reg=%08x\n",value, ioread32(slave->regs + AK_SPICON)); + akspi_slave_set_irq(slave, 0); + + if (slave->pdata) { + if (slave->pdata->gpio_setup){ + slave->pdata->gpio_setup(slave->pdata, 1); + } + } +} + +static const struct file_operations slave_ops = { + .owner = THIS_MODULE, + .open = akspi_slave_open, + .release = akspi_slave_release, + .unlocked_ioctl = akspi_slave_ioctl, + .read = akspi_slave_read, + .write = akspi_slave_write, + .poll = akspi_slave_poll, + .fasync = akspi_slave_fasync, +}; + +/** +* @Copyright (C) Anyka 2012 +* @brief probe slave device +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-10-23 +* @param[out] slave +* @param[in] *pdev +* @param[in] *pdata +* @return fail or not +*/ +static int ak_spi_slave_probe(struct platform_device *pdev) +{ + struct ak_spi_info *pdata; + struct spi_anyka_slave *slave = NULL; + struct resource *res; + int err = 0; + + pdata = pdev->dev.platform_data; + if (pdata == NULL) + { + dev_err(&pdev->dev, "No platform data supplied\n"); + err = -ENOENT; + goto err_no_pdata; + } + + /* Allocate Slave with space for drv_data and null dma buffer */ + slave = kzalloc(sizeof(struct spi_anyka_slave), GFP_KERNEL); + if (!slave) { + dev_err(&pdev->dev, "cannot alloc mem\n"); + err = -ENOMEM; + goto err_nomem; + } + + slave->pdata = pdata; + slave->pdev = pdev; + cdev_init(&slave->cdev, &slave_ops); + slave->cdev.owner = THIS_MODULE; + + spin_lock_init(&slave->regs_lock); + mutex_init(&slave->slave_lock); + init_waitqueue_head(&slave->readq); + init_waitqueue_head(&slave->writeq); + slave->mode = SPI_MODE_0; + slave->bits_per_word = 8; + slave->max_speed_hz = 2147483647; + + /* get basic io resource and map it */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); + err = -ENOENT; + goto err_no_iores; + } + + slave->ioarea = request_mem_region(res->start, resource_size(res), pdev->name); + if (slave->ioarea == NULL) { + dev_err(&pdev->dev, "Cannot reserve region\n"); + err = -ENXIO; + goto err_no_iores; + } + + slave->regs = ioremap(res->start, resource_size(res)); + if (!slave->regs) { + err = -ENOMEM; + goto err_no_iomap; + } + printk(KERN_INFO "SPI-Slave:map regs = %08x\n", (int)slave->regs); + + /* Attach to IRQ */ + slave->irq = platform_get_irq(pdev, 0); + if (slave->irq < 0) { + dev_err(&pdev->dev, "No IRQ specified\n"); + err = -ENOENT; + goto err_no_irq; + } + printk(KERN_INFO "SPI-Slave:get irq = %04x\n", (int)slave->irq); + + err = request_irq(slave->irq, akspi_slave_int, IRQF_DISABLED, pdev->name, slave); + if (err < 0) { + dev_err(&pdev->dev, "can not get IRQ\n"); + goto err_no_irq; + } + printk(KERN_INFO "SPI-Slave: request IRQ: %04x\n", slave->irq); + + err = cdev_add(&slave->cdev, MKDEV(slave_major, slave_minor), 1); + if (err){ + dev_err(&pdev->dev, "cannot add cdev\n"); + err = -ENOMEM; + goto err_register; + } + printk(KERN_INFO "SPI-Slave: register with char device framework\n"); + + if (IS_ERR(device_create(slave_class, &pdev->dev, + MKDEV(slave_major, slave_minor), + slave, "spi_slave.%u", slave_minor))){ + dev_err(&pdev->dev, "cannot device_create\n"); + } + + slave->clk = clk_get(&pdev->dev, pdata->clk_name); + sdbug("%s: %lu \n", slave->clk->name, clk_get_rate(slave->clk)); + if (IS_ERR(slave->clk)) { + dev_err(&pdev->dev, "No clock for device\n"); + err = PTR_ERR(slave->clk); + goto err_no_clk; + } + + akspi_slave_initial_setup(slave); + + platform_set_drvdata(pdev, slave); + printk("Ak spi slave initialized!\n"); + return 0; + + err_no_clk: + device_destroy(slave_class, MKDEV(slave_major, slave_minor)); + cdev_del(&slave->cdev); + err_register: + free_irq(slave->irq, slave); + err_no_irq: + iounmap(slave->regs); + err_no_iomap: + release_resource(slave->ioarea); + kfree(slave->ioarea); + err_no_iores: + err_no_pdata: + err_nomem: + return err; +} + +/** +* @Copyright (C) Anyka 2012 +* @brief remove slave device +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-10-23 +* @param[out] void +* @param[in] *pdev +* @param[in] *slave +* @return void +*/ +static void __devexit ak_spi_slave_remove(struct platform_device *pdev) +{ + struct spi_anyka_slave *slave = platform_get_drvdata(pdev); + int minor = MINOR(slave->cdev.dev); + + if (!slave) + return; + + platform_set_drvdata(pdev, NULL); + device_destroy(slave_class, MKDEV(slave_major, minor)); + cdev_del(&slave->cdev); + + /* Release IRQ */ + free_irq(slave->irq, slave); + iounmap(slave->regs); + release_resource(slave->ioarea); + kfree(slave->ioarea); + kfree(slave); + + return; +} + +/** +* @Copyright (C) Anyka 2012 +* @brief suspend and resume slave device +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-10-23 +* @param[out] void +* @param[in] *dev +* @param[in] state +* @return fail or not +*/ +#ifdef CONFIG_PM +static int ak_spi_slave_suspend(struct device *dev, pm_message_t state) +{ + struct spi_anyka_slave *slave = dev_get_drvdata(dev); + + printk(KERN_ERR "%s: suspend\n", slave->pdev->name); + return 0; +} + +static int ak_spi_slave_resume(struct device *dev) +{ + struct spi_anyka_slave *slave = dev_get_drvdata(dev); + + printk(KERN_ERR "%s: resume\n", slave->pdev->name); + return 0; +} +#else +#define ak_spi_slave_suspend NULL +#define ak_spi_slave_resume NULL +#endif /* CONFIG_PM */ + +static struct platform_driver akspi_slave_driver = { + .driver = { + .name = "akspi-spi", + .owner = THIS_MODULE, + .suspend = ak_spi_slave_suspend, + .resume = ak_spi_slave_resume, + }, + .remove = __exit_p(ak_spi_slave_remove), + .probe = ak_spi_slave_probe, +}; + +static ssize_t slave_show_version(struct class *cls, struct class_attribute *attr, char *buf) +{ + return sprintf(buf, "spi-1.0.01\n"); +} +static CLASS_ATTR(version, 0444, slave_show_version, NULL); + +/** +* @Copyright (C) Anyka 2012 +* @brief init slave device +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-10-23 +* @param[out] void +* @param[in] void +* @param[in] void +* @return fail or not +*/ +static int __init ak_spi_slave_init(void) +{ + int ret; + dev_t dev; + + slave_class = class_create(THIS_MODULE, "spi_slave"); + if (IS_ERR(slave_class)){ + ret = PTR_ERR(slave_class); + printk("%s:class create fail!\n", __func__); + goto err; + } + + ret = class_create_file(slave_class, &class_attr_version); + if (ret){ + printk("%s:class create file fail!\n", __func__); + goto err_class; + } + + ret = alloc_chrdev_region(&dev, 0, SLAVE_MAX_MINOR, "spi_slave"); + if (ret){ + printk("%s:alloc chrdev fail!\n", __func__); + goto err_chrdev; + } + slave_major = MAJOR(dev); + + ret = platform_driver_register(&akspi_slave_driver); + if (ret){ + printk("%s:platform_driver_register fail!\n", __func__); + goto err_plat; + } + + return 0; + +err_plat: + unregister_chrdev_region(dev, SLAVE_MAX_MINOR); +err_chrdev: + class_remove_file(slave_class, &class_attr_version); +err_class: + class_destroy(slave_class); +err: + return ret; +} + +/** +* @Copyright (C) Anyka 2012 +* @brief exit slave device +* @author Gao wangsheng +* @email gao_wangsheng@anyka.oa +* @date 2012-10-23 +* @param[out] void +* @param[in] void +* @param[in] void +* @return void +*/ +static void __exit ak_spi_slave_exit(void) +{ + platform_driver_unregister(&akspi_slave_driver); + unregister_chrdev_region(MKDEV(slave_major, 0), SLAVE_MAX_MINOR); + class_remove_file(slave_class, &class_attr_version); + class_destroy(slave_class); +} + +MODULE_AUTHOR("Wangsheng Gao"); +MODULE_DESCRIPTION("Anyka SPI Slave Contoller"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:AK-spi-slave"); +module_init(ak_spi_slave_init); +module_exit(ak_spi_slave_exit); diff --git a/drivers/spi/spi-anyka.c b/drivers/spi/spi-anyka.c new file mode 100644 index 00000000..1d2bc688 --- /dev/null +++ b/drivers/spi/spi-anyka.c @@ -0,0 +1,1770 @@ +/** +* @file /driver/spi/spi_anyka.c +* @brief AK On-chip SPI driver +* Copyright C 2011 Anyka CO.,LTD +* modify based on spi_s3c24xx.c +* +* Copyright (c) 2006 Ben Dooks +* Copyright (c) 2006 Simtec Electronics +* Ben Dooks +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* @author zhou wenyong +* @date 2011-08-19 +* @note 2011-5-16 created +* @note 2011-08-19 add more comments +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +//#define SPI_DEBUG + +#define TMPDBG(fmt, args...) //printk( KERN_INFO fmt,## args) + +#define TMPDEBUG(fmt, args...) //printk( KERN_INFO fmt,## args) + +/* #define DEBUG */ +#undef PDEBUG /* undef it, just in case */ +#ifdef SPI_DEBUG +# ifdef __KERNEL__ +/* This one if debugging is on, and kernel space */ +# define PDEBUG(fmt, args...) printk( KERN_INFO fmt,## args) +# else +/* This one for user space */ +# define PDEBUG(fmt, args...) fprintf(stderr, "%s %d: "fmt,__FILE__, __LINE__, ## args) +# endif +#else +# define PDEBUG(fmt, args...) /* not debugging: nothing */ +#endif + + /*usually use for spi keyboard or spi mouse.*/ +//#define SPI_CPU_MODE_USE_INTERRUPT + + +/** + * ak_spi_devstate - per device data + * @hz: Last frequency calculated for @sppre field. + * @mode: Last mode setting for the @spcon field. + * @spcon: Value to write to the SPCON register. + * @sppre: Value to write to the SPIINT register. + */ +struct ak_spi_devstate { + unsigned int hz; + u16 mode; + u32 spcon; + u8 spint; + u8 initialed; +}; + +struct ak_spi { + /* bitbang has to be first */ + struct spi_bitbang bitbang; + struct completion done; + struct spi_master *master; + struct spi_device *curdev; + struct device *dev; + struct ak_spi_info *pdata; + + void __iomem *regs; + struct clk *clk; + + /* spi controller need a DMA memory for L2 transfer.*/ + void *txbuffer; + dma_addr_t txdma_buffer; + + void *rxbuffer; + dma_addr_t rxdma_buffer; + + struct resource *ioarea; + int irq; + int len; /*need transfer len*/ + int count; /*have transferred len*/ + + u8 l2buf_tid; + u8 l2buf_rid; + + /* data buffers */ + unsigned char *tx; + unsigned char *rx; + int xfer_dir; + int xfer_mode; /*use for dma or cpu*/ +}; + +enum spi_xfer_dir { + SPI_DIR_TX, + SPI_DIR_RX, + SPI_DIR_TXRX, + SPI_DIR_XFER_NUM, +}; + + +#define TRANS_TIMEOUT (10000) +#define MAX_XFER_LEN (4*1024) +#define SPI_TRANS_TIMEOUT (5000) +#define SPI_DMA_MAX_LEN (4096) + + +#define DFT_CON (AK_SPICON_EN | AK_SPICON_MS) +#define DFT_DIV (1) //5 /*127*/ +#define DFT_BIT_PER_WORD (8) +#define FORCE_CS (1 << 5) +#define SPPIN_DEFAULT (0) + +#if 1 +/** +* @brief print the value of registers related spi bus. +* @author zhou wenyong +* @date 2011-08-19 +* @param[in] *hw +* @return void +*/ +static void dbg_dumpregs(struct ak_spi *hw) +{ + PDEBUG("\n"); + PDEBUG("CON: \t0x%x\n", ioread32(hw->regs + AK_SPICON)); + PDEBUG("STA: \t0x%x\n", ioread32(hw->regs + AK_SPISTA)); + PDEBUG("INT: \t0x%x\n", ioread32(hw->regs + AK_SPIINT)); + PDEBUG("CNT: \t0x%x\n", ioread32(hw->regs + AK_SPICNT)); + PDEBUG("DOUT: \t0x%x\n", ioread32(hw->regs + AK_SPIOUT)); + PDEBUG("DIN: \t0x%x\n", ioread32(hw->regs + AK_SPIIN)); +} + +static inline void dbg_dumpdata(struct ak_spi *hw, + void *data, int size) +{ + int ii; + int dsize = (size +3)/4; + u32 *dptr = data; + + printk("xfer data (size:%d):", size); + + for(ii = 0; ii < dsize; ii++) { + if((ii%10) == 0) + printk("\n"); + + printk("%08x ", *(dptr + ii)); + } + printk("\n"); +} +#endif + + +#define AKSPI_DATA_WIRE(mode) \ + (((mode & XFER_4DATAWIRE) == XFER_4DATAWIRE) ? \ + AKSPI_4DATAWIRE:((mode & XFER_2DATAWIRE) == XFER_2DATAWIRE) ? \ + AKSPI_2DATAWIRE : AKSPI_1DATAWIRE) + + +#define SPI_L2_TXADDR(m) \ + ((m->bus_num == AKSPI_BUS_NUM1) ? ADDR_SPI1_TX : ADDR_SPI2_TX) + +#define SPI_L2_RXADDR(m) \ + ((m->bus_num == AKSPI_BUS_NUM1) ? ADDR_SPI1_RX : ADDR_SPI2_RX) + + +#define SPI_SHAREPIN(m) \ + ((m->bus_num == AKSPI_BUS_NUM1) ? ePIN_AS_SPI1 : ePIN_AS_SPI2) + +#define SPI_RESET_NUM(m) \ + ((m->bus_num == AKSPI_BUS_NUM1) ? AK39_SRESET_SPI1 : AK39_SRESET_SPI2) + + +static inline struct ak_spi *to_hw(struct spi_device *sdev) +{ + return spi_master_get_devdata(sdev->master); +} + +/** +* @brief hw_txbyte +* TODO: send one bytes +* @author lixinhai +* @date 2013-03-19 +* @param[in] *hw +* @param[in] count +* @return unsigned int +*/ +static inline unsigned int hw_txbyte(struct ak_spi *hw, int len) +{ + u32 val = 0; + int i = 0; + + while (i < len) + { + val |= (hw->tx[hw->count+i] << i*8); + i++; + } + return val; +} + +/** +* @brief hw_txdword +* TODO: send double words +* @author lixinhai +* @date 2013-03-19 +* @param[in] *hw +* @param[in] count +* @return unsigned int +*/ +static unsigned int hw_txdword(struct ak_spi *hw) +{ + u32 val = 0; + int l = 0; + + l = (hw->len - hw->count) > 4 ? 4 : (hw->len - hw->count); + + val = hw_txbyte(hw, l); + + hw->count += l; + PDEBUG("[%08x] ", val); + return val; +} + +/** +* @brief hw_rxbyte +* TODO: recv one bytes +* @author lixinhai +* @date 2013-03-19 +* @param[in] count +* @return unsigned int +*/ +static inline void hw_rxbyte(struct ak_spi *hw, unsigned int val, int len) +{ + int i = 0; + + while (i < len) + { + hw->rx[hw->count + i] = (val >> i*8) & 0xff; + i++; + } +} + +/** +* @brief hw_rxbyte +* TODO: double words +* @author lixinhai +* @date 2013-03-19 +* @param[in] count +* @return unsigned int +*/ +static void hw_rxdword(struct ak_spi *hw, unsigned int val) +{ + int l = 0; + + l = (hw->len - hw->count) > 4 ? 4 : (hw->len - hw->count); + + hw_rxbyte(hw, val, l); + hw->count += l; + PDEBUG("[%08x] ", val); +} + +static inline u32 hw_remain_datalen(struct ak_spi *hw) +{ + return (hw->len - hw->count); +} + +static inline bool ak_spi_use_dma(struct ak_spi *hw) +{ + return (hw->xfer_mode == AKSPI_XFER_MODE_DMA); +} + +static inline int wait_for_spi_cnt_to_zero(struct ak_spi *hw, u32 timeout) +{ + do { + if (readl(hw->regs + AK_SPICNT) == 0) + break; + udelay(1); + }while(timeout--); + + return (timeout > 0) ? 0 : -EBUSY; +} + + +static void ak_spi_gpio_setup(struct ak_spi *hw, int enable) +{ + struct ak_spi_info *plat = hw->pdata; + + if(!plat) + return; + + if (enable) + { + int ii; + ak_group_config(SPI_SHAREPIN(hw->master)); + + +// REG_VA_VAL(AK_VA_SYSCTRL, 0x74) |= ((0x1<<25)); //0x0800,0074, cdh:check ok + // REG_VA_VAL(AK_VA_SYSCTRL,0xDC) |= (0x3 | (0x3f<<8)); + + for(ii=0; iinum_cs; ii++) { + if(ii == AKSPI_ONCHIP_CS) + continue; + + ak_setpin_as_gpio(plat->pin_cs[ii]); + ak_gpio_dircfg(plat->pin_cs[ii], AK_GPIO_DIR_OUTPUT); + ak_gpio_setpin(plat->pin_cs[ii], 1); + } + } +} + +static void ak_spi_set_cs(struct ak_spi *hw, int cs, int pol) +{ + struct ak_spi_info *plat = hw->pdata; + + if(!plat) + return; + + BUG_ON(cs >= plat->num_cs); + + ak_gpio_setpin(plat->pin_cs[cs], pol); +} + + + +/** +* @brief ak_spi_chipsel +* @author zhou wenyong +* @date 2011-08-19 +* @param[out] *spi +* @param[in] value +* @return void +*/ +static void ak_spi_chipsel(struct spi_device *spi, int value) +{ + struct ak_spi_devstate *cs = spi->controller_state; + struct ak_spi *hw = to_hw(spi); + unsigned int cspol = (spi->mode & SPI_CS_HIGH) ? 1 : 0; + + /* change the chipselect state and the state of the spi engine clock */ + switch (value) { + case BITBANG_CS_INACTIVE: + PDEBUG("BITBANG_CS_INACTIVE\n"); + if(spi->chip_select == AKSPI_ONCHIP_CS) { + cs->spcon = ioread32(hw->regs + AK_SPICON); + cs->spcon &= ~FORCE_CS; + iowrite32(cs->spcon, hw->regs + AK_SPICON); + } + else + ak_spi_set_cs(hw, spi->chip_select, cspol^1); + + udelay(2); + break; + case BITBANG_CS_ACTIVE: + PDEBUG("BITBANG_CS_ACTIVE"); + udelay(2); + + if(spi->chip_select == AKSPI_ONCHIP_CS) { + cs->spcon = ioread32(hw->regs + AK_SPICON); + cs->spcon |= FORCE_CS; //by Shaohua + iowrite32(cs->spcon, hw->regs + AK_SPICON); + } + else + ak_spi_set_cs(hw, spi->chip_select, cspol); + break; + default: + break; + } +} + +/** +* @brief update the device's state +* @author zhou wenyong +* @date 2011-08-19 +* @param[in] *spi +* @param[in] *t +* @return int +*/ +static int ak_spi_update_state(struct spi_device *spi, + struct spi_transfer *t) +{ + struct ak_spi_devstate *cs = spi->controller_state; + unsigned int bpw; + unsigned int hz, typical_hz; + unsigned int div = 0; + unsigned long clk; + + bpw = t ? t->bits_per_word : spi->bits_per_word; + typical_hz = t ? t->speed_hz : spi->max_speed_hz; + + if (!bpw) + bpw = DFT_BIT_PER_WORD; + + if (bpw != DFT_BIT_PER_WORD) { + dev_err(&spi->dev, "invalid bits-per-word (%d)\n", bpw); + return -EINVAL; + } + + /*spi mode change, config again*/ + if (spi->mode != cs->mode) { + cs->spcon &= ~(AK_SPICON_CPHA|AK_SPICON_CPOL); + + if ((spi->mode & SPI_CPHA) == SPI_CPHA) + cs->spcon |= AK_SPICON_CPHA; + + if ((spi->mode & SPI_CPOL) == SPI_CPOL) + cs->spcon |= AK_SPICON_CPOL; + + cs->mode = spi->mode; + } + + if (cs->hz != typical_hz) { + if (!typical_hz) + typical_hz = spi->max_speed_hz; + + clk = ak_get_asic_clk(); + div = clk / (typical_hz*2) - 1; + + if (div > 255) + div = 255; + + if (div < 0) + div = 0; + + hz = clk /((div+1)*2); + + /*when got clock greater than wanted clock, increase divider*/ + if((hz - typical_hz) > 0) + div++; + + printk("pre-scaler=%d (wanted %ldMhz, got %ldMhz)\n", + div, typical_hz/MHz, (clk / (2 * (div + 1)))/MHz); + + cs->hz = hz; + cs->spcon &= ~AK_SPICON_CLKDIV; + cs->spcon |= div << 8; + } + PDEBUG("spi new hz is %u, div is %u(%s).\n", cs->hz, div, div ?"change":"not change"); + return 0; +} + +/** +* @brief setup one transfer +* setup_transfer() changes clock and/or wordsize to match settings +* for this transfer; zeroes restore defaults from spi_device. +* @author zhou wenyong +* @date 2011-08-19 +* @param[in] *spi +* @param[in] *t +* @return int +*/ +static int ak_spi_setupxfer(struct spi_device *spi, + struct spi_transfer *t) +{ + struct ak_spi_devstate *cs = spi->controller_state; + struct ak_spi *hw = to_hw(spi); + int ret=0; + + /*spi device not change and has been initilize, return.*/ + if(likely((cs->initialed == 1) && (spi == hw->curdev))) + return ret; + + cs->spcon = readl(hw->regs + AK_SPICON); + + ret = ak_spi_update_state(spi, t); + if (!ret) + { + /*Add code to fix the issue that the SPICON register + was deranged. Shaohua @2012-3-23 + */ + writel(cs->spcon, hw->regs + AK_SPICON); + printk("ak_spi_setupxfer,con:%08x.\n", cs->spcon); + cs->initialed = 1; + hw->curdev = spi; + } + + return ret; +} + +#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH) + +/** +* @brief ak_spi_setup +* updates the device mode and clocking records used by a +* device's SPI controller; +* @author zhou wenyong +* @date 2011-08-19 +* @param[in] *spi -- Master side proxy +* @return int -- return 0 on success +*/ +static int ak_spi_setup(struct spi_device *spi) +{ + struct ak_spi_devstate *cs = spi->controller_state; + struct ak_spi *hw = to_hw(spi); + int ret; + + printk("ak_spi setup the master.\n"); + + /* allocate settings on the first call */ + if (!cs) { + cs = kzalloc(sizeof(struct ak_spi_devstate), GFP_KERNEL); + if (!cs) { + dev_err(&spi->dev, "no memory for controller state\n"); + return -ENOMEM; + } + + cs->spcon = DFT_CON; + cs->hz = -1; + spi->controller_state = cs; + cs->initialed = 0; + } + + if (spi->mode & ~(hw->pdata->mode_bits)) { + dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n", + spi->mode & ~(hw->pdata->mode_bits)); + return -EINVAL; + } + /* initialise the state from the device */ + ret = ak_spi_update_state(spi, NULL); + if (ret) + return ret; + + spin_lock(&hw->bitbang.lock); + if (!hw->bitbang.busy) { + hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE); + /* need to ndelay for 0.5 clocktick ? */ + } + spin_unlock(&hw->bitbang.lock); + + return 0; +} + +/** +* @brief ak_spi_cleanup +* called on to free memory provided by spi_master +* @author zhou wenyong +* @date 2011-08-19 +* @param[out] *spi +* @return void +*/ +static void ak_spi_cleanup(struct spi_device *spi) +{ + kfree(spi->controller_state); +} + + +/** +* @brief enable the irq mask. +* @author lixinhai +* @date 2013-03-10 +* @param[in] *hw :akspi master dev. +* @param[in] imask: interrupt bit mask. +* @return new mask. +*/ +static inline u32 enable_imask(struct ak_spi *hw, u32 imask) +{ + u32 newmask; + + newmask = readl(hw->regs + AK_SPIINT); + newmask |= imask; + + writel(newmask, hw->regs + AK_SPIINT); + + return newmask; +} + + +/** +* @brief disable the irq mask. +* @author lixinhai +* @date 2013-03-10 +* @param[in] *hw :akspi master dev. +* @param[in] imask: interrupt bit mask. +* @return new mask. +*/ +static inline u32 disable_imask(struct ak_spi *hw, u32 imask) +{ + u32 newmask; + + newmask = readl(hw->regs + AK_SPIINT); + newmask &= ~imask; + + writel(newmask, hw->regs + AK_SPIINT); + + return newmask; +} + + +/** +* @brief configure the master register when start transfer data. +* @author lixinhai +* @date 2013-03-15 +* @param[in] *hw :akspi master dev. +* @param[in] t: a read/write buffer pair. +* @return void +*/ +static void ak_spi_start_txrx(struct ak_spi *hw, struct spi_transfer *t) +{ + u32 reg_value; + enum spi_xfer_dir dir = hw->xfer_dir; + + PDEBUG("the spi transfer mode is %s.\n", (dir == SPI_DIR_TXRX) ? + "txrx" : (dir == SPI_DIR_RX)? "rx":"tx"); + + reg_value = readl(hw->regs + AK_SPICON); + + switch(dir) { + case SPI_DIR_TX: + reg_value &= ~AK_SPICON_TGDM; + reg_value |= AK_SPICON_ARRM; + break; + case SPI_DIR_RX: + reg_value |= AK_SPICON_TGDM; + reg_value &= ~AK_SPICON_ARRM; + break; + case SPI_DIR_TXRX: + reg_value &= ~AK_SPICON_TGDM; + reg_value &= ~AK_SPICON_ARRM; + break; + default: + break; + } + + /*configure the data wire*/ + reg_value &= ~AK_SPICON_WIRE; + reg_value |= AKSPI_DATA_WIRE(t->xfer_mode); + + writel(reg_value, hw->regs + AK_SPICON); +} + + +/** +* @brief configure the master register when stop transfer data. +* @author lixinhai +* @date 2013-03-15 +* @param[in] *hw :akspi master dev. +* @param[in] t: a read/write buffer pair. +* @return void +*/ +static void ak_spi_stop_txrx(struct ak_spi *hw, struct spi_transfer *t) +{ + u32 reg_value; + + reg_value = readl(hw->regs + AK_SPICON); + reg_value &= ~AK_SPICON_WIRE; + writel(reg_value, hw->regs + AK_SPICON); +} + + +/** +* @brief spi write data function by l2dma/l2cpu mode. +* @author lixinhai +* @date 2013-03-15 +* @param[in] *hw :akspi master dev. +* @param[in] buf: transfer data pointer. +* @param[in] count: transfer data length. +* @return transfer success result 0, otherwise result a negative value +*/ +static int spi_dma_write(struct ak_spi *hw, unsigned char *buf, int count) +{ + int ret = 0; + bool flags = false; + u32 val; + + init_completion(&hw->done); + enable_imask(hw, AK_SPIINT_TRANSF); + + val = AK_SPIEXTX_BUFEN|AK_SPIEXTX_DMAEN; + iowrite32(val, hw->regs + AK_SPIEXTX); + iowrite32(count, hw->regs + AK_SPICNT); + + /*use for dma mode: greater than 256 bytes, align 4bytes of buf addr, + align 64 bytes of data count*/ + if((count < 256) || ((unsigned long)buf & 0x3) || (count & (64 - 1))) { + l2_combuf_cpu((unsigned long)buf, hw->l2buf_tid, count, MEM2BUF); + + } else { + memcpy(hw->txbuffer, buf, count); + + //start l2 dma transmit + l2_combuf_dma(hw->txdma_buffer, hw->l2buf_tid, count, MEM2BUF, AK_FALSE); + flags = true; + } + + ret = wait_for_completion_timeout(&hw->done, msecs_to_jiffies(SPI_TRANS_TIMEOUT)); + if(ret <= 0) { + printk("wait for spi transfer interrupt timeout(%s).\n", __func__); + dbg_dumpregs(hw); + ret = -EINVAL; + goto xfer_fail; + } + + if (flags && (l2_combuf_wait_dma_finish(hw->l2buf_tid) == AK_FALSE)) { + printk("%s: l2_combuf_wait_dma_finish failed!\n", __func__); + ret = -EINVAL; + goto xfer_fail; + } + + ret = wait_for_spi_cnt_to_zero(hw, TRANS_TIMEOUT); + if(ret) { + printk("%s: wait_for_spi_cnt_to_zero failed!\n", __func__); + ret = -EINVAL; + goto xfer_fail; + } + + ret = count; +xfer_fail: + //disable l2 dma + iowrite32(0, hw->regs + AK_SPIEXTX); + l2_clr_status(hw->l2buf_tid); + return ret; +} + + +/** +* @brief spi read data function by l2dma/l2cpu mode. +* @author lixinhai +* @date 2013-03-15 +* @param[in] *hw :akspi master dev. +* @param[in] buf: transfer data pointer. +* @param[in] count: transfer data length. +* @return transfer success result 0, otherwise result a negative value +*/ +static int spi_dma_read(struct ak_spi *hw, unsigned char *buf, int count) +{ + int ret = 0; + bool flags = false; + u32 val; + + //prepare spi read + init_completion(&hw->done); + enable_imask(hw, AK_SPIINT_TRANSF); + + val = AK_SPIEXTX_BUFEN|AK_SPIEXTX_DMAEN; + iowrite32(val, hw->regs + AK_SPIEXRX); + iowrite32(count, hw->regs + AK_SPICNT); + + if(count < 256 || ((unsigned long)buf & 0x3) || (count & (64 - 1))) { + l2_combuf_cpu((unsigned long)buf, hw->l2buf_rid, count, BUF2MEM); + } + else { + //start L2 dma + l2_combuf_dma(hw->rxdma_buffer, hw->l2buf_rid, count, BUF2MEM, AK_FALSE); + flags = true; + } + + ret = wait_for_completion_timeout(&hw->done, msecs_to_jiffies(SPI_TRANS_TIMEOUT)); + if(ret <= 0) { + printk("wait for spi transfer interrupt timeout(%s).\n", __func__); + dbg_dumpregs(hw); + ret = -EINVAL; + goto xfer_fail; + } + + //wait L2 dma finish, if need frac dma,start frac dma + if (flags && l2_combuf_wait_dma_finish(hw->l2buf_rid) == AK_FALSE) { + ret = -EINVAL; + goto xfer_fail; + } + + /*wait for spi count register value to zero.*/ + ret = wait_for_spi_cnt_to_zero(hw, TRANS_TIMEOUT); + if(ret) { + printk("%s: wait for spi count to zero failed!\n", __func__); + ret = -EINVAL; + goto xfer_fail; + } + + if(flags == true) { + memcpy(buf, hw->rxbuffer, count); + } + ret = count; +xfer_fail: + //disable l2 dma + iowrite32(0, hw->regs + AK_SPIEXRX); + l2_clr_status(hw->l2buf_rid); + return ret; +} + + +/** +* @brief spi transfer function by l2dma/l2cpu mode. +* @author lixinhai +* @date 2013-03-15 +* @param[in] *hw :akspi master dev. +* @param[in] tx: send data pointer. +* @param[in] rx: receive data pointer. +* @param[in] count: transfer data length. +* @return transfer success result 0, otherwise result a negative value +*/ +static int spi_dma_duplex(struct ak_spi *hw, + unsigned char *tx, unsigned char *rx, int count) +{ + int ret = 0; + bool flags = false; + //dma_addr_t tx_phyaddr, rx_phyaddr; + u32 val; + + init_completion(&hw->done); + enable_imask(hw, AK_SPIINT_TRANSF); + + val = AK_SPIEXTX_BUFEN|AK_SPIEXTX_DMAEN; + iowrite32(val, hw->regs + AK_SPIEXTX); + + val = AK_SPIEXRX_BUFEN|AK_SPIEXRX_DMAEN; + iowrite32(val, hw->regs + AK_SPIEXRX); + + iowrite32(count, hw->regs + AK_SPICNT); + + if((count < 512) || ((unsigned long)tx & 0x3)) { + //printk("write: l2_combuf_cpu\n"); + l2_combuf_cpu((unsigned long)tx, hw->l2buf_tid, count, MEM2BUF); + l2_combuf_cpu((unsigned long)rx, hw->l2buf_rid, count, BUF2MEM); + } + else { + memcpy(hw->txbuffer, tx, count); + //start l2 dma transmit + l2_combuf_dma(hw->txdma_buffer, hw->l2buf_tid, count, MEM2BUF, AK_FALSE); + //start L2 dma + l2_combuf_dma(hw->rxdma_buffer, hw->l2buf_rid, count, BUF2MEM, AK_FALSE); + flags = true; + } + ret = wait_for_completion_timeout(&hw->done, msecs_to_jiffies(SPI_TRANS_TIMEOUT)); + if(ret <= 0) { + printk("wait for spi transfer interrupt timeout(%s).\n", __func__); + dbg_dumpregs(hw); + ret = -EINVAL; + goto xfer_fail; + } + + if (flags && ((AK_FALSE == l2_combuf_wait_dma_finish(hw->l2buf_tid)) || + (AK_FALSE == l2_combuf_wait_dma_finish(hw->l2buf_rid)))) + { + printk("%s: l2_combuf_wait_dma_finish failed!\n", __func__); + ret = -EINVAL; + goto xfer_fail; + } + + ret = wait_for_spi_cnt_to_zero(hw, TRANS_TIMEOUT); + if(ret) { + printk("%s: wait for spi count to zero failed!\n", __func__); + ret = -EINVAL; + goto xfer_fail; + } + + if(flags == true) { + memcpy(rx, hw->rxbuffer, count); + } + ret = 0; + +xfer_fail: + //disable l2 dma + iowrite32(0, hw->regs + AK_SPIEXTX); + iowrite32(0, hw->regs + AK_SPIEXRX); + l2_clr_status(hw->l2buf_tid); + l2_clr_status(hw->l2buf_rid); + return ret; +} + + +/** +* @brief spi transfer function by l2dma/l2cpu mode, actual worker +* spi_dma_read()/spi_dma_write() to be call. +* @author lixinhai +* @date 2013-03-15 +* @param[in] *hw :akspi master dev. +* @param[in] dir: transfer direction. +* @return transfer success result 0, otherwise result a negative value +*/ +static int ak_spi_dma_txrx(struct ak_spi *hw, struct spi_transfer *t) +{ + int ret = 0; + u32 retlen; + u32 count = t->len; + + hw->tx = (unsigned char*)t->tx_buf; + hw->rx = t->rx_buf; + hw->l2buf_tid = hw->l2buf_rid = BUF_NULL; + PDEBUG("start the spi dma transfer.\n"); + + switch(hw->xfer_dir) { + case SPI_DIR_TXRX: + { + //alloc L2 buffer + hw->l2buf_tid = l2_alloc(SPI_L2_TXADDR(hw->master)); + hw->l2buf_rid = l2_alloc(SPI_L2_RXADDR(hw->master)); + + if ((BUF_NULL == hw->l2buf_tid) || (BUF_NULL == hw->l2buf_rid)) + { + printk("%s: l2_alloc failed!\n", __func__); + ret = -EBUSY; + goto txrx_ret; + } + + while(count > 0) { + hw->count = 0; + hw->len = (count > MAX_XFER_LEN) ? MAX_XFER_LEN : count; + + retlen = spi_dma_duplex(hw, hw->tx, hw->rx, hw->len); + if(unlikely(retlen < 0)) { + printk("spi master transfer data error!\n"); + ret = -EBUSY; + goto txrx_ret; + } + hw->tx += retlen; + hw->rx += retlen; + count -= retlen; + } + break; + } + case SPI_DIR_TX: + { + //alloc L2 buffer + hw->l2buf_tid = l2_alloc(SPI_L2_TXADDR(hw->master)); + if (unlikely(BUF_NULL == hw->l2buf_tid)) { + printk("%s: l2_alloc failed!\n", __func__); + return -EBUSY; + } + + while(count > 0) { + hw->count = 0; + hw->len = (count > MAX_XFER_LEN) ? MAX_XFER_LEN : count; + + retlen = spi_dma_write(hw, hw->tx + hw->count, hw->len); + if(unlikely(retlen < 0)) { + printk("spi master read data error!\n"); + ret = -EBUSY; + goto txrx_ret; + } + hw->tx += retlen; + count -= retlen; + } + break; + } + case SPI_DIR_RX: + { + //alloc L2 buffer + hw->l2buf_rid = l2_alloc(SPI_L2_RXADDR(hw->master)); + if (unlikely(BUF_NULL == hw->l2buf_rid)) + { + return -EBUSY; + } + + while(count > 0) { + hw->count = 0; + hw->len = (count > MAX_XFER_LEN) ? MAX_XFER_LEN : count; + + retlen = spi_dma_read(hw, hw->rx, hw->len); + if(unlikely(retlen < 0)) { + printk("spi master read data error!\n"); + ret = -EBUSY; + goto txrx_ret; + } + hw->rx += retlen; + count -= retlen; + } + break; + } + } + PDEBUG("finish the spi dma transfer.\n"); +txrx_ret: + if(hw->l2buf_tid != BUF_NULL) + l2_free(SPI_L2_TXADDR(hw->master)); + if(hw->l2buf_rid != BUF_NULL) + l2_free(SPI_L2_RXADDR(hw->master)); + + return ret ? ret : t->len; +} + + +/** +* @brief spi read/write data function by cpu mode, +* @author lixinhai +* @date 2013-03-15 +* @param[in] *hw :akspi master dev. +* @param[in] t: a read/write buffer pair. +* @return transfer success result 0, otherwise result a negative value +*/ +static int ak_cpu_duplex(struct spi_device *spi, struct spi_transfer *t) +{ + struct ak_spi *hw = to_hw(spi); + u32 tran_4_nbr = hw->len/4; + u32 frac_nbr = hw->len%4; + u32 status, val; + u32 off_set_read = 0, off_set_write = 0; + const u8 *buff_tx; + u8 *buff_rx; + int i = 0, j; + u32 to_read = 0, to_write = 0; //, to = 0; + + hw->tx = (unsigned char*)t->tx_buf; + hw->rx = t->rx_buf; + + buff_tx = hw->tx; + buff_rx = hw->rx; + + PDEBUG("duplex transfer,tran_4_nbr:%u frac_nbr:%u\n", tran_4_nbr, frac_nbr); + + if (hw->len >= MAX_XFER_LEN) + { + printk("Too much to be read and send...\n"); + return -EINVAL; + } + + //set data count, and the the master will rise clk + writel(hw->len, hw->regs + AK_SPICNT); + + while(1) { + //write 4 bytes first, and then read 4 bytes + if (iregs + AK_SPISTA); + if ((status & AK_SPISTA_TXHEMP) == AK_SPISTA_TXHEMP) { + PDEBUG("TX HEMP...\n"); + break; + } else { + if(to_write++ < TRANS_TIMEOUT) + udelay(10); + else { + PDEBUG("master transfer timeout...\n"); + goto SPI_TRANS_FAIL; + } + } + } + iowrite32(*(volatile u32 *)(buff_tx + off_set_write), hw->regs + AK_SPIOUT); + off_set_write += 4; + i++; + } + //write not finished + else if (off_set_write < hw->len) { + PDEBUG("write frac...\n"); + to_write = 0; + val = 0; + if (frac_nbr != 0) { + while(1) { + status = ioread32(hw->regs + AK_SPISTA); + if ((status & AK_SPISTA_TXHEMP) == AK_SPISTA_TXHEMP) + break; + if (to_write++ < TRANS_TIMEOUT) + udelay(10); + else { + printk("SPI master write timeout...\n"); + goto SPI_TRANS_FAIL; + } + } + + for (j=0; jregs + AK_SPIOUT); + off_set_write += frac_nbr; + } + } + + //read + status = ioread32(hw->regs + AK_SPISTA); + + if ((status & AK_SPISTA_TRANSF) == AK_SPISTA_TRANSF) { + if (status & AK_SPISTA_RXFULL) { + val = ioread32(hw->regs + AK_SPIIN); + *(volatile u32 *)(buff_rx + off_set_read) = val; + off_set_read += 4; + + val = ioread32(hw->regs + AK_SPIIN); + *(volatile u32 *)(buff_rx + off_set_read) = val; + off_set_read += 4; + + } else if (status & AK_SPISTA_RXHFULL) { + + val = ioread32(hw->regs + AK_SPIIN); + *(volatile u32 *)(buff_rx + off_set_read) = val; + off_set_read += 4; + } + if (frac_nbr != 0) { + PDEBUG("read frac...\n"); + val = ioread32(hw->regs + AK_SPIIN); + + for (j=0; j> j*8) & 0xff; + PDEBUG("%x ", *(buff_rx+off_set_read+j)); + } + } + break; + } else { + if ( (status & AK_SPISTA_RXHFULL) == AK_SPISTA_RXHFULL) { + val = ioread32(hw->regs + AK_SPIIN); + *(volatile u32 *)(buff_rx + off_set_read) = val; + PDEBUG("rx hfull .. %x\n", val); + PDEBUG("[0]%x [1]%x [2]%x [3]%x\n", buff_rx[0], buff_rx[1], buff_rx[2], buff_rx[3]); + off_set_read += 4; + } else { + if (to_read++ < TRANS_TIMEOUT) + udelay(10); + else { + PDEBUG("master read timeout...\n"); + goto SPI_READ_TIMEOUT; + } + } + } + } + + return hw->len; + +SPI_TRANS_FAIL: +SPI_READ_TIMEOUT: + return off_set_read > off_set_write ? off_set_read: off_set_write; + +} + + +#if defined(SPI_CPU_MODE_USE_INTERRUPT) +static int pio_imasks[SPI_DIR_XFER_NUM] = { + AK_SPIINT_TRANSF | AK_SPIINT_TXHEMP, + AK_SPIINT_TRANSF | AK_SPIINT_RXHFULL, + AK_SPIINT_TXHEMP | AK_SPIINT_RXHFULL, +}; + + +/** +* @brief spi transfer function by cpu mode. +* @author lixinhai +* @date 2013-03-15 +* @param[in] *hw :akspi master dev. +* @param[in] dir: transfer direction. +* @return transfer success result 0, otherwise result a negative value +*/ +static int ak_spi_pio_txrx(struct ak_spi *hw, struct spi_transfer *t) +{ + int ret; + + init_completion(&hw->done); + + writel(hw->len, hw->regs + AK_SPICNT); + enable_imask(hw, pio_imasks[hw->xfer_dir]); + + ret = wait_for_completion_timeout(&hw->done, msecs_to_jiffies(SPI_TRANS_TIMEOUT)); + if(ret <= 0) { + printk("wait for spi transfer interrupt timeout(%s).\n", __func__); + dbg_dumpregs(hw); + return -EINVAL; + } + + return hw->count; +} +#else + + +/** +* @brief ak37_spi_writeCPU +* send message function with CPU mode and polling mode +* @author zhou wenyong +* @date 2011-08-19 +* @param[in] *spi +* @param[in] *t +* @return int +*/ +static int spi_pio_write(struct ak_spi *hw, unsigned char *buf, int count) +{ + u32 status; + u32 to = 0; + + PDEBUG("ak spi write by cpu mode\n"); + if (count > 64*1024) + { + printk("too much to be send...\n"); + return -EINVAL; + } + //set data count, and the the master will rise clk + writel(count, hw->regs + AK_SPICNT); + + while(hw_remain_datalen(hw) > 0) { + status = readl(hw->regs + AK_SPISTA); + if ((status & AK_SPISTA_TXHEMP) == AK_SPISTA_TXHEMP) { + writel(hw_txdword(hw), hw->regs + AK_SPIOUT); + }else { + if (to++ > 10 * 1000000) { + printk("master write data timeout.\n"); + return hw->count; + } + } + } + + //wait transfer finish + while(1) { + status = readl(hw->regs + AK_SPISTA); + if ((status & AK_SPISTA_TRANSF) == AK_SPISTA_TRANSF) + break; + + if (to++ > 10 * 1000000) { + printk("wait for write data finish timeout..\n"); + return hw->count; + } + } + + if (hw_remain_datalen(hw) > 0) + printk("write wasn't finished.\n"); + + return hw->count; +} + +/** +* @brief spi_pio_read +* receiving message function with CPU mode and polling mode +* @author zhou wenyong +* @date 2011-08-19 +* @param[in] *spi +* @param[in] *t +* @return int +*/ +static int spi_pio_read(struct ak_spi *hw, unsigned char *buf, int count) +{ + u32 status; + u32 to=0; + + PDEBUG("ak spi read by cpu mode\n"); + if (count >= 64*1024) + { + printk("too much to be read...\n"); + return -EINVAL; + } + + //set data count, and the the master will rise clk + writel(count, hw->regs + AK_SPICNT); + + while(1) { + status = readl(hw->regs + AK_SPISTA); + + if((status & AK_SPISTA_TRANSF) == AK_SPISTA_TRANSF) + { + if(status & AK_SPISTA_RXFULL) { + hw_rxdword(hw, readl(hw->regs + AK_SPIIN)); + hw_rxdword(hw, readl(hw->regs + AK_SPIIN)); + }else if (status & AK_SPISTA_RXHFULL) { + hw_rxdword(hw, readl(hw->regs + AK_SPIIN)); + } + + if (hw_remain_datalen(hw) > 0) { + hw_rxdword(hw, readl(hw->regs + AK_SPIIN)); + } + break; + } else { + if((status & AK_SPISTA_RXHFULL) == AK_SPISTA_RXHFULL) { + hw_rxdword(hw, readl(hw->regs + AK_SPIIN)); + } + else { + if (to++ > 10 * 1000000) { + PDEBUG("master read timeout.\n"); + return hw->count; + } + } + } + } + if (hw_remain_datalen(hw) > 0) + printk("read wasn't finished.\n"); + + return hw->count; +} + + +/** +* @brief spi transfer function by cpu mode. +* @author lixinhai +* @date 2013-03-15 +* @param[in] *hw :akspi master dev. +* @param[in] dir: transfer direction. +* @return transfer success result 0, otherwise result a negative value +*/ +static int ak_spi_pio_txrx(struct ak_spi *hw, struct spi_transfer *t) +{ + int retlen = -1; + u32 count = t->len; + + hw->tx = (unsigned char*)t->tx_buf; + hw->rx = t->rx_buf; + PDEBUG("start the spi dma transfer.\n"); + + switch(hw->xfer_dir) { + case SPI_DIR_TX: + { + while(count > 0) { + hw->count = 0; + hw->len = (count > MAX_XFER_LEN) ? MAX_XFER_LEN : count; + + retlen = spi_pio_write(hw, hw->tx, hw->len); + if(unlikely(retlen < 0)) { + printk("spi master transfer data error!\n"); + goto txrx_ret; + } + hw->tx += retlen; + count -= retlen; + + } + break; + } + case SPI_DIR_RX: + { + while(count > 0) { + hw->count = 0; + hw->len = (count > MAX_XFER_LEN) ? MAX_XFER_LEN : count; + + retlen = spi_pio_read(hw, hw->rx, hw->len); + if(unlikely(retlen < 0)) { + printk("spi master transfer data error!\n"); + goto txrx_ret; + } + hw->rx += retlen; + count -= retlen; + } + break; + } + } + PDEBUG("finish the spi dma transfer.\n"); + +txrx_ret: + return (retlen<0) ? retlen : t->len; +} + +#endif + +/** +* @brief transfer a message +* call proper function to complete the transefer +* @author zhou wenyong +* @date 2011-08-19 +* @param[in] *spi +* @param[in] *t +* @return int +*/ +static int ak_spi_txrx(struct spi_device *spi, struct spi_transfer *t) +{ + int ret; + struct ak_spi *hw = to_hw(spi); + + TMPDEBUG("txrx: tx %p, rx %p, len %d\n", + t->tx_buf, t->rx_buf, t->len); + + dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", + t->tx_buf, t->rx_buf, t->len); + + hw->xfer_mode = t->len < 256 ? AKSPI_XFER_MODE_CPU : AKSPI_XFER_MODE_DMA; + + hw->xfer_dir = (t->tx_buf && t->rx_buf) ? SPI_DIR_TXRX : + t->tx_buf ? SPI_DIR_TX : SPI_DIR_RX; + + ak_spi_start_txrx(hw, t); + + if(hw->xfer_dir == SPI_DIR_TXRX) { + //printk(KERN_WARNING "this spi master driver no support duplex now!\n"); + ret = ak_cpu_duplex(spi, t); + return ret; + } + + if(ak_spi_use_dma(hw)) { + ret = ak_spi_dma_txrx(hw, t); + } else { + ret = ak_spi_pio_txrx(hw, t); + } + + ak_spi_stop_txrx(hw, t); + + return ret; +} + +/** +* @brief ak_spi_irq +* TODO: used by interrupt mode +* @author zhou wenyong +* @date 2011-08-19 +* @param[in] irq +* @param[out] *dev +* @return irqreturn_t +*/ +static irqreturn_t ak_spi_irq(int irq, void *dev) +{ + struct ak_spi *hw = dev; + unsigned int status; + + status = readl(hw->regs + AK_SPISTA); + + PDEBUG("spi interrupt: status register value is %08x\n", status); + + if(ak_spi_use_dma(hw)) { + if((status & AK_SPISTA_TRANSF) == AK_SPISTA_TRANSF ) { + PDEBUG("spi transfer data have been finish.\n"); + + //printk("--->status:%d, cnt:%d\n", status, readl(hw->regs + AK_SPICNT)); + disable_imask(hw, AK_SPIINT_TRANSF); + complete(&hw->done); + } + } else { + switch(hw->xfer_dir) { + case SPI_DIR_RX: + if(status & (AK_SPISTA_RXHFULL | AK_SPISTA_TRANSF)) { + PDEBUG("spi recv buffer half full or data transfer finish.\n"); + + if(status & (AK_SPISTA_RXFULL|AK_SPISTA_TRANSF)) + hw_rxdword(hw, readl(hw->regs + AK_SPIIN)); + hw_rxdword(hw, readl(hw->regs + AK_SPIIN)); + + if (hw->count >= hw->len) { + disable_imask(hw, AK_SPIINT_RXHFULL|AK_SPIINT_TRANSF); + complete(&hw->done); + } + } + break; + case SPI_DIR_TX: + if(status & (AK_SPISTA_TXHEMP | AK_SPISTA_TRANSF)) { + PDEBUG("spi send buffer half empty or data transfer finish.\n"); + + if (hw->count < hw->len) { + if((status & AK_SPISTA_TXEMP) && ((hw->count - hw->len)>4)) + writel(hw_txdword(hw), hw->regs + AK_SPIOUT); + + writel(hw_txdword(hw), hw->regs + AK_SPIOUT); + } else { + disable_imask(hw, AK_SPIINT_TXHEMP|AK_SPIINT_TRANSF); + complete(&hw->done); + } + } + break; + case SPI_DIR_TXRX : + default: + BUG(); + break; + } + } + + return IRQ_HANDLED; +} + + +/** +* @brief ak_spi_initialsetup +* set up gpio and spi master +* @author zhou wenyong +* @date 2011-08-19 +* @param[out] *hw +* @return void +*/ +static void ak_spi_initialsetup(struct ak_spi *hw) +{ + /* for the moment, permanently enable the clock */ + TMPDEBUG("Entering %s\n", __FUNCTION__); + + BUG_ON(hw->master && hw->master->bus_num >= AKSPI_MAX_BUS_NUM); + + ak_soft_reset(SPI_RESET_NUM(hw->master)); + clk_enable(hw->clk); + + /* program defaults into the registers */ + writel(DFT_DIV<<8 | DFT_CON | (1<<1),hw->regs + AK_SPICON); + + writel(0, hw->regs + AK_SPIINT); + + ak_spi_gpio_setup(hw, 1); + + printk("akpi regs: SPICON:%08x, SPISTA:%08x, SPIINT:%08x.\n", + ioread32(hw->regs + AK_SPICON), + ioread32(hw->regs + AK_SPISTA), + ioread32(hw->regs + AK_SPIINT)); + +} + +/** +* @brief ak_spi_probe +* @author zhou wenyong +* @date 2011-08-19 +* @param[out] *pdev +* @return int __init +*/ +static int __init ak_spi_probe(struct platform_device *pdev) +{ + struct ak_spi_info *pdata; + struct ak_spi *hw; + struct spi_master *master; + struct resource *res; + int err = 0; + + master = spi_alloc_master(&pdev->dev, sizeof(struct ak_spi)); + if (master == NULL) + { + dev_err(&pdev->dev, "No memory for spi_master\n"); + err = -ENOMEM; + goto err_nomem; + } + + hw = spi_master_get_devdata(master); + memset(hw, 0, sizeof(struct ak_spi)); + + hw->master = spi_master_get(master); + hw->pdata = pdata = pdev->dev.platform_data; + hw->dev = &pdev->dev; + hw->xfer_mode = pdata->xfer_mode; + + if (pdata == NULL) + { + dev_err(&pdev->dev, "No platform data supplied\n"); + err = -ENOENT; + goto err_no_pdata; + } + + platform_set_drvdata(pdev, hw); + init_completion(&hw->done); + + /* setup the master state. */ + /* the spi->mode bits understood by this driver: */ + master->mode_bits = hw->pdata->mode_bits; + + master->num_chipselect = hw->pdata->num_cs; + master->bus_num = pdata->bus_num; + + /* setup the state for the bitbang driver */ + + hw->bitbang.master = hw->master; + hw->bitbang.setup_transfer = ak_spi_setupxfer; + hw->bitbang.chipselect = ak_spi_chipsel; + hw->bitbang.txrx_bufs = ak_spi_txrx; + + hw->master->setup = ak_spi_setup; + hw->master->cleanup = ak_spi_cleanup; + + dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang); + + /* find and map our resources */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); + err = -ENOENT; + goto err_no_iores; + } + + hw->ioarea = request_mem_region(res->start, resource_size(res), + pdev->name); + + if (hw->ioarea == NULL) { + dev_err(&pdev->dev, "Cannot reserve region\n"); + err = -ENXIO; + goto err_no_iores; + } + + hw->regs = ioremap(res->start, resource_size(res)); + if (hw->regs == NULL) { + dev_err(&pdev->dev, "Cannot map IO\n"); + err = -ENXIO; + goto err_no_iomap; + } + + hw->irq = platform_get_irq(pdev, 0); + if (hw->irq < 0) { + dev_err(&pdev->dev, "No IRQ specified\n"); + err = -ENOENT; + goto err_no_irq; + } + + err = request_irq(hw->irq, ak_spi_irq, 0, pdev->name, hw); + if (err) { + dev_err(&pdev->dev, "Cannot claim IRQ\n"); + goto err_no_irq; + } + + hw->clk = clk_get(&pdev->dev, pdata->clk_name); + if (IS_ERR(hw->clk)) { + dev_err(&pdev->dev, "No clock for device\n"); + err = PTR_ERR(hw->clk); + goto err_no_clk; + } + PDEBUG("%s: %luMhz \n", hw->clk->name, clk_get_rate(hw->clk)/MHz); + + /* spi controller need a DMA memory for L2 transfer.*/ + hw->txbuffer = dma_alloc_coherent(NULL, SPI_DMA_MAX_LEN, + &hw->txdma_buffer, GFP_KERNEL); + if (!hw->txbuffer) + { + dev_err(&pdev->dev, "Failed to hw->txdma_buffer\n"); + return -ENOMEM; + } + + hw->rxbuffer = dma_alloc_coherent(NULL, SPI_DMA_MAX_LEN, + &hw->rxdma_buffer, GFP_KERNEL); + if (!hw->rxbuffer) + { + dev_err(&pdev->dev, "Failed to hw->rxdma_buffer\n"); + return -ENOMEM; + } + + /* setup any gpio we can */ + + ak_spi_initialsetup(hw); + + /* register our spi controller */ + + err = spi_bitbang_start(&hw->bitbang); + if (err) { + dev_err(&pdev->dev, "Failed to register SPI master\n"); + goto err_register; + } + + printk("akspi master initialize success, use for %s mode.\n", + ak_spi_use_dma(hw)?"DMA":"PIO"); + + return 0; + + err_register: + //if (hw->set_cs == ak_spi_gpiocs) + // gpio_free(pdata->pin_cs); + + clk_disable(hw->clk); + clk_put(hw->clk); + + err_no_clk: + free_irq(hw->irq, hw); + + err_no_irq: + iounmap(hw->regs); + + err_no_iomap: + release_resource(hw->ioarea); + + err_no_iores: + err_no_pdata: + spi_master_put(hw->master); + + err_nomem: + return err; +} + +/** +* @brief ak_spi_remove +* free allocated resources while remove +* @author zhou wenyong +* @date 2011-08-19 +* @param[out] *dev +* @return int __exit +*/ +static int __exit ak_spi_remove(struct platform_device *dev) +{ + struct ak_spi *hw = platform_get_drvdata(dev); + + platform_set_drvdata(dev, NULL); + + spi_unregister_master(hw->master); + + clk_disable(hw->clk); + clk_put(hw->clk); + + free_irq(hw->irq, hw); + iounmap(hw->regs); + + release_resource(hw->ioarea); + + spi_master_put(hw->master); + return 0; +} + + +#ifdef CONFIG_PM + +/** +* @brief ak_spi_suspend +* suspend, disable the clock to the SPI +* @author zhou wenyong +* @date 2011-08-19 +* @param[in] *dev +* @return int +*/ +static int ak_spi_suspend(struct device *dev) +{ + struct ak_spi *hw = platform_get_drvdata(to_platform_device(dev)); + + ak_spi_gpio_setup(hw, 0); + + clk_disable(hw->clk); + return 0; +} + +/** +* @brief ak_spi_resume +* resume, initialize the spi master +* @author zhou wenyong +* @date 2011-08-19 +* @param[out] *dev +* @return int +*/ +static int ak_spi_resume(struct device *dev) +{ + struct ak_spi *hw = platform_get_drvdata(to_platform_device(dev)); + + ak_spi_initialsetup(hw); + return 0; +} + +static struct dev_pm_ops ak_spi_pmops = { + .suspend = ak_spi_suspend, + .resume = ak_spi_resume, +}; + +#define AK_SPI_PMOPS &ak_spi_pmops +#else +#define AK_SPI_PMOPS NULL +#endif /* CONFIG_PM */ + +MODULE_ALIAS("platform:ak-spi"); +static struct platform_driver ak_spi_driver = { + .remove = __exit_p(ak_spi_remove), + .driver = { + .name = "ak-spi", + .owner = THIS_MODULE, + .pm = AK_SPI_PMOPS, + }, +}; + +/** +* @brief ak_spi_init +* init, register this driver +* @author zhou wenyong +* @date 2011-08-19 +* @return int __init +*/ +static int __init ak_spi_init(void) +{ + printk("AK SPI Driver, (c) 2012 ANYKA\n"); + return platform_driver_probe(&ak_spi_driver, ak_spi_probe); +} + +/** +* @brief ak_spi_exit +* exit, unregister this driver +* @author zhou wenyong +* @date 2011-08-19 +* @return void __exit +*/ +static void __exit ak_spi_exit(void) +{ + platform_driver_unregister(&ak_spi_driver); +} + +module_init(ak_spi_init); +module_exit(ak_spi_exit); + +MODULE_DESCRIPTION("AK On-Chip SPI Driver"); +MODULE_AUTHOR("ANYKA"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c index aef59b1a..b6f31bc1 100644 --- a/drivers/spi/spi-bitbang.c +++ b/drivers/spi/spi-bitbang.c @@ -348,8 +348,6 @@ static void bitbang_work(struct work_struct *work) if (!cs_change) continue; - if (t->transfer_list.next == &m->transfers) - break; /* sometimes a short mid-message deselect of the chip * may be needed to terminate a mode or command @@ -357,6 +355,9 @@ static void bitbang_work(struct work_struct *work) ndelay(nsecs); bitbang->chipselect(spi, BITBANG_CS_INACTIVE); ndelay(nsecs); + + if (t->transfer_list.next == &m->transfers) + break; } m->status = status; diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index eb1dee26..43d17c27 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -38,6 +38,20 @@ config ANDROID_RAM_CONSOLE select ANDROID_PERSISTENT_RAM default n +config PERSISTENT_TRACER + bool "Persistent function tracer" + depends on HAVE_FUNCTION_TRACER + select FUNCTION_TRACER + select ANDROID_PERSISTENT_RAM + help + persistent_trace traces function calls into a persistent ram + buffer that can be decoded and dumped after reboot through + /sys/kernel/debug/persistent_trace. It can be used to + determine what function was last called before a reset or + panic. + + If unsure, say N. + config ANDROID_TIMED_OUTPUT bool "Timed output class driver" default y @@ -53,33 +67,25 @@ config ANDROID_LOW_MEMORY_KILLER ---help--- Register processes to be killed when memory is low +config ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES + bool "Android Low Memory Killer: detect oom_adj values" + depends on ANDROID_LOW_MEMORY_KILLER + default y + ---help--- + Detect oom_adj values written to + /sys/module/lowmemorykiller/parameters/adj and convert them + to oom_score_adj values. + source "drivers/staging/android/switch/Kconfig" -config ANDROID_INTF_ALARM +config ANDROID_INTF_ALARM_DEV bool "Android alarm driver" depends on RTC_CLASS default n help Provides non-wakeup and rtc backed wakeup alarms based on rtc or elapsed realtime, and a non-wakeup alarm on the monotonic clock. - Also provides an interface to set the wall time which must be used - for elapsed realtime to work. - -config ANDROID_INTF_ALARM_DEV - bool "Android alarm device" - depends on ANDROID_INTF_ALARM - default y - help - Exports the alarm interface to user-space. - -config ANDROID_ALARM_OLDDRV_COMPAT - bool "Android Alarm compatability with old drivers" - depends on ANDROID_INTF_ALARM - default n - help - Provides preprocessor alias to aid compatability with - older out-of-tree drivers that use the Android Alarm - in-kernel API. This will be removed eventually. + Also exports the alarm interface to user-space. endif # if ANDROID diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile index 9b6c9ed9..8769e325 100644 --- a/drivers/staging/android/Makefile +++ b/drivers/staging/android/Makefile @@ -1,3 +1,5 @@ +ccflags-y += -I$(src) # needed for trace events + obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o obj-$(CONFIG_ASHMEM) += ashmem.o obj-$(CONFIG_ANDROID_LOGGER) += logger.o @@ -7,5 +9,7 @@ obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o obj-$(CONFIG_ANDROID_SWITCH) += switch/ -obj-$(CONFIG_ANDROID_INTF_ALARM) += alarm.o obj-$(CONFIG_ANDROID_INTF_ALARM_DEV) += alarm-dev.o +obj-$(CONFIG_PERSISTENT_TRACER) += trace_persistent.o + +CFLAGS_REMOVE_trace_persistent.o = -pg diff --git a/drivers/staging/android/alarm-dev.c b/drivers/staging/android/alarm-dev.c index 03efb34c..e001fe58 100644 --- a/drivers/staging/android/alarm-dev.c +++ b/drivers/staging/android/alarm-dev.c @@ -22,24 +22,14 @@ #include #include #include +#include +#include #include "android_alarm.h" -/* XXX - Hack out wakelocks, while they are out of tree */ -struct wake_lock { - int i; -}; -#define wake_lock(x) -#define wake_lock_timeout(x, y) -#define wake_unlock(x) -#define WAKE_LOCK_SUSPEND 0 -#define wake_lock_init(x, y, z) ((x)->i = 1) -#define wake_lock_destroy(x) - #define ANDROID_ALARM_PRINT_INFO (1U << 0) #define ANDROID_ALARM_PRINT_IO (1U << 1) #define ANDROID_ALARM_PRINT_INT (1U << 2) - static int debug_mask = ANDROID_ALARM_PRINT_INFO; module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); @@ -66,7 +56,53 @@ static uint32_t alarm_pending; static uint32_t alarm_enabled; static uint32_t wait_pending; -static struct android_alarm alarms[ANDROID_ALARM_TYPE_COUNT]; +struct devalarm { + union { + struct hrtimer hrt; + struct alarm alrm; + } u; + enum android_alarm_type type; +}; + +static struct devalarm alarms[ANDROID_ALARM_TYPE_COUNT]; + + +static int is_wakeup(enum android_alarm_type type) +{ + if (type == ANDROID_ALARM_RTC_WAKEUP || + type == ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP) + return 1; + return 0; +} + + +static void devalarm_start(struct devalarm *alrm, ktime_t exp) +{ + if (is_wakeup(alrm->type)) + alarm_start(&alrm->u.alrm, exp); + else + hrtimer_start(&alrm->u.hrt, exp, HRTIMER_MODE_ABS); +} + + +static int devalarm_try_to_cancel(struct devalarm *alrm) +{ + int ret; + if (is_wakeup(alrm->type)) + ret = alarm_try_to_cancel(&alrm->u.alrm); + else + ret = hrtimer_try_to_cancel(&alrm->u.hrt); + return ret; +} + +static void devalarm_cancel(struct devalarm *alrm) +{ + if (is_wakeup(alrm->type)) + alarm_cancel(&alrm->u.alrm); + else + hrtimer_cancel(&alrm->u.hrt); +} + static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -75,6 +111,8 @@ static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) struct timespec new_alarm_time; struct timespec new_rtc_time; struct timespec tmp_time; + struct rtc_time new_rtc_tm; + struct rtc_device *rtc_dev; enum android_alarm_type alarm_type = ANDROID_ALARM_IOCTL_TO_TYPE(cmd); uint32_t alarm_type_mask = 1U << alarm_type; @@ -101,7 +139,7 @@ static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case ANDROID_ALARM_CLEAR(0): spin_lock_irqsave(&alarm_slock, flags); pr_alarm(IO, "alarm %d clear\n", alarm_type); - android_alarm_try_to_cancel(&alarms[alarm_type]); + devalarm_try_to_cancel(&alarms[alarm_type]); if (alarm_pending) { alarm_pending &= ~alarm_type_mask; if (!alarm_pending && !wait_pending) @@ -132,8 +170,7 @@ from_old_alarm_set: pr_alarm(IO, "alarm %d set %ld.%09ld\n", alarm_type, new_alarm_time.tv_sec, new_alarm_time.tv_nsec); alarm_enabled |= alarm_type_mask; - android_alarm_start_range(&alarms[alarm_type], - timespec_to_ktime(new_alarm_time), + devalarm_start(&alarms[alarm_type], timespec_to_ktime(new_alarm_time)); spin_unlock_irqrestore(&alarm_slock, flags); if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_SET_AND_WAIT(0) @@ -163,7 +200,13 @@ from_old_alarm_set: rv = -EFAULT; goto err1; } - rv = android_alarm_set_rtc(new_rtc_time); + rtc_time_to_tm(new_rtc_time.tv_sec, &new_rtc_tm); + rtc_dev = alarmtimer_get_rtcdev(); + rv = do_settimeofday(&new_rtc_time); + if (rv < 0) + goto err1; + if (rtc_dev) + rv = rtc_set_time(rtc_dev, &new_rtc_tm); spin_lock_irqsave(&alarm_slock, flags); alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK; wake_up(&alarm_wait_queue); @@ -179,8 +222,7 @@ from_old_alarm_set: break; case ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP: case ANDROID_ALARM_ELAPSED_REALTIME: - tmp_time = - ktime_to_timespec(alarm_get_elapsed_realtime()); + get_monotonic_boottime(&tmp_time); break; case ANDROID_ALARM_TYPE_COUNT: case ANDROID_ALARM_SYSTEMTIME: @@ -224,7 +266,7 @@ static int alarm_release(struct inode *inode, struct file *file) alarm_enabled &= ~alarm_type_mask; } spin_unlock_irqrestore(&alarm_slock, flags); - android_alarm_cancel(&alarms[i]); + devalarm_cancel(&alarms[i]); spin_lock_irqsave(&alarm_slock, flags); } if (alarm_pending | wait_pending) { @@ -241,12 +283,12 @@ static int alarm_release(struct inode *inode, struct file *file) return 0; } -static void alarm_triggered(struct android_alarm *alarm) +static void devalarm_triggered(struct devalarm *alarm) { unsigned long flags; uint32_t alarm_type_mask = 1U << alarm->type; - pr_alarm(INT, "alarm_triggered type %d\n", alarm->type); + pr_alarm(INT, "devalarm_triggered type %d\n", alarm->type); spin_lock_irqsave(&alarm_slock, flags); if (alarm_enabled & alarm_type_mask) { wake_lock_timeout(&alarm_wake_lock, 5 * HZ); @@ -257,6 +299,25 @@ static void alarm_triggered(struct android_alarm *alarm) spin_unlock_irqrestore(&alarm_slock, flags); } + +static enum hrtimer_restart devalarm_hrthandler(struct hrtimer *hrt) +{ + struct devalarm *devalrm = container_of(hrt, struct devalarm, u.hrt); + + devalarm_triggered(devalrm); + return HRTIMER_NORESTART; +} + +static enum alarmtimer_restart devalarm_alarmhandler(struct alarm *alrm, + ktime_t now) +{ + struct devalarm *devalrm = container_of(alrm, struct devalarm, u.alrm); + + devalarm_triggered(devalrm); + return ALARMTIMER_NORESTART; +} + + static const struct file_operations alarm_fops = { .owner = THIS_MODULE, .unlocked_ioctl = alarm_ioctl, @@ -279,8 +340,23 @@ static int __init alarm_dev_init(void) if (err) return err; - for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) - android_alarm_init(&alarms[i], i, alarm_triggered); + alarm_init(&alarms[ANDROID_ALARM_RTC_WAKEUP].u.alrm, + ALARM_REALTIME, devalarm_alarmhandler); + hrtimer_init(&alarms[ANDROID_ALARM_RTC].u.hrt, + CLOCK_REALTIME, HRTIMER_MODE_ABS); + alarm_init(&alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].u.alrm, + ALARM_BOOTTIME, devalarm_alarmhandler); + hrtimer_init(&alarms[ANDROID_ALARM_ELAPSED_REALTIME].u.hrt, + CLOCK_BOOTTIME, HRTIMER_MODE_ABS); + hrtimer_init(&alarms[ANDROID_ALARM_SYSTEMTIME].u.hrt, + CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + + for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) { + alarms[i].type = i; + if (!is_wakeup(i)) + alarms[i].u.hrt.function = devalarm_hrthandler; + } + wake_lock_init(&alarm_wake_lock, WAKE_LOCK_SUSPEND, "alarm"); return 0; diff --git a/drivers/staging/android/android_alarm.h b/drivers/staging/android/android_alarm.h index 6eecbde2..d0cafd63 100644 --- a/drivers/staging/android/android_alarm.h +++ b/drivers/staging/android/android_alarm.h @@ -33,65 +33,6 @@ enum android_alarm_type { /* ANDROID_ALARM_TIME_CHANGE = 16 */ }; -#ifdef __KERNEL__ - -#include -#include - -/* - * The alarm interface is similar to the hrtimer interface but adds support - * for wakeup from suspend. It also adds an elapsed realtime clock that can - * be used for periodic timers that need to keep runing while the system is - * suspended and not be disrupted when the wall time is set. - */ - -/** - * struct alarm - the basic alarm structure - * @node: red black tree node for time ordered insertion - * @type: alarm type. rtc/elapsed-realtime/systemtime, wakeup/non-wakeup. - * @softexpires: the absolute earliest expiry time of the alarm. - * @expires: the absolute expiry time. - * @function: alarm expiry callback function - * - * The alarm structure must be initialized by alarm_init() - * - */ - -struct android_alarm { - struct rb_node node; - enum android_alarm_type type; - ktime_t softexpires; - ktime_t expires; - void (*function)(struct android_alarm *); -}; - -void android_alarm_init(struct android_alarm *alarm, - enum android_alarm_type type, void (*function)(struct android_alarm *)); -void android_alarm_start_range(struct android_alarm *alarm, ktime_t start, - ktime_t end); -int android_alarm_try_to_cancel(struct android_alarm *alarm); -int android_alarm_cancel(struct android_alarm *alarm); -ktime_t alarm_get_elapsed_realtime(void); - -/* set rtc while preserving elapsed realtime */ -int android_alarm_set_rtc(const struct timespec ts); - -#ifdef CONFIG_ANDROID_ALARM_OLDDRV_COMPAT -/* - * Some older drivers depend on the old API, - * so provide compatability macros for now. - */ -#define alarm android_alarm -#define alarm_init(x, y, z) android_alarm_init(x, y, z) -#define alarm_start_range(x, y, z) android_alarm_start_range(x, y, z) -#define alarm_try_to_cancel(x) android_alarm_try_to_cancel(x) -#define alarm_cancel(x) android_alarm_cancel(x) -#define alarm_set_rtc(x) android_alarm_set_rtc(x) -#endif - - -#endif - enum android_alarm_return_flags { ANDROID_ALARM_RTC_WAKEUP_MASK = 1U << ANDROID_ALARM_RTC_WAKEUP, ANDROID_ALARM_RTC_MASK = 1U << ANDROID_ALARM_RTC, diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c index 9f1f27e7..bda20bf1 100644 --- a/drivers/staging/android/ashmem.c +++ b/drivers/staging/android/ashmem.c @@ -314,21 +314,13 @@ static int ashmem_mmap(struct file *file, struct vm_area_struct *vma) } get_file(asma->file); - /* - * XXX - Reworked to use shmem_zero_setup() instead of - * shmem_set_file while we're in staging. -jstultz - */ - if (vma->vm_flags & VM_SHARED) { - ret = shmem_zero_setup(vma); - if (ret) { - fput(asma->file); - goto out; - } + if (vma->vm_flags & VM_SHARED) + shmem_set_file(vma, asma->file); + else { + if (vma->vm_file) + fput(vma->vm_file); + vma->vm_file = asma->file; } - - if (vma->vm_file) - fput(vma->vm_file); - vma->vm_file = asma->file; vma->vm_flags |= VM_CAN_NONLINEAR; out: diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index 223639a7..a7a5f2ae 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -33,10 +33,12 @@ #include #include #include +#include #include "binder.h" +#include "binder_trace.h" -static DEFINE_MUTEX(binder_lock); +static DEFINE_MUTEX(binder_main_lock); static DEFINE_MUTEX(binder_deferred_lock); static DEFINE_MUTEX(binder_mmap_lock); @@ -499,6 +501,19 @@ out_unlock: return -EBADF; } +static inline void binder_lock(const char *tag) +{ + trace_binder_lock(tag); + mutex_lock(&binder_main_lock); + trace_binder_locked(tag); +} + +static inline void binder_unlock(const char *tag) +{ + trace_binder_unlock(tag); + mutex_unlock(&binder_main_lock); +} + static void binder_set_nice(long nice) { long min_nice; @@ -625,6 +640,8 @@ static int binder_update_page_range(struct binder_proc *proc, int allocate, if (end <= start) return 0; + trace_binder_update_page_range(proc, allocate, start, end); + if (vma) mm = NULL; else @@ -1473,6 +1490,10 @@ static void binder_transaction(struct binder_proc *proc, return_error = BR_DEAD_REPLY; goto err_dead_binder; } + if (security_binder_transaction(proc->tsk, target_proc->tsk) < 0) { + return_error = BR_FAILED_REPLY; + goto err_invalid_target_handle; + } if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) { struct binder_transaction *tmp; tmp = thread->transaction_stack; @@ -1549,6 +1570,9 @@ static void binder_transaction(struct binder_proc *proc, t->code = tr->code; t->flags = tr->flags; t->priority = task_nice(current); + + trace_binder_transaction(reply, t, target_node); + t->buffer = binder_alloc_buf(target_proc, tr->data_size, tr->offsets_size, !reply && (t->flags & TF_ONE_WAY)); if (t->buffer == NULL) { @@ -1559,6 +1583,7 @@ static void binder_transaction(struct binder_proc *proc, t->buffer->debug_id = t->debug_id; t->buffer->transaction = t; t->buffer->target_node = target_node; + trace_binder_transaction_alloc_buf(t->buffer); if (target_node) binder_inc_node(target_node, 1, 0, NULL); @@ -1618,6 +1643,10 @@ static void binder_transaction(struct binder_proc *proc, fp->cookie, node->cookie); goto err_binder_get_ref_for_node_failed; } + if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) { + return_error = BR_FAILED_REPLY; + goto err_binder_get_ref_for_node_failed; + } ref = binder_get_ref_for_node(target_proc, node); if (ref == NULL) { return_error = BR_FAILED_REPLY; @@ -1631,6 +1660,7 @@ static void binder_transaction(struct binder_proc *proc, binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE, &thread->todo); + trace_binder_transaction_node_to_ref(t, node, ref); binder_debug(BINDER_DEBUG_TRANSACTION, " node %d u%p -> ref %d desc %d\n", node->debug_id, node->ptr, ref->debug_id, @@ -1647,6 +1677,10 @@ static void binder_transaction(struct binder_proc *proc, return_error = BR_FAILED_REPLY; goto err_binder_get_ref_failed; } + if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) { + return_error = BR_FAILED_REPLY; + goto err_binder_get_ref_failed; + } if (ref->node->proc == target_proc) { if (fp->type == BINDER_TYPE_HANDLE) fp->type = BINDER_TYPE_BINDER; @@ -1655,6 +1689,7 @@ static void binder_transaction(struct binder_proc *proc, fp->binder = ref->node->ptr; fp->cookie = ref->node->cookie; binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL); + trace_binder_transaction_ref_to_node(t, ref); binder_debug(BINDER_DEBUG_TRANSACTION, " ref %d desc %d -> node %d u%p\n", ref->debug_id, ref->desc, ref->node->debug_id, @@ -1668,6 +1703,8 @@ static void binder_transaction(struct binder_proc *proc, } fp->handle = new_ref->desc; binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL); + trace_binder_transaction_ref_to_ref(t, ref, + new_ref); binder_debug(BINDER_DEBUG_TRANSACTION, " ref %d desc %d -> ref %d desc %d (node %d)\n", ref->debug_id, ref->desc, new_ref->debug_id, @@ -1700,6 +1737,11 @@ static void binder_transaction(struct binder_proc *proc, return_error = BR_FAILED_REPLY; goto err_fget_failed; } + if (security_binder_transfer_file(proc->tsk, target_proc->tsk, file) < 0) { + fput(file); + return_error = BR_FAILED_REPLY; + goto err_get_unused_fd_failed; + } target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC); if (target_fd < 0) { fput(file); @@ -1707,6 +1749,7 @@ static void binder_transaction(struct binder_proc *proc, goto err_get_unused_fd_failed; } task_fd_install(target_proc, target_fd, file); + trace_binder_transaction_fd(t, fp->handle, target_fd); binder_debug(BINDER_DEBUG_TRANSACTION, " fd %ld -> %d\n", fp->handle, target_fd); /* TODO: fput? */ @@ -1755,6 +1798,7 @@ err_binder_new_node_failed: err_bad_object_type: err_bad_offset: err_copy_data_failed: + trace_binder_transaction_failed_buffer_release(t->buffer); binder_transaction_buffer_release(target_proc, t->buffer, offp); t->buffer->transaction = NULL; binder_free_buf(target_proc, t->buffer); @@ -1800,6 +1844,7 @@ int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, if (get_user(cmd, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); + trace_binder_command(cmd); if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) { binder_stats.bc[_IOC_NR(cmd)]++; proc->stats.bc[_IOC_NR(cmd)]++; @@ -1969,6 +2014,7 @@ int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, else list_move_tail(buffer->target_node->async_todo.next, &thread->todo); } + trace_binder_transaction_buffer_release(buffer); binder_transaction_buffer_release(proc, buffer, NULL); binder_free_buf(proc, buffer); break; @@ -2177,6 +2223,7 @@ int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, void binder_stat_br(struct binder_proc *proc, struct binder_thread *thread, uint32_t cmd) { + trace_binder_return(cmd); if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.br)) { binder_stats.br[_IOC_NR(cmd)]++; proc->stats.br[_IOC_NR(cmd)]++; @@ -2223,6 +2270,7 @@ retry: if (put_user(thread->return_error2, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); + binder_stat_br(proc, thread, thread->return_error2); if (ptr == end) goto done; thread->return_error2 = BR_OK; @@ -2230,6 +2278,7 @@ retry: if (put_user(thread->return_error, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); + binder_stat_br(proc, thread, thread->return_error); thread->return_error = BR_OK; goto done; } @@ -2238,7 +2287,12 @@ retry: thread->looper |= BINDER_LOOPER_STATE_WAITING; if (wait_for_proc_work) proc->ready_threads++; - mutex_unlock(&binder_lock); + + binder_unlock(__func__); + + trace_binder_wait_for_work(wait_for_proc_work, + !!thread->transaction_stack, + !list_empty(&thread->todo)); if (wait_for_proc_work) { if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED))) { @@ -2262,7 +2316,9 @@ retry: } else ret = wait_event_interruptible(thread->wait, binder_has_thread_work(thread)); } - mutex_lock(&binder_lock); + + binder_lock(__func__); + if (wait_for_proc_work) proc->ready_threads--; thread->looper &= ~BINDER_LOOPER_STATE_WAITING; @@ -2385,6 +2441,7 @@ retry: if (put_user(death->cookie, (void * __user *)ptr)) return -EFAULT; ptr += sizeof(void *); + binder_stat_br(proc, thread, cmd); binder_debug(BINDER_DEBUG_DEATH_NOTIFICATION, "binder: %d:%d %s %p\n", proc->pid, thread->pid, @@ -2452,6 +2509,7 @@ retry: return -EFAULT; ptr += sizeof(tr); + trace_binder_transaction_received(t); binder_stat_br(proc, thread, cmd); binder_debug(BINDER_DEBUG_TRANSACTION, "binder: %d:%d %s %d %d:%d, cmd %d" @@ -2492,6 +2550,7 @@ done: proc->pid, thread->pid); if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer)) return -EFAULT; + binder_stat_br(proc, thread, BR_SPAWN_LOOPER); } return 0; } @@ -2628,12 +2687,14 @@ static unsigned int binder_poll(struct file *filp, struct binder_thread *thread = NULL; int wait_for_proc_work; - mutex_lock(&binder_lock); + binder_lock(__func__); + thread = binder_get_thread(proc); wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo) && thread->return_error == BR_OK; - mutex_unlock(&binder_lock); + + binder_unlock(__func__); if (wait_for_proc_work) { if (binder_has_proc_work(proc, thread)) @@ -2661,11 +2722,13 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) /*printk(KERN_INFO "binder_ioctl: %d:%d %x %lx\n", proc->pid, current->pid, cmd, arg);*/ + trace_binder_ioctl(cmd, arg); + ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); if (ret) - return ret; + goto err_unlocked; - mutex_lock(&binder_lock); + binder_lock(__func__); thread = binder_get_thread(proc); if (thread == NULL) { ret = -ENOMEM; @@ -2690,6 +2753,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (bwr.write_size > 0) { ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed); + trace_binder_write_done(ret); if (ret < 0) { bwr.read_consumed = 0; if (copy_to_user(ubuf, &bwr, sizeof(bwr))) @@ -2699,6 +2763,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } if (bwr.read_size > 0) { ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK); + trace_binder_read_done(ret); if (!list_empty(&proc->todo)) wake_up_interruptible(&proc->wait); if (ret < 0) { @@ -2729,6 +2794,9 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ret = -EBUSY; goto err; } + ret = security_binder_set_context_mgr(proc->tsk); + if (ret < 0) + goto err; if (binder_context_mgr_uid != -1) { if (binder_context_mgr_uid != current->cred->euid) { printk(KERN_ERR "binder: BINDER_SET_" @@ -2774,10 +2842,12 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) err: if (thread) thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN; - mutex_unlock(&binder_lock); + binder_unlock(__func__); wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); if (ret && ret != -ERESTARTSYS) printk(KERN_INFO "binder: %d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret); +err_unlocked: + trace_binder_ioctl_done(ret); return ret; } @@ -2920,13 +2990,16 @@ static int binder_open(struct inode *nodp, struct file *filp) INIT_LIST_HEAD(&proc->todo); init_waitqueue_head(&proc->wait); proc->default_priority = task_nice(current); - mutex_lock(&binder_lock); + + binder_lock(__func__); + binder_stats_created(BINDER_STAT_PROC); hlist_add_head(&proc->proc_node, &binder_procs); proc->pid = current->group_leader->pid; INIT_LIST_HEAD(&proc->delivered_death); filp->private_data = proc; - mutex_unlock(&binder_lock); + + binder_unlock(__func__); if (binder_debugfs_dir_entry_proc) { char strbuf[11]; @@ -3108,7 +3181,7 @@ static void binder_deferred_func(struct work_struct *work) int defer; do { - mutex_lock(&binder_lock); + binder_lock(__func__); mutex_lock(&binder_deferred_lock); if (!hlist_empty(&binder_deferred_list)) { proc = hlist_entry(binder_deferred_list.first, @@ -3135,7 +3208,7 @@ static void binder_deferred_func(struct work_struct *work) if (defer & BINDER_DEFERRED_RELEASE) binder_deferred_release(proc); /* frees proc */ - mutex_unlock(&binder_lock); + binder_unlock(__func__); if (files) put_files_struct(files); } while (proc); @@ -3476,7 +3549,7 @@ static int binder_state_show(struct seq_file *m, void *unused) int do_lock = !binder_debug_no_lock; if (do_lock) - mutex_lock(&binder_lock); + binder_lock(__func__); seq_puts(m, "binder state:\n"); @@ -3488,7 +3561,7 @@ static int binder_state_show(struct seq_file *m, void *unused) hlist_for_each_entry(proc, pos, &binder_procs, proc_node) print_binder_proc(m, proc, 1); if (do_lock) - mutex_unlock(&binder_lock); + binder_unlock(__func__); return 0; } @@ -3499,7 +3572,7 @@ static int binder_stats_show(struct seq_file *m, void *unused) int do_lock = !binder_debug_no_lock; if (do_lock) - mutex_lock(&binder_lock); + binder_lock(__func__); seq_puts(m, "binder stats:\n"); @@ -3508,7 +3581,7 @@ static int binder_stats_show(struct seq_file *m, void *unused) hlist_for_each_entry(proc, pos, &binder_procs, proc_node) print_binder_proc_stats(m, proc); if (do_lock) - mutex_unlock(&binder_lock); + binder_unlock(__func__); return 0; } @@ -3519,13 +3592,13 @@ static int binder_transactions_show(struct seq_file *m, void *unused) int do_lock = !binder_debug_no_lock; if (do_lock) - mutex_lock(&binder_lock); + binder_lock(__func__); seq_puts(m, "binder transactions:\n"); hlist_for_each_entry(proc, pos, &binder_procs, proc_node) print_binder_proc(m, proc, 0); if (do_lock) - mutex_unlock(&binder_lock); + binder_unlock(__func__); return 0; } @@ -3535,11 +3608,11 @@ static int binder_proc_show(struct seq_file *m, void *unused) int do_lock = !binder_debug_no_lock; if (do_lock) - mutex_lock(&binder_lock); + binder_lock(__func__); seq_puts(m, "binder proc state:\n"); print_binder_proc(m, proc, 1); if (do_lock) - mutex_unlock(&binder_lock); + binder_unlock(__func__); return 0; } @@ -3634,4 +3707,7 @@ static int __init binder_init(void) device_initcall(binder_init); +#define CREATE_TRACE_POINTS +#include "binder_trace.h" + MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/android/binder_trace.h b/drivers/staging/android/binder_trace.h new file mode 100644 index 00000000..82a567c2 --- /dev/null +++ b/drivers/staging/android/binder_trace.h @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2012 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM binder + +#if !defined(_BINDER_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _BINDER_TRACE_H + +#include + +struct binder_buffer; +struct binder_node; +struct binder_proc; +struct binder_ref; +struct binder_thread; +struct binder_transaction; + +TRACE_EVENT(binder_ioctl, + TP_PROTO(unsigned int cmd, unsigned long arg), + TP_ARGS(cmd, arg), + + TP_STRUCT__entry( + __field(unsigned int, cmd) + __field(unsigned long, arg) + ), + TP_fast_assign( + __entry->cmd = cmd; + __entry->arg = arg; + ), + TP_printk("cmd=0x%x arg=0x%lx", __entry->cmd, __entry->arg) +); + +DECLARE_EVENT_CLASS(binder_lock_class, + TP_PROTO(const char *tag), + TP_ARGS(tag), + TP_STRUCT__entry( + __field(const char *, tag) + ), + TP_fast_assign( + __entry->tag = tag; + ), + TP_printk("tag=%s", __entry->tag) +); + +#define DEFINE_BINDER_LOCK_EVENT(name) \ +DEFINE_EVENT(binder_lock_class, name, \ + TP_PROTO(const char *func), \ + TP_ARGS(func)) + +DEFINE_BINDER_LOCK_EVENT(binder_lock); +DEFINE_BINDER_LOCK_EVENT(binder_locked); +DEFINE_BINDER_LOCK_EVENT(binder_unlock); + +DECLARE_EVENT_CLASS(binder_function_return_class, + TP_PROTO(int ret), + TP_ARGS(ret), + TP_STRUCT__entry( + __field(int, ret) + ), + TP_fast_assign( + __entry->ret = ret; + ), + TP_printk("ret=%d", __entry->ret) +); + +#define DEFINE_BINDER_FUNCTION_RETURN_EVENT(name) \ +DEFINE_EVENT(binder_function_return_class, name, \ + TP_PROTO(int ret), \ + TP_ARGS(ret)) + +DEFINE_BINDER_FUNCTION_RETURN_EVENT(binder_ioctl_done); +DEFINE_BINDER_FUNCTION_RETURN_EVENT(binder_write_done); +DEFINE_BINDER_FUNCTION_RETURN_EVENT(binder_read_done); + +TRACE_EVENT(binder_wait_for_work, + TP_PROTO(bool proc_work, bool transaction_stack, bool thread_todo), + TP_ARGS(proc_work, transaction_stack, thread_todo), + + TP_STRUCT__entry( + __field(bool, proc_work) + __field(bool, transaction_stack) + __field(bool, thread_todo) + ), + TP_fast_assign( + __entry->proc_work = proc_work; + __entry->transaction_stack = transaction_stack; + __entry->thread_todo = thread_todo; + ), + TP_printk("proc_work=%d transaction_stack=%d thread_todo=%d", + __entry->proc_work, __entry->transaction_stack, + __entry->thread_todo) +); + +TRACE_EVENT(binder_transaction, + TP_PROTO(bool reply, struct binder_transaction *t, + struct binder_node *target_node), + TP_ARGS(reply, t, target_node), + TP_STRUCT__entry( + __field(int, debug_id) + __field(int, target_node) + __field(int, to_proc) + __field(int, to_thread) + __field(int, reply) + __field(unsigned int, code) + __field(unsigned int, flags) + ), + TP_fast_assign( + __entry->debug_id = t->debug_id; + __entry->target_node = target_node ? target_node->debug_id : 0; + __entry->to_proc = t->to_proc->pid; + __entry->to_thread = t->to_thread ? t->to_thread->pid : 0; + __entry->reply = reply; + __entry->code = t->code; + __entry->flags = t->flags; + ), + TP_printk("transaction=%d dest_node=%d dest_proc=%d dest_thread=%d reply=%d flags=0x%x code=0x%x", + __entry->debug_id, __entry->target_node, + __entry->to_proc, __entry->to_thread, + __entry->reply, __entry->flags, __entry->code) +); + +TRACE_EVENT(binder_transaction_received, + TP_PROTO(struct binder_transaction *t), + TP_ARGS(t), + + TP_STRUCT__entry( + __field(int, debug_id) + ), + TP_fast_assign( + __entry->debug_id = t->debug_id; + ), + TP_printk("transaction=%d", __entry->debug_id) +); + +TRACE_EVENT(binder_transaction_node_to_ref, + TP_PROTO(struct binder_transaction *t, struct binder_node *node, + struct binder_ref *ref), + TP_ARGS(t, node, ref), + + TP_STRUCT__entry( + __field(int, debug_id) + __field(int, node_debug_id) + __field(void __user *, node_ptr) + __field(int, ref_debug_id) + __field(uint32_t, ref_desc) + ), + TP_fast_assign( + __entry->debug_id = t->debug_id; + __entry->node_debug_id = node->debug_id; + __entry->node_ptr = node->ptr; + __entry->ref_debug_id = ref->debug_id; + __entry->ref_desc = ref->desc; + ), + TP_printk("transaction=%d node=%d src_ptr=0x%p ==> dest_ref=%d dest_desc=%d", + __entry->debug_id, __entry->node_debug_id, __entry->node_ptr, + __entry->ref_debug_id, __entry->ref_desc) +); + +TRACE_EVENT(binder_transaction_ref_to_node, + TP_PROTO(struct binder_transaction *t, struct binder_ref *ref), + TP_ARGS(t, ref), + + TP_STRUCT__entry( + __field(int, debug_id) + __field(int, ref_debug_id) + __field(uint32_t, ref_desc) + __field(int, node_debug_id) + __field(void __user *, node_ptr) + ), + TP_fast_assign( + __entry->debug_id = t->debug_id; + __entry->ref_debug_id = ref->debug_id; + __entry->ref_desc = ref->desc; + __entry->node_debug_id = ref->node->debug_id; + __entry->node_ptr = ref->node->ptr; + ), + TP_printk("transaction=%d node=%d src_ref=%d src_desc=%d ==> dest_ptr=0x%p", + __entry->debug_id, __entry->node_debug_id, + __entry->ref_debug_id, __entry->ref_desc, __entry->node_ptr) +); + +TRACE_EVENT(binder_transaction_ref_to_ref, + TP_PROTO(struct binder_transaction *t, struct binder_ref *src_ref, + struct binder_ref *dest_ref), + TP_ARGS(t, src_ref, dest_ref), + + TP_STRUCT__entry( + __field(int, debug_id) + __field(int, node_debug_id) + __field(int, src_ref_debug_id) + __field(uint32_t, src_ref_desc) + __field(int, dest_ref_debug_id) + __field(uint32_t, dest_ref_desc) + ), + TP_fast_assign( + __entry->debug_id = t->debug_id; + __entry->node_debug_id = src_ref->node->debug_id; + __entry->src_ref_debug_id = src_ref->debug_id; + __entry->src_ref_desc = src_ref->desc; + __entry->dest_ref_debug_id = dest_ref->debug_id; + __entry->dest_ref_desc = dest_ref->desc; + ), + TP_printk("transaction=%d node=%d src_ref=%d src_desc=%d ==> dest_ref=%d dest_desc=%d", + __entry->debug_id, __entry->node_debug_id, + __entry->src_ref_debug_id, __entry->src_ref_desc, + __entry->dest_ref_debug_id, __entry->dest_ref_desc) +); + +TRACE_EVENT(binder_transaction_fd, + TP_PROTO(struct binder_transaction *t, int src_fd, int dest_fd), + TP_ARGS(t, src_fd, dest_fd), + + TP_STRUCT__entry( + __field(int, debug_id) + __field(int, src_fd) + __field(int, dest_fd) + ), + TP_fast_assign( + __entry->debug_id = t->debug_id; + __entry->src_fd = src_fd; + __entry->dest_fd = dest_fd; + ), + TP_printk("transaction=%d src_fd=%d ==> dest_fd=%d", + __entry->debug_id, __entry->src_fd, __entry->dest_fd) +); + +DECLARE_EVENT_CLASS(binder_buffer_class, + TP_PROTO(struct binder_buffer *buf), + TP_ARGS(buf), + TP_STRUCT__entry( + __field(int, debug_id) + __field(size_t, data_size) + __field(size_t, offsets_size) + ), + TP_fast_assign( + __entry->debug_id = buf->debug_id; + __entry->data_size = buf->data_size; + __entry->offsets_size = buf->offsets_size; + ), + TP_printk("transaction=%d data_size=%zd offsets_size=%zd", + __entry->debug_id, __entry->data_size, __entry->offsets_size) +); + +DEFINE_EVENT(binder_buffer_class, binder_transaction_alloc_buf, + TP_PROTO(struct binder_buffer *buffer), + TP_ARGS(buffer)); + +DEFINE_EVENT(binder_buffer_class, binder_transaction_buffer_release, + TP_PROTO(struct binder_buffer *buffer), + TP_ARGS(buffer)); + +DEFINE_EVENT(binder_buffer_class, binder_transaction_failed_buffer_release, + TP_PROTO(struct binder_buffer *buffer), + TP_ARGS(buffer)); + +TRACE_EVENT(binder_update_page_range, + TP_PROTO(struct binder_proc *proc, bool allocate, + void *start, void *end), + TP_ARGS(proc, allocate, start, end), + TP_STRUCT__entry( + __field(int, proc) + __field(bool, allocate) + __field(size_t, offset) + __field(size_t, size) + ), + TP_fast_assign( + __entry->proc = proc->pid; + __entry->allocate = allocate; + __entry->offset = start - proc->buffer; + __entry->size = end - start; + ), + TP_printk("proc=%d allocate=%d offset=%zu size=%zu", + __entry->proc, __entry->allocate, + __entry->offset, __entry->size) +); + +TRACE_EVENT(binder_command, + TP_PROTO(uint32_t cmd), + TP_ARGS(cmd), + TP_STRUCT__entry( + __field(uint32_t, cmd) + ), + TP_fast_assign( + __entry->cmd = cmd; + ), + TP_printk("cmd=0x%x %s", + __entry->cmd, + _IOC_NR(__entry->cmd) < ARRAY_SIZE(binder_command_strings) ? + binder_command_strings[_IOC_NR(__entry->cmd)] : + "unknown") +); + +TRACE_EVENT(binder_return, + TP_PROTO(uint32_t cmd), + TP_ARGS(cmd), + TP_STRUCT__entry( + __field(uint32_t, cmd) + ), + TP_fast_assign( + __entry->cmd = cmd; + ), + TP_printk("cmd=0x%x %s", + __entry->cmd, + _IOC_NR(__entry->cmd) < ARRAY_SIZE(binder_return_strings) ? + binder_return_strings[_IOC_NR(__entry->cmd)] : + "unknown") +); + +#endif /* _BINDER_TRACE_H */ + +#undef TRACE_INCLUDE_PATH +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE binder_trace +#include diff --git a/drivers/staging/android/logger.c b/drivers/staging/android/logger.c index ea69b6a7..3813c743 100644 --- a/drivers/staging/android/logger.c +++ b/drivers/staging/android/logger.c @@ -57,6 +57,8 @@ struct logger_reader { struct logger_log *log; /* associated log */ struct list_head list; /* entry in logger_log's list */ size_t r_off; /* current read head offset */ + bool r_all; /* reader can read all entries */ + int r_ver; /* reader ABI version */ }; /* logger_offset - returns index 'n' into the log via (optimized) modulus */ @@ -90,8 +92,29 @@ static inline struct logger_log *file_get_log(struct file *file) } /* - * get_entry_len - Grabs the length of the payload of the next entry starting - * from 'off'. + * get_entry_header - returns a pointer to the logger_entry header within + * 'log' starting at offset 'off'. A temporary logger_entry 'scratch' must + * be provided. Typically the return value will be a pointer within + * 'logger->buf'. However, a pointer to 'scratch' may be returned if + * the log entry spans the end and beginning of the circular buffer. + */ +static struct logger_entry *get_entry_header(struct logger_log *log, + size_t off, struct logger_entry *scratch) +{ + size_t len = min(sizeof(struct logger_entry), log->size - off); + if (len != sizeof(struct logger_entry)) { + memcpy(((void *) scratch), log->buffer + off, len); + memcpy(((void *) scratch) + len, log->buffer, + sizeof(struct logger_entry) - len); + return scratch; + } + + return (struct logger_entry *) (log->buffer + off); +} + +/* + * get_entry_msg_len - Grabs the length of the message of the entry + * starting from from 'off'. * * An entry length is 2 bytes (16 bits) in host endian order. * In the log, the length does not include the size of the log entry structure. @@ -99,20 +122,45 @@ static inline struct logger_log *file_get_log(struct file *file) * * Caller needs to hold log->mutex. */ -static __u32 get_entry_len(struct logger_log *log, size_t off) +static __u32 get_entry_msg_len(struct logger_log *log, size_t off) { - __u16 val; + struct logger_entry scratch; + struct logger_entry *entry; - /* copy 2 bytes from buffer, in memcpy order, */ - /* handling possible wrap at end of buffer */ + entry = get_entry_header(log, off, &scratch); + return entry->len; +} - ((__u8 *)&val)[0] = log->buffer[off]; - if (likely(off+1 < log->size)) - ((__u8 *)&val)[1] = log->buffer[off+1]; +static size_t get_user_hdr_len(int ver) +{ + if (ver < 2) + return sizeof(struct user_logger_entry_compat); else - ((__u8 *)&val)[1] = log->buffer[0]; + return sizeof(struct logger_entry); +} - return sizeof(struct logger_entry) + val; +static ssize_t copy_header_to_user(int ver, struct logger_entry *entry, + char __user *buf) +{ + void *hdr; + size_t hdr_len; + struct user_logger_entry_compat v1; + + if (ver < 2) { + v1.len = entry->len; + v1.__pad = 0; + v1.pid = entry->pid; + v1.tid = entry->tid; + v1.sec = entry->sec; + v1.nsec = entry->nsec; + hdr = &v1; + hdr_len = sizeof(struct user_logger_entry_compat); + } else { + hdr = entry; + hdr_len = sizeof(struct logger_entry); + } + + return copy_to_user(buf, hdr, hdr_len); } /* @@ -126,15 +174,31 @@ static ssize_t do_read_log_to_user(struct logger_log *log, char __user *buf, size_t count) { + struct logger_entry scratch; + struct logger_entry *entry; size_t len; + size_t msg_start; /* - * We read from the log in two disjoint operations. First, we read from - * the current read head offset up to 'count' bytes or to the end of + * First, copy the header to userspace, using the version of + * the header requested + */ + entry = get_entry_header(log, reader->r_off, &scratch); + if (copy_header_to_user(reader->r_ver, entry, buf)) + return -EFAULT; + + count -= get_user_hdr_len(reader->r_ver); + buf += get_user_hdr_len(reader->r_ver); + msg_start = logger_offset(log, + reader->r_off + sizeof(struct logger_entry)); + + /* + * We read from the msg in two disjoint operations. First, we read from + * the current msg head offset up to 'count' bytes or to the end of * the log, whichever comes first. */ - len = min(count, log->size - reader->r_off); - if (copy_to_user(buf, log->buffer + reader->r_off, len)) + len = min(count, log->size - msg_start); + if (copy_to_user(buf, log->buffer + msg_start, len)) return -EFAULT; /* @@ -145,9 +209,34 @@ static ssize_t do_read_log_to_user(struct logger_log *log, if (copy_to_user(buf + len, log->buffer, count - len)) return -EFAULT; - reader->r_off = logger_offset(log, reader->r_off + count); + reader->r_off = logger_offset(log, reader->r_off + + sizeof(struct logger_entry) + count); - return count; + return count + get_user_hdr_len(reader->r_ver); +} + +/* + * get_next_entry_by_uid - Starting at 'off', returns an offset into + * 'log->buffer' which contains the first entry readable by 'euid' + */ +static size_t get_next_entry_by_uid(struct logger_log *log, + size_t off, uid_t euid) +{ + while (off != log->w_off) { + struct logger_entry *entry; + struct logger_entry scratch; + size_t next_len; + + entry = get_entry_header(log, off, &scratch); + + if (entry->euid == euid) + return off; + + next_len = sizeof(struct logger_entry) + entry->len; + off = logger_offset(log, off + next_len); + } + + return off; } /* @@ -159,7 +248,7 @@ static ssize_t do_read_log_to_user(struct logger_log *log, * - If there are no log entries to read, blocks until log is written to * - Atomically reads exactly one log entry * - * Optimal read size is LOGGER_ENTRY_MAX_LEN. Will set errno to EINVAL if read + * Will set errno to EINVAL if read * buffer is insufficient to hold next entry. */ static ssize_t logger_read(struct file *file, char __user *buf, @@ -200,6 +289,10 @@ start: mutex_lock(&log->mutex); + if (!reader->r_all) + reader->r_off = get_next_entry_by_uid(log, + reader->r_off, current_euid()); + /* is there still something to read or did we race? */ if (unlikely(log->w_off == reader->r_off)) { mutex_unlock(&log->mutex); @@ -207,7 +300,8 @@ start: } /* get the size of the next entry */ - ret = get_entry_len(log, reader->r_off); + ret = get_user_hdr_len(reader->r_ver) + + get_entry_msg_len(log, reader->r_off); if (count < ret) { ret = -EINVAL; goto out; @@ -233,7 +327,8 @@ static size_t get_next_entry(struct logger_log *log, size_t off, size_t len) size_t count = 0; do { - size_t nr = get_entry_len(log, off); + size_t nr = sizeof(struct logger_entry) + + get_entry_msg_len(log, off); off = logger_offset(log, off + nr); count += nr; } while (count < len); @@ -363,7 +458,9 @@ ssize_t logger_aio_write(struct kiocb *iocb, const struct iovec *iov, header.tid = current->pid; header.sec = now.tv_sec; header.nsec = now.tv_nsec; + header.euid = current_euid(); header.len = min_t(size_t, iocb->ki_left, LOGGER_ENTRY_MAX_PAYLOAD); + header.hdr_size = sizeof(struct logger_entry); /* null writes succeed, return zero */ if (unlikely(!header.len)) @@ -436,6 +533,10 @@ static int logger_open(struct inode *inode, struct file *file) return -ENOMEM; reader->log = log; + reader->r_ver = 1; + reader->r_all = in_egroup_p(inode->i_gid) || + capable(CAP_SYSLOG); + INIT_LIST_HEAD(&reader->list); mutex_lock(&log->mutex); @@ -495,6 +596,10 @@ static unsigned int logger_poll(struct file *file, poll_table *wait) poll_wait(file, &log->wq, wait); mutex_lock(&log->mutex); + if (!reader->r_all) + reader->r_off = get_next_entry_by_uid(log, + reader->r_off, current_euid()); + if (log->w_off != reader->r_off) ret |= POLLIN | POLLRDNORM; mutex_unlock(&log->mutex); @@ -502,11 +607,25 @@ static unsigned int logger_poll(struct file *file, poll_table *wait) return ret; } +static long logger_set_version(struct logger_reader *reader, void __user *arg) +{ + int version; + if (copy_from_user(&version, arg, sizeof(int))) + return -EFAULT; + + if ((version < 1) || (version > 2)) + return -EINVAL; + + reader->r_ver = version; + return 0; +} + static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct logger_log *log = file_get_log(file); struct logger_reader *reader; - long ret = -ENOTTY; + long ret = -EINVAL; + void __user *argp = (void __user *) arg; mutex_lock(&log->mutex); @@ -531,8 +650,14 @@ static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; } reader = file->private_data; + + if (!reader->r_all) + reader->r_off = get_next_entry_by_uid(log, + reader->r_off, current_euid()); + if (log->w_off != reader->r_off) - ret = get_entry_len(log, reader->r_off); + ret = get_user_hdr_len(reader->r_ver) + + get_entry_msg_len(log, reader->r_off); else ret = 0; break; @@ -541,11 +666,32 @@ static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ret = -EBADF; break; } + if (!(in_egroup_p(file->f_dentry->d_inode->i_gid) || + capable(CAP_SYSLOG))) { + ret = -EPERM; + break; + } list_for_each_entry(reader, &log->readers, list) reader->r_off = log->w_off; log->head = log->w_off; ret = 0; break; + case LOGGER_GET_VERSION: + if (!(file->f_mode & FMODE_READ)) { + ret = -EBADF; + break; + } + reader = file->private_data; + ret = reader->r_ver; + break; + case LOGGER_SET_VERSION: + if (!(file->f_mode & FMODE_READ)) { + ret = -EBADF; + break; + } + reader = file->private_data; + ret = logger_set_version(reader, argp); + break; } mutex_unlock(&log->mutex); @@ -566,8 +712,8 @@ static const struct file_operations logger_fops = { /* * Defines a log structure with name 'NAME' and a size of 'SIZE' bytes, which - * must be a power of two, greater than LOGGER_ENTRY_MAX_LEN, and less than - * LONG_MAX minus LOGGER_ENTRY_MAX_LEN. + * must be a power of two, and greater than + * (LOGGER_ENTRY_MAX_PAYLOAD + sizeof(struct logger_entry)). */ #define DEFINE_LOGGER_DEVICE(VAR, NAME, SIZE) \ static unsigned char _buf_ ## VAR[SIZE]; \ diff --git a/drivers/staging/android/logger.h b/drivers/staging/android/logger.h index 2cb06e9d..3f612a3b 100644 --- a/drivers/staging/android/logger.h +++ b/drivers/staging/android/logger.h @@ -20,7 +20,12 @@ #include #include -struct logger_entry { +/* + * The userspace structure for version 1 of the logger_entry ABI. + * This structure is returned to userspace unless the caller requests + * an upgrade to a newer ABI version. + */ +struct user_logger_entry_compat { __u16 len; /* length of the payload */ __u16 __pad; /* no matter what, we get 2 bytes of padding */ __s32 pid; /* generating process's pid */ @@ -30,14 +35,28 @@ struct logger_entry { char msg[0]; /* the entry's payload */ }; +/* + * The structure for version 2 of the logger_entry ABI. + * This structure is returned to userspace if ioctl(LOGGER_SET_VERSION) + * is called with version >= 2 + */ +struct logger_entry { + __u16 len; /* length of the payload */ + __u16 hdr_size; /* sizeof(struct logger_entry_v2) */ + __s32 pid; /* generating process's pid */ + __s32 tid; /* generating process's tid */ + __s32 sec; /* seconds since Epoch */ + __s32 nsec; /* nanoseconds */ + uid_t euid; /* effective UID of logger */ + char msg[0]; /* the entry's payload */ +}; + #define LOGGER_LOG_RADIO "log_radio" /* radio-related messages */ #define LOGGER_LOG_EVENTS "log_events" /* system/hardware events */ #define LOGGER_LOG_SYSTEM "log_system" /* system/framework messages */ #define LOGGER_LOG_MAIN "log_main" /* everything else */ -#define LOGGER_ENTRY_MAX_LEN (4*1024) -#define LOGGER_ENTRY_MAX_PAYLOAD \ - (LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry)) +#define LOGGER_ENTRY_MAX_PAYLOAD 4076 #define __LOGGERIO 0xAE @@ -45,5 +64,7 @@ struct logger_entry { #define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */ #define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */ #define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */ +#define LOGGER_GET_VERSION _IO(__LOGGERIO, 5) /* abi version */ +#define LOGGER_SET_VERSION _IO(__LOGGERIO, 6) /* abi version */ #endif /* _LINUX_LOGGER_H */ diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index b91e4bc3..12f0a13a 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -35,11 +35,11 @@ #include #include #include +#include #include -#include #include -static uint32_t lowmem_debug_level = 2; +static uint32_t lowmem_debug_level = 1; static int lowmem_adj[6] = { 0, 1, @@ -74,7 +74,7 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) int selected_tasksize = 0; int selected_oom_score_adj; int array_size = ARRAY_SIZE(lowmem_adj); - int other_free = global_page_state(NR_FREE_PAGES); + int other_free = global_page_state(NR_FREE_PAGES) - totalreserve_pages; int other_file = global_page_state(NR_FILE_PAGES) - global_page_state(NR_SHMEM); @@ -175,9 +175,94 @@ static void __exit lowmem_exit(void) unregister_shrinker(&lowmem_shrinker); } +#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES +static int lowmem_oom_adj_to_oom_score_adj(int oom_adj) +{ + if (oom_adj == OOM_ADJUST_MAX) + return OOM_SCORE_ADJ_MAX; + else + return (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE; +} + +static void lowmem_autodetect_oom_adj_values(void) +{ + int i; + int oom_adj; + int oom_score_adj; + int array_size = ARRAY_SIZE(lowmem_adj); + + if (lowmem_adj_size < array_size) + array_size = lowmem_adj_size; + + if (array_size <= 0) + return; + + oom_adj = lowmem_adj[array_size - 1]; + if (oom_adj > OOM_ADJUST_MAX) + return; + + oom_score_adj = lowmem_oom_adj_to_oom_score_adj(oom_adj); + if (oom_score_adj <= OOM_ADJUST_MAX) + return; + + lowmem_print(1, "lowmem_shrink: convert oom_adj to oom_score_adj:\n"); + for (i = 0; i < array_size; i++) { + oom_adj = lowmem_adj[i]; + oom_score_adj = lowmem_oom_adj_to_oom_score_adj(oom_adj); + lowmem_adj[i] = oom_score_adj; + lowmem_print(1, "oom_adj %d => oom_score_adj %d\n", + oom_adj, oom_score_adj); + } +} + +static int lowmem_adj_array_set(const char *val, const struct kernel_param *kp) +{ + int ret; + + ret = param_array_ops.set(val, kp); + + /* HACK: Autodetect oom_adj values in lowmem_adj array */ + lowmem_autodetect_oom_adj_values(); + + return ret; +} + +static int lowmem_adj_array_get(char *buffer, const struct kernel_param *kp) +{ + return param_array_ops.get(buffer, kp); +} + +static void lowmem_adj_array_free(void *arg) +{ + param_array_ops.free(arg); +} + +static struct kernel_param_ops lowmem_adj_array_ops = { + .set = lowmem_adj_array_set, + .get = lowmem_adj_array_get, + .free = lowmem_adj_array_free, +}; + +static const struct kparam_array __param_arr_adj = { + .max = ARRAY_SIZE(lowmem_adj), + .num = &lowmem_adj_size, + .ops = ¶m_ops_int, + .elemsize = sizeof(lowmem_adj[0]), + .elem = lowmem_adj, +}; +#endif + module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR); +#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES +__module_param_call(MODULE_PARAM_PREFIX, adj, + &lowmem_adj_array_ops, + .arr = &__param_arr_adj, + S_IRUGO | S_IWUSR, -1); +__MODULE_PARM_TYPE(adj, "array of int"); +#else module_param_array_named(adj, lowmem_adj, int, &lowmem_adj_size, S_IRUGO | S_IWUSR); +#endif module_param_array_named(minfree, lowmem_minfree, uint, &lowmem_minfree_size, S_IRUGO | S_IWUSR); module_param_named(debug_level, lowmem_debug_level, uint, S_IRUGO | S_IWUSR); diff --git a/drivers/staging/android/persistent_ram.c b/drivers/staging/android/persistent_ram.c index 3d986ce5..e9e08fd0 100644 --- a/drivers/staging/android/persistent_ram.c +++ b/drivers/staging/android/persistent_ram.c @@ -20,10 +20,10 @@ #include #include #include +#include #include #include #include -#include "persistent_ram.h" struct persistent_ram_buffer { uint32_t sig; @@ -34,7 +34,7 @@ struct persistent_ram_buffer { #define PERSISTENT_RAM_SIG (0x43474244) /* DBGC */ -static __initdata LIST_HEAD(persistent_ram_list); +static __devinitdata LIST_HEAD(persistent_ram_list); static inline size_t buffer_size(struct persistent_ram_zone *prz) { @@ -173,7 +173,7 @@ static void persistent_ram_ecc_old(struct persistent_ram_zone *prz) } static int persistent_ram_init_ecc(struct persistent_ram_zone *prz, - size_t buffer_size) + size_t buffer_size, struct persistent_ram *ram) { int numerr; struct persistent_ram_buffer *buffer = prz->buffer; @@ -182,12 +182,13 @@ static int persistent_ram_init_ecc(struct persistent_ram_zone *prz, if (!prz->ecc) return 0; - prz->ecc_block_size = 128; - prz->ecc_size = 16; - prz->ecc_symsize = 8; - prz->ecc_poly = 0x11d; + prz->ecc_block_size = ram->ecc_block_size ?: 128; + prz->ecc_size = ram->ecc_size ?: 16; + prz->ecc_symsize = ram->ecc_symsize ?: 8; + prz->ecc_poly = ram->ecc_poly ?: 0x11d; - ecc_blocks = DIV_ROUND_UP(prz->buffer_size, prz->ecc_block_size); + ecc_blocks = DIV_ROUND_UP(prz->buffer_size - prz->ecc_size, + prz->ecc_block_size + prz->ecc_size); prz->buffer_size -= (ecc_blocks + 1) * prz->ecc_size; if (prz->buffer_size > buffer_size) { @@ -249,7 +250,7 @@ static void notrace persistent_ram_update(struct persistent_ram_zone *prz, persistent_ram_update_ecc(prz, start, count); } -static void __init +static void __devinit persistent_ram_save_old(struct persistent_ram_zone *prz) { struct persistent_ram_buffer *buffer = prz->buffer; @@ -356,8 +357,8 @@ static int persistent_ram_buffer_map(phys_addr_t start, phys_addr_t size, return 0; } -static int __init persistent_ram_buffer_init(const char *name, - struct persistent_ram_zone *prz) +static int __devinit persistent_ram_buffer_init(const char *name, + struct persistent_ram_zone *prz, struct persistent_ram **ramp) { int i; struct persistent_ram *ram; @@ -368,9 +369,11 @@ static int __init persistent_ram_buffer_init(const char *name, start = ram->start; for (i = 0; i < ram->num_descs; i++) { desc = &ram->descs[i]; - if (!strcmp(desc->name, name)) + if (!strcmp(desc->name, name)) { + *ramp = ram; return persistent_ram_buffer_map(start, desc->size, prz); + } start += desc->size; } } @@ -378,9 +381,10 @@ static int __init persistent_ram_buffer_init(const char *name, return -EINVAL; } -static __init +static __devinit struct persistent_ram_zone *__persistent_ram_init(struct device *dev, bool ecc) { + struct persistent_ram *ram; struct persistent_ram_zone *prz; int ret = -ENOMEM; @@ -392,14 +396,14 @@ struct persistent_ram_zone *__persistent_ram_init(struct device *dev, bool ecc) INIT_LIST_HEAD(&prz->node); - ret = persistent_ram_buffer_init(dev_name(dev), prz); + ret = persistent_ram_buffer_init(dev_name(dev), prz, &ram); if (ret) { pr_err("persistent_ram: failed to initialize buffer\n"); goto err; } prz->ecc = ecc; - ret = persistent_ram_init_ecc(prz, prz->buffer_size); + ret = persistent_ram_init_ecc(prz, prz->buffer_size, ram); if (ret) goto err; @@ -407,11 +411,11 @@ struct persistent_ram_zone *__persistent_ram_init(struct device *dev, bool ecc) if (buffer_size(prz) > prz->buffer_size || buffer_start(prz) > buffer_size(prz)) pr_info("persistent_ram: found existing invalid buffer," - " size %ld, start %ld\n", + " size %zu, start %zu\n", buffer_size(prz), buffer_start(prz)); else { pr_info("persistent_ram: found existing buffer," - " size %ld, start %ld\n", + " size %zu, start %zu\n", buffer_size(prz), buffer_start(prz)); persistent_ram_save_old(prz); } @@ -430,7 +434,7 @@ err: return ERR_PTR(ret); } -struct persistent_ram_zone * __init +struct persistent_ram_zone * __devinit persistent_ram_init_ringbuffer(struct device *dev, bool ecc) { return __persistent_ram_init(dev, ecc); diff --git a/drivers/staging/android/ram_console.c b/drivers/staging/android/ram_console.c index ce140ffc..cf0f8fb4 100644 --- a/drivers/staging/android/ram_console.c +++ b/drivers/staging/android/ram_console.c @@ -16,12 +16,12 @@ #include #include #include +#include #include #include #include #include #include -#include "persistent_ram.h" #include "ram_console.h" static struct persistent_ram_zone *ram_console_zone; @@ -50,7 +50,7 @@ void ram_console_enable_console(int enabled) ram_console.flags &= ~CON_ENABLED; } -static int __init ram_console_probe(struct platform_device *pdev) +static int __devinit ram_console_probe(struct platform_device *pdev) { struct ram_console_platform_data *pdata = pdev->dev.platform_data; struct persistent_ram_zone *prz; @@ -78,11 +78,12 @@ static struct platform_driver ram_console_driver = { .driver = { .name = "ram_console", }, + .probe = ram_console_probe, }; static int __init ram_console_module_init(void) { - return platform_driver_probe(&ram_console_driver, ram_console_probe); + return platform_driver_register(&ram_console_driver); } #ifndef CONFIG_PRINTK @@ -100,9 +101,6 @@ static ssize_t ram_console_read_old(struct file *file, char __user *buf, char *str; int ret; - if (dmesg_restrict && !capable(CAP_SYSLOG)) - return -EPERM; - /* Main last_kmsg log */ if (pos < old_log_size) { count = min(len, (size_t)(old_log_size - pos)); diff --git a/drivers/staging/android/trace_persistent.c b/drivers/staging/android/trace_persistent.c new file mode 100644 index 00000000..7c3e9499 --- /dev/null +++ b/drivers/staging/android/trace_persistent.c @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2012 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../../kernel/trace/trace.h" + +struct persistent_trace_record { + unsigned long ip; + unsigned long parent_ip; +}; + +#define REC_SIZE sizeof(struct persistent_trace_record) + +static struct persistent_ram_zone *persistent_trace; + +static int persistent_trace_enabled; + +static struct trace_array *persistent_trace_array; + +static struct ftrace_ops trace_ops; + +static int persistent_tracer_init(struct trace_array *tr) +{ + persistent_trace_array = tr; + tr->cpu = get_cpu(); + put_cpu(); + + tracing_start_cmdline_record(); + + persistent_trace_enabled = 0; + smp_wmb(); + + register_ftrace_function(&trace_ops); + + smp_wmb(); + persistent_trace_enabled = 1; + + return 0; +} + +static void persistent_trace_reset(struct trace_array *tr) +{ + persistent_trace_enabled = 0; + smp_wmb(); + + unregister_ftrace_function(&trace_ops); + + tracing_stop_cmdline_record(); +} + +static void persistent_trace_start(struct trace_array *tr) +{ + tracing_reset_online_cpus(tr); +} + +static void persistent_trace_call(unsigned long ip, unsigned long parent_ip) +{ + struct trace_array *tr = persistent_trace_array; + struct trace_array_cpu *data; + long disabled; + struct persistent_trace_record rec; + unsigned long flags; + int cpu; + + smp_rmb(); + if (unlikely(!persistent_trace_enabled)) + return; + + if (unlikely(oops_in_progress)) + return; + + /* + * Need to use raw, since this must be called before the + * recursive protection is performed. + */ + local_irq_save(flags); + cpu = raw_smp_processor_id(); + data = tr->data[cpu]; + disabled = atomic_inc_return(&data->disabled); + + if (likely(disabled == 1)) { + rec.ip = ip; + rec.parent_ip = parent_ip; + rec.ip |= cpu; + persistent_ram_write(persistent_trace, &rec, sizeof(rec)); + } + + atomic_dec(&data->disabled); + local_irq_restore(flags); +} + +static struct ftrace_ops trace_ops __read_mostly = { + .func = persistent_trace_call, + .flags = FTRACE_OPS_FL_GLOBAL, +}; + +static struct tracer persistent_tracer __read_mostly = { + .name = "persistent", + .init = persistent_tracer_init, + .reset = persistent_trace_reset, + .start = persistent_trace_start, + .wait_pipe = poll_wait_pipe, +}; + +struct persistent_trace_seq_data { + const void *ptr; + size_t off; + size_t size; +}; + +void *persistent_trace_seq_start(struct seq_file *s, loff_t *pos) +{ + struct persistent_trace_seq_data *data; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return NULL; + + data->ptr = persistent_ram_old(persistent_trace); + data->size = persistent_ram_old_size(persistent_trace); + data->off = data->size % REC_SIZE; + + data->off += *pos * REC_SIZE; + + if (data->off + REC_SIZE > data->size) { + kfree(data); + return NULL; + } + + return data; + +} +void persistent_trace_seq_stop(struct seq_file *s, void *v) +{ + kfree(v); +} + +void *persistent_trace_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + struct persistent_trace_seq_data *data = v; + + data->off += REC_SIZE; + + if (data->off + REC_SIZE > data->size) + return NULL; + + (*pos)++; + + return data; +} + +int persistent_trace_seq_show(struct seq_file *s, void *v) +{ + struct persistent_trace_seq_data *data = v; + struct persistent_trace_record *rec; + + rec = (struct persistent_trace_record *)(data->ptr + data->off); + + seq_printf(s, "%ld %08lx %08lx %pf <- %pF\n", + rec->ip & 3, rec->ip, rec->parent_ip, + (void *)rec->ip, (void *)rec->parent_ip); + + return 0; +} + +static const struct seq_operations persistent_trace_seq_ops = { + .start = persistent_trace_seq_start, + .next = persistent_trace_seq_next, + .stop = persistent_trace_seq_stop, + .show = persistent_trace_seq_show, +}; + +static int persistent_trace_old_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &persistent_trace_seq_ops); +} + +static const struct file_operations persistent_trace_old_fops = { + .open = persistent_trace_old_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int __devinit persistent_trace_probe(struct platform_device *pdev) +{ + struct dentry *d; + int ret; + + persistent_trace = persistent_ram_init_ringbuffer(&pdev->dev, false); + if (IS_ERR(persistent_trace)) { + pr_err("persistent_trace: failed to init ringbuffer: %ld\n", + PTR_ERR(persistent_trace)); + return PTR_ERR(persistent_trace); + } + + ret = register_tracer(&persistent_tracer); + if (ret) + pr_err("persistent_trace: failed to register tracer"); + + if (persistent_ram_old_size(persistent_trace) > 0) { + d = debugfs_create_file("persistent_trace", S_IRUGO, NULL, + NULL, &persistent_trace_old_fops); + if (IS_ERR_OR_NULL(d)) + pr_err("persistent_trace: failed to create old file\n"); + } + + return 0; +} + +static struct platform_driver persistent_trace_driver = { + .probe = persistent_trace_probe, + .driver = { + .name = "persistent_trace", + }, +}; + +static int __init persistent_trace_init(void) +{ + return platform_driver_register(&persistent_trace_driver); +} +core_initcall(persistent_trace_init); diff --git a/drivers/staging/iio/industrialio-event.c b/drivers/staging/iio/industrialio-event.c index 5fdf739e..68ef8264 100644 --- a/drivers/staging/iio/industrialio-event.c +++ b/drivers/staging/iio/industrialio-event.c @@ -35,6 +35,7 @@ */ struct iio_event_interface { wait_queue_head_t wait; + struct mutex read_lock; DECLARE_KFIFO(det_events, struct iio_event_data, 16); struct list_head dev_attr_list; @@ -96,14 +97,16 @@ static ssize_t iio_event_chrdev_read(struct file *filep, if (count < sizeof(struct iio_event_data)) return -EINVAL; - spin_lock(&ev_int->wait.lock); + if (mutex_lock_interruptible(&ev_int->read_lock)) + return -ERESTARTSYS; + if (kfifo_is_empty(&ev_int->det_events)) { if (filep->f_flags & O_NONBLOCK) { ret = -EAGAIN; goto error_unlock; } /* Blocking on device; waiting for something to be there */ - ret = wait_event_interruptible_locked(ev_int->wait, + ret = wait_event_interruptible(ev_int->wait, !kfifo_is_empty(&ev_int->det_events)); if (ret) goto error_unlock; @@ -113,7 +116,7 @@ static ssize_t iio_event_chrdev_read(struct file *filep, ret = kfifo_to_user(&ev_int->det_events, buf, count, &copied); error_unlock: - spin_unlock(&ev_int->wait.lock); + mutex_unlock(&ev_int->read_lock); return ret ? ret : copied; } @@ -376,6 +379,7 @@ static void iio_setup_ev_int(struct iio_event_interface *ev_int) { INIT_KFIFO(ev_int->det_events); init_waitqueue_head(&ev_int->wait); + mutex_init(&ev_int->read_lock); } static const char *iio_event_group_name = "events"; @@ -437,6 +441,7 @@ int iio_device_register_eventset(struct iio_dev *indio_dev) error_free_setup_event_lines: __iio_remove_event_config_attrs(indio_dev); + mutex_destroy(&indio_dev->event_interface->read_lock); kfree(indio_dev->event_interface); error_ret: @@ -449,5 +454,6 @@ void iio_device_unregister_eventset(struct iio_dev *indio_dev) return; __iio_remove_event_config_attrs(indio_dev); kfree(indio_dev->event_interface->group.attrs); + mutex_destroy(&indio_dev->event_interface->read_lock); kfree(indio_dev->event_interface); } diff --git a/drivers/switch/Kconfig b/drivers/switch/Kconfig new file mode 100644 index 00000000..52385914 --- /dev/null +++ b/drivers/switch/Kconfig @@ -0,0 +1,15 @@ +menuconfig SWITCH + tristate "Switch class support" + help + Say Y here to enable switch class support. This allows + monitoring switches by userspace via sysfs and uevent. + +if SWITCH + +config SWITCH_GPIO + tristate "GPIO Swith support" + depends on GENERIC_GPIO + help + Say Y here to enable GPIO based switch support. + +endif # SWITCH diff --git a/drivers/switch/Makefile b/drivers/switch/Makefile new file mode 100644 index 00000000..f7606ed4 --- /dev/null +++ b/drivers/switch/Makefile @@ -0,0 +1,4 @@ +# Switch Class Driver +obj-$(CONFIG_SWITCH) += switch_class.o +obj-$(CONFIG_SWITCH_GPIO) += switch_gpio.o + diff --git a/drivers/switch/switch_class.c b/drivers/switch/switch_class.c new file mode 100644 index 00000000..e05fc259 --- /dev/null +++ b/drivers/switch/switch_class.c @@ -0,0 +1,174 @@ +/* + * drivers/switch/switch_class.c + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * +*/ + +#include +#include +#include +#include +#include +#include +#include + +struct class *switch_class; +static atomic_t device_count; + +static ssize_t state_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct switch_dev *sdev = (struct switch_dev *) + dev_get_drvdata(dev); + + if (sdev->print_state) { + int ret = sdev->print_state(sdev, buf); + if (ret >= 0) + return ret; + } + return sprintf(buf, "%d\n", sdev->state); +} + +static ssize_t name_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct switch_dev *sdev = (struct switch_dev *) + dev_get_drvdata(dev); + + if (sdev->print_name) { + int ret = sdev->print_name(sdev, buf); + if (ret >= 0) + return ret; + } + return sprintf(buf, "%s\n", sdev->name); +} + +static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, state_show, NULL); +static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, name_show, NULL); + +void switch_set_state(struct switch_dev *sdev, int state) +{ + char name_buf[120]; + char state_buf[120]; + char *prop_buf; + char *envp[3]; + int env_offset = 0; + int length; + + if (sdev->state != state) { + sdev->state = state; + + prop_buf = (char *)get_zeroed_page(GFP_KERNEL); + if (prop_buf) { + length = name_show(sdev->dev, NULL, prop_buf); + if (length > 0) { + if (prop_buf[length - 1] == '\n') + prop_buf[length - 1] = 0; + snprintf(name_buf, sizeof(name_buf), + "SWITCH_NAME=%s", prop_buf); + envp[env_offset++] = name_buf; + } + length = state_show(sdev->dev, NULL, prop_buf); + if (length > 0) { + if (prop_buf[length - 1] == '\n') + prop_buf[length - 1] = 0; + snprintf(state_buf, sizeof(state_buf), + "SWITCH_STATE=%s", prop_buf); + envp[env_offset++] = state_buf; + } + envp[env_offset] = NULL; + kobject_uevent_env(&sdev->dev->kobj, KOBJ_CHANGE, envp); + free_page((unsigned long)prop_buf); + } else { + printk(KERN_ERR "out of memory in switch_set_state\n"); + kobject_uevent(&sdev->dev->kobj, KOBJ_CHANGE); + } + } +} +EXPORT_SYMBOL_GPL(switch_set_state); + +static int create_switch_class(void) +{ + if (!switch_class) { + switch_class = class_create(THIS_MODULE, "switch"); + if (IS_ERR(switch_class)) + return PTR_ERR(switch_class); + atomic_set(&device_count, 0); + } + + return 0; +} + +int switch_dev_register(struct switch_dev *sdev) +{ + int ret; + + if (!switch_class) { + ret = create_switch_class(); + if (ret < 0) + return ret; + } + + sdev->index = atomic_inc_return(&device_count); + sdev->dev = device_create(switch_class, NULL, + MKDEV(0, sdev->index), NULL, sdev->name); + if (IS_ERR(sdev->dev)) + return PTR_ERR(sdev->dev); + + ret = device_create_file(sdev->dev, &dev_attr_state); + if (ret < 0) + goto err_create_file_1; + ret = device_create_file(sdev->dev, &dev_attr_name); + if (ret < 0) + goto err_create_file_2; + + dev_set_drvdata(sdev->dev, sdev); + sdev->state = 0; + return 0; + +err_create_file_2: + device_remove_file(sdev->dev, &dev_attr_state); +err_create_file_1: + device_destroy(switch_class, MKDEV(0, sdev->index)); + printk(KERN_ERR "switch: Failed to register driver %s\n", sdev->name); + + return ret; +} +EXPORT_SYMBOL_GPL(switch_dev_register); + +void switch_dev_unregister(struct switch_dev *sdev) +{ + device_remove_file(sdev->dev, &dev_attr_name); + device_remove_file(sdev->dev, &dev_attr_state); + device_destroy(switch_class, MKDEV(0, sdev->index)); + dev_set_drvdata(sdev->dev, NULL); +} +EXPORT_SYMBOL_GPL(switch_dev_unregister); + +static int __init switch_class_init(void) +{ + return create_switch_class(); +} + +static void __exit switch_class_exit(void) +{ + class_destroy(switch_class); +} + +module_init(switch_class_init); +module_exit(switch_class_exit); + +MODULE_AUTHOR("Mike Lockwood "); +MODULE_DESCRIPTION("Switch class driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/switch/switch_gpio.c b/drivers/switch/switch_gpio.c new file mode 100644 index 00000000..7e9faa21 --- /dev/null +++ b/drivers/switch/switch_gpio.c @@ -0,0 +1,172 @@ +/* + * drivers/switch/switch_gpio.c + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct gpio_switch_data { + struct switch_dev sdev; + unsigned gpio; + const char *name_on; + const char *name_off; + const char *state_on; + const char *state_off; + int irq; + struct work_struct work; +}; + +static void gpio_switch_work(struct work_struct *work) +{ + int state; + struct gpio_switch_data *data = + container_of(work, struct gpio_switch_data, work); + + state = gpio_get_value(data->gpio); + switch_set_state(&data->sdev, state); +} + +static irqreturn_t gpio_irq_handler(int irq, void *dev_id) +{ + struct gpio_switch_data *switch_data = + (struct gpio_switch_data *)dev_id; + + schedule_work(&switch_data->work); + return IRQ_HANDLED; +} + +static ssize_t switch_gpio_print_state(struct switch_dev *sdev, char *buf) +{ + struct gpio_switch_data *switch_data = + container_of(sdev, struct gpio_switch_data, sdev); + const char *state; + if (switch_get_state(sdev)) + state = switch_data->state_on; + else + state = switch_data->state_off; + + if (state) + return sprintf(buf, "%s\n", state); + return -1; +} + +static int gpio_switch_probe(struct platform_device *pdev) +{ + struct gpio_switch_platform_data *pdata = pdev->dev.platform_data; + struct gpio_switch_data *switch_data; + int ret = 0; + + if (!pdata) + return -EBUSY; + + switch_data = kzalloc(sizeof(struct gpio_switch_data), GFP_KERNEL); + if (!switch_data) + return -ENOMEM; + + switch_data->sdev.name = pdata->name; + switch_data->gpio = pdata->gpio; + switch_data->name_on = pdata->name_on; + switch_data->name_off = pdata->name_off; + switch_data->state_on = pdata->state_on; + switch_data->state_off = pdata->state_off; + switch_data->sdev.print_state = switch_gpio_print_state; + + ret = switch_dev_register(&switch_data->sdev); + if (ret < 0) + goto err_switch_dev_register; + + ret = gpio_request(switch_data->gpio, pdev->name); + if (ret < 0) + goto err_request_gpio; + + ret = gpio_direction_input(switch_data->gpio); + if (ret < 0) + goto err_set_gpio_input; + + INIT_WORK(&switch_data->work, gpio_switch_work); + + switch_data->irq = gpio_to_irq(switch_data->gpio); + if (switch_data->irq < 0) { + ret = switch_data->irq; + goto err_detect_irq_num_failed; + } + + ret = request_irq(switch_data->irq, gpio_irq_handler, + IRQF_TRIGGER_LOW, pdev->name, switch_data); + if (ret < 0) + goto err_request_irq; + + /* Perform initial detection */ + gpio_switch_work(&switch_data->work); + + return 0; + +err_request_irq: +err_detect_irq_num_failed: +err_set_gpio_input: + gpio_free(switch_data->gpio); +err_request_gpio: + switch_dev_unregister(&switch_data->sdev); +err_switch_dev_register: + kfree(switch_data); + + return ret; +} + +static int __devexit gpio_switch_remove(struct platform_device *pdev) +{ + struct gpio_switch_data *switch_data = platform_get_drvdata(pdev); + + cancel_work_sync(&switch_data->work); + gpio_free(switch_data->gpio); + switch_dev_unregister(&switch_data->sdev); + kfree(switch_data); + + return 0; +} + +static struct platform_driver gpio_switch_driver = { + .probe = gpio_switch_probe, + .remove = __devexit_p(gpio_switch_remove), + .driver = { + .name = "switch-gpio", + .owner = THIS_MODULE, + }, +}; + +static int __init gpio_switch_init(void) +{ + return platform_driver_register(&gpio_switch_driver); +} + +static void __exit gpio_switch_exit(void) +{ + platform_driver_unregister(&gpio_switch_driver); +} + +module_init(gpio_switch_init); +module_exit(gpio_switch_exit); + +MODULE_AUTHOR("Mike Lockwood "); +MODULE_DESCRIPTION("GPIO Switch driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 070b442c..296b412f 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1360,4 +1360,34 @@ config SERIAL_EFM32_UART_CONSOLE depends on SERIAL_EFM32_UART=y select SERIAL_CORE_CONSOLE +config SERIAL_AK39_UART + tristate "AK39xx serial port support" + select SERIAL_CORE + depends on ARCH_AK39 + help + AK98 Uart support. + +config SERIAL_AK39_CONSOLE + bool "Console on AK39 serial port" + depends on SERIAL_AK39_UART + select SERIAL_CORE_CONSOLE + help + Say Y here if you want enable console support on AK39xx serial port. + +config SERIAL_GPIO_UART + tristate "GPIO serial port support" + select SERIAL_CORE + depends on ARCH_AK39 + default n + help + AK39 GPIO simulate UART support. + +config SERIAL_GPIO_CONSOLE + bool "Console on GPIO serial port" + depends on SERIAL_GPIO_UART + select SERIAL_CORE_CONSOLE + help + AK39 GPIO simulate UART console support. + + endmenu diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 7257c5d8..e0d7f32f 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -79,3 +79,6 @@ obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o obj-$(CONFIG_SERIAL_SIRFSOC) += sirfsoc_uart.o obj-$(CONFIG_SERIAL_AR933X) += ar933x_uart.o obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o +obj-$(CONFIG_SERIAL_AK39_UART) += ak39_uart.o +obj-$(CONFIG_SERIAL_GPIO_UART) += gpio_uart.o + diff --git a/drivers/tty/serial/ak39_uart.c b/drivers/tty/serial/ak39_uart.c new file mode 100644 index 00000000..8d761b51 --- /dev/null +++ b/drivers/tty/serial/ak39_uart.c @@ -0,0 +1,1310 @@ +/* + * driver/tty/serial/ak39_uart.c + */ + +#if defined(CONFIG_SERIAL_AK39_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ak39_uart.h" + +extern void printch(char); +extern void printascii(const char *); + + +#if 0 +#define dbg(x...) printk(x) +#else +#define dbg(x...) do {} while(0) +#endif + +/* UART name and device definitions */ +#define NR_PORTS 2 + +#define AK39_SERIAL_NAME "ttySAK" +#define AK39_SERIAL_MAJOR 204 +#define AK39_SERIAL_MINOR 64 + + +#ifdef CONFIG_SERIAL_AK39_CONSOLE + +static struct console ak39_serial_console; + +#define AK39_SERIAL_CONSOLE &ak39_serial_console +#else +#define AK39_SERIAL_CONSOLE NULL +#endif + +struct ak39_uart_port { + char *name; + struct uart_port port; + + unsigned char __iomem *rxfifo_base; + unsigned char __iomem *txfifo_base; + + unsigned int rxfifo_offset; + unsigned int nbr_to_read; + unsigned int timeout_cnt; + + unsigned char claimed; + struct clk *clk; +#ifdef CONFIG_CPU_FREQ + struct notifier_block freq_transition; +#endif +}; + +/* macros to change one thing to another */ +#define tx_enabled(port) ((port)->unused[0]) +#define rx_enabled(port) ((port)->unused[1]) + +static int uart_intevent_decode(unsigned long status, unsigned int maskbit, unsigned int statusbit) +{ + if ((status & 1<port.membase + UART_CONF2); + uart_reg &= ~mask; + __raw_writel(uart_reg, ourport->port.membase + UART_CONF2); +} + +static inline void uart_subint_enable(struct ak39_uart_port *ourport, unsigned long unmask) +{ + unsigned long uart_reg; + + /* enable tx_end interrupt */ + uart_reg = __raw_readl(ourport->port.membase + UART_CONF2); + uart_reg |= unmask; + __raw_writel(uart_reg, ourport->port.membase + UART_CONF2); +} + +static void uart_subint_clear(struct ak39_uart_port *ourport, unsigned int subint) +{ + unsigned long uart_reg; + + switch (subint) { + + case TX_THR_INT: + uart_reg = __raw_readl(ourport->port.membase + UART_CONF2); + uart_reg |= (1<port.membase + UART_CONF2); + break; + + case RX_THR_INT: + uart_reg = __raw_readl(ourport->port.membase + UART_CONF2); + uart_reg &= AKUART_INT_MASK; + uart_reg |= (1<port.membase + UART_CONF2); + break; + + case RECVDATA_ERR_INT: + uart_reg = __raw_readl(ourport->port.membase + UART_CONF2); + uart_reg &= AKUART_INT_MASK; + uart_reg |= (0x1 << subint); + __raw_writel(uart_reg, ourport->port.membase + UART_CONF2); + break; + + case RX_TIMEOUT: + uart_reg = __raw_readl(ourport->port.membase + UART_CONF2); + uart_reg |= (0x1 << subint); + uart_reg &= ~( 0x1<<3 ); + __raw_writel(uart_reg, ourport->port.membase + UART_CONF2); + + /* start to receive data */ + uart_reg = __raw_readl(ourport->port.membase + BUF_THRESHOLD); + uart_reg |= (1 << 31); + __raw_writel(uart_reg, ourport->port.membase + BUF_THRESHOLD); + break; + + default: + printk(KERN_ERR "ak39xx 9xx 9xx 9xx 9xx 9xx 9xx 9xx 9xx kown subint type: %d\n", subint); + break; + } + + return; +} + +static inline struct ak39_uart_port *to_ourport(struct uart_port *port) +{ + return container_of(port, struct ak39_uart_port, port); +} + +static inline void uart_txend_interrupt(struct ak39_uart_port *ourport, unsigned short status) +{ + unsigned long uart_reg; + + /*handle Tx_end end interrupt */ + uart_reg = __raw_readl(ourport->port.membase + UART_CONF2); + switch(status) + { + case ENABLE: + uart_reg |= (UARTN_CONFIG2_TX_END_INT_EN); + break; + + case DISABLE: + uart_reg &= ~(UARTN_CONFIG2_TX_END_INT_EN); + break; + + default: + break; + } + __raw_writel(uart_reg, ourport->port.membase + UART_CONF2); +} + +/* clear a UARTn buffer status flag */ +static inline void clear_uart_buf_status(struct ak39_uart_port *ourport, unsigned short status) +{ + unsigned long regval; + unsigned long flags; + + local_irq_save(flags); + + regval = __raw_readl(AK_VA_L2CTRL + 0x8C); + switch(status) + { + case RX_STATUS: + regval |= (0x1 << (17 + ourport->port.line * 2)); + break; + + case TX_STATUS: + regval |= (0x1 << (16 + ourport->port.line * 2)); + break; + + default: + break; + } + __raw_writel(regval, AK_VA_L2CTRL + 0x8C); + + local_irq_restore(flags); +} + +/* clear TX and RX internal status */ +static inline void clear_internal_status(struct ak39_uart_port *ourport, unsigned short status) +{ + unsigned long regval; + unsigned long flags; + + local_irq_save(flags); + + regval = __raw_readl(ourport->port.membase + UART_CONF1); + switch(status) + { + case RX_STATUS: + __raw_writel(regval | (0x1 << 29), ourport->port.membase + UART_CONF1); + break; + + case TX_STATUS: + __raw_writel(regval | (0x1 << 28), ourport->port.membase + UART_CONF1); + break; + + default: + break; + } + + local_irq_restore(flags); +} + +/* clear TX_th and RX_th count interrupt */ +static inline void clear_Int_status(struct ak39_uart_port *ourport, unsigned short status) +{ + unsigned long regval; + unsigned long flags; + + local_irq_save(flags); + + regval = __raw_readl(ourport->port.membase + BUF_THRESHOLD); + switch(status) + { + case RX_STATUS: + __raw_writel(regval | (0x1 << 5), ourport->port.membase + BUF_THRESHOLD); + break; + + case TX_STATUS: + __raw_writel(regval | (0x1 << 11), ourport->port.membase + BUF_THRESHOLD); + break; + + default: + break; + } + + local_irq_restore(flags); +} + + +/* enable/disable interrupt of RX_th */ +static inline void uart_Rx_interrupt(struct ak39_uart_port *ourport, unsigned short status) +{ + unsigned long regval; + unsigned long flags; + + local_irq_save(flags); + + regval = __raw_readl(ourport->port.membase + UART_CONF2); + if(status) + __raw_writel(regval | (0x1 << RX_INTTERUPT), ourport->port.membase + UART_CONF2); + else + __raw_writel(regval & ~(0x1 << RX_INTTERUPT), ourport->port.membase + UART_CONF2); + + local_irq_restore(flags); +} + +static inline int uart_hwport_init(struct ak39_uart_port *ourport) +{ + /* set share pin to UARTn, and disable pull-up */ + switch (ourport->port.line) { + case 0: + ak_group_config(ePIN_AS_UART1); + + /* configuration shared pins to UART0 cdh:add &check ok*/ +// REG_VA_VAL(AK_VA_SYSCTRL, 0x74) |= ((0x1<<1)|(0x1<<2)); //0x0800,0074, cdh:check ok + + /* clear tx/rx buffer status flag */ + rL2_CONBUF8_15 |= (0x3 << 16); + /* enable buffer status bit may be changed */ + rL2_FRACDMAADDR |= (0x1 << 29); + break; + + case 1: + /* set share pin */ + ak_group_config(ePIN_AS_UART2); + + rL2_CONBUF8_15 |= (0x3 << 18); + rL2_FRACDMAADDR |= (0x1 << 29); + break; + + default: + printk(KERN_ERR "unknown uart port\n"); + return -1; + break; + } + + return 0; +} + +static int uart_enable_clock(struct ak39_uart_port *ourport, int enable) +{ + unsigned long regval; + + regval = __raw_readl(AK_VA_SYSCTRL + 0x1C); + + switch (ourport->port.line) { + case 0: + if (enable) + regval &= ~(1 << 7); + else + regval |= (1 << 7); + + case 1: + if (enable) + regval &= ~(1 << 8); + else + regval |= (1 << 8); + break; + + default: + printk(KERN_ERR "unknown uart port\n"); + return -1; + } + __raw_writel(regval, AK_VA_SYSCTRL + 0x1C); + + return 0; +} + +/* power management control */ +static void ak39_serial_pm(struct uart_port *port, unsigned int level, unsigned int old) +{ + switch (level) { + case 3: /* disable */ + //dbg("%s: enterring pm level: %d\n", __FUNCTION__, level); + break; + + case 0: /* enable */ + //dbg("%s: enterring pm level: %d\n", __FUNCTION__, level); + break; + + default: + dbg(KERN_ERR "ak39xx serial: unknown pm %d\n", level); + break; + } +} + +/* is tx fifo empty */ +static unsigned int ak39_serial_tx_empty(struct uart_port *port) +{ + unsigned long uart_reg; + + uart_reg = __raw_readl(port->membase + UART_CONF2); + + if (uart_reg & (1 << TXFIFO_EMPTY)) + return 1; + + return 0; +} + +/* no modem control lines */ +static unsigned int ak39_serial_get_mctrl(struct uart_port *port) +{ + /* FIXME */ + dbg("%s\n", __FUNCTION__); + return 0; +} + +static void ak39_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* todo - possibly remove AFC and do manual CTS */ + dbg("%s\n", __FUNCTION__); +} + + +static void ak39_serial_start_tx(struct uart_port *port) +{ + struct ak39_uart_port *ourport = to_ourport(port); + + dbg("%s\n", __FUNCTION__); + + if (!tx_enabled(port)) + { + uart_txend_interrupt(ourport, ENABLE); + tx_enabled(port) = 1; + } +} + +static void ak39_serial_stop_tx(struct uart_port *port) +{ + struct ak39_uart_port *ourport = to_ourport(port); + + dbg("%s\n", __FUNCTION__); + + if (tx_enabled(port)) + { + uart_txend_interrupt(ourport, DISABLE); + tx_enabled(port) = 0; + } +} + +static void ak39_serial_stop_rx(struct uart_port *port) +{ + struct ak39_uart_port *ourport = to_ourport(port); + + dbg("%s\n", __FUNCTION__); + + if (rx_enabled(port)) + { + uart_Rx_interrupt(ourport, DISABLE); + rx_enabled(port) = 0; + } +} + +static void ak39_serial_enable_ms(struct uart_port *port) +{ + dbg("%s\n", __FUNCTION__); +} + +static void ak39_serial_break_ctl(struct uart_port *port, int break_state) +{ + dbg("%s\n", __FUNCTION__); +} + +static irqreturn_t ak39_uart_irqhandler(int irq, void *dev_id) +{ + struct ak39_uart_port *ourport = dev_id; + struct uart_port *port = &ourport->port; + struct circ_buf *xmit = &port->state->xmit; //&port->info->xmit; + struct tty_struct *tty = port->state->port.tty; //port->info->tty; + unsigned int flag= TTY_NORMAL; + + unsigned char __iomem *pbuf; + unsigned char *pxmitbuf; + unsigned long uart_status; + unsigned int rxcount = 0; + unsigned char ch = 0; + unsigned int i; + int txcount , tx_tail; + unsigned int l2_offset = 0; + unsigned long regval; + + uart_status = __raw_readl(ourport->port.membase + UART_CONF2); + + /* clear R_err interrupt */ + if ( uart_intevent_decode(uart_status, RECVDATA_ERR_INT_ENABLE, RECVDATA_ERR_INT) ) + { + uart_subint_clear(ourport, RECVDATA_ERR_INT); + } + + if ( uart_intevent_decode(uart_status, TX_END_INTERRUPT, TX_END_STATUS) ) + { + /* if there is not anything more to transmit, or the uart is now + * stopped, disable the uart and exit + */ + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + { + ak39_serial_stop_tx(port); + goto rx_irq; + } + + txcount = uart_circ_chars_pending(xmit); + + if(txcount > 32) + txcount = 32; + pbuf = ourport->txfifo_base; + pxmitbuf = xmit->buf; + + /* clear a uartx buffer status */ + clear_uart_buf_status(ourport, TX_STATUS); + + /* clear the tx internal status */ + clear_internal_status(ourport, TX_STATUS); + + __raw_writel(0x0, ourport->txfifo_base + 0x3C); + + l2_offset = 0; + tx_tail = xmit->tail; + regval = 0; + for(i = 0; i < txcount; i++) + { + regval |= pxmitbuf[tx_tail]<<((i & 3) * 8 ); + if((i & 3) == 3) + { + __raw_writel(regval, pbuf + l2_offset); + l2_offset = l2_offset + 4; + regval = 0; + } + tx_tail = (tx_tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx += 1; + } + if(i & 3) + { + __raw_writel(regval, pbuf + l2_offset); + } + + regval = (__raw_readl(ourport->port.membase + UART_CONF2)&(~UARTN_CONFIG2_TX_BYT_CNT_MASK)) | (UARTN_CONFIG2_TX_BYT_CNT(txcount)) | (UARTN_CONFIG2_TX_BYT_CNT_VLD); + __raw_writel(regval, ourport->port.membase + UART_CONF2); + xmit->tail = tx_tail; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + ak39_serial_stop_tx(port); + } + +rx_irq: + + /* rx threshold interrupt */ + if ( uart_intevent_decode(uart_status, RX_THR_INT_ENABLE, RX_THR_INT) || + uart_intevent_decode(uart_status, RX_TIMEOUT_INT_ENABLE, RX_TIMEOUT)) + { + if ( uart_intevent_decode(uart_status, RX_THR_INT_ENABLE, RX_THR_INT)) + uart_subint_clear(ourport, RX_THR_INT); + else { + uart_subint_clear(ourport, RX_TIMEOUT); + } + + while(__raw_readl(ourport->port.membase + UART_CONF2) & (UARTN_CONFIG2_MEM_RDY)); + + ourport->nbr_to_read = (__raw_readl(ourport->port.membase + DATA_CONF)>>13) & 0x7f; + + if (ourport->nbr_to_read != ourport->rxfifo_offset) { + l2_offset = ourport->rxfifo_offset; + pbuf = ourport->rxfifo_base + l2_offset; + + /* copy data */ + if (ourport->nbr_to_read > l2_offset) { + rxcount = ourport->nbr_to_read - l2_offset; + for (i=0; irxfifo_base; + for (i=0; i < ourport->nbr_to_read; i++) { + ch = __raw_readb(pbuf + i); + uart_insert_char(port, 0, 0, ch, flag); + } + } + ourport->rxfifo_offset = ourport->nbr_to_read; + } + + tty_flip_buffer_push(tty); + } + + return IRQ_HANDLED; + +} + +static void ak39_serial_shutdown(struct uart_port *port) +{ + struct ak39_uart_port *ourport = to_ourport(port); + unsigned int uart_reg = 0; + + /* + * 1st, free irq. + * 2nd, disable/mask hw uart setting. + * 3rd, close uart clock. + */ + + /* mask all interrupt */ + __raw_writel(0, ourport->port.membase + UART_CONF2); + + uart_reg = __raw_readl(ourport->port.membase + 0xc); + uart_reg &= (~(1<<5)); + __raw_writel(uart_reg, ourport->port.membase + 0xc); + uart_reg = __raw_readl(ourport->port.membase + 0x0); + uart_reg &= (~(1<<29)); + __raw_writel(uart_reg, ourport->port.membase + 0x0); + /* clear uartx interrupt */ + uart_reg &= (~1<<21); + __raw_writel(uart_reg, ourport->port.membase + 0x0); + + /* clear uartn TX/RX buf status flag and call ak_setpin_as_gpio() */ + switch(ourport->port.line) { + case 0: + rL2_CONBUF8_15 |= (0x3<<16); + break; + case 1: + rL2_CONBUF8_15 |= (0x3<<18); + break; + } + free_irq(port->irq, ourport); + uart_enable_clock(ourport, 0); +} + +/* + * 1, setup gpio. + * 2, enable clock. + * 3, request irq and setting up uart control. + * 4, enable subirq. + */ +static int ak39_serial_startup(struct uart_port *port) +{ + struct ak39_uart_port *ourport = to_ourport(port); + unsigned long uart_reg; + int ret; + + if ( rx_enabled(port) && tx_enabled(port)) + return 0; + + /* enable uart clock */ + uart_enable_clock(ourport, 1); + + /* set share pin to UARTn */ + uart_hwport_init(ourport); + + //clear L2 Buffer + clear_uart_buf_status(ourport, RX_STATUS); + + uart_reg = UARTN_CONFIG1_RTS_EN_BY_CIRCUIT | UARTN_CONFIG1_EN |UARTN_CONFIG1_RX_STA_CLR|UARTN_CONFIG1_TX_STA_CLR|UARTN_CONFIG1_TIMEOUT_EN; + __raw_writel(uart_reg, ourport->port.membase + UART_CONF1); + + /* mask all interrupt */ + __raw_writel(0, ourport->port.membase + UART_CONF2); + + /* + * config stop bit and timeout value + * set timeout = 32, stop bit = 1; + */ + uart_reg = (0x1f << 16); + __raw_writel(uart_reg, ourport->port.membase + UART_STOPBIT_TIMEOUT); + + + /* + * set threshold to 32bytes + * set RX_th_cfg_h = 0, set RX_th_cfg_l = 31 + */ + uart_reg = __raw_readl(ourport->port.membase + DATA_CONF); + uart_reg &= ~UARTN_RX_TH_CFG_H_MASK; + __raw_writel(uart_reg, ourport->port.membase + DATA_CONF); + uart_reg = __raw_readl(ourport->port.membase + BUF_THRESHOLD); + uart_reg &= ~(UARTN_RX_TH_CFG_L_MASK); + uart_reg |= UARTN_RX_TH_CFG_L(0x1F); /* 32 Bytes */ + //uart_reg |= (1 << 11); + __raw_writel(uart_reg, ourport->port.membase + BUF_THRESHOLD); + + clear_internal_status(ourport, RX_STATUS); + + /* ourport->rxfifo_offset = 0; */ + ourport->rxfifo_offset = 0; + + /* to clear RX_th count interrupt */ + uart_reg = __raw_readl(ourport->port.membase + BUF_THRESHOLD); + uart_reg |= (UARTN_RX_TH_CLR); + __raw_writel(uart_reg, ourport->port.membase + BUF_THRESHOLD); + udelay(10); + uart_reg = __raw_readl(ourport->port.membase + BUF_THRESHOLD); + uart_reg &= ~(UARTN_RX_TH_CLR); + __raw_writel(uart_reg, ourport->port.membase + BUF_THRESHOLD); + udelay(10); + uart_reg = __raw_readl(ourport->port.membase + BUF_THRESHOLD); + uart_reg |= (UARTN_RX_START); + __raw_writel(uart_reg, ourport->port.membase + BUF_THRESHOLD); + + + /* + * enable timeout, rx mem_rdy and rx_th tx_end interrupt + */ + uart_reg = (UARTN_CONFIG2_RX_TH_INT_EN|UARTN_CONFIG2_RX_BUF_FULL_INT_EN|UARTN_CONFIG2_TIMEOUT_INT_EN|UARTN_CONFIG2_R_ERR_INT_EN); + __raw_writel(uart_reg, ourport->port.membase + UART_CONF2); + + rx_enabled(port) = 1; + tx_enabled(port) = 0; + + //ourport->rxfifo_offset =0; + + /* register interrupt */ + ret = request_irq(port->irq, ak39_uart_irqhandler, IRQF_DISABLED, ourport->name, ourport); + if (ret) { + printk(KERN_ERR "can't request irq %d for %s\n", port->irq, ourport->name); + goto startup_err; + } + return 0; + +startup_err: + ak39_serial_shutdown(port); + return ret; +} + +static void ak39_serial_set_termios(struct uart_port *port, + struct ktermios *termios, struct ktermios *old) +{ + struct ak39_uart_port *ourport = to_ourport(port); + unsigned int baud; + unsigned long flags; + unsigned long regval; + unsigned long asic_clk; + + asic_clk = ak_get_asic_clk(); + + termios->c_cflag &= ~(HUPCL | CMSPAR); + termios->c_cflag |= CLOCAL; + + /* + * Ask the core to calculate the divisor for us. + * min: 2.4kbps, max: 2.4Mbps + */ +#ifdef CONFIG_AEC_DUMP_DEBUG + baud = 921600; +#else + baud = uart_get_baud_rate(port, termios, old, 2400, 115200*20); +#endif + + spin_lock_irqsave(&port->lock, flags); + + /* baudrate setting */ + regval = __raw_readl(port->membase + UART_CONF1); + regval &= ~(0xffff); + regval &= ~(0x1 << 22); + regval |= ((asic_clk / baud - 1) & 0xffff); + regval |= (1 << 28) | (1 << 29); + + if (asic_clk % baud) + regval |= (0x1 << 22); + + ourport->rxfifo_offset = 0; + + /* flow control setting */ + if(port->line != 0) + { + if((termios->c_cflag & CRTSCTS)) /* directly */ + { + switch (port->line) { + case 1: + AK_GPIO_UART1_FLOW(1); + printk(KERN_WARNING "%s: Hardware flow control is open\n", __func__); + break; + } + regval &= ~(1<<18|1<<19); + } + else /* inversly */ + { + switch(port->line) { + case 1: +// ak_setpin_as_gpio(8); +// ak_setpin_as_gpio(9); + break; + } + regval &= ~(1<<18|1<<19); + } + } + + /* parity setting */ + if (termios->c_cflag & PARENB) { + if (termios->c_cflag & PARODD) + regval |= (0x2 << 25); /* odd parity */ + else + regval |= (0x3 << 25); /* evnt parity*/ + } + __raw_writel(regval, port->membase + UART_CONF1); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + /* + * Which character status flags should we ignore? + */ + port->ignore_status_mask = 0; + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *ak39_serial_type(struct uart_port *port) +{ + switch (port->type) + { + case PORT_AK39: + return "AK39"; + default: + return NULL; + } +} + +static void ak39_serial_release_port(struct uart_port *port) +{ + dbg("%s\n", __FUNCTION__); +} + +static int ak39_serial_request_port(struct uart_port *port) +{ + dbg("%s\n", __FUNCTION__); + return 0; +} + +static void ak39_serial_config_port(struct uart_port *port, int flags) +{ + struct ak39_uart_port *ourport = to_ourport(port); + + port->type = PORT_AK39; + ourport->rxfifo_offset = 0; +} + +/* + * verify the new serial_struct (for TIOCSSERIAL). + */ +static int +ak39_serial_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + dbg("%s\n", __FUNCTION__); + + return 0; +} + + +static struct uart_ops ak39_serial_ops = { + .pm = ak39_serial_pm, + .tx_empty = ak39_serial_tx_empty, + .get_mctrl = ak39_serial_get_mctrl, + .set_mctrl = ak39_serial_set_mctrl, + .stop_tx = ak39_serial_stop_tx, + .start_tx = ak39_serial_start_tx, + .stop_rx = ak39_serial_stop_rx, + .enable_ms = ak39_serial_enable_ms, + .break_ctl = ak39_serial_break_ctl, + .startup = ak39_serial_startup, + .shutdown = ak39_serial_shutdown, + .set_termios = ak39_serial_set_termios, + .type = ak39_serial_type, + .release_port = ak39_serial_release_port, + .request_port = ak39_serial_request_port, + .config_port = ak39_serial_config_port, + .verify_port = ak39_serial_verify_port, +}; + + +static struct ak39_uart_port ak39_serial_ports[NR_PORTS] = { + [0] = { + .name = "uart0", + .rxfifo_base = AK39_UART0_RXBUF_BASE, + .txfifo_base = AK39_UART0_TXBUF_BASE, + .port = { + .lock = __SPIN_LOCK_UNLOCKED(ak39_serial_ports[0].port.lock), + .iotype = UPIO_MEM, + .mapbase = AK39_UART0_PA_BASE, + .membase = AK39_UART0_BASE, + .irq = IRQ_UART0, + .uartclk = 0, + .fifosize = 64, + .ops = &ak39_serial_ops, + .flags = UPF_BOOT_AUTOCONF, + .line = 0, + }, + #ifdef CONFIG_CPU_FREQ + .freq_transition = { + .priority = INT_MAX -1, + }, + #endif + }, + [1] = { + .name = "uart1", + .rxfifo_base = AK39_UART1_RXBUF_BASE, + .txfifo_base = AK39_UART1_TXBUF_BASE, + .port = { + .lock = __SPIN_LOCK_UNLOCKED(ak39_serial_ports[1].port.lock), + .iotype = UPIO_MEM, + .mapbase = AK39_UART1_PA_BASE, + .membase = AK39_UART1_BASE, + .irq = IRQ_UART1, + .uartclk = 0, + .fifosize = 64, + .ops = &ak39_serial_ops, + .flags = UPF_BOOT_AUTOCONF, + .line = 1, + }, + }, +}; + +static struct uart_driver ak39_uart_drv = { + .owner = THIS_MODULE, + .dev_name = AK39_SERIAL_NAME, + .driver_name = AK39_SERIAL_NAME, + .nr = NR_PORTS, + .major = AK39_SERIAL_MAJOR, + .minor = AK39_SERIAL_MINOR, + .cons = AK39_SERIAL_CONSOLE, +}; + +#ifdef CONFIG_CPU_FREQ + +static int ak39_serial_cpufreq_transition(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct ak39_uart_port *port; + struct uart_port *uport; + + port = container_of(nb, struct ak39_uart_port, freq_transition); + uport = &port->port; + + if(val == CPUFREQ_PRECHANGE){ + /* we should really shut the port down whilst the + * frequency change is in progress. */ + } + else if(val == CPUFREQ_POSTCHANGE) { + + struct ktermios *termios; + struct tty_struct *tty; + + if (uport->state == NULL) + goto exit; + + tty = uport->state->port.tty; + if (tty == NULL) + goto exit; + + termios = tty->termios; + if (termios == NULL) { + printk(KERN_WARNING "%s: no termios?\n", __func__); + goto exit; + } + + ak39_serial_set_termios(uport, termios, NULL); + } +exit: + return 0; + +} + +static inline int ak39_serial_cpufreq_register(struct ak39_uart_port *port) +{ + port->freq_transition.notifier_call = ak39_serial_cpufreq_transition; + + return cpufreq_register_notifier(&port->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +static inline void ak39_serial_cpufreq_unregister(struct ak39_uart_port *port) +{ + cpufreq_unregister_notifier(&port->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} +#else +static inline int ak39_serial_cpufreq_register(struct ak39_uart_port *port) +{ + return 0; +} + +static inline void ak39_serial_cpufreq_unregister(struct ak39_uart_port *port) +{ +} + +#endif + + +/* ak39_serial_init_port + * + * initialise a single serial port from the platform device given + */ +static int ak39_serial_init_port(struct ak39_uart_port *ourport, + struct platform_device *platdev) +{ + struct uart_port *port = &ourport->port; + + if (platdev == NULL) + return -ENODEV; + + /* setup info for port */ + port->dev = &platdev->dev; + + ourport->clk = clk_get(port->dev, "asic_clk"); + + return 0; +} + +static int ak39_serial_probe(struct platform_device *dev) +{ + struct ak39_uart_port *ourport; + int ret = 0; + + ourport = &ak39_serial_ports[dev->id]; + + ret = ak39_serial_init_port(ourport, dev); + if (ret < 0) + goto probe_err; + + uart_add_one_port(&ak39_uart_drv, &ourport->port); + + platform_set_drvdata(dev, &ourport->port); + + ret = ak39_serial_cpufreq_register(ourport); + if(ret < 0) + dev_err(&dev->dev, "faild to add cpufreq notifier\n"); + return 0; + +probe_err: + return ret; +} + +static int ak39_serial_remove(struct platform_device *dev) +{ + struct uart_port *port = (struct uart_port *)dev_get_drvdata(&dev->dev); + + if (port) { + ak39_serial_cpufreq_unregister(to_ourport(port)); + uart_remove_one_port(&ak39_uart_drv, port); + } + return 0; +} + +/* UART power management code */ + +#ifdef CONFIG_PM + +static int ak39_serial_suspend(struct platform_device *dev, pm_message_t state) +{ + struct uart_port *port = (struct uart_port *)dev_get_drvdata(&dev->dev); + + if (port) + uart_suspend_port(&ak39_uart_drv, port); + return 0; +} + +static int ak39_serial_resume(struct platform_device *dev) +{ + struct uart_port *port = (struct uart_port *)dev_get_drvdata(&dev->dev); + + if (port) + uart_resume_port(&ak39_uart_drv, port); + return 0; +} +#else +#define ak39_serial_suspend NULL +#define ak39_serial_resume NULL +#endif + +static struct platform_driver ak39_serial_drv = { + .probe = ak39_serial_probe, + .remove = ak39_serial_remove, + .suspend = ak39_serial_suspend, + .resume = ak39_serial_resume, + .driver = { + .name = "ak39-uart", + .owner = THIS_MODULE, + }, +}; + + +/* module initialisation code */ +static int __init ak39_serial_modinit(void) +{ + int ret; + + printk("AK39xx uart driver init, (c) 2013 ANYKA\n"); + + //register + ret = uart_register_driver(&ak39_uart_drv); + if (ret < 0) { + printk(KERN_ERR "failed to register UART driver\n"); + return -1; + } + + platform_driver_register(&ak39_serial_drv); + + return 0; +} + +static void __exit ak39_serial_modexit(void) +{ + platform_driver_unregister(&ak39_serial_drv); + + uart_unregister_driver(&ak39_uart_drv); +} + +module_init(ak39_serial_modinit); +module_exit(ak39_serial_modexit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("anyka"); +MODULE_DESCRIPTION("Anyka Serial port driver"); + + +/************* Console code ************/ +#ifdef CONFIG_SERIAL_AK39_CONSOLE + +struct uart_port *cons_uart; + +static inline void ak39_uart_putchar(struct ak39_uart_port *ourport, unsigned char ch) +{ + unsigned long regval; + + /* clear the tx internal status */ + clear_internal_status(ourport, TX_STATUS); + + /* clear a uartx buffer status */ + clear_uart_buf_status(ourport, TX_STATUS); + + /*to inform the buf is full*/ + __raw_writel(ch, ourport->txfifo_base); + __raw_writel(0x0, ourport->txfifo_base + 0x3C); + + /* to clear TX_th count interrupt */ + clear_Int_status(ourport, TX_STATUS); + + /* start to transmit */ + regval = __raw_readl(ourport->port.membase + UART_CONF2); + regval &= AKUART_INT_MASK; + regval |= (0x1<<4) | (0x1<<16); + __raw_writel(regval, ourport->port.membase + UART_CONF2); + + + /* wait for tx end */ + while (!(__raw_readl(ourport->port.membase + UART_CONF2) & (1 << TX_END_STATUS))) + ; +} + +static inline void ak39_uart_send(struct ak39_uart_port *ourport, const char *s, unsigned int count) +{ + unsigned long regval; + unsigned int left, trans_size; + int offset; + + /* clear the tx internal status */ + clear_internal_status(ourport, TX_STATUS); + + /* clear a uartx buffer status */ + clear_uart_buf_status(ourport, TX_STATUS); + + left = count; + + while (left) + { + trans_size = 128; // L2 buffer for UART is 128 bytes + if (left < trans_size) + trans_size = left; + + if ((int)s & 0x3) // this should not happen + return; + + // copy + for (offset=0; offsettxfifo_base + offset); + } + left -= trans_size; + s += trans_size; + + if ((offset&0x3f) != 0) + {// when an L2 block is not filled, write last dword to inform the buf is full + __raw_writel(0x0, ourport->txfifo_base + offset - (offset&0x3f) + 0x3C); + } + + /* to clear TX_th count interrupt */ + clear_Int_status(ourport, TX_STATUS); + + /* start to transmit */ + regval = __raw_readl(ourport->port.membase + UART_CONF2); + regval &= AKUART_INT_MASK; + regval |= (trans_size<<4) | (0x1<<16); + __raw_writel(regval, ourport->port.membase + UART_CONF2); + + /* wait for tx end */ + while (!(__raw_readl(ourport->port.membase + UART_CONF2) & (1 << TX_END_STATUS))) + ; + } +} + +static inline void ak39_wait_for_txend(struct ak39_uart_port *ourport) +{ + unsigned int timeout = 10000; + + /* + * Wait up to 10ms for the character(s) to be sent + */ + while (!(__raw_readl(ourport->port.membase + UART_CONF2) & (1 << TX_END_STATUS))) { + if (--timeout == 0) + break; + udelay(1); + } +} + +static void +ak39_serial_console_putchar(struct uart_port *port, int ch) +{ + struct ak39_uart_port *ourport = to_ourport(port); + + ak39_wait_for_txend(ourport); + + ak39_uart_putchar(ourport, ch); +} + +static void +ak39_serial_console_write(struct console *co, const char *s, unsigned int count) +{ + uart_console_write(cons_uart, s, count, ak39_serial_console_putchar); +} + +void +ak39_serial_console_send(struct uart_port *port, const char *s, unsigned int count) +{ + struct ak39_uart_port *ourport = to_ourport(port); + + ak39_wait_for_txend(ourport); + ak39_uart_send(ourport, s, count); +} + +static void __init +ak39_serial_get_options(struct uart_port *port, int *baud, int *parity, int *bits) +{ + +#if 0 + unsigned long regval; + struct clk *clk; + + *bits = 8; + + regval = __raw_readl(port->membase + UART_CONF1); + + if (regval & 0x1<<26) { + if (regval & 0x1<<25) + *parity = 'e'; + else + *parity = 'o'; + } + else + *parity = 'n'; + + clk = clk_get(port->dev, "asic_clk"); + if (!IS_ERR(clk) && clk != NULL) + *baud = clk_get_rate(clk) / ((regval & 0xFFFF) + 1); + + printk("calculated baudrate: %d\n", *baud); +#endif +} + + +static int __init +ak39_serial_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + dbg("ak39_serial_console_setup: co=%p (%d), %s\n", co, co->index, options); + + port = &ak39_serial_ports[co->index].port; + + /* is this a valid port */ + + if (co->index == -1 || co->index >= NR_PORTS) + co->index = 0; + + dbg("ak39_serial_console_setup: port=%p (%d)\n", port, co->index); + + cons_uart = port; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + ak39_serial_get_options(port, &baud, &parity, &bits); + + dbg("ak39_serial_console_setup: baud %d\n", baud); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + + +static struct console ak39_serial_console = { + .name = AK39_SERIAL_NAME, + .device = uart_console_device, + .flags = CON_PRINTBUFFER, + .index = -1, + .write = ak39_serial_console_write, + .setup = ak39_serial_console_setup +}; + + +/* ak39_serial_initconsole + * + * initialise the console from one of the uart drivers +*/ +static int ak39_serial_initconsole(void) +{ + printk("AK39 console driver initial\n"); + + ak39_serial_console.data = &ak39_uart_drv; + + register_console(&ak39_serial_console); + + return 0; +} + +console_initcall(ak39_serial_initconsole); + +#endif /* CONFIG_SERIAL_AK39_CONSOLE */ + diff --git a/drivers/tty/serial/ak39_uart.h b/drivers/tty/serial/ak39_uart.h new file mode 100644 index 00000000..20f5b76d --- /dev/null +++ b/drivers/tty/serial/ak39_uart.h @@ -0,0 +1,125 @@ +#ifndef __AK39_UART_H_ +#define __AK39_UART_H_ + +#define AK39_UART0_TXBUF_BASE REG_VA_ADDR(AK_VA_L2MEM, 0x1000) +#define AK39_UART0_RXBUF_BASE REG_VA_ADDR(AK_VA_L2MEM, 0x1080) +#define AK39_UART1_TXBUF_BASE REG_VA_ADDR(AK_VA_L2MEM, 0x1100) +#define AK39_UART1_RXBUF_BASE REG_VA_ADDR(AK_VA_L2MEM, 0x1180) + +#define AK39_UART0_BASE REG_VA_ADDR(AK_VA_UART, 0x0000) +#define AK39_UART1_BASE REG_VA_ADDR(AK_VA_UART, 0x8000) + +#define AK39_UART0_PA_BASE REG_PA_ADDR(AK_PA_UART, 0x0000) +#define AK39_UART1_PA_BASE REG_PA_ADDR(AK_PA_UART, 0x8000) + + +#define UART_CONF1 0x00 +#define UART_CONF2 0x04 +#define DATA_CONF 0x08 +#define BUF_THRESHOLD 0x0C +#define UART_RXBUF 0x10 +#define UART_STOPBIT_TIMEOUT (0x18) + +#define RX_THR_INT_ENABLE (28) +#define TX_END_INTERRUPT (27) +#define TXBUF_EMP_INT_ENABLE (24) +#define RECVDATA_ERR_INT_ENABLE (23) +#define RX_TIMEOUT_INT_ENABLE (22) +#define MEM_RDY_INT_ENABLE (21) +#define TX_THR_INT (31) +#define RX_THR_INT (30) +#define TX_END_STATUS (19) +#define RX_OV (18) +#define MEM_RDY_INT (17) +#define TX_BYT_CNT_VLD (16) +#define RECVDATA_ERR_INT (3) +#define RX_TIMEOUT (2) +#define RXBUF_FULL (1) +#define TXFIFO_EMPTY (0) + +#define TX_INTTERUPT (29) +#define RX_INTTERUPT (28) + +#define TX_STATUS (1) +#define RX_STATUS (0) +#define DISABLE (0) +#define ENABLE (1) +#define AKUART_INT_MASK 0x3FE00000 +#define UART_RX_FIFO_SIZE 128 + +// Configuration Register 1 of UARTn +#define UARTN_CONFIG1_DIV_CNT(cnt) ((cnt) & 0xffff) +#define UARTN_CONFIG1_UTD_INVERSELY (1 << 16) +#define UARTN_CONFIG1_URD_INVERSELY (1 << 17) +#define UARTN_CONFIG1_CTS_INVERSELY (1 << 18) +#define UARTN_CONFIG1_RTS_INVERSELY (1 << 19) +#define UARTN_CONFIG1_RTS_EN_BY_CIRCUIT (1 << 20) +#define UARTN_CONFIG1_EN (1 << 21) +#define UARTN_CONFIG1_DIV_ADJ_EN (1 << 22) +#define UARTN_CONFIG1_TIMEOUT_EN (1 << 23) +#define UARTN_CONFIG1_PAR_EVEN (1 << 25) +#define UARTN_CONFIG1_PAR_EN (1 << 26) +#define UARTN_CONFIG1_ENDIAN_BIG (1 << 27) +#define UARTN_CONFIG1_TX_STA_CLR (1 << 28) +#define UARTN_CONFIG1_RX_STA_CLR (1 << 29) +#define UARTN_RX_ADDR_CLR (1 << 30) +#define UARTN_TX_ADDR_CLR (1 << 31) + +// Configuration Register 2 of UARTn +#define UARTN_CONFIG2_TX_FIFO_EMPTY (1 << 0) // read only +#define UARTN_CONFIG2_RX_BUF_FULL (1 << 1) // write clear +#define UARTN_CONFIG2_TIMEOUT (1 << 2) // write clear +#define UARTN_CONFIG2_R_ERR (1 << 3) // write clear +#define UARTN_CONFIG2_TX_BYT_CNT(cnt) (cnt << 4) +#define UARTN_CONFIG2_TX_BYT_CNT_MASK (0xfff << 4) +#define UARTN_CONFIG2_TX_BYT_CNT_VLD (1 << 16) // auto clear +#define UARTN_CONFIG2_MEM_RDY (1 << 17) // read only + +#define UARTN_CONFIG2_TX_END (1 << 19) // read only + +#define UARTN_CONFIG2_RX_BUF_FULL_INT_EN (1 << 21) +#define UARTN_CONFIG2_TIMEOUT_INT_EN (1 << 22) +#define UARTN_CONFIG2_R_ERR_INT_EN (1 << 23) +#define UARTN_CONFIG2_TX_BUF_EMP_INT_EN (1 << 24) + +#define UARTN_CONFIG2_TX_END_INT_EN (1 << 27) +#define UARTN_CONFIG2_RX_TH_INT_EN (1 << 28) +#define UARTN_CONFIG2_TX_TH_INT_EN (1 << 29) + +#define UARTN_CONFIG2_RX_TH_STA (1 << 30) // write clear +#define UARTN_CONFIG2_TX_TH_STA (1 << 31) // write clear + +// Data Configuration Register of UARTn +#define UARTN_DATACONFIG_TX_BYT_SUM(rval) ((rval) & 0x1FFF) +#define UARTN_DATACONFIG_RX_ADR(rval) (((rval) >> 13) & 0x7F) +#define UARTN_DATACONFIG_TX_ADR(rval) (((rval) >> 20) & 0x1F) +#define UARTN_RX_TH_CFG_H_MASK (0x7f >> 25) +#define UARTN_RX_TH_CFG_H(value) (((value & 0x7f) >> 25)) + + +// TX RX Data Threshold Resgister +#define UARTN_RX_TH_CFG_L_MASK (0x1f) +#define UARTN_RX_TH_CFG_L(value) (value) + +#define UARTN_RX_TH_CLR (1 << 5) +#define UARTN_TX_TH_CFG(value) (((value) & 0x1f) << 6) +#define UARTN_TX_TH_CLR (1 << 11) +#define UARTN_RX_TH_CNT(rvalue) (((rvalue) >> 12) & 0xFFF) // read only +#define UARTN_TX_TH_CNT(rvalue) (((rvalue) >> 24) & 0x1F) // read only +#define UARTN_BFIFO_BYTE_NUM(rvalue) (((rvalue) >> 29) & 0x03) // read only +#define UARTN_RX_START (1 <<31) + + +//Stop Bit Timeout Configuration Register + +#define UARTN_BIT_CFG(value) ((value) & 0x1ff) +#define UARTN_TIME_OUT_CFG(value) (((value) >> 16) & 0x0ffff) + + +#undef REG_VA_VAL +#define REG_VA_VAL(base_addr, offset) (*(volatile unsigned long *)((base_addr) + (offset))) + +#define rL2_FRACDMAADDR REG_VA_VAL(AK_VA_L2CTRL, 0x84) +#define rL2_CONBUF8_15 REG_VA_VAL(AK_VA_L2CTRL, 0x8C) + +#endif /* end __AK39_UART_H_ */ diff --git a/drivers/tty/serial/gpio_uart.c b/drivers/tty/serial/gpio_uart.c new file mode 100644 index 00000000..534b0191 --- /dev/null +++ b/drivers/tty/serial/gpio_uart.c @@ -0,0 +1,393 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define TIMER_CLOCK (12000000) /* Hz */ +#define GPIO_SERIAL_NAME ("ttySAK") +#define GPIO_SERIAL_NR_PORTS (1) +#define FAKE_PORT_MEMBASE ((unsigned char __iomem*)(0xffffffff)) +#define FAKE_PORT_MAPBASE ((resource_size_t)(0xffffffff)) + +struct gpio_uart { + int baud; + int parity; + int bits; + int flow; + + int timer_count; + int correction; /* correction coefficient */ + + struct ak_pwm_timer *timer; + struct completion comp; + struct gpio_info tx_pin; +}; + +static struct gpio_uart guart = { + /* UART configure: 115200/75, 38400/80, 19200/80, 9600/80 */ + .baud = 115200, + .parity = 'n', + .bits = 8, + .flow = 'n', + .correction = 55, + + /* TX GPIO pin configure */ + .tx_pin = { + .pin = AK_GPIO_2, + .dir = AK_GPIO_DIR_OUTPUT, + .pullup = AK_PULLUP_DISABLE, + .pulldown = -1, + .value = AK_GPIO_OUT_HIGH, + .int_pol = -1, + }, +}; + +#ifdef CONFIG_SERIAL_CORE_CONSOLE +#define uart_console(port) ((port)->cons && (port)->cons->index == (port)->line) +#else +#define uart_console(port) (0) +#endif + +#ifdef CONFIG_SERIAL_GPIO_CONSOLE +static struct uart_port port; +static struct uart_driver gpio_uart_driver; +static void gpio_putchar(unsigned char value); + + +static void gpio_uart_console_write(struct console *co, const char *s, unsigned count) +{ + unsigned int i; + const char *ts = s; + + for (i = 0; i < count; i++, ts++) { + if (*ts == '\n') + gpio_putchar('\r'); + gpio_putchar(*ts); + } +} + +static struct tty_driver *gpio_uart_console_device(struct console *co, int *index) +{ + struct uart_driver *p = co->data; + + *index = co->index; + return p->tty_driver; +} + +static int gpio_uart_console_setup(struct console *co, char *options) +{ + int baud; + int bits; + int parity; + int flow; + + ak_gpio_set(&guart.tx_pin); + guart.timer_count = TIMER_CLOCK / guart.baud - guart.correction; + port.mapbase = FAKE_PORT_MAPBASE; + port.membase = FAKE_PORT_MEMBASE; + + if (options) { + uart_parse_options(options, &baud, &parity, &bits, &flow); + } else { + baud = guart.baud; + bits = guart.bits; + parity = guart.parity; + flow = guart.flow; + } + + return uart_set_options(&port, co, baud, parity, bits, flow); +} + +static struct console gpio_serial_console = { + .name = GPIO_SERIAL_NAME, + .write = gpio_uart_console_write, + .device = gpio_uart_console_device, + .setup = gpio_uart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &gpio_uart_driver, +}; + +static int __init gpio_serial_console_init(void) +{ + guart.timer = ak_timer_request(AK_PWM_TIMER2, 0, NULL); + + register_console(&gpio_serial_console); + return 0; +} +console_initcall(gpio_serial_console_init); + +#define GPIO_SERIAL_CONSOLE &gpio_serial_console +#else +#define GPIO_SERIAL_CONSOLE NULL +#endif +#if 0 +static void gpio_delay(int count) +{ +#if 0 + int delay = 1000*1000/115200; + udelay(delay); + return; +#endif + + REG32(guart.membase) = (count << 0); + + REG32(guart.membase+0x04) = (1 << 30); + REG32(guart.membase+0x04) |= (1 << 24); + REG32(guart.membase+0x04) |= (1 << 29) | (1 << 28); + + while(!(REG32(guart.membase+0x04) & (1 << 27))) + ; +} +#else + +static void gpio_delay(int count) +{ + init_completion(&guart.comp); + + ak_timer_config(guart.timer, count, 0); + ak_timer_enable_sync(guart.timer); +} + +#endif + +static void gpio_putchar(unsigned char value) +{ + int count = 8; + + /* send start bit */ + ak_gpio_setpin(guart.tx_pin.pin, AK_GPIO_OUT_LOW); + gpio_delay(guart.timer_count); + + /* send data, no parity */ + while(count--) { + ak_gpio_setpin(guart.tx_pin.pin, (value & 0x1)); + gpio_delay(guart.timer_count); + value >>= 1; + } + + /* send stop bit */ + ak_gpio_setpin(guart.tx_pin.pin, AK_GPIO_OUT_HIGH); + gpio_delay(guart.timer_count); +} + +static void gpio_uart_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) +{ + switch (state) { + case 3: + break; + case 0: + break; + default: + break; + } +} + +static unsigned int gpio_uart_tx_empty(struct uart_port *port) +{ + return 1; +} + +static unsigned int gpio_uart_get_mctrl(struct uart_port *port) +{ + return 0; +} + +static void gpio_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +static void gpio_uart_stop_tx(struct uart_port *port) +{ +} + +static void gpio_uart_start_tx(struct uart_port *port) +{ + struct uart_state *state = port->state; + struct circ_buf *circ = &state->xmit; + int txcount = uart_circ_chars_pending(circ); + unsigned char value = 0; + int i; + + for (i = 0; i < txcount; i++) { + value = circ->buf[circ->tail]; + gpio_putchar(value); + circ->tail = (circ->tail + 1) & (UART_XMIT_SIZE - 1); + } +} + +static void gpio_uart_stop_rx(struct uart_port *port) +{ +} + +static void gpio_uart_enable_ms(struct uart_port *port) +{ +} + +static void gpio_uart_break_ctl(struct uart_port *port, int break_state) +{ +} + +static int gpio_uart_startup(struct uart_port *port) +{ + return 0; +} + +static void gpio_uart_shutdown(struct uart_port *port) +{ +} + +static void gpio_uart_set_termios(struct uart_port *port, + struct ktermios *termios, struct ktermios *old) +{ +} + +static const char *gpio_uart_type(struct uart_port *port) +{ + if (port->type == PORT_GPIO) + return "GPIO"; + else + return NULL; +} + +static void gpio_uart_release_port(struct uart_port *port) +{ +} + +static int gpio_uart_request_port(struct uart_port *port) +{ + return 0; +} + +static void gpio_uart_config_port(struct uart_port *port, int flags) +{ + port->type = PORT_GPIO; +} + +static int gpio_uart_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + return 0; +} + +static struct uart_ops gpio_uart_ops = { + .pm = gpio_uart_pm, + .tx_empty = gpio_uart_tx_empty, + .get_mctrl = gpio_uart_get_mctrl, + .set_mctrl = gpio_uart_set_mctrl, + .stop_tx = gpio_uart_stop_tx, + .start_tx = gpio_uart_start_tx, + .stop_rx = gpio_uart_stop_rx, + .enable_ms = gpio_uart_enable_ms, + .break_ctl = gpio_uart_break_ctl, + .startup = gpio_uart_startup, + .shutdown = gpio_uart_shutdown, + .set_termios = gpio_uart_set_termios, + .type = gpio_uart_type, + .release_port = gpio_uart_release_port, + .request_port = gpio_uart_request_port, + .config_port = gpio_uart_config_port, + .verify_port = gpio_uart_verify_port, +}; + +static struct uart_port port = { + .iotype = UPIO_MEM, + .ops = &gpio_uart_ops, + .flags = UPF_BOOT_AUTOCONF, + .line = 0, + .cons = GPIO_SERIAL_CONSOLE, +}; + +static struct uart_driver gpio_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "gpio-uart", + .dev_name = GPIO_SERIAL_NAME, + .major = 0, + .minor = 0, + .nr = GPIO_SERIAL_NR_PORTS, + .cons = GPIO_SERIAL_CONSOLE, +}; + +static int gpio_uart_probe(struct platform_device *pdev) +{ + int ret; + + if (!uart_console(&port)) { + port.membase = FAKE_PORT_MEMBASE; + port.mapbase = FAKE_PORT_MAPBASE; + guart.timer_count = TIMER_CLOCK / guart.baud - guart.correction; + ak_gpio_set(&guart.tx_pin); + guart.timer = ak_timer_request(AK_PWM_TIMER2, 0, NULL); + } + + ret = uart_register_driver(&gpio_uart_driver); + if (ret) { + printk("UART register driver failed\n"); + return ret; + } + + port.dev = &pdev->dev; + ret = uart_add_one_port(&gpio_uart_driver, &port); + if (ret) { + printk("Add gpio uart port failed\n"); + return ret; + } + + platform_set_drvdata(pdev, &port); + + printk("ak gpio uart initialize success.\n"); + return 0; +} + +static int gpio_uart_remove(struct platform_device *pdev) +{ + platform_set_drvdata(pdev, NULL); + uart_remove_one_port(&gpio_uart_driver, &port); + uart_unregister_driver(&gpio_uart_driver); + return 0; +} + +static int gpio_uart_suspend(struct platform_device *pdev, pm_message_t state) +{ + return 0; +} + +static int gpio_uart_resume(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver gpio_uart_pdrv = { + .probe = gpio_uart_probe, + .remove = gpio_uart_remove, + .suspend = gpio_uart_suspend, + .resume = gpio_uart_resume, + .driver = { + .name = "gpio-uart", + .owner = THIS_MODULE, + }, +}; + +static int __init gpio_uart_init(void) +{ + printk("AK GPIO UART Driver (c) 2013 ANYKA.\n"); + return platform_driver_register(&gpio_uart_pdrv); +} + +static void __exit gpio_uart_exit(void) +{ + platform_driver_unregister(&gpio_uart_pdrv); +} +module_init(gpio_uart_init); +module_exit(gpio_uart_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("J.C.Zhong "); +MODULE_DESCRIPTION("GPIO simulate UART driver"); diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index d8b0aee3..c49f3bf6 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -427,6 +427,8 @@ static void s3c24xx_serial_shutdown(struct uart_port *port) if (ourport->tx_claimed) { if (!s3c24xx_serial_has_interrupt_mask(port)) free_irq(ourport->tx_irq, ourport); + else + free_irq(port->irq, ourport); tx_enabled(port) = 0; ourport->tx_claimed = 0; } @@ -434,6 +436,8 @@ static void s3c24xx_serial_shutdown(struct uart_port *port) if (ourport->rx_claimed) { if (!s3c24xx_serial_has_interrupt_mask(port)) free_irq(ourport->rx_irq, ourport); + /* else already freed above as the s3c64xx_serial_startup() + * will have set both tx_claimed and rx_claimed */ ourport->rx_claimed = 0; rx_enabled(port) = 0; } diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 246b823c..a4895dcc 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -94,6 +94,9 @@ static void __uart_start(struct tty_struct *tty) struct uart_state *state = tty->driver_data; struct uart_port *port = state->uart_port; + if (port->ops->wake_peer) + port->ops->wake_peer(port); + if (!uart_circ_empty(&state->xmit) && state->xmit.buf && !tty->stopped && !tty->hw_stopped) port->ops->start_tx(port); diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig index 6f3ea9bb..19b1ab55 100644 --- a/drivers/uio/Kconfig +++ b/drivers/uio/Kconfig @@ -111,4 +111,12 @@ config UIO_PRUSS To compile this driver as a module, choose M here: the module will be called uio_pruss. +config UIO_VCODEC + tristate "Anyka Video Codec support" + help + This driver supports Video HW Codec on Anyka SoC. + + To compile this driver as a module, choose M here: the module + will be called uio_video_codec. + endif diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile index d4dd9a55..9da3159e 100644 --- a/drivers/uio/Makefile +++ b/drivers/uio/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_UIO_SERCOS3) += uio_sercos3.o obj-$(CONFIG_UIO_PCI_GENERIC) += uio_pci_generic.o obj-$(CONFIG_UIO_NETX) += uio_netx.o obj-$(CONFIG_UIO_PRUSS) += uio_pruss.o +obj-$(CONFIG_UIO_VCODEC) += uio_video_codec.o \ No newline at end of file diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index a783d533..c3612cf3 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -503,6 +503,19 @@ static int uio_release(struct inode *inode, struct file *filep) return ret; } +static long uio_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + struct uio_listener *listener = filep->private_data; + struct uio_device *idev = listener->dev; + int ret; + + ret = EINVAL; + if (idev->info->ioctl) + ret = idev->info->ioctl(idev->info, cmd, arg); + + return ret; +} + static unsigned int uio_poll(struct file *filep, poll_table *wait) { struct uio_listener *listener = filep->private_data; @@ -721,6 +734,7 @@ static const struct file_operations uio_fops = { .poll = uio_poll, .fasync = uio_fasync, .llseek = noop_llseek, + .unlocked_ioctl = uio_ioctl, }; static int uio_major_init(void) diff --git a/drivers/uio/uio_video_codec.c b/drivers/uio/uio_video_codec.c new file mode 100644 index 00000000..1edd4403 --- /dev/null +++ b/drivers/uio/uio_video_codec.c @@ -0,0 +1,563 @@ +/** + * drivers/uio/uio_video_codec.c + * + * Userspace I/O driver for anyka soc video hardware codec. + * Based on uio_pdrv.c by Uwe Kleine-Koenig, + * + * Jacky Lau + * 2011-07-05 + * + * Copyright (C) 2011 by Anyka Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + + +#define DRIVER_NAME "uio_vcodec" + +#define AL5_BASE_OFFSET 0x8000 +#define AL5_INTERRUPT_MASK (AL5_BASE_OFFSET + 0x14) +#define AL5_INTERRUPT (AL5_BASE_OFFSET + 0x18) + +#define init_MUTEX(sem) sema_init(sem, 1) +#define init_MUTEX_LOCKED(sem) sema_init(sem, 0) + +/* IRQs of hw codec */ +const unsigned VIDEO_IRQ_MASK = (1 << IRQ_VIDEO_ENCODER); +const int MASK_BITS_NUM = sizeof(VIDEO_IRQ_MASK) * 8; + +#if 0 +/* platform data of this driver */ +struct uio_platdata { + struct uio_info *uioinfo; + struct semaphore vcodec_sem; + unsigned int open_count; +}; +#endif + +struct r_irq +{ + struct list_head list; + u32 bitfield; +}; + + +struct uio_platdata { + struct uio_info *uioinfo; + wait_queue_head_t irq_queue; //irq queue + int unblock; + struct list_head irq_masks;//iqr mask list + spinlock_t i_lock; //lock for list + struct kmem_cache* cache; + unsigned int open_count; + int irq; + void __iomem* regs; +}; + +#if 0 +struct al5r_codec_chan +{ + wait_queue_head_t irq_queue; + struct list_head irq_numbers; + int unblock; + struct al5r_codec_desc* codec; +}; + + +struct al5r_codec_desc +{ + struct device* device; + void __iomem* regs; /* Base addr for regs */ + unsigned long regs_size; /* end addr for regs */ + struct cdev cdev; + /* one for one mapping in the no mcu case */ + struct al5r_codec_chan* chan; + struct list_head irq_masks; + spinlock_t i_lock; + struct kmem_cache* cache; +}; +#endif + +int channel_is_ready(struct uio_platdata* codec) +{ + unsigned long flags; + int ret = codec->unblock; + + spin_lock_irqsave(&codec->i_lock, flags); + ret = ret || !list_empty(&codec->irq_masks); + spin_unlock_irqrestore(&codec->i_lock, flags); + return ret; +} + +irqreturn_t al5r_hardirq_handler(int irq, void* data) +{ + struct uio_platdata* codec = (struct uio_platdata*)data; + u32 unmasked_irq_bitfield, irq_bitfield; + u32 mask; + unsigned long flags; + struct r_irq* i_callback; + int callback_nb; + int i = 0; + int al5_interrupt_nb = 20; + + mask = readl(codec->regs + AL5_INTERRUPT_MASK); + unmasked_irq_bitfield = readl(codec->regs + AL5_INTERRUPT); + irq_bitfield = unmasked_irq_bitfield & mask; + + if(irq_bitfield == 0) + { + printk("bitfield is 0\n"); + return IRQ_NONE; + } + writel(unmasked_irq_bitfield, codec->regs + AL5_INTERRUPT); + readl(codec->regs + AL5_INTERRUPT); + + for(i = 0; i < al5_interrupt_nb; ++i) + { + callback_nb = 1U << i; + + if(irq_bitfield & callback_nb) + { + i_callback = kmem_cache_alloc(codec->cache, GFP_ATOMIC); + + if(!i_callback) + { + printk("ENOMEM: Missed interrupt\n"); + return IRQ_NONE; + } + i_callback->bitfield = i; + spin_lock_irqsave(&codec->i_lock, flags); + list_add_tail(&i_callback->list, &codec->irq_masks); + spin_unlock_irqrestore(&codec->i_lock, flags); + } + } + + spin_lock_irqsave(&codec->i_lock, flags); + + //if(codec->chan) + wake_up_interruptible(&codec->irq_queue);//wake up wait_irq + + spin_unlock_irqrestore(&codec->i_lock, flags); + + return IRQ_HANDLED; +} + + +static int wait_irq(struct uio_platdata* codec, unsigned long arg) +{ + int callback; + struct r_irq* i_callback; + unsigned long flags; + int ret; + + ret = wait_event_interruptible(codec->irq_queue, + channel_is_ready(codec)); + + if(ret == -ERESTARTSYS) + return ret; + + if(codec->unblock) + { + printk("Unblocking channel\n"); + return -EINTR; + } + + spin_lock_irqsave(&codec->i_lock, flags); + i_callback = list_first_entry(&codec->irq_masks, + struct r_irq, list); + callback = i_callback->bitfield; + list_del(&i_callback->list); + kmem_cache_free(codec->cache, i_callback); + spin_unlock_irqrestore(&codec->i_lock, flags); + + if(copy_to_user((void*)arg, &callback, sizeof(__u32))) + return -EFAULT; + + return ret; +} + +static int unblock_channel(struct uio_platdata* codec) +{ + codec->unblock = 1; + wake_up_interruptible(&codec->irq_queue); + return 0; +} + + +/** + * @brief Handle hw codec irq + * @author Jacky Lau + * @date 2011-07-05 + * @param [in] irq : irq id + * @param [in] dev_id : platform data + * @return irqreturn_t : return handle result + * @retval IRQ_HANDLE : irq handled successful. + */ + #if 0 +static irqreturn_t uio_vcodec_irq_handler(int irq, void *dev_id) +{ + struct uio_platdata *pdata = dev_id; + int i; + + for (i = 0; i < MASK_BITS_NUM; i++) + { + if ((1 << i) & VIDEO_IRQ_MASK) + disable_irq_nosync(i); + } + + up (&(pdata->vcodec_sem)); + + return IRQ_HANDLED; +} +#endif +/** + * @brief Handle hw codec ioctl + * @author Jacky Lau + * @date 2011-07-05 + * @param [in] uioinfo : information of uio driver + * @param [in] cmd : ioctl request code + * @param [in] arg : argument + * @return int : return 0 when handle successful, otherwise return negative + * @retval 0 : handled successful. + * @retval <0 : handle failed. + */ +static int uio_vcodec_ioctl(struct uio_info *uioinfo, unsigned int cmd, unsigned long arg) +{ + struct uio_platdata *pdata = uioinfo->priv; + int err; + + switch (cmd) { + case AKUIO_SYSREG_WRITE: + { + struct akuio_sysreg_write_t reg_write; + + if (copy_from_user(®_write, (void __user *)arg, sizeof(struct akuio_sysreg_write_t))) + return -EFAULT; + + sys_ctrl_reg_set(reg_write.paddr, reg_write.mask, reg_write.val); + + err = 0; + } + break; + + #if 0 + case AKUIO_WAIT_IRQ: + { + int i; + + for (i = 0; i < MASK_BITS_NUM; i++) + { + if ((1 << i) & VIDEO_IRQ_MASK) + enable_irq(i); + } + + down (&pdata->vcodec_sem); + err = 0; + } + break; + #endif + + case AKUIO_WAIT_IRQ: + wait_irq(pdata, arg); + err = 0; + break; + + case AKUIO_UNBLOCK_CHANNEL: + unblock_channel(pdata); + err = 0; + break; + + case AKUIO_INVALIDATE_L2CACHE: + l2cache_invalidate (); + flush_cache_all(); + err = 0; + break; + + case AKUIO_INVALIDATE_L1CACHE: +// flush_cache_all(); + err = 0; + break; + + case AKUIO_VIDEO_RESET: + ak_soft_reset(AK_SRESET_VIDEO); + err = 0; + printk("%s AKUIO_VIDEO_RESET\n", __func__); + break; + + default: + printk(KERN_ERR "[%s] Unknow cmd: %u\n", __func__, cmd); + err = -EINVAL; + break; + } + + return err; +} + +/** + * @brief When application open the akuio device, request all irq of the hw codec and disable the irq immediately + * @author Jacky Lau + * @date 2011-07-05 + * @param [in] uioinfo : information of uio driver + * @param [in] inode : inode of the device + * @return int : return 0 when handle successful, otherwise return negative + * @retval 0 : handled successful. + * @retval <0 : handle failed. + */ +static int uio_vcodec_open(struct uio_info *uioinfo, struct inode *inode) +{ + struct uio_platdata *pdata = uioinfo->priv; + int ret = 0; + //int i; + //Add code here to make sure uio0 can be multi-opened. + if( pdata->open_count++ > 0 ) { + DBG("uio0 has opened! open_count=%d\n", pdata->open_count ); + return 0; + } + + INIT_LIST_HEAD(&pdata->irq_masks); + + #if 0 + init_MUTEX_LOCKED(&(pdata->vcodec_sem)); + + for (i = 0; i < MASK_BITS_NUM; i++) + { + if ((1 << i) & VIDEO_IRQ_MASK) { + ret = request_irq(i, uio_vcodec_irq_handler, IRQF_DISABLED, "VIDEO HW CODEC", pdata); + disable_irq_nosync(i); + } + } + #endif + + ak_soft_reset(AK_SRESET_VIDEO); + + ret = request_irq(pdata->irq, al5r_hardirq_handler, IRQF_DISABLED, + "VIDEO HW CODEC", pdata); + if(ret) + { + printk("Failed to request IRQ #%d -> :%d\n",pdata->irq, ret); + return -1; + } + + return 0; +} + +/** + * @brief When application close the akuio device, free all irq of the hw codec + * @author Jacky Lau + * @date 2011-07-05 + * @param [in] uioinfo : information of uio driver + * @param [in] inode : inode of the device + * @return int : return 0 when handle successful, otherwise return negative + * @retval 0 : handled successful. + * @retval <0 : handle failed. + */ +static int uio_vcodec_release(struct uio_info *uioinfo, struct inode *inode) +{ + struct uio_platdata *pdata = uioinfo->priv; + struct r_irq *pos, *n; + //int i; + //Add code here to make sure uio0 can be multi-opened. + if(--pdata->open_count != 0) { + DBG("uio0 does's closed to 0! open_count=%d\n", pdata->open_count ); + return 0; + } + + #if 0 + for (i = 0; i < MASK_BITS_NUM; i++) + { + if ((1 << i) & VIDEO_IRQ_MASK) + free_irq(i, pdata); + } + #endif + + free_irq(pdata->irq, pdata); + + list_for_each_entry_safe(pos, n, &pdata->irq_masks, list) { + list_del(&pos->list); + kmem_cache_free(pdata->cache, pos); + + } + + return 0; +} + +/** + * @brief Init the device which was probed + * @author Jacky Lau + * @date 2011-07-05 + * @param [in] pdev : the device definition + * @return int : return 0 when handle successful, otherwise return negative + * @retval 0 : handled successful. + * @retval <0 : handle failed. + */ +static int uio_vcodec_probe(struct platform_device *pdev) +{ + struct uio_info *uioinfo = pdev->dev.platform_data; + struct uio_platdata *pdata; + struct uio_mem *uiomem; + int ret = -ENODEV; + int i; + + if (!uioinfo || !uioinfo->name || !uioinfo->version) { + dev_dbg(&pdev->dev, "%s: err_uioinfo\n", __func__); + goto err_uioinfo; + } + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + ret = -ENOMEM; + dev_dbg(&pdev->dev, "%s: err_alloc_pdata\n", __func__); + goto err_alloc_pdata; + } + + pdata->uioinfo = uioinfo; + + uiomem = &uioinfo->mem[0]; + + for (i = 0; i < pdev->num_resources; ++i) { + struct resource *r = &pdev->resource[i]; + if (r->flags != IORESOURCE_MEM) + continue; + + if (uiomem >= &uioinfo->mem[MAX_UIO_MAPS]) { + dev_warn(&pdev->dev, "device has more than " + __stringify(MAX_UIO_MAPS) + " I/O memory resources.\n"); + break; + } + + uiomem->memtype = UIO_MEM_PHYS; + uiomem->addr = r->start; + uiomem->size = r->end - r->start + 1; + pdata->regs = ioremap_nocache(uiomem->addr, uiomem->size); + ++uiomem; + + } + + while (uiomem < &uioinfo->mem[MAX_UIO_MAPS]) { + uiomem->size = 0; + ++uiomem; + } + + /* open count */ + pdata->open_count = 0; + + /* irq */ + pdata->uioinfo->irq = UIO_IRQ_CUSTOM; + + /* file handle */ + pdata->uioinfo->open = uio_vcodec_open; + pdata->uioinfo->release = uio_vcodec_release; + pdata->uioinfo->ioctl = uio_vcodec_ioctl; + pdata->uioinfo->priv = pdata; + + spin_lock_init(&pdata->i_lock); + pdata->cache = kmem_cache_create("al_codec_ram", + sizeof(struct r_irq), + 0, SLAB_HWCACHE_ALIGN, NULL); + /*ak39 video irq */ + pdata->irq = IRQ_VIDEO_ENCODER; + + init_waitqueue_head(&pdata->irq_queue); + + ret = uio_register_device(&pdev->dev, pdata->uioinfo); + + if (ret) { + kfree(pdata); +err_alloc_pdata: +err_uioinfo: + return ret; + } + + platform_set_drvdata(pdev, pdata); + + return 0; +} + +/** + * @brief De-init the device which will be removed + * @author Jacky Lau + * @date 2011-07-05 + * @param [in] pdev : the device definition + * @return int : return 0 when handle successful, otherwise return negative + * @retval 0 : handled successful. + * @retval <0 : handle failed. + */ +static int uio_vcodec_remove(struct platform_device *pdev) +{ + struct uio_platdata *pdata = platform_get_drvdata(pdev); + + iounmap(pdata->regs); + uio_unregister_device(pdata->uioinfo); + + kfree(pdata); + + return 0; +} + +/* driver definition */ +static struct platform_driver uio_vcodec = { + .probe = uio_vcodec_probe, + .remove = uio_vcodec_remove, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +/** + * @brief kernel module init function, register the driver to kernel + * @author Jacky Lau + * @date 2011-07-05 + * @param + * @return int : return 0 when handle successful, otherwise return negative + * @retval 0 : handled successful. + * @retval <0 : handle failed. + */ +static int __init uio_vcodec_init(void) +{ + return platform_driver_register(&uio_vcodec); +} + +/** + * @brief kernel module finally function, unregister the driver from kernel + * @author Jacky Lau + * @date 2011-07-05 + * @param + * @return void + * @retval + */ +static void __exit uio_vcodec_exit(void) +{ + platform_driver_unregister(&uio_vcodec); +} +module_init(uio_vcodec_init); +module_exit(uio_vcodec_exit); + +MODULE_AUTHOR("Jacky Lau"); +MODULE_DESCRIPTION("Userspace driver for anyka video hw codec"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 53a7bc07..e10b0f3e 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_USB_HWA_HCD) += host/ obj-$(CONFIG_USB_ISP1760_HCD) += host/ obj-$(CONFIG_USB_IMX21_HCD) += host/ obj-$(CONFIG_USB_FSL_MPH_DR_OF) += host/ +obj-$(CONFIG_USB_ANYKA_HCD) += host/ obj-$(CONFIG_USB_C67X00_HCD) += c67x00/ diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 3a7a9508..ebd4e974 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -553,12 +553,13 @@ static int acm_port_activate(struct tty_port *port, struct tty_struct *tty) set_bit(TTY_NO_WRITE_SPLIT, &tty->flags); acm->control->needs_remote_wakeup = 1; - acm->ctrlurb->dev = acm->dev; + //add for notion 4g module:disable interrupt urb submit + /*acm->ctrlurb->dev = acm->dev; if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) { dev_err(&acm->control->dev, "%s - usb_submit_urb(ctrl irq) failed\n", __func__); goto error_submit_urb; - } + }*/ acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS; if (acm_set_control(acm, acm->ctrlout) < 0 && @@ -1253,12 +1254,13 @@ made_compressed_probe: urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; urb->transfer_dma = rb->dma; if (acm->is_int_ep) { - usb_fill_int_urb(urb, acm->dev, + //add for notion 4g module:disable interrupt urb submit + /*usb_fill_int_urb(urb, acm->dev, acm->rx_endpoint, rb->base, acm->readsize, acm_read_bulk_callback, rb, - acm->bInterval); + acm->bInterval);*/ } else { usb_fill_bulk_urb(urb, acm->dev, acm->rx_endpoint, @@ -1327,12 +1329,15 @@ made_compressed_probe: } skip_countries: - usb_fill_int_urb(acm->ctrlurb, usb_dev, + //add for notion 4g module:disable interrupt urb submit + #if 0 + usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, /* works around buggy devices */ epctrl->bInterval ? epctrl->bInterval : 0xff); - acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + #endif + acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; acm->ctrlurb->transfer_dma = acm->ctrl_dma; dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor); @@ -1685,7 +1690,10 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0694, 0xff00), .driver_info = NOT_A_MODEM, }, - + //add for notion 4g module + { USB_DEVICE(0x1286, 0x4e3d), + .driver_info = NOT_A_MODEM, + }, /* Support for Droids MuIn LCD */ { USB_DEVICE(0x04d8, 0x000b), .driver_info = NO_DATA_INTERFACE, diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index e2cc8df3..70fd01e3 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -728,7 +728,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd) * fire at the same time to give the CPU a break in between */ if (hcd->uses_new_polling ? HCD_POLL_RH(hcd) : (length == 0 && hcd->status_urb != NULL)) - mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4)); + mod_timer (&hcd->rh_timer, (jiffies/(HZ/16) + 1) * (HZ/16)); } EXPORT_SYMBOL_GPL(usb_hcd_poll_rh_status); @@ -760,7 +760,7 @@ static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb) hcd->status_urb = urb; urb->hcpriv = hcd; /* indicate it's queued */ if (!hcd->uses_new_polling) - mod_timer(&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4)); + mod_timer(&hcd->rh_timer, (jiffies/(HZ/16) + 1) * (HZ/16)); /* If a status change has already occurred, report it ASAP */ else if (HCD_POLL_PENDING(hcd)) @@ -1591,7 +1591,7 @@ void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status) /* pass ownership to the completion handler */ urb->status = status; - urb->complete (urb); + urb->complete (urb); // hub_irq atomic_dec (&urb->use_count); if (unlikely(atomic_read(&urb->reject))) wake_up (&usb_kill_urb_queue); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 67dda0db..4101becc 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3156,7 +3156,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, udev->tt = &hub->tt; udev->ttport = port1; } - + /* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way? * Because device hardware and firmware is sometimes buggy in * this area, and this is how Linux has done it for ages. @@ -4265,6 +4265,17 @@ int usb_reset_device(struct usb_device *udev) return -EINVAL; } + if (udev->state == USB_STATE_CONFIGURED) + { + struct usb_hcd *hcd; + hcd = bus_to_hcd(udev->bus); + if(hcd->driver->reset){ + usb_remove_device(udev); + hcd->driver->reset(hcd); + return 0; + } + } + /* Prevent autosuspend during the reset */ usb_autoresume_device(udev); diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 9d912bfd..edeff3fe 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -626,8 +626,9 @@ void usb_kill_urb(struct urb *urb) atomic_inc(&urb->reject); usb_hcd_unlink_urb(urb, -ENOENT); - wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0); +// wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0); + wait_event_timeout(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0, HZ/10); atomic_dec(&urb->reject); } EXPORT_SYMBOL_GPL(usb_kill_urb); diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 2633f759..228f22d8 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -316,6 +316,8 @@ config USB_MV_UDC USB2.0 OTG controller, which can be configured as high speed or full speed USB peripheral. +source "drivers/usb/gadget/plat-anyka/Kconfig" + # # Controllers available in both integrated and discrete versions # @@ -846,6 +848,15 @@ config USB_G_PRINTER For more information, see Documentation/usb/gadget_printer.txt which includes sample code for accessing the device file. +config USB_G_ANDROID + boolean "Android Composite Gadget" + help + The Android Composite Gadget supports multiple USB + functions: adb, acm, mass storage, mtp, accessory + and rndis. + Each function can be configured and enabled/disabled + dynamically from userspace through a sysfs interface. + config USB_CDC_COMPOSITE tristate "CDC Composite Device (Ethernet and ACM)" depends on NET diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index b7f6eefc..a92bbbe8 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -31,6 +31,8 @@ obj-$(CONFIG_USB_MV_UDC) += mv_udc.o mv_udc-y := mv_udc_core.o obj-$(CONFIG_USB_CI13XXX_MSM) += ci13xxx_msm.o obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o +obj-$(CONFIG_USB_AKUDC) += plat-anyka/ +obj-$(CONFIG_USB_AKUDC_PRODUCER) += plat-anyka/ # # USB gadget drivers @@ -52,6 +54,7 @@ g_nokia-y := nokia.o g_webcam-y := webcam.o g_ncm-y := ncm.o g_acm_ms-y := acm_ms.o +g_android-y := android.o obj-$(CONFIG_USB_ZERO) += g_zero.o obj-$(CONFIG_USB_AUDIO) += g_audio.o @@ -71,3 +74,4 @@ obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o obj-$(CONFIG_USB_G_NCM) += g_ncm.o obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o +obj-$(CONFIG_USB_G_ANDROID) += g_android.o diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c new file mode 100644 index 00000000..4e73bf1a --- /dev/null +++ b/drivers/usb/gadget/android.c @@ -0,0 +1,1565 @@ +/* + * Gadget Driver for Android + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * Benoit Goby + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "gadget_chips.h" + +/* + * Kbuild is not very cooperative with respect to linking separately + * compiled library objects into one module. So for now we won't use + * separate compilation ... ensuring init/exit sections work to shrink + * the runtime footprint, and giving us at least some parts of what + * a "gcc --combine ... part1.c part2.c part3.c ... " build would. + */ +#include "usbstring.c" +#include "config.c" +#include "epautoconf.c" +#include "composite.c" + +#include "f_fs.c" +#include "f_audio_source.c" +#include "f_mass_storage.c" +#include "u_serial.c" +#include "f_acm.c" +#include "f_adb.c" +#include "f_mtp.c" +#include "f_accessory.c" +#define USB_ETH_RNDIS y +#include "f_rndis.c" +#include "rndis.c" +#include "u_ether.c" + +MODULE_AUTHOR("Mike Lockwood"); +MODULE_DESCRIPTION("Android Composite USB Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0"); + +static const char longname[] = "Gadget Android"; + +/* Default vendor and product IDs, overridden by userspace */ +#define VENDOR_ID 0x18D1 +#define PRODUCT_ID 0x0001 + +struct android_usb_function { + char *name; + void *config; + + struct device *dev; + char *dev_name; + struct device_attribute **attributes; + + /* for android_dev.enabled_functions */ + struct list_head enabled_list; + + /* Optional: initialization during gadget bind */ + int (*init)(struct android_usb_function *, struct usb_composite_dev *); + /* Optional: cleanup during gadget unbind */ + void (*cleanup)(struct android_usb_function *); + /* Optional: called when the function is added the list of + * enabled functions */ + void (*enable)(struct android_usb_function *); + /* Optional: called when it is removed */ + void (*disable)(struct android_usb_function *); + + int (*bind_config)(struct android_usb_function *, + struct usb_configuration *); + + /* Optional: called when the configuration is removed */ + void (*unbind_config)(struct android_usb_function *, + struct usb_configuration *); + /* Optional: handle ctrl requests before the device is configured */ + int (*ctrlrequest)(struct android_usb_function *, + struct usb_composite_dev *, + const struct usb_ctrlrequest *); +}; + +struct android_dev { + struct android_usb_function **functions; + struct list_head enabled_functions; + struct usb_composite_dev *cdev; + struct device *dev; + + bool enabled; + int disable_depth; + struct mutex mutex; + bool connected; + bool sw_connected; + struct work_struct work; + char ffs_aliases[256]; +}; + +static struct class *android_class; +static struct android_dev *_android_dev; +static int android_bind_config(struct usb_configuration *c); +static void android_unbind_config(struct usb_configuration *c); + +/* string IDs are assigned dynamically */ +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 +#define STRING_SERIAL_IDX 2 + +static char manufacturer_string[256]; +static char product_string[256]; +static char serial_string[256]; + +/* String Table */ +static struct usb_string strings_dev[] = { + [STRING_MANUFACTURER_IDX].s = manufacturer_string, + [STRING_PRODUCT_IDX].s = product_string, + [STRING_SERIAL_IDX].s = serial_string, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, +}; + +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, +}; + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof(device_desc), + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = __constant_cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_PER_INTERFACE, + .idVendor = __constant_cpu_to_le16(VENDOR_ID), + .idProduct = __constant_cpu_to_le16(PRODUCT_ID), + .bcdDevice = __constant_cpu_to_le16(0xffff), + .bNumConfigurations = 1, +}; + +static struct usb_configuration android_config_driver = { + .label = "android", + .unbind = android_unbind_config, + .bConfigurationValue = 1, + .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 0xFA, /* 500ma */ +}; + +static void android_work(struct work_struct *data) +{ + struct android_dev *dev = container_of(data, struct android_dev, work); + struct usb_composite_dev *cdev = dev->cdev; + char *disconnected[2] = { "USB_STATE=DISCONNECTED", NULL }; + char *connected[2] = { "USB_STATE=CONNECTED", NULL }; + char *configured[2] = { "USB_STATE=CONFIGURED", NULL }; + char **uevent_envp = NULL; + unsigned long flags; + + spin_lock_irqsave(&cdev->lock, flags); + if (cdev->config) + uevent_envp = configured; + else if (dev->connected != dev->sw_connected) + uevent_envp = dev->connected ? connected : disconnected; + dev->sw_connected = dev->connected; + spin_unlock_irqrestore(&cdev->lock, flags); + + if (uevent_envp) { + kobject_uevent_env(&dev->dev->kobj, KOBJ_CHANGE, uevent_envp); + pr_info("%s: sent uevent %s\n", __func__, uevent_envp[0]); + } else { + pr_info("%s: did not send uevent (%d %d %p)\n", __func__, + dev->connected, dev->sw_connected, cdev->config); + } +} + +static void android_enable(struct android_dev *dev) +{ + struct usb_composite_dev *cdev = dev->cdev; + + if (WARN_ON(!dev->disable_depth)) + return; + + if (--dev->disable_depth == 0) { + usb_add_config(cdev, &android_config_driver, + android_bind_config); + usb_gadget_connect(cdev->gadget); + } +} + +static void android_disable(struct android_dev *dev) +{ + struct usb_composite_dev *cdev = dev->cdev; + + if (dev->disable_depth++ == 0) { + usb_gadget_disconnect(cdev->gadget); + /* Cancel pending control requests */ + usb_ep_dequeue(cdev->gadget->ep0, cdev->req); + usb_remove_config(cdev, &android_config_driver); + } +} + +/*-------------------------------------------------------------------------*/ +/* Supported functions initialization */ + +struct functionfs_config { + bool opened; + bool enabled; + struct ffs_data *data; +}; + +static int ffs_function_init(struct android_usb_function *f, + struct usb_composite_dev *cdev) +{ + f->config = kzalloc(sizeof(struct functionfs_config), GFP_KERNEL); + if (!f->config) + return -ENOMEM; + + return functionfs_init(); +} + +static void ffs_function_cleanup(struct android_usb_function *f) +{ + functionfs_cleanup(); + kfree(f->config); +} + +static void ffs_function_enable(struct android_usb_function *f) +{ + struct android_dev *dev = _android_dev; + struct functionfs_config *config = f->config; + + config->enabled = true; + + /* Disable the gadget until the function is ready */ + if (!config->opened) + android_disable(dev); +} + +static void ffs_function_disable(struct android_usb_function *f) +{ + struct android_dev *dev = _android_dev; + struct functionfs_config *config = f->config; + + config->enabled = false; + + /* Balance the disable that was called in closed_callback */ + if (!config->opened) + android_enable(dev); +} + +static int ffs_function_bind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + struct functionfs_config *config = f->config; + return functionfs_bind_config(c->cdev, c, config->data); +} + +static ssize_t +ffs_aliases_show(struct device *pdev, struct device_attribute *attr, char *buf) +{ + struct android_dev *dev = _android_dev; + int ret; + + mutex_lock(&dev->mutex); + ret = sprintf(buf, "%s\n", dev->ffs_aliases); + mutex_unlock(&dev->mutex); + + return ret; +} + +static ssize_t +ffs_aliases_store(struct device *pdev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct android_dev *dev = _android_dev; + char buff[256]; + + mutex_lock(&dev->mutex); + + if (dev->enabled) { + mutex_unlock(&dev->mutex); + return -EBUSY; + } + + strlcpy(buff, buf, sizeof(buff)); + strlcpy(dev->ffs_aliases, strim(buff), sizeof(dev->ffs_aliases)); + + mutex_unlock(&dev->mutex); + + return size; +} + +static DEVICE_ATTR(aliases, S_IRUGO | S_IWUSR, ffs_aliases_show, + ffs_aliases_store); +static struct device_attribute *ffs_function_attributes[] = { + &dev_attr_aliases, + NULL +}; + +static struct android_usb_function ffs_function = { + .name = "ffs", + .init = ffs_function_init, + .enable = ffs_function_enable, + .disable = ffs_function_disable, + .cleanup = ffs_function_cleanup, + .bind_config = ffs_function_bind_config, + .attributes = ffs_function_attributes, +}; + +static int functionfs_ready_callback(struct ffs_data *ffs) +{ + struct android_dev *dev = _android_dev; + struct functionfs_config *config = ffs_function.config; + int ret = 0; + + mutex_lock(&dev->mutex); + + ret = functionfs_bind(ffs, dev->cdev); + if (ret) + goto err; + + config->data = ffs; + config->opened = true; + + if (config->enabled) + android_enable(dev); + +err: + mutex_unlock(&dev->mutex); + return ret; +} + +static void functionfs_closed_callback(struct ffs_data *ffs) +{ + struct android_dev *dev = _android_dev; + struct functionfs_config *config = ffs_function.config; + + mutex_lock(&dev->mutex); + + if (config->enabled) + android_disable(dev); + + config->opened = false; + config->data = NULL; + + functionfs_unbind(ffs); + + mutex_unlock(&dev->mutex); +} + +static int functionfs_check_dev_callback(const char *dev_name) +{ + return 0; +} + + +struct adb_data { + bool opened; + bool enabled; +}; + +static int +adb_function_init(struct android_usb_function *f, + struct usb_composite_dev *cdev) +{ + f->config = kzalloc(sizeof(struct adb_data), GFP_KERNEL); + if (!f->config) + return -ENOMEM; + + return adb_setup(); +} + +static void adb_function_cleanup(struct android_usb_function *f) +{ + adb_cleanup(); + kfree(f->config); +} + +static int +adb_function_bind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + return adb_bind_config(c); +} + +static void adb_android_function_enable(struct android_usb_function *f) +{ + struct android_dev *dev = _android_dev; + struct adb_data *data = f->config; + + data->enabled = true; + + /* Disable the gadget until adbd is ready */ + if (!data->opened) + android_disable(dev); +} + +static void adb_android_function_disable(struct android_usb_function *f) +{ + struct android_dev *dev = _android_dev; + struct adb_data *data = f->config; + + data->enabled = false; + + /* Balance the disable that was called in closed_callback */ + if (!data->opened) + android_enable(dev); +} + +static struct android_usb_function adb_function = { + .name = "adb", + .enable = adb_android_function_enable, + .disable = adb_android_function_disable, + .init = adb_function_init, + .cleanup = adb_function_cleanup, + .bind_config = adb_function_bind_config, +}; + +static void adb_ready_callback(void) +{ + struct android_dev *dev = _android_dev; + struct adb_data *data = adb_function.config; + + mutex_lock(&dev->mutex); + + data->opened = true; + + if (data->enabled) + android_enable(dev); + + mutex_unlock(&dev->mutex); +} + +static void adb_closed_callback(void) +{ + struct android_dev *dev = _android_dev; + struct adb_data *data = adb_function.config; + + mutex_lock(&dev->mutex); + + data->opened = false; + + if (data->enabled) + android_disable(dev); + + mutex_unlock(&dev->mutex); +} + + +#define MAX_ACM_INSTANCES 4 +struct acm_function_config { + int instances; +}; + +static int +acm_function_init(struct android_usb_function *f, + struct usb_composite_dev *cdev) +{ + f->config = kzalloc(sizeof(struct acm_function_config), GFP_KERNEL); + if (!f->config) + return -ENOMEM; + + return gserial_setup(cdev->gadget, MAX_ACM_INSTANCES); +} + +static void acm_function_cleanup(struct android_usb_function *f) +{ + gserial_cleanup(); + kfree(f->config); + f->config = NULL; +} + +static int +acm_function_bind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + int i; + int ret = 0; + struct acm_function_config *config = f->config; + + for (i = 0; i < config->instances; i++) { + ret = acm_bind_config(c, i); + if (ret) { + pr_err("Could not bind acm%u config\n", i); + break; + } + } + + return ret; +} + +static ssize_t acm_instances_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct acm_function_config *config = f->config; + return sprintf(buf, "%d\n", config->instances); +} + +static ssize_t acm_instances_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct acm_function_config *config = f->config; + int value; + + sscanf(buf, "%d", &value); + if (value > MAX_ACM_INSTANCES) + value = MAX_ACM_INSTANCES; + config->instances = value; + return size; +} + +static DEVICE_ATTR(instances, S_IRUGO | S_IWUSR, acm_instances_show, + acm_instances_store); +static struct device_attribute *acm_function_attributes[] = { + &dev_attr_instances, + NULL +}; + +static struct android_usb_function acm_function = { + .name = "acm", + .init = acm_function_init, + .cleanup = acm_function_cleanup, + .bind_config = acm_function_bind_config, + .attributes = acm_function_attributes, +}; + + +static int +mtp_function_init(struct android_usb_function *f, + struct usb_composite_dev *cdev) +{ + return mtp_setup(); +} + +static void mtp_function_cleanup(struct android_usb_function *f) +{ + mtp_cleanup(); +} + +static int +mtp_function_bind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + return mtp_bind_config(c, false); +} + +static int +ptp_function_init(struct android_usb_function *f, + struct usb_composite_dev *cdev) +{ + /* nothing to do - initialization is handled by mtp_function_init */ + return 0; +} + +static void ptp_function_cleanup(struct android_usb_function *f) +{ + /* nothing to do - cleanup is handled by mtp_function_cleanup */ +} + +static int +ptp_function_bind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + return mtp_bind_config(c, true); +} + +static int mtp_function_ctrlrequest(struct android_usb_function *f, + struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *c) +{ + return mtp_ctrlrequest(cdev, c); +} + +static struct android_usb_function mtp_function = { + .name = "mtp", + .init = mtp_function_init, + .cleanup = mtp_function_cleanup, + .bind_config = mtp_function_bind_config, + .ctrlrequest = mtp_function_ctrlrequest, +}; + +/* PTP function is same as MTP with slightly different interface descriptor */ +static struct android_usb_function ptp_function = { + .name = "ptp", + .init = ptp_function_init, + .cleanup = ptp_function_cleanup, + .bind_config = ptp_function_bind_config, +}; + + +struct rndis_function_config { + u8 ethaddr[ETH_ALEN]; + u32 vendorID; + char manufacturer[256]; + /* "Wireless" RNDIS; auto-detected by Windows */ + bool wceis; +}; + +static int +rndis_function_init(struct android_usb_function *f, + struct usb_composite_dev *cdev) +{ + f->config = kzalloc(sizeof(struct rndis_function_config), GFP_KERNEL); + if (!f->config) + return -ENOMEM; + return 0; +} + +static void rndis_function_cleanup(struct android_usb_function *f) +{ + kfree(f->config); + f->config = NULL; +} + +static int +rndis_function_bind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + int ret; + struct rndis_function_config *rndis = f->config; + + if (!rndis) { + pr_err("%s: rndis_pdata\n", __func__); + return -1; + } + + pr_info("%s MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", __func__, + rndis->ethaddr[0], rndis->ethaddr[1], rndis->ethaddr[2], + rndis->ethaddr[3], rndis->ethaddr[4], rndis->ethaddr[5]); + + ret = gether_setup_name(c->cdev->gadget, rndis->ethaddr, "rndis"); + if (ret) { + pr_err("%s: gether_setup failed\n", __func__); + return ret; + } + + if (rndis->wceis) { + /* "Wireless" RNDIS; auto-detected by Windows */ + rndis_iad_descriptor.bFunctionClass = + USB_CLASS_WIRELESS_CONTROLLER; + rndis_iad_descriptor.bFunctionSubClass = 0x01; + rndis_iad_descriptor.bFunctionProtocol = 0x03; + rndis_control_intf.bInterfaceClass = + USB_CLASS_WIRELESS_CONTROLLER; + rndis_control_intf.bInterfaceSubClass = 0x01; + rndis_control_intf.bInterfaceProtocol = 0x03; + } + + return rndis_bind_config_vendor(c, rndis->ethaddr, rndis->vendorID, + rndis->manufacturer); +} + +static void rndis_function_unbind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + gether_cleanup(); +} + +static ssize_t rndis_manufacturer_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct rndis_function_config *config = f->config; + return sprintf(buf, "%s\n", config->manufacturer); +} + +static ssize_t rndis_manufacturer_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct rndis_function_config *config = f->config; + + if (size >= sizeof(config->manufacturer)) + return -EINVAL; + if (sscanf(buf, "%s", config->manufacturer) == 1) + return size; + return -1; +} + +static DEVICE_ATTR(manufacturer, S_IRUGO | S_IWUSR, rndis_manufacturer_show, + rndis_manufacturer_store); + +static ssize_t rndis_wceis_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct rndis_function_config *config = f->config; + return sprintf(buf, "%d\n", config->wceis); +} + +static ssize_t rndis_wceis_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct rndis_function_config *config = f->config; + int value; + + if (sscanf(buf, "%d", &value) == 1) { + config->wceis = value; + return size; + } + return -EINVAL; +} + +static DEVICE_ATTR(wceis, S_IRUGO | S_IWUSR, rndis_wceis_show, + rndis_wceis_store); + +static ssize_t rndis_ethaddr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct rndis_function_config *rndis = f->config; + return sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n", + rndis->ethaddr[0], rndis->ethaddr[1], rndis->ethaddr[2], + rndis->ethaddr[3], rndis->ethaddr[4], rndis->ethaddr[5]); +} + +static ssize_t rndis_ethaddr_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct rndis_function_config *rndis = f->config; + + if (sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n", + (int *)&rndis->ethaddr[0], (int *)&rndis->ethaddr[1], + (int *)&rndis->ethaddr[2], (int *)&rndis->ethaddr[3], + (int *)&rndis->ethaddr[4], (int *)&rndis->ethaddr[5]) == 6) + return size; + return -EINVAL; +} + +static DEVICE_ATTR(ethaddr, S_IRUGO | S_IWUSR, rndis_ethaddr_show, + rndis_ethaddr_store); + +static ssize_t rndis_vendorID_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct rndis_function_config *config = f->config; + return sprintf(buf, "%04x\n", config->vendorID); +} + +static ssize_t rndis_vendorID_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct rndis_function_config *config = f->config; + int value; + + if (sscanf(buf, "%04x", &value) == 1) { + config->vendorID = value; + return size; + } + return -EINVAL; +} + +static DEVICE_ATTR(vendorID, S_IRUGO | S_IWUSR, rndis_vendorID_show, + rndis_vendorID_store); + +static struct device_attribute *rndis_function_attributes[] = { + &dev_attr_manufacturer, + &dev_attr_wceis, + &dev_attr_ethaddr, + &dev_attr_vendorID, + NULL +}; + +static struct android_usb_function rndis_function = { + .name = "rndis", + .init = rndis_function_init, + .cleanup = rndis_function_cleanup, + .bind_config = rndis_function_bind_config, + .unbind_config = rndis_function_unbind_config, + .attributes = rndis_function_attributes, +}; + + +struct mass_storage_function_config { + struct fsg_config fsg; + struct fsg_common *common; +}; + +static int mass_storage_function_init(struct android_usb_function *f, + struct usb_composite_dev *cdev) +{ + struct mass_storage_function_config *config; + struct fsg_common *common; + int err; + + config = kzalloc(sizeof(struct mass_storage_function_config), + GFP_KERNEL); + if (!config) + return -ENOMEM; + + config->fsg.nluns = 1; + config->fsg.luns[0].removable = 1; + + common = fsg_common_init(NULL, cdev, &config->fsg); + if (IS_ERR(common)) { + kfree(config); + return PTR_ERR(common); + } + + err = sysfs_create_link(&f->dev->kobj, + &common->luns[0].dev.kobj, + "lun"); + if (err) { + kfree(config); + return err; + } + + config->common = common; + f->config = config; + return 0; +} + +static void mass_storage_function_cleanup(struct android_usb_function *f) +{ + kfree(f->config); + f->config = NULL; +} + +static int mass_storage_function_bind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + struct mass_storage_function_config *config = f->config; + return fsg_bind_config(c->cdev, c, config->common); +} + +static ssize_t mass_storage_inquiry_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct mass_storage_function_config *config = f->config; + return sprintf(buf, "%s\n", config->common->inquiry_string); +} + +static ssize_t mass_storage_inquiry_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct mass_storage_function_config *config = f->config; + if (size >= sizeof(config->common->inquiry_string)) + return -EINVAL; + if (sscanf(buf, "%s", config->common->inquiry_string) != 1) + return -EINVAL; + return size; +} + +static DEVICE_ATTR(inquiry_string, S_IRUGO | S_IWUSR, + mass_storage_inquiry_show, + mass_storage_inquiry_store); + +static struct device_attribute *mass_storage_function_attributes[] = { + &dev_attr_inquiry_string, + NULL +}; + +static struct android_usb_function mass_storage_function = { + .name = "mass_storage", + .init = mass_storage_function_init, + .cleanup = mass_storage_function_cleanup, + .bind_config = mass_storage_function_bind_config, + .attributes = mass_storage_function_attributes, +}; + + +static int accessory_function_init(struct android_usb_function *f, + struct usb_composite_dev *cdev) +{ + return acc_setup(); +} + +static void accessory_function_cleanup(struct android_usb_function *f) +{ + acc_cleanup(); +} + +static int accessory_function_bind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + return acc_bind_config(c); +} + +static int accessory_function_ctrlrequest(struct android_usb_function *f, + struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *c) +{ + return acc_ctrlrequest(cdev, c); +} + +static struct android_usb_function accessory_function = { + .name = "accessory", + .init = accessory_function_init, + .cleanup = accessory_function_cleanup, + .bind_config = accessory_function_bind_config, + .ctrlrequest = accessory_function_ctrlrequest, +}; + +static int audio_source_function_init(struct android_usb_function *f, + struct usb_composite_dev *cdev) +{ + struct audio_source_config *config; + + config = kzalloc(sizeof(struct audio_source_config), GFP_KERNEL); + if (!config) + return -ENOMEM; + config->card = -1; + config->device = -1; + f->config = config; + return 0; +} + +static void audio_source_function_cleanup(struct android_usb_function *f) +{ + kfree(f->config); +} + +static int audio_source_function_bind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + struct audio_source_config *config = f->config; + + return audio_source_bind_config(c, config); +} + +static void audio_source_function_unbind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + struct audio_source_config *config = f->config; + + config->card = -1; + config->device = -1; +} + +static ssize_t audio_source_pcm_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct audio_source_config *config = f->config; + + /* print PCM card and device numbers */ + return sprintf(buf, "%d %d\n", config->card, config->device); +} + +static DEVICE_ATTR(pcm, S_IRUGO | S_IWUSR, audio_source_pcm_show, NULL); + +static struct device_attribute *audio_source_function_attributes[] = { + &dev_attr_pcm, + NULL +}; + +static struct android_usb_function audio_source_function = { + .name = "audio_source", + .init = audio_source_function_init, + .cleanup = audio_source_function_cleanup, + .bind_config = audio_source_function_bind_config, + .unbind_config = audio_source_function_unbind_config, + .attributes = audio_source_function_attributes, +}; + +static struct android_usb_function *supported_functions[] = { + &ffs_function, + &adb_function, + &acm_function, + &mtp_function, + &ptp_function, + &rndis_function, + &mass_storage_function, + &accessory_function, + &audio_source_function, + NULL +}; + + +static int android_init_functions(struct android_usb_function **functions, + struct usb_composite_dev *cdev) +{ + struct android_dev *dev = _android_dev; + struct android_usb_function *f; + struct device_attribute **attrs; + struct device_attribute *attr; + int err; + int index = 0; + + for (; (f = *functions++); index++) { + f->dev_name = kasprintf(GFP_KERNEL, "f_%s", f->name); + f->dev = device_create(android_class, dev->dev, + MKDEV(0, index), f, f->dev_name); + if (IS_ERR(f->dev)) { + pr_err("%s: Failed to create dev %s", __func__, + f->dev_name); + err = PTR_ERR(f->dev); + goto err_create; + } + + if (f->init) { + err = f->init(f, cdev); + if (err) { + pr_err("%s: Failed to init %s", __func__, + f->name); + goto err_out; + } + } + + attrs = f->attributes; + if (attrs) { + while ((attr = *attrs++) && !err) + err = device_create_file(f->dev, attr); + } + if (err) { + pr_err("%s: Failed to create function %s attributes", + __func__, f->name); + goto err_out; + } + } + return 0; + +err_out: + device_destroy(android_class, f->dev->devt); +err_create: + kfree(f->dev_name); + return err; +} + +static void android_cleanup_functions(struct android_usb_function **functions) +{ + struct android_usb_function *f; + + while (*functions) { + f = *functions++; + + if (f->dev) { + device_destroy(android_class, f->dev->devt); + kfree(f->dev_name); + } + + if (f->cleanup) + f->cleanup(f); + } +} + +static int +android_bind_enabled_functions(struct android_dev *dev, + struct usb_configuration *c) +{ + struct android_usb_function *f; + int ret; + + list_for_each_entry(f, &dev->enabled_functions, enabled_list) { + ret = f->bind_config(f, c); + if (ret) { + pr_err("%s: %s failed", __func__, f->name); + return ret; + } + } + return 0; +} + +static void +android_unbind_enabled_functions(struct android_dev *dev, + struct usb_configuration *c) +{ + struct android_usb_function *f; + + list_for_each_entry(f, &dev->enabled_functions, enabled_list) { + if (f->unbind_config) + f->unbind_config(f, c); + } +} + +static int android_enable_function(struct android_dev *dev, char *name) +{ + struct android_usb_function **functions = dev->functions; + struct android_usb_function *f; + while ((f = *functions++)) { + if (!strcmp(name, f->name)) { + list_add_tail(&f->enabled_list, + &dev->enabled_functions); + return 0; + } + } + return -EINVAL; +} + +/*-------------------------------------------------------------------------*/ +/* /sys/class/android_usb/android%d/ interface */ + +static ssize_t +functions_show(struct device *pdev, struct device_attribute *attr, char *buf) +{ + struct android_dev *dev = dev_get_drvdata(pdev); + struct android_usb_function *f; + char *buff = buf; + + mutex_lock(&dev->mutex); + + list_for_each_entry(f, &dev->enabled_functions, enabled_list) + buff += sprintf(buff, "%s,", f->name); + + mutex_unlock(&dev->mutex); + + if (buff != buf) + *(buff-1) = '\n'; + return buff - buf; +} + +static ssize_t +functions_store(struct device *pdev, struct device_attribute *attr, + const char *buff, size_t size) +{ + struct android_dev *dev = dev_get_drvdata(pdev); + char *name; + char buf[256], *b; + char aliases[256], *a; + int err; + int is_ffs; + int ffs_enabled = 0; + + mutex_lock(&dev->mutex); + + if (dev->enabled) { + mutex_unlock(&dev->mutex); + return -EBUSY; + } + + INIT_LIST_HEAD(&dev->enabled_functions); + + strlcpy(buf, buff, sizeof(buf)); + b = strim(buf); + + while (b) { + name = strsep(&b, ","); + if (!name) + continue; + + is_ffs = 0; + strlcpy(aliases, dev->ffs_aliases, sizeof(aliases)); + a = aliases; + + while (a) { + char *alias = strsep(&a, ","); + if (alias && !strcmp(name, alias)) { + is_ffs = 1; + break; + } + } + + if (is_ffs) { + if (ffs_enabled) + continue; + err = android_enable_function(dev, "ffs"); + if (err) + pr_err("android_usb: Cannot enable ffs (%d)", + err); + else + ffs_enabled = 1; + continue; + } + + err = android_enable_function(dev, name); + if (err) + pr_err("android_usb: Cannot enable '%s' (%d)", + name, err); + } + + mutex_unlock(&dev->mutex); + + return size; +} + +static ssize_t enable_show(struct device *pdev, struct device_attribute *attr, + char *buf) +{ + struct android_dev *dev = dev_get_drvdata(pdev); + return sprintf(buf, "%d\n", dev->enabled); +} + +static ssize_t enable_store(struct device *pdev, struct device_attribute *attr, + const char *buff, size_t size) +{ + struct android_dev *dev = dev_get_drvdata(pdev); + struct usb_composite_dev *cdev = dev->cdev; + struct android_usb_function *f; + int enabled = 0; + + + if (!cdev) + return -ENODEV; + + mutex_lock(&dev->mutex); + + sscanf(buff, "%d", &enabled); + if (enabled && !dev->enabled) { + /* + * Update values in composite driver's copy of + * device descriptor. + */ + cdev->desc.idVendor = device_desc.idVendor; + cdev->desc.idProduct = device_desc.idProduct; + cdev->desc.bcdDevice = device_desc.bcdDevice; + cdev->desc.bDeviceClass = device_desc.bDeviceClass; + cdev->desc.bDeviceSubClass = device_desc.bDeviceSubClass; + cdev->desc.bDeviceProtocol = device_desc.bDeviceProtocol; + list_for_each_entry(f, &dev->enabled_functions, enabled_list) { + if (f->enable) + f->enable(f); + } + android_enable(dev); + dev->enabled = true; + } else if (!enabled && dev->enabled) { + android_disable(dev); + list_for_each_entry(f, &dev->enabled_functions, enabled_list) { + if (f->disable) + f->disable(f); + } + dev->enabled = false; + } else { + pr_err("android_usb: already %s\n", + dev->enabled ? "enabled" : "disabled"); + } + + mutex_unlock(&dev->mutex); + return size; +} + +static ssize_t state_show(struct device *pdev, struct device_attribute *attr, + char *buf) +{ + struct android_dev *dev = dev_get_drvdata(pdev); + struct usb_composite_dev *cdev = dev->cdev; + char *state = "DISCONNECTED"; + unsigned long flags; + + if (!cdev) + goto out; + + spin_lock_irqsave(&cdev->lock, flags); + if (cdev->config) + state = "CONFIGURED"; + else if (dev->connected) + state = "CONNECTED"; + spin_unlock_irqrestore(&cdev->lock, flags); +out: + return sprintf(buf, "%s\n", state); +} + +#define DESCRIPTOR_ATTR(field, format_string) \ +static ssize_t \ +field ## _show(struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + return sprintf(buf, format_string, device_desc.field); \ +} \ +static ssize_t \ +field ## _store(struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t size) \ +{ \ + int value; \ + if (sscanf(buf, format_string, &value) == 1) { \ + device_desc.field = value; \ + return size; \ + } \ + return -1; \ +} \ +static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, field ## _show, field ## _store); + +#define DESCRIPTOR_STRING_ATTR(field, buffer) \ +static ssize_t \ +field ## _show(struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + return sprintf(buf, "%s", buffer); \ +} \ +static ssize_t \ +field ## _store(struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t size) \ +{ \ + if (size >= sizeof(buffer)) \ + return -EINVAL; \ + return strlcpy(buffer, buf, sizeof(buffer)); \ +} \ +static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, field ## _show, field ## _store); + + +DESCRIPTOR_ATTR(idVendor, "%04x\n") +DESCRIPTOR_ATTR(idProduct, "%04x\n") +DESCRIPTOR_ATTR(bcdDevice, "%04x\n") +DESCRIPTOR_ATTR(bDeviceClass, "%d\n") +DESCRIPTOR_ATTR(bDeviceSubClass, "%d\n") +DESCRIPTOR_ATTR(bDeviceProtocol, "%d\n") +DESCRIPTOR_STRING_ATTR(iManufacturer, manufacturer_string) +DESCRIPTOR_STRING_ATTR(iProduct, product_string) +DESCRIPTOR_STRING_ATTR(iSerial, serial_string) + +static DEVICE_ATTR(functions, S_IRUGO | S_IWUSR, functions_show, + functions_store); +static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store); +static DEVICE_ATTR(state, S_IRUGO, state_show, NULL); + +static struct device_attribute *android_usb_attributes[] = { + &dev_attr_idVendor, + &dev_attr_idProduct, + &dev_attr_bcdDevice, + &dev_attr_bDeviceClass, + &dev_attr_bDeviceSubClass, + &dev_attr_bDeviceProtocol, + &dev_attr_iManufacturer, + &dev_attr_iProduct, + &dev_attr_iSerial, + &dev_attr_functions, + &dev_attr_enable, + &dev_attr_state, + NULL +}; + +/*-------------------------------------------------------------------------*/ +/* Composite driver */ + +static int android_bind_config(struct usb_configuration *c) +{ + struct android_dev *dev = _android_dev; + int ret = 0; + + ret = android_bind_enabled_functions(dev, c); + if (ret) + return ret; + + return 0; +} + +static void android_unbind_config(struct usb_configuration *c) +{ + struct android_dev *dev = _android_dev; + + android_unbind_enabled_functions(dev, c); +} + +static int android_bind(struct usb_composite_dev *cdev) +{ + struct android_dev *dev = _android_dev; + struct usb_gadget *gadget = cdev->gadget; + int gcnum, id, ret; + + /* + * Start disconnected. Userspace will connect the gadget once + * it is done configuring the functions. + */ + usb_gadget_disconnect(gadget); + + ret = android_init_functions(dev->functions, cdev); + if (ret) + return ret; + + /* Allocate string descriptor numbers ... note that string + * contents can be overridden by the composite_dev glue. + */ + id = usb_string_id(cdev); + if (id < 0) + return id; + strings_dev[STRING_MANUFACTURER_IDX].id = id; + device_desc.iManufacturer = id; + + id = usb_string_id(cdev); + if (id < 0) + return id; + strings_dev[STRING_PRODUCT_IDX].id = id; + device_desc.iProduct = id; + + /* Default strings - should be updated by userspace */ + strncpy(manufacturer_string, "Android", sizeof(manufacturer_string)-1); + strncpy(product_string, "Android", sizeof(product_string) - 1); + strncpy(serial_string, "0123456789ABCDEF", sizeof(serial_string) - 1); + + id = usb_string_id(cdev); + if (id < 0) + return id; + strings_dev[STRING_SERIAL_IDX].id = id; + device_desc.iSerialNumber = id; + + gcnum = usb_gadget_controller_number(gadget); + if (gcnum >= 0) + device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); + else { + pr_warning("%s: controller '%s' not recognized\n", + longname, gadget->name); + device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); + } + + usb_gadget_set_selfpowered(gadget); + dev->cdev = cdev; + + return 0; +} + +static int android_usb_unbind(struct usb_composite_dev *cdev) +{ + struct android_dev *dev = _android_dev; + + cancel_work_sync(&dev->work); + android_cleanup_functions(dev->functions); + return 0; +} + +static struct usb_composite_driver android_usb_driver = { + .name = "android_usb", + .dev = &device_desc, + .strings = dev_strings, + .unbind = android_usb_unbind, + .max_speed = USB_SPEED_HIGH, +}; + +static int +android_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *c) +{ + struct android_dev *dev = _android_dev; + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_request *req = cdev->req; + struct android_usb_function *f; + int value = -EOPNOTSUPP; + unsigned long flags; + + req->zero = 0; + req->complete = composite_setup_complete; + req->length = 0; + gadget->ep0->driver_data = cdev; + + list_for_each_entry(f, &dev->enabled_functions, enabled_list) { + if (f->ctrlrequest) { + value = f->ctrlrequest(f, cdev, c); + if (value >= 0) + break; + } + } + + /* Special case the accessory function. + * It needs to handle control requests before it is enabled. + */ + if (value < 0) + value = acc_ctrlrequest(cdev, c); + + if (value < 0) + value = composite_setup(gadget, c); + + spin_lock_irqsave(&cdev->lock, flags); + if (!dev->connected) { + dev->connected = 1; + schedule_work(&dev->work); + } else if (c->bRequest == USB_REQ_SET_CONFIGURATION && + cdev->config) { + schedule_work(&dev->work); + } + spin_unlock_irqrestore(&cdev->lock, flags); + + return value; +} + +static void android_disconnect(struct usb_gadget *gadget) +{ + struct android_dev *dev = _android_dev; + struct usb_composite_dev *cdev = get_gadget_data(gadget); + unsigned long flags; + + composite_disconnect(gadget); + /* accessory HID support can be active while the + accessory function is not actually enabled, + so we need to inform it when we are disconnected. + */ + acc_disconnect(); + + spin_lock_irqsave(&cdev->lock, flags); + dev->connected = 0; + schedule_work(&dev->work); + spin_unlock_irqrestore(&cdev->lock, flags); +} + +static int android_create_device(struct android_dev *dev) +{ + struct device_attribute **attrs = android_usb_attributes; + struct device_attribute *attr; + int err; + + dev->dev = device_create(android_class, NULL, + MKDEV(0, 0), NULL, "android0"); + if (IS_ERR(dev->dev)) + return PTR_ERR(dev->dev); + + dev_set_drvdata(dev->dev, dev); + + while ((attr = *attrs++)) { + err = device_create_file(dev->dev, attr); + if (err) { + device_destroy(android_class, dev->dev->devt); + return err; + } + } + return 0; +} + + +static int __init init(void) +{ + struct android_dev *dev; + int err; + + android_class = class_create(THIS_MODULE, "android_usb"); + if (IS_ERR(android_class)) + return PTR_ERR(android_class); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->disable_depth = 1; + dev->functions = supported_functions; + INIT_LIST_HEAD(&dev->enabled_functions); + INIT_WORK(&dev->work, android_work); + mutex_init(&dev->mutex); + + err = android_create_device(dev); + if (err) { + class_destroy(android_class); + kfree(dev); + return err; + } + + _android_dev = dev; + + /* Override composite driver functions */ + composite_driver.setup = android_setup; + composite_driver.disconnect = android_disconnect; + + return usb_composite_probe(&android_usb_driver, android_bind); +} +module_init(init); + +static void __exit cleanup(void) +{ + usb_composite_unregister(&android_usb_driver); + class_destroy(android_class); + kfree(_android_dev); + _android_dev = NULL; +} +module_exit(cleanup); diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index baaebf28..8f82fc0e 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -734,6 +734,7 @@ int usb_add_config(struct usb_composite_dev *cdev, INIT_LIST_HEAD(&config->functions); config->next_interface_id = 0; + memset(config->interface, 0, sizeof(config->interface)); status = bind(config); if (status < 0) { @@ -774,6 +775,55 @@ done: return status; } +static int unbind_config(struct usb_composite_dev *cdev, + struct usb_configuration *config) +{ + while (!list_empty(&config->functions)) { + struct usb_function *f; + + f = list_first_entry(&config->functions, + struct usb_function, list); + list_del(&f->list); + if (f->unbind) { + DBG(cdev, "unbind function '%s'/%p\n", f->name, f); + f->unbind(config, f); + /* may free memory for "f" */ + } + } + if (config->unbind) { + DBG(cdev, "unbind config '%s'/%p\n", config->label, config); + config->unbind(config); + /* may free memory for "c" */ + } + return 0; +} + +/** + * usb_remove_config() - remove a configuration from a device. + * @cdev: wraps the USB gadget + * @config: the configuration + * + * Drivers must call usb_gadget_disconnect before calling this function + * to disconnect the device from the host and make sure the host will not + * try to enumerate the device while we are changing the config list. + */ +int usb_remove_config(struct usb_composite_dev *cdev, + struct usb_configuration *config) +{ + unsigned long flags; + + spin_lock_irqsave(&cdev->lock, flags); + + if (cdev->config == config) + reset_config(cdev); + + list_del(&config->list); + + spin_unlock_irqrestore(&cdev->lock, flags); + + return unbind_config(cdev, config); +} + /*-------------------------------------------------------------------------*/ /* We support strings in multiple languages ... string descriptor zero @@ -1328,28 +1378,10 @@ composite_unbind(struct usb_gadget *gadget) while (!list_empty(&cdev->configs)) { struct usb_configuration *c; - c = list_first_entry(&cdev->configs, struct usb_configuration, list); - while (!list_empty(&c->functions)) { - struct usb_function *f; - - f = list_first_entry(&c->functions, - struct usb_function, list); - list_del(&f->list); - if (f->unbind) { - DBG(cdev, "unbind function '%s'/%p\n", - f->name, f); - f->unbind(c, f); - /* may free memory for "f" */ - } - } list_del(&c->list); - if (c->unbind) { - DBG(cdev, "unbind config '%s'/%p\n", c->label, c); - c->unbind(c); - /* may free memory for "c" */ - } + unbind_config(cdev, c); } if (composite->unbind) composite->unbind(cdev); diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c new file mode 100644 index 00000000..a244265c --- /dev/null +++ b/drivers/usb/gadget/f_accessory.c @@ -0,0 +1,1180 @@ +/* + * Gadget Function Driver for Android USB accessories + * + * Copyright (C) 2011 Google, Inc. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +/* #define DEBUG */ +/* #define VERBOSE_DEBUG */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define BULK_BUFFER_SIZE 16384 +#define ACC_STRING_SIZE 256 + +#define PROTOCOL_VERSION 2 + +/* String IDs */ +#define INTERFACE_STRING_INDEX 0 + +/* number of tx and rx requests to allocate */ +#define TX_REQ_MAX 4 +#define RX_REQ_MAX 2 + +struct acc_hid_dev { + struct list_head list; + struct hid_device *hid; + struct acc_dev *dev; + /* accessory defined ID */ + int id; + /* HID report descriptor */ + u8 *report_desc; + /* length of HID report descriptor */ + int report_desc_len; + /* number of bytes of report_desc we have received so far */ + int report_desc_offset; +}; + +struct acc_dev { + struct usb_function function; + struct usb_composite_dev *cdev; + spinlock_t lock; + + struct usb_ep *ep_in; + struct usb_ep *ep_out; + + /* set to 1 when we connect */ + int online:1; + /* Set to 1 when we disconnect. + * Not cleared until our file is closed. + */ + int disconnected:1; + + /* strings sent by the host */ + char manufacturer[ACC_STRING_SIZE]; + char model[ACC_STRING_SIZE]; + char description[ACC_STRING_SIZE]; + char version[ACC_STRING_SIZE]; + char uri[ACC_STRING_SIZE]; + char serial[ACC_STRING_SIZE]; + + /* for acc_complete_set_string */ + int string_index; + + /* set to 1 if we have a pending start request */ + int start_requested; + + int audio_mode; + + /* synchronize access to our device file */ + atomic_t open_excl; + + struct list_head tx_idle; + + wait_queue_head_t read_wq; + wait_queue_head_t write_wq; + struct usb_request *rx_req[RX_REQ_MAX]; + int rx_done; + + /* delayed work for handling ACCESSORY_START */ + struct delayed_work start_work; + + /* worker for registering and unregistering hid devices */ + struct work_struct hid_work; + + /* list of active HID devices */ + struct list_head hid_list; + + /* list of new HID devices to register */ + struct list_head new_hid_list; + + /* list of dead HID devices to unregister */ + struct list_head dead_hid_list; +}; + +static struct usb_interface_descriptor acc_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, + .bInterfaceProtocol = 0, +}; + +static struct usb_endpoint_descriptor acc_highspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor acc_highspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor acc_fullspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor acc_fullspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *fs_acc_descs[] = { + (struct usb_descriptor_header *) &acc_interface_desc, + (struct usb_descriptor_header *) &acc_fullspeed_in_desc, + (struct usb_descriptor_header *) &acc_fullspeed_out_desc, + NULL, +}; + +static struct usb_descriptor_header *hs_acc_descs[] = { + (struct usb_descriptor_header *) &acc_interface_desc, + (struct usb_descriptor_header *) &acc_highspeed_in_desc, + (struct usb_descriptor_header *) &acc_highspeed_out_desc, + NULL, +}; + +static struct usb_string acc_string_defs[] = { + [INTERFACE_STRING_INDEX].s = "Android Accessory Interface", + { }, /* end of list */ +}; + +static struct usb_gadget_strings acc_string_table = { + .language = 0x0409, /* en-US */ + .strings = acc_string_defs, +}; + +static struct usb_gadget_strings *acc_strings[] = { + &acc_string_table, + NULL, +}; + +/* temporary variable used between acc_open() and acc_gadget_bind() */ +static struct acc_dev *_acc_dev; + +static inline struct acc_dev *func_to_dev(struct usb_function *f) +{ + return container_of(f, struct acc_dev, function); +} + +static struct usb_request *acc_request_new(struct usb_ep *ep, int buffer_size) +{ + struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req) + return NULL; + + /* now allocate buffers for the requests */ + req->buf = kmalloc(buffer_size, GFP_KERNEL); + if (!req->buf) { + usb_ep_free_request(ep, req); + return NULL; + } + + return req; +} + +static void acc_request_free(struct usb_request *req, struct usb_ep *ep) +{ + if (req) { + kfree(req->buf); + usb_ep_free_request(ep, req); + } +} + +/* add a request to the tail of a list */ +static void req_put(struct acc_dev *dev, struct list_head *head, + struct usb_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + list_add_tail(&req->list, head); + spin_unlock_irqrestore(&dev->lock, flags); +} + +/* remove a request from the head of a list */ +static struct usb_request *req_get(struct acc_dev *dev, struct list_head *head) +{ + unsigned long flags; + struct usb_request *req; + + spin_lock_irqsave(&dev->lock, flags); + if (list_empty(head)) { + req = 0; + } else { + req = list_first_entry(head, struct usb_request, list); + list_del(&req->list); + } + spin_unlock_irqrestore(&dev->lock, flags); + return req; +} + +static void acc_set_disconnected(struct acc_dev *dev) +{ + dev->online = 0; + dev->disconnected = 1; +} + +static void acc_complete_in(struct usb_ep *ep, struct usb_request *req) +{ + struct acc_dev *dev = _acc_dev; + + if (req->status != 0) + acc_set_disconnected(dev); + + req_put(dev, &dev->tx_idle, req); + + wake_up(&dev->write_wq); +} + +static void acc_complete_out(struct usb_ep *ep, struct usb_request *req) +{ + struct acc_dev *dev = _acc_dev; + + dev->rx_done = 1; + if (req->status != 0) + acc_set_disconnected(dev); + + wake_up(&dev->read_wq); +} + +static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req) +{ + struct acc_dev *dev = ep->driver_data; + char *string_dest = NULL; + int length = req->actual; + + if (req->status != 0) { + pr_err("acc_complete_set_string, err %d\n", req->status); + return; + } + + switch (dev->string_index) { + case ACCESSORY_STRING_MANUFACTURER: + string_dest = dev->manufacturer; + break; + case ACCESSORY_STRING_MODEL: + string_dest = dev->model; + break; + case ACCESSORY_STRING_DESCRIPTION: + string_dest = dev->description; + break; + case ACCESSORY_STRING_VERSION: + string_dest = dev->version; + break; + case ACCESSORY_STRING_URI: + string_dest = dev->uri; + break; + case ACCESSORY_STRING_SERIAL: + string_dest = dev->serial; + break; + } + if (string_dest) { + unsigned long flags; + + if (length >= ACC_STRING_SIZE) + length = ACC_STRING_SIZE - 1; + + spin_lock_irqsave(&dev->lock, flags); + memcpy(string_dest, req->buf, length); + /* ensure zero termination */ + string_dest[length] = 0; + spin_unlock_irqrestore(&dev->lock, flags); + } else { + pr_err("unknown accessory string index %d\n", + dev->string_index); + } +} + +static void acc_complete_set_hid_report_desc(struct usb_ep *ep, + struct usb_request *req) +{ + struct acc_hid_dev *hid = req->context; + struct acc_dev *dev = hid->dev; + int length = req->actual; + + if (req->status != 0) { + pr_err("acc_complete_set_hid_report_desc, err %d\n", + req->status); + return; + } + + memcpy(hid->report_desc + hid->report_desc_offset, req->buf, length); + hid->report_desc_offset += length; + if (hid->report_desc_offset == hid->report_desc_len) { + /* After we have received the entire report descriptor + * we schedule work to initialize the HID device + */ + schedule_work(&dev->hid_work); + } +} + +static void acc_complete_send_hid_event(struct usb_ep *ep, + struct usb_request *req) +{ + struct acc_hid_dev *hid = req->context; + int length = req->actual; + + if (req->status != 0) { + pr_err("acc_complete_send_hid_event, err %d\n", req->status); + return; + } + + hid_report_raw_event(hid->hid, HID_INPUT_REPORT, req->buf, length, 1); +} + +static int acc_hid_parse(struct hid_device *hid) +{ + struct acc_hid_dev *hdev = hid->driver_data; + + hid_parse_report(hid, hdev->report_desc, hdev->report_desc_len); + return 0; +} + +static int acc_hid_start(struct hid_device *hid) +{ + return 0; +} + +static void acc_hid_stop(struct hid_device *hid) +{ +} + +static int acc_hid_open(struct hid_device *hid) +{ + return 0; +} + +static void acc_hid_close(struct hid_device *hid) +{ +} + +static struct hid_ll_driver acc_hid_ll_driver = { + .parse = acc_hid_parse, + .start = acc_hid_start, + .stop = acc_hid_stop, + .open = acc_hid_open, + .close = acc_hid_close, +}; + +static struct acc_hid_dev *acc_hid_new(struct acc_dev *dev, + int id, int desc_len) +{ + struct acc_hid_dev *hdev; + + hdev = kzalloc(sizeof(*hdev), GFP_ATOMIC); + if (!hdev) + return NULL; + hdev->report_desc = kzalloc(desc_len, GFP_ATOMIC); + if (!hdev->report_desc) { + kfree(hdev); + return NULL; + } + hdev->dev = dev; + hdev->id = id; + hdev->report_desc_len = desc_len; + + return hdev; +} + +static struct acc_hid_dev *acc_hid_get(struct list_head *list, int id) +{ + struct acc_hid_dev *hid; + + list_for_each_entry(hid, list, list) { + if (hid->id == id) + return hid; + } + return NULL; +} + +static int acc_register_hid(struct acc_dev *dev, int id, int desc_length) +{ + struct acc_hid_dev *hid; + unsigned long flags; + + /* report descriptor length must be > 0 */ + if (desc_length <= 0) + return -EINVAL; + + spin_lock_irqsave(&dev->lock, flags); + /* replace HID if one already exists with this ID */ + hid = acc_hid_get(&dev->hid_list, id); + if (!hid) + hid = acc_hid_get(&dev->new_hid_list, id); + if (hid) + list_move(&hid->list, &dev->dead_hid_list); + + hid = acc_hid_new(dev, id, desc_length); + if (!hid) { + spin_unlock_irqrestore(&dev->lock, flags); + return -ENOMEM; + } + + list_add(&hid->list, &dev->new_hid_list); + spin_unlock_irqrestore(&dev->lock, flags); + + /* schedule work to register the HID device */ + schedule_work(&dev->hid_work); + return 0; +} + +static int acc_unregister_hid(struct acc_dev *dev, int id) +{ + struct acc_hid_dev *hid; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + hid = acc_hid_get(&dev->hid_list, id); + if (!hid) + hid = acc_hid_get(&dev->new_hid_list, id); + if (!hid) { + spin_unlock_irqrestore(&dev->lock, flags); + return -EINVAL; + } + + list_move(&hid->list, &dev->dead_hid_list); + spin_unlock_irqrestore(&dev->lock, flags); + + schedule_work(&dev->hid_work); + return 0; +} + +static int create_bulk_endpoints(struct acc_dev *dev, + struct usb_endpoint_descriptor *in_desc, + struct usb_endpoint_descriptor *out_desc) +{ + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req; + struct usb_ep *ep; + int i; + + DBG(cdev, "create_bulk_endpoints dev: %p\n", dev); + + ep = usb_ep_autoconfig(cdev->gadget, in_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_in failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_in = ep; + + ep = usb_ep_autoconfig(cdev->gadget, out_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for ep_out got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_out = ep; + + ep = usb_ep_autoconfig(cdev->gadget, out_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for ep_out got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_out = ep; + + /* now allocate requests for our endpoints */ + for (i = 0; i < TX_REQ_MAX; i++) { + req = acc_request_new(dev->ep_in, BULK_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = acc_complete_in; + req_put(dev, &dev->tx_idle, req); + } + for (i = 0; i < RX_REQ_MAX; i++) { + req = acc_request_new(dev->ep_out, BULK_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = acc_complete_out; + dev->rx_req[i] = req; + } + + return 0; + +fail: + pr_err("acc_bind() could not allocate requests\n"); + while ((req = req_get(dev, &dev->tx_idle))) + acc_request_free(req, dev->ep_in); + for (i = 0; i < RX_REQ_MAX; i++) + acc_request_free(dev->rx_req[i], dev->ep_out); + return -1; +} + +static ssize_t acc_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + struct acc_dev *dev = fp->private_data; + struct usb_request *req; + int r = count, xfer; + int ret = 0; + + pr_debug("acc_read(%d)\n", count); + + if (dev->disconnected) + return -ENODEV; + + if (count > BULK_BUFFER_SIZE) + count = BULK_BUFFER_SIZE; + + /* we will block until we're online */ + pr_debug("acc_read: waiting for online\n"); + ret = wait_event_interruptible(dev->read_wq, dev->online); + if (ret < 0) { + r = ret; + goto done; + } + +requeue_req: + /* queue a request */ + req = dev->rx_req[0]; + req->length = count; + dev->rx_done = 0; + ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL); + if (ret < 0) { + r = -EIO; + goto done; + } else { + pr_debug("rx %p queue\n", req); + } + + /* wait for a request to complete */ + ret = wait_event_interruptible(dev->read_wq, dev->rx_done); + if (ret < 0) { + r = ret; + usb_ep_dequeue(dev->ep_out, req); + goto done; + } + if (dev->online) { + /* If we got a 0-len packet, throw it back and try again. */ + if (req->actual == 0) + goto requeue_req; + + pr_debug("rx %p %d\n", req, req->actual); + xfer = (req->actual < count) ? req->actual : count; + r = xfer; + if (copy_to_user(buf, req->buf, xfer)) + r = -EFAULT; + } else + r = -EIO; + +done: + pr_debug("acc_read returning %d\n", r); + return r; +} + +static ssize_t acc_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + struct acc_dev *dev = fp->private_data; + struct usb_request *req = 0; + int r = count, xfer; + int ret; + + pr_debug("acc_write(%d)\n", count); + + if (!dev->online || dev->disconnected) + return -ENODEV; + + while (count > 0) { + if (!dev->online) { + pr_debug("acc_write dev->error\n"); + r = -EIO; + break; + } + + /* get an idle tx request to use */ + req = 0; + ret = wait_event_interruptible(dev->write_wq, + ((req = req_get(dev, &dev->tx_idle)) || !dev->online)); + if (!req) { + r = ret; + break; + } + + if (count > BULK_BUFFER_SIZE) + xfer = BULK_BUFFER_SIZE; + else + xfer = count; + if (copy_from_user(req->buf, buf, xfer)) { + r = -EFAULT; + break; + } + + req->length = xfer; + ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL); + if (ret < 0) { + pr_debug("acc_write: xfer error %d\n", ret); + r = -EIO; + break; + } + + buf += xfer; + count -= xfer; + + /* zero this so we don't try to free it on error exit */ + req = 0; + } + + if (req) + req_put(dev, &dev->tx_idle, req); + + pr_debug("acc_write returning %d\n", r); + return r; +} + +static long acc_ioctl(struct file *fp, unsigned code, unsigned long value) +{ + struct acc_dev *dev = fp->private_data; + char *src = NULL; + int ret; + + switch (code) { + case ACCESSORY_GET_STRING_MANUFACTURER: + src = dev->manufacturer; + break; + case ACCESSORY_GET_STRING_MODEL: + src = dev->model; + break; + case ACCESSORY_GET_STRING_DESCRIPTION: + src = dev->description; + break; + case ACCESSORY_GET_STRING_VERSION: + src = dev->version; + break; + case ACCESSORY_GET_STRING_URI: + src = dev->uri; + break; + case ACCESSORY_GET_STRING_SERIAL: + src = dev->serial; + break; + case ACCESSORY_IS_START_REQUESTED: + return dev->start_requested; + case ACCESSORY_GET_AUDIO_MODE: + return dev->audio_mode; + } + if (!src) + return -EINVAL; + + ret = strlen(src) + 1; + if (copy_to_user((void __user *)value, src, ret)) + ret = -EFAULT; + return ret; +} + +static int acc_open(struct inode *ip, struct file *fp) +{ + printk(KERN_INFO "acc_open\n"); + if (atomic_xchg(&_acc_dev->open_excl, 1)) + return -EBUSY; + + _acc_dev->disconnected = 0; + fp->private_data = _acc_dev; + return 0; +} + +static int acc_release(struct inode *ip, struct file *fp) +{ + printk(KERN_INFO "acc_release\n"); + + WARN_ON(!atomic_xchg(&_acc_dev->open_excl, 0)); + _acc_dev->disconnected = 0; + return 0; +} + +/* file operations for /dev/usb_accessory */ +static const struct file_operations acc_fops = { + .owner = THIS_MODULE, + .read = acc_read, + .write = acc_write, + .unlocked_ioctl = acc_ioctl, + .open = acc_open, + .release = acc_release, +}; + +static int acc_hid_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int ret; + + ret = hid_parse(hdev); + if (ret) + return ret; + return hid_hw_start(hdev, HID_CONNECT_DEFAULT); +} + +static struct miscdevice acc_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "usb_accessory", + .fops = &acc_fops, +}; + +static const struct hid_device_id acc_hid_table[] = { + { HID_USB_DEVICE(HID_ANY_ID, HID_ANY_ID) }, + { } +}; + +static struct hid_driver acc_hid_driver = { + .name = "USB accessory", + .id_table = acc_hid_table, + .probe = acc_hid_probe, +}; + +static int acc_ctrlrequest(struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *ctrl) +{ + struct acc_dev *dev = _acc_dev; + int value = -EOPNOTSUPP; + struct acc_hid_dev *hid; + int offset; + u8 b_requestType = ctrl->bRequestType; + u8 b_request = ctrl->bRequest; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + unsigned long flags; + +/* + printk(KERN_INFO "acc_ctrlrequest " + "%02x.%02x v%04x i%04x l%u\n", + b_requestType, b_request, + w_value, w_index, w_length); +*/ + + if (b_requestType == (USB_DIR_OUT | USB_TYPE_VENDOR)) { + if (b_request == ACCESSORY_START) { + dev->start_requested = 1; + schedule_delayed_work( + &dev->start_work, msecs_to_jiffies(10)); + value = 0; + } else if (b_request == ACCESSORY_SEND_STRING) { + dev->string_index = w_index; + cdev->gadget->ep0->driver_data = dev; + cdev->req->complete = acc_complete_set_string; + value = w_length; + } else if (b_request == ACCESSORY_SET_AUDIO_MODE && + w_index == 0 && w_length == 0) { + dev->audio_mode = w_value; + value = 0; + } else if (b_request == ACCESSORY_REGISTER_HID) { + value = acc_register_hid(dev, w_value, w_index); + } else if (b_request == ACCESSORY_UNREGISTER_HID) { + value = acc_unregister_hid(dev, w_value); + } else if (b_request == ACCESSORY_SET_HID_REPORT_DESC) { + spin_lock_irqsave(&dev->lock, flags); + hid = acc_hid_get(&dev->new_hid_list, w_value); + spin_unlock_irqrestore(&dev->lock, flags); + if (!hid) { + value = -EINVAL; + goto err; + } + offset = w_index; + if (offset != hid->report_desc_offset + || offset + w_length > hid->report_desc_len) { + value = -EINVAL; + goto err; + } + cdev->req->context = hid; + cdev->req->complete = acc_complete_set_hid_report_desc; + value = w_length; + } else if (b_request == ACCESSORY_SEND_HID_EVENT) { + spin_lock_irqsave(&dev->lock, flags); + hid = acc_hid_get(&dev->hid_list, w_value); + spin_unlock_irqrestore(&dev->lock, flags); + if (!hid) { + value = -EINVAL; + goto err; + } + cdev->req->context = hid; + cdev->req->complete = acc_complete_send_hid_event; + value = w_length; + } + } else if (b_requestType == (USB_DIR_IN | USB_TYPE_VENDOR)) { + if (b_request == ACCESSORY_GET_PROTOCOL) { + *((u16 *)cdev->req->buf) = PROTOCOL_VERSION; + value = sizeof(u16); + + /* clear any string left over from a previous session */ + memset(dev->manufacturer, 0, sizeof(dev->manufacturer)); + memset(dev->model, 0, sizeof(dev->model)); + memset(dev->description, 0, sizeof(dev->description)); + memset(dev->version, 0, sizeof(dev->version)); + memset(dev->uri, 0, sizeof(dev->uri)); + memset(dev->serial, 0, sizeof(dev->serial)); + dev->start_requested = 0; + dev->audio_mode = 0; + } + } + + if (value >= 0) { + cdev->req->zero = 0; + cdev->req->length = value; + value = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC); + if (value < 0) + ERROR(cdev, "%s setup response queue error\n", + __func__); + } + +err: + if (value == -EOPNOTSUPP) + VDBG(cdev, + "unknown class-specific control req " + "%02x.%02x v%04x i%04x l%u\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + return value; +} + +static int +acc_function_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct acc_dev *dev = func_to_dev(f); + int id; + int ret; + + DBG(cdev, "acc_function_bind dev: %p\n", dev); + + ret = hid_register_driver(&acc_hid_driver); + if (ret) + return ret; + + dev->start_requested = 0; + + /* allocate interface ID(s) */ + id = usb_interface_id(c, f); + if (id < 0) + return id; + acc_interface_desc.bInterfaceNumber = id; + + /* allocate endpoints */ + ret = create_bulk_endpoints(dev, &acc_fullspeed_in_desc, + &acc_fullspeed_out_desc); + if (ret) + return ret; + + /* support high speed hardware */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + acc_highspeed_in_desc.bEndpointAddress = + acc_fullspeed_in_desc.bEndpointAddress; + acc_highspeed_out_desc.bEndpointAddress = + acc_fullspeed_out_desc.bEndpointAddress; + } + + DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + f->name, dev->ep_in->name, dev->ep_out->name); + return 0; +} + +static void +kill_all_hid_devices(struct acc_dev *dev) +{ + struct acc_hid_dev *hid; + struct list_head *entry, *temp; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + list_for_each_safe(entry, temp, &dev->hid_list) { + hid = list_entry(entry, struct acc_hid_dev, list); + list_del(&hid->list); + list_add(&hid->list, &dev->dead_hid_list); + } + list_for_each_safe(entry, temp, &dev->new_hid_list) { + hid = list_entry(entry, struct acc_hid_dev, list); + list_del(&hid->list); + list_add(&hid->list, &dev->dead_hid_list); + } + spin_unlock_irqrestore(&dev->lock, flags); + + schedule_work(&dev->hid_work); +} + +static void +acc_hid_unbind(struct acc_dev *dev) +{ + hid_unregister_driver(&acc_hid_driver); + kill_all_hid_devices(dev); +} + +static void +acc_function_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct acc_dev *dev = func_to_dev(f); + struct usb_request *req; + int i; + + while ((req = req_get(dev, &dev->tx_idle))) + acc_request_free(req, dev->ep_in); + for (i = 0; i < RX_REQ_MAX; i++) + acc_request_free(dev->rx_req[i], dev->ep_out); + + acc_hid_unbind(dev); +} + +static void acc_start_work(struct work_struct *data) +{ + char *envp[2] = { "ACCESSORY=START", NULL }; + kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp); +} + +static int acc_hid_init(struct acc_hid_dev *hdev) +{ + struct hid_device *hid; + int ret; + + hid = hid_allocate_device(); + if (IS_ERR(hid)) + return PTR_ERR(hid); + + hid->ll_driver = &acc_hid_ll_driver; + hid->dev.parent = acc_device.this_device; + + hid->bus = BUS_USB; + hid->vendor = HID_ANY_ID; + hid->product = HID_ANY_ID; + hid->driver_data = hdev; + ret = hid_add_device(hid); + if (ret) { + pr_err("can't add hid device: %d\n", ret); + hid_destroy_device(hid); + return ret; + } + + hdev->hid = hid; + return 0; +} + +static void acc_hid_delete(struct acc_hid_dev *hid) +{ + kfree(hid->report_desc); + kfree(hid); +} + +static void acc_hid_work(struct work_struct *data) +{ + struct acc_dev *dev = _acc_dev; + struct list_head *entry, *temp; + struct acc_hid_dev *hid; + struct list_head new_list, dead_list; + unsigned long flags; + + INIT_LIST_HEAD(&new_list); + + spin_lock_irqsave(&dev->lock, flags); + + /* copy hids that are ready for initialization to new_list */ + list_for_each_safe(entry, temp, &dev->new_hid_list) { + hid = list_entry(entry, struct acc_hid_dev, list); + if (hid->report_desc_offset == hid->report_desc_len) + list_move(&hid->list, &new_list); + } + + if (list_empty(&dev->dead_hid_list)) { + INIT_LIST_HEAD(&dead_list); + } else { + /* move all of dev->dead_hid_list to dead_list */ + dead_list.prev = dev->dead_hid_list.prev; + dead_list.next = dev->dead_hid_list.next; + dead_list.next->prev = &dead_list; + dead_list.prev->next = &dead_list; + INIT_LIST_HEAD(&dev->dead_hid_list); + } + + spin_unlock_irqrestore(&dev->lock, flags); + + /* register new HID devices */ + list_for_each_safe(entry, temp, &new_list) { + hid = list_entry(entry, struct acc_hid_dev, list); + if (acc_hid_init(hid)) { + pr_err("can't add HID device %p\n", hid); + acc_hid_delete(hid); + } else { + spin_lock_irqsave(&dev->lock, flags); + list_move(&hid->list, &dev->hid_list); + spin_unlock_irqrestore(&dev->lock, flags); + } + } + + /* remove dead HID devices */ + list_for_each_safe(entry, temp, &dead_list) { + hid = list_entry(entry, struct acc_hid_dev, list); + list_del(&hid->list); + if (hid->hid) + hid_destroy_device(hid->hid); + acc_hid_delete(hid); + } +} + +static int acc_function_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct acc_dev *dev = func_to_dev(f); + struct usb_composite_dev *cdev = f->config->cdev; + int ret; + + DBG(cdev, "acc_function_set_alt intf: %d alt: %d\n", intf, alt); + + ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in); + if (ret) + return ret; + + ret = usb_ep_enable(dev->ep_in); + if (ret) + return ret; + + ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out); + if (ret) + return ret; + + ret = usb_ep_enable(dev->ep_out); + if (ret) { + usb_ep_disable(dev->ep_in); + return ret; + } + + dev->online = 1; + + /* readers may be blocked waiting for us to go online */ + wake_up(&dev->read_wq); + return 0; +} + +static void acc_function_disable(struct usb_function *f) +{ + struct acc_dev *dev = func_to_dev(f); + struct usb_composite_dev *cdev = dev->cdev; + + DBG(cdev, "acc_function_disable\n"); + acc_set_disconnected(dev); + usb_ep_disable(dev->ep_in); + usb_ep_disable(dev->ep_out); + + /* readers may be blocked waiting for us to go online */ + wake_up(&dev->read_wq); + + VDBG(cdev, "%s disabled\n", dev->function.name); +} + +static int acc_bind_config(struct usb_configuration *c) +{ + struct acc_dev *dev = _acc_dev; + int ret; + + printk(KERN_INFO "acc_bind_config\n"); + + /* allocate a string ID for our interface */ + if (acc_string_defs[INTERFACE_STRING_INDEX].id == 0) { + ret = usb_string_id(c->cdev); + if (ret < 0) + return ret; + acc_string_defs[INTERFACE_STRING_INDEX].id = ret; + acc_interface_desc.iInterface = ret; + } + + dev->cdev = c->cdev; + dev->function.name = "accessory"; + dev->function.strings = acc_strings, + dev->function.descriptors = fs_acc_descs; + dev->function.hs_descriptors = hs_acc_descs; + dev->function.bind = acc_function_bind; + dev->function.unbind = acc_function_unbind; + dev->function.set_alt = acc_function_set_alt; + dev->function.disable = acc_function_disable; + + return usb_add_function(c, &dev->function); +} + +static int acc_setup(void) +{ + struct acc_dev *dev; + int ret; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + spin_lock_init(&dev->lock); + init_waitqueue_head(&dev->read_wq); + init_waitqueue_head(&dev->write_wq); + atomic_set(&dev->open_excl, 0); + INIT_LIST_HEAD(&dev->tx_idle); + INIT_LIST_HEAD(&dev->hid_list); + INIT_LIST_HEAD(&dev->new_hid_list); + INIT_LIST_HEAD(&dev->dead_hid_list); + INIT_DELAYED_WORK(&dev->start_work, acc_start_work); + INIT_WORK(&dev->hid_work, acc_hid_work); + + /* _acc_dev must be set before calling usb_gadget_register_driver */ + _acc_dev = dev; + + ret = misc_register(&acc_device); + if (ret) + goto err; + + return 0; + +err: + kfree(dev); + pr_err("USB accessory gadget driver failed to initialize\n"); + return ret; +} + +static void acc_disconnect(void) +{ + /* unregister all HID devices if USB is disconnected */ + kill_all_hid_devices(_acc_dev); +} + +static void acc_cleanup(void) +{ + misc_deregister(&acc_device); + kfree(_acc_dev); + _acc_dev = NULL; +} diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c new file mode 100644 index 00000000..1629ffb5 --- /dev/null +++ b/drivers/usb/gadget/f_adb.c @@ -0,0 +1,619 @@ +/* + * Gadget Driver for Android ADB + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ADB_BULK_BUFFER_SIZE 4096 + +/* number of tx requests to allocate */ +#define TX_REQ_MAX 4 + +static const char adb_shortname[] = "android_adb"; + +struct adb_dev { + struct usb_function function; + struct usb_composite_dev *cdev; + spinlock_t lock; + + struct usb_ep *ep_in; + struct usb_ep *ep_out; + + int online; + int error; + + atomic_t read_excl; + atomic_t write_excl; + atomic_t open_excl; + + struct list_head tx_idle; + + wait_queue_head_t read_wq; + wait_queue_head_t write_wq; + struct usb_request *rx_req; + int rx_done; +}; + +static struct usb_interface_descriptor adb_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = 0xFF, + .bInterfaceSubClass = 0x42, + .bInterfaceProtocol = 1, +}; + +static struct usb_endpoint_descriptor adb_highspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor adb_highspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor adb_fullspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor adb_fullspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *fs_adb_descs[] = { + (struct usb_descriptor_header *) &adb_interface_desc, + (struct usb_descriptor_header *) &adb_fullspeed_in_desc, + (struct usb_descriptor_header *) &adb_fullspeed_out_desc, + NULL, +}; + +static struct usb_descriptor_header *hs_adb_descs[] = { + (struct usb_descriptor_header *) &adb_interface_desc, + (struct usb_descriptor_header *) &adb_highspeed_in_desc, + (struct usb_descriptor_header *) &adb_highspeed_out_desc, + NULL, +}; + +static void adb_ready_callback(void); +static void adb_closed_callback(void); + +/* temporary variable used between adb_open() and adb_gadget_bind() */ +static struct adb_dev *_adb_dev; + +static inline struct adb_dev *func_to_adb(struct usb_function *f) +{ + return container_of(f, struct adb_dev, function); +} + + +static struct usb_request *adb_request_new(struct usb_ep *ep, int buffer_size) +{ + struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req) + return NULL; + + /* now allocate buffers for the requests */ + req->buf = kmalloc(buffer_size, GFP_KERNEL); + if (!req->buf) { + usb_ep_free_request(ep, req); + return NULL; + } + + return req; +} + +static void adb_request_free(struct usb_request *req, struct usb_ep *ep) +{ + if (req) { + kfree(req->buf); + usb_ep_free_request(ep, req); + } +} + +static inline int adb_lock(atomic_t *excl) +{ + if (atomic_inc_return(excl) == 1) { + return 0; + } else { + atomic_dec(excl); + return -1; + } +} + +static inline void adb_unlock(atomic_t *excl) +{ + atomic_dec(excl); +} + +/* add a request to the tail of a list */ +void adb_req_put(struct adb_dev *dev, struct list_head *head, + struct usb_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + list_add_tail(&req->list, head); + spin_unlock_irqrestore(&dev->lock, flags); +} + +/* remove a request from the head of a list */ +struct usb_request *adb_req_get(struct adb_dev *dev, struct list_head *head) +{ + unsigned long flags; + struct usb_request *req; + + spin_lock_irqsave(&dev->lock, flags); + if (list_empty(head)) { + req = 0; + } else { + req = list_first_entry(head, struct usb_request, list); + list_del(&req->list); + } + spin_unlock_irqrestore(&dev->lock, flags); + return req; +} + +static void adb_complete_in(struct usb_ep *ep, struct usb_request *req) +{ + struct adb_dev *dev = _adb_dev; + + if (req->status != 0) + dev->error = 1; + + adb_req_put(dev, &dev->tx_idle, req); + + wake_up(&dev->write_wq); +} + +static void adb_complete_out(struct usb_ep *ep, struct usb_request *req) +{ + struct adb_dev *dev = _adb_dev; + + dev->rx_done = 1; + if (req->status != 0 && req->status != -ECONNRESET) + dev->error = 1; + + wake_up(&dev->read_wq); +} + +static int adb_create_bulk_endpoints(struct adb_dev *dev, + struct usb_endpoint_descriptor *in_desc, + struct usb_endpoint_descriptor *out_desc) +{ + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req; + struct usb_ep *ep; + int i; + + DBG(cdev, "create_bulk_endpoints dev: %p\n", dev); + + ep = usb_ep_autoconfig(cdev->gadget, in_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_in failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_in = ep; + + ep = usb_ep_autoconfig(cdev->gadget, out_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for adb ep_out got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_out = ep; + + /* now allocate requests for our endpoints */ + req = adb_request_new(dev->ep_out, ADB_BULK_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = adb_complete_out; + dev->rx_req = req; + + for (i = 0; i < TX_REQ_MAX; i++) { + req = adb_request_new(dev->ep_in, ADB_BULK_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = adb_complete_in; + adb_req_put(dev, &dev->tx_idle, req); + } + + return 0; + +fail: + printk(KERN_ERR "adb_bind() could not allocate requests\n"); + return -1; +} + +static ssize_t adb_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + struct adb_dev *dev = fp->private_data; + struct usb_request *req; + int r = count, xfer; + int ret; + + pr_debug("adb_read(%d)\n", count); + if (!_adb_dev) + return -ENODEV; + + if (count > ADB_BULK_BUFFER_SIZE) + return -EINVAL; + + if (adb_lock(&dev->read_excl)) + return -EBUSY; + + /* we will block until we're online */ + while (!(dev->online || dev->error)) { + pr_debug("adb_read: waiting for online state\n"); + ret = wait_event_interruptible(dev->read_wq, + (dev->online || dev->error)); + if (ret < 0) { + adb_unlock(&dev->read_excl); + return ret; + } + } + if (dev->error) { + r = -EIO; + goto done; + } + +requeue_req: + /* queue a request */ + req = dev->rx_req; + req->length = count; + dev->rx_done = 0; + ret = usb_ep_queue(dev->ep_out, req, GFP_ATOMIC); + if (ret < 0) { + pr_debug("adb_read: failed to queue req %p (%d)\n", req, ret); + r = -EIO; + dev->error = 1; + goto done; + } else { + pr_debug("rx %p queue\n", req); + } + + /* wait for a request to complete */ + ret = wait_event_interruptible(dev->read_wq, dev->rx_done); + if (ret < 0) { + if (ret != -ERESTARTSYS) + dev->error = 1; + r = ret; + usb_ep_dequeue(dev->ep_out, req); + goto done; + } + if (!dev->error) { + /* If we got a 0-len packet, throw it back and try again. */ + if (req->actual == 0) + goto requeue_req; + + pr_debug("rx %p %d\n", req, req->actual); + xfer = (req->actual < count) ? req->actual : count; + if (copy_to_user(buf, req->buf, xfer)) + r = -EFAULT; + + } else + r = -EIO; + +done: + adb_unlock(&dev->read_excl); + pr_debug("adb_read returning %d\n", r); + return r; +} + +static ssize_t adb_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + struct adb_dev *dev = fp->private_data; + struct usb_request *req = 0; + int r = count, xfer; + int ret; + + if (!_adb_dev) + return -ENODEV; + pr_debug("adb_write(%d)\n", count); + + if (adb_lock(&dev->write_excl)) + return -EBUSY; + + while (count > 0) { + if (dev->error) { + pr_debug("adb_write dev->error\n"); + r = -EIO; + break; + } + + /* get an idle tx request to use */ + req = 0; + ret = wait_event_interruptible(dev->write_wq, + (req = adb_req_get(dev, &dev->tx_idle)) || dev->error); + + if (ret < 0) { + r = ret; + break; + } + + if (req != 0) { + if (count > ADB_BULK_BUFFER_SIZE) + xfer = ADB_BULK_BUFFER_SIZE; + else + xfer = count; + if (copy_from_user(req->buf, buf, xfer)) { + r = -EFAULT; + break; + } + + req->length = xfer; + ret = usb_ep_queue(dev->ep_in, req, GFP_ATOMIC); + if (ret < 0) { + pr_debug("adb_write: xfer error %d\n", ret); + dev->error = 1; + r = -EIO; + break; + } + + buf += xfer; + count -= xfer; + + /* zero this so we don't try to free it on error exit */ + req = 0; + } + } + + if (req) + adb_req_put(dev, &dev->tx_idle, req); + + adb_unlock(&dev->write_excl); + pr_debug("adb_write returning %d\n", r); + return r; +} + +static int adb_open(struct inode *ip, struct file *fp) +{ + pr_info("adb_open\n"); + if (!_adb_dev) + return -ENODEV; + + if (adb_lock(&_adb_dev->open_excl)) + return -EBUSY; + + fp->private_data = _adb_dev; + + /* clear the error latch */ + _adb_dev->error = 0; + + adb_ready_callback(); + + return 0; +} + +static int adb_release(struct inode *ip, struct file *fp) +{ + pr_info("adb_release\n"); + + adb_closed_callback(); + + adb_unlock(&_adb_dev->open_excl); + return 0; +} + +/* file operations for ADB device /dev/android_adb */ +static const struct file_operations adb_fops = { + .owner = THIS_MODULE, + .read = adb_read, + .write = adb_write, + .open = adb_open, + .release = adb_release, +}; + +static struct miscdevice adb_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = adb_shortname, + .fops = &adb_fops, +}; + + + + +static int +adb_function_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct adb_dev *dev = func_to_adb(f); + int id; + int ret; + + dev->cdev = cdev; + DBG(cdev, "adb_function_bind dev: %p\n", dev); + + /* allocate interface ID(s) */ + id = usb_interface_id(c, f); + if (id < 0) + return id; + adb_interface_desc.bInterfaceNumber = id; + + /* allocate endpoints */ + ret = adb_create_bulk_endpoints(dev, &adb_fullspeed_in_desc, + &adb_fullspeed_out_desc); + if (ret) + return ret; + + /* support high speed hardware */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + adb_highspeed_in_desc.bEndpointAddress = + adb_fullspeed_in_desc.bEndpointAddress; + adb_highspeed_out_desc.bEndpointAddress = + adb_fullspeed_out_desc.bEndpointAddress; + } + + DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + f->name, dev->ep_in->name, dev->ep_out->name); + return 0; +} + +static void +adb_function_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct adb_dev *dev = func_to_adb(f); + struct usb_request *req; + + + dev->online = 0; + dev->error = 1; + + wake_up(&dev->read_wq); + + adb_request_free(dev->rx_req, dev->ep_out); + while ((req = adb_req_get(dev, &dev->tx_idle))) + adb_request_free(req, dev->ep_in); +} + +static int adb_function_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct adb_dev *dev = func_to_adb(f); + struct usb_composite_dev *cdev = f->config->cdev; + int ret; + + DBG(cdev, "adb_function_set_alt intf: %d alt: %d\n", intf, alt); + + ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in); + if (ret) + return ret; + + ret = usb_ep_enable(dev->ep_in); + if (ret) + return ret; + + ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out); + if (ret) + return ret; + + ret = usb_ep_enable(dev->ep_out); + if (ret) { + usb_ep_disable(dev->ep_in); + return ret; + } + dev->online = 1; + + /* readers may be blocked waiting for us to go online */ + wake_up(&dev->read_wq); + return 0; +} + +static void adb_function_disable(struct usb_function *f) +{ + struct adb_dev *dev = func_to_adb(f); + struct usb_composite_dev *cdev = dev->cdev; + + DBG(cdev, "adb_function_disable cdev %p\n", cdev); + dev->online = 0; + dev->error = 1; + usb_ep_disable(dev->ep_in); + usb_ep_disable(dev->ep_out); + + /* readers may be blocked waiting for us to go online */ + wake_up(&dev->read_wq); + + VDBG(cdev, "%s disabled\n", dev->function.name); +} + +static int adb_bind_config(struct usb_configuration *c) +{ + struct adb_dev *dev = _adb_dev; + + printk(KERN_INFO "adb_bind_config\n"); + + dev->cdev = c->cdev; + dev->function.name = "adb"; + dev->function.descriptors = fs_adb_descs; + dev->function.hs_descriptors = hs_adb_descs; + dev->function.bind = adb_function_bind; + dev->function.unbind = adb_function_unbind; + dev->function.set_alt = adb_function_set_alt; + dev->function.disable = adb_function_disable; + + return usb_add_function(c, &dev->function); +} + +static int adb_setup(void) +{ + struct adb_dev *dev; + int ret; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + spin_lock_init(&dev->lock); + + init_waitqueue_head(&dev->read_wq); + init_waitqueue_head(&dev->write_wq); + + atomic_set(&dev->open_excl, 0); + atomic_set(&dev->read_excl, 0); + atomic_set(&dev->write_excl, 0); + + INIT_LIST_HEAD(&dev->tx_idle); + + _adb_dev = dev; + + ret = misc_register(&adb_device); + if (ret) + goto err; + + return 0; + +err: + kfree(dev); + printk(KERN_ERR "adb gadget driver failed to initialize\n"); + return ret; +} + +static void adb_cleanup(void) +{ + misc_deregister(&adb_device); + + kfree(_adb_dev); + _adb_dev = NULL; +} diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c new file mode 100644 index 00000000..c757409e --- /dev/null +++ b/drivers/usb/gadget/f_audio_source.c @@ -0,0 +1,828 @@ +/* + * Gadget Function Driver for USB audio source device + * + * Copyright (C) 2012 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include + +#define SAMPLE_RATE 44100 +#define FRAMES_PER_MSEC (SAMPLE_RATE / 1000) + +#define IN_EP_MAX_PACKET_SIZE 384 + +/* Number of requests to allocate */ +#define IN_EP_REQ_COUNT 4 + +#define AUDIO_AC_INTERFACE 0 +#define AUDIO_AS_INTERFACE 1 +#define AUDIO_NUM_INTERFACES 2 + +/* B.3.1 Standard AC Interface Descriptor */ +static struct usb_interface_descriptor ac_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, +}; + +DECLARE_UAC_AC_HEADER_DESCRIPTOR(2); + +#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(AUDIO_NUM_INTERFACES) +/* 1 input terminal, 1 output terminal and 1 feature unit */ +#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH \ + + UAC_DT_INPUT_TERMINAL_SIZE + UAC_DT_OUTPUT_TERMINAL_SIZE \ + + UAC_DT_FEATURE_UNIT_SIZE(0)) +/* B.3.2 Class-Specific AC Interface Descriptor */ +static struct uac1_ac_header_descriptor_2 ac_header_desc = { + .bLength = UAC_DT_AC_HEADER_LENGTH, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_HEADER, + .bcdADC = __constant_cpu_to_le16(0x0100), + .wTotalLength = __constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH), + .bInCollection = AUDIO_NUM_INTERFACES, + .baInterfaceNr = { + [0] = AUDIO_AC_INTERFACE, + [1] = AUDIO_AS_INTERFACE, + } +}; + +#define INPUT_TERMINAL_ID 1 +static struct uac_input_terminal_descriptor input_terminal_desc = { + .bLength = UAC_DT_INPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_INPUT_TERMINAL, + .bTerminalID = INPUT_TERMINAL_ID, + .wTerminalType = UAC_INPUT_TERMINAL_MICROPHONE, + .bAssocTerminal = 0, + .wChannelConfig = 0x3, +}; + +DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0); + +#define FEATURE_UNIT_ID 2 +static struct uac_feature_unit_descriptor_0 feature_unit_desc = { + .bLength = UAC_DT_FEATURE_UNIT_SIZE(0), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_FEATURE_UNIT, + .bUnitID = FEATURE_UNIT_ID, + .bSourceID = INPUT_TERMINAL_ID, + .bControlSize = 2, +}; + +#define OUTPUT_TERMINAL_ID 3 +static struct uac1_output_terminal_descriptor output_terminal_desc = { + .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, + .bTerminalID = OUTPUT_TERMINAL_ID, + .wTerminalType = UAC_TERMINAL_STREAMING, + .bAssocTerminal = FEATURE_UNIT_ID, + .bSourceID = FEATURE_UNIT_ID, +}; + +/* B.4.1 Standard AS Interface Descriptor */ +static struct usb_interface_descriptor as_interface_alt_0_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, +}; + +static struct usb_interface_descriptor as_interface_alt_1_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bAlternateSetting = 1, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, +}; + +/* B.4.2 Class-Specific AS Interface Descriptor */ +static struct uac1_as_header_descriptor as_header_desc = { + .bLength = UAC_DT_AS_HEADER_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_AS_GENERAL, + .bTerminalLink = INPUT_TERMINAL_ID, + .bDelay = 1, + .wFormatTag = UAC_FORMAT_TYPE_I_PCM, +}; + +DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1); + +static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = { + .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_FORMAT_TYPE, + .bFormatType = UAC_FORMAT_TYPE_I, + .bSubframeSize = 2, + .bBitResolution = 16, + .bSamFreqType = 1, +}; + +/* Standard ISO IN Endpoint Descriptor for highspeed */ +static struct usb_endpoint_descriptor hs_as_in_ep_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_SYNC_SYNC + | USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE), + .bInterval = 4, /* poll 1 per millisecond */ +}; + +/* Standard ISO IN Endpoint Descriptor for highspeed */ +static struct usb_endpoint_descriptor fs_as_in_ep_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_SYNC_SYNC + | USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE), + .bInterval = 1, /* poll 1 per millisecond */ +}; + +/* Class-specific AS ISO OUT Endpoint Descriptor */ +static struct uac_iso_endpoint_descriptor as_iso_in_desc = { + .bLength = UAC_ISO_ENDPOINT_DESC_SIZE, + .bDescriptorType = USB_DT_CS_ENDPOINT, + .bDescriptorSubtype = UAC_EP_GENERAL, + .bmAttributes = 1, + .bLockDelayUnits = 1, + .wLockDelay = __constant_cpu_to_le16(1), +}; + +static struct usb_descriptor_header *hs_audio_desc[] = { + (struct usb_descriptor_header *)&ac_interface_desc, + (struct usb_descriptor_header *)&ac_header_desc, + + (struct usb_descriptor_header *)&input_terminal_desc, + (struct usb_descriptor_header *)&output_terminal_desc, + (struct usb_descriptor_header *)&feature_unit_desc, + + (struct usb_descriptor_header *)&as_interface_alt_0_desc, + (struct usb_descriptor_header *)&as_interface_alt_1_desc, + (struct usb_descriptor_header *)&as_header_desc, + + (struct usb_descriptor_header *)&as_type_i_desc, + + (struct usb_descriptor_header *)&hs_as_in_ep_desc, + (struct usb_descriptor_header *)&as_iso_in_desc, + NULL, +}; + +static struct usb_descriptor_header *fs_audio_desc[] = { + (struct usb_descriptor_header *)&ac_interface_desc, + (struct usb_descriptor_header *)&ac_header_desc, + + (struct usb_descriptor_header *)&input_terminal_desc, + (struct usb_descriptor_header *)&output_terminal_desc, + (struct usb_descriptor_header *)&feature_unit_desc, + + (struct usb_descriptor_header *)&as_interface_alt_0_desc, + (struct usb_descriptor_header *)&as_interface_alt_1_desc, + (struct usb_descriptor_header *)&as_header_desc, + + (struct usb_descriptor_header *)&as_type_i_desc, + + (struct usb_descriptor_header *)&fs_as_in_ep_desc, + (struct usb_descriptor_header *)&as_iso_in_desc, + NULL, +}; + +static struct snd_pcm_hardware audio_hw_info = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER, + + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 2, + .channels_max = 2, + .rate_min = SAMPLE_RATE, + .rate_max = SAMPLE_RATE, + + .buffer_bytes_max = 1024 * 1024, + .period_bytes_min = 64, + .period_bytes_max = 512 * 1024, + .periods_min = 2, + .periods_max = 1024, +}; + +/*-------------------------------------------------------------------------*/ + +struct audio_source_config { + int card; + int device; +}; + +struct audio_dev { + struct usb_function func; + struct snd_card *card; + struct snd_pcm *pcm; + struct snd_pcm_substream *substream; + + struct list_head idle_reqs; + struct usb_ep *in_ep; + + spinlock_t lock; + + /* beginning, end and current position in our buffer */ + void *buffer_start; + void *buffer_end; + void *buffer_pos; + + /* byte size of a "period" */ + unsigned int period; + /* bytes sent since last call to snd_pcm_period_elapsed */ + unsigned int period_offset; + /* time we started playing */ + ktime_t start_time; + /* number of frames sent since start_time */ + s64 frames_sent; +}; + +static inline struct audio_dev *func_to_audio(struct usb_function *f) +{ + return container_of(f, struct audio_dev, func); +} + +/*-------------------------------------------------------------------------*/ + +static struct usb_request *audio_request_new(struct usb_ep *ep, int buffer_size) +{ + struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req) + return NULL; + + req->buf = kmalloc(buffer_size, GFP_KERNEL); + if (!req->buf) { + usb_ep_free_request(ep, req); + return NULL; + } + req->length = buffer_size; + return req; +} + +static void audio_request_free(struct usb_request *req, struct usb_ep *ep) +{ + if (req) { + kfree(req->buf); + usb_ep_free_request(ep, req); + } +} + +static void audio_req_put(struct audio_dev *audio, struct usb_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&audio->lock, flags); + list_add_tail(&req->list, &audio->idle_reqs); + spin_unlock_irqrestore(&audio->lock, flags); +} + +static struct usb_request *audio_req_get(struct audio_dev *audio) +{ + unsigned long flags; + struct usb_request *req; + + spin_lock_irqsave(&audio->lock, flags); + if (list_empty(&audio->idle_reqs)) { + req = 0; + } else { + req = list_first_entry(&audio->idle_reqs, struct usb_request, + list); + list_del(&req->list); + } + spin_unlock_irqrestore(&audio->lock, flags); + return req; +} + +/* send the appropriate number of packets to match our bitrate */ +static void audio_send(struct audio_dev *audio) +{ + struct snd_pcm_runtime *runtime; + struct usb_request *req; + int length, length1, length2, ret; + s64 msecs; + s64 frames; + ktime_t now; + + /* audio->substream will be null if we have been closed */ + if (!audio->substream) + return; + /* audio->buffer_pos will be null if we have been stopped */ + if (!audio->buffer_pos) + return; + + runtime = audio->substream->runtime; + + /* compute number of frames to send */ + now = ktime_get(); + msecs = ktime_to_ns(now) - ktime_to_ns(audio->start_time); + do_div(msecs, 1000000); + frames = msecs * SAMPLE_RATE; + do_div(frames, 1000); + + /* Readjust our frames_sent if we fall too far behind. + * If we get too far behind it is better to drop some frames than + * to keep sending data too fast in an attempt to catch up. + */ + if (frames - audio->frames_sent > 10 * FRAMES_PER_MSEC) + audio->frames_sent = frames - FRAMES_PER_MSEC; + + frames -= audio->frames_sent; + + /* We need to send something to keep the pipeline going */ + if (frames <= 0) + frames = FRAMES_PER_MSEC; + + while (frames > 0) { + req = audio_req_get(audio); + if (!req) + break; + + length = frames_to_bytes(runtime, frames); + if (length > IN_EP_MAX_PACKET_SIZE) + length = IN_EP_MAX_PACKET_SIZE; + + if (audio->buffer_pos + length > audio->buffer_end) + length1 = audio->buffer_end - audio->buffer_pos; + else + length1 = length; + memcpy(req->buf, audio->buffer_pos, length1); + if (length1 < length) { + /* Wrap around and copy remaining length + * at beginning of buffer. + */ + length2 = length - length1; + memcpy(req->buf + length1, audio->buffer_start, + length2); + audio->buffer_pos = audio->buffer_start + length2; + } else { + audio->buffer_pos += length1; + if (audio->buffer_pos >= audio->buffer_end) + audio->buffer_pos = audio->buffer_start; + } + + req->length = length; + ret = usb_ep_queue(audio->in_ep, req, GFP_ATOMIC); + if (ret < 0) { + pr_err("usb_ep_queue failed ret: %d\n", ret); + audio_req_put(audio, req); + break; + } + + frames -= bytes_to_frames(runtime, length); + audio->frames_sent += bytes_to_frames(runtime, length); + } +} + +static void audio_control_complete(struct usb_ep *ep, struct usb_request *req) +{ + /* nothing to do here */ +} + +static void audio_data_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct audio_dev *audio = req->context; + + pr_debug("audio_data_complete req->status %d req->actual %d\n", + req->status, req->actual); + + audio_req_put(audio, req); + + if (!audio->buffer_start || req->status) + return; + + audio->period_offset += req->actual; + if (audio->period_offset >= audio->period) { + snd_pcm_period_elapsed(audio->substream); + audio->period_offset = 0; + } + audio_send(audio); +} + +static int audio_set_endpoint_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + int value = -EOPNOTSUPP; + u16 ep = le16_to_cpu(ctrl->wIndex); + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + + pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", + ctrl->bRequest, w_value, len, ep); + + switch (ctrl->bRequest) { + case UAC_SET_CUR: + case UAC_SET_MIN: + case UAC_SET_MAX: + case UAC_SET_RES: + value = len; + break; + default: + break; + } + + return value; +} + +static int audio_get_endpoint_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = f->config->cdev; + int value = -EOPNOTSUPP; + u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + u8 *buf = cdev->req->buf; + + pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", + ctrl->bRequest, w_value, len, ep); + + if (w_value == UAC_EP_CS_ATTR_SAMPLE_RATE << 8) { + switch (ctrl->bRequest) { + case UAC_GET_CUR: + case UAC_GET_MIN: + case UAC_GET_MAX: + case UAC_GET_RES: + /* return our sample rate */ + buf[0] = (u8)SAMPLE_RATE; + buf[1] = (u8)(SAMPLE_RATE >> 8); + buf[2] = (u8)(SAMPLE_RATE >> 16); + value = 3; + break; + default: + break; + } + } + + return value; +} + +static int +audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + /* composite driver infrastructure handles everything; interface + * activation uses set_alt(). + */ + switch (ctrl->bRequestType) { + case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: + value = audio_set_endpoint_req(f, ctrl); + break; + + case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: + value = audio_get_endpoint_req(f, ctrl); + break; + } + + /* respond with data transfer or status phase? */ + if (value >= 0) { + pr_debug("audio req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + req->zero = 0; + req->length = value; + req->complete = audio_control_complete; + value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) + pr_err("audio response on err %d\n", value); + } + + /* device either stalls (value < 0) or reports success */ + return value; +} + +static int audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct audio_dev *audio = func_to_audio(f); + struct usb_composite_dev *cdev = f->config->cdev; + int ret; + + pr_debug("audio_set_alt intf %d, alt %d\n", intf, alt); + + ret = config_ep_by_speed(cdev->gadget, f, audio->in_ep); + if (ret) + return ret; + + usb_ep_enable(audio->in_ep); + return 0; +} + +static void audio_disable(struct usb_function *f) +{ + struct audio_dev *audio = func_to_audio(f); + + pr_debug("audio_disable\n"); + usb_ep_disable(audio->in_ep); +} + +/*-------------------------------------------------------------------------*/ + +static void audio_build_desc(struct audio_dev *audio) +{ + u8 *sam_freq; + int rate; + + /* Set channel numbers */ + input_terminal_desc.bNrChannels = 2; + as_type_i_desc.bNrChannels = 2; + + /* Set sample rates */ + rate = SAMPLE_RATE; + sam_freq = as_type_i_desc.tSamFreq[0]; + memcpy(sam_freq, &rate, 3); +} + +/* audio function driver setup/binding */ +static int +audio_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct audio_dev *audio = func_to_audio(f); + int status; + struct usb_ep *ep; + struct usb_request *req; + int i; + + audio_build_desc(audio); + + /* allocate instance-specific interface IDs, and patch descriptors */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + ac_interface_desc.bInterfaceNumber = status; + + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + as_interface_alt_0_desc.bInterfaceNumber = status; + as_interface_alt_1_desc.bInterfaceNumber = status; + + status = -ENODEV; + + /* allocate our endpoint */ + ep = usb_ep_autoconfig(cdev->gadget, &fs_as_in_ep_desc); + if (!ep) + goto fail; + audio->in_ep = ep; + ep->driver_data = audio; /* claim */ + + if (gadget_is_dualspeed(c->cdev->gadget)) + hs_as_in_ep_desc.bEndpointAddress = + fs_as_in_ep_desc.bEndpointAddress; + + f->descriptors = fs_audio_desc; + f->hs_descriptors = hs_audio_desc; + + for (i = 0, status = 0; i < IN_EP_REQ_COUNT && status == 0; i++) { + req = audio_request_new(ep, IN_EP_MAX_PACKET_SIZE); + if (req) { + req->context = audio; + req->complete = audio_data_complete; + audio_req_put(audio, req); + } else + status = -ENOMEM; + } + +fail: + return status; +} + +static void +audio_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct audio_dev *audio = func_to_audio(f); + struct usb_request *req; + + while ((req = audio_req_get(audio))) + audio_request_free(req, audio->in_ep); + + snd_card_free_when_closed(audio->card); + audio->card = NULL; + audio->pcm = NULL; + audio->substream = NULL; + audio->in_ep = NULL; +} + +static void audio_pcm_playback_start(struct audio_dev *audio) +{ + audio->start_time = ktime_get(); + audio->frames_sent = 0; + audio_send(audio); +} + +static void audio_pcm_playback_stop(struct audio_dev *audio) +{ + unsigned long flags; + + spin_lock_irqsave(&audio->lock, flags); + audio->buffer_start = 0; + audio->buffer_end = 0; + audio->buffer_pos = 0; + spin_unlock_irqrestore(&audio->lock, flags); +} + +static int audio_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct audio_dev *audio = substream->private_data; + + runtime->private_data = audio; + runtime->hw = audio_hw_info; + snd_pcm_limit_hw_rates(runtime); + runtime->hw.channels_max = 2; + + audio->substream = substream; + return 0; +} + +static int audio_pcm_close(struct snd_pcm_substream *substream) +{ + struct audio_dev *audio = substream->private_data; + unsigned long flags; + + spin_lock_irqsave(&audio->lock, flags); + audio->substream = NULL; + spin_unlock_irqrestore(&audio->lock, flags); + + return 0; +} + +static int audio_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + unsigned int channels = params_channels(params); + unsigned int rate = params_rate(params); + + if (rate != SAMPLE_RATE) + return -EINVAL; + if (channels != 2) + return -EINVAL; + + return snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(params)); +} + +static int audio_pcm_hw_free(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int audio_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct audio_dev *audio = runtime->private_data; + + audio->period = snd_pcm_lib_period_bytes(substream); + audio->period_offset = 0; + audio->buffer_start = runtime->dma_area; + audio->buffer_end = audio->buffer_start + + snd_pcm_lib_buffer_bytes(substream); + audio->buffer_pos = audio->buffer_start; + + return 0; +} + +static snd_pcm_uframes_t audio_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct audio_dev *audio = runtime->private_data; + ssize_t bytes = audio->buffer_pos - audio->buffer_start; + + /* return offset of next frame to fill in our buffer */ + return bytes_to_frames(runtime, bytes); +} + +static int audio_pcm_playback_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct audio_dev *audio = substream->runtime->private_data; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + audio_pcm_playback_start(audio); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + audio_pcm_playback_stop(audio); + break; + + default: + ret = -EINVAL; + } + + return ret; +} + +static struct audio_dev _audio_dev = { + .func = { + .name = "audio_source", + .bind = audio_bind, + .unbind = audio_unbind, + .set_alt = audio_set_alt, + .setup = audio_setup, + .disable = audio_disable, + }, + .lock = __SPIN_LOCK_UNLOCKED(_audio_dev.lock), + .idle_reqs = LIST_HEAD_INIT(_audio_dev.idle_reqs), +}; + +static struct snd_pcm_ops audio_playback_ops = { + .open = audio_pcm_open, + .close = audio_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = audio_pcm_hw_params, + .hw_free = audio_pcm_hw_free, + .prepare = audio_pcm_prepare, + .trigger = audio_pcm_playback_trigger, + .pointer = audio_pcm_pointer, +}; + +int audio_source_bind_config(struct usb_configuration *c, + struct audio_source_config *config) +{ + struct audio_dev *audio; + struct snd_card *card; + struct snd_pcm *pcm; + int err; + + config->card = -1; + config->device = -1; + + audio = &_audio_dev; + + err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0, &card); + if (err) + return err; + + snd_card_set_dev(card, &c->cdev->gadget->dev); + + err = snd_pcm_new(card, "USB audio source", 0, 1, 0, &pcm); + if (err) + goto pcm_fail; + pcm->private_data = audio; + pcm->info_flags = 0; + audio->pcm = pcm; + + strlcpy(pcm->name, "USB gadget audio", sizeof(pcm->name)); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &audio_playback_ops); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + NULL, 0, 64 * 1024); + + strlcpy(card->driver, "audio_source", sizeof(card->driver)); + strlcpy(card->shortname, card->driver, sizeof(card->shortname)); + strlcpy(card->longname, "USB accessory audio source", + sizeof(card->longname)); + + err = snd_card_register(card); + if (err) + goto register_fail; + + err = usb_add_function(c, &audio->func); + if (err) + goto add_fail; + + config->card = pcm->card->number; + config->device = pcm->device; + audio->card = card; + return 0; + +add_fail: +register_fail: +pcm_fail: + snd_card_free(audio->card); + return err; +} diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index f52cb1ae..7a86f350 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -1519,7 +1519,12 @@ static int ffs_func_eps_enable(struct ffs_function *func) spin_lock_irqsave(&func->ffs->eps_lock, flags); do { struct usb_endpoint_descriptor *ds; - ds = ep->descs[ep->descs[1] ? 1 : 0]; + int desc_idx = ffs->gadget->speed == USB_SPEED_HIGH ? 1 : 0; + ds = ep->descs[desc_idx]; + if (!ds) { + ret = -EINVAL; + break; + } ep->ep->driver_data = ep; ep->ep->desc = ds; diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c new file mode 100644 index 00000000..1638977a --- /dev/null +++ b/drivers/usb/gadget/f_mtp.c @@ -0,0 +1,1283 @@ +/* + * Gadget Function Driver for MTP + * + * Copyright (C) 2010 Google, Inc. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +/* #define DEBUG */ +/* #define VERBOSE_DEBUG */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#define MTP_BULK_BUFFER_SIZE 16384 +#define INTR_BUFFER_SIZE 28 + +/* String IDs */ +#define INTERFACE_STRING_INDEX 0 + +/* values for mtp_dev.state */ +#define STATE_OFFLINE 0 /* initial state, disconnected */ +#define STATE_READY 1 /* ready for userspace calls */ +#define STATE_BUSY 2 /* processing userspace calls */ +#define STATE_CANCELED 3 /* transaction canceled by host */ +#define STATE_ERROR 4 /* error from completion routine */ + +/* number of tx and rx requests to allocate */ +#define TX_REQ_MAX 4 +#define RX_REQ_MAX 2 +#define INTR_REQ_MAX 5 + +/* ID for Microsoft MTP OS String */ +#define MTP_OS_STRING_ID 0xEE + +/* MTP class reqeusts */ +#define MTP_REQ_CANCEL 0x64 +#define MTP_REQ_GET_EXT_EVENT_DATA 0x65 +#define MTP_REQ_RESET 0x66 +#define MTP_REQ_GET_DEVICE_STATUS 0x67 + +/* constants for device status */ +#define MTP_RESPONSE_OK 0x2001 +#define MTP_RESPONSE_DEVICE_BUSY 0x2019 + +static const char mtp_shortname[] = "mtp_usb"; + +struct mtp_dev { + struct usb_function function; + struct usb_composite_dev *cdev; + spinlock_t lock; + + struct usb_ep *ep_in; + struct usb_ep *ep_out; + struct usb_ep *ep_intr; + + int state; + + /* synchronize access to our device file */ + atomic_t open_excl; + /* to enforce only one ioctl at a time */ + atomic_t ioctl_excl; + + struct list_head tx_idle; + struct list_head intr_idle; + + wait_queue_head_t read_wq; + wait_queue_head_t write_wq; + wait_queue_head_t intr_wq; + struct usb_request *rx_req[RX_REQ_MAX]; + int rx_done; + + /* for processing MTP_SEND_FILE, MTP_RECEIVE_FILE and + * MTP_SEND_FILE_WITH_HEADER ioctls on a work queue + */ + struct workqueue_struct *wq; + struct work_struct send_file_work; + struct work_struct receive_file_work; + struct file *xfer_file; + loff_t xfer_file_offset; + int64_t xfer_file_length; + unsigned xfer_send_header; + uint16_t xfer_command; + uint32_t xfer_transaction_id; + int xfer_result; +}; + +static struct usb_interface_descriptor mtp_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 3, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, + .bInterfaceProtocol = 0, +}; + +static struct usb_interface_descriptor ptp_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 3, + .bInterfaceClass = USB_CLASS_STILL_IMAGE, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 1, +}; + +static struct usb_endpoint_descriptor mtp_highspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor mtp_highspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor mtp_fullspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor mtp_fullspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor mtp_intr_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(INTR_BUFFER_SIZE), + .bInterval = 6, +}; + +static struct usb_descriptor_header *fs_mtp_descs[] = { + (struct usb_descriptor_header *) &mtp_interface_desc, + (struct usb_descriptor_header *) &mtp_fullspeed_in_desc, + (struct usb_descriptor_header *) &mtp_fullspeed_out_desc, + (struct usb_descriptor_header *) &mtp_intr_desc, + NULL, +}; + +static struct usb_descriptor_header *hs_mtp_descs[] = { + (struct usb_descriptor_header *) &mtp_interface_desc, + (struct usb_descriptor_header *) &mtp_highspeed_in_desc, + (struct usb_descriptor_header *) &mtp_highspeed_out_desc, + (struct usb_descriptor_header *) &mtp_intr_desc, + NULL, +}; + +static struct usb_descriptor_header *fs_ptp_descs[] = { + (struct usb_descriptor_header *) &ptp_interface_desc, + (struct usb_descriptor_header *) &mtp_fullspeed_in_desc, + (struct usb_descriptor_header *) &mtp_fullspeed_out_desc, + (struct usb_descriptor_header *) &mtp_intr_desc, + NULL, +}; + +static struct usb_descriptor_header *hs_ptp_descs[] = { + (struct usb_descriptor_header *) &ptp_interface_desc, + (struct usb_descriptor_header *) &mtp_highspeed_in_desc, + (struct usb_descriptor_header *) &mtp_highspeed_out_desc, + (struct usb_descriptor_header *) &mtp_intr_desc, + NULL, +}; + +static struct usb_string mtp_string_defs[] = { + /* Naming interface "MTP" so libmtp will recognize us */ + [INTERFACE_STRING_INDEX].s = "MTP", + { }, /* end of list */ +}; + +static struct usb_gadget_strings mtp_string_table = { + .language = 0x0409, /* en-US */ + .strings = mtp_string_defs, +}; + +static struct usb_gadget_strings *mtp_strings[] = { + &mtp_string_table, + NULL, +}; + +/* Microsoft MTP OS String */ +static u8 mtp_os_string[] = { + 18, /* sizeof(mtp_os_string) */ + USB_DT_STRING, + /* Signature field: "MSFT100" */ + 'M', 0, 'S', 0, 'F', 0, 'T', 0, '1', 0, '0', 0, '0', 0, + /* vendor code */ + 1, + /* padding */ + 0 +}; + +/* Microsoft Extended Configuration Descriptor Header Section */ +struct mtp_ext_config_desc_header { + __le32 dwLength; + __u16 bcdVersion; + __le16 wIndex; + __u8 bCount; + __u8 reserved[7]; +}; + +/* Microsoft Extended Configuration Descriptor Function Section */ +struct mtp_ext_config_desc_function { + __u8 bFirstInterfaceNumber; + __u8 bInterfaceCount; + __u8 compatibleID[8]; + __u8 subCompatibleID[8]; + __u8 reserved[6]; +}; + +/* MTP Extended Configuration Descriptor */ +struct { + struct mtp_ext_config_desc_header header; + struct mtp_ext_config_desc_function function; +} mtp_ext_config_desc = { + .header = { + .dwLength = __constant_cpu_to_le32(sizeof(mtp_ext_config_desc)), + .bcdVersion = __constant_cpu_to_le16(0x0100), + .wIndex = __constant_cpu_to_le16(4), + .bCount = __constant_cpu_to_le16(1), + }, + .function = { + .bFirstInterfaceNumber = 0, + .bInterfaceCount = 1, + .compatibleID = { 'M', 'T', 'P' }, + }, +}; + +struct mtp_device_status { + __le16 wLength; + __le16 wCode; +}; + +/* temporary variable used between mtp_open() and mtp_gadget_bind() */ +static struct mtp_dev *_mtp_dev; + +static inline struct mtp_dev *func_to_mtp(struct usb_function *f) +{ + return container_of(f, struct mtp_dev, function); +} + +static struct usb_request *mtp_request_new(struct usb_ep *ep, int buffer_size) +{ + struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req) + return NULL; + + /* now allocate buffers for the requests */ + req->buf = kmalloc(buffer_size, GFP_KERNEL); + if (!req->buf) { + usb_ep_free_request(ep, req); + return NULL; + } + + return req; +} + +static void mtp_request_free(struct usb_request *req, struct usb_ep *ep) +{ + if (req) { + kfree(req->buf); + usb_ep_free_request(ep, req); + } +} + +static inline int mtp_lock(atomic_t *excl) +{ + if (atomic_inc_return(excl) == 1) { + return 0; + } else { + atomic_dec(excl); + return -1; + } +} + +static inline void mtp_unlock(atomic_t *excl) +{ + atomic_dec(excl); +} + +/* add a request to the tail of a list */ +static void mtp_req_put(struct mtp_dev *dev, struct list_head *head, + struct usb_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + list_add_tail(&req->list, head); + spin_unlock_irqrestore(&dev->lock, flags); +} + +/* remove a request from the head of a list */ +static struct usb_request +*mtp_req_get(struct mtp_dev *dev, struct list_head *head) +{ + unsigned long flags; + struct usb_request *req; + + spin_lock_irqsave(&dev->lock, flags); + if (list_empty(head)) { + req = 0; + } else { + req = list_first_entry(head, struct usb_request, list); + list_del(&req->list); + } + spin_unlock_irqrestore(&dev->lock, flags); + return req; +} + +static void mtp_complete_in(struct usb_ep *ep, struct usb_request *req) +{ + struct mtp_dev *dev = _mtp_dev; + + if (req->status != 0) + dev->state = STATE_ERROR; + + mtp_req_put(dev, &dev->tx_idle, req); + + wake_up(&dev->write_wq); +} + +static void mtp_complete_out(struct usb_ep *ep, struct usb_request *req) +{ + struct mtp_dev *dev = _mtp_dev; + + dev->rx_done = 1; + if (req->status != 0) + dev->state = STATE_ERROR; + + wake_up(&dev->read_wq); +} + +static void mtp_complete_intr(struct usb_ep *ep, struct usb_request *req) +{ + struct mtp_dev *dev = _mtp_dev; + + if (req->status != 0) + dev->state = STATE_ERROR; + + mtp_req_put(dev, &dev->intr_idle, req); + + wake_up(&dev->intr_wq); +} + +static int mtp_create_bulk_endpoints(struct mtp_dev *dev, + struct usb_endpoint_descriptor *in_desc, + struct usb_endpoint_descriptor *out_desc, + struct usb_endpoint_descriptor *intr_desc) +{ + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req; + struct usb_ep *ep; + int i; + + DBG(cdev, "create_bulk_endpoints dev: %p\n", dev); + + ep = usb_ep_autoconfig(cdev->gadget, in_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_in failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_in = ep; + + ep = usb_ep_autoconfig(cdev->gadget, out_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for mtp ep_out got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_out = ep; + + ep = usb_ep_autoconfig(cdev->gadget, out_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for mtp ep_out got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_out = ep; + + ep = usb_ep_autoconfig(cdev->gadget, intr_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_intr failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for mtp ep_intr got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_intr = ep; + + /* now allocate requests for our endpoints */ + for (i = 0; i < TX_REQ_MAX; i++) { + req = mtp_request_new(dev->ep_in, MTP_BULK_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = mtp_complete_in; + mtp_req_put(dev, &dev->tx_idle, req); + } + for (i = 0; i < RX_REQ_MAX; i++) { + req = mtp_request_new(dev->ep_out, MTP_BULK_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = mtp_complete_out; + dev->rx_req[i] = req; + } + for (i = 0; i < INTR_REQ_MAX; i++) { + req = mtp_request_new(dev->ep_intr, INTR_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = mtp_complete_intr; + mtp_req_put(dev, &dev->intr_idle, req); + } + + return 0; + +fail: + printk(KERN_ERR "mtp_bind() could not allocate requests\n"); + return -1; +} + +static ssize_t mtp_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + struct mtp_dev *dev = fp->private_data; + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req; + int r = count, xfer; + int ret = 0; + + DBG(cdev, "mtp_read(%d)\n", count); + + if (count > MTP_BULK_BUFFER_SIZE) + return -EINVAL; + + /* we will block until we're online */ + DBG(cdev, "mtp_read: waiting for online state\n"); + ret = wait_event_interruptible(dev->read_wq, + dev->state != STATE_OFFLINE); + if (ret < 0) { + r = ret; + goto done; + } + spin_lock_irq(&dev->lock); + if (dev->state == STATE_CANCELED) { + /* report cancelation to userspace */ + dev->state = STATE_READY; + spin_unlock_irq(&dev->lock); + return -ECANCELED; + } + dev->state = STATE_BUSY; + spin_unlock_irq(&dev->lock); + +requeue_req: + /* queue a request */ + req = dev->rx_req[0]; + req->length = count; + dev->rx_done = 0; + ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL); + if (ret < 0) { + r = -EIO; + goto done; + } else { + DBG(cdev, "rx %p queue\n", req); + } + + /* wait for a request to complete */ + ret = wait_event_interruptible(dev->read_wq, dev->rx_done); + if (ret < 0) { + r = ret; + usb_ep_dequeue(dev->ep_out, req); + goto done; + } + if (dev->state == STATE_BUSY) { + /* If we got a 0-len packet, throw it back and try again. */ + if (req->actual == 0) + goto requeue_req; + + DBG(cdev, "rx %p %d\n", req, req->actual); + xfer = (req->actual < count) ? req->actual : count; + r = xfer; + if (copy_to_user(buf, req->buf, xfer)) + r = -EFAULT; + } else + r = -EIO; + +done: + spin_lock_irq(&dev->lock); + if (dev->state == STATE_CANCELED) + r = -ECANCELED; + else if (dev->state != STATE_OFFLINE) + dev->state = STATE_READY; + spin_unlock_irq(&dev->lock); + + DBG(cdev, "mtp_read returning %d\n", r); + return r; +} + +static ssize_t mtp_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + struct mtp_dev *dev = fp->private_data; + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req = 0; + int r = count, xfer; + int sendZLP = 0; + int ret; + + DBG(cdev, "mtp_write(%d)\n", count); + + spin_lock_irq(&dev->lock); + if (dev->state == STATE_CANCELED) { + /* report cancelation to userspace */ + dev->state = STATE_READY; + spin_unlock_irq(&dev->lock); + return -ECANCELED; + } + if (dev->state == STATE_OFFLINE) { + spin_unlock_irq(&dev->lock); + return -ENODEV; + } + dev->state = STATE_BUSY; + spin_unlock_irq(&dev->lock); + + /* we need to send a zero length packet to signal the end of transfer + * if the transfer size is aligned to a packet boundary. + */ + if ((count & (dev->ep_in->maxpacket - 1)) == 0) + sendZLP = 1; + + while (count > 0 || sendZLP) { + /* so we exit after sending ZLP */ + if (count == 0) + sendZLP = 0; + + if (dev->state != STATE_BUSY) { + DBG(cdev, "mtp_write dev->error\n"); + r = -EIO; + break; + } + + /* get an idle tx request to use */ + req = 0; + ret = wait_event_interruptible(dev->write_wq, + ((req = mtp_req_get(dev, &dev->tx_idle)) + || dev->state != STATE_BUSY)); + if (!req) { + r = ret; + break; + } + + if (count > MTP_BULK_BUFFER_SIZE) + xfer = MTP_BULK_BUFFER_SIZE; + else + xfer = count; + if (xfer && copy_from_user(req->buf, buf, xfer)) { + r = -EFAULT; + break; + } + + req->length = xfer; + ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL); + if (ret < 0) { + DBG(cdev, "mtp_write: xfer error %d\n", ret); + r = -EIO; + break; + } + + buf += xfer; + count -= xfer; + + /* zero this so we don't try to free it on error exit */ + req = 0; + } + + if (req) + mtp_req_put(dev, &dev->tx_idle, req); + + spin_lock_irq(&dev->lock); + if (dev->state == STATE_CANCELED) + r = -ECANCELED; + else if (dev->state != STATE_OFFLINE) + dev->state = STATE_READY; + spin_unlock_irq(&dev->lock); + + DBG(cdev, "mtp_write returning %d\n", r); + return r; +} + +/* read from a local file and write to USB */ +static void send_file_work(struct work_struct *data) +{ + struct mtp_dev *dev = container_of(data, struct mtp_dev, + send_file_work); + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req = 0; + struct mtp_data_header *header; + struct file *filp; + loff_t offset; + int64_t count; + int xfer, ret, hdr_size; + int r = 0; + int sendZLP = 0; + + /* read our parameters */ + smp_rmb(); + filp = dev->xfer_file; + offset = dev->xfer_file_offset; + count = dev->xfer_file_length; + + DBG(cdev, "send_file_work(%lld %lld)\n", offset, count); + + if (dev->xfer_send_header) { + hdr_size = sizeof(struct mtp_data_header); + count += hdr_size; + } else { + hdr_size = 0; + } + + /* we need to send a zero length packet to signal the end of transfer + * if the transfer size is aligned to a packet boundary. + */ + if ((count & (dev->ep_in->maxpacket - 1)) == 0) + sendZLP = 1; + + while (count > 0 || sendZLP) { + /* so we exit after sending ZLP */ + if (count == 0) + sendZLP = 0; + + /* get an idle tx request to use */ + req = 0; + ret = wait_event_interruptible(dev->write_wq, + (req = mtp_req_get(dev, &dev->tx_idle)) + || dev->state != STATE_BUSY); + if (dev->state == STATE_CANCELED) { + r = -ECANCELED; + break; + } + if (!req) { + r = ret; + break; + } + + if (count > MTP_BULK_BUFFER_SIZE) + xfer = MTP_BULK_BUFFER_SIZE; + else + xfer = count; + + if (hdr_size) { + /* prepend MTP data header */ + header = (struct mtp_data_header *)req->buf; + header->length = __cpu_to_le32(count); + header->type = __cpu_to_le16(2); /* data packet */ + header->command = __cpu_to_le16(dev->xfer_command); + header->transaction_id = + __cpu_to_le32(dev->xfer_transaction_id); + } + + ret = vfs_read(filp, req->buf + hdr_size, xfer - hdr_size, + &offset); + if (ret < 0) { + r = ret; + break; + } + xfer = ret + hdr_size; + hdr_size = 0; + + req->length = xfer; + ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL); + if (ret < 0) { + DBG(cdev, "send_file_work: xfer error %d\n", ret); + dev->state = STATE_ERROR; + r = -EIO; + break; + } + + count -= xfer; + + /* zero this so we don't try to free it on error exit */ + req = 0; + } + + if (req) + mtp_req_put(dev, &dev->tx_idle, req); + + DBG(cdev, "send_file_work returning %d\n", r); + /* write the result */ + dev->xfer_result = r; + smp_wmb(); +} + +/* read from USB and write to a local file */ +static void receive_file_work(struct work_struct *data) +{ + struct mtp_dev *dev = container_of(data, struct mtp_dev, + receive_file_work); + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *read_req = NULL, *write_req = NULL; + struct file *filp; + loff_t offset; + int64_t count; + int ret, cur_buf = 0; + int r = 0; + + /* read our parameters */ + smp_rmb(); + filp = dev->xfer_file; + offset = dev->xfer_file_offset; + count = dev->xfer_file_length; + + DBG(cdev, "receive_file_work(%lld)\n", count); + + while (count > 0 || write_req) { + if (count > 0) { + /* queue a request */ + read_req = dev->rx_req[cur_buf]; + cur_buf = (cur_buf + 1) % RX_REQ_MAX; + + read_req->length = (count > MTP_BULK_BUFFER_SIZE + ? MTP_BULK_BUFFER_SIZE : count); + dev->rx_done = 0; + ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL); + if (ret < 0) { + r = -EIO; + dev->state = STATE_ERROR; + break; + } + } + + if (write_req) { + DBG(cdev, "rx %p %d\n", write_req, write_req->actual); + ret = vfs_write(filp, write_req->buf, write_req->actual, + &offset); + DBG(cdev, "vfs_write %d\n", ret); + if (ret != write_req->actual) { + r = -EIO; + dev->state = STATE_ERROR; + break; + } + write_req = NULL; + } + + if (read_req) { + /* wait for our last read to complete */ + ret = wait_event_interruptible(dev->read_wq, + dev->rx_done || dev->state != STATE_BUSY); + if (dev->state == STATE_CANCELED) { + r = -ECANCELED; + if (!dev->rx_done) + usb_ep_dequeue(dev->ep_out, read_req); + break; + } + /* if xfer_file_length is 0xFFFFFFFF, then we read until + * we get a zero length packet + */ + if (count != 0xFFFFFFFF) + count -= read_req->actual; + if (read_req->actual < read_req->length) { + /* + * short packet is used to signal EOF for + * sizes > 4 gig + */ + DBG(cdev, "got short packet\n"); + count = 0; + } + + write_req = read_req; + read_req = NULL; + } + } + + DBG(cdev, "receive_file_work returning %d\n", r); + /* write the result */ + dev->xfer_result = r; + smp_wmb(); +} + +static int mtp_send_event(struct mtp_dev *dev, struct mtp_event *event) +{ + struct usb_request *req = NULL; + int ret; + int length = event->length; + + DBG(dev->cdev, "mtp_send_event(%d)\n", event->length); + + if (length < 0 || length > INTR_BUFFER_SIZE) + return -EINVAL; + if (dev->state == STATE_OFFLINE) + return -ENODEV; + + ret = wait_event_interruptible_timeout(dev->intr_wq, + (req = mtp_req_get(dev, &dev->intr_idle)), + msecs_to_jiffies(1000)); + if (!req) + return -ETIME; + + if (copy_from_user(req->buf, (void __user *)event->data, length)) { + mtp_req_put(dev, &dev->intr_idle, req); + return -EFAULT; + } + req->length = length; + ret = usb_ep_queue(dev->ep_intr, req, GFP_KERNEL); + if (ret) + mtp_req_put(dev, &dev->intr_idle, req); + + return ret; +} + +static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value) +{ + struct mtp_dev *dev = fp->private_data; + struct file *filp = NULL; + int ret = -EINVAL; + + if (mtp_lock(&dev->ioctl_excl)) + return -EBUSY; + + switch (code) { + case MTP_SEND_FILE: + case MTP_RECEIVE_FILE: + case MTP_SEND_FILE_WITH_HEADER: + { + struct mtp_file_range mfr; + struct work_struct *work; + + spin_lock_irq(&dev->lock); + if (dev->state == STATE_CANCELED) { + /* report cancelation to userspace */ + dev->state = STATE_READY; + spin_unlock_irq(&dev->lock); + ret = -ECANCELED; + goto out; + } + if (dev->state == STATE_OFFLINE) { + spin_unlock_irq(&dev->lock); + ret = -ENODEV; + goto out; + } + dev->state = STATE_BUSY; + spin_unlock_irq(&dev->lock); + + if (copy_from_user(&mfr, (void __user *)value, sizeof(mfr))) { + ret = -EFAULT; + goto fail; + } + /* hold a reference to the file while we are working with it */ + filp = fget(mfr.fd); + if (!filp) { + ret = -EBADF; + goto fail; + } + + /* write the parameters */ + dev->xfer_file = filp; + dev->xfer_file_offset = mfr.offset; + dev->xfer_file_length = mfr.length; + smp_wmb(); + + if (code == MTP_SEND_FILE_WITH_HEADER) { + work = &dev->send_file_work; + dev->xfer_send_header = 1; + dev->xfer_command = mfr.command; + dev->xfer_transaction_id = mfr.transaction_id; + } else if (code == MTP_SEND_FILE) { + work = &dev->send_file_work; + dev->xfer_send_header = 0; + } else { + work = &dev->receive_file_work; + } + + /* We do the file transfer on a work queue so it will run + * in kernel context, which is necessary for vfs_read and + * vfs_write to use our buffers in the kernel address space. + */ + queue_work(dev->wq, work); + /* wait for operation to complete */ + flush_workqueue(dev->wq); + fput(filp); + + /* read the result */ + smp_rmb(); + ret = dev->xfer_result; + break; + } + case MTP_SEND_EVENT: + { + struct mtp_event event; + /* return here so we don't change dev->state below, + * which would interfere with bulk transfer state. + */ + if (copy_from_user(&event, (void __user *)value, sizeof(event))) + ret = -EFAULT; + else + ret = mtp_send_event(dev, &event); + goto out; + } + } + +fail: + spin_lock_irq(&dev->lock); + if (dev->state == STATE_CANCELED) + ret = -ECANCELED; + else if (dev->state != STATE_OFFLINE) + dev->state = STATE_READY; + spin_unlock_irq(&dev->lock); +out: + mtp_unlock(&dev->ioctl_excl); + DBG(dev->cdev, "ioctl returning %d\n", ret); + return ret; +} + +static int mtp_open(struct inode *ip, struct file *fp) +{ + printk(KERN_INFO "mtp_open\n"); + if (mtp_lock(&_mtp_dev->open_excl)) + return -EBUSY; + + /* clear any error condition */ + if (_mtp_dev->state != STATE_OFFLINE) + _mtp_dev->state = STATE_READY; + + fp->private_data = _mtp_dev; + return 0; +} + +static int mtp_release(struct inode *ip, struct file *fp) +{ + printk(KERN_INFO "mtp_release\n"); + + mtp_unlock(&_mtp_dev->open_excl); + return 0; +} + +/* file operations for /dev/mtp_usb */ +static const struct file_operations mtp_fops = { + .owner = THIS_MODULE, + .read = mtp_read, + .write = mtp_write, + .unlocked_ioctl = mtp_ioctl, + .open = mtp_open, + .release = mtp_release, +}; + +static struct miscdevice mtp_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = mtp_shortname, + .fops = &mtp_fops, +}; + +static int mtp_ctrlrequest(struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *ctrl) +{ + struct mtp_dev *dev = _mtp_dev; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + unsigned long flags; + + VDBG(cdev, "mtp_ctrlrequest " + "%02x.%02x v%04x i%04x l%u\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + + /* Handle MTP OS string */ + if (ctrl->bRequestType == + (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE) + && ctrl->bRequest == USB_REQ_GET_DESCRIPTOR + && (w_value >> 8) == USB_DT_STRING + && (w_value & 0xFF) == MTP_OS_STRING_ID) { + value = (w_length < sizeof(mtp_os_string) + ? w_length : sizeof(mtp_os_string)); + memcpy(cdev->req->buf, mtp_os_string, value); + } else if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) { + /* Handle MTP OS descriptor */ + DBG(cdev, "vendor request: %d index: %d value: %d length: %d\n", + ctrl->bRequest, w_index, w_value, w_length); + + if (ctrl->bRequest == 1 + && (ctrl->bRequestType & USB_DIR_IN) + && (w_index == 4 || w_index == 5)) { + value = (w_length < sizeof(mtp_ext_config_desc) ? + w_length : sizeof(mtp_ext_config_desc)); + memcpy(cdev->req->buf, &mtp_ext_config_desc, value); + } + } else if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) { + DBG(cdev, "class request: %d index: %d value: %d length: %d\n", + ctrl->bRequest, w_index, w_value, w_length); + + if (ctrl->bRequest == MTP_REQ_CANCEL && w_index == 0 + && w_value == 0) { + DBG(cdev, "MTP_REQ_CANCEL\n"); + + spin_lock_irqsave(&dev->lock, flags); + if (dev->state == STATE_BUSY) { + dev->state = STATE_CANCELED; + wake_up(&dev->read_wq); + wake_up(&dev->write_wq); + } + spin_unlock_irqrestore(&dev->lock, flags); + + /* We need to queue a request to read the remaining + * bytes, but we don't actually need to look at + * the contents. + */ + value = w_length; + } else if (ctrl->bRequest == MTP_REQ_GET_DEVICE_STATUS + && w_index == 0 && w_value == 0) { + struct mtp_device_status *status = cdev->req->buf; + status->wLength = + __constant_cpu_to_le16(sizeof(*status)); + + DBG(cdev, "MTP_REQ_GET_DEVICE_STATUS\n"); + spin_lock_irqsave(&dev->lock, flags); + /* device status is "busy" until we report + * the cancelation to userspace + */ + if (dev->state == STATE_CANCELED) + status->wCode = + __cpu_to_le16(MTP_RESPONSE_DEVICE_BUSY); + else + status->wCode = + __cpu_to_le16(MTP_RESPONSE_OK); + spin_unlock_irqrestore(&dev->lock, flags); + value = sizeof(*status); + } + } + + /* respond with data transfer or status phase? */ + if (value >= 0) { + int rc; + cdev->req->zero = value < w_length; + cdev->req->length = value; + rc = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC); + if (rc < 0) + ERROR(cdev, "%s: response queue error\n", __func__); + } + return value; +} + +static int +mtp_function_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct mtp_dev *dev = func_to_mtp(f); + int id; + int ret; + + dev->cdev = cdev; + DBG(cdev, "mtp_function_bind dev: %p\n", dev); + + /* allocate interface ID(s) */ + id = usb_interface_id(c, f); + if (id < 0) + return id; + mtp_interface_desc.bInterfaceNumber = id; + + /* allocate endpoints */ + ret = mtp_create_bulk_endpoints(dev, &mtp_fullspeed_in_desc, + &mtp_fullspeed_out_desc, &mtp_intr_desc); + if (ret) + return ret; + + /* support high speed hardware */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + mtp_highspeed_in_desc.bEndpointAddress = + mtp_fullspeed_in_desc.bEndpointAddress; + mtp_highspeed_out_desc.bEndpointAddress = + mtp_fullspeed_out_desc.bEndpointAddress; + } + + DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + f->name, dev->ep_in->name, dev->ep_out->name); + return 0; +} + +static void +mtp_function_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct mtp_dev *dev = func_to_mtp(f); + struct usb_request *req; + int i; + + while ((req = mtp_req_get(dev, &dev->tx_idle))) + mtp_request_free(req, dev->ep_in); + for (i = 0; i < RX_REQ_MAX; i++) + mtp_request_free(dev->rx_req[i], dev->ep_out); + while ((req = mtp_req_get(dev, &dev->intr_idle))) + mtp_request_free(req, dev->ep_intr); + dev->state = STATE_OFFLINE; +} + +static int mtp_function_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct mtp_dev *dev = func_to_mtp(f); + struct usb_composite_dev *cdev = f->config->cdev; + int ret; + + DBG(cdev, "mtp_function_set_alt intf: %d alt: %d\n", intf, alt); + + ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in); + if (ret) + return ret; + + ret = usb_ep_enable(dev->ep_in); + if (ret) + return ret; + + ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out); + if (ret) + return ret; + + ret = usb_ep_enable(dev->ep_out); + if (ret) { + usb_ep_disable(dev->ep_in); + return ret; + } + + ret = config_ep_by_speed(cdev->gadget, f, dev->ep_intr); + if (ret) + return ret; + + ret = usb_ep_enable(dev->ep_intr); + if (ret) { + usb_ep_disable(dev->ep_out); + usb_ep_disable(dev->ep_in); + return ret; + } + dev->state = STATE_READY; + + /* readers may be blocked waiting for us to go online */ + wake_up(&dev->read_wq); + return 0; +} + +static void mtp_function_disable(struct usb_function *f) +{ + struct mtp_dev *dev = func_to_mtp(f); + struct usb_composite_dev *cdev = dev->cdev; + + DBG(cdev, "mtp_function_disable\n"); + dev->state = STATE_OFFLINE; + usb_ep_disable(dev->ep_in); + usb_ep_disable(dev->ep_out); + usb_ep_disable(dev->ep_intr); + + /* readers may be blocked waiting for us to go online */ + wake_up(&dev->read_wq); + + VDBG(cdev, "%s disabled\n", dev->function.name); +} + +static int mtp_bind_config(struct usb_configuration *c, bool ptp_config) +{ + struct mtp_dev *dev = _mtp_dev; + int ret = 0; + + printk(KERN_INFO "mtp_bind_config\n"); + + /* allocate a string ID for our interface */ + if (mtp_string_defs[INTERFACE_STRING_INDEX].id == 0) { + ret = usb_string_id(c->cdev); + if (ret < 0) + return ret; + mtp_string_defs[INTERFACE_STRING_INDEX].id = ret; + mtp_interface_desc.iInterface = ret; + } + + dev->cdev = c->cdev; + dev->function.name = "mtp"; + dev->function.strings = mtp_strings; + if (ptp_config) { + dev->function.descriptors = fs_ptp_descs; + dev->function.hs_descriptors = hs_ptp_descs; + } else { + dev->function.descriptors = fs_mtp_descs; + dev->function.hs_descriptors = hs_mtp_descs; + } + dev->function.bind = mtp_function_bind; + dev->function.unbind = mtp_function_unbind; + dev->function.set_alt = mtp_function_set_alt; + dev->function.disable = mtp_function_disable; + + return usb_add_function(c, &dev->function); +} + +static int mtp_setup(void) +{ + struct mtp_dev *dev; + int ret; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + spin_lock_init(&dev->lock); + init_waitqueue_head(&dev->read_wq); + init_waitqueue_head(&dev->write_wq); + init_waitqueue_head(&dev->intr_wq); + atomic_set(&dev->open_excl, 0); + atomic_set(&dev->ioctl_excl, 0); + INIT_LIST_HEAD(&dev->tx_idle); + INIT_LIST_HEAD(&dev->intr_idle); + + dev->wq = create_singlethread_workqueue("f_mtp"); + if (!dev->wq) { + ret = -ENOMEM; + goto err1; + } + INIT_WORK(&dev->send_file_work, send_file_work); + INIT_WORK(&dev->receive_file_work, receive_file_work); + + _mtp_dev = dev; + + ret = misc_register(&mtp_device); + if (ret) + goto err2; + + return 0; + +err2: + destroy_workqueue(dev->wq); +err1: + _mtp_dev = NULL; + kfree(dev); + printk(KERN_ERR "mtp gadget driver failed to initialize\n"); + return ret; +} + +static void mtp_cleanup(void) +{ + struct mtp_dev *dev = _mtp_dev; + + if (!dev) + return; + + misc_deregister(&mtp_device); + destroy_workqueue(dev->wq); + _mtp_dev = NULL; + kfree(dev); +} diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 345f8388..9d26ef09 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -71,6 +71,8 @@ struct f_rndis { struct gether port; u8 ctrl_id, data_id; u8 ethaddr[ETH_ALEN]; + u32 vendorID; + const char *manufacturer; int config; struct usb_ep *notify; @@ -768,12 +770,10 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, 0); rndis_set_host_mac(rndis->config, rndis->ethaddr); -#if 0 -// FIXME - if (rndis_set_param_vendor(rndis->config, vendorID, - manufacturer)) - goto fail0; -#endif + if (rndis->manufacturer && rndis->vendorID && + rndis_set_param_vendor(rndis->config, rndis->vendorID, + rndis->manufacturer)) + goto fail; /* NOTE: all that is done without knowing or caring about * the network link ... which is unavailable to this code @@ -854,6 +854,13 @@ static inline bool can_support_rndis(struct usb_configuration *c) */ int rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) +{ + return rndis_bind_config_vendor(c, ethaddr, 0, NULL); +} + +int +rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], + u32 vendorID, const char *manufacturer) { struct f_rndis *rndis; int status; @@ -861,14 +868,14 @@ rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) if (!can_support_rndis(c) || !ethaddr) return -EINVAL; + /* setup RNDIS itself */ + status = rndis_init(); + if (status < 0) + return status; + /* maybe allocate device-global string IDs */ if (rndis_string_defs[0].id == 0) { - /* ... and setup RNDIS itself */ - status = rndis_init(); - if (status < 0) - return status; - /* control interface label */ status = usb_string_id(c->cdev); if (status < 0) @@ -898,6 +905,8 @@ rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) goto fail; memcpy(rndis->ethaddr, ethaddr, ETH_ALEN); + rndis->vendorID = vendorID; + rndis->manufacturer = manufacturer; /* RNDIS activates when the host changes this filter */ rndis->port.cdc_filter = 0; diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index a896d73f..ab443a09 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -257,7 +257,6 @@ #include "gadget_chips.h" - /* * Kbuild is not very cooperative with respect to linking separately * compiled library objects into one module. So for now we won't use @@ -335,7 +334,11 @@ static struct { .vendor = FSG_VENDOR_ID, .product = FSG_PRODUCT_ID, .release = 0xffff, // Use controller chip type +#ifdef CONFIG_USB_AKUDC_PRODUCER + .buflen = 65536 +#else .buflen = 16384, +#endif }; @@ -555,10 +558,15 @@ device_desc = { .idVendor = cpu_to_le16(FSG_VENDOR_ID), .idProduct = cpu_to_le16(FSG_PRODUCT_ID), .bcdDevice = cpu_to_le16(0xffff), - +#ifdef CONFIG_USB_AKUDC_PRODUCER + .iManufacturer = 0,//STRING_MANUFACTURER, + .iProduct = 0,//STRING_PRODUCT, + .iSerialNumber = 0,//STRING_SERIAL, +#else .iManufacturer = FSG_STRING_MANUFACTURER, .iProduct = FSG_STRING_PRODUCT, .iSerialNumber = FSG_STRING_SERIAL, +#endif .bNumConfigurations = 1, }; @@ -2360,6 +2368,10 @@ static int check_command_size_in_blocks(struct fsg_dev *fsg, int cmnd_size, mask, needs_medium, name); } +#ifdef CONFIG_USB_AKUDC_PRODUCER +#include "plat-anyka/anyka_usbburn.c" //modified by anyka Zhang Jingyuan +#endif + static int do_scsi_command(struct fsg_dev *fsg) { struct fsg_buffhd *bh; @@ -2563,6 +2575,23 @@ static int do_scsi_command(struct fsg_dev *fsg) "WRITE(12)")) == 0) reply = do_write(fsg); break; + +#ifdef CONFIG_USB_AKUDC_PRODUCER + case SCSI_ANYKA_UBOOT: + fsg->data_size_from_cmnd = fsg->data_size; + if ((reply = check_anyka_command(fsg, 1)) == 0) + reply = usbburn_write(fsg->cmnd, 16 + 8); + if (fsg->data_size_from_cmnd > 0) { + if (fsg->data_dir == DATA_DIR_TO_HOST) + reply = do_anyka_read(fsg); + else + reply = do_anyka_write(fsg); + } + down(&sense_data_lock); + fsg->curlun->sense_data = sense_data; + break; + //end of modified by anyka Zhang Jingyuan +#endif /* Some mandatory commands that we recognize but don't implement. * They don't mean much in this setting. It's left as an exercise diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index a8855d0b..941b498a 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -50,6 +50,7 @@ #define gadget_is_s3c2410(g) (!strcmp("s3c2410_udc", (g)->name)) #define gadget_is_s3c_hsotg(g) (!strcmp("s3c-hsotg", (g)->name)) #define gadget_is_s3c_hsudc(g) (!strcmp("s3c-hsudc", (g)->name)) +#define gadget_is_ak_hsudc(g) (!strcmp("ak-hsudc", (g)->name)) /** * usb_gadget_controller_number - support bcdDevice id convention @@ -118,6 +119,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x31; else if (gadget_is_dwc3(gadget)) return 0x32; + else if (gadget_is_ak_hsudc(gadget)) + return 0x33; return -ENOENT; } diff --git a/drivers/usb/gadget/plat-anyka/Kconfig b/drivers/usb/gadget/plat-anyka/Kconfig new file mode 100644 index 00000000..a3900893 --- /dev/null +++ b/drivers/usb/gadget/plat-anyka/Kconfig @@ -0,0 +1,31 @@ +config USB_AKUDC_PRODUCER + tristate "Anyka usb device Port for PRODUCER" + depends on ARCH_AK39 + select USB_GADGET_DUALSPEED + help + Anyka USB-OTG device support for producer + +config USB_GADGET_AKUDC + tristate "Anyka usb device Port" + depends on ARCH_AK39 + select USB_GADGET_DUALSPEED + help + Anyka USB-OTG gadget device support + +config USB_AKUDC + tristate "udc driver support(usb-otg)" + depends on USB_GADGET_AKUDC + default USB_GADGET + help + udc driver in ak39xx platfrom + +config USB_AKUDC_DEBUG_FS + bool "Debugging information files (DEVELOPMENT)" + depends on USB_AKUDC + help + Some of the drivers in the "gadget" framework can expose debugging information + in files such as /proc/driver/udc (for a peripheral controller). The information in these + files may help when you're troubleshooting or bringing up a driver on a new board. + + Enable these files by choosing "Y" here. If in doubt, or to conserve kernel memory, say "N". + diff --git a/drivers/usb/gadget/plat-anyka/Makefile b/drivers/usb/gadget/plat-anyka/Makefile new file mode 100644 index 00000000..13c933d0 --- /dev/null +++ b/drivers/usb/gadget/plat-anyka/Makefile @@ -0,0 +1,6 @@ + +obj-$(CONFIG_USB_AKUDC_PRODUCER) += udc.o +obj-$(CONFIG_USB_AKUDC_PRODUCER) += usbburn.o + +obj-$(CONFIG_USB_AKUDC) += udc.o + diff --git a/drivers/usb/gadget/plat-anyka/anyka_usbburn.c b/drivers/usb/gadget/plat-anyka/anyka_usbburn.c new file mode 100644 index 00000000..5685a1b8 --- /dev/null +++ b/drivers/usb/gadget/plat-anyka/anyka_usbburn.c @@ -0,0 +1,188 @@ +#include + +/* + * to check whether the anyka command is correct + * modify from check_command in file_storage.c + */ +static int check_anyka_command(struct fsg_dev *fsg, int needs_medium) +{ + struct fsg_lun *curlun; + + fsg->residue = fsg->usb_amount_left = fsg->data_size; + + /* Check the LUN */ + if (fsg->lun >= 0 && fsg->lun < fsg->nluns) { + fsg->curlun = curlun = &fsg->luns[fsg->lun]; + curlun->sense_data = SS_NO_SENSE; + curlun->sense_data_info = 0; + curlun->info_valid = 0; + } else { + fsg->curlun = curlun = NULL; + fsg->bad_lun_okay = 0; + + DBG(fsg, "unsupported LUN %d\n", fsg->lun); + return -EINVAL; + } + + if (curlun && !fsg_lun_is_open(curlun) && needs_medium) { + curlun->sense_data = SS_MEDIUM_NOT_PRESENT; + return -EINVAL; + } + + return 0; +} + +/* + * to read data in akudc_usbburn char dev that come from producer + * modify from do_read in file_storage.c + */ +static int do_anyka_read(struct fsg_dev *fsg) +{ + struct fsg_buffhd *bh; + int rc; + u32 amount_left; + unsigned int amount; + ssize_t nread; + + /* Carry out the file reads */ + amount_left = fsg->data_size_from_cmnd; + if (unlikely(amount_left == 0)) + return -EIO; // No default reply + + for (;;) { + + /* Figure out how much we need to read: + * Try to read the remaining amount. + * But don't read more than the buffer size. + * And don't try to read past the end of the file. + * Finally, if we're not at a page boundary, don't read past + * the next page. + * If this means reading 0 then we were asked to read past + * the end of file. */ + amount = min((unsigned int) amount_left, mod_data.buflen); + + /* Wait for the next buffer to become available */ + bh = fsg->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(fsg); + if (rc) + return rc; + } + + nread = usbburn_read(bh->buf + nread, amount); + + amount_left -= nread; + fsg->residue -= nread; + bh->inreq->length = nread; + bh->state = BUF_STATE_FULL; + + if (nread < amount) + break; + if (amount_left == 0) + break; // No more left to read + + /* Send this buffer and go read some more */ + bh->inreq->zero = 0; + start_transfer(fsg, fsg->bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state); + fsg->next_buffhd_to_fill = bh->next; + } + + return -EIO; // No default reply +} + +/* + * to write data to akudc_usbburn char dev that can be read by producer + * modify from do_write in file_storage.c + */ +static int do_anyka_write(struct fsg_dev *fsg) +{ + struct fsg_lun *curlun = fsg->curlun; + struct fsg_buffhd *bh; + int get_some_more; + u32 amount_left_to_req, amount_left_to_write; + loff_t file_offset; + unsigned int amount; + ssize_t nwritten; + int rc; + + /* Carry out the file writes */ + get_some_more = 1; + file_offset = 0; + amount_left_to_req = amount_left_to_write = fsg->data_size_from_cmnd; + + while (amount_left_to_write > 0) { + + /* Queue a request for more data from the host */ + bh = fsg->next_buffhd_to_fill; + if (bh->state == BUF_STATE_EMPTY && get_some_more) { + + amount = min(amount_left_to_req, mod_data.buflen); + + /* Get the next buffer */ + fsg->usb_amount_left -= amount; + amount_left_to_req -= amount; + if (amount_left_to_req == 0) + get_some_more = 0; + + /* amount is always divisible by 512, hence by + * the bulk-out maxpacket size */ + bh->outreq->length = bh->bulk_out_intended_length = + amount; + bh->outreq->short_not_ok = 1; + start_transfer(fsg, fsg->bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state); + fsg->next_buffhd_to_fill = bh->next; + continue; + } + + /* Write the received data to the backing file */ + bh = fsg->next_buffhd_to_drain; + if (bh->state == BUF_STATE_EMPTY && !get_some_more) + break; // We stopped early + if (bh->state == BUF_STATE_FULL) { + smp_rmb(); + fsg->next_buffhd_to_drain = bh->next; + bh->state = BUF_STATE_EMPTY; + + /* Did something go wrong with the transfer? */ + if (bh->outreq->status != 0) { + curlun->sense_data = SS_COMMUNICATION_FAILURE; + // curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; + break; + } + + amount = bh->outreq->actual; + if (fsg->data_size_from_cmnd - file_offset < amount) { + LERROR(curlun, + "write %u @ %llu beyond end %llu\n", + amount, (unsigned long long) file_offset, + (unsigned long long) curlun->file_length); + amount = curlun->file_length - file_offset; + } + + /* Perform the write */ + nwritten = 0; + nwritten = usbburn_write(bh->buf + nwritten, amount); + + file_offset += nwritten; + amount_left_to_write -= nwritten; + fsg->residue -= nwritten; + + /* Did the host decide to stop early? */ + if (bh->outreq->actual != bh->outreq->length) { + fsg->short_packet_received = 1; + break; + } + continue; + } + + /* Wait for something to happen */ + rc = sleep_thread(fsg); + if (rc) + return rc; + } + + return -EIO; // No default reply +} diff --git a/drivers/usb/gadget/plat-anyka/udc.c b/drivers/usb/gadget/plat-anyka/udc.c new file mode 100644 index 00000000..37494006 --- /dev/null +++ b/drivers/usb/gadget/plat-anyka/udc.c @@ -0,0 +1,2664 @@ +/* + * akudc_udc -- driver for anyka USB peripheral controller + * Features + * The USB 2.0 HS OTG has following features: + * ? compliant with USB Specification Version 2.0 (HS) and On-The-Go supplement to + * the USB 2.0 specification + * ? operating as the host in point-to-point communications with another USB function + * or as a function controller for a USB peripheral + * ? supporting UTMI+ Level 2 Transceiver Interface + * ? 4 Transmit/Receive endpoints in addition to Endpoint 0 + * ? 3 DMA channels + * AUTHOR ANYKA Zhang Jingyuan + * 09-11-14 10:45:03 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION "30-May-2013" +#define DRIVER_DESC "ANYKA AK39 USB Device Controller driver" + +#if 0 +#define dbg(fmt, arg...) printk("%s(%d): " fmt "\n", __func__, __LINE__, ##arg) +#else +#define dbg(fmt, arg...) +#endif + +static const char ep0name[] = "ep0"; +static const char driver_name[] = "ak-hsudc"; + +#define udc_readb(reg) __raw_readb(udc->baseaddr + (reg)) +#define udc_readw(reg) __raw_readw(udc->baseaddr + (reg)) +#define udc_readl(reg) __raw_readl(udc->baseaddr + (reg)) +#define udc_writeb(reg, val) __raw_writeb(val, udc->baseaddr + (reg)) +#define udc_writew(reg, val) __raw_writew(val, udc->baseaddr + (reg)) +#define udc_writel(reg, val) __raw_writel(val, udc->baseaddr + (reg)) + +static struct akudc_udc controller; +struct workqueue_struct *ep_wqueue; //the workqueue for non-ep0 endpoint transmission + +volatile int usb_detect; + +unsigned int dma_rx; +unsigned int dma_tx; +dma_addr_t phys_rx; +dma_addr_t phys_tx; + +static volatile char flag = 0; +static atomic_t udc_clk = ATOMIC_INIT(0); //the clk condition for udc +static atomic_t usb_enable_flag = ATOMIC_INIT(0); //the current status of udc + +#ifdef CONFIG_USB_AKUDC_DEBUG_FS +#include +#include + +#define akudc_udc_read(udc, reg) \ + __raw_readl((udc)->baseaddr + (reg)) +#define akudc_udc_write(udc, reg, val) \ + __raw_writel((val), (udc)->baseaddr + (reg)) + +static const char debug_filename[] = "driver/udc"; + +static char *parse_linestate(unsigned long value) +{ + char *str; + + switch(value & USB_LINESTATE_WP) { + case 0: + str = "SE0"; + break; + case 1: + str = "'J'State"; + break; + case 2: + str = "'K'State"; + break; + case 3: + str = "SE1"; + break; + } + return str; +} + +static int parse_state(unsigned int val1, unsigned int val2) +{ + if (val1 == val2) + return 1; + return 0; +} + +static int udc_seq_show(struct seq_file *s, void *data) +{ + struct akudc_udc *udc = s->private; + unsigned long flags; + int i; + unsigned long tmp; + + seq_printf(s, "%s: version %s\n", "akudc", DRIVER_VERSION); + + local_irq_save(flags); + tmp = __raw_readl(USB_OP_MOD_REG); + seq_printf(s, "usb opmod control:0x%08x, linestate:%s,%s,%s,%s,%s\n\n", tmp, + parse_linestate(tmp), + (tmp & USB_DP_PU_EN) ? " DP en pullup" : " DP dis pullup", + parse_state(((tmp & USB_ID_CFG) >> 12), USB_ID_CLIENT) ? " slave" : " host", + parse_state(((tmp & USB_PHY_CFG) >> 6), USB_PHY_CLIENT) ? " slave" : " host", + (tmp & USB_SUSPEND_EN) ? "en suspend controller & transceiver" : " en suspend transceiver"); + + tmp = akudc_udc_read(udc, USB_POWER_CTRL); + seq_printf(s, "usb power control:0x%08x %s,%s,%s,%s,%s,%s\n\n", tmp, + (tmp & USB_ISO_UPDATE) ? " ISO" : " invalid", + (tmp & USB_HSPEED_EN) ? " Hspeed mode" : " Fspeed mode", + (tmp & USB_HSPEED_MODE) ? " HSmode success" : "", + (tmp & USB_RESUME_EN) ? " en resume" : " invalid", + (tmp & USB_SUSPEND_MODE)? " en suspend controller & transceiver" : "", + (tmp & USB_SUSPENDM_EN) ? " en suspendm" : " invalid"); + + tmp = akudc_udc_read(udc, USB_FRAME_NUM); + seq_printf(s, "frame=%d\n", tmp); + + tmp = akudc_udc_read(udc, USB_FUNCTION_ADDR); + seq_printf(s, "faddr=0x%p\n", tmp); + + tmp = __raw_readl(AK_VA_SYSCTRL + 0x1C); + seq_printf(s, "usb clock:%s\n", + (tmp & (1<<15)) ? " disable" : " enable"); + + local_irq_restore(flags); + return 0; +} +static int udc_debugfs_open(struct inode *inode, struct file *file) +{ + single_open(file, udc_seq_show, PDE(inode)->data); +} + +static const struct file_operations proc_ops = { + .owner = THIS_MODULE, + .open = udc_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +void create_debugfs_files(struct akudc_udc *udc) +{ + udc->pde = proc_create_data(debug_filename, S_IRUGO, NULL, &proc_ops, udc); +} + +void remove_debugfs_files(struct akudc_udc *udc) +{ + if(udc->pde) + remove_proc_entry(debug_filename, NULL); +} +#else +static inline void create_debugfs_files(struct akudc_udc *udc) {} +static inline void remove_debugfs_files(struct akudc_udc *udc) {} +#endif + +static void udc_reg_writel(unsigned long __iomem *__reg, + unsigned long value, int bits, int index) +{ + unsigned long tmp; + tmp = __raw_readl(__reg); + tmp &= ~(((1 << bits)-1) << index); + tmp |= (value << index); + __raw_writel(tmp, __reg); +} + +#if 0 +static unsigned long udc_reg_readl(unsigned long __iomem *__reg, + int bits, int index) +{ + unsigned long tmp; + tmp = __raw_readl(__reg); + tmp = (tmp&(((1 << bits)-1) << index)) >> index; + return tmp; +} +#endif + +static inline int whether_enable(void) +{ + /* + * if all of the udc condition is 1 and it is disable, + * return 1 + */ + if ((atomic_read(&udc_clk) == 1) + && (atomic_read(&usb_enable_flag) == 0)) { + + atomic_set(&usb_enable_flag, 1); + return 1; + } + + return 0; +} + +static inline int whether_disable(void) +{ + /* + * if any of the udc condition is 0 and it is enable, + * return 1 + */ + if ((atomic_read(&udc_clk) == 0) + && (atomic_read(&usb_enable_flag) == 1)) { + + atomic_set(&usb_enable_flag, 0); + return 1; + } + + return 0; +} + +/* enable the ep interrupt */ +static void ep_irq_enable(struct usb_ep *_ep) +{ + static struct akudc_udc *udc = &controller; + + /* enable ep1 rx */ + if (!strcmp(_ep->name, udc->ep[1].ep.name)) + //udc_writew(USB_INTERRUPT_RX, udc_readw(USB_INTERRUPT_RX) | (0x1<<1)); + udc_writew(USB_INTERRUPT_TX, udc_readw(USB_INTERRUPT_TX) | (0x1<<1)); + + /* enable ep2 tx */ + if (!strcmp(_ep->name, udc->ep[2].ep.name)) + udc_writew(USB_INTERRUPT_TX, udc_readw(USB_INTERRUPT_TX) | (0x1<<2)); + + /* enable ep3 rx */ + if (!strcmp(_ep->name, udc->ep[3].ep.name)) + udc_writew(USB_INTERRUPT_RX, udc_readw(USB_INTERRUPT_RX) | (0x1<<3)); + + /* enable ep4 tx */ + if (!strcmp(_ep->name, udc->ep[4].ep.name)) + udc_writew(USB_INTERRUPT_TX, udc_readw(USB_INTERRUPT_TX) | (0x1<<4)); + + /* enable ep5 rx */ + if (!strcmp(_ep->name, udc->ep[5].ep.name)) + udc_writew(USB_INTERRUPT_RX, udc_readw(USB_INTERRUPT_RX) | (0x1<<5)); +} + +/* disable the ep interrupt */ +static void ep_irq_disable(struct usb_ep *_ep) +{ + static struct akudc_udc *udc = &controller; + + /* disable ep1 rx */ + if (!strcmp(_ep->name, udc->ep[1].ep.name)) + //udc_writew(USB_INTERRUPT_RX, udc_readw(USB_INTERRUPT_RX) & ~(0x1<<1)); + udc_writew(USB_INTERRUPT_TX, udc_readw(USB_INTERRUPT_TX) & ~(0x1<<1)); + + /* disable ep2 tx */ + if (!strcmp(_ep->name, udc->ep[2].ep.name)) + udc_writew(USB_INTERRUPT_TX, udc_readw(USB_INTERRUPT_TX) & ~(0x1<<2)); + + /* disable ep3 rx */ + if (!strcmp(_ep->name, udc->ep[3].ep.name)) + udc_writew(USB_INTERRUPT_RX, udc_readw(USB_INTERRUPT_RX) & ~(0x1<<3)); + + /* disable ep4 tx */ + if (!strcmp(_ep->name, udc->ep[4].ep.name)) + udc_writew(USB_INTERRUPT_TX, udc_readw(USB_INTERRUPT_TX) & ~(0x1<<4)); + + /* disable ep5 rx */ + if (!strcmp(_ep->name, udc->ep[5].ep.name)) + udc_writew(USB_INTERRUPT_RX, udc_readw(USB_INTERRUPT_RX) & ~(0x1<<5)); +} + + +/* + * enable or disable the udc controller + * @enable: if 1, enable. if 0, disable + */ +void usbcontroller_enable(int enable) +{ + struct akudc_udc *udc = &controller; + if (enable) { + clk_enable(udc->clk); //enable clk for udc + /* reset usb phy and suspend transceiver, and power up(ak98) */ + udc_reg_writel(USB_OP_MOD_REG, 0x6, 3, 0); + set_usb_as_slave(); + + udc_writeb(USB_POWER_CTRL, 1<<5); // enable high speed negotiation + udc_writeb(USB_INTERRUPT_TX, 0x1<<0); // enable ep0 interrupt and disable other tx endpoint + udc_writeb(USB_INTERRUPT_RX, 0); // disable rx endpoint inttrupt + } else { + clk_disable(udc->clk); //disable clk for udc + /* reset usb phy */ + udc_reg_writel(USB_OP_MOD_REG, 0x1, 3, 0); + udc_writeb(USB_POWER_CTRL, 0); + ak_soft_reset(AK_SRESET_USBHS); //reset the udc controller + } +} + +/* + * set the udc condition, and judge whether the udc + * can be enabled, if is , enable it + */ +void set_condition(atomic_t *cond) { + atomic_set(cond, 1); + if (whether_enable()) + usbcontroller_enable(1); +} + +/* + * clear the udc condition, and judge whether the udc + * can be disabled, if is , disable it + */ +void clear_condition(atomic_t *cond) { + atomic_set(cond, 0); + if (whether_disable()) + usbcontroller_enable(0); +} + +/* + * enable the udc + */ + +/* + * when the req is complete, call this function + * @ep: which ep's request is complete + * @req: the request which is complete + * @status: the request's status + */ +static void done(struct akudc_ep *ep, struct akudc_request *req, int status) +{ + unsigned stopped = ep->stopped; + + /* delete req from ep queue */ + list_del_init(&req->queue); + + if (likely (req->req.status == -EINPROGRESS)) + req->req.status = status; + else + status = req->req.status; + + ep->stopped = 1; + /* call complete callback before return */ + req->req.complete(&ep->ep, &req->req); + ep->stopped = stopped; + + dbg("%s done, req.status(%d)", ep->ep.name, req->req.status); +} + +/* + * send data to pc in ep0 + * @ep: point to ep0 + * @req: the request which is doing + * return: 0 = still running, 1 = completed, negative = errno + */ +static int write_ep0_fifo(struct akudc_ep *ep, struct akudc_request *req) +{ + int i; + unsigned total, count, is_last; + struct akudc_udc *udc = ep->udc; + unsigned char *buf; + + total = req->req.length - req->req.actual; + + if (ep->ep.maxpacket < total) { /* if this transition is not last */ + count = ep->ep.maxpacket; + is_last = 0; + } else { /* if this transition is last */ + count = total; + /* + * if the req zero flag not set, the "count == ep->ep.maxpacket" + * is the last + */ + is_last = (count < ep->ep.maxpacket) || !req->req.zero; + } + + dbg("is_last(%d), count(%d), total(%d), actual(%d), length(%d)", + is_last, count, total, req->req.actual, req->req.length); + + udc_writeb(USB_EP_INDEX, 0); + /* send zero packet */ + if (count == 0) { + dbg(" count == 0 "); + udc_writel(USB_EP0_NUM, count&0x7f); + udc_writeb(USB_CTRL_1, 0x1<<3 | 0x1<<1); + udc->ep0state = EP0_IDLE; + done(ep, req, 0); + return 1; + } + + buf = req->req.buf + req->req.actual; + for (i = 0; i < count; i++) { + /* write data to the ep0 fifo */ + udc_writeb(USB_EP0_FIFO, buf[i]); + } + + udc_writel(USB_EP0_NUM, count&0x7f); /* 7bits */ + if (is_last) /* if last, set data end */ + udc_writeb(USB_CTRL_1, 0x1<<3 | 0x1<<1); + else + udc_writeb(USB_CTRL_1, 0x1<<1); + + req->req.actual += count; + if (is_last) { /* if last ,done the req with status 0 */ + udc->ep0state = EP0_IDLE; + done(ep, req, 0); + } + + ep->done = is_last; + return is_last; + /* return 1; */ +} + +/* + * read data from pc in ep0 + * @ep: point to ep0 + * @req: the request which is doing + * return: 0 = still running, 1 = completed, negative = errno + */ +static int read_ep0_fifo(struct akudc_ep *ep, struct akudc_request *req) +{ + int i; + unsigned total, count, is_last; + struct akudc_udc *udc = ep->udc; + unsigned char *buf; + + total = req->req.length - req->req.actual; + + if (ep->ep.maxpacket < total) { + count = ep->ep.maxpacket; + is_last = 0; + } else { + count = total; + is_last = (count < ep->ep.maxpacket) || !req->req.zero; + } + + dbg("is_last(%d), count(%d), total(%d), actual(%d), length(%d)", + is_last, count, total, req->req.actual, req->req.length); + + udc_writeb(USB_EP_INDEX, 0); + /* read zero packet */ + if (count == 0) { + dbg(" count == 0 "); + udc_writeb(USB_CTRL_1, 0x1<<3 | 0x1<<6); + udc->ep0state = EP0_IDLE; + done(ep, req, 0); + return 1; + } + + buf = req->req.buf + req->req.actual; + for (i = 0; i < count; i++) { + /* read data from ep0 fifo */ + buf[i] = udc_readb(USB_EP0_FIFO); + } + + if (is_last) /* if last, set data end */ + udc_writeb(USB_CTRL_1, 0x1<<3 | 0x1<<6); + else + udc_writeb(USB_CTRL_1, 0x1<<6); + + req->req.actual += count; + if (is_last) { /* if last ,done the req with status 0 */ + udc->ep0state = EP0_IDLE; + done(ep, req, 0); + } + + ep->done = is_last; + return is_last; + /* return 1; */ +} + +/* the ep1 is not used */ +static int write_ep1_fifo(struct akudc_ep *ep, struct akudc_request *req) +{ + struct akudc_udc *udc = ep->udc; + unsigned total, count, is_last; + unsigned char *buf; + int dma = 0, i; + + total = req->req.length - req->req.actual; + if (ep->ep.maxpacket <= total) { + count = ep->ep.maxpacket; + is_last = (total == ep->ep.maxpacket) && !req->req.zero; +#if 0 +#ifdef CONFIG_USB_AKUDC_PRODUCER + if (udc->high_speed) + dma = 1; +#endif +#endif + } else { + count = total; + is_last = count < ep->ep.maxpacket; + dma = 0; + } + dbg("total(%d), count(%d), is_last(%d)", total, count, is_last); + + if (count == 0) { /* not support command */ + dbg("count == 0\n"); + ep->done = 1; + udc_writeb(USB_EP_INDEX, 1); + udc_writeb(USB_CTRL_1, 0x1); + return 0; + } + udc_writeb(USB_EP_INDEX, 1); + + buf = req->req.buf + req->req.actual; +#if 0 +#ifdef CONFIG_USB_AKUDC_PRODUCER + dbg("producer"); + if (udc->high_speed && (dma == 1)) { + dma_tx = total - (total % ep->ep.maxpacket); + /* map the dma buffer */ + phys_tx = dma_map_single(NULL, buf, dma_tx, DMA_TO_DEVICE); + if (phys_tx == 0) { + printk("tx dma_map_single error!\n"); + goto cpu; + } + + udc_writeb(USB_CTRL_1_2, (1<<2) | (1<<4) | (1<<5) | (1<<7)); + + //send data to l2 + l2_clr_status(ep->l2_buf_id); + l2_combuf_dma(phys_tx, ep->l2_buf_id, dma_tx, MEM2BUF, false); + udc_writel(USB_DMA_ADDR3, 0x72000000); + udc_writel(USB_DMA_COUNT3, dma_tx); + udc_writel(USB_DMA_CTRL3, (USB_ENABLE_DMA | USB_DIRECTION_TX | USB_DMA_MODE1 | USB_DMA_INT_ENABLE| (USB_EP4_INDEX<<4) | USB_DMA_BUS_MODE3)); + + ep->done = 0; + return 0; + } +#endif +cpu: +#endif + + for (i = 0; i < count; i++) { + /* send data to ep4 fifo without dma */ + udc_writeb(USB_EP1_FIFO, buf[i]); + } + + udc_writeb(USB_CTRL_1, 0x1); + + req->req.actual += count; + /* wait a tx complete int */ + if (is_last) + done(ep, req, 0); + + return is_last; +} + +/* + * send data to pc in ep2 + * @ep: point to ep2 + * @req: the request which is doing + */ +static int write_ep2_fifo(struct akudc_ep *ep, struct akudc_request *req) /* ep2 */ +{ + struct akudc_udc *udc = ep->udc; + unsigned total, count, is_last; + unsigned char *buf; + int dma = 0, i; + + total = req->req.length - req->req.actual; + if (ep->ep.maxpacket <= total) { + count = ep->ep.maxpacket; + is_last = (total == ep->ep.maxpacket) && !req->req.zero; +#if 0 +#ifdef CONFIG_USB_AKUDC_PRODUCER + if (udc->high_speed) + dma = 1; +#endif +#endif + } else { + count = total; + is_last = count < ep->ep.maxpacket; + dma = 0; + } + dbg("total(%d), count(%d), is_last(%d)", total, count, is_last); + + if (count == 0) { /* not support command */ + dbg("count == 0\n"); + ep->done = 1; + udc_writeb(USB_EP_INDEX, 2); + udc_writeb(USB_CTRL_1, 0x1); + return 0; + } + udc_writeb(USB_EP_INDEX, 2); + + buf = req->req.buf + req->req.actual; +#if 0 +#ifdef CONFIG_USB_AKUDC_PRODUCER + if (udc->high_speed && (dma == 1)) { + dma_tx = total - (total % ep->ep.maxpacket); + /* map the dma buffer */ + phys_tx = dma_map_single(NULL, buf, dma_tx, DMA_TO_DEVICE); + if (phys_tx == 0) { + printk("tx dma_map_single error!\n"); + goto cpu; + } + + udc_writeb(USB_CTRL_1_2, (1<<2) | (1<<4) | (1<<5) | (1<<7)); + + //send data to l2 + l2_clr_status(ep->l2_buf_id); + l2_combuf_dma(phys_tx, ep->l2_buf_id, dma_tx, MEM2BUF, false); + udc_writel(USB_DMA_ADDR1, 0x70000000); + udc_writel(USB_DMA_COUNT1, dma_tx); + udc_writel(USB_DMA_CTRL1, (USB_ENABLE_DMA | USB_DIRECTION_TX | USB_DMA_MODE1 | USB_DMA_INT_ENABLE| (USB_EP2_INDEX<<4) | USB_DMA_BUS_MODE3)); + + ep->done = 0; + return 0; + } +#endif +cpu: +#endif + + for (i = 0; i < count; i++) { + /* send data to ep2 fifo without dma */ + udc_writeb(USB_EP2_FIFO, buf[i]); + } + udc_writeb(USB_CTRL_1, 0x1); + + req->req.actual += count; + /* wait a tx complete int */ + /* + * if (is_last) + * done(ep, req, 0); + */ + + /* return is_last; */ + ep->done = is_last; + + return 0; +} + +/* + * read data from pc in ep3 + * @ep: point to ep3 + * @req: the request which is doing + */ +static int read_ep3_fifo(struct akudc_ep *ep, struct akudc_request *req) /* ep3 */ +{ + struct akudc_udc *udc = ep->udc; + unsigned char *buf; + unsigned int csr, i; + unsigned int count, bufferspace, is_done; + + if (flag == 1) + return 0; + + bufferspace = req->req.length - req->req.actual; + + udc_writeb(USB_EP_INDEX, 3); + csr = udc_readb(USB_CTRL_2); + if ((csr & 0x1) == 0) { + dbg("waiting bulkout data"); + return 0; + } + + count = udc_readw(USB_EP_COUNT); + if (count == 0) { + dbg("USB_CTRL_2(0x%x), USB_EP_COUNT(%d), bufferspace(%d)", + csr, count, bufferspace); + goto stall; + } else if (count > ep->ep.maxpacket) + count = ep->ep.maxpacket; + + if (count > bufferspace) { + dbg("%s buffer overflow\n", ep->ep.name); + req->req.status = -EOVERFLOW; + count = bufferspace; + } + dbg("USB_CTRL_2(0x%x), USB_EP_COUNT(%d), bufferspace(%d)", csr, count, bufferspace); + + buf = req->req.buf + req->req.actual; + + for (i = 0; i < count; i++) { + /* read data from ep3 fifo without dma */ + buf[i] = udc_readb(USB_EP3_FIFO); + } +#if 0 +#ifdef CONFIG_USB_AKUDC_PRODUCER + if (udc->high_speed) { + dma_rx = bufferspace - count; + if (dma_rx >= ep->ep.maxpacket) { + dma_rx -= (dma_rx % ep->ep.maxpacket); + + buf = req->req.buf + req->req.actual + count; + /* send data to ep2 fifo without dma */ + phys_rx = dma_map_single(NULL, buf, dma_rx, DMA_FROM_DEVICE); + if (phys_rx == 0) { + printk("rx dma_map_single error!\n"); + goto stall; + } + + req->req.actual += count; + flag = 1; + udc_writeb(USB_CTRL_2_2, udc_readb(USB_CTRL_2_2) | (1<<3) | (1<<5) | (1<<7)); + l2_combuf_dma(phys_rx, ep->l2_buf_id, dma_rx, BUF2MEM, false); + udc_writel(USB_DMA_ADDR2, 0x71000000); + udc_writel(USB_DMA_COUNT2, dma_rx); + udc_writel(USB_DMA_CTRL2, (USB_ENABLE_DMA|USB_DIRECTION_RX|USB_DMA_MODE1|USB_DMA_INT_ENABLE|(USB_EP3_INDEX<<4)|USB_DMA_BUS_MODE3)); + udc_writeb(USB_EP_INDEX, 3); + udc_writeb(USB_CTRL_2, csr & ~0x1); + ep->done = 0; + + return 0; + } + } +#endif +#endif +stall: + udc_writeb(USB_CTRL_2, csr & ~0x1); + + req->req.actual += count; + is_done = (count < ep->ep.maxpacket); + if (count == bufferspace) + is_done = 1; + + ep->done = is_done; + if (is_done) { + done(ep, req, 0); + } + + return is_done; +} + +/* + * send data to pc in ep4 + * @ep: point to ep4 + * @req: the request which is doing + */ +static int write_ep4_fifo(struct akudc_ep *ep, struct akudc_request *req) /* ep4 */ +{ + struct akudc_udc *udc = ep->udc; + unsigned total, count, is_last; + unsigned char *buf; + int dma = 0, i; + + total = req->req.length - req->req.actual; + if (ep->ep.maxpacket <= total) { + count = ep->ep.maxpacket; + is_last = (total == ep->ep.maxpacket) && !req->req.zero; +#if 0 +#ifdef CONFIG_USB_AKUDC_PRODUCER + if (udc->high_speed) + dma = 1; +#endif +#endif + } else { + count = total; + is_last = count < ep->ep.maxpacket; + dma = 0; + } + dbg("total(%d), count(%d), is_last(%d)", total, count, is_last); + + if (count == 0) { /* not support command */ + dbg("count == 0\n"); + ep->done = 1; + udc_writeb(USB_EP_INDEX, 4); + udc_writeb(USB_CTRL_1, 0x1); + return 0; + } + udc_writeb(USB_EP_INDEX, 4); + + buf = req->req.buf + req->req.actual; +#if 0 +#ifdef CONFIG_USB_AKUDC_PRODUCER + if (udc->high_speed && (dma == 1)) { + dma_tx = total - (total % ep->ep.maxpacket); + /* map the dma buffer */ + phys_tx = dma_map_single(NULL, buf, dma_tx, DMA_TO_DEVICE); + if (phys_tx == 0) { + printk("tx dma_map_single error!\n"); + goto cpu; + } + + udc_writeb(USB_CTRL_1_2, (1<<2) | (1<<4) | (1<<5) | (1<<7)); + + //send data to l2 + l2_clr_status(ep->l2_buf_id); + l2_combuf_dma(phys_tx, ep->l2_buf_id, dma_tx, MEM2BUF, false); + udc_writel(USB_DMA_ADDR3, 0x72000000); + udc_writel(USB_DMA_COUNT3, dma_tx); + udc_writel(USB_DMA_CTRL3, (USB_ENABLE_DMA | USB_DIRECTION_TX | USB_DMA_MODE1 | USB_DMA_INT_ENABLE| (USB_EP4_INDEX<<4) | USB_DMA_BUS_MODE3)); + + ep->done = 0; + return 0; + } +#endif +cpu: +#endif + + for (i = 0; i < count; i++) { + /* send data to ep4 fifo without dma */ + udc_writeb(USB_EP4_FIFO, buf[i]); + } + + udc_writeb(USB_CTRL_1, 0x1); + + req->req.actual += count; + /* wait a tx complete int */ + /* + * if (is_last) + * done(ep, req, 0); + */ + + /* return is_last; */ + ep->done = is_last; + + return 0; +} + +/* + * read data from pc in ep5 + * @ep: point to ep5 + * @req: the request which is doing + */ +static int read_ep5_fifo(struct akudc_ep *ep, struct akudc_request *req) /* ep5 */ +{ + struct akudc_udc *udc = ep->udc; + unsigned char *buf; + unsigned int csr, i; + unsigned int count, bufferspace, is_done; + + if (flag == 1) + return 0; + + bufferspace = req->req.length - req->req.actual; + + udc_writeb(USB_EP_INDEX, 5); + csr = udc_readb(USB_CTRL_2); + if ((csr & 0x1) == 0) { + dbg("waiting bulkout data"); + return 0; + } + + count = udc_readw(USB_EP_COUNT); + if (count == 0) { + dbg("USB_CTRL_2(0x%x), USB_EP_COUNT(%d), bufferspace(%d)", + csr, count, bufferspace); + goto stall; + } else if (count > ep->ep.maxpacket) + count = ep->ep.maxpacket; + + if (count > bufferspace) { + dbg("%s buffer overflow\n", ep->ep.name); + req->req.status = -EOVERFLOW; + count = bufferspace; + } + dbg("USB_CTRL_2(0x%x), USB_EP_COUNT(%d), bufferspace(%d)", csr, count, bufferspace); + + buf = req->req.buf + req->req.actual; + + for (i = 0; i < count; i++) { + /* read data from ep5 fifo without dma */ + buf[i] = udc_readb(USB_EP5_FIFO); + } +#if 0 +#ifdef CONFIG_USB_AKUDC_PRODUCER + if (udc->high_speed) { + dma_rx = bufferspace - count; + if (dma_rx >= ep->ep.maxpacket) { + dma_rx -= (dma_rx % ep->ep.maxpacket); + + buf = req->req.buf + req->req.actual + count; + /* map the dma buffer */ + phys_rx = dma_map_single(NULL, buf, dma_rx, DMA_FROM_DEVICE); + if (phys_rx == 0) { + printk("rx dma_map_single error!\n"); + goto stall; + } + + req->req.actual += count; + flag = 1; + udc_writeb(USB_CTRL_2_2, udc_readb(USB_CTRL_2_2) | (1<<3) | (1<<5) | (1<<7)); + l2_combuf_dma(phys_rx, ep->l2_buf_id, dma_rx, BUF2MEM, false); + udc_writel(USB_DMA_ADDR4, 0x73000000); + udc_writel(USB_DMA_COUNT4, dma_rx); + udc_writel(USB_DMA_CTRL4, (USB_ENABLE_DMA|USB_DIRECTION_RX|USB_DMA_MODE1|USB_DMA_INT_ENABLE|(USB_EP5_INDEX<<4)|USB_DMA_BUS_MODE3)); + udc_writeb(USB_EP_INDEX, 5); + udc_writeb(USB_CTRL_2, csr & ~0x1); + ep->done = 0; + + return 0; + } + } +#endif +#endif + +stall: + udc_writeb(USB_CTRL_2, csr & ~0x1); + + req->req.actual += count; + is_done = (count < ep->ep.maxpacket); + if (count == bufferspace) + is_done = 1; + + ep->done = is_done; + if (is_done) { + done(ep, req, 0); + } + + return is_done; +} + + +/* the funciton is not used */ +static int akudc_get_frame(struct usb_gadget *gadget) +{ + dbg(""); + return 0; +} + +/* the funciton is not used */ +static int akudc_wakeup(struct usb_gadget *gadget) +{ + dbg(""); + return 0; +} + +/* + * the function to enable or disable the udc + * @is_on: 1,enable 0,disable + */ +static int akudc_pullup(struct usb_gadget *gadget, int is_on) +{ + if (is_on) + set_condition(&udc_clk); + else + clear_condition(&udc_clk); + return 0; +} + +/* the function is not used */ +static int akudc_vbus_session(struct usb_gadget *gadget, int is_active) +{ + dbg(""); + return 0; +} + +/* + * the function set the device power status + * @value: 1,selfpowered 0,powered by usb cable + */ +static int akudc_set_selfpowered(struct usb_gadget *gadget, int value) +{ + struct akudc_udc *udc = &controller; + + dbg("%d", value); + if (value) + udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED); + else + udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED); + + return 0; +} + +static int akudc_start(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)); +static int akudc_stop(struct usb_gadget_driver *driver); + +/* the operation struct for gadget */ +static const struct usb_gadget_ops akudc_udc_ops = { + .get_frame = akudc_get_frame, + .wakeup = akudc_wakeup, + .set_selfpowered = akudc_set_selfpowered, + .vbus_session = akudc_vbus_session, + .pullup = akudc_pullup, + .start = akudc_start, + .stop = akudc_stop, +}; + +static void ep1_work(struct work_struct *work) +{ + struct akudc_request *req = NULL; + struct akudc_udc *udc = &controller; + struct akudc_ep *ep = &udc->ep[1]; + unsigned long flags; + + local_irq_save(flags); + + /* if the queue is not empty, get the head of the queue */ + if (!list_empty(&ep->queue)){ + req = list_entry(ep->queue.next, struct akudc_request, queue); + } + + if (req) + req->status = write_ep1_fifo(ep, req); + else + dbg("something happend"); + local_irq_restore(flags); +} + +static void ep2_work(struct work_struct *work) +{ + struct akudc_request *req = NULL; + struct akudc_udc *udc = &controller; + struct akudc_ep *ep = &udc->ep[2]; + unsigned long flags; + + local_irq_save(flags); + + /* if the queue is not empty, get the head of the queue */ + if (!list_empty(&ep->queue)) + req = list_entry(ep->queue.next, struct akudc_request, queue); + + if (req) + req->status = write_ep2_fifo(ep, req); + else + dbg("something happend"); + local_irq_restore(flags); +} + +static void ep3_work(struct work_struct *work) +{ + struct akudc_request *req = NULL; + struct akudc_udc *udc = &controller; + struct akudc_ep *ep = &udc->ep[3]; + unsigned long flags; + + local_irq_save(flags); + + /* if the queue is not empty, get the head of the queue */ + if (!list_empty(&ep->queue)) + req = list_entry(ep->queue.next, struct akudc_request, queue); + + if (req) + req->status = read_ep3_fifo(ep, req); + else + dbg("something happend"); + local_irq_restore(flags); +} + +static void ep4_work(struct work_struct *work) +{ + struct akudc_request *req = NULL; + struct akudc_udc *udc = &controller; + struct akudc_ep *ep = &udc->ep[4]; + unsigned long flags; + + local_irq_save(flags); + + /* if the queue is not empty, get the head of the queue */ + if (!list_empty(&ep->queue)) + req = list_entry(ep->queue.next, struct akudc_request, queue); + + if (req) + req->status = write_ep4_fifo(ep, req); + else + dbg("something happend"); + local_irq_restore(flags); +} + +static void ep5_work(struct work_struct *work) +{ + struct akudc_request *req = NULL; + struct akudc_udc *udc = &controller; + struct akudc_ep *ep = &udc->ep[5]; + unsigned long flags; + + local_irq_save(flags); + + /* if the queue is not empty, get the head of the queue */ + if (!list_empty(&ep->queue)) + req = list_entry(ep->queue.next, struct akudc_request, queue); + + if (req) + req->status = read_ep5_fifo(ep, req); + else + dbg("something happend"); + local_irq_restore(flags); +} + +/* + * enable the ep, include enable the irq for the ep + * init the fifo and toggle for ep + * @ep: the ep which is enable + * @desc: the ep descriptor + */ +static int akudc_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct akudc_ep *ep = container_of(_ep, struct akudc_ep, ep); + struct akudc_udc *udc = ep->udc; + int tmp, maxpacket; + unsigned long flags; + + if (!_ep || !ep + || !desc || ep->desc + || _ep->name == ep0name + || desc->bDescriptorType != USB_DT_ENDPOINT + || (maxpacket = usb_endpoint_maxp(desc)) == 0 + || maxpacket > ep->maxpacket) { + dbg("bad ep or descriptor"); + dbg("%p, %p, %p, %p", _ep, ep, desc, ep->desc); + return -EINVAL; + } + + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) { + dbg("bogus udcice state\n"); + return -ESHUTDOWN; + } + + local_irq_save (flags); + + tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + switch (tmp) { + case USB_ENDPOINT_XFER_CONTROL: + dbg("only one control endpoint\n"); + return -EINVAL; + case USB_ENDPOINT_XFER_INT: + if (maxpacket > EP1_FIFO_SIZE) + dbg("maxpacket too large"); + break; + case USB_ENDPOINT_XFER_BULK: + switch (maxpacket) { + case 8: + case 16: + case 32: + case 64: + case 512: /* for usb20 */ + _ep->maxpacket = maxpacket & 0x7ff; + break; + default: + dbg("bogus maxpacket %d\n", maxpacket); + return -EINVAL; + } + break; + case USB_ENDPOINT_XFER_ISOC: + dbg("USB_ENDPOINT_XFER_ISOC"); + break; + } + ep->is_in = (desc->bEndpointAddress & USB_DIR_IN) != 0; + ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC); + + ep->stopped = 0; + ep->desc = (struct usb_endpoint_descriptor *)desc; + + dbg("%s, maxpacket(%d), desc->bEndpointAddress (0x%x) is_in(%d)", + _ep->name, maxpacket, desc->bEndpointAddress, ep->is_in); + + if (!strcmp(_ep->name, udc->ep[1].ep.name)) { /* ep1 -- int */ + dbg(""); + udc_writeb(USB_EP_INDEX, 1); + udc_writeb(USB_CTRL_1, (1<<3) | (1<<6)); // flush fifo and clear toggle + udc_writeb(USB_CTRL_1_2, 1<<5); //set tx + udc_writew(USB_TX_MAX, 64); //set maxpacket size + } else if (!strcmp(_ep->name, udc->ep[2].ep.name)) { /* ep2 -- tx */ + dbg(""); + udc_writeb(USB_EP_INDEX, 2); + udc_writeb(USB_CTRL_1, (1<<3) | (1<<6)); // flush fifo and clear toggle + udc_writeb(USB_CTRL_1_2, 1<<5); //set tx + udc_writew(USB_TX_MAX, 512); //set maxpacket size + } else if (!strcmp(_ep->name, udc->ep[3].ep.name)){ /* ep3 -- rx */ + udc_writeb(USB_EP_INDEX, 3); + udc_writeb(USB_CTRL_1, 1<<6); // clear tx toggle + udc_writeb(USB_CTRL_1_2, 0); // set rx + udc_writew(USB_RX_MAX, 512); // set maxpacket size + udc_writeb(USB_CTRL_2, udc_readb(USB_CTRL_2) & ~(0x1)); + udc_writeb(USB_CTRL_2, (1<<4) | (1<<7)); // flush fifo and clear rx toggle + udc_writeb(USB_CTRL_2_2, 0); //enable rx endpint + } else if (!strcmp(_ep->name, udc->ep[4].ep.name)) { /* ep4 -- tx */ + udc_writeb(USB_EP_INDEX, 4); + udc_writeb(USB_CTRL_1, (1<<3) | (1<<6)); // flush fifo and clear toggle + udc_writeb(USB_CTRL_1_2, 1<<5); //set tx + udc_writew(USB_TX_MAX, 512); //set maxpacket size + } else if (!strcmp(_ep->name, udc->ep[5].ep.name)){ /* ep5 -- rx */ + udc_writeb(USB_EP_INDEX, 5); + udc_writeb(USB_CTRL_1, 1<<6); // clear tx toggle + udc_writeb(USB_CTRL_1_2, 0); // set rx + udc_writew(USB_RX_MAX, 512); //set maxpacket size + udc_writeb(USB_CTRL_2, udc_readb(USB_CTRL_2) & ~(0x1)); + udc_writeb(USB_CTRL_2, (1<<4) | (1<<7)); // flush fifo and clear rx toggle + udc_writeb(USB_CTRL_2_2, 0); //enable rx endpint + } else { + printk("Invalid ep"); + return -EINVAL; + } + + ep_irq_enable(_ep); + local_irq_restore (flags); + + return 0; +} + +/* + * disable the ep, include disable the irq for the ep + * @ep: the ep which is disable + */ +static int akudc_ep_disable (struct usb_ep * _ep) +{ + struct akudc_ep *ep = container_of(_ep, struct akudc_ep, ep); + struct akudc_request *req; + unsigned long flags; + + if (!_ep || !ep->desc) { + dbg("%s not enabled\n", + _ep ? ep->ep.name : NULL); + return -EINVAL; + } + + local_irq_save(flags); + dbg("%s", _ep->name); + ep->desc = NULL; + ep->stopped = 1; + + /* + * delete every req in the endpoint queue + * dont it by status -ESHUTDOWN + */ + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct akudc_request, queue); + done(ep, req, -ESHUTDOWN); + } + ep_irq_disable(_ep); + local_irq_restore(flags); + + return 0; +} + +/* + * alloc the request for function driver + * @ep: the ep which the request deal with + */ +static struct usb_request * + akudc_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct akudc_request *req; + + dbg("%s", _ep->name); + req = kzalloc(sizeof (struct akudc_request), gfp_flags); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + return &req->req; +} + +/* + * free the request for function driver + * @ep: the ep which the request deal with + */ +static void akudc_ep_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct akudc_request *req; + + dbg("%s", _ep->name); + req = container_of(_req, struct akudc_request, req); + WARN_ON(!list_empty(&req->queue)); + kfree(req); +} + +/* + * add the request to the ep request queue + * @ep: the ep which the request deal with + */ +static int akudc_ep_queue(struct usb_ep *_ep, + struct usb_request *_req, gfp_t gfp_flags) +{ + struct akudc_request *req; + struct akudc_ep *ep; + struct akudc_udc *udc; + int status = 0; + unsigned long flags; + + req = container_of(_req, struct akudc_request, req); + ep = container_of(_ep, struct akudc_ep, ep); + + if (!_ep || (!ep->desc && ep->ep.name != ep0name)) { + dbg("invalid ep\n"); + return -EINVAL; + } + + udc = ep->udc; + if (!udc || !udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) { + printk("invalid device\n"); + return -EINVAL; + } + + local_irq_save(flags); + + if (!_req || !_req->complete + || !_req->buf || !list_empty(&req->queue)) { + printk("%s invalid request\n", _ep->name); + local_irq_restore(flags); + return -EINVAL; + } + + _req->status = -EINPROGRESS; + _req->actual = 0; + + dbg("%s queue is_in(%d)", ep->ep.name, ep->is_in); + + /* + * if the queue is empty and the ep is not stopped, + * do the request + */ + if (list_empty(&ep->queue) && !ep->stopped) { + if (ep->ep.name == ep0name) { + udc_writeb(USB_EP_INDEX, 0); + + switch (udc->ep0state) { + case EP0_IN_DATA_PHASE: + if ((udc_readb(USB_CTRL_1) & (1 << 1)) == 0 + && write_ep0_fifo(ep, req)) { + udc->ep0state = EP0_IDLE; + req = NULL; + } + break; + + case EP0_OUT_DATA_PHASE: + if ((!_req->length) || + ((udc_readb(USB_CTRL_1) & (1 << 0)) + && read_ep0_fifo(ep, req))) { + udc->ep0state = EP0_IDLE; + req = NULL; + } + break; + + default: + local_irq_restore(flags); + return -EL2HLT; + } + if (req) + list_add_tail(&req->queue, &ep->queue); + } else { + list_add_tail(&req->queue, &ep->queue); + if (!strcmp(_ep->name, udc->ep[1].ep.name)) { /* ep1 */ + dbg("ep1"); + //status = write_ep1_fifo(ep, req); + udc_writeb(USB_EP_INDEX, 1); + if ((udc_readb(USB_CTRL_1) & 1) == 0) + queue_work(ep_wqueue, &ep->work); + } else if (!strcmp(_ep->name, udc->ep[2].ep.name)) { /* ep2 */ + udc_writeb(USB_EP_INDEX, 2); + if ((udc_readb(USB_CTRL_1) & 1) == 0) + queue_work(ep_wqueue, &ep->work); + } else if (!strcmp(_ep->name, udc->ep[3].ep.name)){ /* ep3 */ + udc_writeb(USB_EP_INDEX, 3); + if (udc_readb(USB_CTRL_2) & 1) + queue_work(ep_wqueue, &ep->work); + } else if (!strcmp(_ep->name, udc->ep[4].ep.name)) { /* ep4 */ + udc_writeb(USB_EP_INDEX, 4); + if ((udc_readb(USB_CTRL_1) & 1) == 0) + queue_work(ep_wqueue, &ep->work); + } else if (!strcmp(_ep->name, udc->ep[5].ep.name)){ /* ep5 */ + udc_writeb(USB_EP_INDEX, 5); + if (udc_readb(USB_CTRL_2) & 1) + queue_work(ep_wqueue, &ep->work); + } else { + printk("Invalid ep"); + status = -EINVAL; + goto stall; + } + } + } else { + /* + * if the queue is not empty, add the req to the queue only + */ + status = 0; + list_add_tail(&req->queue, &ep->queue); + dbg("waiting for %s int", ep->ep.name); + } + +stall: + local_irq_restore(flags); + /* return (status < 0) ? status : 0; */ + return status; +} + +/* + * delete the request from the ep request queue + * @ep: the ep which the request deal with + */ +static int akudc_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct akudc_ep *ep; + struct akudc_request *req; + unsigned long flags; + struct akudc_udc *udc = &controller; + + dbg("dequeue"); + + if (!udc->driver) + return -ESHUTDOWN; + + ep = container_of(_ep, struct akudc_ep, ep); + if (!_ep || !_req) + return -EINVAL; + + local_irq_save(flags); + /* find the req from head */ + list_for_each_entry (req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + /* if cannot find, return error */ + if (&req->req != _req) { + local_irq_restore(flags); + return -EINVAL; + } + /* delete the req and set the req status -ECONNRESET */ + done(ep, req, -ECONNRESET); + local_irq_restore(flags); + + return 0; +} + +/* + * set or clear the stall function for ep + * @ep: which ep is being setting + * @value: 1 stall, 0 clear stall + */ +static int akudc_ep_set_halt(struct usb_ep *_ep, int value) +{ + struct akudc_ep *ep = container_of(_ep, struct akudc_ep, ep); + unsigned int csr = 0; + unsigned long flags; + struct akudc_udc *udc = &controller; + + if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) { + dbg("inval 2"); + return -EINVAL; + } + + local_irq_save (flags); + + if ((ep->ep.name == ep0name) && value) { + udc_writeb(USB_EP_INDEX, 0); + udc_writeb(USB_CTRL_1, 1<<5); + udc_writeb(USB_CTRL_1, 1<<6 | 1<<3); + }else if(!strcmp(ep->ep.name, udc->ep[1].ep.name)){/* ep1 */ + udc_writeb(USB_EP_INDEX, 1); + csr = udc_readw(USB_CTRL_1); + if (value) + udc_writew(USB_CTRL_1, csr | 1<<4); + else { + csr &= ~(1<<4 | 1<<5); + udc_writew(USB_CTRL_1, csr); + csr |= 1<<6; + udc_writew(USB_CTRL_1, csr); + } + } else if (ep->is_in) { /* ep2 or ep4*/ + if (!strcmp(ep->ep.name, udc->ep[2].ep.name)) + udc_writeb(USB_EP_INDEX, 2); + else + udc_writeb(USB_EP_INDEX, 4); + + csr = udc_readw(USB_CTRL_1); + if (value) + udc_writew(USB_CTRL_1, csr | 1<<4); + else { + csr &= ~(1<<4 | 1<<5); + udc_writew(USB_CTRL_1, csr); + csr |= 1<<6; + udc_writew(USB_CTRL_1, csr); + } + } else { /* ep3 or ep5*/ + if (!strcmp(ep->ep.name, udc->ep[3].ep.name)) + udc_writeb(USB_EP_INDEX, 3); + else + udc_writeb(USB_EP_INDEX, 5); + + csr = udc_readw(USB_CTRL_2); + if (value) + udc_writew(USB_CTRL_2, csr | 1<<5); + else { + csr &= ~(1<<5 | 1<<6); + udc_writew(USB_CTRL_2, csr); + csr |= 1<<7; + udc_writew(USB_CTRL_2, csr); + } + } + + ep->stopped= value ? 1 : 0; + local_irq_restore (flags); + + return 0; +} + +/* the operation struct for ep */ +static const struct usb_ep_ops akudc_ep_ops = { + .enable = akudc_ep_enable, + .disable = akudc_ep_disable, + .alloc_request = akudc_ep_alloc_request, + .free_request = akudc_ep_free_request, + .queue = akudc_ep_queue, + .dequeue = akudc_ep_dequeue, + .set_halt = akudc_ep_set_halt, + // there's only imprecise fifo status reporting +}; + +static void nop_release(struct device *dev) +{ + /* nothing to free */ +} + +/* the core struct for udc that include many important struct */ +static struct akudc_udc controller = { + /* the gadget , include ep0 info*/ + .gadget = { + .ops = &akudc_udc_ops, + .ep0 = &controller.ep[0].ep, + .name = driver_name, + .dev = { + .init_name = "gadget", + .release = nop_release, + } + }, + /* the array of endpoint */ + .ep[0] = { + .ep = { + .name = ep0name,//ep_name[0], + .ops = &akudc_ep_ops, + }, + .udc = &controller, + .maxpacket = EP0_FIFO_SIZE, + }, + .ep[1] = { + .ep = { + .name = "ep1-int",//ep_name[1], + .ops = &akudc_ep_ops, + }, + .udc = &controller, + .maxpacket = EP1_FIFO_SIZE, + }, + .ep[2] = { + .ep = { + .name = "ep2in-bulk",//ep_name[2], + .ops = &akudc_ep_ops, + }, + .udc = &controller, + .maxpacket = EP2_FIFO_SIZE, + }, + .ep[3] = { + .ep = { + /* could actually do bulk too */ + .name = "ep3out-bulk",//ep_name[3], + .ops = &akudc_ep_ops, + }, + .udc = &controller, + .maxpacket = EP3_FIFO_SIZE, + }, + .ep[4] = { + .ep = { + .name = "ep4in-bulk",//ep_name[4], + .ops = &akudc_ep_ops, + }, + .udc = &controller, + .maxpacket = EP4_FIFO_SIZE, + }, + .ep[5] = { + .ep = { + .name = "ep5out-bulk",//ep_name[5], + .ops = &akudc_ep_ops, + }, + .udc = &controller, + .maxpacket = EP5_FIFO_SIZE, + }, +}; + +/** + * akudc_udc_get_status - process request GET_STATUS + * @udc: The device state + * @ctrl: USB control request + */ +static int akudc_udc_get_status(struct akudc_udc *udc, + struct usb_ctrlrequest *ctrl) +{ + u16 status = 0; + u8 ep_num = ctrl->wIndex & 0x7F; + u8 is_in = ctrl->wIndex & USB_DIR_IN; + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + status = udc->devstatus; + break; + + case USB_RECIP_INTERFACE: + /* currently, the data result should be zero */ + break; + + case USB_RECIP_ENDPOINT: + if (ep_num > 5 || ctrl->wLength > 2) + return 1; + + if (ep_num == 0) { + udc_writeb(USB_EP_INDEX, 0); + status = udc_readb(USB_CTRL_1); + status = status & (1<<5); + } else { + udc_writeb(USB_EP_INDEX, ep_num); + if (is_in) { + status = udc_readb(USB_CTRL_1); + status = status & (1<<4); + } else { + status = udc_readb(USB_CTRL_2); + status = status & (1<<5); + } + } + + status = status ? 1 : 0; + break; + default: + return 1; + } + + udc_writeb(USB_EP0_FIFO, status & 0xff); + udc_writeb(USB_EP0_FIFO, status >> 8); + + udc_writel(USB_EP0_NUM, 2); /* 7bits */ + udc_writeb(USB_CTRL_1, 0x1<<3 | 0x1<<1); + + udc->ep0state = EP0_END_XFER; + + return 0; +} + +/* function for ep0 setup phase */ +static void akudc_udc_handle_ep0_idle(struct akudc_udc *udc, + struct akudc_ep *ep, u32 ep0csr) +{ + struct usb_ctrlrequest crq; + int i, len, ret, tmp, timeout; + unsigned char *buf; + + /* start control request? */ + if (!(ep0csr & 1)) + return; + + len = udc_readw(USB_EP_COUNT); + buf = (unsigned char *)&crq; + if (len > sizeof(struct usb_ctrlrequest)) + len = sizeof(struct usb_ctrlrequest); + for (i = 0; i < len; i++) { + /* read the setup data from ep0 fifo */ + buf[i] = udc_readb(USB_EP0_FIFO); + } + if (len != sizeof(crq)) { + dbg("setup begin: fifo READ ERROR" + " wanted %d bytes got %d. Stalling out...", + sizeof(crq), len); + udc_writeb(USB_CTRL_1, 1<<5); + return; + } + + dbg("bRequest = %d bRequestType %d wLength = %d", + crq.bRequest, crq.bRequestType, crq.wLength); + + /* cope with automagic for some standard requests. */ + udc->req_std = (crq.bRequestType & USB_TYPE_MASK) + == USB_TYPE_STANDARD; + udc->req_config = 0; + udc->req_pending = 1; + + switch (crq.bRequest) { + case USB_REQ_SET_CONFIGURATION: + if (crq.bRequestType == USB_RECIP_DEVICE) { + udc->req_config = 1; + udc_writeb(USB_CTRL_1, 0x1<<3 | 0x1<<6); + } + break; + + case USB_REQ_SET_INTERFACE: + if (crq.bRequestType == USB_RECIP_INTERFACE) { + udc->req_config = 1; + udc_writeb(USB_CTRL_1, 0x1<<3 | 0x1<<6); + } + break; + + case USB_REQ_SET_ADDRESS: + if (crq.bRequestType == USB_RECIP_DEVICE) { + tmp = crq.wValue & 0x7F; + udc_writeb(USB_CTRL_1, 0x1<<3 | 0x1<<6); + timeout = 20000; + /* waiting for next interrupt */ + while (!(udc_readb(USB_INTERRUPT_1) & 0x1) && timeout) {timeout--;} + udc_writeb(USB_FUNCTION_ADDR, tmp); + udc->addr = tmp; + return; + } + break; + + case USB_REQ_GET_STATUS: + udc_writeb(USB_CTRL_1, 0x1<<6); + + if (udc->req_std) { + if (!akudc_udc_get_status(udc, &crq)) { + return; + } + } + break; + + case USB_REQ_CLEAR_FEATURE: + udc_writeb(USB_CTRL_1, 0x1<<6); + + if (crq.bRequestType != USB_RECIP_ENDPOINT) + break; + + if (crq.wValue != USB_ENDPOINT_HALT || crq.wLength != 0) + break; + + akudc_ep_set_halt(&udc->ep[crq.wIndex & 0x7f].ep, 0); + udc_writeb(USB_CTRL_1, 0x1<<6 | 0x1<<3); + + return; + + case USB_REQ_SET_FEATURE: + udc_writeb(USB_CTRL_1, 0x1<<6); + + if (crq.bRequestType != USB_RECIP_ENDPOINT) + break; + + if (crq.wValue != USB_ENDPOINT_HALT || crq.wLength != 0) + break; + + akudc_ep_set_halt(&udc->ep[crq.wIndex & 0x7f].ep, 1); + udc_writeb(USB_CTRL_1, 0x1<<6 | 0x1<<3); + return; + + default: + udc_writeb(USB_CTRL_1, 0x1<<6); + break; + } + + /* set ep0state according to the command */ + if (crq.bRequestType & USB_DIR_IN) + udc->ep0state = EP0_IN_DATA_PHASE; + else + udc->ep0state = EP0_OUT_DATA_PHASE; + + /* call the function drvier setup */ + ret = udc->driver->setup(&udc->gadget, &crq); + /* + * if the setup failed, sent stall ep0 + */ + if (ret < 0) { + if (udc->req_config) { + dbg("config change %02x fail %d?", + crq.bRequest, ret); + return; + } + + if (ret == -EOPNOTSUPP) + dbg("Operation not supported"); + else + dbg("udc->driver->setup failed. (%d)", ret); + + udc_writeb(USB_CTRL_1, 1<<5); + udc_writeb(USB_CTRL_1, 1<<3 | 1<<6); + udc->ep0state = EP0_IDLE; + /* deferred i/o == no response yet */ + } else if (udc->req_pending) { + dbg("dev->req_pending... what now?"); + udc->req_pending=0; + } +} + +/* deal with interrupt for ep0 */ +static void handle_ep0(struct akudc_udc *udc) +{ + int csr, error = 0; + struct akudc_ep *ep0 = &udc->ep[0]; + struct akudc_request *req = NULL; + + if (!list_empty(&ep0->queue)) + req = list_entry(ep0->queue.next, struct akudc_request, queue); + + udc_writeb(USB_EP_INDEX, 0); + csr = udc_readb(USB_CTRL_1); + dbg("csr(0x%x)", csr); + + if (csr & 0x1<<3) { + dbg("data end"); + } + if (csr & 0x1<<4) { + dbg("A control transaction ends before the DataEnd bit has been set"); + udc_writeb(USB_CTRL_1, 0x1<<7); + /* do something else? */ + udc->ep0state = EP0_IDLE; + error = 1; + } + if (csr & 0x1<<2) { + udc_writeb(USB_CTRL_1, udc_readb(USB_CTRL_1) & ~(1 << 2)); + udc->ep0state = EP0_IDLE; + error = 1; + } + switch (udc->ep0state) { + case EP0_IDLE: + akudc_udc_handle_ep0_idle(udc, ep0, csr); + break; + + case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ + dbg("EP0_IN_DATA_PHASE ... what now?"); + if (!(csr & (1<<1)) && req && error == 0) + write_ep0_fifo(ep0, req); + break; + + case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */ + dbg("EP0_OUT_DATA_PHASE ... what now?"); + if ((csr & (1<<0)) && req) + read_ep0_fifo(ep0, req); + break; + + case EP0_END_XFER: + dbg("EP0_END_XFER ... what now?"); + udc->ep0state = EP0_IDLE; + break; + + case EP0_STALL: + dbg("EP0_STALL ... what now?"); + udc->ep0state = EP0_IDLE; + break; + } +} + +/* not ep0 */ +static void handle_ep(struct akudc_ep *ep) +{ + struct akudc_request *req; + struct akudc_udc *udc = ep->udc; + unsigned int csr; + + if (!list_empty(&ep->queue)) + req = list_entry(ep->queue.next, + struct akudc_request, queue); + else { + dbg("%s: no req waiting", ep->ep.name); + req = NULL; + } + + if (!strcmp(ep->ep.name, udc->ep[1].ep.name)) { /* ep1 */ + dbg("ep1"); + udc_writeb(USB_EP_INDEX, 1); + + /* read the epx status */ + csr = udc_readb(USB_CTRL_1); + dbg("ep1 csr(0x%x), req(0x%p)", csr, req); + + if (csr & 0x1<<2) { /* clear underrun */ + udc_writeb(USB_CTRL_1, csr & ~(0x1<<2)); + } + if (csr & 0x1<<5) { /* clear sentstall */ + udc_writeb(USB_CTRL_1, csr & ~(0x1<<5)); + return; + } + + if (req) { + /* + * if the current req is complete, done it with status 0 + * and if the next req is being, and the epx can be written + * data, do it with work queue + * else do the current req with work queue if the epx can be written + */ + if (ep->done) { + done(ep, req, 0); + if (!list_empty(&ep->queue)) { + dbg("do next queue"); + req = list_entry(ep->queue.next, struct akudc_request, queue); + if ((csr & 1) == 0) + queue_work(ep_wqueue, &ep->work); + } + } else { + if ((csr & 1) == 0) + queue_work(ep_wqueue, &ep->work); + } + + } + } else if (ep->is_in) { /* ep2 or ep4*/ + + if (!strcmp(ep->ep.name, udc->ep[2].ep.name)) + udc_writeb(USB_EP_INDEX, 2); + else + udc_writeb(USB_EP_INDEX, 4); + + /* read the epx status */ + csr = udc_readb(USB_CTRL_1); + dbg("ep2 or ep4 csr(0x%x), req(0x%p)", csr, req); + + /* udc_writeb(USB_CTRL_1, 0x1); */ + if (csr & 0x1<<2) { /* clear underrun */ + udc_writeb(USB_CTRL_1, csr & ~(0x1<<2)); + } + if (csr & 0x1<<5) { /* clear sentstall */ + udc_writeb(USB_CTRL_1, csr & ~(0x1<<5)); + return; + } + + if (req) { + /* + * if the current req is complete, done it with status 0 + * and if the next req is being, and the epx can be written + * data, do it with work queue + * else do the current req with work queue if the epx can be written + */ + if (ep->done) { + done(ep, req, 0); + if (!list_empty(&ep->queue)) { + dbg("do next queue"); + req = list_entry(ep->queue.next, struct akudc_request, queue); + if ((csr & 1) == 0) + queue_work(ep_wqueue, &ep->work); + } + } else { + if ((csr & 1) == 0) + queue_work(ep_wqueue, &ep->work); + } + + } + } else { /* ep3 or ep 5*/ + if (!strcmp(ep->ep.name, udc->ep[3].ep.name)) + udc_writeb(USB_EP_INDEX, 3); + else + udc_writeb(USB_EP_INDEX, 5); + + /* read the epx status */ + csr = udc_readb(USB_CTRL_2); + dbg("ep3 or ep5 csr(0x%x)", csr); + + if (csr & 0x1<<6) { /* clear sentstall */ + udc_writeb(USB_CTRL_2, csr & ~(0x1<<6)); + } + /* + * if the req is being and the data come to the endpoint fifo + * do the current req with work queue + */ + if (req && (csr & 0x1<<0)) + queue_work(ep_wqueue, &ep->work); + } +} + +/* + * the function execute when the suspend signal come + * or the system is being suspend + */ +static void udc_disconnect(struct akudc_udc *udc) +{ + struct usb_gadget_driver *driver = udc->driver; + int i; + + if (udc->gadget.speed == USB_SPEED_UNKNOWN) + driver = NULL; + + for (i = 0; i < ENDPOINTS_NUM; i++) { + struct akudc_ep *ep = &udc->ep[i]; + struct akudc_request *req; + + /* sign every endpoint stop */ + ep->stopped = 1; + + // terminer chaque requete dans la queue + if (list_empty(&ep->queue)) + continue; + + /* + * delete every req in every endpoint queue + * dont it by status -ESHUTDOWN + */ + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct akudc_request, queue); + done(ep, req, -ESHUTDOWN); + } + } + + /* if the driver exist, call the function drvier disconnect */ + if (driver) + driver->disconnect(&udc->gadget); + + /* init the udc */ + udc_reinit(udc); +} + +#if 0 +static void akudc_udc_fun(void *data) +{ + struct akudc_udc *udc = data; + + msleep(500); + clk_enable(udc->clk); + udc_reg_writel(USB_OP_MOD_REG, 0x6, 3, 0); + udc_writeb(USB_POWER_CTRL, 0x1<<5); +} +#endif + +static void akudc_conctrol_reset(struct akudc_udc *udc) +{ + // reset the udc controller module + ak_soft_reset(AK_SRESET_USBHS); + REG32(AK_VA_SYSCTRL + 0x58) &= ~(0xff << 0); +} + +/* the function execute when the reset signal come */ +static void udc_reset(struct akudc_udc *udc) +{ + int i ,temp; + + udc_writeb(USB_FUNCTION_ADDR, 0); // set the usb device addr 0 + udc_writeb(USB_INTERRUPT_USB, ~(0x1<<3)); // clear SOF interrupt + udc_writeb(USB_INTERRUPT_TX, 0x1<<0); // enable ep0 interrupt and disable other tx endpoint + udc_writeb(USB_INTERRUPT_RX, 0); // disable rx endpoint inttrupt + + /* negotiated high speed */ + if ((udc_readb(USB_POWER_CTRL) & (0x1 << 4)) == (0x1 << 4)) { + udc_writel(USB_MODE_STATUS, udc_readl(USB_MODE_STATUS) & (~0x1)); + /* enable high speed for udc */ + udc_writeb(USB_POWER_CTRL, 0x1<<5); + udc->gadget.speed = USB_SPEED_HIGH; + udc->high_speed = 1; + } else { /* negotiated full speed */ + udc_writeb(USB_POWER_CTRL, 0); + udc_writel(USB_MODE_STATUS, udc_readl(USB_MODE_STATUS) | 0x1); + udc->gadget.speed = USB_SPEED_FULL; + udc->high_speed = 0; + } + + /* read and clear the common interrupt status */ + temp = udc_readw(USB_INTERRUPT_COMM); + /* read and clear the ep0 and all tx ep interrupt status */ + temp = udc_readw(USB_INTERRUPT_1); + /* read and clear all rx ep interrupt status */ + temp = udc_readw(USB_INTERRUPT_2); + + /* init the ep0 status */ + udc->ep0state = EP0_IDLE; + + for (i = 0; i < ENDPOINTS_NUM; i++) { + struct akudc_ep *ep = &udc->ep[i]; + struct akudc_request *req; + + if (list_empty(&ep->queue)) + continue; + + /* + * delete every req in every endpoint queue + * dont it by status -ECONNRESET + */ + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct akudc_request, queue); + done(ep, req, -ECONNRESET); + } + } +} + + +/* the udc irq irqhandler */ +static irqreturn_t udc_irqhandler(int irq, void *_udc) +{ + struct akudc_udc *udc = _udc; + short status_1, status_2; + char status_int; + + status_int = udc_readb(USB_INTERRUPT_COMM); + if (status_int & 0x1<<2) { + /* dbg("status_int(0x%x), reset", status_int); */ + printk("\n\nstatus_int(0x%x), reset\n\n", status_int); + if (usb_detect) { + usb_detect = 0; + if (!udc->driver) { + panic("If you see this, come to find Zhang Jingyuan\n"); + akudc_disable(udc); + return IRQ_HANDLED; + } + } + udc_reset(udc); + goto done; + } else if(status_int & 0x1<<1) { /* resume */ + dbg("status_int(0x%x)", status_int); + goto done; + } else if(status_int & 0x1<<0) { /* suspend */ + dbg("status_int(0x%x)", status_int); +#if 0 +#ifdef CONFIG_USB_AKUDC_PRODUCER + pm_power_off(); +#endif +#endif + udc_disconnect(udc); + goto done; + } + + status_1 = udc_readb(USB_INTERRUPT_1); + status_2 = udc_readb(USB_INTERRUPT_2); + /* ep0 */ + if (status_1 & 0x1<<0) { + handle_ep0(udc); + } + /* ep2 */ + if (status_1 & 0x1<<2) { + dbg("endpoint2"); + handle_ep(&udc->ep[2]); + } + /* ep4 */ + if (status_1 & 0x1<<4) { + dbg("endpoint4"); + handle_ep(&udc->ep[4]); + } + + /* ep1 */ + if (status_2 & 0x1<<1) { + dbg("endpoint 1"); + handle_ep(&udc->ep[1]); + } + /* ep3 */ + if (status_2 & 0x1<<3) { + dbg("endpoint3"); + handle_ep(&udc->ep[3]); + } + /* ep5 */ + if (status_2 & 0x1<<5) { + dbg("endpoint5"); + handle_ep(&udc->ep[5]); + } + +done: + return IRQ_HANDLED; +} + +#if 0 +/* the l2 buffer dma irqhandler */ +static irqreturn_t udc_dmahandler(int irq, void *_udc) +{ + struct akudc_request *req = NULL; + struct akudc_udc *udc = _udc; + struct akudc_ep *ep; + unsigned int is_done = 0; + + u32 usb_dma_int = udc_readl(USB_DMA_INTR); + + /* + * affirm which channel's interrupt + * channel1 -- ep2 + * channel2 -- ep3 + * channel3 -- ep4 + * channel4 -- ep5 + */ + if ((usb_dma_int & DMA_CHANNEL1_INT) == DMA_CHANNEL1_INT) { + ep = &udc->ep[2]; + udc_writeb(USB_EP_INDEX, USB_EP2_INDEX); + udc_writeb(USB_CTRL_1_2, USB_TXCSR_MODE1); + udc_writel(USB_DMA_CTRL1, 0); + + l2_combuf_wait_dma_finish(ep->l2_buf_id); + + /* if the queue is not empty, get the head req */ + if (!list_empty(&ep->queue)) + req = list_entry(ep->queue.next, struct akudc_request, queue); + + if (req) { + /* + * judge whether the req is complete + * if it is, done the req and do next req transmission + * else do the current req transmission + */ + if (dma_tx == req->req.length - req->req.actual && !req->req.zero) + is_done = 1; + req->req.actual += dma_tx; + ep->done = is_done; + if (is_done) { + done(ep, req, 0); + if (!list_empty(&ep->queue)) { + dbg("do next queue"); + queue_work(ep_wqueue, &ep->work); + } + } else { + queue_work(ep_wqueue, &ep->work); + } + /* unmap the buffer */ + dma_unmap_single(NULL, phys_tx, dma_tx, DMA_TO_DEVICE); + dma_tx = 0; + + req->status = is_done; + } + else + dbg("something happend"); + } + + if ((usb_dma_int & DMA_CHANNEL2_INT) == DMA_CHANNEL2_INT) { + ep = &udc->ep[3]; + + udc_writeb(USB_EP_INDEX, USB_EP3_INDEX); + udc_writeb(USB_CTRL_2_2, 0); + udc_writel(USB_DMA_CTRL2, 0); + + l2_combuf_wait_dma_finish(ep->l2_buf_id); + + req = NULL; + is_done = 0; + /* if the queue is not empty, get the head req */ + if (!list_empty(&ep->queue)) + req = list_entry(ep->queue.next, struct akudc_request, queue); + + if (req) { + /* + * judge whether the req is complete + * if it is, done the req + */ + if (dma_rx == req->req.length - req->req.actual) + is_done = 1; + req->req.actual += dma_rx; + ep->done = is_done; + if (is_done) + done(ep, req, 0); + /* unmap the buffer */ + dma_unmap_single(NULL, phys_rx, dma_rx, DMA_FROM_DEVICE); + req->status = is_done; + dma_rx = 0; + + flag = 0; + } + else + dbg("something happend"); + } + + if ((usb_dma_int & DMA_CHANNEL3_INT) == DMA_CHANNEL3_INT) { + ep = &udc->ep[4]; + udc_writeb(USB_EP_INDEX, USB_EP4_INDEX); + udc_writeb(USB_CTRL_1_2, USB_TXCSR_MODE1); + udc_writel(USB_DMA_CTRL1, 0); + + l2_combuf_wait_dma_finish(ep->l2_buf_id); + + /* if the queue is not empty, get the head req */ + if (!list_empty(&ep->queue)) + req = list_entry(ep->queue.next, struct akudc_request, queue); + + if (req) { + /* + * judge whether the req is complete + * if it is, done the req and do next req transmission + * else do the current req transmission + */ + if (dma_tx == req->req.length - req->req.actual && !req->req.zero) + is_done = 1; + req->req.actual += dma_tx; + ep->done = is_done; + if (is_done) { + done(ep, req, 0); + if (!list_empty(&ep->queue)) { + dbg("do next queue"); + queue_work(ep_wqueue, &ep->work); + } + } else { + queue_work(ep_wqueue, &ep->work); + } + /* unmap the buffer */ + dma_unmap_single(NULL, phys_tx, dma_tx, DMA_TO_DEVICE); + dma_tx = 0; + + req->status = is_done; + } + else + dbg("something happend"); + } + + if ((usb_dma_int & DMA_CHANNEL4_INT) == DMA_CHANNEL4_INT) { + ep = &udc->ep[5]; + + udc_writeb(USB_EP_INDEX, USB_EP5_INDEX); + udc_writeb(USB_CTRL_2_2, 0); + udc_writel(USB_DMA_CTRL2, 0); + + l2_combuf_wait_dma_finish(ep->l2_buf_id); + + req = NULL; + is_done = 0; + /* if the queue is not empty, get the head req */ + if (!list_empty(&ep->queue)) + req = list_entry(ep->queue.next, struct akudc_request, queue); + + if (req) { + /* + * judge whether the req is complete + * if it is, done the req + */ + if (dma_rx == req->req.length - req->req.actual) + is_done = 1; + req->req.actual += dma_rx; + ep->done = is_done; + if (is_done) + done(ep, req, 0); + /* unmap the buffer */ + dma_unmap_single(NULL, phys_rx, dma_rx, DMA_FROM_DEVICE); + req->status = is_done; + dma_rx = 0; + + flag = 0; + } + else + dbg("something happend"); + } + + return IRQ_HANDLED; +} +#endif + +void akudc_enable(struct akudc_udc *udc) +{ + set_condition(&udc_clk); +} + +void akudc_disable(struct akudc_udc *udc) +{ + clear_condition(&udc_clk); +} + +/* + * usb_gadget_register_driver for upper function driver + */ +static int akudc_start(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) +{ + struct akudc_udc *udc = &controller; + int retval; + + printk("akudc start() '%s'\n", driver->driver.name); + + if (!bind || !driver->setup + || driver->max_speed < USB_SPEED_FULL) { + printk(KERN_ERR "Invalid driver: bind %p setup %p speed %d\n", + bind, driver->setup, driver->max_speed); + return -EINVAL; + } + +#if defined(MODULE) + if (!driver->unbind) { + printk(KERN_ERR "Invalid driver: no unbind method\n"); + return -EINVAL; + } +#endif + + udc->driver = driver; + udc->gadget.dev.driver = &driver->driver; + + /* + * bind the gadget to function driver + * give the gadget point to function driver + */ + retval = bind(&udc->gadget); + if (retval) { + dbg("driver->bind() returned %d\n", retval); + udc->driver = NULL; + udc->gadget.dev.driver = NULL; + return retval; + } + + /* init work for every endpoint */ + INIT_WORK(&udc->ep[1].work, ep1_work); + INIT_WORK(&udc->ep[2].work, ep2_work); + INIT_WORK(&udc->ep[3].work, ep3_work); + INIT_WORK(&udc->ep[4].work, ep4_work); + INIT_WORK(&udc->ep[5].work, ep5_work); + + disable_irq(udc->mcu_irq); + akudc_enable(udc); + enable_irq(udc->mcu_irq); + + printk("binding gadget driver '%s'\n", driver->driver.name); + return 0; +} + + +/* + * usb_gadget_unregister_driver for upper function driver + */ +static int akudc_stop(struct usb_gadget_driver *driver) +{ + struct akudc_udc *udc = &controller; + + printk("akudc_stop() '%s'\n", driver->driver.name); + + if (!driver || driver != udc->driver || !driver->unbind) + return -EINVAL; + + /* report disconnect */ + if(driver->disconnect) + driver->disconnect(&udc->gadget); + + /* unbind the gadget for function driver */ + driver->unbind(&udc->gadget); + udc->gadget.dev.driver = NULL; + //udc->gadget.dev.driver_data = NULL; + udc->driver = NULL; + + disable_irq(udc->mcu_irq); + + akudc_disable(udc); + + /*cacel work for every endpoint */ + cancel_work_sync(&udc->ep[1].work); + cancel_work_sync(&udc->ep[2].work); + cancel_work_sync(&udc->ep[3].work); + cancel_work_sync(&udc->ep[4].work); + cancel_work_sync(&udc->ep[5].work); + + flush_workqueue(ep_wqueue); + + enable_irq(udc->mcu_irq); + + dbg("unbound from '%s'\n", driver->driver.name); + udc_reinit(udc); + return 0; +} + +/* reinit == restore initial software state */ +static void udc_reinit(struct akudc_udc *udc) +{ + u32 i; + + /* init non-ep0 endpint list and ep0 list */ + INIT_LIST_HEAD(&udc->gadget.ep_list); + INIT_LIST_HEAD(&udc->gadget.ep0->ep_list); + udc->ep0state = EP0_IDLE; + + for (i = 0; i < ENDPOINTS_NUM; i++) { + struct akudc_ep *ep = &udc->ep[i]; + /* add every non-ep0 endpoint to gadget endpoint list */ + if (i != 0) + list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); + ep->desc = NULL; + ep->stopped = 0; + ep->ep.maxpacket = ep->maxpacket; + /* initialize one queue per endpoint */ + INIT_LIST_HEAD(&ep->queue); + } +} + +static int __init akudc_udc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct akudc_udc *udc = &controller; + struct resource *res; + int retval; + + if (pdev->num_resources < 2) { + dbg("invalid num_resources\n"); + return -ENODEV; + } + if ((pdev->resource[0].flags != IORESOURCE_MEM) + || (pdev->resource[1].flags != IORESOURCE_IRQ)) { + dbg("invalid resource type\n"); + return -ENODEV; + } + + /* get the mem resoure */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; + + if (!request_mem_region(res->start, resource_size(res), driver_name)) { + dbg("someone's using UDC memory\n"); + return -EBUSY; + } + + /* map the physical mem to virtual for register */ + udc->baseaddr = ioremap_nocache(res->start, resource_size(res)); + if (!udc->baseaddr) { + retval = -ENOMEM; + goto fail0a; + } + /* init software state */ + udc->gadget.dev.parent = dev; //is null, + + /* get interface and function clocks */ + udc->clk = clk_get(dev, "usb-slave"); + if (IS_ERR(udc->clk)) { + dbg("clocks missing\n"); + retval = -ENODEV; + /* NOTE: we "know" here that refcounts on these are NOPs */ + goto fail0b; + } + + akudc_conctrol_reset(udc); + udc_reinit(udc); + + retval = device_register(&udc->gadget.dev); + if (retval < 0) + goto fail0b; + + /* creat the work queue for every ep */ + ep_wqueue = create_workqueue("akudc"); + + /* request UDC and maybe VBUS irqs */ + udc->mcu_irq = platform_get_irq(pdev, 0); + /* register the udc irq handler */ + retval = request_irq(udc->mcu_irq, udc_irqhandler, + 0, driver_name, udc); + if (retval < 0) { + dbg("request irq %d failed\n", udc->mcu_irq); + goto fail1; + } + +#if 0 +#ifdef CONFIG_USB_AKUDC_PRODUCER + /* request DMA irqs */ + udc->dma_irq = platform_get_irq(pdev, 1); + retval = request_irq(udc->dma_irq, udc_dmahandler, + 0, driver_name, udc); + if (retval < 0) { + dbg("request irq %d failed\n", udc->dma_irq); + goto fail1; + } + + /* USB slave L2 buffer initialization */ + udc->ep[2].l2_buf_id = l2_alloc(ADDR_USB_EP2); + udc->ep[3].l2_buf_id = l2_alloc(ADDR_USB_EP3); + udc->ep[4].l2_buf_id = l2_alloc(ADDR_USB_EP4); + udc->ep[5].l2_buf_id = l2_alloc(ADDR_USB_EP5); +#endif +#endif + + /* set the udc pointer to pdev private pointer */ + platform_set_drvdata(pdev, udc); + + create_debugfs_files(udc); + + retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget); + if (retval) + goto fail1; + + return 0; + + free_irq(udc->mcu_irq, udc); +fail1: + device_unregister(&udc->gadget.dev); +fail0b: + iounmap(udc->baseaddr); +fail0a: + release_mem_region(res->start, resource_size(res)); + + return retval; +} + +static int __exit akudc_udc_remove(struct platform_device *pdev) +{ + struct akudc_udc *udc = platform_get_drvdata(pdev); + struct resource *res; + + if (udc->driver) + return -EBUSY; + + usb_del_gadget_udc(&udc->gadget); + + /* clear the udc_clk condition for udc */ + clear_condition(&udc_clk); + + create_debugfs_files(udc); + + free_irq(udc->mcu_irq, udc); + device_unregister(&udc->gadget.dev); + + destroy_workqueue(ep_wqueue); + + iounmap(udc->baseaddr); + +#if 0 +#ifdef CONFIG_USB_AKUDC_PRODUCER + free_irq(udc->dma_irq, udc); + + /* USB slave L2 buffer initialization */ + l2_free(ADDR_USB_EP2); + l2_free(ADDR_USB_EP3); + l2_free(ADDR_USB_EP4); + l2_free(ADDR_USB_EP5); +#endif +#endif + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + return 0; +} + +static void akudc_udc_shutdown(struct platform_device *dev) +{ +} + +#ifdef CONFIG_PM +static int akudc_udc_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + struct akudc_udc *udc = platform_get_drvdata(pdev); + + /*cacel work for every endpoint */ + cancel_work_sync(&udc->ep[1].work); + cancel_work_sync(&udc->ep[2].work); + cancel_work_sync(&udc->ep[3].work); + cancel_work_sync(&udc->ep[4].work); + cancel_work_sync(&udc->ep[5].work); + + flush_workqueue(ep_wqueue); + + /* enable usb vbus wakeup function before enter standby */ + usb_vbus_wakeup(true); + + /* clear the udc_clk condition for udc */ + clear_condition(&udc_clk); + + udc_disconnect(udc); + + return 0; +} + +static int akudc_udc_resume(struct platform_device *pdev) +{ + struct akudc_udc *udc = platform_get_drvdata(pdev); + + /* disable usb vbus wakeup function after wakeup */ + usb_vbus_wakeup(false); + + /* if we have the function driver, we set the udc_clk condition */ + if (udc->driver) + set_condition(&udc_clk); + + return 0; +} +#else +#define akudc_udc_suspend NULL +#define akudc_udc_resume NULL +#endif + +static struct platform_driver akudc_udc_driver = { + .remove = __exit_p(akudc_udc_remove), + .shutdown = akudc_udc_shutdown, + .suspend = akudc_udc_suspend, + .resume = akudc_udc_resume, + .driver = { + .name = (char *) driver_name, + .owner = THIS_MODULE, + }, +}; + +static int __init udc_init_module(void) +{ + printk("udc driver initialize, (c) 2013 Anyka\n"); + + return platform_driver_probe(&akudc_udc_driver, akudc_udc_probe); +} +module_init(udc_init_module); + +static void __exit udc_exit_module(void) +{ + platform_driver_unregister(&akudc_udc_driver); +} +module_exit(udc_exit_module); + +MODULE_DESCRIPTION("Anyka udc driver"); +MODULE_AUTHOR("Anyka"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform: usb-slave"); diff --git a/drivers/usb/gadget/plat-anyka/usbburn.c b/drivers/usb/gadget/plat-anyka/usbburn.c new file mode 100644 index 00000000..a105d9fd --- /dev/null +++ b/drivers/usb/gadget/plat-anyka/usbburn.c @@ -0,0 +1,356 @@ +/* + * akudc_usbburn -- driver for Anyka USB burntool; + * Features + * AUTHOR Zhang Jingyuan + * 10-09-27 16:28:08 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DEVICE_NAME "akudc_usbburn" + +#define AK_USBBURN_STALL _IO('U', 0xf0) // set stall feature command for producer +#define AK_USBBURN_STATUS _IO('U', 0xf1) // set CSW status command +#define AK_GET_CHANNEL_ID _IOR('U', 0xf2, __u32) //get channel id command + +#define CHANNEL_ID_ADDR (CONFIG_RAM_BASE + SZ_1M) /* the address for abtain channel id for burntool */ + +unsigned int sense_data; // the CSW status +struct semaphore sense_data_lock; // synchronization sense_data + +EXPORT_SYMBOL(sense_data); +EXPORT_SYMBOL(sense_data_lock); + +static int major = 0; +struct usbburn_dev { + struct cdev cdev; + wait_queue_head_t rq_rbuf, wq_rbuf; /* read and write queues for rbuf */ + wait_queue_head_t rq_wbuf, wq_wbuf; /* read and write queues for wbuf */ + void *rbuf; /* This buffer is used for user reading */ + size_t rlen; /* The length of rbuf */ + void *wbuf; /* This buffer is used for user writing */ + size_t wlen; /* The length of wbuf */ + struct semaphore r_sem, w_sem; /* mutual exclusion semaphore for rbuf and wbuf; */ + int r_stall; /* The flag of stop of rbuf */ + int w_stall; /* The flag of sotp of wbuf */ +} *b_dev; +struct class *usbburn_class; + +static int akudc_usbburn_open(struct inode *inode, struct file *file) +{ + printk("akudc_usbburn device is open\n"); + + return 0; +} + +static int akudc_usbburn_close(struct inode *inode, struct file *file) +{ + printk("akudc_usbburn device is closed\n"); + + return 0; +} + +/* the read function for the producer */ +ssize_t akudc_usbburn_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + /* + * count is used to record the number of data to read; + * count1 is used to record the total number of data been read, + * readn is used to record the number of data been read each time. + */ + size_t count1 = 0, readn; + + while (count != 0) /* when the all data has been read, return. */ + { + down(&b_dev->r_sem); /* before access the rbuf member of b_dev, down the r_sem */ + while (b_dev->rbuf == NULL) { /* when there is no data to read, sleep to wait. */ + up(&b_dev->r_sem); /* up the r_sem */ + wait_event(b_dev->rq_rbuf, b_dev->rbuf != NULL); /* to sleep until some data is coming. */ + down(&b_dev->r_sem); + } + readn = min(count, b_dev->rlen); + + /* copy the data to user space */ + if (copy_to_user(buf + count1, b_dev->rbuf, readn)) { + up(&b_dev->r_sem); + return -EFAULT; + } + count1 += readn; + count -= readn; + + /* the number of data which has been read is less than b_dev->rlen */ + if (readn < b_dev->rlen) { + b_dev->rbuf += readn; + b_dev->rlen -= readn; + up(&b_dev->r_sem); + } else { /* wake up the wq_rbuf if the all data in rbuf has been read. */ + b_dev->rbuf = NULL; + up(&b_dev->r_sem); + wake_up(&b_dev->wq_rbuf); + + /* if the r_stall is set, return */ + if (b_dev->r_stall == 1) { + b_dev->r_stall = 0; + break; + } + } + } + + return count1; +} + +/* the write function for the usb file_storage */ +int usbburn_write(void *buf, size_t count) +{ + down(&b_dev->r_sem); /* down the r_sem before access the rbuf member of b_dev. */ + b_dev->rbuf = buf; + b_dev->rlen = count; + + /* sleep until the all data in rbuf has been read. */ + while (b_dev->rbuf != NULL) { + up(&b_dev->r_sem); + wake_up(&b_dev->rq_rbuf); + wait_event(b_dev->wq_rbuf, b_dev->rbuf == NULL); + down(&b_dev->r_sem); + } + up(&b_dev->r_sem); + + return count; +} +EXPORT_SYMBOL(usbburn_write); + +/* the write function for the producer */ +ssize_t akudc_usbburn_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + /* + * count is used to record the number of data to write; + * count1 is used to record the total number of data been written, + * written is used to record the number of data been written each time. + */ + size_t count1 = 0, written; + + while (count != 0) { + down(&b_dev->w_sem); /* before access the wbuf member of b_dev, down the w_sem. */ + while (b_dev->wbuf == NULL) { + up(&b_dev->w_sem); /* up the w_sem. */ + wait_event(b_dev->wq_wbuf, b_dev->wbuf != NULL); /* sleep until wbuf can be written. */ + down(&b_dev->w_sem); + } + written = min(count, b_dev->wlen); + + /* copy the user space buffer to the kernel. */ + if (copy_from_user(b_dev->wbuf, buf + count1, written)) { + up(&b_dev->w_sem); + return -EFAULT; + } + count1 += written; + count -= written; + + /* if written if less than b_dev->wlen. */ + if (written < b_dev->wlen) { + b_dev->wbuf += written; + b_dev->wlen -= written; + + /* if w_stall is set, wake up the rq_buf and return. */ + if (b_dev->w_stall == 1) { + b_dev->wbuf = NULL; + b_dev->wlen = written; + b_dev->w_stall = 0; + up(&b_dev->w_sem); + wake_up(&b_dev->rq_wbuf); + break; + } + up(&b_dev->w_sem); + } else { /* wake up rq_wbuf if the wbuf is full written. */ + b_dev->wbuf = NULL; + up(&b_dev->w_sem); + wake_up(&b_dev->rq_wbuf); + } + } + + return count1; +} + +/* the read function for the usb file_storage */ +int usbburn_read(void *buf, size_t count) +{ + down(&b_dev->w_sem); + b_dev->wbuf = buf; + b_dev->wlen = count; + + /* sleep until wbuf has been written. */ + while (b_dev->wbuf != NULL) { + up(&b_dev->w_sem); + wake_up(&b_dev->wq_wbuf); + wait_event(b_dev->rq_wbuf, b_dev->wbuf == NULL); + down(&b_dev->w_sem); + } + count = b_dev->wlen; + up(&b_dev->w_sem); + + return count; +} +EXPORT_SYMBOL(usbburn_read); + +long akudc_usbburn_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + unsigned long *channel_id; + + switch (cmd) { + case AK_USBBURN_STALL: + down(&b_dev->w_sem); + b_dev->w_stall = 1; /*set the w_stall flag for wbuf. */ + up(&b_dev->w_sem); + break; + case AK_USBBURN_STATUS: + sense_data = arg; + up(&sense_data_lock); + break; + case AK_GET_CHANNEL_ID: + channel_id = ioremap(CHANNEL_ID_ADDR, sizeof (*channel_id)); + if (channel_id == NULL) { + printk("ioremap error\n"); + return -1; + } + if (copy_to_user((void __user *)arg, channel_id, sizeof (*channel_id))) { + printk("copy id to user space failed!\n"); + iounmap(channel_id); + return -1; + } + iounmap(channel_id); + break; + default: + return -1; + } + + return 0; +} + +void usbburn_ioctl(void) +{ + down(&b_dev->r_sem); + b_dev->r_stall = 1; /*set the r_stall flag for rbuf. */ + up(&b_dev->r_sem); +} +EXPORT_SYMBOL(usbburn_ioctl); + +/* The file operation for the usbburn device.*/ +static struct file_operations akudc_usbburn_fops = { + .owner = THIS_MODULE, + .read = akudc_usbburn_read, + .write = akudc_usbburn_write, + .unlocked_ioctl = akudc_usbburn_ioctl, + .open = akudc_usbburn_open, + .release = akudc_usbburn_close, +}; + +int __init akudc_usbburn_init(void) +{ + int ret = 0; + struct device *device; + dev_t dev = MKDEV(major, 0); + + /* allocate the burn device. */ + b_dev = kmalloc(sizeof(*b_dev), GFP_KERNEL); + if (unlikely (!b_dev)) { + ret = -ENOMEM; + printk("error, out of memory\n"); + goto out1; + } + memset(b_dev, 0, sizeof(*b_dev)); + + /* Register device major, and accept a dynamic number. */ + if (major) + ret = register_chrdev_region(dev, 1, DEVICE_NAME); + else { + ret = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME); + major = MAJOR(dev); + } + if (ret < 0) { + printk("register chrdev major and minor number failed\n"); + goto out1; + } + + cdev_init(&b_dev->cdev, &akudc_usbburn_fops); + b_dev->cdev.owner = THIS_MODULE; + ret = cdev_add(&b_dev->cdev, dev, 1); + /* Fail gracefully if need be */ + if (ret) { + printk("Error %d adding akudc_usbburn_dev", ret); + goto out1; + } + + /* create the usbburn char device's class */ + usbburn_class = class_create(THIS_MODULE, "usbburn"); + if (IS_ERR(usbburn_class)) { + ret = PTR_ERR(usbburn_class); + printk("create usbburn class failed\n"); + goto out2; + } + + /* create the usbburn char device node in /dev/akudc_usbburn */ + device = device_create(usbburn_class, NULL, dev, NULL,"akudc_usbburn"); + if (IS_ERR(device)) { + printk("akudc_usbburn chrdev create failed! %x\n", ret); + ret = PTR_ERR(device); + goto out2; + } + else + printk("akudc_usbburn chrdev create success!\n"); + + /* init the waitqueue head for the rbuf and wbuf */ + init_waitqueue_head(&b_dev->rq_rbuf); + init_waitqueue_head(&b_dev->wq_rbuf); + init_waitqueue_head(&b_dev->rq_wbuf); + init_waitqueue_head(&b_dev->wq_wbuf); + + /* init the r_sem and w_sem with 1 */ + sema_init(&b_dev->r_sem, 1); + sema_init(&b_dev->w_sem, 1); + + /* init the sense_data_lock with 0 */ + memset(&sense_data_lock, 0, sizeof(sense_data_lock)); + sema_init(&sense_data_lock, 0); + + return ret; +out2: + cdev_del(&b_dev->cdev); +out1: + kfree(b_dev); + + return ret; +} + +void __exit akudc_usbburn_exit(void) +{ + /* delete the device node */ + device_destroy(usbburn_class, MKDEV(major, 0)); + /* delete the class for usbburn */ + class_destroy(usbburn_class); + cdev_del(&b_dev->cdev); + kfree(b_dev); + unregister_chrdev_region(MKDEV(major, 0), 1); +} + +module_init(akudc_usbburn_init); +module_exit(akudc_usbburn_exit); + +MODULE_DESCRIPTION("AKudc usbburn driver"); +MODULE_AUTHOR("Anyka"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index 73a934a1..0cb21218 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -1146,11 +1146,15 @@ static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS]; #endif /* CONFIG_USB_GADGET_DEBUG_FILES */ +static bool rndis_initialized; int rndis_init(void) { u8 i; + if (rndis_initialized) + return 0; + for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { #ifdef CONFIG_USB_GADGET_DEBUG_FILES char name [20]; @@ -1177,6 +1181,7 @@ int rndis_init(void) INIT_LIST_HEAD(&(rndis_per_dev_params[i].resp_queue)); } + rndis_initialized = true; return 0; } @@ -1185,7 +1190,13 @@ void rndis_exit(void) #ifdef CONFIG_USB_GADGET_DEBUG_FILES u8 i; char name[20]; +#endif + if (!rndis_initialized) + return; + rndis_initialized = false; + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { sprintf(name, NAME_TEMPLATE, i); remove_proc_entry(name, NULL); diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index 9a2a1ae9..8f0400b9 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -763,6 +763,26 @@ static struct device_type gadget_type = { * Returns negative errno, or zero on success */ int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]) +{ + return gether_setup_name(g, ethaddr, "eth"); +} + +/** + * gether_setup_name - initialize one ethernet-over-usb link + * @g: gadget to associated with these links + * @ethaddr: NULL, or a buffer in which the ethernet address of the + * host side of the link is recorded + * @netname: name for network device (for example, "usb") + * Context: may sleep + * + * This sets up the single network link that may be exported by a + * gadget driver using this framework. The link layer addresses are + * set up using module parameters. + * + * Returns negative errno, or zero on success + */ +int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], + const char *netname) { struct eth_dev *dev; struct net_device *net; @@ -786,7 +806,7 @@ int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]) /* network device setup */ dev->net = net; - strcpy(net->name, "usb%d"); + snprintf(net->name, sizeof(net->name), "%s%%d", netname); if (get_ether_addr(dev_addr, net->dev_addr)) dev_warn(&g->dev, diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h index 8012357e..37431f52 100644 --- a/drivers/usb/gadget/u_ether.h +++ b/drivers/usb/gadget/u_ether.h @@ -73,6 +73,9 @@ struct gether { /* netdev setup/teardown as directed by the gadget driver */ int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]); void gether_cleanup(void); +/* variant of gether_setup that allows customizing network device name */ +int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], + const char *netname); /* connect/disconnect is handled by individual functions */ struct net_device *gether_connect(struct gether *); @@ -100,6 +103,8 @@ int eem_bind_config(struct usb_configuration *c); #ifdef USB_ETH_RNDIS int rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); +int rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], + u32 vendorID, const char *manufacturer); #else @@ -109,6 +114,13 @@ rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) return 0; } +static inline int +rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], + u32 vendorID, const char *manufacturer) +{ + return 0; +} + #endif #endif /* __U_ETHER_H */ diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index 6c23938d..380a87f6 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -1025,7 +1025,7 @@ static const struct tty_operations gs_tty_ops = { static struct tty_driver *gs_tty_driver; -static int __init +static int gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding) { struct gs_port *port; @@ -1071,7 +1071,7 @@ gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding) * * Returns negative errno or zero. */ -int __init gserial_setup(struct usb_gadget *g, unsigned count) +int gserial_setup(struct usb_gadget *g, unsigned count) { unsigned i; struct usb_cdc_line_coding coding; diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index f788eb86..056a1ccf 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -638,3 +638,6 @@ config USB_OCTEON_OHCI config USB_OCTEON2_COMMON bool default y if USB_OCTEON_EHCI || USB_OCTEON_OHCI + +source "drivers/usb/host/plat-anyka/Kconfig" + diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 0982bcc1..33a6dc12 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -41,3 +41,4 @@ obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd.o obj-$(CONFIG_USB_FSL_MPH_DR_OF) += fsl-mph-dr-of.o obj-$(CONFIG_USB_OCTEON2_COMMON) += octeon2-common.o obj-$(CONFIG_MIPS_ALCHEMY) += alchemy-common.o +obj-$(CONFIG_USB_ANYKA_HCD) += plat-anyka/ diff --git a/drivers/usb/host/plat-anyka/Kconfig b/drivers/usb/host/plat-anyka/Kconfig new file mode 100644 index 00000000..f58dc4ee --- /dev/null +++ b/drivers/usb/host/plat-anyka/Kconfig @@ -0,0 +1,26 @@ +config USB_ANYKA_HCD + bool "Anyka on-chip HCD support" + depends on USB && ARCH_AK39 + help + Enable support for the ANYKA on-chip HCD, It is support full speed and high speed + +config USB_AKOTG_HS_HCD + tristate "USB(otg) High-Speed HCD support" + depends on USB_ANYKA_HCD + help + The ANYKA chips inner USB otg high speed host controllers. Enable this + option if your board has this chip. If unsure, say N. + + This driver does not support isochronous transfers. + + To compile this driver as a module, choose M here: the + module will be called otg-hs. + +config USB_AKOTG_DMA + bool "USB(otg) support for DMA transfer" + depends on USB_AKOTG_HS_HCD + help + Say 'Y' to turn on dma support for anyka otg host. + + If no need, say N. + diff --git a/drivers/usb/host/plat-anyka/Makefile b/drivers/usb/host/plat-anyka/Makefile new file mode 100644 index 00000000..f749f649 --- /dev/null +++ b/drivers/usb/host/plat-anyka/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for USB Host Controller Drivers +# + +obj-$(CONFIG_USB_AKOTG_HS_HCD) += otg-hs.o + otg-hs-objs := otg-hshcd.o usb-hc.o + diff --git a/drivers/usb/host/plat-anyka/otg-hshcd.c b/drivers/usb/host/plat-anyka/otg-hshcd.c new file mode 100644 index 00000000..7dd1f6d3 --- /dev/null +++ b/drivers/usb/host/plat-anyka/otg-hshcd.c @@ -0,0 +1,358 @@ +/* + * Anyka OTG HS HCD (Full-Speed Host Controller Driver) for USB. + * + * Derived from the SL811 HCD, rewritten for AKOTG HS HCD. + * Copyright (C) 2010 ANYKA LTD. + * + * Periodic scheduling is based on Roman's OHCI code + * Copyright (C) 1999 Roman Weissgaerber + * + * The AK OTG HS Host controller handles host side USB. For Documentation, + * refer to chapter 22 USB Controllers of Anyka chip Mobile Multimedia Application + * Processor Programmer's Guide. + * + */ + +/* + * Status: Enumeration of USB Optical Mouse, USB Keyboard, USB Flash Disk, Ralink 2070/3070 USB WiFi OK. + * Pass basic test with USB Optical Mouse/USB Keyboard/USB Flash Disk. + * Ralink 2070/3070 USB WiFI Scanning/WEP basic test OK. Full Functions TBD. + * + * TODO: + * - Use up to 6 active queues of FS HC(for now only 2 queues: EP0 & EPX(1-6)) + * - USB Suspend/Resume support + * - Use urb->iso_frame_desc[] with ISO transfers + * - Optimize USB FIFO data transfer/receive(4B->2B->1B) + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +static char *host_sw = "onboard"; +module_param(host_sw, charp, S_IRUGO); + +static const char hcd_name[] = "usb-host"; +extern struct akotghc_epfifo_mapping akotg_epfifo_mapping;; +extern struct workqueue_struct *g_otghc_wq;; + + +static struct hc_driver akhs_otg_driver = { + .description = hcd_name, + .product_desc = "Anyka usb host controller", + .hcd_priv_size = sizeof(struct akotg_usbhc), + + /* + * generic hardware linkage + */ + .irq = akotg_usbhc_irq, + .flags = HCD_USB2 | HCD_MEMORY, + + /* Basic lifecycle operations */ + .start = akotg_usbhc_start, + .reset = akotg_usbhc_reset, + .stop = akotg_usbhc_stop, + + /* + * managing i/o requests and associated device resources + */ + + .urb_dequeue = akotg_usbhc_urb_dequeue, + .urb_enqueue = akotg_usbhc_urb_enqueue, + .endpoint_reset = akotg_usbhc_endpoint_reset, + .endpoint_disable = akotg_usbhc_endpoint_disable, + + /* + * periodic schedule support + */ + .get_frame_number = akotg_usbhc_get_frame, + + /* + * root hub support + */ + .hub_status_data = akotg_usbhc_hub_status_data, + .hub_control = akotg_usbhc_hub_control, + #ifdef CONFIG_PM + .bus_suspend = akotg_usbhc_bus_suspend, + .bus_resume = akotg_usbhc_bus_resume, + #else + .bus_suspend = NULL, + .bus_resume = NULL, + #endif +}; + +/*-------------------------------------------------------------------------*/ + +static void usb_poweron_for_device(struct akotghc_usb_platform_data *pdata) +{ + /* power on for usb device */ + if(pdata->gpio_pwr_on.pin >= 0) + pdata->gpio_init(&pdata->gpio_pwr_on); + + /* ust otg host port switch */ + if((pdata->switch_onboard.pin >= 0)&&(pdata->switch_extport.pin >= 0) + && (pdata->switch_onboard.pin == pdata->switch_extport.pin)) { + if (!strcmp(host_sw, "extport")) + pdata->gpio_init(&pdata->switch_extport); + else + pdata->gpio_init(&pdata->switch_onboard); + + } +} + +static void usb_poweroff_for_device(struct akotghc_usb_platform_data *pdata) +{ + /* ust otg host port switch */ + if((pdata->switch_onboard.pin >= 0)&&(pdata->switch_extport.pin >= 0) + && (pdata->switch_onboard.pin == pdata->switch_extport.pin)) { + if (!strcmp(host_sw, "onboard")) + pdata->gpio_init(&pdata->switch_extport); + else + pdata->gpio_init(&pdata->switch_onboard); + } + + /* power off for usb device */ + if(pdata->gpio_pwr_off.pin >= 0) + pdata->gpio_init(&pdata->gpio_pwr_off); +} + +static int __devexit +akotg_hc_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct akotghc_usb_platform_data *pdata = NULL; + +#ifdef CONFIG_USB_AKOTG_DMA + struct akotg_usbhc *akotghc; +#endif + if(g_otghc_wq) + { + flush_workqueue(g_otghc_wq); + destroy_workqueue(g_otghc_wq); + g_otghc_wq = NULL; + } + + pdata = pdev->dev.platform_data; + if (pdata != NULL) + /* hwinit power off */ + usb_poweroff_for_device(pdata); + + usb_remove_hcd(hcd); + usb_put_hcd(hcd); + +#ifdef CONFIG_USB_AKOTG_DMA + + //free dma irq + akotghc = hcd_to_akotg_usbhc(hcd); + if(akotghc->dma_irq > 0) + { + free_irq(akotghc->dma_irq, hcd); + akotghc->dma_irq = -1; + } + +#endif + + return 0; +} + +static int __devinit +akotg_hc_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + struct akotg_usbhc *akotghc; + struct resource *res; + struct akotghc_usb_platform_data *pdata = NULL; + int irq; + int retval = 0; + unsigned long irqflags; + int i, j; + + pdata = pdev->dev.platform_data; + if (pdata == NULL) + return -ENODEV; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if(!res) + return -ENODEV; + + /* usb_create_hcd requires dma_mask != NULL */ + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + if (!pdev->dev.dma_mask){ + pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; + } + + /* allocate and initialize hcd */ + hcd = usb_create_hcd(&akhs_otg_driver, &pdev->dev, akhs_otg_driver.product_desc); + if (!hcd) { + retval = -ENOMEM; + goto err_nomem; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + akotghc = hcd_to_akotg_usbhc(hcd); + + usb_poweron_for_device(pdata); + + akotghc->clk = clk_get(&pdev->dev, "usb-host"); + if (IS_ERR(akotghc->clk)) { + dbg("usb otg hs clocks missing\n"); + akotghc->clk = NULL; + goto err_nomem; + } + + /* basic sanity checks first. board-specific init logic should + * have initialized these three resources and probably board + * specific platform_data. we don't probe for IRQs, and do only + * minimal sanity checking. + */ + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(&pdev->dev, "Found HC with no IRQ. Check %s setup!\n", + dev_name(&pdev->dev)); + retval = -ENODEV; + goto err_nodev; + } + akotghc->mcu_irq = irq; + + spin_lock_init(&akotghc->lock); + INIT_LIST_HEAD(&akotghc->async_ep0); + for(i=0; iasync_epx[i]); + + akotghc->active_ep0 = NULL; + for(j=0; jactive_epx[j] = NULL; + + init_timer(&akotghc->timer); + akotghc->timer.function = akotg_usbhc_timer; + akotghc->timer.data = (unsigned long) akotghc; + + hcd->speed = HCD_USB2; + //hcd->power_budget = 0; //or get from platfrom data + + /* The chip's IRQ is level triggered, active high. A requirement + * for platform device setup is to cope with things like signal + * inverters (e.g. CF is active low) or working only with edge + * triggers (e.g. most ARM CPUs). Initial driver stress testing + * was on a system with single edge triggering, so most sorts of + * triggering arrangement should work. + * + * Use resource IRQ flags if set by platform device setup. + */ + irqflags = IRQF_SHARED; + retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | irqflags); + if (retval != 0) + goto err_addhcd; + + init_epfifo_mapping(&akotg_epfifo_mapping); + +#ifdef CONFIG_USB_AKOTG_DMA + + akotg_dma_init(akotghc); + + //request dma irq + irq = platform_get_irq(pdev, 1); + retval = request_irq(irq, akotg_dma_irq, IRQF_DISABLED, "otgdma", hcd); + if (retval < 0){ + akotghc->dma_irq = -1; + printk("request irq %d failed\n", irq); + } + else { + akotghc->dma_irq = irq; + } + +#endif + + printk("Usb otg-hs controller driver initialized\n"); + return retval; + + err_addhcd: + usb_put_hcd(hcd); + err_nomem: + err_nodev: + + return retval; +} + +#ifdef CONFIG_PM + +/* for this device there's no useful distinction between the controller + * and its root hub, except that the root hub only gets direct PM calls + * when CONFIG_USB_SUSPEND is enabled. + */ +static int +akotg_hc_suspend(struct platform_device *pdev, pm_message_t state) +{ + int retval = 0; + + switch (state.event) { + case PM_EVENT_FREEZE: + break; + case PM_EVENT_SUSPEND: + case PM_EVENT_HIBERNATE: + case PM_EVENT_PRETHAW: /* explicitly discard hw state */ + break; + } + return retval; +} + +static int +akotg_hc_resume(struct platform_device *dev) +{ + return 0; +} + +#else +#define akotg_hc_suspend NULL +#define akotg_hc_resume NULL +#endif + + +struct platform_driver akotg_hc_driver = { + .probe = akotg_hc_probe, + .remove = __devexit_p(akotg_hc_remove), + + .suspend = akotg_hc_suspend, + .resume = akotg_hc_resume, + .driver = { + .name = (char *) hcd_name, + .owner = THIS_MODULE, + }, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init akotg_hc_init(void) +{ + + if (usb_disabled()) + return -ENODEV; + + return platform_driver_register(&akotg_hc_driver); +} +module_init(akotg_hc_init); + +static void __exit akotg_hc_cleanup(void) +{ + platform_driver_unregister(&akotg_hc_driver); + if(g_otghc_wq) + { + flush_workqueue(g_otghc_wq); + destroy_workqueue(g_otghc_wq); + g_otghc_wq = NULL; + } +} +module_exit(akotg_hc_cleanup); +MODULE_DESCRIPTION("Anyka Host Controller Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ak_hcd"); diff --git a/drivers/usb/host/plat-anyka/usb-hc.c b/drivers/usb/host/plat-anyka/usb-hc.c new file mode 100644 index 00000000..45208c2a --- /dev/null +++ b/drivers/usb/host/plat-anyka/usb-hc.c @@ -0,0 +1,1909 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +//#define AKOTG_HS_DEBUG +//#define AKOTG_HS_VERBOSE_DEBUG + +#ifdef AKOTG_HS_DEBUG +#define HDBG(stuff...) printk("USBHS: " stuff) +#else +#define HDBG(fmt, args...) do{}while(0) +#endif + +#ifdef AKOTG_HS_VERBOSE_DEBUG +#define VDBG HDBG +#else +#define VDBG(fmt, args...) do{}while(0) +#endif + + +static int period_epfifo; +struct workqueue_struct *g_otghc_wq; +static struct delayed_work g_otg_rest; +static u8 ep_fifos[] = { USB_FIFO_EP0, USB_FIFO_EP1, USB_FIFO_EP2, USB_FIFO_EP3, USB_FIFO_EP4}; +struct akotghc_epfifo_mapping akotg_epfifo_mapping; +#ifdef AKOTG_HS_DEBUG +static char *trans_desc[4] = {"iso", "intterrup", "conroller", "bulk"}; +static char *xfer_name[4] = {"control", "iso", "bulk", "int"}; +#endif + +static inline int get_ep_type(int pipe) +{ + int eptype = 0; + + switch(usb_pipetype(pipe)) { + case PIPE_ISOCHRONOUS: + eptype = 1; + break; + case PIPE_BULK: + eptype = 2; + break; + case PIPE_INTERRUPT: + eptype = 3; + break; + } + return eptype; +} + +void port_power(struct akotg_usbhc *akotghc, int is_on) +{ + /*Anyka usb host is self power currently.*/ + struct usb_hcd *hcd = akotg_usbhc_to_hcd(akotghc); + + /* hub is inactive unless the port is powered */ + if (is_on) { + if (akotghc->port_status & (1 << USB_PORT_FEAT_POWER)) + return; + + akotghc->port_status = (1 << USB_PORT_FEAT_POWER); + } else { + akotghc->port_status = 0; + hcd->state = HC_STATE_HALT; + } + + /* reset as thoroughly as we can */ + ak_soft_reset(AK_SRESET_USBHS); +} + +/*-------------------------------------------------------------------------*/ + +/* This is a PIO-only HCD. Queueing appends URBs to the endpoint's queue, + * and may start I/O. Endpoint queues are scanned during completion irq + * handlers (one per packet: ACK, NAK, faults, etc) and urb cancellation. + * + * Using an external DMA engine to copy a packet at a time could work, + * though setup/teardown costs may be too big to make it worthwhile. + */ + +/* SETUP starts a new control request. Devices are not allowed to + * STALL or NAK these; they must cancel any pending control requests. + */ +static void setup_packet( + struct akotg_usbhc *akotghc, + struct akotghc_ep *ep, + struct urb *urb +) +{ + int i; + unsigned int fifo_val; + u8 len; + unsigned char *buf = urb->setup_packet; + + HDBG("Packet: Setup Packet (EP %d)\n", ep->epnum); + + len = sizeof(struct usb_ctrlrequest); + + hc_index_writeb(0, 0, USB_REG_TXCSR1); + hc_writeb(usb_pipedevice(urb->pipe), USB_REG_FADDR); + for (i = 0; i < len; i += 4) { + fifo_val = (*(buf+i) | ( *(buf+i+1)<<8 ) + | ( *(buf+i+2)<<16 ) | ( *(buf+i+3)<<24 )); + hc_writel(fifo_val, USB_FIFO_EP0); + } + hc_index_writeb(0, USB_TXCSR1_FLUSHFIFO |USB_TXCSR1_FIFONOTEMPTY, USB_REG_TXCSR1); + + ep->length = 0; +} + +/* STATUS finishes control requests, often after IN or OUT data packets */ +static void status_packet( + struct akotg_usbhc *akotghc, + struct akotghc_ep *ep, + struct urb *urb +) +{ + int is_in; + + HDBG("Packet: Status Packet (EP %d)\n", ep->epnum); + + is_in = urb->transfer_buffer_length && usb_pipein(urb->pipe); + //if (!epnum_to_epfifo(&akotg_epfifo_mapping, ep->epnum, !usb_pipein(urb->pipe), &epfifo)) + // BUG(); /* Impossible, USB Device Endpoint *MUST* have been mapped to EPFIFO */ + + hc_writeb(usb_pipedevice(urb->pipe), USB_REG_FADDR); + + if (is_in) { + /* send a state packet when IN transaction is finished */ + hc_index_writeb(ep->epfifo, 0x42, USB_REG_TXCSR1); + } else { + /* request a state packet when OUT transaction is finished */ + hc_index_writeb(ep->epfifo, 0x60, USB_REG_TXCSR1); + } + + ep->length = 0; +} + +/* IN packets can be used with any type of endpoint. here we just + * start the transfer, data from the peripheral may arrive later. + * urb->iso_frame_desc is currently ignored here... + */ +static void in_packet( + struct akotg_usbhc *akotghc, + struct akotghc_ep *ep, + struct urb *urb +) +{ + u16 len; + u32 remain; + + bool bDMA = false; + + HDBG("Packet: IN Packet (EP %d), Length=%d,ep->maxpacket=%d\n", + ep->epnum, urb->transfer_buffer_length - urb->actual_length, ep->maxpacket); + + /* avoid losing data on overflow */ + len = ep->maxpacket; + remain = urb->transfer_buffer_length - urb->actual_length; + + ep->length = min_t(u32, len, remain); + + hc_writeb(usb_pipedevice(urb->pipe), USB_REG_FADDR); + if (ep->epnum == 0) { + hc_index_writeb(0, USB_TXCSR1_H_RXSTALL, USB_REG_TXCSR1); + } else { + int eptype = 0; + //if (!epnum_to_epfifo(&akotg_epfifo_mapping, ep->epnum, 0, &epfifo)) + // BUG(); /* Impossible, USB Device Endpoint *MUST* have been mapped to EPFIFO */ + + eptype = get_ep_type(urb->pipe); + + /** + dma condition: + 1. high speed and ep size >= 512 + 2. data leng >= 512 + 3. l2 buffer is alloced for epfifo + */ +#if 0//#ifdef CONFIG_USB_AKOTG_DMA + if((len == 512) && (remain >= len)) + { + struct akotg_dma *usbdma; + u8 regvalue; + u32 pktnum = remain / 512; + u32 count = remain - remain%512; + unsigned char *buf; + dma_addr_t phyaddr; + u8 l2buffer; + + if(count < remain){ + count += 512; + } + + do + { + //map dma buffer + buf = urb->transfer_buffer + urb->actual_length; + phyaddr = dma_map_single(NULL, buf, count, DMA_FROM_DEVICE); + if (phyaddr == 0) { + printk(KERN_ERR"tx dma_map_single error!\n"); + break; + } + + //alloc dma channel + usbdma = akotg_dma_alloc(akotghc, ep->epfifo); + if(!usbdma){ + //printk(KERN_ERR"dma alloc fail for in_packet\n"); + dma_unmap_single(NULL, phyaddr, count, DMA_FROM_DEVICE); + break; + } + + l2buffer = usbdma->l2buffer; + + //config rx reg + regvalue = hc_index_readb(ep->epfifo, USB_REG_RXCSR2); + regvalue |= (USB_RXCSR2_AUTOCLEAR | USB_RXCSR2_AUTOREQ | USB_RXCSR2_DMAENAB | USB_RXCSR2_DMAMODE); + hc_index_writeb(ep->epfifo, regvalue, USB_REG_RXCSR2); + + hc_writew(pktnum, USB_REG_REQPKTCNT(ep->epfifo)); + + //config l2 + l2_clr_status(l2buffer); + l2_combuf_dma(phyaddr, l2buffer, count, BUF2MEM, AK_FALSE); + + //config dma + akotg_dma_set_struct(usbdma, USB_DIRECTION_RX, ep->epfifo, l2buffer, count, phyaddr); + akotg_dma_config(usbdma); + + ep->length = count; + + bDMA = true; + } + while(0); + + } +#endif + ep->bdma = bDMA; + + hc_index_writeb(ep->epfifo, USB_RXCSR1_H_REQPKT, USB_REG_RXCSR1); + } +} + +/* OUT packets can be used with any type of endpoint. + * urb->iso_frame_desc is currently ignored here... + */ +static void out_packet( + struct akotg_usbhc *akotghc, + struct akotghc_ep *ep, + struct urb *urb +) +{ + int i; + unsigned char *buf; + u16 len; + u32 remain; + + bool bDMA = false; + + HDBG("Packet: OUT Packet (EP %d), Length=%d\n", ep->epnum, urb->transfer_buffer_length - urb->actual_length); + + buf = (unsigned char *)(urb->transfer_buffer + urb->actual_length); + prefetch(buf); + + remain = urb->transfer_buffer_length - urb->actual_length; + len = min_t(u32, ep->maxpacket, remain); + + //if (!epnum_to_epfifo(&akotg_epfifo_mapping, ep->epnum, 1, &epfifo)) + // BUG(); /* Impossible, USB Device Endpoint *MUST* have been mapped to EPFIFO */ + + /** + dma condition: + 1. high speed and ep size >= 512 + 2. data leng >= 512 + 3. l2 buffer is alloced for epfifo + */ +#ifdef CONFIG_USB_AKOTG_DMA + if((len == 512) && (remain >= len)) + { + struct akotg_dma *usbdma; + u8 regvalue; + u32 count = remain - remain%512; + unsigned char *buf; + dma_addr_t phyaddr; + u8 l2buffer; + + do + { + //map dma buffer + buf = urb->transfer_buffer + urb->actual_length; + //phyaddr = dma_map_single(NULL, buf, count, DMA_TO_DEVICE); + phyaddr = urb->transfer_dma + urb->actual_length; + if (phyaddr == 0) { + printk(KERN_ERR"rx dma_map_single error!\n"); + break; + } + + //alloc dma channel + usbdma = akotg_dma_alloc(akotghc, ep->epfifo); + if(!usbdma){ + //printk(KERN_ERR"dma alloc fail for out_packet\n"); + dma_unmap_single(NULL, phyaddr, count, DMA_TO_DEVICE); + break; + } + + l2buffer = usbdma->l2buffer; + + + //config tx reg + regvalue = hc_index_readb(ep->epfifo, USB_REG_TXCSR2); + regvalue |= (USB_TXCSR2_DMAMODE1|USB_TXCSR2_DMAENAB|USB_TXCSR2_MODE|USB_TXCSR2_AUTOSET); + hc_index_writeb(ep->epfifo, regvalue, USB_REG_TXCSR2); + + //config l2 + l2_clr_status(l2buffer); + l2_combuf_dma(phyaddr, l2buffer, count, MEM2BUF, AK_FALSE); + + //config dma + akotg_dma_set_struct(usbdma, USB_DIRECTION_TX, ep->epfifo, l2buffer, count, phyaddr); + akotg_dma_config(usbdma); + + ep->length = count; + + bDMA = true; + } + while(0); + + } +#endif + ep->bdma = bDMA; + + if(!bDMA) + { + hc_index_writeb(ep->epfifo, 0, USB_REG_TXCSR1); + hc_writeb(usb_pipedevice(urb->pipe), USB_REG_FADDR); + for (i = 0; i < len; i ++) { + hc_writeb(buf[i], ep_fifos[ep->epfifo]); + } + if (ep->epnum == 0) { + hc_index_writeb(0, 0x02, USB_REG_TXCSR1); + } else { + + hc_index_writeb(ep->epfifo, USB_TXCSR1_TXPKTRDY, USB_REG_TXCSR1); + } + + ep->length = len; + } +} + +/*-------------------------------------------------------------------------*/ + +/* caller updates on-chip enables later */ +static inline void sofirq_on(struct akotg_usbhc *akotghc) +{ + unsigned int regval; + + regval = hc_readb(USB_REG_INTRUSBE); + regval |= USB_INTR_SOF; + hc_writeb(regval, USB_REG_INTRUSBE); +} + +static inline void sofirq_off(struct akotg_usbhc *akotghc) +{ + unsigned int regval; + + regval = hc_readb(USB_REG_INTRUSBE); + regval &= ~USB_INTR_SOF; + hc_writeb(regval, USB_REG_INTRUSBE); +} + +/*-------------------------------------------------------------------------*/ + +static struct akotghc_ep *start_ep0(struct akotg_usbhc *akotghc) +{ + struct akotghc_ep *ep; + struct urb *urb; + + /* use endpoint at schedule head */ + if (akotghc->next_async_ep0) + ep = akotghc->next_async_ep0; + else if (!list_empty(&akotghc->async_ep0)) { + ep = container_of(akotghc->async_ep0.next, + struct akotghc_ep, schedule); + } else { + /* could set up the first fullspeed periodic + * transfer for the next frame ... + */ + return NULL; + } + + if (ep->schedule.next == &akotghc->async_ep0) + akotghc->next_async_ep0 = NULL; + else + akotghc->next_async_ep0 = container_of(ep->schedule.next, + struct akotghc_ep, schedule); + + if (unlikely(list_empty(&ep->hep->urb_list))) { + HDBG("empty %p queue?\n", ep); + return NULL; + } + + urb = container_of(ep->hep->urb_list.next, struct urb, urb_list); + + switch (ep->nextpid) { + case USB_PID_IN: + in_packet(akotghc, ep, urb); + break; + case USB_PID_OUT: + out_packet(akotghc, ep, urb); + break; + case USB_PID_SETUP: + setup_packet(akotghc, ep, urb); + break; + case USB_PID_ACK: /* for control status */ + status_packet(akotghc, ep, urb); + break; + default: + HDBG("bad ep%p pid %02x\n", ep, ep->nextpid); + ep = NULL; + } + return ep; +} + + +/* pick the next endpoint for a transaction, and issue it. + * frames start with periodic transfers (after whatever is pending + * from the previous frame), and the rest of the time is async + * transfers, scheduled round-robin. + */ +static struct akotghc_ep *start_epx(struct akotg_usbhc *akotghc, int epfifo) +{ + struct akotghc_ep *ep; + struct urb *urb; + + + /* use endpoint at schedule head */ + if (akotghc->next_periodic) { + ep = akotghc->next_periodic; + akotghc->next_periodic = ep->next; + } else { + if (akotghc->next_async_epx[epfifo-1]) + ep = akotghc->next_async_epx[epfifo-1]; + else if (!list_empty(&akotghc->async_epx[epfifo-1])) { + ep = container_of(akotghc->async_epx[epfifo-1].next, + struct akotghc_ep, schedule); + + } else { + /* could set up the first fullspeed periodic + * transfer for the next frame ... + */ + return NULL; + } + + if (ep->schedule.next == &akotghc->async_epx[epfifo-1]) + akotghc->next_async_epx[epfifo-1] = NULL; + else { + akotghc->next_async_epx[epfifo-1] = container_of(ep->schedule.next, + struct akotghc_ep, schedule); + } + } + + if (unlikely(list_empty(&ep->hep->urb_list))) { + HDBG("empty %p queue?\n", ep); + return NULL; + } + urb = container_of(ep->hep->urb_list.next, struct urb, urb_list); + switch (ep->nextpid) { + case USB_PID_IN: + in_packet(akotghc, ep, urb); + break; + case USB_PID_OUT: + out_packet(akotghc, ep, urb); + break; + case USB_PID_SETUP: + setup_packet(akotghc, ep, urb); + break; + case USB_PID_ACK: + status_packet(akotghc, ep, urb); + break; + default: + HDBG("bad ep%p pid %02x\n", ep, ep->nextpid); + ep = NULL; + } + return ep; +} + +#define MIN_JIFFIES ((msecs_to_jiffies(2) > 1) ? msecs_to_jiffies(2) : 2) + +static inline void start_transfer_ep0(struct akotg_usbhc *akotghc) +{ + if (akotghc->port_status & (1 << USB_PORT_FEAT_SUSPEND)) + return; + if (akotghc->active_ep0 == NULL) { + akotghc->active_ep0 = start_ep0(akotghc); + if (akotghc->active_ep0 != NULL) + akotghc->jiffies_ep0 = jiffies + MIN_JIFFIES; + } +} + +static inline void start_transfer_epx(struct akotg_usbhc *akotghc, int epfifo) +{ + if (akotghc->port_status & (1 << USB_PORT_FEAT_SUSPEND)){ + HDBG("start_transfer_epx, akotghc->port_status : 0x%x\n", akotghc->port_status); + return; + } + if (akotghc->active_epx[epfifo-1] == NULL) { + akotghc->active_epx[epfifo-1] = start_epx(akotghc, epfifo); + if (akotghc->active_epx[epfifo-1] != NULL) + akotghc->jiffies_epx[epfifo-1] = jiffies + MIN_JIFFIES; + } +} + +static inline void start_transfer(struct akotg_usbhc *akotghc, int epfifo) +{ + + if(epfifo == 0) + start_transfer_ep0(akotghc); + else + start_transfer_epx(akotghc, epfifo); + +} + +static void finish_request_ep0( + struct akotg_usbhc *akotghc, + struct akotghc_ep *ep, + struct urb *urb, + int status +) __releases(akotghc->lock) __acquires(akotghc->lock) +{ + VDBG("Finishing EP0 URB Request...\n"); + + if (usb_pipecontrol(urb->pipe)) + ep->nextpid = USB_PID_SETUP; + + usb_hcd_unlink_urb_from_ep(akotg_usbhc_to_hcd(akotghc), urb); + spin_unlock(&akotghc->lock); + usb_hcd_giveback_urb(akotg_usbhc_to_hcd(akotghc), urb, status); + spin_lock(&akotghc->lock); + + /* leave active endpoints in the schedule */ + if (!list_empty(&ep->hep->urb_list)) + return; + + /* async deschedule? */ + if (!list_empty(&ep->schedule)) { + list_del_init(&ep->schedule); + if (ep == akotghc->next_async_ep0) + akotghc->next_async_ep0 = NULL; + return; + } +} + + +static void finish_request_epx( + struct akotg_usbhc *akotghc, + struct akotghc_ep *ep, + struct urb *urb, + int status +) __releases(akotghc->lock) __acquires(akotghc->lock) +{ + unsigned i; + int is_out; + is_out = usb_pipeout(urb->pipe); + + VDBG("Finishing EP%d URB Request...\n", usb_pipeendpoint(urb->pipe)); + + if (usb_pipecontrol(urb->pipe)) + ep->nextpid = USB_PID_SETUP; + + usb_hcd_unlink_urb_from_ep(akotg_usbhc_to_hcd(akotghc), urb); + spin_unlock(&akotghc->lock); + usb_hcd_giveback_urb(akotg_usbhc_to_hcd(akotghc), urb, status); + spin_lock(&akotghc->lock); + + /* leave active endpoints in the schedule */ + if (!list_empty(&ep->hep->urb_list)) + return; + + + /* async deschedule? */ + //if(epnum_to_epfifo(&akotg_epfifo_mapping, ep->epnum, is_out, &epfifo) && !list_empty(&ep->schedule)) { + if (!list_empty(&ep->schedule)) { + list_del_init(&ep->schedule); + if (ep == akotghc->next_async_epx[ep->epfifo-1]) { + akotghc->next_async_epx[ep->epfifo-1] = NULL; + } + return; + } + + switch(usb_pipetype(urb->pipe)) { + case PIPE_ISOCHRONOUS: + case PIPE_INTERRUPT: + /* periodic deschedule */ + HDBG("%s(): deschedule qh%d/%p branch %d\n", + __func__, ep->period, ep, ep->branch); + for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) { + struct akotghc_ep *temp; + struct akotghc_ep **prev = &akotghc->periodic[i]; + + while (*prev && ((temp = *prev) != ep)) + prev = &temp->next; + if (*prev) + *prev = ep->next; + akotghc->load[i] -= ep->load; + } + ep->branch = PERIODIC_SIZE; + akotghc->periodic_count--; + + akotg_usbhc_to_hcd(akotghc)->self.bandwidth_allocated + -= ep->load / ep->period; + if (ep == akotghc->next_periodic) + akotghc->next_periodic = ep->next; + + /* we might turn SOFs back on again for the async schedule */ + if (akotghc->periodic_count == 0) + sofirq_off(akotghc); + } +} + +static void +done(struct akotg_usbhc *akotghc, struct akotghc_ep *ep) +{ + int i; + int err_occurred = 0; + int epfifo; + u8 status; + u8 rxstatus; + struct urb *urb; + unsigned int pipe; + int is_out; + int urbstat = -EINPROGRESS; + + if (unlikely(!ep)) { + return; + } + + urb = container_of(ep->hep->urb_list.next, struct urb, urb_list); + pipe = urb->pipe; + is_out = usb_pipeout(pipe); + if (!epnum_to_epfifo(&akotg_epfifo_mapping, ep->epnum, is_out, &epfifo)) + BUG(); /* Impossible, USB Device Endpoint *MUST* have been mapped to EPFIFO */ + + status = hc_index_readw(epfifo, USB_REG_TXCSR1); + rxstatus = hc_index_readw(epfifo, USB_REG_RXCSR1); + + //STALL + if (((epfifo != 0) && ((status & (1 << 5)) || rxstatus & (1 << 6))) + || ((epfifo == 0) && (status & (1 << 2)))) { + ep->error_count = 0; + urbstat = -EPIPE; + err_occurred = 1; + } + + //NAK timeout + if ((usb_pipebulk(pipe)) + &&((status & (1 << 7))||(rxstatus & (1 << 3)))) { + if (!ep->period) + ep->nak_count++; + ep->error_count = 0; + err_occurred = 1; + } + + //Error, times expired + if (((epfifo == 0) && (status & (1 << 4))) + ||((epfifo != 0)&&((status || rxstatus) & (1 << 2)) + &&(usb_pipeint(pipe)||usb_pipebulk(pipe)))) { + urbstat = -EPROTO; + ep->error_count = 0; + err_occurred = 1; + } + + if (err_occurred) { + if (epfifo == 0) + hc_index_writew(0, 0, USB_REG_TXCSR1); + else if (is_out) + if (urbstat == -EPIPE) + // clear stall bit and clear toggle + hc_index_writew(epfifo, (1 << 6), USB_REG_TXCSR1); + else + hc_index_writew(epfifo, 0, USB_REG_TXCSR1); + else + if (urbstat == -EPIPE) + // clear stall bit and clear toggle + hc_index_writew(epfifo, (1 << 7), USB_REG_RXCSR1); + else + hc_index_writew(epfifo, 0, USB_REG_RXCSR1); + } + else { + + struct usb_device *udev = urb->dev; + int len; + unsigned char *buf; + + /* urb->iso_frame_desc is currently ignored here... */ + + ep->nak_count = ep->error_count = 0; + switch (ep->nextpid) { + case USB_PID_OUT: + urb->actual_length += ep->length; + usb_dotoggle(udev, ep->epnum, 1); + if (urb->actual_length == urb->transfer_buffer_length) { + if (usb_pipecontrol(urb->pipe)) { + VDBG("NEXT Packet: Status Packet.\n"); + ep->nextpid = USB_PID_ACK; + } + + /* some bulk protocols terminate OUT transfers + * by a short packet, using ZLPs not padding. + */ + else if (ep->length < ep->maxpacket + || !(urb->transfer_flags & URB_ZERO_PACKET)) + urbstat = 0; + } + break; + case USB_PID_IN: + +#ifdef CONFIG_USB_AKOTG_DMA + if(ep->bdma){ + urb->actual_length += akotg_dma_get_trans_length(akotghc, epfifo); + + akotg_dma_clear(akotghc, epfifo); + ep->bdma = false; + } +#endif + + buf = urb->transfer_buffer + urb->actual_length; + + prefetchw(buf); + len = hc_index_readw(epfifo, USB_REG_COUNT0); + if (len > ep->length) { + printk(KERN_ERR"USB_PID_IN(OverFlow): len=%d, ep->length=%d\n", + len, ep->length); + len = ep->length; + urbstat = -EOVERFLOW; + } + // read data from fifo + urb->actual_length += len; + for (i = 0; i < len; i++) + *buf++ = hc_readb(ep_fifos[epfifo]); + + if (ep->epnum == 0) { + u8 regval = hc_index_readb(epfifo, USB_REG_TXCSR1); + regval &= ~USB_TXCSR1_TXPKTRDY; + hc_index_writeb(epfifo, regval, USB_REG_TXCSR1); + } else { + u8 regval = hc_index_readb(epfifo, USB_REG_RXCSR1); + regval &= ~USB_RXCSR1_RXPKTRDY; + hc_index_writeb(epfifo, regval, USB_REG_RXCSR1); + } + + usb_dotoggle(udev, ep->epnum, 0); + if (urbstat == -EINPROGRESS && + (len < ep->maxpacket || + urb->actual_length == urb->transfer_buffer_length)) { + if (usb_pipecontrol(urb->pipe)) { + VDBG("NEXT Packet: Status Packet.\n"); + ep->nextpid = USB_PID_ACK; + } + else + urbstat = 0; + } + break; + case USB_PID_SETUP: + if (urb->transfer_buffer_length == urb->actual_length) { + VDBG("NEXT Packet: Status Packet.\n"); + ep->nextpid = USB_PID_ACK; + } + else if (usb_pipeout(urb->pipe)) { + VDBG("NEXT Packet: OUT Packet.\n"); + usb_settoggle(udev, 0, 1, 1); + ep->nextpid = USB_PID_OUT; + } else { + VDBG("NEXT Packet: IN Packet.\n"); + usb_settoggle(udev, 0, 0, 1); + ep->nextpid = USB_PID_IN; + } + break; + case USB_PID_ACK: + if (!is_out) { + u8 regval = hc_index_readb(epfifo, USB_REG_RXCSR1); + regval &= ~USB_RXCSR1_RXPKTRDY; + hc_index_writeb(epfifo, regval, USB_REG_RXCSR1); + } + urbstat = 0; + break; + } + } + + if (urbstat != -EINPROGRESS || urb->unlinked) { + if (ep->epnum == 0) + finish_request_ep0(akotghc, ep, urb, urbstat); + else + finish_request_epx(akotghc, ep, urb, urbstat); + } +} + +static int balance(struct akotg_usbhc *akotghc, u16 period, u16 load) +{ + int i, branch = -ENOSPC; + + /* search for the least loaded schedule branch of that period + * which has enough bandwidth left unreserved. + */ + for (i = 0; i < period ; i++) { + if (branch < 0 || akotghc->load[branch] > akotghc->load[i]) { + int j; + + for (j = i; j < PERIODIC_SIZE; j += period) { + if ((akotghc->load[j] + load) + > MAX_PERIODIC_LOAD) + break; + } + if (j < PERIODIC_SIZE) + continue; + branch = i; + } + } + return branch; +} + +static void reset_otg(struct work_struct *work) +{ + unsigned long con; + + /* power up and set usb suspend bit, reset the transceiver of USB 2.0 HS controller */ + con = __raw_readl(USB_OP_MOD_REG); + con &= ~(0x7 << 0); + con |= (0x3 << 1); + con |= (1 << 0); + __raw_writel(con, USB_OP_MOD_REG); + + set_usb_as_host(); + + con = __raw_readl(USB_OP_MOD_REG); + //con |= (0x7 << 0); + con &= ~(0x01); + __raw_writel(con, USB_OP_MOD_REG); + + con = hc_readb(USB_REG_POWER); + con &= ~USB_POWER_RESET; + hc_writeb(con, USB_REG_POWER); + + hc_writeb(USB_POWER_ENSUSPEND|USB_HOSG_HIGH_SPEED, USB_REG_POWER); + hc_writeb(0xF7, USB_REG_INTRUSBE); + + /* start fs host session*/ + hc_writeb(USB_DEVCTL_SESSION, USB_REG_DEVCTL); +} + +#ifdef CONFIG_USB_AKOTG_DMA + +static bool handle_dma_irq(struct akotg_usbhc *akotghc, struct akotg_dma *dma) +{ + u8 epfifo = dma->epfifo; + u8 channel = dma->channel; + u32 regtmp; + u32 trans_len; + int urbstat = -EINPROGRESS; + + struct akotghc_ep *ep; + struct urb *urb; + + if(!akotghc || !dma) + return false; + + if(USB_DIRECTION_RX == dma->dir){ + hc_index_writeb(epfifo, 0, USB_REG_RXCSR2); + + regtmp = hc_readl(USB_DMA_ADDR(channel)); + trans_len = regtmp - dma->addr; + + //dma_unmap_single(NULL, dma->phyaddr, dma->count, DMA_FROM_DEVICE); + } + else{ + hc_index_writeb(epfifo, USB_TXCSR2_MODE, USB_REG_TXCSR2); + + trans_len = dma->count; + + //dma_unmap_single(NULL, dma->phyaddr, dma->count, DMA_TO_DEVICE); + } + + + hc_writel(0, USB_DMA_COUNT(channel)); + + //wait dma finish + l2_combuf_wait_dma_finish(dma->l2buffer); + + //free l2 buffer + akotg_free_l2_buffer(epfifo); + dma->l2buffer = BUF_NULL; + + //change status + dma->status = USB_DMA_CHANNEL_IDLE; + + //get urb + ep = akotghc->active_epx[epfifo-1]; + + if (unlikely(!ep)) { + return false; + } + ep->bdma = false; + urb = container_of(ep->hep->urb_list.next, struct urb, urb_list); + + urb->actual_length += trans_len; + + //done + if((urb->actual_length == urb->transfer_buffer_length) || urb->unlinked) + { + urbstat = 0; + finish_request_epx(akotghc, ep, urb, urbstat); + } + + akotghc->active_epx[epfifo-1] = NULL; + + return true; + +} + +irqreturn_t akotg_dma_irq(int irqnum, void *__hcd) +{ + struct usb_hcd *hcd = __hcd; + unsigned long flags; + irqreturn_t rc; + + local_irq_save(flags); + + if (unlikely(HCD_DEAD(hcd) || !HCD_HW_ACCESSIBLE(hcd))) + rc = IRQ_NONE; + else if (akotg_usbhc_irq(hcd) == IRQ_NONE) + rc = IRQ_NONE; + else + rc = IRQ_HANDLED; + + local_irq_restore(flags); + + return rc; +} + +#endif + +irqreturn_t akotg_usbhc_irq(struct usb_hcd *hcd) +{ + struct akotg_usbhc *akotghc = hcd_to_akotg_usbhc(hcd); + irqreturn_t ret = IRQ_NONE; + struct urb *urb; + struct akotghc_ep *ep; + int epnum[MAX_EP_NUM + 1] = { 0 }; + int i; + unsigned index = 0; + + char rINTCOM; + unsigned short rINTTX, rINTRX; + unsigned long con; + + u32 rINTDMA; + +#ifdef CONFIG_USB_AKOTG_DMA + struct akotg_dma *dma; +#endif + + spin_lock(&akotghc->lock); + + /*Read & Clear all interrupt status.*/ + rINTCOM = hc_readb(USB_REG_INTRUSB); + rINTTX =hc_readw(USB_REG_INTRTX); + rINTRX = hc_readw(USB_REG_INTRRX); + rINTDMA =hc_readl(USB_DMA_INTR); + + if (rINTTX &USB_INTR_EP0) { + epnum[0] = 1; + done(akotghc, akotghc->active_ep0); + akotghc->active_ep0 = NULL; + } + +#ifdef CONFIG_USB_AKOTG_DMA + if(rINTDMA) + { + //printk("|%x|", rINTDMA); + //printk("C[%x,%x]", hcd, m_hcd); + for(i = 0; i < USBDMA_CHANNEL_NUM; i++) + { + if(rINTDMA & (1<active_epx[i]); + akotghc->active_epx[i] = NULL; + } + + if (rINTCOM & USB_INTR_SOF) { + index = akotghc->frame++ & (PERIODIC_SIZE - 1); + + + /* be graceful about almost-inevitable periodic schedule + * overruns: continue the previous frame's transfers iff + * this one has nothing scheduled. + */ + + if (akotghc->periodic[index]) { + akotghc->next_periodic = akotghc->periodic[index]; + } + } + + /* manages debouncing and wakeup */ + if(rINTCOM & (USB_INTR_CONNECT | USB_INTR_DISCONNECT)) { + + /* most stats are reset for each VBUS session */ + + /* usbcore nukes other pending transactions on disconnect */ + if (akotghc->active_ep0) { + VDBG("Finishing EP0 Active URBs...\n"); + ep = akotghc->active_ep0; + hc_index_writeb(ep->epfifo, 0, USB_REG_TXCSR1); + hc_index_writeb(ep->epfifo, USB_CSR02_FLUSHFIFO, USB_REG_TXCSR2); + + finish_request_ep0(akotghc, akotghc->active_ep0, + container_of(akotghc->active_ep0->hep->urb_list.next, struct urb, urb_list), + -ESHUTDOWN); + akotghc->active_ep0 = NULL; + } + + for(i=0; iactive_epx[i]) { + VDBG("Finishing EPx Active URBs...\n"); + ep = akotghc->active_epx[i]; + urb = container_of(ep->hep->urb_list.next, struct urb, urb_list); + //if (!epnum_to_epfifo(&akotg_epfifo_mapping, ep->epnum, is_out, &epfifo)) + // BUG(); + if (usb_pipeout(urb->pipe)) { + hc_index_writeb(ep->epfifo, USB_TXCSR1_FLUSHFIFO, USB_REG_TXCSR1); + } else { + hc_index_writeb(ep->epfifo, USB_RXCSR1_FLUSHFIFO, USB_REG_RXCSR1); + } + + finish_request_epx(akotghc, akotghc->active_epx[i], + container_of(akotghc->active_epx[i]->hep->urb_list.next, struct urb, urb_list), + -ESHUTDOWN); + akotghc->active_epx[i] = NULL; + } + + /* port status seems weird until after reset, so + * force the reset and make khubd clean up later. + */ + if (rINTCOM & USB_INTR_CONNECT) { + akotghc->port_status |= 1 << USB_PORT_FEAT_CONNECTION; + akotghc->port_status |= 1 << USB_PORT_FEAT_C_CONNECTION; + } else { + +#ifdef CONFIG_USB_AKOTG_DMA + //clear all dma + akotg_dma_clear(akotghc, 0); +#endif + + akotghc->port_status &= ~(1 << USB_PORT_FEAT_CONNECTION); + akotghc->port_status |= 1 << USB_PORT_FEAT_C_CONNECTION; + + period_epfifo = 0; + init_epfifo_mapping(&akotg_epfifo_mapping); + reset_endpoints(); + + REG32(USB_OP_MOD_REG) &= ~(0xff << 6); + REG32(USB_OP_MOD_REG) |= (0x1f << 6); + + clk_disable(akotghc->clk); + clk_enable(akotghc->clk); + ak_soft_reset(AK_SRESET_USBHS); + + hc_writeb(0x0, USB_REG_DEVCTL); + hc_writeb(0x0, USB_REG_FADDR); + + con = hc_readb(USB_REG_POWER); + con |= USB_POWER_RESET; + hc_writeb(con, USB_REG_POWER); + + queue_delayed_work(g_otghc_wq, &g_otg_rest, 0); + } + } + + if (rINTCOM & USB_INTR_RESUME) { + if (akotghc->port_status & (1 << USB_PORT_FEAT_SUSPEND)) { + HDBG("wakeup\n"); + akotghc->port_status |= 1 << USB_PORT_FEAT_C_SUSPEND; + + } + rINTCOM &= ~(USB_INTR_RESUME); + } + + if (akotghc->port_status & (1 << USB_PORT_FEAT_ENABLE)) { + if (epnum[0]) { + start_transfer(akotghc, 0); + ret = IRQ_HANDLED; + } + if((rINTCOM & USB_INTR_SOF) && akotghc->periodic[index] && period_epfifo){ + start_transfer(akotghc, period_epfifo); + ret = IRQ_HANDLED; + } + for(i = 0; i < MAX_EP_NUM; i++) { + if(epnum[i + 1]) { + start_transfer(akotghc, i + 1); + ret = IRQ_HANDLED; + } + } + +#ifdef CONFIG_USB_AKOTG_DMA + for(i = 0; i < USBDMA_CHANNEL_NUM; i++) + { + if(rINTDMA & (1<epfifo); + ret = IRQ_HANDLED; + } + } +#endif + } + else + { + if(rINTTX &USB_INTR_EP0) + pr_info("USB_INTR_EP0 happen!,but port_status is not USB_PORT_FEAT_ENABLE\n"); + } + + if(akotghc->periodic_count == 0 && list_empty(&akotghc->async_ep0)) { + for(i = 0; i < MAX_EP_NUM; i++) { + if(!list_empty(&akotghc->async_epx[i])) + break; + } + if(i == MAX_EP_NUM) + sofirq_off(akotghc); + } + + spin_unlock(&akotghc->lock); + + return ret; +} +EXPORT_SYMBOL(akotg_usbhc_irq); + +int akotg_usbhc_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +{ + struct akotg_usbhc *akotghc = hcd_to_akotg_usbhc(hcd); + struct usb_host_endpoint *hep; + unsigned long flags; + struct akotghc_ep *ep; + int retval, i; + int is_out = usb_pipeout(urb->pipe); + + HDBG("Dequeue: Direction=%s, Type=%s, EP Num=%d, urb=%p\n", + is_out ? "OUT" : "IN", trans_desc[usb_pipetype(urb->pipe)], + usb_pipeendpoint(urb->pipe), urb); + + spin_lock_irqsave(&akotghc->lock, flags); + + retval = usb_hcd_check_unlink_urb(hcd, urb, status); + if (retval) { + //printk(KERN_ERR"Dequeue: check and unlink urb failed!\n"); + goto fail; + } + + hep = urb->hcpriv; + ep = hep->hcpriv; + if (ep) { + /* finish right away if this urb can't be active ... + * note that some drivers wrongly expect delays + */ + if (ep->hep->urb_list.next != &urb->urb_list) { + /* not front of queue? never active */ + + /* for active transfers, we expect an IRQ */ + } else if (akotghc->active_ep0 == ep) { + if (time_before_eq(akotghc->jiffies_ep0, jiffies)) { + hc_index_writeb(0, 0, USB_REG_TXCSR1); + hc_index_writeb(0, 1 << 0, USB_REG_TXCSR2); + akotghc->active_ep0 = NULL; + } else + urb = NULL; + + } else { + for(i=0; iactive_epx[i] == ep) { + if(time_before_eq(akotghc->jiffies_epx[i], jiffies)) { + //if(!epnum_to_epfifo(&akotg_epfifo_mapping, ep->epnum, is_out, &epfifo)) + // BUG(); + if (is_out) { + hc_index_writeb(ep->epfifo, USB_TXCSR1_FLUSHFIFO, USB_REG_TXCSR1); + } else { + hc_index_writeb(ep->epfifo, USB_RXCSR1_FLUSHFIFO, USB_REG_RXCSR1); + } + akotghc->active_epx[i] = NULL; + } else + urb = NULL; + + } + } + /* front of queue for inactive endpoint */ + } + + if (urb) { + if (akotghc->active_ep0 == ep) + finish_request_ep0(akotghc, ep, urb, 0); + else + finish_request_epx(akotghc, ep, urb, 0); + } else { + HDBG("dequeue, urb %p active, wait for irq.\n", urb); + } + } else + retval = -EINVAL; + fail: + spin_unlock_irqrestore(&akotghc->lock, flags); + + return retval; +} + + + +int +akotg_usbhc_urb_enqueue( + struct usb_hcd *hcd, + struct urb *urb, + gfp_t mem_flags +) +{ + struct akotg_usbhc *akotghc = hcd_to_akotg_usbhc(hcd); + struct usb_device *udev = urb->dev; + int is_out = usb_pipeout(urb->pipe); + int type = usb_pipetype(urb->pipe); + int epnum = usb_pipeendpoint(urb->pipe); + int epfifo = 0; + + struct akotghc_ep *ep = NULL; + unsigned long flags; + int i; + int retval; + struct usb_host_endpoint *hep = urb->ep; + + HDBG("Enqueue: Direction=%s, Type=%s, EP Num=%d, urb=%p" + " urb->transfer_buffer_length=%d\n", + is_out ? "OUT" : "IN", trans_desc[type], epnum, urb, + urb->transfer_buffer_length); + + if (usb_pipeisoc(urb->pipe)) + return -ENOSPC; + + spin_lock_irqsave(&akotg_epfifo_mapping.lock,flags); + if (!__is_epnum_mapped(&akotg_epfifo_mapping, epnum, is_out)) { + if (!__map_epnum_to_epfifo(&akotg_epfifo_mapping, epnum, is_out, &epfifo)) { + spin_unlock_irqrestore(&akotg_epfifo_mapping.lock, flags); + HDBG("ep%d can not mapping\n",epnum); + return -ENOSPC; + } + + HDBG("###ep%d mapping hostEP%d \n",epnum, epfifo); + + if (epnum != 0) { + + int eptype = get_ep_type(urb->pipe); + + if (is_out) { + disable_epx_tx_interrupt(epfifo); + set_epx_tx_type(epfifo, epnum, eptype); + hc_index_writew(epfifo, hep->desc.wMaxPacketSize, USB_REG_TXMAXP); + hc_index_writeb(epfifo, 16, USB_REG_TXINTERVAL); /* Tx Interval */ + clear_epx_tx_data_toggle(epfifo); + set_epx_tx_mode(epfifo); + flush_epx_tx_fifo(epfifo); + enable_epx_tx_interrupt(epfifo); + } else { + disable_epx_rx_interrupt(epfifo); + set_epx_rx_type(epfifo, epnum, eptype); + hc_index_writew(epfifo, hep->desc.wMaxPacketSize, USB_REG_RXMAXP); + if (usb_endpoint_xfer_isoc(&hep->desc) || usb_endpoint_xfer_int(&hep->desc)) + hc_index_writeb(epfifo, hep->desc.bInterval, USB_REG_RXINTERVAL); + else + hc_index_writeb(epfifo, 0, USB_REG_RXINTERVAL); + set_epx_rx_mode(epfifo); + clear_epx_rx_data_toggle(epfifo); + flush_epx_rx_fifo(epfifo); + enable_epx_rx_interrupt(epfifo); + } + } + } else { + if(!epnum_to_epfifo(&akotg_epfifo_mapping, epnum, is_out, &epfifo)) + BUG(); + } + + spin_unlock_irqrestore(&akotg_epfifo_mapping.lock, flags); + + spin_lock_irqsave(&akotghc->lock, flags); + + /* don't submit to a dead or disabled port */ + if (!(akotghc->port_status & (1 << USB_PORT_FEAT_ENABLE)) + || !HC_IS_RUNNING(hcd->state)) { + retval = -ENODEV; + HDBG("ep%d dead or disabled port!\n",epnum); + goto fail_not_linked; + } + + retval = usb_hcd_link_urb_to_ep(hcd, urb); + if (retval) { + HDBG("ep%d usb_hcd_link_urb_to_ep fail!\n",epnum); + goto fail_not_linked; + } + + /* avoid all allocations within spinlocks */ + if (hep->hcpriv) { + ep = hep->hcpriv; + } else { + ep = kzalloc(sizeof *ep, mem_flags); + if (ep == NULL) { + retval = -ENOMEM; + goto fail; + } + + INIT_LIST_HEAD(&ep->schedule); + ep->udev = udev; + ep->epnum = epnum; + ep->epfifo = epfifo; + ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out); + + usb_settoggle(udev, epnum, is_out, 0); + + if (type == PIPE_CONTROL) + ep->nextpid = USB_PID_SETUP; + else if (is_out) + ep->nextpid = USB_PID_OUT; + else + ep->nextpid = USB_PID_IN; + + if (ep->maxpacket > H_MAXPACKET) { + /* iso packets up to 240 bytes could work... */ + HDBG("dev %d ep%d maxpacket %d\n", + udev->devnum, epnum, ep->maxpacket); + retval = -EINVAL; + goto fail; + } + + switch (type) { + case PIPE_ISOCHRONOUS: + case PIPE_INTERRUPT: + if (urb->interval > PERIODIC_SIZE) + urb->interval = PERIODIC_SIZE; + ep->period = urb->interval; + ep->branch = PERIODIC_SIZE; + ep->load = usb_calc_bus_time(udev->speed, !is_out, + (type == PIPE_ISOCHRONOUS), + usb_maxpacket(udev, urb->pipe, is_out)) / 1000; + period_epfifo = epfifo; + break; + } + + ep->hep = hep; + hep->hcpriv = ep; + } + + /* maybe put endpoint into schedule */ + switch (type) { + case PIPE_CONTROL: + case PIPE_BULK: + if (list_empty(&ep->schedule)) { + if (epnum == 0) + list_add_tail(&ep->schedule, &akotghc->async_ep0); + else + list_add_tail(&ep->schedule, &akotghc->async_epx[epfifo-1]); + } + break; + case PIPE_ISOCHRONOUS: + case PIPE_INTERRUPT: + urb->interval = ep->period; + if (ep->branch < PERIODIC_SIZE) { + /* NOTE: the phase is correct here, but the value + * needs offsetting by the transfer queue depth. + * All current drivers ignore start_frame, so this + * is unlikely to ever matter... + */ + urb->start_frame = (akotghc->frame & (PERIODIC_SIZE - 1)) + + ep->branch; + break; + } + + retval = balance(akotghc, ep->period, ep->load); + if (retval < 0) + goto fail; + ep->branch = retval; + retval = 0; + urb->start_frame = (akotghc->frame & (PERIODIC_SIZE - 1)) + + ep->branch; + + /* sort each schedule branch by period (slow before fast) + * to share the faster parts of the tree without needing + * dummy/placeholder nodes + */ + VDBG("schedule qh%d/%p branch %d\n", ep->period, ep, ep->branch); + for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) { + struct akotghc_ep **prev = &akotghc->periodic[i]; + struct akotghc_ep *here = *prev; + + while (here && ep != here) { + if (ep->period > here->period) + break; + prev = &here->next; + here = *prev; + } + if (ep != here) { + ep->next = here; + *prev = ep; + } + akotghc->load[i] += ep->load; + } + akotghc->periodic_count++; + hcd->self.bandwidth_allocated += ep->load / ep->period; + sofirq_on(akotghc); + } + + urb->hcpriv = hep; + start_transfer(akotghc, epfifo); + +fail: + if (retval) + usb_hcd_unlink_urb_from_ep(hcd, urb); +fail_not_linked: + spin_unlock_irqrestore(&akotghc->lock, flags); + return retval; +} + + +void +akotg_usbhc_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *hep) +{ + struct akotg_usbhc *akotghc = hcd_to_akotg_usbhc(hcd); + int epnum = usb_endpoint_num(&hep->desc); + unsigned long flags; + + spin_lock_irqsave(&akotghc->lock, flags); + + HDBG("Resetting EP %d, Type=%s, Dir=%s\n", + epnum, xfer_name[usb_endpoint_type(&hep->desc)], usb_endpoint_dir_out(&hep->desc)? "OUT" : "IN"); + + if (epnum == 0) { + hc_index_writeb(0, 0, USB_REG_TXINTERVAL); + hc_index_writew(0, 0, USB_REG_TXCSR1); + flush_ep0_fifo(); + enable_ep0_interrupt(); + } else { + + } + + spin_unlock_irqrestore(&akotghc->lock, flags); +} + +void +akotg_usbhc_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) +{ + struct akotghc_ep *ep = hep->hcpriv; + + int epnum = usb_endpoint_num(&hep->desc); + int is_out = usb_endpoint_dir_out(&hep->desc); + + int epfifo = 0; + + + if (!ep) { + return; + } + + if(!epnum_to_epfifo(&akotg_epfifo_mapping, epnum, is_out, &epfifo)) + return; + + if (is_epnum_mapped(&akotg_epfifo_mapping, epnum, is_out)) { + + disable_ep_interrupt(epfifo); + flush_ep_fifo(epfifo); + } + /* assume we'd just wait for the irq */ + if (!list_empty(&hep->urb_list)) + msleep(3); + if (!list_empty(&hep->urb_list)) + HDBG("ep %p not empty?\n", ep); + + kfree(ep); + hep->hcpriv = NULL; + + return; + +} + +int +akotg_usbhc_get_frame(struct usb_hcd *hcd) +{ + struct akotg_usbhc *akotghc = hcd_to_akotg_usbhc(hcd); + + /* wrong except while periodic transfers are scheduled; + * never matches the on-the-wire frame; + * subject to overruns. + */ + return akotghc->frame; +} + + +/*-------------------------------------------------------------------------*/ + +/* the virtual root hub timer IRQ checks for hub status */ +int +akotg_usbhc_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct akotg_usbhc *akotghc = hcd_to_akotg_usbhc(hcd); + unsigned long flags; + + /* non-SMP HACK: use root hub timer as i/o watchdog + * this seems essential when SOF IRQs aren't in use... + */ + local_irq_save(flags); + if (!timer_pending(&akotghc->timer)) { + if (akotg_usbhc_irq( /* ~0, */ hcd) != IRQ_NONE) + pr_info("akotg_usbhc_hub_status_data call akotg_usbhc_irq return IRQ_HANDLED\n");//akotghc->stat_lost++; + } + local_irq_restore(flags); + + if (!(akotghc->port_status & (0xffff << 16))) { + return 0; + } + + /* tell khubd port 1 changed */ + *buf = (1 << 1); + + return 1; +} + +void +akotg_usbhc_hub_descriptor ( + struct akotg_usbhc *akotghc, + struct usb_hub_descriptor *desc +) { + u16 temp = 0; + + desc->bDescriptorType = 0x29; + desc->bHubContrCurrent = 0; + + desc->bNbrPorts = 1; + desc->bDescLength = 9; + + /* per-port power switching (gang of one!), or none */ + desc->bPwrOn2PwrGood = 0; + + /* no over current errors detection/handling */ + temp |= HUB_CHAR_COMMON_LPSM|HUB_CHAR_NO_OCPM; + + desc->wHubCharacteristics = cpu_to_le16(temp); + + /* two bitmaps: ports removable, and legacy PortPwrCtrlMask */ + desc->u.hs.DeviceRemovable[0] = 0 << 1; + desc->u.hs.DeviceRemovable[1] = ~0; +} + +void +akotg_usbhc_timer(unsigned long _akotghs) +{ + struct akotg_usbhc *akotghc = (void *) _akotghs; + unsigned long flags; + const u32 mask = (1 << USB_PORT_FEAT_CONNECTION) + | (1 << USB_PORT_FEAT_ENABLE); + + + spin_lock_irqsave(&akotghc->lock, flags); + + if (akotghc->port_status & USB_PORT_STAT_RESET) { + akotghc->port_status = (1 << USB_PORT_FEAT_C_RESET) + | (1 << USB_PORT_FEAT_POWER); + akotghc->port_status |= mask; + + if (akotghc->port_status & (1 << USB_PORT_FEAT_CONNECTION)) { + if ((hc_readb(USB_REG_DEVCTL) & USB_DEVCTL_FSDEV)) { + akotghc->port_status |= USB_PORT_STAT_HIGH_SPEED; + } else { + /* Plug-in & plug-out quickly could lead to this... */ + akotghc->port_status &= ~mask; + } + } + } else { + /* NOT IMPLEMENTED YET */ + BUG(); + } + + + spin_unlock_irqrestore(&akotghc->lock, flags); + +} + +int +akotg_usbhc_hub_control( + struct usb_hcd *hcd, + u16 typeReq, + u16 wValue, + u16 wIndex, + char *buf, + u16 wLength +) { + struct akotg_usbhc *akotghc = hcd_to_akotg_usbhc(hcd); + int retval = 0; + unsigned long flags; + char reg8val; + + HDBG("%s(): typeReq=0x%x, wValue=%d, wIndex=%d, wLength=%d\t", + __func__, typeReq, wValue, wIndex, wLength); + + spin_lock_irqsave(&akotghc->lock, flags); + + switch (typeReq) { + case ClearHubFeature: + case SetHubFeature: + switch (wValue) { + case C_HUB_OVER_CURRENT: + case C_HUB_LOCAL_POWER: + break; + default: + goto error; + } + break; + case ClearPortFeature: + if (wIndex != 1 || wLength != 0) + goto error; + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + HDBG("ClearPortFeature: USB_PORT_FEAT_ENABLE\n"); + akotghc->port_status &= (1 << USB_PORT_FEAT_POWER); + break; + case USB_PORT_FEAT_SUSPEND: + HDBG("ClearPortFeature: USB_PORT_FEAT_SUSPEND\n"); + if (!(akotghc->port_status & (1 << USB_PORT_FEAT_SUSPEND))) + break; + + /* 20 msec of resume/K signaling, other irqs blocked */ + HDBG(" start resume...\n"); + hc_writeb(0x0, USB_REG_INTRUSBE); + reg8val = hc_readb(USB_REG_POWER); + reg8val |= USB_POWER_RESUME; + hc_writeb(reg8val, USB_REG_POWER); + + mod_timer(&akotghc->timer, jiffies + msecs_to_jiffies(20)); + break; + case USB_PORT_FEAT_POWER: + port_power(akotghc, 0); + break; + case USB_PORT_FEAT_C_ENABLE: + case USB_PORT_FEAT_C_SUSPEND: + case USB_PORT_FEAT_C_CONNECTION: + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + case USB_PORT_FEAT_C_RESET: + break; + default: + goto error; + } + akotghc->port_status &= ~(1 << wValue); + break; + case GetHubDescriptor: + akotg_usbhc_hub_descriptor(akotghc, (struct usb_hub_descriptor *) buf); + break; + case GetHubStatus: + put_unaligned_le32(0, buf); + break; + case GetPortStatus: + if (wIndex != 1) + goto error; + put_unaligned_le32(akotghc->port_status, buf); + if (*(u16*)(buf+2)) /* only if wPortChange is interesting */ + HDBG(" GetPortStatus 0x%04x\n", akotghc->port_status); + break; + case SetPortFeature: + if (wIndex != 1 || wLength != 0) + goto error; + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + HDBG("SetPortFeature: USB_PORT_FEAT_SUSPEND\n"); + if (akotghc->port_status & (1 << USB_PORT_FEAT_RESET)) + goto error; + if (!(akotghc->port_status & (1 << USB_PORT_FEAT_ENABLE))) + goto error; + /*to suspend the usb host controller.*/ + reg8val = hc_readb(USB_REG_POWER); + reg8val |= USB_POWER_SUSPENDM; + hc_writeb(reg8val, USB_REG_POWER); + break; + case USB_PORT_FEAT_POWER: + HDBG("SetPortFeature: USB_PORT_FEAT_POWER\n"); + port_power(akotghc, 1); + break; + case USB_PORT_FEAT_RESET: + HDBG("SetPortFeature: USB_PORT_FEAT_RESET, Port Status=0x%04x\n", + akotghc->port_status); + if (akotghc->port_status & (1 << USB_PORT_FEAT_SUSPEND)) { + HDBG(" USB_PORT_FEAT_SUSPEND ....\n"); + goto error; + } + if (!(akotghc->port_status & (1 << USB_PORT_FEAT_POWER))) { + HDBG(" USB_PORT_FEAT_POWER NOT SET.\n"); + break; + } + clear_all_interrupts(); + + /* 50 msec of reset/SE0 signaling, irqs blocked */ + /*reset device.*/ + reg8val = hc_readb(USB_REG_POWER); + reg8val |= USB_POWER_RESET; + hc_writeb(reg8val, USB_REG_POWER); + mdelay(30); + reg8val &= ~USB_POWER_RESET; + hc_writeb(reg8val, USB_REG_POWER); + + hc_writeb(0xF7, USB_REG_INTRUSBE); + enable_ep0_interrupt(); + + akotghc->port_status |= (1 << USB_PORT_FEAT_RESET); + mod_timer(&akotghc->timer, jiffies + msecs_to_jiffies(50)); + break; + default: + goto error; + } + + akotghc->port_status |= 1 << wValue; + break; +error: + /* "protocol stall" on error */ + retval = -EPIPE; + } + + spin_unlock_irqrestore(&akotghc->lock, flags); + return retval; +} + + + int +akotg_usbhc_bus_suspend(struct usb_hcd *hcd) +{ + u8 reg; + unsigned long flags; + msleep(10); + local_irq_save(flags); + reg = hc_readb(USB_REG_POWER); + reg |= USB_POWER_SUSPENDM; + hc_writeb(reg, USB_REG_POWER); + local_irq_restore(flags); + msleep(20); + return 0; +} + + int +akotg_usbhc_bus_resume(struct usb_hcd *hcd) +{ + u8 reg; + unsigned long flags; + local_irq_save(flags); + reg = hc_readb(USB_REG_POWER); + reg |= USB_POWER_RESUME; + hc_writeb(reg, USB_REG_POWER); + local_irq_restore(flags); + msleep(20); + local_irq_save(flags); + reg = hc_readb(USB_REG_POWER); + reg &= ~USB_POWER_RESUME; + hc_writeb(reg, USB_REG_POWER); + local_irq_restore(flags); + msleep(100); + return 0; +} + +void akotg_usbhc_stop(struct usb_hcd *hcd) +{ + struct akotg_usbhc *akotghc = hcd_to_akotg_usbhc(hcd); + unsigned long flags; + + del_timer_sync(&hcd->rh_timer); + clk_disable(akotghc->clk); + + /* reset usb phy */ + usb_reset_phy(akotghc); + + //rMULFUN_CON2 &= ~(0x1 << 18); //iddig is invalid + //REG32(USB_OP_MOD_REG) |= (0x3 << 12); + + spin_lock_irqsave(&akotghc->lock, flags); + port_power(akotghc, 0); + spin_unlock_irqrestore(&akotghc->lock, flags); +} + +static void usb_hwinit_control(struct akotg_usbhc *otghc) +{ + unsigned long flags; + + spin_lock_irqsave(&otghc->lock, flags); + + port_power(otghc, 1); + set_usb_as_host(); + /* reset usb phy */ + usb_reset_phy(otghc); + + clear_all_interrupts(); + reset_endpoints(); + hc_writeb(0x0, USB_REG_FADDR); + + usb_power_up(otghc); + + hc_writeb(USB_POWER_ENSUSPEND|USB_HOSG_HIGH_SPEED, USB_REG_POWER); + hc_writeb(0xF7, USB_REG_INTRUSBE); + + spin_unlock_irqrestore(&otghc->lock, flags); +} + +int akotg_usbhc_reset(struct usb_hcd *hcd) +{ + unsigned long con; + struct akotg_usbhc *akotghc = hcd_to_akotg_usbhc(hcd); + disable_irq(akotghc->mcu_irq); + + period_epfifo = 0; + init_epfifo_mapping(&akotg_epfifo_mapping); + reset_endpoints(); + + clk_disable(akotghc->clk); + clk_enable(akotghc->clk); + ak_soft_reset(AK_SRESET_USBHS); + + REG32(USB_OP_MOD_REG) &= ~(0xff << 6); + REG32(USB_OP_MOD_REG) |= (0x1f << 6); + + hc_writeb(0x0, USB_REG_DEVCTL); + hc_writeb(0x0, USB_REG_FADDR); + + con = hc_readb(USB_REG_POWER); + con |= USB_POWER_RESET; + hc_writeb(con, USB_REG_POWER); + + /* power up and set usb suspend bit, reset the transceiver of USB 2.0 HS controller */ + con = __raw_readl(USB_OP_MOD_REG); + con &= ~(0x7 << 0); + con |= (0x3 << 1); + con |= (1 << 0); + __raw_writel(con, USB_OP_MOD_REG); + + set_usb_as_host(); + + con = __raw_readl(USB_OP_MOD_REG); + con |= (0x7 << 0); + con &= ~(0x01); + __raw_writel(con, USB_OP_MOD_REG); + + con = hc_readb(USB_REG_POWER); + con &= ~USB_POWER_RESET; + hc_writeb(con, USB_REG_POWER); + + hc_writeb(USB_POWER_ENSUSPEND|USB_HOSG_HIGH_SPEED, USB_REG_POWER); + hc_writeb(0xF7, USB_REG_INTRUSBE); + + /* start fs host session*/ + hc_writeb(USB_DEVCTL_SESSION, USB_REG_DEVCTL); + + enable_irq(akotghc->mcu_irq); + return 0; +} + +int akotg_usbhc_start(struct usb_hcd *hcd) +{ + struct akotg_usbhc *akotghc = hcd_to_akotg_usbhc(hcd); + + g_otghc_wq = create_singlethread_workqueue("usb_otg_wq"); + if (!g_otghc_wq) + goto err_otghc_queue; + INIT_DELAYED_WORK(&g_otg_rest, reset_otg); + /*after reset usbhc, set stat to running.*/ + hcd->state = HC_STATE_RUNNING; + + /* chip has been reset, VBUS power is off */ + disable_irq(akotghc->mcu_irq); +#ifdef CONFIG_USB_AKOTG_DMA + disable_irq(akotghc->dma_irq); +#endif + clk_enable(akotghc->clk); + + usb_hwinit_control(akotghc); + + /* start host session*/ // is end session ? + hc_writeb(USB_DEVCTL_SESSION, USB_REG_DEVCTL); + + enable_irq(akotghc->mcu_irq); +#ifdef CONFIG_USB_AKOTG_DMA + enable_irq(akotghc->dma_irq); +#endif + return 0; +err_otghc_queue: + printk(KERN_ERR "akotg_usbhc couldn't create workqueue\n"); + if(g_otghc_wq) + destroy_workqueue(g_otghc_wq); + return -ENOMEM; +} + diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index 5c87db06..c2902a86 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -12,6 +12,14 @@ config USB_OTG_UTILS Select this to make sure the build includes objects from the OTG infrastructure directory. +config USB_OTG_WAKELOCK + bool "Hold a wakelock when USB connected" + depends on WAKELOCK + select USB_OTG_UTILS + help + Select this to automatically hold a wakelock when USB is + connected, preventing suspend. + if USB || USB_GADGET # diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile index 41aa5098..638d040c 100644 --- a/drivers/usb/otg/Makefile +++ b/drivers/usb/otg/Makefile @@ -7,6 +7,7 @@ ccflags-$(CONFIG_USB_GADGET_DEBUG) += -DDEBUG # infrastructure obj-$(CONFIG_USB_OTG_UTILS) += otg.o +obj-$(CONFIG_USB_OTG_WAKELOCK) += otg-wakelock.o # transceiver drivers obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o diff --git a/drivers/usb/otg/otg-wakelock.c b/drivers/usb/otg/otg-wakelock.c new file mode 100644 index 00000000..e17e2729 --- /dev/null +++ b/drivers/usb/otg/otg-wakelock.c @@ -0,0 +1,170 @@ +/* + * otg-wakelock.c + * + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#define TEMPORARY_HOLD_TIME 2000 + +static bool enabled = true; +static struct usb_phy *otgwl_xceiv; +static struct notifier_block otgwl_nb; + +/* + * otgwl_spinlock is held while the VBUS lock is grabbed or dropped and the + * held field is updated to match. + */ + +static DEFINE_SPINLOCK(otgwl_spinlock); + +/* + * Only one lock, but since these 3 fields are associated with each other... + */ + +struct otgwl_lock { + char name[40]; + struct wake_lock wakelock; + bool held; +}; + +/* + * VBUS present lock. Also used as a timed lock on charger + * connect/disconnect and USB host disconnect, to allow the system + * to react to the change in power. + */ + +static struct otgwl_lock vbus_lock; + +static void otgwl_hold(struct otgwl_lock *lock) +{ + if (!lock->held) { + wake_lock(&lock->wakelock); + lock->held = true; + } +} + +static void otgwl_temporary_hold(struct otgwl_lock *lock) +{ + wake_lock_timeout(&lock->wakelock, + msecs_to_jiffies(TEMPORARY_HOLD_TIME)); + lock->held = false; +} + +static void otgwl_drop(struct otgwl_lock *lock) +{ + if (lock->held) { + wake_unlock(&lock->wakelock); + lock->held = false; + } +} + +static void otgwl_handle_event(unsigned long event) +{ + unsigned long irqflags; + + spin_lock_irqsave(&otgwl_spinlock, irqflags); + + if (!enabled) { + otgwl_drop(&vbus_lock); + spin_unlock_irqrestore(&otgwl_spinlock, irqflags); + return; + } + + switch (event) { + case USB_EVENT_VBUS: + case USB_EVENT_ENUMERATED: + otgwl_hold(&vbus_lock); + break; + + case USB_EVENT_NONE: + case USB_EVENT_ID: + case USB_EVENT_CHARGER: + otgwl_temporary_hold(&vbus_lock); + break; + + default: + break; + } + + spin_unlock_irqrestore(&otgwl_spinlock, irqflags); +} + +static int otgwl_otg_notifications(struct notifier_block *nb, + unsigned long event, void *unused) +{ + otgwl_handle_event(event); + return NOTIFY_OK; +} + +static int set_enabled(const char *val, const struct kernel_param *kp) +{ + int rv = param_set_bool(val, kp); + + if (rv) + return rv; + + if (otgwl_xceiv) + otgwl_handle_event(otgwl_xceiv->last_event); + + return 0; +} + +static struct kernel_param_ops enabled_param_ops = { + .set = set_enabled, + .get = param_get_bool, +}; + +module_param_cb(enabled, &enabled_param_ops, &enabled, 0644); +MODULE_PARM_DESC(enabled, "enable wakelock when VBUS present"); + +static int __init otg_wakelock_init(void) +{ + int ret; + + otgwl_xceiv = usb_get_transceiver(); + + if (!otgwl_xceiv) { + pr_err("%s: No USB transceiver found\n", __func__); + return -ENODEV; + } + + snprintf(vbus_lock.name, sizeof(vbus_lock.name), "vbus-%s", + dev_name(otgwl_xceiv->dev)); + wake_lock_init(&vbus_lock.wakelock, WAKE_LOCK_SUSPEND, + vbus_lock.name); + + otgwl_nb.notifier_call = otgwl_otg_notifications; + ret = usb_register_notifier(otgwl_xceiv, &otgwl_nb); + + if (ret) { + pr_err("%s: usb_register_notifier on transceiver %s" + " failed\n", __func__, + dev_name(otgwl_xceiv->dev)); + otgwl_xceiv = NULL; + wake_lock_destroy(&vbus_lock.wakelock); + return ret; + } + + otgwl_handle_event(otgwl_xceiv->last_event); + return ret; +} + +late_initcall(otg_wakelock_init); diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 539247b9..fc13c4bb 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -546,6 +546,7 @@ static const struct option_blacklist_info telit_le920_blacklist = { }; static const struct usb_device_id option_ids[] = { + { USB_DEVICE(0x1286, 0x4e3d) }, //add air720 4g module { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_LIGHT) }, @@ -1356,6 +1357,7 @@ static struct usb_driver option_driver = { #ifdef CONFIG_PM .suspend = usb_serial_suspend, .resume = usb_serial_resume, + .reset_resume = usb_serial_resume, .supports_autosuspend = 1, #endif .id_table = option_ids, @@ -1391,6 +1393,7 @@ static struct usb_serial_driver option_1port_device = { #ifdef CONFIG_PM .suspend = usb_wwan_suspend, .resume = usb_wwan_resume, + .reset_resume = usb_wwan_resume, /* add for 4G module */ #endif }; diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index e4b199cb..f8843e98 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -50,7 +50,8 @@ static struct usb_driver usb_serial_driver = { .disconnect = usb_serial_disconnect, .suspend = usb_serial_suspend, .resume = usb_serial_resume, - .no_dynamic_id = 1, + .reset_resume = usb_serial_resume, //add for air720 4g module + .no_dynamic_id = 1, .supports_autosuspend = 1, }; @@ -1414,6 +1415,7 @@ int usb_serial_register_drivers(struct usb_driver *udriver, udriver->supports_autosuspend = 1; udriver->suspend = usb_serial_suspend; udriver->resume = usb_serial_resume; + udriver->reset_resume = usb_serial_resume; /* add for 4G module */ rc = usb_register(udriver); if (rc) return rc; diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index 820436ec..79539533 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c @@ -480,6 +480,14 @@ static struct urb *usb_wwan_setup_urb(struct usb_serial *serial, int endpoint, usb_sndbulkpipe(serial->dev, endpoint) | dir, buf, len, callback, ctx); + //add air720 4g module + if (dir == USB_DIR_OUT) + { + struct usb_device_descriptor *desc=&serial->dev->descriptor; + if (desc->idVendor==cpu_to_le16(0x1286) + && desc->idProduct==cpu_to_le16(0x4e3d)) + urb->transfer_flags |= URB_ZERO_PACKET; + } return urb; } @@ -560,12 +568,6 @@ int usb_wwan_startup(struct usb_serial *serial) } usb_set_serial_port_data(port, portdata); - - if (!port->interrupt_in_urb) - continue; - err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); - if (err) - dbg("%s: submit irq_in urb failed %d", __func__, err); } usb_wwan_setup_urbs(serial); return 0; diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index a290be51..65b07f4d 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -23,6 +23,8 @@ source "drivers/gpu/drm/Kconfig" source "drivers/gpu/stub/Kconfig" +source "drivers/gpu/ion/Kconfig" + config VGASTATE tristate default n diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index e5f74416..d409352f 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include "../w1.h" @@ -84,7 +86,8 @@ static const u8 ds2482_chan_rd[8] = static int ds2482_probe(struct i2c_client *client, const struct i2c_device_id *id); static int ds2482_remove(struct i2c_client *client); - +static int ds2482_suspend(struct device *dev); +static int ds2482_resume(struct device *dev); /** * Driver data (common to all clients) @@ -94,10 +97,16 @@ static const struct i2c_device_id ds2482_id[] = { { } }; +static const struct dev_pm_ops ds2482_pm_ops = { + .suspend = ds2482_suspend, + .resume = ds2482_resume, +}; + static struct i2c_driver ds2482_driver = { .driver = { .owner = THIS_MODULE, .name = "ds2482", + .pm = &ds2482_pm_ops, }, .probe = ds2482_probe, .remove = ds2482_remove, @@ -119,6 +128,7 @@ struct ds2482_w1_chan { struct ds2482_data { struct i2c_client *client; struct mutex access_lock; + int slpz_gpio; /* 1-wire interface(s) */ int w1_count; /* 1 or 8 */ @@ -407,11 +417,31 @@ static u8 ds2482_w1_reset_bus(void *data) return retval; } +static int ds2482_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ds2482_data *data = i2c_get_clientdata(client); + + if (data->slpz_gpio >= 0) + gpio_set_value(data->slpz_gpio, 0); + return 0; +} + +static int ds2482_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ds2482_data *data = i2c_get_clientdata(client); + + if (data->slpz_gpio >= 0) + gpio_set_value(data->slpz_gpio, 1); + return 0; +} static int ds2482_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ds2482_data *data; + struct ds2482_platform_data *pdata; int err = -ENODEV; int temp1; int idx; @@ -476,6 +506,16 @@ static int ds2482_probe(struct i2c_client *client, } } + pdata = client->dev.platform_data; + data->slpz_gpio = pdata ? pdata->slpz_gpio : -1; + + if (data->slpz_gpio >= 0) { + err = gpio_request_one(data->slpz_gpio, GPIOF_OUT_INIT_HIGH, + "ds2482.slpz"); + if (err < 0) + goto exit_w1_remove; + } + return 0; exit_w1_remove: @@ -500,6 +540,11 @@ static int ds2482_remove(struct i2c_client *client) w1_remove_master_device(&data->w1_ch[idx].w1_bm); } + if (data->slpz_gpio >= 0) { + gpio_set_value(data->slpz_gpio, 0); + gpio_free(data->slpz_gpio); + } + /* Free the memory */ kfree(data); return 0; diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 37096246..87f8a532 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -349,6 +349,30 @@ config IMX2_WDT To compile this driver as a module, choose M here: the module will be called imx2_wdt. +choice + prompt "ANYKA ak39 watchdog" + default AK39_WATCHDOG + depends on ARCH_AK39 + +config AK39_WATCHDOG + bool "RTC" + depends on ARCH_AK39 + help + Say Y here if you are using the rtc watchdog + +config AK39_WATCHDOG_TOP + bool "TOP" + depends on ARCH_AK39 + help + Say Y here if you are using the top watchdog + +config AK39_WATCHDOG_NONE + bool "NONE" + depends on ARCH_AK39 + help + Say Y here if you are not useing any watchdog + +endchoice # AVR32 Architecture config AT32AP700X_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index e8f479a1..ec500e42 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -167,3 +167,5 @@ obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o +obj-$(CONFIG_AK39_WATCHDOG) += ak39_wdt.o +obj-$(CONFIG_AK39_WATCHDOG_TOP) += ak39_top_wdt.o diff --git a/drivers/watchdog/ak39_top_wdt.c b/drivers/watchdog/ak39_top_wdt.c new file mode 100644 index 00000000..666e6bbc --- /dev/null +++ b/drivers/watchdog/ak39_top_wdt.c @@ -0,0 +1,315 @@ +/* + * drivers/char/watchdog/ak98_wdt.c + * + * Watchdog driver for ANYKA ak98 processors + * + * Author: Wenyong Zhou + * + * Adapted from the IXP2000 watchdog driver by Lennert Buytenhek. + * The original version carries these notices: + * + * Author: Deepak Saxena + * + * Copyright 2004 (c) MontaVista, Software, Inc. + * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +//#define SELECT_WTC 1 +//#define SELECT_RTC 0 + +//#define WDT_DEBUG +#undef PDEBUG +#ifdef WDT_DEBUG +#define PDEBUG(fmt, args...) printk(KERN_INFO fmt,## args) +#else +#define PDEBUG(fmt, args...) +#endif + +#define UNIT_NS 21333 +#define WATCH_DOG_1_SECOND_SET (1000000000 / UNIT_NS) // 1000000*64/3 + +static int nowayout = WATCHDOG_NOWAYOUT; +static unsigned int def_heartbeat = 8 * WATCH_DOG_1_SECOND_SET; +static unsigned int now_heartbeat = 8 * WATCH_DOG_1_SECOND_SET; + +static unsigned long in_use; +static atomic_t in_write = ATOMIC_INIT(0); +static DEFINE_SPINLOCK(wdt_lock); + +static int ak98_wdt_disable_nb(struct notifier_block *n, unsigned long state,void *cmd); +static struct notifier_block ak98_wdt_nb = { + .notifier_call = ak98_wdt_disable_nb, +}; + +module_param(def_heartbeat, int, 0); +MODULE_PARM_DESC(def_heartbeat, "Watchdog heartbeat in seconds (default 8s)"); + +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); + +void wdt_enable(void) +{ + unsigned int cfg_val = (unsigned int)def_heartbeat; + spin_lock(&wdt_lock); + + REG32(MODULE_WDT_CFG1) = ((0x55 << 24) | cfg_val); + REG32(MODULE_WDT_CFG2) = ((0xaa << 24) | 0x1); + REG32(MODULE_WDT_CFG2) = ((0xaa << 24) | 0x3); + + spin_unlock(&wdt_lock); +} + +static void wdt_disable(void) +{ + //unsigned long val; + + PDEBUG("%s\n", __func__); + spin_lock(&wdt_lock); + REG32(MODULE_WDT_CFG2) = ((0xaa << 24)); + spin_unlock(&wdt_lock); +} + +void wdt_keepalive(unsigned int heartbeat) +{ + unsigned int cfg_val = (unsigned int)heartbeat; + PDEBUG("heartbeat = %x\n", heartbeat); + spin_lock(&wdt_lock); + REG32(MODULE_WDT_CFG1) = ((0x55 << 24) | cfg_val); + REG32(MODULE_WDT_CFG2) = ((0xaa << 24) | 0x1); + REG32(MODULE_WDT_CFG2) = ((0xaa << 24) | 0x3); + spin_unlock(&wdt_lock); +} + +/** + * @brief: ak98_wdt_disable_nb + * @author: zhongjunchao + * @date: 2011-10-11 + * + * @note: Sometimes, system reboot and doesn`t disable watchdog. we disable it + * for safe. + */ +static int ak98_wdt_disable_nb(struct notifier_block *n, unsigned long state,void *cmd) +{ + wdt_disable(); + return NOTIFY_DONE; +} + +static struct watchdog_info ident = { + .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .identity = "ANYKA ak98 Watchdog", +}; + +/** + * @brief: ak98_wdt_ioctl + * @author: zhouwenyong + * @modify: zhongjunchao + * @date: 2011-9-26 + * + * @note: WDIOC_GETSUPPORT,return struct watchdog_info. + * WDIOC_KEEPALIVE, set a default timeout (8s). + * WDIOC_SETTIMEOUT, set a timeout value what you want (max 8s). + * WDIOC_GETTIMEOUT, query the current timeout. + * More detail, refer to kernel/Documentation/watchdog/watchdog-api.txt + */ +static long ak98_wdt_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + int ret = -ENOTTY; + int time; + + switch (cmd) { + + case WDIOC_GETSUPPORT: + return copy_to_user((struct watchdog_info *)argp, &ident, + sizeof(ident)) ? -EFAULT : 0; + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + + case WDIOC_KEEPALIVE: + wdt_keepalive(def_heartbeat); + now_heartbeat = def_heartbeat; + return 0; + + case WDIOC_SETTIMEOUT: + /*if (get_user(time, p)) + return -EFAULT; + + PDEBUG("timeout = %d\n", time); + + if (time <= 0 || time > 8) + return -EINVAL;*/ + + if(get_user(time, p)) + return -EFAULT; + + PDEBUG("timeout = %d\n", time); + + if(time == -1) + { + PDEBUG("\n[close watch_dog]\n"); + wdt_disable(); + nowayout = 0; + atomic_set(&in_write, 0); + PDEBUG("\n[MODULE_WDT_CFG2]:%lu\n",REG32(MODULE_WDT_CFG2)); + return 0; + } + + if (time > 8) + return -EINVAL; + + now_heartbeat = time * WATCH_DOG_1_SECOND_SET; + wdt_keepalive(now_heartbeat); + return 0; + + case WDIOC_GETTIMEOUT: + return put_user((now_heartbeat + 1) / WATCH_DOG_1_SECOND_SET, p); + + default: + return -ENOTTY; + } + + return ret; +} + +/** + * @brief: ak98_wdt_write + * @author: zhouwenyong + * @modify: zhongjunchao + * @date: 2011-9-26 + * + * @note: We support "Magic Close" that driver will not disable the watchdog unless + * a specific magic character 'V' has been sent to /dev/watchdog just before + * closing the file. + */ +static ssize_t ak98_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ + if (len) { + size_t i; + + atomic_set(&in_write, 1); + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') { + PDEBUG("Detect \"V\" Magic Character\n"); + atomic_set(&in_write, 0); + } + } + wdt_keepalive(def_heartbeat); + } + + return len; +} + +/** + * @brief: ak98_wdt_open + * @author: zhouwenyong + * @modify: zhongjunchao + * @date: 2011-9-26 + * + * @note: We only support one process open /dev/watchdog, to avoid overwrite watchdog + * timeout value. + */ +static int ak98_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &in_use)) + return -EBUSY; + + if (nowayout) + __module_get(THIS_MODULE); + + wdt_enable(); + + return nonseekable_open(inode, file); +} + +/** + * @brief: ak98_wdt_release + * @author: zhouwenyong + * @modify: zhongjunchao + * @date: 2011-9-26 + * + * @note: When open CONFIG_WATCHDOG_NOWAYOUT option, nowayout = 1. + * When you open and write to /dev/watchdog, please write magic character 'V' + * before close the file. If not, watchdog will not disable after close + * /dev/watchdog. + */ +static int ak98_wdt_release(struct inode *inode, struct file *file) +{ + if (nowayout) + printk(KERN_INFO "WATCHDOG: Driver support nowayout option -" + "no way to disable watchdog\n"); + else if (atomic_read(&in_write)) + printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - " + "timer will not stop\n"); + else + wdt_disable(); + clear_bit(0, &in_use); + + return 0; +} + +static const struct file_operations ak98_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = ak98_wdt_write, + .unlocked_ioctl = ak98_wdt_ioctl, + .open = ak98_wdt_open, + .release = ak98_wdt_release, +}; + +static struct miscdevice ak98_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &ak98_wdt_fops, +}; + +static int __init ak98_wdt_init(void) +{ + //spin_lock(&wdt_lock); + //ak_rtc_power(RTC_ON); + //spin_unlock(&wdt_lock); + register_reboot_notifier(&ak98_wdt_nb); + + return misc_register(&ak98_wdt_miscdev); +} + +static void __exit ak98_wdt_exit(void) +{ + unregister_reboot_notifier(&ak98_wdt_nb); + //ak_rtc_power(RTC_OFF); + + misc_deregister(&ak98_wdt_miscdev); +} +module_init(ak98_wdt_init); +module_exit(ak98_wdt_exit); + +MODULE_DESCRIPTION("ANYKA ak98 Watchdog"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/ak39_wdt.c b/drivers/watchdog/ak39_wdt.c new file mode 100644 index 00000000..adbf719f --- /dev/null +++ b/drivers/watchdog/ak39_wdt.c @@ -0,0 +1,362 @@ +/* + * drivers/char/watchdog/ak98_wdt.c + * + * Watchdog driver for ANYKA ak98 processors + * + * Author: Wenyong Zhou + * + * Adapted from the IXP2000 watchdog driver by Lennert Buytenhek. + * The original version carries these notices: + * + * Author: Deepak Saxena + * + * Copyright 2004 (c) MontaVista, Software, Inc. + * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define SELECT_WTC 1 +#define SELECT_RTC 0 + +//#define WDT_DEBUG +#undef PDEBUG +#ifdef WDT_DEBUG +#define PDEBUG(fmt, args...) printk(KERN_INFO fmt,## args) +#else +#define PDEBUG(fmt, args...) +#endif + +static int nowayout = WATCHDOG_NOWAYOUT; +static unsigned int def_heartbeat = (0x1FFF); /* Default is 8 seconds */ +static unsigned int now_heartbeat = (0x1FFF); + +static unsigned long in_use; +static atomic_t in_write = ATOMIC_INIT(0); +static DEFINE_SPINLOCK(wdt_lock); + +static int ak98_wdt_disable_nb(struct notifier_block *n, unsigned long state,void *cmd); +static struct notifier_block ak98_wdt_nb = { + .notifier_call = ak98_wdt_disable_nb, +}; + +module_param(def_heartbeat, int, 0); +MODULE_PARM_DESC(def_heartbeat, "Watchdog heartbeat in seconds (default 8s)"); + +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); + +static void select_wdt_rtc(int which) +{ + unsigned long val; + + val = ak_rtc_read(AK_RTC_SETTING); + if (which == 0) + val &= ~(1 << 10); + else + val |= (1 << 10); + ak_rtc_write(AK_RTC_SETTING, val); +} + +void wdt_enable(void) +{ + unsigned long val; + + spin_lock(&wdt_lock); + select_wdt_rtc(SELECT_WTC); + + //enable watchdog timer + val = ak_rtc_read(AK_WDT_RTC_TIMER_CONF); + val |= (1<<13); + ak_rtc_write(AK_WDT_RTC_TIMER_CONF, val); + + //set timer + val = ak_rtc_read(AK_WDT_RTC_TIMER_CONF); + val &= (1<<13); + val |= (def_heartbeat & 0x1FFF); + ak_rtc_write(AK_WDT_RTC_TIMER_CONF, val); + + //open watchdog and watchdog output + val = ak_rtc_read(AK_RTC_SETTING); + val |= ((1<<5) | (1<<2)); + val &= ~(1<<11); + ak_rtc_write(AK_RTC_SETTING, val); + + select_wdt_rtc(SELECT_RTC); + spin_unlock(&wdt_lock); +} + +static void wdt_disable(void) +{ + unsigned long val; + + spin_lock(&wdt_lock); + select_wdt_rtc(SELECT_WTC); + + //clear watchdog timer + val = ak_rtc_read(AK_RTC_SETTING); + val |= (1<<6); + ak_rtc_write(AK_RTC_SETTING, val); + + //disable watchdog timer + val = ak_rtc_read(AK_WDT_RTC_TIMER_CONF); + val &= ~(1<<13); + ak_rtc_write(AK_WDT_RTC_TIMER_CONF, val); + + //close watchdog and watchdog output + val = ak_rtc_read(AK_RTC_SETTING); + val &= ~((1<<2) | (1<<5)); + ak_rtc_write(AK_RTC_SETTING, val); + + select_wdt_rtc(SELECT_RTC); + spin_unlock(&wdt_lock); +} + +void wdt_keepalive(unsigned int heartbeat) +{ + unsigned long val; + + PDEBUG("heartbeat = %x\n", heartbeat); + spin_lock(&wdt_lock); + select_wdt_rtc(SELECT_WTC); + + //clear watchdog timer + val = ak_rtc_read(AK_RTC_SETTING); + val |= (1<<6); + ak_rtc_write(AK_RTC_SETTING, val); + + val = ak_rtc_read(AK_WDT_RTC_TIMER_CONF); + val &= (1<<13); + val |= (heartbeat & 0x1FFF); + ak_rtc_write(AK_WDT_RTC_TIMER_CONF, val); + + select_wdt_rtc(SELECT_RTC); + spin_unlock(&wdt_lock); +} + +/** + * @brief: ak98_wdt_disable_nb + * @author: zhongjunchao + * @date: 2011-10-11 + * + * @note: Sometimes, system reboot and doesn`t disable watchdog. we disable it + * for safe. + */ +static int ak98_wdt_disable_nb(struct notifier_block *n, unsigned long state,void *cmd) +{ + wdt_disable(); + return NOTIFY_DONE; +} + +static struct watchdog_info ident = { + .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .identity = "ANYKA ak98 Watchdog", +}; + +/** + * @brief: ak98_wdt_ioctl + * @author: zhouwenyong + * @modify: zhongjunchao + * @date: 2011-9-26 + * + * @note: WDIOC_GETSUPPORT,return struct watchdog_info. + * WDIOC_KEEPALIVE, set a default timeout (8s). + * WDIOC_SETTIMEOUT, set a timeout value what you want (max 8s). + * WDIOC_GETTIMEOUT, query the current timeout. + * More detail, refer to kernel/Documentation/watchdog/watchdog-api.txt + */ +static long ak98_wdt_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + int ret = -ENOTTY; + int time; + + switch (cmd) { + + case WDIOC_GETSUPPORT: + return copy_to_user((struct watchdog_info *)argp, &ident, + sizeof(ident)) ? -EFAULT : 0; + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + + case WDIOC_KEEPALIVE: + wdt_keepalive(def_heartbeat); + now_heartbeat = def_heartbeat; + return 0; + + case WDIOC_SETTIMEOUT: + /*if (get_user(time, p)) + return -EFAULT; + + PDEBUG("timeout = %d\n", time); + if (time <= 0 || time > 8) + return -EINVAL;*/ + + if(get_user(time, p)) + return -EFAULT; + + PDEBUG("timeout = %d\n", time); + + if(time == -1) + { + PDEBUG("\n[close watch dog]\n"); + wdt_disable(); + return 0; + } + + if(time > 8) + return -EINVAL; + + now_heartbeat = time * 1024 - 1; + wdt_keepalive(now_heartbeat); + return 0; + + case WDIOC_GETTIMEOUT: + return put_user((now_heartbeat + 1)/1024, p); + + default: + return -ENOTTY; + } + + return ret; +} + +/** + * @brief: ak98_wdt_write + * @author: zhouwenyong + * @modify: zhongjunchao + * @date: 2011-9-26 + * + * @note: We support "Magic Close" that driver will not disable the watchdog unless + * a specific magic character 'V' has been sent to /dev/watchdog just before + * closing the file. + */ +static ssize_t ak98_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ + if (len) { + size_t i; + + atomic_set(&in_write, 1); + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') { + PDEBUG("Detect \"V\" Magic Character\n"); + atomic_set(&in_write, 0); + } + } + wdt_keepalive(def_heartbeat); + } + + return len; +} + +/** + * @brief: ak98_wdt_open + * @author: zhouwenyong + * @modify: zhongjunchao + * @date: 2011-9-26 + * + * @note: We only support one process open /dev/watchdog, to avoid overwrite watchdog + * timeout value. + */ +static int ak98_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &in_use)) + return -EBUSY; + + if (nowayout) + __module_get(THIS_MODULE); + + wdt_enable(); + + return nonseekable_open(inode, file); +} + +/** + * @brief: ak98_wdt_release + * @author: zhouwenyong + * @modify: zhongjunchao + * @date: 2011-9-26 + * + * @note: When open CONFIG_WATCHDOG_NOWAYOUT option, nowayout = 1. + * When you open and write to /dev/watchdog, please write magic character 'V' + * before close the file. If not, watchdog will not disable after close + * /dev/watchdog. + */ +static int ak98_wdt_release(struct inode *inode, struct file *file) +{ + if (nowayout) + printk(KERN_INFO "WATCHDOG: Driver support nowayout option -" + "no way to disable watchdog\n"); + else if (atomic_read(&in_write)) + printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - " + "timer will not stop\n"); + else + wdt_disable(); + clear_bit(0, &in_use); + + return 0; +} + +static const struct file_operations ak98_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = ak98_wdt_write, + .unlocked_ioctl = ak98_wdt_ioctl, + .open = ak98_wdt_open, + .release = ak98_wdt_release, +}; + +static struct miscdevice ak98_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &ak98_wdt_fops, +}; + +static int __init ak98_wdt_init(void) +{ + spin_lock(&wdt_lock); + ak_rtc_power(RTC_ON); + spin_unlock(&wdt_lock); + register_reboot_notifier(&ak98_wdt_nb); + + return misc_register(&ak98_wdt_miscdev); +} + +static void __exit ak98_wdt_exit(void) +{ + unregister_reboot_notifier(&ak98_wdt_nb); + ak_rtc_power(RTC_OFF); + + misc_deregister(&ak98_wdt_miscdev); +} +module_init(ak98_wdt_init); +module_exit(ak98_wdt_exit); + +MODULE_DESCRIPTION("ANYKA ak98 Watchdog"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/fs/Kconfig b/fs/Kconfig index f95ae3a0..87ae3fa3 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -99,10 +99,11 @@ endmenu endif # BLOCK if BLOCK -menu "DOS/FAT/NT Filesystems" +menu "DOS/FAT/NT/EXFAT Filesystems" source "fs/fat/Kconfig" source "fs/ntfs/Kconfig" +source "fs/exfat/Kconfig" endmenu endif # BLOCK @@ -203,6 +204,10 @@ source "fs/hfsplus/Kconfig" source "fs/befs/Kconfig" source "fs/bfs/Kconfig" source "fs/efs/Kconfig" + +# Patched by YAFFS +source "fs/yaffs2/Kconfig" + source "fs/jffs2/Kconfig" # UBIFS File system configuration source "fs/ubifs/Kconfig" diff --git a/fs/Makefile b/fs/Makefile index 2fb97793..c05a41fd 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -78,6 +78,7 @@ obj-$(CONFIG_HUGETLBFS) += hugetlbfs/ obj-$(CONFIG_CODA_FS) += coda/ obj-$(CONFIG_MINIX_FS) += minix/ obj-$(CONFIG_FAT_FS) += fat/ +obj-$(CONFIG_EXFAT_FS) += exfat/ obj-$(CONFIG_BFS_FS) += bfs/ obj-$(CONFIG_ISO9660_FS) += isofs/ obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+ @@ -125,3 +126,6 @@ obj-$(CONFIG_GFS2_FS) += gfs2/ obj-y += exofs/ # Multiple modules obj-$(CONFIG_CEPH_FS) += ceph/ obj-$(CONFIG_PSTORE) += pstore/ + +# Patched by YAFFS +obj-$(CONFIG_YAFFS_FS) += yaffs2/ diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 33c9599c..0863bab4 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -87,7 +88,7 @@ */ /* Epoll private bits inside the event mask */ -#define EP_PRIVATE_BITS (EPOLLONESHOT | EPOLLET) +#define EP_PRIVATE_BITS (EPOLLWAKEUP | EPOLLONESHOT | EPOLLET) /* Maximum number of nesting allowed inside epoll sets */ #define EP_MAX_NESTS 4 @@ -154,6 +155,9 @@ struct epitem { /* List header used to link this item to the "struct file" items list */ struct list_head fllink; + /* wakeup_source used when EPOLLWAKEUP is set */ + struct wakeup_source *ws; + /* The structure that describe the interested events and the source fd */ struct epoll_event event; }; @@ -194,6 +198,9 @@ struct eventpoll { */ struct epitem *ovflist; + /* wakeup_source used when ep_scan_ready_list is running */ + struct wakeup_source *ws; + /* The user that created the eventpoll descriptor */ struct user_struct *user; @@ -588,8 +595,10 @@ static int ep_scan_ready_list(struct eventpoll *ep, * queued into ->ovflist but the "txlist" might already * contain them, and the list_splice() below takes care of them. */ - if (!ep_is_linked(&epi->rdllink)) + if (!ep_is_linked(&epi->rdllink)) { list_add_tail(&epi->rdllink, &ep->rdllist); + __pm_stay_awake(epi->ws); + } } /* * We need to set back ep->ovflist to EP_UNACTIVE_PTR, so that after @@ -602,6 +611,7 @@ static int ep_scan_ready_list(struct eventpoll *ep, * Quickly re-inject items left on "txlist". */ list_splice(&txlist, &ep->rdllist); + __pm_relax(ep->ws); if (!list_empty(&ep->rdllist)) { /* @@ -656,6 +666,8 @@ static int ep_remove(struct eventpoll *ep, struct epitem *epi) list_del_init(&epi->rdllink); spin_unlock_irqrestore(&ep->lock, flags); + wakeup_source_unregister(epi->ws); + /* At this point it is safe to free the eventpoll item */ kmem_cache_free(epi_cache, epi); @@ -706,6 +718,7 @@ static void ep_free(struct eventpoll *ep) mutex_unlock(&epmutex); mutex_destroy(&ep->mtx); free_uid(ep->user); + wakeup_source_unregister(ep->ws); kfree(ep); } @@ -737,6 +750,7 @@ static int ep_read_events_proc(struct eventpoll *ep, struct list_head *head, * callback, but it's not actually ready, as far as * caller requested events goes. We can remove it here. */ + __pm_relax(epi->ws); list_del_init(&epi->rdllink); } } @@ -927,13 +941,23 @@ static int ep_poll_callback(wait_queue_t *wait, unsigned mode, int sync, void *k if (epi->next == EP_UNACTIVE_PTR) { epi->next = ep->ovflist; ep->ovflist = epi; + if (epi->ws) { + /* + * Activate ep->ws since epi->ws may get + * deactivated at any time. + */ + __pm_stay_awake(ep->ws); + } + } goto out_unlock; } /* If this file is already in the ready list we exit soon */ - if (!ep_is_linked(&epi->rdllink)) + if (!ep_is_linked(&epi->rdllink)) { list_add_tail(&epi->rdllink, &ep->rdllist); + __pm_stay_awake(epi->ws); + } /* * Wake up ( if active ) both the eventpoll wait list and the ->poll() @@ -1091,6 +1115,30 @@ static int reverse_path_check(void) return error; } +static int ep_create_wakeup_source(struct epitem *epi) +{ + const char *name; + + if (!epi->ep->ws) { + epi->ep->ws = wakeup_source_register("eventpoll"); + if (!epi->ep->ws) + return -ENOMEM; + } + + name = epi->ffd.file->f_path.dentry->d_name.name; + epi->ws = wakeup_source_register(name); + if (!epi->ws) + return -ENOMEM; + + return 0; +} + +static void ep_destroy_wakeup_source(struct epitem *epi) +{ + wakeup_source_unregister(epi->ws); + epi->ws = NULL; +} + /* * Must be called with "mtx" held. */ @@ -1118,6 +1166,13 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event, epi->event = *event; epi->nwait = 0; epi->next = EP_UNACTIVE_PTR; + if (epi->event.events & EPOLLWAKEUP) { + error = ep_create_wakeup_source(epi); + if (error) + goto error_create_wakeup_source; + } else { + epi->ws = NULL; + } /* Initialize the poll table using the queue callback */ epq.epi = epi; @@ -1164,6 +1219,7 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event, /* If the file is already "ready" we drop it inside the ready list */ if ((revents & event->events) && !ep_is_linked(&epi->rdllink)) { list_add_tail(&epi->rdllink, &ep->rdllist); + __pm_stay_awake(epi->ws); /* Notify waiting tasks that events are available */ if (waitqueue_active(&ep->wq)) @@ -1204,6 +1260,9 @@ error_unregister: list_del_init(&epi->rdllink); spin_unlock_irqrestore(&ep->lock, flags); + wakeup_source_unregister(epi->ws); + +error_create_wakeup_source: kmem_cache_free(epi_cache, epi); return error; @@ -1229,6 +1288,12 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, struct epoll_even epi->event.events = event->events; /* need barrier below */ pt._key = event->events; epi->event.data = event->data; /* protected by mtx */ + if (epi->event.events & EPOLLWAKEUP) { + if (!epi->ws) + ep_create_wakeup_source(epi); + } else if (epi->ws) { + ep_destroy_wakeup_source(epi); + } /* * The following barrier has two effects: @@ -1264,6 +1329,7 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, struct epoll_even spin_lock_irq(&ep->lock); if (!ep_is_linked(&epi->rdllink)) { list_add_tail(&epi->rdllink, &ep->rdllist); + __pm_stay_awake(epi->ws); /* Notify waiting tasks that events are available */ if (waitqueue_active(&ep->wq)) @@ -1302,6 +1368,18 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head, !list_empty(head) && eventcnt < esed->maxevents;) { epi = list_first_entry(head, struct epitem, rdllink); + /* + * Activate ep->ws before deactivating epi->ws to prevent + * triggering auto-suspend here (in case we reactive epi->ws + * below). + * + * This could be rearranged to delay the deactivation of epi->ws + * instead, but then epi->ws would temporarily be out of sync + * with ep_is_linked(). + */ + if (epi->ws && epi->ws->active) + __pm_stay_awake(ep->ws); + __pm_relax(epi->ws); list_del_init(&epi->rdllink); pt._key = epi->event.events; @@ -1318,6 +1396,7 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head, if (__put_user(revents, &uevent->events) || __put_user(epi->event.data, &uevent->data)) { list_add(&epi->rdllink, head); + __pm_stay_awake(epi->ws); return eventcnt ? eventcnt : -EFAULT; } eventcnt++; @@ -1337,6 +1416,7 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head, * poll callback will queue them in ep->ovflist. */ list_add_tail(&epi->rdllink, &ep->rdllist); + __pm_stay_awake(epi->ws); } } } @@ -1649,6 +1729,10 @@ SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd, if (!tfile->f_op || !tfile->f_op->poll) goto error_tgt_fput; + /* Check if EPOLLWAKEUP is allowed */ + if ((epds.events & EPOLLWAKEUP) && !capable(CAP_EPOLLWAKEUP)) + epds.events &= ~EPOLLWAKEUP; + /* * We have to check that the file structure underneath the file descriptor * the user passed to us _is_ an eventpoll file. And also we do not permit diff --git a/fs/exfat/Kconfig b/fs/exfat/Kconfig new file mode 100644 index 00000000..60e159df --- /dev/null +++ b/fs/exfat/Kconfig @@ -0,0 +1,40 @@ +config EXFAT_FS + tristate "exFAT fs support" + select NLS + default y + help + This adds support for the exFAT file system. + +config EXFAT_DISCARD + bool "enable discard support" + depends on EXFAT_FS + default y + +config EXFAT_DELAYED_SYNC + bool "enable delayed sync" + depends on EXFAT_FS + default n + +config EXFAT_KERNEL_DEBUG + bool "enable kernel debug features via ioctl" + depends on EXFAT_FS + default n + +config EXFAT_DEBUG_MSG + bool "print debug messages" + depends on EXFAT_FS + default n + +config EXFAT_DEFAULT_CODEPAGE + int "Default codepage for exFAT" + default 437 + depends on EXFAT_FS + help + This option should be set to the codepage of your exFAT filesystems. + +config EXFAT_DEFAULT_IOCHARSET + string "Default iocharset for exFAT" + default "utf8" + depends on EXFAT_FS + help + Set this to the default input/output character set you'd like exFAT to use. diff --git a/fs/exfat/LICENSE b/fs/exfat/LICENSE new file mode 100644 index 00000000..ff0812fd --- /dev/null +++ b/fs/exfat/LICENSE @@ -0,0 +1,359 @@ +Valid-License-Identifier: GPL-2.0 +Valid-License-Identifier: GPL-2.0-only +Valid-License-Identifier: GPL-2.0+ +Valid-License-Identifier: GPL-2.0-or-later +SPDX-URL: https://spdx.org/licenses/GPL-2.0.html +Usage-Guide: + To use this license in source code, put one of the following SPDX + tag/value pairs into a comment according to the placement + guidelines in the licensing rules documentation. + For 'GNU General Public License (GPL) version 2 only' use: + SPDX-License-Identifier: GPL-2.0 + or + SPDX-License-Identifier: GPL-2.0-only + For 'GNU General Public License (GPL) version 2 or any later version' use: + SPDX-License-Identifier: GPL-2.0+ + or + SPDX-License-Identifier: GPL-2.0-or-later +License-Text: + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/fs/exfat/Makefile b/fs/exfat/Makefile new file mode 100644 index 00000000..ac657e68 --- /dev/null +++ b/fs/exfat/Makefile @@ -0,0 +1,56 @@ +# +# Makefile for Linux FAT12/FAT16/FAT32(VFAT)/FAT64(ExFAT) filesystem driver. +# + +ifneq ($(KERNELRELEASE),) +# call from kernel build system + +obj-$(CONFIG_EXFAT_FS) += exfat.o + +exfat-objs := super.o core.o core_exfat.o blkdev.o fatent.o cache.o \ + nls.o misc.o extent.o xattr.o + +else +# external module build + +EXTRA_FLAGS += -I$(PWD) + +# +# KDIR is a path to a directory containing kernel source. +# It can be specified on the command line passed to make to enable the module to +# be built and installed for a kernel other than the one currently running. +# By default it is the path to the symbolic link created when +# the current kernel's modules were installed, but +# any valid path to the directory in which the target kernel's source is located +# can be provided on the command line. +# +KDIR := /lib/modules/$(shell uname -r)/build +MDIR := /lib/modules/$(shell uname -r) +PWD := $(shell pwd) +KREL := $(shell cd ${KDIR} && make -s kernelrelease) +MDIR := /lib/modules/${KREL} +PWD := $(shell pwd) + +export CONFIG_EXFAT_FS := m + +all: + $(MAKE) -C $(KDIR) M=$(PWD) modules + +clean: + $(MAKE) -C $(KDIR) M=$(PWD) clean + +help: + $(MAKE) -C $(KDIR) M=$(PWD) help + +install: exfat.ko + rm -f ${MDIR}/kernel/fs/exfat/exfat.ko + install -m644 -b -D exfat.ko ${MDIR}/kernel/fs/exfat/exfat.ko + depmod -aq + +uninstall: + rm -rf ${MDIR}/kernel/fs/exfat + depmod -aq + +endif + +.PHONY : all clean install uninstall diff --git a/fs/exfat/README.md b/fs/exfat/README.md new file mode 100644 index 00000000..ad0462f6 --- /dev/null +++ b/fs/exfat/README.md @@ -0,0 +1,78 @@ +exfat-nofuse +============ + +Android ARM Linux non-fuse read/write kernel driver for the exFAT, FAT12, FAT16 and vfat (FAT32) file systems.
+Originally ported from Android kernel v3.0. + +Will not work on no ARM builds. +Till someone can fix that :) + + +Kudos to ksv1986 for the mutex patch!
+Thanks to JackNorris for being awesome and providing the clear_inode() patch.
+
+Big thanks to lqs for completing the driver! +Big thanks to benpicco for fixing 3.11.y compatibility! + + +Special thanks to github user AndreiLux for spreading the word about the leak!
+ + +Installing as a stand-alone module: +==================================== + + make + sudo make install + +To load the driver manually, run this as root: + + modprobe exfat + +You may also specify custom toolchains by using CROSS_COMPILE flag, in my case: +>CROSS_COMPILE=../dorimanx-SG2-I9100-Kernel/android-toolchain/bin/arm-eabi- + +Installing as a part of the kernel: +====================================== + +Let's take [linux] as the path to your kernel source dir... + + cd [linux] + cp -rvf exfat-nofuse [linux]/fs/exfat + +edit [linux]/fs/Kconfig +``` + menu "DOS/FAT/NT Filesystems" + + source "fs/fat/Kconfig" + +source "fs/exfat/Kconfig" + source "fs/ntfs/Kconfig" +``` + + endmenu + +edit [linux]/fs/Makefile +``` + obj-$(CONFIG_FAT_FS) += fat/ + +obj-$(CONFIG_EXFAT_FS) += exfat/ + obj-$(CONFIG_BFS_FS) += bfs/ +``` + + cd [linux] + make menuconfig + +Go to: +> File systems > DOS/FAT/NT +> check exfat as MODULE (M) +> (437) Default codepage for exFAT +> (utf8) Default iocharset for exFAT + +> ESC to main menu +> Save an Alternate Configuration File +> ESC ESC + +build your kernel + +Have fun. + +Free Software for the Free Minds! +================================= diff --git a/fs/exfat/api.h b/fs/exfat/api.h new file mode 100644 index 00000000..5b13c0a1 --- /dev/null +++ b/fs/exfat/api.h @@ -0,0 +1,265 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + */ + +#ifndef _EXFAT_API_H +#define _EXFAT_API_H + +#include "config.h" +#include "exfat_fs.h" + +/* cache size (in number of sectors) */ +/* (should be an exponential value of 2) */ +#define FAT_CACHE_SIZE 128 +#define FAT_CACHE_HASH_SIZE 64 +#define BUF_CACHE_SIZE 256 +#define BUF_CACHE_HASH_SIZE 64 + +/* Read-ahead related */ +/* First config vars. should be pow of 2 */ +#define FCACHE_MAX_RA_SIZE (PAGE_SIZE) +#define DCACHE_MAX_RA_SIZE (128*1024) + +/* type values */ +#define TYPE_UNUSED 0x0000 +#define TYPE_DELETED 0x0001 +#define TYPE_INVALID 0x0002 +#define TYPE_CRITICAL_PRI 0x0100 +#define TYPE_BITMAP 0x0101 +#define TYPE_UPCASE 0x0102 +#define TYPE_VOLUME 0x0103 +#define TYPE_DIR 0x0104 +#define TYPE_FILE 0x011F +#define TYPE_SYMLINK 0x015F +#define TYPE_CRITICAL_SEC 0x0200 +#define TYPE_STREAM 0x0201 +#define TYPE_EXTEND 0x0202 +#define TYPE_ACL 0x0203 +#define TYPE_BENIGN_PRI 0x0400 +#define TYPE_GUID 0x0401 +#define TYPE_PADDING 0x0402 +#define TYPE_ACLTAB 0x0403 +#define TYPE_BENIGN_SEC 0x0800 +#define TYPE_ALL 0x0FFF + +/* eio values */ +#define EXFAT_EIO_NONE (0x00000000) +#define EXFAT_EIO_READ (0x00000001) +#define EXFAT_EIO_WRITE (0x00000002) +#define EXFAT_EIO_BDI (0x00000004) + +/* modes for volume allocation unit status */ +#define VOL_AU_STAT_TOTAL (0) +#define VOL_AU_STAT_CLEAN (1) +#define VOL_AU_STAT_FULL (2) + +/* DOS name structure */ +typedef struct { + u8 name[DOS_NAME_LENGTH]; + u8 name_case; +} DOS_NAME_T; + +/* unicode name structure */ +typedef struct { + u16 name[MAX_NAME_LENGTH+3]; /* +3 for null and for converting */ + u16 name_hash; + u8 name_len; +} UNI_NAME_T; + +/* should be merged it to DATE_TIME_T */ +typedef struct { + u16 sec; /* 0 ~ 59 */ + u16 min; /* 0 ~ 59 */ + u16 hour; /* 0 ~ 23 */ + u16 day; /* 1 ~ 31 */ + u16 mon; /* 1 ~ 12 */ + u16 year; /* 0 ~ 127 (since 1980) */ +} TIMESTAMP_T; + + +typedef struct { + u16 Year; + u16 Month; + u16 Day; + u16 Hour; + u16 Minute; + u16 Second; + u16 MilliSecond; +} DATE_TIME_T; + +typedef struct { + u64 Offset; // start sector number of the partition + u64 Size; // in sectors +} PART_INFO_T; + +typedef struct { + u32 SecSize; // sector size in bytes + u64 DevSize; // block device size in sectors +} DEV_INFO_T; + +typedef struct { + u32 ClusterSize; + u32 NumClusters; + u32 FreeClusters; + u32 UsedClusters; +} VOL_INFO_T; + +/* directory structure */ +typedef struct { + u32 dir; + u32 size; + u8 flags; +} CHAIN_T; + +/* hint structure */ +typedef struct { + u32 clu; + union { + u32 off; // cluster offset + s32 eidx; // entry index + }; +} HINT_T; + +typedef struct { + spinlock_t cache_lru_lock; + struct list_head cache_lru; + s32 nr_caches; + u32 cache_valid_id; // for avoiding the race between alloc and free +} EXTENT_T; + +/* first empty entry hint information */ +typedef struct { + s32 eidx; // entry index of a directory + s32 count; // count of continuous empty entry + CHAIN_T cur; // the cluster that first empty slot exists in +} HINT_FEMP_T; + +/* file id structure */ +typedef struct { + CHAIN_T dir; + s32 entry; + u32 type; + u32 attr; + u32 start_clu; + u64 size; + u8 flags; + u8 reserved[3]; // padding + u32 version; // the copy of low 32bit of i_version to check the validation of hint_stat + s64 rwoffset; // file offset or dentry index for readdir + EXTENT_T extent; // extent cache for a file + HINT_T hint_bmap; // hint for cluster last accessed + HINT_T hint_stat; // hint for entry index we try to lookup next time + HINT_FEMP_T hint_femp; // hint for first empty entry +} FILE_ID_T; + +typedef struct { + s8 *lfn; + s8 *sfn; + s32 lfnbuf_len; //usally MAX_UNINAME_BUF_SIZE + s32 sfnbuf_len; //usally MAX_DOSNAME_BUF_SIZE, used only for vfat, not for exfat +} DENTRY_NAMEBUF_T; + +typedef struct { + u32 Attr; + u64 Size; + u32 NumSubdirs; + DATE_TIME_T CreateTimestamp; + DATE_TIME_T ModifyTimestamp; + DATE_TIME_T AccessTimestamp; + DENTRY_NAMEBUF_T NameBuf; +} DIR_ENTRY_T; + +/* cache information */ +typedef struct __cache_entry { + struct __cache_entry *next; + struct __cache_entry *prev; + struct { + struct __cache_entry *next; + struct __cache_entry *prev; + } hash; + u64 sec; + u32 flag; + struct buffer_head *bh; +} cache_ent_t; + +typedef struct { + s32 (*alloc_cluster)(struct super_block *, u32, CHAIN_T *, s32); + s32 (*free_cluster)(struct super_block *, CHAIN_T *, s32); + s32 (*count_used_clusters)(struct super_block *, u32 *); + s32 (*init_dir_entry)(struct super_block *, CHAIN_T *, s32, u32, u32, u64); + s32 (*init_ext_entry)(struct super_block *, CHAIN_T *, s32, s32, UNI_NAME_T *, DOS_NAME_T *); + s32 (*find_dir_entry)(struct super_block *, FILE_ID_T *, CHAIN_T *, UNI_NAME_T *, s32, DOS_NAME_T *, u32); + s32 (*delete_dir_entry)(struct super_block *, CHAIN_T *, s32, s32, s32); + void (*get_uniname_from_ext_entry)(struct super_block *, CHAIN_T *, s32, u16 *); + s32 (*count_ext_entries)(struct super_block *, CHAIN_T *, s32, DENTRY_T *); + s32 (*calc_num_entries)(UNI_NAME_T *); + s32 (*check_max_dentries)(FILE_ID_T *); + u32 (*get_entry_type)(DENTRY_T *); + void (*set_entry_type)(DENTRY_T *, u32); + u32 (*get_entry_attr)(DENTRY_T *); + void (*set_entry_attr)(DENTRY_T *, u32); + u8 (*get_entry_flag)(DENTRY_T *); + void (*set_entry_flag)(DENTRY_T *, u8); + u32 (*get_entry_clu0)(DENTRY_T *); + void (*set_entry_clu0)(DENTRY_T *, u32); + u64 (*get_entry_size)(DENTRY_T *); + void (*set_entry_size)(DENTRY_T *, u64); + void (*get_entry_time)(DENTRY_T *, TIMESTAMP_T *, u8); + void (*set_entry_time)(DENTRY_T *, TIMESTAMP_T *, u8); + u32 (*get_au_stat)(struct super_block *, s32); +} FS_FUNC_T; + +typedef struct __FS_INFO_T { + s32 bd_opened; // opened or not + u32 vol_id; // volume serial number + u64 num_sectors; // num of sectors in volume + u32 num_clusters; // num of clusters in volume + u32 cluster_size; // cluster size in bytes + u32 cluster_size_bits; + u32 sect_per_clus; // cluster size in sectors + u32 sect_per_clus_bits; + u64 FAT1_start_sector; // FAT1 start sector + u64 FAT2_start_sector; // FAT2 start sector + u64 root_start_sector; // root dir start sector + u64 data_start_sector; // data area start sector + u32 num_FAT_sectors; // num of FAT sectors + u32 root_dir; // root dir cluster + u32 dentries_in_root; // num of dentries in root dir + u32 dentries_per_clu; // num of dentries per cluster + u32 vol_flag; // volume dirty flag + struct buffer_head *pbr_bh; // buffer_head of PBR sector + + u32 map_clu; // allocation bitmap start cluster + u32 map_sectors; // num of allocation bitmap sectors + struct buffer_head **vol_amap; // allocation bitmap + + u16 **vol_utbl; // upcase table + + u32 clu_srch_ptr; // cluster search pointer + u32 used_clusters; // number of used clusters + + u32 prev_eio; // block device operation error flag + + FS_FUNC_T *fs_func; + + s32 reserved_clusters; // # of reserved clusters (DA) + void *amap; // AU Allocation Map + + /* fat cache */ + struct { + cache_ent_t pool[FAT_CACHE_SIZE]; + cache_ent_t lru_list; + cache_ent_t hash_list[FAT_CACHE_HASH_SIZE]; + } fcache; + + /* meta cache */ + struct { + cache_ent_t pool[BUF_CACHE_SIZE]; + cache_ent_t lru_list; + cache_ent_t keep_list; // CACHEs in this list will not be kicked by normal lru operations + cache_ent_t hash_list[BUF_CACHE_HASH_SIZE]; + } dcache; +} FS_INFO_T; + +#endif /* _EXFAT_API_H */ diff --git a/fs/exfat/blkdev.c b/fs/exfat/blkdev.c new file mode 100644 index 00000000..fe3a085e --- /dev/null +++ b/fs/exfat/blkdev.c @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * blkdev.c: exFAT Block Device Driver Glue Layer + */ + +#include +#include +#include +#include "exfat.h" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + /* EMPTY */ +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0) */ +static struct backing_dev_info *inode_to_bdi(struct inode *bd_inode) +{ + return bd_inode->i_mapping->backing_dev_info; +} +#endif + +s32 exfat_bdev_open_dev(struct super_block *sb) +{ + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + if (fsi->bd_opened) + return 0; + + fsi->bd_opened = true; + return 0; +} + +s32 exfat_bdev_close_dev(struct super_block *sb) +{ + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + fsi->bd_opened = false; + return 0; +} + +static inline s32 block_device_ejected(struct super_block *sb) +{ + struct inode *bd_inode = sb->s_bdev->bd_inode; + struct backing_dev_info *bdi = inode_to_bdi(bd_inode); + + return (bdi->dev == NULL); +} + +s32 exfat_bdev_check_bdi_valid(struct super_block *sb) +{ + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + if (block_device_ejected(sb)) { + if (!(fsi->prev_eio & EXFAT_EIO_BDI)) { + fsi->prev_eio |= EXFAT_EIO_BDI; + exfat_log_msg(sb, KERN_ERR, "%s: block device is " + "eliminated.(bdi:%p)", __func__, sb->s_bdi); + exfat_debug_warn_on(1); + } + return -ENXIO; + } + + return 0; +} + + +/* Make a readahead request */ +s32 exfat_bdev_readahead(struct super_block *sb, u64 secno, u64 num_secs) +{ + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + u32 sects_per_page = (PAGE_SIZE >> sb->s_blocksize_bits); + struct blk_plug plug; + u64 i; + + if (!fsi->bd_opened) + return -EIO; + + blk_start_plug(&plug); + for (i = 0; i < num_secs; i++) { + if (i && !(i & (sects_per_page - 1))) { +#ifdef MODULE + /* TODO: fix this by using proper APIs */ + blk_finish_plug(&plug); + blk_start_plug(&plug); +#else + blk_flush_plug(current); +#endif + } + sb_breadahead(sb, (sector_t)(secno + i)); + } + blk_finish_plug(&plug); + + return 0; +} + +s32 exfat_bdev_mread(struct super_block *sb, u64 secno, struct buffer_head **bh, u64 num_secs, s32 read) +{ + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + u8 blksize_bits = sb->s_blocksize_bits; + + if (!fsi->bd_opened) + return -EIO; + + brelse(*bh); + + if (read) + *bh = __bread(sb->s_bdev, (sector_t)secno, num_secs << blksize_bits); + else + *bh = __getblk(sb->s_bdev, (sector_t)secno, num_secs << blksize_bits); + + /* read successfully */ + if (*bh) + return 0; + + /* + * patch 1.2.4 : reset ONCE warning message per volume. + */ + if (!(fsi->prev_eio & EXFAT_EIO_READ)) { + fsi->prev_eio |= EXFAT_EIO_READ; + exfat_log_msg(sb, KERN_ERR, "%s: No bh. I/O error.", __func__); + exfat_debug_warn_on(1); + } + + return -EIO; +} + +s32 exfat_bdev_mwrite(struct super_block *sb, u64 secno, struct buffer_head *bh, u64 num_secs, s32 sync) +{ + u64 count; + struct buffer_head *bh2; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + if (!fsi->bd_opened) + return -EIO; + + if (secno == bh->b_blocknr) { + set_buffer_uptodate(bh); + mark_buffer_dirty(bh); + if (sync && (sync_dirty_buffer(bh) != 0)) + return -EIO; + } else { + count = num_secs << sb->s_blocksize_bits; + + bh2 = __getblk(sb->s_bdev, (sector_t)secno, count); + + if (!bh2) + goto no_bh; + + lock_buffer(bh2); + memcpy(bh2->b_data, bh->b_data, count); + set_buffer_uptodate(bh2); + mark_buffer_dirty(bh2); + unlock_buffer(bh2); + if (sync && (sync_dirty_buffer(bh2) != 0)) { + __brelse(bh2); + goto no_bh; + } + __brelse(bh2); + } + return 0; +no_bh: + /* + * patch 1.2.4 : reset ONCE warning message per volume. + */ + if (!(fsi->prev_eio & EXFAT_EIO_WRITE)) { + fsi->prev_eio |= EXFAT_EIO_WRITE; + exfat_log_msg(sb, KERN_ERR, "%s: No bh. I/O error.", __func__); + exfat_debug_warn_on(1); + } + + return -EIO; +} + +s32 exfat_bdev_sync_all(struct super_block *sb) +{ + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + if (!fsi->bd_opened) + return -EIO; + + return sync_blockdev(sb->s_bdev); +} + +/* + * Sector Read/Write Functions + */ +s32 exfat_read_sect(struct super_block *sb, u64 sec, struct buffer_head **bh, s32 read) +{ + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + BUG_ON(!bh); + if ((sec >= fsi->num_sectors) && (fsi->num_sectors > 0)) { + exfat_fs_error_ratelimit(sb, + "%s: out of range (sect:%llu)", __func__, sec); + return -EIO; + } + + if (exfat_bdev_mread(sb, sec, bh, 1, read)) { + exfat_fs_error_ratelimit(sb, + "%s: I/O error (sect:%llu)", __func__, sec); + return -EIO; + } + + return 0; +} + +s32 exfat_write_sect(struct super_block *sb, u64 sec, struct buffer_head *bh, s32 sync) +{ + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + BUG_ON(!bh); + if ((sec >= fsi->num_sectors) && (fsi->num_sectors > 0)) { + exfat_fs_error_ratelimit(sb, + "%s: out of range (sect:%llu)", __func__, sec); + return -EIO; + } + + if (exfat_bdev_mwrite(sb, sec, bh, 1, sync)) { + exfat_fs_error_ratelimit(sb, "%s: I/O error (sect:%llu)", + __func__, sec); + return -EIO; + } + + return 0; +} + +static inline void __blkdev_write_bhs(struct buffer_head **bhs, s32 nr_bhs) +{ + s32 i; + + for (i = 0; i < nr_bhs; i++) + write_dirty_buffer(bhs[i], WRITE); +} + +static inline s32 __blkdev_sync_bhs(struct buffer_head **bhs, s32 nr_bhs) +{ + s32 i, err = 0; + + for (i = 0; i < nr_bhs; i++) { + wait_on_buffer(bhs[i]); + if (!err && !buffer_uptodate(bhs[i])) + err = -EIO; + } + return err; +} + +static inline s32 __buffer_zeroed(struct super_block *sb, u64 blknr, u64 num_secs) +{ + struct buffer_head *bhs[MAX_BUF_PER_PAGE]; + s32 nr_bhs = MAX_BUF_PER_PAGE; + u64 last_blknr = blknr + num_secs; + s32 err, i, n; + struct blk_plug plug; + + /* Zeroing the unused blocks on this cluster */ + n = 0; + blk_start_plug(&plug); + while (blknr < last_blknr) { + bhs[n] = sb_getblk(sb, (sector_t)blknr); + if (!bhs[n]) { + err = -ENOMEM; + blk_finish_plug(&plug); + goto error; + } + memset(bhs[n]->b_data, 0, sb->s_blocksize); + set_buffer_uptodate(bhs[n]); + mark_buffer_dirty(bhs[n]); + + n++; + blknr++; + + if (blknr == last_blknr) + break; + + if (n == nr_bhs) { + __blkdev_write_bhs(bhs, n); + + for (i = 0; i < n; i++) + brelse(bhs[i]); + n = 0; + } + } + __blkdev_write_bhs(bhs, n); + blk_finish_plug(&plug); + + err = __blkdev_sync_bhs(bhs, n); + if (err) + goto error; + + for (i = 0; i < n; i++) + brelse(bhs[i]); + + return 0; + +error: + EMSG("%s: failed zeroed sect %llu\n", __func__, blknr); + for (i = 0; i < n; i++) + bforget(bhs[i]); + + return err; +} + +s32 exfat_write_msect_zero(struct super_block *sb, u64 sec, u64 num_secs) +{ + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + if (((sec+num_secs) > fsi->num_sectors) && (fsi->num_sectors > 0)) { + exfat_fs_error_ratelimit(sb, "%s: out of range(sect:%llu len:%llu)", + __func__, sec, num_secs); + return -EIO; + } + + /* Just return -EAGAIN if it is failed */ + if (__buffer_zeroed(sb, sec, num_secs)) + return -EAGAIN; + + return 0; +} diff --git a/fs/exfat/cache.c b/fs/exfat/cache.c new file mode 100644 index 00000000..2bd94633 --- /dev/null +++ b/fs/exfat/cache.c @@ -0,0 +1,782 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * cache.c: exFAT Cache Manager + */ + +#include /* for mark_page_accessed() */ +#include +#include "exfat.h" +#include "core.h" + +#define DEBUG_HASH_LIST +#define DEBUG_HASH_PREV (0xAAAA5555) +#define DEBUG_HASH_NEXT (0x5555AAAA) + +#define LOCKBIT (0x01) +#define DIRTYBIT (0x02) +#define KEEPBIT (0x04) + +static cache_ent_t *__fcache_find(struct super_block *sb, u64 sec); +static cache_ent_t *__fcache_get(struct super_block *sb); +static void __fcache_insert_hash(struct super_block *sb, cache_ent_t *bp); +static void __fcache_remove_hash(cache_ent_t *bp); + +static cache_ent_t *__dcache_find(struct super_block *sb, u64 sec); +static cache_ent_t *__dcache_get(struct super_block *sb); +static void __dcache_insert_hash(struct super_block *sb, cache_ent_t *bp); +static void __dcache_remove_hash(cache_ent_t *bp); + +static void push_to_mru(cache_ent_t *bp, cache_ent_t *list) +{ + bp->next = list->next; + bp->prev = list; + list->next->prev = bp; + list->next = bp; +} + +static void push_to_lru(cache_ent_t *bp, cache_ent_t *list) +{ + bp->prev = list->prev; + bp->next = list; + list->prev->next = bp; + list->prev = bp; +} + +static void move_to_mru(cache_ent_t *bp, cache_ent_t *list) +{ + bp->prev->next = bp->next; + bp->next->prev = bp->prev; + push_to_mru(bp, list); +} + +static void move_to_lru(cache_ent_t *bp, cache_ent_t *list) +{ + bp->prev->next = bp->next; + bp->next->prev = bp->prev; + push_to_lru(bp, list); +} + +static inline s32 __check_hash_valid(cache_ent_t *bp) +{ +#ifdef DEBUG_HASH_LIST + if ((bp->hash.next == (cache_ent_t *)DEBUG_HASH_NEXT) || + (bp->hash.prev == (cache_ent_t *)DEBUG_HASH_PREV)) { + return -EINVAL; + } +#endif + if ((bp->hash.next == bp) || (bp->hash.prev == bp)) + return -EINVAL; + + return 0; +} + +static inline void __remove_from_hash(cache_ent_t *bp) +{ + (bp->hash.prev)->hash.next = bp->hash.next; + (bp->hash.next)->hash.prev = bp->hash.prev; + bp->hash.next = bp; + bp->hash.prev = bp; +#ifdef DEBUG_HASH_LIST + bp->hash.next = (cache_ent_t *)DEBUG_HASH_NEXT; + bp->hash.prev = (cache_ent_t *)DEBUG_HASH_PREV; +#endif +} + +/* + * Do FAT mirroring (don't sync) + * sec: sector No. in FAT1 + * bh: bh of sec. + */ +static inline s32 __fat_copy(struct super_block *sb, u64 sec, struct buffer_head *bh, int sync) +{ + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + u64 sec2; + + if (fsi->FAT2_start_sector != fsi->FAT1_start_sector) { + sec2 = sec - fsi->FAT1_start_sector + fsi->FAT2_start_sector; + BUG_ON(sec2 != (sec + (u64)fsi->num_FAT_sectors)); + + MMSG("BD: fat mirroring (%llu in FAT1, %llu in FAT2)\n", sec, sec2); + if (exfat_write_sect(sb, sec2, bh, sync)) + return -EIO; + } + + return 0; +} + +/* + * returns 1, if bp is flushed + * returns 0, if bp is not dirty + * returns -1, if error occurs + */ +static s32 __fcache_ent_flush(struct super_block *sb, cache_ent_t *bp, u32 sync) +{ + struct exfat_sb_info *sbi; + + if (!(bp->flag & DIRTYBIT)) + return 0; + + sbi = EXFAT_SB(sb); + if (sbi->options.delayed_meta) { + // Make buffer dirty (XXX: Naive impl.) + if (exfat_write_sect(sb, bp->sec, bp->bh, 0)) + return -EIO; + + if (__fat_copy(sb, bp->sec, bp->bh, 0)) + return -EIO; + } + + bp->flag &= ~(DIRTYBIT); + + if (sync) + sync_dirty_buffer(bp->bh); + + return 1; +} + +static s32 __fcache_ent_discard(struct super_block *sb, cache_ent_t *bp) +{ + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + __fcache_remove_hash(bp); + bp->sec = ~0; + bp->flag = 0; + + if (bp->bh) { + __brelse(bp->bh); + bp->bh = NULL; + } + move_to_lru(bp, &fsi->fcache.lru_list); + return 0; +} + +u8 *exfat_fcache_getblk(struct super_block *sb, u64 sec) +{ + cache_ent_t *bp; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + u32 page_ra_count = FCACHE_MAX_RA_SIZE >> sb->s_blocksize_bits; + + bp = __fcache_find(sb, sec); + if (bp) { + if (exfat_bdev_check_bdi_valid(sb)) { + __fcache_ent_flush(sb, bp, 0); + __fcache_ent_discard(sb, bp); + return NULL; + } + move_to_mru(bp, &fsi->fcache.lru_list); + return bp->bh->b_data; + } + + bp = __fcache_get(sb); + if (!__check_hash_valid(bp)) + __fcache_remove_hash(bp); + + bp->sec = sec; + bp->flag = 0; + __fcache_insert_hash(sb, bp); + + /* Naive FAT read-ahead (increase I/O unit to page_ra_count) */ + if ((sec & (page_ra_count - 1)) == 0) + exfat_bdev_readahead(sb, sec, (u64)page_ra_count); + + /* + * patch 1.2.4 : buffer_head null pointer exception problem. + * + * When exfat_read_sect is failed, fcache should be moved to + * EMPTY hash_list and the first of lru_list. + */ + if (exfat_read_sect(sb, sec, &(bp->bh), 1)) { + __fcache_ent_discard(sb, bp); + return NULL; + } + + return bp->bh->b_data; +} + +s32 exfat_fcache_modify(struct super_block *sb, u64 sec) +{ + cache_ent_t *bp; + + bp = __fcache_find(sb, sec); + if (!bp) { + exfat_fs_error(sb, "Can`t find fcache (sec 0x%016llx)", sec); + return -EIO; + } + + if (exfat_write_sect(sb, sec, bp->bh, 0)) + return -EIO; + + if (__fat_copy(sb, sec, bp->bh, 0)) + return -EIO; + + return 0; +} + +s32 exfat_meta_cache_init(struct super_block *sb) +{ + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + s32 i; + + /* LRU list */ + fsi->fcache.lru_list.next = &fsi->fcache.lru_list; + fsi->fcache.lru_list.prev = fsi->fcache.lru_list.next; + + for (i = 0; i < FAT_CACHE_SIZE; i++) { + fsi->fcache.pool[i].sec = ~0; + fsi->fcache.pool[i].flag = 0; + fsi->fcache.pool[i].bh = NULL; + fsi->fcache.pool[i].prev = NULL; + fsi->fcache.pool[i].next = NULL; + push_to_mru(&(fsi->fcache.pool[i]), &fsi->fcache.lru_list); + } + + fsi->dcache.lru_list.next = &fsi->dcache.lru_list; + fsi->dcache.lru_list.prev = fsi->dcache.lru_list.next; + fsi->dcache.keep_list.next = &fsi->dcache.keep_list; + fsi->dcache.keep_list.prev = fsi->dcache.keep_list.next; + + // Initially, all the BUF_CACHEs are in the LRU list + for (i = 0; i < BUF_CACHE_SIZE; i++) { + fsi->dcache.pool[i].sec = ~0; + fsi->dcache.pool[i].flag = 0; + fsi->dcache.pool[i].bh = NULL; + fsi->dcache.pool[i].prev = NULL; + fsi->dcache.pool[i].next = NULL; + push_to_mru(&(fsi->dcache.pool[i]), &fsi->dcache.lru_list); + } + + /* HASH list */ + for (i = 0; i < FAT_CACHE_HASH_SIZE; i++) { + fsi->fcache.hash_list[i].sec = ~0; + fsi->fcache.hash_list[i].hash.next = &(fsi->fcache.hash_list[i]); +; + fsi->fcache.hash_list[i].hash.prev = fsi->fcache.hash_list[i].hash.next; + } + + for (i = 0; i < FAT_CACHE_SIZE; i++) + __fcache_insert_hash(sb, &(fsi->fcache.pool[i])); + + for (i = 0; i < BUF_CACHE_HASH_SIZE; i++) { + fsi->dcache.hash_list[i].sec = ~0; + fsi->dcache.hash_list[i].hash.next = &(fsi->dcache.hash_list[i]); + + fsi->dcache.hash_list[i].hash.prev = fsi->dcache.hash_list[i].hash.next; + } + + for (i = 0; i < BUF_CACHE_SIZE; i++) + __dcache_insert_hash(sb, &(fsi->dcache.pool[i])); + + return 0; +} + +s32 exfat_meta_cache_shutdown(struct super_block *sb) +{ + return 0; +} + +s32 exfat_fcache_release_all(struct super_block *sb) +{ + s32 ret = 0; + cache_ent_t *bp; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + s32 dirtycnt = 0; + + bp = fsi->fcache.lru_list.next; + while (bp != &fsi->fcache.lru_list) { + s32 ret_tmp = __fcache_ent_flush(sb, bp, 0); + + if (ret_tmp < 0) + ret = ret_tmp; + else + dirtycnt += ret_tmp; + + bp->sec = ~0; + bp->flag = 0; + + if (bp->bh) { + __brelse(bp->bh); + bp->bh = NULL; + } + bp = bp->next; + } + + DMSG("BD:Release / dirty fat cache: %d (err:%d)\n", dirtycnt, ret); + return ret; +} + + +/* internal DIRTYBIT marked => bh dirty */ +s32 exfat_fcache_flush(struct super_block *sb, u32 sync) +{ + s32 ret = 0; + cache_ent_t *bp; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + s32 dirtycnt = 0; + + bp = fsi->fcache.lru_list.next; + while (bp != &fsi->fcache.lru_list) { + ret = __fcache_ent_flush(sb, bp, sync); + if (ret < 0) + break; + + dirtycnt += ret; + bp = bp->next; + } + + MMSG("BD: flush / dirty fat cache: %d (err:%d)\n", dirtycnt, ret); + return ret; +} + +static cache_ent_t *__fcache_find(struct super_block *sb, u64 sec) +{ + s32 off; + cache_ent_t *bp, *hp; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + off = (sec + (sec >> fsi->sect_per_clus_bits)) & (FAT_CACHE_HASH_SIZE - 1); + hp = &(fsi->fcache.hash_list[off]); + for (bp = hp->hash.next; bp != hp; bp = bp->hash.next) { + if (bp->sec == sec) { + /* + * patch 1.2.4 : for debugging + */ + WARN(!bp->bh, "exFAT: fcache has no bh. " + "It will make system panic.\n"); + + touch_buffer(bp->bh); + return bp; + } + } + return NULL; +} + +static cache_ent_t *__fcache_get(struct super_block *sb) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + FS_INFO_T *fsi = &(sbi->fsi); + cache_ent_t *bp; + + bp = fsi->fcache.lru_list.prev; + if (sbi->options.delayed_meta) { + while (bp->flag & DIRTYBIT) { + cache_ent_t *bp_prev = bp->prev; + + bp = bp_prev; + if (bp == &fsi->fcache.lru_list) { + DMSG("BD: fat cache flooding\n"); + exfat_fcache_flush(sb, 0); // flush all dirty FAT caches + bp = fsi->fcache.lru_list.prev; + } + } + } +// if (bp->flag & DIRTYBIT) +// sync_dirty_buffer(bp->bh); + + move_to_mru(bp, &fsi->fcache.lru_list); + return bp; +} + +static void __fcache_insert_hash(struct super_block *sb, cache_ent_t *bp) +{ + s32 off; + cache_ent_t *hp; + FS_INFO_T *fsi; + + fsi = &(EXFAT_SB(sb)->fsi); + off = (bp->sec + (bp->sec >> fsi->sect_per_clus_bits)) & (FAT_CACHE_HASH_SIZE-1); + + hp = &(fsi->fcache.hash_list[off]); + bp->hash.next = hp->hash.next; + bp->hash.prev = hp; + hp->hash.next->hash.prev = bp; + hp->hash.next = bp; +} + + +static void __fcache_remove_hash(cache_ent_t *bp) +{ +#ifdef DEBUG_HASH_LIST + if ((bp->hash.next == (cache_ent_t *)DEBUG_HASH_NEXT) || + (bp->hash.prev == (cache_ent_t *)DEBUG_HASH_PREV)) { + EMSG("%s: FATAL: tried to remove already-removed-cache-entry" + "(bp:%p)\n", __func__, bp); + return; + } +#endif + WARN_ON(bp->flag & DIRTYBIT); + __remove_from_hash(bp); +} + +/* Read-ahead a cluster */ +s32 exfat_dcache_readahead(struct super_block *sb, u64 sec) +{ + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + struct buffer_head *bh; + u32 max_ra_count = DCACHE_MAX_RA_SIZE >> sb->s_blocksize_bits; + u32 page_ra_count = PAGE_SIZE >> sb->s_blocksize_bits; + u32 adj_ra_count = max(fsi->sect_per_clus, page_ra_count); + u32 ra_count = min(adj_ra_count, max_ra_count); + + /* Read-ahead is not required */ + if (fsi->sect_per_clus == 1) + return 0; + + if (sec < fsi->data_start_sector) { + EMSG("BD: %s: requested sector is invalid(sect:%llu, root:%llu)\n", + __func__, sec, fsi->data_start_sector); + return -EIO; + } + + /* Not sector aligned with ra_count, resize ra_count to page size */ + if ((sec - fsi->data_start_sector) & (ra_count - 1)) + ra_count = page_ra_count; + + bh = sb_find_get_block(sb, sec); + if (!bh || !buffer_uptodate(bh)) + exfat_bdev_readahead(sb, sec, (u64)ra_count); + + brelse(bh); + + return 0; +} + +/* + * returns 1, if bp is flushed + * returns 0, if bp is not dirty + * returns -1, if error occurs + */ +static s32 __dcache_ent_flush(struct super_block *sb, cache_ent_t *bp, u32 sync) +{ + struct exfat_sb_info *sbi; + + if (!(bp->flag & DIRTYBIT)) + return 0; + + sbi = EXFAT_SB(sb); + if (sbi->options.delayed_meta) { + // Make buffer dirty (XXX: Naive impl.) + if (exfat_write_sect(sb, bp->sec, bp->bh, 0)) + return -EIO; + } + bp->flag &= ~(DIRTYBIT); + + if (sync) + sync_dirty_buffer(bp->bh); + + return 1; +} + +static s32 __dcache_ent_discard(struct super_block *sb, cache_ent_t *bp) +{ + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + MMSG("%s : bp[%p] (sec:%016llx flag:%08x bh:%p) list(prev:%p next:%p) " + "hash(prev:%p next:%p)\n", __func__, + bp, bp->sec, bp->flag, bp->bh, bp->prev, bp->next, + bp->hash.prev, bp->hash.next); + + __dcache_remove_hash(bp); + bp->sec = ~0; + bp->flag = 0; + + if (bp->bh) { + __brelse(bp->bh); + bp->bh = NULL; + } + + move_to_lru(bp, &fsi->dcache.lru_list); + return 0; +} + +u8 *exfat_dcache_getblk(struct super_block *sb, u64 sec) +{ + cache_ent_t *bp; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + bp = __dcache_find(sb, sec); + if (bp) { + if (exfat_bdev_check_bdi_valid(sb)) { + MMSG("%s: found cache(%p, sect:%llu). But invalid BDI\n" + , __func__, bp, sec); + __dcache_ent_flush(sb, bp, 0); + __dcache_ent_discard(sb, bp); + return NULL; + } + + if (!(bp->flag & KEEPBIT)) // already in keep list + move_to_mru(bp, &fsi->dcache.lru_list); + + return bp->bh->b_data; + } + + bp = __dcache_get(sb); + + if (!__check_hash_valid(bp)) + __dcache_remove_hash(bp); + + bp->sec = sec; + bp->flag = 0; + __dcache_insert_hash(sb, bp); + + if (exfat_read_sect(sb, sec, &(bp->bh), 1)) { + __dcache_ent_discard(sb, bp); + return NULL; + } + + return bp->bh->b_data; + +} + +s32 exfat_dcache_modify(struct super_block *sb, u64 sec) +{ + s32 ret = -EIO; + cache_ent_t *bp; + + exfat_set_sb_dirty(sb); + + bp = __dcache_find(sb, sec); + if (unlikely(!bp)) { + exfat_fs_error(sb, "Can`t find dcache (sec 0x%016llx)", sec); + return -EIO; + } + + ret = exfat_write_sect(sb, sec, bp->bh, 0); + + if (ret) { + DMSG("%s : failed to modify buffer(err:%d, sec:%llu, bp:0x%p)\n", + __func__, ret, sec, bp); + } + + return ret; +} + +s32 exfat_dcache_lock(struct super_block *sb, u64 sec) +{ + cache_ent_t *bp; + + bp = __dcache_find(sb, sec); + if (likely(bp)) { + bp->flag |= LOCKBIT; + return 0; + } + + EMSG("%s : failed to lock buffer(sec:%llu, bp:0x%p)\n", __func__, sec, bp); + return -EIO; +} + +s32 exfat_dcache_unlock(struct super_block *sb, u64 sec) +{ + cache_ent_t *bp; + + bp = __dcache_find(sb, sec); + if (likely(bp)) { + bp->flag &= ~(LOCKBIT); + return 0; + } + + EMSG("%s : failed to unlock buffer (sec:%llu, bp:0x%p)\n", __func__, sec, bp); + return -EIO; +} + +s32 exfat_dcache_release(struct super_block *sb, u64 sec) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + cache_ent_t *bp; + FS_INFO_T *fsi = &(sbi->fsi); + + bp = __dcache_find(sb, sec); + if (unlikely(!bp)) + return -ENOENT; + + if (sbi->options.delayed_meta) { + if (bp->flag & DIRTYBIT) { + if (exfat_write_sect(sb, bp->sec, bp->bh, 0)) + return -EIO; + } + } + + bp->sec = ~0; + bp->flag = 0; + + if (bp->bh) { + __brelse(bp->bh); + bp->bh = NULL; + } + + move_to_lru(bp, &fsi->dcache.lru_list); + return 0; +} + +s32 exfat_dcache_release_all(struct super_block *sb) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + s32 ret = 0; + cache_ent_t *bp; + FS_INFO_T *fsi = &(sbi->fsi); + s32 dirtycnt = 0; + + /* Connect list elements: + * LRU list : (A - B - ... - bp_front) + (bp_first + ... + bp_last) + */ + while (fsi->dcache.keep_list.prev != &fsi->dcache.keep_list) { + cache_ent_t *bp_keep = fsi->dcache.keep_list.prev; + // bp_keep->flag &= ~(KEEPBIT); // Will be 0-ed later + move_to_mru(bp_keep, &fsi->dcache.lru_list); + } + + bp = fsi->dcache.lru_list.next; + while (bp != &fsi->dcache.lru_list) { + if (sbi->options.delayed_meta) { + if (bp->flag & DIRTYBIT) { + dirtycnt++; + if (exfat_write_sect(sb, bp->sec, bp->bh, 0)) + ret = -EIO; + } + } + + bp->sec = ~0; + bp->flag = 0; + + if (bp->bh) { + __brelse(bp->bh); + bp->bh = NULL; + } + bp = bp->next; + } + + DMSG("BD:Release / dirty buf cache: %d (err:%d)", dirtycnt, ret); + return ret; +} + + +s32 exfat_dcache_flush(struct super_block *sb, u32 sync) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + s32 ret = 0; + cache_ent_t *bp; + FS_INFO_T *fsi = &(sbi->fsi); + s32 dirtycnt = 0; + s32 keepcnt = 0; + + /* Connect list elements: + * LRU list : (A - B - ... - bp_front) + (bp_first + ... + bp_last) + */ + while (fsi->dcache.keep_list.prev != &fsi->dcache.keep_list) { + cache_ent_t *bp_keep = fsi->dcache.keep_list.prev; + + bp_keep->flag &= ~(KEEPBIT); // Will be 0-ed later + move_to_mru(bp_keep, &fsi->dcache.lru_list); + keepcnt++; + } + + bp = fsi->dcache.lru_list.next; + while (bp != &fsi->dcache.lru_list) { + if (bp->flag & DIRTYBIT) { + if (sbi->options.delayed_meta) { + // Make buffer dirty (XXX: Naive impl.) + if (exfat_write_sect(sb, bp->sec, bp->bh, 0)) { + ret = -EIO; + break; + } + } + + bp->flag &= ~(DIRTYBIT); + dirtycnt++; + + if (sync != 0) + sync_dirty_buffer(bp->bh); + } + bp = bp->next; + } + + MMSG("BD: flush / dirty dentry cache: %d (%d from keeplist, err:%d)\n", + dirtycnt, keepcnt, ret); + return ret; +} + +static cache_ent_t *__dcache_find(struct super_block *sb, u64 sec) +{ + s32 off; + cache_ent_t *bp, *hp; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + off = (sec + (sec >> fsi->sect_per_clus_bits)) & (BUF_CACHE_HASH_SIZE - 1); + + hp = &(fsi->dcache.hash_list[off]); + for (bp = hp->hash.next; bp != hp; bp = bp->hash.next) { + if (bp->sec == sec) { + touch_buffer(bp->bh); + return bp; + } + } + return NULL; +} + +static cache_ent_t *__dcache_get(struct super_block *sb) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + cache_ent_t *bp; + FS_INFO_T *fsi = &(sbi->fsi); + + bp = fsi->dcache.lru_list.prev; + + if (sbi->options.delayed_meta) { + while (bp->flag & (DIRTYBIT | LOCKBIT)) { + cache_ent_t *bp_prev = bp->prev; // hold prev + + if (bp->flag & DIRTYBIT) { + MMSG("BD: Buf cache => Keep list\n"); + bp->flag |= KEEPBIT; + move_to_mru(bp, &fsi->dcache.keep_list); + } + bp = bp_prev; + + /* If all dcaches are dirty */ + if (bp == &fsi->dcache.lru_list) { + DMSG("BD: buf cache flooding\n"); + exfat_dcache_flush(sb, 0); + bp = fsi->dcache.lru_list.prev; + } + } + } else { + while (bp->flag & LOCKBIT) + bp = bp->prev; + } +// if (bp->flag & DIRTYBIT) +// sync_dirty_buffer(bp->bh); + + move_to_mru(bp, &fsi->dcache.lru_list); + return bp; +} + +static void __dcache_insert_hash(struct super_block *sb, cache_ent_t *bp) +{ + s32 off; + cache_ent_t *hp; + FS_INFO_T *fsi; + + fsi = &(EXFAT_SB(sb)->fsi); + off = (bp->sec + (bp->sec >> fsi->sect_per_clus_bits)) & (BUF_CACHE_HASH_SIZE-1); + + hp = &(fsi->dcache.hash_list[off]); + bp->hash.next = hp->hash.next; + bp->hash.prev = hp; + hp->hash.next->hash.prev = bp; + hp->hash.next = bp; +} + +static void __dcache_remove_hash(cache_ent_t *bp) +{ +#ifdef DEBUG_HASH_LIST + if ((bp->hash.next == (cache_ent_t *)DEBUG_HASH_NEXT) || + (bp->hash.prev == (cache_ent_t *)DEBUG_HASH_PREV)) { + EMSG("%s: FATAL: tried to remove already-removed-cache-entry" + "(bp:%p)\n", __func__, bp); + return; + } +#endif + WARN_ON(bp->flag & DIRTYBIT); + __remove_from_hash(bp); +} diff --git a/fs/exfat/config.h b/fs/exfat/config.h new file mode 100644 index 00000000..4cad85e1 --- /dev/null +++ b/fs/exfat/config.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + */ + +#ifndef _EXFAT_CONFIG_H +#define _EXFAT_CONFIG_H + +#ifndef CONFIG_EXFAT_WRITE_SB_INTERVAL_CSECS +#define CONFIG_EXFAT_WRITE_SB_INTERVAL_CSECS (dirty_writeback_interval) +#endif + +#ifndef CONFIG_EXFAT_DEFAULT_CODEPAGE /* if Kconfig lacked codepage */ +#define CONFIG_EXFAT_DEFAULT_CODEPAGE 437 +#endif + +#ifndef CONFIG_EXFAT_DEFAULT_IOCHARSET /* if Kconfig lacked iocharset */ +#define CONFIG_EXFAT_DEFAULT_IOCHARSET "utf8" +#endif + +#ifndef CONFIG_EXFAT_VIRTUAL_XATTR +#define CONFIG_EXFAT_VIRTUAL_XATTR +#endif + +#endif /* _EXFAT_CONFIG_H */ diff --git a/fs/exfat/core.c b/fs/exfat/core.c new file mode 100644 index 00000000..d6d49d41 --- /dev/null +++ b/fs/exfat/core.c @@ -0,0 +1,3171 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * core.c: FAT-common core code + */ + +#include +#include +#include +#include +#include +#include + +#include "exfat.h" +#include "core.h" +#include +#include + +void exfat_set_sb_dirty(struct super_block *sb) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) + sb->s_dirt = 1; +#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0) */ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + sbi->s_dirt = 1; + /* Insert work */ + spin_lock(&sbi->work_lock); + if (!sbi->write_super_queued) { + unsigned long delay; + + delay = msecs_to_jiffies(CONFIG_EXFAT_WRITE_SB_INTERVAL_CSECS * 10); + queue_delayed_work(system_long_wq, &sbi->write_super_work, delay); + sbi->write_super_queued = 1; + } + spin_unlock(&sbi->work_lock); +#endif +} + +static s32 check_type_size(void) +{ + /* critical check for system requirement on size of DENTRY_T structure */ + if (sizeof(DENTRY_T) != DENTRY_SIZE) + return -EINVAL; + + if (sizeof(DOS_DENTRY_T) != DENTRY_SIZE) + return -EINVAL; + + if (sizeof(EXT_DENTRY_T) != DENTRY_SIZE) + return -EINVAL; + + if (sizeof(FILE_DENTRY_T) != DENTRY_SIZE) + return -EINVAL; + + if (sizeof(STRM_DENTRY_T) != DENTRY_SIZE) + return -EINVAL; + + if (sizeof(NAME_DENTRY_T) != DENTRY_SIZE) + return -EINVAL; + + if (sizeof(BMAP_DENTRY_T) != DENTRY_SIZE) + return -EINVAL; + + if (sizeof(CASE_DENTRY_T) != DENTRY_SIZE) + return -EINVAL; + + if (sizeof(VOLM_DENTRY_T) != DENTRY_SIZE) + return -EINVAL; + + return 0; +} + +static s32 __fs_set_vol_flags(struct super_block *sb, u16 new_flag, s32 always_sync) +{ + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + pbr64_t *bpb; + s32 err; + s32 sync = 0; + + /* flags are not changed */ + if (fsi->vol_flag == new_flag) + return 0; + + fsi->vol_flag = new_flag; + + /* skip updating volume dirty flag, + * if this volume has been mounted with read-only + */ + if (EXFAT_IS_SB_RDONLY(sb)) + return 0; + + if (!fsi->pbr_bh) { + err = exfat_read_sect(sb, 0, &(fsi->pbr_bh), 1); + if (err) { + EMSG("%s : failed to read boot sector\n", __func__); + return err; + } + } + + bpb = (pbr64_t *)fsi->pbr_bh->b_data; + bpb->bsx.vol_flags = cpu_to_le16(new_flag); + + if (always_sync) + sync = 1; + else if ((new_flag == VOL_DIRTY) && (!buffer_dirty(fsi->pbr_bh))) + sync = 1; + else + sync = 0; + + err = exfat_write_sect(sb, 0, fsi->pbr_bh, sync); + if (err) + EMSG("%s : failed to modify volume flag\n", __func__); + + return err; +} + +static s32 fs_set_vol_flags(struct super_block *sb, u16 new_flag) +{ + return __fs_set_vol_flags(sb, new_flag, 0); +} + +s32 exfat_fscore_set_vol_flags(struct super_block *sb, u16 new_flag, s32 always_sync) +{ + return __fs_set_vol_flags(sb, new_flag, always_sync); +} + +static s32 fs_sync(struct super_block *sb, s32 do_sync) +{ + s32 err; + + if (!do_sync) + return 0; + + err = exfat_bdev_sync_all(sb); + if (err) + EMSG("%s : failed to sync. (err:%d)\n", __func__, err); + + return err; +} + +/* + * Cluster Management Functions + */ + +static s32 __clear_cluster(struct inode *inode, u32 clu) +{ + u64 s, n; + struct super_block *sb = inode->i_sb; + u32 sect_size = (u32)sb->s_blocksize; + s32 ret = 0; + struct buffer_head *tmp_bh = NULL; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + if (IS_CLUS_FREE(clu)) { /* FAT16 root_dir */ + s = fsi->root_start_sector; + n = fsi->data_start_sector; + } else { + s = CLUS_TO_SECT(fsi, clu); + n = s + fsi->sect_per_clus; + } + + if (IS_DIRSYNC(inode)) { + ret = exfat_write_msect_zero(sb, s, (u64)fsi->sect_per_clus); + if (ret != -EAGAIN) + return ret; + } + + /* Trying buffered zero writes + * if it doesn't have DIRSYNC or exfat_write_msect_zero() returned -EAGAIN + */ + for ( ; s < n; s++) { +#if 0 + exfat_dcache_release(sb, s); +#endif + ret = exfat_read_sect(sb, s, &tmp_bh, 0); + if (ret) + goto out; + + memset((u8 *)tmp_bh->b_data, 0x0, sect_size); + ret = exfat_write_sect(sb, s, tmp_bh, 0); + if (ret) + goto out; + } +out: + brelse(tmp_bh); + return ret; +} + +static s32 __find_last_cluster(struct super_block *sb, CHAIN_T *p_chain, u32 *ret_clu) +{ + u32 clu, next; + u32 count = 0; + + next = p_chain->dir; + if (p_chain->flags == 0x03) { + *ret_clu = next + p_chain->size - 1; + return 0; + } + + do { + count++; + clu = next; + if (exfat_ent_get_safe(sb, clu, &next)) + return -EIO; + } while (!IS_CLUS_EOF(next)); + + if (p_chain->size != count) { + exfat_fs_error(sb, "bogus directory size " + "(clus : ondisk(%d) != counted(%d))", + p_chain->size, count); + exfat_debug_bug_on(1); + return -EIO; + } + + *ret_clu = clu; + return 0; +} + + +static s32 __count_num_clusters(struct super_block *sb, CHAIN_T *p_chain, u32 *ret_count) +{ + u32 i, count; + u32 clu; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + if (!p_chain->dir || IS_CLUS_EOF(p_chain->dir)) { + *ret_count = 0; + return 0; + } + + if (p_chain->flags == 0x03) { + *ret_count = p_chain->size; + return 0; + } + + clu = p_chain->dir; + count = 0; + for (i = CLUS_BASE; i < fsi->num_clusters; i++) { + count++; + if (exfat_ent_get_safe(sb, clu, &clu)) + return -EIO; + if (IS_CLUS_EOF(clu)) + break; + } + + *ret_count = count; + return 0; +} + +/* + * Upcase table Management Functions + */ +static void free_upcase_table(struct super_block *sb) +{ + u32 i; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + u16 **upcase_table; + + upcase_table = fsi->vol_utbl; + for (i = 0 ; i < UTBL_COL_COUNT ; i++) { + /* kfree(NULL) is safe */ + kfree(upcase_table[i]); + upcase_table[i] = NULL; + } + + /* kfree(NULL) is safe */ + kfree(fsi->vol_utbl); + fsi->vol_utbl = NULL; +} + +static s32 __load_upcase_table(struct super_block *sb, u64 sector, u64 num_sectors, u32 utbl_checksum) +{ + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + struct buffer_head *tmp_bh = NULL; + u32 sect_size = (u32)sb->s_blocksize; + s32 ret = -EIO; + u32 i, j; + + u8 skip = false; + u32 index = 0; + u32 checksum = 0; + u16 **upcase_table = kzalloc((UTBL_COL_COUNT * sizeof(u16 *)), GFP_KERNEL); + + if (!upcase_table) + return -ENOMEM; + /* thanks for kzalloc + * memset(upcase_table, 0, UTBL_COL_COUNT * sizeof(u16 *)); + */ + + fsi->vol_utbl = upcase_table; + num_sectors += sector; + + while (sector < num_sectors) { + ret = exfat_read_sect(sb, sector, &tmp_bh, 1); + if (ret) { + EMSG("%s: failed to read sector(0x%llx)\n", + __func__, sector); + goto error; + } + sector++; + + for (i = 0; i < sect_size && index <= 0xFFFF; i += 2) { + /* FIXME : is __le16 ok? */ + //u16 uni = le16_to_cpu(((__le16*)(tmp_bh->b_data))[i]); + u16 uni = get_unaligned_le16((u8 *)tmp_bh->b_data+i); + + checksum = ((checksum & 1) ? 0x80000000 : 0) + + (checksum >> 1) + *(((u8 *)tmp_bh->b_data)+i); + checksum = ((checksum & 1) ? 0x80000000 : 0) + + (checksum >> 1) + *(((u8 *)tmp_bh->b_data)+(i+1)); + + if (skip) { + MMSG("skip from 0x%X to 0x%X(amount of 0x%X)\n", + index, index+uni, uni); + index += uni; + skip = false; + } else if (uni == index) { + index++; + } else if (uni == 0xFFFF) { + skip = true; + } else { /* uni != index , uni != 0xFFFF */ + u16 col_index = exfat_get_col_index(index); + + if (!upcase_table[col_index]) { + upcase_table[col_index] = + kmalloc((UTBL_ROW_COUNT * sizeof(u16)), GFP_KERNEL); + if (!upcase_table[col_index]) { + EMSG("failed to allocate memory" + " for column 0x%X\n", + col_index); + ret = -ENOMEM; + goto error; + } + + for (j = 0; j < UTBL_ROW_COUNT; j++) + upcase_table[col_index][j] = (col_index << LOW_INDEX_BIT) | j; + } + + upcase_table[col_index][exfat_get_row_index(index)] = uni; + index++; + } + } + } + if (index >= 0xFFFF && utbl_checksum == checksum) { + DMSG("%s: load upcase table successfully" + "(idx:0x%08x, utbl_chksum:0x%08x)\n", + __func__, index, utbl_checksum); + if (tmp_bh) + brelse(tmp_bh); + return 0; + } + + EMSG("%s: failed to load upcase table" + "(idx:0x%08x, chksum:0x%08x, utbl_chksum:0x%08x)\n", + __func__, index, checksum, utbl_checksum); + + ret = -EINVAL; +error: + if (tmp_bh) + brelse(tmp_bh); + free_upcase_table(sb); + return ret; +} + +static s32 __load_default_upcase_table(struct super_block *sb) +{ + s32 i, ret = -EIO; + u32 j; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + u8 skip = false; + u32 index = 0; + u16 uni = 0; + u16 **upcase_table; + + upcase_table = kmalloc((UTBL_COL_COUNT * sizeof(u16 *)), GFP_KERNEL); + if (!upcase_table) + return -ENOMEM; + + fsi->vol_utbl = upcase_table; + memset(upcase_table, 0, UTBL_COL_COUNT * sizeof(u16 *)); + + for (i = 0; index <= 0xFFFF && i < EXFAT_NUM_UPCASE*2; i += 2) { + /* FIXME : is __le16 ok? */ + //uni = le16_to_cpu(((__le16*)uni_def_upcase)[i>>1]); + uni = get_unaligned_le16((u8 *)uni_def_upcase+i); + if (skip) { + MMSG("skip from 0x%x ", index); + index += uni; + MMSG("to 0x%x (amount of 0x%x)\n", index, uni); + skip = false; + } else if (uni == index) { + index++; + } else if (uni == 0xFFFF) { + skip = true; + } else { /* uni != index , uni != 0xFFFF */ + u16 col_index = exfat_get_col_index(index); + + if (!upcase_table[col_index]) { + upcase_table[col_index] = kmalloc((UTBL_ROW_COUNT * sizeof(u16)), GFP_KERNEL); + if (!upcase_table[col_index]) { + EMSG("failed to allocate memory for " + "new column 0x%x\n", col_index); + ret = -ENOMEM; + goto error; + } + + for (j = 0; j < UTBL_ROW_COUNT; j++) + upcase_table[col_index][j] = (col_index << LOW_INDEX_BIT) | j; + } + + upcase_table[col_index][exfat_get_row_index(index)] = uni; + index++; + } + } + + if (index >= 0xFFFF) + return 0; + +error: + /* FATAL error: default upcase table has error */ + free_upcase_table(sb); + return ret; +} + +static s32 load_upcase_table(struct super_block *sb) +{ + s32 i, ret; + u32 tbl_clu, type; + u64 sector, tbl_size, num_sectors; + u8 blksize_bits = sb->s_blocksize_bits; + CHAIN_T clu; + CASE_DENTRY_T *ep; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + clu.dir = fsi->root_dir; + clu.flags = 0x01; + + while (!IS_CLUS_EOF(clu.dir)) { + for (i = 0; i < fsi->dentries_per_clu; i++) { + ep = (CASE_DENTRY_T *) exfat_get_dentry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -EIO; + + type = fsi->fs_func->get_entry_type((DENTRY_T *) ep); + + if (type == TYPE_UNUSED) + break; + if (type != TYPE_UPCASE) + continue; + + tbl_clu = le32_to_cpu(ep->start_clu); + tbl_size = le64_to_cpu(ep->size); + + sector = CLUS_TO_SECT(fsi, tbl_clu); + num_sectors = ((tbl_size - 1) >> blksize_bits) + 1; + ret = __load_upcase_table(sb, sector, num_sectors, + le32_to_cpu(ep->checksum)); + + if (ret && (ret != -EIO)) + goto load_default; + + /* load successfully */ + return ret; + } + + if (get_next_clus_safe(sb, &(clu.dir))) + return -EIO; + } + +load_default: + exfat_log_msg(sb, KERN_INFO, "trying to load default upcase table"); + /* load default upcase table */ + return __load_default_upcase_table(sb); +} + + +/* + * Directory Entry Management Functions + */ +s32 exfat_walk_fat_chain(struct super_block *sb, CHAIN_T *p_dir, u32 byte_offset, u32 *clu) +{ + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + u32 clu_offset; + u32 cur_clu; + + clu_offset = byte_offset >> fsi->cluster_size_bits; + cur_clu = p_dir->dir; + + if (p_dir->flags == 0x03) { + cur_clu += clu_offset; + } else { + while (clu_offset > 0) { + if (get_next_clus_safe(sb, &cur_clu)) + return -EIO; + if (IS_CLUS_EOF(cur_clu)) { + exfat_fs_error(sb, "invalid dentry access " + "beyond EOF (clu : %u, eidx : %d)", + p_dir->dir, + byte_offset >> DENTRY_SIZE_BITS); + return -EIO; + } + clu_offset--; + } + } + + if (clu) + *clu = cur_clu; + return 0; +} + +static s32 find_location(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u64 *sector, s32 *offset) +{ + s32 ret; + u32 off, clu = 0; + u32 blksize_mask = (u32)(sb->s_blocksize-1); + u8 blksize_bits = sb->s_blocksize_bits; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + off = entry << DENTRY_SIZE_BITS; + + /* FAT16 root_dir */ + if (IS_CLUS_FREE(p_dir->dir)) { + *offset = off & blksize_mask; + *sector = off >> blksize_bits; + *sector += fsi->root_start_sector; + return 0; + } + + ret = exfat_walk_fat_chain(sb, p_dir, off, &clu); + if (ret) + return ret; + + /* byte offset in cluster */ + off &= (fsi->cluster_size - 1); + + /* byte offset in sector */ + *offset = off & blksize_mask; + + /* sector offset in cluster */ + *sector = off >> blksize_bits; + *sector += CLUS_TO_SECT(fsi, clu); + return 0; +} + +DENTRY_T *exfat_get_dentry_in_dir(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u64 *sector) +{ + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + u32 dentries_per_page = PAGE_SIZE >> DENTRY_SIZE_BITS; + s32 off; + u64 sec; + u8 *buf; + + if (p_dir->dir == DIR_DELETED) { + EMSG("%s : abnormal access to deleted dentry\n", __func__); + BUG_ON(!fsi->prev_eio); + return NULL; + } + + if (find_location(sb, p_dir, entry, &sec, &off)) + return NULL; + + /* DIRECTORY READAHEAD : + * Try to read ahead per a page except root directory of fat12/16 + */ + if ((!IS_CLUS_FREE(p_dir->dir)) && + !(entry & (dentries_per_page - 1))) + exfat_dcache_readahead(sb, sec); + + buf = exfat_dcache_getblk(sb, sec); + if (!buf) + return NULL; + + if (sector) + *sector = sec; + return (DENTRY_T *)(buf + off); +} + +/* used only in search empty_slot() */ +#define CNT_UNUSED_NOHIT (-1) +#define CNT_UNUSED_HIT (-2) +/* search EMPTY CONTINUOUS "num_entries" entries */ +static s32 search_empty_slot(struct super_block *sb, HINT_FEMP_T *hint_femp, CHAIN_T *p_dir, s32 num_entries) +{ + s32 i, dentry, num_empty = 0; + s32 dentries_per_clu; + u32 type; + CHAIN_T clu; + DENTRY_T *ep; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + if (IS_CLUS_FREE(p_dir->dir)) /* FAT16 root_dir */ + dentries_per_clu = fsi->dentries_in_root; + else + dentries_per_clu = fsi->dentries_per_clu; + + ASSERT(-1 <= hint_femp->eidx); + + if (hint_femp->eidx != -1) { + clu.dir = hint_femp->cur.dir; + clu.size = hint_femp->cur.size; + clu.flags = hint_femp->cur.flags; + + dentry = hint_femp->eidx; + + if (num_entries <= hint_femp->count) { + MMSG("%s: empty slot(HIT) - found " + "(clu : 0x%08x eidx : %d)\n", + __func__, hint_femp->cur.dir, hint_femp->eidx); + hint_femp->eidx = -1; + + return dentry; + } + MMSG("%s: empty slot(HIT) - search from " + "(clu : 0x%08x eidx : %d)\n", + __func__, hint_femp->cur.dir, hint_femp->eidx); + } else { + MMSG("%s: empty slot(MISS) - search from " + "(clu:0x%08x eidx : 0)\n", + __func__, p_dir->dir); + + clu.dir = p_dir->dir; + clu.size = p_dir->size; + clu.flags = p_dir->flags; + + dentry = 0; + } + + while (!IS_CLUS_EOF(clu.dir)) { + /* FAT16 root_dir */ + if (IS_CLUS_FREE(p_dir->dir)) + i = dentry % dentries_per_clu; + else + i = dentry & (dentries_per_clu-1); + + for ( ; i < dentries_per_clu; i++, dentry++) { + ep = exfat_get_dentry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -EIO; + + type = fsi->fs_func->get_entry_type(ep); + + if ((type == TYPE_UNUSED) || (type == TYPE_DELETED)) { + num_empty++; + if (hint_femp->eidx == -1) { + hint_femp->eidx = dentry; + hint_femp->count = CNT_UNUSED_NOHIT; + + hint_femp->cur.dir = clu.dir; + hint_femp->cur.size = clu.size; + hint_femp->cur.flags = clu.flags; + } + + if ((type == TYPE_UNUSED) && + (hint_femp->count != CNT_UNUSED_HIT)) { + hint_femp->count = CNT_UNUSED_HIT; + } + } else { + if ((hint_femp->eidx != -1) && + (hint_femp->count == CNT_UNUSED_HIT)) { + /* unused empty group means + * an empty group which includes + * unused dentry + */ + exfat_fs_error(sb, + "found bogus dentry(%d) " + "beyond unused empty group(%d) " + "(start_clu : %u, cur_clu : %u)", + dentry, hint_femp->eidx, p_dir->dir, + clu.dir); + return -EIO; + } + + num_empty = 0; + hint_femp->eidx = -1; + } + + if (num_empty >= num_entries) { + /* found and invalidate hint_femp */ + hint_femp->eidx = -1; + + return (dentry - (num_entries-1)); + } + } + + if (IS_CLUS_FREE(p_dir->dir)) + break; /* FAT16 root_dir */ + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUS_EOF; + } else { + if (get_next_clus_safe(sb, &(clu.dir))) + return -EIO; + } + } + + return -ENOSPC; +} + +/* + * find empty directory entry. + * if there isn't any empty slot, expand cluster chain. + */ +static s32 find_empty_entry(struct inode *inode, CHAIN_T *p_dir, s32 num_entries) +{ + s32 dentry; + u32 ret, last_clu; + u64 sector; + u64 size = 0; + CHAIN_T clu; + DENTRY_T *ep = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + HINT_FEMP_T hint_femp; + + hint_femp.eidx = -1; + + ASSERT(-1 <= fid->hint_femp.eidx); + + if (fid->hint_femp.eidx != -1) { + memcpy(&hint_femp, &fid->hint_femp, sizeof(HINT_FEMP_T)); + fid->hint_femp.eidx = -1; + } + + /* FAT16 root_dir */ + if (IS_CLUS_FREE(p_dir->dir)) + return search_empty_slot(sb, &hint_femp, p_dir, num_entries); + + while ((dentry = search_empty_slot(sb, &hint_femp, p_dir, num_entries)) < 0) { + if (dentry == -EIO) + break; + + if (fsi->fs_func->check_max_dentries(fid)) + return -ENOSPC; + + /* we trust p_dir->size regardless of FAT type */ + if (__find_last_cluster(sb, p_dir, &last_clu)) + return -EIO; + + /* + * Allocate new cluster to this directory + */ + clu.dir = last_clu + 1; + clu.size = 0; /* UNUSED */ + clu.flags = p_dir->flags; + + /* (0) check if there are reserved clusters + * Refer to create_dir's comments + */ + if (!IS_CLUS_EOF(fsi->used_clusters) && + ((fsi->used_clusters + fsi->reserved_clusters) >= (fsi->num_clusters - 2))) + return -ENOSPC; + + /* (1) allocate a cluster */ + ret = fsi->fs_func->alloc_cluster(sb, 1, &clu, ALLOC_HOT); + if (ret) + return ret; + + if (__clear_cluster(inode, clu.dir)) + return -EIO; + + /* (2) append to the FAT chain */ + if (clu.flags != p_dir->flags) { + /* no-fat-chain bit is disabled, + * so fat-chain should be synced with alloc-bmp + */ + exfat_chain_cont_cluster(sb, p_dir->dir, p_dir->size); + p_dir->flags = 0x01; + hint_femp.cur.flags = 0x01; + } + + if (clu.flags == 0x01) + if (exfat_ent_set(sb, last_clu, clu.dir)) + return -EIO; + + if (hint_femp.eidx == -1) { + /* the special case that new dentry + * should be allocated from the start of new cluster + */ + hint_femp.eidx = (s32)(p_dir->size << + (fsi->cluster_size_bits - DENTRY_SIZE_BITS)); + hint_femp.count = fsi->dentries_per_clu; + + hint_femp.cur.dir = clu.dir; + hint_femp.cur.size = 0; + hint_femp.cur.flags = clu.flags; + } + hint_femp.cur.size++; + p_dir->size++; + size = (p_dir->size << fsi->cluster_size_bits); + + /* (3) update the directory entry */ + if (p_dir->dir != fsi->root_dir) { + ep = exfat_get_dentry_in_dir(sb, + &(fid->dir), fid->entry+1, §or); + if (!ep) + return -EIO; + fsi->fs_func->set_entry_size(ep, size); + fsi->fs_func->set_entry_flag(ep, p_dir->flags); + if (exfat_dcache_modify(sb, sector)) + return -EIO; + + if (exfat_update_dir_chksum(sb, &(fid->dir), fid->entry)) + return -EIO; + } + + /* directory inode should be updated in here */ + i_size_write(inode, (loff_t)size); + EXFAT_I(inode)->i_size_ondisk += fsi->cluster_size; + EXFAT_I(inode)->i_size_aligned += fsi->cluster_size; + EXFAT_I(inode)->fid.size = size; + EXFAT_I(inode)->fid.flags = p_dir->flags; + inode->i_blocks += 1 << (fsi->cluster_size_bits - sb->s_blocksize_bits); + } + + return dentry; +} + +#define EXFAT_MIN_SUBDIR (2) + +static s32 __count_dos_name_entries(struct super_block *sb, CHAIN_T *p_dir, u32 type, u32 *dotcnt) +{ + s32 i, count = 0, check_dot = 0; + s32 dentries_per_clu; + u32 entry_type; + CHAIN_T clu; + DENTRY_T *ep; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + if (IS_CLUS_FREE(p_dir->dir)) /* FAT16 root_dir */ + dentries_per_clu = fsi->dentries_in_root; + else + dentries_per_clu = fsi->dentries_per_clu; + + clu.dir = p_dir->dir; + clu.size = p_dir->size; + clu.flags = p_dir->flags; + + if (dotcnt) + *dotcnt = 0; + + while (!IS_CLUS_EOF(clu.dir)) { + for (i = 0; i < dentries_per_clu; i++) { + ep = exfat_get_dentry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -EIO; + + entry_type = fsi->fs_func->get_entry_type(ep); + + if (entry_type == TYPE_UNUSED) + return count; + if (!(type & TYPE_CRITICAL_PRI) && !(type & TYPE_BENIGN_PRI)) + continue; + + if ((type != TYPE_ALL) && (type != entry_type)) + continue; + + count++; + } + + /* FAT16 root_dir */ + if (IS_CLUS_FREE(p_dir->dir)) + break; + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUS_EOF; + } else { + if (get_next_clus_safe(sb, &(clu.dir))) + return -EIO; + } + + check_dot = 0; + } + + return count; +} + +s32 check_dir_empty(struct super_block *sb, CHAIN_T *p_dir) +{ + s32 i; + s32 dentries_per_clu; + u32 type; + CHAIN_T clu; + DENTRY_T *ep; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + if (IS_CLUS_FREE(p_dir->dir)) /* FAT16 root_dir */ + dentries_per_clu = fsi->dentries_in_root; + else + dentries_per_clu = fsi->dentries_per_clu; + + clu.dir = p_dir->dir; + clu.size = p_dir->size; + clu.flags = p_dir->flags; + + while (!IS_CLUS_EOF(clu.dir)) { + for (i = 0; i < dentries_per_clu; i++) { + ep = exfat_get_dentry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -EIO; + + type = fsi->fs_func->get_entry_type(ep); + + if (type == TYPE_UNUSED) + return 0; + + if ((type != TYPE_FILE) && (type != TYPE_DIR)) + continue; + + return -ENOTEMPTY; + } + + /* FAT16 root_dir */ + if (IS_CLUS_FREE(p_dir->dir)) + return -ENOTEMPTY; + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUS_EOF; + } else { + if (get_next_clus_safe(sb, &(clu.dir))) + return -EIO; + } + } + + return 0; +} + +/* input : dir, uni_name + * output : num_of_entry, dos_name(format : aaaaaa~1.bbb) + */ +static s32 get_num_entries_and_dos_name(struct super_block *sb, CHAIN_T *p_dir, + UNI_NAME_T *p_uniname, s32 *entries, + DOS_NAME_T *p_dosname, s32 lookup) +{ + s32 num_entries; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + /* Init null char. */ + p_dosname->name[0] = '\0'; + + num_entries = fsi->fs_func->calc_num_entries(p_uniname); + if (num_entries == 0) + return -EINVAL; + + *entries = num_entries; + return 0; +} + +void exfat_get_uniname_from_dos_entry(struct super_block *sb, DOS_DENTRY_T *ep, UNI_NAME_T *p_uniname, u8 mode) +{ + DOS_NAME_T dos_name; + + if (mode == 0x0) + dos_name.name_case = 0x0; + else + dos_name.name_case = ep->lcase; + + memcpy(dos_name.name, ep->name, DOS_NAME_LENGTH); + exfat_nls_sfn_to_uni16s(sb, &dos_name, p_uniname); +} + +/* returns the length of a struct qstr, ignoring trailing dots */ +static inline unsigned int __striptail_len(unsigned int len, const char *name) +{ + while (len && name[len - 1] == '.') + len--; + return len; +} + +/* + * Name Resolution Functions : + * Zero if it was successful; otherwise nonzero. + */ +static s32 __resolve_path(struct inode *inode, const u8 *path, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, int lookup) +{ + s32 namelen; + s32 lossy = NLS_NAME_NO_LOSSY; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + + /* DOT and DOTDOT are handled by VFS layer */ + + /* strip all trailing spaces */ + /* DO NOTHING : Is needed? */ + + /* strip all trailing periods */ + namelen = __striptail_len(strlen(path), path); + if (!namelen) + return -ENOENT; + + /* the limitation of linux? */ + if (strlen(path) > (MAX_NAME_LENGTH * MAX_CHARSET_SIZE)) + return -ENAMETOOLONG; + + /* + * strip all leading spaces : + * "MS windows 7" supports leading spaces. + * So we should skip this preprocessing for compatibility. + */ + + /* file name conversion : + * If lookup case, we allow bad-name for compatibility. + */ + namelen = exfat_nls_vfsname_to_uni16s(sb, path, namelen, p_uniname, &lossy); + if (namelen < 0) + return namelen; /* return error value */ + + if ((lossy && !lookup) || !namelen) + return -EINVAL; + + exfat_debug_bug_on(fid->size != i_size_read(inode)); +// fid->size = i_size_read(inode); + + p_dir->dir = fid->start_clu; + p_dir->size = (u32)(fid->size >> fsi->cluster_size_bits); + p_dir->flags = fid->flags; + + return 0; +} + +static inline s32 resolve_path(struct inode *inode, const u8 *path, CHAIN_T *dir, UNI_NAME_T *uni) +{ + return __resolve_path(inode, path, dir, uni, 0); +} + +static inline s32 resolve_path_for_lookup(struct inode *inode, const u8 *path, CHAIN_T *dir, UNI_NAME_T *uni) +{ + return __resolve_path(inode, path, dir, uni, 1); +} + +static s32 create_dir(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, FILE_ID_T *fid) +{ + s32 dentry, num_entries; + u64 ret; + u64 size; + CHAIN_T clu; + DOS_NAME_T dos_name; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_entries, &dos_name, 0); + if (ret) + return ret; + + /* find_empty_entry must be called before alloc_cluster */ + dentry = find_empty_entry(inode, p_dir, num_entries); + if (dentry < 0) + return dentry; /* -EIO or -ENOSPC */ + + clu.dir = CLUS_EOF; + clu.size = 0; + clu.flags = 0x03; + + /* (0) Check if there are reserved clusters up to max. */ + if ((fsi->used_clusters + fsi->reserved_clusters) >= (fsi->num_clusters - CLUS_BASE)) + return -ENOSPC; + + /* (1) allocate a cluster */ + ret = fsi->fs_func->alloc_cluster(sb, 1, &clu, ALLOC_HOT); + if (ret) + return ret; + + ret = __clear_cluster(inode, clu.dir); + if (ret) + return ret; + + size = fsi->cluster_size; + + /* (2) update the directory entry */ + /* make sub-dir entry in parent directory */ + ret = fsi->fs_func->init_dir_entry(sb, p_dir, dentry, TYPE_DIR, clu.dir, size); + if (ret) + return ret; + + ret = fsi->fs_func->init_ext_entry(sb, p_dir, dentry, num_entries, p_uniname, &dos_name); + if (ret) + return ret; + + fid->dir.dir = p_dir->dir; + fid->dir.size = p_dir->size; + fid->dir.flags = p_dir->flags; + fid->entry = dentry; + + fid->attr = ATTR_SUBDIR; + fid->flags = 0x03; + fid->size = size; + fid->start_clu = clu.dir; + + fid->type = TYPE_DIR; + fid->rwoffset = 0; + fid->hint_bmap.off = CLUS_EOF; + + /* hint_stat will be used if this is directory. */ + fid->version = 0; + fid->hint_stat.eidx = 0; + fid->hint_stat.clu = fid->start_clu; + fid->hint_femp.eidx = -1; + + return 0; +} + +static s32 create_file(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, u8 mode, FILE_ID_T *fid) +{ + s32 ret, dentry, num_entries; + DOS_NAME_T dos_name; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_entries, &dos_name, 0); + if (ret) + return ret; + + /* find_empty_entry must be called before alloc_cluster() */ + dentry = find_empty_entry(inode, p_dir, num_entries); + if (dentry < 0) + return dentry; /* -EIO or -ENOSPC */ + + /* (1) update the directory entry */ + /* fill the dos name directory entry information of the created file. + * the first cluster is not determined yet. (0) + */ + ret = fsi->fs_func->init_dir_entry(sb, p_dir, dentry, TYPE_FILE | mode, CLUS_FREE, 0); + if (ret) + return ret; + + ret = fsi->fs_func->init_ext_entry(sb, p_dir, dentry, num_entries, p_uniname, &dos_name); + if (ret) + return ret; + + fid->dir.dir = p_dir->dir; + fid->dir.size = p_dir->size; + fid->dir.flags = p_dir->flags; + fid->entry = dentry; + + fid->attr = ATTR_ARCHIVE | mode; + fid->flags = 0x03; + fid->size = 0; + fid->start_clu = CLUS_EOF; + + fid->type = TYPE_FILE; + fid->rwoffset = 0; + fid->hint_bmap.off = CLUS_EOF; + + /* hint_stat will be used if this is directory. */ + fid->version = 0; + fid->hint_stat.eidx = 0; + fid->hint_stat.clu = fid->start_clu; + fid->hint_femp.eidx = -1; + + return 0; +} + +static s32 remove_file(struct inode *inode, CHAIN_T *p_dir, s32 entry) +{ + s32 num_entries; + u64 sector; + DENTRY_T *ep; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + ep = exfat_get_dentry_in_dir(sb, p_dir, entry, §or); + if (!ep) + return -EIO; + + exfat_dcache_lock(sb, sector); + + /* exfat_dcache_lock() before call count_ext_entries() */ + num_entries = fsi->fs_func->count_ext_entries(sb, p_dir, entry, ep); + if (num_entries < 0) { + exfat_dcache_unlock(sb, sector); + return -EIO; + } + num_entries++; + + exfat_dcache_unlock(sb, sector); + + /* (1) update the directory entry */ + return fsi->fs_func->delete_dir_entry(sb, p_dir, entry, 0, num_entries); +} + +static s32 rename_file(struct inode *inode, CHAIN_T *p_dir, s32 oldentry, UNI_NAME_T *p_uniname, FILE_ID_T *fid) +{ + s32 ret, newentry = -1, num_old_entries, num_new_entries; + u64 sector_old, sector_new; + DOS_NAME_T dos_name; + DENTRY_T *epold, *epnew; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + epold = exfat_get_dentry_in_dir(sb, p_dir, oldentry, §or_old); + if (!epold) + return -EIO; + + exfat_dcache_lock(sb, sector_old); + + /* exfat_dcache_lock() before call count_ext_entries() */ + num_old_entries = fsi->fs_func->count_ext_entries(sb, p_dir, oldentry, epold); + if (num_old_entries < 0) { + exfat_dcache_unlock(sb, sector_old); + return -EIO; + } + num_old_entries++; + + ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_new_entries, &dos_name, 0); + if (ret) { + exfat_dcache_unlock(sb, sector_old); + return ret; + } + + if (num_old_entries < num_new_entries) { + newentry = find_empty_entry(inode, p_dir, num_new_entries); + if (newentry < 0) { + exfat_dcache_unlock(sb, sector_old); + return newentry; /* -EIO or -ENOSPC */ + } + + epnew = exfat_get_dentry_in_dir(sb, p_dir, newentry, §or_new); + if (!epnew) { + exfat_dcache_unlock(sb, sector_old); + return -EIO; + } + + memcpy((void *) epnew, (void *) epold, DENTRY_SIZE); + if (fsi->fs_func->get_entry_type(epnew) == TYPE_FILE) { + fsi->fs_func->set_entry_attr(epnew, fsi->fs_func->get_entry_attr(epnew) | ATTR_ARCHIVE); + fid->attr |= ATTR_ARCHIVE; + } + exfat_dcache_modify(sb, sector_new); + exfat_dcache_unlock(sb, sector_old); + + epold = exfat_get_dentry_in_dir(sb, p_dir, oldentry+1, §or_old); + exfat_dcache_lock(sb, sector_old); + epnew = exfat_get_dentry_in_dir(sb, p_dir, newentry+1, §or_new); + + if (!epold || !epnew) { + exfat_dcache_unlock(sb, sector_old); + return -EIO; + } + + memcpy((void *) epnew, (void *) epold, DENTRY_SIZE); + exfat_dcache_modify(sb, sector_new); + exfat_dcache_unlock(sb, sector_old); + + ret = fsi->fs_func->init_ext_entry(sb, p_dir, newentry, num_new_entries, p_uniname, &dos_name); + if (ret) + return ret; + + fsi->fs_func->delete_dir_entry(sb, p_dir, oldentry, 0, num_old_entries); + fid->entry = newentry; + } else { + if (fsi->fs_func->get_entry_type(epold) == TYPE_FILE) { + fsi->fs_func->set_entry_attr(epold, fsi->fs_func->get_entry_attr(epold) | ATTR_ARCHIVE); + fid->attr |= ATTR_ARCHIVE; + } + exfat_dcache_modify(sb, sector_old); + exfat_dcache_unlock(sb, sector_old); + + ret = fsi->fs_func->init_ext_entry(sb, p_dir, oldentry, num_new_entries, p_uniname, &dos_name); + if (ret) + return ret; + + fsi->fs_func->delete_dir_entry(sb, p_dir, oldentry, num_new_entries, num_old_entries); + } + + return 0; +} + +static s32 move_file(struct inode *inode, CHAIN_T *p_olddir, s32 oldentry, + CHAIN_T *p_newdir, UNI_NAME_T *p_uniname, FILE_ID_T *fid) +{ + s32 ret, newentry, num_new_entries, num_old_entries; + u64 sector_mov, sector_new; + DOS_NAME_T dos_name; + DENTRY_T *epmov, *epnew; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + epmov = exfat_get_dentry_in_dir(sb, p_olddir, oldentry, §or_mov); + if (!epmov) + return -EIO; + + /* check if the source and target directory is the same */ + if (fsi->fs_func->get_entry_type(epmov) == TYPE_DIR && + fsi->fs_func->get_entry_clu0(epmov) == p_newdir->dir) + return -EINVAL; + + exfat_dcache_lock(sb, sector_mov); + + /* exfat_dcache_lock() before call count_ext_entries() */ + num_old_entries = fsi->fs_func->count_ext_entries(sb, p_olddir, oldentry, epmov); + if (num_old_entries < 0) { + exfat_dcache_unlock(sb, sector_mov); + return -EIO; + } + num_old_entries++; + + ret = get_num_entries_and_dos_name(sb, p_newdir, p_uniname, &num_new_entries, &dos_name, 0); + if (ret) { + exfat_dcache_unlock(sb, sector_mov); + return ret; + } + + newentry = find_empty_entry(inode, p_newdir, num_new_entries); + if (newentry < 0) { + exfat_dcache_unlock(sb, sector_mov); + return newentry; /* -EIO or -ENOSPC */ + } + + epnew = exfat_get_dentry_in_dir(sb, p_newdir, newentry, §or_new); + if (!epnew) { + exfat_dcache_unlock(sb, sector_mov); + return -EIO; + } + + memcpy((void *) epnew, (void *) epmov, DENTRY_SIZE); + if (fsi->fs_func->get_entry_type(epnew) == TYPE_FILE) { + fsi->fs_func->set_entry_attr(epnew, fsi->fs_func->get_entry_attr(epnew) | ATTR_ARCHIVE); + fid->attr |= ATTR_ARCHIVE; + } + exfat_dcache_modify(sb, sector_new); + exfat_dcache_unlock(sb, sector_mov); + + epmov = exfat_get_dentry_in_dir(sb, p_olddir, oldentry+1, §or_mov); + exfat_dcache_lock(sb, sector_mov); + epnew = exfat_get_dentry_in_dir(sb, p_newdir, newentry+1, §or_new); + if (!epmov || !epnew) { + exfat_dcache_unlock(sb, sector_mov); + return -EIO; + } + + memcpy((void *) epnew, (void *) epmov, DENTRY_SIZE); + exfat_dcache_modify(sb, sector_new); + exfat_dcache_unlock(sb, sector_mov); + + ret = fsi->fs_func->init_ext_entry(sb, p_newdir, newentry, num_new_entries, p_uniname, &dos_name); + if (ret) + return ret; + + fsi->fs_func->delete_dir_entry(sb, p_olddir, oldentry, 0, num_old_entries); + + fid->dir.dir = p_newdir->dir; + fid->dir.size = p_newdir->size; + fid->dir.flags = p_newdir->flags; + + fid->entry = newentry; + + return 0; +} + +/* roll back to the initial state of the file system */ +s32 exfat_fscore_init(void) +{ + s32 ret; + + ret = check_type_size(); + if (ret) + return ret; + + return exfat_extent_cache_init(); +} + +/* make free all memory-alloced global buffers */ +s32 exfat_fscore_shutdown(void) +{ + exfat_extent_cache_shutdown(); + return 0; +} + +/* check device is ejected */ +s32 exfat_fscore_check_bdi_valid(struct super_block *sb) +{ + return exfat_bdev_check_bdi_valid(sb); +} + +static bool is_exfat(pbr_t *pbr) +{ + int i = 53; + + do { + if (pbr->bpb.f64.res_zero[i-1]) + break; + } while (--i); + return i ? false : true; +} + +inline pbr_t *read_pbr_with_logical_sector(struct super_block *sb, struct buffer_head **prev_bh) +{ + pbr_t *p_pbr = (pbr_t *) (*prev_bh)->b_data; + u16 logical_sect = 0; + + if (is_exfat(p_pbr)) + logical_sect = 1 << p_pbr->bsx.f64.sect_size_bits; + else + logical_sect = get_unaligned_le16(&p_pbr->bpb.f16.sect_size); + + /* is x a power of 2? + * (x) != 0 && (((x) & ((x) - 1)) == 0) + */ + if (!is_power_of_2(logical_sect) + || (logical_sect < 512) + || (logical_sect > 4096)) { + exfat_log_msg(sb, KERN_ERR, "bogus logical sector size %u", + logical_sect); + return NULL; + } + + if (logical_sect < sb->s_blocksize) { + exfat_log_msg(sb, KERN_ERR, + "logical sector size too small for device" + " (logical sector size = %u)", logical_sect); + return NULL; + } + + if (logical_sect > sb->s_blocksize) { + struct buffer_head *bh = NULL; + + __brelse(*prev_bh); + *prev_bh = NULL; + + if (!sb_set_blocksize(sb, logical_sect)) { + exfat_log_msg(sb, KERN_ERR, + "unable to set blocksize %u", logical_sect); + return NULL; + } + bh = sb_bread(sb, 0); + if (!bh) { + exfat_log_msg(sb, KERN_ERR, + "unable to read boot sector " + "(logical sector size = %lu)", sb->s_blocksize); + return NULL; + } + + *prev_bh = bh; + p_pbr = (pbr_t *) bh->b_data; + } + + exfat_log_msg(sb, KERN_INFO, + "set logical sector size : %lu", sb->s_blocksize); + + return p_pbr; +} + +/* mount the file system volume */ +s32 exfat_fscore_mount(struct super_block *sb) +{ + s32 ret; + pbr_t *p_pbr; + struct buffer_head *tmp_bh = NULL; + struct gendisk *disk = sb->s_bdev->bd_disk; + struct hd_struct *part = sb->s_bdev->bd_part; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + /* initialize previous I/O error */ + fsi->prev_eio = 0; + + /* open the block device */ + if (exfat_bdev_open_dev(sb)) + return -EIO; + + /* set block size to read super block */ + sb_min_blocksize(sb, 512); + + /* read boot sector */ + ret = exfat_read_sect(sb, 0, &tmp_bh, 1); + if (ret) { + exfat_log_msg(sb, KERN_ERR, "unable to read boot sector"); + ret = -EIO; + goto bd_close; + } + + /* PRB is read */ + p_pbr = (pbr_t *) tmp_bh->b_data; + + /* check the validity of PBR */ + if (le16_to_cpu((p_pbr->signature)) != PBR_SIGNATURE) { + exfat_log_msg(sb, KERN_ERR, "invalid boot record signature"); + brelse(tmp_bh); + ret = -EINVAL; + goto bd_close; + } + + /* check logical sector size */ + p_pbr = read_pbr_with_logical_sector(sb, &tmp_bh); + if (!p_pbr) { + brelse(tmp_bh); + ret = -EIO; + goto bd_close; + } + + /* fill fs_struct */ + if (!is_exfat(p_pbr)) { + ret = -EINVAL; + goto free_bh; + } + + /* set maximum file size for exFAT */ + sb->s_maxbytes = 0x7fffffffffffffffLL; + ret = mount_exfat(sb, p_pbr); + +free_bh: + brelse(tmp_bh); + if (ret) { + exfat_log_msg(sb, KERN_ERR, "failed to mount fs-core"); + goto bd_close; + } + + /* warn misaligned data data start sector must be a multiple of clu_size */ + exfat_log_msg(sb, KERN_INFO, + "(bps : %lu, spc : %u, data start : %llu, %s)", + sb->s_blocksize, fsi->sect_per_clus, fsi->data_start_sector, + (fsi->data_start_sector & (fsi->sect_per_clus - 1)) ? + "misaligned" : "aligned"); + + exfat_log_msg(sb, KERN_INFO, + "detected volume size : %llu KB (disk : %llu KB, " + "part : %llu KB)", + (fsi->num_sectors * (sb->s_blocksize >> SECTOR_SIZE_BITS)) >> 1, + disk ? (u64)((disk->part0.nr_sects) >> 1) : 0, + part ? (u64)((part->nr_sects) >> 1) : 0); + + ret = load_upcase_table(sb); + if (ret) { + exfat_log_msg(sb, KERN_ERR, "failed to load upcase table"); + goto bd_close; + } + + /* allocate-bitmap is only for exFAT */ + ret = exfat_load_alloc_bmp(sb); + if (ret) { + exfat_log_msg(sb, KERN_ERR, "failed to load alloc-bitmap"); + goto free_upcase; + } + + if (fsi->used_clusters == UINT_MAX) { + ret = fsi->fs_func->count_used_clusters(sb, &fsi->used_clusters); + if (ret) { + exfat_log_msg(sb, KERN_ERR, "failed to scan clusters"); + goto exfat_free_alloc_bmp; + } + } + + return 0; +exfat_free_alloc_bmp: + exfat_free_alloc_bmp(sb); +free_upcase: + free_upcase_table(sb); +bd_close: + exfat_bdev_close_dev(sb); + return ret; +} + +/* umount the file system volume */ +s32 exfat_fscore_umount(struct super_block *sb) +{ + s32 ret = 0; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + if (fs_sync(sb, 0)) + ret = -EIO; + + if (fs_set_vol_flags(sb, VOL_CLEAN)) + ret = -EIO; + + free_upcase_table(sb); + + exfat_free_alloc_bmp(sb); + + if (exfat_fcache_release_all(sb)) + ret = -EIO; + + if (exfat_dcache_release_all(sb)) + ret = -EIO; + + if (fsi->prev_eio) + ret = -EIO; + /* close the block device */ + exfat_bdev_close_dev(sb); + return ret; +} + +/* get the information of a file system volume */ +s32 exfat_fscore_statfs(struct super_block *sb, VOL_INFO_T *info) +{ + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + if (fsi->used_clusters == UINT_MAX) { + if (fsi->fs_func->count_used_clusters(sb, &fsi->used_clusters)) + return -EIO; + } + + info->ClusterSize = fsi->cluster_size; + info->NumClusters = fsi->num_clusters - 2; /* clu 0 & 1 */ + info->UsedClusters = fsi->used_clusters + fsi->reserved_clusters; + info->FreeClusters = info->NumClusters - info->UsedClusters; + + return 0; +} + +/* synchronize all file system volumes */ +s32 exfat_fscore_sync_fs(struct super_block *sb, s32 do_sync) +{ + /* synchronize the file system */ + if (fs_sync(sb, do_sync)) + return -EIO; + + if (fs_set_vol_flags(sb, VOL_CLEAN)) + return -EIO; + + return 0; +} + +/* stat allocation unit of a file system volume */ +u32 exfat_fscore_get_au_stat(struct super_block *sb, s32 mode) +{ + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + if (fsi->fs_func->get_au_stat) + return fsi->fs_func->get_au_stat(sb, mode); + + /* No error, just returns 0 */ + return 0; +} + +/* lookup a file */ +s32 exfat_fscore_lookup(struct inode *inode, u8 *path, FILE_ID_T *fid) +{ + s32 ret, dentry, num_entries; + CHAIN_T dir; + UNI_NAME_T uni_name; + DOS_NAME_T dos_name; + DENTRY_T *ep, *ep2; + ENTRY_SET_CACHE_T *es = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + FILE_ID_T *dir_fid = &(EXFAT_I(inode)->fid); + + /* check the validity of directory name in the given pathname */ + ret = resolve_path_for_lookup(inode, path, &dir, &uni_name); + if (ret) + return ret; + + ret = get_num_entries_and_dos_name(sb, &dir, &uni_name, &num_entries, &dos_name, 1); + if (ret) + return ret; + + /* check the validation of hint_stat and initialize it if required */ + if (dir_fid->version != (u32)(GET_IVERSION(inode) & 0xffffffff)) { + dir_fid->hint_stat.clu = dir.dir; + dir_fid->hint_stat.eidx = 0; + dir_fid->version = (u32)(GET_IVERSION(inode) & 0xffffffff); + dir_fid->hint_femp.eidx = -1; + } + + /* search the file name for directories */ + dentry = fsi->fs_func->find_dir_entry(sb, dir_fid, &dir, &uni_name, + num_entries, &dos_name, TYPE_ALL); + + if ((dentry < 0) && (dentry != -EEXIST)) + return dentry; /* -error value */ + + fid->dir.dir = dir.dir; + fid->dir.size = dir.size; + fid->dir.flags = dir.flags; + fid->entry = dentry; + + /* root directory itself */ + if (unlikely(dentry == -EEXIST)) { + fid->type = TYPE_DIR; + fid->rwoffset = 0; + fid->hint_bmap.off = CLUS_EOF; + + fid->attr = ATTR_SUBDIR; + fid->flags = 0x01; + fid->size = 0; + fid->start_clu = fsi->root_dir; + } else { + es = exfat_get_dentry_set_in_dir(sb, &dir, dentry, ES_2_ENTRIES, &ep); + if (!es) + return -EIO; + ep2 = ep+1; + + fid->type = fsi->fs_func->get_entry_type(ep); + fid->rwoffset = 0; + fid->hint_bmap.off = CLUS_EOF; + fid->attr = fsi->fs_func->get_entry_attr(ep); + + fid->size = fsi->fs_func->get_entry_size(ep2); + if ((fid->type == TYPE_FILE) && (fid->size == 0)) { + fid->flags = 0x03; + fid->start_clu = CLUS_EOF; + } else { + fid->flags = fsi->fs_func->get_entry_flag(ep2); + fid->start_clu = fsi->fs_func->get_entry_clu0(ep2); + } + + /* FOR GRACEFUL ERROR HANDLING */ + if (IS_CLUS_FREE(fid->start_clu)) { + exfat_fs_error(sb, + "non-zero size file starts with zero cluster " + "(size : %llu, p_dir : %u, entry : 0x%08x)", + fid->size, fid->dir.dir, fid->entry); + exfat_debug_bug_on(1); + return -EIO; + } + + exfat_release_dentry_set(es); + } + + /* hint_stat will be used if this is directory. */ + fid->version = 0; + fid->hint_stat.eidx = 0; + fid->hint_stat.clu = fid->start_clu; + fid->hint_femp.eidx = -1; + + return 0; +} + +/* create a file */ +s32 exfat_fscore_create(struct inode *inode, u8 *path, u8 mode, FILE_ID_T *fid) +{ + s32 ret/*, dentry*/; + CHAIN_T dir; + UNI_NAME_T uni_name; + struct super_block *sb = inode->i_sb; + + /* check the validity of directory name in the given pathname */ + ret = resolve_path(inode, path, &dir, &uni_name); + if (ret) + return ret; + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* create a new file */ + ret = create_file(inode, &dir, &uni_name, mode, fid); + + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); + + return ret; +} + +/* read data from a opened file */ +s32 exfat_fscore_read_link(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount) +{ + s32 ret = 0; + s32 offset, sec_offset; + u32 clu_offset; + u32 clu; + u64 logsector, oneblkread, read_bytes; + struct buffer_head *tmp_bh = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + /* check if the given file ID is opened */ + if (fid->type != TYPE_FILE) + return -EPERM; + + if (fid->rwoffset > fid->size) + fid->rwoffset = fid->size; + + if (count > (fid->size - fid->rwoffset)) + count = fid->size - fid->rwoffset; + + if (count == 0) { + if (rcount) + *rcount = 0; + return 0; + } + + read_bytes = 0; + + while (count > 0) { + clu_offset = fid->rwoffset >> fsi->cluster_size_bits; + clu = fid->start_clu; + + if (fid->flags == 0x03) { + clu += clu_offset; + } else { + /* hint information */ + if ((clu_offset > 0) && + ((fid->hint_bmap.off != CLUS_EOF) && (fid->hint_bmap.off > 0)) && + (clu_offset >= fid->hint_bmap.off)) { + clu_offset -= fid->hint_bmap.off; + clu = fid->hint_bmap.clu; + } + + while (clu_offset > 0) { + ret = get_next_clus_safe(sb, &clu); + if (ret) + goto err_out; + + clu_offset--; + } + } + + /* hint information */ + fid->hint_bmap.off = fid->rwoffset >> fsi->cluster_size_bits; + fid->hint_bmap.clu = clu; + + offset = (s32)(fid->rwoffset & (fsi->cluster_size - 1)); /* byte offset in cluster */ + sec_offset = offset >> sb->s_blocksize_bits; /* sector offset in cluster */ + offset &= (sb->s_blocksize - 1); /* byte offset in sector */ + + logsector = CLUS_TO_SECT(fsi, clu) + sec_offset; + + oneblkread = (u64)(sb->s_blocksize - offset); + if (oneblkread > count) + oneblkread = count; + + if ((offset == 0) && (oneblkread == sb->s_blocksize)) { + ret = exfat_read_sect(sb, logsector, &tmp_bh, 1); + if (ret) + goto err_out; + memcpy(((s8 *) buffer)+read_bytes, ((s8 *) tmp_bh->b_data), (s32) oneblkread); + } else { + ret = exfat_read_sect(sb, logsector, &tmp_bh, 1); + if (ret) + goto err_out; + memcpy(((s8 *) buffer)+read_bytes, ((s8 *) tmp_bh->b_data)+offset, (s32) oneblkread); + } + count -= oneblkread; + read_bytes += oneblkread; + fid->rwoffset += oneblkread; + } + +err_out: + brelse(tmp_bh); + + /* set the size of read bytes */ + if (rcount != NULL) + *rcount = read_bytes; + + return ret; +} + +/* write data into a opened file */ +s32 exfat_fscore_write_link(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount) +{ + s32 ret = 0; + s32 modified = false, offset, sec_offset; + u32 clu_offset, num_clusters, num_alloc; + u32 clu, last_clu; + u64 logsector, oneblkwrite, write_bytes; + CHAIN_T new_clu; + TIMESTAMP_T tm; + DENTRY_T *ep, *ep2; + ENTRY_SET_CACHE_T *es = NULL; + struct buffer_head *tmp_bh = NULL; + struct super_block *sb = inode->i_sb; + u32 blksize = (u32)sb->s_blocksize; + u32 blksize_mask = (u32)(sb->s_blocksize-1); + u8 blksize_bits = sb->s_blocksize_bits; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + /* check if the given file ID is opened */ + if (fid->type != TYPE_FILE) + return -EPERM; + + if (fid->rwoffset > fid->size) + fid->rwoffset = fid->size; + + if (count == 0) { + if (wcount) + *wcount = 0; + return 0; + } + + fs_set_vol_flags(sb, VOL_DIRTY); + + if (fid->size == 0) + num_clusters = 0; + else + num_clusters = ((fid->size-1) >> fsi->cluster_size_bits) + 1; + + write_bytes = 0; + + while (count > 0) { + clu_offset = (fid->rwoffset >> fsi->cluster_size_bits); + clu = last_clu = fid->start_clu; + + if (fid->flags == 0x03) { + if ((clu_offset > 0) && (!IS_CLUS_EOF(clu))) { + last_clu += clu_offset - 1; + + if (clu_offset == num_clusters) + clu = CLUS_EOF; + else + clu += clu_offset; + } + } else { + /* hint information */ + if ((clu_offset > 0) && + ((fid->hint_bmap.off != CLUS_EOF) && (fid->hint_bmap.off > 0)) && + (clu_offset >= fid->hint_bmap.off)) { + clu_offset -= fid->hint_bmap.off; + clu = fid->hint_bmap.clu; + } + + while ((clu_offset > 0) && (!IS_CLUS_EOF(clu))) { + last_clu = clu; + ret = get_next_clus_safe(sb, &clu); + if (ret) + goto err_out; + + clu_offset--; + } + } + + if (IS_CLUS_EOF(clu)) { + num_alloc = ((count-1) >> fsi->cluster_size_bits) + 1; + new_clu.dir = IS_CLUS_EOF(last_clu) ? CLUS_EOF : last_clu+1; + new_clu.size = 0; + new_clu.flags = fid->flags; + + /* (1) allocate a chain of clusters */ + ret = fsi->fs_func->alloc_cluster(sb, num_alloc, &new_clu, ALLOC_COLD); + if (ret) + goto err_out; + + /* (2) append to the FAT chain */ + if (IS_CLUS_EOF(last_clu)) { + if (new_clu.flags == 0x01) + fid->flags = 0x01; + fid->start_clu = new_clu.dir; + modified = true; + } else { + if (new_clu.flags != fid->flags) { + /* no-fat-chain bit is disabled, + * so fat-chain should be synced with + * alloc-bmp + */ + exfat_chain_cont_cluster(sb, fid->start_clu, num_clusters); + fid->flags = 0x01; + modified = true; + } + if (new_clu.flags == 0x01) { + ret = exfat_ent_set(sb, last_clu, new_clu.dir); + if (ret) + goto err_out; + } + } + + num_clusters += num_alloc; + clu = new_clu.dir; + } + + /* hint information */ + fid->hint_bmap.off = fid->rwoffset >> fsi->cluster_size_bits; + fid->hint_bmap.clu = clu; + + /* byte offset in cluster */ + offset = (s32)(fid->rwoffset & (fsi->cluster_size-1)); + /* sector offset in cluster */ + sec_offset = offset >> blksize_bits; + /* byte offset in sector */ + offset &= blksize_mask; + logsector = CLUS_TO_SECT(fsi, clu) + sec_offset; + + oneblkwrite = (u64)(blksize - offset); + if (oneblkwrite > count) + oneblkwrite = count; + + if ((offset == 0) && (oneblkwrite == blksize)) { + ret = exfat_read_sect(sb, logsector, &tmp_bh, 0); + if (ret) + goto err_out; + + memcpy(((s8 *)tmp_bh->b_data), + ((s8 *)buffer)+write_bytes, + (s32)oneblkwrite); + + ret = exfat_write_sect(sb, logsector, tmp_bh, 0); + if (ret) { + brelse(tmp_bh); + goto err_out; + } + } else { + if ((offset > 0) || ((fid->rwoffset+oneblkwrite) < fid->size)) { + ret = exfat_read_sect(sb, logsector, &tmp_bh, 1); + if (ret) + goto err_out; + } else { + ret = exfat_read_sect(sb, logsector, &tmp_bh, 0); + if (ret) + goto err_out; + } + + memcpy(((s8 *) tmp_bh->b_data)+offset, ((s8 *) buffer)+write_bytes, (s32) oneblkwrite); + ret = exfat_write_sect(sb, logsector, tmp_bh, 0); + if (ret) { + brelse(tmp_bh); + goto err_out; + } + } + + count -= oneblkwrite; + write_bytes += oneblkwrite; + fid->rwoffset += oneblkwrite; + + fid->attr |= ATTR_ARCHIVE; + + if (fid->size < fid->rwoffset) { + fid->size = fid->rwoffset; + modified = true; + } + } + + brelse(tmp_bh); + + /* (3) update the direcoty entry */ + /* get_entry_(set_)in_dir shoulb be check DIR_DELETED flag. */ + es = exfat_get_dentry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); + if (!es) { + ret = -EIO; + goto err_out; + } + ep2 = ep+1; + + fsi->fs_func->set_entry_time(ep, exfat_tm_now(EXFAT_SB(sb), &tm), TM_MODIFY); + fsi->fs_func->set_entry_attr(ep, fid->attr); + + if (modified) { + if (fsi->fs_func->get_entry_flag(ep2) != fid->flags) + fsi->fs_func->set_entry_flag(ep2, fid->flags); + + if (fsi->fs_func->get_entry_size(ep2) != fid->size) + fsi->fs_func->set_entry_size(ep2, fid->size); + + if (fsi->fs_func->get_entry_clu0(ep2) != fid->start_clu) + fsi->fs_func->set_entry_clu0(ep2, fid->start_clu); + } + + if (exfat_update_dir_chksum_with_entry_set(sb, es)) { + ret = -EIO; + goto err_out; + } + exfat_release_dentry_set(es); + + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); + +err_out: + /* set the size of written bytes */ + if (wcount) + *wcount = write_bytes; + + return ret; +} + +/* resize the file length */ +s32 exfat_fscore_truncate(struct inode *inode, u64 old_size, u64 new_size) +{ + u32 num_clusters_new, num_clusters_da, num_clusters_phys; + u32 last_clu = CLUS_FREE; + CHAIN_T clu; + TIMESTAMP_T tm; + DENTRY_T *ep, *ep2; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + ENTRY_SET_CACHE_T *es = NULL; + s32 evict = (fid->dir.dir == DIR_DELETED) ? 1 : 0; + + /* check if the given file ID is opened */ + if ((fid->type != TYPE_FILE) && (fid->type != TYPE_DIR)) + return -EPERM; + + /* TO CHECK inode type and size */ + MMSG("%s: inode(%p) type(%s) size:%lld->%lld\n", __func__, inode, + (fid->type == TYPE_FILE) ? "file" : "dir", old_size, new_size); + + /* XXX : This is for debugging. */ + + /* It can be when write failed */ +#if 0 + if (fid->size != old_size) { + DMSG("%s: inode(%p) size-mismatch(old:%lld != fid:%lld)\n", + __func__, inode, old_size, fid->size); + WARN_ON(1); + } +#endif + /* + * There is no lock to protect fid->size. + * So, we should get old_size and use it. + */ + if (old_size <= new_size) + return 0; + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* Reserved count update */ + #define num_clusters(v) ((v) ? (u32)(((v) - 1) >> fsi->cluster_size_bits) + 1 : 0) + num_clusters_da = num_clusters(EXFAT_I(inode)->i_size_aligned); + num_clusters_new = num_clusters(i_size_read(inode)); + num_clusters_phys = num_clusters(EXFAT_I(inode)->i_size_ondisk); + + /* num_clusters(i_size_old) should be equal to num_clusters_da */ + BUG_ON((num_clusters(old_size)) != (num_clusters(EXFAT_I(inode)->i_size_aligned))); + + /* for debugging (FIXME: is okay on no-da case?) */ + BUG_ON(num_clusters_da < num_clusters_phys); + + if ((num_clusters_da != num_clusters_phys) && + (num_clusters_new < num_clusters_da)) { + /* Decrement reserved clusters + * n_reserved = num_clusters_da - max(new,phys) + */ + int n_reserved = (num_clusters_new > num_clusters_phys) ? + (num_clusters_da - num_clusters_new) : + (num_clusters_da - num_clusters_phys); + + fsi->reserved_clusters -= n_reserved; + BUG_ON(fsi->reserved_clusters < 0); + } + + clu.dir = fid->start_clu; + /* In no-da case, num_clusters_phys is equal to below value + * clu.size = (u32)((old_size-1) >> fsi->cluster_size_bits) + 1; + */ + clu.size = num_clusters_phys; + clu.flags = fid->flags; + + if (new_size > 0) { + /* Truncate FAT chain num_clusters after the first cluster + * num_clusters = min(new, phys); + */ + u32 num_clusters = (num_clusters_new < num_clusters_phys) ? + num_clusters_new : num_clusters_phys; + + /* Follow FAT chain + * (defensive coding - works fine even with corrupted FAT table + */ + if (clu.flags == 0x03) { + clu.dir += num_clusters; + clu.size -= num_clusters; +#if 0 + /* exfat_extent_get_clus can`t know last_cluster + * when find target cluster in cache. + */ + } else if (fid->type == TYPE_FILE) { + u32 fclus = 0; + s32 err = exfat_extent_get_clus(inode, num_clusters, + &fclus, &(clu.dir), &last_clu, 0); + if (err) + return -EIO; + ASSERT(fclus == num_clusters); + + if ((num_clusters > 1) && (last_clu == fid->start_clu)) { + u32 fclus_tmp = 0; + u32 temp = 0; + + err = exfat_extent_get_clus(inode, num_clusters - 1, + &fclus_tmp, &last_clu, &temp, 0); + if (err) + return -EIO; + ASSERT(fclus_tmp == (num_clusters - 1)); + } + + num_clusters -= fclus; + clu.size -= fclus; +#endif + } else { + while (num_clusters > 0) { + last_clu = clu.dir; + if (get_next_clus_safe(sb, &(clu.dir))) + return -EIO; + + num_clusters--; + clu.size--; + } + } + + /* Optimization avialable: */ +#if 0 + if (num_clusters_new < num_clusters) { + < loop > + } else { + // num_clusters_new >= num_clusters_phys + // FAT truncation is not necessary + + clu.dir = CLUS_EOF; + clu.size = 0; + } +#endif + } else if (new_size == 0) { + fid->flags = 0x03; + fid->start_clu = CLUS_EOF; + } + fid->size = new_size; + + if (fid->type == TYPE_FILE) + fid->attr |= ATTR_ARCHIVE; + + /* + * clu.dir: free from + * clu.size: # of clusters to free (exFAT, 0x03 only), no fat_free if 0 + * clu.flags: fid->flags (exFAT only) + */ + + /* (1) update the directory entry */ + if (!evict) { + es = exfat_get_dentry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); + if (!es) + return -EIO; + ep2 = ep+1; + + fsi->fs_func->set_entry_time(ep, exfat_tm_now(EXFAT_SB(sb), &tm), TM_MODIFY); + fsi->fs_func->set_entry_attr(ep, fid->attr); + + /* File size should be zero if there is no cluster allocated */ + if (IS_CLUS_EOF(fid->start_clu)) + fsi->fs_func->set_entry_size(ep2, 0); + else + fsi->fs_func->set_entry_size(ep2, new_size); + + if (new_size == 0) { + /* Any directory can not be truncated to zero */ + BUG_ON(fid->type != TYPE_FILE); + + fsi->fs_func->set_entry_flag(ep2, 0x01); + fsi->fs_func->set_entry_clu0(ep2, CLUS_FREE); + } + + if (exfat_update_dir_chksum_with_entry_set(sb, es)) + return -EIO; + exfat_release_dentry_set(es); + } + + /* (2) cut off from the FAT chain */ + if ((fid->flags == 0x01) && + (!IS_CLUS_FREE(last_clu)) && (!IS_CLUS_EOF(last_clu))) { + if (exfat_ent_set(sb, last_clu, CLUS_EOF)) + return -EIO; + } + + /* (3) invalidate cache and free the clusters */ + /* clear extent cache */ + exfat_extent_cache_inval_inode(inode); + + /* hint information */ + fid->hint_bmap.off = CLUS_EOF; + fid->hint_bmap.clu = CLUS_EOF; + if (fid->rwoffset > fid->size) + fid->rwoffset = fid->size; + + /* hint_stat will be used if this is directory. */ + fid->hint_stat.eidx = 0; + fid->hint_stat.clu = fid->start_clu; + fid->hint_femp.eidx = -1; + + /* free the clusters */ + if (fsi->fs_func->free_cluster(sb, &clu, evict)) + return -EIO; + + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); + + return 0; +} + +static void update_parent_info(FILE_ID_T *fid, struct inode *parent_inode) +{ + FS_INFO_T *fsi = &(EXFAT_SB(parent_inode->i_sb)->fsi); + FILE_ID_T *parent_fid = &(EXFAT_I(parent_inode)->fid); + + /* + * the problem that FILE_ID_T caches wrong parent info. + * + * because of flag-mismatch of fid->dir, + * there is abnormal traversing cluster chain. + */ + if (unlikely((parent_fid->flags != fid->dir.flags) + || (parent_fid->size != (fid->dir.size<cluster_size_bits)) + || (parent_fid->start_clu != fid->dir.dir))) { + + fid->dir.dir = parent_fid->start_clu; + fid->dir.flags = parent_fid->flags; + fid->dir.size = ((parent_fid->size + (fsi->cluster_size-1)) + >> fsi->cluster_size_bits); + } +} + +/* rename or move a old file into a new file */ +s32 exfat_fscore_rename(struct inode *old_parent_inode, FILE_ID_T *fid, + struct inode *new_parent_inode, struct dentry *new_dentry) +{ + s32 ret; + s32 dentry; + CHAIN_T olddir, newdir; + CHAIN_T *p_dir = NULL; + UNI_NAME_T uni_name; + DENTRY_T *ep; + struct super_block *sb = old_parent_inode->i_sb; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + u8 *new_path = (u8 *) new_dentry->d_name.name; + struct inode *new_inode = new_dentry->d_inode; + int num_entries; + FILE_ID_T *new_fid = NULL; + u32 new_entry_type = TYPE_UNUSED; + s32 new_entry = 0; + + /* check the validity of pointer parameters */ + if ((new_path == NULL) || (strlen(new_path) == 0)) + return -EINVAL; + + if (fid->dir.dir == DIR_DELETED) { + EMSG("%s : abnormal access to deleted source dentry\n", __func__); + return -ENOENT; + } + + /* patch 1.2.4 : the problem that FILE_ID_T caches wrong parent info. */ + update_parent_info(fid, old_parent_inode); + + olddir.dir = fid->dir.dir; + olddir.size = fid->dir.size; + olddir.flags = fid->dir.flags; + + dentry = fid->entry; + + ep = exfat_get_dentry_in_dir(sb, &olddir, dentry, NULL); + if (!ep) + return -EIO; + + if (fsi->fs_func->get_entry_attr(ep) & ATTR_READONLY) + return -EPERM; + + /* check whether new dir is existing directory and empty */ + if (new_inode) { + ret = -EIO; + new_fid = &EXFAT_I(new_inode)->fid; + + if (new_fid->dir.dir == DIR_DELETED) { + EMSG("%s : abnormal access to deleted target dentry\n", __func__); + goto out; + } + + /* patch 1.2.4 : + * the problem that FILE_ID_T caches wrong parent info. + * + * FIXME : is needed? + */ + update_parent_info(new_fid, new_parent_inode); + + p_dir = &(new_fid->dir); + new_entry = new_fid->entry; + ep = exfat_get_dentry_in_dir(sb, p_dir, new_entry, NULL); + if (!ep) + goto out; + + new_entry_type = fsi->fs_func->get_entry_type(ep); + + /* if new_inode exists, update fid */ + new_fid->size = i_size_read(new_inode); + + if (new_entry_type == TYPE_DIR) { + CHAIN_T new_clu; + + new_clu.dir = new_fid->start_clu; + new_clu.size = ((new_fid->size-1) >> fsi->cluster_size_bits) + 1; + new_clu.flags = new_fid->flags; + + ret = check_dir_empty(sb, &new_clu); + if (ret) + return ret; + } + } + + /* check the validity of directory name in the given new pathname */ + ret = resolve_path(new_parent_inode, new_path, &newdir, &uni_name); + if (ret) + return ret; + + fs_set_vol_flags(sb, VOL_DIRTY); + + if (olddir.dir == newdir.dir) + ret = rename_file(new_parent_inode, &olddir, dentry, &uni_name, fid); + else + ret = move_file(new_parent_inode, &olddir, dentry, &newdir, &uni_name, fid); + + if ((!ret) && new_inode) { + /* delete entries of new_dir */ + ep = exfat_get_dentry_in_dir(sb, p_dir, new_entry, NULL); + if (!ep) { + ret = -EIO; + goto del_out; + } + + num_entries = fsi->fs_func->count_ext_entries(sb, p_dir, new_entry, ep); + if (num_entries < 0) { + ret = -EIO; + goto del_out; + } + + + if (fsi->fs_func->delete_dir_entry(sb, p_dir, new_entry, 0, num_entries+1)) { + ret = -EIO; + goto del_out; + } + + /* Free the clusters if new_inode is a dir(as if exfat_fscore_rmdir) */ + if (new_entry_type == TYPE_DIR) { + /* new_fid, new_clu_to_free */ + CHAIN_T new_clu_to_free; + + new_clu_to_free.dir = new_fid->start_clu; + new_clu_to_free.size = ((new_fid->size-1) >> fsi->cluster_size_bits) + 1; + new_clu_to_free.flags = new_fid->flags; + + if (fsi->fs_func->free_cluster(sb, &new_clu_to_free, 1)) { + /* just set I/O error only */ + ret = -EIO; + } + + new_fid->size = 0; + new_fid->start_clu = CLUS_EOF; + new_fid->flags = 0x03; + } +del_out: + /* Update new_inode fid + * Prevent syncing removed new_inode + * (new_fid is already initialized above code ("if (new_inode)") + */ + new_fid->dir.dir = DIR_DELETED; + } +out: + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); + + return ret; +} + +/* remove a file */ +s32 exfat_fscore_remove(struct inode *inode, FILE_ID_T *fid) +{ + s32 ret; + s32 dentry; + CHAIN_T dir, clu_to_free; + DENTRY_T *ep; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + dir.dir = fid->dir.dir; + dir.size = fid->dir.size; + dir.flags = fid->dir.flags; + + dentry = fid->entry; + + if (fid->dir.dir == DIR_DELETED) { + EMSG("%s : abnormal access to deleted dentry\n", __func__); + return -ENOENT; + } + + ep = exfat_get_dentry_in_dir(sb, &dir, dentry, NULL); + if (!ep) + return -EIO; + + if (fsi->fs_func->get_entry_attr(ep) & ATTR_READONLY) + return -EPERM; + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* (1) update the directory entry */ + ret = remove_file(inode, &dir, dentry); + if (ret) + goto out; + + clu_to_free.dir = fid->start_clu; + clu_to_free.size = ((fid->size-1) >> fsi->cluster_size_bits) + 1; + clu_to_free.flags = fid->flags; + + /* (2) invalidate extent cache and free the clusters + */ + /* clear extent cache */ + exfat_extent_cache_inval_inode(inode); + ret = fsi->fs_func->free_cluster(sb, &clu_to_free, 0); + /* WARN : DO NOT RETURN ERROR IN HERE */ + + /* (3) update FILE_ID_T */ + fid->size = 0; + fid->start_clu = CLUS_EOF; + fid->flags = 0x03; + fid->dir.dir = DIR_DELETED; + + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +out: + return ret; +} + + +/* + * Get the information of a given file + * REMARK : This function does not need any file name on linux + * + * info.Size means the value saved on disk. + * But root directory doesn`t have real dentry, + * so the size of root directory returns calculated one exceptively. + */ +s32 exfat_fscore_read_inode(struct inode *inode, DIR_ENTRY_T *info) +{ + s32 count; + CHAIN_T dir; + TIMESTAMP_T tm; + DENTRY_T *ep, *ep2; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + ENTRY_SET_CACHE_T *es = NULL; + u8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0; + + exfat_extent_cache_init_inode(inode); + + /* if root directory */ + if (is_dir && (fid->dir.dir == fsi->root_dir) && (fid->entry == -1)) { + info->Attr = ATTR_SUBDIR; + memset((s8 *) &info->CreateTimestamp, 0, sizeof(DATE_TIME_T)); + memset((s8 *) &info->ModifyTimestamp, 0, sizeof(DATE_TIME_T)); + memset((s8 *) &info->AccessTimestamp, 0, sizeof(DATE_TIME_T)); + //strcpy(info->NameBuf.sfn, "."); + //strcpy(info->NameBuf.lfn, "."); + + dir.dir = fsi->root_dir; + dir.flags = 0x01; + dir.size = 0; /* UNUSED */ + + /* FAT16 root_dir */ + if (IS_CLUS_FREE(fsi->root_dir)) { + info->Size = fsi->dentries_in_root << DENTRY_SIZE_BITS; + } else { + u32 num_clu; + + if (__count_num_clusters(sb, &dir, &num_clu)) + return -EIO; + info->Size = (u64)num_clu << fsi->cluster_size_bits; + } + + count = __count_dos_name_entries(sb, &dir, TYPE_DIR, NULL); + if (count < 0) + return -EIO; + info->NumSubdirs = count; + + return 0; + } + + /* get the directory entry of given file or directory */ + /* es should be released */ + es = exfat_get_dentry_set_in_dir(sb, &(fid->dir), fid->entry, ES_2_ENTRIES, &ep); + if (!es) + return -EIO; + ep2 = ep+1; + + /* set FILE_INFO structure using the acquired DENTRY_T */ + info->Attr = fsi->fs_func->get_entry_attr(ep); + + fsi->fs_func->get_entry_time(ep, &tm, TM_CREATE); + info->CreateTimestamp.Year = tm.year; + info->CreateTimestamp.Month = tm.mon; + info->CreateTimestamp.Day = tm.day; + info->CreateTimestamp.Hour = tm.hour; + info->CreateTimestamp.Minute = tm.min; + info->CreateTimestamp.Second = tm.sec; + info->CreateTimestamp.MilliSecond = 0; + + fsi->fs_func->get_entry_time(ep, &tm, TM_MODIFY); + info->ModifyTimestamp.Year = tm.year; + info->ModifyTimestamp.Month = tm.mon; + info->ModifyTimestamp.Day = tm.day; + info->ModifyTimestamp.Hour = tm.hour; + info->ModifyTimestamp.Minute = tm.min; + info->ModifyTimestamp.Second = tm.sec; + info->ModifyTimestamp.MilliSecond = 0; + + memset((s8 *) &info->AccessTimestamp, 0, sizeof(DATE_TIME_T)); + + info->NumSubdirs = 0; + info->Size = fsi->fs_func->get_entry_size(ep2); + + exfat_release_dentry_set(es); + + if (is_dir) { + u32 dotcnt = 0; + + dir.dir = fid->start_clu; + dir.flags = fid->flags; + dir.size = fid->size >> fsi->cluster_size_bits; + /* + * NOTE : + * If "dir.flags" has 0x01, "dir.size" is meaningless. + */ +#if 0 + if (info->Size == 0) { + s32 num_clu; + + if (__count_num_clusters(sb, &dir, &num_clu)) + return -EIO; + info->Size = (u64)num_clu << fsi->cluster_size_bits; + } +#endif + count = __count_dos_name_entries(sb, &dir, TYPE_DIR, &dotcnt); + if (count < 0) + return -EIO; + + count += EXFAT_MIN_SUBDIR; + info->NumSubdirs = count; + } + + return 0; +} + +/* set the information of a given file + * REMARK : This function does not need any file name on linux + */ +s32 exfat_fscore_write_inode(struct inode *inode, DIR_ENTRY_T *info, s32 sync) +{ + s32 ret = -EIO; + TIMESTAMP_T tm; + DENTRY_T *ep, *ep2; + ENTRY_SET_CACHE_T *es = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + u8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0; + u64 on_disk_size; + + /* SKIP WRITING INODE : + * if the indoe is already unlinked, + * there is no need for updating inode + */ + if (fid->dir.dir == DIR_DELETED) + return 0; + + if (is_dir && (fid->dir.dir == fsi->root_dir) && (fid->entry == -1)) + return 0; + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* get the directory entry of given file or directory */ + es = exfat_get_dentry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); + if (!es) + return -EIO; + ep2 = ep+1; + + fsi->fs_func->set_entry_attr(ep, info->Attr); + + /* set FILE_INFO structure using the acquired DENTRY_T */ + tm.sec = info->CreateTimestamp.Second; + tm.min = info->CreateTimestamp.Minute; + tm.hour = info->CreateTimestamp.Hour; + tm.day = info->CreateTimestamp.Day; + tm.mon = info->CreateTimestamp.Month; + tm.year = info->CreateTimestamp.Year; + fsi->fs_func->set_entry_time(ep, &tm, TM_CREATE); + + tm.sec = info->ModifyTimestamp.Second; + tm.min = info->ModifyTimestamp.Minute; + tm.hour = info->ModifyTimestamp.Hour; + tm.day = info->ModifyTimestamp.Day; + tm.mon = info->ModifyTimestamp.Month; + tm.year = info->ModifyTimestamp.Year; + fsi->fs_func->set_entry_time(ep, &tm, TM_MODIFY); + + /* File size should be zero if there is no cluster allocated */ + on_disk_size = info->Size; + + if (IS_CLUS_EOF(fid->start_clu)) + on_disk_size = 0; + + fsi->fs_func->set_entry_size(ep2, on_disk_size); + + ret = exfat_update_dir_chksum_with_entry_set(sb, es); + exfat_release_dentry_set(es); + + fs_sync(sb, sync); + /* Comment below code to prevent super block update frequently */ + //fs_set_vol_flags(sb, VOL_CLEAN); + + return ret; +} + + +/* + * Input: inode, (logical) clu_offset, target allocation area + * Output: errcode, cluster number + * *clu = (~0), if it's unable to allocate a new cluster + */ +s32 exfat_fscore_map_clus(struct inode *inode, u32 clu_offset, u32 *clu, int dest) +{ + s32 ret, modified = false; + u32 last_clu; + CHAIN_T new_clu; + DENTRY_T *ep; + ENTRY_SET_CACHE_T *es = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + u32 local_clu_offset = clu_offset; + s32 reserved_clusters = fsi->reserved_clusters; + u32 num_to_be_allocated = 0, num_clusters = 0; + + fid->rwoffset = (s64)(clu_offset) << fsi->cluster_size_bits; + + if (EXFAT_I(inode)->i_size_ondisk > 0) + num_clusters = (u32)((EXFAT_I(inode)->i_size_ondisk-1) >> fsi->cluster_size_bits) + 1; + + if (clu_offset >= num_clusters) + num_to_be_allocated = clu_offset - num_clusters + 1; + + if ((dest == ALLOC_NOWHERE) && (num_to_be_allocated > 0)) { + *clu = CLUS_EOF; + return 0; + } + + /* check always request cluster is 1 */ + //ASSERT(num_to_be_allocated == 1); + + *clu = last_clu = fid->start_clu; + + /* XXX: Defensive code needed. + * what if i_size_ondisk != # of allocated clusters + */ + if (fid->flags == 0x03) { + if ((clu_offset > 0) && (!IS_CLUS_EOF(*clu))) { + last_clu += clu_offset - 1; + + if (clu_offset == num_clusters) + *clu = CLUS_EOF; + else + *clu += clu_offset; + } + } else if (fid->type == TYPE_FILE) { + u32 fclus = 0; + s32 err = exfat_extent_get_clus(inode, clu_offset, + &fclus, clu, &last_clu, 1); + if (err) + return -EIO; + + clu_offset -= fclus; + } else { + /* hint information */ + if ((clu_offset > 0) && + ((fid->hint_bmap.off != CLUS_EOF) && (fid->hint_bmap.off > 0)) && + (clu_offset >= fid->hint_bmap.off)) { + clu_offset -= fid->hint_bmap.off; + /* hint_bmap.clu should be valid */ + ASSERT(fid->hint_bmap.clu >= 2); + *clu = fid->hint_bmap.clu; + } + + while ((clu_offset > 0) && (!IS_CLUS_EOF(*clu))) { + last_clu = *clu; + if (get_next_clus_safe(sb, clu)) + return -EIO; + clu_offset--; + } + } + + if (IS_CLUS_EOF(*clu)) { + fs_set_vol_flags(sb, VOL_DIRTY); + + new_clu.dir = (IS_CLUS_EOF(last_clu)) ? CLUS_EOF : last_clu + 1; + new_clu.size = 0; + new_clu.flags = fid->flags; + + /* (1) allocate a cluster */ + if (num_to_be_allocated < 1) { + /* Broken FAT (i_sze > allocated FAT) */ + EMSG("%s: invalid fat chain : inode(%p) " + "num_to_be_allocated(%d) " + "i_size_ondisk(%lld) fid->flags(%02x) " + "fid->start(%08x) fid->hint_off(%u) " + "fid->hint_clu(%u) fid->rwoffset(%llu) " + "modified_clu_off(%d) last_clu(%08x) " + "new_clu(%08x)", __func__, inode, + num_to_be_allocated, + (EXFAT_I(inode)->i_size_ondisk), + fid->flags, fid->start_clu, + fid->hint_bmap.off, fid->hint_bmap.clu, + fid->rwoffset, clu_offset, + last_clu, new_clu.dir); + exfat_fs_error(sb, "broken FAT chain."); + return -EIO; + } + + ret = fsi->fs_func->alloc_cluster(sb, num_to_be_allocated, &new_clu, ALLOC_COLD); + if (ret) + return ret; + + if (IS_CLUS_EOF(new_clu.dir) || IS_CLUS_FREE(new_clu.dir)) { + exfat_fs_error(sb, "bogus cluster new allocated" + "(last_clu : %u, new_clu : %u)", + last_clu, new_clu.dir); + ASSERT(0); + return -EIO; + } + + /* (2) append to the FAT chain */ + if (IS_CLUS_EOF(last_clu)) { + if (new_clu.flags == 0x01) + fid->flags = 0x01; + fid->start_clu = new_clu.dir; + modified = true; + } else { + if (new_clu.flags != fid->flags) { + /* no-fat-chain bit is disabled, + * so fat-chain should be synced with alloc-bmp + */ + exfat_chain_cont_cluster(sb, fid->start_clu, num_clusters); + fid->flags = 0x01; + modified = true; + } + if (new_clu.flags == 0x01) + if (exfat_ent_set(sb, last_clu, new_clu.dir)) + return -EIO; + } + + num_clusters += num_to_be_allocated; + *clu = new_clu.dir; + + if (fid->dir.dir != DIR_DELETED) { + es = exfat_get_dentry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); + if (!es) + return -EIO; + /* get stream entry */ + ep++; + + /* (3) update directory entry */ + if (modified) { + if (fsi->fs_func->get_entry_flag(ep) != fid->flags) + fsi->fs_func->set_entry_flag(ep, fid->flags); + + if (fsi->fs_func->get_entry_clu0(ep) != fid->start_clu) + fsi->fs_func->set_entry_clu0(ep, fid->start_clu); + + fsi->fs_func->set_entry_size(ep, fid->size); + } + + if (exfat_update_dir_chksum_with_entry_set(sb, es)) + return -EIO; + exfat_release_dentry_set(es); + } + + /* add number of new blocks to inode */ + inode->i_blocks += num_to_be_allocated << (fsi->cluster_size_bits - sb->s_blocksize_bits); +#if 0 + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + /* (4) Move *clu pointer along FAT chains (hole care) + * because the caller of this function expect *clu to be the last cluster. + * This only works when num_to_be_allocated >= 2, + * *clu = (the first cluster of the allocated chain) => (the last cluster of ...) + */ + if (fid->flags == 0x03) { + *clu += num_to_be_allocated - 1; + } else { + while (num_to_be_allocated > 1) { + if (get_next_clus_safe(sb, clu)) + return -EIO; + num_to_be_allocated--; + } + } + + } + + /* update reserved_clusters */ + fsi->reserved_clusters = reserved_clusters; + + /* hint information */ + fid->hint_bmap.off = local_clu_offset; + fid->hint_bmap.clu = *clu; + + return 0; +} + +/* remove an entry, BUT don't truncate */ +s32 exfat_fscore_unlink(struct inode *inode, FILE_ID_T *fid) +{ + s32 dentry; + CHAIN_T dir; + DENTRY_T *ep; + struct super_block *sb = inode->i_sb; + + dir.dir = fid->dir.dir; + dir.size = fid->dir.size; + dir.flags = fid->dir.flags; + + dentry = fid->entry; + + if (fid->dir.dir == DIR_DELETED) { + EMSG("%s : abnormal access to deleted dentry\n", __func__); + return -ENOENT; + } + + ep = exfat_get_dentry_in_dir(sb, &dir, dentry, NULL); + if (!ep) + return -EIO; + + if (EXFAT_SB(sb)->fsi.fs_func->get_entry_attr(ep) & ATTR_READONLY) + return -EPERM; + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* (1) update the directory entry */ + if (remove_file(inode, &dir, dentry)) + return -EIO; + + /* This doesn't modify fid */ + fid->dir.dir = DIR_DELETED; + + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); + + return 0; +} + +/* create a directory */ +s32 exfat_fscore_mkdir(struct inode *inode, u8 *path, FILE_ID_T *fid) +{ + s32 ret/*, dentry*/; + CHAIN_T dir; + UNI_NAME_T uni_name; + struct super_block *sb = inode->i_sb; + + /* check the validity of directory name in the given old pathname */ + ret = resolve_path(inode, path, &dir, &uni_name); + if (ret) + goto out; + + fs_set_vol_flags(sb, VOL_DIRTY); + + ret = create_dir(inode, &dir, &uni_name, fid); + + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +out: + return ret; +} + +/* read a directory entry from the opened directory */ +s32 exfat_fscore_readdir(struct inode *inode, DIR_ENTRY_T *dir_entry) +{ + s32 i; + s32 dentries_per_clu, dentries_per_clu_bits = 0; + u32 type, clu_offset; + u64 sector; + CHAIN_T dir, clu; + UNI_NAME_T uni_name; + TIMESTAMP_T tm; + DENTRY_T *ep; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + u32 dentry = (u32)(fid->rwoffset & 0xFFFFFFFF); /* u32 is enough for directory */ + + /* check if the given file ID is opened */ + if (fid->type != TYPE_DIR) + return -EPERM; + + if (fid->entry == -1) { + dir.dir = fsi->root_dir; + dir.size = 0; /* just initialize, but will not use */ + dir.flags = 0x01; + } else { + dir.dir = fid->start_clu; + dir.size = fid->size >> fsi->cluster_size_bits; + dir.flags = fid->flags; + exfat_debug_bug_on(dentry >= (dir.size * fsi->dentries_per_clu)); + } + + if (IS_CLUS_FREE(dir.dir)) { /* FAT16 root_dir */ + dentries_per_clu = fsi->dentries_in_root; + + /* Prevent readdir over directory size */ + if (dentry >= dentries_per_clu) { + clu.dir = CLUS_EOF; + } else { + clu.dir = dir.dir; + clu.size = dir.size; + clu.flags = dir.flags; + } + } else { + dentries_per_clu = fsi->dentries_per_clu; + dentries_per_clu_bits = ilog2(dentries_per_clu); + + clu_offset = dentry >> dentries_per_clu_bits; + clu.dir = dir.dir; + clu.size = dir.size; + clu.flags = dir.flags; + + if (clu.flags == 0x03) { + clu.dir += clu_offset; + clu.size -= clu_offset; + } else { + /* hint_information */ + if ((clu_offset > 0) && + ((fid->hint_bmap.off != CLUS_EOF) && (fid->hint_bmap.off > 0)) && + (clu_offset >= fid->hint_bmap.off)) { + clu_offset -= fid->hint_bmap.off; + clu.dir = fid->hint_bmap.clu; + } + + while (clu_offset > 0) { + if (get_next_clus_safe(sb, &(clu.dir))) + return -EIO; + + clu_offset--; + } + } + } + + while (!IS_CLUS_EOF(clu.dir)) { + if (IS_CLUS_FREE(dir.dir)) /* FAT16 root_dir */ + i = dentry % dentries_per_clu; + else + i = dentry & (dentries_per_clu-1); + + for ( ; i < dentries_per_clu; i++, dentry++) { + ep = exfat_get_dentry_in_dir(sb, &clu, i, §or); + if (!ep) + return -EIO; + + type = fsi->fs_func->get_entry_type(ep); + + if (type == TYPE_UNUSED) + break; + + if ((type != TYPE_FILE) && (type != TYPE_DIR)) + continue; + + exfat_dcache_lock(sb, sector); + dir_entry->Attr = fsi->fs_func->get_entry_attr(ep); + + fsi->fs_func->get_entry_time(ep, &tm, TM_CREATE); + dir_entry->CreateTimestamp.Year = tm.year; + dir_entry->CreateTimestamp.Month = tm.mon; + dir_entry->CreateTimestamp.Day = tm.day; + dir_entry->CreateTimestamp.Hour = tm.hour; + dir_entry->CreateTimestamp.Minute = tm.min; + dir_entry->CreateTimestamp.Second = tm.sec; + dir_entry->CreateTimestamp.MilliSecond = 0; + + fsi->fs_func->get_entry_time(ep, &tm, TM_MODIFY); + dir_entry->ModifyTimestamp.Year = tm.year; + dir_entry->ModifyTimestamp.Month = tm.mon; + dir_entry->ModifyTimestamp.Day = tm.day; + dir_entry->ModifyTimestamp.Hour = tm.hour; + dir_entry->ModifyTimestamp.Minute = tm.min; + dir_entry->ModifyTimestamp.Second = tm.sec; + dir_entry->ModifyTimestamp.MilliSecond = 0; + + memset((s8 *) &dir_entry->AccessTimestamp, 0, sizeof(DATE_TIME_T)); + + *(uni_name.name) = 0x0; + fsi->fs_func->get_uniname_from_ext_entry(sb, &dir, dentry, uni_name.name); + if (*(uni_name.name) == 0x0) + exfat_get_uniname_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x1); + exfat_nls_uni16s_to_vfsname(sb, &uni_name, + dir_entry->NameBuf.lfn, + dir_entry->NameBuf.lfnbuf_len); + exfat_dcache_unlock(sb, sector); + + ep = exfat_get_dentry_in_dir(sb, &clu, i+1, NULL); + if (!ep) + return -EIO; + + dir_entry->Size = fsi->fs_func->get_entry_size(ep); + + /* + * Update hint information : + * fat16 root directory does not need it. + */ + if (!IS_CLUS_FREE(dir.dir)) { + fid->hint_bmap.off = dentry >> dentries_per_clu_bits; + fid->hint_bmap.clu = clu.dir; + } + + fid->rwoffset = (s64) ++dentry; + + return 0; + } + + /* fat16 root directory */ + if (IS_CLUS_FREE(dir.dir)) + break; + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUS_EOF; + } else { + if (get_next_clus_safe(sb, &(clu.dir))) + return -EIO; + } + } + + dir_entry->NameBuf.lfn[0] = '\0'; + + fid->rwoffset = (s64)dentry; + + return 0; +} + +/* remove a directory */ +s32 exfat_fscore_rmdir(struct inode *inode, FILE_ID_T *fid) +{ + s32 ret; + s32 dentry; + DENTRY_T *ep; + CHAIN_T dir, clu_to_free; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + dir.dir = fid->dir.dir; + dir.size = fid->dir.size; + dir.flags = fid->dir.flags; + + dentry = fid->entry; + + if (fid->dir.dir == DIR_DELETED) { + EMSG("%s : abnormal access to deleted dentry\n", __func__); + return -ENOENT; + } + + ep = exfat_get_dentry_in_dir(sb, &dir, dentry, NULL); + if (!ep) + return -EIO; + + if (EXFAT_SB(sb)->fsi.fs_func->get_entry_attr(ep) & ATTR_READONLY) + return -EPERM; + + clu_to_free.dir = fid->start_clu; + clu_to_free.size = ((fid->size-1) >> fsi->cluster_size_bits) + 1; + clu_to_free.flags = fid->flags; + + ret = check_dir_empty(sb, &clu_to_free); + if (ret) { + if (ret == -EIO) + EMSG("%s : failed to check_dir_empty : err(%d)\n", + __func__, ret); + return ret; + } + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* (1) update the directory entry */ + ret = remove_file(inode, &dir, dentry); + if (ret) { + EMSG("%s : failed to remove_file : err(%d)\n", __func__, ret); + return ret; + } + + fid->dir.dir = DIR_DELETED; + + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); + + return ret; +} diff --git a/fs/exfat/core.h b/fs/exfat/core.h new file mode 100644 index 00000000..258bb490 --- /dev/null +++ b/fs/exfat/core.h @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + */ + +#ifndef _EXFAT_CORE_H +#define _EXFAT_CORE_H + +#include + +#include "config.h" +#include "api.h" +#include "upcase.h" + +#define get_next_clus(sb, pclu) exfat_ent_get(sb, *(pclu), pclu) +#define get_next_clus_safe(sb, pclu) exfat_ent_get_safe(sb, *(pclu), pclu) + +/* file status */ +/* this prevents + * exfat_fscore_write_inode, exfat_fscore_map_clus, ... with the unlinked inodes + * from corrupting on-disk dentry data. + * + * The fid->dir value of unlinked inode will be DIR_DELETED + * and those functions must check if fid->dir is valid prior to + * the calling of exfat_get_dentry_in_dir() + */ +#define DIR_DELETED 0xFFFF0321 + +#define ES_2_ENTRIES 2 +#define ES_3_ENTRIES 3 +#define ES_ALL_ENTRIES 0 + +typedef struct { + u64 sector; // sector number that contains file_entry + u32 offset; // byte offset in the sector + s32 alloc_flag; // flag in stream entry. 01 for cluster chain, 03 for contig. clusters. + u32 num_entries; + void *__buf; // __buf should be the last member +} ENTRY_SET_CACHE_T; + +/* file system initialization & shutdown functions */ +s32 exfat_fscore_init(void); +s32 exfat_fscore_shutdown(void); + +/* bdev management */ +s32 exfat_fscore_check_bdi_valid(struct super_block *sb); + +/* chain management */ +s32 exfat_chain_cont_cluster(struct super_block *sb, u32 chain, u32 len); + +/* volume management functions */ +s32 exfat_fscore_mount(struct super_block *sb); +s32 exfat_fscore_umount(struct super_block *sb); +s32 exfat_fscore_statfs(struct super_block *sb, VOL_INFO_T *info); +s32 exfat_fscore_sync_fs(struct super_block *sb, s32 do_sync); +s32 exfat_fscore_set_vol_flags(struct super_block *sb, u16 new_flag, s32 always_sync); +u32 exfat_fscore_get_au_stat(struct super_block *sb, s32 mode); + +/* file management functions */ +s32 exfat_fscore_lookup(struct inode *inode, u8 *path, FILE_ID_T *fid); +s32 exfat_fscore_create(struct inode *inode, u8 *path, u8 mode, FILE_ID_T *fid); +s32 exfat_fscore_read_link(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount); +s32 exfat_fscore_write_link(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount); +s32 exfat_fscore_truncate(struct inode *inode, u64 old_size, u64 new_size); +s32 exfat_fscore_rename(struct inode *old_parent_inode, FILE_ID_T *fid, + struct inode *new_parent_inode, struct dentry *new_dentry); +s32 exfat_fscore_remove(struct inode *inode, FILE_ID_T *fid); +s32 exfat_fscore_read_inode(struct inode *inode, DIR_ENTRY_T *info); +s32 exfat_fscore_write_inode(struct inode *inode, DIR_ENTRY_T *info, int sync); +s32 exfat_fscore_map_clus(struct inode *inode, u32 clu_offset, u32 *clu, int dest); +s32 exfat_fscore_unlink(struct inode *inode, FILE_ID_T *fid); + +/* directory management functions */ +s32 exfat_fscore_mkdir(struct inode *inode, u8 *path, FILE_ID_T *fid); +s32 exfat_fscore_readdir(struct inode *inode, DIR_ENTRY_T *dir_ent); +s32 exfat_fscore_rmdir(struct inode *inode, FILE_ID_T *fid); + +/* core.c : core code for common */ +/* dir entry management functions */ +DENTRY_T *exfat_get_dentry_in_dir(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u64 *sector); + +/* name conversion functions */ +void exfat_get_uniname_from_dos_entry(struct super_block *sb, DOS_DENTRY_T *ep, UNI_NAME_T *p_uniname, u8 mode); + +/* file operation functions */ +s32 exfat_walk_fat_chain(struct super_block *sb, CHAIN_T *p_dir, u32 byte_offset, u32 *clu); + +/* exfat/cache.c */ +s32 exfat_meta_cache_init(struct super_block *sb); +s32 exfat_meta_cache_shutdown(struct super_block *sb); +u8 *exfat_fcache_getblk(struct super_block *sb, u64 sec); +s32 exfat_fcache_modify(struct super_block *sb, u64 sec); +s32 exfat_fcache_release_all(struct super_block *sb); +s32 exfat_fcache_flush(struct super_block *sb, u32 sync); + +u8 *exfat_dcache_getblk(struct super_block *sb, u64 sec); +s32 exfat_dcache_modify(struct super_block *sb, u64 sec); +s32 exfat_dcache_lock(struct super_block *sb, u64 sec); +s32 exfat_dcache_unlock(struct super_block *sb, u64 sec); +s32 exfat_dcache_release(struct super_block *sb, u64 sec); +s32 exfat_dcache_release_all(struct super_block *sb); +s32 exfat_dcache_flush(struct super_block *sb, u32 sync); +s32 exfat_dcache_readahead(struct super_block *sb, u64 sec); + + +/* fatent.c */ +s32 exfat_ent_get(struct super_block *sb, u32 loc, u32 *content); +s32 exfat_ent_set(struct super_block *sb, u32 loc, u32 content); +s32 exfat_ent_get_safe(struct super_block *sb, u32 loc, u32 *content); + +/* core_exfat.c : core code for exfat */ + +s32 exfat_load_alloc_bmp(struct super_block *sb); +void exfat_free_alloc_bmp(struct super_block *sb); +ENTRY_SET_CACHE_T *exfat_get_dentry_set_in_dir(struct super_block *sb, + CHAIN_T *p_dir, s32 entry, u32 type, DENTRY_T **file_ep); +void exfat_release_dentry_set(ENTRY_SET_CACHE_T *es); +s32 exfat_update_dir_chksum(struct super_block *sb, CHAIN_T *p_dir, s32 entry); +s32 exfat_update_dir_chksum_with_entry_set(struct super_block *sb, ENTRY_SET_CACHE_T *es); +s32 mount_exfat(struct super_block *sb, pbr_t *p_pbr); + +/* blkdev.c */ +s32 exfat_bdev_open_dev(struct super_block *sb); +s32 exfat_bdev_close_dev(struct super_block *sb); +s32 exfat_bdev_check_bdi_valid(struct super_block *sb); +s32 exfat_bdev_readahead(struct super_block *sb, u64 secno, u64 num_secs); +s32 exfat_bdev_mread(struct super_block *sb, u64 secno, struct buffer_head **bh, u64 num_secs, s32 read); +s32 exfat_bdev_mwrite(struct super_block *sb, u64 secno, struct buffer_head *bh, u64 num_secs, s32 sync); +s32 exfat_bdev_sync_all(struct super_block *sb); + +/* blkdev.c : sector read/write functions */ +s32 exfat_read_sect(struct super_block *sb, u64 sec, struct buffer_head **bh, s32 read); +s32 exfat_write_sect(struct super_block *sb, u64 sec, struct buffer_head *bh, s32 sync); +s32 exfat_write_msect_zero(struct super_block *sb, u64 sec, u64 num_secs); + +/* misc.c */ +u16 exfat_calc_chksum_2byte(void *data, s32 len, u16 chksum, s32 type); + +/* extent.c */ +s32 exfat_extent_cache_init(void); +void exfat_extent_cache_shutdown(void); +void exfat_extent_cache_init_inode(struct inode *inode); +void exfat_extent_cache_inval_inode(struct inode *inode); +s32 exfat_extent_get_clus(struct inode *inode, u32 cluster, u32 *fclus, + u32 *dclus, u32 *last_dclus, s32 allow_eof); + +void exfat_set_sb_dirty(struct super_block *sb); + +#endif /* _EXFAT_CORE_H */ diff --git a/fs/exfat/core_exfat.c b/fs/exfat/core_exfat.c new file mode 100644 index 00000000..28eba2f2 --- /dev/null +++ b/fs/exfat/core_exfat.c @@ -0,0 +1,1485 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * core_exfat.c: exFAT core code + */ + +#include +#include +#include +#include +#include + +#include "exfat.h" +#include "core.h" +#include +#include + +static u8 free_bit[] = { + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,/* 0 ~ 19*/ + 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3,/* 20 ~ 39*/ + 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/* 40 ~ 59*/ + 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,/* 60 ~ 79*/ + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2,/* 80 ~ 99*/ + 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3,/*100 ~ 119*/ + 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/*120 ~ 139*/ + 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5,/*140 ~ 159*/ + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,/*160 ~ 179*/ + 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3,/*180 ~ 199*/ + 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/*200 ~ 219*/ + 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,/*220 ~ 239*/ + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /*240 ~ 254*/ +}; + +static u8 used_bit[] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3,/* 0 ~ 19*/ + 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4,/* 20 ~ 39*/ + 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5,/* 40 ~ 59*/ + 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,/* 60 ~ 79*/ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4,/* 80 ~ 99*/ + 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,/*100 ~ 119*/ + 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4,/*120 ~ 139*/ + 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,/*140 ~ 159*/ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5,/*160 ~ 179*/ + 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5,/*180 ~ 199*/ + 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6,/*200 ~ 219*/ + 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,/*220 ~ 239*/ + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 /*240 ~ 255*/ +}; + +/* + * Directory Entry Management Functions + */ +static u32 exfat_get_entry_type(DENTRY_T *p_entry) +{ + FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; + + if (ep->type == EXFAT_UNUSED) + return TYPE_UNUSED; + if (ep->type < 0x80) + return TYPE_DELETED; + if (ep->type == 0x80) + return TYPE_INVALID; + if (ep->type < 0xA0) { + if (ep->type == 0x81) + return TYPE_BITMAP; + if (ep->type == 0x82) + return TYPE_UPCASE; + if (ep->type == 0x83) + return TYPE_VOLUME; + if (ep->type == 0x85) { + if (le16_to_cpu(ep->attr) & ATTR_SUBDIR) + return TYPE_DIR; + return TYPE_FILE; + } + return TYPE_CRITICAL_PRI; + } + if (ep->type < 0xC0) { + if (ep->type == 0xA0) + return TYPE_GUID; + if (ep->type == 0xA1) + return TYPE_PADDING; + if (ep->type == 0xA2) + return TYPE_ACLTAB; + return TYPE_BENIGN_PRI; + } + if (ep->type < 0xE0) { + if (ep->type == 0xC0) + return TYPE_STREAM; + if (ep->type == 0xC1) + return TYPE_EXTEND; + if (ep->type == 0xC2) + return TYPE_ACL; + return TYPE_CRITICAL_SEC; + } + return TYPE_BENIGN_SEC; +} + +static void exfat_set_entry_type(DENTRY_T *p_entry, u32 type) +{ + FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; + + if (type == TYPE_UNUSED) { + ep->type = 0x0; + } else if (type == TYPE_DELETED) { + ep->type &= ~0x80; + } else if (type == TYPE_STREAM) { + ep->type = 0xC0; + } else if (type == TYPE_EXTEND) { + ep->type = 0xC1; + } else if (type == TYPE_BITMAP) { + ep->type = 0x81; + } else if (type == TYPE_UPCASE) { + ep->type = 0x82; + } else if (type == TYPE_VOLUME) { + ep->type = 0x83; + } else if (type == TYPE_DIR) { + ep->type = 0x85; + ep->attr = cpu_to_le16(ATTR_SUBDIR); + } else if (type == TYPE_FILE) { + ep->type = 0x85; + ep->attr = cpu_to_le16(ATTR_ARCHIVE); + } else if (type == TYPE_SYMLINK) { + ep->type = 0x85; + ep->attr = cpu_to_le16(ATTR_ARCHIVE | ATTR_SYMLINK); + } +} + +static u32 exfat_get_entry_attr(DENTRY_T *p_entry) +{ + FILE_DENTRY_T *ep = (FILE_DENTRY_T *)p_entry; + + return (u32)le16_to_cpu(ep->attr); +} + +static void exfat_set_entry_attr(DENTRY_T *p_entry, u32 attr) +{ + FILE_DENTRY_T *ep = (FILE_DENTRY_T *)p_entry; + + ep->attr = cpu_to_le16((u16) attr); +} + +static u8 exfat_get_entry_flag(DENTRY_T *p_entry) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *)p_entry; + + return ep->flags; +} + +static void exfat_set_entry_flag(DENTRY_T *p_entry, u8 flags) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *)p_entry; + + ep->flags = flags; +} + +static u32 exfat_get_entry_clu0(DENTRY_T *p_entry) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *)p_entry; + + return (u32)le32_to_cpu(ep->start_clu); +} + +static void exfat_set_entry_clu0(DENTRY_T *p_entry, u32 start_clu) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *)p_entry; + + ep->start_clu = cpu_to_le32(start_clu); +} + +static u64 exfat_get_entry_size(DENTRY_T *p_entry) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *)p_entry; + + return le64_to_cpu(ep->valid_size); +} + +static void exfat_set_entry_size(DENTRY_T *p_entry, u64 size) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *)p_entry; + + ep->valid_size = cpu_to_le64(size); + ep->size = cpu_to_le64(size); +} + +static void exfat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode) +{ + u16 t = 0x00, d = 0x21; + FILE_DENTRY_T *ep = (FILE_DENTRY_T *)p_entry; + + switch (mode) { + case TM_CREATE: + t = le16_to_cpu(ep->create_time); + d = le16_to_cpu(ep->create_date); + break; + case TM_MODIFY: + t = le16_to_cpu(ep->modify_time); + d = le16_to_cpu(ep->modify_date); + break; + case TM_ACCESS: + t = le16_to_cpu(ep->access_time); + d = le16_to_cpu(ep->access_date); + break; + } + + tp->sec = (t & 0x001F) << 1; + tp->min = (t >> 5) & 0x003F; + tp->hour = (t >> 11); + tp->day = (d & 0x001F); + tp->mon = (d >> 5) & 0x000F; + tp->year = (d >> 9); +} + +static void exfat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode) +{ + u16 t, d; + FILE_DENTRY_T *ep = (FILE_DENTRY_T *)p_entry; + + t = (tp->hour << 11) | (tp->min << 5) | (tp->sec >> 1); + d = (tp->year << 9) | (tp->mon << 5) | tp->day; + + switch (mode) { + case TM_CREATE: + ep->create_time = cpu_to_le16(t); + ep->create_date = cpu_to_le16(d); + break; + case TM_MODIFY: + ep->modify_time = cpu_to_le16(t); + ep->modify_date = cpu_to_le16(d); + break; + case TM_ACCESS: + ep->access_time = cpu_to_le16(t); + ep->access_date = cpu_to_le16(d); + break; + } +} + + +static void __init_file_entry(struct super_block *sb, FILE_DENTRY_T *ep, u32 type) +{ + TIMESTAMP_T tm, *tp; + + exfat_set_entry_type((DENTRY_T *) ep, type); + + tp = exfat_tm_now(EXFAT_SB(sb), &tm); + exfat_set_entry_time((DENTRY_T *) ep, tp, TM_CREATE); + exfat_set_entry_time((DENTRY_T *) ep, tp, TM_MODIFY); + exfat_set_entry_time((DENTRY_T *) ep, tp, TM_ACCESS); + ep->create_time_ms = 0; + ep->modify_time_ms = 0; + ep->access_time_ms = 0; +} + +static void __init_strm_entry(STRM_DENTRY_T *ep, u8 flags, u32 start_clu, u64 size) +{ + exfat_set_entry_type((DENTRY_T *) ep, TYPE_STREAM); + ep->flags = flags; + ep->start_clu = cpu_to_le32(start_clu); + ep->valid_size = cpu_to_le64(size); + ep->size = cpu_to_le64(size); +} + +static void __init_name_entry(NAME_DENTRY_T *ep, u16 *uniname) +{ + s32 i; + + exfat_set_entry_type((DENTRY_T *) ep, TYPE_EXTEND); + ep->flags = 0x0; + + for (i = 0; i < 15; i++) { + ep->unicode_0_14[i] = cpu_to_le16(*uniname); + if (*uniname == 0x0) + break; + uniname++; + } +} + +static s32 exfat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 type, u32 start_clu, u64 size) +{ + u64 sector; + u8 flags; + FILE_DENTRY_T *file_ep; + STRM_DENTRY_T *strm_ep; + + flags = (type == TYPE_FILE) ? 0x01 : 0x03; + + /* we cannot use exfat_get_dentry_set_in_dir here because file ep is not initialized yet */ + file_ep = (FILE_DENTRY_T *)exfat_get_dentry_in_dir(sb, p_dir, entry, §or); + if (!file_ep) + return -EIO; + + strm_ep = (STRM_DENTRY_T *)exfat_get_dentry_in_dir(sb, p_dir, entry+1, §or); + if (!strm_ep) + return -EIO; + + __init_file_entry(sb, file_ep, type); + if (exfat_dcache_modify(sb, sector)) + return -EIO; + + __init_strm_entry(strm_ep, flags, start_clu, size); + if (exfat_dcache_modify(sb, sector)) + return -EIO; + + return 0; +} + +s32 exfat_update_dir_chksum(struct super_block *sb, CHAIN_T *p_dir, s32 entry) +{ + s32 ret = -EIO; + s32 i, num_entries; + u64 sector; + u16 chksum; + FILE_DENTRY_T *file_ep; + DENTRY_T *ep; + + file_ep = (FILE_DENTRY_T *)exfat_get_dentry_in_dir(sb, p_dir, entry, §or); + if (!file_ep) + return -EIO; + + exfat_dcache_lock(sb, sector); + + num_entries = (s32) file_ep->num_ext + 1; + chksum = exfat_calc_chksum_2byte((void *) file_ep, DENTRY_SIZE, 0, CS_DIR_ENTRY); + + for (i = 1; i < num_entries; i++) { + ep = exfat_get_dentry_in_dir(sb, p_dir, entry+i, NULL); + if (!ep) + goto out_unlock; + + chksum = exfat_calc_chksum_2byte((void *) ep, DENTRY_SIZE, chksum, CS_DEFAULT); + } + + file_ep->checksum = cpu_to_le16(chksum); + ret = exfat_dcache_modify(sb, sector); +out_unlock: + exfat_dcache_unlock(sb, sector); + return ret; + +} + + +static s32 exfat_init_ext_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 num_entries, + UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname) +{ + s32 i; + u64 sector; + u16 *uniname = p_uniname->name; + FILE_DENTRY_T *file_ep; + STRM_DENTRY_T *strm_ep; + NAME_DENTRY_T *name_ep; + + file_ep = (FILE_DENTRY_T *)exfat_get_dentry_in_dir(sb, p_dir, entry, §or); + if (!file_ep) + return -EIO; + + file_ep->num_ext = (u8)(num_entries - 1); + exfat_dcache_modify(sb, sector); + + strm_ep = (STRM_DENTRY_T *)exfat_get_dentry_in_dir(sb, p_dir, entry+1, §or); + if (!strm_ep) + return -EIO; + + strm_ep->name_len = p_uniname->name_len; + strm_ep->name_hash = cpu_to_le16(p_uniname->name_hash); + exfat_dcache_modify(sb, sector); + + for (i = 2; i < num_entries; i++) { + name_ep = (NAME_DENTRY_T *)exfat_get_dentry_in_dir(sb, p_dir, entry+i, §or); + if (!name_ep) + return -EIO; + + __init_name_entry(name_ep, uniname); + exfat_dcache_modify(sb, sector); + uniname += 15; + } + + exfat_update_dir_chksum(sb, p_dir, entry); + + return 0; +} + + +static s32 exfat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 order, s32 num_entries) +{ + s32 i; + u64 sector; + DENTRY_T *ep; + + for (i = order; i < num_entries; i++) { + ep = exfat_get_dentry_in_dir(sb, p_dir, entry+i, §or); + if (!ep) + return -EIO; + + exfat_set_entry_type(ep, TYPE_DELETED); + if (exfat_dcache_modify(sb, sector)) + return -EIO; + } + + return 0; +} + +static s32 __write_partial_entries_in_entry_set(struct super_block *sb, + ENTRY_SET_CACHE_T *es, u64 sec, u32 off, u32 count) +{ + s32 num_entries; + u32 buf_off = (off - es->offset); + u32 remaining_byte_in_sector, copy_entries; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + u32 clu; + u8 *buf, *esbuf = (u8 *)&(es->__buf); + + MMSG("%s: es %p sec %llu off %u cnt %d\n", __func__, es, sec, off, count); + num_entries = count; + + while (num_entries) { + /* write per sector base */ + remaining_byte_in_sector = (1 << sb->s_blocksize_bits) - off; + copy_entries = min((s32)(remaining_byte_in_sector >> DENTRY_SIZE_BITS), num_entries); + buf = exfat_dcache_getblk(sb, sec); + if (!buf) + goto err_out; + MMSG("es->buf %p buf_off %u\n", esbuf, buf_off); + MMSG("copying %d entries from %p to sector %llu\n", copy_entries, (esbuf + buf_off), sec); + memcpy(buf + off, esbuf + buf_off, copy_entries << DENTRY_SIZE_BITS); + exfat_dcache_modify(sb, sec); + num_entries -= copy_entries; + + if (num_entries) { + // get next sector + if (IS_LAST_SECT_IN_CLUS(fsi, sec)) { + clu = SECT_TO_CLUS(fsi, sec); + if (es->alloc_flag == 0x03) + clu++; + else if (get_next_clus_safe(sb, &clu)) + goto err_out; + sec = CLUS_TO_SECT(fsi, clu); + } else { + sec++; + } + off = 0; + buf_off += copy_entries << DENTRY_SIZE_BITS; + } + } + + return 0; +err_out: + return -EIO; +} + +/* write back all entries in entry set */ +static s32 __write_whole_entry_set(struct super_block *sb, ENTRY_SET_CACHE_T *es) +{ + return __write_partial_entries_in_entry_set(sb, es, es->sector, es->offset, es->num_entries); +} + +s32 exfat_update_dir_chksum_with_entry_set(struct super_block *sb, ENTRY_SET_CACHE_T *es) +{ + DENTRY_T *ep; + u16 chksum = 0; + s32 chksum_type = CS_DIR_ENTRY, i; + + ep = (DENTRY_T *)&(es->__buf); + for (i = 0; i < es->num_entries; i++) { + MMSG("%s %p\n", __func__, ep); + chksum = exfat_calc_chksum_2byte((void *) ep, DENTRY_SIZE, chksum, chksum_type); + ep++; + chksum_type = CS_DEFAULT; + } + + ep = (DENTRY_T *)&(es->__buf); + ((FILE_DENTRY_T *)ep)->checksum = cpu_to_le16(chksum); + return __write_whole_entry_set(sb, es); +} + +/* returns a set of dentries for a file or dir. + * Note that this is a copy (dump) of dentries so that user should call write_entry_set() + * to apply changes made in this entry set to the real device. + * in: + * sb+p_dir+entry: indicates a file/dir + * type: specifies how many dentries should be included. + * out: + * file_ep: will point the first dentry(= file dentry) on success + * return: + * pointer of entry set on success, + * NULL on failure. + */ + +#define ES_MODE_STARTED 0 +#define ES_MODE_GET_FILE_ENTRY 1 +#define ES_MODE_GET_STRM_ENTRY 2 +#define ES_MODE_GET_NAME_ENTRY 3 +#define ES_MODE_GET_CRITICAL_SEC_ENTRY 4 +ENTRY_SET_CACHE_T *exfat_get_dentry_set_in_dir(struct super_block *sb, + CHAIN_T *p_dir, s32 entry, u32 type, DENTRY_T **file_ep) +{ + s32 ret; + u32 off, byte_offset, clu = 0; + u32 entry_type; + u64 sec; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + ENTRY_SET_CACHE_T *es = NULL; + DENTRY_T *ep, *pos; + u8 *buf; + u8 num_entries; + s32 mode = ES_MODE_STARTED; + + /* FIXME : is available in error case? */ + if (p_dir->dir == DIR_DELETED) { + EMSG("%s : access to deleted dentry\n", __func__); + BUG_ON(!fsi->prev_eio); + return NULL; + } + + MMSG("p_dir dir %u flags %x size %d\n", p_dir->dir, p_dir->flags, p_dir->size); + MMSG("entry %d type %d\n", entry, type); + + byte_offset = entry << DENTRY_SIZE_BITS; + ret = exfat_walk_fat_chain(sb, p_dir, byte_offset, &clu); + if (ret) + return NULL; + + /* byte offset in cluster */ + byte_offset &= fsi->cluster_size - 1; + + /* byte offset in sector */ + off = byte_offset & (u32)(sb->s_blocksize - 1); + + /* sector offset in cluster */ + sec = byte_offset >> (sb->s_blocksize_bits); + sec += CLUS_TO_SECT(fsi, clu); + + buf = exfat_dcache_getblk(sb, sec); + if (!buf) + goto err_out; + + ep = (DENTRY_T *)(buf + off); + entry_type = exfat_get_entry_type(ep); + + if ((entry_type != TYPE_FILE) + && (entry_type != TYPE_DIR)) + goto err_out; + + if (type == ES_ALL_ENTRIES) + num_entries = ((FILE_DENTRY_T *)ep)->num_ext+1; + else + num_entries = type; + + MMSG("trying to malloc %lx bytes for %d entries\n", + (unsigned long)(offsetof(ENTRY_SET_CACHE_T, __buf) + (num_entries) * sizeof(DENTRY_T)), num_entries); + es = kmalloc((offsetof(ENTRY_SET_CACHE_T, __buf) + (num_entries) * sizeof(DENTRY_T)), GFP_KERNEL); + if (!es) { + EMSG("%s: failed to alloc entryset\n", __func__); + goto err_out; + } + + es->num_entries = num_entries; + es->sector = sec; + es->offset = off; + es->alloc_flag = p_dir->flags; + + pos = (DENTRY_T *) &(es->__buf); + + while (num_entries) { + // instead of copying whole sector, we will check every entry. + // this will provide minimum stablity and consistency. + entry_type = exfat_get_entry_type(ep); + + if ((entry_type == TYPE_UNUSED) || (entry_type == TYPE_DELETED)) + goto err_out; + + switch (mode) { + case ES_MODE_STARTED: + if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR)) + mode = ES_MODE_GET_FILE_ENTRY; + else + goto err_out; + break; + case ES_MODE_GET_FILE_ENTRY: + if (entry_type == TYPE_STREAM) + mode = ES_MODE_GET_STRM_ENTRY; + else + goto err_out; + break; + case ES_MODE_GET_STRM_ENTRY: + if (entry_type == TYPE_EXTEND) + mode = ES_MODE_GET_NAME_ENTRY; + else + goto err_out; + break; + case ES_MODE_GET_NAME_ENTRY: + if (entry_type == TYPE_EXTEND) + break; + else if (entry_type == TYPE_STREAM) + goto err_out; + else if (entry_type & TYPE_CRITICAL_SEC) + mode = ES_MODE_GET_CRITICAL_SEC_ENTRY; + else + goto err_out; + break; + case ES_MODE_GET_CRITICAL_SEC_ENTRY: + if ((entry_type == TYPE_EXTEND) || (entry_type == TYPE_STREAM)) + goto err_out; + else if ((entry_type & TYPE_CRITICAL_SEC) != TYPE_CRITICAL_SEC) + goto err_out; + break; + } + + /* copy dentry */ + memcpy(pos, ep, sizeof(DENTRY_T)); + + if (--num_entries == 0) + break; + + if (((off + DENTRY_SIZE) & (u32)(sb->s_blocksize - 1)) < + (off & (u32)(sb->s_blocksize - 1))) { + // get the next sector + if (IS_LAST_SECT_IN_CLUS(fsi, sec)) { + if (es->alloc_flag == 0x03) + clu++; + else if (get_next_clus_safe(sb, &clu)) + goto err_out; + sec = CLUS_TO_SECT(fsi, clu); + } else { + sec++; + } + buf = exfat_dcache_getblk(sb, sec); + if (!buf) + goto err_out; + off = 0; + ep = (DENTRY_T *)(buf); + } else { + ep++; + off += DENTRY_SIZE; + } + pos++; + } + + if (file_ep) + *file_ep = (DENTRY_T *)&(es->__buf); + + MMSG("es sec %llu offset %u flags %d, num_entries %u buf ptr %p\n", + es->sector, es->offset, es->alloc_flag, es->num_entries, &(es->__buf)); + return es; + +err_out: + /* kfree(NULL) is safe */ + kfree(es); + es = NULL; + return NULL; +} + +void exfat_release_dentry_set(ENTRY_SET_CACHE_T *es) +{ + /* kfree(NULL) is safe */ + kfree(es); + es = NULL; +} + +static s32 __extract_uni_name_from_name_entry(NAME_DENTRY_T *ep, u16 *uniname, s32 order) +{ + s32 i, len = 0; + + for (i = 0; i < 15; i++) { + /* FIXME : unaligned? */ + *uniname = le16_to_cpu(ep->unicode_0_14[i]); + if (*uniname == 0x0) + return len; + uniname++; + len++; + } + + *uniname = 0x0; + return len; + +} + +#define DIRENT_STEP_FILE (0) +#define DIRENT_STEP_STRM (1) +#define DIRENT_STEP_NAME (2) +#define DIRENT_STEP_SECD (3) + +/* return values of exfat_find_dir_entry() + * >= 0 : return dir entiry position with the name in dir + * -EEXIST : (root dir, ".") it is the root dir itself + * -ENOENT : entry with the name does not exist + * -EIO : I/O error + */ +static s32 exfat_find_dir_entry(struct super_block *sb, FILE_ID_T *fid, + CHAIN_T *p_dir, UNI_NAME_T *p_uniname, s32 num_entries, DOS_NAME_T *unused, u32 type) +{ + s32 i, rewind = 0, dentry = 0, end_eidx = 0, num_ext = 0, len; + s32 order, step, name_len = 0; + s32 dentries_per_clu, num_empty = 0; + u32 entry_type; + u16 entry_uniname[16], *uniname = NULL, unichar; + CHAIN_T clu; + DENTRY_T *ep; + HINT_T *hint_stat = &fid->hint_stat; + HINT_FEMP_T candi_empty; + FILE_DENTRY_T *file_ep; + STRM_DENTRY_T *strm_ep; + NAME_DENTRY_T *name_ep; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + /* + * REMARK: + * DOT and DOTDOT are handled by VFS layer + */ + + if (IS_CLUS_FREE(p_dir->dir)) + return -EIO; + + dentries_per_clu = fsi->dentries_per_clu; + + clu.dir = p_dir->dir; + clu.size = p_dir->size; + clu.flags = p_dir->flags; + + if (hint_stat->eidx) { + clu.dir = hint_stat->clu; + dentry = hint_stat->eidx; + end_eidx = dentry; + } + + candi_empty.eidx = -1; +rewind: + order = 0; + step = DIRENT_STEP_FILE; + while (!IS_CLUS_EOF(clu.dir)) { + i = dentry & (dentries_per_clu - 1); + for (; i < dentries_per_clu; i++, dentry++) { + if (rewind && (dentry == end_eidx)) + goto not_found; + + ep = exfat_get_dentry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -EIO; + + entry_type = exfat_get_entry_type(ep); + + if ((entry_type == TYPE_UNUSED) || (entry_type == TYPE_DELETED)) { + step = DIRENT_STEP_FILE; + + num_empty++; + if (candi_empty.eidx == -1) { + if (num_empty == 1) { + candi_empty.cur.dir = clu.dir; + candi_empty.cur.size = clu.size; + candi_empty.cur.flags = clu.flags; + } + + if (num_empty >= num_entries) { + candi_empty.eidx = dentry - (num_empty - 1); + ASSERT(0 <= candi_empty.eidx); + candi_empty.count = num_empty; + + if ((fid->hint_femp.eidx == -1) || + (candi_empty.eidx <= fid->hint_femp.eidx)) { + memcpy(&fid->hint_femp, + &candi_empty, + sizeof(HINT_FEMP_T)); + } + } + } + + if (entry_type == TYPE_UNUSED) + goto not_found; + continue; + } + + num_empty = 0; + candi_empty.eidx = -1; + + if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR)) { + step = DIRENT_STEP_FILE; + if ((type == TYPE_ALL) || (type == entry_type)) { + file_ep = (FILE_DENTRY_T *) ep; + num_ext = file_ep->num_ext; + step = DIRENT_STEP_STRM; + } + continue; + } + + if (entry_type == TYPE_STREAM) { + if (step != DIRENT_STEP_STRM) { + step = DIRENT_STEP_FILE; + continue; + } + step = DIRENT_STEP_FILE; + strm_ep = (STRM_DENTRY_T *) ep; + if ((p_uniname->name_hash == le16_to_cpu(strm_ep->name_hash)) && + (p_uniname->name_len == strm_ep->name_len)) { + step = DIRENT_STEP_NAME; + order = 1; + name_len = 0; + } + continue; + } + + if (entry_type == TYPE_EXTEND) { + if (step != DIRENT_STEP_NAME) { + step = DIRENT_STEP_FILE; + continue; + } + name_ep = (NAME_DENTRY_T *) ep; + + if ((++order) == 2) + uniname = p_uniname->name; + else + uniname += 15; + + len = __extract_uni_name_from_name_entry(name_ep, entry_uniname, order); + name_len += len; + + unichar = *(uniname+len); + *(uniname+len) = 0x0; + + if (exfat_nls_cmp_uniname(sb, uniname, entry_uniname)) { + step = DIRENT_STEP_FILE; + } else if (name_len == p_uniname->name_len) { + if (order == num_ext) { + //fid->hint_femp.eidx = -1; + goto found; + } + step = DIRENT_STEP_SECD; + } + + *(uniname+len) = unichar; + continue; + } + + if (entry_type & (TYPE_CRITICAL_SEC | TYPE_BENIGN_SEC)) { + if (step == DIRENT_STEP_SECD) { + if (++order == num_ext) + goto found; + continue; + } + } + step = DIRENT_STEP_FILE; + } + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUS_EOF; + } else { + if (get_next_clus_safe(sb, &clu.dir)) + return -EIO; + } + } + +not_found: + /* we started at not 0 index,so we should try to find target + * from 0 index to the index we started at. + */ + if (!rewind && end_eidx) { + rewind = 1; + dentry = 0; + clu.dir = p_dir->dir; + /* reset empty hint */ + num_empty = 0; + candi_empty.eidx = -1; + goto rewind; + } + + /* initialized hint_stat */ + hint_stat->clu = p_dir->dir; + hint_stat->eidx = 0; + return -ENOENT; + +found: + /* next dentry we'll find is out of this cluster */ + if (!((dentry + 1) & (dentries_per_clu-1))) { + int ret = 0; + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUS_EOF; + } else { + ret = get_next_clus_safe(sb, &clu.dir); + } + + if (ret || IS_CLUS_EOF(clu.dir)) { + /* just initialized hint_stat */ + hint_stat->clu = p_dir->dir; + hint_stat->eidx = 0; + return (dentry - num_ext); + } + } + + hint_stat->clu = clu.dir; + hint_stat->eidx = dentry + 1; + return (dentry - num_ext); +} + +/* returns -EIO on error */ +static s32 exfat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, s32 entry, DENTRY_T *p_entry) +{ + s32 i, count = 0; + u32 type; + FILE_DENTRY_T *file_ep = (FILE_DENTRY_T *) p_entry; + DENTRY_T *ext_ep; + + for (i = 0, entry++; i < file_ep->num_ext; i++, entry++) { + ext_ep = exfat_get_dentry_in_dir(sb, p_dir, entry, NULL); + if (!ext_ep) + return -EIO; + + type = exfat_get_entry_type(ext_ep); + if ((type == TYPE_EXTEND) || (type == TYPE_STREAM)) + count++; + else + return count; + } + + return count; +} + + +/* + * Name Conversion Functions + */ +static void exfat_get_uniname_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u16 *uniname) +{ + s32 i; + DENTRY_T *ep; + ENTRY_SET_CACHE_T *es; + + es = exfat_get_dentry_set_in_dir(sb, p_dir, entry, ES_ALL_ENTRIES, &ep); + if (!es) + return; + + if (es->num_entries < 3) + goto out; + + ep += 2; + + /* + * First entry : file entry + * Second entry : stream-extension entry + * Third entry : first file-name entry + * So, the index of first file-name dentry should start from 2. + */ + for (i = 2; i < es->num_entries; i++, ep++) { + if (exfat_get_entry_type(ep) != TYPE_EXTEND) + goto out; + + __extract_uni_name_from_name_entry((NAME_DENTRY_T *)ep, uniname, i); + uniname += 15; + } + +out: + exfat_release_dentry_set(es); +} + +static s32 exfat_calc_num_entries(UNI_NAME_T *p_uniname) +{ + s32 len; + + len = p_uniname->name_len; + if (len == 0) + return 0; + + /* 1 file entry + 1 stream entry + name entries */ + return((len-1) / 15 + 3); + +} + +static s32 exfat_check_max_dentries(FILE_ID_T *fid) +{ + if ((fid->size >> DENTRY_SIZE_BITS) >= MAX_EXFAT_DENTRIES) { + /* exFAT spec allows a dir to grow upto 8388608(256MB) dentries */ + return -ENOSPC; + } + return 0; +} + +/* + * Allocation Bitmap Management Functions + */ +s32 exfat_load_alloc_bmp(struct super_block *sb) +{ + s32 ret; + u32 i, j, map_size, type, need_map_size; + u64 sector; + CHAIN_T clu; + BMAP_DENTRY_T *ep; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + clu.dir = fsi->root_dir; + clu.flags = 0x01; + + while (!IS_CLUS_EOF(clu.dir)) { + for (i = 0; i < fsi->dentries_per_clu; i++) { + ep = (BMAP_DENTRY_T *) exfat_get_dentry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -EIO; + + type = exfat_get_entry_type((DENTRY_T *) ep); + + if (type == TYPE_UNUSED) + break; + if (type != TYPE_BITMAP) + continue; + + if (ep->flags == 0x0) { + fsi->map_clu = le32_to_cpu(ep->start_clu); + map_size = (u32) le64_to_cpu(ep->size); + + need_map_size = (((fsi->num_clusters - CLUS_BASE) - 1) >> 3) + 1; + if (need_map_size != map_size) { + exfat_log_msg(sb, KERN_ERR, + "bogus allocation bitmap size(need : %u, cur : %u)", + need_map_size, map_size); + /* Only allowed when bogus allocation bitmap size is large */ + if (need_map_size > map_size) + return -EIO; + } + fsi->map_sectors = ((need_map_size - 1) >> (sb->s_blocksize_bits)) + 1; + fsi->vol_amap = + kmalloc((sizeof(struct buffer_head *) * fsi->map_sectors), GFP_KERNEL); + if (!fsi->vol_amap) + return -ENOMEM; + + sector = CLUS_TO_SECT(fsi, fsi->map_clu); + + for (j = 0; j < fsi->map_sectors; j++) { + fsi->vol_amap[j] = NULL; + ret = exfat_read_sect(sb, sector+j, &(fsi->vol_amap[j]), 1); + if (ret) { + /* release all buffers and free vol_amap */ + i = 0; + while (i < j) + brelse(fsi->vol_amap[i++]); + + /* kfree(NULL) is safe */ + kfree(fsi->vol_amap); + fsi->vol_amap = NULL; + return ret; + } + } + + fsi->pbr_bh = NULL; + return 0; + } + } + + if (get_next_clus_safe(sb, &clu.dir)) + return -EIO; + } + + return -EINVAL; +} + +void exfat_free_alloc_bmp(struct super_block *sb) +{ + s32 i; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + brelse(fsi->pbr_bh); + + for (i = 0; i < fsi->map_sectors; i++) + __brelse(fsi->vol_amap[i]); + + /* kfree(NULL) is safe */ + kfree(fsi->vol_amap); + fsi->vol_amap = NULL; +} + +/* WARN : + * If the value of "clu" is 0, it means cluster 2 which is + * the first cluster of cluster heap. + */ +static s32 set_alloc_bitmap(struct super_block *sb, u32 clu) +{ + s32 i, b; + u64 sector; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + i = clu >> (sb->s_blocksize_bits + 3); + b = clu & (u32)((sb->s_blocksize << 3) - 1); + + sector = CLUS_TO_SECT(fsi, fsi->map_clu) + i; + bitmap_set((unsigned long *)(fsi->vol_amap[i]->b_data), b, 1); + + return exfat_write_sect(sb, sector, fsi->vol_amap[i], 0); +} + +/* WARN : + * If the value of "clu" is 0, it means cluster 2 which is + * the first cluster of cluster heap. + */ +static s32 clr_alloc_bitmap(struct super_block *sb, u32 clu) +{ + s32 ret; + s32 i, b; + u64 sector; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_mount_options *opts = &sbi->options; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + i = clu >> (sb->s_blocksize_bits + 3); + b = clu & (u32)((sb->s_blocksize << 3) - 1); + + sector = CLUS_TO_SECT(fsi, fsi->map_clu) + i; + + bitmap_clear((unsigned long *)(fsi->vol_amap[i]->b_data), b, 1); + + ret = exfat_write_sect(sb, sector, fsi->vol_amap[i], 0); + + if (opts->discard) { + s32 ret_discard; + + ret_discard = sb_issue_discard(sb, CLUS_TO_SECT(fsi, clu+2), + (1 << fsi->sect_per_clus_bits), GFP_NOFS, 0); + + if (ret_discard == -EOPNOTSUPP) { + exfat_msg(sb, KERN_ERR, + "discard not supported by device, disabling"); + opts->discard = 0; + } + } + + return ret; +} + +/* WARN : + * If the value of "clu" is 0, it means cluster 2 which is + * the first cluster of cluster heap. + */ +static u32 test_alloc_bitmap(struct super_block *sb, u32 clu) +{ + u32 i, map_i, map_b; + u32 clu_base, clu_free; + u8 k, clu_mask; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + clu_base = (clu & ~(0x7)) + 2; + clu_mask = (1 << (clu - clu_base + 2)) - 1; + + map_i = clu >> (sb->s_blocksize_bits + 3); + map_b = (clu >> 3) & (u32)(sb->s_blocksize - 1); + + for (i = 2; i < fsi->num_clusters; i += 8) { + k = *(((u8 *) fsi->vol_amap[map_i]->b_data) + map_b); + if (clu_mask > 0) { + k |= clu_mask; + clu_mask = 0; + } + if (k < 0xFF) { + clu_free = clu_base + free_bit[k]; + if (clu_free < fsi->num_clusters) + return clu_free; + } + clu_base += 8; + + if (((++map_b) >= (u32)sb->s_blocksize) || + (clu_base >= fsi->num_clusters)) { + if ((++map_i) >= fsi->map_sectors) { + clu_base = 2; + map_i = 0; + } + map_b = 0; + } + } + + return CLUS_EOF; +} + +s32 exfat_chain_cont_cluster(struct super_block *sb, u32 chain, u32 len) +{ + if (!len) + return 0; + + while (len > 1) { + if (exfat_ent_set(sb, chain, chain+1)) + return -EIO; + chain++; + len--; + } + + if (exfat_ent_set(sb, chain, CLUS_EOF)) + return -EIO; + return 0; +} + +static s32 exfat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, s32 do_relse) +{ + s32 ret = -EIO; + u32 num_clusters = 0; + u32 clu; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + s32 i; + u64 sector; + + /* invalid cluster number */ + if (IS_CLUS_FREE(p_chain->dir) || IS_CLUS_EOF(p_chain->dir)) + return 0; + + /* no cluster to truncate */ + if (p_chain->size == 0) { + DMSG("%s: cluster(%u) truncation is not required.", + __func__, p_chain->dir); + return 0; + } + + /* check cluster validation */ + if ((p_chain->dir < 2) && (p_chain->dir >= fsi->num_clusters)) { + EMSG("%s: invalid start cluster (%u)\n", __func__, p_chain->dir); + exfat_debug_bug_on(1); + return -EIO; + } + + exfat_set_sb_dirty(sb); + clu = p_chain->dir; + + if (p_chain->flags == 0x03) { + do { + if (do_relse) { + sector = CLUS_TO_SECT(fsi, clu); + for (i = 0; i < fsi->sect_per_clus; i++) { + if (exfat_dcache_release(sb, sector+i) == -EIO) + goto out; + } + } + + if (clr_alloc_bitmap(sb, clu-2)) + goto out; + clu++; + + num_clusters++; + } while (num_clusters < p_chain->size); + } else { + do { + if (do_relse) { + sector = CLUS_TO_SECT(fsi, clu); + for (i = 0; i < fsi->sect_per_clus; i++) { + if (exfat_dcache_release(sb, sector+i) == -EIO) + goto out; + } + } + + if (clr_alloc_bitmap(sb, (clu - CLUS_BASE))) + goto out; + + if (get_next_clus_safe(sb, &clu)) + goto out; + + num_clusters++; + } while (!IS_CLUS_EOF(clu)); + } + + /* success */ + ret = 0; +out: + + fsi->used_clusters -= num_clusters; + return ret; +} + +static s32 exfat_alloc_cluster(struct super_block *sb, u32 num_alloc, CHAIN_T *p_chain, s32 dest) +{ + s32 ret = -ENOSPC; + u32 num_clusters = 0, total_cnt; + u32 hint_clu, new_clu, last_clu = CLUS_EOF; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + total_cnt = fsi->num_clusters - CLUS_BASE; + + if (unlikely(total_cnt < fsi->used_clusters)) { + exfat_fs_error_ratelimit(sb, + "%s: invalid used clusters(t:%u,u:%u)\n", + __func__, total_cnt, fsi->used_clusters); + return -EIO; + } + + if (num_alloc > total_cnt - fsi->used_clusters) + return -ENOSPC; + + hint_clu = p_chain->dir; + /* find new cluster */ + if (IS_CLUS_EOF(hint_clu)) { + if (fsi->clu_srch_ptr < CLUS_BASE) { + EMSG("%s: fsi->clu_srch_ptr is invalid (%u)\n", + __func__, fsi->clu_srch_ptr); + ASSERT(0); + fsi->clu_srch_ptr = CLUS_BASE; + } + + hint_clu = test_alloc_bitmap(sb, fsi->clu_srch_ptr - CLUS_BASE); + if (IS_CLUS_EOF(hint_clu)) + return -ENOSPC; + } + + /* check cluster validation */ + if ((hint_clu < CLUS_BASE) && (hint_clu >= fsi->num_clusters)) { + EMSG("%s: hint_cluster is invalid (%u)\n", __func__, hint_clu); + ASSERT(0); + hint_clu = CLUS_BASE; + if (p_chain->flags == 0x03) { + if (exfat_chain_cont_cluster(sb, p_chain->dir, num_clusters)) + return -EIO; + p_chain->flags = 0x01; + } + } + + exfat_set_sb_dirty(sb); + + p_chain->dir = CLUS_EOF; + + while ((new_clu = test_alloc_bitmap(sb, hint_clu - CLUS_BASE)) != CLUS_EOF) { + if ((new_clu != hint_clu) && (p_chain->flags == 0x03)) { + if (exfat_chain_cont_cluster(sb, p_chain->dir, num_clusters)) { + ret = -EIO; + goto error; + } + p_chain->flags = 0x01; + } + + /* update allocation bitmap */ + if (set_alloc_bitmap(sb, new_clu - CLUS_BASE)) { + ret = -EIO; + goto error; + } + + num_clusters++; + + /* update FAT table */ + if (p_chain->flags == 0x01) { + if (exfat_ent_set(sb, new_clu, CLUS_EOF)) { + ret = -EIO; + goto error; + } + } + + if (IS_CLUS_EOF(p_chain->dir)) { + p_chain->dir = new_clu; + } else if (p_chain->flags == 0x01) { + if (exfat_ent_set(sb, last_clu, new_clu)) { + ret = -EIO; + goto error; + } + } + last_clu = new_clu; + + if ((--num_alloc) == 0) { + fsi->clu_srch_ptr = hint_clu; + fsi->used_clusters += num_clusters; + + p_chain->size += num_clusters; + return 0; + } + + hint_clu = new_clu + 1; + if (hint_clu >= fsi->num_clusters) { + hint_clu = CLUS_BASE; + + if (p_chain->flags == 0x03) { + if (exfat_chain_cont_cluster(sb, p_chain->dir, num_clusters)) { + ret = -EIO; + goto error; + } + p_chain->flags = 0x01; + } + } + } +error: + if (num_clusters) + exfat_free_cluster(sb, p_chain, 0); + return ret; +} + +static s32 exfat_count_used_clusters(struct super_block *sb, u32 *ret_count) +{ + u32 count = 0; + u32 i, map_i, map_b; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + u32 total_clus = fsi->num_clusters - 2; + + map_i = map_b = 0; + + for (i = 0; i < total_clus; i += 8) { + u8 k = *(((u8 *) fsi->vol_amap[map_i]->b_data) + map_b); + + count += used_bit[k]; + if ((++map_b) >= (u32)sb->s_blocksize) { + map_i++; + map_b = 0; + } + } + + /* FIXME : abnormal bitmap count should be handled as more smart */ + if (total_clus < count) + count = total_clus; + + *ret_count = count; + return 0; +} + + +/* + * File Operation Functions + */ +static FS_FUNC_T exfat_fs_func = { + .alloc_cluster = exfat_alloc_cluster, + .free_cluster = exfat_free_cluster, + .count_used_clusters = exfat_count_used_clusters, + + .init_dir_entry = exfat_init_dir_entry, + .init_ext_entry = exfat_init_ext_entry, + .find_dir_entry = exfat_find_dir_entry, + .delete_dir_entry = exfat_delete_dir_entry, + .get_uniname_from_ext_entry = exfat_get_uniname_from_ext_entry, + .count_ext_entries = exfat_count_ext_entries, + .calc_num_entries = exfat_calc_num_entries, + .check_max_dentries = exfat_check_max_dentries, + + .get_entry_type = exfat_get_entry_type, + .set_entry_type = exfat_set_entry_type, + .get_entry_attr = exfat_get_entry_attr, + .set_entry_attr = exfat_set_entry_attr, + .get_entry_flag = exfat_get_entry_flag, + .set_entry_flag = exfat_set_entry_flag, + .get_entry_clu0 = exfat_get_entry_clu0, + .set_entry_clu0 = exfat_set_entry_clu0, + .get_entry_size = exfat_get_entry_size, + .set_entry_size = exfat_set_entry_size, + .get_entry_time = exfat_get_entry_time, + .set_entry_time = exfat_set_entry_time, +}; + +s32 mount_exfat(struct super_block *sb, pbr_t *p_pbr) +{ + pbr64_t *p_bpb = (pbr64_t *)p_pbr; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + if (!p_bpb->bsx.num_fats) { + exfat_msg(sb, KERN_ERR, "bogus number of FAT structure"); + return -EINVAL; + } + + fsi->sect_per_clus = 1 << p_bpb->bsx.sect_per_clus_bits; + fsi->sect_per_clus_bits = p_bpb->bsx.sect_per_clus_bits; + fsi->cluster_size_bits = fsi->sect_per_clus_bits + sb->s_blocksize_bits; + fsi->cluster_size = 1 << fsi->cluster_size_bits; + + fsi->num_FAT_sectors = le32_to_cpu(p_bpb->bsx.fat_length); + + fsi->FAT1_start_sector = le32_to_cpu(p_bpb->bsx.fat_offset); + if (p_bpb->bsx.num_fats == 1) + fsi->FAT2_start_sector = fsi->FAT1_start_sector; + else + fsi->FAT2_start_sector = fsi->FAT1_start_sector + fsi->num_FAT_sectors; + + fsi->root_start_sector = le32_to_cpu(p_bpb->bsx.clu_offset); + fsi->data_start_sector = fsi->root_start_sector; + + fsi->num_sectors = le64_to_cpu(p_bpb->bsx.vol_length); + fsi->num_clusters = le32_to_cpu(p_bpb->bsx.clu_count) + 2; + /* because the cluster index starts with 2 */ + + fsi->vol_id = le32_to_cpu(p_bpb->bsx.vol_serial); + + fsi->root_dir = le32_to_cpu(p_bpb->bsx.root_cluster); + fsi->dentries_in_root = 0; + fsi->dentries_per_clu = 1 << (fsi->cluster_size_bits - DENTRY_SIZE_BITS); + + fsi->vol_flag = (u32) le16_to_cpu(p_bpb->bsx.vol_flags); + fsi->clu_srch_ptr = CLUS_BASE; + fsi->used_clusters = UINT_MAX; + + fsi->fs_func = &exfat_fs_func; + + if (p_bpb->bsx.vol_flags & VOL_DIRTY) { + fsi->vol_flag |= VOL_DIRTY; + exfat_log_msg(sb, KERN_WARNING, "Volume was not properly " + "unmounted. Some data may be corrupt. " + "Please run fsck."); + } + + return 0; +} diff --git a/fs/exfat/debian/changelog b/fs/exfat/debian/changelog new file mode 100644 index 00000000..1da1548e --- /dev/null +++ b/fs/exfat/debian/changelog @@ -0,0 +1,19 @@ +exfat-dkms (2.2.0-3arter97) UNRELEASED; urgency=medium + + * Added "quiet" mount option. + + -- Park Ju Hyung Tue, 29 Oct 2019 17:34:32 +0900 + +exfat-dkms (2.2.0-2arter97) UNRELEASED; urgency=medium + + * Fix xattr support. + * Merged upstream exFAT changes. + * More code clean-ups. + + -- Park Ju Hyung Wed, 9 Oct 2019 23:16:22 +0900 + +exfat-dkms (2.2.0-1arter97) UNRELEASED; urgency=medium + + * Initial release. + + -- Park Ju Hyung Tue, 13 Aug 2019 08:53:31 +0900 diff --git a/fs/exfat/debian/compat b/fs/exfat/debian/compat new file mode 100644 index 00000000..f599e28b --- /dev/null +++ b/fs/exfat/debian/compat @@ -0,0 +1 @@ +10 diff --git a/fs/exfat/debian/control b/fs/exfat/debian/control new file mode 100644 index 00000000..d6ccaabb --- /dev/null +++ b/fs/exfat/debian/control @@ -0,0 +1,11 @@ +Source: exfat-dkms +Section: misc +Priority: optional +Maintainer: Park Ju Hyung +Build-Depends: debhelper (>= 9), dkms + +Package: exfat-dkms +Architecture: all +Depends: ${misc:Depends}, dkms +Conflicts: exfat-fuse +Description: exFAT filesystem driver for Linux kernel diff --git a/fs/exfat/debian/copyright b/fs/exfat/debian/copyright new file mode 100644 index 00000000..ac85309b --- /dev/null +++ b/fs/exfat/debian/copyright @@ -0,0 +1,6 @@ +Author: Park Ju Hyung + +License: + +GNU General Public License version 2, see /usr/share/common-licenses/GPL-2 +for more information. diff --git a/fs/exfat/debian/exfat-dkms.dkms b/fs/exfat/debian/exfat-dkms.dkms new file mode 100644 index 00000000..bdb443c8 --- /dev/null +++ b/fs/exfat/debian/exfat-dkms.dkms @@ -0,0 +1,5 @@ +PACKAGE_NAME="exfat" +PACKAGE_VERSION="#MODULE_VERSION#" +BUILT_MODULE_NAME[0]="$PACKAGE_NAME" +DEST_MODULE_LOCATION[0]="/extra/dkms" +AUTOINSTALL="YES" diff --git a/fs/exfat/debian/exfat-dkms.install b/fs/exfat/debian/exfat-dkms.install new file mode 100644 index 00000000..b601f22c --- /dev/null +++ b/fs/exfat/debian/exfat-dkms.install @@ -0,0 +1 @@ +usr/src diff --git a/fs/exfat/debian/local/print_rule.mk b/fs/exfat/debian/local/print_rule.mk new file mode 100644 index 00000000..43f1c668 --- /dev/null +++ b/fs/exfat/debian/local/print_rule.mk @@ -0,0 +1,2 @@ +writeKbuild: + @printf 'obj-m += exfat.o\nexfat-objs := %s\n' "$(strip $(exfat-objs))" > $(KBUILD_PATH) diff --git a/fs/exfat/debian/rules b/fs/exfat/debian/rules new file mode 100644 index 00000000..5c9a58b1 --- /dev/null +++ b/fs/exfat/debian/rules @@ -0,0 +1,23 @@ +#!/usr/bin/make -f + +include /usr/share/dpkg/pkg-info.mk + +version := $(shell dpkg-parsechangelog | grep '^Version:' | cut -d' ' -f2) +targetdir := debian/tmp/usr/src/exfat-$(version) + +%: + dh $@ --with dkms + +override_dh_auto_install: + mkdir -p $(targetdir) + install -m644 *.c *.h Kconfig LICENSE Makefile $(targetdir) + # create a Kbuild file instead of patching the Makefile + $(MAKE) KERNELRELEASE=1 KBUILD_PATH=$(targetdir)/Kbuild -s -f Makefile -f debian/local/print_rule.mk writeKbuild + +override_dh_dkms: + dh_dkms -V $(version) + +override_dh_auto_configure: +override_dh_auto_build: +override_dh_auto_test: +override_dh_auto_clean: diff --git a/fs/exfat/exfat.h b/fs/exfat/exfat.h new file mode 100644 index 00000000..e1b6be50 --- /dev/null +++ b/fs/exfat/exfat.h @@ -0,0 +1,389 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + */ + +#ifndef _EXFAT_H +#define _EXFAT_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "api.h" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) +#include +#define INC_IVERSION(x) (inode_inc_iversion(x)) +#define GET_IVERSION(x) (inode_peek_iversion_raw(x)) +#define SET_IVERSION(x,y) (inode_set_iversion(x, y)) +#else +#define INC_IVERSION(x) (x->i_version++) +#define GET_IVERSION(x) (x->i_version) +#define SET_IVERSION(x,y) (x->i_version = y) +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) +#define timespec_compat timespec64 +#define KTIME_GET_REAL_TS ktime_get_real_ts64 +#else +#define timespec_compat timespec +#define KTIME_GET_REAL_TS ktime_get_real_ts +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +#define EXFAT_IS_SB_RDONLY(sb) ((sb)->s_flags & MS_RDONLY) +#else +#define EXFAT_IS_SB_RDONLY(sb) ((sb)->s_flags & SB_RDONLY) +#endif + +/* + * exfat error flags + */ +#define EXFAT_ERRORS_CONT (1) /* ignore error and continue */ +#define EXFAT_ERRORS_PANIC (2) /* panic on error */ +#define EXFAT_ERRORS_RO (3) /* remount r/o on error */ + +/* + * exfat allocator destination for smart allocation + */ +#define ALLOC_NOWHERE (0) +#define ALLOC_COLD (1) +#define ALLOC_HOT (16) +#define ALLOC_COLD_ALIGNED (1) +#define ALLOC_COLD_PACKING (2) +#define ALLOC_COLD_SEQ (4) + +/* + * exfat nls lossy flag + */ +#define NLS_NAME_NO_LOSSY (0x00) /* no lossy */ +#define NLS_NAME_LOSSY (0x01) /* just detected incorrect filename(s) */ +#define NLS_NAME_OVERLEN (0x02) /* the length is over than its limit */ + +/* + * exfat common MACRO + */ +#define CLUSTER_16(x) ((u16)((x) & 0xFFFFU)) +#define CLUSTER_32(x) ((u32)((x) & 0xFFFFFFFFU)) +#define CLUS_EOF CLUSTER_32(~0) +#define CLUS_BAD (0xFFFFFFF7U) +#define CLUS_FREE (0) +#define CLUS_BASE (2) +#define IS_CLUS_EOF(x) ((x) == CLUS_EOF) +#define IS_CLUS_BAD(x) ((x) == CLUS_BAD) +#define IS_CLUS_FREE(x) ((x) == CLUS_FREE) +#define IS_LAST_SECT_IN_CLUS(fsi, sec) \ + ((((sec) - (fsi)->data_start_sector + 1) \ + & ((1 << (fsi)->sect_per_clus_bits) - 1)) == 0) + +#define CLUS_TO_SECT(fsi, x) \ + ((((unsigned long long)(x) - CLUS_BASE) << (fsi)->sect_per_clus_bits) + (fsi)->data_start_sector) + +#define SECT_TO_CLUS(fsi, sec) \ + ((u32)((((sec) - (fsi)->data_start_sector) >> (fsi)->sect_per_clus_bits) + CLUS_BASE)) + +/* + * exfat mount in-memory data + */ +struct exfat_mount_options { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) + kuid_t fs_uid; + kgid_t fs_gid; +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0) */ + uid_t fs_uid; + gid_t fs_gid; +#endif + unsigned short fs_fmask; + unsigned short fs_dmask; + unsigned short allow_utime; /* permission for setting the [am]time */ + unsigned short codepage; /* codepage for shortname conversions */ + char *iocharset; /* charset for filename input/display */ + unsigned char quiet; /* fake return success on setattr(e.g. chmods/chowns) */ + + unsigned char utf8; + unsigned char casesensitive; + unsigned char tz_utc; + unsigned char symlink; /* support symlink operation */ + unsigned char errors; /* on error: continue, panic, remount-ro */ + unsigned char discard; /* flag on if -o dicard specified and device support discard() */ + unsigned char delayed_meta; /* delay flushing dirty metadata */ +}; + +#define EXFAT_HASH_BITS 8 +#define EXFAT_HASH_SIZE (1UL << EXFAT_HASH_BITS) + +/* + * EXFAT file system superblock in-memory data + */ +struct exfat_sb_info { + FS_INFO_T fsi; /* private filesystem info */ + + struct mutex s_vlock; /* volume lock */ + int use_vmalloc; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0) + int s_dirt; + struct mutex s_lock; /* superblock lock */ + int write_super_queued; /* Write_super work is pending? */ + struct delayed_work write_super_work; /* Work_queue data structrue for write_super() */ + spinlock_t work_lock; /* Lock for WQ */ +#endif + struct super_block *host_sb; /* sb pointer */ + struct exfat_mount_options options; + struct nls_table *nls_disk; /* Codepage used on disk */ + struct nls_table *nls_io; /* Charset used for input and display */ + struct ratelimit_state ratelimit; + + spinlock_t inode_hash_lock; + struct hlist_head inode_hashtable[EXFAT_HASH_SIZE]; + struct kobject sb_kobj; + + atomic_t stat_n_pages_queued; /* # of pages in the request queue (approx.) */ +}; + +/* + * EXFAT file system inode in-memory data + */ +struct exfat_inode_info { + FILE_ID_T fid; + char *target; + /* NOTE: i_size_ondisk is 64bits, so must hold ->inode_lock to access */ + loff_t i_size_ondisk; /* physically allocated size */ + loff_t i_size_aligned; /* block-aligned i_size (used in cont_write_begin) */ + loff_t i_pos; /* on-disk position of directory entry or 0 */ + struct hlist_node i_hash_fat; /* hash by i_location */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + struct rw_semaphore truncate_lock; /* protect bmap against truncate */ +#endif + struct inode vfs_inode; +}; + +/* + * FIXME : needs on-disk-slot in-memory data + */ + +static inline struct exfat_sb_info *EXFAT_SB(struct super_block *sb) +{ + return (struct exfat_sb_info *)sb->s_fs_info; +} + +static inline struct exfat_inode_info *EXFAT_I(struct inode *inode) +{ + return container_of(inode, struct exfat_inode_info, vfs_inode); +} + +/* + * If ->i_mode can't hold S_IWUGO (i.e. ATTR_RO), we use ->i_attrs to + * save ATTR_RO instead of ->i_mode. + * + * If it's directory and !sbi->options.rodir, ATTR_RO isn't read-only + * bit, it's just used as flag for app. + */ +static inline int exfat_mode_can_hold_ro(struct inode *inode) +{ + struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); + + if (S_ISDIR(inode->i_mode)) + return 0; + + if ((~sbi->options.fs_fmask) & S_IWUGO) + return 1; + return 0; +} + +/* + * FIXME : needs to check symlink option. + */ +/* Convert attribute bits and a mask to the UNIX mode. */ +static inline mode_t exfat_make_mode(struct exfat_sb_info *sbi, + u32 attr, mode_t mode) +{ + if ((attr & ATTR_READONLY) && !(attr & ATTR_SUBDIR)) + mode &= ~S_IWUGO; + + if (attr & ATTR_SUBDIR) + return (mode & ~sbi->options.fs_dmask) | S_IFDIR; + else if (attr & ATTR_SYMLINK) + return (mode & ~sbi->options.fs_dmask) | S_IFLNK; + else + return (mode & ~sbi->options.fs_fmask) | S_IFREG; +} + +/* Return the FAT attribute byte for this inode */ +static inline u32 exfat_make_attr(struct inode *inode) +{ + u32 attrs = EXFAT_I(inode)->fid.attr; + + if (S_ISDIR(inode->i_mode)) + attrs |= ATTR_SUBDIR; + if (exfat_mode_can_hold_ro(inode) && !(inode->i_mode & S_IWUGO)) + attrs |= ATTR_READONLY; + return attrs; +} + +static inline void exfat_save_attr(struct inode *inode, u32 attr) +{ + if (exfat_mode_can_hold_ro(inode)) + EXFAT_I(inode)->fid.attr = attr & ATTR_RWMASK; + else + EXFAT_I(inode)->fid.attr = attr & (ATTR_RWMASK | ATTR_READONLY); +} + +/* exfat/nls.c */ +/* NLS management function */ +s32 exfat_nls_cmp_uniname(struct super_block *sb, u16 *a, u16 *b); +s32 exfat_nls_sfn_to_uni16s(struct super_block *sb, DOS_NAME_T *p_dosname, UNI_NAME_T *p_uniname); +s32 exfat_nls_uni16s_to_vfsname(struct super_block *sb, UNI_NAME_T *uniname, u8 *p_cstring, s32 len); +s32 exfat_nls_vfsname_to_uni16s(struct super_block *sb, const u8 *p_cstring, + const s32 len, UNI_NAME_T *uniname, s32 *p_lossy); + +/* exfat/xattr.c */ +#ifdef CONFIG_EXFAT_VIRTUAL_XATTR +void setup_exfat_xattr_handler(struct super_block *sb); +extern int exfat_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags); +extern ssize_t exfat_getxattr(struct dentry *dentry, const char *name, void *value, size_t size); +extern ssize_t exfat_listxattr(struct dentry *dentry, char *list, size_t size); +extern int exfat_removexattr(struct dentry *dentry, const char *name); +#else +static inline void setup_exfat_xattr_handler(struct super_block *sb) {}; +#endif + +/* exfat/misc.c */ +#ifdef CONFIG_EXFAT_UEVENT +extern int exfat_uevent_init(struct kset *exfat_kset); +extern void exfat_uevent_uninit(void); +extern void exfat_uevent_ro_remount(struct super_block *sb); +#else +static inline int exfat_uevent_init(struct kset *exfat_kset) +{ + return 0; +} +static inline void exfat_uevent_uninit(void) {}; +static inline void exfat_uevent_ro_remount(struct super_block *sb) {}; +#endif +extern void +__exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...) + __printf(3, 4) __cold; +#define exfat_fs_error(sb, fmt, args...) \ + __exfat_fs_error(sb, 1, fmt, ## args) +#define exfat_fs_error_ratelimit(sb, fmt, args...) \ + __exfat_fs_error(sb, __ratelimit(&EXFAT_SB(sb)->ratelimit), fmt, ## args) +extern void +__exfat_msg(struct super_block *sb, const char *lv, int st, const char *fmt, ...) + __printf(4, 5) __cold; +#define exfat_msg(sb, lv, fmt, args...) \ + __exfat_msg(sb, lv, 0, fmt, ## args) +#define exfat_log_msg(sb, lv, fmt, args...) \ + __exfat_msg(sb, lv, 1, fmt, ## args) +extern void exfat_log_version(void); +extern void exfat_time_fat2unix(struct exfat_sb_info *sbi, struct timespec_compat *ts, + DATE_TIME_T *tp); +extern void exfat_time_unix2fat(struct exfat_sb_info *sbi, struct timespec_compat *ts, + DATE_TIME_T *tp); +extern TIMESTAMP_T *exfat_tm_now(struct exfat_sb_info *sbi, TIMESTAMP_T *tm); + +#ifdef CONFIG_EXFAT_DEBUG + +#ifdef CONFIG_EXFAT_DBG_BUGON +#define exfat_debug_bug_on(expr) BUG_ON(expr) +#else +#define exfat_debug_bug_on(expr) +#endif + +#ifdef CONFIG_EXFAT_DBG_WARNON +#define exfat_debug_warn_on(expr) WARN_ON(expr) +#else +#define exfat_debug_warn_on(expr) +#endif + +#else /* CONFIG_EXFAT_DEBUG */ + +#define exfat_debug_bug_on(expr) +#define exfat_debug_warn_on(expr) + +#endif /* CONFIG_EXFAT_DEBUG */ + +#define EXFAT_MSG_LV_NONE (0x00000000) +#define EXFAT_MSG_LV_ERR (0x00000001) +#define EXFAT_MSG_LV_INFO (0x00000002) +#define EXFAT_MSG_LV_DBG (0x00000003) +#define EXFAT_MSG_LV_MORE (0x00000004) +#define EXFAT_MSG_LV_TRACE (0x00000005) +#define EXFAT_MSG_LV_ALL (0x00000006) + +#define EXFAT_MSG_LEVEL EXFAT_MSG_LV_INFO + +#define EXFAT_TAG_NAME "EXFAT" +#define __S(x) #x +#define _S(x) __S(x) + +extern void __exfat_dmsg(int level, const char *fmt, ...) __printf(2, 3) __cold; + +#define EXFAT_EMSG_T(level, ...) \ + __exfat_dmsg(level, KERN_ERR "[" EXFAT_TAG_NAME "] [" _S(__FILE__) "(" _S(__LINE__) ")] " __VA_ARGS__) +#define EXFAT_DMSG_T(level, ...) \ + __exfat_dmsg(level, KERN_INFO "[" EXFAT_TAG_NAME "] " __VA_ARGS__) + +#define EXFAT_EMSG(...) EXFAT_EMSG_T(EXFAT_MSG_LV_ERR, __VA_ARGS__) +#define EXFAT_IMSG(...) EXFAT_DMSG_T(EXFAT_MSG_LV_INFO, __VA_ARGS__) +#define EXFAT_DMSG(...) EXFAT_DMSG_T(EXFAT_MSG_LV_DBG, __VA_ARGS__) +#define EXFAT_MMSG(...) EXFAT_DMSG_T(EXFAT_MSG_LV_MORE, __VA_ARGS__) + +#define EMSG(...) +#define IMSG(...) +#define DMSG(...) +#define MMSG(...) + +#define EMSG_VAR(exp) +#define IMSG_VAR(exp) +#define DMSG_VAR(exp) +#define MMSG_VAR(exp) + +#ifdef CONFIG_EXFAT_DBG_MSG + + +#if (EXFAT_MSG_LEVEL >= EXFAT_MSG_LV_ERR) +#undef EMSG +#undef EMSG_VAR +#define EMSG(...) EXFAT_EMSG(__VA_ARGS__) +#define EMSG_VAR(exp) exp +#endif + +#if (EXFAT_MSG_LEVEL >= EXFAT_MSG_LV_INFO) +#undef IMSG +#undef IMSG_VAR +#define IMSG(...) EXFAT_IMSG(__VA_ARGS__) +#define IMSG_VAR(exp) exp +#endif + +#if (EXFAT_MSG_LEVEL >= EXFAT_MSG_LV_DBG) +#undef DMSG +#undef DMSG_VAR +#define DMSG(...) EXFAT_DMSG(__VA_ARGS__) +#define DMSG_VAR(exp) exp +#endif + +#if (EXFAT_MSG_LEVEL >= EXFAT_MSG_LV_MORE) +#undef MMSG +#undef MMSG_VAR +#define MMSG(...) EXFAT_MMSG(__VA_ARGS__) +#define MMSG_VAR(exp) exp +#endif + +#endif /* CONFIG_EXFAT_DBG_MSG */ + +#define ASSERT(expr) { \ + if (!(expr)) { \ + pr_err("exFAT: Assertion failed! %s\n", #expr); \ + BUG_ON(1); \ + } \ +} + +#endif /* !_EXFAT_H */ + diff --git a/fs/exfat/exfat_api.c b/fs/exfat/exfat_api.c new file mode 100644 index 00000000..32b29f0d --- /dev/null +++ b/fs/exfat/exfat_api.c @@ -0,0 +1,528 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_api.c */ +/* PURPOSE : exFAT API Glue Layer */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#include +#include +#include + +#include "exfat_version.h" +#include "exfat_config.h" +#include "exfat_data.h" +#include "exfat_oal.h" + +#include "exfat_nls.h" +#include "exfat_api.h" +#include "exfat_super.h" +#include "exfat_core.h" + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Global Variable Definitions */ +/*----------------------------------------------------------------------*/ + +extern struct semaphore z_sem; + +/*----------------------------------------------------------------------*/ +/* Local Variable Definitions */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Local Function Declarations */ +/*----------------------------------------------------------------------*/ + +/*======================================================================*/ +/* Global Function Definitions */ +/* - All functions for global use have same return value format, */ +/* that is, FFS_SUCCESS on success and several FS error code on */ +/* various error condition. */ +/*======================================================================*/ + +/*----------------------------------------------------------------------*/ +/* exFAT Filesystem Init & Exit Functions */ +/*----------------------------------------------------------------------*/ + +int FsInit(void) +{ + return ffsInit(); +} + +int FsShutdown(void) +{ + return ffsShutdown(); +} + +/*----------------------------------------------------------------------*/ +/* Volume Management Functions */ +/*----------------------------------------------------------------------*/ + +/* FsMountVol : mount the file system volume */ +int FsMountVol(struct super_block *sb) +{ + int err; + + sm_P(&z_sem); + + err = buf_init(sb); + if (!err) + err = ffsMountVol(sb); + else + buf_shutdown(sb); + + sm_V(&z_sem); + + return err; +} /* end of FsMountVol */ + +/* FsUmountVol : unmount the file system volume */ +int FsUmountVol(struct super_block *sb) +{ + int err; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + sm_P(&z_sem); + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsUmountVol(sb); + buf_shutdown(sb); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + sm_V(&z_sem); + + return err; +} /* end of FsUmountVol */ + +/* FsGetVolInfo : get the information of a file system volume */ +int FsGetVolInfo(struct super_block *sb, VOL_INFO_T *info) +{ + int err; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of pointer parameters */ + if (info == NULL) + return FFS_ERROR; + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsGetVolInfo(sb, info); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsGetVolInfo */ + +/* FsSyncVol : synchronize a file system volume */ +int FsSyncVol(struct super_block *sb, int do_sync) +{ + int err; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsSyncVol(sb, do_sync); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsSyncVol */ + + +/*----------------------------------------------------------------------*/ +/* File Operation Functions */ +/*----------------------------------------------------------------------*/ + +/* FsCreateFile : create a file */ +int FsLookupFile(struct inode *inode, char *path, FILE_ID_T *fid) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of pointer parameters */ + if ((fid == NULL) || (path == NULL) || (*path == '\0')) + return FFS_ERROR; + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsLookupFile(inode, path, fid); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsLookupFile */ + +/* FsCreateFile : create a file */ +int FsCreateFile(struct inode *inode, char *path, u8 mode, FILE_ID_T *fid) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of pointer parameters */ + if ((fid == NULL) || (path == NULL) || (*path == '\0')) + return FFS_ERROR; + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsCreateFile(inode, path, mode, fid); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsCreateFile */ + +int FsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of the given file id */ + if (fid == NULL) + return FFS_INVALIDFID; + + /* check the validity of pointer parameters */ + if (buffer == NULL) + return FFS_ERROR; + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsReadFile(inode, fid, buffer, count, rcount); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsReadFile */ + +int FsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of the given file id */ + if (fid == NULL) + return FFS_INVALIDFID; + + /* check the validity of pointer parameters */ + if (buffer == NULL) + return FFS_ERROR; + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsWriteFile(inode, fid, buffer, count, wcount); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsWriteFile */ + +/* FsTruncateFile : resize the file length */ +int FsTruncateFile(struct inode *inode, u64 old_size, u64 new_size) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + DPRINTK("FsTruncateFile entered (inode %p size %llu)\n", inode, new_size); + + err = ffsTruncateFile(inode, old_size, new_size); + + DPRINTK("FsTruncateFile exitted (%d)\n", err); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsTruncateFile */ + +/* FsMoveFile : move(rename) a old file into a new file */ +int FsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry) +{ + int err; + struct super_block *sb = old_parent_inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of the given file id */ + if (fid == NULL) + return FFS_INVALIDFID; + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsMoveFile(old_parent_inode, fid, new_parent_inode, new_dentry); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsMoveFile */ + +/* FsRemoveFile : remove a file */ +int FsRemoveFile(struct inode *inode, FILE_ID_T *fid) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of the given file id */ + if (fid == NULL) + return FFS_INVALIDFID; + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsRemoveFile(inode, fid); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsRemoveFile */ + +/* FsSetAttr : set the attribute of a given file */ +int FsSetAttr(struct inode *inode, u32 attr) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsSetAttr(inode, attr); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsSetAttr */ + +/* FsReadStat : get the information of a given file */ +int FsReadStat(struct inode *inode, DIR_ENTRY_T *info) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsGetStat(inode, info); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsReadStat */ + +/* FsWriteStat : set the information of a given file */ +int FsWriteStat(struct inode *inode, DIR_ENTRY_T *info) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + DPRINTK("FsWriteStat entered (inode %p info %p\n", inode, info); + + err = ffsSetStat(inode, info); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + DPRINTK("FsWriteStat exited (%d)\n", err); + + return err; +} /* end of FsWriteStat */ + +/* FsMapCluster : return the cluster number in the given cluster offset */ +int FsMapCluster(struct inode *inode, s32 clu_offset, u32 *clu) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of pointer parameters */ + if (clu == NULL) + return FFS_ERROR; + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsMapCluster(inode, clu_offset, clu); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsMapCluster */ + +/*----------------------------------------------------------------------*/ +/* Directory Operation Functions */ +/*----------------------------------------------------------------------*/ + +/* FsCreateDir : create(make) a directory */ +int FsCreateDir(struct inode *inode, char *path, FILE_ID_T *fid) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of pointer parameters */ + if ((fid == NULL) || (path == NULL) || (*path == '\0')) + return FFS_ERROR; + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsCreateDir(inode, path, fid); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsCreateDir */ + +/* FsReadDir : read a directory entry from the opened directory */ +int FsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of pointer parameters */ + if (dir_entry == NULL) + return FFS_ERROR; + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsReadDir(inode, dir_entry); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsReadDir */ + +/* FsRemoveDir : remove a directory */ +int FsRemoveDir(struct inode *inode, FILE_ID_T *fid) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of the given file id */ + if (fid == NULL) + return FFS_INVALIDFID; + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsRemoveDir(inode, fid); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsRemoveDir */ + +EXPORT_SYMBOL(FsMountVol); +EXPORT_SYMBOL(FsUmountVol); +EXPORT_SYMBOL(FsGetVolInfo); +EXPORT_SYMBOL(FsSyncVol); +EXPORT_SYMBOL(FsLookupFile); +EXPORT_SYMBOL(FsCreateFile); +EXPORT_SYMBOL(FsReadFile); +EXPORT_SYMBOL(FsWriteFile); +EXPORT_SYMBOL(FsTruncateFile); +EXPORT_SYMBOL(FsMoveFile); +EXPORT_SYMBOL(FsRemoveFile); +EXPORT_SYMBOL(FsSetAttr); +EXPORT_SYMBOL(FsReadStat); +EXPORT_SYMBOL(FsWriteStat); +EXPORT_SYMBOL(FsMapCluster); +EXPORT_SYMBOL(FsCreateDir); +EXPORT_SYMBOL(FsReadDir); +EXPORT_SYMBOL(FsRemoveDir); + +#ifdef CONFIG_EXFAT_KERNEL_DEBUG +/* FsReleaseCache: Release FAT & buf cache */ +int FsReleaseCache(struct super_block *sb) +{ + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + FAT_release_all(sb); + buf_release_all(sb); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return 0; +} +/* FsReleaseCache */ + +EXPORT_SYMBOL(FsReleaseCache); +#endif /* CONFIG_EXFAT_KERNEL_DEBUG */ + +/*======================================================================*/ +/* Local Function Definitions */ +/*======================================================================*/ diff --git a/fs/exfat/exfat_api.h b/fs/exfat/exfat_api.h new file mode 100644 index 00000000..84bdf612 --- /dev/null +++ b/fs/exfat/exfat_api.h @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_api.h */ +/* PURPOSE : Header File for exFAT API Glue Layer */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_API_H +#define _EXFAT_API_H + +#include +#include "exfat_config.h" + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions */ +/*----------------------------------------------------------------------*/ + +#define EXFAT_SUPER_MAGIC (0x2011BAB0L) +#define EXFAT_ROOT_INO 1 + +/* FAT types */ +#define FAT12 0x01 /* FAT12 */ +#define FAT16 0x0E /* Win95 FAT16 (LBA) */ +#define FAT32 0x0C /* Win95 FAT32 (LBA) */ +#define EXFAT 0x07 /* exFAT */ + +/* file name lengths */ +#define MAX_CHARSET_SIZE 3 /* max size of multi-byte character */ +#define MAX_PATH_DEPTH 15 /* max depth of path name */ +#define MAX_NAME_LENGTH 256 /* max len of file name including NULL */ +#define MAX_PATH_LENGTH 260 /* max len of path name including NULL */ +#define DOS_NAME_LENGTH 11 /* DOS file name length excluding NULL */ +#define DOS_PATH_LENGTH 80 /* DOS path name length excluding NULL */ + +/* file attributes */ +#define ATTR_NORMAL 0x0000 +#define ATTR_READONLY 0x0001 +#define ATTR_HIDDEN 0x0002 +#define ATTR_SYSTEM 0x0004 +#define ATTR_VOLUME 0x0008 +#define ATTR_SUBDIR 0x0010 +#define ATTR_ARCHIVE 0x0020 +#define ATTR_SYMLINK 0x0040 +#define ATTR_EXTEND 0x000F +#define ATTR_RWMASK 0x007E + +/* file creation modes */ +#define FM_REGULAR 0x00 +#define FM_SYMLINK 0x40 + +/* return values */ +#define FFS_SUCCESS 0 +#define FFS_MEDIAERR 1 +#define FFS_FORMATERR 2 +#define FFS_MOUNTED 3 +#define FFS_NOTMOUNTED 4 +#define FFS_ALIGNMENTERR 5 +#define FFS_SEMAPHOREERR 6 +#define FFS_INVALIDPATH 7 +#define FFS_INVALIDFID 8 +#define FFS_NOTFOUND 9 +#define FFS_FILEEXIST 10 +#define FFS_PERMISSIONERR 11 +#define FFS_NOTOPENED 12 +#define FFS_MAXOPENED 13 +#define FFS_FULL 14 +#define FFS_EOF 15 +#define FFS_DIRBUSY 16 +#define FFS_MEMORYERR 17 +#define FFS_NAMETOOLONG 18 +#define FFS_ERROR 19 + +/*----------------------------------------------------------------------*/ +/* Type Definitions */ +/*----------------------------------------------------------------------*/ + +typedef struct { + u16 Year; + u16 Month; + u16 Day; + u16 Hour; + u16 Minute; + u16 Second; + u16 MilliSecond; +} DATE_TIME_T; + +typedef struct { + u32 Offset; /* start sector number of the partition */ + u32 Size; /* in sectors */ +} PART_INFO_T; + +typedef struct { + u32 SecSize; /* sector size in bytes */ + u32 DevSize; /* block device size in sectors */ +} DEV_INFO_T; + +typedef struct { + u32 FatType; + u32 ClusterSize; + u32 NumClusters; + u32 FreeClusters; + u32 UsedClusters; +} VOL_INFO_T; + +/* directory structure */ +typedef struct { + u32 dir; + s32 size; + u8 flags; +} CHAIN_T; + +/* file id structure */ +typedef struct { + CHAIN_T dir; + s32 entry; + u32 type; + u32 attr; + u32 start_clu; + u64 size; + u8 flags; + s64 rwoffset; + s32 hint_last_off; + u32 hint_last_clu; +} FILE_ID_T; + +typedef struct { + char Name[MAX_NAME_LENGTH * MAX_CHARSET_SIZE]; + char ShortName[DOS_NAME_LENGTH + 2]; /* used only for FAT12/16/32, not used for exFAT */ + u32 Attr; + u64 Size; + u32 NumSubdirs; + DATE_TIME_T CreateTimestamp; + DATE_TIME_T ModifyTimestamp; + DATE_TIME_T AccessTimestamp; +} DIR_ENTRY_T; + +/*======================================================================*/ +/* */ +/* API FUNCTION DECLARATIONS */ +/* (CHANGE THIS PART IF REQUIRED) */ +/* */ +/*======================================================================*/ + +/*----------------------------------------------------------------------*/ +/* External Function Declarations */ +/*----------------------------------------------------------------------*/ + +/* file system initialization & shutdown functions */ + int FsInit(void); + int FsShutdown(void); + +/* volume management functions */ + int FsMountVol(struct super_block *sb); + int FsUmountVol(struct super_block *sb); + int FsGetVolInfo(struct super_block *sb, VOL_INFO_T *info); + int FsSyncVol(struct super_block *sb, int do_sync); + +/* file management functions */ + int FsLookupFile(struct inode *inode, char *path, FILE_ID_T *fid); + int FsCreateFile(struct inode *inode, char *path, u8 mode, FILE_ID_T *fid); + int FsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount); + int FsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount); + int FsTruncateFile(struct inode *inode, u64 old_size, u64 new_size); + int FsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry); + int FsRemoveFile(struct inode *inode, FILE_ID_T *fid); + int FsSetAttr(struct inode *inode, u32 attr); + int FsReadStat(struct inode *inode, DIR_ENTRY_T *info); + int FsWriteStat(struct inode *inode, DIR_ENTRY_T *info); + int FsMapCluster(struct inode *inode, s32 clu_offset, u32 *clu); + +/* directory management functions */ + int FsCreateDir(struct inode *inode, char *path, FILE_ID_T *fid); + int FsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry); + int FsRemoveDir(struct inode *inode, FILE_ID_T *fid); + +/* debug functions */ +s32 FsReleaseCache(struct super_block *sb); + +#endif /* _EXFAT_API_H */ diff --git a/fs/exfat/exfat_bitmap.c b/fs/exfat/exfat_bitmap.c new file mode 100644 index 00000000..b0672dd0 --- /dev/null +++ b/fs/exfat/exfat_bitmap.c @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_global.c */ +/* PURPOSE : exFAT Miscellaneous Functions */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#include "exfat_config.h" +#include "exfat_bitmap.h" + +/*----------------------------------------------------------------------*/ +/* Bitmap Manipulation Functions */ +/*----------------------------------------------------------------------*/ + +#define BITMAP_LOC(v) ((v) >> 3) +#define BITMAP_SHIFT(v) ((v) & 0x07) + +s32 exfat_bitmap_test(u8 *bitmap, int i) +{ + u8 data; + + data = bitmap[BITMAP_LOC(i)]; + if ((data >> BITMAP_SHIFT(i)) & 0x01) + return 1; + return 0; +} /* end of Bitmap_test */ + +void exfat_bitmap_set(u8 *bitmap, int i) +{ + bitmap[BITMAP_LOC(i)] |= (0x01 << BITMAP_SHIFT(i)); +} /* end of Bitmap_set */ + +void exfat_bitmap_clear(u8 *bitmap, int i) +{ + bitmap[BITMAP_LOC(i)] &= ~(0x01 << BITMAP_SHIFT(i)); +} /* end of Bitmap_clear */ diff --git a/fs/exfat/exfat_bitmap.h b/fs/exfat/exfat_bitmap.h new file mode 100644 index 00000000..4f482c7b --- /dev/null +++ b/fs/exfat/exfat_bitmap.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_global.h */ +/* PURPOSE : Header File for exFAT Global Definitions & Misc Functions */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_BITMAP_H +#define _EXFAT_BITMAP_H + +#include + +/*======================================================================*/ +/* */ +/* LIBRARY FUNCTION DECLARATIONS -- OTHER UTILITY FUNCTIONS */ +/* (DO NOT CHANGE THIS PART !!) */ +/* */ +/*======================================================================*/ + +/*----------------------------------------------------------------------*/ +/* Bitmap Manipulation Functions */ +/*----------------------------------------------------------------------*/ + +s32 exfat_bitmap_test(u8 *bitmap, int i); +void exfat_bitmap_set(u8 *bitmap, int i); +void exfat_bitmap_clear(u8 *bitmpa, int i); + +#endif /* _EXFAT_BITMAP_H */ diff --git a/fs/exfat/exfat_blkdev.c b/fs/exfat/exfat_blkdev.c new file mode 100644 index 00000000..02fa4fe1 --- /dev/null +++ b/fs/exfat/exfat_blkdev.c @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_blkdev.c */ +/* PURPOSE : exFAT Block Device Driver Glue Layer */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#include +#include +#include "exfat_config.h" +#include "exfat_blkdev.h" +#include "exfat_data.h" +#include "exfat_api.h" +#include "exfat_super.h" + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Global Variable Definitions */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Local Variable Definitions */ +/*----------------------------------------------------------------------*/ + +/*======================================================================*/ +/* Function Definitions */ +/*======================================================================*/ + +s32 bdev_init(void) +{ + return FFS_SUCCESS; +} + +s32 bdev_shutdown(void) +{ + return FFS_SUCCESS; +} + +s32 bdev_open(struct super_block *sb) +{ + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + if (p_bd->opened) + return FFS_SUCCESS; + + p_bd->sector_size = bdev_logical_block_size(sb->s_bdev); + p_bd->sector_size_bits = ilog2(p_bd->sector_size); + p_bd->sector_size_mask = p_bd->sector_size - 1; + p_bd->num_sectors = i_size_read(sb->s_bdev->bd_inode) >> p_bd->sector_size_bits; + + p_bd->opened = TRUE; + + return FFS_SUCCESS; +} + +s32 bdev_close(struct super_block *sb) +{ + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + if (!p_bd->opened) + return FFS_SUCCESS; + + p_bd->opened = FALSE; + return FFS_SUCCESS; +} + +s32 bdev_read(struct super_block *sb, u32 secno, struct buffer_head **bh, u32 num_secs, s32 read) +{ + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); +#ifdef CONFIG_EXFAT_KERNEL_DEBUG + struct exfat_sb_info *sbi = EXFAT_SB(sb); + long flags = sbi->debug_flags; + + if (flags & EXFAT_DEBUGFLAGS_ERROR_RW) + return FFS_MEDIAERR; +#endif /* CONFIG_EXFAT_KERNEL_DEBUG */ + + if (!p_bd->opened) + return FFS_MEDIAERR; + + if (*bh) + __brelse(*bh); + + if (read) + *bh = __bread(sb->s_bdev, secno, num_secs << p_bd->sector_size_bits); + else + *bh = __getblk(sb->s_bdev, secno, num_secs << p_bd->sector_size_bits); + + if (*bh) + return FFS_SUCCESS; + + WARN(!p_fs->dev_ejected, + "[EXFAT] No bh, device seems wrong or to be ejected.\n"); + + return FFS_MEDIAERR; +} + +s32 bdev_write(struct super_block *sb, u32 secno, struct buffer_head *bh, u32 num_secs, s32 sync) +{ + s32 count; + struct buffer_head *bh2; + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); +#ifdef CONFIG_EXFAT_KERNEL_DEBUG + struct exfat_sb_info *sbi = EXFAT_SB(sb); + long flags = sbi->debug_flags; + + if (flags & EXFAT_DEBUGFLAGS_ERROR_RW) + return FFS_MEDIAERR; +#endif /* CONFIG_EXFAT_KERNEL_DEBUG */ + + if (!p_bd->opened) + return FFS_MEDIAERR; + + if (secno == bh->b_blocknr) { + lock_buffer(bh); + set_buffer_uptodate(bh); + mark_buffer_dirty(bh); + unlock_buffer(bh); + if (sync && (sync_dirty_buffer(bh) != 0)) + return FFS_MEDIAERR; + } else { + count = num_secs << p_bd->sector_size_bits; + + bh2 = __getblk(sb->s_bdev, secno, count); + + if (bh2 == NULL) + goto no_bh; + + lock_buffer(bh2); + memcpy(bh2->b_data, bh->b_data, count); + set_buffer_uptodate(bh2); + mark_buffer_dirty(bh2); + unlock_buffer(bh2); + if (sync && (sync_dirty_buffer(bh2) != 0)) { + __brelse(bh2); + goto no_bh; + } + __brelse(bh2); + } + + return FFS_SUCCESS; + +no_bh: + WARN(!p_fs->dev_ejected, + "[EXFAT] No bh, device seems wrong or to be ejected.\n"); + + return FFS_MEDIAERR; +} + +s32 bdev_sync(struct super_block *sb) +{ + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); +#ifdef CONFIG_EXFAT_KERNEL_DEBUG + struct exfat_sb_info *sbi = EXFAT_SB(sb); + long flags = sbi->debug_flags; + + if (flags & EXFAT_DEBUGFLAGS_ERROR_RW) + return FFS_MEDIAERR; +#endif /* CONFIG_EXFAT_KERNEL_DEBUG */ + + if (!p_bd->opened) + return FFS_MEDIAERR; + + return sync_blockdev(sb->s_bdev); +} diff --git a/fs/exfat/exfat_blkdev.h b/fs/exfat/exfat_blkdev.h new file mode 100644 index 00000000..169d25d4 --- /dev/null +++ b/fs/exfat/exfat_blkdev.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_blkdev.h */ +/* PURPOSE : Header File for exFAT Block Device Driver Glue Layer */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_BLKDEV_H +#define _EXFAT_BLKDEV_H + +#include +#include "exfat_config.h" + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions (Non-Configurable) */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Type Definitions */ +/*----------------------------------------------------------------------*/ + +typedef struct __BD_INFO_T { + s32 sector_size; /* in bytes */ + s32 sector_size_bits; + s32 sector_size_mask; + s32 num_sectors; /* total number of sectors in this block device */ + bool opened; /* opened or not */ +} BD_INFO_T; + +/*----------------------------------------------------------------------*/ +/* External Variable Declarations */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* External Function Declarations */ +/*----------------------------------------------------------------------*/ + +s32 bdev_init(void); +s32 bdev_shutdown(void); +s32 bdev_open(struct super_block *sb); +s32 bdev_close(struct super_block *sb); +s32 bdev_read(struct super_block *sb, u32 secno, struct buffer_head **bh, u32 num_secs, s32 read); +s32 bdev_write(struct super_block *sb, u32 secno, struct buffer_head *bh, u32 num_secs, s32 sync); +s32 bdev_sync(struct super_block *sb); + +#endif /* _EXFAT_BLKDEV_H */ diff --git a/fs/exfat/exfat_cache.c b/fs/exfat/exfat_cache.c new file mode 100644 index 00000000..e6ca88b8 --- /dev/null +++ b/fs/exfat/exfat_cache.c @@ -0,0 +1,780 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_cache.c */ +/* PURPOSE : exFAT Cache Manager */ +/* (FAT Cache & Buffer Cache) */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Sung-Kwan Kim] : first writing */ +/* */ +/************************************************************************/ + +#include "exfat_config.h" +#include "exfat_data.h" + +#include "exfat_cache.h" +#include "exfat_super.h" +#include "exfat_core.h" + +/*----------------------------------------------------------------------*/ +/* Global Variable Definitions */ +/*----------------------------------------------------------------------*/ + +#define sm_P(s) +#define sm_V(s) + +static s32 __FAT_read(struct super_block *sb, u32 loc, u32 *content); +static s32 __FAT_write(struct super_block *sb, u32 loc, u32 content); + +static BUF_CACHE_T *FAT_cache_find(struct super_block *sb, u32 sec); +static BUF_CACHE_T *FAT_cache_get(struct super_block *sb, u32 sec); +static void FAT_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp); +static void FAT_cache_remove_hash(BUF_CACHE_T *bp); + +static u8 *__buf_getblk(struct super_block *sb, u32 sec); + +static BUF_CACHE_T *buf_cache_find(struct super_block *sb, u32 sec); +static BUF_CACHE_T *buf_cache_get(struct super_block *sb, u32 sec); +static void buf_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp); +static void buf_cache_remove_hash(BUF_CACHE_T *bp); + +static void push_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list); +static void push_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list); +static void move_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list); +static void move_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list); + +/*======================================================================*/ +/* Cache Initialization Functions */ +/*======================================================================*/ + +s32 buf_init(struct super_block *sb) +{ + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + int i; + + /* LRU list */ + p_fs->FAT_cache_lru_list.next = p_fs->FAT_cache_lru_list.prev = &p_fs->FAT_cache_lru_list; + + for (i = 0; i < FAT_CACHE_SIZE; i++) { + p_fs->FAT_cache_array[i].drv = -1; + p_fs->FAT_cache_array[i].sec = ~0; + p_fs->FAT_cache_array[i].flag = 0; + p_fs->FAT_cache_array[i].buf_bh = NULL; + p_fs->FAT_cache_array[i].prev = p_fs->FAT_cache_array[i].next = NULL; + push_to_mru(&(p_fs->FAT_cache_array[i]), &p_fs->FAT_cache_lru_list); + } + + p_fs->buf_cache_lru_list.next = p_fs->buf_cache_lru_list.prev = &p_fs->buf_cache_lru_list; + + for (i = 0; i < BUF_CACHE_SIZE; i++) { + p_fs->buf_cache_array[i].drv = -1; + p_fs->buf_cache_array[i].sec = ~0; + p_fs->buf_cache_array[i].flag = 0; + p_fs->buf_cache_array[i].buf_bh = NULL; + p_fs->buf_cache_array[i].prev = p_fs->buf_cache_array[i].next = NULL; + push_to_mru(&(p_fs->buf_cache_array[i]), &p_fs->buf_cache_lru_list); + } + + /* HASH list */ + for (i = 0; i < FAT_CACHE_HASH_SIZE; i++) { + p_fs->FAT_cache_hash_list[i].drv = -1; + p_fs->FAT_cache_hash_list[i].sec = ~0; + p_fs->FAT_cache_hash_list[i].hash_next = p_fs->FAT_cache_hash_list[i].hash_prev = &(p_fs->FAT_cache_hash_list[i]); + } + + for (i = 0; i < FAT_CACHE_SIZE; i++) + FAT_cache_insert_hash(sb, &(p_fs->FAT_cache_array[i])); + + for (i = 0; i < BUF_CACHE_HASH_SIZE; i++) { + p_fs->buf_cache_hash_list[i].drv = -1; + p_fs->buf_cache_hash_list[i].sec = ~0; + p_fs->buf_cache_hash_list[i].hash_next = p_fs->buf_cache_hash_list[i].hash_prev = &(p_fs->buf_cache_hash_list[i]); + } + + for (i = 0; i < BUF_CACHE_SIZE; i++) + buf_cache_insert_hash(sb, &(p_fs->buf_cache_array[i])); + + return FFS_SUCCESS; +} /* end of buf_init */ + +s32 buf_shutdown(struct super_block *sb) +{ + return FFS_SUCCESS; +} /* end of buf_shutdown */ + +/*======================================================================*/ +/* FAT Read/Write Functions */ +/*======================================================================*/ + +/* in : sb, loc + * out: content + * returns 0 on success + * -1 on error + */ +s32 FAT_read(struct super_block *sb, u32 loc, u32 *content) +{ + s32 ret; + + sm_P(&f_sem); + + ret = __FAT_read(sb, loc, content); + + sm_V(&f_sem); + + return ret; +} /* end of FAT_read */ + +s32 FAT_write(struct super_block *sb, u32 loc, u32 content) +{ + s32 ret; + + sm_P(&f_sem); + + ret = __FAT_write(sb, loc, content); + + sm_V(&f_sem); + + return ret; +} /* end of FAT_write */ + +static s32 __FAT_read(struct super_block *sb, u32 loc, u32 *content) +{ + s32 off; + u32 sec, _content; + u8 *fat_sector, *fat_entry; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + if (p_fs->vol_type == FAT12) { + sec = p_fs->FAT1_start_sector + ((loc + (loc >> 1)) >> p_bd->sector_size_bits); + off = (loc + (loc >> 1)) & p_bd->sector_size_mask; + + if (off == (p_bd->sector_size-1)) { + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + _content = (u32) fat_sector[off]; + + fat_sector = FAT_getblk(sb, ++sec); + if (!fat_sector) + return -1; + + _content |= (u32) fat_sector[0] << 8; + } else { + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + fat_entry = &(fat_sector[off]); + _content = GET16(fat_entry); + } + + if (loc & 1) + _content >>= 4; + + _content &= 0x00000FFF; + + if (_content >= CLUSTER_16(0x0FF8)) { + *content = CLUSTER_32(~0); + return 0; + } else { + *content = CLUSTER_32(_content); + return 0; + } + } else if (p_fs->vol_type == FAT16) { + sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-1)); + off = (loc << 1) & p_bd->sector_size_mask; + + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + fat_entry = &(fat_sector[off]); + + _content = GET16_A(fat_entry); + + _content &= 0x0000FFFF; + + if (_content >= CLUSTER_16(0xFFF8)) { + *content = CLUSTER_32(~0); + return 0; + } else { + *content = CLUSTER_32(_content); + return 0; + } + } else if (p_fs->vol_type == FAT32) { + sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); + off = (loc << 2) & p_bd->sector_size_mask; + + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + fat_entry = &(fat_sector[off]); + + _content = GET32_A(fat_entry); + + _content &= 0x0FFFFFFF; + + if (_content >= CLUSTER_32(0x0FFFFFF8)) { + *content = CLUSTER_32(~0); + return 0; + } else { + *content = CLUSTER_32(_content); + return 0; + } + } else { + sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); + off = (loc << 2) & p_bd->sector_size_mask; + + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + fat_entry = &(fat_sector[off]); + _content = GET32_A(fat_entry); + + if (_content >= CLUSTER_32(0xFFFFFFF8)) { + *content = CLUSTER_32(~0); + return 0; + } else { + *content = CLUSTER_32(_content); + return 0; + } + } + + *content = CLUSTER_32(~0); + return 0; +} /* end of __FAT_read */ + +static s32 __FAT_write(struct super_block *sb, u32 loc, u32 content) +{ + s32 off; + u32 sec; + u8 *fat_sector, *fat_entry; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + if (p_fs->vol_type == FAT12) { + + content &= 0x00000FFF; + + sec = p_fs->FAT1_start_sector + ((loc + (loc >> 1)) >> p_bd->sector_size_bits); + off = (loc + (loc >> 1)) & p_bd->sector_size_mask; + + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + if (loc & 1) { /* odd */ + + content <<= 4; + + if (off == (p_bd->sector_size-1)) { + fat_sector[off] = (u8)(content | (fat_sector[off] & 0x0F)); + FAT_modify(sb, sec); + + fat_sector = FAT_getblk(sb, ++sec); + if (!fat_sector) + return -1; + + fat_sector[0] = (u8)(content >> 8); + } else { + fat_entry = &(fat_sector[off]); + content |= GET16(fat_entry) & 0x000F; + + SET16(fat_entry, content); + } + } else { /* even */ + fat_sector[off] = (u8)(content); + + if (off == (p_bd->sector_size-1)) { + fat_sector[off] = (u8)(content); + FAT_modify(sb, sec); + + fat_sector = FAT_getblk(sb, ++sec); + fat_sector[0] = (u8)((fat_sector[0] & 0xF0) | (content >> 8)); + } else { + fat_entry = &(fat_sector[off]); + content |= GET16(fat_entry) & 0xF000; + + SET16(fat_entry, content); + } + } + } + + else if (p_fs->vol_type == FAT16) { + + content &= 0x0000FFFF; + + sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-1)); + off = (loc << 1) & p_bd->sector_size_mask; + + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + fat_entry = &(fat_sector[off]); + + SET16_A(fat_entry, content); + } + + else if (p_fs->vol_type == FAT32) { + + content &= 0x0FFFFFFF; + + sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); + off = (loc << 2) & p_bd->sector_size_mask; + + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + fat_entry = &(fat_sector[off]); + + content |= GET32_A(fat_entry) & 0xF0000000; + + SET32_A(fat_entry, content); + } + + else { /* p_fs->vol_type == EXFAT */ + + sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); + off = (loc << 2) & p_bd->sector_size_mask; + + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + fat_entry = &(fat_sector[off]); + + SET32_A(fat_entry, content); + } + + FAT_modify(sb, sec); + return 0; +} /* end of __FAT_write */ + +u8 *FAT_getblk(struct super_block *sb, u32 sec) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + bp = FAT_cache_find(sb, sec); + if (bp != NULL) { + move_to_mru(bp, &p_fs->FAT_cache_lru_list); + return bp->buf_bh->b_data; + } + + bp = FAT_cache_get(sb, sec); + + FAT_cache_remove_hash(bp); + + bp->drv = p_fs->drv; + bp->sec = sec; + bp->flag = 0; + + FAT_cache_insert_hash(sb, bp); + + if (sector_read(sb, sec, &(bp->buf_bh), 1) != FFS_SUCCESS) { + FAT_cache_remove_hash(bp); + bp->drv = -1; + bp->sec = ~0; + bp->flag = 0; + bp->buf_bh = NULL; + + move_to_lru(bp, &p_fs->FAT_cache_lru_list); + return NULL; + } + + return bp->buf_bh->b_data; +} /* end of FAT_getblk */ + +void FAT_modify(struct super_block *sb, u32 sec) +{ + BUF_CACHE_T *bp; + + bp = FAT_cache_find(sb, sec); + if (bp != NULL) + sector_write(sb, sec, bp->buf_bh, 0); +} /* end of FAT_modify */ + +void FAT_release_all(struct super_block *sb) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + sm_P(&f_sem); + + bp = p_fs->FAT_cache_lru_list.next; + while (bp != &p_fs->FAT_cache_lru_list) { + if (bp->drv == p_fs->drv) { + bp->drv = -1; + bp->sec = ~0; + bp->flag = 0; + + if (bp->buf_bh) { + __brelse(bp->buf_bh); + bp->buf_bh = NULL; + } + } + bp = bp->next; + } + + sm_V(&f_sem); +} /* end of FAT_release_all */ + +void FAT_sync(struct super_block *sb) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + sm_P(&f_sem); + + bp = p_fs->FAT_cache_lru_list.next; + while (bp != &p_fs->FAT_cache_lru_list) { + if ((bp->drv == p_fs->drv) && (bp->flag & DIRTYBIT)) { + sync_dirty_buffer(bp->buf_bh); + bp->flag &= ~(DIRTYBIT); + } + bp = bp->next; + } + + sm_V(&f_sem); +} /* end of FAT_sync */ + +static BUF_CACHE_T *FAT_cache_find(struct super_block *sb, u32 sec) +{ + s32 off; + BUF_CACHE_T *bp, *hp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + off = (sec + (sec >> p_fs->sectors_per_clu_bits)) & (FAT_CACHE_HASH_SIZE - 1); + + hp = &(p_fs->FAT_cache_hash_list[off]); + for (bp = hp->hash_next; bp != hp; bp = bp->hash_next) { + if ((bp->drv == p_fs->drv) && (bp->sec == sec)) { + + WARN(!bp->buf_bh, "[EXFAT] FAT_cache has no bh. " + "It will make system panic.\n"); + + touch_buffer(bp->buf_bh); + return bp; + } + } + return NULL; +} /* end of FAT_cache_find */ + +static BUF_CACHE_T *FAT_cache_get(struct super_block *sb, u32 sec) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + bp = p_fs->FAT_cache_lru_list.prev; + + + move_to_mru(bp, &p_fs->FAT_cache_lru_list); + return bp; +} /* end of FAT_cache_get */ + +static void FAT_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp) +{ + s32 off; + BUF_CACHE_T *hp; + FS_INFO_T *p_fs; + + p_fs = &(EXFAT_SB(sb)->fs_info); + off = (bp->sec + (bp->sec >> p_fs->sectors_per_clu_bits)) & (FAT_CACHE_HASH_SIZE-1); + + hp = &(p_fs->FAT_cache_hash_list[off]); + bp->hash_next = hp->hash_next; + bp->hash_prev = hp; + hp->hash_next->hash_prev = bp; + hp->hash_next = bp; +} /* end of FAT_cache_insert_hash */ + +static void FAT_cache_remove_hash(BUF_CACHE_T *bp) +{ + (bp->hash_prev)->hash_next = bp->hash_next; + (bp->hash_next)->hash_prev = bp->hash_prev; +} /* end of FAT_cache_remove_hash */ + +/*======================================================================*/ +/* Buffer Read/Write Functions */ +/*======================================================================*/ + +u8 *buf_getblk(struct super_block *sb, u32 sec) +{ + u8 *buf; + + sm_P(&b_sem); + + buf = __buf_getblk(sb, sec); + + sm_V(&b_sem); + + return buf; +} /* end of buf_getblk */ + +static u8 *__buf_getblk(struct super_block *sb, u32 sec) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + bp = buf_cache_find(sb, sec); + if (bp != NULL) { + move_to_mru(bp, &p_fs->buf_cache_lru_list); + return bp->buf_bh->b_data; + } + + bp = buf_cache_get(sb, sec); + + buf_cache_remove_hash(bp); + + bp->drv = p_fs->drv; + bp->sec = sec; + bp->flag = 0; + + buf_cache_insert_hash(sb, bp); + + if (sector_read(sb, sec, &(bp->buf_bh), 1) != FFS_SUCCESS) { + buf_cache_remove_hash(bp); + bp->drv = -1; + bp->sec = ~0; + bp->flag = 0; + bp->buf_bh = NULL; + + move_to_lru(bp, &p_fs->buf_cache_lru_list); + return NULL; + } + + return bp->buf_bh->b_data; + +} /* end of __buf_getblk */ + +void buf_modify(struct super_block *sb, u32 sec) +{ + BUF_CACHE_T *bp; + + sm_P(&b_sem); + + bp = buf_cache_find(sb, sec); + if (likely(bp != NULL)) + sector_write(sb, sec, bp->buf_bh, 0); + + WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%u).\n", sec); + + sm_V(&b_sem); +} /* end of buf_modify */ + +void buf_lock(struct super_block *sb, u32 sec) +{ + BUF_CACHE_T *bp; + + sm_P(&b_sem); + + bp = buf_cache_find(sb, sec); + if (likely(bp != NULL)) + bp->flag |= LOCKBIT; + + WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%u).\n", sec); + + sm_V(&b_sem); +} /* end of buf_lock */ + +void buf_unlock(struct super_block *sb, u32 sec) +{ + BUF_CACHE_T *bp; + + sm_P(&b_sem); + + bp = buf_cache_find(sb, sec); + if (likely(bp != NULL)) + bp->flag &= ~(LOCKBIT); + + WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%u).\n", sec); + + sm_V(&b_sem); +} /* end of buf_unlock */ + +void buf_release(struct super_block *sb, u32 sec) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + sm_P(&b_sem); + + bp = buf_cache_find(sb, sec); + if (likely(bp != NULL)) { + bp->drv = -1; + bp->sec = ~0; + bp->flag = 0; + + if (bp->buf_bh) { + __brelse(bp->buf_bh); + bp->buf_bh = NULL; + } + + move_to_lru(bp, &p_fs->buf_cache_lru_list); + } + + sm_V(&b_sem); +} /* end of buf_release */ + +void buf_release_all(struct super_block *sb) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + sm_P(&b_sem); + + bp = p_fs->buf_cache_lru_list.next; + while (bp != &p_fs->buf_cache_lru_list) { + if (bp->drv == p_fs->drv) { + bp->drv = -1; + bp->sec = ~0; + bp->flag = 0; + + if (bp->buf_bh) { + __brelse(bp->buf_bh); + bp->buf_bh = NULL; + } + } + bp = bp->next; + } + + sm_V(&b_sem); +} /* end of buf_release_all */ + +void buf_sync(struct super_block *sb) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + sm_P(&b_sem); + + bp = p_fs->buf_cache_lru_list.next; + while (bp != &p_fs->buf_cache_lru_list) { + if ((bp->drv == p_fs->drv) && (bp->flag & DIRTYBIT)) { + sync_dirty_buffer(bp->buf_bh); + bp->flag &= ~(DIRTYBIT); + } + bp = bp->next; + } + + sm_V(&b_sem); +} /* end of buf_sync */ + +static BUF_CACHE_T *buf_cache_find(struct super_block *sb, u32 sec) +{ + s32 off; + BUF_CACHE_T *bp, *hp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + off = (sec + (sec >> p_fs->sectors_per_clu_bits)) & (BUF_CACHE_HASH_SIZE - 1); + + hp = &(p_fs->buf_cache_hash_list[off]); + for (bp = hp->hash_next; bp != hp; bp = bp->hash_next) { + if ((bp->drv == p_fs->drv) && (bp->sec == sec)) { + touch_buffer(bp->buf_bh); + return bp; + } + } + return NULL; +} /* end of buf_cache_find */ + +static BUF_CACHE_T *buf_cache_get(struct super_block *sb, u32 sec) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + bp = p_fs->buf_cache_lru_list.prev; + while (bp->flag & LOCKBIT) + bp = bp->prev; + + + move_to_mru(bp, &p_fs->buf_cache_lru_list); + return bp; +} /* end of buf_cache_get */ + +static void buf_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp) +{ + s32 off; + BUF_CACHE_T *hp; + FS_INFO_T *p_fs; + + p_fs = &(EXFAT_SB(sb)->fs_info); + off = (bp->sec + (bp->sec >> p_fs->sectors_per_clu_bits)) & (BUF_CACHE_HASH_SIZE-1); + + hp = &(p_fs->buf_cache_hash_list[off]); + bp->hash_next = hp->hash_next; + bp->hash_prev = hp; + hp->hash_next->hash_prev = bp; + hp->hash_next = bp; +} /* end of buf_cache_insert_hash */ + +static void buf_cache_remove_hash(BUF_CACHE_T *bp) +{ + (bp->hash_prev)->hash_next = bp->hash_next; + (bp->hash_next)->hash_prev = bp->hash_prev; +} /* end of buf_cache_remove_hash */ + +/*======================================================================*/ +/* Local Function Definitions */ +/*======================================================================*/ + +static void push_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list) +{ + bp->next = list->next; + bp->prev = list; + list->next->prev = bp; + list->next = bp; +} /* end of buf_cache_push_to_mru */ + +static void push_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list) +{ + bp->prev = list->prev; + bp->next = list; + list->prev->next = bp; + list->prev = bp; +} /* end of buf_cache_push_to_lru */ + +static void move_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list) +{ + bp->prev->next = bp->next; + bp->next->prev = bp->prev; + push_to_mru(bp, list); +} /* end of buf_cache_move_to_mru */ + +static void move_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list) +{ + bp->prev->next = bp->next; + bp->next->prev = bp->prev; + push_to_lru(bp, list); +} /* end of buf_cache_move_to_lru */ diff --git a/fs/exfat/exfat_cache.h b/fs/exfat/exfat_cache.h new file mode 100644 index 00000000..d82aad58 --- /dev/null +++ b/fs/exfat/exfat_cache.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_cache.h */ +/* PURPOSE : Header File for exFAT Cache Manager */ +/* (FAT Cache & Buffer Cache) */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Sung-Kwan Kim] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_CACHE_H +#define _EXFAT_CACHE_H + +#include +#include +#include "exfat_config.h" + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions */ +/*----------------------------------------------------------------------*/ + +#define LOCKBIT 0x01 +#define DIRTYBIT 0x02 + +/*----------------------------------------------------------------------*/ +/* Type Definitions */ +/*----------------------------------------------------------------------*/ + +typedef struct __BUF_CACHE_T { + struct __BUF_CACHE_T *next; + struct __BUF_CACHE_T *prev; + struct __BUF_CACHE_T *hash_next; + struct __BUF_CACHE_T *hash_prev; + s32 drv; + u32 sec; + u32 flag; + struct buffer_head *buf_bh; +} BUF_CACHE_T; + +/*----------------------------------------------------------------------*/ +/* External Function Declarations */ +/*----------------------------------------------------------------------*/ + +s32 buf_init(struct super_block *sb); +s32 buf_shutdown(struct super_block *sb); +s32 FAT_read(struct super_block *sb, u32 loc, u32 *content); +s32 FAT_write(struct super_block *sb, u32 loc, u32 content); +u8 *FAT_getblk(struct super_block *sb, u32 sec); +void FAT_modify(struct super_block *sb, u32 sec); +void FAT_release_all(struct super_block *sb); +void FAT_sync(struct super_block *sb); +u8 *buf_getblk(struct super_block *sb, u32 sec); +void buf_modify(struct super_block *sb, u32 sec); +void buf_lock(struct super_block *sb, u32 sec); +void buf_unlock(struct super_block *sb, u32 sec); +void buf_release(struct super_block *sb, u32 sec); +void buf_release_all(struct super_block *sb); +void buf_sync(struct super_block *sb); + +#endif /* _EXFAT_CACHE_H */ diff --git a/fs/exfat/exfat_config.h b/fs/exfat/exfat_config.h new file mode 100644 index 00000000..33c6525e --- /dev/null +++ b/fs/exfat/exfat_config.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_config.h */ +/* PURPOSE : Header File for exFAT Configuable Policies */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_CONFIG_H +#define _EXFAT_CONFIG_H + +/*======================================================================*/ +/* */ +/* FFS CONFIGURATIONS */ +/* (CHANGE THIS PART IF REQUIRED) */ +/* */ +/*======================================================================*/ + +/*----------------------------------------------------------------------*/ +/* Feature Config */ +/*----------------------------------------------------------------------*/ +#ifndef CONFIG_EXFAT_DISCARD +#define CONFIG_EXFAT_DISCARD 1 /* mount option -o discard support */ +#endif + +#ifndef CONFIG_EXFAT_DELAYED_SYNC +#define CONFIG_EXFAT_DELAYED_SYNC 0 +#endif + +#ifndef CONFIG_EXFAT_KERNEL_DEBUG +#define CONFIG_EXFAT_KERNEL_DEBUG 1 /* kernel debug features via ioctl */ +#endif + +#ifndef CONFIG_EXFAT_DEBUG_MSG +#define CONFIG_EXFAT_DEBUG_MSG 0 /* debugging message on/off */ +#endif + +#ifndef CONFIG_EXFAT_DEFAULT_CODEPAGE +#define CONFIG_EXFAT_DEFAULT_CODEPAGE 437 +#define CONFIG_EXFAT_DEFAULT_IOCHARSET "utf8" +#endif + +#endif /* _EXFAT_CONFIG_H */ diff --git a/fs/exfat/exfat_core.c b/fs/exfat/exfat_core.c new file mode 100644 index 00000000..2a2132cc --- /dev/null +++ b/fs/exfat/exfat_core.c @@ -0,0 +1,5522 @@ +/* Some of the source code in this file came from "linux/fs/fat/misc.c". */ +/* + * linux/fs/fat/misc.c + * + * Written 1992,1993 by Werner Almesberger + * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980 + * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru) + */ + +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_core.c */ +/* PURPOSE : exFAT File Manager */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#include +#include +#include + +#include "exfat_bitmap.h" +#include "exfat_config.h" +#include "exfat_data.h" +#include "exfat_oal.h" +#include "exfat_blkdev.h" +#include "exfat_cache.h" +#include "exfat_nls.h" +#include "exfat_api.h" +#include "exfat_super.h" +#include "exfat_core.h" + +#include + +static void __set_sb_dirty(struct super_block *sb) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) + sb->s_dirt = 1; +#else + struct exfat_sb_info *sbi = EXFAT_SB(sb); + sbi->s_dirt = 1; +#endif +} + +/*----------------------------------------------------------------------*/ +/* Global Variable Definitions */ +/*----------------------------------------------------------------------*/ + +extern u8 uni_upcase[]; + +/*----------------------------------------------------------------------*/ +/* Local Variable Definitions */ +/*----------------------------------------------------------------------*/ + +static u8 name_buf[MAX_PATH_LENGTH * MAX_CHARSET_SIZE]; + +static char *reserved_names[] = { + "AUX ", "CON ", "NUL ", "PRN ", + "COM1 ", "COM2 ", "COM3 ", "COM4 ", + "COM5 ", "COM6 ", "COM7 ", "COM8 ", "COM9 ", + "LPT1 ", "LPT2 ", "LPT3 ", "LPT4 ", + "LPT5 ", "LPT6 ", "LPT7 ", "LPT8 ", "LPT9 ", + NULL +}; + +static u8 free_bit[] = { + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, /* 0 ~ 19 */ + 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, /* 20 ~ 39 */ + 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, /* 40 ~ 59 */ + 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, /* 60 ~ 79 */ + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, /* 80 ~ 99 */ + 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, /* 100 ~ 119 */ + 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, /* 120 ~ 139 */ + 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, /* 140 ~ 159 */ + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, /* 160 ~ 179 */ + 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, /* 180 ~ 199 */ + 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, /* 200 ~ 219 */ + 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, /* 220 ~ 239 */ + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /* 240 ~ 254 */ +}; + +static u8 used_bit[] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, /* 0 ~ 19 */ + 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, /* 20 ~ 39 */ + 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, /* 40 ~ 59 */ + 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, /* 60 ~ 79 */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, /* 80 ~ 99 */ + 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, /* 100 ~ 119 */ + 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, /* 120 ~ 139 */ + 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 140 ~ 159 */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, /* 160 ~ 179 */ + 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, /* 180 ~ 199 */ + 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, /* 200 ~ 219 */ + 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 220 ~ 239 */ + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 /* 240 ~ 255 */ +}; + +/*======================================================================*/ +/* Global Function Definitions */ +/*======================================================================*/ + +/* ffsInit : roll back to the initial state of the file system */ +s32 ffsInit(void) +{ + s32 ret; + + ret = bdev_init(); + if (ret) + return ret; + + ret = fs_init(); + if (ret) + return ret; + + return FFS_SUCCESS; +} /* end of ffsInit */ + +/* ffsShutdown : make free all memory-alloced global buffers */ +s32 ffsShutdown(void) +{ + s32 ret; + ret = fs_shutdown(); + if (ret) + return ret; + + ret = bdev_shutdown(); + if (ret) + return ret; + + return FFS_SUCCESS; +} /* end of ffsShutdown */ + +/* ffsMountVol : mount the file system volume */ +s32 ffsMountVol(struct super_block *sb) +{ + int ret; + PBR_SECTOR_T *p_pbr; + struct buffer_head *tmp_bh = NULL; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + printk("[EXFAT] trying to mount...\n"); + + sm_init(&p_fs->v_sem); + p_fs->dev_ejected = FALSE; + + /* open the block device */ + if (bdev_open(sb)) + return FFS_MEDIAERR; + + if (p_bd->sector_size < sb->s_blocksize) + return FFS_MEDIAERR; + if (p_bd->sector_size > sb->s_blocksize) + sb_set_blocksize(sb, p_bd->sector_size); + + /* read Sector 0 */ + if (sector_read(sb, 0, &tmp_bh, 1) != FFS_SUCCESS) + return FFS_MEDIAERR; + + p_fs->PBR_sector = 0; + + p_pbr = (PBR_SECTOR_T *) tmp_bh->b_data; + + /* check the validity of PBR */ + if (GET16_A(p_pbr->signature) != PBR_SIGNATURE) { + brelse(tmp_bh); + bdev_close(sb); + return FFS_FORMATERR; + } + +#if 0 + /* fill fs_stuct */ + for (i = 0; i < 53; i++) + if (p_pbr->bpb[i]) + break; + + if (i < 53) { + if (GET16(p_pbr->bpb+11)) /* num_fat_sectors */ + ret = fat16_mount(sb, p_pbr); + else + ret = fat32_mount(sb, p_pbr); + #else + if (memcmp(p_pbr->oem_name, (u8*)"EXFAT", 3)) { + printk("[EXFAT] it don't have the exfat's flag!\n"); + ret = FFS_FORMATERR; + #endif + } else { + ret = exfat_mount(sb, p_pbr); + } + + brelse(tmp_bh); + + if (ret) { + bdev_close(sb); + return ret; + } + + if (p_fs->vol_type == EXFAT) { + ret = load_alloc_bitmap(sb); + if (ret) { + bdev_close(sb); + return ret; + } + ret = load_upcase_table(sb); + if (ret) { + free_alloc_bitmap(sb); + bdev_close(sb); + return ret; + } + } + + if (p_fs->dev_ejected) { + if (p_fs->vol_type == EXFAT) { + free_upcase_table(sb); + free_alloc_bitmap(sb); + } + bdev_close(sb); + return FFS_MEDIAERR; + } + + printk("[EXFAT] mounted successfully\n"); + + return FFS_SUCCESS; +} /* end of ffsMountVol */ + +/* ffsUmountVol : umount the file system volume */ +s32 ffsUmountVol(struct super_block *sb) +{ + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + printk("[EXFAT] trying to unmount...\n"); + + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); + + if (p_fs->vol_type == EXFAT) { + free_upcase_table(sb); + free_alloc_bitmap(sb); + } + + FAT_release_all(sb); + buf_release_all(sb); + + /* close the block device */ + bdev_close(sb); + + if (p_fs->dev_ejected) { + printk("[EXFAT] unmounted with media errors. " + "device's already ejected.\n"); + return FFS_MEDIAERR; + } + + printk("[EXFAT] unmounted successfully\n"); + + return FFS_SUCCESS; +} /* end of ffsUmountVol */ + +/* ffsGetVolInfo : get the information of a file system volume */ +s32 ffsGetVolInfo(struct super_block *sb, VOL_INFO_T *info) +{ + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (p_fs->used_clusters == (u32) ~0) + p_fs->used_clusters = p_fs->fs_func->count_used_clusters(sb); + + info->FatType = p_fs->vol_type; + info->ClusterSize = p_fs->cluster_size; + info->NumClusters = p_fs->num_clusters - 2; /* clu 0 & 1 */ + info->UsedClusters = p_fs->used_clusters; + info->FreeClusters = info->NumClusters - info->UsedClusters; + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsGetVolInfo */ + +/* ffsSyncVol : synchronize all file system volumes */ +s32 ffsSyncVol(struct super_block *sb, s32 do_sync) +{ + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* synchronize the file system */ + fs_sync(sb, do_sync); + fs_set_vol_flags(sb, VOL_CLEAN); + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsSyncVol */ + +/*----------------------------------------------------------------------*/ +/* File Operation Functions */ +/*----------------------------------------------------------------------*/ + +/* ffsLookupFile : lookup a file */ +s32 ffsLookupFile(struct inode *inode, char *path, FILE_ID_T *fid) +{ + s32 ret, dentry, num_entries; + CHAIN_T dir; + UNI_NAME_T uni_name; + DOS_NAME_T dos_name; + DENTRY_T *ep, *ep2; + ENTRY_SET_CACHE_T *es = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + DPRINTK("ffsLookupFile entered\n"); + + /* check the validity of directory name in the given pathname */ + ret = resolve_path(inode, path, &dir, &uni_name); + if (ret) + return ret; + + ret = get_num_entries_and_dos_name(sb, &dir, &uni_name, &num_entries, &dos_name); + if (ret) + return ret; + + /* search the file name for directories */ + dentry = p_fs->fs_func->find_dir_entry(sb, &dir, &uni_name, num_entries, &dos_name, TYPE_ALL); + if (dentry < -1) + return FFS_NOTFOUND; + + fid->dir.dir = dir.dir; + fid->dir.size = dir.size; + fid->dir.flags = dir.flags; + fid->entry = dentry; + + if (dentry == -1) { + fid->type = TYPE_DIR; + fid->rwoffset = 0; + fid->hint_last_off = -1; + + fid->attr = ATTR_SUBDIR; + fid->flags = 0x01; + fid->size = 0; + fid->start_clu = p_fs->root_dir; + } else { + if (p_fs->vol_type == EXFAT) { + es = get_entry_set_in_dir(sb, &dir, dentry, ES_2_ENTRIES, &ep); + if (!es) + return FFS_MEDIAERR; + ep2 = ep+1; + } else { + ep = get_entry_in_dir(sb, &dir, dentry, NULL); + if (!ep) + return FFS_MEDIAERR; + ep2 = ep; + } + + fid->type = p_fs->fs_func->get_entry_type(ep); + fid->rwoffset = 0; + fid->hint_last_off = -1; + fid->attr = p_fs->fs_func->get_entry_attr(ep); + + fid->size = p_fs->fs_func->get_entry_size(ep2); + if ((fid->type == TYPE_FILE) && (fid->size == 0)) { + fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; + fid->start_clu = CLUSTER_32(~0); + } else { + fid->flags = p_fs->fs_func->get_entry_flag(ep2); + fid->start_clu = p_fs->fs_func->get_entry_clu0(ep2); + } + + if (p_fs->vol_type == EXFAT) + release_entry_set(es); + } + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + DPRINTK("ffsLookupFile exited successfully\n"); + + return FFS_SUCCESS; +} /* end of ffsLookupFile */ + +/* ffsCreateFile : create a file */ +s32 ffsCreateFile(struct inode *inode, char *path, u8 mode, FILE_ID_T *fid) +{ + s32 ret/*, dentry*/; + CHAIN_T dir; + UNI_NAME_T uni_name; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of directory name in the given pathname */ + ret = resolve_path(inode, path, &dir, &uni_name); + if (ret) + return ret; + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* create a new file */ + ret = create_file(inode, &dir, &uni_name, mode, fid); + +#ifdef CONFIG_EXFAT_DELAYED_SYNC + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return ret; +} /* end of ffsCreateFile */ + +/* ffsReadFile : read data from a opened file */ +s32 ffsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount) +{ + s32 offset, sec_offset, clu_offset; + u32 clu, LogSector; + u64 oneblkread, read_bytes; + struct buffer_head *tmp_bh = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + /* check if the given file ID is opened */ + if (fid->type != TYPE_FILE) + return FFS_PERMISSIONERR; + + if (fid->rwoffset > fid->size) + fid->rwoffset = fid->size; + + if (count > (fid->size - fid->rwoffset)) + count = fid->size - fid->rwoffset; + + if (count == 0) { + if (rcount != NULL) + *rcount = 0; + return FFS_EOF; + } + + read_bytes = 0; + + while (count > 0) { + clu_offset = (s32)(fid->rwoffset >> p_fs->cluster_size_bits); + clu = fid->start_clu; + + if (fid->flags == 0x03) { + clu += clu_offset; + } else { + /* hint information */ + if ((clu_offset > 0) && (fid->hint_last_off > 0) && + (clu_offset >= fid->hint_last_off)) { + clu_offset -= fid->hint_last_off; + clu = fid->hint_last_clu; + } + + while (clu_offset > 0) { + /* clu = FAT_read(sb, clu); */ + if (FAT_read(sb, clu, &clu) == -1) + return FFS_MEDIAERR; + + clu_offset--; + } + } + + /* hint information */ + fid->hint_last_off = (s32)(fid->rwoffset >> p_fs->cluster_size_bits); + fid->hint_last_clu = clu; + + offset = (s32)(fid->rwoffset & (p_fs->cluster_size-1)); /* byte offset in cluster */ + sec_offset = offset >> p_bd->sector_size_bits; /* sector offset in cluster */ + offset &= p_bd->sector_size_mask; /* byte offset in sector */ + + LogSector = START_SECTOR(clu) + sec_offset; + + oneblkread = (u64)(p_bd->sector_size - offset); + if (oneblkread > count) + oneblkread = count; + + if ((offset == 0) && (oneblkread == p_bd->sector_size)) { + if (sector_read(sb, LogSector, &tmp_bh, 1) != FFS_SUCCESS) + goto err_out; + memcpy(((char *) buffer)+read_bytes, ((char *) tmp_bh->b_data), (s32) oneblkread); + } else { + if (sector_read(sb, LogSector, &tmp_bh, 1) != FFS_SUCCESS) + goto err_out; + memcpy(((char *) buffer)+read_bytes, ((char *) tmp_bh->b_data)+offset, (s32) oneblkread); + } + count -= oneblkread; + read_bytes += oneblkread; + fid->rwoffset += oneblkread; + } + brelse(tmp_bh); + +err_out: + /* set the size of read bytes */ + if (rcount != NULL) + *rcount = read_bytes; + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsReadFile */ + +/* ffsWriteFile : write data into a opened file */ +s32 ffsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount) +{ + s32 modified = FALSE, offset, sec_offset, clu_offset; + s32 num_clusters, num_alloc, num_alloced = (s32) ~0; + u32 clu, last_clu, LogSector, sector = 0; + u64 oneblkwrite, write_bytes; + CHAIN_T new_clu; + TIMESTAMP_T tm; + DENTRY_T *ep, *ep2; + ENTRY_SET_CACHE_T *es = NULL; + struct buffer_head *tmp_bh = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + /* check if the given file ID is opened */ + if (fid->type != TYPE_FILE) + return FFS_PERMISSIONERR; + + if (fid->rwoffset > fid->size) + fid->rwoffset = fid->size; + + if (count == 0) { + if (wcount != NULL) + *wcount = 0; + return FFS_SUCCESS; + } + + fs_set_vol_flags(sb, VOL_DIRTY); + + if (fid->size == 0) + num_clusters = 0; + else + num_clusters = (s32)((fid->size-1) >> p_fs->cluster_size_bits) + 1; + + write_bytes = 0; + + while (count > 0) { + clu_offset = (s32)(fid->rwoffset >> p_fs->cluster_size_bits); + clu = last_clu = fid->start_clu; + + if (fid->flags == 0x03) { + if ((clu_offset > 0) && (clu != CLUSTER_32(~0))) { + last_clu += clu_offset - 1; + + if (clu_offset == num_clusters) + clu = CLUSTER_32(~0); + else + clu += clu_offset; + } + } else { + /* hint information */ + if ((clu_offset > 0) && (fid->hint_last_off > 0) && + (clu_offset >= fid->hint_last_off)) { + clu_offset -= fid->hint_last_off; + clu = fid->hint_last_clu; + } + + while ((clu_offset > 0) && (clu != CLUSTER_32(~0))) { + last_clu = clu; + /* clu = FAT_read(sb, clu); */ + if (FAT_read(sb, clu, &clu) == -1) + return FFS_MEDIAERR; + + clu_offset--; + } + } + + if (clu == CLUSTER_32(~0)) { + num_alloc = (s32)((count-1) >> p_fs->cluster_size_bits) + 1; + new_clu.dir = (last_clu == CLUSTER_32(~0)) ? CLUSTER_32(~0) : last_clu+1; + new_clu.size = 0; + new_clu.flags = fid->flags; + + /* (1) allocate a chain of clusters */ + num_alloced = p_fs->fs_func->alloc_cluster(sb, num_alloc, &new_clu); + if (num_alloced == 0) + break; + else if (num_alloced < 0) + return FFS_MEDIAERR; + + /* (2) append to the FAT chain */ + if (last_clu == CLUSTER_32(~0)) { + if (new_clu.flags == 0x01) + fid->flags = 0x01; + fid->start_clu = new_clu.dir; + modified = TRUE; + } else { + if (new_clu.flags != fid->flags) { + exfat_chain_cont_cluster(sb, fid->start_clu, num_clusters); + fid->flags = 0x01; + modified = TRUE; + } + if (new_clu.flags == 0x01) + FAT_write(sb, last_clu, new_clu.dir); + } + + num_clusters += num_alloced; + clu = new_clu.dir; + } + + /* hint information */ + fid->hint_last_off = (s32)(fid->rwoffset >> p_fs->cluster_size_bits); + fid->hint_last_clu = clu; + + offset = (s32)(fid->rwoffset & (p_fs->cluster_size-1)); /* byte offset in cluster */ + sec_offset = offset >> p_bd->sector_size_bits; /* sector offset in cluster */ + offset &= p_bd->sector_size_mask; /* byte offset in sector */ + + LogSector = START_SECTOR(clu) + sec_offset; + + oneblkwrite = (u64)(p_bd->sector_size - offset); + if (oneblkwrite > count) + oneblkwrite = count; + + if ((offset == 0) && (oneblkwrite == p_bd->sector_size)) { + if (sector_read(sb, LogSector, &tmp_bh, 0) != FFS_SUCCESS) + goto err_out; + memcpy(((char *) tmp_bh->b_data), ((char *) buffer)+write_bytes, (s32) oneblkwrite); + if (sector_write(sb, LogSector, tmp_bh, 0) != FFS_SUCCESS) { + brelse(tmp_bh); + goto err_out; + } + } else { + if ((offset > 0) || ((fid->rwoffset+oneblkwrite) < fid->size)) { + if (sector_read(sb, LogSector, &tmp_bh, 1) != FFS_SUCCESS) + goto err_out; + } else { + if (sector_read(sb, LogSector, &tmp_bh, 0) != FFS_SUCCESS) + goto err_out; + } + + memcpy(((char *) tmp_bh->b_data)+offset, ((char *) buffer)+write_bytes, (s32) oneblkwrite); + if (sector_write(sb, LogSector, tmp_bh, 0) != FFS_SUCCESS) { + brelse(tmp_bh); + goto err_out; + } + } + + count -= oneblkwrite; + write_bytes += oneblkwrite; + fid->rwoffset += oneblkwrite; + + fid->attr |= ATTR_ARCHIVE; + + if (fid->size < fid->rwoffset) { + fid->size = fid->rwoffset; + modified = TRUE; + } + } + + brelse(tmp_bh); + + /* (3) update the direcoty entry */ + if (p_fs->vol_type == EXFAT) { + es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); + if (es == NULL) + goto err_out; + ep2 = ep+1; + } else { + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); + if (!ep) + goto err_out; + ep2 = ep; + } + + p_fs->fs_func->set_entry_time(ep, tm_current(&tm), TM_MODIFY); + p_fs->fs_func->set_entry_attr(ep, fid->attr); + + if (p_fs->vol_type != EXFAT) + buf_modify(sb, sector); + + if (modified) { + if (p_fs->fs_func->get_entry_flag(ep2) != fid->flags) + p_fs->fs_func->set_entry_flag(ep2, fid->flags); + + if (p_fs->fs_func->get_entry_size(ep2) != fid->size) + p_fs->fs_func->set_entry_size(ep2, fid->size); + + if (p_fs->fs_func->get_entry_clu0(ep2) != fid->start_clu) + p_fs->fs_func->set_entry_clu0(ep2, fid->start_clu); + + if (p_fs->vol_type != EXFAT) + buf_modify(sb, sector); + } + + if (p_fs->vol_type == EXFAT) { + update_dir_checksum_with_entry_set(sb, es); + release_entry_set(es); + } + +#ifdef CONFIG_EXFAT_DELAYED_SYNC + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + +err_out: + /* set the size of written bytes */ + if (wcount != NULL) + *wcount = write_bytes; + + if (num_alloced == 0) + return FFS_FULL; + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsWriteFile */ + +/* ffsTruncateFile : resize the file length */ +s32 ffsTruncateFile(struct inode *inode, u64 old_size, u64 new_size) +{ + s32 num_clusters; + u32 last_clu = CLUSTER_32(0), sector = 0; + CHAIN_T clu; + TIMESTAMP_T tm; + DENTRY_T *ep, *ep2; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + ENTRY_SET_CACHE_T *es = NULL; + + /* check if the given file ID is opened */ + if (fid->type != TYPE_FILE) + return FFS_PERMISSIONERR; + + if (fid->size != old_size) { + printk(KERN_ERR "[EXFAT] truncate : can't skip it because of " + "size-mismatch(old:%lld->fid:%lld).\n" + ,old_size, fid->size); + } + + if (old_size <= new_size) + return FFS_SUCCESS; + + fs_set_vol_flags(sb, VOL_DIRTY); + + clu.dir = fid->start_clu; + clu.size = (s32)((old_size-1) >> p_fs->cluster_size_bits) + 1; + clu.flags = fid->flags; + + if (new_size > 0) { + num_clusters = (s32)((new_size-1) >> p_fs->cluster_size_bits) + 1; + + if (clu.flags == 0x03) { + clu.dir += num_clusters; + } else { + while (num_clusters > 0) { + last_clu = clu.dir; + if (FAT_read(sb, clu.dir, &(clu.dir)) == -1) + return FFS_MEDIAERR; + num_clusters--; + } + } + + clu.size -= num_clusters; + } + + fid->size = new_size; + fid->attr |= ATTR_ARCHIVE; + if (new_size == 0) { + fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; + fid->start_clu = CLUSTER_32(~0); + } + + /* (1) update the directory entry */ + if (p_fs->vol_type == EXFAT) { + es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); + if (es == NULL) + return FFS_MEDIAERR; + ep2 = ep+1; + } else { + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); + if (!ep) + return FFS_MEDIAERR; + ep2 = ep; + } + + p_fs->fs_func->set_entry_time(ep, tm_current(&tm), TM_MODIFY); + p_fs->fs_func->set_entry_attr(ep, fid->attr); + + p_fs->fs_func->set_entry_size(ep2, new_size); + if (new_size == 0) { + p_fs->fs_func->set_entry_flag(ep2, 0x01); + p_fs->fs_func->set_entry_clu0(ep2, CLUSTER_32(0)); + } + + if (p_fs->vol_type != EXFAT) + buf_modify(sb, sector); + else { + update_dir_checksum_with_entry_set(sb, es); + release_entry_set(es); + } + + /* (2) cut off from the FAT chain */ + if (last_clu != CLUSTER_32(0)) { + if (fid->flags == 0x01) + FAT_write(sb, last_clu, CLUSTER_32(~0)); + } + + /* (3) free the clusters */ + p_fs->fs_func->free_cluster(sb, &clu, 0); + + /* hint information */ + fid->hint_last_off = -1; + if (fid->rwoffset > fid->size) + fid->rwoffset = fid->size; + +#ifdef CONFIG_EXFAT_DELAYED_SYNC + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsTruncateFile */ + +static void update_parent_info(FILE_ID_T *fid, struct inode *parent_inode) +{ + FS_INFO_T *p_fs = &(EXFAT_SB(parent_inode->i_sb)->fs_info); + FILE_ID_T *parent_fid = &(EXFAT_I(parent_inode)->fid); + + if (unlikely((parent_fid->flags != fid->dir.flags) + || (parent_fid->size != (fid->dir.size<cluster_size_bits)) + || (parent_fid->start_clu != fid->dir.dir))) { + + fid->dir.dir = parent_fid->start_clu; + fid->dir.flags = parent_fid->flags; + fid->dir.size = ((parent_fid->size + (p_fs->cluster_size-1)) + >> p_fs->cluster_size_bits); + } +} + +/* ffsMoveFile : move(rename) a old file into a new file */ +s32 ffsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry) +{ + s32 ret; + s32 dentry; + CHAIN_T olddir, newdir; + CHAIN_T *p_dir = NULL; + UNI_NAME_T uni_name; + DENTRY_T *ep; + struct super_block *sb = old_parent_inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + u8 *new_path = (u8 *) new_dentry->d_name.name; + struct inode *new_inode = new_dentry->d_inode; + int num_entries; + FILE_ID_T *new_fid = NULL; + s32 new_entry = 0; + + /* check the validity of pointer parameters */ + if ((new_path == NULL) || (*new_path == '\0')) + return FFS_ERROR; + + update_parent_info(fid, old_parent_inode); + + olddir.dir = fid->dir.dir; + olddir.size = fid->dir.size; + olddir.flags = fid->dir.flags; + + dentry = fid->entry; + + /* check if the old file is "." or ".." */ + if (p_fs->vol_type != EXFAT) { + if ((olddir.dir != p_fs->root_dir) && (dentry < 2)) + return FFS_PERMISSIONERR; + } + + ep = get_entry_in_dir(sb, &olddir, dentry, NULL); + if (!ep) + return FFS_MEDIAERR; + + if (p_fs->fs_func->get_entry_attr(ep) & ATTR_READONLY) + return FFS_PERMISSIONERR; + + /* check whether new dir is existing directory and empty */ + if (new_inode) { + u32 entry_type; + + ret = FFS_MEDIAERR; + new_fid = &EXFAT_I(new_inode)->fid; + + update_parent_info(new_fid, new_parent_inode); + + p_dir = &(new_fid->dir); + new_entry = new_fid->entry; + ep = get_entry_in_dir(sb, p_dir, new_entry, NULL); + if (!ep) + goto out; + + entry_type = p_fs->fs_func->get_entry_type(ep); + + if (entry_type == TYPE_DIR) { + CHAIN_T new_clu; + new_clu.dir = new_fid->start_clu; + new_clu.size = (s32)((new_fid->size-1) >> p_fs->cluster_size_bits) + 1; + new_clu.flags = new_fid->flags; + + if (!is_dir_empty(sb, &new_clu)) + return FFS_FILEEXIST; + } + } + + /* check the validity of directory name in the given new pathname */ + ret = resolve_path(new_parent_inode, new_path, &newdir, &uni_name); + if (ret) + return ret; + + fs_set_vol_flags(sb, VOL_DIRTY); + + if (olddir.dir == newdir.dir) + ret = rename_file(new_parent_inode, &olddir, dentry, &uni_name, fid); + else + ret = move_file(new_parent_inode, &olddir, dentry, &newdir, &uni_name, fid); + + if ((ret == FFS_SUCCESS) && new_inode) { + /* delete entries of new_dir */ + ep = get_entry_in_dir(sb, p_dir, new_entry, NULL); + if (!ep) + goto out; + + num_entries = p_fs->fs_func->count_ext_entries(sb, p_dir, new_entry, ep); + if (num_entries < 0) + goto out; + p_fs->fs_func->delete_dir_entry(sb, p_dir, new_entry, 0, num_entries+1); + } +out: +#ifdef CONFIG_EXFAT_DELAYED_SYNC + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return ret; +} /* end of ffsMoveFile */ + +/* ffsRemoveFile : remove a file */ +s32 ffsRemoveFile(struct inode *inode, FILE_ID_T *fid) +{ + s32 dentry; + CHAIN_T dir, clu_to_free; + DENTRY_T *ep; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + dir.dir = fid->dir.dir; + dir.size = fid->dir.size; + dir.flags = fid->dir.flags; + + dentry = fid->entry; + + ep = get_entry_in_dir(sb, &dir, dentry, NULL); + if (!ep) + return FFS_MEDIAERR; + + if (p_fs->fs_func->get_entry_attr(ep) & ATTR_READONLY) + return FFS_PERMISSIONERR; + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* (1) update the directory entry */ + remove_file(inode, &dir, dentry); + + clu_to_free.dir = fid->start_clu; + clu_to_free.size = (s32)((fid->size-1) >> p_fs->cluster_size_bits) + 1; + clu_to_free.flags = fid->flags; + + /* (2) free the clusters */ + p_fs->fs_func->free_cluster(sb, &clu_to_free, 0); + + fid->size = 0; + fid->start_clu = CLUSTER_32(~0); + fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; + +#ifdef CONFIG_EXFAT_DELAYED_SYNC + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsRemoveFile */ + +/* ffsSetAttr : set the attribute of a given file */ +s32 ffsSetAttr(struct inode *inode, u32 attr) +{ + u32 type, sector = 0; + DENTRY_T *ep; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + u8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0; + ENTRY_SET_CACHE_T *es = NULL; + + if (fid->attr == attr) { + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + return FFS_SUCCESS; + } + + if (is_dir) { + if ((fid->dir.dir == p_fs->root_dir) && + (fid->entry == -1)) { + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + return FFS_SUCCESS; + } + } + + /* get the directory entry of given file */ + if (p_fs->vol_type == EXFAT) { + es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); + if (es == NULL) + return FFS_MEDIAERR; + } else { + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); + if (!ep) + return FFS_MEDIAERR; + } + + type = p_fs->fs_func->get_entry_type(ep); + + if (((type == TYPE_FILE) && (attr & ATTR_SUBDIR)) || + ((type == TYPE_DIR) && (!(attr & ATTR_SUBDIR)))) { + s32 err; + if (p_fs->dev_ejected) + err = FFS_MEDIAERR; + else + err = FFS_ERROR; + + if (p_fs->vol_type == EXFAT) + release_entry_set(es); + return err; + } + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* set the file attribute */ + fid->attr = attr; + p_fs->fs_func->set_entry_attr(ep, attr); + + if (p_fs->vol_type != EXFAT) + buf_modify(sb, sector); + else { + update_dir_checksum_with_entry_set(sb, es); + release_entry_set(es); + } + +#ifdef CONFIG_EXFAT_DELAYED_SYNC + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsSetAttr */ + +/* ffsGetStat : get the information of a given file */ +s32 ffsGetStat(struct inode *inode, DIR_ENTRY_T *info) +{ + u32 sector = 0; + s32 count; + CHAIN_T dir; + UNI_NAME_T uni_name; + TIMESTAMP_T tm; + DENTRY_T *ep, *ep2; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + ENTRY_SET_CACHE_T *es = NULL; + u8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0; + + DPRINTK("ffsGetStat entered\n"); + + if (is_dir) { + if ((fid->dir.dir == p_fs->root_dir) && + (fid->entry == -1)) { + info->Attr = ATTR_SUBDIR; + memset((char *) &info->CreateTimestamp, 0, sizeof(DATE_TIME_T)); + memset((char *) &info->ModifyTimestamp, 0, sizeof(DATE_TIME_T)); + memset((char *) &info->AccessTimestamp, 0, sizeof(DATE_TIME_T)); + strcpy(info->ShortName, "."); + strcpy(info->Name, "."); + + dir.dir = p_fs->root_dir; + dir.flags = 0x01; + + if (p_fs->root_dir == CLUSTER_32(0)) /* FAT16 root_dir */ + info->Size = p_fs->dentries_in_root << DENTRY_SIZE_BITS; + else + info->Size = count_num_clusters(sb, &dir) << p_fs->cluster_size_bits; + + count = count_dos_name_entries(sb, &dir, TYPE_DIR); + if (count < 0) + return FFS_MEDIAERR; + info->NumSubdirs = count; + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + return FFS_SUCCESS; + } + } + + /* get the directory entry of given file or directory */ + if (p_fs->vol_type == EXFAT) { + es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_2_ENTRIES, &ep); + if (es == NULL) + return FFS_MEDIAERR; + ep2 = ep+1; + } else { + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); + if (!ep) + return FFS_MEDIAERR; + ep2 = ep; + buf_lock(sb, sector); + } + + /* set FILE_INFO structure using the acquired DENTRY_T */ + info->Attr = p_fs->fs_func->get_entry_attr(ep); + + p_fs->fs_func->get_entry_time(ep, &tm, TM_CREATE); + info->CreateTimestamp.Year = tm.year; + info->CreateTimestamp.Month = tm.mon; + info->CreateTimestamp.Day = tm.day; + info->CreateTimestamp.Hour = tm.hour; + info->CreateTimestamp.Minute = tm.min; + info->CreateTimestamp.Second = tm.sec; + info->CreateTimestamp.MilliSecond = 0; + + p_fs->fs_func->get_entry_time(ep, &tm, TM_MODIFY); + info->ModifyTimestamp.Year = tm.year; + info->ModifyTimestamp.Month = tm.mon; + info->ModifyTimestamp.Day = tm.day; + info->ModifyTimestamp.Hour = tm.hour; + info->ModifyTimestamp.Minute = tm.min; + info->ModifyTimestamp.Second = tm.sec; + info->ModifyTimestamp.MilliSecond = 0; + + memset((char *) &info->AccessTimestamp, 0, sizeof(DATE_TIME_T)); + + *(uni_name.name) = 0x0; + /* XXX this is very bad for exfat cuz name is already included in es. + API should be revised */ + p_fs->fs_func->get_uni_name_from_ext_entry(sb, &(fid->dir), fid->entry, uni_name.name); + if (*(uni_name.name) == 0x0 && p_fs->vol_type != EXFAT) + get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x1); + nls_uniname_to_cstring(sb, info->Name, &uni_name); + + if (p_fs->vol_type == EXFAT) { + info->NumSubdirs = 2; + } else { + buf_unlock(sb, sector); + get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x0); + nls_uniname_to_cstring(sb, info->ShortName, &uni_name); + info->NumSubdirs = 0; + } + + info->Size = p_fs->fs_func->get_entry_size(ep2); + + if (p_fs->vol_type == EXFAT) + release_entry_set(es); + + if (is_dir) { + dir.dir = fid->start_clu; + dir.flags = 0x01; + + if (info->Size == 0) + info->Size = (u64) count_num_clusters(sb, &dir) << p_fs->cluster_size_bits; + + count = count_dos_name_entries(sb, &dir, TYPE_DIR); + if (count < 0) + return FFS_MEDIAERR; + info->NumSubdirs += count; + } + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + DPRINTK("ffsGetStat exited successfully\n"); + return FFS_SUCCESS; +} /* end of ffsGetStat */ + +/* ffsSetStat : set the information of a given file */ +s32 ffsSetStat(struct inode *inode, DIR_ENTRY_T *info) +{ + u32 sector = 0; + TIMESTAMP_T tm; + DENTRY_T *ep, *ep2; + ENTRY_SET_CACHE_T *es = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + u8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0; + + if (is_dir) { + if ((fid->dir.dir == p_fs->root_dir) && + (fid->entry == -1)) { + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + return FFS_SUCCESS; + } + } + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* get the directory entry of given file or directory */ + if (p_fs->vol_type == EXFAT) { + es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); + if (es == NULL) + return FFS_MEDIAERR; + ep2 = ep+1; + } else { + /* for other than exfat */ + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); + if (!ep) + return FFS_MEDIAERR; + ep2 = ep; + } + + + p_fs->fs_func->set_entry_attr(ep, info->Attr); + + /* set FILE_INFO structure using the acquired DENTRY_T */ + tm.sec = info->CreateTimestamp.Second; + tm.min = info->CreateTimestamp.Minute; + tm.hour = info->CreateTimestamp.Hour; + tm.day = info->CreateTimestamp.Day; + tm.mon = info->CreateTimestamp.Month; + tm.year = info->CreateTimestamp.Year; + p_fs->fs_func->set_entry_time(ep, &tm, TM_CREATE); + + tm.sec = info->ModifyTimestamp.Second; + tm.min = info->ModifyTimestamp.Minute; + tm.hour = info->ModifyTimestamp.Hour; + tm.day = info->ModifyTimestamp.Day; + tm.mon = info->ModifyTimestamp.Month; + tm.year = info->ModifyTimestamp.Year; + p_fs->fs_func->set_entry_time(ep, &tm, TM_MODIFY); + + + p_fs->fs_func->set_entry_size(ep2, info->Size); + + if (p_fs->vol_type != EXFAT) { + buf_modify(sb, sector); + } else { + update_dir_checksum_with_entry_set(sb, es); + release_entry_set(es); + } + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsSetStat */ + +s32 ffsMapCluster(struct inode *inode, s32 clu_offset, u32 *clu) +{ + s32 num_clusters, num_alloced, modified = FALSE; + u32 last_clu, sector = 0; + CHAIN_T new_clu; + DENTRY_T *ep; + ENTRY_SET_CACHE_T *es = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + + fid->rwoffset = (s64)(clu_offset) << p_fs->cluster_size_bits; + + if (EXFAT_I(inode)->mmu_private == 0) + num_clusters = 0; + else + num_clusters = (s32)((EXFAT_I(inode)->mmu_private-1) >> p_fs->cluster_size_bits) + 1; + + *clu = last_clu = fid->start_clu; + + if (fid->flags == 0x03) { + if ((clu_offset > 0) && (*clu != CLUSTER_32(~0))) { + last_clu += clu_offset - 1; + + if (clu_offset == num_clusters) + *clu = CLUSTER_32(~0); + else + *clu += clu_offset; + } + } else { + /* hint information */ + if ((clu_offset > 0) && (fid->hint_last_off > 0) && + (clu_offset >= fid->hint_last_off)) { + clu_offset -= fid->hint_last_off; + *clu = fid->hint_last_clu; + } + + while ((clu_offset > 0) && (*clu != CLUSTER_32(~0))) { + last_clu = *clu; + if (FAT_read(sb, *clu, clu) == -1) + return FFS_MEDIAERR; + clu_offset--; + } + } + + if (*clu == CLUSTER_32(~0)) { + fs_set_vol_flags(sb, VOL_DIRTY); + + new_clu.dir = (last_clu == CLUSTER_32(~0)) ? CLUSTER_32(~0) : last_clu+1; + new_clu.size = 0; + new_clu.flags = fid->flags; + + /* (1) allocate a cluster */ + num_alloced = p_fs->fs_func->alloc_cluster(sb, 1, &new_clu); + if (num_alloced < 0) + return FFS_MEDIAERR; + else if (num_alloced == 0) + return FFS_FULL; + + /* (2) append to the FAT chain */ + if (last_clu == CLUSTER_32(~0)) { + if (new_clu.flags == 0x01) + fid->flags = 0x01; + fid->start_clu = new_clu.dir; + modified = TRUE; + } else { + if (new_clu.flags != fid->flags) { + exfat_chain_cont_cluster(sb, fid->start_clu, num_clusters); + fid->flags = 0x01; + modified = TRUE; + } + if (new_clu.flags == 0x01) + FAT_write(sb, last_clu, new_clu.dir); + } + + num_clusters += num_alloced; + *clu = new_clu.dir; + + if (p_fs->vol_type == EXFAT) { + es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); + if (es == NULL) + return FFS_MEDIAERR; + /* get stream entry */ + ep++; + } + + /* (3) update directory entry */ + if (modified) { + if (p_fs->vol_type != EXFAT) { + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); + if (!ep) + return FFS_MEDIAERR; + } + + if (p_fs->fs_func->get_entry_flag(ep) != fid->flags) + p_fs->fs_func->set_entry_flag(ep, fid->flags); + + if (p_fs->fs_func->get_entry_clu0(ep) != fid->start_clu) + p_fs->fs_func->set_entry_clu0(ep, fid->start_clu); + + if (p_fs->vol_type != EXFAT) + buf_modify(sb, sector); + } + + if (p_fs->vol_type == EXFAT) { + update_dir_checksum_with_entry_set(sb, es); + release_entry_set(es); + } + + /* add number of new blocks to inode */ + inode->i_blocks += num_alloced << (p_fs->cluster_size_bits - 9); + } + + /* hint information */ + fid->hint_last_off = (s32)(fid->rwoffset >> p_fs->cluster_size_bits); + fid->hint_last_clu = *clu; + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsMapCluster */ + +/*----------------------------------------------------------------------*/ +/* Directory Operation Functions */ +/*----------------------------------------------------------------------*/ + +/* ffsCreateDir : create(make) a directory */ +s32 ffsCreateDir(struct inode *inode, char *path, FILE_ID_T *fid) +{ + s32 ret/*, dentry*/; + CHAIN_T dir; + UNI_NAME_T uni_name; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + DPRINTK("ffsCreateDir entered\n"); + + /* check the validity of directory name in the given old pathname */ + ret = resolve_path(inode, path, &dir, &uni_name); + if (ret) + return ret; + + fs_set_vol_flags(sb, VOL_DIRTY); + + ret = create_dir(inode, &dir, &uni_name, fid); + +#ifdef CONFIG_EXFAT_DELAYED_SYNC + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return ret; +} /* end of ffsCreateDir */ + +/* ffsReadDir : read a directory entry from the opened directory */ +s32 ffsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry) +{ + int i, dentry, clu_offset; + s32 dentries_per_clu, dentries_per_clu_bits = 0; + u32 type, sector; + CHAIN_T dir, clu; + UNI_NAME_T uni_name; + TIMESTAMP_T tm; + DENTRY_T *ep; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + + /* check if the given file ID is opened */ + if (fid->type != TYPE_DIR) + return FFS_PERMISSIONERR; + + if (fid->entry == -1) { + dir.dir = p_fs->root_dir; + dir.flags = 0x01; + } else { + dir.dir = fid->start_clu; + dir.size = (s32)(fid->size >> p_fs->cluster_size_bits); + dir.flags = fid->flags; + } + + dentry = (s32) fid->rwoffset; + + if (dir.dir == CLUSTER_32(0)) { /* FAT16 root_dir */ + dentries_per_clu = p_fs->dentries_in_root; + + if (dentry == dentries_per_clu) { + clu.dir = CLUSTER_32(~0); + } else { + clu.dir = dir.dir; + clu.size = dir.size; + clu.flags = dir.flags; + } + } else { + dentries_per_clu = p_fs->dentries_per_clu; + dentries_per_clu_bits = ilog2(dentries_per_clu); + + clu_offset = dentry >> dentries_per_clu_bits; + clu.dir = dir.dir; + clu.size = dir.size; + clu.flags = dir.flags; + + if (clu.flags == 0x03) { + clu.dir += clu_offset; + clu.size -= clu_offset; + } else { + /* hint_information */ + if ((clu_offset > 0) && (fid->hint_last_off > 0) && + (clu_offset >= fid->hint_last_off)) { + clu_offset -= fid->hint_last_off; + clu.dir = fid->hint_last_clu; + } + + while (clu_offset > 0) { + /* clu.dir = FAT_read(sb, clu.dir); */ + if (FAT_read(sb, clu.dir, &(clu.dir)) == -1) + return FFS_MEDIAERR; + + clu_offset--; + } + } + } + + while (clu.dir != CLUSTER_32(~0)) { + if (p_fs->dev_ejected) + break; + + if (dir.dir == CLUSTER_32(0)) /* FAT16 root_dir */ + i = dentry % dentries_per_clu; + else + i = dentry & (dentries_per_clu-1); + + for ( ; i < dentries_per_clu; i++, dentry++) { + ep = get_entry_in_dir(sb, &clu, i, §or); + if (!ep) + return FFS_MEDIAERR; + + type = p_fs->fs_func->get_entry_type(ep); + + if (type == TYPE_UNUSED) + break; + + if ((type != TYPE_FILE) && (type != TYPE_DIR)) + continue; + + buf_lock(sb, sector); + dir_entry->Attr = p_fs->fs_func->get_entry_attr(ep); + + p_fs->fs_func->get_entry_time(ep, &tm, TM_CREATE); + dir_entry->CreateTimestamp.Year = tm.year; + dir_entry->CreateTimestamp.Month = tm.mon; + dir_entry->CreateTimestamp.Day = tm.day; + dir_entry->CreateTimestamp.Hour = tm.hour; + dir_entry->CreateTimestamp.Minute = tm.min; + dir_entry->CreateTimestamp.Second = tm.sec; + dir_entry->CreateTimestamp.MilliSecond = 0; + + p_fs->fs_func->get_entry_time(ep, &tm, TM_MODIFY); + dir_entry->ModifyTimestamp.Year = tm.year; + dir_entry->ModifyTimestamp.Month = tm.mon; + dir_entry->ModifyTimestamp.Day = tm.day; + dir_entry->ModifyTimestamp.Hour = tm.hour; + dir_entry->ModifyTimestamp.Minute = tm.min; + dir_entry->ModifyTimestamp.Second = tm.sec; + dir_entry->ModifyTimestamp.MilliSecond = 0; + + memset((char *) &dir_entry->AccessTimestamp, 0, sizeof(DATE_TIME_T)); + + *(uni_name.name) = 0x0; + p_fs->fs_func->get_uni_name_from_ext_entry(sb, &dir, dentry, uni_name.name); + if (*(uni_name.name) == 0x0 && p_fs->vol_type != EXFAT) + get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x1); + nls_uniname_to_cstring(sb, dir_entry->Name, &uni_name); + buf_unlock(sb, sector); + + if (p_fs->vol_type == EXFAT) { + ep = get_entry_in_dir(sb, &clu, i+1, NULL); + if (!ep) + return FFS_MEDIAERR; + } else { + get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x0); + nls_uniname_to_cstring(sb, dir_entry->ShortName, &uni_name); + } + + dir_entry->Size = p_fs->fs_func->get_entry_size(ep); + + /* hint information */ + if (dir.dir == CLUSTER_32(0)) { /* FAT16 root_dir */ + } else { + fid->hint_last_off = dentry >> dentries_per_clu_bits; + fid->hint_last_clu = clu.dir; + } + + fid->rwoffset = (s64) ++dentry; + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; + } + + if (dir.dir == CLUSTER_32(0)) + break; /* FAT16 root_dir */ + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUSTER_32(~0); + } else { + /* clu.dir = FAT_read(sb, clu.dir); */ + if (FAT_read(sb, clu.dir, &(clu.dir)) == -1) + return FFS_MEDIAERR; + } + } + + *(dir_entry->Name) = '\0'; + + fid->rwoffset = (s64) ++dentry; + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsReadDir */ + +/* ffsRemoveDir : remove a directory */ +s32 ffsRemoveDir(struct inode *inode, FILE_ID_T *fid) +{ + s32 dentry; + CHAIN_T dir, clu_to_free; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + dir.dir = fid->dir.dir; + dir.size = fid->dir.size; + dir.flags = fid->dir.flags; + + dentry = fid->entry; + + /* check if the file is "." or ".." */ + if (p_fs->vol_type != EXFAT) { + if ((dir.dir != p_fs->root_dir) && (dentry < 2)) + return FFS_PERMISSIONERR; + } + + clu_to_free.dir = fid->start_clu; + clu_to_free.size = (s32)((fid->size-1) >> p_fs->cluster_size_bits) + 1; + clu_to_free.flags = fid->flags; + + if (!is_dir_empty(sb, &clu_to_free)) + return FFS_FILEEXIST; + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* (1) update the directory entry */ + remove_file(inode, &dir, dentry); + + /* (2) free the clusters */ + p_fs->fs_func->free_cluster(sb, &clu_to_free, 1); + + fid->size = 0; + fid->start_clu = CLUSTER_32(~0); + fid->flags = (p_fs->vol_type == EXFAT)? 0x03: 0x01; + +#ifdef CONFIG_EXFAT_DELAYED_SYNC + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsRemoveDir */ + +/*======================================================================*/ +/* Local Function Definitions */ +/*======================================================================*/ + +/* + * File System Management Functions + */ + +s32 fs_init(void) +{ + /* critical check for system requirement on size of DENTRY_T structure */ + if (sizeof(DENTRY_T) != DENTRY_SIZE) + return FFS_ALIGNMENTERR; + + if (sizeof(DOS_DENTRY_T) != DENTRY_SIZE) + return FFS_ALIGNMENTERR; + + if (sizeof(EXT_DENTRY_T) != DENTRY_SIZE) + return FFS_ALIGNMENTERR; + + if (sizeof(FILE_DENTRY_T) != DENTRY_SIZE) + return FFS_ALIGNMENTERR; + + if (sizeof(STRM_DENTRY_T) != DENTRY_SIZE) + return FFS_ALIGNMENTERR; + + if (sizeof(NAME_DENTRY_T) != DENTRY_SIZE) + return FFS_ALIGNMENTERR; + + if (sizeof(BMAP_DENTRY_T) != DENTRY_SIZE) + return FFS_ALIGNMENTERR; + + if (sizeof(CASE_DENTRY_T) != DENTRY_SIZE) + return FFS_ALIGNMENTERR; + + if (sizeof(VOLM_DENTRY_T) != DENTRY_SIZE) + return FFS_ALIGNMENTERR; + + return FFS_SUCCESS; +} /* end of fs_init */ + +s32 fs_shutdown(void) +{ + return FFS_SUCCESS; +} /* end of fs_shutdown */ + +void fs_set_vol_flags(struct super_block *sb, u32 new_flag) +{ + PBR_SECTOR_T *p_pbr; + BPBEX_T *p_bpb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (p_fs->vol_flag == new_flag) + return; + + p_fs->vol_flag = new_flag; + + if (p_fs->vol_type == EXFAT) { + if (p_fs->pbr_bh == NULL) { + if (sector_read(sb, p_fs->PBR_sector, &(p_fs->pbr_bh), 1) != FFS_SUCCESS) + return; + } + + p_pbr = (PBR_SECTOR_T *) p_fs->pbr_bh->b_data; + p_bpb = (BPBEX_T *) p_pbr->bpb; + SET16(p_bpb->vol_flags, (u16) new_flag); + + /* XXX duyoung + what can we do here? (cuz fs_set_vol_flags() is void) */ + if ((new_flag == VOL_DIRTY) && (!buffer_dirty(p_fs->pbr_bh))) + sector_write(sb, p_fs->PBR_sector, p_fs->pbr_bh, 1); + else + sector_write(sb, p_fs->PBR_sector, p_fs->pbr_bh, 0); + } +} /* end of fs_set_vol_flags */ + +void fs_sync(struct super_block *sb, s32 do_sync) +{ + if (do_sync) + bdev_sync(sb); +} /* end of fs_sync */ + +void fs_error(struct super_block *sb) +{ + struct exfat_mount_options *opts = &EXFAT_SB(sb)->options; + + if (opts->errors == EXFAT_ERRORS_PANIC) + panic("[EXFAT] Filesystem panic from previous error\n"); + else if ((opts->errors == EXFAT_ERRORS_RO) && !(sb->s_flags & MS_RDONLY)) { + sb->s_flags |= MS_RDONLY; + printk(KERN_ERR "[EXFAT] Filesystem has been set read-only\n"); + } +} + +/* + * Cluster Management Functions + */ + +s32 clear_cluster(struct super_block *sb, u32 clu) +{ + u32 s, n; + s32 ret = FFS_SUCCESS; + struct buffer_head *tmp_bh = NULL; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + if (clu == CLUSTER_32(0)) { /* FAT16 root_dir */ + s = p_fs->root_start_sector; + n = p_fs->data_start_sector; + } else { + s = START_SECTOR(clu); + n = s + p_fs->sectors_per_clu; + } + + for (; s < n; s++) { + ret = sector_read(sb, s, &tmp_bh, 0); + if (ret != FFS_SUCCESS) + return ret; + + memset((char *) tmp_bh->b_data, 0x0, p_bd->sector_size); + ret = sector_write(sb, s, tmp_bh, 0); + if (ret != FFS_SUCCESS) + break; + } + + brelse(tmp_bh); + return ret; +} /* end of clear_cluster */ + +s32 fat_alloc_cluster(struct super_block *sb, s32 num_alloc, CHAIN_T *p_chain) +{ + int i, num_clusters = 0; + u32 new_clu, last_clu = CLUSTER_32(~0), read_clu; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + new_clu = p_chain->dir; + if (new_clu == CLUSTER_32(~0)) + new_clu = p_fs->clu_srch_ptr; + else if (new_clu >= p_fs->num_clusters) + new_clu = 2; + + __set_sb_dirty(sb); + + p_chain->dir = CLUSTER_32(~0); + + for (i = 2; i < p_fs->num_clusters; i++) { + if (FAT_read(sb, new_clu, &read_clu) != 0) + return -1; + + if (read_clu == CLUSTER_32(0)) { + if (FAT_write(sb, new_clu, CLUSTER_32(~0)) < 0) + return -1; + num_clusters++; + + if (p_chain->dir == CLUSTER_32(~0)) + p_chain->dir = new_clu; + else { + if (FAT_write(sb, last_clu, new_clu) < 0) + return -1; + } + + last_clu = new_clu; + + if ((--num_alloc) == 0) { + p_fs->clu_srch_ptr = new_clu; + if (p_fs->used_clusters != (u32) ~0) + p_fs->used_clusters += num_clusters; + + return num_clusters; + } + } + if ((++new_clu) >= p_fs->num_clusters) + new_clu = 2; + } + + p_fs->clu_srch_ptr = new_clu; + if (p_fs->used_clusters != (u32) ~0) + p_fs->used_clusters += num_clusters; + + return num_clusters; +} /* end of fat_alloc_cluster */ + +s32 exfat_alloc_cluster(struct super_block *sb, s32 num_alloc, CHAIN_T *p_chain) +{ + s32 num_clusters = 0; + u32 hint_clu, new_clu, last_clu = CLUSTER_32(~0); + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + hint_clu = p_chain->dir; + if (hint_clu == CLUSTER_32(~0)) { + hint_clu = test_alloc_bitmap(sb, p_fs->clu_srch_ptr-2); + if (hint_clu == CLUSTER_32(~0)) + return 0; + } else if (hint_clu >= p_fs->num_clusters) { + hint_clu = 2; + p_chain->flags = 0x01; + } + + __set_sb_dirty(sb); + + p_chain->dir = CLUSTER_32(~0); + + while ((new_clu = test_alloc_bitmap(sb, hint_clu-2)) != CLUSTER_32(~0)) { + if (new_clu != hint_clu) { + if (p_chain->flags == 0x03) { + exfat_chain_cont_cluster(sb, p_chain->dir, num_clusters); + p_chain->flags = 0x01; + } + } + + if (set_alloc_bitmap(sb, new_clu-2) != FFS_SUCCESS) + return -1; + + num_clusters++; + + if (p_chain->flags == 0x01) { + if (FAT_write(sb, new_clu, CLUSTER_32(~0)) < 0) + return -1; + } + + if (p_chain->dir == CLUSTER_32(~0)) { + p_chain->dir = new_clu; + } else { + if (p_chain->flags == 0x01) { + if (FAT_write(sb, last_clu, new_clu) < 0) + return -1; + } + } + last_clu = new_clu; + + if ((--num_alloc) == 0) { + p_fs->clu_srch_ptr = hint_clu; + if (p_fs->used_clusters != (u32) ~0) + p_fs->used_clusters += num_clusters; + + p_chain->size += num_clusters; + return num_clusters; + } + + hint_clu = new_clu + 1; + if (hint_clu >= p_fs->num_clusters) { + hint_clu = 2; + + if (p_chain->flags == 0x03) { + exfat_chain_cont_cluster(sb, p_chain->dir, num_clusters); + p_chain->flags = 0x01; + } + } + } + + p_fs->clu_srch_ptr = hint_clu; + if (p_fs->used_clusters != (u32) ~0) + p_fs->used_clusters += num_clusters; + + p_chain->size += num_clusters; + return num_clusters; +} /* end of exfat_alloc_cluster */ + +void fat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, s32 do_relse) +{ + s32 num_clusters = 0; + u32 clu, prev; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + int i; + u32 sector; + + if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0))) + return; + __set_sb_dirty(sb); + clu = p_chain->dir; + + if (p_chain->size <= 0) + return; + + do { + if (p_fs->dev_ejected) + break; + + if (do_relse) { + sector = START_SECTOR(clu); + for (i = 0; i < p_fs->sectors_per_clu; i++) + buf_release(sb, sector+i); + } + + prev = clu; + if (FAT_read(sb, clu, &clu) == -1) + break; + + if (FAT_write(sb, prev, CLUSTER_32(0)) < 0) + break; + num_clusters++; + + } while (clu != CLUSTER_32(~0)); + + if (p_fs->used_clusters != (u32) ~0) + p_fs->used_clusters -= num_clusters; +} /* end of fat_free_cluster */ + +void exfat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, s32 do_relse) +{ + s32 num_clusters = 0; + u32 clu; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + int i; + u32 sector; + + if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0))) + return; + + if (p_chain->size <= 0) { + printk(KERN_ERR "[EXFAT] free_cluster : skip free-req clu:%u, " + "because of zero-size truncation\n" + ,p_chain->dir); + return; + } + + __set_sb_dirty(sb); + clu = p_chain->dir; + + if (p_chain->flags == 0x03) { + do { + if (do_relse) { + sector = START_SECTOR(clu); + for (i = 0; i < p_fs->sectors_per_clu; i++) + buf_release(sb, sector+i); + } + + if (clr_alloc_bitmap(sb, clu-2) != FFS_SUCCESS) + break; + clu++; + + num_clusters++; + } while (num_clusters < p_chain->size); + } else { + do { + if (p_fs->dev_ejected) + break; + + if (do_relse) { + sector = START_SECTOR(clu); + for (i = 0; i < p_fs->sectors_per_clu; i++) + buf_release(sb, sector+i); + } + + if (clr_alloc_bitmap(sb, clu-2) != FFS_SUCCESS) + break; + + if (FAT_read(sb, clu, &clu) == -1) + break; + num_clusters++; + } while ((clu != CLUSTER_32(0)) && (clu != CLUSTER_32(~0))); + } + + if (p_fs->used_clusters != (u32) ~0) + p_fs->used_clusters -= num_clusters; +} /* end of exfat_free_cluster */ + +u32 find_last_cluster(struct super_block *sb, CHAIN_T *p_chain) +{ + u32 clu, next; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + clu = p_chain->dir; + + if (p_chain->flags == 0x03) { + clu += p_chain->size - 1; + } else { + while ((FAT_read(sb, clu, &next) == 0) && (next != CLUSTER_32(~0))) { + if (p_fs->dev_ejected) + break; + clu = next; + } + } + + return clu; +} /* end of find_last_cluster */ + +s32 count_num_clusters(struct super_block *sb, CHAIN_T *p_chain) +{ + int i, count = 0; + u32 clu; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0))) + return 0; + + clu = p_chain->dir; + + if (p_chain->flags == 0x03) { + count = p_chain->size; + } else { + for (i = 2; i < p_fs->num_clusters; i++) { + count++; + if (FAT_read(sb, clu, &clu) != 0) + return 0; + if (clu == CLUSTER_32(~0)) + break; + } + } + + return count; +} /* end of count_num_clusters */ + +s32 fat_count_used_clusters(struct super_block *sb) +{ + int i, count = 0; + u32 clu; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + for (i = 2; i < p_fs->num_clusters; i++) { + if (FAT_read(sb, i, &clu) != 0) + break; + if (clu != CLUSTER_32(0)) + count++; + } + + return count; +} /* end of fat_count_used_clusters */ + +s32 exfat_count_used_clusters(struct super_block *sb) +{ + int i, map_i, map_b, count = 0; + u8 k; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + map_i = map_b = 0; + + for (i = 2; i < p_fs->num_clusters; i += 8) { + k = *(((u8 *) p_fs->vol_amap[map_i]->b_data) + map_b); + count += used_bit[k]; + + if ((++map_b) >= p_bd->sector_size) { + map_i++; + map_b = 0; + } + } + + return count; +} /* end of exfat_count_used_clusters */ + +void exfat_chain_cont_cluster(struct super_block *sb, u32 chain, s32 len) +{ + if (len == 0) + return; + + while (len > 1) { + if (FAT_write(sb, chain, chain+1) < 0) + break; + chain++; + len--; + } + FAT_write(sb, chain, CLUSTER_32(~0)); +} /* end of exfat_chain_cont_cluster */ + +/* + * Allocation Bitmap Management Functions + */ + +s32 load_alloc_bitmap(struct super_block *sb) +{ + int i, j, ret; + u32 map_size; + u32 type, sector; + CHAIN_T clu; + BMAP_DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + clu.dir = p_fs->root_dir; + clu.flags = 0x01; + + while (clu.dir != CLUSTER_32(~0)) { + if (p_fs->dev_ejected) + break; + + for (i = 0; i < p_fs->dentries_per_clu; i++) { + ep = (BMAP_DENTRY_T *) get_entry_in_dir(sb, &clu, i, NULL); + if (!ep) + return FFS_MEDIAERR; + + type = p_fs->fs_func->get_entry_type((DENTRY_T *) ep); + + if (type == TYPE_UNUSED) + break; + if (type != TYPE_BITMAP) + continue; + + if (ep->flags == 0x0) { + p_fs->map_clu = GET32_A(ep->start_clu); + map_size = (u32) GET64_A(ep->size); + + p_fs->map_sectors = ((map_size-1) >> p_bd->sector_size_bits) + 1; + + p_fs->vol_amap = (struct buffer_head **) kmalloc(sizeof(struct buffer_head *) * p_fs->map_sectors, GFP_KERNEL); + if (p_fs->vol_amap == NULL) + return FFS_MEMORYERR; + + sector = START_SECTOR(p_fs->map_clu); + + for (j = 0; j < p_fs->map_sectors; j++) { + p_fs->vol_amap[j] = NULL; + ret = sector_read(sb, sector+j, &(p_fs->vol_amap[j]), 1); + if (ret != FFS_SUCCESS) { + /* release all buffers and free vol_amap */ + i = 0; + while (i < j) + brelse(p_fs->vol_amap[i++]); + + if (p_fs->vol_amap) + kfree(p_fs->vol_amap); + p_fs->vol_amap = NULL; + return ret; + } + } + + p_fs->pbr_bh = NULL; + return FFS_SUCCESS; + } + } + + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) + return FFS_MEDIAERR; + } + + return FFS_FORMATERR; +} /* end of load_alloc_bitmap */ + +void free_alloc_bitmap(struct super_block *sb) +{ + int i; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + brelse(p_fs->pbr_bh); + + for (i = 0; i < p_fs->map_sectors; i++) + __brelse(p_fs->vol_amap[i]); + + if (p_fs->vol_amap) + kfree(p_fs->vol_amap); + p_fs->vol_amap = NULL; +} /* end of free_alloc_bitmap */ + +s32 set_alloc_bitmap(struct super_block *sb, u32 clu) +{ + int i, b; + u32 sector; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + i = clu >> (p_bd->sector_size_bits + 3); + b = clu & ((p_bd->sector_size << 3) - 1); + + sector = START_SECTOR(p_fs->map_clu) + i; + + exfat_bitmap_set((u8 *) p_fs->vol_amap[i]->b_data, b); + + return sector_write(sb, sector, p_fs->vol_amap[i], 0); +} /* end of set_alloc_bitmap */ + +s32 clr_alloc_bitmap(struct super_block *sb, u32 clu) +{ + int i, b; + u32 sector; +#ifdef CONFIG_EXFAT_DISCARD + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_mount_options *opts = &sbi->options; + int ret; +#endif /* CONFIG_EXFAT_DISCARD */ + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + i = clu >> (p_bd->sector_size_bits + 3); + b = clu & ((p_bd->sector_size << 3) - 1); + + sector = START_SECTOR(p_fs->map_clu) + i; + + exfat_bitmap_clear((u8 *) p_fs->vol_amap[i]->b_data, b); + + return sector_write(sb, sector, p_fs->vol_amap[i], 0); + +#ifdef CONFIG_EXFAT_DISCARD + if (opts->discard) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) + ret = sb_issue_discard(sb, START_SECTOR(clu), (1 << p_fs->sectors_per_clu_bits)); +#else + ret = sb_issue_discard(sb, START_SECTOR(clu), (1 << p_fs->sectors_per_clu_bits), GFP_NOFS, 0); +#endif + if (ret == -EOPNOTSUPP) { + printk(KERN_WARNING "discard not supported by device, disabling"); + opts->discard = 0; + } + } +#endif /* CONFIG_EXFAT_DISCARD */ +} /* end of clr_alloc_bitmap */ + +u32 test_alloc_bitmap(struct super_block *sb, u32 clu) +{ + int i, map_i, map_b; + u32 clu_base, clu_free; + u8 k, clu_mask; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + clu_base = (clu & ~(0x7)) + 2; + clu_mask = (1 << (clu - clu_base + 2)) - 1; + + map_i = clu >> (p_bd->sector_size_bits + 3); + map_b = (clu >> 3) & p_bd->sector_size_mask; + + for (i = 2; i < p_fs->num_clusters; i += 8) { + k = *(((u8 *) p_fs->vol_amap[map_i]->b_data) + map_b); + if (clu_mask > 0) { + k |= clu_mask; + clu_mask = 0; + } + if (k < 0xFF) { + clu_free = clu_base + free_bit[k]; + if (clu_free < p_fs->num_clusters) + return clu_free; + } + clu_base += 8; + + if (((++map_b) >= p_bd->sector_size) || (clu_base >= p_fs->num_clusters)) { + if ((++map_i) >= p_fs->map_sectors) { + clu_base = 2; + map_i = 0; + } + map_b = 0; + } + } + + return CLUSTER_32(~0); +} /* end of test_alloc_bitmap */ + +void sync_alloc_bitmap(struct super_block *sb) +{ + int i; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (p_fs->vol_amap == NULL) + return; + + for (i = 0; i < p_fs->map_sectors; i++) + sync_dirty_buffer(p_fs->vol_amap[i]); +} /* end of sync_alloc_bitmap */ + +/* + * Upcase table Management Functions + */ +s32 __load_upcase_table(struct super_block *sb, u32 sector, u32 num_sectors, u32 utbl_checksum) +{ + int i, ret = FFS_ERROR; + u32 j; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + struct buffer_head *tmp_bh = NULL; + + u8 skip = FALSE; + u32 index = 0; + u16 uni = 0; + u16 **upcase_table; + + u32 checksum = 0; + + upcase_table = p_fs->vol_utbl = (u16 **) kmalloc(UTBL_COL_COUNT * sizeof(u16 *), GFP_KERNEL); + if (upcase_table == NULL) + return FFS_MEMORYERR; + memset(upcase_table, 0, UTBL_COL_COUNT * sizeof(u16 *)); + + num_sectors += sector; + + while (sector < num_sectors) { + ret = sector_read(sb, sector, &tmp_bh, 1); + if (ret != FFS_SUCCESS) { + DPRINTK("sector read (0x%X)fail\n", sector); + goto error; + } + sector++; + + for (i = 0; i < p_bd->sector_size && index <= 0xFFFF; i += 2) { + uni = GET16(((u8 *) tmp_bh->b_data)+i); + + checksum = ((checksum & 1) ? 0x80000000 : 0) + (checksum >> 1) + *(((u8 *) tmp_bh->b_data)+i); + checksum = ((checksum & 1) ? 0x80000000 : 0) + (checksum >> 1) + *(((u8 *) tmp_bh->b_data)+(i+1)); + + if (skip) { + DPRINTK("skip from 0x%X ", index); + index += uni; + DPRINTK("to 0x%X (amount of 0x%X)\n", index, uni); + skip = FALSE; + } else if (uni == index) + index++; + else if (uni == 0xFFFF) + skip = TRUE; + else { /* uni != index , uni != 0xFFFF */ + u16 col_index = get_col_index(index); + + if (upcase_table[col_index] == NULL) { + DPRINTK("alloc = 0x%X\n", col_index); + upcase_table[col_index] = (u16 *) kmalloc(UTBL_ROW_COUNT * sizeof(u16), GFP_KERNEL); + if (upcase_table[col_index] == NULL) { + ret = FFS_MEMORYERR; + goto error; + } + + for (j = 0; j < UTBL_ROW_COUNT; j++) + upcase_table[col_index][j] = (col_index << LOW_INDEX_BIT) | j; + } + + upcase_table[col_index][get_row_index(index)] = uni; + index++; + } + } + } + if (index >= 0xFFFF && utbl_checksum == checksum) { + if (tmp_bh) + brelse(tmp_bh); + return FFS_SUCCESS; + } + ret = FFS_ERROR; +error: + if (tmp_bh) + brelse(tmp_bh); + free_upcase_table(sb); + return ret; +} + +s32 __load_default_upcase_table(struct super_block *sb) +{ + int i, ret = FFS_ERROR; + u32 j; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + u8 skip = FALSE; + u32 index = 0; + u16 uni = 0; + u16 **upcase_table; + + upcase_table = p_fs->vol_utbl = (u16 **) kmalloc(UTBL_COL_COUNT * sizeof(u16 *), GFP_KERNEL); + if (upcase_table == NULL) + return FFS_MEMORYERR; + memset(upcase_table, 0, UTBL_COL_COUNT * sizeof(u16 *)); + + for (i = 0; index <= 0xFFFF && i < NUM_UPCASE*2; i += 2) { + uni = GET16(uni_upcase + i); + if (skip) { + DPRINTK("skip from 0x%X ", index); + index += uni; + DPRINTK("to 0x%X (amount of 0x%X)\n", index, uni); + skip = FALSE; + } else if (uni == index) + index++; + else if (uni == 0xFFFF) + skip = TRUE; + else { /* uni != index , uni != 0xFFFF */ + u16 col_index = get_col_index(index); + + if (upcase_table[col_index] == NULL) { + DPRINTK("alloc = 0x%X\n", col_index); + upcase_table[col_index] = (u16 *) kmalloc(UTBL_ROW_COUNT * sizeof(u16), GFP_KERNEL); + if (upcase_table[col_index] == NULL) { + ret = FFS_MEMORYERR; + goto error; + } + + for (j = 0; j < UTBL_ROW_COUNT; j++) + upcase_table[col_index][j] = (col_index << LOW_INDEX_BIT) | j; + } + + upcase_table[col_index][get_row_index(index)] = uni; + index++; + } + } + + if (index >= 0xFFFF) + return FFS_SUCCESS; + +error: + /* FATAL error: default upcase table has error */ + free_upcase_table(sb); + return ret; +} + +s32 load_upcase_table(struct super_block *sb) +{ + int i; + u32 tbl_clu, tbl_size; + u32 type, sector, num_sectors; + CHAIN_T clu; + CASE_DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + clu.dir = p_fs->root_dir; + clu.flags = 0x01; + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + while (clu.dir != CLUSTER_32(~0)) { + for (i = 0; i < p_fs->dentries_per_clu; i++) { + ep = (CASE_DENTRY_T *) get_entry_in_dir(sb, &clu, i, NULL); + if (!ep) + return FFS_MEDIAERR; + + type = p_fs->fs_func->get_entry_type((DENTRY_T *) ep); + + if (type == TYPE_UNUSED) + break; + if (type != TYPE_UPCASE) + continue; + + tbl_clu = GET32_A(ep->start_clu); + tbl_size = (u32) GET64_A(ep->size); + + sector = START_SECTOR(tbl_clu); + num_sectors = ((tbl_size-1) >> p_bd->sector_size_bits) + 1; + if (__load_upcase_table(sb, sector, num_sectors, GET32_A(ep->checksum)) != FFS_SUCCESS) + break; + else + return FFS_SUCCESS; + } + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) + return FFS_MEDIAERR; + } + /* load default upcase table */ + return __load_default_upcase_table(sb); +} /* end of load_upcase_table */ + +void free_upcase_table(struct super_block *sb) +{ + u32 i; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + u16 **upcase_table; + + upcase_table = p_fs->vol_utbl; + for (i = 0; i < UTBL_COL_COUNT; i++) { + if (upcase_table[i]) + kfree(upcase_table[i]); + } + + if (p_fs->vol_utbl) + kfree(p_fs->vol_utbl); + p_fs->vol_utbl = NULL; +} /* end of free_upcase_table */ + +/* + * Directory Entry Management Functions + */ + +u32 fat_get_entry_type(DENTRY_T *p_entry) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + + if (*(ep->name) == 0x0) + return TYPE_UNUSED; + + else if (*(ep->name) == 0xE5) + return TYPE_DELETED; + + else if (ep->attr == ATTR_EXTEND) + return TYPE_EXTEND; + + else if ((ep->attr & (ATTR_SUBDIR|ATTR_VOLUME)) == ATTR_VOLUME) + return TYPE_VOLUME; + + else if ((ep->attr & (ATTR_SUBDIR|ATTR_VOLUME)) == ATTR_SUBDIR) + return TYPE_DIR; + + return TYPE_FILE; +} /* end of fat_get_entry_type */ + +u32 exfat_get_entry_type(DENTRY_T *p_entry) +{ + FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; + + if (ep->type == 0x0) { + return TYPE_UNUSED; + } else if (ep->type < 0x80) { + return TYPE_DELETED; + } else if (ep->type == 0x80) { + return TYPE_INVALID; + } else if (ep->type < 0xA0) { + if (ep->type == 0x81) { + return TYPE_BITMAP; + } else if (ep->type == 0x82) { + return TYPE_UPCASE; + } else if (ep->type == 0x83) { + return TYPE_VOLUME; + } else if (ep->type == 0x85) { + if (GET16_A(ep->attr) & ATTR_SUBDIR) + return TYPE_DIR; + else + return TYPE_FILE; + } + return TYPE_CRITICAL_PRI; + } else if (ep->type < 0xC0) { + if (ep->type == 0xA0) + return TYPE_GUID; + else if (ep->type == 0xA1) + return TYPE_PADDING; + else if (ep->type == 0xA2) + return TYPE_ACLTAB; + return TYPE_BENIGN_PRI; + } else if (ep->type < 0xE0) { + if (ep->type == 0xC0) + return TYPE_STREAM; + else if (ep->type == 0xC1) + return TYPE_EXTEND; + else if (ep->type == 0xC2) + return TYPE_ACL; + return TYPE_CRITICAL_SEC; + } + + return TYPE_BENIGN_SEC; +} /* end of exfat_get_entry_type */ + +void fat_set_entry_type(DENTRY_T *p_entry, u32 type) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + + if (type == TYPE_UNUSED) + *(ep->name) = 0x0; + + else if (type == TYPE_DELETED) + *(ep->name) = 0xE5; + + else if (type == TYPE_EXTEND) + ep->attr = ATTR_EXTEND; + + else if (type == TYPE_DIR) + ep->attr = ATTR_SUBDIR; + + else if (type == TYPE_FILE) + ep->attr = ATTR_ARCHIVE; + + else if (type == TYPE_SYMLINK) + ep->attr = ATTR_ARCHIVE | ATTR_SYMLINK; +} /* end of fat_set_entry_type */ + +void exfat_set_entry_type(DENTRY_T *p_entry, u32 type) +{ + FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; + + if (type == TYPE_UNUSED) { + ep->type = 0x0; + } else if (type == TYPE_DELETED) { + ep->type &= ~0x80; + } else if (type == TYPE_STREAM) { + ep->type = 0xC0; + } else if (type == TYPE_EXTEND) { + ep->type = 0xC1; + } else if (type == TYPE_BITMAP) { + ep->type = 0x81; + } else if (type == TYPE_UPCASE) { + ep->type = 0x82; + } else if (type == TYPE_VOLUME) { + ep->type = 0x83; + } else if (type == TYPE_DIR) { + ep->type = 0x85; + SET16_A(ep->attr, ATTR_SUBDIR); + } else if (type == TYPE_FILE) { + ep->type = 0x85; + SET16_A(ep->attr, ATTR_ARCHIVE); + } else if (type == TYPE_SYMLINK) { + ep->type = 0x85; + SET16_A(ep->attr, ATTR_ARCHIVE | ATTR_SYMLINK); + } +} /* end of exfat_set_entry_type */ + +u32 fat_get_entry_attr(DENTRY_T *p_entry) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + return (u32) ep->attr; +} /* end of fat_get_entry_attr */ + +u32 exfat_get_entry_attr(DENTRY_T *p_entry) +{ + FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; + return (u32) GET16_A(ep->attr); +} /* end of exfat_get_entry_attr */ + +void fat_set_entry_attr(DENTRY_T *p_entry, u32 attr) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + ep->attr = (u8) attr; +} /* end of fat_set_entry_attr */ + +void exfat_set_entry_attr(DENTRY_T *p_entry, u32 attr) +{ + FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; + SET16_A(ep->attr, (u16) attr); +} /* end of exfat_set_entry_attr */ + +u8 fat_get_entry_flag(DENTRY_T *p_entry) +{ + return 0x01; +} /* end of fat_get_entry_flag */ + +u8 exfat_get_entry_flag(DENTRY_T *p_entry) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; + return ep->flags; +} /* end of exfat_get_entry_flag */ + +void fat_set_entry_flag(DENTRY_T *p_entry, u8 flags) +{ +} /* end of fat_set_entry_flag */ + +void exfat_set_entry_flag(DENTRY_T *p_entry, u8 flags) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; + ep->flags = flags; +} /* end of exfat_set_entry_flag */ + +u32 fat_get_entry_clu0(DENTRY_T *p_entry) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + return (GET32_A(ep->start_clu_hi) << 16) | GET16_A(ep->start_clu_lo); +} /* end of fat_get_entry_clu0 */ + +u32 exfat_get_entry_clu0(DENTRY_T *p_entry) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; + return GET32_A(ep->start_clu); +} /* end of exfat_get_entry_clu0 */ + +void fat_set_entry_clu0(DENTRY_T *p_entry, u32 start_clu) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + SET16_A(ep->start_clu_lo, CLUSTER_16(start_clu)); + SET16_A(ep->start_clu_hi, CLUSTER_16(start_clu >> 16)); +} /* end of fat_set_entry_clu0 */ + +void exfat_set_entry_clu0(DENTRY_T *p_entry, u32 start_clu) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; + SET32_A(ep->start_clu, start_clu); +} /* end of exfat_set_entry_clu0 */ + +u64 fat_get_entry_size(DENTRY_T *p_entry) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + return (u64) GET32_A(ep->size); +} /* end of fat_get_entry_size */ + +u64 exfat_get_entry_size(DENTRY_T *p_entry) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; + return GET64_A(ep->valid_size); +} /* end of exfat_get_entry_size */ + +void fat_set_entry_size(DENTRY_T *p_entry, u64 size) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + SET32_A(ep->size, (u32) size); +} /* end of fat_set_entry_size */ + +void exfat_set_entry_size(DENTRY_T *p_entry, u64 size) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; + SET64_A(ep->valid_size, size); + SET64_A(ep->size, size); +} /* end of exfat_set_entry_size */ + +void fat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode) +{ + u16 t = 0x00, d = 0x21; + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + + switch (mode) { + case TM_CREATE: + t = GET16_A(ep->create_time); + d = GET16_A(ep->create_date); + break; + case TM_MODIFY: + t = GET16_A(ep->modify_time); + d = GET16_A(ep->modify_date); + break; + } + + tp->sec = (t & 0x001F) << 1; + tp->min = (t >> 5) & 0x003F; + tp->hour = (t >> 11); + tp->day = (d & 0x001F); + tp->mon = (d >> 5) & 0x000F; + tp->year = (d >> 9); +} /* end of fat_get_entry_time */ + +void exfat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode) +{ + u16 t = 0x00, d = 0x21; + FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; + + switch (mode) { + case TM_CREATE: + t = GET16_A(ep->create_time); + d = GET16_A(ep->create_date); + break; + case TM_MODIFY: + t = GET16_A(ep->modify_time); + d = GET16_A(ep->modify_date); + break; + case TM_ACCESS: + t = GET16_A(ep->access_time); + d = GET16_A(ep->access_date); + break; + } + + tp->sec = (t & 0x001F) << 1; + tp->min = (t >> 5) & 0x003F; + tp->hour = (t >> 11); + tp->day = (d & 0x001F); + tp->mon = (d >> 5) & 0x000F; + tp->year = (d >> 9); +} /* end of exfat_get_entry_time */ + +void fat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode) +{ + u16 t, d; + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + + t = (tp->hour << 11) | (tp->min << 5) | (tp->sec >> 1); + d = (tp->year << 9) | (tp->mon << 5) | tp->day; + + switch (mode) { + case TM_CREATE: + SET16_A(ep->create_time, t); + SET16_A(ep->create_date, d); + break; + case TM_MODIFY: + SET16_A(ep->modify_time, t); + SET16_A(ep->modify_date, d); + break; + } +} /* end of fat_set_entry_time */ + +void exfat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode) +{ + u16 t, d; + FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; + + t = (tp->hour << 11) | (tp->min << 5) | (tp->sec >> 1); + d = (tp->year << 9) | (tp->mon << 5) | tp->day; + + switch (mode) { + case TM_CREATE: + SET16_A(ep->create_time, t); + SET16_A(ep->create_date, d); + break; + case TM_MODIFY: + SET16_A(ep->modify_time, t); + SET16_A(ep->modify_date, d); + break; + case TM_ACCESS: + SET16_A(ep->access_time, t); + SET16_A(ep->access_date, d); + break; + } +} /* end of exfat_set_entry_time */ + +s32 fat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 type, + u32 start_clu, u64 size) +{ + u32 sector; + DOS_DENTRY_T *dos_ep; + + dos_ep = (DOS_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); + if (!dos_ep) + return FFS_MEDIAERR; + + init_dos_entry(dos_ep, type, start_clu); + buf_modify(sb, sector); + + return FFS_SUCCESS; +} /* end of fat_init_dir_entry */ + +s32 exfat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 type, + u32 start_clu, u64 size) +{ + u32 sector; + u8 flags; + FILE_DENTRY_T *file_ep; + STRM_DENTRY_T *strm_ep; + + flags = (type == TYPE_FILE) ? 0x01 : 0x03; + + /* we cannot use get_entry_set_in_dir here because file ep is not initialized yet */ + file_ep = (FILE_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); + if (!file_ep) + return FFS_MEDIAERR; + + strm_ep = (STRM_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+1, §or); + if (!strm_ep) + return FFS_MEDIAERR; + + init_file_entry(file_ep, type); + buf_modify(sb, sector); + + init_strm_entry(strm_ep, flags, start_clu, size); + buf_modify(sb, sector); + + return FFS_SUCCESS; +} /* end of exfat_init_dir_entry */ + +s32 fat_init_ext_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 num_entries, + UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname) +{ + int i; + u32 sector; + u8 chksum; + u16 *uniname = p_uniname->name; + DOS_DENTRY_T *dos_ep; + EXT_DENTRY_T *ext_ep; + + dos_ep = (DOS_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); + if (!dos_ep) + return FFS_MEDIAERR; + + dos_ep->lcase = p_dosname->name_case; + memcpy(dos_ep->name, p_dosname->name, DOS_NAME_LENGTH); + buf_modify(sb, sector); + + if ((--num_entries) > 0) { + chksum = calc_checksum_1byte((void *) dos_ep->name, DOS_NAME_LENGTH, 0); + + for (i = 1; i < num_entries; i++) { + ext_ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry-i, §or); + if (!ext_ep) + return FFS_MEDIAERR; + + init_ext_entry(ext_ep, i, chksum, uniname); + buf_modify(sb, sector); + uniname += 13; + } + + ext_ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry-i, §or); + if (!ext_ep) + return FFS_MEDIAERR; + + init_ext_entry(ext_ep, i+0x40, chksum, uniname); + buf_modify(sb, sector); + } + + return FFS_SUCCESS; +} /* end of fat_init_ext_entry */ + +s32 exfat_init_ext_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 num_entries, + UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname) +{ + int i; + u32 sector; + u16 *uniname = p_uniname->name; + FILE_DENTRY_T *file_ep; + STRM_DENTRY_T *strm_ep; + NAME_DENTRY_T *name_ep; + + file_ep = (FILE_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); + if (!file_ep) + return FFS_MEDIAERR; + + file_ep->num_ext = (u8)(num_entries - 1); + buf_modify(sb, sector); + + strm_ep = (STRM_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+1, §or); + if (!strm_ep) + return FFS_MEDIAERR; + + strm_ep->name_len = p_uniname->name_len; + SET16_A(strm_ep->name_hash, p_uniname->name_hash); + buf_modify(sb, sector); + + for (i = 2; i < num_entries; i++) { + name_ep = (NAME_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+i, §or); + if (!name_ep) + return FFS_MEDIAERR; + + init_name_entry(name_ep, uniname); + buf_modify(sb, sector); + uniname += 15; + } + + update_dir_checksum(sb, p_dir, entry); + + return FFS_SUCCESS; +} /* end of exfat_init_ext_entry */ + +void init_dos_entry(DOS_DENTRY_T *ep, u32 type, u32 start_clu) +{ + TIMESTAMP_T tm, *tp; + + fat_set_entry_type((DENTRY_T *) ep, type); + SET16_A(ep->start_clu_lo, CLUSTER_16(start_clu)); + SET16_A(ep->start_clu_hi, CLUSTER_16(start_clu >> 16)); + SET32_A(ep->size, 0); + + tp = tm_current(&tm); + fat_set_entry_time((DENTRY_T *) ep, tp, TM_CREATE); + fat_set_entry_time((DENTRY_T *) ep, tp, TM_MODIFY); + SET16_A(ep->access_date, 0); + ep->create_time_ms = 0; +} /* end of init_dos_entry */ + +void init_ext_entry(EXT_DENTRY_T *ep, s32 order, u8 chksum, u16 *uniname) +{ + int i; + u8 end = FALSE; + + fat_set_entry_type((DENTRY_T *) ep, TYPE_EXTEND); + ep->order = (u8) order; + ep->sysid = 0; + ep->checksum = chksum; + SET16_A(ep->start_clu, 0); + + for (i = 0; i < 10; i += 2) { + if (!end) { + SET16(ep->unicode_0_4+i, *uniname); + if (*uniname == 0x0) + end = TRUE; + else + uniname++; + } else { + SET16(ep->unicode_0_4+i, 0xFFFF); + } + } + + for (i = 0; i < 12; i += 2) { + if (!end) { + SET16_A(ep->unicode_5_10+i, *uniname); + if (*uniname == 0x0) + end = TRUE; + else + uniname++; + } else { + SET16_A(ep->unicode_5_10+i, 0xFFFF); + } + } + + for (i = 0; i < 4; i += 2) { + if (!end) { + SET16_A(ep->unicode_11_12+i, *uniname); + if (*uniname == 0x0) + end = TRUE; + else + uniname++; + } else { + SET16_A(ep->unicode_11_12+i, 0xFFFF); + } + } +} /* end of init_ext_entry */ + +void init_file_entry(FILE_DENTRY_T *ep, u32 type) +{ + TIMESTAMP_T tm, *tp; + + exfat_set_entry_type((DENTRY_T *) ep, type); + + tp = tm_current(&tm); + exfat_set_entry_time((DENTRY_T *) ep, tp, TM_CREATE); + exfat_set_entry_time((DENTRY_T *) ep, tp, TM_MODIFY); + exfat_set_entry_time((DENTRY_T *) ep, tp, TM_ACCESS); + ep->create_time_ms = 0; + ep->modify_time_ms = 0; + ep->access_time_ms = 0; +} /* end of init_file_entry */ + +void init_strm_entry(STRM_DENTRY_T *ep, u8 flags, u32 start_clu, u64 size) +{ + exfat_set_entry_type((DENTRY_T *) ep, TYPE_STREAM); + ep->flags = flags; + SET32_A(ep->start_clu, start_clu); + SET64_A(ep->valid_size, size); + SET64_A(ep->size, size); +} /* end of init_strm_entry */ + +void init_name_entry(NAME_DENTRY_T *ep, u16 *uniname) +{ + int i; + + exfat_set_entry_type((DENTRY_T *) ep, TYPE_EXTEND); + ep->flags = 0x0; + + for (i = 0; i < 30; i++, i++) { + SET16_A(ep->unicode_0_14+i, *uniname); + if (*uniname == 0x0) + break; + uniname++; + } +} /* end of init_name_entry */ + +void fat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 order, s32 num_entries) +{ + int i; + u32 sector; + DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + for (i = num_entries-1; i >= order; i--) { + ep = get_entry_in_dir(sb, p_dir, entry-i, §or); + if (!ep) + return; + + p_fs->fs_func->set_entry_type(ep, TYPE_DELETED); + buf_modify(sb, sector); + } +} /* end of fat_delete_dir_entry */ + +void exfat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 order, s32 num_entries) +{ + int i; + u32 sector; + DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + for (i = order; i < num_entries; i++) { + ep = get_entry_in_dir(sb, p_dir, entry+i, §or); + if (!ep) + return; + + p_fs->fs_func->set_entry_type(ep, TYPE_DELETED); + buf_modify(sb, sector); + } +} /* end of exfat_delete_dir_entry */ + +void update_dir_checksum(struct super_block *sb, CHAIN_T *p_dir, s32 entry) +{ + int i, num_entries; + u32 sector; + u16 chksum; + FILE_DENTRY_T *file_ep; + DENTRY_T *ep; + + file_ep = (FILE_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); + if (!file_ep) + return; + + buf_lock(sb, sector); + + num_entries = (s32) file_ep->num_ext + 1; + chksum = calc_checksum_2byte((void *) file_ep, DENTRY_SIZE, 0, CS_DIR_ENTRY); + + for (i = 1; i < num_entries; i++) { + ep = get_entry_in_dir(sb, p_dir, entry+i, NULL); + if (!ep) { + buf_unlock(sb, sector); + return; + } + + chksum = calc_checksum_2byte((void *) ep, DENTRY_SIZE, chksum, CS_DEFAULT); + } + + SET16_A(file_ep->checksum, chksum); + buf_modify(sb, sector); + buf_unlock(sb, sector); +} /* end of update_dir_checksum */ + +void update_dir_checksum_with_entry_set(struct super_block *sb, ENTRY_SET_CACHE_T *es) +{ + DENTRY_T *ep; + u16 chksum = 0; + s32 chksum_type = CS_DIR_ENTRY, i; + + ep = (DENTRY_T *)&(es->__buf); + for (i = 0; i < es->num_entries; i++) { + DPRINTK("update_dir_checksum_with_entry_set ep %p\n", ep); + chksum = calc_checksum_2byte((void *) ep, DENTRY_SIZE, chksum, chksum_type); + ep++; + chksum_type = CS_DEFAULT; + } + + ep = (DENTRY_T *)&(es->__buf); + SET16_A(((FILE_DENTRY_T *)ep)->checksum, chksum); + write_whole_entry_set(sb, es); +} + +static s32 _walk_fat_chain(struct super_block *sb, CHAIN_T *p_dir, s32 byte_offset, u32 *clu) +{ + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + s32 clu_offset; + u32 cur_clu; + + clu_offset = byte_offset >> p_fs->cluster_size_bits; + cur_clu = p_dir->dir; + + if (p_dir->flags == 0x03) { + cur_clu += clu_offset; + } else { + while (clu_offset > 0) { + if (FAT_read(sb, cur_clu, &cur_clu) == -1) + return FFS_MEDIAERR; + clu_offset--; + } + } + + if (clu) + *clu = cur_clu; + return FFS_SUCCESS; +} +s32 find_location(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 *sector, s32 *offset) +{ + s32 off, ret; + u32 clu = 0; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + off = entry << DENTRY_SIZE_BITS; + + if (p_dir->dir == CLUSTER_32(0)) { /* FAT16 root_dir */ + *offset = off & p_bd->sector_size_mask; + *sector = off >> p_bd->sector_size_bits; + *sector += p_fs->root_start_sector; + } else { + ret = _walk_fat_chain(sb, p_dir, off, &clu); + if (ret != FFS_SUCCESS) + return ret; + + off &= p_fs->cluster_size - 1; /* byte offset in cluster */ + + *offset = off & p_bd->sector_size_mask; /* byte offset in sector */ + *sector = off >> p_bd->sector_size_bits; /* sector offset in cluster */ + *sector += START_SECTOR(clu); + } + return FFS_SUCCESS; +} /* end of find_location */ + +DENTRY_T *get_entry_with_sector(struct super_block *sb, u32 sector, s32 offset) +{ + u8 *buf; + + buf = buf_getblk(sb, sector); + + if (buf == NULL) + return NULL; + + return (DENTRY_T *)(buf + offset); +} /* end of get_entry_with_sector */ + +DENTRY_T *get_entry_in_dir(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 *sector) +{ + s32 off; + u32 sec; + u8 *buf; + + if (find_location(sb, p_dir, entry, &sec, &off) != FFS_SUCCESS) + return NULL; + + buf = buf_getblk(sb, sec); + + if (buf == NULL) + return NULL; + + if (sector != NULL) + *sector = sec; + return (DENTRY_T *)(buf + off); +} /* end of get_entry_in_dir */ + + +/* returns a set of dentries for a file or dir. + * Note that this is a copy (dump) of dentries so that user should call write_entry_set() + * to apply changes made in this entry set to the real device. + * in: + * sb+p_dir+entry: indicates a file/dir + * type: specifies how many dentries should be included. + * out: + * file_ep: will point the first dentry(= file dentry) on success + * return: + * pointer of entry set on success, + * NULL on failure. + */ + +#define ES_MODE_STARTED 0 +#define ES_MODE_GET_FILE_ENTRY 1 +#define ES_MODE_GET_STRM_ENTRY 2 +#define ES_MODE_GET_NAME_ENTRY 3 +#define ES_MODE_GET_CRITICAL_SEC_ENTRY 4 +ENTRY_SET_CACHE_T *get_entry_set_in_dir(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 type, DENTRY_T **file_ep) +{ + s32 off, ret, byte_offset; + u32 clu = 0; + u32 sec, entry_type; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + ENTRY_SET_CACHE_T *es = NULL; + DENTRY_T *ep, *pos; + u8 *buf; + u8 num_entries; + s32 mode = ES_MODE_STARTED; + + DPRINTK("get_entry_set_in_dir entered\n"); + DPRINTK("p_dir dir %u flags %x size %d\n", p_dir->dir, p_dir->flags, p_dir->size); + + byte_offset = entry << DENTRY_SIZE_BITS; + ret = _walk_fat_chain(sb, p_dir, byte_offset, &clu); + if (ret != FFS_SUCCESS) + return NULL; + + + byte_offset &= p_fs->cluster_size - 1; /* byte offset in cluster */ + + off = byte_offset & p_bd->sector_size_mask; /* byte offset in sector */ + sec = byte_offset >> p_bd->sector_size_bits; /* sector offset in cluster */ + sec += START_SECTOR(clu); + + buf = buf_getblk(sb, sec); + if (buf == NULL) + goto err_out; + + + ep = (DENTRY_T *)(buf + off); + entry_type = p_fs->fs_func->get_entry_type(ep); + + if ((entry_type != TYPE_FILE) + && (entry_type != TYPE_DIR)) + goto err_out; + + if (type == ES_ALL_ENTRIES) + num_entries = ((FILE_DENTRY_T *)ep)->num_ext+1; + else + num_entries = type; + + DPRINTK("trying to kmalloc %zx bytes for %d entries\n", offsetof(ENTRY_SET_CACHE_T, __buf) + (num_entries) * sizeof(DENTRY_T), num_entries); + es = kmalloc(offsetof(ENTRY_SET_CACHE_T, __buf) + (num_entries) * sizeof(DENTRY_T), GFP_KERNEL); + if (es == NULL) + goto err_out; + + es->num_entries = num_entries; + es->sector = sec; + es->offset = off; + es->alloc_flag = p_dir->flags; + + pos = (DENTRY_T *) &(es->__buf); + + while(num_entries) { + /* instead of copying whole sector, we will check every entry. + * this will provide minimum stablity and consistancy. + */ + + entry_type = p_fs->fs_func->get_entry_type(ep); + + if ((entry_type == TYPE_UNUSED) || (entry_type == TYPE_DELETED)) + goto err_out; + + switch (mode) { + case ES_MODE_STARTED: + if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR)) + mode = ES_MODE_GET_FILE_ENTRY; + else + goto err_out; + break; + case ES_MODE_GET_FILE_ENTRY: + if (entry_type == TYPE_STREAM) + mode = ES_MODE_GET_STRM_ENTRY; + else + goto err_out; + break; + case ES_MODE_GET_STRM_ENTRY: + if (entry_type == TYPE_EXTEND) + mode = ES_MODE_GET_NAME_ENTRY; + else + goto err_out; + break; + case ES_MODE_GET_NAME_ENTRY: + if (entry_type == TYPE_EXTEND) + break; + else if (entry_type == TYPE_STREAM) + goto err_out; + else if (entry_type & TYPE_CRITICAL_SEC) + mode = ES_MODE_GET_CRITICAL_SEC_ENTRY; + else + goto err_out; + break; + case ES_MODE_GET_CRITICAL_SEC_ENTRY: + if ((entry_type == TYPE_EXTEND) || (entry_type == TYPE_STREAM)) + goto err_out; + else if ((entry_type & TYPE_CRITICAL_SEC) != TYPE_CRITICAL_SEC) + goto err_out; + break; + } + + memcpy(pos, ep, sizeof(DENTRY_T)); + + if (--num_entries == 0) + break; + + if (((off + DENTRY_SIZE) & p_bd->sector_size_mask) < (off & p_bd->sector_size_mask)) { + /* get the next sector */ + if (IS_LAST_SECTOR_IN_CLUSTER(sec)) { + if (es->alloc_flag == 0x03) { + clu++; + } else { + if (FAT_read(sb, clu, &clu) == -1) + goto err_out; + } + sec = START_SECTOR(clu); + } else { + sec++; + } + buf = buf_getblk(sb, sec); + if (buf == NULL) + goto err_out; + off = 0; + ep = (DENTRY_T *)(buf); + } else { + ep++; + off += DENTRY_SIZE; + } + pos++; + } + + if (file_ep) + *file_ep = (DENTRY_T *)&(es->__buf); + + DPRINTK("es sec %u offset %d flags %d, num_entries %u buf ptr %p\n", + es->sector, es->offset, es->alloc_flag, es->num_entries, &(es->__buf)); + DPRINTK("get_entry_set_in_dir exited %p\n", es); + return es; +err_out: + DPRINTK("get_entry_set_in_dir exited NULL (es %p)\n", es); + if (es) + kfree(es); + return NULL; +} + +void release_entry_set(ENTRY_SET_CACHE_T *es) +{ + DPRINTK("release_entry_set %p\n", es); + if (es) + kfree(es); +} + + +static s32 __write_partial_entries_in_entry_set(struct super_block *sb, ENTRY_SET_CACHE_T *es, u32 sec, s32 off, u32 count) +{ + s32 num_entries, buf_off = (off - es->offset); + u32 remaining_byte_in_sector, copy_entries; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + u32 clu; + u8 *buf, *esbuf = (u8 *)&(es->__buf); + + DPRINTK("__write_partial_entries_in_entry_set entered\n"); + DPRINTK("es %p sec %u off %d count %d\n", es, sec, off, count); + num_entries = count; + + while (num_entries) { + /* white per sector base */ + remaining_byte_in_sector = (1 << p_bd->sector_size_bits) - off; + copy_entries = MIN(remaining_byte_in_sector >> DENTRY_SIZE_BITS , num_entries); + buf = buf_getblk(sb, sec); + if (buf == NULL) + goto err_out; + DPRINTK("es->buf %p buf_off %u\n", esbuf, buf_off); + DPRINTK("copying %d entries from %p to sector %u\n", copy_entries, (esbuf + buf_off), sec); + memcpy(buf + off, esbuf + buf_off, copy_entries << DENTRY_SIZE_BITS); + buf_modify(sb, sec); + num_entries -= copy_entries; + + if (num_entries) { + /* get next sector */ + if (IS_LAST_SECTOR_IN_CLUSTER(sec)) { + clu = GET_CLUSTER_FROM_SECTOR(sec); + if (es->alloc_flag == 0x03) { + clu++; + } else { + if (FAT_read(sb, clu, &clu) == -1) + goto err_out; + } + sec = START_SECTOR(clu); + } else { + sec++; + } + off = 0; + buf_off += copy_entries << DENTRY_SIZE_BITS; + } + } + + DPRINTK("__write_partial_entries_in_entry_set exited successfully\n"); + return FFS_SUCCESS; +err_out: + DPRINTK("__write_partial_entries_in_entry_set failed\n"); + return FFS_ERROR; +} + +/* write back all entries in entry set */ +s32 write_whole_entry_set(struct super_block *sb, ENTRY_SET_CACHE_T *es) +{ + return __write_partial_entries_in_entry_set(sb, es, es->sector, es->offset, es->num_entries); +} + +/* write back some entries in entry set */ +s32 write_partial_entries_in_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es, DENTRY_T *ep, u32 count) +{ + s32 ret, byte_offset, off; + u32 clu=0, sec; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + CHAIN_T dir; + + /* vaidity check */ + if (ep + count > ((DENTRY_T *)&(es->__buf)) + es->num_entries) + return FFS_ERROR; + + dir.dir = GET_CLUSTER_FROM_SECTOR(es->sector); + dir.flags = es->alloc_flag; + dir.size = 0xffffffff; /* XXX */ + + byte_offset = (es->sector - START_SECTOR(dir.dir)) << p_bd->sector_size_bits; + byte_offset += ((void **)ep - &(es->__buf)) + es->offset; + + ret =_walk_fat_chain(sb, &dir, byte_offset, &clu); + if (ret != FFS_SUCCESS) + return ret; + byte_offset &= p_fs->cluster_size - 1; /* byte offset in cluster */ + off = byte_offset & p_bd->sector_size_mask; /* byte offset in sector */ + sec = byte_offset >> p_bd->sector_size_bits; /* sector offset in cluster */ + sec += START_SECTOR(clu); + return __write_partial_entries_in_entry_set(sb, es, sec, off, count); +} + +/* search EMPTY CONTINUOUS "num_entries" entries */ +s32 search_deleted_or_unused_entry(struct super_block *sb, CHAIN_T *p_dir, s32 num_entries) +{ + int i, dentry, num_empty = 0; + s32 dentries_per_clu; + u32 type; + CHAIN_T clu; + DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */ + dentries_per_clu = p_fs->dentries_in_root; + else + dentries_per_clu = p_fs->dentries_per_clu; + + if (p_fs->hint_uentry.dir == p_dir->dir) { + if (p_fs->hint_uentry.entry == -1) + return -1; + + clu.dir = p_fs->hint_uentry.clu.dir; + clu.size = p_fs->hint_uentry.clu.size; + clu.flags = p_fs->hint_uentry.clu.flags; + + dentry = p_fs->hint_uentry.entry; + } else { + p_fs->hint_uentry.entry = -1; + + clu.dir = p_dir->dir; + clu.size = p_dir->size; + clu.flags = p_dir->flags; + + dentry = 0; + } + + while (clu.dir != CLUSTER_32(~0)) { + if (p_fs->dev_ejected) + break; + + if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */ + i = dentry % dentries_per_clu; + else + i = dentry & (dentries_per_clu-1); + + for (; i < dentries_per_clu; i++, dentry++) { + ep = get_entry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -1; + + type = p_fs->fs_func->get_entry_type(ep); + + if (type == TYPE_UNUSED) { + num_empty++; + if (p_fs->hint_uentry.entry == -1) { + p_fs->hint_uentry.dir = p_dir->dir; + p_fs->hint_uentry.entry = dentry; + + p_fs->hint_uentry.clu.dir = clu.dir; + p_fs->hint_uentry.clu.size = clu.size; + p_fs->hint_uentry.clu.flags = clu.flags; + } + } else if (type == TYPE_DELETED) { + num_empty++; + } else { + num_empty = 0; + } + + if (num_empty >= num_entries) { + p_fs->hint_uentry.dir = CLUSTER_32(~0); + p_fs->hint_uentry.entry = -1; + + if (p_fs->vol_type == EXFAT) + return dentry - (num_entries-1); + else + return dentry; + } + } + + if (p_dir->dir == CLUSTER_32(0)) + break; /* FAT16 root_dir */ + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUSTER_32(~0); + } else { + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) + return -1; + } + } + + return -1; +} /* end of search_deleted_or_unused_entry */ + +s32 find_empty_entry(struct inode *inode, CHAIN_T *p_dir, s32 num_entries) +{ + s32 ret, dentry; + u32 last_clu, sector; + u64 size = 0; + CHAIN_T clu; + DENTRY_T *ep = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + + if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */ + return search_deleted_or_unused_entry(sb, p_dir, num_entries); + + while ((dentry = search_deleted_or_unused_entry(sb, p_dir, num_entries)) < 0) { + if (p_fs->dev_ejected) + break; + + if (p_fs->vol_type == EXFAT) { + if (p_dir->dir != p_fs->root_dir) + size = i_size_read(inode); + } + + last_clu = find_last_cluster(sb, p_dir); + clu.dir = last_clu + 1; + clu.size = 0; + clu.flags = p_dir->flags; + + /* (1) allocate a cluster */ + ret = p_fs->fs_func->alloc_cluster(sb, 1, &clu); + if (ret < 1) + return -1; + + if (clear_cluster(sb, clu.dir) != FFS_SUCCESS) + return -1; + + /* (2) append to the FAT chain */ + if (clu.flags != p_dir->flags) { + exfat_chain_cont_cluster(sb, p_dir->dir, p_dir->size); + p_dir->flags = 0x01; + p_fs->hint_uentry.clu.flags = 0x01; + } + if (clu.flags == 0x01) + if (FAT_write(sb, last_clu, clu.dir) < 0) + return -1; + + if (p_fs->hint_uentry.entry == -1) { + p_fs->hint_uentry.dir = p_dir->dir; + p_fs->hint_uentry.entry = p_dir->size << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS); + + p_fs->hint_uentry.clu.dir = clu.dir; + p_fs->hint_uentry.clu.size = 0; + p_fs->hint_uentry.clu.flags = clu.flags; + } + p_fs->hint_uentry.clu.size++; + p_dir->size++; + + /* (3) update the directory entry */ + if (p_fs->vol_type == EXFAT) { + if (p_dir->dir != p_fs->root_dir) { + size += p_fs->cluster_size; + + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry+1, §or); + if (!ep) + return -1; + p_fs->fs_func->set_entry_size(ep, size); + p_fs->fs_func->set_entry_flag(ep, p_dir->flags); + buf_modify(sb, sector); + + update_dir_checksum(sb, &(fid->dir), fid->entry); + } + } + + i_size_write(inode, i_size_read(inode)+p_fs->cluster_size); + EXFAT_I(inode)->mmu_private += p_fs->cluster_size; + EXFAT_I(inode)->fid.size += p_fs->cluster_size; + EXFAT_I(inode)->fid.flags = p_dir->flags; + inode->i_blocks += 1 << (p_fs->cluster_size_bits - 9); + } + + return dentry; +} /* end of find_empty_entry */ + +/* return values of fat_find_dir_entry() + >= 0 : return dir entiry position with the name in dir + -1 : (root dir, ".") it is the root dir itself + -2 : entry with the name does not exist */ +s32 fat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, s32 num_entries, DOS_NAME_T *p_dosname, u32 type) +{ + int i, dentry = 0, lossy = FALSE, len; + s32 order = 0, is_feasible_entry = TRUE, has_ext_entry = FALSE; + s32 dentries_per_clu; + u32 entry_type; + u16 entry_uniname[14], *uniname = NULL, unichar; + CHAIN_T clu; + DENTRY_T *ep; + DOS_DENTRY_T *dos_ep; + EXT_DENTRY_T *ext_ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (p_dir->dir == p_fs->root_dir) { + if ((!nls_uniname_cmp(sb, p_uniname->name, (u16 *) UNI_CUR_DIR_NAME)) || + (!nls_uniname_cmp(sb, p_uniname->name, (u16 *) UNI_PAR_DIR_NAME))) + return -1; // special case, root directory itself + } + + if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */ + dentries_per_clu = p_fs->dentries_in_root; + else + dentries_per_clu = p_fs->dentries_per_clu; + + clu.dir = p_dir->dir; + clu.flags = p_dir->flags; + + while (clu.dir != CLUSTER_32(~0)) { + if (p_fs->dev_ejected) + break; + + for (i = 0; i < dentries_per_clu; i++, dentry++) { + ep = get_entry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -2; + + entry_type = p_fs->fs_func->get_entry_type(ep); + + if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR)) { + if ((type == TYPE_ALL) || (type == entry_type)) { + if (is_feasible_entry && has_ext_entry) + return dentry; + + dos_ep = (DOS_DENTRY_T *) ep; + if ((!lossy) && (!nls_dosname_cmp(sb, p_dosname->name, dos_ep->name))) + return dentry; + } + is_feasible_entry = TRUE; + has_ext_entry = FALSE; + } else if (entry_type == TYPE_EXTEND) { + if (is_feasible_entry) { + ext_ep = (EXT_DENTRY_T *) ep; + if (ext_ep->order > 0x40) { + order = (s32)(ext_ep->order - 0x40); + uniname = p_uniname->name + 13 * (order-1); + } else { + order = (s32) ext_ep->order; + uniname -= 13; + } + + len = extract_uni_name_from_ext_entry(ext_ep, entry_uniname, order); + + unichar = *(uniname+len); + *(uniname+len) = 0x0; + + if (nls_uniname_cmp(sb, uniname, entry_uniname)) + is_feasible_entry = FALSE; + + *(uniname+len) = unichar; + } + has_ext_entry = TRUE; + } else if (entry_type == TYPE_UNUSED) { + return -2; + } else { + is_feasible_entry = TRUE; + has_ext_entry = FALSE; + } + } + + if (p_dir->dir == CLUSTER_32(0)) + break; /* FAT16 root_dir */ + + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) + return -2; + } + + return -2; +} /* end of fat_find_dir_entry */ + +/* return values of exfat_find_dir_entry() + >= 0 : return dir entiry position with the name in dir + -1 : (root dir, ".") it is the root dir itself + -2 : entry with the name does not exist */ +s32 exfat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, s32 num_entries, DOS_NAME_T *p_dosname, u32 type) +{ + int i, dentry = 0, num_ext_entries = 0, len; + s32 order = 0, is_feasible_entry = FALSE; + s32 dentries_per_clu, num_empty = 0; + u32 entry_type; + u16 entry_uniname[16], *uniname = NULL, unichar; + CHAIN_T clu; + DENTRY_T *ep; + FILE_DENTRY_T *file_ep; + STRM_DENTRY_T *strm_ep; + NAME_DENTRY_T *name_ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (p_dir->dir == p_fs->root_dir) { + if ((!nls_uniname_cmp(sb, p_uniname->name, (u16 *) UNI_CUR_DIR_NAME)) || + (!nls_uniname_cmp(sb, p_uniname->name, (u16 *) UNI_PAR_DIR_NAME))) + return -1; // special case, root directory itself + } + + if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */ + dentries_per_clu = p_fs->dentries_in_root; + else + dentries_per_clu = p_fs->dentries_per_clu; + + clu.dir = p_dir->dir; + clu.size = p_dir->size; + clu.flags = p_dir->flags; + + p_fs->hint_uentry.dir = p_dir->dir; + p_fs->hint_uentry.entry = -1; + + while (clu.dir != CLUSTER_32(~0)) { + if (p_fs->dev_ejected) + break; + + for (i = 0; i < dentries_per_clu; i++, dentry++) { + ep = get_entry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -2; + + entry_type = p_fs->fs_func->get_entry_type(ep); + + if ((entry_type == TYPE_UNUSED) || (entry_type == TYPE_DELETED)) { + is_feasible_entry = FALSE; + + if (p_fs->hint_uentry.entry == -1) { + num_empty++; + + if (num_empty == 1) { + p_fs->hint_uentry.clu.dir = clu.dir; + p_fs->hint_uentry.clu.size = clu.size; + p_fs->hint_uentry.clu.flags = clu.flags; + } + if ((num_empty >= num_entries) || (entry_type == TYPE_UNUSED)) + p_fs->hint_uentry.entry = dentry - (num_empty-1); + } + + if (entry_type == TYPE_UNUSED) + return -2; + } else { + num_empty = 0; + + if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR)) { + if ((type == TYPE_ALL) || (type == entry_type)) { + file_ep = (FILE_DENTRY_T *) ep; + num_ext_entries = file_ep->num_ext; + is_feasible_entry = TRUE; + } else { + is_feasible_entry = FALSE; + } + } else if (entry_type == TYPE_STREAM) { + if (is_feasible_entry) { + strm_ep = (STRM_DENTRY_T *) ep; + if (p_uniname->name_len == strm_ep->name_len) { + order = 1; + } else { + is_feasible_entry = FALSE; + } + } + } else if (entry_type == TYPE_EXTEND) { + if (is_feasible_entry) { + name_ep = (NAME_DENTRY_T *) ep; + + if ((++order) == 2) + uniname = p_uniname->name; + else + uniname += 15; + + len = extract_uni_name_from_name_entry(name_ep, entry_uniname, order); + + unichar = *(uniname+len); + *(uniname+len) = 0x0; + + if (nls_uniname_cmp(sb, uniname, entry_uniname)) { + is_feasible_entry = FALSE; + } else if (order == num_ext_entries) { + p_fs->hint_uentry.dir = CLUSTER_32(~0); + p_fs->hint_uentry.entry = -1; + return dentry - (num_ext_entries); + } + + *(uniname+len) = unichar; + } + } else { + is_feasible_entry = FALSE; + } + } + } + + if (p_dir->dir == CLUSTER_32(0)) + break; /* FAT16 root_dir */ + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUSTER_32(~0); + } else { + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) + return -2; + } + } + + return -2; +} /* end of exfat_find_dir_entry */ + +/* returns -1 on error */ +s32 fat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, s32 entry, DENTRY_T *p_entry) +{ + s32 count = 0; + u8 chksum; + DOS_DENTRY_T *dos_ep = (DOS_DENTRY_T *) p_entry; + EXT_DENTRY_T *ext_ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + chksum = calc_checksum_1byte((void *) dos_ep->name, DOS_NAME_LENGTH, 0); + + for (entry--; entry >= 0; entry--) { + ext_ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, NULL); + if (!ext_ep) + return -1; + + if ((p_fs->fs_func->get_entry_type((DENTRY_T *) ext_ep) == TYPE_EXTEND) && + (ext_ep->checksum == chksum)) { + count++; + if (ext_ep->order > 0x40) + return count; + } else { + return count; + } + } + + return count; +} /* end of fat_count_ext_entries */ + +/* returns -1 on error */ +s32 exfat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, s32 entry, DENTRY_T *p_entry) +{ + int i, count = 0; + u32 type; + FILE_DENTRY_T *file_ep = (FILE_DENTRY_T *) p_entry; + DENTRY_T *ext_ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + for (i = 0, entry++; i < file_ep->num_ext; i++, entry++) { + ext_ep = get_entry_in_dir(sb, p_dir, entry, NULL); + if (!ext_ep) + return -1; + + type = p_fs->fs_func->get_entry_type(ext_ep); + if ((type == TYPE_EXTEND) || (type == TYPE_STREAM)) + count++; + else + return count; + } + + return count; +} /* end of exfat_count_ext_entries */ + +/* returns -1 on error */ +s32 count_dos_name_entries(struct super_block *sb, CHAIN_T *p_dir, u32 type) +{ + int i, count = 0; + s32 dentries_per_clu; + u32 entry_type; + CHAIN_T clu; + DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */ + dentries_per_clu = p_fs->dentries_in_root; + else + dentries_per_clu = p_fs->dentries_per_clu; + + clu.dir = p_dir->dir; + clu.size = p_dir->size; + clu.flags = p_dir->flags; + + while (clu.dir != CLUSTER_32(~0)) { + if (p_fs->dev_ejected) + break; + + for (i = 0; i < dentries_per_clu; i++) { + ep = get_entry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -1; + + entry_type = p_fs->fs_func->get_entry_type(ep); + + if (entry_type == TYPE_UNUSED) + return count; + if (!(type & TYPE_CRITICAL_PRI) && !(type & TYPE_BENIGN_PRI)) + continue; + + if ((type == TYPE_ALL) || (type == entry_type)) + count++; + } + + if (p_dir->dir == CLUSTER_32(0)) + break; /* FAT16 root_dir */ + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUSTER_32(~0); + } else { + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) + return -1; + } + } + + return count; +} /* end of count_dos_name_entries */ + +bool is_dir_empty(struct super_block *sb, CHAIN_T *p_dir) +{ + int i, count = 0; + s32 dentries_per_clu; + u32 type; + CHAIN_T clu; + DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */ + dentries_per_clu = p_fs->dentries_in_root; + else + dentries_per_clu = p_fs->dentries_per_clu; + + clu.dir = p_dir->dir; + clu.size = p_dir->size; + clu.flags = p_dir->flags; + + while (clu.dir != CLUSTER_32(~0)) { + if (p_fs->dev_ejected) + break; + + for (i = 0; i < dentries_per_clu; i++) { + ep = get_entry_in_dir(sb, &clu, i, NULL); + if (!ep) + break; + + type = p_fs->fs_func->get_entry_type(ep); + + if (type == TYPE_UNUSED) + return TRUE; + if ((type != TYPE_FILE) && (type != TYPE_DIR)) + continue; + + if (p_dir->dir == CLUSTER_32(0)) { /* FAT16 root_dir */ + return FALSE; + } else { + if (p_fs->vol_type == EXFAT) + return FALSE; + if ((p_dir->dir == p_fs->root_dir) || ((++count) > 2)) + return FALSE; + } + } + + if (p_dir->dir == CLUSTER_32(0)) + break; /* FAT16 root_dir */ + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUSTER_32(~0); + } else { + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) + break; + } + } + + return TRUE; +} /* end of is_dir_empty */ + +/* + * Name Conversion Functions + */ + +/* input : dir, uni_name + output : num_of_entry, dos_name(format : aaaaaa~1.bbb) */ +s32 get_num_entries_and_dos_name(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, s32 *entries, DOS_NAME_T *p_dosname) +{ + s32 ret, num_entries, lossy = FALSE; + char **r; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + num_entries = p_fs->fs_func->calc_num_entries(p_uniname); + if (num_entries == 0) + return FFS_INVALIDPATH; + + if (p_fs->vol_type != EXFAT) { + nls_uniname_to_dosname(sb, p_dosname, p_uniname, &lossy); + + if (lossy) { + ret = fat_generate_dos_name(sb, p_dir, p_dosname); + if (ret) + return ret; + } else { + for (r = reserved_names; *r; r++) { + if (!strncmp((void *) p_dosname->name, *r, 8)) + return FFS_INVALIDPATH; + } + + if (p_dosname->name_case != 0xFF) + num_entries = 1; + } + + if (num_entries > 1) + p_dosname->name_case = 0x0; + } + + *entries = num_entries; + + return FFS_SUCCESS; +} /* end of get_num_entries_and_dos_name */ + +void get_uni_name_from_dos_entry(struct super_block *sb, DOS_DENTRY_T *ep, UNI_NAME_T *p_uniname, u8 mode) +{ + DOS_NAME_T dos_name; + + if (mode == 0x0) + dos_name.name_case = 0x0; + else + dos_name.name_case = ep->lcase; + + memcpy(dos_name.name, ep->name, DOS_NAME_LENGTH); + nls_dosname_to_uniname(sb, p_uniname, &dos_name); +} /* end of get_uni_name_from_dos_entry */ + +void fat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u16 *uniname) +{ + int i; + EXT_DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + for (entry--, i = 1; entry >= 0; entry--, i++) { + ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, NULL); + if (!ep) + return; + + if (p_fs->fs_func->get_entry_type((DENTRY_T *) ep) == TYPE_EXTEND) { + extract_uni_name_from_ext_entry(ep, uniname, i); + if (ep->order > 0x40) + return; + } else { + return; + } + + uniname += 13; + } +} /* end of fat_get_uni_name_from_ext_entry */ + +void exfat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u16 *uniname) +{ + int i; + DENTRY_T *ep; + ENTRY_SET_CACHE_T *es; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + es = get_entry_set_in_dir(sb, p_dir, entry, ES_ALL_ENTRIES, &ep); + if (es == NULL || es->num_entries < 3) { + if (es) + release_entry_set(es); + return; + } + + ep += 2; + + /* + * First entry : file entry + * Second entry : stream-extension entry + * Third entry : first file-name entry + * So, the index of first file-name dentry should start from 2. + */ + for (i = 2; i < es->num_entries; i++, ep++) { + if (p_fs->fs_func->get_entry_type(ep) == TYPE_EXTEND) + extract_uni_name_from_name_entry((NAME_DENTRY_T *)ep, uniname, i); + else + goto out; + uniname += 15; + } + +out: + release_entry_set(es); +} /* end of exfat_get_uni_name_from_ext_entry */ + +s32 extract_uni_name_from_ext_entry(EXT_DENTRY_T *ep, u16 *uniname, s32 order) +{ + int i, len = 0; + + for (i = 0; i < 10; i += 2) { + *uniname = GET16(ep->unicode_0_4+i); + if (*uniname == 0x0) + return len; + uniname++; + len++; + } + + if (order < 20) { + for (i = 0; i < 12; i += 2) { + *uniname = GET16_A(ep->unicode_5_10+i); + if (*uniname == 0x0) + return len; + uniname++; + len++; + } + } else { + for (i = 0; i < 8; i += 2) { + *uniname = GET16_A(ep->unicode_5_10+i); + if (*uniname == 0x0) + return len; + uniname++; + len++; + } + *uniname = 0x0; /* uniname[MAX_NAME_LENGTH-1] */ + return len; + } + + for (i = 0; i < 4; i += 2) { + *uniname = GET16_A(ep->unicode_11_12+i); + if (*uniname == 0x0) + return len; + uniname++; + len++; + } + + *uniname = 0x0; + return len; + +} /* end of extract_uni_name_from_ext_entry */ + +s32 extract_uni_name_from_name_entry(NAME_DENTRY_T *ep, u16 *uniname, s32 order) +{ + int i, len = 0; + + for (i = 0; i < 30; i += 2) { + *uniname = GET16_A(ep->unicode_0_14+i); + if (*uniname == 0x0) + return len; + uniname++; + len++; + } + + *uniname = 0x0; + return len; + +} /* end of extract_uni_name_from_name_entry */ + +s32 fat_generate_dos_name(struct super_block *sb, CHAIN_T *p_dir, DOS_NAME_T *p_dosname) +{ + int i, j, count = 0, count_begin = FALSE; + s32 dentries_per_clu; + u32 type; + u8 bmap[128/* 1 ~ 1023 */]; + CHAIN_T clu; + DOS_DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + memset(bmap, 0, sizeof bmap); + exfat_bitmap_set(bmap, 0); + + if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */ + dentries_per_clu = p_fs->dentries_in_root; + else + dentries_per_clu = p_fs->dentries_per_clu; + + clu.dir = p_dir->dir; + clu.flags = p_dir->flags; + + while (clu.dir != CLUSTER_32(~0)) { + if (p_fs->dev_ejected) + break; + + for (i = 0; i < dentries_per_clu; i++) { + ep = (DOS_DENTRY_T *) get_entry_in_dir(sb, &clu, i, NULL); + if (!ep) + return FFS_MEDIAERR; + + type = p_fs->fs_func->get_entry_type((DENTRY_T *) ep); + + if (type == TYPE_UNUSED) + break; + if ((type != TYPE_FILE) && (type != TYPE_DIR)) + continue; + + count = 0; + count_begin = FALSE; + + for (j = 0; j < 8; j++) { + if (ep->name[j] == ' ') + break; + + if (ep->name[j] == '~') { + count_begin = TRUE; + } else if (count_begin) { + if ((ep->name[j] >= '0') && (ep->name[j] <= '9')) { + count = count * 10 + (ep->name[j] - '0'); + } else { + count = 0; + count_begin = FALSE; + } + } + } + + if ((count > 0) && (count < 1024)) + exfat_bitmap_set(bmap, count); + } + + if (p_dir->dir == CLUSTER_32(0)) + break; /* FAT16 root_dir */ + + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) + return FFS_MEDIAERR; + } + + count = 0; + for (i = 0; i < 128; i++) { + if (bmap[i] != 0xFF) { + for (j = 0; j < 8; j++) { + if (exfat_bitmap_test(&(bmap[i]), j) == 0) { + count = (i << 3) + j; + break; + } + } + if (count != 0) + break; + } + } + + if ((count == 0) || (count >= 1024)) + return FFS_FILEEXIST; + else + fat_attach_count_to_dos_name(p_dosname->name, count); + + /* Now dos_name has DOS~????.EXT */ + return FFS_SUCCESS; +} /* end of generate_dos_name */ + +void fat_attach_count_to_dos_name(u8 *dosname, s32 count) +{ + int i, j, length; + char str_count[6]; + + snprintf(str_count, sizeof str_count, "~%d", count); + length = strlen(str_count); + + i = j = 0; + while (j <= (8 - length)) { + i = j; + if (dosname[j] == ' ') + break; + if (dosname[j] & 0x80) + j += 2; + else + j++; + } + + for (j = 0; j < length; i++, j++) + dosname[i] = (u8) str_count[j]; + + if (i == 7) + dosname[7] = ' '; + +} /* end of attach_count_to_dos_name */ + +s32 fat_calc_num_entries(UNI_NAME_T *p_uniname) +{ + s32 len; + + len = p_uniname->name_len; + if (len == 0) + return 0; + + /* 1 dos name entry + extended entries */ + return (len-1) / 13 + 2; + +} /* end of calc_num_enties */ + +s32 exfat_calc_num_entries(UNI_NAME_T *p_uniname) +{ + s32 len; + + len = p_uniname->name_len; + if (len == 0) + return 0; + + /* 1 file entry + 1 stream entry + name entries */ + return (len-1) / 15 + 3; + +} /* end of exfat_calc_num_enties */ + +u8 calc_checksum_1byte(void *data, s32 len, u8 chksum) +{ + int i; + u8 *c = (u8 *) data; + + for (i = 0; i < len; i++, c++) + chksum = (((chksum & 1) << 7) | ((chksum & 0xFE) >> 1)) + *c; + + return chksum; +} /* end of calc_checksum_1byte */ + +/***************************************************************** +aj for exfat add check sum of the fdt start +*******************************************************************/ + +const unsigned char anyka_exfat_updatecase[5836] = { + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, + 0x08, 0x00, 0x09, 0x00, 0x0A, 0x00, 0x0B, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x0E, 0x00, 0x0F, 0x00, + 0x10, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, + 0x18, 0x00, 0x19, 0x00, 0x1A, 0x00, 0x1B, 0x00, 0x1C, 0x00, 0x1D, 0x00, 0x1E, 0x00, 0x1F, 0x00, + 0x20, 0x00, 0x21, 0x00, 0x22, 0x00, 0x23, 0x00, 0x24, 0x00, 0x25, 0x00, 0x26, 0x00, 0x27, 0x00, + 0x28, 0x00, 0x29, 0x00, 0x2A, 0x00, 0x2B, 0x00, 0x2C, 0x00, 0x2D, 0x00, 0x2E, 0x00, 0x2F, 0x00, + 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, 0x37, 0x00, + 0x38, 0x00, 0x39, 0x00, 0x3A, 0x00, 0x3B, 0x00, 0x3C, 0x00, 0x3D, 0x00, 0x3E, 0x00, 0x3F, 0x00, + 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, + 0x48, 0x00, 0x49, 0x00, 0x4A, 0x00, 0x4B, 0x00, 0x4C, 0x00, 0x4D, 0x00, 0x4E, 0x00, 0x4F, 0x00, + 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, + 0x58, 0x00, 0x59, 0x00, 0x5A, 0x00, 0x5B, 0x00, 0x5C, 0x00, 0x5D, 0x00, 0x5E, 0x00, 0x5F, 0x00, + 0x60, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, + 0x48, 0x00, 0x49, 0x00, 0x4A, 0x00, 0x4B, 0x00, 0x4C, 0x00, 0x4D, 0x00, 0x4E, 0x00, 0x4F, 0x00, + 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, + 0x58, 0x00, 0x59, 0x00, 0x5A, 0x00, 0x7B, 0x00, 0x7C, 0x00, 0x7D, 0x00, 0x7E, 0x00, 0x7F, 0x00, + 0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85, 0x00, 0x86, 0x00, 0x87, 0x00, + 0x88, 0x00, 0x89, 0x00, 0x8A, 0x00, 0x8B, 0x00, 0x8C, 0x00, 0x8D, 0x00, 0x8E, 0x00, 0x8F, 0x00, + 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, + 0x98, 0x00, 0x99, 0x00, 0x9A, 0x00, 0x9B, 0x00, 0x9C, 0x00, 0x9D, 0x00, 0x9E, 0x00, 0x9F, 0x00, + 0xA0, 0x00, 0xA1, 0x00, 0xA2, 0x00, 0xA3, 0x00, 0xA4, 0x00, 0xA5, 0x00, 0xA6, 0x00, 0xA7, 0x00, + 0xA8, 0x00, 0xA9, 0x00, 0xAA, 0x00, 0xAB, 0x00, 0xAC, 0x00, 0xAD, 0x00, 0xAE, 0x00, 0xAF, 0x00, + 0xB0, 0x00, 0xB1, 0x00, 0xB2, 0x00, 0xB3, 0x00, 0xB4, 0x00, 0xB5, 0x00, 0xB6, 0x00, 0xB7, 0x00, + 0xB8, 0x00, 0xB9, 0x00, 0xBA, 0x00, 0xBB, 0x00, 0xBC, 0x00, 0xBD, 0x00, 0xBE, 0x00, 0xBF, 0x00, + 0xC0, 0x00, 0xC1, 0x00, 0xC2, 0x00, 0xC3, 0x00, 0xC4, 0x00, 0xC5, 0x00, 0xC6, 0x00, 0xC7, 0x00, + 0xC8, 0x00, 0xC9, 0x00, 0xCA, 0x00, 0xCB, 0x00, 0xCC, 0x00, 0xCD, 0x00, 0xCE, 0x00, 0xCF, 0x00, + 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD3, 0x00, 0xD4, 0x00, 0xD5, 0x00, 0xD6, 0x00, 0xD7, 0x00, + 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0xDD, 0x00, 0xDE, 0x00, 0xDF, 0x00, + 0xC0, 0x00, 0xC1, 0x00, 0xC2, 0x00, 0xC3, 0x00, 0xC4, 0x00, 0xC5, 0x00, 0xC6, 0x00, 0xC7, 0x00, + 0xC8, 0x00, 0xC9, 0x00, 0xCA, 0x00, 0xCB, 0x00, 0xCC, 0x00, 0xCD, 0x00, 0xCE, 0x00, 0xCF, 0x00, + 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD3, 0x00, 0xD4, 0x00, 0xD5, 0x00, 0xD6, 0x00, 0xF7, 0x00, + 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0xDD, 0x00, 0xDE, 0x00, 0x78, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x02, 0x01, 0x02, 0x01, 0x04, 0x01, 0x04, 0x01, 0x06, 0x01, 0x06, 0x01, + 0x08, 0x01, 0x08, 0x01, 0x0A, 0x01, 0x0A, 0x01, 0x0C, 0x01, 0x0C, 0x01, 0x0E, 0x01, 0x0E, 0x01, + 0x10, 0x01, 0x10, 0x01, 0x12, 0x01, 0x12, 0x01, 0x14, 0x01, 0x14, 0x01, 0x16, 0x01, 0x16, 0x01, + 0x18, 0x01, 0x18, 0x01, 0x1A, 0x01, 0x1A, 0x01, 0x1C, 0x01, 0x1C, 0x01, 0x1E, 0x01, 0x1E, 0x01, + 0x20, 0x01, 0x20, 0x01, 0x22, 0x01, 0x22, 0x01, 0x24, 0x01, 0x24, 0x01, 0x26, 0x01, 0x26, 0x01, + 0x28, 0x01, 0x28, 0x01, 0x2A, 0x01, 0x2A, 0x01, 0x2C, 0x01, 0x2C, 0x01, 0x2E, 0x01, 0x2E, 0x01, + 0x30, 0x01, 0x31, 0x01, 0x32, 0x01, 0x32, 0x01, 0x34, 0x01, 0x34, 0x01, 0x36, 0x01, 0x36, 0x01, + 0x38, 0x01, 0x39, 0x01, 0x39, 0x01, 0x3B, 0x01, 0x3B, 0x01, 0x3D, 0x01, 0x3D, 0x01, 0x3F, 0x01, + 0x3F, 0x01, 0x41, 0x01, 0x41, 0x01, 0x43, 0x01, 0x43, 0x01, 0x45, 0x01, 0x45, 0x01, 0x47, 0x01, + 0x47, 0x01, 0x49, 0x01, 0x4A, 0x01, 0x4A, 0x01, 0x4C, 0x01, 0x4C, 0x01, 0x4E, 0x01, 0x4E, 0x01, + 0x50, 0x01, 0x50, 0x01, 0x52, 0x01, 0x52, 0x01, 0x54, 0x01, 0x54, 0x01, 0x56, 0x01, 0x56, 0x01, + 0x58, 0x01, 0x58, 0x01, 0x5A, 0x01, 0x5A, 0x01, 0x5C, 0x01, 0x5C, 0x01, 0x5E, 0x01, 0x5E, 0x01, + 0x60, 0x01, 0x60, 0x01, 0x62, 0x01, 0x62, 0x01, 0x64, 0x01, 0x64, 0x01, 0x66, 0x01, 0x66, 0x01, + 0x68, 0x01, 0x68, 0x01, 0x6A, 0x01, 0x6A, 0x01, 0x6C, 0x01, 0x6C, 0x01, 0x6E, 0x01, 0x6E, 0x01, + 0x70, 0x01, 0x70, 0x01, 0x72, 0x01, 0x72, 0x01, 0x74, 0x01, 0x74, 0x01, 0x76, 0x01, 0x76, 0x01, + 0x78, 0x01, 0x79, 0x01, 0x79, 0x01, 0x7B, 0x01, 0x7B, 0x01, 0x7D, 0x01, 0x7D, 0x01, 0x7F, 0x01, + 0x43, 0x02, 0x81, 0x01, 0x82, 0x01, 0x82, 0x01, 0x84, 0x01, 0x84, 0x01, 0x86, 0x01, 0x87, 0x01, + 0x87, 0x01, 0x89, 0x01, 0x8A, 0x01, 0x8B, 0x01, 0x8B, 0x01, 0x8D, 0x01, 0x8E, 0x01, 0x8F, 0x01, + 0x90, 0x01, 0x91, 0x01, 0x91, 0x01, 0x93, 0x01, 0x94, 0x01, 0xF6, 0x01, 0x96, 0x01, 0x97, 0x01, + 0x98, 0x01, 0x98, 0x01, 0x3D, 0x02, 0x9B, 0x01, 0x9C, 0x01, 0x9D, 0x01, 0x20, 0x02, 0x9F, 0x01, + 0xA0, 0x01, 0xA0, 0x01, 0xA2, 0x01, 0xA2, 0x01, 0xA4, 0x01, 0xA4, 0x01, 0xA6, 0x01, 0xA7, 0x01, + 0xA7, 0x01, 0xA9, 0x01, 0xAA, 0x01, 0xAB, 0x01, 0xAC, 0x01, 0xAC, 0x01, 0xAE, 0x01, 0xAF, 0x01, + 0xAF, 0x01, 0xB1, 0x01, 0xB2, 0x01, 0xB3, 0x01, 0xB3, 0x01, 0xB5, 0x01, 0xB5, 0x01, 0xB7, 0x01, + 0xB8, 0x01, 0xB8, 0x01, 0xBA, 0x01, 0xBB, 0x01, 0xBC, 0x01, 0xBC, 0x01, 0xBE, 0x01, 0xF7, 0x01, + 0xC0, 0x01, 0xC1, 0x01, 0xC2, 0x01, 0xC3, 0x01, 0xC4, 0x01, 0xC5, 0x01, 0xC4, 0x01, 0xC7, 0x01, + 0xC8, 0x01, 0xC7, 0x01, 0xCA, 0x01, 0xCB, 0x01, 0xCA, 0x01, 0xCD, 0x01, 0xCD, 0x01, 0xCF, 0x01, + 0xCF, 0x01, 0xD1, 0x01, 0xD1, 0x01, 0xD3, 0x01, 0xD3, 0x01, 0xD5, 0x01, 0xD5, 0x01, 0xD7, 0x01, + 0xD7, 0x01, 0xD9, 0x01, 0xD9, 0x01, 0xDB, 0x01, 0xDB, 0x01, 0x8E, 0x01, 0xDE, 0x01, 0xDE, 0x01, + 0xE0, 0x01, 0xE0, 0x01, 0xE2, 0x01, 0xE2, 0x01, 0xE4, 0x01, 0xE4, 0x01, 0xE6, 0x01, 0xE6, 0x01, + 0xE8, 0x01, 0xE8, 0x01, 0xEA, 0x01, 0xEA, 0x01, 0xEC, 0x01, 0xEC, 0x01, 0xEE, 0x01, 0xEE, 0x01, + 0xF0, 0x01, 0xF1, 0x01, 0xF2, 0x01, 0xF1, 0x01, 0xF4, 0x01, 0xF4, 0x01, 0xF6, 0x01, 0xF7, 0x01, + 0xF8, 0x01, 0xF8, 0x01, 0xFA, 0x01, 0xFA, 0x01, 0xFC, 0x01, 0xFC, 0x01, 0xFE, 0x01, 0xFE, 0x01, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x04, 0x02, 0x04, 0x02, 0x06, 0x02, 0x06, 0x02, + 0x08, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x0A, 0x02, 0x0C, 0x02, 0x0C, 0x02, 0x0E, 0x02, 0x0E, 0x02, + 0x10, 0x02, 0x10, 0x02, 0x12, 0x02, 0x12, 0x02, 0x14, 0x02, 0x14, 0x02, 0x16, 0x02, 0x16, 0x02, + 0x18, 0x02, 0x18, 0x02, 0x1A, 0x02, 0x1A, 0x02, 0x1C, 0x02, 0x1C, 0x02, 0x1E, 0x02, 0x1E, 0x02, + 0x20, 0x02, 0x21, 0x02, 0x22, 0x02, 0x22, 0x02, 0x24, 0x02, 0x24, 0x02, 0x26, 0x02, 0x26, 0x02, + 0x28, 0x02, 0x28, 0x02, 0x2A, 0x02, 0x2A, 0x02, 0x2C, 0x02, 0x2C, 0x02, 0x2E, 0x02, 0x2E, 0x02, + 0x30, 0x02, 0x30, 0x02, 0x32, 0x02, 0x32, 0x02, 0x34, 0x02, 0x35, 0x02, 0x36, 0x02, 0x37, 0x02, + 0x38, 0x02, 0x39, 0x02, 0x65, 0x2C, 0x3B, 0x02, 0x3B, 0x02, 0x3D, 0x02, 0x66, 0x2C, 0x3F, 0x02, + 0x40, 0x02, 0x41, 0x02, 0x41, 0x02, 0x43, 0x02, 0x44, 0x02, 0x45, 0x02, 0x46, 0x02, 0x46, 0x02, + 0x48, 0x02, 0x48, 0x02, 0x4A, 0x02, 0x4A, 0x02, 0x4C, 0x02, 0x4C, 0x02, 0x4E, 0x02, 0x4E, 0x02, + 0x50, 0x02, 0x51, 0x02, 0x52, 0x02, 0x81, 0x01, 0x86, 0x01, 0x55, 0x02, 0x89, 0x01, 0x8A, 0x01, + 0x58, 0x02, 0x8F, 0x01, 0x5A, 0x02, 0x90, 0x01, 0x5C, 0x02, 0x5D, 0x02, 0x5E, 0x02, 0x5F, 0x02, + 0x93, 0x01, 0x61, 0x02, 0x62, 0x02, 0x94, 0x01, 0x64, 0x02, 0x65, 0x02, 0x66, 0x02, 0x67, 0x02, + 0x97, 0x01, 0x96, 0x01, 0x6A, 0x02, 0x62, 0x2C, 0x6C, 0x02, 0x6D, 0x02, 0x6E, 0x02, 0x9C, 0x01, + 0x70, 0x02, 0x71, 0x02, 0x9D, 0x01, 0x73, 0x02, 0x74, 0x02, 0x9F, 0x01, 0x76, 0x02, 0x77, 0x02, + 0x78, 0x02, 0x79, 0x02, 0x7A, 0x02, 0x7B, 0x02, 0x7C, 0x02, 0x64, 0x2C, 0x7E, 0x02, 0x7F, 0x02, + 0xA6, 0x01, 0x81, 0x02, 0x82, 0x02, 0xA9, 0x01, 0x84, 0x02, 0x85, 0x02, 0x86, 0x02, 0x87, 0x02, + 0xAE, 0x01, 0x44, 0x02, 0xB1, 0x01, 0xB2, 0x01, 0x45, 0x02, 0x8D, 0x02, 0x8E, 0x02, 0x8F, 0x02, + 0x90, 0x02, 0x91, 0x02, 0xB7, 0x01, 0x93, 0x02, 0x94, 0x02, 0x95, 0x02, 0x96, 0x02, 0x97, 0x02, + 0x98, 0x02, 0x99, 0x02, 0x9A, 0x02, 0x9B, 0x02, 0x9C, 0x02, 0x9D, 0x02, 0x9E, 0x02, 0x9F, 0x02, + 0xA0, 0x02, 0xA1, 0x02, 0xA2, 0x02, 0xA3, 0x02, 0xA4, 0x02, 0xA5, 0x02, 0xA6, 0x02, 0xA7, 0x02, + 0xA8, 0x02, 0xA9, 0x02, 0xAA, 0x02, 0xAB, 0x02, 0xAC, 0x02, 0xAD, 0x02, 0xAE, 0x02, 0xAF, 0x02, + 0xB0, 0x02, 0xB1, 0x02, 0xB2, 0x02, 0xB3, 0x02, 0xB4, 0x02, 0xB5, 0x02, 0xB6, 0x02, 0xB7, 0x02, + 0xB8, 0x02, 0xB9, 0x02, 0xBA, 0x02, 0xBB, 0x02, 0xBC, 0x02, 0xBD, 0x02, 0xBE, 0x02, 0xBF, 0x02, + 0xC0, 0x02, 0xC1, 0x02, 0xC2, 0x02, 0xC3, 0x02, 0xC4, 0x02, 0xC5, 0x02, 0xC6, 0x02, 0xC7, 0x02, + 0xC8, 0x02, 0xC9, 0x02, 0xCA, 0x02, 0xCB, 0x02, 0xCC, 0x02, 0xCD, 0x02, 0xCE, 0x02, 0xCF, 0x02, + 0xD0, 0x02, 0xD1, 0x02, 0xD2, 0x02, 0xD3, 0x02, 0xD4, 0x02, 0xD5, 0x02, 0xD6, 0x02, 0xD7, 0x02, + 0xD8, 0x02, 0xD9, 0x02, 0xDA, 0x02, 0xDB, 0x02, 0xDC, 0x02, 0xDD, 0x02, 0xDE, 0x02, 0xDF, 0x02, + 0xE0, 0x02, 0xE1, 0x02, 0xE2, 0x02, 0xE3, 0x02, 0xE4, 0x02, 0xE5, 0x02, 0xE6, 0x02, 0xE7, 0x02, + 0xE8, 0x02, 0xE9, 0x02, 0xEA, 0x02, 0xEB, 0x02, 0xEC, 0x02, 0xED, 0x02, 0xEE, 0x02, 0xEF, 0x02, + 0xF0, 0x02, 0xF1, 0x02, 0xF2, 0x02, 0xF3, 0x02, 0xF4, 0x02, 0xF5, 0x02, 0xF6, 0x02, 0xF7, 0x02, + 0xF8, 0x02, 0xF9, 0x02, 0xFA, 0x02, 0xFB, 0x02, 0xFC, 0x02, 0xFD, 0x02, 0xFE, 0x02, 0xFF, 0x02, + 0x00, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x03, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x07, 0x03, + 0x08, 0x03, 0x09, 0x03, 0x0A, 0x03, 0x0B, 0x03, 0x0C, 0x03, 0x0D, 0x03, 0x0E, 0x03, 0x0F, 0x03, + 0x10, 0x03, 0x11, 0x03, 0x12, 0x03, 0x13, 0x03, 0x14, 0x03, 0x15, 0x03, 0x16, 0x03, 0x17, 0x03, + 0x18, 0x03, 0x19, 0x03, 0x1A, 0x03, 0x1B, 0x03, 0x1C, 0x03, 0x1D, 0x03, 0x1E, 0x03, 0x1F, 0x03, + 0x20, 0x03, 0x21, 0x03, 0x22, 0x03, 0x23, 0x03, 0x24, 0x03, 0x25, 0x03, 0x26, 0x03, 0x27, 0x03, + 0x28, 0x03, 0x29, 0x03, 0x2A, 0x03, 0x2B, 0x03, 0x2C, 0x03, 0x2D, 0x03, 0x2E, 0x03, 0x2F, 0x03, + 0x30, 0x03, 0x31, 0x03, 0x32, 0x03, 0x33, 0x03, 0x34, 0x03, 0x35, 0x03, 0x36, 0x03, 0x37, 0x03, + 0x38, 0x03, 0x39, 0x03, 0x3A, 0x03, 0x3B, 0x03, 0x3C, 0x03, 0x3D, 0x03, 0x3E, 0x03, 0x3F, 0x03, + 0x40, 0x03, 0x41, 0x03, 0x42, 0x03, 0x43, 0x03, 0x44, 0x03, 0x45, 0x03, 0x46, 0x03, 0x47, 0x03, + 0x48, 0x03, 0x49, 0x03, 0x4A, 0x03, 0x4B, 0x03, 0x4C, 0x03, 0x4D, 0x03, 0x4E, 0x03, 0x4F, 0x03, + 0x50, 0x03, 0x51, 0x03, 0x52, 0x03, 0x53, 0x03, 0x54, 0x03, 0x55, 0x03, 0x56, 0x03, 0x57, 0x03, + 0x58, 0x03, 0x59, 0x03, 0x5A, 0x03, 0x5B, 0x03, 0x5C, 0x03, 0x5D, 0x03, 0x5E, 0x03, 0x5F, 0x03, + 0x60, 0x03, 0x61, 0x03, 0x62, 0x03, 0x63, 0x03, 0x64, 0x03, 0x65, 0x03, 0x66, 0x03, 0x67, 0x03, + 0x68, 0x03, 0x69, 0x03, 0x6A, 0x03, 0x6B, 0x03, 0x6C, 0x03, 0x6D, 0x03, 0x6E, 0x03, 0x6F, 0x03, + 0x70, 0x03, 0x71, 0x03, 0x72, 0x03, 0x73, 0x03, 0x74, 0x03, 0x75, 0x03, 0x76, 0x03, 0x77, 0x03, + 0x78, 0x03, 0x79, 0x03, 0x7A, 0x03, 0xFD, 0x03, 0xFE, 0x03, 0xFF, 0x03, 0x7E, 0x03, 0x7F, 0x03, + 0x80, 0x03, 0x81, 0x03, 0x82, 0x03, 0x83, 0x03, 0x84, 0x03, 0x85, 0x03, 0x86, 0x03, 0x87, 0x03, + 0x88, 0x03, 0x89, 0x03, 0x8A, 0x03, 0x8B, 0x03, 0x8C, 0x03, 0x8D, 0x03, 0x8E, 0x03, 0x8F, 0x03, + 0x90, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03, 0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03, + 0x98, 0x03, 0x99, 0x03, 0x9A, 0x03, 0x9B, 0x03, 0x9C, 0x03, 0x9D, 0x03, 0x9E, 0x03, 0x9F, 0x03, + 0xA0, 0x03, 0xA1, 0x03, 0xA2, 0x03, 0xA3, 0x03, 0xA4, 0x03, 0xA5, 0x03, 0xA6, 0x03, 0xA7, 0x03, + 0xA8, 0x03, 0xA9, 0x03, 0xAA, 0x03, 0xAB, 0x03, 0x86, 0x03, 0x88, 0x03, 0x89, 0x03, 0x8A, 0x03, + 0xB0, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03, 0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03, + 0x98, 0x03, 0x99, 0x03, 0x9A, 0x03, 0x9B, 0x03, 0x9C, 0x03, 0x9D, 0x03, 0x9E, 0x03, 0x9F, 0x03, + 0xA0, 0x03, 0xA1, 0x03, 0xA3, 0x03, 0xA3, 0x03, 0xA4, 0x03, 0xA5, 0x03, 0xA6, 0x03, 0xA7, 0x03, + 0xA8, 0x03, 0xA9, 0x03, 0xAA, 0x03, 0xAB, 0x03, 0x8C, 0x03, 0x8E, 0x03, 0x8F, 0x03, 0xCF, 0x03, + 0xD0, 0x03, 0xD1, 0x03, 0xD2, 0x03, 0xD3, 0x03, 0xD4, 0x03, 0xD5, 0x03, 0xD6, 0x03, 0xD7, 0x03, + 0xD8, 0x03, 0xD8, 0x03, 0xDA, 0x03, 0xDA, 0x03, 0xDC, 0x03, 0xDC, 0x03, 0xDE, 0x03, 0xDE, 0x03, + 0xE0, 0x03, 0xE0, 0x03, 0xE2, 0x03, 0xE2, 0x03, 0xE4, 0x03, 0xE4, 0x03, 0xE6, 0x03, 0xE6, 0x03, + 0xE8, 0x03, 0xE8, 0x03, 0xEA, 0x03, 0xEA, 0x03, 0xEC, 0x03, 0xEC, 0x03, 0xEE, 0x03, 0xEE, 0x03, + 0xF0, 0x03, 0xF1, 0x03, 0xF9, 0x03, 0xF3, 0x03, 0xF4, 0x03, 0xF5, 0x03, 0xF6, 0x03, 0xF7, 0x03, + 0xF7, 0x03, 0xF9, 0x03, 0xFA, 0x03, 0xFA, 0x03, 0xFC, 0x03, 0xFD, 0x03, 0xFE, 0x03, 0xFF, 0x03, + 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04, + 0x08, 0x04, 0x09, 0x04, 0x0A, 0x04, 0x0B, 0x04, 0x0C, 0x04, 0x0D, 0x04, 0x0E, 0x04, 0x0F, 0x04, + 0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04, 0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04, + 0x18, 0x04, 0x19, 0x04, 0x1A, 0x04, 0x1B, 0x04, 0x1C, 0x04, 0x1D, 0x04, 0x1E, 0x04, 0x1F, 0x04, + 0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04, 0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04, + 0x28, 0x04, 0x29, 0x04, 0x2A, 0x04, 0x2B, 0x04, 0x2C, 0x04, 0x2D, 0x04, 0x2E, 0x04, 0x2F, 0x04, + 0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04, 0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04, + 0x18, 0x04, 0x19, 0x04, 0x1A, 0x04, 0x1B, 0x04, 0x1C, 0x04, 0x1D, 0x04, 0x1E, 0x04, 0x1F, 0x04, + 0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04, 0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04, + 0x28, 0x04, 0x29, 0x04, 0x2A, 0x04, 0x2B, 0x04, 0x2C, 0x04, 0x2D, 0x04, 0x2E, 0x04, 0x2F, 0x04, + 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04, + 0x08, 0x04, 0x09, 0x04, 0x0A, 0x04, 0x0B, 0x04, 0x0C, 0x04, 0x0D, 0x04, 0x0E, 0x04, 0x0F, 0x04, + 0x60, 0x04, 0x60, 0x04, 0x62, 0x04, 0x62, 0x04, 0x64, 0x04, 0x64, 0x04, 0x66, 0x04, 0x66, 0x04, + 0x68, 0x04, 0x68, 0x04, 0x6A, 0x04, 0x6A, 0x04, 0x6C, 0x04, 0x6C, 0x04, 0x6E, 0x04, 0x6E, 0x04, + 0x70, 0x04, 0x70, 0x04, 0x72, 0x04, 0x72, 0x04, 0x74, 0x04, 0x74, 0x04, 0x76, 0x04, 0x76, 0x04, + 0x78, 0x04, 0x78, 0x04, 0x7A, 0x04, 0x7A, 0x04, 0x7C, 0x04, 0x7C, 0x04, 0x7E, 0x04, 0x7E, 0x04, + 0x80, 0x04, 0x80, 0x04, 0x82, 0x04, 0x83, 0x04, 0x84, 0x04, 0x85, 0x04, 0x86, 0x04, 0x87, 0x04, + 0x88, 0x04, 0x89, 0x04, 0x8A, 0x04, 0x8A, 0x04, 0x8C, 0x04, 0x8C, 0x04, 0x8E, 0x04, 0x8E, 0x04, + 0x90, 0x04, 0x90, 0x04, 0x92, 0x04, 0x92, 0x04, 0x94, 0x04, 0x94, 0x04, 0x96, 0x04, 0x96, 0x04, + 0x98, 0x04, 0x98, 0x04, 0x9A, 0x04, 0x9A, 0x04, 0x9C, 0x04, 0x9C, 0x04, 0x9E, 0x04, 0x9E, 0x04, + 0xA0, 0x04, 0xA0, 0x04, 0xA2, 0x04, 0xA2, 0x04, 0xA4, 0x04, 0xA4, 0x04, 0xA6, 0x04, 0xA6, 0x04, + 0xA8, 0x04, 0xA8, 0x04, 0xAA, 0x04, 0xAA, 0x04, 0xAC, 0x04, 0xAC, 0x04, 0xAE, 0x04, 0xAE, 0x04, + 0xB0, 0x04, 0xB0, 0x04, 0xB2, 0x04, 0xB2, 0x04, 0xB4, 0x04, 0xB4, 0x04, 0xB6, 0x04, 0xB6, 0x04, + 0xB8, 0x04, 0xB8, 0x04, 0xBA, 0x04, 0xBA, 0x04, 0xBC, 0x04, 0xBC, 0x04, 0xBE, 0x04, 0xBE, 0x04, + 0xC0, 0x04, 0xC1, 0x04, 0xC1, 0x04, 0xC3, 0x04, 0xC3, 0x04, 0xC5, 0x04, 0xC5, 0x04, 0xC7, 0x04, + 0xC7, 0x04, 0xC9, 0x04, 0xC9, 0x04, 0xCB, 0x04, 0xCB, 0x04, 0xCD, 0x04, 0xCD, 0x04, 0xC0, 0x04, + 0xD0, 0x04, 0xD0, 0x04, 0xD2, 0x04, 0xD2, 0x04, 0xD4, 0x04, 0xD4, 0x04, 0xD6, 0x04, 0xD6, 0x04, + 0xD8, 0x04, 0xD8, 0x04, 0xDA, 0x04, 0xDA, 0x04, 0xDC, 0x04, 0xDC, 0x04, 0xDE, 0x04, 0xDE, 0x04, + 0xE0, 0x04, 0xE0, 0x04, 0xE2, 0x04, 0xE2, 0x04, 0xE4, 0x04, 0xE4, 0x04, 0xE6, 0x04, 0xE6, 0x04, + 0xE8, 0x04, 0xE8, 0x04, 0xEA, 0x04, 0xEA, 0x04, 0xEC, 0x04, 0xEC, 0x04, 0xEE, 0x04, 0xEE, 0x04, + 0xF0, 0x04, 0xF0, 0x04, 0xF2, 0x04, 0xF2, 0x04, 0xF4, 0x04, 0xF4, 0x04, 0xF6, 0x04, 0xF6, 0x04, + 0xF8, 0x04, 0xF8, 0x04, 0xFA, 0x04, 0xFA, 0x04, 0xFC, 0x04, 0xFC, 0x04, 0xFE, 0x04, 0xFE, 0x04, + 0x00, 0x05, 0x00, 0x05, 0x02, 0x05, 0x02, 0x05, 0x04, 0x05, 0x04, 0x05, 0x06, 0x05, 0x06, 0x05, + 0x08, 0x05, 0x08, 0x05, 0x0A, 0x05, 0x0A, 0x05, 0x0C, 0x05, 0x0C, 0x05, 0x0E, 0x05, 0x0E, 0x05, + 0x10, 0x05, 0x10, 0x05, 0x12, 0x05, 0x12, 0x05, 0x14, 0x05, 0x15, 0x05, 0x16, 0x05, 0x17, 0x05, + 0x18, 0x05, 0x19, 0x05, 0x1A, 0x05, 0x1B, 0x05, 0x1C, 0x05, 0x1D, 0x05, 0x1E, 0x05, 0x1F, 0x05, + 0x20, 0x05, 0x21, 0x05, 0x22, 0x05, 0x23, 0x05, 0x24, 0x05, 0x25, 0x05, 0x26, 0x05, 0x27, 0x05, + 0x28, 0x05, 0x29, 0x05, 0x2A, 0x05, 0x2B, 0x05, 0x2C, 0x05, 0x2D, 0x05, 0x2E, 0x05, 0x2F, 0x05, + 0x30, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05, 0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05, + 0x38, 0x05, 0x39, 0x05, 0x3A, 0x05, 0x3B, 0x05, 0x3C, 0x05, 0x3D, 0x05, 0x3E, 0x05, 0x3F, 0x05, + 0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05, 0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05, + 0x48, 0x05, 0x49, 0x05, 0x4A, 0x05, 0x4B, 0x05, 0x4C, 0x05, 0x4D, 0x05, 0x4E, 0x05, 0x4F, 0x05, + 0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05, 0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0x57, 0x05, + 0x58, 0x05, 0x59, 0x05, 0x5A, 0x05, 0x5B, 0x05, 0x5C, 0x05, 0x5D, 0x05, 0x5E, 0x05, 0x5F, 0x05, + 0x60, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05, 0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05, + 0x38, 0x05, 0x39, 0x05, 0x3A, 0x05, 0x3B, 0x05, 0x3C, 0x05, 0x3D, 0x05, 0x3E, 0x05, 0x3F, 0x05, + 0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05, 0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05, + 0x48, 0x05, 0x49, 0x05, 0x4A, 0x05, 0x4B, 0x05, 0x4C, 0x05, 0x4D, 0x05, 0x4E, 0x05, 0x4F, 0x05, + 0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05, 0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0xFF, 0xFF, + 0xF6, 0x17, 0x63, 0x2C, 0x7E, 0x1D, 0x7F, 0x1D, 0x80, 0x1D, 0x81, 0x1D, 0x82, 0x1D, 0x83, 0x1D, + 0x84, 0x1D, 0x85, 0x1D, 0x86, 0x1D, 0x87, 0x1D, 0x88, 0x1D, 0x89, 0x1D, 0x8A, 0x1D, 0x8B, 0x1D, + 0x8C, 0x1D, 0x8D, 0x1D, 0x8E, 0x1D, 0x8F, 0x1D, 0x90, 0x1D, 0x91, 0x1D, 0x92, 0x1D, 0x93, 0x1D, + 0x94, 0x1D, 0x95, 0x1D, 0x96, 0x1D, 0x97, 0x1D, 0x98, 0x1D, 0x99, 0x1D, 0x9A, 0x1D, 0x9B, 0x1D, + 0x9C, 0x1D, 0x9D, 0x1D, 0x9E, 0x1D, 0x9F, 0x1D, 0xA0, 0x1D, 0xA1, 0x1D, 0xA2, 0x1D, 0xA3, 0x1D, + 0xA4, 0x1D, 0xA5, 0x1D, 0xA6, 0x1D, 0xA7, 0x1D, 0xA8, 0x1D, 0xA9, 0x1D, 0xAA, 0x1D, 0xAB, 0x1D, + 0xAC, 0x1D, 0xAD, 0x1D, 0xAE, 0x1D, 0xAF, 0x1D, 0xB0, 0x1D, 0xB1, 0x1D, 0xB2, 0x1D, 0xB3, 0x1D, + 0xB4, 0x1D, 0xB5, 0x1D, 0xB6, 0x1D, 0xB7, 0x1D, 0xB8, 0x1D, 0xB9, 0x1D, 0xBA, 0x1D, 0xBB, 0x1D, + 0xBC, 0x1D, 0xBD, 0x1D, 0xBE, 0x1D, 0xBF, 0x1D, 0xC0, 0x1D, 0xC1, 0x1D, 0xC2, 0x1D, 0xC3, 0x1D, + 0xC4, 0x1D, 0xC5, 0x1D, 0xC6, 0x1D, 0xC7, 0x1D, 0xC8, 0x1D, 0xC9, 0x1D, 0xCA, 0x1D, 0xCB, 0x1D, + 0xCC, 0x1D, 0xCD, 0x1D, 0xCE, 0x1D, 0xCF, 0x1D, 0xD0, 0x1D, 0xD1, 0x1D, 0xD2, 0x1D, 0xD3, 0x1D, + 0xD4, 0x1D, 0xD5, 0x1D, 0xD6, 0x1D, 0xD7, 0x1D, 0xD8, 0x1D, 0xD9, 0x1D, 0xDA, 0x1D, 0xDB, 0x1D, + 0xDC, 0x1D, 0xDD, 0x1D, 0xDE, 0x1D, 0xDF, 0x1D, 0xE0, 0x1D, 0xE1, 0x1D, 0xE2, 0x1D, 0xE3, 0x1D, + 0xE4, 0x1D, 0xE5, 0x1D, 0xE6, 0x1D, 0xE7, 0x1D, 0xE8, 0x1D, 0xE9, 0x1D, 0xEA, 0x1D, 0xEB, 0x1D, + 0xEC, 0x1D, 0xED, 0x1D, 0xEE, 0x1D, 0xEF, 0x1D, 0xF0, 0x1D, 0xF1, 0x1D, 0xF2, 0x1D, 0xF3, 0x1D, + 0xF4, 0x1D, 0xF5, 0x1D, 0xF6, 0x1D, 0xF7, 0x1D, 0xF8, 0x1D, 0xF9, 0x1D, 0xFA, 0x1D, 0xFB, 0x1D, + 0xFC, 0x1D, 0xFD, 0x1D, 0xFE, 0x1D, 0xFF, 0x1D, 0x00, 0x1E, 0x00, 0x1E, 0x02, 0x1E, 0x02, 0x1E, + 0x04, 0x1E, 0x04, 0x1E, 0x06, 0x1E, 0x06, 0x1E, 0x08, 0x1E, 0x08, 0x1E, 0x0A, 0x1E, 0x0A, 0x1E, + 0x0C, 0x1E, 0x0C, 0x1E, 0x0E, 0x1E, 0x0E, 0x1E, 0x10, 0x1E, 0x10, 0x1E, 0x12, 0x1E, 0x12, 0x1E, + 0x14, 0x1E, 0x14, 0x1E, 0x16, 0x1E, 0x16, 0x1E, 0x18, 0x1E, 0x18, 0x1E, 0x1A, 0x1E, 0x1A, 0x1E, + 0x1C, 0x1E, 0x1C, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x20, 0x1E, 0x20, 0x1E, 0x22, 0x1E, 0x22, 0x1E, + 0x24, 0x1E, 0x24, 0x1E, 0x26, 0x1E, 0x26, 0x1E, 0x28, 0x1E, 0x28, 0x1E, 0x2A, 0x1E, 0x2A, 0x1E, + 0x2C, 0x1E, 0x2C, 0x1E, 0x2E, 0x1E, 0x2E, 0x1E, 0x30, 0x1E, 0x30, 0x1E, 0x32, 0x1E, 0x32, 0x1E, + 0x34, 0x1E, 0x34, 0x1E, 0x36, 0x1E, 0x36, 0x1E, 0x38, 0x1E, 0x38, 0x1E, 0x3A, 0x1E, 0x3A, 0x1E, + 0x3C, 0x1E, 0x3C, 0x1E, 0x3E, 0x1E, 0x3E, 0x1E, 0x40, 0x1E, 0x40, 0x1E, 0x42, 0x1E, 0x42, 0x1E, + 0x44, 0x1E, 0x44, 0x1E, 0x46, 0x1E, 0x46, 0x1E, 0x48, 0x1E, 0x48, 0x1E, 0x4A, 0x1E, 0x4A, 0x1E, + 0x4C, 0x1E, 0x4C, 0x1E, 0x4E, 0x1E, 0x4E, 0x1E, 0x50, 0x1E, 0x50, 0x1E, 0x52, 0x1E, 0x52, 0x1E, + 0x54, 0x1E, 0x54, 0x1E, 0x56, 0x1E, 0x56, 0x1E, 0x58, 0x1E, 0x58, 0x1E, 0x5A, 0x1E, 0x5A, 0x1E, + 0x5C, 0x1E, 0x5C, 0x1E, 0x5E, 0x1E, 0x5E, 0x1E, 0x60, 0x1E, 0x60, 0x1E, 0x62, 0x1E, 0x62, 0x1E, + 0x64, 0x1E, 0x64, 0x1E, 0x66, 0x1E, 0x66, 0x1E, 0x68, 0x1E, 0x68, 0x1E, 0x6A, 0x1E, 0x6A, 0x1E, + 0x6C, 0x1E, 0x6C, 0x1E, 0x6E, 0x1E, 0x6E, 0x1E, 0x70, 0x1E, 0x70, 0x1E, 0x72, 0x1E, 0x72, 0x1E, + 0x74, 0x1E, 0x74, 0x1E, 0x76, 0x1E, 0x76, 0x1E, 0x78, 0x1E, 0x78, 0x1E, 0x7A, 0x1E, 0x7A, 0x1E, + 0x7C, 0x1E, 0x7C, 0x1E, 0x7E, 0x1E, 0x7E, 0x1E, 0x80, 0x1E, 0x80, 0x1E, 0x82, 0x1E, 0x82, 0x1E, + 0x84, 0x1E, 0x84, 0x1E, 0x86, 0x1E, 0x86, 0x1E, 0x88, 0x1E, 0x88, 0x1E, 0x8A, 0x1E, 0x8A, 0x1E, + 0x8C, 0x1E, 0x8C, 0x1E, 0x8E, 0x1E, 0x8E, 0x1E, 0x90, 0x1E, 0x90, 0x1E, 0x92, 0x1E, 0x92, 0x1E, + 0x94, 0x1E, 0x94, 0x1E, 0x96, 0x1E, 0x97, 0x1E, 0x98, 0x1E, 0x99, 0x1E, 0x9A, 0x1E, 0x9B, 0x1E, + 0x9C, 0x1E, 0x9D, 0x1E, 0x9E, 0x1E, 0x9F, 0x1E, 0xA0, 0x1E, 0xA0, 0x1E, 0xA2, 0x1E, 0xA2, 0x1E, + 0xA4, 0x1E, 0xA4, 0x1E, 0xA6, 0x1E, 0xA6, 0x1E, 0xA8, 0x1E, 0xA8, 0x1E, 0xAA, 0x1E, 0xAA, 0x1E, + 0xAC, 0x1E, 0xAC, 0x1E, 0xAE, 0x1E, 0xAE, 0x1E, 0xB0, 0x1E, 0xB0, 0x1E, 0xB2, 0x1E, 0xB2, 0x1E, + 0xB4, 0x1E, 0xB4, 0x1E, 0xB6, 0x1E, 0xB6, 0x1E, 0xB8, 0x1E, 0xB8, 0x1E, 0xBA, 0x1E, 0xBA, 0x1E, + 0xBC, 0x1E, 0xBC, 0x1E, 0xBE, 0x1E, 0xBE, 0x1E, 0xC0, 0x1E, 0xC0, 0x1E, 0xC2, 0x1E, 0xC2, 0x1E, + 0xC4, 0x1E, 0xC4, 0x1E, 0xC6, 0x1E, 0xC6, 0x1E, 0xC8, 0x1E, 0xC8, 0x1E, 0xCA, 0x1E, 0xCA, 0x1E, + 0xCC, 0x1E, 0xCC, 0x1E, 0xCE, 0x1E, 0xCE, 0x1E, 0xD0, 0x1E, 0xD0, 0x1E, 0xD2, 0x1E, 0xD2, 0x1E, + 0xD4, 0x1E, 0xD4, 0x1E, 0xD6, 0x1E, 0xD6, 0x1E, 0xD8, 0x1E, 0xD8, 0x1E, 0xDA, 0x1E, 0xDA, 0x1E, + 0xDC, 0x1E, 0xDC, 0x1E, 0xDE, 0x1E, 0xDE, 0x1E, 0xE0, 0x1E, 0xE0, 0x1E, 0xE2, 0x1E, 0xE2, 0x1E, + 0xE4, 0x1E, 0xE4, 0x1E, 0xE6, 0x1E, 0xE6, 0x1E, 0xE8, 0x1E, 0xE8, 0x1E, 0xEA, 0x1E, 0xEA, 0x1E, + 0xEC, 0x1E, 0xEC, 0x1E, 0xEE, 0x1E, 0xEE, 0x1E, 0xF0, 0x1E, 0xF0, 0x1E, 0xF2, 0x1E, 0xF2, 0x1E, + 0xF4, 0x1E, 0xF4, 0x1E, 0xF6, 0x1E, 0xF6, 0x1E, 0xF8, 0x1E, 0xF8, 0x1E, 0xFA, 0x1E, 0xFB, 0x1E, + 0xFC, 0x1E, 0xFD, 0x1E, 0xFE, 0x1E, 0xFF, 0x1E, 0x08, 0x1F, 0x09, 0x1F, 0x0A, 0x1F, 0x0B, 0x1F, + 0x0C, 0x1F, 0x0D, 0x1F, 0x0E, 0x1F, 0x0F, 0x1F, 0x08, 0x1F, 0x09, 0x1F, 0x0A, 0x1F, 0x0B, 0x1F, + 0x0C, 0x1F, 0x0D, 0x1F, 0x0E, 0x1F, 0x0F, 0x1F, 0x18, 0x1F, 0x19, 0x1F, 0x1A, 0x1F, 0x1B, 0x1F, + 0x1C, 0x1F, 0x1D, 0x1F, 0x16, 0x1F, 0x17, 0x1F, 0x18, 0x1F, 0x19, 0x1F, 0x1A, 0x1F, 0x1B, 0x1F, + 0x1C, 0x1F, 0x1D, 0x1F, 0x1E, 0x1F, 0x1F, 0x1F, 0x28, 0x1F, 0x29, 0x1F, 0x2A, 0x1F, 0x2B, 0x1F, + 0x2C, 0x1F, 0x2D, 0x1F, 0x2E, 0x1F, 0x2F, 0x1F, 0x28, 0x1F, 0x29, 0x1F, 0x2A, 0x1F, 0x2B, 0x1F, + 0x2C, 0x1F, 0x2D, 0x1F, 0x2E, 0x1F, 0x2F, 0x1F, 0x38, 0x1F, 0x39, 0x1F, 0x3A, 0x1F, 0x3B, 0x1F, + 0x3C, 0x1F, 0x3D, 0x1F, 0x3E, 0x1F, 0x3F, 0x1F, 0x38, 0x1F, 0x39, 0x1F, 0x3A, 0x1F, 0x3B, 0x1F, + 0x3C, 0x1F, 0x3D, 0x1F, 0x3E, 0x1F, 0x3F, 0x1F, 0x48, 0x1F, 0x49, 0x1F, 0x4A, 0x1F, 0x4B, 0x1F, + 0x4C, 0x1F, 0x4D, 0x1F, 0x46, 0x1F, 0x47, 0x1F, 0x48, 0x1F, 0x49, 0x1F, 0x4A, 0x1F, 0x4B, 0x1F, + 0x4C, 0x1F, 0x4D, 0x1F, 0x4E, 0x1F, 0x4F, 0x1F, 0x50, 0x1F, 0x59, 0x1F, 0x52, 0x1F, 0x5B, 0x1F, + 0x54, 0x1F, 0x5D, 0x1F, 0x56, 0x1F, 0x5F, 0x1F, 0x58, 0x1F, 0x59, 0x1F, 0x5A, 0x1F, 0x5B, 0x1F, + 0x5C, 0x1F, 0x5D, 0x1F, 0x5E, 0x1F, 0x5F, 0x1F, 0x68, 0x1F, 0x69, 0x1F, 0x6A, 0x1F, 0x6B, 0x1F, + 0x6C, 0x1F, 0x6D, 0x1F, 0x6E, 0x1F, 0x6F, 0x1F, 0x68, 0x1F, 0x69, 0x1F, 0x6A, 0x1F, 0x6B, 0x1F, + 0x6C, 0x1F, 0x6D, 0x1F, 0x6E, 0x1F, 0x6F, 0x1F, 0xBA, 0x1F, 0xBB, 0x1F, 0xC8, 0x1F, 0xC9, 0x1F, + 0xCA, 0x1F, 0xCB, 0x1F, 0xDA, 0x1F, 0xDB, 0x1F, 0xF8, 0x1F, 0xF9, 0x1F, 0xEA, 0x1F, 0xEB, 0x1F, + 0xFA, 0x1F, 0xFB, 0x1F, 0x7E, 0x1F, 0x7F, 0x1F, 0x88, 0x1F, 0x89, 0x1F, 0x8A, 0x1F, 0x8B, 0x1F, + 0x8C, 0x1F, 0x8D, 0x1F, 0x8E, 0x1F, 0x8F, 0x1F, 0x88, 0x1F, 0x89, 0x1F, 0x8A, 0x1F, 0x8B, 0x1F, + 0x8C, 0x1F, 0x8D, 0x1F, 0x8E, 0x1F, 0x8F, 0x1F, 0x98, 0x1F, 0x99, 0x1F, 0x9A, 0x1F, 0x9B, 0x1F, + 0x9C, 0x1F, 0x9D, 0x1F, 0x9E, 0x1F, 0x9F, 0x1F, 0x98, 0x1F, 0x99, 0x1F, 0x9A, 0x1F, 0x9B, 0x1F, + 0x9C, 0x1F, 0x9D, 0x1F, 0x9E, 0x1F, 0x9F, 0x1F, 0xA8, 0x1F, 0xA9, 0x1F, 0xAA, 0x1F, 0xAB, 0x1F, + 0xAC, 0x1F, 0xAD, 0x1F, 0xAE, 0x1F, 0xAF, 0x1F, 0xA8, 0x1F, 0xA9, 0x1F, 0xAA, 0x1F, 0xAB, 0x1F, + 0xAC, 0x1F, 0xAD, 0x1F, 0xAE, 0x1F, 0xAF, 0x1F, 0xB8, 0x1F, 0xB9, 0x1F, 0xB2, 0x1F, 0xBC, 0x1F, + 0xB4, 0x1F, 0xB5, 0x1F, 0xB6, 0x1F, 0xB7, 0x1F, 0xB8, 0x1F, 0xB9, 0x1F, 0xBA, 0x1F, 0xBB, 0x1F, + 0xBC, 0x1F, 0xBD, 0x1F, 0xBE, 0x1F, 0xBF, 0x1F, 0xC0, 0x1F, 0xC1, 0x1F, 0xC2, 0x1F, 0xC3, 0x1F, + 0xC4, 0x1F, 0xC5, 0x1F, 0xC6, 0x1F, 0xC7, 0x1F, 0xC8, 0x1F, 0xC9, 0x1F, 0xCA, 0x1F, 0xCB, 0x1F, + 0xC3, 0x1F, 0xCD, 0x1F, 0xCE, 0x1F, 0xCF, 0x1F, 0xD8, 0x1F, 0xD9, 0x1F, 0xD2, 0x1F, 0xD3, 0x1F, + 0xD4, 0x1F, 0xD5, 0x1F, 0xD6, 0x1F, 0xD7, 0x1F, 0xD8, 0x1F, 0xD9, 0x1F, 0xDA, 0x1F, 0xDB, 0x1F, + 0xDC, 0x1F, 0xDD, 0x1F, 0xDE, 0x1F, 0xDF, 0x1F, 0xE8, 0x1F, 0xE9, 0x1F, 0xE2, 0x1F, 0xE3, 0x1F, + 0xE4, 0x1F, 0xEC, 0x1F, 0xE6, 0x1F, 0xE7, 0x1F, 0xE8, 0x1F, 0xE9, 0x1F, 0xEA, 0x1F, 0xEB, 0x1F, + 0xEC, 0x1F, 0xED, 0x1F, 0xEE, 0x1F, 0xEF, 0x1F, 0xF0, 0x1F, 0xF1, 0x1F, 0xF2, 0x1F, 0xF3, 0x1F, + 0xF4, 0x1F, 0xF5, 0x1F, 0xF6, 0x1F, 0xF7, 0x1F, 0xF8, 0x1F, 0xF9, 0x1F, 0xFA, 0x1F, 0xFB, 0x1F, + 0xF3, 0x1F, 0xFD, 0x1F, 0xFE, 0x1F, 0xFF, 0x1F, 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x20, + 0x04, 0x20, 0x05, 0x20, 0x06, 0x20, 0x07, 0x20, 0x08, 0x20, 0x09, 0x20, 0x0A, 0x20, 0x0B, 0x20, + 0x0C, 0x20, 0x0D, 0x20, 0x0E, 0x20, 0x0F, 0x20, 0x10, 0x20, 0x11, 0x20, 0x12, 0x20, 0x13, 0x20, + 0x14, 0x20, 0x15, 0x20, 0x16, 0x20, 0x17, 0x20, 0x18, 0x20, 0x19, 0x20, 0x1A, 0x20, 0x1B, 0x20, + 0x1C, 0x20, 0x1D, 0x20, 0x1E, 0x20, 0x1F, 0x20, 0x20, 0x20, 0x21, 0x20, 0x22, 0x20, 0x23, 0x20, + 0x24, 0x20, 0x25, 0x20, 0x26, 0x20, 0x27, 0x20, 0x28, 0x20, 0x29, 0x20, 0x2A, 0x20, 0x2B, 0x20, + 0x2C, 0x20, 0x2D, 0x20, 0x2E, 0x20, 0x2F, 0x20, 0x30, 0x20, 0x31, 0x20, 0x32, 0x20, 0x33, 0x20, + 0x34, 0x20, 0x35, 0x20, 0x36, 0x20, 0x37, 0x20, 0x38, 0x20, 0x39, 0x20, 0x3A, 0x20, 0x3B, 0x20, + 0x3C, 0x20, 0x3D, 0x20, 0x3E, 0x20, 0x3F, 0x20, 0x40, 0x20, 0x41, 0x20, 0x42, 0x20, 0x43, 0x20, + 0x44, 0x20, 0x45, 0x20, 0x46, 0x20, 0x47, 0x20, 0x48, 0x20, 0x49, 0x20, 0x4A, 0x20, 0x4B, 0x20, + 0x4C, 0x20, 0x4D, 0x20, 0x4E, 0x20, 0x4F, 0x20, 0x50, 0x20, 0x51, 0x20, 0x52, 0x20, 0x53, 0x20, + 0x54, 0x20, 0x55, 0x20, 0x56, 0x20, 0x57, 0x20, 0x58, 0x20, 0x59, 0x20, 0x5A, 0x20, 0x5B, 0x20, + 0x5C, 0x20, 0x5D, 0x20, 0x5E, 0x20, 0x5F, 0x20, 0x60, 0x20, 0x61, 0x20, 0x62, 0x20, 0x63, 0x20, + 0x64, 0x20, 0x65, 0x20, 0x66, 0x20, 0x67, 0x20, 0x68, 0x20, 0x69, 0x20, 0x6A, 0x20, 0x6B, 0x20, + 0x6C, 0x20, 0x6D, 0x20, 0x6E, 0x20, 0x6F, 0x20, 0x70, 0x20, 0x71, 0x20, 0x72, 0x20, 0x73, 0x20, + 0x74, 0x20, 0x75, 0x20, 0x76, 0x20, 0x77, 0x20, 0x78, 0x20, 0x79, 0x20, 0x7A, 0x20, 0x7B, 0x20, + 0x7C, 0x20, 0x7D, 0x20, 0x7E, 0x20, 0x7F, 0x20, 0x80, 0x20, 0x81, 0x20, 0x82, 0x20, 0x83, 0x20, + 0x84, 0x20, 0x85, 0x20, 0x86, 0x20, 0x87, 0x20, 0x88, 0x20, 0x89, 0x20, 0x8A, 0x20, 0x8B, 0x20, + 0x8C, 0x20, 0x8D, 0x20, 0x8E, 0x20, 0x8F, 0x20, 0x90, 0x20, 0x91, 0x20, 0x92, 0x20, 0x93, 0x20, + 0x94, 0x20, 0x95, 0x20, 0x96, 0x20, 0x97, 0x20, 0x98, 0x20, 0x99, 0x20, 0x9A, 0x20, 0x9B, 0x20, + 0x9C, 0x20, 0x9D, 0x20, 0x9E, 0x20, 0x9F, 0x20, 0xA0, 0x20, 0xA1, 0x20, 0xA2, 0x20, 0xA3, 0x20, + 0xA4, 0x20, 0xA5, 0x20, 0xA6, 0x20, 0xA7, 0x20, 0xA8, 0x20, 0xA9, 0x20, 0xAA, 0x20, 0xAB, 0x20, + 0xAC, 0x20, 0xAD, 0x20, 0xAE, 0x20, 0xAF, 0x20, 0xB0, 0x20, 0xB1, 0x20, 0xB2, 0x20, 0xB3, 0x20, + 0xB4, 0x20, 0xB5, 0x20, 0xB6, 0x20, 0xB7, 0x20, 0xB8, 0x20, 0xB9, 0x20, 0xBA, 0x20, 0xBB, 0x20, + 0xBC, 0x20, 0xBD, 0x20, 0xBE, 0x20, 0xBF, 0x20, 0xC0, 0x20, 0xC1, 0x20, 0xC2, 0x20, 0xC3, 0x20, + 0xC4, 0x20, 0xC5, 0x20, 0xC6, 0x20, 0xC7, 0x20, 0xC8, 0x20, 0xC9, 0x20, 0xCA, 0x20, 0xCB, 0x20, + 0xCC, 0x20, 0xCD, 0x20, 0xCE, 0x20, 0xCF, 0x20, 0xD0, 0x20, 0xD1, 0x20, 0xD2, 0x20, 0xD3, 0x20, + 0xD4, 0x20, 0xD5, 0x20, 0xD6, 0x20, 0xD7, 0x20, 0xD8, 0x20, 0xD9, 0x20, 0xDA, 0x20, 0xDB, 0x20, + 0xDC, 0x20, 0xDD, 0x20, 0xDE, 0x20, 0xDF, 0x20, 0xE0, 0x20, 0xE1, 0x20, 0xE2, 0x20, 0xE3, 0x20, + 0xE4, 0x20, 0xE5, 0x20, 0xE6, 0x20, 0xE7, 0x20, 0xE8, 0x20, 0xE9, 0x20, 0xEA, 0x20, 0xEB, 0x20, + 0xEC, 0x20, 0xED, 0x20, 0xEE, 0x20, 0xEF, 0x20, 0xF0, 0x20, 0xF1, 0x20, 0xF2, 0x20, 0xF3, 0x20, + 0xF4, 0x20, 0xF5, 0x20, 0xF6, 0x20, 0xF7, 0x20, 0xF8, 0x20, 0xF9, 0x20, 0xFA, 0x20, 0xFB, 0x20, + 0xFC, 0x20, 0xFD, 0x20, 0xFE, 0x20, 0xFF, 0x20, 0x00, 0x21, 0x01, 0x21, 0x02, 0x21, 0x03, 0x21, + 0x04, 0x21, 0x05, 0x21, 0x06, 0x21, 0x07, 0x21, 0x08, 0x21, 0x09, 0x21, 0x0A, 0x21, 0x0B, 0x21, + 0x0C, 0x21, 0x0D, 0x21, 0x0E, 0x21, 0x0F, 0x21, 0x10, 0x21, 0x11, 0x21, 0x12, 0x21, 0x13, 0x21, + 0x14, 0x21, 0x15, 0x21, 0x16, 0x21, 0x17, 0x21, 0x18, 0x21, 0x19, 0x21, 0x1A, 0x21, 0x1B, 0x21, + 0x1C, 0x21, 0x1D, 0x21, 0x1E, 0x21, 0x1F, 0x21, 0x20, 0x21, 0x21, 0x21, 0x22, 0x21, 0x23, 0x21, + 0x24, 0x21, 0x25, 0x21, 0x26, 0x21, 0x27, 0x21, 0x28, 0x21, 0x29, 0x21, 0x2A, 0x21, 0x2B, 0x21, + 0x2C, 0x21, 0x2D, 0x21, 0x2E, 0x21, 0x2F, 0x21, 0x30, 0x21, 0x31, 0x21, 0x32, 0x21, 0x33, 0x21, + 0x34, 0x21, 0x35, 0x21, 0x36, 0x21, 0x37, 0x21, 0x38, 0x21, 0x39, 0x21, 0x3A, 0x21, 0x3B, 0x21, + 0x3C, 0x21, 0x3D, 0x21, 0x3E, 0x21, 0x3F, 0x21, 0x40, 0x21, 0x41, 0x21, 0x42, 0x21, 0x43, 0x21, + 0x44, 0x21, 0x45, 0x21, 0x46, 0x21, 0x47, 0x21, 0x48, 0x21, 0x49, 0x21, 0x4A, 0x21, 0x4B, 0x21, + 0x4C, 0x21, 0x4D, 0x21, 0x32, 0x21, 0x4F, 0x21, 0x50, 0x21, 0x51, 0x21, 0x52, 0x21, 0x53, 0x21, + 0x54, 0x21, 0x55, 0x21, 0x56, 0x21, 0x57, 0x21, 0x58, 0x21, 0x59, 0x21, 0x5A, 0x21, 0x5B, 0x21, + 0x5C, 0x21, 0x5D, 0x21, 0x5E, 0x21, 0x5F, 0x21, 0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21, + 0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21, 0x68, 0x21, 0x69, 0x21, 0x6A, 0x21, 0x6B, 0x21, + 0x6C, 0x21, 0x6D, 0x21, 0x6E, 0x21, 0x6F, 0x21, 0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21, + 0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21, 0x68, 0x21, 0x69, 0x21, 0x6A, 0x21, 0x6B, 0x21, + 0x6C, 0x21, 0x6D, 0x21, 0x6E, 0x21, 0x6F, 0x21, 0x80, 0x21, 0x81, 0x21, 0x82, 0x21, 0x83, 0x21, + 0x83, 0x21, 0xFF, 0xFF, 0x4B, 0x03, 0xB6, 0x24, 0xB7, 0x24, 0xB8, 0x24, 0xB9, 0x24, 0xBA, 0x24, + 0xBB, 0x24, 0xBC, 0x24, 0xBD, 0x24, 0xBE, 0x24, 0xBF, 0x24, 0xC0, 0x24, 0xC1, 0x24, 0xC2, 0x24, + 0xC3, 0x24, 0xC4, 0x24, 0xC5, 0x24, 0xC6, 0x24, 0xC7, 0x24, 0xC8, 0x24, 0xC9, 0x24, 0xCA, 0x24, + 0xCB, 0x24, 0xCC, 0x24, 0xCD, 0x24, 0xCE, 0x24, 0xCF, 0x24, 0xFF, 0xFF, 0x46, 0x07, 0x00, 0x2C, + 0x01, 0x2C, 0x02, 0x2C, 0x03, 0x2C, 0x04, 0x2C, 0x05, 0x2C, 0x06, 0x2C, 0x07, 0x2C, 0x08, 0x2C, + 0x09, 0x2C, 0x0A, 0x2C, 0x0B, 0x2C, 0x0C, 0x2C, 0x0D, 0x2C, 0x0E, 0x2C, 0x0F, 0x2C, 0x10, 0x2C, + 0x11, 0x2C, 0x12, 0x2C, 0x13, 0x2C, 0x14, 0x2C, 0x15, 0x2C, 0x16, 0x2C, 0x17, 0x2C, 0x18, 0x2C, + 0x19, 0x2C, 0x1A, 0x2C, 0x1B, 0x2C, 0x1C, 0x2C, 0x1D, 0x2C, 0x1E, 0x2C, 0x1F, 0x2C, 0x20, 0x2C, + 0x21, 0x2C, 0x22, 0x2C, 0x23, 0x2C, 0x24, 0x2C, 0x25, 0x2C, 0x26, 0x2C, 0x27, 0x2C, 0x28, 0x2C, + 0x29, 0x2C, 0x2A, 0x2C, 0x2B, 0x2C, 0x2C, 0x2C, 0x2D, 0x2C, 0x2E, 0x2C, 0x5F, 0x2C, 0x60, 0x2C, + 0x60, 0x2C, 0x62, 0x2C, 0x63, 0x2C, 0x64, 0x2C, 0x65, 0x2C, 0x66, 0x2C, 0x67, 0x2C, 0x67, 0x2C, + 0x69, 0x2C, 0x69, 0x2C, 0x6B, 0x2C, 0x6B, 0x2C, 0x6D, 0x2C, 0x6E, 0x2C, 0x6F, 0x2C, 0x70, 0x2C, + 0x71, 0x2C, 0x72, 0x2C, 0x73, 0x2C, 0x74, 0x2C, 0x75, 0x2C, 0x75, 0x2C, 0x77, 0x2C, 0x78, 0x2C, + 0x79, 0x2C, 0x7A, 0x2C, 0x7B, 0x2C, 0x7C, 0x2C, 0x7D, 0x2C, 0x7E, 0x2C, 0x7F, 0x2C, 0x80, 0x2C, + 0x80, 0x2C, 0x82, 0x2C, 0x82, 0x2C, 0x84, 0x2C, 0x84, 0x2C, 0x86, 0x2C, 0x86, 0x2C, 0x88, 0x2C, + 0x88, 0x2C, 0x8A, 0x2C, 0x8A, 0x2C, 0x8C, 0x2C, 0x8C, 0x2C, 0x8E, 0x2C, 0x8E, 0x2C, 0x90, 0x2C, + 0x90, 0x2C, 0x92, 0x2C, 0x92, 0x2C, 0x94, 0x2C, 0x94, 0x2C, 0x96, 0x2C, 0x96, 0x2C, 0x98, 0x2C, + 0x98, 0x2C, 0x9A, 0x2C, 0x9A, 0x2C, 0x9C, 0x2C, 0x9C, 0x2C, 0x9E, 0x2C, 0x9E, 0x2C, 0xA0, 0x2C, + 0xA0, 0x2C, 0xA2, 0x2C, 0xA2, 0x2C, 0xA4, 0x2C, 0xA4, 0x2C, 0xA6, 0x2C, 0xA6, 0x2C, 0xA8, 0x2C, + 0xA8, 0x2C, 0xAA, 0x2C, 0xAA, 0x2C, 0xAC, 0x2C, 0xAC, 0x2C, 0xAE, 0x2C, 0xAE, 0x2C, 0xB0, 0x2C, + 0xB0, 0x2C, 0xB2, 0x2C, 0xB2, 0x2C, 0xB4, 0x2C, 0xB4, 0x2C, 0xB6, 0x2C, 0xB6, 0x2C, 0xB8, 0x2C, + 0xB8, 0x2C, 0xBA, 0x2C, 0xBA, 0x2C, 0xBC, 0x2C, 0xBC, 0x2C, 0xBE, 0x2C, 0xBE, 0x2C, 0xC0, 0x2C, + 0xC0, 0x2C, 0xC2, 0x2C, 0xC2, 0x2C, 0xC4, 0x2C, 0xC4, 0x2C, 0xC6, 0x2C, 0xC6, 0x2C, 0xC8, 0x2C, + 0xC8, 0x2C, 0xCA, 0x2C, 0xCA, 0x2C, 0xCC, 0x2C, 0xCC, 0x2C, 0xCE, 0x2C, 0xCE, 0x2C, 0xD0, 0x2C, + 0xD0, 0x2C, 0xD2, 0x2C, 0xD2, 0x2C, 0xD4, 0x2C, 0xD4, 0x2C, 0xD6, 0x2C, 0xD6, 0x2C, 0xD8, 0x2C, + 0xD8, 0x2C, 0xDA, 0x2C, 0xDA, 0x2C, 0xDC, 0x2C, 0xDC, 0x2C, 0xDE, 0x2C, 0xDE, 0x2C, 0xE0, 0x2C, + 0xE0, 0x2C, 0xE2, 0x2C, 0xE2, 0x2C, 0xE4, 0x2C, 0xE5, 0x2C, 0xE6, 0x2C, 0xE7, 0x2C, 0xE8, 0x2C, + 0xE9, 0x2C, 0xEA, 0x2C, 0xEB, 0x2C, 0xEC, 0x2C, 0xED, 0x2C, 0xEE, 0x2C, 0xEF, 0x2C, 0xF0, 0x2C, + 0xF1, 0x2C, 0xF2, 0x2C, 0xF3, 0x2C, 0xF4, 0x2C, 0xF5, 0x2C, 0xF6, 0x2C, 0xF7, 0x2C, 0xF8, 0x2C, + 0xF9, 0x2C, 0xFA, 0x2C, 0xFB, 0x2C, 0xFC, 0x2C, 0xFD, 0x2C, 0xFE, 0x2C, 0xFF, 0x2C, 0xA0, 0x10, + 0xA1, 0x10, 0xA2, 0x10, 0xA3, 0x10, 0xA4, 0x10, 0xA5, 0x10, 0xA6, 0x10, 0xA7, 0x10, 0xA8, 0x10, + 0xA9, 0x10, 0xAA, 0x10, 0xAB, 0x10, 0xAC, 0x10, 0xAD, 0x10, 0xAE, 0x10, 0xAF, 0x10, 0xB0, 0x10, + 0xB1, 0x10, 0xB2, 0x10, 0xB3, 0x10, 0xB4, 0x10, 0xB5, 0x10, 0xB6, 0x10, 0xB7, 0x10, 0xB8, 0x10, + 0xB9, 0x10, 0xBA, 0x10, 0xBB, 0x10, 0xBC, 0x10, 0xBD, 0x10, 0xBE, 0x10, 0xBF, 0x10, 0xC0, 0x10, + 0xC1, 0x10, 0xC2, 0x10, 0xC3, 0x10, 0xC4, 0x10, 0xC5, 0x10, 0xFF, 0xFF, 0x1B, 0xD2, 0x21, 0xFF, + 0x22, 0xFF, 0x23, 0xFF, 0x24, 0xFF, 0x25, 0xFF, 0x26, 0xFF, 0x27, 0xFF, 0x28, 0xFF, 0x29, 0xFF, + 0x2A, 0xFF, 0x2B, 0xFF, 0x2C, 0xFF, 0x2D, 0xFF, 0x2E, 0xFF, 0x2F, 0xFF, 0x30, 0xFF, 0x31, 0xFF, + 0x32, 0xFF, 0x33, 0xFF, 0x34, 0xFF, 0x35, 0xFF, 0x36, 0xFF, 0x37, 0xFF, 0x38, 0xFF, 0x39, 0xFF, + 0x3A, 0xFF, 0x5B, 0xFF, 0x5C, 0xFF, 0x5D, 0xFF, 0x5E, 0xFF, 0x5F, 0xFF, 0x60, 0xFF, 0x61, 0xFF, + 0x62, 0xFF, 0x63, 0xFF, 0x64, 0xFF, 0x65, 0xFF, 0x66, 0xFF, 0x67, 0xFF, 0x68, 0xFF, 0x69, 0xFF, + 0x6A, 0xFF, 0x6B, 0xFF, 0x6C, 0xFF, 0x6D, 0xFF, 0x6E, 0xFF, 0x6F, 0xFF, 0x70, 0xFF, 0x71, 0xFF, + 0x72, 0xFF, 0x73, 0xFF, 0x74, 0xFF, 0x75, 0xFF, 0x76, 0xFF, 0x77, 0xFF, 0x78, 0xFF, 0x79, 0xFF, + 0x7A, 0xFF, 0x7B, 0xFF, 0x7C, 0xFF, 0x7D, 0xFF, 0x7E, 0xFF, 0x7F, 0xFF, 0x80, 0xFF, 0x81, 0xFF, + 0x82, 0xFF, 0x83, 0xFF, 0x84, 0xFF, 0x85, 0xFF, 0x86, 0xFF, 0x87, 0xFF, 0x88, 0xFF, 0x89, 0xFF, + 0x8A, 0xFF, 0x8B, 0xFF, 0x8C, 0xFF, 0x8D, 0xFF, 0x8E, 0xFF, 0x8F, 0xFF, 0x90, 0xFF, 0x91, 0xFF, + 0x92, 0xFF, 0x93, 0xFF, 0x94, 0xFF, 0x95, 0xFF, 0x96, 0xFF, 0x97, 0xFF, 0x98, 0xFF, 0x99, 0xFF, + 0x9A, 0xFF, 0x9B, 0xFF, 0x9C, 0xFF, 0x9D, 0xFF, 0x9E, 0xFF, 0x9F, 0xFF, 0xA0, 0xFF, 0xA1, 0xFF, + 0xA2, 0xFF, 0xA3, 0xFF, 0xA4, 0xFF, 0xA5, 0xFF, 0xA6, 0xFF, 0xA7, 0xFF, 0xA8, 0xFF, 0xA9, 0xFF, + 0xAA, 0xFF, 0xAB, 0xFF, 0xAC, 0xFF, 0xAD, 0xFF, 0xAE, 0xFF, 0xAF, 0xFF, 0xB0, 0xFF, 0xB1, 0xFF, + 0xB2, 0xFF, 0xB3, 0xFF, 0xB4, 0xFF, 0xB5, 0xFF, 0xB6, 0xFF, 0xB7, 0xFF, 0xB8, 0xFF, 0xB9, 0xFF, + 0xBA, 0xFF, 0xBB, 0xFF, 0xBC, 0xFF, 0xBD, 0xFF, 0xBE, 0xFF, 0xBF, 0xFF, 0xC0, 0xFF, 0xC1, 0xFF, + 0xC2, 0xFF, 0xC3, 0xFF, 0xC4, 0xFF, 0xC5, 0xFF, 0xC6, 0xFF, 0xC7, 0xFF, 0xC8, 0xFF, 0xC9, 0xFF, + 0xCA, 0xFF, 0xCB, 0xFF, 0xCC, 0xFF, 0xCD, 0xFF, 0xCE, 0xFF, 0xCF, 0xFF, 0xD0, 0xFF, 0xD1, 0xFF, + 0xD2, 0xFF, 0xD3, 0xFF, 0xD4, 0xFF, 0xD5, 0xFF, 0xD6, 0xFF, 0xD7, 0xFF, 0xD8, 0xFF, 0xD9, 0xFF, + 0xDA, 0xFF, 0xDB, 0xFF, 0xDC, 0xFF, 0xDD, 0xFF, 0xDE, 0xFF, 0xDF, 0xFF, 0xE0, 0xFF, 0xE1, 0xFF, + 0xE2, 0xFF, 0xE3, 0xFF, 0xE4, 0xFF, 0xE5, 0xFF, 0xE6, 0xFF, 0xE7, 0xFF, 0xE8, 0xFF, 0xE9, 0xFF, + 0xEA, 0xFF, 0xEB, 0xFF, 0xEC, 0xFF, 0xED, 0xFF, 0xEE, 0xFF, 0xEF, 0xFF, 0xF0, 0xFF, 0xF1, 0xFF, + 0xF2, 0xFF, 0xF3, 0xFF, 0xF4, 0xFF, 0xF5, 0xFF, 0xF6, 0xFF, 0xF7, 0xFF, 0xF8, 0xFF, 0xF9, 0xFF, + 0xFA, 0xFF, 0xFB, 0xFF, 0xFC, 0xFF, 0xFD, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF +}; + +u16 anyka_exfat_GetUnicodeUpdateCase(u16 UniCode) +{ + u16 ret; + if(UniCode >= sizeof(anyka_exfat_updatecase) / 2) + return UniCode; + ret = anyka_exfat_updatecase[UniCode * 2 + 1]; + ret = (ret << 8) + anyka_exfat_updatecase[UniCode * 2]; + return ret; +} +u16 anyka_exfat_GetFileNameHash( u16 *FileName, u16 length) +{ + u16 i, HashValue = 0, tmp; + for(i = 0; i < length; i++) + { + tmp = FileName[i]; + if(tmp == 0 || tmp == '\\' || tmp == '/') + break; + + tmp = anyka_exfat_GetUnicodeUpdateCase(tmp); + HashValue = (u16)(((HashValue << 15) + (HashValue >> 1)) + (tmp & 0xFF)); + HashValue = (u16)(((HashValue << 15) + (HashValue >> 1)) + (tmp >> 8)); + } + return HashValue; +} +/***************************************************************** +aj for exfat add check sum of the fdt end +*******************************************************************/ + +u16 calc_checksum_2byte(void *data, s32 len, u16 chksum, s32 type) +{ + int i; + u8 *c = (u8 *) data; + + switch (type) { + case CS_DIR_ENTRY: + for (i = 0; i < len; i++, c++) { + if ((i == 2) || (i == 3)) + continue; + chksum = (((chksum & 1) << 15) | ((chksum & 0xFFFE) >> 1)) + (u16) *c; + } + break; + case CS_NAME_HASH: + chksum = anyka_exfat_GetFileNameHash((u16 *)c, len >> 1); + break; + default + : + for (i = 0; i < len; i++, c++) + chksum = (((chksum & 1) << 15) | ((chksum & 0xFFFE) >> 1)) + (u16) *c; + } + + return chksum; +} /* end of calc_checksum_2byte */ +u32 calc_checksum_4byte(void *data, s32 len, u32 chksum, s32 type) +{ + int i; + u8 *c = (u8 *) data; + + switch (type) { + case CS_PBR_SECTOR: + for (i = 0; i < len; i++, c++) { + if ((i == 106) || (i == 107) || (i == 112)) + continue; + chksum = (((chksum & 1) << 31) | ((chksum & 0xFFFFFFFE) >> 1)) + (u32) *c; + } + break; + default + : + for (i = 0; i < len; i++, c++) + chksum = (((chksum & 1) << 31) | ((chksum & 0xFFFFFFFE) >> 1)) + (u32) *c; + } + + return chksum; +} /* end of calc_checksum_4byte */ + +/* + * Name Resolution Functions + */ + +/* return values of resolve_path() + > 0 : return the length of the path + < 0 : return error */ +s32 resolve_path(struct inode *inode, char *path, CHAIN_T *p_dir, UNI_NAME_T *p_uniname) +{ + s32 lossy = FALSE; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + + if (strlen(path) >= (MAX_NAME_LENGTH * MAX_CHARSET_SIZE)) + return FFS_INVALIDPATH; + + strcpy(name_buf, path); + + nls_cstring_to_uniname(sb, p_uniname, name_buf, &lossy); + if (lossy) + return FFS_INVALIDPATH; + + fid->size = i_size_read(inode); + + p_dir->dir = fid->start_clu; + p_dir->size = (s32)(fid->size >> p_fs->cluster_size_bits); + p_dir->flags = fid->flags; + + return FFS_SUCCESS; +} + +/* + * File Operation Functions + */ +static FS_FUNC_T fat_fs_func = { + .alloc_cluster = fat_alloc_cluster, + .free_cluster = fat_free_cluster, + .count_used_clusters = fat_count_used_clusters, + + .init_dir_entry = fat_init_dir_entry, + .init_ext_entry = fat_init_ext_entry, + .find_dir_entry = fat_find_dir_entry, + .delete_dir_entry = fat_delete_dir_entry, + .get_uni_name_from_ext_entry = fat_get_uni_name_from_ext_entry, + .count_ext_entries = fat_count_ext_entries, + .calc_num_entries = fat_calc_num_entries, + + .get_entry_type = fat_get_entry_type, + .set_entry_type = fat_set_entry_type, + .get_entry_attr = fat_get_entry_attr, + .set_entry_attr = fat_set_entry_attr, + .get_entry_flag = fat_get_entry_flag, + .set_entry_flag = fat_set_entry_flag, + .get_entry_clu0 = fat_get_entry_clu0, + .set_entry_clu0 = fat_set_entry_clu0, + .get_entry_size = fat_get_entry_size, + .set_entry_size = fat_set_entry_size, + .get_entry_time = fat_get_entry_time, + .set_entry_time = fat_set_entry_time, +}; + + +s32 fat16_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr) +{ + s32 num_reserved, num_root_sectors; + BPB16_T *p_bpb = (BPB16_T *) p_pbr->bpb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + if (p_bpb->num_fats == 0) + return FFS_FORMATERR; + + num_root_sectors = GET16(p_bpb->num_root_entries) << DENTRY_SIZE_BITS; + num_root_sectors = ((num_root_sectors-1) >> p_bd->sector_size_bits) + 1; + + p_fs->sectors_per_clu = p_bpb->sectors_per_clu; + p_fs->sectors_per_clu_bits = ilog2(p_bpb->sectors_per_clu); + p_fs->cluster_size_bits = p_fs->sectors_per_clu_bits + p_bd->sector_size_bits; + p_fs->cluster_size = 1 << p_fs->cluster_size_bits; + + p_fs->num_FAT_sectors = GET16(p_bpb->num_fat_sectors); + + p_fs->FAT1_start_sector = p_fs->PBR_sector + GET16(p_bpb->num_reserved); + if (p_bpb->num_fats == 1) + p_fs->FAT2_start_sector = p_fs->FAT1_start_sector; + else + p_fs->FAT2_start_sector = p_fs->FAT1_start_sector + p_fs->num_FAT_sectors; + + p_fs->root_start_sector = p_fs->FAT2_start_sector + p_fs->num_FAT_sectors; + p_fs->data_start_sector = p_fs->root_start_sector + num_root_sectors; + + p_fs->num_sectors = GET16(p_bpb->num_sectors); + if (p_fs->num_sectors == 0) + p_fs->num_sectors = GET32(p_bpb->num_huge_sectors); + + num_reserved = p_fs->data_start_sector - p_fs->PBR_sector; + p_fs->num_clusters = ((p_fs->num_sectors - num_reserved) >> p_fs->sectors_per_clu_bits) + 2; + /* because the cluster index starts with 2 */ + + if (p_fs->num_clusters < FAT12_THRESHOLD) + p_fs->vol_type = FAT12; + else + p_fs->vol_type = FAT16; + p_fs->vol_id = GET32(p_bpb->vol_serial); + + p_fs->root_dir = 0; + p_fs->dentries_in_root = GET16(p_bpb->num_root_entries); + p_fs->dentries_per_clu = 1 << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS); + + p_fs->vol_flag = VOL_CLEAN; + p_fs->clu_srch_ptr = 2; + p_fs->used_clusters = (u32) ~0; + + p_fs->fs_func = &fat_fs_func; + + return FFS_SUCCESS; +} /* end of fat16_mount */ + +s32 fat32_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr) +{ + s32 num_reserved; + BPB32_T *p_bpb = (BPB32_T *) p_pbr->bpb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + if (p_bpb->num_fats == 0) + return FFS_FORMATERR; + + p_fs->sectors_per_clu = p_bpb->sectors_per_clu; + p_fs->sectors_per_clu_bits = ilog2(p_bpb->sectors_per_clu); + p_fs->cluster_size_bits = p_fs->sectors_per_clu_bits + p_bd->sector_size_bits; + p_fs->cluster_size = 1 << p_fs->cluster_size_bits; + + p_fs->num_FAT_sectors = GET32(p_bpb->num_fat32_sectors); + + p_fs->FAT1_start_sector = p_fs->PBR_sector + GET16(p_bpb->num_reserved); + if (p_bpb->num_fats == 1) + p_fs->FAT2_start_sector = p_fs->FAT1_start_sector; + else + p_fs->FAT2_start_sector = p_fs->FAT1_start_sector + p_fs->num_FAT_sectors; + + p_fs->root_start_sector = p_fs->FAT2_start_sector + p_fs->num_FAT_sectors; + p_fs->data_start_sector = p_fs->root_start_sector; + + p_fs->num_sectors = GET32(p_bpb->num_huge_sectors); + num_reserved = p_fs->data_start_sector - p_fs->PBR_sector; + + p_fs->num_clusters = ((p_fs->num_sectors-num_reserved) >> p_fs->sectors_per_clu_bits) + 2; + /* because the cluster index starts with 2 */ + + p_fs->vol_type = FAT32; + p_fs->vol_id = GET32(p_bpb->vol_serial); + + p_fs->root_dir = GET32(p_bpb->root_cluster); + p_fs->dentries_in_root = 0; + p_fs->dentries_per_clu = 1 << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS); + + p_fs->vol_flag = VOL_CLEAN; + p_fs->clu_srch_ptr = 2; + p_fs->used_clusters = (u32) ~0; + + p_fs->fs_func = &fat_fs_func; + + return FFS_SUCCESS; +} /* end of fat32_mount */ + +static FS_FUNC_T exfat_fs_func = { + .alloc_cluster = exfat_alloc_cluster, + .free_cluster = exfat_free_cluster, + .count_used_clusters = exfat_count_used_clusters, + + .init_dir_entry = exfat_init_dir_entry, + .init_ext_entry = exfat_init_ext_entry, + .find_dir_entry = exfat_find_dir_entry, + .delete_dir_entry = exfat_delete_dir_entry, + .get_uni_name_from_ext_entry = exfat_get_uni_name_from_ext_entry, + .count_ext_entries = exfat_count_ext_entries, + .calc_num_entries = exfat_calc_num_entries, + + .get_entry_type = exfat_get_entry_type, + .set_entry_type = exfat_set_entry_type, + .get_entry_attr = exfat_get_entry_attr, + .set_entry_attr = exfat_set_entry_attr, + .get_entry_flag = exfat_get_entry_flag, + .set_entry_flag = exfat_set_entry_flag, + .get_entry_clu0 = exfat_get_entry_clu0, + .set_entry_clu0 = exfat_set_entry_clu0, + .get_entry_size = exfat_get_entry_size, + .set_entry_size = exfat_set_entry_size, + .get_entry_time = exfat_get_entry_time, + .set_entry_time = exfat_set_entry_time, +}; + +s32 exfat_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr) +{ + BPBEX_T *p_bpb = (BPBEX_T *) p_pbr->bpb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + if (p_bpb->num_fats == 0) + return FFS_FORMATERR; + + p_fs->sectors_per_clu = 1 << p_bpb->sectors_per_clu_bits; + p_fs->sectors_per_clu_bits = p_bpb->sectors_per_clu_bits; + p_fs->cluster_size_bits = p_fs->sectors_per_clu_bits + p_bd->sector_size_bits; + p_fs->cluster_size = 1 << p_fs->cluster_size_bits; + + p_fs->num_FAT_sectors = GET32(p_bpb->fat_length); + + p_fs->FAT1_start_sector = p_fs->PBR_sector + GET32(p_bpb->fat_offset); + if (p_bpb->num_fats == 1) + p_fs->FAT2_start_sector = p_fs->FAT1_start_sector; + else + p_fs->FAT2_start_sector = p_fs->FAT1_start_sector + p_fs->num_FAT_sectors; + + p_fs->root_start_sector = p_fs->PBR_sector + GET32(p_bpb->clu_offset); + p_fs->data_start_sector = p_fs->root_start_sector; + + p_fs->num_sectors = GET64(p_bpb->vol_length); + p_fs->num_clusters = GET32(p_bpb->clu_count) + 2; + /* because the cluster index starts with 2 */ + + p_fs->vol_type = EXFAT; + p_fs->vol_id = GET32(p_bpb->vol_serial); + + p_fs->root_dir = GET32(p_bpb->root_cluster); + p_fs->dentries_in_root = 0; + p_fs->dentries_per_clu = 1 << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS); + + p_fs->vol_flag = (u32) GET16(p_bpb->vol_flags); + p_fs->clu_srch_ptr = 2; + p_fs->used_clusters = (u32) ~0; + + p_fs->fs_func = &exfat_fs_func; + + return FFS_SUCCESS; +} /* end of exfat_mount */ + +s32 create_dir(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, FILE_ID_T *fid) +{ + s32 ret, dentry, num_entries; + u64 size; + CHAIN_T clu; + DOS_NAME_T dos_name, dot_name; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_entries, &dos_name); + if (ret) + return ret; + + /* find_empty_entry must be called before alloc_cluster */ + dentry = find_empty_entry(inode, p_dir, num_entries); + if (dentry < 0) + return FFS_FULL; + + clu.dir = CLUSTER_32(~0); + clu.size = 0; + clu.flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; + + /* (1) allocate a cluster */ + ret = p_fs->fs_func->alloc_cluster(sb, 1, &clu); + if (ret < 0) + return FFS_MEDIAERR; + else if (ret == 0) + return FFS_FULL; + + ret = clear_cluster(sb, clu.dir); + if (ret != FFS_SUCCESS) + return ret; + + if (p_fs->vol_type == EXFAT) { + size = p_fs->cluster_size; + } else { + size = 0; + + /* initialize the . and .. entry + Information for . points to itself + Information for .. points to parent dir */ + + dot_name.name_case = 0x0; + memcpy(dot_name.name, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH); + + ret = p_fs->fs_func->init_dir_entry(sb, &clu, 0, TYPE_DIR, clu.dir, 0); + if (ret != FFS_SUCCESS) + return ret; + + ret = p_fs->fs_func->init_ext_entry(sb, &clu, 0, 1, NULL, &dot_name); + if (ret != FFS_SUCCESS) + return ret; + + memcpy(dot_name.name, DOS_PAR_DIR_NAME, DOS_NAME_LENGTH); + + if (p_dir->dir == p_fs->root_dir) + ret = p_fs->fs_func->init_dir_entry(sb, &clu, 1, TYPE_DIR, CLUSTER_32(0), 0); + else + ret = p_fs->fs_func->init_dir_entry(sb, &clu, 1, TYPE_DIR, p_dir->dir, 0); + + if (ret != FFS_SUCCESS) + return ret; + + ret = p_fs->fs_func->init_ext_entry(sb, &clu, 1, 1, NULL, &dot_name); + if (ret != FFS_SUCCESS) + return ret; + } + + /* (2) update the directory entry */ + /* make sub-dir entry in parent directory */ + ret = p_fs->fs_func->init_dir_entry(sb, p_dir, dentry, TYPE_DIR, clu.dir, size); + if (ret != FFS_SUCCESS) + return ret; + + ret = p_fs->fs_func->init_ext_entry(sb, p_dir, dentry, num_entries, p_uniname, &dos_name); + if (ret != FFS_SUCCESS) + return ret; + + fid->dir.dir = p_dir->dir; + fid->dir.size = p_dir->size; + fid->dir.flags = p_dir->flags; + fid->entry = dentry; + + fid->attr = ATTR_SUBDIR; + fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; + fid->size = size; + fid->start_clu = clu.dir; + + fid->type = TYPE_DIR; + fid->rwoffset = 0; + fid->hint_last_off = -1; + + return FFS_SUCCESS; +} /* end of create_dir */ + +s32 create_file(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, u8 mode, FILE_ID_T *fid) +{ + s32 ret, dentry, num_entries; + DOS_NAME_T dos_name; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_entries, &dos_name); + if (ret) + return ret; + + /* find_empty_entry must be called before alloc_cluster() */ + dentry = find_empty_entry(inode, p_dir, num_entries); + if (dentry < 0) + return FFS_FULL; + + /* (1) update the directory entry */ + /* fill the dos name directory entry information of the created file. + the first cluster is not determined yet. (0) */ + ret = p_fs->fs_func->init_dir_entry(sb, p_dir, dentry, TYPE_FILE | mode, CLUSTER_32(0), 0); + if (ret != FFS_SUCCESS) + return ret; + + ret = p_fs->fs_func->init_ext_entry(sb, p_dir, dentry, num_entries, p_uniname, &dos_name); + if (ret != FFS_SUCCESS) + return ret; + + fid->dir.dir = p_dir->dir; + fid->dir.size = p_dir->size; + fid->dir.flags = p_dir->flags; + fid->entry = dentry; + + fid->attr = ATTR_ARCHIVE | mode; + fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; + fid->size = 0; + fid->start_clu = CLUSTER_32(~0); + + fid->type = TYPE_FILE; + fid->rwoffset = 0; + fid->hint_last_off = -1; + + return FFS_SUCCESS; +} /* end of create_file */ + +void remove_file(struct inode *inode, CHAIN_T *p_dir, s32 entry) +{ + s32 num_entries; + u32 sector; + DENTRY_T *ep; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + ep = get_entry_in_dir(sb, p_dir, entry, §or); + if (!ep) + return; + + buf_lock(sb, sector); + + /* buf_lock() before call count_ext_entries() */ + num_entries = p_fs->fs_func->count_ext_entries(sb, p_dir, entry, ep); + if (num_entries < 0) { + buf_unlock(sb, sector); + return; + } + num_entries++; + + buf_unlock(sb, sector); + + /* (1) update the directory entry */ + p_fs->fs_func->delete_dir_entry(sb, p_dir, entry, 0, num_entries); +} /* end of remove_file */ + +s32 rename_file(struct inode *inode, CHAIN_T *p_dir, s32 oldentry, UNI_NAME_T *p_uniname, FILE_ID_T *fid) +{ + s32 ret, newentry = -1, num_old_entries, num_new_entries; + u32 sector_old, sector_new; + DOS_NAME_T dos_name; + DENTRY_T *epold, *epnew; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + epold = get_entry_in_dir(sb, p_dir, oldentry, §or_old); + if (!epold) + return FFS_MEDIAERR; + + buf_lock(sb, sector_old); + + /* buf_lock() before call count_ext_entries() */ + num_old_entries = p_fs->fs_func->count_ext_entries(sb, p_dir, oldentry, epold); + if (num_old_entries < 0) { + buf_unlock(sb, sector_old); + return FFS_MEDIAERR; + } + num_old_entries++; + + ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_new_entries, &dos_name); + if (ret) { + buf_unlock(sb, sector_old); + return ret; + } + + if (num_old_entries < num_new_entries) { + newentry = find_empty_entry(inode, p_dir, num_new_entries); + if (newentry < 0) { + buf_unlock(sb, sector_old); + return FFS_FULL; + } + + epnew = get_entry_in_dir(sb, p_dir, newentry, §or_new); + if (!epnew) { + buf_unlock(sb, sector_old); + return FFS_MEDIAERR; + } + + memcpy((void *) epnew, (void *) epold, DENTRY_SIZE); + if (p_fs->fs_func->get_entry_type(epnew) == TYPE_FILE) { + p_fs->fs_func->set_entry_attr(epnew, p_fs->fs_func->get_entry_attr(epnew) | ATTR_ARCHIVE); + fid->attr |= ATTR_ARCHIVE; + } + buf_modify(sb, sector_new); + buf_unlock(sb, sector_old); + + if (p_fs->vol_type == EXFAT) { + epold = get_entry_in_dir(sb, p_dir, oldentry+1, §or_old); + buf_lock(sb, sector_old); + epnew = get_entry_in_dir(sb, p_dir, newentry+1, §or_new); + + if (!epold || !epnew) { + buf_unlock(sb, sector_old); + return FFS_MEDIAERR; + } + + memcpy((void *) epnew, (void *) epold, DENTRY_SIZE); + buf_modify(sb, sector_new); + buf_unlock(sb, sector_old); + } + + ret = p_fs->fs_func->init_ext_entry(sb, p_dir, newentry, num_new_entries, p_uniname, &dos_name); + if (ret != FFS_SUCCESS) + return ret; + + p_fs->fs_func->delete_dir_entry(sb, p_dir, oldentry, 0, num_old_entries); + fid->entry = newentry; + } else { + if (p_fs->fs_func->get_entry_type(epold) == TYPE_FILE) { + p_fs->fs_func->set_entry_attr(epold, p_fs->fs_func->get_entry_attr(epold) | ATTR_ARCHIVE); + fid->attr |= ATTR_ARCHIVE; + } + buf_modify(sb, sector_old); + buf_unlock(sb, sector_old); + + ret = p_fs->fs_func->init_ext_entry(sb, p_dir, oldentry, num_new_entries, p_uniname, &dos_name); + if (ret != FFS_SUCCESS) + return ret; + + p_fs->fs_func->delete_dir_entry(sb, p_dir, oldentry, num_new_entries, num_old_entries); + } + + return FFS_SUCCESS; +} /* end of rename_file */ + +s32 move_file(struct inode *inode, CHAIN_T *p_olddir, s32 oldentry, CHAIN_T *p_newdir, UNI_NAME_T *p_uniname, FILE_ID_T *fid) +{ + s32 ret, newentry, num_new_entries, num_old_entries; + u32 sector_mov, sector_new; + CHAIN_T clu; + DOS_NAME_T dos_name; + DENTRY_T *epmov, *epnew; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + epmov = get_entry_in_dir(sb, p_olddir, oldentry, §or_mov); + if (!epmov) + return FFS_MEDIAERR; + + /* check if the source and target directory is the same */ + if (p_fs->fs_func->get_entry_type(epmov) == TYPE_DIR && + p_fs->fs_func->get_entry_clu0(epmov) == p_newdir->dir) + return FFS_INVALIDPATH; + + buf_lock(sb, sector_mov); + + /* buf_lock() before call count_ext_entries() */ + num_old_entries = p_fs->fs_func->count_ext_entries(sb, p_olddir, oldentry, epmov); + if (num_old_entries < 0) { + buf_unlock(sb, sector_mov); + return FFS_MEDIAERR; + } + num_old_entries++; + + ret = get_num_entries_and_dos_name(sb, p_newdir, p_uniname, &num_new_entries, &dos_name); + if (ret) { + buf_unlock(sb, sector_mov); + return ret; + } + + newentry = find_empty_entry(inode, p_newdir, num_new_entries); + if (newentry < 0) { + buf_unlock(sb, sector_mov); + return FFS_FULL; + } + + epnew = get_entry_in_dir(sb, p_newdir, newentry, §or_new); + if (!epnew) { + buf_unlock(sb, sector_mov); + return FFS_MEDIAERR; + } + + memcpy((void *) epnew, (void *) epmov, DENTRY_SIZE); + if (p_fs->fs_func->get_entry_type(epnew) == TYPE_FILE) { + p_fs->fs_func->set_entry_attr(epnew, p_fs->fs_func->get_entry_attr(epnew) | ATTR_ARCHIVE); + fid->attr |= ATTR_ARCHIVE; + } + buf_modify(sb, sector_new); + buf_unlock(sb, sector_mov); + + if (p_fs->vol_type == EXFAT) { + epmov = get_entry_in_dir(sb, p_olddir, oldentry+1, §or_mov); + buf_lock(sb, sector_mov); + epnew = get_entry_in_dir(sb, p_newdir, newentry+1, §or_new); + if (!epmov || !epnew) { + buf_unlock(sb, sector_mov); + return FFS_MEDIAERR; + } + + memcpy((void *) epnew, (void *) epmov, DENTRY_SIZE); + buf_modify(sb, sector_new); + buf_unlock(sb, sector_mov); + } else if (p_fs->fs_func->get_entry_type(epnew) == TYPE_DIR) { + /* change ".." pointer to new parent dir */ + clu.dir = p_fs->fs_func->get_entry_clu0(epnew); + clu.flags = 0x01; + + epnew = get_entry_in_dir(sb, &clu, 1, §or_new); + if (!epnew) + return FFS_MEDIAERR; + + if (p_newdir->dir == p_fs->root_dir) + p_fs->fs_func->set_entry_clu0(epnew, CLUSTER_32(0)); + else + p_fs->fs_func->set_entry_clu0(epnew, p_newdir->dir); + buf_modify(sb, sector_new); + } + + ret = p_fs->fs_func->init_ext_entry(sb, p_newdir, newentry, num_new_entries, p_uniname, &dos_name); + if (ret != FFS_SUCCESS) + return ret; + + p_fs->fs_func->delete_dir_entry(sb, p_olddir, oldentry, 0, num_old_entries); + + fid->dir.dir = p_newdir->dir; + fid->dir.size = p_newdir->size; + fid->dir.flags = p_newdir->flags; + + fid->entry = newentry; + + return FFS_SUCCESS; +} /* end of move_file */ + +/* + * Sector Read/Write Functions + */ + +s32 sector_read(struct super_block *sb, u32 sec, struct buffer_head **bh, s32 read) +{ + s32 ret = FFS_MEDIAERR; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if ((sec >= (p_fs->PBR_sector+p_fs->num_sectors)) && (p_fs->num_sectors > 0)) { + printk("[EXFAT] sector_read: out of range error! (sec = %d)\n", sec); + fs_error(sb); + return ret; + } + + if (!p_fs->dev_ejected) { + ret = bdev_read(sb, sec, bh, 1, read); + if (ret != FFS_SUCCESS) + p_fs->dev_ejected = TRUE; + } + + return ret; +} /* end of sector_read */ + +s32 sector_write(struct super_block *sb, u32 sec, struct buffer_head *bh, s32 sync) +{ + s32 ret = FFS_MEDIAERR; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (sec >= (p_fs->PBR_sector+p_fs->num_sectors) && (p_fs->num_sectors > 0)) { + printk("[EXFAT] sector_write: out of range error! (sec = %d)\n", sec); + fs_error(sb); + return ret; + } + + if (bh == NULL) { + printk("[EXFAT] sector_write: bh is NULL!\n"); + fs_error(sb); + return ret; + } + + if (!p_fs->dev_ejected) { + ret = bdev_write(sb, sec, bh, 1, sync); + if (ret != FFS_SUCCESS) + p_fs->dev_ejected = TRUE; + } + + return ret; +} /* end of sector_write */ + +s32 multi_sector_read(struct super_block *sb, u32 sec, struct buffer_head **bh, s32 num_secs, s32 read) +{ + s32 ret = FFS_MEDIAERR; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (((sec+num_secs) > (p_fs->PBR_sector+p_fs->num_sectors)) && (p_fs->num_sectors > 0)) { + printk("[EXFAT] multi_sector_read: out of range error! (sec = %d, num_secs = %d)\n", sec, num_secs); + fs_error(sb); + return ret; + } + + if (!p_fs->dev_ejected) { + ret = bdev_read(sb, sec, bh, num_secs, read); + if (ret != FFS_SUCCESS) + p_fs->dev_ejected = TRUE; + } + + return ret; +} /* end of multi_sector_read */ + +s32 multi_sector_write(struct super_block *sb, u32 sec, struct buffer_head *bh, s32 num_secs, s32 sync) +{ + s32 ret = FFS_MEDIAERR; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if ((sec+num_secs) > (p_fs->PBR_sector+p_fs->num_sectors) && (p_fs->num_sectors > 0)) { + printk("[EXFAT] multi_sector_write: out of range error! (sec = %d, num_secs = %d)\n", sec, num_secs); + fs_error(sb); + return ret; + } + if (bh == NULL) { + printk("[EXFAT] multi_sector_write: bh is NULL!\n"); + fs_error(sb); + return ret; + } + + if (!p_fs->dev_ejected) { + ret = bdev_write(sb, sec, bh, num_secs, sync); + if (ret != FFS_SUCCESS) + p_fs->dev_ejected = TRUE; + } + + return ret; +} /* end of multi_sector_write */ diff --git a/fs/exfat/exfat_core.h b/fs/exfat/exfat_core.h new file mode 100644 index 00000000..6da9a7f8 --- /dev/null +++ b/fs/exfat/exfat_core.h @@ -0,0 +1,672 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_core.h */ +/* PURPOSE : Header File for exFAT File Manager */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_H +#define _EXFAT_H + +#include "exfat_config.h" +#include "exfat_data.h" +#include "exfat_oal.h" + +#include "exfat_blkdev.h" +#include "exfat_cache.h" +#include "exfat_nls.h" +#include "exfat_api.h" +#include "exfat_cache.h" + +#ifdef CONFIG_EXFAT_KERNEL_DEBUG + /* For Debugging Purpose */ + /* IOCTL code 'f' used by + * - file systems typically #0~0x1F + * - embedded terminal devices #128~ + * - exts for debugging purpose #99 + * number 100 and 101 is availble now but has possible conflicts + */ +#define EXFAT_IOC_GET_DEBUGFLAGS _IOR('f', 100, long) +#define EXFAT_IOC_SET_DEBUGFLAGS _IOW('f', 101, long) + +#define EXFAT_DEBUGFLAGS_INVALID_UMOUNT 0x01 +#define EXFAT_DEBUGFLAGS_ERROR_RW 0x02 +#endif /* CONFIG_EXFAT_KERNEL_DEBUG */ + + /*----------------------------------------------------------------------*/ + /* Constant & Macro Definitions */ + /*----------------------------------------------------------------------*/ + +#define DENTRY_SIZE 32 /* dir entry size */ +#define DENTRY_SIZE_BITS 5 + +/* PBR entries */ +#define PBR_SIGNATURE 0xAA55 +#define EXT_SIGNATURE 0xAA550000 +#define VOL_LABEL "NO NAME " /* size should be 11 */ +#define OEM_NAME "MSWIN4.1" /* size should be 8 */ +#define STR_FAT12 "FAT12 " /* size should be 8 */ +#define STR_FAT16 "FAT16 " /* size should be 8 */ +#define STR_FAT32 "FAT32 " /* size should be 8 */ +#define STR_EXFAT "EXFAT " /* size should be 8 */ +#define VOL_CLEAN 0x0000 +#define VOL_DIRTY 0x0002 + +/* max number of clusters */ +#define FAT12_THRESHOLD 4087 /* 2^12 - 1 + 2 (clu 0 & 1) */ +#define FAT16_THRESHOLD 65527 /* 2^16 - 1 + 2 */ +#define FAT32_THRESHOLD 268435457 /* 2^28 - 1 + 2 */ +#define EXFAT_THRESHOLD 268435457 /* 2^28 - 1 + 2 */ + +/* file types */ +#define TYPE_UNUSED 0x0000 +#define TYPE_DELETED 0x0001 +#define TYPE_INVALID 0x0002 +#define TYPE_CRITICAL_PRI 0x0100 +#define TYPE_BITMAP 0x0101 +#define TYPE_UPCASE 0x0102 +#define TYPE_VOLUME 0x0103 +#define TYPE_DIR 0x0104 +#define TYPE_FILE 0x011F +#define TYPE_SYMLINK 0x015F +#define TYPE_CRITICAL_SEC 0x0200 +#define TYPE_STREAM 0x0201 +#define TYPE_EXTEND 0x0202 +#define TYPE_ACL 0x0203 +#define TYPE_BENIGN_PRI 0x0400 +#define TYPE_GUID 0x0401 +#define TYPE_PADDING 0x0402 +#define TYPE_ACLTAB 0x0403 +#define TYPE_BENIGN_SEC 0x0800 +#define TYPE_ALL 0x0FFF + +/* time modes */ +#define TM_CREATE 0 +#define TM_MODIFY 1 +#define TM_ACCESS 2 + +/* checksum types */ +#define CS_DIR_ENTRY 0 +#define CS_PBR_SECTOR 1 +#define CS_DEFAULT 2 +#define CS_NAME_HASH 3 + +#define CLUSTER_16(x) ((u16)(x)) +#define CLUSTER_32(x) ((u32)(x)) + +#define FALSE 0 +#define TRUE 1 + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +#define START_SECTOR(x) \ + ((((x) - 2) << p_fs->sectors_per_clu_bits) + p_fs->data_start_sector) + +#define IS_LAST_SECTOR_IN_CLUSTER(sec) \ + ((((sec) - p_fs->data_start_sector + 1) & ((1 << p_fs->sectors_per_clu_bits) - 1)) == 0) + +#define GET_CLUSTER_FROM_SECTOR(sec) \ + ((((sec) - p_fs->data_start_sector) >> p_fs->sectors_per_clu_bits) + 2) + +#define GET16(p_src) \ + (((u16)(p_src)[0]) | (((u16)(p_src)[1]) << 8)) +#define GET32(p_src) \ + (((u32)(p_src)[0]) | (((u32)(p_src)[1]) << 8) | \ + (((u32)(p_src)[2]) << 16) | (((u32)(p_src)[3]) << 24)) +#define GET64(p_src) \ + (((u64)(p_src)[0]) | (((u64)(p_src)[1]) << 8) | \ + (((u64)(p_src)[2]) << 16) | (((u64)(p_src)[3]) << 24) | \ + (((u64)(p_src)[4]) << 32) | (((u64)(p_src)[5]) << 40) | \ + (((u64)(p_src)[6]) << 48) | (((u64)(p_src)[7]) << 56)) + + +#define SET16(p_dst, src) \ + do { \ + (p_dst)[0] = (u8)(src); \ + (p_dst)[1] = (u8)(((u16)(src)) >> 8); \ + } while (0) +#define SET32(p_dst, src) \ + do { \ + (p_dst)[0] = (u8)(src); \ + (p_dst)[1] = (u8)(((u32)(src)) >> 8); \ + (p_dst)[2] = (u8)(((u32)(src)) >> 16); \ + (p_dst)[3] = (u8)(((u32)(src)) >> 24); \ + } while (0) +#define SET64(p_dst, src) \ + do { \ + (p_dst)[0] = (u8)(src); \ + (p_dst)[1] = (u8)(((u64)(src)) >> 8); \ + (p_dst)[2] = (u8)(((u64)(src)) >> 16); \ + (p_dst)[3] = (u8)(((u64)(src)) >> 24); \ + (p_dst)[4] = (u8)(((u64)(src)) >> 32); \ + (p_dst)[5] = (u8)(((u64)(src)) >> 40); \ + (p_dst)[6] = (u8)(((u64)(src)) >> 48); \ + (p_dst)[7] = (u8)(((u64)(src)) >> 56); \ + } while (0) + +#ifdef __LITTLE_ENDIAN +#define GET16_A(p_src) (*((u16 *)(p_src))) +#define GET32_A(p_src) (*((u32 *)(p_src))) +#define GET64_A(p_src) (*((u64 *)(p_src))) +#define SET16_A(p_dst, src) (*((u16 *)(p_dst)) = (u16)(src)) +#define SET32_A(p_dst, src) (*((u32 *)(p_dst)) = (u32)(src)) +#define SET64_A(p_dst, src) (*((u64 *)(p_dst)) = (u64)(src)) +#else /* BIG_ENDIAN */ +#define GET16_A(p_src) GET16(p_src) +#define GET32_A(p_src) GET32(p_src) +#define GET64_A(p_src) GET64(p_src) +#define SET16_A(p_dst, src) SET16(p_dst, src) +#define SET32_A(p_dst, src) SET32(p_dst, src) +#define SET64_A(p_dst, src) SET64(p_dst, src) +#endif + +/* Upcase tabel mecro */ +#define HIGH_INDEX_BIT (8) +#define HIGH_INDEX_MASK (0xFF00) +#define LOW_INDEX_BIT (16-HIGH_INDEX_BIT) +#define UTBL_ROW_COUNT (1<> LOW_INDEX_BIT; +} +static inline u16 get_row_index(u16 i) +{ + return i & ~HIGH_INDEX_MASK; +} +/*----------------------------------------------------------------------*/ +/* Type Definitions */ +/*----------------------------------------------------------------------*/ + +/* MS_DOS FAT partition boot record (512 bytes) */ +typedef struct { + u8 jmp_boot[3]; + u8 oem_name[8]; + u8 bpb[109]; + u8 boot_code[390]; + u8 signature[2]; +} PBR_SECTOR_T; + +/* MS-DOS FAT12/16 BIOS parameter block (51 bytes) */ +typedef struct { + u8 sector_size[2]; + u8 sectors_per_clu; + u8 num_reserved[2]; + u8 num_fats; + u8 num_root_entries[2]; + u8 num_sectors[2]; + u8 media_type; + u8 num_fat_sectors[2]; + u8 sectors_in_track[2]; + u8 num_heads[2]; + u8 num_hid_sectors[4]; + u8 num_huge_sectors[4]; + + u8 phy_drv_no; + u8 reserved; + u8 ext_signature; + u8 vol_serial[4]; + u8 vol_label[11]; + u8 vol_type[8]; +} BPB16_T; + +/* MS-DOS FAT32 BIOS parameter block (79 bytes) */ +typedef struct { + u8 sector_size[2]; + u8 sectors_per_clu; + u8 num_reserved[2]; + u8 num_fats; + u8 num_root_entries[2]; + u8 num_sectors[2]; + u8 media_type; + u8 num_fat_sectors[2]; + u8 sectors_in_track[2]; + u8 num_heads[2]; + u8 num_hid_sectors[4]; + u8 num_huge_sectors[4]; + u8 num_fat32_sectors[4]; + u8 ext_flags[2]; + u8 fs_version[2]; + u8 root_cluster[4]; + u8 fsinfo_sector[2]; + u8 backup_sector[2]; + u8 reserved[12]; + + u8 phy_drv_no; + u8 ext_reserved; + u8 ext_signature; + u8 vol_serial[4]; + u8 vol_label[11]; + u8 vol_type[8]; +} BPB32_T; + +/* MS-DOS EXFAT BIOS parameter block (109 bytes) */ +typedef struct { + u8 reserved1[53]; + u8 vol_offset[8]; + u8 vol_length[8]; + u8 fat_offset[4]; + u8 fat_length[4]; + u8 clu_offset[4]; + u8 clu_count[4]; + u8 root_cluster[4]; + u8 vol_serial[4]; + u8 fs_version[2]; + u8 vol_flags[2]; + u8 sector_size_bits; + u8 sectors_per_clu_bits; + u8 num_fats; + u8 phy_drv_no; + u8 perc_in_use; + u8 reserved2[7]; +} BPBEX_T; + +/* MS-DOS FAT file system information sector (512 bytes) */ +typedef struct { + u8 signature1[4]; + u8 reserved1[480]; + u8 signature2[4]; + u8 free_cluster[4]; + u8 next_cluster[4]; + u8 reserved2[14]; + u8 signature3[2]; +} FSI_SECTOR_T; + +/* MS-DOS FAT directory entry (32 bytes) */ +typedef struct { + u8 dummy[32]; +} DENTRY_T; + +typedef struct { + u8 name[DOS_NAME_LENGTH]; + u8 attr; + u8 lcase; + u8 create_time_ms; + u8 create_time[2]; + u8 create_date[2]; + u8 access_date[2]; + u8 start_clu_hi[2]; + u8 modify_time[2]; + u8 modify_date[2]; + u8 start_clu_lo[2]; + u8 size[4]; +} DOS_DENTRY_T; + +/* MS-DOS FAT extended directory entry (32 bytes) */ +typedef struct { + u8 order; + u8 unicode_0_4[10]; + u8 attr; + u8 sysid; + u8 checksum; + u8 unicode_5_10[12]; + u8 start_clu[2]; + u8 unicode_11_12[4]; +} EXT_DENTRY_T; + +/* MS-DOS EXFAT file directory entry (32 bytes) */ +typedef struct { + u8 type; + u8 num_ext; + u8 checksum[2]; + u8 attr[2]; + u8 reserved1[2]; + u8 create_time[2]; + u8 create_date[2]; + u8 modify_time[2]; + u8 modify_date[2]; + u8 access_time[2]; + u8 access_date[2]; + u8 create_time_ms; + u8 modify_time_ms; + u8 access_time_ms; + u8 reserved2[9]; +} FILE_DENTRY_T; + +/* MS-DOS EXFAT stream extension directory entry (32 bytes) */ +typedef struct { + u8 type; + u8 flags; + u8 reserved1; + u8 name_len; + u8 name_hash[2]; + u8 reserved2[2]; + u8 valid_size[8]; + u8 reserved3[4]; + u8 start_clu[4]; + u8 size[8]; +} STRM_DENTRY_T; + +/* MS-DOS EXFAT file name directory entry (32 bytes) */ +typedef struct { + u8 type; + u8 flags; + u8 unicode_0_14[30]; +} NAME_DENTRY_T; + +/* MS-DOS EXFAT allocation bitmap directory entry (32 bytes) */ +typedef struct { + u8 type; + u8 flags; + u8 reserved[18]; + u8 start_clu[4]; + u8 size[8]; +} BMAP_DENTRY_T; + +/* MS-DOS EXFAT up-case table directory entry (32 bytes) */ +typedef struct { + u8 type; + u8 reserved1[3]; + u8 checksum[4]; + u8 reserved2[12]; + u8 start_clu[4]; + u8 size[8]; +} CASE_DENTRY_T; + +/* MS-DOS EXFAT volume label directory entry (32 bytes) */ +typedef struct { + u8 type; + u8 label_len; + u8 unicode_0_10[22]; + u8 reserved[8]; +} VOLM_DENTRY_T; + +/* unused entry hint information */ +typedef struct { + u32 dir; + s32 entry; + CHAIN_T clu; +} UENTRY_T; + +typedef struct { + s32 (*alloc_cluster)(struct super_block *sb, s32 num_alloc, CHAIN_T *p_chain); + void (*free_cluster)(struct super_block *sb, CHAIN_T *p_chain, s32 do_relse); + s32 (*count_used_clusters)(struct super_block *sb); + + s32 (*init_dir_entry)(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 type, + u32 start_clu, u64 size); + s32 (*init_ext_entry)(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 num_entries, + UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname); + s32 (*find_dir_entry)(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, s32 num_entries, DOS_NAME_T *p_dosname, u32 type); + void (*delete_dir_entry)(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 offset, s32 num_entries); + void (*get_uni_name_from_ext_entry)(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u16 *uniname); + s32 (*count_ext_entries)(struct super_block *sb, CHAIN_T *p_dir, s32 entry, DENTRY_T *p_entry); + s32 (*calc_num_entries)(UNI_NAME_T *p_uniname); + + u32 (*get_entry_type)(DENTRY_T *p_entry); + void (*set_entry_type)(DENTRY_T *p_entry, u32 type); + u32 (*get_entry_attr)(DENTRY_T *p_entry); + void (*set_entry_attr)(DENTRY_T *p_entry, u32 attr); + u8 (*get_entry_flag)(DENTRY_T *p_entry); + void (*set_entry_flag)(DENTRY_T *p_entry, u8 flag); + u32 (*get_entry_clu0)(DENTRY_T *p_entry); + void (*set_entry_clu0)(DENTRY_T *p_entry, u32 clu0); + u64 (*get_entry_size)(DENTRY_T *p_entry); + void (*set_entry_size)(DENTRY_T *p_entry, u64 size); + void (*get_entry_time)(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode); + void (*set_entry_time)(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode); +} FS_FUNC_T; + +typedef struct __FS_INFO_T { + u32 drv; /* drive ID */ + u32 vol_type; /* volume FAT type */ + u32 vol_id; /* volume serial number */ + + u32 num_sectors; /* num of sectors in volume */ + u32 num_clusters; /* num of clusters in volume */ + u32 cluster_size; /* cluster size in bytes */ + u32 cluster_size_bits; + u32 sectors_per_clu; /* cluster size in sectors */ + u32 sectors_per_clu_bits; + + u32 PBR_sector; /* PBR sector */ + u32 FAT1_start_sector; /* FAT1 start sector */ + u32 FAT2_start_sector; /* FAT2 start sector */ + u32 root_start_sector; /* root dir start sector */ + u32 data_start_sector; /* data area start sector */ + u32 num_FAT_sectors; /* num of FAT sectors */ + + u32 root_dir; /* root dir cluster */ + u32 dentries_in_root; /* num of dentries in root dir */ + u32 dentries_per_clu; /* num of dentries per cluster */ + + u32 vol_flag; /* volume dirty flag */ + struct buffer_head *pbr_bh; /* PBR sector */ + + u32 map_clu; /* allocation bitmap start cluster */ + u32 map_sectors; /* num of allocation bitmap sectors */ + struct buffer_head **vol_amap; /* allocation bitmap */ + + u16 **vol_utbl; /* upcase table */ + + u32 clu_srch_ptr; /* cluster search pointer */ + u32 used_clusters; /* number of used clusters */ + UENTRY_T hint_uentry; /* unused entry hint information */ + + u32 dev_ejected; /* block device operation error flag */ + + FS_FUNC_T *fs_func; + struct semaphore v_sem; + + /* FAT cache */ + BUF_CACHE_T FAT_cache_array[FAT_CACHE_SIZE]; + BUF_CACHE_T FAT_cache_lru_list; + BUF_CACHE_T FAT_cache_hash_list[FAT_CACHE_HASH_SIZE]; + + /* buf cache */ + BUF_CACHE_T buf_cache_array[BUF_CACHE_SIZE]; + BUF_CACHE_T buf_cache_lru_list; + BUF_CACHE_T buf_cache_hash_list[BUF_CACHE_HASH_SIZE]; +} FS_INFO_T; + +#define ES_2_ENTRIES 2 +#define ES_3_ENTRIES 3 +#define ES_ALL_ENTRIES 0 + +typedef struct { + u32 sector; /* sector number that contains file_entry */ + s32 offset; /* byte offset in the sector */ + s32 alloc_flag; /* flag in stream entry. 01 for cluster chain, 03 for contig. clusteres. */ + u32 num_entries; + + /* __buf should be the last member */ + void *__buf; +} ENTRY_SET_CACHE_T; + +/*----------------------------------------------------------------------*/ +/* External Function Declarations */ +/*----------------------------------------------------------------------*/ + +/* file system initialization & shutdown functions */ +s32 ffsInit(void); +s32 ffsShutdown(void); + +/* volume management functions */ +s32 ffsMountVol(struct super_block *sb); +s32 ffsUmountVol(struct super_block *sb); +s32 ffsCheckVol(struct super_block *sb); +s32 ffsGetVolInfo(struct super_block *sb, VOL_INFO_T *info); +s32 ffsSyncVol(struct super_block *sb, s32 do_sync); + +/* file management functions */ +s32 ffsLookupFile(struct inode *inode, char *path, FILE_ID_T *fid); +s32 ffsCreateFile(struct inode *inode, char *path, u8 mode, FILE_ID_T *fid); +s32 ffsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount); +s32 ffsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount); +s32 ffsTruncateFile(struct inode *inode, u64 old_size, u64 new_size); +s32 ffsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry); +s32 ffsRemoveFile(struct inode *inode, FILE_ID_T *fid); +s32 ffsSetAttr(struct inode *inode, u32 attr); +s32 ffsGetStat(struct inode *inode, DIR_ENTRY_T *info); +s32 ffsSetStat(struct inode *inode, DIR_ENTRY_T *info); +s32 ffsMapCluster(struct inode *inode, s32 clu_offset, u32 *clu); + +/* directory management functions */ +s32 ffsCreateDir(struct inode *inode, char *path, FILE_ID_T *fid); +s32 ffsReadDir(struct inode *inode, DIR_ENTRY_T *dir_ent); +s32 ffsRemoveDir(struct inode *inode, FILE_ID_T *fid); + +/*----------------------------------------------------------------------*/ +/* External Function Declarations (NOT TO UPPER LAYER) */ +/*----------------------------------------------------------------------*/ + +/* fs management functions */ +s32 fs_init(void); +s32 fs_shutdown(void); +void fs_set_vol_flags(struct super_block *sb, u32 new_flag); +void fs_sync(struct super_block *sb, s32 do_sync); +void fs_error(struct super_block *sb); + +/* cluster management functions */ +s32 clear_cluster(struct super_block *sb, u32 clu); +s32 fat_alloc_cluster(struct super_block *sb, s32 num_alloc, CHAIN_T *p_chain); +s32 exfat_alloc_cluster(struct super_block *sb, s32 num_alloc, CHAIN_T *p_chain); +void fat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, s32 do_relse); +void exfat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, s32 do_relse); +u32 find_last_cluster(struct super_block *sb, CHAIN_T *p_chain); +s32 count_num_clusters(struct super_block *sb, CHAIN_T *dir); +s32 fat_count_used_clusters(struct super_block *sb); +s32 exfat_count_used_clusters(struct super_block *sb); +void exfat_chain_cont_cluster(struct super_block *sb, u32 chain, s32 len); + +/* allocation bitmap management functions */ +s32 load_alloc_bitmap(struct super_block *sb); +void free_alloc_bitmap(struct super_block *sb); +s32 set_alloc_bitmap(struct super_block *sb, u32 clu); +s32 clr_alloc_bitmap(struct super_block *sb, u32 clu); +u32 test_alloc_bitmap(struct super_block *sb, u32 clu); +void sync_alloc_bitmap(struct super_block *sb); + +/* upcase table management functions */ +s32 load_upcase_table(struct super_block *sb); +void free_upcase_table(struct super_block *sb); + +/* dir entry management functions */ +u32 fat_get_entry_type(DENTRY_T *p_entry); +u32 exfat_get_entry_type(DENTRY_T *p_entry); +void fat_set_entry_type(DENTRY_T *p_entry, u32 type); +void exfat_set_entry_type(DENTRY_T *p_entry, u32 type); +u32 fat_get_entry_attr(DENTRY_T *p_entry); +u32 exfat_get_entry_attr(DENTRY_T *p_entry); +void fat_set_entry_attr(DENTRY_T *p_entry, u32 attr); +void exfat_set_entry_attr(DENTRY_T *p_entry, u32 attr); +u8 fat_get_entry_flag(DENTRY_T *p_entry); +u8 exfat_get_entry_flag(DENTRY_T *p_entry); +void fat_set_entry_flag(DENTRY_T *p_entry, u8 flag); +void exfat_set_entry_flag(DENTRY_T *p_entry, u8 flag); +u32 fat_get_entry_clu0(DENTRY_T *p_entry); +u32 exfat_get_entry_clu0(DENTRY_T *p_entry); +void fat_set_entry_clu0(DENTRY_T *p_entry, u32 start_clu); +void exfat_set_entry_clu0(DENTRY_T *p_entry, u32 start_clu); +u64 fat_get_entry_size(DENTRY_T *p_entry); +u64 exfat_get_entry_size(DENTRY_T *p_entry); +void fat_set_entry_size(DENTRY_T *p_entry, u64 size); +void exfat_set_entry_size(DENTRY_T *p_entry, u64 size); +void fat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode); +void exfat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode); +void fat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode); +void exfat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode); +s32 fat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 type, u32 start_clu, u64 size); +s32 exfat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 type, u32 start_clu, u64 size); +s32 fat_init_ext_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 num_entries, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname); +s32 exfat_init_ext_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 num_entries, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname); +void init_dos_entry(DOS_DENTRY_T *ep, u32 type, u32 start_clu); +void init_ext_entry(EXT_DENTRY_T *ep, s32 order, u8 chksum, u16 *uniname); +void init_file_entry(FILE_DENTRY_T *ep, u32 type); +void init_strm_entry(STRM_DENTRY_T *ep, u8 flags, u32 start_clu, u64 size); +void init_name_entry(NAME_DENTRY_T *ep, u16 *uniname); +void fat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 order, s32 num_entries); +void exfat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 order, s32 num_entries); + +s32 find_location(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 *sector, s32 *offset); +DENTRY_T *get_entry_with_sector(struct super_block *sb, u32 sector, s32 offset); +DENTRY_T *get_entry_in_dir(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 *sector); +ENTRY_SET_CACHE_T *get_entry_set_in_dir(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 type, DENTRY_T **file_ep); +void release_entry_set(ENTRY_SET_CACHE_T *es); +s32 write_whole_entry_set(struct super_block *sb, ENTRY_SET_CACHE_T *es); +s32 write_partial_entries_in_entry_set(struct super_block *sb, ENTRY_SET_CACHE_T *es, DENTRY_T *ep, u32 count); +s32 search_deleted_or_unused_entry(struct super_block *sb, CHAIN_T *p_dir, s32 num_entries); +s32 find_empty_entry(struct inode *inode, CHAIN_T *p_dir, s32 num_entries); +s32 fat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, s32 num_entries, DOS_NAME_T *p_dosname, u32 type); +s32 exfat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, s32 num_entries, DOS_NAME_T *p_dosname, u32 type); +s32 fat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, s32 entry, DENTRY_T *p_entry); +s32 exfat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, s32 entry, DENTRY_T *p_entry); +s32 count_dos_name_entries(struct super_block *sb, CHAIN_T *p_dir, u32 type); +void update_dir_checksum(struct super_block *sb, CHAIN_T *p_dir, s32 entry); +void update_dir_checksum_with_entry_set(struct super_block *sb, ENTRY_SET_CACHE_T *es); +bool is_dir_empty(struct super_block *sb, CHAIN_T *p_dir); + +/* name conversion functions */ +s32 get_num_entries_and_dos_name(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, s32 *entries, DOS_NAME_T *p_dosname); +void get_uni_name_from_dos_entry(struct super_block *sb, DOS_DENTRY_T *ep, UNI_NAME_T *p_uniname, u8 mode); +void fat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u16 *uniname); +void exfat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u16 *uniname); +s32 extract_uni_name_from_ext_entry(EXT_DENTRY_T *ep, u16 *uniname, s32 order); +s32 extract_uni_name_from_name_entry(NAME_DENTRY_T *ep, u16 *uniname, s32 order); +s32 fat_generate_dos_name(struct super_block *sb, CHAIN_T *p_dir, DOS_NAME_T *p_dosname); +void fat_attach_count_to_dos_name(u8 *dosname, s32 count); +s32 fat_calc_num_entries(UNI_NAME_T *p_uniname); +s32 exfat_calc_num_entries(UNI_NAME_T *p_uniname); +u8 calc_checksum_1byte(void *data, s32 len, u8 chksum); +u16 calc_checksum_2byte(void *data, s32 len, u16 chksum, s32 type); +u32 calc_checksum_4byte(void *data, s32 len, u32 chksum, s32 type); + +/* name resolution functions */ +s32 resolve_path(struct inode *inode, char *path, CHAIN_T *p_dir, UNI_NAME_T *p_uniname); +s32 resolve_name(u8 *name, u8 **arg); + +/* file operation functions */ +s32 fat16_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr); +s32 fat32_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr); +s32 exfat_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr); +s32 create_dir(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, FILE_ID_T *fid); +s32 create_file(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, u8 mode, FILE_ID_T *fid); +void remove_file(struct inode *inode, CHAIN_T *p_dir, s32 entry); +s32 rename_file(struct inode *inode, CHAIN_T *p_dir, s32 old_entry, UNI_NAME_T *p_uniname, FILE_ID_T *fid); +s32 move_file(struct inode *inode, CHAIN_T *p_olddir, s32 oldentry, CHAIN_T *p_newdir, UNI_NAME_T *p_uniname, FILE_ID_T *fid); + +/* sector read/write functions */ +s32 sector_read(struct super_block *sb, u32 sec, struct buffer_head **bh, s32 read); +s32 sector_write(struct super_block *sb, u32 sec, struct buffer_head *bh, s32 sync); +s32 multi_sector_read(struct super_block *sb, u32 sec, struct buffer_head **bh, s32 num_secs, s32 read); +s32 multi_sector_write(struct super_block *sb, u32 sec, struct buffer_head *bh, s32 num_secs, s32 sync); + +#endif /* _EXFAT_H */ diff --git a/fs/exfat/exfat_data.c b/fs/exfat/exfat_data.c new file mode 100644 index 00000000..01f05441 --- /dev/null +++ b/fs/exfat/exfat_data.c @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_data.c */ +/* PURPOSE : exFAT Configuable Data Definitions */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#include "exfat_config.h" +#include "exfat_data.h" +#include "exfat_oal.h" + +#include "exfat_blkdev.h" +#include "exfat_cache.h" +#include "exfat_nls.h" +#include "exfat_super.h" +#include "exfat_core.h" + +/*======================================================================*/ +/* */ +/* GLOBAL VARIABLE DEFINITIONS */ +/* */ +/*======================================================================*/ + +/*----------------------------------------------------------------------*/ +/* File Manager */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Buffer Manager */ +/*----------------------------------------------------------------------*/ + +/* FAT cache */ +DEFINE_SEMAPHORE(f_sem); +BUF_CACHE_T FAT_cache_array[FAT_CACHE_SIZE]; +BUF_CACHE_T FAT_cache_lru_list; +BUF_CACHE_T FAT_cache_hash_list[FAT_CACHE_HASH_SIZE]; + +/* buf cache */ +DEFINE_SEMAPHORE(b_sem); +BUF_CACHE_T buf_cache_array[BUF_CACHE_SIZE]; +BUF_CACHE_T buf_cache_lru_list; +BUF_CACHE_T buf_cache_hash_list[BUF_CACHE_HASH_SIZE]; diff --git a/fs/exfat/exfat_data.h b/fs/exfat/exfat_data.h new file mode 100644 index 00000000..53b0e393 --- /dev/null +++ b/fs/exfat/exfat_data.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_data.h */ +/* PURPOSE : Header File for exFAT Configuable Constants */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_DATA_H +#define _EXFAT_DATA_H + +#include "exfat_config.h" + +/*======================================================================*/ +/* */ +/* FFS CONFIGURATIONS */ +/* (CHANGE THIS PART IF REQUIRED) */ +/* */ +/*======================================================================*/ + +/* max number of root directory entries in FAT12/16 */ +/* (should be an exponential value of 2) */ +#define MAX_DENTRY 512 + +/* cache size (in number of sectors) */ +/* (should be an exponential value of 2) */ +#define FAT_CACHE_SIZE 128 +#define FAT_CACHE_HASH_SIZE 64 +#define BUF_CACHE_SIZE 256 +#define BUF_CACHE_HASH_SIZE 64 + +#endif /* _EXFAT_DATA_H */ diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h new file mode 100644 index 00000000..522b8076 --- /dev/null +++ b/fs/exfat/exfat_fs.h @@ -0,0 +1,377 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + */ + +#ifndef _EXFAT_FS_H +#define _EXFAT_FS_H + +#include +#include +#include + +#ifndef MSDOS_SUPER_MAGIC +#define MSDOS_SUPER_MAGIC 0x4d44 /* MD */ +#endif + +#ifndef EXFAT_SUPER_MAGIC +#define EXFAT_SUPER_MAGIC (0x2011BAB0UL) +#endif /* EXFAT_SUPER_MAGIC */ + +#ifndef EXFAT_SUPER_MAGIC +#define EXFAT_SUPER_MAGIC (0x5EC5DFA4UL) +#endif /* EXFAT_SUPER_MAGIC */ + +#define EXFAT_ROOT_INO 1 + +/* FAT types */ +#define FAT12 0x01 // FAT12 +#define FAT16 0x0E // Win95 FAT16 (LBA) +#define FAT32 0x0C // Win95 FAT32 (LBA) +#define EXFAT 0x07 // exFAT + +/* directory file name */ +#define DOS_CUR_DIR_NAME ". " +#define DOS_PAR_DIR_NAME ".. " + +#ifdef __LITTLE_ENDIAN +#define UNI_CUR_DIR_NAME ".\0" +#define UNI_PAR_DIR_NAME ".\0.\0" +#else +#define UNI_CUR_DIR_NAME "\0." +#define UNI_PAR_DIR_NAME "\0.\0." +#endif + +/* file name lengths */ +/* NOTE : + * The maximum length of input or output is limited to 256 including NULL, + * But we allocate 4 extra bytes for utf8 translation reside in last position, + * because utf8 can uses memory upto 6 bytes per one character. + * Therefore, MAX_CHARSET_SIZE supports upto 6 bytes for utf8 + */ +#define MAX_UNINAME_BUF_SIZE (((MAX_NAME_LENGTH+1)*2)+4) +#define MAX_DOSNAME_BUF_SIZE ((DOS_NAME_LENGTH+2)+6) +#define MAX_VFSNAME_BUF_SIZE ((MAX_NAME_LENGTH+1)*MAX_CHARSET_SIZE) +#define MAX_CHARSET_SIZE 6 // max size of multi-byte character +#define MAX_NAME_LENGTH 255 // max len of file name excluding NULL +#define DOS_NAME_LENGTH 11 // DOS file name length excluding NULL + +#define SECTOR_SIZE_BITS 9 /* VFS sector size is 512 bytes */ + +#define DENTRY_SIZE 32 /* directory entry size */ +#define DENTRY_SIZE_BITS 5 + +#define MAX_FAT_DENTRIES 65536 /* FAT allows 65536 directory entries */ +#define MAX_EXFAT_DENTRIES 8388608 /* exFAT allows 8388608(256MB) directory entries */ + +/* PBR entries */ +#define PBR_SIGNATURE 0xAA55 +#define EXT_SIGNATURE 0xAA550000 +#define VOL_LABEL "NO NAME " /* size should be 11 */ +#define OEM_NAME "MSWIN4.1" /* size should be 8 */ +#define STR_FAT12 "FAT12 " /* size should be 8 */ +#define STR_FAT16 "FAT16 " /* size should be 8 */ +#define STR_FAT32 "FAT32 " /* size should be 8 */ +#define STR_EXFAT "EXFAT " /* size should be 8 */ + +#define VOL_CLEAN 0x0000 +#define VOL_DIRTY 0x0002 + +#define FAT_VOL_DIRTY 0x01 + +/* max number of clusters */ +#define FAT12_THRESHOLD 4087 // 2^12 - 1 + 2 (clu 0 & 1) +#define FAT16_THRESHOLD 65527 // 2^16 - 1 + 2 +#define FAT32_THRESHOLD 268435457 // 2^28 - 1 + 2 +#define EXFAT_THRESHOLD 268435457 // 2^28 - 1 + 2 + +/* dentry types */ +#define MSDOS_DELETED 0xE5 /* deleted mark */ +#define MSDOS_UNUSED 0x00 /* end of directory */ + +#define EXFAT_UNUSED 0x00 /* end of directory */ +#define IS_EXFAT_DELETED(x) ((x) < 0x80) /* deleted file (0x01~0x7F) */ +#define EXFAT_INVAL 0x80 /* invalid value */ +#define EXFAT_BITMAP 0x81 /* allocation bitmap */ +#define EXFAT_UPCASE 0x82 /* upcase table */ +#define EXFAT_VOLUME 0x83 /* volume label */ +#define EXFAT_FILE 0x85 /* file or dir */ +#define EXFAT_STREAM 0xC0 /* stream entry */ +#define EXFAT_NAME 0xC1 /* file name entry */ +#define EXFAT_ACL 0xC2 /* stream entry */ + +/* specific flag */ +#define MSDOS_LAST_LFN 0x40 + +/* file attributes */ +#define ATTR_NORMAL 0x0000 +#define ATTR_READONLY 0x0001 +#define ATTR_HIDDEN 0x0002 +#define ATTR_SYSTEM 0x0004 +#define ATTR_VOLUME 0x0008 +#define ATTR_SUBDIR 0x0010 +#define ATTR_ARCHIVE 0x0020 +#define ATTR_SYMLINK 0x0040 +#define ATTR_EXTEND (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | \ + ATTR_VOLUME) /* 0x000F */ + +#define ATTR_EXTEND_MASK (ATTR_EXTEND | ATTR_SUBDIR | ATTR_ARCHIVE) +#define ATTR_RWMASK (ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME | \ + ATTR_SUBDIR | ATTR_ARCHIVE | ATTR_SYMLINK)/* 0x007E */ + +/* file creation modes */ +#define FM_REGULAR 0x00 +#define FM_SYMLINK 0x40 + +/* time modes */ +#define TM_CREATE 0 +#define TM_MODIFY 1 +#define TM_ACCESS 2 + +/* checksum types */ +#define CS_DIR_ENTRY 0 +#define CS_PBR_SECTOR 1 +#define CS_DEFAULT 2 + +/* + * ioctl command + */ +#define EXFAT_IOCTL_GET_VOLUME_ID _IOR('r', 0x12, __u32) + +/* FAT12/16 BIOS parameter block (64 bytes) */ +typedef struct { + __u8 jmp_boot[3]; + __u8 oem_name[8]; + + __u8 sect_size[2]; /* unaligned */ + __u8 sect_per_clus; + __le16 num_reserved; /* . */ + __u8 num_fats; + __u8 num_root_entries[2]; /* unaligned */ + __u8 num_sectors[2]; /* unaligned */ + __u8 media_type; + __le16 num_fat_sectors; + __le16 sectors_in_track; + __le16 num_heads; + __le32 num_hid_sectors; /* . */ + __le32 num_huge_sectors; + + __u8 phy_drv_no; + __u8 state; /* used by WindowsNT for mount state */ + __u8 ext_signature; + __u8 vol_serial[4]; + __u8 vol_label[11]; + __u8 vol_type[8]; + __le16 dummy; +} bpb16_t; + +/* FAT32 BIOS parameter block (64 bytes) */ +typedef struct { + __u8 jmp_boot[3]; + __u8 oem_name[8]; + + __u8 sect_size[2]; /* unaligned */ + __u8 sect_per_clus; + __le16 num_reserved; + __u8 num_fats; + __u8 num_root_entries[2]; /* unaligned */ + __u8 num_sectors[2]; /* unaligned */ + __u8 media_type; + __le16 num_fat_sectors; /* zero */ + __le16 sectors_in_track; + __le16 num_heads; + __le32 num_hid_sectors; /* . */ + __le32 num_huge_sectors; + + __le32 num_fat32_sectors; + __le16 ext_flags; + __u8 fs_version[2]; + __le32 root_cluster; /* . */ + __le16 fsinfo_sector; + __le16 backup_sector; + __le16 reserved[6]; /* . */ +} bpb32_t; + +/* FAT32 EXTEND BIOS parameter block (32 bytes) */ +typedef struct { + __u8 phy_drv_no; + __u8 state; /* used by WindowsNT for mount state */ + __u8 ext_signature; + __u8 vol_serial[4]; + __u8 vol_label[11]; + __u8 vol_type[8]; + __le16 dummy[3]; +} bsx32_t; + +/* EXFAT BIOS parameter block (64 bytes) */ +typedef struct { + __u8 jmp_boot[3]; + __u8 oem_name[8]; + __u8 res_zero[53]; +} bpb64_t; + +/* EXFAT EXTEND BIOS parameter block (56 bytes) */ +typedef struct { + __le64 vol_offset; + __le64 vol_length; + __le32 fat_offset; + __le32 fat_length; + __le32 clu_offset; + __le32 clu_count; + __le32 root_cluster; + __le32 vol_serial; + __u8 fs_version[2]; + __le16 vol_flags; + __u8 sect_size_bits; + __u8 sect_per_clus_bits; + __u8 num_fats; + __u8 phy_drv_no; + __u8 perc_in_use; + __u8 reserved2[7]; +} bsx64_t; + +/* FAT32 PBR (64 bytes) */ +typedef struct { + bpb16_t bpb; +} pbr16_t; + +/* FAT32 PBR[BPB+BSX] (96 bytes) */ +typedef struct { + bpb32_t bpb; + bsx32_t bsx; +} pbr32_t; + +/* EXFAT PBR[BPB+BSX] (120 bytes) */ +typedef struct { + bpb64_t bpb; + bsx64_t bsx; +} pbr64_t; + +/* Common PBR[Partition Boot Record] (512 bytes) */ +typedef struct { + union { + __u8 raw[64]; + bpb16_t f16; + bpb32_t f32; + bpb64_t f64; + } bpb; + union { + __u8 raw[56]; + bsx32_t f32; + bsx64_t f64; + } bsx; + __u8 boot_code[390]; + __le16 signature; +} pbr_t; + +/* FAT32 filesystem information sector (512 bytes) */ +typedef struct { + __le32 signature1; // aligned + __u8 reserved1[480]; + __le32 signature2; // aligned + __le32 free_cluster; // aligned + __le32 next_cluster; // aligned + __u8 reserved2[14]; + __le16 signature3[2]; +} fat32_fsi_t; + +/* FAT directory entry (32 bytes) */ +typedef struct { + __u8 dummy[32]; +} DENTRY_T; + +typedef struct { + __u8 name[DOS_NAME_LENGTH]; /* 11 chars */ + __u8 attr; + __u8 lcase; + __u8 create_time_ms; + __le16 create_time; // aligned + __le16 create_date; // aligned + __le16 access_date; // aligned + __le16 start_clu_hi; // aligned + __le16 modify_time; // aligned + __le16 modify_date; // aligned + __le16 start_clu_lo; // aligned + __le32 size; // aligned +} DOS_DENTRY_T; + +/* FAT extended directory entry (32 bytes) */ +typedef struct { + __u8 order; + __u8 unicode_0_4[10]; + __u8 attr; + __u8 sysid; + __u8 checksum; + __le16 unicode_5_10[6]; // aligned + __le16 start_clu; // aligned + __le16 unicode_11_12[2]; // aligned +} EXT_DENTRY_T; + +/* EXFAT file directory entry (32 bytes) */ +typedef struct { + __u8 type; + __u8 num_ext; + __le16 checksum; // aligned + __le16 attr; // aligned + __le16 reserved1; + __le16 create_time; // aligned + __le16 create_date; // aligned + __le16 modify_time; // aligned + __le16 modify_date; // aligned + __le16 access_time; // aligned + __le16 access_date; // aligned + __u8 create_time_ms; + __u8 modify_time_ms; + __u8 access_time_ms; + __u8 reserved2[9]; +} FILE_DENTRY_T; + +/* EXFAT stream extension directory entry (32 bytes) */ +typedef struct { + __u8 type; + __u8 flags; + __u8 reserved1; + __u8 name_len; + __le16 name_hash; // aligned + __le16 reserved2; + __le64 valid_size; // aligned + __le32 reserved3; // aligned + __le32 start_clu; // aligned + __le64 size; // aligned +} STRM_DENTRY_T; + +/* EXFAT file name directory entry (32 bytes) */ +typedef struct { + __u8 type; + __u8 flags; + __le16 unicode_0_14[15]; // aligned +} NAME_DENTRY_T; + +/* EXFAT allocation bitmap directory entry (32 bytes) */ +typedef struct { + __u8 type; + __u8 flags; + __u8 reserved[18]; + __le32 start_clu; // aligned + __le64 size; // aligned +} BMAP_DENTRY_T; + +/* EXFAT up-case table directory entry (32 bytes) */ +typedef struct { + __u8 type; + __u8 reserved1[3]; + __le32 checksum; // aligned + __u8 reserved2[12]; + __le32 start_clu; // aligned + __le64 size; // aligned +} CASE_DENTRY_T; + +/* EXFAT volume label directory entry (32 bytes) */ +typedef struct { + __u8 type; + __u8 label_len; + __le16 unicode_0_10[11]; // aligned + __u8 reserved[8]; +} VOLM_DENTRY_T; + +#endif /* _EXFAT_FS_H */ diff --git a/fs/exfat/exfat_nls.c b/fs/exfat/exfat_nls.c new file mode 100644 index 00000000..3dc7e9c6 --- /dev/null +++ b/fs/exfat/exfat_nls.c @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_nls.c */ +/* PURPOSE : exFAT NLS Manager */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#include "exfat_config.h" +#include "exfat_data.h" + +#include "exfat_nls.h" +#include "exfat_api.h" +#include "exfat_super.h" +#include "exfat_core.h" + +#include + +/*----------------------------------------------------------------------*/ +/* Global Variable Definitions */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Local Variable Definitions */ +/*----------------------------------------------------------------------*/ + +static u16 bad_dos_chars[] = { + /* + , ; = [ ] */ + 0x002B, 0x002C, 0x003B, 0x003D, 0x005B, 0x005D, + 0xFF0B, 0xFF0C, 0xFF1B, 0xFF1D, 0xFF3B, 0xFF3D, + 0 +}; + +static u16 bad_uni_chars[] = { + /* " * / : < > ? \ | */ + 0x0022, 0x002A, 0x002F, 0x003A, + 0x003C, 0x003E, 0x003F, 0x005C, 0x007C, + 0 +}; + +/*----------------------------------------------------------------------*/ +/* Local Function Declarations */ +/*----------------------------------------------------------------------*/ + +static s32 convert_uni_to_ch(struct nls_table *nls, u8 *ch, u16 uni, s32 *lossy); +static s32 convert_ch_to_uni(struct nls_table *nls, u16 *uni, u8 *ch, s32 *lossy); + +/*======================================================================*/ +/* Global Function Definitions */ +/*======================================================================*/ + +u16 nls_upper(struct super_block *sb, u16 a) +{ + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (EXFAT_SB(sb)->options.casesensitive) + return a; + if (p_fs->vol_utbl != NULL && (p_fs->vol_utbl)[get_col_index(a)] != NULL) + return (p_fs->vol_utbl)[get_col_index(a)][get_row_index(a)]; + else + return a; +} + +u16 *nls_wstrchr(u16 *str, u16 wchar) +{ + while (*str) { + if (*(str++) == wchar) + return str; + } + + return 0; +} + +s32 nls_dosname_cmp(struct super_block *sb, u8 *a, u8 *b) +{ + return strncmp((void *) a, (void *) b, DOS_NAME_LENGTH); +} /* end of nls_dosname_cmp */ + +s32 nls_uniname_cmp(struct super_block *sb, u16 *a, u16 *b) +{ + int i; + + for (i = 0; i < MAX_NAME_LENGTH; i++, a++, b++) { + if (nls_upper(sb, *a) != nls_upper(sb, *b)) + return 1; + if (*a == 0x0) + return 0; + } + return 0; +} /* end of nls_uniname_cmp */ + +void nls_uniname_to_dosname(struct super_block *sb, DOS_NAME_T *p_dosname, UNI_NAME_T *p_uniname, s32 *p_lossy) +{ + int i, j, len, lossy = FALSE; + u8 buf[MAX_CHARSET_SIZE]; + u8 lower = 0, upper = 0; + u8 *dosname = p_dosname->name; + u16 *uniname = p_uniname->name; + u16 *p, *last_period; + struct nls_table *nls = EXFAT_SB(sb)->nls_disk; + + for (i = 0; i < DOS_NAME_LENGTH; i++) + *(dosname+i) = ' '; + + if (!nls_uniname_cmp(sb, uniname, (u16 *) UNI_CUR_DIR_NAME)) { + *(dosname) = '.'; + p_dosname->name_case = 0x0; + if (p_lossy != NULL) + *p_lossy = FALSE; + return; + } + + if (!nls_uniname_cmp(sb, uniname, (u16 *) UNI_PAR_DIR_NAME)) { + *(dosname) = '.'; + *(dosname+1) = '.'; + p_dosname->name_case = 0x0; + if (p_lossy != NULL) + *p_lossy = FALSE; + return; + } + + /* search for the last embedded period */ + last_period = NULL; + for (p = uniname; *p; p++) { + if (*p == (u16) '.') + last_period = p; + } + + i = 0; + while (i < DOS_NAME_LENGTH) { + if (i == 8) { + if (last_period == NULL) + break; + + if (uniname <= last_period) { + if (uniname < last_period) + lossy = TRUE; + uniname = last_period + 1; + } + } + + if (*uniname == (u16) '\0') { + break; + } else if (*uniname == (u16) ' ') { + lossy = TRUE; + } else if (*uniname == (u16) '.') { + if (uniname < last_period) + lossy = TRUE; + else + i = 8; + } else if (nls_wstrchr(bad_dos_chars, *uniname)) { + lossy = TRUE; + *(dosname+i) = '_'; + i++; + } else { + len = convert_uni_to_ch(nls, buf, *uniname, &lossy); + + if (len > 1) { + if ((i >= 8) && ((i+len) > DOS_NAME_LENGTH)) + break; + + if ((i < 8) && ((i+len) > 8)) { + i = 8; + continue; + } + + lower = 0xFF; + + for (j = 0; j < len; j++, i++) + *(dosname+i) = *(buf+j); + } else { /* len == 1 */ + if ((*buf >= 'a') && (*buf <= 'z')) { + *(dosname+i) = *buf - ('a' - 'A'); + + if (i < 8) + lower |= 0x08; + else + lower |= 0x10; + } else if ((*buf >= 'A') && (*buf <= 'Z')) { + *(dosname+i) = *buf; + + if (i < 8) + upper |= 0x08; + else + upper |= 0x10; + } else { + *(dosname+i) = *buf; + } + i++; + } + } + + uniname++; + } + + if (*dosname == 0xE5) + *dosname = 0x05; + + if (*uniname != 0x0) + lossy = TRUE; + + if (upper & lower) + p_dosname->name_case = 0xFF; + else + p_dosname->name_case = lower; + + if (p_lossy != NULL) + *p_lossy = lossy; +} /* end of nls_uniname_to_dosname */ + +void nls_dosname_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname) +{ + int i = 0, j, n = 0; + u8 buf[DOS_NAME_LENGTH+2]; + u8 *dosname = p_dosname->name; + u16 *uniname = p_uniname->name; + struct nls_table *nls = EXFAT_SB(sb)->nls_disk; + + if (*dosname == 0x05) { + *buf = 0xE5; + i++; + n++; + } + + for (; i < 8; i++, n++) { + if (*(dosname+i) == ' ') + break; + + if ((*(dosname+i) >= 'A') && (*(dosname+i) <= 'Z') && (p_dosname->name_case & 0x08)) + *(buf+n) = *(dosname+i) + ('a' - 'A'); + else + *(buf+n) = *(dosname+i); + } + if (*(dosname+8) != ' ') { + *(buf+n) = '.'; + n++; + } + + for (i = 8; i < DOS_NAME_LENGTH; i++, n++) { + if (*(dosname+i) == ' ') + break; + + if ((*(dosname+i) >= 'A') && (*(dosname+i) <= 'Z') && (p_dosname->name_case & 0x10)) + *(buf+n) = *(dosname+i) + ('a' - 'A'); + else + *(buf+n) = *(dosname+i); + } + *(buf+n) = '\0'; + + i = j = 0; + while (j < (MAX_NAME_LENGTH-1)) { + if (*(buf+i) == '\0') + break; + + i += convert_ch_to_uni(nls, uniname, (buf+i), NULL); + + uniname++; + j++; + } + + *uniname = (u16) '\0'; +} /* end of nls_dosname_to_uniname */ + +void nls_uniname_to_cstring(struct super_block *sb, u8 *p_cstring, UNI_NAME_T *p_uniname) +{ + int i, j, len; + u8 buf[MAX_CHARSET_SIZE]; + u16 *uniname = p_uniname->name; + struct nls_table *nls = EXFAT_SB(sb)->nls_io; + + if (nls == NULL) { + len = utf16s_to_utf8s(uniname, MAX_NAME_LENGTH, UTF16_HOST_ENDIAN, p_cstring, MAX_NAME_LENGTH); + p_cstring[len] = 0; + return; + } + + i = 0; + while (i < (MAX_NAME_LENGTH-1)) { + if (*uniname == (u16) '\0') + break; + + len = convert_uni_to_ch(nls, buf, *uniname, NULL); + + if (len > 1) { + for (j = 0; j < len; j++) + *p_cstring++ = (char) *(buf+j); + } else { /* len == 1 */ + *p_cstring++ = (char) *buf; + } + + uniname++; + i++; + } + + *p_cstring = '\0'; +} /* end of nls_uniname_to_cstring */ + +void nls_cstring_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, u8 *p_cstring, s32 *p_lossy) +{ + int i, j, lossy = FALSE; + u8 *end_of_name; + u8 upname[MAX_NAME_LENGTH * 2]; + u16 *uniname = p_uniname->name; + struct nls_table *nls = EXFAT_SB(sb)->nls_io; + + + /* strip all trailing spaces */ + end_of_name = p_cstring + strlen((char *) p_cstring); + + while (*(--end_of_name) == ' ') { + if (end_of_name < p_cstring) + break; + } + *(++end_of_name) = '\0'; + + if (strcmp((char *) p_cstring, ".") && strcmp((char *) p_cstring, "..")) { + + /* strip all trailing periods */ + while (*(--end_of_name) == '.') { + if (end_of_name < p_cstring) + break; + } + *(++end_of_name) = '\0'; + } + + if (*p_cstring == '\0') + lossy = TRUE; + + if (nls == NULL) { + i = utf8s_to_utf16s(p_cstring, MAX_NAME_LENGTH, UTF16_HOST_ENDIAN, uniname, MAX_NAME_LENGTH); + for (j = 0; j < i; j++) + SET16_A(upname + j * 2, nls_upper(sb, uniname[j])); + uniname[i] = '\0'; + } + else { + i = j = 0; + while (j < (MAX_NAME_LENGTH-1)) { + if (*(p_cstring+i) == '\0') + break; + + i += convert_ch_to_uni(nls, uniname, (u8 *)(p_cstring+i), &lossy); + + if ((*uniname < 0x0020) || nls_wstrchr(bad_uni_chars, *uniname)) + lossy = TRUE; + + SET16_A(upname + j * 2, nls_upper(sb, *uniname)); + + uniname++; + j++; + } + + if (*(p_cstring+i) != '\0') + lossy = TRUE; + *uniname = (u16) '\0'; + } + + p_uniname->name_len = j; + p_uniname->name_hash = calc_checksum_2byte((void *) upname, j<<1, 0, CS_NAME_HASH); + + if (p_lossy != NULL) + *p_lossy = lossy; +} /* end of nls_cstring_to_uniname */ + +/*======================================================================*/ +/* Local Function Definitions */ +/*======================================================================*/ + +static s32 convert_ch_to_uni(struct nls_table *nls, u16 *uni, u8 *ch, s32 *lossy) +{ + int len; + + *uni = 0x0; + + if (ch[0] < 0x80) { + *uni = (u16) ch[0]; + return 1; + } + + if (!nls) + { + printk("%s: no need to use nls\n", __func__); + return 1; + } + len = nls->char2uni(ch, NLS_MAX_CHARSET_SIZE, uni); + if (len < 0) { + /* conversion failed */ + printk("%s: fail to use nls\n", __func__); + if (lossy != NULL) + *lossy = TRUE; + *uni = (u16) '_'; + if (!strcmp(nls->charset, "utf8")) + return 1; + else + return 2; + } + + return len; +} /* end of convert_ch_to_uni */ + +static s32 convert_uni_to_ch(struct nls_table *nls, u8 *ch, u16 uni, s32 *lossy) +{ + int len; + + ch[0] = 0x0; + + if (uni < 0x0080) { + ch[0] = (u8) uni; + return 1; + } + + len = nls->uni2char(uni, ch, NLS_MAX_CHARSET_SIZE); + if (len < 0) { + /* conversion failed */ + printk("%s: fail to use nls\n", __func__); + if (lossy != NULL) + *lossy = TRUE; + ch[0] = '_'; + return 1; + } + + return len; + +} /* end of convert_uni_to_ch */ diff --git a/fs/exfat/exfat_nls.h b/fs/exfat/exfat_nls.h new file mode 100644 index 00000000..bc516d76 --- /dev/null +++ b/fs/exfat/exfat_nls.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_nls.h */ +/* PURPOSE : Header File for exFAT NLS Manager */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_NLS_H +#define _EXFAT_NLS_H + +#include +#include + +#include "exfat_config.h" +#include "exfat_api.h" + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions */ +/*----------------------------------------------------------------------*/ + +#define NUM_UPCASE 2918 + +#define DOS_CUR_DIR_NAME ". " +#define DOS_PAR_DIR_NAME ".. " + +#ifdef __LITTLE_ENDIAN +#define UNI_CUR_DIR_NAME ".\0" +#define UNI_PAR_DIR_NAME ".\0.\0" +#else +#define UNI_CUR_DIR_NAME "\0." +#define UNI_PAR_DIR_NAME "\0.\0." +#endif + +/*----------------------------------------------------------------------*/ +/* Type Definitions */ +/*----------------------------------------------------------------------*/ + +/* DOS name stucture */ +typedef struct { + u8 name[DOS_NAME_LENGTH]; + u8 name_case; +} DOS_NAME_T; + +/* unicode name stucture */ +typedef struct { + u16 name[MAX_NAME_LENGTH]; + u16 name_hash; + u8 name_len; +} UNI_NAME_T; + +/*----------------------------------------------------------------------*/ +/* External Function Declarations */ +/*----------------------------------------------------------------------*/ + +/* NLS management function */ +u16 nls_upper(struct super_block *sb, u16 a); +s32 nls_dosname_cmp(struct super_block *sb, u8 *a, u8 *b); +s32 nls_uniname_cmp(struct super_block *sb, u16 *a, u16 *b); +void nls_uniname_to_dosname(struct super_block *sb, DOS_NAME_T *p_dosname, UNI_NAME_T *p_uniname, s32 *p_lossy); +void nls_dosname_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname); +void nls_uniname_to_cstring(struct super_block *sb, u8 *p_cstring, UNI_NAME_T *p_uniname); +void nls_cstring_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, u8 *p_cstring, s32 *p_lossy); + +#endif /* _EXFAT_NLS_H */ diff --git a/fs/exfat/exfat_oal.c b/fs/exfat/exfat_oal.c new file mode 100644 index 00000000..6ef7c7bf --- /dev/null +++ b/fs/exfat/exfat_oal.c @@ -0,0 +1,186 @@ +/* Some of the source code in this file came from "linux/fs/fat/misc.c". */ +/* + * linux/fs/fat/misc.c + * + * Written 1992,1993 by Werner Almesberger + * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980 + * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru) + */ + +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_oal.c */ +/* PURPOSE : exFAT OS Adaptation Layer */ +/* (Semaphore Functions & Real-Time Clock Functions) */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#include +#include + +#include "exfat_config.h" +#include "exfat_api.h" +#include "exfat_oal.h" + +/*======================================================================*/ +/* */ +/* SEMAPHORE FUNCTIONS */ +/* */ +/*======================================================================*/ + +DEFINE_SEMAPHORE(z_sem); + +s32 sm_init(struct semaphore *sm) +{ + sema_init(sm, 1); + return 0; +} /* end of sm_init */ + +s32 sm_P(struct semaphore *sm) +{ + down(sm); + return 0; +} /* end of sm_P */ + +void sm_V(struct semaphore *sm) +{ + up(sm); +} /* end of sm_V */ + + +/*======================================================================*/ +/* */ +/* REAL-TIME CLOCK FUNCTIONS */ +/* */ +/*======================================================================*/ + +extern struct timezone sys_tz; + +/* + * The epoch of FAT timestamp is 1980. + * : bits : value + * date: 0 - 4: day (1 - 31) + * date: 5 - 8: month (1 - 12) + * date: 9 - 15: year (0 - 127) from 1980 + * time: 0 - 4: sec (0 - 29) 2sec counts + * time: 5 - 10: min (0 - 59) + * time: 11 - 15: hour (0 - 23) + */ +#define UNIX_SECS_1980 315532800L + +#if BITS_PER_LONG == 64 +#define UNIX_SECS_2108 4354819200L +#endif +/* days between 1.1.70 and 1.1.80 (2 leap days) */ +#define DAYS_DELTA_DECADE (365 * 10 + 2) +/* 120 (2100 - 1980) isn't leap year */ +#define NO_LEAP_YEAR_2100 (120) +#define IS_LEAP_YEAR(y) (!((y) & 3) && (y) != NO_LEAP_YEAR_2100) + +#define SECS_PER_MIN (60) +#define SECS_PER_HOUR (60 * SECS_PER_MIN) +#define SECS_PER_DAY (24 * SECS_PER_HOUR) + +#define MAKE_LEAP_YEAR(leap_year, year) \ + do { \ + if (unlikely(year > NO_LEAP_YEAR_2100)) \ + leap_year = ((year + 3) / 4) - 1; \ + else \ + leap_year = ((year + 3) / 4); \ + } while (0) + +/* Linear day numbers of the respective 1sts in non-leap years. */ +static time_t accum_days_in_year[] = { + /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ + 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, +}; + +TIMESTAMP_T *tm_current(TIMESTAMP_T *tp) +{ + struct timespec ts = CURRENT_TIME_SEC; + time_t second = ts.tv_sec; + time_t day, leap_day, month, year; + + second -= sys_tz.tz_minuteswest * SECS_PER_MIN; + + /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */ + if (second < UNIX_SECS_1980) { + tp->sec = 0; + tp->min = 0; + tp->hour = 0; + tp->day = 1; + tp->mon = 1; + tp->year = 0; + return tp; + } +#if BITS_PER_LONG == 64 + if (second >= UNIX_SECS_2108) { + tp->sec = 59; + tp->min = 59; + tp->hour = 23; + tp->day = 31; + tp->mon = 12; + tp->year = 127; + return tp; + } +#endif + + day = second / SECS_PER_DAY - DAYS_DELTA_DECADE; + year = day / 365; + + MAKE_LEAP_YEAR(leap_day, year); + if (year * 365 + leap_day > day) + year--; + + MAKE_LEAP_YEAR(leap_day, year); + + day -= year * 365 + leap_day; + + if (IS_LEAP_YEAR(year) && day == accum_days_in_year[3]) { + month = 2; + } else { + if (IS_LEAP_YEAR(year) && day > accum_days_in_year[3]) + day--; + for (month = 1; month < 12; month++) { + if (accum_days_in_year[month + 1] > day) + break; + } + } + day -= accum_days_in_year[month]; + + tp->sec = second % SECS_PER_MIN; + tp->min = (second / SECS_PER_MIN) % 60; + tp->hour = (second / SECS_PER_HOUR) % 24; + tp->day = day + 1; + tp->mon = month; + tp->year = year; + + return tp; +} /* end of tm_current */ diff --git a/fs/exfat/exfat_oal.h b/fs/exfat/exfat_oal.h new file mode 100644 index 00000000..b6dd7897 --- /dev/null +++ b/fs/exfat/exfat_oal.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_oal.h */ +/* PURPOSE : Header File for exFAT OS Adaptation Layer */ +/* (Semaphore Functions & Real-Time Clock Functions) */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_OAL_H +#define _EXFAT_OAL_H + +#include +#include "exfat_config.h" +#include + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions (Configurable) */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions (Non-Configurable) */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Type Definitions */ +/*----------------------------------------------------------------------*/ + +typedef struct { + u16 sec; /* 0 ~ 59 */ + u16 min; /* 0 ~ 59 */ + u16 hour; /* 0 ~ 23 */ + u16 day; /* 1 ~ 31 */ + u16 mon; /* 1 ~ 12 */ + u16 year; /* 0 ~ 127 (since 1980) */ +} TIMESTAMP_T; + +/*----------------------------------------------------------------------*/ +/* External Function Declarations */ +/*----------------------------------------------------------------------*/ + +s32 sm_init(struct semaphore *sm); +s32 sm_P(struct semaphore *sm); +void sm_V(struct semaphore *sm); + +TIMESTAMP_T *tm_current(TIMESTAMP_T *tm); + +#endif /* _EXFAT_OAL_H */ diff --git a/fs/exfat/exfat_super.c b/fs/exfat/exfat_super.c new file mode 100644 index 00000000..51ade498 --- /dev/null +++ b/fs/exfat/exfat_super.c @@ -0,0 +1,2459 @@ +/* Some of the source code in this file came from "linux/fs/fat/file.c","linux/fs/fat/inode.c" and "linux/fs/fat/misc.c". */ +/* + * linux/fs/fat/file.c + * + * Written 1992,1993 by Werner Almesberger + * + * regular file handling primitives for fat-based filesystems + */ + +/* + * linux/fs/fat/inode.c + * + * Written 1992,1993 by Werner Almesberger + * VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner + * Rewritten for the constant inumbers support by Al Viro + * + * Fixes: + * + * Max Cohan: Fixed invalid FSINFO offset when info_sector is 0 + */ + +/* + * linux/fs/fat/misc.c + * + * Written 1992,1993 by Werner Almesberger + * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980 + * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru) + */ + +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "exfat_version.h" +#include "exfat_config.h" +#include "exfat_data.h" +#include "exfat_oal.h" + +#include "exfat_blkdev.h" +#include "exfat_cache.h" +#include "exfat_nls.h" +#include "exfat_api.h" +#include "exfat_core.h" + +#include "exfat_super.h" + +static struct kmem_cache *exfat_inode_cachep; + +static int exfat_default_codepage = CONFIG_EXFAT_DEFAULT_CODEPAGE; +static char exfat_default_iocharset[] = CONFIG_EXFAT_DEFAULT_IOCHARSET; + +extern struct timezone sys_tz; + +#define CHECK_ERR(x) BUG_ON(x) + +#define UNIX_SECS_1980 315532800L + +#if BITS_PER_LONG == 64 +#define UNIX_SECS_2108 4354819200L +#endif +/* days between 1.1.70 and 1.1.80 (2 leap days) */ +#define DAYS_DELTA_DECADE (365 * 10 + 2) +/* 120 (2100 - 1980) isn't leap year */ +#define NO_LEAP_YEAR_2100 (120) +#define IS_LEAP_YEAR(y) (!((y) & 0x3) && (y) != NO_LEAP_YEAR_2100) + +#define SECS_PER_MIN (60) +#define SECS_PER_HOUR (60 * SECS_PER_MIN) +#define SECS_PER_DAY (24 * SECS_PER_HOUR) + +#define MAKE_LEAP_YEAR(leap_year, year) \ + do { \ + if (unlikely(year > NO_LEAP_YEAR_2100)) \ + leap_year = ((year + 3) / 4) - 1; \ + else \ + leap_year = ((year + 3) / 4); \ + } while (0) + +/* Linear day numbers of the respective 1sts in non-leap years. */ +static time_t accum_days_in_year[] = { + /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ + 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, +}; + +static void _exfat_truncate(struct inode *inode, loff_t old_size); + +/* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */ +void exfat_time_fat2unix(struct exfat_sb_info *sbi, struct timespec *ts, + DATE_TIME_T *tp) +{ + time_t year = tp->Year; + time_t ld; + + MAKE_LEAP_YEAR(ld, year); + + if (IS_LEAP_YEAR(year) && (tp->Month) > 2) + ld++; + + ts->tv_sec = tp->Second + tp->Minute * SECS_PER_MIN + + tp->Hour * SECS_PER_HOUR + + (year * 365 + ld + accum_days_in_year[(tp->Month)] + (tp->Day - 1) + DAYS_DELTA_DECADE) * SECS_PER_DAY + + sys_tz.tz_minuteswest * SECS_PER_MIN; + ts->tv_nsec = 0; +} + +/* Convert linear UNIX date to a FAT time/date pair. */ +void exfat_time_unix2fat(struct exfat_sb_info *sbi, struct timespec *ts, + DATE_TIME_T *tp) +{ + time_t second = ts->tv_sec; + time_t day, month, year; + time_t ld; + + second -= sys_tz.tz_minuteswest * SECS_PER_MIN; + + /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */ + if (second < UNIX_SECS_1980) { + tp->Second = 0; + tp->Minute = 0; + tp->Hour = 0; + tp->Day = 1; + tp->Month = 1; + tp->Year = 0; + return; + } +#if (BITS_PER_LONG == 64) + if (second >= UNIX_SECS_2108) { + tp->Second = 59; + tp->Minute = 59; + tp->Hour = 23; + tp->Day = 31; + tp->Month = 12; + tp->Year = 127; + return; + } +#endif + day = second / SECS_PER_DAY - DAYS_DELTA_DECADE; + year = day / 365; + MAKE_LEAP_YEAR(ld, year); + if (year * 365 + ld > day) + year--; + + MAKE_LEAP_YEAR(ld, year); + day -= year * 365 + ld; + + if (IS_LEAP_YEAR(year) && day == accum_days_in_year[3]) { + month = 2; + } else { + if (IS_LEAP_YEAR(year) && day > accum_days_in_year[3]) + day--; + for (month = 1; month < 12; month++) { + if (accum_days_in_year[month + 1] > day) + break; + } + } + day -= accum_days_in_year[month]; + + tp->Second = second % SECS_PER_MIN; + tp->Minute = (second / SECS_PER_MIN) % 60; + tp->Hour = (second / SECS_PER_HOUR) % 24; + tp->Day = day + 1; + tp->Month = month; + tp->Year = year; +} + +static struct inode *exfat_iget(struct super_block *sb, loff_t i_pos); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) +static int exfat_generic_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +#else +static long exfat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +#endif +static int exfat_sync_inode(struct inode *inode); +static struct inode *exfat_build_inode(struct super_block *sb, FILE_ID_T *fid, loff_t i_pos); +static void exfat_detach(struct inode *inode); +static void exfat_attach(struct inode *inode, loff_t i_pos); +static inline unsigned long exfat_hash(loff_t i_pos); +static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc); +static void exfat_write_super(struct super_block *sb); + +static void __lock_super(struct super_block *sb) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) + lock_super(sb); +#else + struct exfat_sb_info *sbi = EXFAT_SB(sb); + mutex_lock(&sbi->s_lock); +#endif +} + +static void __unlock_super(struct super_block *sb) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) + unlock_super(sb); +#else + struct exfat_sb_info *sbi = EXFAT_SB(sb); + mutex_unlock(&sbi->s_lock); +#endif +} + +static int __is_sb_dirty(struct super_block *sb) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) + return sb->s_dirt; +#else + struct exfat_sb_info *sbi = EXFAT_SB(sb); + return sbi->s_dirt; +#endif +} + +static void __set_sb_clean(struct super_block *sb) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) + sb->s_dirt = 0; +#else + struct exfat_sb_info *sbi = EXFAT_SB(sb); + sbi->s_dirt = 0; +#endif +} + +static int __exfat_revalidate(struct dentry *dentry) +{ + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) +static int exfat_revalidate(struct dentry *dentry, unsigned int flags) +#else +static int exfat_revalidate(struct dentry *dentry, struct nameidata *nd) +#endif +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) + if (flags & LOOKUP_RCU) + return -ECHILD; +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,00) + if (nd && nd->flags & LOOKUP_RCU) + return -ECHILD; +#endif + + if (dentry->d_inode) + return 1; + return __exfat_revalidate(dentry); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) +static int exfat_revalidate_ci(struct dentry *dentry, unsigned int flags) +#else +static int exfat_revalidate_ci(struct dentry *dentry, struct nameidata *nd) +#endif +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) + if (flags & LOOKUP_RCU) + return -ECHILD; +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,00) + unsigned int flags; + + if (nd && nd->flags & LOOKUP_RCU) + return -ECHILD; + + flags = nd ? nd->flags : 0; +#else + flags = nd ? nd->flags : 0; +#endif + + if (dentry->d_inode) + return 1; + + if (!flags) + return 0; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,00) + if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET)) + return 0; +#else + if (!(nd->flags & (LOOKUP_CONTINUE | LOOKUP_PARENT))) { + if (nd->flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET)) + return 0; + } +#endif + + return __exfat_revalidate(dentry); +} + +static unsigned int __exfat_striptail_len(unsigned int len, const char *name) +{ + while (len && name[len - 1] == '.') + len--; + return len; +} + +static unsigned int exfat_striptail_len(const struct qstr *qstr) +{ + return __exfat_striptail_len(qstr->len, qstr->name); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) +static int exfat_d_hash(const struct dentry *dentry, struct qstr *qstr) +#else +static int exfat_d_hash(const struct dentry *dentry, const struct inode *inode, + struct qstr *qstr) +#endif +{ + qstr->hash = full_name_hash(qstr->name, exfat_striptail_len(qstr)); + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) +static int exfat_d_hashi(const struct dentry *dentry, struct qstr *qstr) +#else +static int exfat_d_hashi(const struct dentry *dentry, const struct inode *inode, + struct qstr *qstr) +#endif +{ + struct nls_table *t = EXFAT_SB(dentry->d_sb)->nls_io; + const unsigned char *name; + unsigned int len; + unsigned long hash; + + name = qstr->name; + len = exfat_striptail_len(qstr); + + hash = init_name_hash(); + while (len--) + hash = partial_name_hash(nls_tolower(t, *name++), hash); + qstr->hash = end_name_hash(hash); + + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) +static int exfat_cmpi(const struct dentry *parent, const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) +#else +static int exfat_cmpi(const struct dentry *parent, const struct inode *pinode, + const struct dentry *dentry, const struct inode *inode, + unsigned int len, const char *str, const struct qstr *name) +#endif +{ + struct nls_table *t = EXFAT_SB(parent->d_sb)->nls_io; + unsigned int alen, blen; + + alen = exfat_striptail_len(name); + blen = __exfat_striptail_len(len, str); + if (alen == blen) { + if (nls_strnicmp(t, name->name, str, alen) == 0) + return 0; + } + return 1; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) +static int exfat_cmp(const struct dentry *parent, const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) +#else +static int exfat_cmp(const struct dentry *parent, const struct inode *pinode, + const struct dentry *dentry, const struct inode *inode, + unsigned int len, const char *str, const struct qstr *name) +#endif +{ + unsigned int alen, blen; + + alen = exfat_striptail_len(name); + blen = __exfat_striptail_len(len, str); + if (alen == blen) { + if (strncmp(name->name, str, alen) == 0) + return 0; + } + return 1; +} + +static const struct dentry_operations exfat_ci_dentry_ops = { + .d_revalidate = exfat_revalidate_ci, + .d_hash = exfat_d_hashi, + .d_compare = exfat_cmpi, +}; + +static const struct dentry_operations exfat_dentry_ops = { + .d_revalidate = exfat_revalidate, + .d_hash = exfat_d_hash, + .d_compare = exfat_cmp, +}; + +/*======================================================================*/ +/* Directory Entry Operations */ +/*======================================================================*/ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) +static int exfat_readdir(struct file *filp, struct dir_context *ctx) +#else +static int exfat_readdir(struct file *filp, void *dirent, filldir_t filldir) +#endif +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0) + struct inode *inode = file_inode(filp); +#else + struct inode *inode = filp->f_path.dentry->d_inode; +#endif + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + FS_INFO_T *p_fs = &(sbi->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + DIR_ENTRY_T de; + unsigned long inum; + loff_t cpos; + int err = 0; + + __lock_super(sb); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) + cpos = ctx->pos; +#else + cpos = filp->f_pos; +#endif + /* Fake . and .. for the root directory. */ + if ((p_fs->vol_type == EXFAT) || (inode->i_ino == EXFAT_ROOT_INO)) { + while (cpos < 2) { + if (inode->i_ino == EXFAT_ROOT_INO) + inum = EXFAT_ROOT_INO; + else if (cpos == 0) + inum = inode->i_ino; + else /* (cpos == 1) */ + inum = parent_ino(filp->f_path.dentry); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) + if (!dir_emit_dots(filp, ctx)) +#else + if (filldir(dirent, "..", cpos+1, cpos, inum, DT_DIR) < 0) +#endif + goto out; + cpos++; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) + ctx->pos++; +#else + filp->f_pos++; +#endif + } + if (cpos == 2) + cpos = 0; + } + if (cpos & (DENTRY_SIZE - 1)) { + err = -ENOENT; + goto out; + } + +get_new: + EXFAT_I(inode)->fid.size = i_size_read(inode); + EXFAT_I(inode)->fid.rwoffset = cpos >> DENTRY_SIZE_BITS; + + err = FsReadDir(inode, &de); + if (err) { + /* at least we tried to read a sector + * move cpos to next sector position (should be aligned) + */ + if (err == FFS_MEDIAERR) { + cpos += 1 << p_bd->sector_size_bits; + cpos &= ~((1 << p_bd->sector_size_bits)-1); + } + + err = -EIO; + goto end_of_dir; + } + + cpos = EXFAT_I(inode)->fid.rwoffset << DENTRY_SIZE_BITS; + + if (!de.Name[0]) + goto end_of_dir; + + if (!memcmp(de.ShortName, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH)) { + inum = inode->i_ino; + } else if (!memcmp(de.ShortName, DOS_PAR_DIR_NAME, DOS_NAME_LENGTH)) { + inum = parent_ino(filp->f_path.dentry); + } else { + loff_t i_pos = ((loff_t) EXFAT_I(inode)->fid.start_clu << 32) | + ((EXFAT_I(inode)->fid.rwoffset-1) & 0xffffffff); + + struct inode *tmp = exfat_iget(sb, i_pos); + if (tmp) { + inum = tmp->i_ino; + iput(tmp); + } else { + inum = iunique(sb, EXFAT_ROOT_INO); + } + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) + if (!dir_emit(ctx, de.Name, strlen(de.Name), inum, + (de.Attr & ATTR_SUBDIR) ? DT_DIR : DT_REG)) +#else + if (filldir(dirent, de.Name, strlen(de.Name), cpos-1, inum, + (de.Attr & ATTR_SUBDIR) ? DT_DIR : DT_REG) < 0) +#endif + goto out; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) + ctx->pos = cpos; +#else + filp->f_pos = cpos; +#endif + goto get_new; + +end_of_dir: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) + ctx->pos = cpos; +#else + filp->f_pos = cpos; +#endif +out: + __unlock_super(sb); + return err; +} + +static int exfat_ioctl_volume_id(struct inode *dir) +{ + struct super_block *sb = dir->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + FS_INFO_T *p_fs = &(sbi->fs_info); + + return p_fs->vol_id; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) +static int exfat_generic_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +#else +static long exfat_generic_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +#endif +{ +#if !(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)) + struct inode *inode = filp->f_dentry->d_inode; +#endif +#ifdef CONFIG_EXFAT_KERNEL_DEBUG + unsigned int flags; +#endif /* CONFIG_EXFAT_KERNEL_DEBUG */ + + switch (cmd) { + case EXFAT_IOCTL_GET_VOLUME_ID: + return exfat_ioctl_volume_id(inode); +#ifdef CONFIG_EXFAT_KERNEL_DEBUG + case EXFAT_IOC_GET_DEBUGFLAGS: { + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + flags = sbi->debug_flags; + return put_user(flags, (int __user *)arg); + } + case EXFAT_IOC_SET_DEBUGFLAGS: { + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (get_user(flags, (int __user *) arg)) + return -EFAULT; + + __lock_super(sb); + sbi->debug_flags = flags; + __unlock_super(sb); + + return 0; + } +#endif /* CONFIG_EXFAT_KERNEL_DEBUG */ + default: + return -ENOTTY; /* Inappropriate ioctl for device */ + } +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) +static int exfat_file_fsync(struct file *filp, int datasync) +{ + struct inode *inode = filp->f_mapping->host; + struct super_block *sb = inode->i_sb; + int res, err; + + res = generic_file_fsync(filp, datasync); + err = FsSyncVol(sb, 1); + + return res ? res : err; +} +#endif + +const struct file_operations exfat_dir_operations = { + .llseek = generic_file_llseek, + .read = generic_read_dir, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) + .iterate = exfat_readdir, +#else + .readdir = exfat_readdir, +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) + .ioctl = exfat_generic_ioctl, + .fsync = exfat_file_fsync, +#else + .unlocked_ioctl = exfat_generic_ioctl, + .fsync = generic_file_fsync, +#endif +}; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) +static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode, + bool excl) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) +static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode, + struct nameidata *nd) +#else +static int exfat_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd) +#endif +{ + struct super_block *sb = dir->i_sb; + struct inode *inode; + struct timespec ts; + FILE_ID_T fid; + loff_t i_pos; + int err; + + __lock_super(sb); + + DPRINTK("exfat_create entered\n"); + + ts = CURRENT_TIME_SEC; + + err = FsCreateFile(dir, (u8 *) dentry->d_name.name, FM_REGULAR, &fid); + if (err) { + if (err == FFS_INVALIDPATH) + err = -EINVAL; + else if (err == FFS_FILEEXIST) + err = -EEXIST; + else if (err == FFS_FULL) + err = -ENOSPC; + else if (err == FFS_NAMETOOLONG) + err = -ENAMETOOLONG; + else + err = -EIO; + goto out; + } + dir->i_version++; + dir->i_ctime = dir->i_mtime = dir->i_atime = ts; + if (IS_DIRSYNC(dir)) + (void) exfat_sync_inode(dir); + else + mark_inode_dirty(dir); + + i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff); + + inode = exfat_build_inode(sb, &fid, i_pos); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } + inode->i_version++; + inode->i_mtime = inode->i_atime = inode->i_ctime = ts; + /* timestamp is already written, so mark_inode_dirty() is unnecessary. */ + + dentry->d_time = dentry->d_parent->d_inode->i_version; + d_instantiate(dentry, inode); + +out: + __unlock_super(sb); + DPRINTK("exfat_create exited\n"); + return err; +} + +static int exfat_find(struct inode *dir, struct qstr *qname, + FILE_ID_T *fid) +{ + int err; + + if (qname->len == 0) + return -ENOENT; + + err = FsLookupFile(dir, (u8 *) qname->name, fid); + if (err) + return -ENOENT; + + return 0; +} + +static int exfat_d_anon_disconn(struct dentry *dentry) +{ + return IS_ROOT(dentry) && (dentry->d_flags & DCACHE_DISCONNECTED); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) +static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags) +#else +static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) +#endif +{ + struct super_block *sb = dir->i_sb; + struct inode *inode; + struct dentry *alias; + int err; + FILE_ID_T fid; + loff_t i_pos; + u64 ret; + mode_t i_mode; + + __lock_super(sb); + DPRINTK("exfat_lookup entered\n"); + err = exfat_find(dir, &dentry->d_name, &fid); + if (err) { + if (err == -ENOENT) { + inode = NULL; + goto out; + } + goto error; + } + + i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff); + inode = exfat_build_inode(sb, &fid, i_pos); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto error; + } + + i_mode = inode->i_mode; + if (S_ISLNK(i_mode)) { + EXFAT_I(inode)->target = kmalloc(i_size_read(inode)+1, GFP_KERNEL); + if (!EXFAT_I(inode)->target) { + err = -ENOMEM; + goto error; + } + FsReadFile(dir, &fid, EXFAT_I(inode)->target, i_size_read(inode), &ret); + *(EXFAT_I(inode)->target + i_size_read(inode)) = '\0'; + } + + alias = d_find_alias(inode); + if (alias && !exfat_d_anon_disconn(alias)) { + CHECK_ERR(d_unhashed(alias)); + if (!S_ISDIR(i_mode)) + d_move(alias, dentry); + iput(inode); + __unlock_super(sb); + DPRINTK("exfat_lookup exited 1\n"); + return alias; + } else { + dput(alias); + } +out: + __unlock_super(sb); + dentry->d_time = dentry->d_parent->d_inode->i_version; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) + dentry->d_op = sb->s_root->d_op; + dentry = d_splice_alias(inode, dentry); + if (dentry) { + dentry->d_op = sb->s_root->d_op; + dentry->d_time = dentry->d_parent->d_inode->i_version; + } +#else + dentry = d_splice_alias(inode, dentry); + if (dentry) + dentry->d_time = dentry->d_parent->d_inode->i_version; +#endif + DPRINTK("exfat_lookup exited 2\n"); + return dentry; + +error: + __unlock_super(sb); + DPRINTK("exfat_lookup exited 3\n"); + return ERR_PTR(err); +} + +static int exfat_unlink(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + struct super_block *sb = dir->i_sb; + struct timespec ts; + int err; + + __lock_super(sb); + + DPRINTK("exfat_unlink entered\n"); + + ts = CURRENT_TIME_SEC; + + EXFAT_I(inode)->fid.size = i_size_read(inode); + + err = FsRemoveFile(dir, &(EXFAT_I(inode)->fid)); + if (err) { + if (err == FFS_PERMISSIONERR) + err = -EPERM; + else + err = -EIO; + goto out; + } + dir->i_version++; + dir->i_mtime = dir->i_atime = ts; + if (IS_DIRSYNC(dir)) + (void) exfat_sync_inode(dir); + else + mark_inode_dirty(dir); + + clear_nlink(inode); + inode->i_mtime = inode->i_atime = ts; + exfat_detach(inode); + remove_inode_hash(inode); + +out: + __unlock_super(sb); + DPRINTK("exfat_unlink exited\n"); + return err; +} + +static int exfat_symlink(struct inode *dir, struct dentry *dentry, const char *target) +{ + struct super_block *sb = dir->i_sb; + struct inode *inode; + struct timespec ts; + FILE_ID_T fid; + loff_t i_pos; + int err; + u64 len = (u64) strlen(target); + u64 ret; + + __lock_super(sb); + + DPRINTK("exfat_symlink entered\n"); + + ts = CURRENT_TIME_SEC; + + err = FsCreateFile(dir, (u8 *) dentry->d_name.name, FM_SYMLINK, &fid); + if (err) { + if (err == FFS_INVALIDPATH) + err = -EINVAL; + else if (err == FFS_FILEEXIST) + err = -EEXIST; + else if (err == FFS_FULL) + err = -ENOSPC; + else + err = -EIO; + goto out; + } + + err = FsWriteFile(dir, &fid, (char *) target, len, &ret); + + if (err) { + FsRemoveFile(dir, &fid); + + if (err == FFS_FULL) + err = -ENOSPC; + else + err = -EIO; + goto out; + } + + dir->i_version++; + dir->i_ctime = dir->i_mtime = dir->i_atime = ts; + if (IS_DIRSYNC(dir)) + (void) exfat_sync_inode(dir); + else + mark_inode_dirty(dir); + + i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff); + + inode = exfat_build_inode(sb, &fid, i_pos); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } + inode->i_version++; + inode->i_mtime = inode->i_atime = inode->i_ctime = ts; + /* timestamp is already written, so mark_inode_dirty() is unneeded. */ + + EXFAT_I(inode)->target = kmalloc(len+1, GFP_KERNEL); + if (!EXFAT_I(inode)->target) { + err = -ENOMEM; + goto out; + } + memcpy(EXFAT_I(inode)->target, target, len+1); + + dentry->d_time = dentry->d_parent->d_inode->i_version; + d_instantiate(dentry, inode); + +out: + __unlock_super(sb); + DPRINTK("exfat_symlink exited\n"); + return err; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) +static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) +#else +static int exfat_mkdir(struct inode *dir, struct dentry *dentry, int mode) +#endif +{ + struct super_block *sb = dir->i_sb; + struct inode *inode; + struct timespec ts; + FILE_ID_T fid; + loff_t i_pos; + int err; + + __lock_super(sb); + + DPRINTK("exfat_mkdir entered\n"); + + ts = CURRENT_TIME_SEC; + + err = FsCreateDir(dir, (u8 *) dentry->d_name.name, &fid); + if (err) { + if (err == FFS_INVALIDPATH) + err = -EINVAL; + else if (err == FFS_FILEEXIST) + err = -EEXIST; + else if (err == FFS_FULL) + err = -ENOSPC; + else if (err == FFS_NAMETOOLONG) + err = -ENAMETOOLONG; + else + err = -EIO; + goto out; + } + dir->i_version++; + dir->i_ctime = dir->i_mtime = dir->i_atime = ts; + if (IS_DIRSYNC(dir)) + (void) exfat_sync_inode(dir); + else + mark_inode_dirty(dir); + inc_nlink(dir); + + i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff); + + inode = exfat_build_inode(sb, &fid, i_pos); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } + inode->i_version++; + inode->i_mtime = inode->i_atime = inode->i_ctime = ts; + /* timestamp is already written, so mark_inode_dirty() is unneeded. */ + + dentry->d_time = dentry->d_parent->d_inode->i_version; + d_instantiate(dentry, inode); + +out: + __unlock_super(sb); + DPRINTK("exfat_mkdir exited\n"); + return err; +} + +static int exfat_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + struct super_block *sb = dir->i_sb; + struct timespec ts; + int err; + + __lock_super(sb); + + DPRINTK("exfat_rmdir entered\n"); + + ts = CURRENT_TIME_SEC; + + EXFAT_I(inode)->fid.size = i_size_read(inode); + + err = FsRemoveDir(dir, &(EXFAT_I(inode)->fid)); + if (err) { + if (err == FFS_INVALIDPATH) + err = -EINVAL; + else if (err == FFS_FILEEXIST) + err = -ENOTEMPTY; + else if (err == FFS_NOTFOUND) + err = -ENOENT; + else if (err == FFS_DIRBUSY) + err = -EBUSY; + else + err = -EIO; + goto out; + } + dir->i_version++; + dir->i_mtime = dir->i_atime = ts; + if (IS_DIRSYNC(dir)) + (void) exfat_sync_inode(dir); + else + mark_inode_dirty(dir); + drop_nlink(dir); + + clear_nlink(inode); + inode->i_mtime = inode->i_atime = ts; + exfat_detach(inode); + remove_inode_hash(inode); + +out: + __unlock_super(sb); + DPRINTK("exfat_rmdir exited\n"); + return err; +} + +static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + struct inode *old_inode, *new_inode; + struct super_block *sb = old_dir->i_sb; + struct timespec ts; + loff_t i_pos; + int err; + + __lock_super(sb); + + DPRINTK("exfat_rename entered\n"); + + old_inode = old_dentry->d_inode; + new_inode = new_dentry->d_inode; + + ts = CURRENT_TIME_SEC; + + EXFAT_I(old_inode)->fid.size = i_size_read(old_inode); + + err = FsMoveFile(old_dir, &(EXFAT_I(old_inode)->fid), new_dir, new_dentry); + if (err) { + if (err == FFS_PERMISSIONERR) + err = -EPERM; + else if (err == FFS_INVALIDPATH) + err = -EINVAL; + else if (err == FFS_FILEEXIST) + err = -EEXIST; + else if (err == FFS_NOTFOUND) + err = -ENOENT; + else if (err == FFS_FULL) + err = -ENOSPC; + else + err = -EIO; + goto out; + } + new_dir->i_version++; + new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime = ts; + if (IS_DIRSYNC(new_dir)) + (void) exfat_sync_inode(new_dir); + else + mark_inode_dirty(new_dir); + + i_pos = ((loff_t) EXFAT_I(old_inode)->fid.dir.dir << 32) | + (EXFAT_I(old_inode)->fid.entry & 0xffffffff); + + exfat_detach(old_inode); + exfat_attach(old_inode, i_pos); + if (IS_DIRSYNC(new_dir)) + (void) exfat_sync_inode(old_inode); + else + mark_inode_dirty(old_inode); + + if ((S_ISDIR(old_inode->i_mode)) && (old_dir != new_dir)) { + drop_nlink(old_dir); + if (!new_inode) + inc_nlink(new_dir); + } + + old_dir->i_version++; + old_dir->i_ctime = old_dir->i_mtime = ts; + if (IS_DIRSYNC(old_dir)) + (void) exfat_sync_inode(old_dir); + else + mark_inode_dirty(old_dir); + + if (new_inode) { + exfat_detach(new_inode); + drop_nlink(new_inode); + if (S_ISDIR(new_inode->i_mode)) + drop_nlink(new_inode); + new_inode->i_ctime = ts; + } + +out: + __unlock_super(sb); + DPRINTK("exfat_rename exited\n"); + return err; +} + +static int exfat_cont_expand(struct inode *inode, loff_t size) +{ + struct address_space *mapping = inode->i_mapping; + loff_t start = i_size_read(inode), count = size - i_size_read(inode); + int err, err2; + + err = generic_cont_expand_simple(inode, size); + if (err != 0) + return err; + + inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; + mark_inode_dirty(inode); + + if (IS_SYNC(inode)) { + err = filemap_fdatawrite_range(mapping, start, start + count - 1); + err2 = sync_mapping_buffers(mapping); + err = (err) ? (err) : (err2); + err2 = write_inode_now(inode, 1); + err = (err) ? (err) : (err2); + if (!err) + err = filemap_fdatawait_range(mapping, start, start + count - 1); + } + return err; +} + +static int exfat_allow_set_time(struct exfat_sb_info *sbi, struct inode *inode) +{ + mode_t allow_utime = sbi->options.allow_utime; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) + if (!uid_eq(current_fsuid(), inode->i_uid)) +#else + if (current_fsuid() != inode->i_uid) +#endif + { + if (in_group_p(inode->i_gid)) + allow_utime >>= 3; + if (allow_utime & MAY_WRITE) + return 1; + } + + /* use a default check */ + return 0; +} + +static int exfat_sanitize_mode(const struct exfat_sb_info *sbi, + struct inode *inode, umode_t *mode_ptr) +{ + mode_t i_mode, mask, perm; + + i_mode = inode->i_mode; + + if (S_ISREG(i_mode) || S_ISLNK(i_mode)) + mask = sbi->options.fs_fmask; + else + mask = sbi->options.fs_dmask; + + perm = *mode_ptr & ~(S_IFMT | mask); + + /* Of the r and x bits, all (subject to umask) must be present.*/ + if ((perm & (S_IRUGO | S_IXUGO)) != (i_mode & (S_IRUGO|S_IXUGO))) + return -EPERM; + + if (exfat_mode_can_hold_ro(inode)) { + /* Of the w bits, either all (subject to umask) or none must be present. */ + if ((perm & S_IWUGO) && ((perm & S_IWUGO) != (S_IWUGO & ~mask))) + return -EPERM; + } else { + /* If exfat_mode_can_hold_ro(inode) is false, can't change w bits. */ + if ((perm & S_IWUGO) != (S_IWUGO & ~mask)) + return -EPERM; + } + + *mode_ptr &= S_IFMT | perm; + + return 0; +} + +static int exfat_setattr(struct dentry *dentry, struct iattr *attr) +{ + + struct exfat_sb_info *sbi = EXFAT_SB(dentry->d_sb); + struct inode *inode = dentry->d_inode; + unsigned int ia_valid; + int error; + loff_t old_size; + + DPRINTK("exfat_setattr entered\n"); + + if ((attr->ia_valid & ATTR_SIZE) + && (attr->ia_size > i_size_read(inode))) { + error = exfat_cont_expand(inode, attr->ia_size); + if (error || attr->ia_valid == ATTR_SIZE) + return error; + attr->ia_valid &= ~ATTR_SIZE; + } + + ia_valid = attr->ia_valid; + + if ((ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) + && exfat_allow_set_time(sbi, inode)) { + attr->ia_valid &= ~(ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET); + } + + error = inode_change_ok(inode, attr); + attr->ia_valid = ia_valid; + if (error) + return error; + + if (((attr->ia_valid & ATTR_UID) && +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) + (!uid_eq(attr->ia_uid, sbi->options.fs_uid))) || + ((attr->ia_valid & ATTR_GID) && + (!gid_eq(attr->ia_gid, sbi->options.fs_gid))) || +#else + (attr->ia_uid != sbi->options.fs_uid)) || + ((attr->ia_valid & ATTR_GID) && + (attr->ia_gid != sbi->options.fs_gid)) || +#endif + ((attr->ia_valid & ATTR_MODE) && + (attr->ia_mode & ~(S_IFREG | S_IFLNK | S_IFDIR | S_IRWXUGO)))) { + return -EPERM; + } + + /* + * We don't return -EPERM here. Yes, strange, but this is too + * old behavior. + */ + if (attr->ia_valid & ATTR_MODE) { + if (exfat_sanitize_mode(sbi, inode, &attr->ia_mode) < 0) + attr->ia_valid &= ~ATTR_MODE; + } + + EXFAT_I(inode)->fid.size = i_size_read(inode); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) + if (attr->ia_valid) + error = inode_setattr(inode, attr); +#else + if (attr->ia_valid & ATTR_SIZE) { + old_size = i_size_read(inode); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) + down_write(&EXFAT_I(inode)->truncate_lock); + truncate_setsize(inode, attr->ia_size); + _exfat_truncate(inode, old_size); + up_write(&EXFAT_I(inode)->truncate_lock); +#else + truncate_setsize(inode, attr->ia_size); + _exfat_truncate(inode, old_size); +#endif + } + setattr_copy(inode, attr); + mark_inode_dirty(inode); +#endif + + DPRINTK("exfat_setattr exited\n"); + return error; +} + +static int exfat_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) +{ + struct inode *inode = dentry->d_inode; + + DPRINTK("exfat_getattr entered\n"); + + generic_fillattr(inode, stat); + stat->blksize = EXFAT_SB(inode->i_sb)->fs_info.cluster_size; + + DPRINTK("exfat_getattr exited\n"); + return 0; +} + +const struct inode_operations exfat_dir_inode_operations = { + .create = exfat_create, + .lookup = exfat_lookup, + .unlink = exfat_unlink, + .symlink = exfat_symlink, + .mkdir = exfat_mkdir, + .rmdir = exfat_rmdir, + .rename = exfat_rename, + .setattr = exfat_setattr, + .getattr = exfat_getattr, +}; + +/*======================================================================*/ +/* File Operations */ +/*======================================================================*/ + +static void *exfat_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + struct exfat_inode_info *ei = EXFAT_I(dentry->d_inode); + nd_set_link(nd, (char *)(ei->target)); + return NULL; +} + +const struct inode_operations exfat_symlink_inode_operations = { + .readlink = generic_readlink, + .follow_link = exfat_follow_link, +}; + +static int exfat_file_release(struct inode *inode, struct file *filp) +{ + struct super_block *sb = inode->i_sb; + + EXFAT_I(inode)->fid.size = i_size_read(inode); + FsSyncVol(sb, 0); + return 0; +} + +const struct file_operations exfat_file_operations = { + .llseek = generic_file_llseek, + .read = do_sync_read, + .write = do_sync_write, + .aio_read = generic_file_aio_read, + .aio_write = generic_file_aio_write, + .mmap = generic_file_mmap, + .release = exfat_file_release, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) + .ioctl = exfat_generic_ioctl, + .fsync = exfat_file_fsync, +#else + .unlocked_ioctl = exfat_generic_ioctl, + .fsync = generic_file_fsync, +#endif + .splice_read = generic_file_splice_read, +}; + +static void _exfat_truncate(struct inode *inode, loff_t old_size) +{ + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + FS_INFO_T *p_fs = &(sbi->fs_info); + int err; + + __lock_super(sb); + + /* + * This protects against truncating a file bigger than it was then + * trying to write into the hole. + */ + if (EXFAT_I(inode)->mmu_private > i_size_read(inode)) + EXFAT_I(inode)->mmu_private = i_size_read(inode); + + if (EXFAT_I(inode)->fid.start_clu == 0) + goto out; + + err = FsTruncateFile(inode, old_size, i_size_read(inode)); + if (err) + goto out; + + inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; + if (IS_DIRSYNC(inode)) + (void) exfat_sync_inode(inode); + else + mark_inode_dirty(inode); + + inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1)) + & ~((loff_t)p_fs->cluster_size - 1)) >> 9; +out: + __unlock_super(sb); +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,36) +static void exfat_truncate(struct inode *inode) +{ + _exfat_truncate(inode, i_size_read(inode)); +} +#endif + +const struct inode_operations exfat_file_inode_operations = { +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,36) + .truncate = exfat_truncate, +#endif + .setattr = exfat_setattr, + .getattr = exfat_getattr, +}; + +/*======================================================================*/ +/* Address Space Operations */ +/*======================================================================*/ + +static int exfat_bmap(struct inode *inode, sector_t sector, sector_t *phys, + unsigned long *mapped_blocks, int *create) +{ + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + FS_INFO_T *p_fs = &(sbi->fs_info); + BD_INFO_T *p_bd = &(sbi->bd_info); + const unsigned long blocksize = sb->s_blocksize; + const unsigned char blocksize_bits = sb->s_blocksize_bits; + sector_t last_block; + int err, clu_offset, sec_offset; + unsigned int cluster; + + *phys = 0; + *mapped_blocks = 0; + + if ((p_fs->vol_type == FAT12) || (p_fs->vol_type == FAT16)) { + if (inode->i_ino == EXFAT_ROOT_INO) { + if (sector < (p_fs->dentries_in_root >> (p_bd->sector_size_bits-DENTRY_SIZE_BITS))) { + *phys = sector + p_fs->root_start_sector; + *mapped_blocks = 1; + } + return 0; + } + } + + last_block = (i_size_read(inode) + (blocksize - 1)) >> blocksize_bits; + if (sector >= last_block) { + if (*create == 0) + return 0; + } else { + *create = 0; + } + + clu_offset = sector >> p_fs->sectors_per_clu_bits; /* cluster offset */ + sec_offset = sector & (p_fs->sectors_per_clu - 1); /* sector offset in cluster */ + + EXFAT_I(inode)->fid.size = i_size_read(inode); + + err = FsMapCluster(inode, clu_offset, &cluster); + + if (err) { + if (err == FFS_FULL) + return -ENOSPC; + else + return -EIO; + } else if (cluster != CLUSTER_32(~0)) { + *phys = START_SECTOR(cluster) + sec_offset; + *mapped_blocks = p_fs->sectors_per_clu - sec_offset; + } + + return 0; +} + +static int exfat_get_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create) +{ + struct super_block *sb = inode->i_sb; + unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits; + int err; + unsigned long mapped_blocks; + sector_t phys; + + __lock_super(sb); + + err = exfat_bmap(inode, iblock, &phys, &mapped_blocks, &create); + if (err) { + __unlock_super(sb); + return err; + } + + if (phys) { + max_blocks = min(mapped_blocks, max_blocks); + if (create) { + EXFAT_I(inode)->mmu_private += max_blocks << sb->s_blocksize_bits; + set_buffer_new(bh_result); + } + map_bh(bh_result, sb, phys); + } + + bh_result->b_size = max_blocks << sb->s_blocksize_bits; + __unlock_super(sb); + + return 0; +} + +static int exfat_readpage(struct file *file, struct page *page) +{ + int ret; + ret = mpage_readpage(page, exfat_get_block); + return ret; +} + +static int exfat_readpages(struct file *file, struct address_space *mapping, + struct list_head *pages, unsigned nr_pages) +{ + int ret; + ret = mpage_readpages(mapping, pages, nr_pages, exfat_get_block); + return ret; +} + +static int exfat_writepage(struct page *page, struct writeback_control *wbc) +{ + int ret; + ret = block_write_full_page(page, exfat_get_block, wbc); + return ret; +} + +static int exfat_writepages(struct address_space *mapping, + struct writeback_control *wbc) +{ + int ret; + ret = mpage_writepages(mapping, wbc, exfat_get_block); + return ret; +} + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) +static void exfat_write_failed(struct address_space *mapping, loff_t to) +{ + struct inode *inode = mapping->host; + if (to > i_size_read(inode)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,12,0) + truncate_pagecache(inode, i_size_read(inode)); +#else + truncate_pagecache(inode, to, i_size_read(inode)); +#endif + EXFAT_I(inode)->fid.size = i_size_read(inode); + _exfat_truncate(inode, i_size_read(inode)); + } +} +#endif + +static int exfat_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned flags, + struct page **pagep, void **fsdata) +{ + int ret; + *pagep = NULL; + ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, + exfat_get_block, + &EXFAT_I(mapping->host)->mmu_private); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) + if (ret < 0) + exfat_write_failed(mapping, pos+len); +#endif + return ret; +} + +static int exfat_write_end(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *pagep, void *fsdata) +{ + struct inode *inode = mapping->host; + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + int err; + + err = generic_write_end(file, mapping, pos, len, copied, pagep, fsdata); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) + if (err < len) + exfat_write_failed(mapping, pos+len); +#endif + + if (!(err < 0) && !(fid->attr & ATTR_ARCHIVE)) { + inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; + fid->attr |= ATTR_ARCHIVE; + mark_inode_dirty(inode); + } + return err; +} + +static ssize_t exfat_direct_IO(int rw, struct kiocb *iocb, + const struct iovec *iov, + loff_t offset, unsigned long nr_segs) +{ + struct inode *inode = iocb->ki_filp->f_mapping->host; + struct address_space *mapping = iocb->ki_filp->f_mapping; + ssize_t ret; + + if (rw == WRITE) { + if (EXFAT_I(inode)->mmu_private < (offset + iov_length(iov, nr_segs))) + return 0; + } + + ret = blockdev_direct_IO(rw, iocb, inode, iov, + offset, nr_segs, exfat_get_block); + exfat_write_failed(mapping, offset+iov_length(iov, nr_segs)); + + return ret; +} + +static sector_t _exfat_bmap(struct address_space *mapping, sector_t block) +{ + sector_t blocknr; + + /* exfat_get_cluster() assumes the requested blocknr isn't truncated. */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) + down_read(&EXFAT_I(mapping->host)->truncate_lock); + blocknr = generic_block_bmap(mapping, block, exfat_get_block); + up_read(&EXFAT_I(mapping->host)->truncate_lock); +#else + down_read(&EXFAT_I(mapping->host)->i_alloc_sem); + blocknr = generic_block_bmap(mapping, block, exfat_get_block); + up_read(&EXFAT_I(mapping->host)->i_alloc_sem); +#endif + + return blocknr; +} + +const struct address_space_operations exfat_aops = { + .readpage = exfat_readpage, + .readpages = exfat_readpages, + .writepage = exfat_writepage, + .writepages = exfat_writepages, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39) + .sync_page = block_sync_page, +#endif + .write_begin = exfat_write_begin, + .write_end = exfat_write_end, + .direct_IO = exfat_direct_IO, + .bmap = _exfat_bmap +}; + +/*======================================================================*/ +/* Super Operations */ +/*======================================================================*/ + +static inline unsigned long exfat_hash(loff_t i_pos) +{ + return hash_32(i_pos, EXFAT_HASH_BITS); +} + +static struct inode *exfat_iget(struct super_block *sb, loff_t i_pos) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_inode_info *info; + struct hlist_head *head = sbi->inode_hashtable + exfat_hash(i_pos); + struct inode *inode = NULL; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0) + struct hlist_node *node; + + spin_lock(&sbi->inode_hash_lock); + hlist_for_each_entry(info, node, head, i_hash_fat) { +#else + spin_lock(&sbi->inode_hash_lock); + hlist_for_each_entry(info, head, i_hash_fat) { +#endif + CHECK_ERR(info->vfs_inode.i_sb != sb); + + if (i_pos != info->i_pos) + continue; + inode = igrab(&info->vfs_inode); + if (inode) + break; + } + spin_unlock(&sbi->inode_hash_lock); + return inode; +} + +static void exfat_attach(struct inode *inode, loff_t i_pos) +{ + struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); + struct hlist_head *head = sbi->inode_hashtable + exfat_hash(i_pos); + + spin_lock(&sbi->inode_hash_lock); + EXFAT_I(inode)->i_pos = i_pos; + hlist_add_head(&EXFAT_I(inode)->i_hash_fat, head); + spin_unlock(&sbi->inode_hash_lock); +} + +static void exfat_detach(struct inode *inode) +{ + struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); + + spin_lock(&sbi->inode_hash_lock); + hlist_del_init(&EXFAT_I(inode)->i_hash_fat); + EXFAT_I(inode)->i_pos = 0; + spin_unlock(&sbi->inode_hash_lock); +} + +/* doesn't deal with root inode */ +static int exfat_fill_inode(struct inode *inode, FILE_ID_T *fid) +{ + struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); + FS_INFO_T *p_fs = &(sbi->fs_info); + DIR_ENTRY_T info; + + memcpy(&(EXFAT_I(inode)->fid), fid, sizeof(FILE_ID_T)); + + FsReadStat(inode, &info); + + EXFAT_I(inode)->i_pos = 0; + EXFAT_I(inode)->target = NULL; + inode->i_uid = sbi->options.fs_uid; + inode->i_gid = sbi->options.fs_gid; + inode->i_version++; + inode->i_generation = get_seconds(); + + if (info.Attr & ATTR_SUBDIR) { /* directory */ + inode->i_generation &= ~1; + inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO); + inode->i_op = &exfat_dir_inode_operations; + inode->i_fop = &exfat_dir_operations; + + i_size_write(inode, info.Size); + EXFAT_I(inode)->mmu_private = i_size_read(inode); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,00) + set_nlink(inode, info.NumSubdirs); +#else + inode->i_nlink = info.NumSubdirs; +#endif + } else if (info.Attr & ATTR_SYMLINK) { /* symbolic link */ + inode->i_generation |= 1; + inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO); + inode->i_op = &exfat_symlink_inode_operations; + + i_size_write(inode, info.Size); + EXFAT_I(inode)->mmu_private = i_size_read(inode); + } else { /* regular file */ + inode->i_generation |= 1; + inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO); + inode->i_op = &exfat_file_inode_operations; + inode->i_fop = &exfat_file_operations; + inode->i_mapping->a_ops = &exfat_aops; + inode->i_mapping->nrpages = 0; + + i_size_write(inode, info.Size); + EXFAT_I(inode)->mmu_private = i_size_read(inode); + } + exfat_save_attr(inode, info.Attr); + + inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1)) + & ~((loff_t)p_fs->cluster_size - 1)) >> 9; + + exfat_time_fat2unix(sbi, &inode->i_mtime, &info.ModifyTimestamp); + exfat_time_fat2unix(sbi, &inode->i_ctime, &info.CreateTimestamp); + exfat_time_fat2unix(sbi, &inode->i_atime, &info.AccessTimestamp); + + return 0; +} + +static struct inode *exfat_build_inode(struct super_block *sb, + FILE_ID_T *fid, loff_t i_pos) { + struct inode *inode; + int err; + + inode = exfat_iget(sb, i_pos); + if (inode) + goto out; + inode = new_inode(sb); + if (!inode) { + inode = ERR_PTR(-ENOMEM); + goto out; + } + inode->i_ino = iunique(sb, EXFAT_ROOT_INO); + inode->i_version = 1; + err = exfat_fill_inode(inode, fid); + if (err) { + iput(inode); + inode = ERR_PTR(err); + goto out; + } + exfat_attach(inode, i_pos); + insert_inode_hash(inode); +out: + return inode; +} + +static int exfat_sync_inode(struct inode *inode) +{ + return exfat_write_inode(inode, NULL); +} + +static struct inode *exfat_alloc_inode(struct super_block *sb) +{ + struct exfat_inode_info *ei; + + ei = kmem_cache_alloc(exfat_inode_cachep, GFP_NOFS); + if (!ei) + return NULL; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) + init_rwsem(&ei->truncate_lock); +#endif + + return &ei->vfs_inode; +} + +static void exfat_destroy_inode(struct inode *inode) +{ + if (EXFAT_I(inode)->target) + kfree(EXFAT_I(inode)->target); + EXFAT_I(inode)->target = NULL; + + kmem_cache_free(exfat_inode_cachep, EXFAT_I(inode)); +} + +static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc) +{ + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + DIR_ENTRY_T info; + + if (inode->i_ino == EXFAT_ROOT_INO) + return 0; + + info.Attr = exfat_make_attr(inode); + info.Size = i_size_read(inode); + + exfat_time_unix2fat(sbi, &inode->i_mtime, &info.ModifyTimestamp); + exfat_time_unix2fat(sbi, &inode->i_ctime, &info.CreateTimestamp); + exfat_time_unix2fat(sbi, &inode->i_atime, &info.AccessTimestamp); + + FsWriteStat(inode, &info); + + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) +static void exfat_delete_inode(struct inode *inode) +{ + truncate_inode_pages(&inode->i_data, 0); + clear_inode(inode); +} + +static void exfat_clear_inode(struct inode *inode) +{ + exfat_detach(inode); + remove_inode_hash(inode); +} +#else +static void exfat_evict_inode(struct inode *inode) +{ + truncate_inode_pages(&inode->i_data, 0); + + if (!inode->i_nlink) + i_size_write(inode, 0); + invalidate_inode_buffers(inode); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0) + end_writeback(inode); +#else + clear_inode(inode); +#endif + exfat_detach(inode); + + remove_inode_hash(inode); +} +#endif + +static void exfat_free_super(struct exfat_sb_info *sbi) +{ + if (sbi->nls_disk) + unload_nls(sbi->nls_disk); + if (sbi->nls_io) + unload_nls(sbi->nls_io); + if (sbi->options.iocharset != exfat_default_iocharset) + kfree(sbi->options.iocharset); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) + /* mutex_init is in exfat_fill_super function. only for 3.7+ */ + mutex_destroy(&sbi->s_lock); +#endif + kfree(sbi); +} + +static void exfat_put_super(struct super_block *sb) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + if (__is_sb_dirty(sb)) + exfat_write_super(sb); + + FsUmountVol(sb); + + sb->s_fs_info = NULL; + exfat_free_super(sbi); +} + +static void exfat_write_super(struct super_block *sb) +{ + __lock_super(sb); + + __set_sb_clean(sb); + + if (!(sb->s_flags & MS_RDONLY)) + FsSyncVol(sb, 1); + + __unlock_super(sb); +} + +static int exfat_sync_fs(struct super_block *sb, int wait) +{ + int err = 0; + + if (__is_sb_dirty(sb)) { + __lock_super(sb); + __set_sb_clean(sb); + err = FsSyncVol(sb, 1); + __unlock_super(sb); + } + + return err; +} + +static int exfat_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + struct super_block *sb = dentry->d_sb; + u64 id = huge_encode_dev(sb->s_bdev->bd_dev); + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + VOL_INFO_T info; + + if (p_fs->used_clusters == (u32) ~0) { + if (FFS_MEDIAERR == FsGetVolInfo(sb, &info)) + return -EIO; + + } else { + info.FatType = p_fs->vol_type; + info.ClusterSize = p_fs->cluster_size; + info.NumClusters = p_fs->num_clusters - 2; + info.UsedClusters = p_fs->used_clusters; + info.FreeClusters = info.NumClusters - info.UsedClusters; + + if (p_fs->dev_ejected) + printk("[EXFAT] statfs on device is ejected\n"); + } + + buf->f_type = sb->s_magic; + buf->f_bsize = info.ClusterSize; + buf->f_blocks = info.NumClusters; + buf->f_bfree = info.FreeClusters; + buf->f_bavail = info.FreeClusters; + buf->f_fsid.val[0] = (u32)id; + buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_namelen = 260; + + return 0; +} + +static int exfat_remount(struct super_block *sb, int *flags, char *data) +{ + *flags |= MS_NODIRATIME; + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) +static int exfat_show_options(struct seq_file *m, struct dentry *root) +{ + struct exfat_sb_info *sbi = EXFAT_SB(root->d_sb); +#else +static int exfat_show_options(struct seq_file *m, struct vfsmount *mnt) +{ + struct exfat_sb_info *sbi = EXFAT_SB(mnt->mnt_sb); +#endif + struct exfat_mount_options *opts = &sbi->options; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) + if (__kuid_val(opts->fs_uid)) + seq_printf(m, ",uid=%u", __kuid_val(opts->fs_uid)); + if (__kgid_val(opts->fs_gid)) + seq_printf(m, ",gid=%u", __kgid_val(opts->fs_gid)); +#else + if (opts->fs_uid != 0) + seq_printf(m, ",uid=%u", opts->fs_uid); + if (opts->fs_gid != 0) + seq_printf(m, ",gid=%u", opts->fs_gid); +#endif + seq_printf(m, ",fmask=%04o", opts->fs_fmask); + seq_printf(m, ",dmask=%04o", opts->fs_dmask); + if (opts->allow_utime) + seq_printf(m, ",allow_utime=%04o", opts->allow_utime); + if (sbi->nls_disk) + seq_printf(m, ",codepage=%s", sbi->nls_disk->charset); + if (sbi->nls_io) + seq_printf(m, ",iocharset=%s", sbi->nls_io->charset); + seq_printf(m, ",namecase=%u", opts->casesensitive); + if (opts->errors == EXFAT_ERRORS_CONT) + seq_puts(m, ",errors=continue"); + else if (opts->errors == EXFAT_ERRORS_PANIC) + seq_puts(m, ",errors=panic"); + else + seq_puts(m, ",errors=remount-ro"); +#ifdef CONFIG_EXFAT_DISCARD + if (opts->discard) + seq_printf(m, ",discard"); +#endif + return 0; +} + +const struct super_operations exfat_sops = { + .alloc_inode = exfat_alloc_inode, + .destroy_inode = exfat_destroy_inode, + .write_inode = exfat_write_inode, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) + .delete_inode = exfat_delete_inode, + .clear_inode = exfat_clear_inode, +#else + .evict_inode = exfat_evict_inode, +#endif + .put_super = exfat_put_super, +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) + .write_super = exfat_write_super, +#endif + .sync_fs = exfat_sync_fs, + .statfs = exfat_statfs, + .remount_fs = exfat_remount, + .show_options = exfat_show_options, +}; + +/*======================================================================*/ +/* Super Block Read Operations */ +/*======================================================================*/ + +enum { + Opt_uid, + Opt_gid, + Opt_umask, + Opt_dmask, + Opt_fmask, + Opt_allow_utime, + Opt_codepage, + Opt_charset, + Opt_namecase, + Opt_debug, + Opt_err_cont, + Opt_err_panic, + Opt_err_ro, + Opt_utf8_hack, + Opt_err, +#ifdef CONFIG_EXFAT_DISCARD + Opt_discard, +#endif /* EXFAT_CONFIG_DISCARD */ +}; + +static const match_table_t exfat_tokens = { + {Opt_uid, "uid=%u"}, + {Opt_gid, "gid=%u"}, + {Opt_umask, "umask=%o"}, + {Opt_dmask, "dmask=%o"}, + {Opt_fmask, "fmask=%o"}, + {Opt_allow_utime, "allow_utime=%o"}, + {Opt_codepage, "codepage=%u"}, + {Opt_charset, "iocharset=%s"}, + {Opt_namecase, "namecase=%u"}, + {Opt_debug, "debug"}, + {Opt_err_cont, "errors=continue"}, + {Opt_err_panic, "errors=panic"}, + {Opt_err_ro, "errors=remount-ro"}, + {Opt_utf8_hack, "utf8"}, +#ifdef CONFIG_EXFAT_DISCARD + {Opt_discard, "discard"}, +#endif /* CONFIG_EXFAT_DISCARD */ + {Opt_err, NULL} +}; + +static int parse_options(char *options, int silent, int *debug, + struct exfat_mount_options *opts) +{ + char *p; + substring_t args[MAX_OPT_ARGS]; + int option; + char *iocharset; + + opts->fs_uid = current_uid(); + opts->fs_gid = current_gid(); + opts->fs_fmask = opts->fs_dmask = current->fs->umask; + opts->allow_utime = (unsigned short) -1; + opts->codepage = exfat_default_codepage; + opts->iocharset = exfat_default_iocharset; + opts->casesensitive = 1; + opts->errors = EXFAT_ERRORS_RO; +#ifdef CONFIG_EXFAT_DISCARD + opts->discard = 0; +#endif + *debug = 0; + + if (!options) + goto out; + + while ((p = strsep(&options, ",")) != NULL) { + int token; + if (!*p) + continue; + + token = match_token(p, exfat_tokens, args); + switch (token) { + case Opt_uid: + if (match_int(&args[0], &option)) + return 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) + opts->fs_uid = KUIDT_INIT(option); +#else + opts->fs_uid = option; +#endif + break; + case Opt_gid: + if (match_int(&args[0], &option)) + return 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) + opts->fs_gid = KGIDT_INIT(option); +#else + opts->fs_gid = option; +#endif + break; + case Opt_umask: + case Opt_dmask: + case Opt_fmask: + if (match_octal(&args[0], &option)) + return 0; + if (token != Opt_dmask) + opts->fs_fmask = option; + if (token != Opt_fmask) + opts->fs_dmask = option; + break; + case Opt_allow_utime: + if (match_octal(&args[0], &option)) + return 0; + opts->allow_utime = option & (S_IWGRP | S_IWOTH); + break; + case Opt_codepage: + if (match_int(&args[0], &option)) + return 0; + opts->codepage = option; + break; + case Opt_charset: + if (opts->iocharset != exfat_default_iocharset) + kfree(opts->iocharset); + iocharset = match_strdup(&args[0]); + if (!iocharset) + return -ENOMEM; + opts->iocharset = iocharset; + break; + case Opt_namecase: + if (match_int(&args[0], &option)) + return 0; + opts->casesensitive = option; + break; + case Opt_err_cont: + opts->errors = EXFAT_ERRORS_CONT; + break; + case Opt_err_panic: + opts->errors = EXFAT_ERRORS_PANIC; + break; + case Opt_err_ro: + opts->errors = EXFAT_ERRORS_RO; + break; + case Opt_debug: + *debug = 1; + break; +#ifdef CONFIG_EXFAT_DISCARD + case Opt_discard: + opts->discard = 1; + break; +#endif /* CONFIG_EXFAT_DISCARD */ + case Opt_utf8_hack: + break; + default: + if (!silent) + printk(KERN_ERR "[EXFAT] Unrecognized mount option %s or missing value\n", p); + return -EINVAL; + } + } + +out: + if (opts->allow_utime == (unsigned short) -1) + opts->allow_utime = ~opts->fs_dmask & (S_IWGRP | S_IWOTH); + + return 0; +} + +static void exfat_hash_init(struct super_block *sb) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + int i; + + spin_lock_init(&sbi->inode_hash_lock); + for (i = 0; i < EXFAT_HASH_SIZE; i++) + INIT_HLIST_HEAD(&sbi->inode_hashtable[i]); +} + +static int exfat_read_root(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct timespec ts; + FS_INFO_T *p_fs = &(sbi->fs_info); + DIR_ENTRY_T info; + + ts = CURRENT_TIME_SEC; + + EXFAT_I(inode)->fid.dir.dir = p_fs->root_dir; + EXFAT_I(inode)->fid.dir.flags = 0x01; + EXFAT_I(inode)->fid.entry = -1; + EXFAT_I(inode)->fid.start_clu = p_fs->root_dir; + EXFAT_I(inode)->fid.flags = 0x01; + EXFAT_I(inode)->fid.type = TYPE_DIR; + EXFAT_I(inode)->fid.rwoffset = 0; + EXFAT_I(inode)->fid.hint_last_off = -1; + + EXFAT_I(inode)->target = NULL; + + FsReadStat(inode, &info); + + inode->i_uid = sbi->options.fs_uid; + inode->i_gid = sbi->options.fs_gid; + inode->i_version++; + inode->i_generation = 0; + inode->i_mode = exfat_make_mode(sbi, ATTR_SUBDIR, S_IRWXUGO); + inode->i_op = &exfat_dir_inode_operations; + inode->i_fop = &exfat_dir_operations; + + i_size_write(inode, info.Size); + inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1)) + & ~((loff_t)p_fs->cluster_size - 1)) >> 9; + EXFAT_I(inode)->i_pos = ((loff_t) p_fs->root_dir << 32) | 0xffffffff; + EXFAT_I(inode)->mmu_private = i_size_read(inode); + + exfat_save_attr(inode, ATTR_SUBDIR); + inode->i_mtime = inode->i_atime = inode->i_ctime = ts; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,00) + set_nlink(inode, info.NumSubdirs + 2); +#else + inode->i_nlink = info.NumSubdirs + 2; +#endif + + return 0; +} + +static void setup_dops(struct super_block *sb) +{ + if (EXFAT_SB(sb)->options.casesensitive == 0) + sb->s_d_op = &exfat_ci_dentry_ops; + else + sb->s_d_op = &exfat_dentry_ops; +} + +static int exfat_fill_super(struct super_block *sb, void *data, int silent) +{ + struct inode *root_inode = NULL; + struct exfat_sb_info *sbi; + int debug, ret; + long error; + char buf[50]; + + /* + * GFP_KERNEL is ok here, because while we do hold the + * supeblock lock, memory pressure can't call back into + * the filesystem, since we're only just about to mount + * it and have no inodes etc active! + */ + sbi = kzalloc(sizeof(struct exfat_sb_info), GFP_KERNEL); + if (!sbi) + return -ENOMEM; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) + mutex_init(&sbi->s_lock); +#endif + sb->s_fs_info = sbi; + sb->s_flags |= MS_NODIRATIME; + sb->s_magic = EXFAT_SUPER_MAGIC; + sb->s_op = &exfat_sops; + + error = parse_options(data, silent, &debug, &sbi->options); + if (error) + goto out_fail; + + setup_dops(sb); + + error = -EIO; + sb_min_blocksize(sb, 512); + sb->s_maxbytes = 0x7fffffffffffffffLL; /* maximum file size */ + + ret = FsMountVol(sb); + if (ret) { + if (!silent) + printk(KERN_ERR "[EXFAT] FsMountVol failed\n"); + + goto out_fail; + } + + /* set up enough so that it can read an inode */ + exfat_hash_init(sb); + + /* + * The low byte of FAT's first entry must have same value with + * media-field. But in real world, too many devices is + * writing wrong value. So, removed that validity check. + * + * if (FAT_FIRST_ENT(sb, media) != first) + */ + + /* codepage is not meaningful in exfat */ + if (sbi->fs_info.vol_type != EXFAT) { + error = -EINVAL; + sprintf(buf, "cp%d", sbi->options.codepage); + sbi->nls_disk = load_nls(buf); + if (!sbi->nls_disk) { + printk(KERN_ERR "[EXFAT] Codepage %s not found\n", buf); + goto out_fail2; + } + } + + sbi->nls_io = load_nls(sbi->options.iocharset); + + error = -ENOMEM; + root_inode = new_inode(sb); + if (!root_inode) + goto out_fail2; + root_inode->i_ino = EXFAT_ROOT_INO; + root_inode->i_version = 1; + error = exfat_read_root(root_inode); + if (error < 0) + goto out_fail2; + error = -ENOMEM; + exfat_attach(root_inode, EXFAT_I(root_inode)->i_pos); + insert_inode_hash(root_inode); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) + sb->s_root = d_make_root(root_inode); +#else + sb->s_root = d_alloc_root(root_inode); +#endif + if (!sb->s_root) { + printk(KERN_ERR "[EXFAT] Getting the root inode failed\n"); + goto out_fail2; + } + + return 0; + +out_fail2: + FsUmountVol(sb); +out_fail: + if (root_inode) + iput(root_inode); + sb->s_fs_info = NULL; + exfat_free_super(sbi); + return error; +} +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) +static int exfat_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data, struct vfsmount *mnt) +{ + return get_sb_bdev(fs_type, flags, dev_name, data, exfat_fill_super, mnt); +} +#else +static struct dentry *exfat_fs_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data) { + return mount_bdev(fs_type, flags, dev_name, data, exfat_fill_super); +} +#endif + +static void init_once(void *foo) +{ + struct exfat_inode_info *ei = (struct exfat_inode_info *)foo; + + INIT_HLIST_NODE(&ei->i_hash_fat); + inode_init_once(&ei->vfs_inode); +} + +static int __init exfat_init_inodecache(void) +{ + exfat_inode_cachep = kmem_cache_create("exfat_inode_cache", + sizeof(struct exfat_inode_info), + 0, (SLAB_RECLAIM_ACCOUNT| + SLAB_MEM_SPREAD), + init_once); + if (exfat_inode_cachep == NULL) + return -ENOMEM; + return 0; +} + +static void __exit exfat_destroy_inodecache(void) +{ + kmem_cache_destroy(exfat_inode_cachep); +} + +#ifdef CONFIG_EXFAT_KERNEL_DEBUG +static void exfat_debug_kill_sb(struct super_block *sb) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct block_device *bdev = sb->s_bdev; + + long flags; + + if (sbi) { + flags = sbi->debug_flags; + + if (flags & EXFAT_DEBUGFLAGS_INVALID_UMOUNT) { + /* invalidate_bdev drops all device cache include dirty. + we use this to simulate device removal */ + FsReleaseCache(sb); + invalidate_bdev(bdev); + } + } + + kill_block_super(sb); +} +#endif /* CONFIG_EXFAT_KERNEL_DEBUG */ + +static struct file_system_type exfat_fs_type = { + .owner = THIS_MODULE, +#if defined(CONFIG_MACH_LGE) || defined(CONFIG_HTC_BATT_CORE) + .name = "texfat", +#else + .name = "exfat", +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) + .get_sb = exfat_get_sb, +#else + .mount = exfat_fs_mount, +#endif +#ifdef CONFIG_EXFAT_KERNEL_DEBUG + .kill_sb = exfat_debug_kill_sb, +#else + .kill_sb = kill_block_super, +#endif /* CONFIG_EXFAT_KERNEL_DEBUG */ + .fs_flags = FS_REQUIRES_DEV, +}; + +static int __init init_exfat(void) +{ + int err; + + err = FsInit(); + if (err) { + if (err == FFS_MEMORYERR) + return -ENOMEM; + else + return -EIO; + } + + printk(KERN_INFO "exFAT: Version %s\n", EXFAT_VERSION); + + err = exfat_init_inodecache(); + if (err) + goto out; + + err = register_filesystem(&exfat_fs_type); + if (err) + goto out; + + return 0; +out: + FsShutdown(); + return err; +} + +static void __exit exit_exfat(void) +{ + exfat_destroy_inodecache(); + unregister_filesystem(&exfat_fs_type); + FsShutdown(); +} + +module_init(init_exfat); +module_exit(exit_exfat); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("exFAT Filesystem Driver"); +#ifdef MODULE_ALIAS_FS +#if defined(CONFIG_MACH_LGE) || defined(CONFIG_HTC_BATT_CORE) +MODULE_ALIAS_FS("texfat"); +#else +MODULE_ALIAS_FS("exfat"); +#endif +#endif diff --git a/fs/exfat/exfat_super.h b/fs/exfat/exfat_super.h new file mode 100644 index 00000000..916811e3 --- /dev/null +++ b/fs/exfat/exfat_super.h @@ -0,0 +1,171 @@ +/* Some of the source code in this file came from "linux/fs/fat/fat.h". */ + +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _EXFAT_LINUX_H +#define _EXFAT_LINUX_H + +#include +#include +#include +#include +#include +#include + +#include "exfat_config.h" +#include "exfat_data.h" +#include "exfat_oal.h" + +#include "exfat_blkdev.h" +#include "exfat_cache.h" +#include "exfat_nls.h" +#include "exfat_api.h" +#include "exfat_core.h" + +#define EXFAT_ERRORS_CONT 1 /* ignore error and continue */ +#define EXFAT_ERRORS_PANIC 2 /* panic on error */ +#define EXFAT_ERRORS_RO 3 /* remount r/o on error */ + +/* ioctl command */ +#define EXFAT_IOCTL_GET_VOLUME_ID _IOR('r', 0x12, __u32) + +struct exfat_mount_options { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) + kuid_t fs_uid; + kgid_t fs_gid; +#else + uid_t fs_uid; + gid_t fs_gid; +#endif + unsigned short fs_fmask; + unsigned short fs_dmask; + unsigned short allow_utime; /* permission for setting the [am]time */ + unsigned short codepage; /* codepage for shortname conversions */ + char *iocharset; /* charset for filename input/display */ + unsigned char casesensitive; + unsigned char errors; /* on error: continue, panic, remount-ro */ +#ifdef CONFIG_EXFAT_DISCARD + unsigned char discard; /* flag on if -o dicard specified and device support discard() */ +#endif /* CONFIG_EXFAT_DISCARD */ +}; + +#define EXFAT_HASH_BITS 8 +#define EXFAT_HASH_SIZE (1UL << EXFAT_HASH_BITS) + +/* + * EXFAT file system in-core superblock data + */ +struct exfat_sb_info { + FS_INFO_T fs_info; + BD_INFO_T bd_info; + + struct exfat_mount_options options; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) + int s_dirt; + struct mutex s_lock; +#endif + struct nls_table *nls_disk; /* Codepage used on disk */ + struct nls_table *nls_io; /* Charset used for input and display */ + + struct inode *fat_inode; + + spinlock_t inode_hash_lock; + struct hlist_head inode_hashtable[EXFAT_HASH_SIZE]; +#ifdef CONFIG_EXFAT_KERNEL_DEBUG + long debug_flags; +#endif /* CONFIG_EXFAT_KERNEL_DEBUG */ +}; + +/* + * EXFAT file system inode data in memory + */ +struct exfat_inode_info { + FILE_ID_T fid; + char *target; + /* NOTE: mmu_private is 64bits, so must hold ->i_mutex to access */ + loff_t mmu_private; /* physically allocated size */ + loff_t i_pos; /* on-disk position of directory entry or 0 */ + struct hlist_node i_hash_fat; /* hash by i_location */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) + struct rw_semaphore truncate_lock; +#endif + struct inode vfs_inode; + struct rw_semaphore i_alloc_sem; /* protect bmap against truncate */ +}; + +#define EXFAT_SB(sb) ((struct exfat_sb_info *)((sb)->s_fs_info)) + +static inline struct exfat_inode_info *EXFAT_I(struct inode *inode) +{ + return container_of(inode, struct exfat_inode_info, vfs_inode); +} + +/* + * If ->i_mode can't hold S_IWUGO (i.e. ATTR_RO), we use ->i_attrs to + * save ATTR_RO instead of ->i_mode. + * + * If it's directory and !sbi->options.rodir, ATTR_RO isn't read-only + * bit, it's just used as flag for app. + */ +static inline int exfat_mode_can_hold_ro(struct inode *inode) +{ + struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); + + if (S_ISDIR(inode->i_mode)) + return 0; + + if ((~sbi->options.fs_fmask) & S_IWUGO) + return 1; + return 0; +} + +/* Convert attribute bits and a mask to the UNIX mode. */ +static inline mode_t exfat_make_mode(struct exfat_sb_info *sbi, + u32 attr, mode_t mode) +{ + if ((attr & ATTR_READONLY) && !(attr & ATTR_SUBDIR)) + mode &= ~S_IWUGO; + + if (attr & ATTR_SUBDIR) + return (mode & ~sbi->options.fs_dmask) | S_IFDIR; + else if (attr & ATTR_SYMLINK) + return (mode & ~sbi->options.fs_dmask) | S_IFLNK; + else + return (mode & ~sbi->options.fs_fmask) | S_IFREG; +} + +/* Return the FAT attribute byte for this inode */ +static inline u32 exfat_make_attr(struct inode *inode) +{ + if (exfat_mode_can_hold_ro(inode) && !(inode->i_mode & S_IWUGO)) + return (EXFAT_I(inode)->fid.attr) | ATTR_READONLY; + else + return EXFAT_I(inode)->fid.attr; +} + +static inline void exfat_save_attr(struct inode *inode, u32 attr) +{ + if (exfat_mode_can_hold_ro(inode)) + EXFAT_I(inode)->fid.attr = attr & ATTR_RWMASK; + else + EXFAT_I(inode)->fid.attr = attr & (ATTR_RWMASK | ATTR_READONLY); +} + +#endif /* _EXFAT_LINUX_H */ diff --git a/fs/exfat/exfat_upcase.c b/fs/exfat/exfat_upcase.c new file mode 100644 index 00000000..e0a8668a --- /dev/null +++ b/fs/exfat/exfat_upcase.c @@ -0,0 +1,405 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_upcase.c */ +/* PURPOSE : exFAT Up-case Table */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#include "exfat_config.h" + +#include "exfat_nls.h" + +u8 uni_upcase[NUM_UPCASE<<1] = { + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, + 0x08, 0x00, 0x09, 0x00, 0x0A, 0x00, 0x0B, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x0E, 0x00, 0x0F, 0x00, + 0x10, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, + 0x18, 0x00, 0x19, 0x00, 0x1A, 0x00, 0x1B, 0x00, 0x1C, 0x00, 0x1D, 0x00, 0x1E, 0x00, 0x1F, 0x00, + 0x20, 0x00, 0x21, 0x00, 0x22, 0x00, 0x23, 0x00, 0x24, 0x00, 0x25, 0x00, 0x26, 0x00, 0x27, 0x00, + 0x28, 0x00, 0x29, 0x00, 0x2A, 0x00, 0x2B, 0x00, 0x2C, 0x00, 0x2D, 0x00, 0x2E, 0x00, 0x2F, 0x00, + 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, 0x37, 0x00, + 0x38, 0x00, 0x39, 0x00, 0x3A, 0x00, 0x3B, 0x00, 0x3C, 0x00, 0x3D, 0x00, 0x3E, 0x00, 0x3F, 0x00, + 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, + 0x48, 0x00, 0x49, 0x00, 0x4A, 0x00, 0x4B, 0x00, 0x4C, 0x00, 0x4D, 0x00, 0x4E, 0x00, 0x4F, 0x00, + 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, + 0x58, 0x00, 0x59, 0x00, 0x5A, 0x00, 0x5B, 0x00, 0x5C, 0x00, 0x5D, 0x00, 0x5E, 0x00, 0x5F, 0x00, + 0x60, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, + 0x48, 0x00, 0x49, 0x00, 0x4A, 0x00, 0x4B, 0x00, 0x4C, 0x00, 0x4D, 0x00, 0x4E, 0x00, 0x4F, 0x00, + 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, + 0x58, 0x00, 0x59, 0x00, 0x5A, 0x00, 0x7B, 0x00, 0x7C, 0x00, 0x7D, 0x00, 0x7E, 0x00, 0x7F, 0x00, + 0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85, 0x00, 0x86, 0x00, 0x87, 0x00, + 0x88, 0x00, 0x89, 0x00, 0x8A, 0x00, 0x8B, 0x00, 0x8C, 0x00, 0x8D, 0x00, 0x8E, 0x00, 0x8F, 0x00, + 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, + 0x98, 0x00, 0x99, 0x00, 0x9A, 0x00, 0x9B, 0x00, 0x9C, 0x00, 0x9D, 0x00, 0x9E, 0x00, 0x9F, 0x00, + 0xA0, 0x00, 0xA1, 0x00, 0xA2, 0x00, 0xA3, 0x00, 0xA4, 0x00, 0xA5, 0x00, 0xA6, 0x00, 0xA7, 0x00, + 0xA8, 0x00, 0xA9, 0x00, 0xAA, 0x00, 0xAB, 0x00, 0xAC, 0x00, 0xAD, 0x00, 0xAE, 0x00, 0xAF, 0x00, + 0xB0, 0x00, 0xB1, 0x00, 0xB2, 0x00, 0xB3, 0x00, 0xB4, 0x00, 0xB5, 0x00, 0xB6, 0x00, 0xB7, 0x00, + 0xB8, 0x00, 0xB9, 0x00, 0xBA, 0x00, 0xBB, 0x00, 0xBC, 0x00, 0xBD, 0x00, 0xBE, 0x00, 0xBF, 0x00, + 0xC0, 0x00, 0xC1, 0x00, 0xC2, 0x00, 0xC3, 0x00, 0xC4, 0x00, 0xC5, 0x00, 0xC6, 0x00, 0xC7, 0x00, + 0xC8, 0x00, 0xC9, 0x00, 0xCA, 0x00, 0xCB, 0x00, 0xCC, 0x00, 0xCD, 0x00, 0xCE, 0x00, 0xCF, 0x00, + 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD3, 0x00, 0xD4, 0x00, 0xD5, 0x00, 0xD6, 0x00, 0xD7, 0x00, + 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0xDD, 0x00, 0xDE, 0x00, 0xDF, 0x00, + 0xC0, 0x00, 0xC1, 0x00, 0xC2, 0x00, 0xC3, 0x00, 0xC4, 0x00, 0xC5, 0x00, 0xC6, 0x00, 0xC7, 0x00, + 0xC8, 0x00, 0xC9, 0x00, 0xCA, 0x00, 0xCB, 0x00, 0xCC, 0x00, 0xCD, 0x00, 0xCE, 0x00, 0xCF, 0x00, + 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD3, 0x00, 0xD4, 0x00, 0xD5, 0x00, 0xD6, 0x00, 0xF7, 0x00, + 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0xDD, 0x00, 0xDE, 0x00, 0x78, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x02, 0x01, 0x02, 0x01, 0x04, 0x01, 0x04, 0x01, 0x06, 0x01, 0x06, 0x01, + 0x08, 0x01, 0x08, 0x01, 0x0A, 0x01, 0x0A, 0x01, 0x0C, 0x01, 0x0C, 0x01, 0x0E, 0x01, 0x0E, 0x01, + 0x10, 0x01, 0x10, 0x01, 0x12, 0x01, 0x12, 0x01, 0x14, 0x01, 0x14, 0x01, 0x16, 0x01, 0x16, 0x01, + 0x18, 0x01, 0x18, 0x01, 0x1A, 0x01, 0x1A, 0x01, 0x1C, 0x01, 0x1C, 0x01, 0x1E, 0x01, 0x1E, 0x01, + 0x20, 0x01, 0x20, 0x01, 0x22, 0x01, 0x22, 0x01, 0x24, 0x01, 0x24, 0x01, 0x26, 0x01, 0x26, 0x01, + 0x28, 0x01, 0x28, 0x01, 0x2A, 0x01, 0x2A, 0x01, 0x2C, 0x01, 0x2C, 0x01, 0x2E, 0x01, 0x2E, 0x01, + 0x30, 0x01, 0x31, 0x01, 0x32, 0x01, 0x32, 0x01, 0x34, 0x01, 0x34, 0x01, 0x36, 0x01, 0x36, 0x01, + 0x38, 0x01, 0x39, 0x01, 0x39, 0x01, 0x3B, 0x01, 0x3B, 0x01, 0x3D, 0x01, 0x3D, 0x01, 0x3F, 0x01, + 0x3F, 0x01, 0x41, 0x01, 0x41, 0x01, 0x43, 0x01, 0x43, 0x01, 0x45, 0x01, 0x45, 0x01, 0x47, 0x01, + 0x47, 0x01, 0x49, 0x01, 0x4A, 0x01, 0x4A, 0x01, 0x4C, 0x01, 0x4C, 0x01, 0x4E, 0x01, 0x4E, 0x01, + 0x50, 0x01, 0x50, 0x01, 0x52, 0x01, 0x52, 0x01, 0x54, 0x01, 0x54, 0x01, 0x56, 0x01, 0x56, 0x01, + 0x58, 0x01, 0x58, 0x01, 0x5A, 0x01, 0x5A, 0x01, 0x5C, 0x01, 0x5C, 0x01, 0x5E, 0x01, 0x5E, 0x01, + 0x60, 0x01, 0x60, 0x01, 0x62, 0x01, 0x62, 0x01, 0x64, 0x01, 0x64, 0x01, 0x66, 0x01, 0x66, 0x01, + 0x68, 0x01, 0x68, 0x01, 0x6A, 0x01, 0x6A, 0x01, 0x6C, 0x01, 0x6C, 0x01, 0x6E, 0x01, 0x6E, 0x01, + 0x70, 0x01, 0x70, 0x01, 0x72, 0x01, 0x72, 0x01, 0x74, 0x01, 0x74, 0x01, 0x76, 0x01, 0x76, 0x01, + 0x78, 0x01, 0x79, 0x01, 0x79, 0x01, 0x7B, 0x01, 0x7B, 0x01, 0x7D, 0x01, 0x7D, 0x01, 0x7F, 0x01, + 0x43, 0x02, 0x81, 0x01, 0x82, 0x01, 0x82, 0x01, 0x84, 0x01, 0x84, 0x01, 0x86, 0x01, 0x87, 0x01, + 0x87, 0x01, 0x89, 0x01, 0x8A, 0x01, 0x8B, 0x01, 0x8B, 0x01, 0x8D, 0x01, 0x8E, 0x01, 0x8F, 0x01, + 0x90, 0x01, 0x91, 0x01, 0x91, 0x01, 0x93, 0x01, 0x94, 0x01, 0xF6, 0x01, 0x96, 0x01, 0x97, 0x01, + 0x98, 0x01, 0x98, 0x01, 0x3D, 0x02, 0x9B, 0x01, 0x9C, 0x01, 0x9D, 0x01, 0x20, 0x02, 0x9F, 0x01, + 0xA0, 0x01, 0xA0, 0x01, 0xA2, 0x01, 0xA2, 0x01, 0xA4, 0x01, 0xA4, 0x01, 0xA6, 0x01, 0xA7, 0x01, + 0xA7, 0x01, 0xA9, 0x01, 0xAA, 0x01, 0xAB, 0x01, 0xAC, 0x01, 0xAC, 0x01, 0xAE, 0x01, 0xAF, 0x01, + 0xAF, 0x01, 0xB1, 0x01, 0xB2, 0x01, 0xB3, 0x01, 0xB3, 0x01, 0xB5, 0x01, 0xB5, 0x01, 0xB7, 0x01, + 0xB8, 0x01, 0xB8, 0x01, 0xBA, 0x01, 0xBB, 0x01, 0xBC, 0x01, 0xBC, 0x01, 0xBE, 0x01, 0xF7, 0x01, + 0xC0, 0x01, 0xC1, 0x01, 0xC2, 0x01, 0xC3, 0x01, 0xC4, 0x01, 0xC5, 0x01, 0xC4, 0x01, 0xC7, 0x01, + 0xC8, 0x01, 0xC7, 0x01, 0xCA, 0x01, 0xCB, 0x01, 0xCA, 0x01, 0xCD, 0x01, 0xCD, 0x01, 0xCF, 0x01, + 0xCF, 0x01, 0xD1, 0x01, 0xD1, 0x01, 0xD3, 0x01, 0xD3, 0x01, 0xD5, 0x01, 0xD5, 0x01, 0xD7, 0x01, + 0xD7, 0x01, 0xD9, 0x01, 0xD9, 0x01, 0xDB, 0x01, 0xDB, 0x01, 0x8E, 0x01, 0xDE, 0x01, 0xDE, 0x01, + 0xE0, 0x01, 0xE0, 0x01, 0xE2, 0x01, 0xE2, 0x01, 0xE4, 0x01, 0xE4, 0x01, 0xE6, 0x01, 0xE6, 0x01, + 0xE8, 0x01, 0xE8, 0x01, 0xEA, 0x01, 0xEA, 0x01, 0xEC, 0x01, 0xEC, 0x01, 0xEE, 0x01, 0xEE, 0x01, + 0xF0, 0x01, 0xF1, 0x01, 0xF2, 0x01, 0xF1, 0x01, 0xF4, 0x01, 0xF4, 0x01, 0xF6, 0x01, 0xF7, 0x01, + 0xF8, 0x01, 0xF8, 0x01, 0xFA, 0x01, 0xFA, 0x01, 0xFC, 0x01, 0xFC, 0x01, 0xFE, 0x01, 0xFE, 0x01, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x04, 0x02, 0x04, 0x02, 0x06, 0x02, 0x06, 0x02, + 0x08, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x0A, 0x02, 0x0C, 0x02, 0x0C, 0x02, 0x0E, 0x02, 0x0E, 0x02, + 0x10, 0x02, 0x10, 0x02, 0x12, 0x02, 0x12, 0x02, 0x14, 0x02, 0x14, 0x02, 0x16, 0x02, 0x16, 0x02, + 0x18, 0x02, 0x18, 0x02, 0x1A, 0x02, 0x1A, 0x02, 0x1C, 0x02, 0x1C, 0x02, 0x1E, 0x02, 0x1E, 0x02, + 0x20, 0x02, 0x21, 0x02, 0x22, 0x02, 0x22, 0x02, 0x24, 0x02, 0x24, 0x02, 0x26, 0x02, 0x26, 0x02, + 0x28, 0x02, 0x28, 0x02, 0x2A, 0x02, 0x2A, 0x02, 0x2C, 0x02, 0x2C, 0x02, 0x2E, 0x02, 0x2E, 0x02, + 0x30, 0x02, 0x30, 0x02, 0x32, 0x02, 0x32, 0x02, 0x34, 0x02, 0x35, 0x02, 0x36, 0x02, 0x37, 0x02, + 0x38, 0x02, 0x39, 0x02, 0x65, 0x2C, 0x3B, 0x02, 0x3B, 0x02, 0x3D, 0x02, 0x66, 0x2C, 0x3F, 0x02, + 0x40, 0x02, 0x41, 0x02, 0x41, 0x02, 0x43, 0x02, 0x44, 0x02, 0x45, 0x02, 0x46, 0x02, 0x46, 0x02, + 0x48, 0x02, 0x48, 0x02, 0x4A, 0x02, 0x4A, 0x02, 0x4C, 0x02, 0x4C, 0x02, 0x4E, 0x02, 0x4E, 0x02, + 0x50, 0x02, 0x51, 0x02, 0x52, 0x02, 0x81, 0x01, 0x86, 0x01, 0x55, 0x02, 0x89, 0x01, 0x8A, 0x01, + 0x58, 0x02, 0x8F, 0x01, 0x5A, 0x02, 0x90, 0x01, 0x5C, 0x02, 0x5D, 0x02, 0x5E, 0x02, 0x5F, 0x02, + 0x93, 0x01, 0x61, 0x02, 0x62, 0x02, 0x94, 0x01, 0x64, 0x02, 0x65, 0x02, 0x66, 0x02, 0x67, 0x02, + 0x97, 0x01, 0x96, 0x01, 0x6A, 0x02, 0x62, 0x2C, 0x6C, 0x02, 0x6D, 0x02, 0x6E, 0x02, 0x9C, 0x01, + 0x70, 0x02, 0x71, 0x02, 0x9D, 0x01, 0x73, 0x02, 0x74, 0x02, 0x9F, 0x01, 0x76, 0x02, 0x77, 0x02, + 0x78, 0x02, 0x79, 0x02, 0x7A, 0x02, 0x7B, 0x02, 0x7C, 0x02, 0x64, 0x2C, 0x7E, 0x02, 0x7F, 0x02, + 0xA6, 0x01, 0x81, 0x02, 0x82, 0x02, 0xA9, 0x01, 0x84, 0x02, 0x85, 0x02, 0x86, 0x02, 0x87, 0x02, + 0xAE, 0x01, 0x44, 0x02, 0xB1, 0x01, 0xB2, 0x01, 0x45, 0x02, 0x8D, 0x02, 0x8E, 0x02, 0x8F, 0x02, + 0x90, 0x02, 0x91, 0x02, 0xB7, 0x01, 0x93, 0x02, 0x94, 0x02, 0x95, 0x02, 0x96, 0x02, 0x97, 0x02, + 0x98, 0x02, 0x99, 0x02, 0x9A, 0x02, 0x9B, 0x02, 0x9C, 0x02, 0x9D, 0x02, 0x9E, 0x02, 0x9F, 0x02, + 0xA0, 0x02, 0xA1, 0x02, 0xA2, 0x02, 0xA3, 0x02, 0xA4, 0x02, 0xA5, 0x02, 0xA6, 0x02, 0xA7, 0x02, + 0xA8, 0x02, 0xA9, 0x02, 0xAA, 0x02, 0xAB, 0x02, 0xAC, 0x02, 0xAD, 0x02, 0xAE, 0x02, 0xAF, 0x02, + 0xB0, 0x02, 0xB1, 0x02, 0xB2, 0x02, 0xB3, 0x02, 0xB4, 0x02, 0xB5, 0x02, 0xB6, 0x02, 0xB7, 0x02, + 0xB8, 0x02, 0xB9, 0x02, 0xBA, 0x02, 0xBB, 0x02, 0xBC, 0x02, 0xBD, 0x02, 0xBE, 0x02, 0xBF, 0x02, + 0xC0, 0x02, 0xC1, 0x02, 0xC2, 0x02, 0xC3, 0x02, 0xC4, 0x02, 0xC5, 0x02, 0xC6, 0x02, 0xC7, 0x02, + 0xC8, 0x02, 0xC9, 0x02, 0xCA, 0x02, 0xCB, 0x02, 0xCC, 0x02, 0xCD, 0x02, 0xCE, 0x02, 0xCF, 0x02, + 0xD0, 0x02, 0xD1, 0x02, 0xD2, 0x02, 0xD3, 0x02, 0xD4, 0x02, 0xD5, 0x02, 0xD6, 0x02, 0xD7, 0x02, + 0xD8, 0x02, 0xD9, 0x02, 0xDA, 0x02, 0xDB, 0x02, 0xDC, 0x02, 0xDD, 0x02, 0xDE, 0x02, 0xDF, 0x02, + 0xE0, 0x02, 0xE1, 0x02, 0xE2, 0x02, 0xE3, 0x02, 0xE4, 0x02, 0xE5, 0x02, 0xE6, 0x02, 0xE7, 0x02, + 0xE8, 0x02, 0xE9, 0x02, 0xEA, 0x02, 0xEB, 0x02, 0xEC, 0x02, 0xED, 0x02, 0xEE, 0x02, 0xEF, 0x02, + 0xF0, 0x02, 0xF1, 0x02, 0xF2, 0x02, 0xF3, 0x02, 0xF4, 0x02, 0xF5, 0x02, 0xF6, 0x02, 0xF7, 0x02, + 0xF8, 0x02, 0xF9, 0x02, 0xFA, 0x02, 0xFB, 0x02, 0xFC, 0x02, 0xFD, 0x02, 0xFE, 0x02, 0xFF, 0x02, + 0x00, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x03, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x07, 0x03, + 0x08, 0x03, 0x09, 0x03, 0x0A, 0x03, 0x0B, 0x03, 0x0C, 0x03, 0x0D, 0x03, 0x0E, 0x03, 0x0F, 0x03, + 0x10, 0x03, 0x11, 0x03, 0x12, 0x03, 0x13, 0x03, 0x14, 0x03, 0x15, 0x03, 0x16, 0x03, 0x17, 0x03, + 0x18, 0x03, 0x19, 0x03, 0x1A, 0x03, 0x1B, 0x03, 0x1C, 0x03, 0x1D, 0x03, 0x1E, 0x03, 0x1F, 0x03, + 0x20, 0x03, 0x21, 0x03, 0x22, 0x03, 0x23, 0x03, 0x24, 0x03, 0x25, 0x03, 0x26, 0x03, 0x27, 0x03, + 0x28, 0x03, 0x29, 0x03, 0x2A, 0x03, 0x2B, 0x03, 0x2C, 0x03, 0x2D, 0x03, 0x2E, 0x03, 0x2F, 0x03, + 0x30, 0x03, 0x31, 0x03, 0x32, 0x03, 0x33, 0x03, 0x34, 0x03, 0x35, 0x03, 0x36, 0x03, 0x37, 0x03, + 0x38, 0x03, 0x39, 0x03, 0x3A, 0x03, 0x3B, 0x03, 0x3C, 0x03, 0x3D, 0x03, 0x3E, 0x03, 0x3F, 0x03, + 0x40, 0x03, 0x41, 0x03, 0x42, 0x03, 0x43, 0x03, 0x44, 0x03, 0x45, 0x03, 0x46, 0x03, 0x47, 0x03, + 0x48, 0x03, 0x49, 0x03, 0x4A, 0x03, 0x4B, 0x03, 0x4C, 0x03, 0x4D, 0x03, 0x4E, 0x03, 0x4F, 0x03, + 0x50, 0x03, 0x51, 0x03, 0x52, 0x03, 0x53, 0x03, 0x54, 0x03, 0x55, 0x03, 0x56, 0x03, 0x57, 0x03, + 0x58, 0x03, 0x59, 0x03, 0x5A, 0x03, 0x5B, 0x03, 0x5C, 0x03, 0x5D, 0x03, 0x5E, 0x03, 0x5F, 0x03, + 0x60, 0x03, 0x61, 0x03, 0x62, 0x03, 0x63, 0x03, 0x64, 0x03, 0x65, 0x03, 0x66, 0x03, 0x67, 0x03, + 0x68, 0x03, 0x69, 0x03, 0x6A, 0x03, 0x6B, 0x03, 0x6C, 0x03, 0x6D, 0x03, 0x6E, 0x03, 0x6F, 0x03, + 0x70, 0x03, 0x71, 0x03, 0x72, 0x03, 0x73, 0x03, 0x74, 0x03, 0x75, 0x03, 0x76, 0x03, 0x77, 0x03, + 0x78, 0x03, 0x79, 0x03, 0x7A, 0x03, 0xFD, 0x03, 0xFE, 0x03, 0xFF, 0x03, 0x7E, 0x03, 0x7F, 0x03, + 0x80, 0x03, 0x81, 0x03, 0x82, 0x03, 0x83, 0x03, 0x84, 0x03, 0x85, 0x03, 0x86, 0x03, 0x87, 0x03, + 0x88, 0x03, 0x89, 0x03, 0x8A, 0x03, 0x8B, 0x03, 0x8C, 0x03, 0x8D, 0x03, 0x8E, 0x03, 0x8F, 0x03, + 0x90, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03, 0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03, + 0x98, 0x03, 0x99, 0x03, 0x9A, 0x03, 0x9B, 0x03, 0x9C, 0x03, 0x9D, 0x03, 0x9E, 0x03, 0x9F, 0x03, + 0xA0, 0x03, 0xA1, 0x03, 0xA2, 0x03, 0xA3, 0x03, 0xA4, 0x03, 0xA5, 0x03, 0xA6, 0x03, 0xA7, 0x03, + 0xA8, 0x03, 0xA9, 0x03, 0xAA, 0x03, 0xAB, 0x03, 0x86, 0x03, 0x88, 0x03, 0x89, 0x03, 0x8A, 0x03, + 0xB0, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03, 0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03, + 0x98, 0x03, 0x99, 0x03, 0x9A, 0x03, 0x9B, 0x03, 0x9C, 0x03, 0x9D, 0x03, 0x9E, 0x03, 0x9F, 0x03, + 0xA0, 0x03, 0xA1, 0x03, 0xA3, 0x03, 0xA3, 0x03, 0xA4, 0x03, 0xA5, 0x03, 0xA6, 0x03, 0xA7, 0x03, + 0xA8, 0x03, 0xA9, 0x03, 0xAA, 0x03, 0xAB, 0x03, 0x8C, 0x03, 0x8E, 0x03, 0x8F, 0x03, 0xCF, 0x03, + 0xD0, 0x03, 0xD1, 0x03, 0xD2, 0x03, 0xD3, 0x03, 0xD4, 0x03, 0xD5, 0x03, 0xD6, 0x03, 0xD7, 0x03, + 0xD8, 0x03, 0xD8, 0x03, 0xDA, 0x03, 0xDA, 0x03, 0xDC, 0x03, 0xDC, 0x03, 0xDE, 0x03, 0xDE, 0x03, + 0xE0, 0x03, 0xE0, 0x03, 0xE2, 0x03, 0xE2, 0x03, 0xE4, 0x03, 0xE4, 0x03, 0xE6, 0x03, 0xE6, 0x03, + 0xE8, 0x03, 0xE8, 0x03, 0xEA, 0x03, 0xEA, 0x03, 0xEC, 0x03, 0xEC, 0x03, 0xEE, 0x03, 0xEE, 0x03, + 0xF0, 0x03, 0xF1, 0x03, 0xF9, 0x03, 0xF3, 0x03, 0xF4, 0x03, 0xF5, 0x03, 0xF6, 0x03, 0xF7, 0x03, + 0xF7, 0x03, 0xF9, 0x03, 0xFA, 0x03, 0xFA, 0x03, 0xFC, 0x03, 0xFD, 0x03, 0xFE, 0x03, 0xFF, 0x03, + 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04, + 0x08, 0x04, 0x09, 0x04, 0x0A, 0x04, 0x0B, 0x04, 0x0C, 0x04, 0x0D, 0x04, 0x0E, 0x04, 0x0F, 0x04, + 0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04, 0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04, + 0x18, 0x04, 0x19, 0x04, 0x1A, 0x04, 0x1B, 0x04, 0x1C, 0x04, 0x1D, 0x04, 0x1E, 0x04, 0x1F, 0x04, + 0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04, 0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04, + 0x28, 0x04, 0x29, 0x04, 0x2A, 0x04, 0x2B, 0x04, 0x2C, 0x04, 0x2D, 0x04, 0x2E, 0x04, 0x2F, 0x04, + 0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04, 0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04, + 0x18, 0x04, 0x19, 0x04, 0x1A, 0x04, 0x1B, 0x04, 0x1C, 0x04, 0x1D, 0x04, 0x1E, 0x04, 0x1F, 0x04, + 0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04, 0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04, + 0x28, 0x04, 0x29, 0x04, 0x2A, 0x04, 0x2B, 0x04, 0x2C, 0x04, 0x2D, 0x04, 0x2E, 0x04, 0x2F, 0x04, + 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04, + 0x08, 0x04, 0x09, 0x04, 0x0A, 0x04, 0x0B, 0x04, 0x0C, 0x04, 0x0D, 0x04, 0x0E, 0x04, 0x0F, 0x04, + 0x60, 0x04, 0x60, 0x04, 0x62, 0x04, 0x62, 0x04, 0x64, 0x04, 0x64, 0x04, 0x66, 0x04, 0x66, 0x04, + 0x68, 0x04, 0x68, 0x04, 0x6A, 0x04, 0x6A, 0x04, 0x6C, 0x04, 0x6C, 0x04, 0x6E, 0x04, 0x6E, 0x04, + 0x70, 0x04, 0x70, 0x04, 0x72, 0x04, 0x72, 0x04, 0x74, 0x04, 0x74, 0x04, 0x76, 0x04, 0x76, 0x04, + 0x78, 0x04, 0x78, 0x04, 0x7A, 0x04, 0x7A, 0x04, 0x7C, 0x04, 0x7C, 0x04, 0x7E, 0x04, 0x7E, 0x04, + 0x80, 0x04, 0x80, 0x04, 0x82, 0x04, 0x83, 0x04, 0x84, 0x04, 0x85, 0x04, 0x86, 0x04, 0x87, 0x04, + 0x88, 0x04, 0x89, 0x04, 0x8A, 0x04, 0x8A, 0x04, 0x8C, 0x04, 0x8C, 0x04, 0x8E, 0x04, 0x8E, 0x04, + 0x90, 0x04, 0x90, 0x04, 0x92, 0x04, 0x92, 0x04, 0x94, 0x04, 0x94, 0x04, 0x96, 0x04, 0x96, 0x04, + 0x98, 0x04, 0x98, 0x04, 0x9A, 0x04, 0x9A, 0x04, 0x9C, 0x04, 0x9C, 0x04, 0x9E, 0x04, 0x9E, 0x04, + 0xA0, 0x04, 0xA0, 0x04, 0xA2, 0x04, 0xA2, 0x04, 0xA4, 0x04, 0xA4, 0x04, 0xA6, 0x04, 0xA6, 0x04, + 0xA8, 0x04, 0xA8, 0x04, 0xAA, 0x04, 0xAA, 0x04, 0xAC, 0x04, 0xAC, 0x04, 0xAE, 0x04, 0xAE, 0x04, + 0xB0, 0x04, 0xB0, 0x04, 0xB2, 0x04, 0xB2, 0x04, 0xB4, 0x04, 0xB4, 0x04, 0xB6, 0x04, 0xB6, 0x04, + 0xB8, 0x04, 0xB8, 0x04, 0xBA, 0x04, 0xBA, 0x04, 0xBC, 0x04, 0xBC, 0x04, 0xBE, 0x04, 0xBE, 0x04, + 0xC0, 0x04, 0xC1, 0x04, 0xC1, 0x04, 0xC3, 0x04, 0xC3, 0x04, 0xC5, 0x04, 0xC5, 0x04, 0xC7, 0x04, + 0xC7, 0x04, 0xC9, 0x04, 0xC9, 0x04, 0xCB, 0x04, 0xCB, 0x04, 0xCD, 0x04, 0xCD, 0x04, 0xC0, 0x04, + 0xD0, 0x04, 0xD0, 0x04, 0xD2, 0x04, 0xD2, 0x04, 0xD4, 0x04, 0xD4, 0x04, 0xD6, 0x04, 0xD6, 0x04, + 0xD8, 0x04, 0xD8, 0x04, 0xDA, 0x04, 0xDA, 0x04, 0xDC, 0x04, 0xDC, 0x04, 0xDE, 0x04, 0xDE, 0x04, + 0xE0, 0x04, 0xE0, 0x04, 0xE2, 0x04, 0xE2, 0x04, 0xE4, 0x04, 0xE4, 0x04, 0xE6, 0x04, 0xE6, 0x04, + 0xE8, 0x04, 0xE8, 0x04, 0xEA, 0x04, 0xEA, 0x04, 0xEC, 0x04, 0xEC, 0x04, 0xEE, 0x04, 0xEE, 0x04, + 0xF0, 0x04, 0xF0, 0x04, 0xF2, 0x04, 0xF2, 0x04, 0xF4, 0x04, 0xF4, 0x04, 0xF6, 0x04, 0xF6, 0x04, + 0xF8, 0x04, 0xF8, 0x04, 0xFA, 0x04, 0xFA, 0x04, 0xFC, 0x04, 0xFC, 0x04, 0xFE, 0x04, 0xFE, 0x04, + 0x00, 0x05, 0x00, 0x05, 0x02, 0x05, 0x02, 0x05, 0x04, 0x05, 0x04, 0x05, 0x06, 0x05, 0x06, 0x05, + 0x08, 0x05, 0x08, 0x05, 0x0A, 0x05, 0x0A, 0x05, 0x0C, 0x05, 0x0C, 0x05, 0x0E, 0x05, 0x0E, 0x05, + 0x10, 0x05, 0x10, 0x05, 0x12, 0x05, 0x12, 0x05, 0x14, 0x05, 0x15, 0x05, 0x16, 0x05, 0x17, 0x05, + 0x18, 0x05, 0x19, 0x05, 0x1A, 0x05, 0x1B, 0x05, 0x1C, 0x05, 0x1D, 0x05, 0x1E, 0x05, 0x1F, 0x05, + 0x20, 0x05, 0x21, 0x05, 0x22, 0x05, 0x23, 0x05, 0x24, 0x05, 0x25, 0x05, 0x26, 0x05, 0x27, 0x05, + 0x28, 0x05, 0x29, 0x05, 0x2A, 0x05, 0x2B, 0x05, 0x2C, 0x05, 0x2D, 0x05, 0x2E, 0x05, 0x2F, 0x05, + 0x30, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05, 0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05, + 0x38, 0x05, 0x39, 0x05, 0x3A, 0x05, 0x3B, 0x05, 0x3C, 0x05, 0x3D, 0x05, 0x3E, 0x05, 0x3F, 0x05, + 0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05, 0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05, + 0x48, 0x05, 0x49, 0x05, 0x4A, 0x05, 0x4B, 0x05, 0x4C, 0x05, 0x4D, 0x05, 0x4E, 0x05, 0x4F, 0x05, + 0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05, 0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0x57, 0x05, + 0x58, 0x05, 0x59, 0x05, 0x5A, 0x05, 0x5B, 0x05, 0x5C, 0x05, 0x5D, 0x05, 0x5E, 0x05, 0x5F, 0x05, + 0x60, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05, 0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05, + 0x38, 0x05, 0x39, 0x05, 0x3A, 0x05, 0x3B, 0x05, 0x3C, 0x05, 0x3D, 0x05, 0x3E, 0x05, 0x3F, 0x05, + 0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05, 0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05, + 0x48, 0x05, 0x49, 0x05, 0x4A, 0x05, 0x4B, 0x05, 0x4C, 0x05, 0x4D, 0x05, 0x4E, 0x05, 0x4F, 0x05, + 0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05, 0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0xFF, 0xFF, + 0xF6, 0x17, 0x63, 0x2C, 0x7E, 0x1D, 0x7F, 0x1D, 0x80, 0x1D, 0x81, 0x1D, 0x82, 0x1D, 0x83, 0x1D, + 0x84, 0x1D, 0x85, 0x1D, 0x86, 0x1D, 0x87, 0x1D, 0x88, 0x1D, 0x89, 0x1D, 0x8A, 0x1D, 0x8B, 0x1D, + 0x8C, 0x1D, 0x8D, 0x1D, 0x8E, 0x1D, 0x8F, 0x1D, 0x90, 0x1D, 0x91, 0x1D, 0x92, 0x1D, 0x93, 0x1D, + 0x94, 0x1D, 0x95, 0x1D, 0x96, 0x1D, 0x97, 0x1D, 0x98, 0x1D, 0x99, 0x1D, 0x9A, 0x1D, 0x9B, 0x1D, + 0x9C, 0x1D, 0x9D, 0x1D, 0x9E, 0x1D, 0x9F, 0x1D, 0xA0, 0x1D, 0xA1, 0x1D, 0xA2, 0x1D, 0xA3, 0x1D, + 0xA4, 0x1D, 0xA5, 0x1D, 0xA6, 0x1D, 0xA7, 0x1D, 0xA8, 0x1D, 0xA9, 0x1D, 0xAA, 0x1D, 0xAB, 0x1D, + 0xAC, 0x1D, 0xAD, 0x1D, 0xAE, 0x1D, 0xAF, 0x1D, 0xB0, 0x1D, 0xB1, 0x1D, 0xB2, 0x1D, 0xB3, 0x1D, + 0xB4, 0x1D, 0xB5, 0x1D, 0xB6, 0x1D, 0xB7, 0x1D, 0xB8, 0x1D, 0xB9, 0x1D, 0xBA, 0x1D, 0xBB, 0x1D, + 0xBC, 0x1D, 0xBD, 0x1D, 0xBE, 0x1D, 0xBF, 0x1D, 0xC0, 0x1D, 0xC1, 0x1D, 0xC2, 0x1D, 0xC3, 0x1D, + 0xC4, 0x1D, 0xC5, 0x1D, 0xC6, 0x1D, 0xC7, 0x1D, 0xC8, 0x1D, 0xC9, 0x1D, 0xCA, 0x1D, 0xCB, 0x1D, + 0xCC, 0x1D, 0xCD, 0x1D, 0xCE, 0x1D, 0xCF, 0x1D, 0xD0, 0x1D, 0xD1, 0x1D, 0xD2, 0x1D, 0xD3, 0x1D, + 0xD4, 0x1D, 0xD5, 0x1D, 0xD6, 0x1D, 0xD7, 0x1D, 0xD8, 0x1D, 0xD9, 0x1D, 0xDA, 0x1D, 0xDB, 0x1D, + 0xDC, 0x1D, 0xDD, 0x1D, 0xDE, 0x1D, 0xDF, 0x1D, 0xE0, 0x1D, 0xE1, 0x1D, 0xE2, 0x1D, 0xE3, 0x1D, + 0xE4, 0x1D, 0xE5, 0x1D, 0xE6, 0x1D, 0xE7, 0x1D, 0xE8, 0x1D, 0xE9, 0x1D, 0xEA, 0x1D, 0xEB, 0x1D, + 0xEC, 0x1D, 0xED, 0x1D, 0xEE, 0x1D, 0xEF, 0x1D, 0xF0, 0x1D, 0xF1, 0x1D, 0xF2, 0x1D, 0xF3, 0x1D, + 0xF4, 0x1D, 0xF5, 0x1D, 0xF6, 0x1D, 0xF7, 0x1D, 0xF8, 0x1D, 0xF9, 0x1D, 0xFA, 0x1D, 0xFB, 0x1D, + 0xFC, 0x1D, 0xFD, 0x1D, 0xFE, 0x1D, 0xFF, 0x1D, 0x00, 0x1E, 0x00, 0x1E, 0x02, 0x1E, 0x02, 0x1E, + 0x04, 0x1E, 0x04, 0x1E, 0x06, 0x1E, 0x06, 0x1E, 0x08, 0x1E, 0x08, 0x1E, 0x0A, 0x1E, 0x0A, 0x1E, + 0x0C, 0x1E, 0x0C, 0x1E, 0x0E, 0x1E, 0x0E, 0x1E, 0x10, 0x1E, 0x10, 0x1E, 0x12, 0x1E, 0x12, 0x1E, + 0x14, 0x1E, 0x14, 0x1E, 0x16, 0x1E, 0x16, 0x1E, 0x18, 0x1E, 0x18, 0x1E, 0x1A, 0x1E, 0x1A, 0x1E, + 0x1C, 0x1E, 0x1C, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x20, 0x1E, 0x20, 0x1E, 0x22, 0x1E, 0x22, 0x1E, + 0x24, 0x1E, 0x24, 0x1E, 0x26, 0x1E, 0x26, 0x1E, 0x28, 0x1E, 0x28, 0x1E, 0x2A, 0x1E, 0x2A, 0x1E, + 0x2C, 0x1E, 0x2C, 0x1E, 0x2E, 0x1E, 0x2E, 0x1E, 0x30, 0x1E, 0x30, 0x1E, 0x32, 0x1E, 0x32, 0x1E, + 0x34, 0x1E, 0x34, 0x1E, 0x36, 0x1E, 0x36, 0x1E, 0x38, 0x1E, 0x38, 0x1E, 0x3A, 0x1E, 0x3A, 0x1E, + 0x3C, 0x1E, 0x3C, 0x1E, 0x3E, 0x1E, 0x3E, 0x1E, 0x40, 0x1E, 0x40, 0x1E, 0x42, 0x1E, 0x42, 0x1E, + 0x44, 0x1E, 0x44, 0x1E, 0x46, 0x1E, 0x46, 0x1E, 0x48, 0x1E, 0x48, 0x1E, 0x4A, 0x1E, 0x4A, 0x1E, + 0x4C, 0x1E, 0x4C, 0x1E, 0x4E, 0x1E, 0x4E, 0x1E, 0x50, 0x1E, 0x50, 0x1E, 0x52, 0x1E, 0x52, 0x1E, + 0x54, 0x1E, 0x54, 0x1E, 0x56, 0x1E, 0x56, 0x1E, 0x58, 0x1E, 0x58, 0x1E, 0x5A, 0x1E, 0x5A, 0x1E, + 0x5C, 0x1E, 0x5C, 0x1E, 0x5E, 0x1E, 0x5E, 0x1E, 0x60, 0x1E, 0x60, 0x1E, 0x62, 0x1E, 0x62, 0x1E, + 0x64, 0x1E, 0x64, 0x1E, 0x66, 0x1E, 0x66, 0x1E, 0x68, 0x1E, 0x68, 0x1E, 0x6A, 0x1E, 0x6A, 0x1E, + 0x6C, 0x1E, 0x6C, 0x1E, 0x6E, 0x1E, 0x6E, 0x1E, 0x70, 0x1E, 0x70, 0x1E, 0x72, 0x1E, 0x72, 0x1E, + 0x74, 0x1E, 0x74, 0x1E, 0x76, 0x1E, 0x76, 0x1E, 0x78, 0x1E, 0x78, 0x1E, 0x7A, 0x1E, 0x7A, 0x1E, + 0x7C, 0x1E, 0x7C, 0x1E, 0x7E, 0x1E, 0x7E, 0x1E, 0x80, 0x1E, 0x80, 0x1E, 0x82, 0x1E, 0x82, 0x1E, + 0x84, 0x1E, 0x84, 0x1E, 0x86, 0x1E, 0x86, 0x1E, 0x88, 0x1E, 0x88, 0x1E, 0x8A, 0x1E, 0x8A, 0x1E, + 0x8C, 0x1E, 0x8C, 0x1E, 0x8E, 0x1E, 0x8E, 0x1E, 0x90, 0x1E, 0x90, 0x1E, 0x92, 0x1E, 0x92, 0x1E, + 0x94, 0x1E, 0x94, 0x1E, 0x96, 0x1E, 0x97, 0x1E, 0x98, 0x1E, 0x99, 0x1E, 0x9A, 0x1E, 0x9B, 0x1E, + 0x9C, 0x1E, 0x9D, 0x1E, 0x9E, 0x1E, 0x9F, 0x1E, 0xA0, 0x1E, 0xA0, 0x1E, 0xA2, 0x1E, 0xA2, 0x1E, + 0xA4, 0x1E, 0xA4, 0x1E, 0xA6, 0x1E, 0xA6, 0x1E, 0xA8, 0x1E, 0xA8, 0x1E, 0xAA, 0x1E, 0xAA, 0x1E, + 0xAC, 0x1E, 0xAC, 0x1E, 0xAE, 0x1E, 0xAE, 0x1E, 0xB0, 0x1E, 0xB0, 0x1E, 0xB2, 0x1E, 0xB2, 0x1E, + 0xB4, 0x1E, 0xB4, 0x1E, 0xB6, 0x1E, 0xB6, 0x1E, 0xB8, 0x1E, 0xB8, 0x1E, 0xBA, 0x1E, 0xBA, 0x1E, + 0xBC, 0x1E, 0xBC, 0x1E, 0xBE, 0x1E, 0xBE, 0x1E, 0xC0, 0x1E, 0xC0, 0x1E, 0xC2, 0x1E, 0xC2, 0x1E, + 0xC4, 0x1E, 0xC4, 0x1E, 0xC6, 0x1E, 0xC6, 0x1E, 0xC8, 0x1E, 0xC8, 0x1E, 0xCA, 0x1E, 0xCA, 0x1E, + 0xCC, 0x1E, 0xCC, 0x1E, 0xCE, 0x1E, 0xCE, 0x1E, 0xD0, 0x1E, 0xD0, 0x1E, 0xD2, 0x1E, 0xD2, 0x1E, + 0xD4, 0x1E, 0xD4, 0x1E, 0xD6, 0x1E, 0xD6, 0x1E, 0xD8, 0x1E, 0xD8, 0x1E, 0xDA, 0x1E, 0xDA, 0x1E, + 0xDC, 0x1E, 0xDC, 0x1E, 0xDE, 0x1E, 0xDE, 0x1E, 0xE0, 0x1E, 0xE0, 0x1E, 0xE2, 0x1E, 0xE2, 0x1E, + 0xE4, 0x1E, 0xE4, 0x1E, 0xE6, 0x1E, 0xE6, 0x1E, 0xE8, 0x1E, 0xE8, 0x1E, 0xEA, 0x1E, 0xEA, 0x1E, + 0xEC, 0x1E, 0xEC, 0x1E, 0xEE, 0x1E, 0xEE, 0x1E, 0xF0, 0x1E, 0xF0, 0x1E, 0xF2, 0x1E, 0xF2, 0x1E, + 0xF4, 0x1E, 0xF4, 0x1E, 0xF6, 0x1E, 0xF6, 0x1E, 0xF8, 0x1E, 0xF8, 0x1E, 0xFA, 0x1E, 0xFB, 0x1E, + 0xFC, 0x1E, 0xFD, 0x1E, 0xFE, 0x1E, 0xFF, 0x1E, 0x08, 0x1F, 0x09, 0x1F, 0x0A, 0x1F, 0x0B, 0x1F, + 0x0C, 0x1F, 0x0D, 0x1F, 0x0E, 0x1F, 0x0F, 0x1F, 0x08, 0x1F, 0x09, 0x1F, 0x0A, 0x1F, 0x0B, 0x1F, + 0x0C, 0x1F, 0x0D, 0x1F, 0x0E, 0x1F, 0x0F, 0x1F, 0x18, 0x1F, 0x19, 0x1F, 0x1A, 0x1F, 0x1B, 0x1F, + 0x1C, 0x1F, 0x1D, 0x1F, 0x16, 0x1F, 0x17, 0x1F, 0x18, 0x1F, 0x19, 0x1F, 0x1A, 0x1F, 0x1B, 0x1F, + 0x1C, 0x1F, 0x1D, 0x1F, 0x1E, 0x1F, 0x1F, 0x1F, 0x28, 0x1F, 0x29, 0x1F, 0x2A, 0x1F, 0x2B, 0x1F, + 0x2C, 0x1F, 0x2D, 0x1F, 0x2E, 0x1F, 0x2F, 0x1F, 0x28, 0x1F, 0x29, 0x1F, 0x2A, 0x1F, 0x2B, 0x1F, + 0x2C, 0x1F, 0x2D, 0x1F, 0x2E, 0x1F, 0x2F, 0x1F, 0x38, 0x1F, 0x39, 0x1F, 0x3A, 0x1F, 0x3B, 0x1F, + 0x3C, 0x1F, 0x3D, 0x1F, 0x3E, 0x1F, 0x3F, 0x1F, 0x38, 0x1F, 0x39, 0x1F, 0x3A, 0x1F, 0x3B, 0x1F, + 0x3C, 0x1F, 0x3D, 0x1F, 0x3E, 0x1F, 0x3F, 0x1F, 0x48, 0x1F, 0x49, 0x1F, 0x4A, 0x1F, 0x4B, 0x1F, + 0x4C, 0x1F, 0x4D, 0x1F, 0x46, 0x1F, 0x47, 0x1F, 0x48, 0x1F, 0x49, 0x1F, 0x4A, 0x1F, 0x4B, 0x1F, + 0x4C, 0x1F, 0x4D, 0x1F, 0x4E, 0x1F, 0x4F, 0x1F, 0x50, 0x1F, 0x59, 0x1F, 0x52, 0x1F, 0x5B, 0x1F, + 0x54, 0x1F, 0x5D, 0x1F, 0x56, 0x1F, 0x5F, 0x1F, 0x58, 0x1F, 0x59, 0x1F, 0x5A, 0x1F, 0x5B, 0x1F, + 0x5C, 0x1F, 0x5D, 0x1F, 0x5E, 0x1F, 0x5F, 0x1F, 0x68, 0x1F, 0x69, 0x1F, 0x6A, 0x1F, 0x6B, 0x1F, + 0x6C, 0x1F, 0x6D, 0x1F, 0x6E, 0x1F, 0x6F, 0x1F, 0x68, 0x1F, 0x69, 0x1F, 0x6A, 0x1F, 0x6B, 0x1F, + 0x6C, 0x1F, 0x6D, 0x1F, 0x6E, 0x1F, 0x6F, 0x1F, 0xBA, 0x1F, 0xBB, 0x1F, 0xC8, 0x1F, 0xC9, 0x1F, + 0xCA, 0x1F, 0xCB, 0x1F, 0xDA, 0x1F, 0xDB, 0x1F, 0xF8, 0x1F, 0xF9, 0x1F, 0xEA, 0x1F, 0xEB, 0x1F, + 0xFA, 0x1F, 0xFB, 0x1F, 0x7E, 0x1F, 0x7F, 0x1F, 0x88, 0x1F, 0x89, 0x1F, 0x8A, 0x1F, 0x8B, 0x1F, + 0x8C, 0x1F, 0x8D, 0x1F, 0x8E, 0x1F, 0x8F, 0x1F, 0x88, 0x1F, 0x89, 0x1F, 0x8A, 0x1F, 0x8B, 0x1F, + 0x8C, 0x1F, 0x8D, 0x1F, 0x8E, 0x1F, 0x8F, 0x1F, 0x98, 0x1F, 0x99, 0x1F, 0x9A, 0x1F, 0x9B, 0x1F, + 0x9C, 0x1F, 0x9D, 0x1F, 0x9E, 0x1F, 0x9F, 0x1F, 0x98, 0x1F, 0x99, 0x1F, 0x9A, 0x1F, 0x9B, 0x1F, + 0x9C, 0x1F, 0x9D, 0x1F, 0x9E, 0x1F, 0x9F, 0x1F, 0xA8, 0x1F, 0xA9, 0x1F, 0xAA, 0x1F, 0xAB, 0x1F, + 0xAC, 0x1F, 0xAD, 0x1F, 0xAE, 0x1F, 0xAF, 0x1F, 0xA8, 0x1F, 0xA9, 0x1F, 0xAA, 0x1F, 0xAB, 0x1F, + 0xAC, 0x1F, 0xAD, 0x1F, 0xAE, 0x1F, 0xAF, 0x1F, 0xB8, 0x1F, 0xB9, 0x1F, 0xB2, 0x1F, 0xBC, 0x1F, + 0xB4, 0x1F, 0xB5, 0x1F, 0xB6, 0x1F, 0xB7, 0x1F, 0xB8, 0x1F, 0xB9, 0x1F, 0xBA, 0x1F, 0xBB, 0x1F, + 0xBC, 0x1F, 0xBD, 0x1F, 0xBE, 0x1F, 0xBF, 0x1F, 0xC0, 0x1F, 0xC1, 0x1F, 0xC2, 0x1F, 0xC3, 0x1F, + 0xC4, 0x1F, 0xC5, 0x1F, 0xC6, 0x1F, 0xC7, 0x1F, 0xC8, 0x1F, 0xC9, 0x1F, 0xCA, 0x1F, 0xCB, 0x1F, + 0xC3, 0x1F, 0xCD, 0x1F, 0xCE, 0x1F, 0xCF, 0x1F, 0xD8, 0x1F, 0xD9, 0x1F, 0xD2, 0x1F, 0xD3, 0x1F, + 0xD4, 0x1F, 0xD5, 0x1F, 0xD6, 0x1F, 0xD7, 0x1F, 0xD8, 0x1F, 0xD9, 0x1F, 0xDA, 0x1F, 0xDB, 0x1F, + 0xDC, 0x1F, 0xDD, 0x1F, 0xDE, 0x1F, 0xDF, 0x1F, 0xE8, 0x1F, 0xE9, 0x1F, 0xE2, 0x1F, 0xE3, 0x1F, + 0xE4, 0x1F, 0xEC, 0x1F, 0xE6, 0x1F, 0xE7, 0x1F, 0xE8, 0x1F, 0xE9, 0x1F, 0xEA, 0x1F, 0xEB, 0x1F, + 0xEC, 0x1F, 0xED, 0x1F, 0xEE, 0x1F, 0xEF, 0x1F, 0xF0, 0x1F, 0xF1, 0x1F, 0xF2, 0x1F, 0xF3, 0x1F, + 0xF4, 0x1F, 0xF5, 0x1F, 0xF6, 0x1F, 0xF7, 0x1F, 0xF8, 0x1F, 0xF9, 0x1F, 0xFA, 0x1F, 0xFB, 0x1F, + 0xF3, 0x1F, 0xFD, 0x1F, 0xFE, 0x1F, 0xFF, 0x1F, 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x20, + 0x04, 0x20, 0x05, 0x20, 0x06, 0x20, 0x07, 0x20, 0x08, 0x20, 0x09, 0x20, 0x0A, 0x20, 0x0B, 0x20, + 0x0C, 0x20, 0x0D, 0x20, 0x0E, 0x20, 0x0F, 0x20, 0x10, 0x20, 0x11, 0x20, 0x12, 0x20, 0x13, 0x20, + 0x14, 0x20, 0x15, 0x20, 0x16, 0x20, 0x17, 0x20, 0x18, 0x20, 0x19, 0x20, 0x1A, 0x20, 0x1B, 0x20, + 0x1C, 0x20, 0x1D, 0x20, 0x1E, 0x20, 0x1F, 0x20, 0x20, 0x20, 0x21, 0x20, 0x22, 0x20, 0x23, 0x20, + 0x24, 0x20, 0x25, 0x20, 0x26, 0x20, 0x27, 0x20, 0x28, 0x20, 0x29, 0x20, 0x2A, 0x20, 0x2B, 0x20, + 0x2C, 0x20, 0x2D, 0x20, 0x2E, 0x20, 0x2F, 0x20, 0x30, 0x20, 0x31, 0x20, 0x32, 0x20, 0x33, 0x20, + 0x34, 0x20, 0x35, 0x20, 0x36, 0x20, 0x37, 0x20, 0x38, 0x20, 0x39, 0x20, 0x3A, 0x20, 0x3B, 0x20, + 0x3C, 0x20, 0x3D, 0x20, 0x3E, 0x20, 0x3F, 0x20, 0x40, 0x20, 0x41, 0x20, 0x42, 0x20, 0x43, 0x20, + 0x44, 0x20, 0x45, 0x20, 0x46, 0x20, 0x47, 0x20, 0x48, 0x20, 0x49, 0x20, 0x4A, 0x20, 0x4B, 0x20, + 0x4C, 0x20, 0x4D, 0x20, 0x4E, 0x20, 0x4F, 0x20, 0x50, 0x20, 0x51, 0x20, 0x52, 0x20, 0x53, 0x20, + 0x54, 0x20, 0x55, 0x20, 0x56, 0x20, 0x57, 0x20, 0x58, 0x20, 0x59, 0x20, 0x5A, 0x20, 0x5B, 0x20, + 0x5C, 0x20, 0x5D, 0x20, 0x5E, 0x20, 0x5F, 0x20, 0x60, 0x20, 0x61, 0x20, 0x62, 0x20, 0x63, 0x20, + 0x64, 0x20, 0x65, 0x20, 0x66, 0x20, 0x67, 0x20, 0x68, 0x20, 0x69, 0x20, 0x6A, 0x20, 0x6B, 0x20, + 0x6C, 0x20, 0x6D, 0x20, 0x6E, 0x20, 0x6F, 0x20, 0x70, 0x20, 0x71, 0x20, 0x72, 0x20, 0x73, 0x20, + 0x74, 0x20, 0x75, 0x20, 0x76, 0x20, 0x77, 0x20, 0x78, 0x20, 0x79, 0x20, 0x7A, 0x20, 0x7B, 0x20, + 0x7C, 0x20, 0x7D, 0x20, 0x7E, 0x20, 0x7F, 0x20, 0x80, 0x20, 0x81, 0x20, 0x82, 0x20, 0x83, 0x20, + 0x84, 0x20, 0x85, 0x20, 0x86, 0x20, 0x87, 0x20, 0x88, 0x20, 0x89, 0x20, 0x8A, 0x20, 0x8B, 0x20, + 0x8C, 0x20, 0x8D, 0x20, 0x8E, 0x20, 0x8F, 0x20, 0x90, 0x20, 0x91, 0x20, 0x92, 0x20, 0x93, 0x20, + 0x94, 0x20, 0x95, 0x20, 0x96, 0x20, 0x97, 0x20, 0x98, 0x20, 0x99, 0x20, 0x9A, 0x20, 0x9B, 0x20, + 0x9C, 0x20, 0x9D, 0x20, 0x9E, 0x20, 0x9F, 0x20, 0xA0, 0x20, 0xA1, 0x20, 0xA2, 0x20, 0xA3, 0x20, + 0xA4, 0x20, 0xA5, 0x20, 0xA6, 0x20, 0xA7, 0x20, 0xA8, 0x20, 0xA9, 0x20, 0xAA, 0x20, 0xAB, 0x20, + 0xAC, 0x20, 0xAD, 0x20, 0xAE, 0x20, 0xAF, 0x20, 0xB0, 0x20, 0xB1, 0x20, 0xB2, 0x20, 0xB3, 0x20, + 0xB4, 0x20, 0xB5, 0x20, 0xB6, 0x20, 0xB7, 0x20, 0xB8, 0x20, 0xB9, 0x20, 0xBA, 0x20, 0xBB, 0x20, + 0xBC, 0x20, 0xBD, 0x20, 0xBE, 0x20, 0xBF, 0x20, 0xC0, 0x20, 0xC1, 0x20, 0xC2, 0x20, 0xC3, 0x20, + 0xC4, 0x20, 0xC5, 0x20, 0xC6, 0x20, 0xC7, 0x20, 0xC8, 0x20, 0xC9, 0x20, 0xCA, 0x20, 0xCB, 0x20, + 0xCC, 0x20, 0xCD, 0x20, 0xCE, 0x20, 0xCF, 0x20, 0xD0, 0x20, 0xD1, 0x20, 0xD2, 0x20, 0xD3, 0x20, + 0xD4, 0x20, 0xD5, 0x20, 0xD6, 0x20, 0xD7, 0x20, 0xD8, 0x20, 0xD9, 0x20, 0xDA, 0x20, 0xDB, 0x20, + 0xDC, 0x20, 0xDD, 0x20, 0xDE, 0x20, 0xDF, 0x20, 0xE0, 0x20, 0xE1, 0x20, 0xE2, 0x20, 0xE3, 0x20, + 0xE4, 0x20, 0xE5, 0x20, 0xE6, 0x20, 0xE7, 0x20, 0xE8, 0x20, 0xE9, 0x20, 0xEA, 0x20, 0xEB, 0x20, + 0xEC, 0x20, 0xED, 0x20, 0xEE, 0x20, 0xEF, 0x20, 0xF0, 0x20, 0xF1, 0x20, 0xF2, 0x20, 0xF3, 0x20, + 0xF4, 0x20, 0xF5, 0x20, 0xF6, 0x20, 0xF7, 0x20, 0xF8, 0x20, 0xF9, 0x20, 0xFA, 0x20, 0xFB, 0x20, + 0xFC, 0x20, 0xFD, 0x20, 0xFE, 0x20, 0xFF, 0x20, 0x00, 0x21, 0x01, 0x21, 0x02, 0x21, 0x03, 0x21, + 0x04, 0x21, 0x05, 0x21, 0x06, 0x21, 0x07, 0x21, 0x08, 0x21, 0x09, 0x21, 0x0A, 0x21, 0x0B, 0x21, + 0x0C, 0x21, 0x0D, 0x21, 0x0E, 0x21, 0x0F, 0x21, 0x10, 0x21, 0x11, 0x21, 0x12, 0x21, 0x13, 0x21, + 0x14, 0x21, 0x15, 0x21, 0x16, 0x21, 0x17, 0x21, 0x18, 0x21, 0x19, 0x21, 0x1A, 0x21, 0x1B, 0x21, + 0x1C, 0x21, 0x1D, 0x21, 0x1E, 0x21, 0x1F, 0x21, 0x20, 0x21, 0x21, 0x21, 0x22, 0x21, 0x23, 0x21, + 0x24, 0x21, 0x25, 0x21, 0x26, 0x21, 0x27, 0x21, 0x28, 0x21, 0x29, 0x21, 0x2A, 0x21, 0x2B, 0x21, + 0x2C, 0x21, 0x2D, 0x21, 0x2E, 0x21, 0x2F, 0x21, 0x30, 0x21, 0x31, 0x21, 0x32, 0x21, 0x33, 0x21, + 0x34, 0x21, 0x35, 0x21, 0x36, 0x21, 0x37, 0x21, 0x38, 0x21, 0x39, 0x21, 0x3A, 0x21, 0x3B, 0x21, + 0x3C, 0x21, 0x3D, 0x21, 0x3E, 0x21, 0x3F, 0x21, 0x40, 0x21, 0x41, 0x21, 0x42, 0x21, 0x43, 0x21, + 0x44, 0x21, 0x45, 0x21, 0x46, 0x21, 0x47, 0x21, 0x48, 0x21, 0x49, 0x21, 0x4A, 0x21, 0x4B, 0x21, + 0x4C, 0x21, 0x4D, 0x21, 0x32, 0x21, 0x4F, 0x21, 0x50, 0x21, 0x51, 0x21, 0x52, 0x21, 0x53, 0x21, + 0x54, 0x21, 0x55, 0x21, 0x56, 0x21, 0x57, 0x21, 0x58, 0x21, 0x59, 0x21, 0x5A, 0x21, 0x5B, 0x21, + 0x5C, 0x21, 0x5D, 0x21, 0x5E, 0x21, 0x5F, 0x21, 0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21, + 0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21, 0x68, 0x21, 0x69, 0x21, 0x6A, 0x21, 0x6B, 0x21, + 0x6C, 0x21, 0x6D, 0x21, 0x6E, 0x21, 0x6F, 0x21, 0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21, + 0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21, 0x68, 0x21, 0x69, 0x21, 0x6A, 0x21, 0x6B, 0x21, + 0x6C, 0x21, 0x6D, 0x21, 0x6E, 0x21, 0x6F, 0x21, 0x80, 0x21, 0x81, 0x21, 0x82, 0x21, 0x83, 0x21, + 0x83, 0x21, 0xFF, 0xFF, 0x4B, 0x03, 0xB6, 0x24, 0xB7, 0x24, 0xB8, 0x24, 0xB9, 0x24, 0xBA, 0x24, + 0xBB, 0x24, 0xBC, 0x24, 0xBD, 0x24, 0xBE, 0x24, 0xBF, 0x24, 0xC0, 0x24, 0xC1, 0x24, 0xC2, 0x24, + 0xC3, 0x24, 0xC4, 0x24, 0xC5, 0x24, 0xC6, 0x24, 0xC7, 0x24, 0xC8, 0x24, 0xC9, 0x24, 0xCA, 0x24, + 0xCB, 0x24, 0xCC, 0x24, 0xCD, 0x24, 0xCE, 0x24, 0xCF, 0x24, 0xFF, 0xFF, 0x46, 0x07, 0x00, 0x2C, + 0x01, 0x2C, 0x02, 0x2C, 0x03, 0x2C, 0x04, 0x2C, 0x05, 0x2C, 0x06, 0x2C, 0x07, 0x2C, 0x08, 0x2C, + 0x09, 0x2C, 0x0A, 0x2C, 0x0B, 0x2C, 0x0C, 0x2C, 0x0D, 0x2C, 0x0E, 0x2C, 0x0F, 0x2C, 0x10, 0x2C, + 0x11, 0x2C, 0x12, 0x2C, 0x13, 0x2C, 0x14, 0x2C, 0x15, 0x2C, 0x16, 0x2C, 0x17, 0x2C, 0x18, 0x2C, + 0x19, 0x2C, 0x1A, 0x2C, 0x1B, 0x2C, 0x1C, 0x2C, 0x1D, 0x2C, 0x1E, 0x2C, 0x1F, 0x2C, 0x20, 0x2C, + 0x21, 0x2C, 0x22, 0x2C, 0x23, 0x2C, 0x24, 0x2C, 0x25, 0x2C, 0x26, 0x2C, 0x27, 0x2C, 0x28, 0x2C, + 0x29, 0x2C, 0x2A, 0x2C, 0x2B, 0x2C, 0x2C, 0x2C, 0x2D, 0x2C, 0x2E, 0x2C, 0x5F, 0x2C, 0x60, 0x2C, + 0x60, 0x2C, 0x62, 0x2C, 0x63, 0x2C, 0x64, 0x2C, 0x65, 0x2C, 0x66, 0x2C, 0x67, 0x2C, 0x67, 0x2C, + 0x69, 0x2C, 0x69, 0x2C, 0x6B, 0x2C, 0x6B, 0x2C, 0x6D, 0x2C, 0x6E, 0x2C, 0x6F, 0x2C, 0x70, 0x2C, + 0x71, 0x2C, 0x72, 0x2C, 0x73, 0x2C, 0x74, 0x2C, 0x75, 0x2C, 0x75, 0x2C, 0x77, 0x2C, 0x78, 0x2C, + 0x79, 0x2C, 0x7A, 0x2C, 0x7B, 0x2C, 0x7C, 0x2C, 0x7D, 0x2C, 0x7E, 0x2C, 0x7F, 0x2C, 0x80, 0x2C, + 0x80, 0x2C, 0x82, 0x2C, 0x82, 0x2C, 0x84, 0x2C, 0x84, 0x2C, 0x86, 0x2C, 0x86, 0x2C, 0x88, 0x2C, + 0x88, 0x2C, 0x8A, 0x2C, 0x8A, 0x2C, 0x8C, 0x2C, 0x8C, 0x2C, 0x8E, 0x2C, 0x8E, 0x2C, 0x90, 0x2C, + 0x90, 0x2C, 0x92, 0x2C, 0x92, 0x2C, 0x94, 0x2C, 0x94, 0x2C, 0x96, 0x2C, 0x96, 0x2C, 0x98, 0x2C, + 0x98, 0x2C, 0x9A, 0x2C, 0x9A, 0x2C, 0x9C, 0x2C, 0x9C, 0x2C, 0x9E, 0x2C, 0x9E, 0x2C, 0xA0, 0x2C, + 0xA0, 0x2C, 0xA2, 0x2C, 0xA2, 0x2C, 0xA4, 0x2C, 0xA4, 0x2C, 0xA6, 0x2C, 0xA6, 0x2C, 0xA8, 0x2C, + 0xA8, 0x2C, 0xAA, 0x2C, 0xAA, 0x2C, 0xAC, 0x2C, 0xAC, 0x2C, 0xAE, 0x2C, 0xAE, 0x2C, 0xB0, 0x2C, + 0xB0, 0x2C, 0xB2, 0x2C, 0xB2, 0x2C, 0xB4, 0x2C, 0xB4, 0x2C, 0xB6, 0x2C, 0xB6, 0x2C, 0xB8, 0x2C, + 0xB8, 0x2C, 0xBA, 0x2C, 0xBA, 0x2C, 0xBC, 0x2C, 0xBC, 0x2C, 0xBE, 0x2C, 0xBE, 0x2C, 0xC0, 0x2C, + 0xC0, 0x2C, 0xC2, 0x2C, 0xC2, 0x2C, 0xC4, 0x2C, 0xC4, 0x2C, 0xC6, 0x2C, 0xC6, 0x2C, 0xC8, 0x2C, + 0xC8, 0x2C, 0xCA, 0x2C, 0xCA, 0x2C, 0xCC, 0x2C, 0xCC, 0x2C, 0xCE, 0x2C, 0xCE, 0x2C, 0xD0, 0x2C, + 0xD0, 0x2C, 0xD2, 0x2C, 0xD2, 0x2C, 0xD4, 0x2C, 0xD4, 0x2C, 0xD6, 0x2C, 0xD6, 0x2C, 0xD8, 0x2C, + 0xD8, 0x2C, 0xDA, 0x2C, 0xDA, 0x2C, 0xDC, 0x2C, 0xDC, 0x2C, 0xDE, 0x2C, 0xDE, 0x2C, 0xE0, 0x2C, + 0xE0, 0x2C, 0xE2, 0x2C, 0xE2, 0x2C, 0xE4, 0x2C, 0xE5, 0x2C, 0xE6, 0x2C, 0xE7, 0x2C, 0xE8, 0x2C, + 0xE9, 0x2C, 0xEA, 0x2C, 0xEB, 0x2C, 0xEC, 0x2C, 0xED, 0x2C, 0xEE, 0x2C, 0xEF, 0x2C, 0xF0, 0x2C, + 0xF1, 0x2C, 0xF2, 0x2C, 0xF3, 0x2C, 0xF4, 0x2C, 0xF5, 0x2C, 0xF6, 0x2C, 0xF7, 0x2C, 0xF8, 0x2C, + 0xF9, 0x2C, 0xFA, 0x2C, 0xFB, 0x2C, 0xFC, 0x2C, 0xFD, 0x2C, 0xFE, 0x2C, 0xFF, 0x2C, 0xA0, 0x10, + 0xA1, 0x10, 0xA2, 0x10, 0xA3, 0x10, 0xA4, 0x10, 0xA5, 0x10, 0xA6, 0x10, 0xA7, 0x10, 0xA8, 0x10, + 0xA9, 0x10, 0xAA, 0x10, 0xAB, 0x10, 0xAC, 0x10, 0xAD, 0x10, 0xAE, 0x10, 0xAF, 0x10, 0xB0, 0x10, + 0xB1, 0x10, 0xB2, 0x10, 0xB3, 0x10, 0xB4, 0x10, 0xB5, 0x10, 0xB6, 0x10, 0xB7, 0x10, 0xB8, 0x10, + 0xB9, 0x10, 0xBA, 0x10, 0xBB, 0x10, 0xBC, 0x10, 0xBD, 0x10, 0xBE, 0x10, 0xBF, 0x10, 0xC0, 0x10, + 0xC1, 0x10, 0xC2, 0x10, 0xC3, 0x10, 0xC4, 0x10, 0xC5, 0x10, 0xFF, 0xFF, 0x1B, 0xD2, 0x21, 0xFF, + 0x22, 0xFF, 0x23, 0xFF, 0x24, 0xFF, 0x25, 0xFF, 0x26, 0xFF, 0x27, 0xFF, 0x28, 0xFF, 0x29, 0xFF, + 0x2A, 0xFF, 0x2B, 0xFF, 0x2C, 0xFF, 0x2D, 0xFF, 0x2E, 0xFF, 0x2F, 0xFF, 0x30, 0xFF, 0x31, 0xFF, + 0x32, 0xFF, 0x33, 0xFF, 0x34, 0xFF, 0x35, 0xFF, 0x36, 0xFF, 0x37, 0xFF, 0x38, 0xFF, 0x39, 0xFF, + 0x3A, 0xFF, 0x5B, 0xFF, 0x5C, 0xFF, 0x5D, 0xFF, 0x5E, 0xFF, 0x5F, 0xFF, 0x60, 0xFF, 0x61, 0xFF, + 0x62, 0xFF, 0x63, 0xFF, 0x64, 0xFF, 0x65, 0xFF, 0x66, 0xFF, 0x67, 0xFF, 0x68, 0xFF, 0x69, 0xFF, + 0x6A, 0xFF, 0x6B, 0xFF, 0x6C, 0xFF, 0x6D, 0xFF, 0x6E, 0xFF, 0x6F, 0xFF, 0x70, 0xFF, 0x71, 0xFF, + 0x72, 0xFF, 0x73, 0xFF, 0x74, 0xFF, 0x75, 0xFF, 0x76, 0xFF, 0x77, 0xFF, 0x78, 0xFF, 0x79, 0xFF, + 0x7A, 0xFF, 0x7B, 0xFF, 0x7C, 0xFF, 0x7D, 0xFF, 0x7E, 0xFF, 0x7F, 0xFF, 0x80, 0xFF, 0x81, 0xFF, + 0x82, 0xFF, 0x83, 0xFF, 0x84, 0xFF, 0x85, 0xFF, 0x86, 0xFF, 0x87, 0xFF, 0x88, 0xFF, 0x89, 0xFF, + 0x8A, 0xFF, 0x8B, 0xFF, 0x8C, 0xFF, 0x8D, 0xFF, 0x8E, 0xFF, 0x8F, 0xFF, 0x90, 0xFF, 0x91, 0xFF, + 0x92, 0xFF, 0x93, 0xFF, 0x94, 0xFF, 0x95, 0xFF, 0x96, 0xFF, 0x97, 0xFF, 0x98, 0xFF, 0x99, 0xFF, + 0x9A, 0xFF, 0x9B, 0xFF, 0x9C, 0xFF, 0x9D, 0xFF, 0x9E, 0xFF, 0x9F, 0xFF, 0xA0, 0xFF, 0xA1, 0xFF, + 0xA2, 0xFF, 0xA3, 0xFF, 0xA4, 0xFF, 0xA5, 0xFF, 0xA6, 0xFF, 0xA7, 0xFF, 0xA8, 0xFF, 0xA9, 0xFF, + 0xAA, 0xFF, 0xAB, 0xFF, 0xAC, 0xFF, 0xAD, 0xFF, 0xAE, 0xFF, 0xAF, 0xFF, 0xB0, 0xFF, 0xB1, 0xFF, + 0xB2, 0xFF, 0xB3, 0xFF, 0xB4, 0xFF, 0xB5, 0xFF, 0xB6, 0xFF, 0xB7, 0xFF, 0xB8, 0xFF, 0xB9, 0xFF, + 0xBA, 0xFF, 0xBB, 0xFF, 0xBC, 0xFF, 0xBD, 0xFF, 0xBE, 0xFF, 0xBF, 0xFF, 0xC0, 0xFF, 0xC1, 0xFF, + 0xC2, 0xFF, 0xC3, 0xFF, 0xC4, 0xFF, 0xC5, 0xFF, 0xC6, 0xFF, 0xC7, 0xFF, 0xC8, 0xFF, 0xC9, 0xFF, + 0xCA, 0xFF, 0xCB, 0xFF, 0xCC, 0xFF, 0xCD, 0xFF, 0xCE, 0xFF, 0xCF, 0xFF, 0xD0, 0xFF, 0xD1, 0xFF, + 0xD2, 0xFF, 0xD3, 0xFF, 0xD4, 0xFF, 0xD5, 0xFF, 0xD6, 0xFF, 0xD7, 0xFF, 0xD8, 0xFF, 0xD9, 0xFF, + 0xDA, 0xFF, 0xDB, 0xFF, 0xDC, 0xFF, 0xDD, 0xFF, 0xDE, 0xFF, 0xDF, 0xFF, 0xE0, 0xFF, 0xE1, 0xFF, + 0xE2, 0xFF, 0xE3, 0xFF, 0xE4, 0xFF, 0xE5, 0xFF, 0xE6, 0xFF, 0xE7, 0xFF, 0xE8, 0xFF, 0xE9, 0xFF, + 0xEA, 0xFF, 0xEB, 0xFF, 0xEC, 0xFF, 0xED, 0xFF, 0xEE, 0xFF, 0xEF, 0xFF, 0xF0, 0xFF, 0xF1, 0xFF, + 0xF2, 0xFF, 0xF3, 0xFF, 0xF4, 0xFF, 0xF5, 0xFF, 0xF6, 0xFF, 0xF7, 0xFF, 0xF8, 0xFF, 0xF9, 0xFF, + 0xFA, 0xFF, 0xFB, 0xFF, 0xFC, 0xFF, 0xFD, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF +}; diff --git a/fs/exfat/exfat_version.h b/fs/exfat/exfat_version.h new file mode 100644 index 00000000..a93fa46b --- /dev/null +++ b/fs/exfat/exfat_version.h @@ -0,0 +1,19 @@ +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_version.h */ +/* PURPOSE : exFAT File Manager */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY */ +/* */ +/* - 2012.02.10 : Release Version 1.1.0 */ +/* - 2012.04.02 : P1 : Change Module License to Samsung Proprietary */ +/* - 2012.06.07 : P2 : Fixed incorrect filename problem */ +/* */ +/************************************************************************/ + +#define EXFAT_VERSION "1.2.9" diff --git a/fs/exfat/extent.c b/fs/exfat/extent.c new file mode 100644 index 00000000..99d116cb --- /dev/null +++ b/fs/exfat/extent.c @@ -0,0 +1,329 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * extent.c: Improve the performance of traversing fat chain + */ + +/* + * linux/fs/fat/cache.c + * + * Written 1992,1993 by Werner Almesberger + * + * Mar 1999. AV. Changed cache, so that it uses the starting cluster instead + * of inode number. + * May 1999. AV. Fixed the bogosity with FAT32 (read "FAT28"). Fscking lusers. + */ + +#include +#include "exfat.h" +#include "core.h" + +#define EXTENT_CACHE_VALID 0 +/* this must be > 0. */ +#define EXTENT_MAX_CACHE 16 + +struct extent_cache { + struct list_head cache_list; + u32 nr_contig; /* number of contiguous clusters */ + u32 fcluster; /* cluster number in the file. */ + u32 dcluster; /* cluster number on disk. */ +}; + +struct extent_cache_id { + u32 id; + u32 nr_contig; + u32 fcluster; + u32 dcluster; +}; + +static struct kmem_cache *extent_cache_cachep; + +static void init_once(void *c) +{ + struct extent_cache *cache = (struct extent_cache *)c; + + INIT_LIST_HEAD(&cache->cache_list); +} + +s32 exfat_extent_cache_init(void) +{ + extent_cache_cachep = kmem_cache_create("exfat_extent_cache", + sizeof(struct extent_cache), + 0, SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, + init_once); + if (!extent_cache_cachep) + return -ENOMEM; + return 0; +} + +void exfat_extent_cache_shutdown(void) +{ + if (!extent_cache_cachep) + return; + kmem_cache_destroy(extent_cache_cachep); +} + +void exfat_extent_cache_init_inode(struct inode *inode) +{ + EXTENT_T *extent = &(EXFAT_I(inode)->fid.extent); + + spin_lock_init(&extent->cache_lru_lock); + extent->nr_caches = 0; + extent->cache_valid_id = EXTENT_CACHE_VALID + 1; + INIT_LIST_HEAD(&extent->cache_lru); +} + +static inline struct extent_cache *extent_cache_alloc(void) +{ + return kmem_cache_alloc(extent_cache_cachep, GFP_NOFS); +} + +static inline void extent_cache_free(struct extent_cache *cache) +{ + BUG_ON(!list_empty(&cache->cache_list)); + kmem_cache_free(extent_cache_cachep, cache); +} + +static inline void extent_cache_update_lru(struct inode *inode, + struct extent_cache *cache) +{ + EXTENT_T *extent = &(EXFAT_I(inode)->fid.extent); + + if (extent->cache_lru.next != &cache->cache_list) + list_move(&cache->cache_list, &extent->cache_lru); +} + +static u32 extent_cache_lookup(struct inode *inode, u32 fclus, + struct extent_cache_id *cid, + u32 *cached_fclus, u32 *cached_dclus) +{ + EXTENT_T *extent = &(EXFAT_I(inode)->fid.extent); + + static struct extent_cache nohit = { .fcluster = 0, }; + + struct extent_cache *hit = &nohit, *p; + u32 offset = CLUS_EOF; + + spin_lock(&extent->cache_lru_lock); + list_for_each_entry(p, &extent->cache_lru, cache_list) { + /* Find the cache of "fclus" or nearest cache. */ + if (p->fcluster <= fclus && hit->fcluster < p->fcluster) { + hit = p; + if ((hit->fcluster + hit->nr_contig) < fclus) { + offset = hit->nr_contig; + } else { + offset = fclus - hit->fcluster; + break; + } + } + } + if (hit != &nohit) { + extent_cache_update_lru(inode, hit); + + cid->id = extent->cache_valid_id; + cid->nr_contig = hit->nr_contig; + cid->fcluster = hit->fcluster; + cid->dcluster = hit->dcluster; + *cached_fclus = cid->fcluster + offset; + *cached_dclus = cid->dcluster + offset; + } + spin_unlock(&extent->cache_lru_lock); + + return offset; +} + +static struct extent_cache *extent_cache_merge(struct inode *inode, + struct extent_cache_id *new) +{ + EXTENT_T *extent = &(EXFAT_I(inode)->fid.extent); + + struct extent_cache *p; + + list_for_each_entry(p, &extent->cache_lru, cache_list) { + /* Find the same part as "new" in cluster-chain. */ + if (p->fcluster == new->fcluster) { + ASSERT(p->dcluster == new->dcluster); + if (new->nr_contig > p->nr_contig) + p->nr_contig = new->nr_contig; + return p; + } + } + return NULL; +} + +static void extent_cache_add(struct inode *inode, struct extent_cache_id *new) +{ + EXTENT_T *extent = &(EXFAT_I(inode)->fid.extent); + + struct extent_cache *cache, *tmp; + + if (new->fcluster == -1) /* dummy cache */ + return; + + spin_lock(&extent->cache_lru_lock); + if (new->id != EXTENT_CACHE_VALID && + new->id != extent->cache_valid_id) + goto out; /* this cache was invalidated */ + + cache = extent_cache_merge(inode, new); + if (cache == NULL) { + if (extent->nr_caches < EXTENT_MAX_CACHE) { + extent->nr_caches++; + spin_unlock(&extent->cache_lru_lock); + + tmp = extent_cache_alloc(); + if (!tmp) { + spin_lock(&extent->cache_lru_lock); + extent->nr_caches--; + spin_unlock(&extent->cache_lru_lock); + return; + } + + spin_lock(&extent->cache_lru_lock); + cache = extent_cache_merge(inode, new); + if (cache != NULL) { + extent->nr_caches--; + extent_cache_free(tmp); + goto out_update_lru; + } + cache = tmp; + } else { + struct list_head *p = extent->cache_lru.prev; + cache = list_entry(p, struct extent_cache, cache_list); + } + cache->fcluster = new->fcluster; + cache->dcluster = new->dcluster; + cache->nr_contig = new->nr_contig; + } +out_update_lru: + extent_cache_update_lru(inode, cache); +out: + spin_unlock(&extent->cache_lru_lock); +} + +/* + * Cache invalidation occurs rarely, thus the LRU chain is not updated. It + * fixes itself after a while. + */ +static void __exfat_extent_cache_inval_inode(struct inode *inode) +{ + EXTENT_T *extent = &(EXFAT_I(inode)->fid.extent); + struct extent_cache *cache; + + while (!list_empty(&extent->cache_lru)) { + cache = list_entry(extent->cache_lru.next, + struct extent_cache, cache_list); + list_del_init(&cache->cache_list); + extent->nr_caches--; + extent_cache_free(cache); + } + /* Update. The copy of caches before this id is discarded. */ + extent->cache_valid_id++; + if (extent->cache_valid_id == EXTENT_CACHE_VALID) + extent->cache_valid_id++; +} + +void exfat_extent_cache_inval_inode(struct inode *inode) +{ + EXTENT_T *extent = &(EXFAT_I(inode)->fid.extent); + + spin_lock(&extent->cache_lru_lock); + __exfat_extent_cache_inval_inode(inode); + spin_unlock(&extent->cache_lru_lock); +} + +static inline s32 cache_contiguous(struct extent_cache_id *cid, u32 dclus) +{ + cid->nr_contig++; + return ((cid->dcluster + cid->nr_contig) == dclus); +} + +static inline void cache_init(struct extent_cache_id *cid, u32 fclus, u32 dclus) +{ + cid->id = EXTENT_CACHE_VALID; + cid->fcluster = fclus; + cid->dcluster = dclus; + cid->nr_contig = 0; +} + +s32 exfat_extent_get_clus(struct inode *inode, u32 cluster, u32 *fclus, + u32 *dclus, u32 *last_dclus, s32 allow_eof) +{ + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + u32 limit = fsi->num_clusters; + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + struct extent_cache_id cid; + u32 content; + + /* FOR GRACEFUL ERROR HANDLING */ + if (IS_CLUS_FREE(fid->start_clu)) { + exfat_fs_error(sb, "invalid access to " + "extent cache (entry 0x%08x)", fid->start_clu); + ASSERT(0); + return -EIO; + } + + *fclus = 0; + *dclus = fid->start_clu; + *last_dclus = *dclus; + + /* + * Don`t use extent_cache if zero offset or non-cluster allocation + */ + if ((cluster == 0) || IS_CLUS_EOF(*dclus)) + return 0; + + cache_init(&cid, CLUS_EOF, CLUS_EOF); + + if (extent_cache_lookup(inode, cluster, &cid, fclus, dclus) == CLUS_EOF) { + /* + * dummy, always not contiguous + * This is reinitialized by cache_init(), later. + */ + ASSERT((cid.id == EXTENT_CACHE_VALID) + && (cid.fcluster == CLUS_EOF) + && (cid.dcluster == CLUS_EOF) + && (cid.nr_contig == 0)); + } + + if (*fclus == cluster) + return 0; + + while (*fclus < cluster) { + /* prevent the infinite loop of cluster chain */ + if (*fclus > limit) { + exfat_fs_error(sb, + "%s: detected the cluster chain loop" + " (i_pos %u)", __func__, + (*fclus)); + return -EIO; + } + + if (exfat_ent_get_safe(sb, *dclus, &content)) + return -EIO; + + *last_dclus = *dclus; + *dclus = content; + (*fclus)++; + + if (IS_CLUS_EOF(content)) { + if (!allow_eof) { + exfat_fs_error(sb, + "%s: invalid cluster chain (i_pos %u," + "last_clus 0x%08x is EOF)", + __func__, *fclus, (*last_dclus)); + return -EIO; + } + + break; + } + + if (!cache_contiguous(&cid, *dclus)) + cache_init(&cid, *fclus, *dclus); + } + + extent_cache_add(inode, &cid); + return 0; +} diff --git a/fs/exfat/fatent.c b/fs/exfat/fatent.c new file mode 100644 index 00000000..8b87d03c --- /dev/null +++ b/fs/exfat/fatent.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * fatent.c: exFAT FAT entry manager + */ + +#include + +#include "exfat.h" +#include "core.h" + +/* All buffer structures are protected w/ fsi->v_sem */ + +static inline bool is_reserved_clus(u32 clus) +{ + if (IS_CLUS_FREE(clus)) + return true; + if (IS_CLUS_EOF(clus)) + return true; + if (IS_CLUS_BAD(clus)) + return true; + return false; +} + +static inline bool is_valid_clus(FS_INFO_T *fsi, u32 clus) +{ + if (clus < CLUS_BASE || fsi->num_clusters <= clus) + return false; + return true; +} + +s32 exfat_ent_get(struct super_block *sb, u32 loc, u32 *content) +{ + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + u32 off, _content; + u64 sec; + u8 *fat_sector; + + if (!is_valid_clus(fsi, loc)) { + exfat_fs_error(sb, "invalid access to FAT (entry 0x%08x)", loc); + return -EIO; + } + + sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-2)); + off = (loc << 2) & (u32)(sb->s_blocksize - 1); + + fat_sector = exfat_fcache_getblk(sb, sec); + if (!fat_sector) { + exfat_fs_error(sb, "failed to access to FAT " + "(entry 0x%08x)", loc); + return -EIO; + } + + _content = le32_to_cpu(*(__le32 *)(&fat_sector[off])); + + /* remap reserved clusters to simplify code */ + if (_content >= CLUSTER_32(0xFFFFFFF8)) + _content = CLUS_EOF; + + *content = CLUSTER_32(_content); + + if (!is_reserved_clus(*content) && !is_valid_clus(fsi, *content)) { + exfat_fs_error(sb, "invalid access to FAT (entry 0x%08x) " + "bogus content (0x%08x)", loc, *content); + return -EIO; + } + + return 0; +} + +s32 exfat_ent_set(struct super_block *sb, u32 loc, u32 content) +{ + u32 off; + u64 sec; + u8 *fat_sector; + __le32 *fat_entry; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-2)); + off = (loc << 2) & (u32)(sb->s_blocksize - 1); + + fat_sector = exfat_fcache_getblk(sb, sec); + if (!fat_sector) + return -EIO; + + fat_entry = (__le32 *)&(fat_sector[off]); + *fat_entry = cpu_to_le32(content); + + return exfat_fcache_modify(sb, sec); +} + +s32 exfat_ent_get_safe(struct super_block *sb, u32 loc, u32 *content) +{ + s32 err = exfat_ent_get(sb, loc, content); + + if (err) + return err; + + if (IS_CLUS_FREE(*content)) { + exfat_fs_error(sb, "invalid access to FAT free cluster " + "(entry 0x%08x)", loc); + return -EIO; + } + + if (IS_CLUS_BAD(*content)) { + exfat_fs_error(sb, "invalid access to FAT bad cluster " + "(entry 0x%08x)", loc); + return -EIO; + } + + return 0; +} diff --git a/fs/exfat/misc.c b/fs/exfat/misc.c new file mode 100644 index 00000000..9cc95e9d --- /dev/null +++ b/fs/exfat/misc.c @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * misc.c: Helper function for checksum and handling exFAT errors + */ + +/* + * linux/fs/fat/misc.c + * + * Written 1992,1993 by Werner Almesberger + * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980 + * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru) + */ + +#include +#include +#include +#include +#include "exfat.h" +#include "version.h" + +#ifdef CONFIG_EXFAT_UEVENT +static struct kobject exfat_uevent_kobj; + +int exfat_uevent_init(struct kset *exfat_kset) +{ + int err; + struct kobj_type *ktype = get_ktype(&exfat_kset->kobj); + + exfat_uevent_kobj.kset = exfat_kset; + err = kobject_init_and_add(&exfat_uevent_kobj, ktype, NULL, "uevent"); + if (err) + pr_err("[EXFAT] Unable to create exfat uevent kobj\n"); + + return err; +} + +void exfat_uevent_uninit(void) +{ + kobject_del(&exfat_uevent_kobj); + memset(&exfat_uevent_kobj, 0, sizeof(struct kobject)); +} + +void exfat_uevent_ro_remount(struct super_block *sb) +{ + struct block_device *bdev = sb->s_bdev; + dev_t bd_dev = bdev ? bdev->bd_dev : 0; + + char major[16], minor[16]; + char *envp[] = { major, minor, NULL }; + + snprintf(major, sizeof(major), "MAJOR=%d", MAJOR(bd_dev)); + snprintf(minor, sizeof(minor), "MINOR=%d", MINOR(bd_dev)); + + kobject_uevent_env(&exfat_uevent_kobj, KOBJ_CHANGE, envp); +} +#endif + +/* + * exfat_fs_error reports a file system problem that might indicate fa data + * corruption/inconsistency. Depending on 'errors' mount option the + * panic() is called, or error message is printed FAT and nothing is done, + * or filesystem is remounted read-only (default behavior). + * In case the file system is remounted read-only, it can be made writable + * again by remounting it. + */ +void __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...) +{ + struct exfat_mount_options *opts = &EXFAT_SB(sb)->options; + va_list args; + struct va_format vaf; + struct block_device *bdev = sb->s_bdev; + dev_t bd_dev = bdev ? bdev->bd_dev : 0; + + if (report) { + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + pr_err("exFAT-fs (%s[%d:%d]): ERR: %pV\n", + sb->s_id, MAJOR(bd_dev), MINOR(bd_dev), &vaf); + va_end(args); + } + + if (opts->errors == EXFAT_ERRORS_PANIC) { + panic("exFAT-fs (%s[%d:%d]): fs panic from previous error\n", + sb->s_id, MAJOR(bd_dev), MINOR(bd_dev)); + } else if (opts->errors == EXFAT_ERRORS_RO && !EXFAT_IS_SB_RDONLY(sb)) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + sb->s_flags |= MS_RDONLY; +#else + sb->s_flags |= SB_RDONLY; +#endif + pr_err("exFAT-fs (%s[%d:%d]): file-system has been set to " + "read-only\n", sb->s_id, MAJOR(bd_dev), MINOR(bd_dev)); + exfat_uevent_ro_remount(sb); + } +} +EXPORT_SYMBOL(__exfat_fs_error); + +/** + * __exfat_msg() - print preformated EXFAT specific messages. + * All logs except what uses exfat_fs_error() should be written by __exfat_msg() + */ +void __exfat_msg(struct super_block *sb, const char *level, int st, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + struct block_device *bdev = sb->s_bdev; + dev_t bd_dev = bdev ? bdev->bd_dev : 0; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + /* level means KERN_ pacility level */ + printk("%sexFAT-fs (%s[%d:%d]): %pV\n", level, + sb->s_id, MAJOR(bd_dev), MINOR(bd_dev), &vaf); + va_end(args); +} +EXPORT_SYMBOL(__exfat_msg); + +void exfat_log_version(void) +{ + pr_info("exFAT: file-system version %s\n", EXFAT_VERSION); +} +EXPORT_SYMBOL(exfat_log_version); + +/* externs sys_tz + * extern struct timezone sys_tz; + */ +#define UNIX_SECS_1980 315532800L + +#if BITS_PER_LONG == 64 +#define UNIX_SECS_2108 4354819200L +#endif + +/* days between 1970/01/01 and 1980/01/01 (2 leap days) */ +#define DAYS_DELTA_DECADE (365 * 10 + 2) +/* 120 (2100 - 1980) isn't leap year */ +#define NO_LEAP_YEAR_2100 (120) +#define IS_LEAP_YEAR(y) (!((y) & 0x3) && (y) != NO_LEAP_YEAR_2100) + +#define SECS_PER_MIN (60) +#define SECS_PER_HOUR (60 * SECS_PER_MIN) +#define SECS_PER_DAY (24 * SECS_PER_HOUR) + +#define MAKE_LEAP_YEAR(leap_year, year) \ + do { \ + /* 2100 isn't leap year */ \ + if (unlikely(year > NO_LEAP_YEAR_2100)) \ + leap_year = ((year + 3) / 4) - 1; \ + else \ + leap_year = ((year + 3) / 4); \ + } while (0) + +/* Linear day numbers of the respective 1sts in non-leap years. */ +static time_t accum_days_in_year[] = { + /* Month : N 01 02 03 04 05 06 07 08 09 10 11 12 */ + 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, +}; + +/* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */ +void exfat_time_fat2unix(struct exfat_sb_info *sbi, struct timespec_compat *ts, + DATE_TIME_T *tp) +{ + time_t year = tp->Year; + time_t ld; /* leap day */ + + MAKE_LEAP_YEAR(ld, year); + + if (IS_LEAP_YEAR(year) && (tp->Month) > 2) + ld++; + + ts->tv_sec = tp->Second + tp->Minute * SECS_PER_MIN + + tp->Hour * SECS_PER_HOUR + + (year * 365 + ld + accum_days_in_year[tp->Month] + + (tp->Day - 1) + DAYS_DELTA_DECADE) * SECS_PER_DAY; + + if (!sbi->options.tz_utc) + ts->tv_sec += sys_tz.tz_minuteswest * SECS_PER_MIN; + + ts->tv_nsec = 0; +} + +/* Convert linear UNIX date to a FAT time/date pair. */ +void exfat_time_unix2fat(struct exfat_sb_info *sbi, struct timespec_compat *ts, + DATE_TIME_T *tp) +{ + time_t second = ts->tv_sec; + time_t day, month, year; + time_t ld; /* leap day */ + + if (!sbi->options.tz_utc) + second -= sys_tz.tz_minuteswest * SECS_PER_MIN; + + /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */ + if (second < UNIX_SECS_1980) { + tp->Second = 0; + tp->Minute = 0; + tp->Hour = 0; + tp->Day = 1; + tp->Month = 1; + tp->Year = 0; + return; + } +#if (BITS_PER_LONG == 64) + if (second >= UNIX_SECS_2108) { + tp->Second = 59; + tp->Minute = 59; + tp->Hour = 23; + tp->Day = 31; + tp->Month = 12; + tp->Year = 127; + return; + } +#endif + + day = second / SECS_PER_DAY - DAYS_DELTA_DECADE; + year = day / 365; + + MAKE_LEAP_YEAR(ld, year); + if (year * 365 + ld > day) + year--; + + MAKE_LEAP_YEAR(ld, year); + day -= year * 365 + ld; + + if (IS_LEAP_YEAR(year) && day == accum_days_in_year[3]) { + month = 2; + } else { + if (IS_LEAP_YEAR(year) && day > accum_days_in_year[3]) + day--; + for (month = 1; month < 12; month++) { + if (accum_days_in_year[month + 1] > day) + break; + } + } + day -= accum_days_in_year[month]; + + tp->Second = second % SECS_PER_MIN; + tp->Minute = (second / SECS_PER_MIN) % 60; + tp->Hour = (second / SECS_PER_HOUR) % 24; + tp->Day = day + 1; + tp->Month = month; + tp->Year = year; +} + +TIMESTAMP_T *exfat_tm_now(struct exfat_sb_info *sbi, TIMESTAMP_T *tp) +{ + struct timespec_compat ts; + DATE_TIME_T dt; + + KTIME_GET_REAL_TS(&ts); + exfat_time_unix2fat(sbi, &ts, &dt); + + tp->year = dt.Year; + tp->mon = dt.Month; + tp->day = dt.Day; + tp->hour = dt.Hour; + tp->min = dt.Minute; + tp->sec = dt.Second; + + return tp; +} + +u16 exfat_calc_chksum_2byte(void *data, s32 len, u16 chksum, s32 type) +{ + s32 i; + u8 *c = (u8 *) data; + + for (i = 0; i < len; i++, c++) { + if (((i == 2) || (i == 3)) && (type == CS_DIR_ENTRY)) + continue; + chksum = (((chksum & 1) << 15) | ((chksum & 0xFFFE) >> 1)) + (u16) *c; + } + return chksum; +} + +#ifdef CONFIG_EXFAT_DBG_MSG +void __exfat_dmsg(int level, const char *fmt, ...) +{ + va_list args; + + /* should check type */ + if (level > EXFAT_MSG_LEVEL) + return; + + va_start(args, fmt); + /* fmt already includes KERN_ pacility level */ + vprintk(fmt, args); + va_end(args); +} +#endif diff --git a/fs/exfat/nls.c b/fs/exfat/nls.c new file mode 100644 index 00000000..b367b4d9 --- /dev/null +++ b/fs/exfat/nls.c @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * nls.c: exFAT NLS manager + */ + +#include +#include + +#include "exfat.h" +#include "core.h" + +/* + * Allow full-width illegal characters : + * "MS windows 7" supports full-width-invalid-name-characters. + * So we should check half-width-invalid-name-characters(ASCII) only + * for compatibility. + * + * " * / : < > ? \ | + * + * patch 1.2.0 + */ +static u16 bad_uni_chars[] = { + 0x0022, 0x002A, 0x002F, 0x003A, + 0x003C, 0x003E, 0x003F, 0x005C, 0x007C, +#if 0 /* allow full-width characters */ + 0x201C, 0x201D, 0xFF0A, 0xFF0F, 0xFF1A, + 0xFF1C, 0xFF1E, 0xFF1F, 0xFF3C, 0xFF5C, +#endif + 0 +}; + +static s32 convert_uni_to_ch(struct nls_table *nls, u16 uni, u8 *ch, s32 *lossy); +static s32 convert_ch_to_uni(struct nls_table *nls, u8 *ch, u16 *uni, s32 *lossy); + +static u16 nls_upper(struct super_block *sb, u16 a) +{ + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + + if (EXFAT_SB(sb)->options.casesensitive) + return a; + if ((fsi->vol_utbl)[exfat_get_col_index(a)] != NULL) + return (fsi->vol_utbl)[exfat_get_col_index(a)][exfat_get_row_index(a)]; + else + return a; +} + +u16 *nls_wstrchr(u16 *str, u16 wchar) +{ + while (*str) { + if (*(str++) == wchar) + return str; + } + + return 0; +} + +s32 exfat_nls_cmp_uniname(struct super_block *sb, u16 *a, u16 *b) +{ + s32 i; + + for (i = 0; i < MAX_NAME_LENGTH; i++, a++, b++) { + if (nls_upper(sb, *a) != nls_upper(sb, *b)) + return 1; + if (*a == 0x0) + return 0; + } + return 0; +} + +#define CASE_LOWER_BASE (0x08) /* base is lower case */ +#define CASE_LOWER_EXT (0x10) /* extension is lower case */ + +s32 exfat_nls_sfn_to_uni16s(struct super_block *sb, DOS_NAME_T *p_dosname, UNI_NAME_T *p_uniname) +{ + s32 i = 0, j, n = 0; + u8 buf[MAX_DOSNAME_BUF_SIZE]; + u8 *dosname = p_dosname->name; + u16 *uniname = p_uniname->name; + struct nls_table *nls = EXFAT_SB(sb)->nls_disk; + + if (*dosname == 0x05) { + *buf = 0xE5; + i++; + n++; + } + + for ( ; i < 8; i++, n++) { + if (*(dosname+i) == ' ') + break; + + if ((*(dosname+i) >= 'A') && (*(dosname+i) <= 'Z') && + (p_dosname->name_case & CASE_LOWER_BASE)) + *(buf+n) = *(dosname+i) + ('a' - 'A'); + else + *(buf+n) = *(dosname+i); + } + if (*(dosname+8) != ' ') { + *(buf+n) = '.'; + n++; + } + + for (i = 8; i < DOS_NAME_LENGTH; i++, n++) { + if (*(dosname+i) == ' ') + break; + + if ((*(dosname+i) >= 'A') && (*(dosname+i) <= 'Z') && + (p_dosname->name_case & CASE_LOWER_EXT)) + *(buf+n) = *(dosname+i) + ('a' - 'A'); + else + *(buf+n) = *(dosname+i); + } + *(buf+n) = '\0'; + + i = j = 0; + while (j < MAX_NAME_LENGTH) { + if (*(buf+i) == '\0') + break; + + i += convert_ch_to_uni(nls, (buf+i), uniname, NULL); + + uniname++; + j++; + } + + *uniname = (u16) '\0'; + return j; +} + +static s32 __nls_utf16s_to_vfsname(struct super_block *sb, UNI_NAME_T *p_uniname, u8 *p_cstring, s32 buflen) +{ + s32 len; + const u16 *uniname = p_uniname->name; + + /* always len >= 0 */ + len = utf16s_to_utf8s(uniname, MAX_NAME_LENGTH, UTF16_HOST_ENDIAN, + p_cstring, buflen); + p_cstring[len] = '\0'; + return len; +} + +static s32 __nls_vfsname_to_utf16s(struct super_block *sb, const u8 *p_cstring, + const s32 len, UNI_NAME_T *p_uniname, s32 *p_lossy) +{ + s32 i, unilen, lossy = NLS_NAME_NO_LOSSY; + u16 upname[MAX_NAME_LENGTH+1]; + u16 *uniname = p_uniname->name; + + BUG_ON(!len); + + unilen = utf8s_to_utf16s(p_cstring, len, UTF16_HOST_ENDIAN, + (wchar_t *)uniname, MAX_NAME_LENGTH+2); + if (unilen < 0) { + MMSG("%s: failed to vfsname_to_utf16(err:%d) " + "vfsnamelen:%d", __func__, unilen, len); + return unilen; + } + + if (unilen > MAX_NAME_LENGTH) { + MMSG("%s: failed to vfsname_to_utf16(estr:ENAMETOOLONG) " + "vfsnamelen:%d, unilen:%d>%d", + __func__, len, unilen, MAX_NAME_LENGTH); + return -ENAMETOOLONG; + } + + p_uniname->name_len = (u8)(unilen & 0xFF); + + for (i = 0; i < unilen; i++) { + if ((*uniname < 0x0020) || nls_wstrchr(bad_uni_chars, *uniname)) + lossy |= NLS_NAME_LOSSY; + + *(upname+i) = nls_upper(sb, *uniname); + uniname++; + } + + *uniname = (u16)'\0'; + p_uniname->name_len = unilen; + p_uniname->name_hash = exfat_calc_chksum_2byte((void *) upname, + unilen << 1, 0, CS_DEFAULT); + + if (p_lossy) + *p_lossy = lossy; + + return unilen; +} + +static s32 __exfat_nls_uni16s_to_vfsname(struct super_block *sb, UNI_NAME_T *p_uniname, u8 *p_cstring, s32 buflen) +{ + s32 i, j, len, out_len = 0; + u8 buf[MAX_CHARSET_SIZE]; + const u16 *uniname = p_uniname->name; + struct nls_table *nls = EXFAT_SB(sb)->nls_io; + + i = 0; + while ((i < MAX_NAME_LENGTH) && (out_len < (buflen-1))) { + if (*uniname == (u16)'\0') + break; + + len = convert_uni_to_ch(nls, *uniname, buf, NULL); + + if (out_len + len >= buflen) + len = (buflen - 1) - out_len; + + out_len += len; + + if (len > 1) { + for (j = 0; j < len; j++) + *p_cstring++ = (s8) *(buf+j); + } else { /* len == 1 */ + *p_cstring++ = (s8) *buf; + } + + uniname++; + i++; + } + + *p_cstring = '\0'; + return out_len; +} + +static s32 __exfat_nls_vfsname_to_uni16s(struct super_block *sb, const u8 *p_cstring, + const s32 len, UNI_NAME_T *p_uniname, s32 *p_lossy) +{ + s32 i, unilen, lossy = NLS_NAME_NO_LOSSY; + u16 upname[MAX_NAME_LENGTH+1]; + u16 *uniname = p_uniname->name; + struct nls_table *nls = EXFAT_SB(sb)->nls_io; + + BUG_ON(!len); + + i = unilen = 0; + while ((unilen < MAX_NAME_LENGTH) && (i < len)) { + i += convert_ch_to_uni(nls, (u8 *)(p_cstring+i), uniname, &lossy); + + if ((*uniname < 0x0020) || nls_wstrchr(bad_uni_chars, *uniname)) + lossy |= NLS_NAME_LOSSY; + + *(upname+unilen) = nls_upper(sb, *uniname); + + uniname++; + unilen++; + } + + if (*(p_cstring+i) != '\0') + lossy |= NLS_NAME_OVERLEN; + + *uniname = (u16)'\0'; + p_uniname->name_len = unilen; + p_uniname->name_hash = + exfat_calc_chksum_2byte((void *) upname, unilen<<1, 0, CS_DEFAULT); + + if (p_lossy) + *p_lossy = lossy; + + return unilen; +} + +s32 exfat_nls_uni16s_to_vfsname(struct super_block *sb, UNI_NAME_T *uniname, u8 *p_cstring, s32 buflen) +{ + if (EXFAT_SB(sb)->options.utf8) + return __nls_utf16s_to_vfsname(sb, uniname, p_cstring, buflen); + + return __exfat_nls_uni16s_to_vfsname(sb, uniname, p_cstring, buflen); +} + +s32 exfat_nls_vfsname_to_uni16s(struct super_block *sb, const u8 *p_cstring, const s32 len, UNI_NAME_T *uniname, s32 *p_lossy) +{ + if (EXFAT_SB(sb)->options.utf8) + return __nls_vfsname_to_utf16s(sb, p_cstring, len, uniname, p_lossy); + return __exfat_nls_vfsname_to_uni16s(sb, p_cstring, len, uniname, p_lossy); +} + +static s32 convert_ch_to_uni(struct nls_table *nls, u8 *ch, u16 *uni, s32 *lossy) +{ + int len; + + *uni = 0x0; + + if (ch[0] < 0x80) { + *uni = (u16) ch[0]; + return 1; + } + + len = nls->char2uni(ch, MAX_CHARSET_SIZE, uni); + if (len < 0) { + /* conversion failed */ + DMSG("%s: fail to use nls\n", __func__); + if (lossy != NULL) + *lossy |= NLS_NAME_LOSSY; + *uni = (u16) '_'; + if (!strcmp(nls->charset, "utf8")) + return 1; + return 2; + } + + return len; +} + +static s32 convert_uni_to_ch(struct nls_table *nls, u16 uni, u8 *ch, s32 *lossy) +{ + int len; + + ch[0] = 0x0; + + if (uni < 0x0080) { + ch[0] = (u8) uni; + return 1; + } + + len = nls->uni2char(uni, ch, MAX_CHARSET_SIZE); + if (len < 0) { + /* conversion failed */ + DMSG("%s: fail to use nls\n", __func__); + if (lossy != NULL) + *lossy |= NLS_NAME_LOSSY; + ch[0] = '_'; + return 1; + } + + return len; + +} diff --git a/fs/exfat/super.c b/fs/exfat/super.c new file mode 100644 index 00000000..5f27729d --- /dev/null +++ b/fs/exfat/super.c @@ -0,0 +1,3893 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * super.c: exFAT glue layer for supporting VFS + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for mark_page_accessed() */ +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) +#include +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0) +#error EXFAT only supports linux kernel version 3.0 or higher +#endif + +#include "version.h" +#include "config.h" + +#include "exfat.h" +#include "core.h" + +/* skip iterating emit_dots when dir is empty */ +#define ITER_POS_FILLED_DOTS (2) + +#define U16_MAX ((u16)~0U) + +static struct kset *exfat_kset; +static struct kmem_cache *exfat_inode_cachep; + +static DEFINE_MUTEX(_lock_core); + +static int exfat_default_codepage = CONFIG_EXFAT_DEFAULT_CODEPAGE; +static char exfat_default_iocharset[] = CONFIG_EXFAT_DEFAULT_IOCHARSET; +static const char exfat_iocharset_with_utf8[] = "iso8859-1"; + +static void exfat_truncate(struct inode *inode, loff_t old_size); +static int exfat_get_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create); + +static struct inode *exfat_iget(struct super_block *sb, loff_t i_pos); +static struct inode *exfat_build_inode(struct super_block *sb, const FILE_ID_T *fid, loff_t i_pos); +static void exfat_detach(struct inode *inode); +static void exfat_attach(struct inode *inode, loff_t i_pos); +static inline unsigned long exfat_hash(loff_t i_pos); +static s32 __exfat_sync_fs(struct super_block *sb, s32 do_sync); +static int __exfat_write_inode(struct inode *inode, int sync); +static int exfat_sync_inode(struct inode *inode); +static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc); +static void exfat_write_super(struct super_block *sb); +static void exfat_write_failed(struct address_space *mapping, loff_t to); + +static void exfat_init_namebuf(DENTRY_NAMEBUF_T *nb); +static int exfat_alloc_namebuf(DENTRY_NAMEBUF_T *nb); +static void exfat_free_namebuf(DENTRY_NAMEBUF_T *nb); + +static int __exfat_getattr(struct inode *inode, struct kstat *stat); +static void __exfat_writepage_end_io(struct bio *bio, int err); +void lock_super(struct super_block *sb); +void unlock_super(struct super_block *sb); +static int exfat_create_compat(struct inode *dir, struct dentry *dentry); +static int __exfat_revalidate(struct dentry *dentry); +static int __exfat_revalidate_ci(struct dentry *dentry, unsigned int flags); +static int __exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync); +static struct dentry *__exfat_lookup(struct inode *dir, struct dentry *dentry); +static int __exfat_mkdir(struct inode *dir, struct dentry *dentry); +static int __exfat_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry); +static int __exfat_show_options(struct seq_file *m, struct super_block *sb); +static inline ssize_t __exfat_blkdev_direct_IO(int rw, struct kiocb *iocb, + struct inode *inode, void *iov_u, loff_t offset, + unsigned long nr_segs); +static inline ssize_t __exfat_direct_IO(int rw, struct kiocb *iocb, + struct inode *inode, void *iov_u, loff_t offset, + loff_t count, unsigned long nr_segs); +static int __exfat_d_hash(const struct dentry *dentry, struct qstr *qstr); +static int __exfat_d_hashi(const struct dentry *dentry, struct qstr *qstr); +static int __exfat_cmp(const struct dentry *dentry, unsigned int len, + const char *str, const struct qstr *name); +static int __exfat_cmpi(const struct dentry *dentry, unsigned int len, + const char *str, const struct qstr *name); + +/* mount the file system volume */ +static s32 exfat_mount(struct super_block *sb) +{ + s32 err; + + /* acquire the core lock for file system ccritical section */ + mutex_lock(&_lock_core); + + err = exfat_meta_cache_init(sb); + if (err) + goto out; + + err = exfat_fscore_mount(sb); +out: + if (err) + exfat_meta_cache_shutdown(sb); + + /* release the core lock for file system critical section */ + mutex_unlock(&_lock_core); + + return err; +} + +/* unmount the file system volume */ +static s32 exfat_umount(struct super_block *sb) +{ + s32 err; + + /* acquire the core lock for file system ccritical section */ + mutex_lock(&_lock_core); + + mutex_lock(&(EXFAT_SB(sb)->s_vlock)); + err = exfat_fscore_umount(sb); + exfat_meta_cache_shutdown(sb); + mutex_unlock(&(EXFAT_SB(sb)->s_vlock)); + + /* release the core lock for file system critical section */ + mutex_unlock(&_lock_core); + + return err; +} + +static s32 exfat_set_vol_flags(struct super_block *sb, u16 new_flag, s32 always_sync) +{ + s32 err; + + mutex_lock(&(EXFAT_SB(sb)->s_vlock)); + err = exfat_fscore_set_vol_flags(sb, new_flag, always_sync); + mutex_unlock(&(EXFAT_SB(sb)->s_vlock)); + return err; +} + +/* read the target string of symlink */ +static s32 exfat_read_link(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount) +{ + s32 err; + struct super_block *sb = inode->i_sb; + + /* check the validity of pointer parameters */ + ASSERT(fid && buffer); + + mutex_lock(&(EXFAT_SB(sb)->s_vlock)); + err = exfat_fscore_read_link(inode, fid, buffer, count, rcount); + mutex_unlock(&(EXFAT_SB(sb)->s_vlock)); + return err; +} + +/* write the target string of symlink */ +static s32 exfat_write_link(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount) +{ + s32 err; + struct super_block *sb = inode->i_sb; + + /* check the validity of pointer parameters */ + ASSERT(fid && buffer); + + mutex_lock(&(EXFAT_SB(sb)->s_vlock)); + err = exfat_fscore_write_link(inode, fid, buffer, count, wcount); + mutex_unlock(&(EXFAT_SB(sb)->s_vlock)); + return err; +} + +/* remove a file */ +static s32 exfat_remove(struct inode *inode, FILE_ID_T *fid) +{ + s32 err; + struct super_block *sb = inode->i_sb; + + /* check the validity of pointer parameters */ + ASSERT(fid); + + mutex_lock(&(EXFAT_SB(sb)->s_vlock)); + err = exfat_fscore_remove(inode, fid); + mutex_unlock(&(EXFAT_SB(sb)->s_vlock)); + return err; +} + +/* get the information of a given file */ +static s32 exfat_read_inode(struct inode *inode, DIR_ENTRY_T *info) +{ + s32 err; + struct super_block *sb = inode->i_sb; + + mutex_lock(&(EXFAT_SB(sb)->s_vlock)); + err = exfat_fscore_read_inode(inode, info); + mutex_unlock(&(EXFAT_SB(sb)->s_vlock)); + return err; +} + +/* return the cluster number in the given cluster offset */ +static s32 exfat_map_clus(struct inode *inode, u32 clu_offset, u32 *clu, int dest) +{ + s32 err; + struct super_block *sb = inode->i_sb; + + /* check the validity of pointer parameters */ + ASSERT(clu); + + mutex_lock(&(EXFAT_SB(sb)->s_vlock)); + err = exfat_fscore_map_clus(inode, clu_offset, clu, dest); + mutex_unlock(&(EXFAT_SB(sb)->s_vlock)); + return err; +} + +/* read a directory entry from the opened directory */ +static s32 __exfat_readdir(struct inode *inode, DIR_ENTRY_T *dir_entry) +{ + s32 err; + struct super_block *sb = inode->i_sb; + + /* check the validity of pointer parameters */ + ASSERT(dir_entry); + + mutex_lock(&(EXFAT_SB(sb)->s_vlock)); + err = exfat_fscore_readdir(inode, dir_entry); + mutex_unlock(&(EXFAT_SB(sb)->s_vlock)); + return err; +} + +/* reflect the internal dirty flags to VFS bh dirty flags */ +static s32 exfat_cache_flush(struct super_block *sb, int do_sync) +{ + mutex_lock(&(EXFAT_SB(sb)->s_vlock)); + exfat_fcache_flush(sb, do_sync); + exfat_dcache_flush(sb, do_sync); + mutex_unlock(&(EXFAT_SB(sb)->s_vlock)); + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + /* EMPTY */ +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) */ +static inline void bio_set_dev(struct bio *bio, struct block_device *bdev) +{ + bio->bi_bdev = bdev; +} +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,9,0) +#define current_time(x) CURRENT_TIME_SEC +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) +static int exfat_getattr(const struct path *path, struct kstat *stat, + u32 request_mask, unsigned int query_flags) +{ + struct inode *inode = d_backing_inode(path->dentry); + + return __exfat_getattr(inode, stat); +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) */ +static int exfat_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) +{ + struct inode *inode = dentry->d_inode; + + return __exfat_getattr(inode, stat); +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) +static inline void __exfat_clean_bdev_aliases(struct block_device *bdev, sector_t block) +{ + clean_bdev_aliases(bdev, block, 1); +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0) */ +static inline void __exfat_clean_bdev_aliases(struct block_device *bdev, sector_t block) +{ + unmap_underlying_metadata(bdev, block); +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) +static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + unsigned int flags) +{ + /* + * The VFS already checks for existence, so for local filesystems + * the RENAME_NOREPLACE implementation is equivalent to plain rename. + * Don't support any other flags + */ + if (flags & ~RENAME_NOREPLACE) + return -EINVAL; + return __exfat_rename(old_dir, old_dentry, new_dir, new_dentry); +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) */ +static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + return __exfat_rename(old_dir, old_dentry, new_dir, new_dentry); +} + +// setattr_prepare() was backported to several LTS kernels +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 39) && \ + LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0) +#define SETATTR_PREPARE_AVAILABLE +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 37) && \ + LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0) +#define SETATTR_PREPARE_AVAILABLE +#endif + +#ifndef SETATTR_PREPARE_AVAILABLE +static int setattr_prepare(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + + return inode_change_ok(inode, attr); +} +#endif +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) +static inline void __exfat_submit_bio_write(struct bio *bio) +{ + bio_set_op_attrs(bio, REQ_OP_WRITE, 0); + submit_bio(bio); +} + +static inline unsigned int __exfat_full_name_hash(const struct dentry *dentry, const char *name, unsigned int len) +{ + return full_name_hash(dentry, name, len); +} + +static inline unsigned long __exfat_init_name_hash(const struct dentry *dentry) +{ + return init_name_hash(dentry); +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) */ +static inline void __exfat_submit_bio_write(struct bio *bio) +{ + submit_bio(WRITE, bio); +} + +static inline unsigned int __exfat_full_name_hash(const struct dentry *unused, const char *name, unsigned int len) +{ + return full_name_hash(name, len); +} + +static inline unsigned long __exfat_init_name_hash(const struct dentry *unused) +{ + return init_name_hash(); +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 21) + /* EMPTY */ +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 21) */ +static inline void inode_lock(struct inode *inode) +{ + mutex_lock(&inode->i_mutex); +} + +static inline void inode_unlock(struct inode *inode) +{ + mutex_unlock(&inode->i_mutex); +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) +static inline int exfat_remount_syncfs(struct super_block *sb) +{ + sync_filesystem(sb); + return 0; +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0) */ +static inline int exfat_remount_syncfs(struct super_block *sb) +{ + /* + * We don`t need to call sync_filesystem(sb), + * Because VFS calls it. + */ + return 0; +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +static inline sector_t __exfat_bio_sector(struct bio *bio) +{ + return bio->bi_iter.bi_sector; +} + +static inline void __exfat_set_bio_iterate(struct bio *bio, sector_t sector, + unsigned int size, unsigned int idx, unsigned int done) +{ + struct bvec_iter *iter = &(bio->bi_iter); + + iter->bi_sector = sector; + iter->bi_size = size; + iter->bi_idx = idx; + iter->bi_bvec_done = done; +} + +static void __exfat_truncate_pagecache(struct inode *inode, + loff_t to, loff_t newsize) +{ + truncate_pagecache(inode, newsize); +} + +static int exfat_d_hash(const struct dentry *dentry, struct qstr *qstr) +{ + return __exfat_d_hash(dentry, qstr); +} + +static int exfat_d_hashi(const struct dentry *dentry, struct qstr *qstr) +{ + return __exfat_d_hashi(dentry, qstr); +} + +//instead of exfat_readdir +static int exfat_iterate(struct file *filp, struct dir_context *ctx) +{ + struct inode *inode = filp->f_path.dentry->d_inode; + struct super_block *sb = inode->i_sb; + DIR_ENTRY_T de; + DENTRY_NAMEBUF_T *nb = &(de.NameBuf); + unsigned long inum; + loff_t cpos; + int err = 0, fake_offset = 0; + + exfat_init_namebuf(nb); + lock_super(sb); + + cpos = ctx->pos; + if (!dir_emit_dots(filp, ctx)) + goto out; + if (ctx->pos == ITER_POS_FILLED_DOTS) { + cpos = 0; + fake_offset = 1; + } + if (cpos & (DENTRY_SIZE - 1)) { + err = -ENOENT; + goto out; + } + + /* name buffer should be allocated before use */ + err = exfat_alloc_namebuf(nb); + if (err) + goto out; +get_new: + EXFAT_I(inode)->fid.size = i_size_read(inode); + EXFAT_I(inode)->fid.rwoffset = cpos >> DENTRY_SIZE_BITS; + + if (cpos >= EXFAT_I(inode)->fid.size) + goto end_of_dir; + + err = __exfat_readdir(inode, &de); + if (err) { + // at least we tried to read a sector + // move cpos to next sector position (should be aligned) + if (err == -EIO) { + cpos += 1 << (sb->s_blocksize_bits); + cpos &= ~((u32)sb->s_blocksize-1); + } + + err = -EIO; + goto end_of_dir; + } + + cpos = EXFAT_I(inode)->fid.rwoffset << DENTRY_SIZE_BITS; + + if (!nb->lfn[0]) + goto end_of_dir; + + if (!memcmp(nb->sfn, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH)) { + inum = inode->i_ino; + } else if (!memcmp(nb->sfn, DOS_PAR_DIR_NAME, DOS_NAME_LENGTH)) { + inum = parent_ino(filp->f_path.dentry); + } else { + loff_t i_pos = ((loff_t) EXFAT_I(inode)->fid.start_clu << 32) | + ((EXFAT_I(inode)->fid.rwoffset-1) & 0xffffffff); + struct inode *tmp = exfat_iget(sb, i_pos); + + if (tmp) { + inum = tmp->i_ino; + iput(tmp); + } else { + inum = iunique(sb, EXFAT_ROOT_INO); + } + } + + /* Before calling dir_emit(), sb_lock should be released. + * Because page fault can occur in dir_emit() when the size of buffer given + * from user is larger than one page size + */ + unlock_super(sb); + if (!dir_emit(ctx, nb->lfn, strlen(nb->lfn), inum, + (de.Attr & ATTR_SUBDIR) ? DT_DIR : DT_REG)) + goto out_unlocked; + lock_super(sb); + + ctx->pos = cpos; + goto get_new; + +end_of_dir: + if (!cpos && fake_offset) + cpos = ITER_POS_FILLED_DOTS; + ctx->pos = cpos; +out: + unlock_super(sb); +out_unlocked: + /* + * To improve performance, free namebuf after unlock sb_lock. + * If namebuf is not allocated, this function do nothing + */ + exfat_free_namebuf(nb); + return err; +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0) */ +static inline sector_t __exfat_bio_sector(struct bio *bio) +{ + return bio->bi_sector; +} + +static inline void __exfat_set_bio_iterate(struct bio *bio, sector_t sector, + unsigned int size, unsigned int idx, unsigned int done) +{ + bio->bi_sector = sector; + bio->bi_idx = idx; + bio->bi_size = size; //PAGE_SIZE; +} + +static void __exfat_truncate_pagecache(struct inode *inode, + loff_t to, loff_t newsize) +{ + truncate_pagecache(inode, to, newsize); +} + +static int exfat_d_hash(const struct dentry *dentry, + const struct inode *inode, struct qstr *qstr) +{ + return __exfat_d_hash(dentry, qstr); +} + +static int exfat_d_hashi(const struct dentry *dentry, + const struct inode *inode, struct qstr *qstr) +{ + return __exfat_d_hashi(dentry, qstr); +} + +static int exfat_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + struct inode *inode = filp->f_path.dentry->d_inode; + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + FS_INFO_T *fsi = &(sbi->fsi); + DIR_ENTRY_T de; + DENTRY_NAMEBUF_T *nb = &(de.NameBuf); + unsigned long inum; + loff_t cpos; + int err = 0, fake_offset = 0; + + exfat_init_namebuf(nb); + lock_super(sb); + + cpos = filp->f_pos; + /* Fake . and .. for the root directory. */ + while (cpos < ITER_POS_FILLED_DOTS) { + if (inode->i_ino == EXFAT_ROOT_INO) + inum = EXFAT_ROOT_INO; + else if (cpos == 0) + inum = inode->i_ino; + else /* (cpos == 1) */ + inum = parent_ino(filp->f_path.dentry); + + if (filldir(dirent, "..", cpos+1, cpos, inum, DT_DIR) < 0) + goto out; + cpos++; + filp->f_pos++; + } + if (cpos == ITER_POS_FILLED_DOTS) { + cpos = 0; + fake_offset = 1; + } + if (cpos & (DENTRY_SIZE - 1)) { + err = -ENOENT; + goto out; + } + + /* name buffer should be allocated before use */ + err = exfat_alloc_namebuf(nb); + if (err) + goto out; +get_new: + EXFAT_I(inode)->fid.size = i_size_read(inode); + EXFAT_I(inode)->fid.rwoffset = cpos >> DENTRY_SIZE_BITS; + + if (cpos >= EXFAT_I(inode)->fid.size) + goto end_of_dir; + + err = __exfat_readdir(inode, &de); + if (err) { + // at least we tried to read a sector + // move cpos to next sector position (should be aligned) + if (err == -EIO) { + cpos += 1 << (sb->s_blocksize_bits); + cpos &= ~((u32)sb->s_blocksize-1); + } + + err = -EIO; + goto end_of_dir; + } + + cpos = EXFAT_I(inode)->fid.rwoffset << DENTRY_SIZE_BITS; + + if (!nb->lfn[0]) + goto end_of_dir; + + if (!memcmp(nb->sfn, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH)) { + inum = inode->i_ino; + } else if (!memcmp(nb->sfn, DOS_PAR_DIR_NAME, DOS_NAME_LENGTH)) { + inum = parent_ino(filp->f_path.dentry); + } else { + loff_t i_pos = ((loff_t) EXFAT_I(inode)->fid.start_clu << 32) | + ((EXFAT_I(inode)->fid.rwoffset-1) & 0xffffffff); + struct inode *tmp = exfat_iget(sb, i_pos); + + if (tmp) { + inum = tmp->i_ino; + iput(tmp); + } else { + inum = iunique(sb, EXFAT_ROOT_INO); + } + } + + /* Before calling dir_emit(), sb_lock should be released. + * Because page fault can occur in dir_emit() when the size of buffer given + * from user is larger than one page size + */ + unlock_super(sb); + if (filldir(dirent, nb->lfn, strlen(nb->lfn), cpos, inum, + (de.Attr & ATTR_SUBDIR) ? DT_DIR : DT_REG) < 0) + goto out_unlocked; + lock_super(sb); + + filp->f_pos = cpos; + goto get_new; + +end_of_dir: + if (!cpos && fake_offset) + cpos = ITER_POS_FILLED_DOTS; + filp->f_pos = cpos; +out: + unlock_super(sb); +out_unlocked: + /* + * To improve performance, free namebuf after unlock sb_lock. + * If namebuf is not allocated, this function do nothing + */ + exfat_free_namebuf(nb); + return err; +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) + /* EMPTY */ +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0) */ +static inline struct inode *file_inode(const struct file *f) +{ + return f->f_dentry->d_inode; +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0) +static inline int __is_sb_dirty(struct super_block *sb) +{ + return EXFAT_SB(sb)->s_dirt; +} + +static inline void __set_sb_clean(struct super_block *sb) +{ + EXFAT_SB(sb)->s_dirt = 0; +} + +/* Workqueue wrapper for exfat_write_super () */ +static void __write_super_delayed(struct work_struct *work) +{ + struct exfat_sb_info *sbi; + struct super_block *sb; + + sbi = container_of(work, struct exfat_sb_info, write_super_work.work); + sb = sbi->host_sb; + + /* XXX: Is this needed? */ + if (!sb || !down_read_trylock(&sb->s_umount)) { + DMSG("%s: skip delayed work(write_super).\n", __func__); + return; + } + + DMSG("%s: do delayed_work(write_super).\n", __func__); + + spin_lock(&sbi->work_lock); + sbi->write_super_queued = 0; + spin_unlock(&sbi->work_lock); + + exfat_write_super(sb); + + up_read(&sb->s_umount); +} + +static void setup_exfat_sync_super_wq(struct super_block *sb) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + mutex_init(&sbi->s_lock); + spin_lock_init(&sbi->work_lock); + INIT_DELAYED_WORK(&sbi->write_super_work, __write_super_delayed); + sbi->host_sb = sb; +} + +static inline bool __cancel_delayed_work_sync(struct exfat_sb_info *sbi) +{ + return cancel_delayed_work_sync(&sbi->write_super_work); +} + +void lock_super(struct super_block *sb) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + mutex_lock(&sbi->s_lock); +} + +void unlock_super(struct super_block *sb) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + mutex_unlock(&sbi->s_lock); +} + +static int exfat_revalidate(struct dentry *dentry, unsigned int flags) +{ + if (flags & LOOKUP_RCU) + return -ECHILD; + + return __exfat_revalidate(dentry); +} + +static int exfat_revalidate_ci(struct dentry *dentry, unsigned int flags) +{ + if (flags & LOOKUP_RCU) + return -ECHILD; + + return __exfat_revalidate_ci(dentry, flags); +} + +static struct inode *exfat_iget(struct super_block *sb, loff_t i_pos) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_inode_info *info; + struct hlist_head *head = sbi->inode_hashtable + exfat_hash(i_pos); + struct inode *inode = NULL; + + spin_lock(&sbi->inode_hash_lock); + hlist_for_each_entry(info, head, i_hash_fat) { + BUG_ON(info->vfs_inode.i_sb != sb); + + if (i_pos != info->i_pos) + continue; + inode = igrab(&info->vfs_inode); + if (inode) + break; + } + spin_unlock(&sbi->inode_hash_lock); + return inode; +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) */ +static inline int __is_sb_dirty(struct super_block *sb) +{ + return sb->s_dirt; +} + +static inline void __set_sb_clean(struct super_block *sb) +{ + sb->s_dirt = 0; +} + +static void setup_exfat_sync_super_wq(struct super_block *sb) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + sbi->host_sb = sb; +} + +static inline bool __cancel_delayed_work_sync(struct exfat_sb_info *sbi) +{ + /* DO NOTHING */ + return 0; +} + +static inline void clear_inode(struct inode *inode) +{ + end_writeback(inode); +} + +static int exfat_revalidate(struct dentry *dentry, struct nameidata *nd) +{ + if (nd && nd->flags & LOOKUP_RCU) + return -ECHILD; + + return __exfat_revalidate(dentry); +} + +static int exfat_revalidate_ci(struct dentry *dentry, struct nameidata *nd) +{ + if (nd && nd->flags & LOOKUP_RCU) + return -ECHILD; + + return __exfat_revalidate_ci(dentry, nd ? nd->flags : 0); + +} + +static struct inode *exfat_iget(struct super_block *sb, loff_t i_pos) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_inode_info *info; + struct hlist_node *node; + struct hlist_head *head = sbi->inode_hashtable + exfat_hash(i_pos); + struct inode *inode = NULL; + + spin_lock(&sbi->inode_hash_lock); + hlist_for_each_entry(info, node, head, i_hash_fat) { + BUG_ON(info->vfs_inode.i_sb != sb); + + if (i_pos != info->i_pos) + continue; + inode = igrab(&info->vfs_inode); + if (inode) + break; + } + spin_unlock(&sbi->inode_hash_lock); + return inode; +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) +static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags) +{ + return __exfat_lookup(dir, dentry); +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0) */ +static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) +{ + return __exfat_lookup(dir, dentry); +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) + /* NOTHING NOW */ +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0) */ +#define GLOBAL_ROOT_UID (0) +#define GLOBAL_ROOT_GID (0) + +static inline bool uid_eq(uid_t left, uid_t right) +{ + return left == right; +} + +static inline bool gid_eq(gid_t left, gid_t right) +{ + return left == right; +} + +static inline uid_t from_kuid_munged(struct user_namespace *to, uid_t kuid) +{ + return kuid; +} + +static inline gid_t from_kgid_munged(struct user_namespace *to, gid_t kgid) +{ + return kgid; +} + +static inline uid_t make_kuid(struct user_namespace *from, uid_t uid) +{ + return uid; +} + +static inline gid_t make_kgid(struct user_namespace *from, gid_t gid) +{ + return gid; +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) +static struct dentry *__d_make_root(struct inode *root_inode) +{ + return d_make_root(root_inode); +} + +static void __exfat_do_truncate(struct inode *inode, loff_t old, loff_t new) +{ + down_write(&EXFAT_I(inode)->truncate_lock); + truncate_setsize(inode, new); + exfat_truncate(inode, old); + up_write(&EXFAT_I(inode)->truncate_lock); +} + +static sector_t exfat_aop_bmap(struct address_space *mapping, sector_t block) +{ + sector_t blocknr; + + /* exfat_get_cluster() assumes the requested blocknr isn't truncated. */ + down_read(&EXFAT_I(mapping->host)->truncate_lock); + blocknr = generic_block_bmap(mapping, block, exfat_get_block); + up_read(&EXFAT_I(mapping->host)->truncate_lock); + return blocknr; +} + +static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + return __exfat_mkdir(dir, dentry); +} + +static int exfat_show_options(struct seq_file *m, struct dentry *root) +{ + return __exfat_show_options(m, root->d_sb); +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) */ +static inline void set_nlink(struct inode *inode, unsigned int nlink) +{ + inode->i_nlink = nlink; +} + +static struct dentry *__d_make_root(struct inode *root_inode) +{ + return d_alloc_root(root_inode); +} + +static void __exfat_do_truncate(struct inode *inode, loff_t old, loff_t new) +{ + truncate_setsize(inode, new); + exfat_truncate(inode, old); +} + +static sector_t exfat_aop_bmap(struct address_space *mapping, sector_t block) +{ + sector_t blocknr; + + /* exfat_get_cluster() assumes the requested blocknr isn't truncated. */ + down_read(&mapping->host->i_alloc_sem); + blocknr = generic_block_bmap(mapping, block, exfat_get_block); + up_read(&mapping->host->i_alloc_sem); + return blocknr; +} + +static int exfat_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + return __exfat_mkdir(dir, dentry); +} + +static int exfat_show_options(struct seq_file *m, struct vfsmount *mnt) +{ + return __exfat_show_options(m, mnt->mnt_sb); +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) +#define __exfat_generic_file_fsync(filp, start, end, datasync) \ + generic_file_fsync(filp, start, end, datasync) + +static int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync) +{ + return __exfat_file_fsync(filp, start, end, datasync); +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) */ +#define __exfat_generic_file_fsync(filp, start, end, datasync) \ + generic_file_fsync(filp, datasync) +static int exfat_file_fsync(struct file *filp, int datasync) +{ + return __exfat_file_fsync(filp, 0, 0, datasync); +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) +static void exfat_writepage_end_io(struct bio *bio) +{ + __exfat_writepage_end_io(bio, blk_status_to_errno(bio->bi_status)); +} +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) +static void exfat_writepage_end_io(struct bio *bio) +{ + __exfat_writepage_end_io(bio, bio->bi_error); +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0) */ +static void exfat_writepage_end_io(struct bio *bio, int err) +{ + if (test_bit(BIO_UPTODATE, &bio->bi_flags)) + err = 0; + __exfat_writepage_end_io(bio, err); +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) +static int exfat_cmp(const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) +{ + return __exfat_cmp(dentry, len, str, name); +} + +static int exfat_cmpi(const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) +{ + return __exfat_cmpi(dentry, len, str, name); +} +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +static int exfat_cmp(const struct dentry *parent, const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) +{ + return __exfat_cmp(dentry, len, str, name); +} + +static int exfat_cmpi(const struct dentry *parent, const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) +{ + return __exfat_cmpi(dentry, len, str, name); +} +#else +static int exfat_cmp(const struct dentry *parent, const struct inode *pinode, + const struct dentry *dentry, const struct inode *inode, + unsigned int len, const char *str, const struct qstr *name) +{ + return __exfat_cmp(dentry, len, str, name); +} + +static int exfat_cmpi(const struct dentry *parent, const struct inode *pinode, + const struct dentry *dentry, const struct inode *inode, + unsigned int len, const char *str, const struct qstr *name) +{ + return __exfat_cmpi(dentry, len, str, name); +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) +static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter) +{ + struct file *file = iocb->ki_filp; + struct address_space *mapping = file->f_mapping; + struct inode *inode = mapping->host; + size_t count = iov_iter_count(iter); + int rw = iov_iter_rw(iter); + loff_t offset = iocb->ki_pos; + + return __exfat_direct_IO(rw, iocb, inode, + (void *)iter, offset, count, 0 /* UNUSED */); +} +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +static ssize_t exfat_direct_IO(struct kiocb *iocb, + struct iov_iter *iter, + loff_t offset) +{ + struct file *file = iocb->ki_filp; + struct address_space *mapping = file->f_mapping; + struct inode *inode = mapping->host; + size_t count = iov_iter_count(iter); + int rw = iov_iter_rw(iter); + + return __exfat_direct_IO(rw, iocb, inode, + (void *)iter, offset, count, 0 /* UNUSED */); +} +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) +static ssize_t exfat_direct_IO(int rw, struct kiocb *iocb, + struct iov_iter *iter, + loff_t offset) +{ + struct file *file = iocb->ki_filp; + struct address_space *mapping = file->f_mapping; + struct inode *inode = mapping->host; + size_t count = iov_iter_count(iter); + + return __exfat_direct_IO(rw, iocb, inode, + (void *)iter, offset, count, 0 /* UNUSED */); +} +#else +static ssize_t exfat_direct_IO(int rw, struct kiocb *iocb, + const struct iovec *iov, loff_t offset, unsigned long nr_segs) +{ + struct file *file = iocb->ki_filp; + struct address_space *mapping = file->f_mapping; + struct inode *inode = mapping->host; + size_t count = iov_length(iov, nr_segs); + + return __exfat_direct_IO(rw, iocb, inode, + (void *)iov, offset, count, nr_segs); +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) +static inline ssize_t __exfat_blkdev_direct_IO(int unused, struct kiocb *iocb, + struct inode *inode, void *iov_u, loff_t unused_1, + unsigned long nr_segs) +{ + struct iov_iter *iter = (struct iov_iter *)iov_u; + + return blockdev_direct_IO(iocb, inode, iter, exfat_get_block); +} +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +static inline ssize_t __exfat_blkdev_direct_IO(int unused, struct kiocb *iocb, + struct inode *inode, void *iov_u, loff_t offset, + unsigned long nr_segs) +{ + struct iov_iter *iter = (struct iov_iter *)iov_u; + + return blockdev_direct_IO(iocb, inode, iter, offset, exfat_get_block); +} +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) +static inline ssize_t __exfat_blkdev_direct_IO(int rw, struct kiocb *iocb, + struct inode *inode, void *iov_u, loff_t offset, + unsigned long nr_segs) +{ + struct iov_iter *iter = (struct iov_iter *)iov_u; + + return blockdev_direct_IO(rw, iocb, inode, iter, + offset, exfat_get_block); +} +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) +static inline ssize_t __exfat_blkdev_direct_IO(int rw, struct kiocb *iocb, + struct inode *inode, void *iov_u, loff_t offset, + unsigned long nr_segs) +{ + const struct iovec *iov = (const struct iovec *)iov_u; + + return blockdev_direct_IO(rw, iocb, inode, iov, + offset, nr_segs, exfat_get_block); +} +#else +static inline ssize_t __exfat_blkdev_direct_IO(int rw, struct kiocb *iocb, + struct inode *inode, void *iov_u, loff_t offset, + unsigned long nr_segs) +{ + const struct iovec *iov = (const struct iovec *)iov_u; + + return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, + offset, nr_segs, exfat_get_block, NULL); +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) +static const char *exfat_follow_link(struct dentry *dentry, struct inode *inode, struct delayed_call *done) +{ + struct exfat_inode_info *ei = EXFAT_I(inode); + + return (char *)(ei->target); +} +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) +static const char *exfat_follow_link(struct dentry *dentry, void **cookie) +{ + struct exfat_inode_info *ei = EXFAT_I(dentry->d_inode); + + return *cookie = (char *)(ei->target); +} +#else +static void *exfat_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + struct exfat_inode_info *ei = EXFAT_I(dentry->d_inode); + + nd_set_link(nd, (char *)(ei->target)); + return NULL; +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) +static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode, + bool excl) +{ + return exfat_create_compat(dir, dentry); +} +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) +static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode, + struct nameidata *nd) +{ + return exfat_create_compat(dir, dentry); +} +#else +static int exfat_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd) +{ + return exfat_create_compat(dir, dentry); +} +#endif + +static inline loff_t exfat_make_i_pos(FILE_ID_T *fid) +{ + return ((loff_t) fid->dir.dir << 32) | (fid->entry & 0xffffffff); +} + +static void exfat_init_namebuf(DENTRY_NAMEBUF_T *nb) +{ + nb->lfn = NULL; + nb->sfn = NULL; + nb->lfnbuf_len = 0; + nb->sfnbuf_len = 0; +} + +static int exfat_alloc_namebuf(DENTRY_NAMEBUF_T *nb) +{ + nb->lfn = __getname(); + if (!nb->lfn) + return -ENOMEM; + nb->sfn = nb->lfn + MAX_VFSNAME_BUF_SIZE; + nb->lfnbuf_len = MAX_VFSNAME_BUF_SIZE; + nb->sfnbuf_len = MAX_VFSNAME_BUF_SIZE; + return 0; +} + +static void exfat_free_namebuf(DENTRY_NAMEBUF_T *nb) +{ + if (!nb->lfn) + return; + + __putname(nb->lfn); + exfat_init_namebuf(nb); +} + +#define EXFAT_DSTATE_LOCKED (void *)(0xCAFE2016) +#define EXFAT_DSTATE_UNLOCKED (void *)(0x00000000) + +static inline void __lock_d_revalidate(struct dentry *dentry) +{ + spin_lock(&dentry->d_lock); + dentry->d_fsdata = EXFAT_DSTATE_LOCKED; + spin_unlock(&dentry->d_lock); +} + +static inline void __unlock_d_revalidate(struct dentry *dentry) +{ + spin_lock(&dentry->d_lock); + dentry->d_fsdata = EXFAT_DSTATE_UNLOCKED; + spin_unlock(&dentry->d_lock); +} + +/* __check_dstate_locked requires dentry->d_lock */ +static inline int __check_dstate_locked(struct dentry *dentry) +{ + if (dentry->d_fsdata == EXFAT_DSTATE_LOCKED) + return 1; + + return 0; +} + +/* + * If new entry was created in the parent, it could create the 8.3 + * alias (the shortname of logname). So, the parent may have the + * negative-dentry which matches the created 8.3 alias. + * + * If it happened, the negative dentry isn't actually negative + * anymore. So, drop it. + */ +static int __exfat_revalidate_common(struct dentry *dentry) +{ + int ret = 1; + + spin_lock(&dentry->d_lock); + if ((!dentry->d_inode) && (!__check_dstate_locked(dentry) && + (dentry->d_time != GET_IVERSION(dentry->d_parent->d_inode)))) { + ret = 0; + } + spin_unlock(&dentry->d_lock); + return ret; +} + +static int __exfat_revalidate(struct dentry *dentry) +{ + /* This is not negative dentry. Always valid. */ + if (dentry->d_inode) + return 1; + return __exfat_revalidate_common(dentry); +} + +static int __exfat_revalidate_ci(struct dentry *dentry, unsigned int flags) +{ + /* + * This is not negative dentry. Always valid. + * + * Note, rename() to existing directory entry will have ->d_inode, + * and will use existing name which isn't specified name by user. + * + * We may be able to drop this positive dentry here. But dropping + * positive dentry isn't good idea. So it's unsupported like + * rename("filename", "FILENAME") for now. + */ + if (dentry->d_inode) + return 1; +#if 0 /* Blocked below code for lookup_one_len() called by stackable FS */ + /* + * This may be nfsd (or something), anyway, we can't see the + * intent of this. So, since this can be for creation, drop it. + */ + if (!flags) + return 0; +#endif + /* + * Drop the negative dentry, in order to make sure to use the + * case sensitive name which is specified by user if this is + * for creation. + */ + if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET)) + return 0; + return __exfat_revalidate_common(dentry); +} + + +/* returns the length of a struct qstr, ignoring trailing dots */ +static unsigned int __exfat_striptail_len(unsigned int len, const char *name) +{ + while (len && name[len - 1] == '.') + len--; + return len; +} + +static unsigned int exfat_striptail_len(const struct qstr *qstr) +{ + return __exfat_striptail_len(qstr->len, qstr->name); +} + +/* + * Compute the hash for the exfat name corresponding to the dentry. + * Note: if the name is invalid, we leave the hash code unchanged so + * that the existing dentry can be used. The exfat fs routines will + * return ENOENT or EINVAL as appropriate. + */ +static int __exfat_d_hash(const struct dentry *dentry, struct qstr *qstr) +{ + unsigned int len = exfat_striptail_len(qstr); + + qstr->hash = __exfat_full_name_hash(dentry, qstr->name, len); + return 0; +} + +/* + * Compute the hash for the exfat name corresponding to the dentry. + * Note: if the name is invalid, we leave the hash code unchanged so + * that the existing dentry can be used. The exfat fs routines will + * return ENOENT or EINVAL as appropriate. + */ +static int __exfat_d_hashi(const struct dentry *dentry, struct qstr *qstr) +{ + struct nls_table *t = EXFAT_SB(dentry->d_sb)->nls_io; + const unsigned char *name; + unsigned int len; + unsigned long hash; + + name = qstr->name; + len = exfat_striptail_len(qstr); + + hash = __exfat_init_name_hash(dentry); + while (len--) + hash = partial_name_hash(nls_tolower(t, *name++), hash); + qstr->hash = end_name_hash(hash); + + return 0; +} + +/* + * Case sensitive compare of two exfat names. + */ +static int __exfat_cmp(const struct dentry *dentry, unsigned int len, + const char *str, const struct qstr *name) +{ + unsigned int alen, blen; + + /* A filename cannot end in '.' or we treat it like it has none */ + alen = exfat_striptail_len(name); + blen = __exfat_striptail_len(len, str); + if (alen == blen) { + if (strncmp(name->name, str, alen) == 0) + return 0; + } + return 1; +} + +/* + * Case insensitive compare of two exfat names. + */ +static int __exfat_cmpi(const struct dentry *dentry, unsigned int len, + const char *str, const struct qstr *name) +{ + struct nls_table *t = EXFAT_SB(dentry->d_sb)->nls_io; + unsigned int alen, blen; + + /* A filename cannot end in '.' or we treat it like it has none */ + alen = exfat_striptail_len(name); + blen = __exfat_striptail_len(len, str); + if (alen == blen) { + if (nls_strnicmp(t, name->name, str, alen) == 0) + return 0; + } + return 1; +} + +static const struct dentry_operations exfat_dentry_ops = { + .d_revalidate = exfat_revalidate, + .d_hash = exfat_d_hash, + .d_compare = exfat_cmp, +}; + +static const struct dentry_operations exfat_ci_dentry_ops = { + .d_revalidate = exfat_revalidate_ci, + .d_hash = exfat_d_hashi, + .d_compare = exfat_cmpi, +}; + +static int exfat_file_mmap(struct file *file, struct vm_area_struct *vm_struct) +{ + return generic_file_mmap(file, vm_struct); +} + +static int exfat_ioctl_volume_id(struct inode *dir) +{ + struct exfat_sb_info *sbi = EXFAT_SB(dir->i_sb); + FS_INFO_T *fsi = &(sbi->fsi); + + return fsi->vol_id; +} + +static long exfat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct inode *inode = file_inode(filp); + + if (cmd != EXFAT_IOCTL_GET_VOLUME_ID) + return -ENOTSUPP; + + return exfat_ioctl_volume_id(inode); +} + +static int __exfat_getattr(struct inode *inode, struct kstat *stat) +{ + generic_fillattr(inode, stat); + stat->blksize = EXFAT_SB(inode->i_sb)->fsi.cluster_size; + + return 0; +} + +static void __exfat_writepage_end_io(struct bio *bio, int err) +{ + struct page *page = bio->bi_io_vec->bv_page; + struct super_block *sb = page->mapping->host->i_sb; + + ASSERT(bio->bi_vcnt == 1); /* Single page endio */ + ASSERT(bio_data_dir(bio)); /* Write */ + + if (err) { + SetPageError(page); + mapping_set_error(page->mapping, err); + } + + end_page_writeback(page); + bio_put(bio); + + // Update trace info. + atomic_dec(&EXFAT_SB(sb)->stat_n_pages_queued); +} + +static int __exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync) +{ + return __exfat_generic_file_fsync(filp, start, end, datasync); +} + +static const struct file_operations exfat_dir_operations = { + .llseek = generic_file_llseek, + .read = generic_read_dir, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + .iterate = exfat_iterate, +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0) */ + .readdir = exfat_readdir, +#endif + .fsync = exfat_file_fsync, + .unlocked_ioctl = exfat_generic_ioctl, +}; + +/* create a file */ +static s32 __exfat_create(struct inode *inode, u8 *path, u8 mode, FILE_ID_T *fid) +{ + s32 err; + struct super_block *sb = inode->i_sb; + + /* check the validity of pointer parameters */ + ASSERT(fid && path); + + if (unlikely(!strlen(path))) + return -EINVAL; + + mutex_lock(&(EXFAT_SB(sb)->s_vlock)); + err = exfat_fscore_create(inode, path, mode, fid); + mutex_unlock(&(EXFAT_SB(sb)->s_vlock)); + return err; +} + +static int exfat_create_compat(struct inode *dir, struct dentry *dentry) +{ + struct super_block *sb = dir->i_sb; + struct inode *inode; + FILE_ID_T fid; + loff_t i_pos; + int err; + + lock_super(sb); + + err = __exfat_create(dir, (u8 *) dentry->d_name.name, FM_REGULAR, &fid); + if (err) + goto out; + + __lock_d_revalidate(dentry); + + INC_IVERSION(dir); + dir->i_ctime = dir->i_mtime = dir->i_atime = current_time(dir); + if (IS_DIRSYNC(dir)) + (void) exfat_sync_inode(dir); + else + mark_inode_dirty(dir); + + i_pos = exfat_make_i_pos(&fid); + inode = exfat_build_inode(sb, &fid, i_pos); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } + INC_IVERSION(inode); + inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); + /* timestamp is already written, so mark_inode_dirty() is unneeded. */ + + d_instantiate(dentry, inode); +out: + __unlock_d_revalidate(dentry); + unlock_super(sb); + + return err; +} + + +static int exfat_find(struct inode *dir, struct qstr *qname, FILE_ID_T *fid) +{ + struct super_block *sb = dir->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + u8 *path = (u8 *)qname->name; + int err; + + if (qname->len == 0) + return -ENOENT; + + /* check the validity of pointer parameters */ + ASSERT(fid && path); + + if (unlikely(!strlen(path))) + return -EINVAL; + + mutex_lock(&(sbi->s_vlock)); + err = exfat_fscore_lookup(dir, path, fid); + mutex_unlock(&(sbi->s_vlock)); + if (err) + return -ENOENT; + + return 0; +} + +static int exfat_d_anon_disconn(struct dentry *dentry) +{ + return IS_ROOT(dentry) && (dentry->d_flags & DCACHE_DISCONNECTED); +} + +static struct dentry *__exfat_lookup(struct inode *dir, struct dentry *dentry) +{ + struct super_block *sb = dir->i_sb; + struct inode *inode; + struct dentry *alias; + int err; + FILE_ID_T fid; + loff_t i_pos; + u64 ret; + mode_t i_mode; + + lock_super(sb); + err = exfat_find(dir, &dentry->d_name, &fid); + if (err) { + if (err == -ENOENT) { + inode = NULL; + goto out; + } + goto error; + } + + i_pos = exfat_make_i_pos(&fid); + inode = exfat_build_inode(sb, &fid, i_pos); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto error; + } + + i_mode = inode->i_mode; + if (S_ISLNK(i_mode) && !EXFAT_I(inode)->target) { + EXFAT_I(inode)->target = kmalloc((i_size_read(inode)+1), GFP_KERNEL); + if (!EXFAT_I(inode)->target) { + err = -ENOMEM; + goto error; + } + exfat_read_link(dir, &fid, EXFAT_I(inode)->target, i_size_read(inode), &ret); + *(EXFAT_I(inode)->target + i_size_read(inode)) = '\0'; + } + + alias = d_find_alias(inode); + + /* + * Checking "alias->d_parent == dentry->d_parent" to make sure + * FS is not corrupted (especially double linked dir). + */ + if (alias && alias->d_parent == dentry->d_parent && + !exfat_d_anon_disconn(alias)) { + + /* + * Unhashed alias is able to exist because of revalidate() + * called by lookup_fast. You can easily make this status + * by calling create and lookup concurrently + * In such case, we reuse an alias instead of new dentry + */ + if (d_unhashed(alias)) { + exfat_msg(sb, KERN_INFO, "rehashed a dentry(%p) " + "in read lookup", alias); + d_drop(dentry); + d_rehash(alias); + } else if (!S_ISDIR(i_mode)) { + /* + * This inode has non anonymous-DCACHE_DISCONNECTED + * dentry. This means, the user did ->lookup() by an + * another name (longname vs 8.3 alias of it) in past. + * + * Switch to new one for reason of locality if possible. + */ + d_move(alias, dentry); + } + iput(inode); + unlock_super(sb); + return alias; + } + dput(alias); +out: + /* initialize d_time even though it is positive dentry */ + dentry->d_time = GET_IVERSION(dir); + unlock_super(sb); + + dentry = d_splice_alias(inode, dentry); + + return dentry; + +error: + unlock_super(sb); + return ERR_PTR(err); +} + + +static int exfat_unlink(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + struct super_block *sb = dir->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + int err; + + lock_super(sb); + + EXFAT_I(inode)->fid.size = i_size_read(inode); + + /* check the validity of pointer parameters */ + ASSERT(fid); + + mutex_lock(&(sbi->s_vlock)); + err = exfat_fscore_unlink(dir, fid); + mutex_unlock(&(sbi->s_vlock)); + if (err) + goto out; + + __lock_d_revalidate(dentry); + + INC_IVERSION(dir); + dir->i_mtime = dir->i_atime = current_time(dir); + if (IS_DIRSYNC(dir)) + (void) exfat_sync_inode(dir); + else + mark_inode_dirty(dir); + + clear_nlink(inode); + inode->i_mtime = inode->i_atime = current_time(inode); + exfat_detach(inode); + dentry->d_time = GET_IVERSION(dir); +out: + __unlock_d_revalidate(dentry); + unlock_super(sb); + return err; +} + +static int exfat_symlink(struct inode *dir, struct dentry *dentry, const char *target) +{ + struct super_block *sb = dir->i_sb; + struct inode *inode; + FILE_ID_T fid; + loff_t i_pos; + int err; + u64 len = (u64) strlen(target); + u64 ret; + + /* symlink option check */ + if (!EXFAT_SB(sb)->options.symlink) + return -ENOTSUPP; + + lock_super(sb); + + err = __exfat_create(dir, (u8 *) dentry->d_name.name, FM_SYMLINK, &fid); + if (err) + goto out; + + err = exfat_write_link(dir, &fid, (char *) target, len, &ret); + + if (err) { + exfat_remove(dir, &fid); + goto out; + } + + __lock_d_revalidate(dentry); + + INC_IVERSION(dir); + dir->i_ctime = dir->i_mtime = dir->i_atime = current_time(dir); + if (IS_DIRSYNC(dir)) + (void) exfat_sync_inode(dir); + else + mark_inode_dirty(dir); + + i_pos = exfat_make_i_pos(&fid); + inode = exfat_build_inode(sb, &fid, i_pos); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } + INC_IVERSION(inode); + inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); + /* timestamp is already written, so mark_inode_dirty() is unneeded. */ + + EXFAT_I(inode)->target = kmemdup(target, len + 1, GFP_KERNEL); + if (!EXFAT_I(inode)->target) { + err = -ENOMEM; + goto out; + } + + d_instantiate(dentry, inode); +out: + __unlock_d_revalidate(dentry); + unlock_super(sb); + return err; +} + +static int __exfat_mkdir(struct inode *dir, struct dentry *dentry) +{ + struct super_block *sb = dir->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct inode *inode; + FILE_ID_T fid; + loff_t i_pos; + u8 *path; + int err; + + lock_super(sb); + + path = (u8 *) dentry->d_name.name; + + /* check the validity of pointer parameters */ + ASSERT(path); + + if (unlikely(!strlen(path))) { + err = -EINVAL; + goto out; + } + + mutex_lock(&(sbi->s_vlock)); + err = exfat_fscore_mkdir(dir, path, &fid); + mutex_unlock(&(sbi->s_vlock)); + if (err) + goto out; + + __lock_d_revalidate(dentry); + + INC_IVERSION(dir); + dir->i_ctime = dir->i_mtime = dir->i_atime = current_time(dir); + if (IS_DIRSYNC(dir)) + (void) exfat_sync_inode(dir); + else + mark_inode_dirty(dir); + inc_nlink(dir); + + i_pos = exfat_make_i_pos(&fid); + inode = exfat_build_inode(sb, &fid, i_pos); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } + INC_IVERSION(inode); + inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); + /* timestamp is already written, so mark_inode_dirty() is unneeded. */ + + d_instantiate(dentry, inode); + +out: + __unlock_d_revalidate(dentry); + unlock_super(sb); + + return err; +} + + +static int exfat_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + struct super_block *sb = dir->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + int err; + + lock_super(sb); + + EXFAT_I(inode)->fid.size = i_size_read(inode); + + /* check the validity of pointer parameters */ + ASSERT(fid); + + mutex_lock(&(sbi->s_vlock)); + err = exfat_fscore_rmdir(inode, fid); + mutex_unlock(&(sbi->s_vlock)); + if (err) + goto out; + + __lock_d_revalidate(dentry); + + INC_IVERSION(dir); + dir->i_mtime = dir->i_atime = current_time(dir); + if (IS_DIRSYNC(dir)) + (void) exfat_sync_inode(dir); + else + mark_inode_dirty(dir); + drop_nlink(dir); + + clear_nlink(inode); + inode->i_mtime = inode->i_atime = current_time(inode); + exfat_detach(inode); + dentry->d_time = GET_IVERSION(dir); +out: + __unlock_d_revalidate(dentry); + unlock_super(sb); + return err; +} + +static int __exfat_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + struct inode *old_inode, *new_inode; + struct super_block *sb = old_dir->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + loff_t i_pos; + int err; + + lock_super(sb); + + old_inode = old_dentry->d_inode; + new_inode = new_dentry->d_inode; + + EXFAT_I(old_inode)->fid.size = i_size_read(old_inode); + + /* check the validity of pointer parameters */ + ASSERT(&(EXFAT_I(old_inode)->fid)); + + mutex_lock(&(sbi->s_vlock)); + err = exfat_fscore_rename(old_dir, &(EXFAT_I(old_inode)->fid), + new_dir, new_dentry); + mutex_unlock(&(sbi->s_vlock)); + if (err) + goto out; + + __lock_d_revalidate(old_dentry); + __lock_d_revalidate(new_dentry); + + INC_IVERSION(new_dir); + new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime = current_time(new_dir); + if (IS_DIRSYNC(new_dir)) + (void) exfat_sync_inode(new_dir); + else + mark_inode_dirty(new_dir); + + i_pos = exfat_make_i_pos(&(EXFAT_I(old_inode)->fid)); + exfat_detach(old_inode); + exfat_attach(old_inode, i_pos); + if (IS_DIRSYNC(new_dir)) + (void) exfat_sync_inode(old_inode); + else + mark_inode_dirty(old_inode); + + if ((S_ISDIR(old_inode->i_mode)) && (old_dir != new_dir)) { + drop_nlink(old_dir); + if (!new_inode) + inc_nlink(new_dir); + } + + INC_IVERSION(old_dir); + old_dir->i_ctime = old_dir->i_mtime = current_time(old_dir); + if (IS_DIRSYNC(old_dir)) + (void) exfat_sync_inode(old_dir); + else + mark_inode_dirty(old_dir); + + if (new_inode) { + exfat_detach(new_inode); + + /* skip drop_nlink if new_inode already has been dropped */ + if (new_inode->i_nlink) { + drop_nlink(new_inode); + if (S_ISDIR(new_inode->i_mode)) + drop_nlink(new_inode); + } else { + EMSG("%s : abnormal access to an inode dropped\n", + __func__); + WARN_ON(new_inode->i_nlink == 0); + } + new_inode->i_ctime = current_time(new_inode); +#if 0 + (void) exfat_sync_inode(new_inode); +#endif + } + +out: + __unlock_d_revalidate(old_dentry); + __unlock_d_revalidate(new_dentry); + unlock_super(sb); + return err; +} + +static int exfat_cont_expand(struct inode *inode, loff_t size) +{ + struct address_space *mapping = inode->i_mapping; + loff_t start = i_size_read(inode), count = size - i_size_read(inode); + int err, err2; + + err = generic_cont_expand_simple(inode, size); + if (err) + return err; + + inode->i_ctime = inode->i_mtime = current_time(inode); + mark_inode_dirty(inode); + + if (!IS_SYNC(inode)) + return 0; + + err = filemap_fdatawrite_range(mapping, start, start + count - 1); + err2 = sync_mapping_buffers(mapping); + err = (err)?(err):(err2); + err2 = write_inode_now(inode, 1); + err = (err)?(err):(err2); + if (err) + return err; + + return filemap_fdatawait_range(mapping, start, start + count - 1); +} + +static int exfat_allow_set_time(struct exfat_sb_info *sbi, struct inode *inode) +{ + mode_t allow_utime = sbi->options.allow_utime; + + if (!uid_eq(current_fsuid(), inode->i_uid)) { + if (in_group_p(inode->i_gid)) + allow_utime >>= 3; + if (allow_utime & MAY_WRITE) + return 1; + } + + /* use a default check */ + return 0; +} + +static int exfat_sanitize_mode(const struct exfat_sb_info *sbi, + struct inode *inode, umode_t *mode_ptr) +{ + mode_t i_mode, mask, perm; + + i_mode = inode->i_mode; + + if (S_ISREG(i_mode) || S_ISLNK(i_mode)) + mask = sbi->options.fs_fmask; + else + mask = sbi->options.fs_dmask; + + perm = *mode_ptr & ~(S_IFMT | mask); + + /* Of the r and x bits, all (subject to umask) must be present.*/ + if ((perm & (S_IRUGO | S_IXUGO)) != (i_mode & (S_IRUGO | S_IXUGO))) + return -EPERM; + + if (exfat_mode_can_hold_ro(inode)) { + /* Of the w bits, either all (subject to umask) or none must be present. */ + if ((perm & S_IWUGO) && ((perm & S_IWUGO) != (S_IWUGO & ~mask))) + return -EPERM; + } else { + /* If exfat_mode_can_hold_ro(inode) is false, can't change w bits. */ + if ((perm & S_IWUGO) != (S_IWUGO & ~mask)) + return -EPERM; + } + + *mode_ptr &= S_IFMT | perm; + + return 0; +} + +static int exfat_setattr(struct dentry *dentry, struct iattr *attr) +{ + + struct exfat_sb_info *sbi = EXFAT_SB(dentry->d_sb); + struct inode *inode = dentry->d_inode; + unsigned int ia_valid; + int error; + loff_t old_size; + + if ((attr->ia_valid & ATTR_SIZE) + && (attr->ia_size > i_size_read(inode))) { + error = exfat_cont_expand(inode, attr->ia_size); + if (error || attr->ia_valid == ATTR_SIZE) + goto out; + attr->ia_valid &= ~ATTR_SIZE; + } + + /* Check for setting the inode time. */ + ia_valid = attr->ia_valid; + if ((ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) + && exfat_allow_set_time(sbi, inode)) { + attr->ia_valid &= ~(ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET); + } + + error = setattr_prepare(dentry, attr); + attr->ia_valid = ia_valid; + if (error) { + if (sbi->options.quiet) + error = 0; + goto out; + } + + if (((attr->ia_valid & ATTR_UID) && + (!uid_eq(attr->ia_uid, sbi->options.fs_uid))) || + ((attr->ia_valid & ATTR_GID) && + (!gid_eq(attr->ia_gid, sbi->options.fs_gid))) || + ((attr->ia_valid & ATTR_MODE) && + (attr->ia_mode & ~(S_IFREG | S_IFLNK | S_IFDIR | S_IRWXUGO)))) { + error = -EPERM; + } + + if (error) { + if (sbi->options.quiet) + error = 0; + goto out; + } + + /* + * We don't return -EPERM here. Yes, strange, but this is too + * old behavior. + */ + if (attr->ia_valid & ATTR_MODE) { + if (exfat_sanitize_mode(sbi, inode, &attr->ia_mode) < 0) + attr->ia_valid &= ~ATTR_MODE; + } + + EXFAT_I(inode)->fid.size = i_size_read(inode); + + /* patch 1.2.0 : fixed the problem of size mismatch. */ + if (attr->ia_valid & ATTR_SIZE) { + old_size = i_size_read(inode); + + /* TO CHECK evicting directory works correctly */ + MMSG("%s: inode(%p) truncate size (%llu->%llu)\n", __func__, + inode, (u64)old_size, (u64)attr->ia_size); + __exfat_do_truncate(inode, old_size, attr->ia_size); + } + setattr_copy(inode, attr); + mark_inode_dirty(inode); + +out: + return error; +} + +static const struct inode_operations exfat_dir_inode_operations = { + .create = exfat_create, + .lookup = exfat_lookup, + .unlink = exfat_unlink, + .symlink = exfat_symlink, + .mkdir = exfat_mkdir, + .rmdir = exfat_rmdir, + .rename = exfat_rename, + .setattr = exfat_setattr, + .getattr = exfat_getattr, +#ifdef CONFIG_EXFAT_VIRTUAL_XATTR + .listxattr = exfat_listxattr, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) + .setxattr = exfat_setxattr, + .getxattr = exfat_getxattr, + .removexattr = exfat_removexattr, +#endif +#endif +}; + +static const struct inode_operations exfat_symlink_inode_operations = { +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) + .readlink = generic_readlink, +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + .get_link = exfat_follow_link, +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) */ + .follow_link = exfat_follow_link, +#endif +#ifdef CONFIG_EXFAT_VIRTUAL_XATTR + .listxattr = exfat_listxattr, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) + .setxattr = exfat_setxattr, + .getxattr = exfat_getxattr, + .removexattr = exfat_removexattr, +#endif +#endif +}; + +static int exfat_file_release(struct inode *inode, struct file *filp) +{ + struct super_block *sb = inode->i_sb; + + /* Moved below code from exfat_write_inode + * TO FIX size-mismatch problem. + */ + /* FIXME : Added bug_on to confirm that there is no size mismatch */ + exfat_debug_bug_on(EXFAT_I(inode)->fid.size != i_size_read(inode)); + EXFAT_I(inode)->fid.size = i_size_read(inode); + __exfat_sync_fs(sb, 0); + return 0; +} + +static const struct file_operations exfat_file_operations = { + .llseek = generic_file_llseek, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + .read_iter = generic_file_read_iter, + .write_iter = generic_file_write_iter, +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + .read = new_sync_read, + .write = new_sync_write, + .read_iter = generic_file_read_iter, + .write_iter = generic_file_write_iter, +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0) */ + .read = do_sync_read, + .write = do_sync_write, + .aio_read = generic_file_aio_read, + .aio_write = generic_file_aio_write, +#endif + .mmap = exfat_file_mmap, + .release = exfat_file_release, + .unlocked_ioctl = exfat_generic_ioctl, + .fsync = exfat_file_fsync, + .splice_read = generic_file_splice_read, +}; + +static const struct address_space_operations exfat_aops; + +/* resize the file length */ +static s32 __exfat_truncate(struct inode *inode, u64 old_size, u64 new_size) +{ + s32 err; + struct super_block *sb = inode->i_sb; + + mutex_lock(&(EXFAT_SB(sb)->s_vlock)); + err = exfat_fscore_truncate(inode, old_size, new_size); + mutex_unlock(&(EXFAT_SB(sb)->s_vlock)); + return err; +} + +static void exfat_truncate(struct inode *inode, loff_t old_size) +{ + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + FS_INFO_T *fsi = &(sbi->fsi); + unsigned int blocksize = 1 << inode->i_blkbits; + loff_t aligned_size; + int err; + + lock_super(sb); + + if (EXFAT_I(inode)->fid.start_clu == 0) { + /* Stange statement: + * Empty start_clu != ~0 (not allocated) + */ + exfat_fs_error(sb, "tried to truncate zeroed cluster."); + goto out; + } + + err = __exfat_truncate(inode, old_size, i_size_read(inode)); + if (err) + goto out; + + inode->i_ctime = inode->i_mtime = current_time(inode); + if (IS_DIRSYNC(inode)) + (void) exfat_sync_inode(inode); + else + mark_inode_dirty(inode); + + // FIXME: Please check + // inode->i_blocks = ((EXFAT_I(inode)->i_size_ondisk + (fsi->cluster_size - 1)) + inode->i_blocks = ((i_size_read(inode) + (fsi->cluster_size - 1)) & + ~((loff_t)fsi->cluster_size - 1)) >> inode->i_blkbits; +out: + /* + * This protects against truncating a file bigger than it was then + * trying to write into the hole. + * + * comment by sh.hong: + * This seems to mean 'intra page/block' truncate and writing. + * I couldn't find a reason to change the values prior to __exfat_truncate + * Therefore, I switched the order of operations + * so that it's possible to utilize i_size_ondisk in __exfat_truncate + */ + + aligned_size = i_size_read(inode); + if (aligned_size & (blocksize - 1)) { + aligned_size |= (blocksize - 1); + aligned_size++; + } + + if (EXFAT_I(inode)->i_size_ondisk > i_size_read(inode)) + EXFAT_I(inode)->i_size_ondisk = aligned_size; + + if (EXFAT_I(inode)->i_size_aligned > i_size_read(inode)) + EXFAT_I(inode)->i_size_aligned = aligned_size; + + /* After truncation : + * 1) Delayed allocation is OFF + * i_size = i_size_ondisk <= i_size_aligned + * (useless size var.) + * (block-aligned) + * 2) Delayed allocation is ON + * i_size = i_size_ondisk = i_size_aligned + * (will be block-aligned after write) + * or + * i_size_ondisk < i_size <= i_size_aligned (block_aligned) + * (will be block-aligned after write) + */ + + unlock_super(sb); +} + +static const struct inode_operations exfat_file_inode_operations = { + .setattr = exfat_setattr, + .getattr = exfat_getattr, +#ifdef CONFIG_EXFAT_VIRTUAL_XATTR + .listxattr = exfat_listxattr, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) + .setxattr = exfat_setxattr, + .getxattr = exfat_getxattr, + .removexattr = exfat_removexattr, +#endif +#endif +}; + +/* 2-level option flag */ +#define BMAP_NOT_CREATE 0 +#define BMAP_ADD_BLOCK 1 +#define BMAP_ADD_CLUSTER 2 +#define BLOCK_ADDED(bmap_ops) (bmap_ops) +static int exfat_bmap(struct inode *inode, sector_t sector, sector_t *phys, + unsigned long *mapped_blocks, int *create) +{ + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + FS_INFO_T *fsi = &(sbi->fsi); + const unsigned long blocksize = sb->s_blocksize; + const unsigned char blocksize_bits = sb->s_blocksize_bits; + sector_t last_block; + unsigned int cluster, clu_offset, sec_offset; + int err = 0; + + *phys = 0; + *mapped_blocks = 0; + + /* core code should handle EIO */ +#if 0 + if (fsi->prev_eio && BLOCK_ADDED(*create)) + return -EIO; +#endif + + last_block = (i_size_read(inode) + (blocksize - 1)) >> blocksize_bits; + if ((sector >= last_block) && (*create == BMAP_NOT_CREATE)) + return 0; + + /* Is this block already allocated? */ + clu_offset = sector >> fsi->sect_per_clus_bits; /* cluster offset */ + + EXFAT_I(inode)->fid.size = i_size_read(inode); + + if (*create & BMAP_ADD_CLUSTER) + err = exfat_map_clus(inode, clu_offset, &cluster, 1); + else + err = exfat_map_clus(inode, clu_offset, &cluster, ALLOC_NOWHERE); + + if (err) { + if (err != -ENOSPC) + return -EIO; + return err; + } + + if (!IS_CLUS_EOF(cluster)) { + /* sector offset in cluster */ + sec_offset = sector & (fsi->sect_per_clus - 1); + + *phys = CLUS_TO_SECT(fsi, cluster) + sec_offset; + *mapped_blocks = fsi->sect_per_clus - sec_offset; + } +#if 0 + else { + /* Debug purpose (new clu needed) */ + ASSERT((*create & BMAP_ADD_CLUSTER) == 0); + ASSERT(sector >= last_block); + } +#endif + + if (sector < last_block) + *create = BMAP_NOT_CREATE; +#if 0 + else if (sector >= last_block) + *create = non-zero; + + if (iblock <= last mapped-block) + *phys != 0 + *create = BMAP_NOT_CREATE + else if (iblock <= last cluster) + *phys != 0 + *create = non-zero +#endif + return 0; +} + +static int exfat_get_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create) +{ + struct super_block *sb = inode->i_sb; + unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits; + int err = 0; + unsigned long mapped_blocks; + sector_t phys; + loff_t pos; + int bmap_create = create ? BMAP_ADD_CLUSTER : BMAP_NOT_CREATE; + + lock_super(sb); + err = exfat_bmap(inode, iblock, &phys, &mapped_blocks, &bmap_create); + if (err) { + if (err != -ENOSPC) + exfat_fs_error_ratelimit(sb, "%s: failed to bmap " + "(inode:%p iblock:%u, err:%d)", + __func__, inode, (u32)iblock, err); + goto unlock_ret; + } + + if (phys) { + max_blocks = min(mapped_blocks, max_blocks); + + /* Treat newly added block / cluster */ + if (BLOCK_ADDED(bmap_create) || buffer_delay(bh_result)) { + + /* Update i_size_ondisk */ + pos = (iblock + 1) << sb->s_blocksize_bits; + if (EXFAT_I(inode)->i_size_ondisk < pos) { + /* Debug purpose */ + if ((pos - EXFAT_I(inode)->i_size_ondisk) > bh_result->b_size) { + /* This never happens without DA */ + MMSG("Jumping get_block\n"); + } + + EXFAT_I(inode)->i_size_ondisk = pos; + } + + if (BLOCK_ADDED(bmap_create)) { + /* Old way (w/o DA) + * create == 1 only if iblock > i_size + * (in block unit) + */ + + /* 20130723 CHECK + * If happened concurrently with truncation, + * buffer_delay() can be left on whilst + * i_size < (position of i_block). + * + * It's not a big deal as we're re-writing + * previously allocated blocks, but in such cases, + * it has to be an area with i_size_aligned expanded + * beforehand. + */ + + /* FOR GRACEFUL ERROR HANDLING */ + if (buffer_delay(bh_result) && + (pos > EXFAT_I(inode)->i_size_aligned)) { + exfat_fs_error(sb, "requested for bmap " + "out of range(pos:(%llu)>i_size_aligned(%llu)\n", + pos, EXFAT_I(inode)->i_size_aligned); + exfat_debug_bug_on(1); + err = -EIO; + goto unlock_ret; + } + set_buffer_new(bh_result); + + /* + * adjust i_size_aligned if i_size_ondisk is + * bigger than it. (i.e. non-DA) + */ + if (EXFAT_I(inode)->i_size_ondisk > + EXFAT_I(inode)->i_size_aligned) { + EXFAT_I(inode)->i_size_aligned = + EXFAT_I(inode)->i_size_ondisk; + } + } + + if (buffer_delay(bh_result)) + clear_buffer_delay(bh_result); + +#if 0 + /* Debug purpose */ + if (EXFAT_I(inode)->i_size_ondisk > + EXFAT_I(inode)->i_size_aligned) { + /* Only after truncate + * and the two size variables should indicate + * same i_block + */ + unsigned int blocksize = 1 << inode->i_blkbits; + BUG_ON(EXFAT_I(inode)->i_size_ondisk - + EXFAT_I(inode)->i_size_aligned >= blocksize); + } +#endif + } + map_bh(bh_result, sb, phys); + } + + bh_result->b_size = max_blocks << sb->s_blocksize_bits; +unlock_ret: + unlock_super(sb); + return err; +} + +static int exfat_readpage(struct file *file, struct page *page) +{ + int ret; + + ret = mpage_readpage(page, exfat_get_block); + return ret; +} + +static int exfat_readpages(struct file *file, struct address_space *mapping, + struct list_head *pages, unsigned int nr_pages) +{ + int ret; + + ret = mpage_readpages(mapping, pages, nr_pages, exfat_get_block); + return ret; +} + +static inline void exfat_submit_fullpage_bio(struct block_device *bdev, + sector_t sector, unsigned int length, struct page *page) +{ + /* Single page bio submit */ + struct bio *bio; + + BUG_ON((length > PAGE_SIZE) || (length == 0)); + + /* + * If __GFP_WAIT is set, then bio_alloc will always be able to allocate + * a bio. This is due to the mempool guarantees. To make this work, callers + * must never allocate more than 1 bio at a time from this pool. + * + * #define GFP_NOIO (__GFP_WAIT) + */ + bio = bio_alloc(GFP_NOIO, 1); + + bio_set_dev(bio, bdev); + bio->bi_vcnt = 1; + bio->bi_io_vec[0].bv_page = page; /* Inline vec */ + bio->bi_io_vec[0].bv_len = length; /* PAGE_SIZE */ + bio->bi_io_vec[0].bv_offset = 0; + __exfat_set_bio_iterate(bio, sector, length, 0, 0); + + bio->bi_end_io = exfat_writepage_end_io; + __exfat_submit_bio_write(bio); +} + +static int exfat_writepage(struct page *page, struct writeback_control *wbc) +{ + struct inode * const inode = page->mapping->host; + struct super_block *sb = inode->i_sb; + loff_t i_size = i_size_read(inode); + const pgoff_t end_index = i_size >> PAGE_SHIFT; + const unsigned int blocks_per_page = PAGE_SIZE >> inode->i_blkbits; + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + struct buffer_head *bh, *head; + sector_t block, block_0, last_phys; + int ret; + unsigned int nr_blocks_towrite = blocks_per_page; + + /* Don't distinguish 0-filled/clean block. + * Just write back the whole page + */ + if (fsi->cluster_size < PAGE_SIZE) + goto confused; + + if (!PageUptodate(page)) { + MMSG("%s: Not up-to-date page -> block_write_full_page\n", + __func__); + goto confused; + } + + if (page->index >= end_index) { + /* last page or outside i_size */ + unsigned int offset = i_size & (PAGE_SIZE-1); + + /* If a truncation is in progress */ + if (page->index > end_index || !offset) + goto confused; + + /* 0-fill after i_size */ + zero_user_segment(page, offset, PAGE_SIZE); + } + + if (!page_has_buffers(page)) { + MMSG("WP: No buffers -> block_write_full_page\n"); + goto confused; + } + + block = (sector_t)page->index << (PAGE_SHIFT - inode->i_blkbits); + block_0 = block; /* first block */ + head = page_buffers(page); + bh = head; + + last_phys = 0; + do { + BUG_ON(buffer_locked(bh)); + + if (!buffer_dirty(bh) || !buffer_uptodate(bh)) { + if (nr_blocks_towrite == blocks_per_page) + nr_blocks_towrite = (unsigned int) (block - block_0); + + BUG_ON(nr_blocks_towrite >= blocks_per_page); + + // !uptodate but dirty?? + if (buffer_dirty(bh)) + goto confused; + + // Nothing to writeback in this block + bh = bh->b_this_page; + block++; + continue; + } + + if (nr_blocks_towrite != blocks_per_page) + // Dirty -> Non-dirty -> Dirty again case + goto confused; + + /* Map if needed */ + if (!buffer_mapped(bh) || buffer_delay(bh)) { + BUG_ON(bh->b_size != (1 << (inode->i_blkbits))); + ret = exfat_get_block(inode, block, bh, 1); + if (ret) + goto confused; + + if (buffer_new(bh)) { + clear_buffer_new(bh); + __exfat_clean_bdev_aliases(bh->b_bdev, bh->b_blocknr); + } + } + + /* continuity check */ + if (((last_phys + 1) != bh->b_blocknr) && (last_phys != 0)) { + DMSG("Non-contiguous block mapping in single page"); + goto confused; + } + + last_phys = bh->b_blocknr; + bh = bh->b_this_page; + block++; + } while (bh != head); + + if (nr_blocks_towrite == 0) { + DMSG("Page dirty but no dirty bh? alloc_208\n"); + goto confused; + } + + + /* Write-back */ + do { + clear_buffer_dirty(bh); + bh = bh->b_this_page; + } while (bh != head); + + BUG_ON(PageWriteback(page)); + set_page_writeback(page); + + // Trace # of pages queued (Approx.) + atomic_inc(&EXFAT_SB(sb)->stat_n_pages_queued); + + exfat_submit_fullpage_bio(head->b_bdev, + head->b_blocknr << (sb->s_blocksize_bits - SECTOR_SIZE_BITS), + nr_blocks_towrite << inode->i_blkbits, + page); + + unlock_page(page); + + return 0; + +confused: + ret = block_write_full_page(page, exfat_get_block, wbc); + return ret; +} + +static int exfat_writepages(struct address_space *mapping, + struct writeback_control *wbc) +{ + MMSG("%s(inode:%p) with nr_to_write = 0x%08lx " + "(ku %d, bg %d, tag %d, rc %d )\n", + __func__, mapping->host, wbc->nr_to_write, + wbc->for_kupdate, wbc->for_background, wbc->tagged_writepages, + wbc->for_reclaim); + + ASSERT(mapping->a_ops == &exfat_aops); + + return mpage_writepages(mapping, wbc, exfat_get_block); +} + +static void exfat_write_failed(struct address_space *mapping, loff_t to) +{ + struct inode *inode = mapping->host; + + if (to > i_size_read(inode)) { + __exfat_truncate_pagecache(inode, to, i_size_read(inode)); + exfat_truncate(inode, EXFAT_I(inode)->i_size_aligned); + } +} + +static int exfat_check_writable(struct super_block *sb) +{ + if (exfat_fscore_check_bdi_valid(sb)) + return -EIO; + + if (EXFAT_IS_SB_RDONLY(sb)) + return -EROFS; + + return 0; +} + +static int __exfat_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned int len, + unsigned int flags, struct page **pagep, + void **fsdata, get_block_t *get_block, + loff_t *bytes, const char *fname) +{ + struct super_block *sb = mapping->host->i_sb; + int ret; + + ret = exfat_check_writable(sb); + if (unlikely(ret < 0)) + return ret; + + *pagep = NULL; + ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, + get_block, bytes); + + if (ret < 0) + exfat_write_failed(mapping, pos+len); + + return ret; +} + +static int exfat_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned int len, unsigned int flags, + struct page **pagep, void **fsdata) +{ + return __exfat_write_begin(file, mapping, pos, len, flags, + pagep, fsdata, exfat_get_block, + &EXFAT_I(mapping->host)->i_size_ondisk, + __func__); +} + +static int exfat_write_end(struct file *file, struct address_space *mapping, + loff_t pos, unsigned int len, unsigned int copied, + struct page *pagep, void *fsdata) +{ + struct inode *inode = mapping->host; + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + int err; + + err = generic_write_end(file, mapping, pos, len, copied, pagep, fsdata); + + /* FOR GRACEFUL ERROR HANDLING */ + if (EXFAT_I(inode)->i_size_aligned < i_size_read(inode)) { + exfat_fs_error(inode->i_sb, "invalid size(size(%llu) " + "> aligned(%llu)\n", i_size_read(inode), + EXFAT_I(inode)->i_size_aligned); + exfat_debug_bug_on(1); + } + + if (err < len) + exfat_write_failed(mapping, pos+len); + + if (!(err < 0) && !(fid->attr & ATTR_ARCHIVE)) { + inode->i_mtime = inode->i_ctime = current_time(inode); + fid->attr |= ATTR_ARCHIVE; + mark_inode_dirty(inode); + } + + return err; +} + +static inline ssize_t __exfat_direct_IO(int rw, struct kiocb *iocb, + struct inode *inode, void *iov_u, loff_t offset, + loff_t count, unsigned long nr_segs) +{ + struct address_space *mapping = inode->i_mapping; + loff_t size = offset + count; + ssize_t ret; + + if (rw == WRITE) { + /* + * FIXME: blockdev_direct_IO() doesn't use ->write_begin(), + * so we need to update the ->i_size_aligned to block boundary. + * + * But we must fill the remaining area or hole by nul for + * updating ->i_size_aligned + * + * Return 0, and fallback to normal buffered write. + */ + if (EXFAT_I(inode)->i_size_aligned < size) + return 0; + } + + /* + * exFAT need to use the DIO_LOCKING for avoiding the race + * condition of exfat_get_block() and ->truncate(). + */ + ret = __exfat_blkdev_direct_IO(rw, iocb, inode, iov_u, offset, nr_segs); + if (ret < 0 && (rw & WRITE)) + exfat_write_failed(mapping, size); + + return ret; +} + +static const struct address_space_operations exfat_aops = { + .readpage = exfat_readpage, + .readpages = exfat_readpages, + .writepage = exfat_writepage, + .writepages = exfat_writepages, + .write_begin = exfat_write_begin, + .write_end = exfat_write_end, + .direct_IO = exfat_direct_IO, + .bmap = exfat_aop_bmap +}; + +static inline unsigned long exfat_hash(loff_t i_pos) +{ + return hash_32(i_pos, EXFAT_HASH_BITS); +} + +static void exfat_attach(struct inode *inode, loff_t i_pos) +{ + struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); + struct hlist_head *head = sbi->inode_hashtable + exfat_hash(i_pos); + + spin_lock(&sbi->inode_hash_lock); + EXFAT_I(inode)->i_pos = i_pos; + hlist_add_head(&EXFAT_I(inode)->i_hash_fat, head); + spin_unlock(&sbi->inode_hash_lock); +} + +static void exfat_detach(struct inode *inode) +{ + struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); + + spin_lock(&sbi->inode_hash_lock); + hlist_del_init(&EXFAT_I(inode)->i_hash_fat); + EXFAT_I(inode)->i_pos = 0; + spin_unlock(&sbi->inode_hash_lock); +} + + +/* doesn't deal with root inode */ +static int exfat_fill_inode(struct inode *inode, const FILE_ID_T *fid) +{ + struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); + FS_INFO_T *fsi = &(sbi->fsi); + DIR_ENTRY_T info; + u64 size = fid->size; + + memcpy(&(EXFAT_I(inode)->fid), fid, sizeof(FILE_ID_T)); + + EXFAT_I(inode)->i_pos = 0; + EXFAT_I(inode)->target = NULL; + inode->i_uid = sbi->options.fs_uid; + inode->i_gid = sbi->options.fs_gid; + INC_IVERSION(inode); + inode->i_generation = get_seconds(); + + if (exfat_read_inode(inode, &info) < 0) { + MMSG("%s: failed to read stat!\n", __func__); + return -EIO; + } + + if (info.Attr & ATTR_SUBDIR) { /* directory */ + inode->i_generation &= ~1; + inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO); + inode->i_op = &exfat_dir_inode_operations; + inode->i_fop = &exfat_dir_operations; + + set_nlink(inode, info.NumSubdirs); + } else if (info.Attr & ATTR_SYMLINK) { /* symbolic link */ + inode->i_op = &exfat_symlink_inode_operations; + inode->i_generation |= 1; + inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO); + } else { /* regular file */ + inode->i_generation |= 1; + inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO); + inode->i_op = &exfat_file_inode_operations; + inode->i_fop = &exfat_file_operations; + inode->i_mapping->a_ops = &exfat_aops; + + inode->i_mapping->nrpages = 0; + + } + + /* + * Use fid->size instead of info.Size + * because info.Size means the value saved on disk + */ + i_size_write(inode, size); + + /* ondisk and aligned size should be aligned with block size */ + if (size & (inode->i_sb->s_blocksize - 1)) { + size |= (inode->i_sb->s_blocksize - 1); + size++; + } + + EXFAT_I(inode)->i_size_aligned = size; + EXFAT_I(inode)->i_size_ondisk = size; + + exfat_save_attr(inode, info.Attr); + + inode->i_blocks = ((i_size_read(inode) + (fsi->cluster_size - 1)) + & ~((loff_t)fsi->cluster_size - 1)) >> inode->i_blkbits; + + exfat_time_fat2unix(sbi, &inode->i_mtime, &info.ModifyTimestamp); + exfat_time_fat2unix(sbi, &inode->i_ctime, &info.CreateTimestamp); + exfat_time_fat2unix(sbi, &inode->i_atime, &info.AccessTimestamp); + + return 0; +} + +static struct inode *exfat_build_inode(struct super_block *sb, + const FILE_ID_T *fid, loff_t i_pos) +{ + struct inode *inode; + int err; + + inode = exfat_iget(sb, i_pos); + if (inode) + goto out; + inode = new_inode(sb); + if (!inode) { + inode = ERR_PTR(-ENOMEM); + goto out; + } + inode->i_ino = iunique(sb, EXFAT_ROOT_INO); + SET_IVERSION(inode, 1); + err = exfat_fill_inode(inode, fid); + if (err) { + iput(inode); + inode = ERR_PTR(err); + goto out; + } + exfat_attach(inode, i_pos); + insert_inode_hash(inode); +out: + return inode; +} + +static struct inode *exfat_alloc_inode(struct super_block *sb) +{ + struct exfat_inode_info *ei; + + ei = kmem_cache_alloc(exfat_inode_cachep, GFP_NOFS); + if (!ei) + return NULL; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + init_rwsem(&ei->truncate_lock); +#endif + return &ei->vfs_inode; +} + +static void exfat_destroy_inode(struct inode *inode) +{ + kfree(EXFAT_I(inode)->target); + EXFAT_I(inode)->target = NULL; + + kmem_cache_free(exfat_inode_cachep, EXFAT_I(inode)); +} + +static int __exfat_write_inode(struct inode *inode, int sync) +{ + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + DIR_ENTRY_T info; + s32 err; + + if (inode->i_ino == EXFAT_ROOT_INO) + return 0; + + info.Attr = exfat_make_attr(inode); + info.Size = i_size_read(inode); + + exfat_time_unix2fat(sbi, &inode->i_mtime, &info.ModifyTimestamp); + exfat_time_unix2fat(sbi, &inode->i_ctime, &info.CreateTimestamp); + exfat_time_unix2fat(sbi, &inode->i_atime, &info.AccessTimestamp); + + /* FIXME : Do we need handling error? */ + mutex_lock(&(sbi->s_vlock)); + err = exfat_fscore_write_inode(inode, &info, sync); + mutex_unlock(&(sbi->s_vlock)); + + return err; +} + +static int exfat_sync_inode(struct inode *inode) +{ + return __exfat_write_inode(inode, 1); +} + +static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc) +{ + return __exfat_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL); +} + +static void exfat_evict_inode(struct inode *inode) +{ + truncate_inode_pages(&inode->i_data, 0); + + if (!inode->i_nlink) { + loff_t old_size = i_size_read(inode); + + i_size_write(inode, 0); + + EXFAT_I(inode)->fid.size = old_size; + + /* TO CHECK evicting directory works correctly */ + MMSG("%s: inode(%p) evict %s (size(%llu) to zero)\n", + __func__, inode, + S_ISDIR(inode->i_mode) ? "directory" : "file", + (u64)old_size); + __exfat_truncate(inode, old_size, 0); + } + + invalidate_inode_buffers(inode); + clear_inode(inode); + exfat_extent_cache_inval_inode(inode); + exfat_detach(inode); + + /* after end of this function, caller will remove inode hash */ + /* remove_inode_hash(inode); */ +} + + + +static void exfat_put_super(struct super_block *sb) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + int err; + + exfat_log_msg(sb, KERN_INFO, "trying to unmount..."); + + __cancel_delayed_work_sync(sbi); + + if (__is_sb_dirty(sb)) + exfat_write_super(sb); + + err = exfat_umount(sb); + + if (sbi->nls_disk) { + unload_nls(sbi->nls_disk); + sbi->nls_disk = NULL; + sbi->options.codepage = exfat_default_codepage; + } + if (sbi->nls_io) { + unload_nls(sbi->nls_io); + sbi->nls_io = NULL; + } + if (sbi->options.iocharset != exfat_default_iocharset) { + kfree(sbi->options.iocharset); + sbi->options.iocharset = exfat_default_iocharset; + } + + sb->s_fs_info = NULL; + + kobject_del(&sbi->sb_kobj); + kobject_put(&sbi->sb_kobj); + if (!sbi->use_vmalloc) + kfree(sbi); + else + vfree(sbi); + + exfat_log_msg(sb, KERN_INFO, "unmounted successfully! %s", + err ? "(with previous I/O errors)" : ""); +} + +static inline void __flush_delayed_meta(struct super_block *sb, s32 sync) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + if (sbi->options.delayed_meta) + exfat_cache_flush(sb, sync); +} + +static void exfat_write_super(struct super_block *sb) +{ + int time = 0; + + lock_super(sb); + + __set_sb_clean(sb); + + /* flush delayed FAT/DIR dirty */ + __flush_delayed_meta(sb, 0); + + if (!EXFAT_IS_SB_RDONLY(sb)) + __exfat_sync_fs(sb, 0); + + unlock_super(sb); + + time = jiffies; + + /* Issuing bdev requests is needed + * to guarantee DIR updates in time + * whether w/ or w/o delayed DIR dirty feature. + * (otherwise DIR updates could be delayed for 5 + 5 secs at max.) + */ + sync_blockdev(sb->s_bdev); + + MMSG("BD: exfat_write_super (bdev_sync for %ld ms)\n", + (jiffies - time) * 1000 / HZ); +} + +/* synchronize a file system volume */ +static s32 __exfat_sync_fs(struct super_block *sb, s32 do_sync) +{ + s32 err; + + mutex_lock(&(EXFAT_SB(sb)->s_vlock)); + err = exfat_fscore_sync_fs(sb, do_sync); + mutex_unlock(&(EXFAT_SB(sb)->s_vlock)); + return err; +} + +static int exfat_sync_fs(struct super_block *sb, int wait) +{ + int err = 0; + + /* If there are some dirty buffers in the bdev inode */ + if (__is_sb_dirty(sb)) { + lock_super(sb); + __set_sb_clean(sb); + + err = __exfat_sync_fs(sb, 1); + + unlock_super(sb); + } + + return err; +} + +static int exfat_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + /* + * patch 1.2.2 : + * fixed the slow-call problem because of volume-lock contention. + */ + struct super_block *sb = dentry->d_sb; + u64 id = huge_encode_dev(sb->s_bdev->bd_dev); + FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi); + VOL_INFO_T info; + + /* exfat_statfs will try to get a volume lock if needed */ + if (fsi->used_clusters == UINT_MAX) { + s32 err; + + mutex_lock(&(EXFAT_SB(sb)->s_vlock)); + err = exfat_fscore_statfs(sb, &info); + mutex_unlock(&(EXFAT_SB(sb)->s_vlock)); + + return -EIO; + } + + info.ClusterSize = fsi->cluster_size; + info.NumClusters = fsi->num_clusters - 2; /* clu 0 & 1 */ + info.UsedClusters = fsi->used_clusters + fsi->reserved_clusters; + info.FreeClusters = info.NumClusters - info.UsedClusters; + + if (fsi->prev_eio) + exfat_msg(sb, KERN_INFO, "called statfs with previous" + " I/O error(0x%02X).", fsi->prev_eio); + + buf->f_type = sb->s_magic; + buf->f_bsize = info.ClusterSize; + buf->f_blocks = info.NumClusters; + buf->f_bfree = info.FreeClusters; + buf->f_bavail = info.FreeClusters; + buf->f_fsid.val[0] = (u32)id; + buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_namelen = 260; + + return 0; +} + +static int exfat_remount(struct super_block *sb, int *flags, char *data) +{ + unsigned long prev_sb_flags; + char *orig_data = kstrdup(data, GFP_KERNEL); + struct exfat_sb_info *sbi = EXFAT_SB(sb); + FS_INFO_T *fsi = &(sbi->fsi); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + *flags |= MS_NODIRATIME; +#else + *flags |= SB_NODIRATIME; +#endif + + prev_sb_flags = sb->s_flags; + + exfat_remount_syncfs(sb); + + exfat_set_vol_flags(sb, VOL_CLEAN, 1); + + exfat_log_msg(sb, KERN_INFO, "re-mounted(%s->%s), eio=0x%x, Opts: %s", +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + (prev_sb_flags & MS_RDONLY) ? "ro" : "rw", + (*flags & MS_RDONLY) ? "ro" : "rw", +#else + (prev_sb_flags & SB_RDONLY) ? "ro" : "rw", + (*flags & SB_RDONLY) ? "ro" : "rw", +#endif + fsi->prev_eio, orig_data); + kfree(orig_data); + return 0; +} + +static int __exfat_show_options(struct seq_file *m, struct super_block *sb) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_mount_options *opts = &sbi->options; + FS_INFO_T *fsi = &(sbi->fsi); + + /* Show partition info */ + if (fsi->prev_eio) + seq_printf(m, ",eio=0x%x", fsi->prev_eio); + if (!uid_eq(opts->fs_uid, GLOBAL_ROOT_UID)) + seq_printf(m, ",uid=%u", + from_kuid_munged(&init_user_ns, opts->fs_uid)); + if (!gid_eq(opts->fs_gid, GLOBAL_ROOT_GID)) + seq_printf(m, ",gid=%u", + from_kgid_munged(&init_user_ns, opts->fs_gid)); + seq_printf(m, ",fmask=%04o", opts->fs_fmask); + seq_printf(m, ",dmask=%04o", opts->fs_dmask); + if (opts->allow_utime) + seq_printf(m, ",allow_utime=%04o", opts->allow_utime); + if (sbi->nls_disk) + seq_printf(m, ",codepage=%s", sbi->nls_disk->charset); + if (sbi->nls_io) + seq_printf(m, ",iocharset=%s", sbi->nls_io->charset); + if (opts->quiet) + seq_puts(m, ",quiet"); + if (opts->utf8) + seq_puts(m, ",utf8"); + seq_printf(m, ",namecase=%u", opts->casesensitive); + if (opts->tz_utc) + seq_puts(m, ",tz=UTC"); + seq_printf(m, ",symlink=%u", opts->symlink); + seq_printf(m, ",bps=%ld", sb->s_blocksize); + if (opts->errors == EXFAT_ERRORS_CONT) + seq_puts(m, ",errors=continue"); + else if (opts->errors == EXFAT_ERRORS_PANIC) + seq_puts(m, ",errors=panic"); + else + seq_puts(m, ",errors=remount-ro"); + if (opts->discard) + seq_puts(m, ",discard"); + if (opts->delayed_meta) + seq_puts(m, ",delayed_meta"); + + return 0; +} + +static const struct super_operations exfat_sops = { + .alloc_inode = exfat_alloc_inode, + .destroy_inode = exfat_destroy_inode, + .write_inode = exfat_write_inode, + .evict_inode = exfat_evict_inode, + .put_super = exfat_put_super, +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) + .write_super = exfat_write_super, +#endif + .sync_fs = exfat_sync_fs, + .statfs = exfat_statfs, + .remount_fs = exfat_remount, + .show_options = exfat_show_options, +}; + +#define EXFAT_ATTR(name, mode, show, store) \ +static struct exfat_attr exfat_attr_##name = __ATTR(name, mode, show, store) + +struct exfat_attr { + struct attribute attr; + ssize_t (*show)(struct exfat_sb_info *, char *); + ssize_t (*store)(struct exfat_sb_info *, const char *, size_t); +}; + +static ssize_t exfat_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) +{ + struct exfat_sb_info *sbi = container_of(kobj, struct exfat_sb_info, sb_kobj); + struct exfat_attr *a = container_of(attr, struct exfat_attr, attr); + + return a->show ? a->show(sbi, buf) : 0; +} + +static ssize_t exfat_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t len) +{ + struct exfat_sb_info *sbi = container_of(kobj, struct exfat_sb_info, sb_kobj); + struct exfat_attr *a = container_of(attr, struct exfat_attr, attr); + + return a->store ? a->store(sbi, buf, len) : len; +} + +static const struct sysfs_ops exfat_attr_ops = { + .show = exfat_attr_show, + .store = exfat_attr_store, +}; + +static ssize_t eio_show(struct exfat_sb_info *sbi, char *buf) +{ + FS_INFO_T *fsi = &(sbi->fsi); + + return snprintf(buf, PAGE_SIZE, "0x%x\n", fsi->prev_eio); +} +EXFAT_ATTR(eio, 0444, eio_show, NULL); + +static ssize_t fratio_show(struct exfat_sb_info *sbi, char *buf) +{ + unsigned int n_total_au = 0; + unsigned int n_clean_au = 0; + unsigned int n_full_au = 0; + unsigned int n_dirty_au = 0; + unsigned int fr = 0; + + n_total_au = exfat_fscore_get_au_stat(sbi->host_sb, VOL_AU_STAT_TOTAL); + n_clean_au = exfat_fscore_get_au_stat(sbi->host_sb, VOL_AU_STAT_CLEAN); + n_full_au = exfat_fscore_get_au_stat(sbi->host_sb, VOL_AU_STAT_FULL); + n_dirty_au = n_total_au - (n_full_au + n_clean_au); + + if (!n_dirty_au) + fr = 0; + else if (!n_clean_au) + fr = 100; + else + fr = (n_dirty_au * 100) / (n_clean_au + n_dirty_au); + + return snprintf(buf, PAGE_SIZE, "%u\n", fr); +} +EXFAT_ATTR(fratio, 0444, fratio_show, NULL); + +static ssize_t totalau_show(struct exfat_sb_info *sbi, char *buf) +{ + unsigned int n_au = 0; + + n_au = exfat_fscore_get_au_stat(sbi->host_sb, VOL_AU_STAT_TOTAL); + return snprintf(buf, PAGE_SIZE, "%u\n", n_au); +} +EXFAT_ATTR(totalau, 0444, totalau_show, NULL); + +static ssize_t cleanau_show(struct exfat_sb_info *sbi, char *buf) +{ + unsigned int n_clean_au = 0; + + n_clean_au = exfat_fscore_get_au_stat(sbi->host_sb, VOL_AU_STAT_CLEAN); + return snprintf(buf, PAGE_SIZE, "%u\n", n_clean_au); +} +EXFAT_ATTR(cleanau, 0444, cleanau_show, NULL); + +static ssize_t fullau_show(struct exfat_sb_info *sbi, char *buf) +{ + unsigned int n_full_au = 0; + + n_full_au = exfat_fscore_get_au_stat(sbi->host_sb, VOL_AU_STAT_FULL); + return snprintf(buf, PAGE_SIZE, "%u\n", n_full_au); +} +EXFAT_ATTR(fullau, 0444, fullau_show, NULL); + +static struct attribute *exfat_attrs[] = { + &exfat_attr_eio.attr, + &exfat_attr_fratio.attr, + &exfat_attr_totalau.attr, + &exfat_attr_cleanau.attr, + &exfat_attr_fullau.attr, + NULL, +}; + +static struct kobj_type exfat_ktype = { + .default_attrs = exfat_attrs, + .sysfs_ops = &exfat_attr_ops, +}; + +static ssize_t version_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + return snprintf(buff, PAGE_SIZE, "FS Version %s\n", EXFAT_VERSION); +} + +static struct kobj_attribute version_attr = __ATTR_RO(version); + +static struct attribute *attributes[] = { + &version_attr.attr, + NULL, +}; + +static struct attribute_group attr_group = { + .attrs = attributes, +}; + +enum { + Opt_uid, + Opt_gid, + Opt_umask, + Opt_dmask, + Opt_fmask, + Opt_allow_utime, + Opt_codepage, + Opt_charset, + Opt_quiet, + Opt_utf8, + Opt_namecase, + Opt_tz_utc, + Opt_symlink, + Opt_err_cont, + Opt_err_panic, + Opt_err_ro, + Opt_err, + Opt_discard, + Opt_delayed_meta, + Opt_nodelayed_meta, +}; + +static const match_table_t exfat_tokens = { + {Opt_uid, "uid=%u"}, + {Opt_gid, "gid=%u"}, + {Opt_umask, "umask=%o"}, + {Opt_dmask, "dmask=%o"}, + {Opt_fmask, "fmask=%o"}, + {Opt_allow_utime, "allow_utime=%o"}, + {Opt_codepage, "codepage=%u"}, + {Opt_charset, "iocharset=%s"}, + {Opt_quiet, "quiet"}, + {Opt_utf8, "utf8"}, + {Opt_namecase, "namecase=%u"}, + {Opt_tz_utc, "tz=UTC"}, + {Opt_symlink, "symlink=%u"}, + {Opt_err_cont, "errors=continue"}, + {Opt_err_panic, "errors=panic"}, + {Opt_err_ro, "errors=remount-ro"}, + {Opt_discard, "discard"}, + {Opt_delayed_meta, "delayed_meta"}, + {Opt_nodelayed_meta, "nodelayed_meta"}, + {Opt_err, NULL} +}; + +static int parse_options(struct super_block *sb, char *options, int silent, + struct exfat_mount_options *opts) +{ + char *p; + substring_t args[MAX_OPT_ARGS]; + int option; + char *tmpstr; + + opts->fs_uid = current_uid(); + opts->fs_gid = current_gid(); + opts->fs_fmask = opts->fs_dmask = current->fs->umask; + opts->allow_utime = U16_MAX; + opts->codepage = exfat_default_codepage; + opts->iocharset = exfat_default_iocharset; + opts->quiet = 0; + opts->casesensitive = 0; + opts->utf8 = 0; + opts->tz_utc = 0; + opts->symlink = 0; + opts->errors = EXFAT_ERRORS_RO; + opts->discard = 0; + opts->delayed_meta = 1; + + if (!options) + goto out; + + while ((p = strsep(&options, ",")) != NULL) { + int token; + + if (!*p) + continue; + token = match_token(p, exfat_tokens, args); + switch (token) { + case Opt_uid: + if (match_int(&args[0], &option)) + return 0; + opts->fs_uid = make_kuid(current_user_ns(), option); + break; + case Opt_gid: + if (match_int(&args[0], &option)) + return 0; + opts->fs_gid = make_kgid(current_user_ns(), option); + break; + case Opt_umask: + case Opt_dmask: + case Opt_fmask: + if (match_octal(&args[0], &option)) + return 0; + if (token != Opt_dmask) + opts->fs_fmask = option; + if (token != Opt_fmask) + opts->fs_dmask = option; + break; + case Opt_allow_utime: + if (match_octal(&args[0], &option)) + return 0; + opts->allow_utime = option & (S_IWGRP | S_IWOTH); + break; + case Opt_codepage: + if (match_int(&args[0], &option)) + return 0; + opts->codepage = option; + break; + case Opt_charset: + if (opts->iocharset != exfat_default_iocharset) + kfree(opts->iocharset); + tmpstr = match_strdup(&args[0]); + if (!tmpstr) + return -ENOMEM; + opts->iocharset = tmpstr; + break; + case Opt_quiet: + opts->quiet = 1; + break; + case Opt_namecase: + if (match_int(&args[0], &option)) + return 0; + opts->casesensitive = (option > 0) ? 1:0; + break; + case Opt_utf8: + opts->utf8 = 1; + break; + case Opt_tz_utc: + opts->tz_utc = 1; + break; + case Opt_symlink: + if (match_int(&args[0], &option)) + return 0; + opts->symlink = option > 0 ? 1 : 0; + break; + case Opt_err_cont: + opts->errors = EXFAT_ERRORS_CONT; + break; + case Opt_err_panic: + opts->errors = EXFAT_ERRORS_PANIC; + break; + case Opt_err_ro: + opts->errors = EXFAT_ERRORS_RO; + break; + case Opt_discard: + opts->discard = 1; + break; + case Opt_nodelayed_meta: + opts->delayed_meta = 0; + break; + default: + if (!silent) { + exfat_msg(sb, KERN_ERR, + "unrecognized mount option \"%s\" " + "or missing value", p); + } + return -EINVAL; + } + } + +out: + if (opts->allow_utime == U16_MAX) + opts->allow_utime = ~opts->fs_dmask & (S_IWGRP | S_IWOTH); + + if (opts->utf8 && strcmp(opts->iocharset, exfat_iocharset_with_utf8)) { + exfat_msg(sb, KERN_WARNING, + "utf8 enabled, \"iocharset=%s\" is recommended", + exfat_iocharset_with_utf8); + } + + if (opts->discard) { + struct request_queue *q = bdev_get_queue(sb->s_bdev); + + if (!blk_queue_discard(q)) + exfat_msg(sb, KERN_WARNING, + "mounting with \"discard\" option, but " + "the device does not support discard"); + opts->discard = 0; + } + + return 0; +} + +static void exfat_hash_init(struct super_block *sb) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + int i; + + spin_lock_init(&sbi->inode_hash_lock); + for (i = 0; i < EXFAT_HASH_SIZE; i++) + INIT_HLIST_HEAD(&sbi->inode_hashtable[i]); +} + +static int exfat_read_root(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + FS_INFO_T *fsi = &(sbi->fsi); + DIR_ENTRY_T info; + + EXFAT_I(inode)->fid.dir.dir = fsi->root_dir; + EXFAT_I(inode)->fid.dir.flags = 0x01; + EXFAT_I(inode)->fid.entry = -1; + EXFAT_I(inode)->fid.start_clu = fsi->root_dir; + EXFAT_I(inode)->fid.flags = 0x01; + EXFAT_I(inode)->fid.type = TYPE_DIR; + EXFAT_I(inode)->fid.version = 0; + EXFAT_I(inode)->fid.rwoffset = 0; + EXFAT_I(inode)->fid.hint_bmap.off = CLUS_EOF; + EXFAT_I(inode)->fid.hint_stat.eidx = 0; + EXFAT_I(inode)->fid.hint_stat.clu = fsi->root_dir; + EXFAT_I(inode)->fid.hint_femp.eidx = -1; + + EXFAT_I(inode)->target = NULL; + + if (exfat_read_inode(inode, &info) < 0) + return -EIO; + + inode->i_uid = sbi->options.fs_uid; + inode->i_gid = sbi->options.fs_gid; + INC_IVERSION(inode); + inode->i_generation = 0; + inode->i_mode = exfat_make_mode(sbi, ATTR_SUBDIR, S_IRWXUGO); + inode->i_op = &exfat_dir_inode_operations; + inode->i_fop = &exfat_dir_operations; + + i_size_write(inode, info.Size); + EXFAT_I(inode)->fid.size = info.Size; + inode->i_blocks = ((i_size_read(inode) + (fsi->cluster_size - 1)) + & ~((loff_t)fsi->cluster_size - 1)) >> inode->i_blkbits; + EXFAT_I(inode)->i_pos = ((loff_t) fsi->root_dir << 32) | 0xffffffff; + EXFAT_I(inode)->i_size_aligned = i_size_read(inode); + EXFAT_I(inode)->i_size_ondisk = i_size_read(inode); + + exfat_save_attr(inode, ATTR_SUBDIR); + inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); + set_nlink(inode, info.NumSubdirs + 2); + return 0; +} + +static void setup_dops(struct super_block *sb) +{ + if (EXFAT_SB(sb)->options.casesensitive == 0) + sb->s_d_op = &exfat_ci_dentry_ops; + else + sb->s_d_op = &exfat_dentry_ops; +} + +static int exfat_fill_super(struct super_block *sb, void *data, int silent) +{ + struct inode *root_inode = NULL; + struct exfat_sb_info *sbi; + int err; + char buf[50]; + struct block_device *bdev = sb->s_bdev; + dev_t bd_dev = bdev ? bdev->bd_dev : 0; + + exfat_log_msg(sb, KERN_INFO, "trying to mount..."); + + /* + * GFP_KERNEL is ok here, because while we do hold the + * supeblock lock, memory pressure can't call back into + * the filesystem, since we're only just about to mount + * it and have no inodes etc active! + */ + sbi = kzalloc(sizeof(struct exfat_sb_info), GFP_KERNEL); + if (!sbi) { + exfat_log_msg(sb, KERN_INFO, + "trying to alloc sbi with vzalloc()"); + sbi = vzalloc(sizeof(struct exfat_sb_info)); + if (!sbi) { + exfat_log_msg(sb, KERN_ERR, "failed to mount! (ENOMEM)"); + return -ENOMEM; + } + sbi->use_vmalloc = 1; + } + + mutex_init(&sbi->s_vlock); + sb->s_fs_info = sbi; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + sb->s_flags |= MS_NODIRATIME; +#else + sb->s_flags |= SB_NODIRATIME; +#endif + sb->s_magic = EXFAT_SUPER_MAGIC; + sb->s_op = &exfat_sops; + ratelimit_state_init(&sbi->ratelimit, DEFAULT_RATELIMIT_INTERVAL, + DEFAULT_RATELIMIT_BURST); + err = parse_options(sb, data, silent, &sbi->options); + if (err) { + exfat_log_msg(sb, KERN_ERR, "failed to parse options"); + goto failed_mount; + } + + setup_exfat_xattr_handler(sb); + setup_exfat_sync_super_wq(sb); + setup_dops(sb); + + err = exfat_mount(sb); + if (err) { + exfat_log_msg(sb, KERN_ERR, "failed to recognize fat type"); + goto failed_mount; + } + + /* set up enough so that it can read an inode */ + exfat_hash_init(sb); + + /* + * The low byte of FAT's first entry must have same value with + * media-field. But in real world, too many devices is + * writing wrong value. So, removed that validity check. + * + * if (FAT_FIRST_ENT(sb, media) != first) + */ + + err = -EINVAL; + sprintf(buf, "cp%d", sbi->options.codepage); + sbi->nls_disk = load_nls(buf); + if (!sbi->nls_disk) { + exfat_log_msg(sb, KERN_ERR, "codepage %s not found", buf); + goto failed_mount2; + } + + sbi->nls_io = load_nls(sbi->options.iocharset); + if (!sbi->nls_io) { + exfat_log_msg(sb, KERN_ERR, "IO charset %s not found", + sbi->options.iocharset); + goto failed_mount2; + } + + err = -ENOMEM; + root_inode = new_inode(sb); + if (!root_inode) { + exfat_log_msg(sb, KERN_ERR, "failed to allocate root inode."); + goto failed_mount2; + } + + root_inode->i_ino = EXFAT_ROOT_INO; + SET_IVERSION(root_inode, 1); + + err = exfat_read_root(root_inode); + if (err) { + exfat_log_msg(sb, KERN_ERR, "failed to initialize root inode."); + goto failed_mount2; + } + + exfat_attach(root_inode, EXFAT_I(root_inode)->i_pos); + insert_inode_hash(root_inode); + + err = -ENOMEM; + sb->s_root = __d_make_root(root_inode); + if (!sb->s_root) { + exfat_msg(sb, KERN_ERR, "failed to get the root dentry"); + goto failed_mount2; + } + + /* + * Initialize filesystem attributes (for sysfs) + * ex: /sys/fs/exfat/mmcblk1[179:17] + */ + sbi->sb_kobj.kset = exfat_kset; + err = kobject_init_and_add(&sbi->sb_kobj, &exfat_ktype, NULL, + "%s[%d:%d]", sb->s_id, MAJOR(bd_dev), MINOR(bd_dev)); + if (err) { + exfat_msg(sb, KERN_ERR, "Unable to create exfat attributes for" + " %s[%d:%d](%d)", sb->s_id, + MAJOR(bd_dev), MINOR(bd_dev), err); + goto failed_mount2; + } + + exfat_log_msg(sb, KERN_INFO, "mounted successfully!"); + + return 0; + +failed_mount2: + exfat_umount(sb); +failed_mount: + exfat_log_msg(sb, KERN_INFO, "failed to mount! (%d)", err); + + if (root_inode) + iput(root_inode); + sb->s_root = NULL; + + if (sbi->nls_io) + unload_nls(sbi->nls_io); + if (sbi->nls_disk) + unload_nls(sbi->nls_disk); + if (sbi->options.iocharset != exfat_default_iocharset) + kfree(sbi->options.iocharset); + sb->s_fs_info = NULL; + if (!sbi->use_vmalloc) + kfree(sbi); + else + vfree(sbi); + return err; +} + +static struct dentry *exfat_fs_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) +{ + return mount_bdev(fs_type, flags, dev_name, data, exfat_fill_super); +} + +static void init_once(void *foo) +{ + struct exfat_inode_info *ei = (struct exfat_inode_info *)foo; + + INIT_HLIST_NODE(&ei->i_hash_fat); + inode_init_once(&ei->vfs_inode); +} + +static int __init exfat_init_inodecache(void) +{ + exfat_inode_cachep = kmem_cache_create("exfat_inode_cache", + sizeof(struct exfat_inode_info), + 0, (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD), + init_once); + if (!exfat_inode_cachep) + return -ENOMEM; + return 0; +} + +static void exfat_destroy_inodecache(void) +{ + /* + * Make sure all delayed rcu free inodes are flushed before we + * destroy cache. + */ + rcu_barrier(); + kmem_cache_destroy(exfat_inode_cachep); +} + +static struct file_system_type exfat_fs_type = { + .owner = THIS_MODULE, + .name = "exfat", + .mount = exfat_fs_mount, + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, +}; + +static int __init init_exfat_fs(void) +{ + int err; + + exfat_log_version(); + err = exfat_fscore_init(); + if (err) + goto error; + + exfat_kset = kset_create_and_add("exfat", NULL, fs_kobj); + if (!exfat_kset) { + pr_err("exFAT: failed to create fs_kobj\n"); + err = -ENOMEM; + goto error; + } + + err = sysfs_create_group(&exfat_kset->kobj, &attr_group); + if (err) { + pr_err("exFAT: failed to create exfat version attributes\n"); + goto error; + } + + err = exfat_uevent_init(exfat_kset); + if (err) + goto error; + + err = exfat_init_inodecache(); + if (err) { + pr_err("exFAT: failed to initialize inode cache\n"); + goto error; + } + + err = register_filesystem(&exfat_fs_type); + if (err) { + pr_err("exFAT: failed to register filesystem\n"); + goto error; + } + + return 0; +error: + exfat_uevent_uninit(); + + if (exfat_kset) { + sysfs_remove_group(&exfat_kset->kobj, &attr_group); + kset_unregister(exfat_kset); + exfat_kset = NULL; + } + + exfat_destroy_inodecache(); + exfat_fscore_shutdown(); + + pr_err("exFAT: failed to initialize FS driver(err:%d)\n", err); + return err; +} + +static void __exit exit_exfat_fs(void) +{ + exfat_uevent_uninit(); + + if (exfat_kset) { + sysfs_remove_group(&exfat_kset->kobj, &attr_group); + kset_unregister(exfat_kset); + exfat_kset = NULL; + } + + exfat_destroy_inodecache(); + unregister_filesystem(&exfat_fs_type); + + exfat_fscore_shutdown(); +} + +module_init(init_exfat_fs); +module_exit(exit_exfat_fs); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) +MODULE_ALIAS_FS("exfat"); +#endif +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("FAT/exFAT filesystem support"); +MODULE_AUTHOR("Samsung Electronics Co., Ltd."); diff --git a/fs/exfat/upcase.h b/fs/exfat/upcase.h new file mode 100644 index 00000000..f8635632 --- /dev/null +++ b/fs/exfat/upcase.h @@ -0,0 +1,394 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + */ + +#ifndef _UPCASE_H +#define _UPCASE_H + +/* Upcase tabel macro */ +#define EXFAT_NUM_UPCASE 2918 +#define HIGH_INDEX_BIT (8) +#define HIGH_INDEX_MASK (0xFF00) +#define LOW_INDEX_BIT (16-HIGH_INDEX_BIT) +#define UTBL_ROW_COUNT (1<> LOW_INDEX_BIT; +} +static inline u16 exfat_get_row_index(u16 i) +{ + return i & ~HIGH_INDEX_MASK; +} + +static const u8 uni_def_upcase[EXFAT_NUM_UPCASE<<1] = { + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, + 0x08, 0x00, 0x09, 0x00, 0x0A, 0x00, 0x0B, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x0E, 0x00, 0x0F, 0x00, + 0x10, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, + 0x18, 0x00, 0x19, 0x00, 0x1A, 0x00, 0x1B, 0x00, 0x1C, 0x00, 0x1D, 0x00, 0x1E, 0x00, 0x1F, 0x00, + 0x20, 0x00, 0x21, 0x00, 0x22, 0x00, 0x23, 0x00, 0x24, 0x00, 0x25, 0x00, 0x26, 0x00, 0x27, 0x00, + 0x28, 0x00, 0x29, 0x00, 0x2A, 0x00, 0x2B, 0x00, 0x2C, 0x00, 0x2D, 0x00, 0x2E, 0x00, 0x2F, 0x00, + 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, 0x37, 0x00, + 0x38, 0x00, 0x39, 0x00, 0x3A, 0x00, 0x3B, 0x00, 0x3C, 0x00, 0x3D, 0x00, 0x3E, 0x00, 0x3F, 0x00, + 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, + 0x48, 0x00, 0x49, 0x00, 0x4A, 0x00, 0x4B, 0x00, 0x4C, 0x00, 0x4D, 0x00, 0x4E, 0x00, 0x4F, 0x00, + 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, + 0x58, 0x00, 0x59, 0x00, 0x5A, 0x00, 0x5B, 0x00, 0x5C, 0x00, 0x5D, 0x00, 0x5E, 0x00, 0x5F, 0x00, + 0x60, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, + 0x48, 0x00, 0x49, 0x00, 0x4A, 0x00, 0x4B, 0x00, 0x4C, 0x00, 0x4D, 0x00, 0x4E, 0x00, 0x4F, 0x00, + 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, + 0x58, 0x00, 0x59, 0x00, 0x5A, 0x00, 0x7B, 0x00, 0x7C, 0x00, 0x7D, 0x00, 0x7E, 0x00, 0x7F, 0x00, + 0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85, 0x00, 0x86, 0x00, 0x87, 0x00, + 0x88, 0x00, 0x89, 0x00, 0x8A, 0x00, 0x8B, 0x00, 0x8C, 0x00, 0x8D, 0x00, 0x8E, 0x00, 0x8F, 0x00, + 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, + 0x98, 0x00, 0x99, 0x00, 0x9A, 0x00, 0x9B, 0x00, 0x9C, 0x00, 0x9D, 0x00, 0x9E, 0x00, 0x9F, 0x00, + 0xA0, 0x00, 0xA1, 0x00, 0xA2, 0x00, 0xA3, 0x00, 0xA4, 0x00, 0xA5, 0x00, 0xA6, 0x00, 0xA7, 0x00, + 0xA8, 0x00, 0xA9, 0x00, 0xAA, 0x00, 0xAB, 0x00, 0xAC, 0x00, 0xAD, 0x00, 0xAE, 0x00, 0xAF, 0x00, + 0xB0, 0x00, 0xB1, 0x00, 0xB2, 0x00, 0xB3, 0x00, 0xB4, 0x00, 0xB5, 0x00, 0xB6, 0x00, 0xB7, 0x00, + 0xB8, 0x00, 0xB9, 0x00, 0xBA, 0x00, 0xBB, 0x00, 0xBC, 0x00, 0xBD, 0x00, 0xBE, 0x00, 0xBF, 0x00, + 0xC0, 0x00, 0xC1, 0x00, 0xC2, 0x00, 0xC3, 0x00, 0xC4, 0x00, 0xC5, 0x00, 0xC6, 0x00, 0xC7, 0x00, + 0xC8, 0x00, 0xC9, 0x00, 0xCA, 0x00, 0xCB, 0x00, 0xCC, 0x00, 0xCD, 0x00, 0xCE, 0x00, 0xCF, 0x00, + 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD3, 0x00, 0xD4, 0x00, 0xD5, 0x00, 0xD6, 0x00, 0xD7, 0x00, + 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0xDD, 0x00, 0xDE, 0x00, 0xDF, 0x00, + 0xC0, 0x00, 0xC1, 0x00, 0xC2, 0x00, 0xC3, 0x00, 0xC4, 0x00, 0xC5, 0x00, 0xC6, 0x00, 0xC7, 0x00, + 0xC8, 0x00, 0xC9, 0x00, 0xCA, 0x00, 0xCB, 0x00, 0xCC, 0x00, 0xCD, 0x00, 0xCE, 0x00, 0xCF, 0x00, + 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD3, 0x00, 0xD4, 0x00, 0xD5, 0x00, 0xD6, 0x00, 0xF7, 0x00, + 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0xDD, 0x00, 0xDE, 0x00, 0x78, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x02, 0x01, 0x02, 0x01, 0x04, 0x01, 0x04, 0x01, 0x06, 0x01, 0x06, 0x01, + 0x08, 0x01, 0x08, 0x01, 0x0A, 0x01, 0x0A, 0x01, 0x0C, 0x01, 0x0C, 0x01, 0x0E, 0x01, 0x0E, 0x01, + 0x10, 0x01, 0x10, 0x01, 0x12, 0x01, 0x12, 0x01, 0x14, 0x01, 0x14, 0x01, 0x16, 0x01, 0x16, 0x01, + 0x18, 0x01, 0x18, 0x01, 0x1A, 0x01, 0x1A, 0x01, 0x1C, 0x01, 0x1C, 0x01, 0x1E, 0x01, 0x1E, 0x01, + 0x20, 0x01, 0x20, 0x01, 0x22, 0x01, 0x22, 0x01, 0x24, 0x01, 0x24, 0x01, 0x26, 0x01, 0x26, 0x01, + 0x28, 0x01, 0x28, 0x01, 0x2A, 0x01, 0x2A, 0x01, 0x2C, 0x01, 0x2C, 0x01, 0x2E, 0x01, 0x2E, 0x01, + 0x30, 0x01, 0x31, 0x01, 0x32, 0x01, 0x32, 0x01, 0x34, 0x01, 0x34, 0x01, 0x36, 0x01, 0x36, 0x01, + 0x38, 0x01, 0x39, 0x01, 0x39, 0x01, 0x3B, 0x01, 0x3B, 0x01, 0x3D, 0x01, 0x3D, 0x01, 0x3F, 0x01, + 0x3F, 0x01, 0x41, 0x01, 0x41, 0x01, 0x43, 0x01, 0x43, 0x01, 0x45, 0x01, 0x45, 0x01, 0x47, 0x01, + 0x47, 0x01, 0x49, 0x01, 0x4A, 0x01, 0x4A, 0x01, 0x4C, 0x01, 0x4C, 0x01, 0x4E, 0x01, 0x4E, 0x01, + 0x50, 0x01, 0x50, 0x01, 0x52, 0x01, 0x52, 0x01, 0x54, 0x01, 0x54, 0x01, 0x56, 0x01, 0x56, 0x01, + 0x58, 0x01, 0x58, 0x01, 0x5A, 0x01, 0x5A, 0x01, 0x5C, 0x01, 0x5C, 0x01, 0x5E, 0x01, 0x5E, 0x01, + 0x60, 0x01, 0x60, 0x01, 0x62, 0x01, 0x62, 0x01, 0x64, 0x01, 0x64, 0x01, 0x66, 0x01, 0x66, 0x01, + 0x68, 0x01, 0x68, 0x01, 0x6A, 0x01, 0x6A, 0x01, 0x6C, 0x01, 0x6C, 0x01, 0x6E, 0x01, 0x6E, 0x01, + 0x70, 0x01, 0x70, 0x01, 0x72, 0x01, 0x72, 0x01, 0x74, 0x01, 0x74, 0x01, 0x76, 0x01, 0x76, 0x01, + 0x78, 0x01, 0x79, 0x01, 0x79, 0x01, 0x7B, 0x01, 0x7B, 0x01, 0x7D, 0x01, 0x7D, 0x01, 0x7F, 0x01, + 0x43, 0x02, 0x81, 0x01, 0x82, 0x01, 0x82, 0x01, 0x84, 0x01, 0x84, 0x01, 0x86, 0x01, 0x87, 0x01, + 0x87, 0x01, 0x89, 0x01, 0x8A, 0x01, 0x8B, 0x01, 0x8B, 0x01, 0x8D, 0x01, 0x8E, 0x01, 0x8F, 0x01, + 0x90, 0x01, 0x91, 0x01, 0x91, 0x01, 0x93, 0x01, 0x94, 0x01, 0xF6, 0x01, 0x96, 0x01, 0x97, 0x01, + 0x98, 0x01, 0x98, 0x01, 0x3D, 0x02, 0x9B, 0x01, 0x9C, 0x01, 0x9D, 0x01, 0x20, 0x02, 0x9F, 0x01, + 0xA0, 0x01, 0xA0, 0x01, 0xA2, 0x01, 0xA2, 0x01, 0xA4, 0x01, 0xA4, 0x01, 0xA6, 0x01, 0xA7, 0x01, + 0xA7, 0x01, 0xA9, 0x01, 0xAA, 0x01, 0xAB, 0x01, 0xAC, 0x01, 0xAC, 0x01, 0xAE, 0x01, 0xAF, 0x01, + 0xAF, 0x01, 0xB1, 0x01, 0xB2, 0x01, 0xB3, 0x01, 0xB3, 0x01, 0xB5, 0x01, 0xB5, 0x01, 0xB7, 0x01, + 0xB8, 0x01, 0xB8, 0x01, 0xBA, 0x01, 0xBB, 0x01, 0xBC, 0x01, 0xBC, 0x01, 0xBE, 0x01, 0xF7, 0x01, + 0xC0, 0x01, 0xC1, 0x01, 0xC2, 0x01, 0xC3, 0x01, 0xC4, 0x01, 0xC5, 0x01, 0xC4, 0x01, 0xC7, 0x01, + 0xC8, 0x01, 0xC7, 0x01, 0xCA, 0x01, 0xCB, 0x01, 0xCA, 0x01, 0xCD, 0x01, 0xCD, 0x01, 0xCF, 0x01, + 0xCF, 0x01, 0xD1, 0x01, 0xD1, 0x01, 0xD3, 0x01, 0xD3, 0x01, 0xD5, 0x01, 0xD5, 0x01, 0xD7, 0x01, + 0xD7, 0x01, 0xD9, 0x01, 0xD9, 0x01, 0xDB, 0x01, 0xDB, 0x01, 0x8E, 0x01, 0xDE, 0x01, 0xDE, 0x01, + 0xE0, 0x01, 0xE0, 0x01, 0xE2, 0x01, 0xE2, 0x01, 0xE4, 0x01, 0xE4, 0x01, 0xE6, 0x01, 0xE6, 0x01, + 0xE8, 0x01, 0xE8, 0x01, 0xEA, 0x01, 0xEA, 0x01, 0xEC, 0x01, 0xEC, 0x01, 0xEE, 0x01, 0xEE, 0x01, + 0xF0, 0x01, 0xF1, 0x01, 0xF2, 0x01, 0xF1, 0x01, 0xF4, 0x01, 0xF4, 0x01, 0xF6, 0x01, 0xF7, 0x01, + 0xF8, 0x01, 0xF8, 0x01, 0xFA, 0x01, 0xFA, 0x01, 0xFC, 0x01, 0xFC, 0x01, 0xFE, 0x01, 0xFE, 0x01, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x04, 0x02, 0x04, 0x02, 0x06, 0x02, 0x06, 0x02, + 0x08, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x0A, 0x02, 0x0C, 0x02, 0x0C, 0x02, 0x0E, 0x02, 0x0E, 0x02, + 0x10, 0x02, 0x10, 0x02, 0x12, 0x02, 0x12, 0x02, 0x14, 0x02, 0x14, 0x02, 0x16, 0x02, 0x16, 0x02, + 0x18, 0x02, 0x18, 0x02, 0x1A, 0x02, 0x1A, 0x02, 0x1C, 0x02, 0x1C, 0x02, 0x1E, 0x02, 0x1E, 0x02, + 0x20, 0x02, 0x21, 0x02, 0x22, 0x02, 0x22, 0x02, 0x24, 0x02, 0x24, 0x02, 0x26, 0x02, 0x26, 0x02, + 0x28, 0x02, 0x28, 0x02, 0x2A, 0x02, 0x2A, 0x02, 0x2C, 0x02, 0x2C, 0x02, 0x2E, 0x02, 0x2E, 0x02, + 0x30, 0x02, 0x30, 0x02, 0x32, 0x02, 0x32, 0x02, 0x34, 0x02, 0x35, 0x02, 0x36, 0x02, 0x37, 0x02, + 0x38, 0x02, 0x39, 0x02, 0x65, 0x2C, 0x3B, 0x02, 0x3B, 0x02, 0x3D, 0x02, 0x66, 0x2C, 0x3F, 0x02, + 0x40, 0x02, 0x41, 0x02, 0x41, 0x02, 0x43, 0x02, 0x44, 0x02, 0x45, 0x02, 0x46, 0x02, 0x46, 0x02, + 0x48, 0x02, 0x48, 0x02, 0x4A, 0x02, 0x4A, 0x02, 0x4C, 0x02, 0x4C, 0x02, 0x4E, 0x02, 0x4E, 0x02, + 0x50, 0x02, 0x51, 0x02, 0x52, 0x02, 0x81, 0x01, 0x86, 0x01, 0x55, 0x02, 0x89, 0x01, 0x8A, 0x01, + 0x58, 0x02, 0x8F, 0x01, 0x5A, 0x02, 0x90, 0x01, 0x5C, 0x02, 0x5D, 0x02, 0x5E, 0x02, 0x5F, 0x02, + 0x93, 0x01, 0x61, 0x02, 0x62, 0x02, 0x94, 0x01, 0x64, 0x02, 0x65, 0x02, 0x66, 0x02, 0x67, 0x02, + 0x97, 0x01, 0x96, 0x01, 0x6A, 0x02, 0x62, 0x2C, 0x6C, 0x02, 0x6D, 0x02, 0x6E, 0x02, 0x9C, 0x01, + 0x70, 0x02, 0x71, 0x02, 0x9D, 0x01, 0x73, 0x02, 0x74, 0x02, 0x9F, 0x01, 0x76, 0x02, 0x77, 0x02, + 0x78, 0x02, 0x79, 0x02, 0x7A, 0x02, 0x7B, 0x02, 0x7C, 0x02, 0x64, 0x2C, 0x7E, 0x02, 0x7F, 0x02, + 0xA6, 0x01, 0x81, 0x02, 0x82, 0x02, 0xA9, 0x01, 0x84, 0x02, 0x85, 0x02, 0x86, 0x02, 0x87, 0x02, + 0xAE, 0x01, 0x44, 0x02, 0xB1, 0x01, 0xB2, 0x01, 0x45, 0x02, 0x8D, 0x02, 0x8E, 0x02, 0x8F, 0x02, + 0x90, 0x02, 0x91, 0x02, 0xB7, 0x01, 0x93, 0x02, 0x94, 0x02, 0x95, 0x02, 0x96, 0x02, 0x97, 0x02, + 0x98, 0x02, 0x99, 0x02, 0x9A, 0x02, 0x9B, 0x02, 0x9C, 0x02, 0x9D, 0x02, 0x9E, 0x02, 0x9F, 0x02, + 0xA0, 0x02, 0xA1, 0x02, 0xA2, 0x02, 0xA3, 0x02, 0xA4, 0x02, 0xA5, 0x02, 0xA6, 0x02, 0xA7, 0x02, + 0xA8, 0x02, 0xA9, 0x02, 0xAA, 0x02, 0xAB, 0x02, 0xAC, 0x02, 0xAD, 0x02, 0xAE, 0x02, 0xAF, 0x02, + 0xB0, 0x02, 0xB1, 0x02, 0xB2, 0x02, 0xB3, 0x02, 0xB4, 0x02, 0xB5, 0x02, 0xB6, 0x02, 0xB7, 0x02, + 0xB8, 0x02, 0xB9, 0x02, 0xBA, 0x02, 0xBB, 0x02, 0xBC, 0x02, 0xBD, 0x02, 0xBE, 0x02, 0xBF, 0x02, + 0xC0, 0x02, 0xC1, 0x02, 0xC2, 0x02, 0xC3, 0x02, 0xC4, 0x02, 0xC5, 0x02, 0xC6, 0x02, 0xC7, 0x02, + 0xC8, 0x02, 0xC9, 0x02, 0xCA, 0x02, 0xCB, 0x02, 0xCC, 0x02, 0xCD, 0x02, 0xCE, 0x02, 0xCF, 0x02, + 0xD0, 0x02, 0xD1, 0x02, 0xD2, 0x02, 0xD3, 0x02, 0xD4, 0x02, 0xD5, 0x02, 0xD6, 0x02, 0xD7, 0x02, + 0xD8, 0x02, 0xD9, 0x02, 0xDA, 0x02, 0xDB, 0x02, 0xDC, 0x02, 0xDD, 0x02, 0xDE, 0x02, 0xDF, 0x02, + 0xE0, 0x02, 0xE1, 0x02, 0xE2, 0x02, 0xE3, 0x02, 0xE4, 0x02, 0xE5, 0x02, 0xE6, 0x02, 0xE7, 0x02, + 0xE8, 0x02, 0xE9, 0x02, 0xEA, 0x02, 0xEB, 0x02, 0xEC, 0x02, 0xED, 0x02, 0xEE, 0x02, 0xEF, 0x02, + 0xF0, 0x02, 0xF1, 0x02, 0xF2, 0x02, 0xF3, 0x02, 0xF4, 0x02, 0xF5, 0x02, 0xF6, 0x02, 0xF7, 0x02, + 0xF8, 0x02, 0xF9, 0x02, 0xFA, 0x02, 0xFB, 0x02, 0xFC, 0x02, 0xFD, 0x02, 0xFE, 0x02, 0xFF, 0x02, + 0x00, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x03, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x07, 0x03, + 0x08, 0x03, 0x09, 0x03, 0x0A, 0x03, 0x0B, 0x03, 0x0C, 0x03, 0x0D, 0x03, 0x0E, 0x03, 0x0F, 0x03, + 0x10, 0x03, 0x11, 0x03, 0x12, 0x03, 0x13, 0x03, 0x14, 0x03, 0x15, 0x03, 0x16, 0x03, 0x17, 0x03, + 0x18, 0x03, 0x19, 0x03, 0x1A, 0x03, 0x1B, 0x03, 0x1C, 0x03, 0x1D, 0x03, 0x1E, 0x03, 0x1F, 0x03, + 0x20, 0x03, 0x21, 0x03, 0x22, 0x03, 0x23, 0x03, 0x24, 0x03, 0x25, 0x03, 0x26, 0x03, 0x27, 0x03, + 0x28, 0x03, 0x29, 0x03, 0x2A, 0x03, 0x2B, 0x03, 0x2C, 0x03, 0x2D, 0x03, 0x2E, 0x03, 0x2F, 0x03, + 0x30, 0x03, 0x31, 0x03, 0x32, 0x03, 0x33, 0x03, 0x34, 0x03, 0x35, 0x03, 0x36, 0x03, 0x37, 0x03, + 0x38, 0x03, 0x39, 0x03, 0x3A, 0x03, 0x3B, 0x03, 0x3C, 0x03, 0x3D, 0x03, 0x3E, 0x03, 0x3F, 0x03, + 0x40, 0x03, 0x41, 0x03, 0x42, 0x03, 0x43, 0x03, 0x44, 0x03, 0x45, 0x03, 0x46, 0x03, 0x47, 0x03, + 0x48, 0x03, 0x49, 0x03, 0x4A, 0x03, 0x4B, 0x03, 0x4C, 0x03, 0x4D, 0x03, 0x4E, 0x03, 0x4F, 0x03, + 0x50, 0x03, 0x51, 0x03, 0x52, 0x03, 0x53, 0x03, 0x54, 0x03, 0x55, 0x03, 0x56, 0x03, 0x57, 0x03, + 0x58, 0x03, 0x59, 0x03, 0x5A, 0x03, 0x5B, 0x03, 0x5C, 0x03, 0x5D, 0x03, 0x5E, 0x03, 0x5F, 0x03, + 0x60, 0x03, 0x61, 0x03, 0x62, 0x03, 0x63, 0x03, 0x64, 0x03, 0x65, 0x03, 0x66, 0x03, 0x67, 0x03, + 0x68, 0x03, 0x69, 0x03, 0x6A, 0x03, 0x6B, 0x03, 0x6C, 0x03, 0x6D, 0x03, 0x6E, 0x03, 0x6F, 0x03, + 0x70, 0x03, 0x71, 0x03, 0x72, 0x03, 0x73, 0x03, 0x74, 0x03, 0x75, 0x03, 0x76, 0x03, 0x77, 0x03, + 0x78, 0x03, 0x79, 0x03, 0x7A, 0x03, 0xFD, 0x03, 0xFE, 0x03, 0xFF, 0x03, 0x7E, 0x03, 0x7F, 0x03, + 0x80, 0x03, 0x81, 0x03, 0x82, 0x03, 0x83, 0x03, 0x84, 0x03, 0x85, 0x03, 0x86, 0x03, 0x87, 0x03, + 0x88, 0x03, 0x89, 0x03, 0x8A, 0x03, 0x8B, 0x03, 0x8C, 0x03, 0x8D, 0x03, 0x8E, 0x03, 0x8F, 0x03, + 0x90, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03, 0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03, + 0x98, 0x03, 0x99, 0x03, 0x9A, 0x03, 0x9B, 0x03, 0x9C, 0x03, 0x9D, 0x03, 0x9E, 0x03, 0x9F, 0x03, + 0xA0, 0x03, 0xA1, 0x03, 0xA2, 0x03, 0xA3, 0x03, 0xA4, 0x03, 0xA5, 0x03, 0xA6, 0x03, 0xA7, 0x03, + 0xA8, 0x03, 0xA9, 0x03, 0xAA, 0x03, 0xAB, 0x03, 0x86, 0x03, 0x88, 0x03, 0x89, 0x03, 0x8A, 0x03, + 0xB0, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03, 0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03, + 0x98, 0x03, 0x99, 0x03, 0x9A, 0x03, 0x9B, 0x03, 0x9C, 0x03, 0x9D, 0x03, 0x9E, 0x03, 0x9F, 0x03, + 0xA0, 0x03, 0xA1, 0x03, 0xA3, 0x03, 0xA3, 0x03, 0xA4, 0x03, 0xA5, 0x03, 0xA6, 0x03, 0xA7, 0x03, + 0xA8, 0x03, 0xA9, 0x03, 0xAA, 0x03, 0xAB, 0x03, 0x8C, 0x03, 0x8E, 0x03, 0x8F, 0x03, 0xCF, 0x03, + 0xD0, 0x03, 0xD1, 0x03, 0xD2, 0x03, 0xD3, 0x03, 0xD4, 0x03, 0xD5, 0x03, 0xD6, 0x03, 0xD7, 0x03, + 0xD8, 0x03, 0xD8, 0x03, 0xDA, 0x03, 0xDA, 0x03, 0xDC, 0x03, 0xDC, 0x03, 0xDE, 0x03, 0xDE, 0x03, + 0xE0, 0x03, 0xE0, 0x03, 0xE2, 0x03, 0xE2, 0x03, 0xE4, 0x03, 0xE4, 0x03, 0xE6, 0x03, 0xE6, 0x03, + 0xE8, 0x03, 0xE8, 0x03, 0xEA, 0x03, 0xEA, 0x03, 0xEC, 0x03, 0xEC, 0x03, 0xEE, 0x03, 0xEE, 0x03, + 0xF0, 0x03, 0xF1, 0x03, 0xF9, 0x03, 0xF3, 0x03, 0xF4, 0x03, 0xF5, 0x03, 0xF6, 0x03, 0xF7, 0x03, + 0xF7, 0x03, 0xF9, 0x03, 0xFA, 0x03, 0xFA, 0x03, 0xFC, 0x03, 0xFD, 0x03, 0xFE, 0x03, 0xFF, 0x03, + 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04, + 0x08, 0x04, 0x09, 0x04, 0x0A, 0x04, 0x0B, 0x04, 0x0C, 0x04, 0x0D, 0x04, 0x0E, 0x04, 0x0F, 0x04, + 0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04, 0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04, + 0x18, 0x04, 0x19, 0x04, 0x1A, 0x04, 0x1B, 0x04, 0x1C, 0x04, 0x1D, 0x04, 0x1E, 0x04, 0x1F, 0x04, + 0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04, 0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04, + 0x28, 0x04, 0x29, 0x04, 0x2A, 0x04, 0x2B, 0x04, 0x2C, 0x04, 0x2D, 0x04, 0x2E, 0x04, 0x2F, 0x04, + 0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04, 0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04, + 0x18, 0x04, 0x19, 0x04, 0x1A, 0x04, 0x1B, 0x04, 0x1C, 0x04, 0x1D, 0x04, 0x1E, 0x04, 0x1F, 0x04, + 0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04, 0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04, + 0x28, 0x04, 0x29, 0x04, 0x2A, 0x04, 0x2B, 0x04, 0x2C, 0x04, 0x2D, 0x04, 0x2E, 0x04, 0x2F, 0x04, + 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04, + 0x08, 0x04, 0x09, 0x04, 0x0A, 0x04, 0x0B, 0x04, 0x0C, 0x04, 0x0D, 0x04, 0x0E, 0x04, 0x0F, 0x04, + 0x60, 0x04, 0x60, 0x04, 0x62, 0x04, 0x62, 0x04, 0x64, 0x04, 0x64, 0x04, 0x66, 0x04, 0x66, 0x04, + 0x68, 0x04, 0x68, 0x04, 0x6A, 0x04, 0x6A, 0x04, 0x6C, 0x04, 0x6C, 0x04, 0x6E, 0x04, 0x6E, 0x04, + 0x70, 0x04, 0x70, 0x04, 0x72, 0x04, 0x72, 0x04, 0x74, 0x04, 0x74, 0x04, 0x76, 0x04, 0x76, 0x04, + 0x78, 0x04, 0x78, 0x04, 0x7A, 0x04, 0x7A, 0x04, 0x7C, 0x04, 0x7C, 0x04, 0x7E, 0x04, 0x7E, 0x04, + 0x80, 0x04, 0x80, 0x04, 0x82, 0x04, 0x83, 0x04, 0x84, 0x04, 0x85, 0x04, 0x86, 0x04, 0x87, 0x04, + 0x88, 0x04, 0x89, 0x04, 0x8A, 0x04, 0x8A, 0x04, 0x8C, 0x04, 0x8C, 0x04, 0x8E, 0x04, 0x8E, 0x04, + 0x90, 0x04, 0x90, 0x04, 0x92, 0x04, 0x92, 0x04, 0x94, 0x04, 0x94, 0x04, 0x96, 0x04, 0x96, 0x04, + 0x98, 0x04, 0x98, 0x04, 0x9A, 0x04, 0x9A, 0x04, 0x9C, 0x04, 0x9C, 0x04, 0x9E, 0x04, 0x9E, 0x04, + 0xA0, 0x04, 0xA0, 0x04, 0xA2, 0x04, 0xA2, 0x04, 0xA4, 0x04, 0xA4, 0x04, 0xA6, 0x04, 0xA6, 0x04, + 0xA8, 0x04, 0xA8, 0x04, 0xAA, 0x04, 0xAA, 0x04, 0xAC, 0x04, 0xAC, 0x04, 0xAE, 0x04, 0xAE, 0x04, + 0xB0, 0x04, 0xB0, 0x04, 0xB2, 0x04, 0xB2, 0x04, 0xB4, 0x04, 0xB4, 0x04, 0xB6, 0x04, 0xB6, 0x04, + 0xB8, 0x04, 0xB8, 0x04, 0xBA, 0x04, 0xBA, 0x04, 0xBC, 0x04, 0xBC, 0x04, 0xBE, 0x04, 0xBE, 0x04, + 0xC0, 0x04, 0xC1, 0x04, 0xC1, 0x04, 0xC3, 0x04, 0xC3, 0x04, 0xC5, 0x04, 0xC5, 0x04, 0xC7, 0x04, + 0xC7, 0x04, 0xC9, 0x04, 0xC9, 0x04, 0xCB, 0x04, 0xCB, 0x04, 0xCD, 0x04, 0xCD, 0x04, 0xC0, 0x04, + 0xD0, 0x04, 0xD0, 0x04, 0xD2, 0x04, 0xD2, 0x04, 0xD4, 0x04, 0xD4, 0x04, 0xD6, 0x04, 0xD6, 0x04, + 0xD8, 0x04, 0xD8, 0x04, 0xDA, 0x04, 0xDA, 0x04, 0xDC, 0x04, 0xDC, 0x04, 0xDE, 0x04, 0xDE, 0x04, + 0xE0, 0x04, 0xE0, 0x04, 0xE2, 0x04, 0xE2, 0x04, 0xE4, 0x04, 0xE4, 0x04, 0xE6, 0x04, 0xE6, 0x04, + 0xE8, 0x04, 0xE8, 0x04, 0xEA, 0x04, 0xEA, 0x04, 0xEC, 0x04, 0xEC, 0x04, 0xEE, 0x04, 0xEE, 0x04, + 0xF0, 0x04, 0xF0, 0x04, 0xF2, 0x04, 0xF2, 0x04, 0xF4, 0x04, 0xF4, 0x04, 0xF6, 0x04, 0xF6, 0x04, + 0xF8, 0x04, 0xF8, 0x04, 0xFA, 0x04, 0xFA, 0x04, 0xFC, 0x04, 0xFC, 0x04, 0xFE, 0x04, 0xFE, 0x04, + 0x00, 0x05, 0x00, 0x05, 0x02, 0x05, 0x02, 0x05, 0x04, 0x05, 0x04, 0x05, 0x06, 0x05, 0x06, 0x05, + 0x08, 0x05, 0x08, 0x05, 0x0A, 0x05, 0x0A, 0x05, 0x0C, 0x05, 0x0C, 0x05, 0x0E, 0x05, 0x0E, 0x05, + 0x10, 0x05, 0x10, 0x05, 0x12, 0x05, 0x12, 0x05, 0x14, 0x05, 0x15, 0x05, 0x16, 0x05, 0x17, 0x05, + 0x18, 0x05, 0x19, 0x05, 0x1A, 0x05, 0x1B, 0x05, 0x1C, 0x05, 0x1D, 0x05, 0x1E, 0x05, 0x1F, 0x05, + 0x20, 0x05, 0x21, 0x05, 0x22, 0x05, 0x23, 0x05, 0x24, 0x05, 0x25, 0x05, 0x26, 0x05, 0x27, 0x05, + 0x28, 0x05, 0x29, 0x05, 0x2A, 0x05, 0x2B, 0x05, 0x2C, 0x05, 0x2D, 0x05, 0x2E, 0x05, 0x2F, 0x05, + 0x30, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05, 0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05, + 0x38, 0x05, 0x39, 0x05, 0x3A, 0x05, 0x3B, 0x05, 0x3C, 0x05, 0x3D, 0x05, 0x3E, 0x05, 0x3F, 0x05, + 0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05, 0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05, + 0x48, 0x05, 0x49, 0x05, 0x4A, 0x05, 0x4B, 0x05, 0x4C, 0x05, 0x4D, 0x05, 0x4E, 0x05, 0x4F, 0x05, + 0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05, 0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0x57, 0x05, + 0x58, 0x05, 0x59, 0x05, 0x5A, 0x05, 0x5B, 0x05, 0x5C, 0x05, 0x5D, 0x05, 0x5E, 0x05, 0x5F, 0x05, + 0x60, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05, 0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05, + 0x38, 0x05, 0x39, 0x05, 0x3A, 0x05, 0x3B, 0x05, 0x3C, 0x05, 0x3D, 0x05, 0x3E, 0x05, 0x3F, 0x05, + 0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05, 0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05, + 0x48, 0x05, 0x49, 0x05, 0x4A, 0x05, 0x4B, 0x05, 0x4C, 0x05, 0x4D, 0x05, 0x4E, 0x05, 0x4F, 0x05, + 0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05, 0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0xFF, 0xFF, + 0xF6, 0x17, 0x63, 0x2C, 0x7E, 0x1D, 0x7F, 0x1D, 0x80, 0x1D, 0x81, 0x1D, 0x82, 0x1D, 0x83, 0x1D, + 0x84, 0x1D, 0x85, 0x1D, 0x86, 0x1D, 0x87, 0x1D, 0x88, 0x1D, 0x89, 0x1D, 0x8A, 0x1D, 0x8B, 0x1D, + 0x8C, 0x1D, 0x8D, 0x1D, 0x8E, 0x1D, 0x8F, 0x1D, 0x90, 0x1D, 0x91, 0x1D, 0x92, 0x1D, 0x93, 0x1D, + 0x94, 0x1D, 0x95, 0x1D, 0x96, 0x1D, 0x97, 0x1D, 0x98, 0x1D, 0x99, 0x1D, 0x9A, 0x1D, 0x9B, 0x1D, + 0x9C, 0x1D, 0x9D, 0x1D, 0x9E, 0x1D, 0x9F, 0x1D, 0xA0, 0x1D, 0xA1, 0x1D, 0xA2, 0x1D, 0xA3, 0x1D, + 0xA4, 0x1D, 0xA5, 0x1D, 0xA6, 0x1D, 0xA7, 0x1D, 0xA8, 0x1D, 0xA9, 0x1D, 0xAA, 0x1D, 0xAB, 0x1D, + 0xAC, 0x1D, 0xAD, 0x1D, 0xAE, 0x1D, 0xAF, 0x1D, 0xB0, 0x1D, 0xB1, 0x1D, 0xB2, 0x1D, 0xB3, 0x1D, + 0xB4, 0x1D, 0xB5, 0x1D, 0xB6, 0x1D, 0xB7, 0x1D, 0xB8, 0x1D, 0xB9, 0x1D, 0xBA, 0x1D, 0xBB, 0x1D, + 0xBC, 0x1D, 0xBD, 0x1D, 0xBE, 0x1D, 0xBF, 0x1D, 0xC0, 0x1D, 0xC1, 0x1D, 0xC2, 0x1D, 0xC3, 0x1D, + 0xC4, 0x1D, 0xC5, 0x1D, 0xC6, 0x1D, 0xC7, 0x1D, 0xC8, 0x1D, 0xC9, 0x1D, 0xCA, 0x1D, 0xCB, 0x1D, + 0xCC, 0x1D, 0xCD, 0x1D, 0xCE, 0x1D, 0xCF, 0x1D, 0xD0, 0x1D, 0xD1, 0x1D, 0xD2, 0x1D, 0xD3, 0x1D, + 0xD4, 0x1D, 0xD5, 0x1D, 0xD6, 0x1D, 0xD7, 0x1D, 0xD8, 0x1D, 0xD9, 0x1D, 0xDA, 0x1D, 0xDB, 0x1D, + 0xDC, 0x1D, 0xDD, 0x1D, 0xDE, 0x1D, 0xDF, 0x1D, 0xE0, 0x1D, 0xE1, 0x1D, 0xE2, 0x1D, 0xE3, 0x1D, + 0xE4, 0x1D, 0xE5, 0x1D, 0xE6, 0x1D, 0xE7, 0x1D, 0xE8, 0x1D, 0xE9, 0x1D, 0xEA, 0x1D, 0xEB, 0x1D, + 0xEC, 0x1D, 0xED, 0x1D, 0xEE, 0x1D, 0xEF, 0x1D, 0xF0, 0x1D, 0xF1, 0x1D, 0xF2, 0x1D, 0xF3, 0x1D, + 0xF4, 0x1D, 0xF5, 0x1D, 0xF6, 0x1D, 0xF7, 0x1D, 0xF8, 0x1D, 0xF9, 0x1D, 0xFA, 0x1D, 0xFB, 0x1D, + 0xFC, 0x1D, 0xFD, 0x1D, 0xFE, 0x1D, 0xFF, 0x1D, 0x00, 0x1E, 0x00, 0x1E, 0x02, 0x1E, 0x02, 0x1E, + 0x04, 0x1E, 0x04, 0x1E, 0x06, 0x1E, 0x06, 0x1E, 0x08, 0x1E, 0x08, 0x1E, 0x0A, 0x1E, 0x0A, 0x1E, + 0x0C, 0x1E, 0x0C, 0x1E, 0x0E, 0x1E, 0x0E, 0x1E, 0x10, 0x1E, 0x10, 0x1E, 0x12, 0x1E, 0x12, 0x1E, + 0x14, 0x1E, 0x14, 0x1E, 0x16, 0x1E, 0x16, 0x1E, 0x18, 0x1E, 0x18, 0x1E, 0x1A, 0x1E, 0x1A, 0x1E, + 0x1C, 0x1E, 0x1C, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x20, 0x1E, 0x20, 0x1E, 0x22, 0x1E, 0x22, 0x1E, + 0x24, 0x1E, 0x24, 0x1E, 0x26, 0x1E, 0x26, 0x1E, 0x28, 0x1E, 0x28, 0x1E, 0x2A, 0x1E, 0x2A, 0x1E, + 0x2C, 0x1E, 0x2C, 0x1E, 0x2E, 0x1E, 0x2E, 0x1E, 0x30, 0x1E, 0x30, 0x1E, 0x32, 0x1E, 0x32, 0x1E, + 0x34, 0x1E, 0x34, 0x1E, 0x36, 0x1E, 0x36, 0x1E, 0x38, 0x1E, 0x38, 0x1E, 0x3A, 0x1E, 0x3A, 0x1E, + 0x3C, 0x1E, 0x3C, 0x1E, 0x3E, 0x1E, 0x3E, 0x1E, 0x40, 0x1E, 0x40, 0x1E, 0x42, 0x1E, 0x42, 0x1E, + 0x44, 0x1E, 0x44, 0x1E, 0x46, 0x1E, 0x46, 0x1E, 0x48, 0x1E, 0x48, 0x1E, 0x4A, 0x1E, 0x4A, 0x1E, + 0x4C, 0x1E, 0x4C, 0x1E, 0x4E, 0x1E, 0x4E, 0x1E, 0x50, 0x1E, 0x50, 0x1E, 0x52, 0x1E, 0x52, 0x1E, + 0x54, 0x1E, 0x54, 0x1E, 0x56, 0x1E, 0x56, 0x1E, 0x58, 0x1E, 0x58, 0x1E, 0x5A, 0x1E, 0x5A, 0x1E, + 0x5C, 0x1E, 0x5C, 0x1E, 0x5E, 0x1E, 0x5E, 0x1E, 0x60, 0x1E, 0x60, 0x1E, 0x62, 0x1E, 0x62, 0x1E, + 0x64, 0x1E, 0x64, 0x1E, 0x66, 0x1E, 0x66, 0x1E, 0x68, 0x1E, 0x68, 0x1E, 0x6A, 0x1E, 0x6A, 0x1E, + 0x6C, 0x1E, 0x6C, 0x1E, 0x6E, 0x1E, 0x6E, 0x1E, 0x70, 0x1E, 0x70, 0x1E, 0x72, 0x1E, 0x72, 0x1E, + 0x74, 0x1E, 0x74, 0x1E, 0x76, 0x1E, 0x76, 0x1E, 0x78, 0x1E, 0x78, 0x1E, 0x7A, 0x1E, 0x7A, 0x1E, + 0x7C, 0x1E, 0x7C, 0x1E, 0x7E, 0x1E, 0x7E, 0x1E, 0x80, 0x1E, 0x80, 0x1E, 0x82, 0x1E, 0x82, 0x1E, + 0x84, 0x1E, 0x84, 0x1E, 0x86, 0x1E, 0x86, 0x1E, 0x88, 0x1E, 0x88, 0x1E, 0x8A, 0x1E, 0x8A, 0x1E, + 0x8C, 0x1E, 0x8C, 0x1E, 0x8E, 0x1E, 0x8E, 0x1E, 0x90, 0x1E, 0x90, 0x1E, 0x92, 0x1E, 0x92, 0x1E, + 0x94, 0x1E, 0x94, 0x1E, 0x96, 0x1E, 0x97, 0x1E, 0x98, 0x1E, 0x99, 0x1E, 0x9A, 0x1E, 0x9B, 0x1E, + 0x9C, 0x1E, 0x9D, 0x1E, 0x9E, 0x1E, 0x9F, 0x1E, 0xA0, 0x1E, 0xA0, 0x1E, 0xA2, 0x1E, 0xA2, 0x1E, + 0xA4, 0x1E, 0xA4, 0x1E, 0xA6, 0x1E, 0xA6, 0x1E, 0xA8, 0x1E, 0xA8, 0x1E, 0xAA, 0x1E, 0xAA, 0x1E, + 0xAC, 0x1E, 0xAC, 0x1E, 0xAE, 0x1E, 0xAE, 0x1E, 0xB0, 0x1E, 0xB0, 0x1E, 0xB2, 0x1E, 0xB2, 0x1E, + 0xB4, 0x1E, 0xB4, 0x1E, 0xB6, 0x1E, 0xB6, 0x1E, 0xB8, 0x1E, 0xB8, 0x1E, 0xBA, 0x1E, 0xBA, 0x1E, + 0xBC, 0x1E, 0xBC, 0x1E, 0xBE, 0x1E, 0xBE, 0x1E, 0xC0, 0x1E, 0xC0, 0x1E, 0xC2, 0x1E, 0xC2, 0x1E, + 0xC4, 0x1E, 0xC4, 0x1E, 0xC6, 0x1E, 0xC6, 0x1E, 0xC8, 0x1E, 0xC8, 0x1E, 0xCA, 0x1E, 0xCA, 0x1E, + 0xCC, 0x1E, 0xCC, 0x1E, 0xCE, 0x1E, 0xCE, 0x1E, 0xD0, 0x1E, 0xD0, 0x1E, 0xD2, 0x1E, 0xD2, 0x1E, + 0xD4, 0x1E, 0xD4, 0x1E, 0xD6, 0x1E, 0xD6, 0x1E, 0xD8, 0x1E, 0xD8, 0x1E, 0xDA, 0x1E, 0xDA, 0x1E, + 0xDC, 0x1E, 0xDC, 0x1E, 0xDE, 0x1E, 0xDE, 0x1E, 0xE0, 0x1E, 0xE0, 0x1E, 0xE2, 0x1E, 0xE2, 0x1E, + 0xE4, 0x1E, 0xE4, 0x1E, 0xE6, 0x1E, 0xE6, 0x1E, 0xE8, 0x1E, 0xE8, 0x1E, 0xEA, 0x1E, 0xEA, 0x1E, + 0xEC, 0x1E, 0xEC, 0x1E, 0xEE, 0x1E, 0xEE, 0x1E, 0xF0, 0x1E, 0xF0, 0x1E, 0xF2, 0x1E, 0xF2, 0x1E, + 0xF4, 0x1E, 0xF4, 0x1E, 0xF6, 0x1E, 0xF6, 0x1E, 0xF8, 0x1E, 0xF8, 0x1E, 0xFA, 0x1E, 0xFB, 0x1E, + 0xFC, 0x1E, 0xFD, 0x1E, 0xFE, 0x1E, 0xFF, 0x1E, 0x08, 0x1F, 0x09, 0x1F, 0x0A, 0x1F, 0x0B, 0x1F, + 0x0C, 0x1F, 0x0D, 0x1F, 0x0E, 0x1F, 0x0F, 0x1F, 0x08, 0x1F, 0x09, 0x1F, 0x0A, 0x1F, 0x0B, 0x1F, + 0x0C, 0x1F, 0x0D, 0x1F, 0x0E, 0x1F, 0x0F, 0x1F, 0x18, 0x1F, 0x19, 0x1F, 0x1A, 0x1F, 0x1B, 0x1F, + 0x1C, 0x1F, 0x1D, 0x1F, 0x16, 0x1F, 0x17, 0x1F, 0x18, 0x1F, 0x19, 0x1F, 0x1A, 0x1F, 0x1B, 0x1F, + 0x1C, 0x1F, 0x1D, 0x1F, 0x1E, 0x1F, 0x1F, 0x1F, 0x28, 0x1F, 0x29, 0x1F, 0x2A, 0x1F, 0x2B, 0x1F, + 0x2C, 0x1F, 0x2D, 0x1F, 0x2E, 0x1F, 0x2F, 0x1F, 0x28, 0x1F, 0x29, 0x1F, 0x2A, 0x1F, 0x2B, 0x1F, + 0x2C, 0x1F, 0x2D, 0x1F, 0x2E, 0x1F, 0x2F, 0x1F, 0x38, 0x1F, 0x39, 0x1F, 0x3A, 0x1F, 0x3B, 0x1F, + 0x3C, 0x1F, 0x3D, 0x1F, 0x3E, 0x1F, 0x3F, 0x1F, 0x38, 0x1F, 0x39, 0x1F, 0x3A, 0x1F, 0x3B, 0x1F, + 0x3C, 0x1F, 0x3D, 0x1F, 0x3E, 0x1F, 0x3F, 0x1F, 0x48, 0x1F, 0x49, 0x1F, 0x4A, 0x1F, 0x4B, 0x1F, + 0x4C, 0x1F, 0x4D, 0x1F, 0x46, 0x1F, 0x47, 0x1F, 0x48, 0x1F, 0x49, 0x1F, 0x4A, 0x1F, 0x4B, 0x1F, + 0x4C, 0x1F, 0x4D, 0x1F, 0x4E, 0x1F, 0x4F, 0x1F, 0x50, 0x1F, 0x59, 0x1F, 0x52, 0x1F, 0x5B, 0x1F, + 0x54, 0x1F, 0x5D, 0x1F, 0x56, 0x1F, 0x5F, 0x1F, 0x58, 0x1F, 0x59, 0x1F, 0x5A, 0x1F, 0x5B, 0x1F, + 0x5C, 0x1F, 0x5D, 0x1F, 0x5E, 0x1F, 0x5F, 0x1F, 0x68, 0x1F, 0x69, 0x1F, 0x6A, 0x1F, 0x6B, 0x1F, + 0x6C, 0x1F, 0x6D, 0x1F, 0x6E, 0x1F, 0x6F, 0x1F, 0x68, 0x1F, 0x69, 0x1F, 0x6A, 0x1F, 0x6B, 0x1F, + 0x6C, 0x1F, 0x6D, 0x1F, 0x6E, 0x1F, 0x6F, 0x1F, 0xBA, 0x1F, 0xBB, 0x1F, 0xC8, 0x1F, 0xC9, 0x1F, + 0xCA, 0x1F, 0xCB, 0x1F, 0xDA, 0x1F, 0xDB, 0x1F, 0xF8, 0x1F, 0xF9, 0x1F, 0xEA, 0x1F, 0xEB, 0x1F, + 0xFA, 0x1F, 0xFB, 0x1F, 0x7E, 0x1F, 0x7F, 0x1F, 0x88, 0x1F, 0x89, 0x1F, 0x8A, 0x1F, 0x8B, 0x1F, + 0x8C, 0x1F, 0x8D, 0x1F, 0x8E, 0x1F, 0x8F, 0x1F, 0x88, 0x1F, 0x89, 0x1F, 0x8A, 0x1F, 0x8B, 0x1F, + 0x8C, 0x1F, 0x8D, 0x1F, 0x8E, 0x1F, 0x8F, 0x1F, 0x98, 0x1F, 0x99, 0x1F, 0x9A, 0x1F, 0x9B, 0x1F, + 0x9C, 0x1F, 0x9D, 0x1F, 0x9E, 0x1F, 0x9F, 0x1F, 0x98, 0x1F, 0x99, 0x1F, 0x9A, 0x1F, 0x9B, 0x1F, + 0x9C, 0x1F, 0x9D, 0x1F, 0x9E, 0x1F, 0x9F, 0x1F, 0xA8, 0x1F, 0xA9, 0x1F, 0xAA, 0x1F, 0xAB, 0x1F, + 0xAC, 0x1F, 0xAD, 0x1F, 0xAE, 0x1F, 0xAF, 0x1F, 0xA8, 0x1F, 0xA9, 0x1F, 0xAA, 0x1F, 0xAB, 0x1F, + 0xAC, 0x1F, 0xAD, 0x1F, 0xAE, 0x1F, 0xAF, 0x1F, 0xB8, 0x1F, 0xB9, 0x1F, 0xB2, 0x1F, 0xBC, 0x1F, + 0xB4, 0x1F, 0xB5, 0x1F, 0xB6, 0x1F, 0xB7, 0x1F, 0xB8, 0x1F, 0xB9, 0x1F, 0xBA, 0x1F, 0xBB, 0x1F, + 0xBC, 0x1F, 0xBD, 0x1F, 0xBE, 0x1F, 0xBF, 0x1F, 0xC0, 0x1F, 0xC1, 0x1F, 0xC2, 0x1F, 0xC3, 0x1F, + 0xC4, 0x1F, 0xC5, 0x1F, 0xC6, 0x1F, 0xC7, 0x1F, 0xC8, 0x1F, 0xC9, 0x1F, 0xCA, 0x1F, 0xCB, 0x1F, + 0xC3, 0x1F, 0xCD, 0x1F, 0xCE, 0x1F, 0xCF, 0x1F, 0xD8, 0x1F, 0xD9, 0x1F, 0xD2, 0x1F, 0xD3, 0x1F, + 0xD4, 0x1F, 0xD5, 0x1F, 0xD6, 0x1F, 0xD7, 0x1F, 0xD8, 0x1F, 0xD9, 0x1F, 0xDA, 0x1F, 0xDB, 0x1F, + 0xDC, 0x1F, 0xDD, 0x1F, 0xDE, 0x1F, 0xDF, 0x1F, 0xE8, 0x1F, 0xE9, 0x1F, 0xE2, 0x1F, 0xE3, 0x1F, + 0xE4, 0x1F, 0xEC, 0x1F, 0xE6, 0x1F, 0xE7, 0x1F, 0xE8, 0x1F, 0xE9, 0x1F, 0xEA, 0x1F, 0xEB, 0x1F, + 0xEC, 0x1F, 0xED, 0x1F, 0xEE, 0x1F, 0xEF, 0x1F, 0xF0, 0x1F, 0xF1, 0x1F, 0xF2, 0x1F, 0xF3, 0x1F, + 0xF4, 0x1F, 0xF5, 0x1F, 0xF6, 0x1F, 0xF7, 0x1F, 0xF8, 0x1F, 0xF9, 0x1F, 0xFA, 0x1F, 0xFB, 0x1F, + 0xF3, 0x1F, 0xFD, 0x1F, 0xFE, 0x1F, 0xFF, 0x1F, 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x20, + 0x04, 0x20, 0x05, 0x20, 0x06, 0x20, 0x07, 0x20, 0x08, 0x20, 0x09, 0x20, 0x0A, 0x20, 0x0B, 0x20, + 0x0C, 0x20, 0x0D, 0x20, 0x0E, 0x20, 0x0F, 0x20, 0x10, 0x20, 0x11, 0x20, 0x12, 0x20, 0x13, 0x20, + 0x14, 0x20, 0x15, 0x20, 0x16, 0x20, 0x17, 0x20, 0x18, 0x20, 0x19, 0x20, 0x1A, 0x20, 0x1B, 0x20, + 0x1C, 0x20, 0x1D, 0x20, 0x1E, 0x20, 0x1F, 0x20, 0x20, 0x20, 0x21, 0x20, 0x22, 0x20, 0x23, 0x20, + 0x24, 0x20, 0x25, 0x20, 0x26, 0x20, 0x27, 0x20, 0x28, 0x20, 0x29, 0x20, 0x2A, 0x20, 0x2B, 0x20, + 0x2C, 0x20, 0x2D, 0x20, 0x2E, 0x20, 0x2F, 0x20, 0x30, 0x20, 0x31, 0x20, 0x32, 0x20, 0x33, 0x20, + 0x34, 0x20, 0x35, 0x20, 0x36, 0x20, 0x37, 0x20, 0x38, 0x20, 0x39, 0x20, 0x3A, 0x20, 0x3B, 0x20, + 0x3C, 0x20, 0x3D, 0x20, 0x3E, 0x20, 0x3F, 0x20, 0x40, 0x20, 0x41, 0x20, 0x42, 0x20, 0x43, 0x20, + 0x44, 0x20, 0x45, 0x20, 0x46, 0x20, 0x47, 0x20, 0x48, 0x20, 0x49, 0x20, 0x4A, 0x20, 0x4B, 0x20, + 0x4C, 0x20, 0x4D, 0x20, 0x4E, 0x20, 0x4F, 0x20, 0x50, 0x20, 0x51, 0x20, 0x52, 0x20, 0x53, 0x20, + 0x54, 0x20, 0x55, 0x20, 0x56, 0x20, 0x57, 0x20, 0x58, 0x20, 0x59, 0x20, 0x5A, 0x20, 0x5B, 0x20, + 0x5C, 0x20, 0x5D, 0x20, 0x5E, 0x20, 0x5F, 0x20, 0x60, 0x20, 0x61, 0x20, 0x62, 0x20, 0x63, 0x20, + 0x64, 0x20, 0x65, 0x20, 0x66, 0x20, 0x67, 0x20, 0x68, 0x20, 0x69, 0x20, 0x6A, 0x20, 0x6B, 0x20, + 0x6C, 0x20, 0x6D, 0x20, 0x6E, 0x20, 0x6F, 0x20, 0x70, 0x20, 0x71, 0x20, 0x72, 0x20, 0x73, 0x20, + 0x74, 0x20, 0x75, 0x20, 0x76, 0x20, 0x77, 0x20, 0x78, 0x20, 0x79, 0x20, 0x7A, 0x20, 0x7B, 0x20, + 0x7C, 0x20, 0x7D, 0x20, 0x7E, 0x20, 0x7F, 0x20, 0x80, 0x20, 0x81, 0x20, 0x82, 0x20, 0x83, 0x20, + 0x84, 0x20, 0x85, 0x20, 0x86, 0x20, 0x87, 0x20, 0x88, 0x20, 0x89, 0x20, 0x8A, 0x20, 0x8B, 0x20, + 0x8C, 0x20, 0x8D, 0x20, 0x8E, 0x20, 0x8F, 0x20, 0x90, 0x20, 0x91, 0x20, 0x92, 0x20, 0x93, 0x20, + 0x94, 0x20, 0x95, 0x20, 0x96, 0x20, 0x97, 0x20, 0x98, 0x20, 0x99, 0x20, 0x9A, 0x20, 0x9B, 0x20, + 0x9C, 0x20, 0x9D, 0x20, 0x9E, 0x20, 0x9F, 0x20, 0xA0, 0x20, 0xA1, 0x20, 0xA2, 0x20, 0xA3, 0x20, + 0xA4, 0x20, 0xA5, 0x20, 0xA6, 0x20, 0xA7, 0x20, 0xA8, 0x20, 0xA9, 0x20, 0xAA, 0x20, 0xAB, 0x20, + 0xAC, 0x20, 0xAD, 0x20, 0xAE, 0x20, 0xAF, 0x20, 0xB0, 0x20, 0xB1, 0x20, 0xB2, 0x20, 0xB3, 0x20, + 0xB4, 0x20, 0xB5, 0x20, 0xB6, 0x20, 0xB7, 0x20, 0xB8, 0x20, 0xB9, 0x20, 0xBA, 0x20, 0xBB, 0x20, + 0xBC, 0x20, 0xBD, 0x20, 0xBE, 0x20, 0xBF, 0x20, 0xC0, 0x20, 0xC1, 0x20, 0xC2, 0x20, 0xC3, 0x20, + 0xC4, 0x20, 0xC5, 0x20, 0xC6, 0x20, 0xC7, 0x20, 0xC8, 0x20, 0xC9, 0x20, 0xCA, 0x20, 0xCB, 0x20, + 0xCC, 0x20, 0xCD, 0x20, 0xCE, 0x20, 0xCF, 0x20, 0xD0, 0x20, 0xD1, 0x20, 0xD2, 0x20, 0xD3, 0x20, + 0xD4, 0x20, 0xD5, 0x20, 0xD6, 0x20, 0xD7, 0x20, 0xD8, 0x20, 0xD9, 0x20, 0xDA, 0x20, 0xDB, 0x20, + 0xDC, 0x20, 0xDD, 0x20, 0xDE, 0x20, 0xDF, 0x20, 0xE0, 0x20, 0xE1, 0x20, 0xE2, 0x20, 0xE3, 0x20, + 0xE4, 0x20, 0xE5, 0x20, 0xE6, 0x20, 0xE7, 0x20, 0xE8, 0x20, 0xE9, 0x20, 0xEA, 0x20, 0xEB, 0x20, + 0xEC, 0x20, 0xED, 0x20, 0xEE, 0x20, 0xEF, 0x20, 0xF0, 0x20, 0xF1, 0x20, 0xF2, 0x20, 0xF3, 0x20, + 0xF4, 0x20, 0xF5, 0x20, 0xF6, 0x20, 0xF7, 0x20, 0xF8, 0x20, 0xF9, 0x20, 0xFA, 0x20, 0xFB, 0x20, + 0xFC, 0x20, 0xFD, 0x20, 0xFE, 0x20, 0xFF, 0x20, 0x00, 0x21, 0x01, 0x21, 0x02, 0x21, 0x03, 0x21, + 0x04, 0x21, 0x05, 0x21, 0x06, 0x21, 0x07, 0x21, 0x08, 0x21, 0x09, 0x21, 0x0A, 0x21, 0x0B, 0x21, + 0x0C, 0x21, 0x0D, 0x21, 0x0E, 0x21, 0x0F, 0x21, 0x10, 0x21, 0x11, 0x21, 0x12, 0x21, 0x13, 0x21, + 0x14, 0x21, 0x15, 0x21, 0x16, 0x21, 0x17, 0x21, 0x18, 0x21, 0x19, 0x21, 0x1A, 0x21, 0x1B, 0x21, + 0x1C, 0x21, 0x1D, 0x21, 0x1E, 0x21, 0x1F, 0x21, 0x20, 0x21, 0x21, 0x21, 0x22, 0x21, 0x23, 0x21, + 0x24, 0x21, 0x25, 0x21, 0x26, 0x21, 0x27, 0x21, 0x28, 0x21, 0x29, 0x21, 0x2A, 0x21, 0x2B, 0x21, + 0x2C, 0x21, 0x2D, 0x21, 0x2E, 0x21, 0x2F, 0x21, 0x30, 0x21, 0x31, 0x21, 0x32, 0x21, 0x33, 0x21, + 0x34, 0x21, 0x35, 0x21, 0x36, 0x21, 0x37, 0x21, 0x38, 0x21, 0x39, 0x21, 0x3A, 0x21, 0x3B, 0x21, + 0x3C, 0x21, 0x3D, 0x21, 0x3E, 0x21, 0x3F, 0x21, 0x40, 0x21, 0x41, 0x21, 0x42, 0x21, 0x43, 0x21, + 0x44, 0x21, 0x45, 0x21, 0x46, 0x21, 0x47, 0x21, 0x48, 0x21, 0x49, 0x21, 0x4A, 0x21, 0x4B, 0x21, + 0x4C, 0x21, 0x4D, 0x21, 0x32, 0x21, 0x4F, 0x21, 0x50, 0x21, 0x51, 0x21, 0x52, 0x21, 0x53, 0x21, + 0x54, 0x21, 0x55, 0x21, 0x56, 0x21, 0x57, 0x21, 0x58, 0x21, 0x59, 0x21, 0x5A, 0x21, 0x5B, 0x21, + 0x5C, 0x21, 0x5D, 0x21, 0x5E, 0x21, 0x5F, 0x21, 0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21, + 0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21, 0x68, 0x21, 0x69, 0x21, 0x6A, 0x21, 0x6B, 0x21, + 0x6C, 0x21, 0x6D, 0x21, 0x6E, 0x21, 0x6F, 0x21, 0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21, + 0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21, 0x68, 0x21, 0x69, 0x21, 0x6A, 0x21, 0x6B, 0x21, + 0x6C, 0x21, 0x6D, 0x21, 0x6E, 0x21, 0x6F, 0x21, 0x80, 0x21, 0x81, 0x21, 0x82, 0x21, 0x83, 0x21, + 0x83, 0x21, 0xFF, 0xFF, 0x4B, 0x03, 0xB6, 0x24, 0xB7, 0x24, 0xB8, 0x24, 0xB9, 0x24, 0xBA, 0x24, + 0xBB, 0x24, 0xBC, 0x24, 0xBD, 0x24, 0xBE, 0x24, 0xBF, 0x24, 0xC0, 0x24, 0xC1, 0x24, 0xC2, 0x24, + 0xC3, 0x24, 0xC4, 0x24, 0xC5, 0x24, 0xC6, 0x24, 0xC7, 0x24, 0xC8, 0x24, 0xC9, 0x24, 0xCA, 0x24, + 0xCB, 0x24, 0xCC, 0x24, 0xCD, 0x24, 0xCE, 0x24, 0xCF, 0x24, 0xFF, 0xFF, 0x46, 0x07, 0x00, 0x2C, + 0x01, 0x2C, 0x02, 0x2C, 0x03, 0x2C, 0x04, 0x2C, 0x05, 0x2C, 0x06, 0x2C, 0x07, 0x2C, 0x08, 0x2C, + 0x09, 0x2C, 0x0A, 0x2C, 0x0B, 0x2C, 0x0C, 0x2C, 0x0D, 0x2C, 0x0E, 0x2C, 0x0F, 0x2C, 0x10, 0x2C, + 0x11, 0x2C, 0x12, 0x2C, 0x13, 0x2C, 0x14, 0x2C, 0x15, 0x2C, 0x16, 0x2C, 0x17, 0x2C, 0x18, 0x2C, + 0x19, 0x2C, 0x1A, 0x2C, 0x1B, 0x2C, 0x1C, 0x2C, 0x1D, 0x2C, 0x1E, 0x2C, 0x1F, 0x2C, 0x20, 0x2C, + 0x21, 0x2C, 0x22, 0x2C, 0x23, 0x2C, 0x24, 0x2C, 0x25, 0x2C, 0x26, 0x2C, 0x27, 0x2C, 0x28, 0x2C, + 0x29, 0x2C, 0x2A, 0x2C, 0x2B, 0x2C, 0x2C, 0x2C, 0x2D, 0x2C, 0x2E, 0x2C, 0x5F, 0x2C, 0x60, 0x2C, + 0x60, 0x2C, 0x62, 0x2C, 0x63, 0x2C, 0x64, 0x2C, 0x65, 0x2C, 0x66, 0x2C, 0x67, 0x2C, 0x67, 0x2C, + 0x69, 0x2C, 0x69, 0x2C, 0x6B, 0x2C, 0x6B, 0x2C, 0x6D, 0x2C, 0x6E, 0x2C, 0x6F, 0x2C, 0x70, 0x2C, + 0x71, 0x2C, 0x72, 0x2C, 0x73, 0x2C, 0x74, 0x2C, 0x75, 0x2C, 0x75, 0x2C, 0x77, 0x2C, 0x78, 0x2C, + 0x79, 0x2C, 0x7A, 0x2C, 0x7B, 0x2C, 0x7C, 0x2C, 0x7D, 0x2C, 0x7E, 0x2C, 0x7F, 0x2C, 0x80, 0x2C, + 0x80, 0x2C, 0x82, 0x2C, 0x82, 0x2C, 0x84, 0x2C, 0x84, 0x2C, 0x86, 0x2C, 0x86, 0x2C, 0x88, 0x2C, + 0x88, 0x2C, 0x8A, 0x2C, 0x8A, 0x2C, 0x8C, 0x2C, 0x8C, 0x2C, 0x8E, 0x2C, 0x8E, 0x2C, 0x90, 0x2C, + 0x90, 0x2C, 0x92, 0x2C, 0x92, 0x2C, 0x94, 0x2C, 0x94, 0x2C, 0x96, 0x2C, 0x96, 0x2C, 0x98, 0x2C, + 0x98, 0x2C, 0x9A, 0x2C, 0x9A, 0x2C, 0x9C, 0x2C, 0x9C, 0x2C, 0x9E, 0x2C, 0x9E, 0x2C, 0xA0, 0x2C, + 0xA0, 0x2C, 0xA2, 0x2C, 0xA2, 0x2C, 0xA4, 0x2C, 0xA4, 0x2C, 0xA6, 0x2C, 0xA6, 0x2C, 0xA8, 0x2C, + 0xA8, 0x2C, 0xAA, 0x2C, 0xAA, 0x2C, 0xAC, 0x2C, 0xAC, 0x2C, 0xAE, 0x2C, 0xAE, 0x2C, 0xB0, 0x2C, + 0xB0, 0x2C, 0xB2, 0x2C, 0xB2, 0x2C, 0xB4, 0x2C, 0xB4, 0x2C, 0xB6, 0x2C, 0xB6, 0x2C, 0xB8, 0x2C, + 0xB8, 0x2C, 0xBA, 0x2C, 0xBA, 0x2C, 0xBC, 0x2C, 0xBC, 0x2C, 0xBE, 0x2C, 0xBE, 0x2C, 0xC0, 0x2C, + 0xC0, 0x2C, 0xC2, 0x2C, 0xC2, 0x2C, 0xC4, 0x2C, 0xC4, 0x2C, 0xC6, 0x2C, 0xC6, 0x2C, 0xC8, 0x2C, + 0xC8, 0x2C, 0xCA, 0x2C, 0xCA, 0x2C, 0xCC, 0x2C, 0xCC, 0x2C, 0xCE, 0x2C, 0xCE, 0x2C, 0xD0, 0x2C, + 0xD0, 0x2C, 0xD2, 0x2C, 0xD2, 0x2C, 0xD4, 0x2C, 0xD4, 0x2C, 0xD6, 0x2C, 0xD6, 0x2C, 0xD8, 0x2C, + 0xD8, 0x2C, 0xDA, 0x2C, 0xDA, 0x2C, 0xDC, 0x2C, 0xDC, 0x2C, 0xDE, 0x2C, 0xDE, 0x2C, 0xE0, 0x2C, + 0xE0, 0x2C, 0xE2, 0x2C, 0xE2, 0x2C, 0xE4, 0x2C, 0xE5, 0x2C, 0xE6, 0x2C, 0xE7, 0x2C, 0xE8, 0x2C, + 0xE9, 0x2C, 0xEA, 0x2C, 0xEB, 0x2C, 0xEC, 0x2C, 0xED, 0x2C, 0xEE, 0x2C, 0xEF, 0x2C, 0xF0, 0x2C, + 0xF1, 0x2C, 0xF2, 0x2C, 0xF3, 0x2C, 0xF4, 0x2C, 0xF5, 0x2C, 0xF6, 0x2C, 0xF7, 0x2C, 0xF8, 0x2C, + 0xF9, 0x2C, 0xFA, 0x2C, 0xFB, 0x2C, 0xFC, 0x2C, 0xFD, 0x2C, 0xFE, 0x2C, 0xFF, 0x2C, 0xA0, 0x10, + 0xA1, 0x10, 0xA2, 0x10, 0xA3, 0x10, 0xA4, 0x10, 0xA5, 0x10, 0xA6, 0x10, 0xA7, 0x10, 0xA8, 0x10, + 0xA9, 0x10, 0xAA, 0x10, 0xAB, 0x10, 0xAC, 0x10, 0xAD, 0x10, 0xAE, 0x10, 0xAF, 0x10, 0xB0, 0x10, + 0xB1, 0x10, 0xB2, 0x10, 0xB3, 0x10, 0xB4, 0x10, 0xB5, 0x10, 0xB6, 0x10, 0xB7, 0x10, 0xB8, 0x10, + 0xB9, 0x10, 0xBA, 0x10, 0xBB, 0x10, 0xBC, 0x10, 0xBD, 0x10, 0xBE, 0x10, 0xBF, 0x10, 0xC0, 0x10, + 0xC1, 0x10, 0xC2, 0x10, 0xC3, 0x10, 0xC4, 0x10, 0xC5, 0x10, 0xFF, 0xFF, 0x1B, 0xD2, 0x21, 0xFF, + 0x22, 0xFF, 0x23, 0xFF, 0x24, 0xFF, 0x25, 0xFF, 0x26, 0xFF, 0x27, 0xFF, 0x28, 0xFF, 0x29, 0xFF, + 0x2A, 0xFF, 0x2B, 0xFF, 0x2C, 0xFF, 0x2D, 0xFF, 0x2E, 0xFF, 0x2F, 0xFF, 0x30, 0xFF, 0x31, 0xFF, + 0x32, 0xFF, 0x33, 0xFF, 0x34, 0xFF, 0x35, 0xFF, 0x36, 0xFF, 0x37, 0xFF, 0x38, 0xFF, 0x39, 0xFF, + 0x3A, 0xFF, 0x5B, 0xFF, 0x5C, 0xFF, 0x5D, 0xFF, 0x5E, 0xFF, 0x5F, 0xFF, 0x60, 0xFF, 0x61, 0xFF, + 0x62, 0xFF, 0x63, 0xFF, 0x64, 0xFF, 0x65, 0xFF, 0x66, 0xFF, 0x67, 0xFF, 0x68, 0xFF, 0x69, 0xFF, + 0x6A, 0xFF, 0x6B, 0xFF, 0x6C, 0xFF, 0x6D, 0xFF, 0x6E, 0xFF, 0x6F, 0xFF, 0x70, 0xFF, 0x71, 0xFF, + 0x72, 0xFF, 0x73, 0xFF, 0x74, 0xFF, 0x75, 0xFF, 0x76, 0xFF, 0x77, 0xFF, 0x78, 0xFF, 0x79, 0xFF, + 0x7A, 0xFF, 0x7B, 0xFF, 0x7C, 0xFF, 0x7D, 0xFF, 0x7E, 0xFF, 0x7F, 0xFF, 0x80, 0xFF, 0x81, 0xFF, + 0x82, 0xFF, 0x83, 0xFF, 0x84, 0xFF, 0x85, 0xFF, 0x86, 0xFF, 0x87, 0xFF, 0x88, 0xFF, 0x89, 0xFF, + 0x8A, 0xFF, 0x8B, 0xFF, 0x8C, 0xFF, 0x8D, 0xFF, 0x8E, 0xFF, 0x8F, 0xFF, 0x90, 0xFF, 0x91, 0xFF, + 0x92, 0xFF, 0x93, 0xFF, 0x94, 0xFF, 0x95, 0xFF, 0x96, 0xFF, 0x97, 0xFF, 0x98, 0xFF, 0x99, 0xFF, + 0x9A, 0xFF, 0x9B, 0xFF, 0x9C, 0xFF, 0x9D, 0xFF, 0x9E, 0xFF, 0x9F, 0xFF, 0xA0, 0xFF, 0xA1, 0xFF, + 0xA2, 0xFF, 0xA3, 0xFF, 0xA4, 0xFF, 0xA5, 0xFF, 0xA6, 0xFF, 0xA7, 0xFF, 0xA8, 0xFF, 0xA9, 0xFF, + 0xAA, 0xFF, 0xAB, 0xFF, 0xAC, 0xFF, 0xAD, 0xFF, 0xAE, 0xFF, 0xAF, 0xFF, 0xB0, 0xFF, 0xB1, 0xFF, + 0xB2, 0xFF, 0xB3, 0xFF, 0xB4, 0xFF, 0xB5, 0xFF, 0xB6, 0xFF, 0xB7, 0xFF, 0xB8, 0xFF, 0xB9, 0xFF, + 0xBA, 0xFF, 0xBB, 0xFF, 0xBC, 0xFF, 0xBD, 0xFF, 0xBE, 0xFF, 0xBF, 0xFF, 0xC0, 0xFF, 0xC1, 0xFF, + 0xC2, 0xFF, 0xC3, 0xFF, 0xC4, 0xFF, 0xC5, 0xFF, 0xC6, 0xFF, 0xC7, 0xFF, 0xC8, 0xFF, 0xC9, 0xFF, + 0xCA, 0xFF, 0xCB, 0xFF, 0xCC, 0xFF, 0xCD, 0xFF, 0xCE, 0xFF, 0xCF, 0xFF, 0xD0, 0xFF, 0xD1, 0xFF, + 0xD2, 0xFF, 0xD3, 0xFF, 0xD4, 0xFF, 0xD5, 0xFF, 0xD6, 0xFF, 0xD7, 0xFF, 0xD8, 0xFF, 0xD9, 0xFF, + 0xDA, 0xFF, 0xDB, 0xFF, 0xDC, 0xFF, 0xDD, 0xFF, 0xDE, 0xFF, 0xDF, 0xFF, 0xE0, 0xFF, 0xE1, 0xFF, + 0xE2, 0xFF, 0xE3, 0xFF, 0xE4, 0xFF, 0xE5, 0xFF, 0xE6, 0xFF, 0xE7, 0xFF, 0xE8, 0xFF, 0xE9, 0xFF, + 0xEA, 0xFF, 0xEB, 0xFF, 0xEC, 0xFF, 0xED, 0xFF, 0xEE, 0xFF, 0xEF, 0xFF, 0xF0, 0xFF, 0xF1, 0xFF, + 0xF2, 0xFF, 0xF3, 0xFF, 0xF4, 0xFF, 0xF5, 0xFF, 0xF6, 0xFF, 0xF7, 0xFF, 0xF8, 0xFF, 0xF9, 0xFF, + 0xFA, 0xFF, 0xFB, 0xFF, 0xFC, 0xFF, 0xFD, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF +}; + +#endif /* _UPCASE_H */ diff --git a/fs/exfat/version.h b/fs/exfat/version.h new file mode 100644 index 00000000..def916cf --- /dev/null +++ b/fs/exfat/version.h @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * version.h: exFAT version definition + */ + +#define EXFAT_BASE_VERSION "2.2.0" +#define EXFAT_EXTRAVERSION "3" +#define EXFAT_VARIANT "arter97" +#define EXFAT_VERSION EXFAT_BASE_VERSION "-" EXFAT_EXTRAVERSION EXFAT_VARIANT diff --git a/fs/exfat/xattr.c b/fs/exfat/xattr.c new file mode 100644 index 00000000..ab3bb171 --- /dev/null +++ b/fs/exfat/xattr.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * xattr.c: exFAT code for supporting xattr(Extended File Attributes) + */ + +#include "config.h" + +#ifdef CONFIG_EXFAT_VIRTUAL_XATTR + +#include +#include +#include +#include +#include "exfat.h" + +#ifndef CONFIG_EXFAT_VIRTUAL_XATTR_SELINUX_LABEL +#define CONFIG_EXFAT_VIRTUAL_XATTR_SELINUX_LABEL ("undefined") +#endif + +static const char default_xattr[] = CONFIG_EXFAT_VIRTUAL_XATTR_SELINUX_LABEL; + +static int can_support(const char *name) +{ + if (!name || strcmp(name, "security.selinux")) + return -1; + return 0; +} + +ssize_t exfat_listxattr(struct dentry *dentry, char *list, size_t size) +{ + return 0; +} + +static int __exfat_xattr_check_support(const char *name) +{ + if (can_support(name)) + return -EOPNOTSUPP; + + return 0; +} + +ssize_t __exfat_getxattr(const char *name, void *value, size_t size) +{ + if (can_support(name)) + return -EOPNOTSUPP; + + if ((size > strlen(default_xattr)+1) && value) + strcpy(value, default_xattr); + + return strlen(default_xattr); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) +static int exfat_xattr_get(const struct xattr_handler *handler, + struct dentry *dentry, struct inode *inode, + const char *name, void *buffer, size_t size) +{ + return __exfat_getxattr(name, buffer, size); +} + +static int exfat_xattr_set(const struct xattr_handler *handler, + struct dentry *dentry, struct inode *inode, + const char *name, const void *value, size_t size, + int flags) +{ + return __exfat_xattr_check_support(name); +} + +const struct xattr_handler exfat_xattr_handler = { + .prefix = "", /* match anything */ + .get = exfat_xattr_get, + .set = exfat_xattr_set, +}; + +const struct xattr_handler *exfat_xattr_handlers[] = { + &exfat_xattr_handler, + NULL +}; + +void setup_exfat_xattr_handler(struct super_block *sb) +{ + sb->s_xattr = exfat_xattr_handlers; +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) */ +int exfat_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) +{ + return __exfat_xattr_check_support(name); +} + +ssize_t exfat_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) +{ + return __exfat_getxattr(name, value, size); +} + +int exfat_removexattr(struct dentry *dentry, const char *name) +{ + return __exfat_xattr_check_support(name); +} + +void setup_exfat_xattr_handler(struct super_block *sb) +{ + /* DO NOTHING */ +} +#endif + +#endif /* CONFIG_EXFAT_VIRTUAL_XATTR */ diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 902544e7..8b15179b 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -778,7 +778,10 @@ got: ext4_itable_unused_set(sb, gdp, (EXT4_INODES_PER_GROUP(sb) - ino)); up_read(&grp->alloc_sem); + } else { + ext4_lock_group(sb, group); } + ext4_free_inodes_set(sb, gdp, ext4_free_inodes_count(sb, gdp) - 1); if (S_ISDIR(mode)) { ext4_used_dirs_set(sb, gdp, ext4_used_dirs_count(sb, gdp) + 1); @@ -790,8 +793,8 @@ got: } if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { gdp->bg_checksum = ext4_group_desc_csum(sbi, group, gdp); - ext4_unlock_group(sb, group); } + ext4_unlock_group(sb, group); BUFFER_TRACE(group_desc_bh, "call ext4_handle_dirty_metadata"); err = ext4_handle_dirty_metadata(handle, NULL, group_desc_bh); diff --git a/fs/fat/Kconfig b/fs/fat/Kconfig index 182f9ffe..037500f9 100644 --- a/fs/fat/Kconfig +++ b/fs/fat/Kconfig @@ -83,6 +83,44 @@ config FAT_DEFAULT_CODEPAGE It can be overridden with the "codepage" mount option. See for more information. + +config FAT_FCBMP + tristate "Free clusters bitmap support for FAT" + depends on MSDOS_FS || VFAT_FS + select BMP_TREE + default n + help + This option should be set to support the fast alloc clusters of your + FAT filesystems. + + +choice + prompt "Fat Filesystem check disk Algorithm" + depends on MSDOS_FS || VFAT_FS + + +config FAT_FSCK + bool "Check disk support by Bmptree" + select BMP_TREE + help + This option should be set to check disk when FAT filesystems + execption by bitmap tree. + + +config FAT_CHK_DISK + bool "Check disk support by Anyka Rtos Alg" + help + This option should be set to check disk when FAT filesystems execption (Anyka Rtos). + +config FAT_NOT_CHKDSK + bool "Unsupport Fat filesystem disk check." + +endchoice + + +config BMP_TREE + bool + config FAT_DEFAULT_IOCHARSET string "Default iocharset for FAT" depends on VFAT_FS diff --git a/fs/fat/Makefile b/fs/fat/Makefile index e0619032..2fa335af 100644 --- a/fs/fat/Makefile +++ b/fs/fat/Makefile @@ -5,6 +5,8 @@ obj-$(CONFIG_FAT_FS) += fat.o obj-$(CONFIG_VFAT_FS) += vfat.o obj-$(CONFIG_MSDOS_FS) += msdos.o +obj-$(CONFIG_BMP_TREE) += bmp_tree.o +obj-$(CONFIG_FAT_CHK_DISK) += chkdsk.o fat-y := cache.o dir.o fatent.o file.o inode.o misc.o vfat-y := namei_vfat.o diff --git a/fs/fat/bmp_tree.c b/fs/fat/bmp_tree.c new file mode 100644 index 00000000..2960200a --- /dev/null +++ b/fs/fat/bmp_tree.c @@ -0,0 +1,1063 @@ +/* + * linux/fs/fat/bmp_tree.c + * + * Written 1992,1993 by Werner Almesberger + * VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner + * Rewritten for the constant inumbers support by Al Viro + * + * author:lixinhai + * date :2013-11-01 + * + * + */ + +#include +#include +#include +#include +#include "bmp_tree.h" + +//#define FAT_BMPTREE_DBG + +#ifdef FAT_BMPTREE_DBG +#ifdef __KERNEL__ +#define PDEBUG(fmt, args...) printk(KERN_INFO "bmptree:" fmt, ##args) +#else +#define PDEBUG(fmt, args...) fprintf(stderr, "%s %d:" fmt,__FILE__, __LINE__, ## args) +#endif +#else +#define PDEBUG(fmt, args...) +#endif + + +static inline unsigned long bmptree_maxindex(struct bmptree *bt, + unsigned long height) +{ + return bt->height_to_maxidx[height]; +} + + +static unsigned long __calc_maxidx(unsigned long height) +{ + unsigned long width = height * BMPTREE_NODE_MAP_SHIFT; + int shift = BMPTREE_TREE_INDEX_BITS - width; + + if(shift < 0) + return ~0UL; + if(shift > BMPTREE_TREE_INDEX_BITS) + return 0UL; + + return ~0UL >> shift; +} + +static void bmptree_init_maxindex(struct bmptree *bt) +{ + int i; + + for(i=0; iheight_to_maxidx); i++) + bt->height_to_maxidx[i] = __calc_maxidx(i); +} + +static struct bmptree_node *bmptree_node_alloc(struct bmptree *bt) +{ + struct bmptree_node *node; + + bt->used_ncount++; + + /*first, get node in free list.*/ + if(bt->free_ncount > 0) { + struct list_head *free; + + PDEBUG("free node list count:%d.\n", bt->free_ncount); + free = bt->free_nlist.next; + bt->free_ncount--; + list_del(free); + node = list_entry(free, struct bmptree_node, n_head); + + return node; + } + + node = kmem_cache_alloc(bt->bt_ncache, bt->gfp_mask); + if(node == NULL) { + return NULL; + } + return node; +} + + +static struct bmptree_leaf *bmptree_leaf_alloc(struct bmptree *bt) +{ + struct bmptree_leaf *leaf; + + bt->used_lcount++; + + /*first, get leaf in free list.*/ + if(bt->free_lcount > 0) { + struct list_head *free; + + PDEBUG("free leaf list count:%d.\n", bt->free_lcount); + bt->free_lcount--; + free = bt->free_llist.next; + list_del(free); + leaf = list_entry(free, struct bmptree_leaf, head); + + return leaf; + } + + leaf = kmem_cache_alloc(bt->bt_lcache, bt->gfp_mask); + + if(leaf == NULL) { + return NULL; + } + return leaf; +} + + +static void bmptree_free_node(struct bmptree *bt, struct bmptree_node *node) +{ + bt->used_ncount--; + + PDEBUG("free node. curr used count:%d.\n", bt->used_ncount); + list_add_tail(&node->n_head, &bt->free_nlist); + bt->free_ncount++; + + if(bt->free_ncount > bt->recliam_high) { + struct bmptree_node *free_node; + struct list_head *free; + + PDEBUG("free node, free_ncount:[%d] > recliam_high:[%d].\n", + bt->free_ncount, bt->recliam_high); + + while(bt->free_ncount > bt->recliam_low) { + bt->free_ncount--; + free = bt->free_nlist.next; + list_del(free); + free_node = list_entry(free, struct bmptree_node, n_head); + + kmem_cache_free(bt->bt_ncache, free_node); + } + } +} + + +static void bmptree_free_leaf(struct bmptree *bt, struct bmptree_leaf *leaf) +{ + bt->used_lcount--; + PDEBUG("free leaf. curr used count:%d.\n", bt->used_lcount); + + list_add_tail(&leaf->head, &bt->free_llist); + bt->free_lcount++; + + if(bt->free_lcount > bt->recliam_high) { + struct bmptree_leaf *free_leaf; + struct list_head *free; + + PDEBUG("free leaf, free_lcount[%d] > recliam_high[%d].\n", + bt->free_lcount, bt->recliam_high); + + while(bt->free_lcount > bt->recliam_low) { + bt->free_lcount--; + free = bt->free_llist.next; + + list_del(free); + free_leaf = list_entry(free, struct bmptree_leaf, head); + + kmem_cache_free(bt->bt_lcache, free_leaf); + } + } +} + + +static int bmptree_free_subtree(struct bmptree *bt, struct bmptree_node *node) +{ + struct bmptree_node *slot = NULL; + int offset = 0; + + if(!node) { + return 0; + } + + PDEBUG("free subtree. height:%d, count:%d.\n", + node->n_height, node->n_count); + + while(node->n_count > 0) { + BUG_ON(offset >= BMPTREE_NODE_MAP_SIZE); + slot = node->slots[offset]; + if(!slot) { + offset++; + continue; + } + + if(slot->n_height > 0) + bmptree_free_subtree(bt, slot); + else + bmptree_free_leaf(bt, (struct bmptree_leaf *)slot); + + node->slots[offset] = NULL; + node->n_count--; + offset++; + } + + bmptree_free_node(bt, node); + return 0; +} + + +static int bmptree_free_tree(struct bmptree *bt) +{ + return bmptree_free_subtree(bt, bt->rnode); +} + +static int bmptree_node_insert(struct bmptree *bt, + struct bmptree_leaf *new, struct bmptree_node *parent, int index) +{ + PDEBUG("insert node to slot[%d].\n", index); + + if(unlikely(parent == NULL)) { + if(bt->height < new->height) { + struct bmptree_node *node = bt->rnode; + + PDEBUG("insert the root node, height=%d.\n", new->height); + parent = bt->rnode = (struct bmptree_node *)new; + new->parent = NULL; + bt->height++; + + /*when this node is first node, return.*/ + if(node == NULL) + return 0; + + new = (struct bmptree_leaf *)node; + index = 0; + } else { + printk("Fat: node insert to subtree, but parent is NULL.\n"); + return -EINVAL; + } + } + + /*when only a part of bit assigned. not set the bit */ + /*set_bit(index, &parent->n_tags);*/ + parent->n_count++; + parent->slots[index] = new; + new->parent = parent; + return 0; +} + + +static int bmptree_init_leaf(struct bmptree_leaf *leaf, int height, int is_full) +{ + unsigned int i; + + leaf->parent = NULL; + leaf->height = height; + if(is_full) { + for(i=0; itags[i] = ~0UL; + } else { + for(i=0; itags[i] = 0; + } + + return 0; +} + +static int bmptree_init_node(struct bmptree *bt, + struct bmptree_node *node, unsigned int height, int is_full) +{ + memset(node->slots, 0, BMPTREE_NODE_MAP_SIZE*sizeof(void *)); + + return bmptree_init_leaf((struct bmptree_leaf *)node, height, is_full); +} + +#define RECLIAM_REASON_NODEEMPTY (0) +#define RECLIAM_REASON_NODEFULL (1) + +static int bmptree_recliam_node(struct bmptree *bt, + struct bmptree_node *node, int index, int reason) +{ + struct bmptree_node *parent; + int shift; + int pos; + + /*pos: leaf position in parent.*/ + shift = (node->n_height + 1) * BMPTREE_NODE_MAP_SHIFT; + pos = (index >> shift) & BMPTREE_NODE_MAP_MASK; + + BUG_ON(node->n_count != 0); + PDEBUG("try to recliam node,node pos in parent: %d, height:%d.\n", + pos, node->n_height); + parent = node->n_parent; + if(!parent) + return 0; + + /* + * if all bit has been set in this node, set parent bit, and + * free current node. + * */ + if(reason == RECLIAM_REASON_NODEFULL) { + PDEBUG("recliam node,position in parent:%d, all assign.\n", pos); + set_bit(pos, parent->n_tags); + parent->slots[pos] = NULL; + } else if(reason == RECLIAM_REASON_NODEEMPTY) { + PDEBUG("recliam node,position in parent:%d, all empty.\n", pos); + /*not necessary*/ + clear_bit(pos, parent->n_tags); + parent->slots[pos] = NULL; + } else { + //printk("node bmp counter error(%d).\n", node->n_count); + BUG(); + return 0; + } + + bmptree_free_node(bt, node); + + if(--parent->n_count == 0) { + int bit; + bit = (reason == RECLIAM_REASON_NODEEMPTY) ? + find_first_bit(parent->n_tags, BMPTREE_NODE_MAP_SIZE): + find_first_zero_bit(parent->n_tags, BMPTREE_NODE_MAP_SIZE); + + if(bit >= BMPTREE_NODE_MAP_SIZE) + return bmptree_recliam_node(bt, parent, index, reason); + } + return 0; +} + + +static int bmptree_recliam_leaf(struct bmptree *bt, + struct bmptree_leaf *leaf, int index, int reason) +{ + struct bmptree_node *parent; + int shift; + int pos; + + /*pos: leaf position in parent.*/ + shift = (leaf->height + 1) * BMPTREE_NODE_MAP_SHIFT; + pos = (index >> shift) & BMPTREE_NODE_MAP_MASK; + + PDEBUG("try to recliam leaf,leaf position in parent: %d.\n", pos); + parent = leaf->parent; + /* + * if all bit has been set in this node, set parent bit, and + * free current node. + * */ + if(reason == RECLIAM_REASON_NODEFULL) { + PDEBUG("recliam leaf,position in parent: %d, all assign.\n", pos); + set_bit(pos, parent->n_tags); + parent->slots[pos] = NULL; + } else if(reason == RECLIAM_REASON_NODEEMPTY) { + PDEBUG("recliam leaf,position in parent: %d, all empty.\n", pos); + clear_bit(pos, parent->n_tags); + parent->slots[pos] = NULL; + } else { + BUG(); + return 0; + } + + bmptree_free_leaf(bt, leaf); + + if(--parent->n_count == 0 ) { + int bit; + bit = (reason == RECLIAM_REASON_NODEEMPTY) ? + find_first_bit(parent->n_tags, BMPTREE_NODE_MAP_SIZE): + find_first_zero_bit(parent->n_tags, BMPTREE_NODE_MAP_SIZE); + + if(bit >= BMPTREE_NODE_MAP_SIZE) + return bmptree_recliam_node(bt, parent, index, reason); + } + return 0; +} + + +int bmptree_check(struct bmptree *bt, unsigned long index) +{ + struct bmptree_node *node, *slot = NULL; + struct bmptree_leaf *leaf; + unsigned int height, shift; + int offset; + + height = bt->height; + node = bt->rnode; + shift = height * BMPTREE_NODE_MAP_SHIFT; + offset = (index >> shift) & BMPTREE_NODE_MAP_MASK; + + while(height > 1) { + offset = (index >> shift) & BMPTREE_NODE_MAP_MASK; + slot = node->slots[offset]; + height--; + + if(test_bit(offset, node->n_tags)) { + return 1; + } + + if(!slot){ + return 0; + } + shift -= BMPTREE_NODE_MAP_SHIFT; + node = slot; + } + + /*leaf operation*/ + leaf = node->slots[index & BMPTREE_NODE_MAP_MASK]; + if(!leaf) { + return 0; + } + + return test_bit(index & BMPTREE_NODE_MAP_MASK, leaf->tags); +} + +static int bmptree_find_free_in_leaf(struct bmptree *bt, + struct bmptree_leaf *leaf, unsigned long start) +{ + int offset; + + /*leaf operation*/ + offset = find_next_zero_bit(leaf->tags, BMPTREE_NODE_MAP_SIZE, + (start & BMPTREE_NODE_MAP_MASK)); + + PDEBUG("%s:offset:%d, start:%d, tags:%08lx.\n", + __func__, offset, start, leaf->tags[0]); + + if(offset >= BMPTREE_NODE_MAP_SIZE) { + return -EAGAIN; + } + + return (start& ~(BMPTREE_NODE_MAP_MASK)) | offset; +} + + +static int bmptree_find_free_in_node(struct bmptree *bt, + struct bmptree_node *node, unsigned long start) +{ + struct bmptree_node *slot; + unsigned int height, shift, slot_mask; + int ofs, new_ofs; + int ret = -EAGAIN; + + height = node->n_height; + shift = height * BMPTREE_NODE_MAP_SHIFT; + ofs = (start >> shift) & BMPTREE_NODE_MAP_MASK; + slot_mask = ~((1 << (shift + BMPTREE_NODE_MAP_SHIFT)) - 1); + + PDEBUG("%s:tree height:%d, shift:%d, offset:%d, start:%d.\n", + __func__, height, shift, ofs, start); + + do { + new_ofs = find_next_zero_bit(node->n_tags, BMPTREE_NODE_MAP_SIZE, ofs); + if(ofs != new_ofs) { + /*re calc the start pos.*/ + ofs = new_ofs; + start = (start & slot_mask) | (ofs << shift); + } + if(ofs >= BMPTREE_NODE_MAP_SIZE) { + return -EAGAIN; + } + PDEBUG("height:%d, slot[%d], tags:%08lx.\n", + height, ofs, node->n_tags[0]); + slot = node->slots[ofs]; + + if(!slot){ + /*all sub node is empty.*/ + ret = start; + goto found; + } + + if(slot->n_height > 0) + ret = bmptree_find_free_in_node(bt, slot, start); + else + ret = bmptree_find_free_in_leaf(bt, (struct bmptree_leaf *)slot, start); + + if(ret >= 0) + goto found; + + start = (start & slot_mask) | ((++ofs) << shift); + }while(ofs < BMPTREE_NODE_MAP_SIZE); + + return -EAGAIN; +found: + return ret; +} + +static inline void bmptree_set_cache(struct bmptree *bt, + struct bmptree_leaf *leaf, unsigned long index) +{ + struct bmptree_cache *cache = &bt->cache; + + cache->leaf = leaf; + cache->path_idx = index >> BMPTREE_NODE_MAP_SHIFT; +} + +static inline void bmptree_init_cache(struct bmptree *bt) +{ + bmptree_set_cache(bt, NULL, 0); +} + +static inline struct bmptree_leaf *bmptree_find_leaf_cache(struct bmptree *bt, + unsigned long index) +{ + struct bmptree_cache *cache = &bt->cache; + + if((cache->leaf == NULL) || + (cache->path_idx != index >> BMPTREE_NODE_MAP_SHIFT)) + return NULL; + + PDEBUG("leaf has found in cache. path index:%08lx, index:%08lx.\n", + cache->path_idx, index); + + return cache->leaf; +} + + +static int bmptree_find_free_in_cache(struct bmptree *bt, int start) +{ + int ret; + struct bmptree_leaf *leaf; + struct bmptree_cache *cache = &bt->cache; + + leaf = bmptree_find_leaf_cache(bt, start); + if(!leaf) + return -EAGAIN; + + ret = find_next_zero_bit(leaf->tags, BMPTREE_NODE_MAP_SIZE, start & BMPTREE_NODE_MAP_MASK); + + if(ret >= BMPTREE_NODE_MAP_SIZE) + return -EAGAIN; + + return (cache->path_idx << BMPTREE_NODE_MAP_SHIFT) | ret; +} + +static int bmptree_extend(struct bmptree *bt, unsigned long index) +{ + struct bmptree_node *node; + unsigned int height; + + height = bt->height; + node = bt->rnode; + + while(index > bmptree_maxindex(bt, height)) { + PDEBUG("grow up the bmptree.\n"); + + node = bmptree_node_alloc(bt); + bmptree_init_node(bt, node, ++height, 0); + bmptree_node_insert(bt, (struct bmptree_leaf *)node, NULL, 0); + bt->rnode = node; + } + + return 0; +} + +int bmptree_set(struct bmptree *bt, unsigned long index) +{ + int err = 0; + struct bmptree_node *node, *slot; + struct bmptree_leaf *leaf; + unsigned int height, shift; + int offset; + + leaf = bmptree_find_leaf_cache(bt, index); + if(leaf != NULL) + goto found; + + height = bt->height; + node = bt->rnode; + shift = height * BMPTREE_NODE_MAP_SHIFT; + offset = (index >> shift) & BMPTREE_NODE_MAP_MASK; + + PDEBUG("%s:tree height:%d, shift:%d, offset:%d, index:%lu.\n", + __func__, height, shift, offset, index); + + if(unlikely(index > bmptree_maxindex(bt, height))) { + err = bmptree_extend(bt, index); + if(err) + return err; + } + + /*leaf node: height equals 0*/ + while(height > 1) { + slot = node->slots[offset]; + height--; + + if(!slot) { + //BUG_ON(test_bit(offset, &node->n_tags)); + if(test_bit(offset, node->n_tags)) { + PDEBUG("%s:set bit exist\n", __func__); + return -EEXIST; + } + + if(!(slot = bmptree_node_alloc(bt))) { + return -ENOMEM; + } + + bmptree_init_node(bt, slot, height, 0); + bmptree_node_insert(bt, (struct bmptree_leaf *)slot, node, offset); + } + shift -= BMPTREE_NODE_MAP_SHIFT; + offset = (index >> shift) & BMPTREE_NODE_MAP_MASK; + node = slot; + } + + BUG_ON(!node); + /*leaf operation*/ + leaf = node->slots[offset]; + if(!leaf) { + //BUG_ON(test_bit(offset, &node->n_tags)); + if(test_bit(offset, node->n_tags)) { + PDEBUG("%s:set bit exist.\n", __func__); + return -EEXIST; + } + + if(!(leaf = bmptree_leaf_alloc(bt))) { + return -ENOMEM; + } + + bmptree_init_leaf(leaf, 0, 0); + bmptree_node_insert(bt, leaf, node, offset); + } + + bmptree_set_cache(bt, leaf, index); +found: + if(test_and_set_bit(index & BMPTREE_NODE_MAP_MASK, leaf->tags)) { + PDEBUG("%s:set bit exist..\n", __func__); + return -EEXIST; + } + + if(find_first_zero_bit(leaf->tags, BMPTREE_NODE_MAP_SIZE) >= BMPTREE_NODE_MAP_SIZE) + bmptree_recliam_leaf(bt, leaf, index, RECLIAM_REASON_NODEFULL); + + return 0; +} + +int bmptree_clear(struct bmptree *bt, unsigned long index) +{ + struct bmptree_node *node, *slot; + struct bmptree_leaf *leaf; + unsigned int height, shift; + int offset; + + leaf = bmptree_find_leaf_cache(bt, index); + if(leaf != NULL) + goto found; + + height = bt->height; + node = bt->rnode; + shift = height * BMPTREE_NODE_MAP_SHIFT; + offset = (index >> shift) & BMPTREE_NODE_MAP_MASK; + + PDEBUG("%s:tree height:%d, shift:%d, offset:%d, index:%lu.\n", + __func__, height, shift, offset, index); + + while(height > 1) { + slot = node->slots[offset]; + height--; + + if(!slot && test_bit(offset, node->n_tags)) { + if(!(slot = bmptree_node_alloc(bt))) { + return -ENOMEM; + } + + bmptree_init_node(bt, slot, height, 1); + bmptree_node_insert(bt, (struct bmptree_leaf *)slot, node, offset); + clear_bit(offset, node->n_tags); + } + shift -= BMPTREE_NODE_MAP_SHIFT; + offset = (index >> shift) & BMPTREE_NODE_MAP_MASK; + node = slot; + } + + BUG_ON(!node); + + /*leaf operation*/ + leaf = node->slots[offset]; + if(!leaf) { + if(!(leaf = bmptree_leaf_alloc(bt))) { + return -ENOMEM; + } + + bmptree_init_leaf(leaf, 0, 1); + bmptree_node_insert(bt, leaf, node, offset); + clear_bit(offset, node->n_tags); + } + bmptree_set_cache(bt, leaf, index); + +found: + clear_bit(index & BMPTREE_NODE_MAP_MASK, leaf->tags); + + if(find_first_bit(leaf->tags, BMPTREE_NODE_MAP_SIZE) >= BMPTREE_NODE_MAP_SIZE) + bmptree_recliam_leaf(bt, leaf, index, RECLIAM_REASON_NODEEMPTY); + + return 0; +} + + +static inline int verify_vaild_pos(int pos, unsigned long min, unsigned long max) +{ + return (pos >=min) && (pos <= max); +} + +int bmptree_find_next_free_pos(struct bmptree *bt, unsigned long start, + unsigned long min, unsigned long max) +{ + int ret; + struct bmptree_node *root; + + PDEBUG("find next free pos, max index:%lu, start pos:%d.\n", + max, start); + + if(unlikely(start > max)) { + return -EINVAL; + } + + /*frist:search the cache node.*/ + ret = bmptree_find_free_in_cache(bt, start); + if(verify_vaild_pos(ret, min, max)) + return ret; + + /*not hit in cache node , search in tree.*/ + root = bt->rnode; + ret = bmptree_find_free_in_node(bt, root, start); + if(unlikely(!verify_vaild_pos(ret, min, max))) { + + /*if the tree max index less than max index, next index is + * 1 << ((root->height + 1) * BMPTREE_NODE_MAP_SHIFT). + * */ + ret = 1 << ((root->n_height + 1) * BMPTREE_NODE_MAP_SHIFT); + if(ret <= max) { + PDEBUG("free bit in parent of curr root node, pos:%d\n", ret); + return ret; + } + /*research from start pos.*/ + ret = bmptree_find_free_in_node(bt, root, min); + } + + return verify_vaild_pos(ret, min, max) ? ret : -EAGAIN; +} + +static void bmptree_node_ctor(void *node) +{ + memset(node, 0, sizeof(struct bmptree_node)); +} + +static void bmptree_leaf_ctor(void *leaf) +{ + return; +} + +void *get_bmptree_priv(struct bmptree *bt) +{ + return bt->priv_data; +} + +/* + * fat bitmap tree initialize. + * */ +struct bmptree *bmptree_init(char *name, void *priv) +{ + struct bmptree *bt; + struct bmptree_node *node; + char cache_name[BMPTREE_NAME_LEN + 8] = {0}; + + PDEBUG("fat bitmap tree initilize.\n"); + + bt = kmalloc(sizeof(struct bmptree), GFP_KERNEL); + + memset(bt, 0, sizeof(struct bmptree)); + + strlcpy(bt->name, name, sizeof(bt->name)); + + snprintf(cache_name, sizeof(cache_name), "%s_ncache", bt->name); + bt->bt_ncache = kmem_cache_create(cache_name, + sizeof(struct bmptree_node), 0, + SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, bmptree_node_ctor); + if(!bt->bt_ncache) + return ERR_PTR(-ENOMEM); + + snprintf(cache_name, sizeof(cache_name), "%s_lcache", bt->name); + bt->bt_lcache = kmem_cache_create(cache_name, + sizeof(struct bmptree_leaf), 0, + SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, bmptree_leaf_ctor); + if(!bt->bt_lcache) + return ERR_PTR(-ENOMEM); + + bt->height = 0; + bt->gfp_mask = GFP_NOFS; + bt->rnode = NULL; + bt->free_ncount = bt->free_lcount = 0; + bt->recliam_low = BMPTREE_FREE_NODE_MIN_LIMIT; + bt->recliam_high = BMPTREE_FREE_NODE_MAX_LIMIT; + INIT_LIST_HEAD(&bt->free_nlist); + INIT_LIST_HEAD(&bt->free_llist); + + bt->used_ncount = bt->used_lcount = 0; + + bt->priv_data = priv; + bmptree_init_cache(bt); + bmptree_init_maxindex(bt); + + /*alloc a root node.*/ + if(!(node = bmptree_node_alloc(bt))) { + return ERR_PTR(-ENOMEM); + } + + bmptree_init_node(bt, node, 1, 0); + bmptree_node_insert(bt, (struct bmptree_leaf *)node, NULL, 0); + + PDEBUG("bitmap tree initilize success.\n"); + return bt; +} + +void bmptree_release(struct bmptree *bt) +{ + struct bmptree_node *n, *ntmp; + struct bmptree_leaf *l, *ltmp; + + PDEBUG("fat bitmap tree release.\n"); + if(!bt) { + return; + } + + bmptree_free_tree(bt); + + list_for_each_entry_safe(n, ntmp, &bt->free_nlist, n_head) { + list_del(&n->n_head); + kmem_cache_free(bt->bt_ncache, n); + } + + list_for_each_entry_safe(l, ltmp, &bt->free_llist, head) { + list_del(&l->head); + kmem_cache_free(bt->bt_lcache, l); + } + + kmem_cache_destroy(bt->bt_ncache); + kmem_cache_destroy(bt->bt_lcache); + kfree(bt); +} + + +static void bmptree_dump_leaf(struct bmptree_leaf *leaf) +{ + int i; + //printk("tree height:%d, count:%d.\n", node->height, node->n_count); + + for(i=0; itags)) ? "X":"O"); + } + + printk("\n"); +} + + +static void bmptree_dump_node(struct bmptree_node *node) +{ + int i; + struct bmptree_node *slot; + + printk("tree height:%d, count:%d.\n", node->n_height, node->n_count); + for(i=0; islots[i]; + if(slot != NULL) { + printk(" %s ", (test_bit(i, node->n_tags)) ? "Err":"P"); + }else + printk(" %s ", (test_bit(i, node->n_tags)) ? "X":"O"); + } + + printk("\n"); + + for(i=0; islots[i]; + if(slot != NULL) { + + if(slot->n_height > 0) { + printk("node slot[%d]:\n", i); + bmptree_dump_node(slot); + } else { + printk("leaf slot[%d]:\n", i); + bmptree_dump_leaf((struct bmptree_leaf *)slot); + } + } + } +} + +void bmptree_dump_tree(struct bmptree *bt) +{ +#if 0 + printk("note: \nErr: bitmap error. \n" + "P: a part of node has been assign(have subtree.).\n" + "O: all node is free. \n" + "X:all node has been assign.\n"); +#endif + printk("-----------------------tree----------------------------\n"); + if(bt->rnode == NULL) { + printk("[empty tree]\n"); + } else + bmptree_dump_node(bt->rnode); + + printk("-------------------------------------------------------\n"); + return; +} + +void show_tree_amount(struct bmptree *bt) +{ + /*use for debug.*/ + printk("bmptree(%s) infomation:\n", bt->name); + + printk("tree height:%d, maxindex:%lu.\n", + bt->height, bt->height_to_maxidx[bt->height]); + + printk("total used node count:%d(totals %dBytes), leaf count:%d(totals %dBytes).\n", + bt->used_ncount, sizeof(struct bmptree_node)*bt->used_ncount, + bt->used_lcount, sizeof(struct bmptree_leaf)*bt->used_lcount); +} + + +int bmptree_testmyself(void) +{ +#define CHK_RET(r) \ + do { \ + if(r < 0) { \ + printk("ERR:%d.\n", __LINE__); \ + return r; \ + } \ + }while(0) + + int ret = 0; + int i; + unsigned long t = 0, c = 0; + struct bmptree *bt = NULL; + + printk("bmptree start test myself.\n"); + bt = bmptree_init("bmptree_test", NULL); + CHK_RET(ret); + + bmptree_dump_tree(bt); + + printk("start test set bit.\n"); + for(i=0; i<1000; i++) { + //printk("set pos:%d.\n", i*2); + ret = bmptree_set(bt, i*2); + CHK_RET(ret); + // bmptree_dump_tree(bt); + } + show_tree_amount(bt); + + for(i=200; i<600; i++) { + //printk("set pos:%d.\n", i*2+1); + ret = bmptree_set(bt, i*2+1); + CHK_RET(ret); + // bmptree_dump_tree(bt); + } + show_tree_amount(bt); + bmptree_dump_tree(bt); + +//#define TEST_FIND_FREE_CLUSTER + +#ifdef TEST_FIND_FREE_CLUSTER + printk("start test find free bit.\n"); + for(i=0; i<2000; i++) { + ret = bmptree_find_next_free_pos(bt, i, 0, 3000); + printk("find bit:first pos:%d, free bit:%d.\n", i, ret); + } + + for(i=2000; i<2500; i++) { + ret = bmptree_find_next_free_pos(bt, i, 1, 2500); + printk("find bit:first pos:%d, free bit:%d.\n", i, ret); + } + + for(i=300; i<800; i++) { + ret = bmptree_find_next_free_pos(bt, i, 2, 1000); + printk("find bit:first pos:%d, free bit:%d.\n", i, ret); + } + + for(i=32760; i<32780; i++) { + ret = bmptree_find_next_free_pos(bt, i, 2, 40000); + printk("find bit:first pos:%d, free bit:%d.\n", i, ret); + } +#endif + + +#define TEST_CLEAR_CLUSTER + +#ifdef TEST_CLEAR_CLUSTER + printk("start test clear bit.\n"); + bmptree_dump_tree(bt); + for(i=0; i<1000; i++) { + //printk("clear pos:%d.\n", i*2); + bmptree_clear(bt, i*2); + CHK_RET(ret); + // bmptree_dump_tree(bt); + } + + bmptree_dump_tree(bt); + show_tree_amount(bt); + + for(i=200; i<600; i++) { + //printk("clear pos:%d.\n", i*2+1); + ret = bmptree_clear(bt, i*2+1); + CHK_RET(ret); + // bmptree_dump_tree(bt); + } + + show_tree_amount(bt); +#endif + +#define TEST_FIND_TIMES +#ifdef TEST_FIND_TIMES + printk("start insert data.\n"); + t = jiffies; + for(i=0; i<16*1024*1024; i++) { + ret = bmptree_set(bt, i*2); + CHK_RET(ret); + } + t = ((jiffies - t)*1000)/HZ; + c = (t*1000) /(16*1024*1024/10000); + printk("insert 16M counts data use %lums. avg:%lu.%lu ms/10000cnt\n", + t, c/1000, (c%1000)/10); + + show_tree_amount(bt); + + printk("start find data.\n"); + t = jiffies; + for(i=0; i<1024*1024; i++) { + ret = bmptree_find_next_free_pos(bt, 17*i, 0, 16*1024*1024); + } + + t = ((jiffies - t)*1000)/HZ; + c = (t*1000) /(1024*1024/10000); + printk("find 1M counts use %lums. avg:%lu.%lu ms/10000cnt.\n", + t, c/1000, (c%1000)/10); + + + printk("start clear data.\n"); + t = jiffies; + for(i=0; i<16*1024*1024; i++) { + ret = bmptree_clear(bt, i*2); + CHK_RET(ret); + } + + t = ((jiffies - t)*1000)/HZ; + c = (t*1000) /(16*1024*1024/10000); + printk("delete 16M counts use %lums. avg:%lu.%lu ms/10000cnt.\n", + t, c/1000, (c%1000)/10); + + show_tree_amount(bt); + +#endif + + + printk("test release bmptree.\n"); + bmptree_dump_tree(bt); + bmptree_release(bt); + + printk("test bmptree finish.\n"); + return ret; +} + + diff --git a/fs/fat/bmp_tree.h b/fs/fat/bmp_tree.h new file mode 100644 index 00000000..55384bbb --- /dev/null +++ b/fs/fat/bmp_tree.h @@ -0,0 +1,99 @@ +#ifndef __BMP_TREE_H__ +#define __BMP_TREE_H__ + + +#define BMPTREE_TREE_INDEX_BITS (8 /*char bit*/ * sizeof(unsigned long)) + +#define BMPTREE_NODE_MAP_SHIFT (5) +#define BMPTREE_NODE_MAP_SIZE (1UL << BMPTREE_NODE_MAP_SHIFT) +#define BMPTREE_NODE_MAP_MASK (BMPTREE_NODE_MAP_SIZE - 1) + +#define BMPTREE_MAX_PATH \ + (DIV_ROUND_UP(BMPTREE_TREE_INDEX_BITS, BMPTREE_NODE_MAP_SHIFT)) + +#define BMPTREE_NODE_TAG_LONGS \ + ((BMPTREE_NODE_MAP_SIZE + BITS_PER_LONG -1)/BITS_PER_LONG) + +#define BMPTREE_FREE_NODE_MAX_LIMIT (64) +#define BMPTREE_FREE_NODE_MIN_LIMIT (8) + +struct bmptree_leaf +{ + unsigned long tags[BMPTREE_NODE_TAG_LONGS]; /*curr only one tags*/ + uint8_t height; + uint8_t reserved; /*align*/ + union { + struct bmptree_node *parent; + struct list_head head; + }; +}; + + +struct bmptree_node +{ + struct bmptree_leaf base; + +#define n_tags base.tags +#define n_height base.height +#define n_parent base.parent +#define n_head base.head + + uint8_t n_count; + void *slots[BMPTREE_NODE_MAP_SIZE]; +}; + +struct bmptree_cache +{ + struct bmptree_leaf *leaf; + unsigned long path_idx; + +}; + +#define BMPTREE_NAME_LEN (32) +struct bmptree +{ + char name[BMPTREE_NAME_LEN]; + unsigned int height; + gfp_t gfp_mask; + struct bmptree_node *rnode; + struct bmptree_cache cache; /*use for fast set/clear*/ + + unsigned long height_to_maxidx[BMPTREE_MAX_PATH + 1]; + + struct kmem_cache *bt_ncache; /*bmptree_node cache*/ + struct kmem_cache *bt_lcache; /*bmptree_leaf cache*/ + + struct list_head free_nlist; /*free node list*/ + struct list_head free_llist; /*free leaf list*/ + int free_ncount; /*free node count*/ + int free_lcount; /*free leaf count*/ + + int recliam_low; /**/ + int recliam_high; + + int used_ncount; /*total used node count, used for debug*/ + int used_lcount; /*total used leaf count, used for debug*/ + void *priv_data; +}; + +void *get_bmptree_priv(struct bmptree *bt); + +int bmptree_check(struct bmptree *bt, unsigned long index); +int bmptree_set(struct bmptree *bt, unsigned long index); +int bmptree_clear(struct bmptree *bt, unsigned long index); + +int bmptree_find_next_free_pos(struct bmptree *bt, unsigned long start, + unsigned long min, unsigned long max); + +/* + * fat bitmap tree initialize. + * */ +struct bmptree *bmptree_init(char *name, void *priv); + + +void bmptree_release(struct bmptree *bt); + +void bmptree_dump_tree(struct bmptree *bt); +int bmptree_testmyself(void); + +#endif diff --git a/fs/fat/chkdsk.c b/fs/fat/chkdsk.c new file mode 100644 index 00000000..71cfdfe3 --- /dev/null +++ b/fs/fat/chkdsk.c @@ -0,0 +1,562 @@ +/* + * @(#)chkdsk.c + * @date 2010/07/10 + * @version 1.0 + * @author AiJun. + * @Leader:xuchuang + * @modify: lixinhai + * Copyright 2015 Anyka corporation, Inc. All rights reserved. + * ANYKA PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + */ + +#include +#include +#include +#include "fat_fsck.h" + +//#define FAT_CHKDSK_DBG +#ifdef FAT_CHKDSK_DBG +#define PDEBUG(fmt, args...) printk(KERN_INFO "chkdsk:" fmt, ##args) +#else +#define PDEBUG(fmt, args...) +#endif + +#ifdef FAT_CHKDSK_DBG +void dump_data(const unsigned long *addr, unsigned long size) +{ + const unsigned long *p = addr; + int i = 0; + + while (size--) { + printk("%08lx ", *(p++)); + if((++i)%8 == 0) + printk("\n"); + } + printk("\n"); +} +#else +void dump_data(const unsigned long *addr, unsigned long size) +{ +} +#endif + + + +#define MAX_CHKDSK_RAM_NUMBER 10 // chkdsk use the max big buffer number + +/************************************************************************ + * NAME: FAT_GetFatLinkInfo + * FUNCTION it will get the fat link value in fat table with offset and fstype + * PARAM: T_U8 * pFatBuf + T_U16 offset + T_U8 FSType + * RETURN: NULL +**************************************************************************/ +T_U32 FAT_GetFatLinkInfo_chkdsk(struct msdos_sb_info *sbi, T_U8 * pFatBuf, T_U16 offset) +{ + T_U32 ret; + + if(sbi->fat_bits == 16) + { + ret = (((T_U16 *) pFatBuf)[offset]); + } + else + { + ret = (((T_U32 *) pFatBuf)[offset]); + } + return ret; +} + + +T_U32 FAT_GetFatLinkInfo(struct msdos_sb_info *sbi, T_U8 * pFatBuf, T_U16 offset) +{ + T_U32 ret; + + if(sbi->fat_bits == 16) + { + ret = (((T_U16 *) pFatBuf)[offset]); + if(ret >= FAT16_MAX_USER_CLUSTER) + ret = FAT32_MAX_USER_CLUSTER; + } + else + { + ret = (((T_U32 *) pFatBuf)[offset]); + } + return ret; +} + + +T_VOID FAT_SetFatLinkInfo(struct msdos_sb_info *sbi, T_U8 * pFatBuf, T_U16 offset, T_U32 newValue) +{ + if(sbi->fat_bits == 16) + { + (((T_U16 *) pFatBuf)[offset]) = (T_U16)newValue; + } + else + { + (((T_U32 *) pFatBuf)[offset]) = newValue; + } +} + +/************************************************************************ + * NAME: FAT_MallocChkDskBuf + * FUNCTION it will malloc some chkdsk buffer, and add it to the link + * PARAM: PSECTOR_MAP_ARRAY_INFO pSectorMapBuf, + T_U16 SectorSize + * RETURN: the new buffer ptr +**************************************************************************/ + +PSECTOR_MAP_ARRAY_INFO FAT_MallocChkDskBuf(PSECTOR_MAP_ARRAY_INFO pSectorMapBuf, T_U16 SectorSize) +{ + PSECTOR_MAP_ARRAY_INFO next; + T_U16 i, BufSize, BytesPerClusMap; + + BytesPerClusMap = (SectorSize >> 3); + BufSize = sizeof(SECTOR_MAP_ARRAY_INFO) + CLUSTER_MAP_NUMBER * (sizeof(CLUSTER_MAP_INFO) + BytesPerClusMap); + next = kmalloc(BufSize, GFP_KERNEL); + if(next == AK_NULL) + return AK_NULL; + + next->pMapArray = (PCLUSTER_MAP_INFO)&next[1]; + next->next = AK_NULL; + next->pCurSecMap = AK_NULL; + for(i = 0; i < CLUSTER_MAP_NUMBER; i++) + { + next->pMapArray[i].sectorAddr = T_U32_MAX; + next->pMapArray[i].bitNum = 0; + next->pMapArray[i].bitMap = (T_U8 *)(&next->pMapArray[CLUSTER_MAP_NUMBER]) + i * BytesPerClusMap; + memset(next->pMapArray[i].bitMap, 0, BytesPerClusMap); + } + if(pSectorMapBuf == AK_NULL) + return next; + while(pSectorMapBuf->next) + { + pSectorMapBuf = pSectorMapBuf->next; + } + pSectorMapBuf->next = next; + return next; +} + + +/************************************************************************ + * NAME: FAT_CheckChkDskRamNumber + * FUNCTION it will check whether system can malloc more ram + * PARAM: PSECTOR_MAP_ARRAY_INFO pSectorMapBuf, + * RETURN: return ak_true if system can +**************************************************************************/ + +T_BOOL FAT_CheckChkDskRamNumber(PSECTOR_MAP_ARRAY_INFO pSectorMapBuf) +{ + PSECTOR_MAP_ARRAY_INFO cur = pSectorMapBuf; + T_U32 RamNum = 0; + + while(cur->next) + { + RamNum ++; + cur = cur->next; + } + return RamNum < MAX_CHKDSK_RAM_NUMBER; +} + +/************************************************************************ + * NAME: FAT_FIndChkDskClusMap + * FUNCTION it will search a empty cluster map ram + * PARAM: T_U8 *SectorBitMap + PSECTOR_MAP_ARRAY_INFO pSectorMapBuf + PSECTOR_MAP_ARRAY_INFO *pFindPos + * RETURN: return the find item +**************************************************************************/ + +PCLUSTER_MAP_INFO FAT_FIndChkDskClusMap(T_U8 *SectorBitMap, PSECTOR_MAP_ARRAY_INFO pSectorMapBuf, PSECTOR_MAP_ARRAY_INFO *pFindPos) +{ + PCLUSTER_MAP_INFO pMaxClusMap = AK_NULL; + PSECTOR_MAP_ARRAY_INFO pCurMap = pSectorMapBuf; + T_U32 i, MaxBitNum = 0; + + while(pCurMap != AK_NULL) + { + for(i = 0; i < CLUSTER_MAP_NUMBER; i++) + { + if(pCurMap->pMapArray[i].sectorAddr == T_U32_MAX) + { + *pFindPos = pCurMap; + return pCurMap->pMapArray + i; + } + + if(pCurMap->pMapArray[i].bitNum > MaxBitNum) + { + *pFindPos = pCurMap; + pMaxClusMap = pCurMap->pMapArray + i; + MaxBitNum = pCurMap->pMapArray[i].bitNum; + } + } + pCurMap = pCurMap->next; + } + SetBitMap(SectorBitMap, pMaxClusMap->sectorAddr); + return pMaxClusMap; +} + +/************************************************************************ + * NAME: FAT_AddMarkFatItemToBuf + * FUNCTION it will search a empty cluster map ram + * PARAM: T_U8 *SectorBitMap + PSECTOR_MAP_ARRAY_INFO pSectorMapBuf + T_U32 MarkItem + T_U16 ClusPerSector + * RETURN: return the find item +**************************************************************************/ + +T_U8 FAT_AddMarkFatItemToBuf(T_U8 *SectorBitMap, PSECTOR_MAP_ARRAY_INFO pSectorMapBuf, T_U32 MarkItem, T_U16 ClusPerSector) +{ + T_U16 i; + T_U32 SectorAddr, SectorOff; + PSECTOR_MAP_ARRAY_INFO pCurMap = pSectorMapBuf; + PCLUSTER_MAP_INFO pFreeClusMap = AK_NULL; + + SectorAddr = MarkItem / ClusPerSector; + SectorOff = MarkItem % ClusPerSector; + if(TestBitMap(SectorBitMap, SectorAddr)) + { + return FAT_LINK_ERROR; + } + if(pCurMap->pCurSecMap && (pCurMap->pCurSecMap->pMapArray[pCurMap->index].sectorAddr == SectorAddr)) + { + i = (T_U16)pSectorMapBuf->index; + pCurMap = pCurMap->pCurSecMap; + } + else + { + while(pCurMap != AK_NULL) + { + for(i = 0; i < CLUSTER_MAP_NUMBER; i++) + { + if(pFreeClusMap == AK_NULL) + { + if(pCurMap->pMapArray[i].sectorAddr == T_U32_MAX) + pFreeClusMap = &pCurMap->pMapArray[i]; + } + if(pCurMap->pMapArray[i].sectorAddr == SectorAddr) + break; + } + if(i != CLUSTER_MAP_NUMBER) + break; + pCurMap = pCurMap->next; + } + } + + if(pCurMap != AK_NULL) + { + pSectorMapBuf->pCurSecMap = pCurMap; + pSectorMapBuf->index = i; + if(TestBitMap(pCurMap->pMapArray[i].bitMap, SectorOff)) + { + return FAT_LINK_ERROR; + } + else + { + SetBitMap(pCurMap->pMapArray[i].bitMap, SectorOff); + pCurMap->pMapArray[i].bitNum ++ ; + if(ClusPerSector == pCurMap->pMapArray[i].bitNum) + { + // we will free the small buffer + pSectorMapBuf->pCurSecMap = AK_NULL; + SetBitMap(SectorBitMap, SectorAddr); + pCurMap->pMapArray[i].sectorAddr = T_U32_MAX; + pCurMap->pMapArray[i].bitNum = 0; + if(pCurMap != pSectorMapBuf) + { + // we will check if the big buffer need free + for(i = 0; i < CLUSTER_MAP_NUMBER; i++) + { + if(pCurMap->pMapArray[i].sectorAddr != T_U32_MAX) + break; + } + if(i == CLUSTER_MAP_NUMBER) + { + while(pSectorMapBuf->next != pCurMap) + pSectorMapBuf = pSectorMapBuf->next; + pSectorMapBuf->next = pCurMap->next; + kfree(pCurMap); + } + } + } + } + } + else + { + if(pFreeClusMap == AK_NULL) + { + if(FAT_CheckChkDskRamNumber(pSectorMapBuf) == AK_TRUE) + { + pCurMap = FAT_MallocChkDskBuf(pSectorMapBuf,ClusPerSector); + if(pCurMap == AK_NULL) + return MARK_MALLOC_ERROR; + + pFreeClusMap = &pCurMap->pMapArray[0]; + } + else + { + pFreeClusMap = FAT_FIndChkDskClusMap(SectorBitMap, pSectorMapBuf, &pCurMap); + } + } + pSectorMapBuf->pCurSecMap = pCurMap; + pSectorMapBuf->index = 0; + pFreeClusMap->sectorAddr = SectorAddr; + pFreeClusMap->bitNum = 1; + memset(pFreeClusMap->bitMap, 0, ClusPerSector >> 3); + SetBitMap(pFreeClusMap->bitMap, SectorOff); + } + return MARK_FAT_OK; +} + +/************************************************************************ + * NAME: FAT_FreeChkDskBuf + * FUNCTION it will free all buffer with chkdsk + * PARAM: PSECTOR_MAP_ARRAY_INFO pSectorMapBuf + * RETURN: NONE +**************************************************************************/ + +void FAT_FreeChkDskBuf(PSECTOR_MAP_ARRAY_INFO pSectorMapBuf) +{ + PSECTOR_MAP_ARRAY_INFO next; + + while(pSectorMapBuf != AK_NULL) + { + next = pSectorMapBuf->next; + kfree(pSectorMapBuf); + pSectorMapBuf = next; + } +} + +/************************************************************************ + * NAME: FAT_DowithFatError + * FUNCTION it will fix fat error + * PARAM: T_PDRIVER driver + T_U32 ErrFat + * RETURN: NONE +**************************************************************************/ + +T_BOOL FAT_DowithFatError(struct super_block *sb, T_U32 ErrFat, T_U32 ClusPerBuf) +{ + struct msdos_sb_info *sbi = MSDOS_SB(sb); + struct buffer_head *bh = NULL; + T_U8 *FatDataBuf; + T_U32 PageNum, PageOff; + int err; + + printk("FSLIB: we will fix the cluser:%08lx!, set the end flag.\r\n",ErrFat); + + PageNum = ErrFat / ClusPerBuf; + PageOff = ErrFat % ClusPerBuf; + + PDEBUG("pagenum:%lu, page_off:%lu, ErrFat:%lu, ClusPerBuf:%lu.\n", PageNum, PageOff, ErrFat, ClusPerBuf); + bh = sb_bread(sb, sbi->fat_start + PageNum); + if(!bh) { + return AK_FALSE; + } + + FatDataBuf = bh->b_data; + + if(sbi->fat_bits == 16) + { + ((T_U16 *)FatDataBuf)[PageOff] = EOF_FAT16; + } + else + { + ((T_U32 *)FatDataBuf)[PageOff] = EOF_FAT32; + } + + mark_buffer_dirty(bh); + err = sync_dirty_buffer(bh); + + brelse(bh); + if(err) { + return AK_FALSE; + } + return AK_TRUE; + +} + + +/************************************************************************ + * NAME: FAT_CheckFatLinkError + * FUNCTION it will check the fat error + * PARAM: T_PDRIVER driver, + T_U32 MarkItem + T_U32 ClusPerSector + * RETURN: NONE +**************************************************************************/ + +T_U8 FAT_CheckFatLinkError(struct super_block *sb, T_U32 MarkItem, T_U32 ClusPerSector) +{ + struct msdos_sb_info *sbi = MSDOS_SB(sb); + struct buffer_head *bh; + T_U8 *FatDataBuf, ret; + T_U32 SectorAddr, SectorOff; + + SectorAddr = MarkItem / ClusPerSector; + SectorOff = MarkItem % ClusPerSector; + + PDEBUG("SectorAddr:%lu, SectorOff:%lu, MarkItem:%lu, ClusPerSector:%lu.\n", + SectorAddr, SectorOff, MarkItem, ClusPerSector); + bh = sb_bread(sb, sbi->fat_start + SectorAddr); + if(!bh) + { + return FAT_READ_ERROR; + } + + FatDataBuf = bh->b_data; + + if(sbi->fat_bits == 16) + { + ret = (((T_U16 *)FatDataBuf)[SectorOff] == 0); + } + else + { + ret = (((T_U32 *)FatDataBuf)[SectorOff] == 0); + } + + brelse(bh); + if(ret == AK_TRUE) + return FAT_LINK_ERROR; + else + return MARK_FAT_OK; + +} + + +/* 128kb is the whole sectors for FAT12 and FAT16 */ +#define FAT_READA_SIZE (128 * 1024) + + +/************************************************************************ + * NAME: FAT_CheckFatLinkError + * FUNCTION it will fix the fat error when system poweroff without closing file + * PARAM: T_U32 DriverID , + F_ChkDskCallback pCallBack + T_VOID *CallbackData + * RETURN: NONE +**************************************************************************/ + +T_BOOL Fat_ChkDsk(struct super_block *sb, F_ChkDskCallback pCallBack, T_VOID *CallbackData) +{ + struct msdos_sb_info *sbi = MSDOS_SB(sb); + struct buffer_head *bh = NULL; + T_U8 *FatDataBuf, *SectorBitMap,percent, ret; + T_U32 i, j, ClusPerBuf, ClusPerSector; + T_U32 CurCluster, NextFatItem, MarkFatItem; + PSECTOR_MAP_ARRAY_INFO pSectorMapBuf = AK_NULL; + T_U32 sector_bit; + int diff_time; /*used for test.*/ + + unsigned long reada_blocks, reada_mask; + + reada_blocks = FAT_READA_SIZE >> sb->s_blocksize_bits; + reada_mask = reada_blocks - 1; + + sector_bit = sbi->cluster_bits - (ffs(sbi->sec_per_clus) - 1); + + diff_time = jiffies; /*record start time.*/ + + SectorBitMap = kmalloc((sbi->fat_length >> 3) + 1, GFP_KERNEL); + if(SectorBitMap == AK_NULL) + return AK_FALSE; + memset(SectorBitMap, 0, (sbi->fat_length >> 3) + 1); + + ClusPerSector = (1 << (sector_bit - 1)); + ClusPerBuf = sb->s_blocksize >> 1; + + PDEBUG("sector_bit:%lu, ClusPerBuf:%lu, ClusPerSector:%lu, sbi->fat_length=%lu, s_blocksize:%lu.\n", + sector_bit, ClusPerBuf, ClusPerSector, sbi->fat_length, sb->s_blocksize); + + if(sbi->fat_bits == 32) + { + ClusPerBuf >>= 1; + ClusPerSector >>= 1; + } + + pSectorMapBuf = FAT_MallocChkDskBuf(AK_NULL, (T_U16)ClusPerSector); + if(pSectorMapBuf == AK_NULL) + return AK_FALSE; + + percent = 0; + + for(i = 0, CurCluster = 0; i < sbi->fat_length && CurCluster <= sbi->max_cluster; i++) + { + if((i * 50) / sbi->fat_length != percent) + { + percent = (T_U8)((i * 50) /sbi->fat_length); + printk("."); + } + /* readahead of fat blocks , current block equals i.*/ + if ((i & reada_mask) == 0) { + int tmp; + unsigned long rest = sbi->fat_length - i; + rest = min(reada_blocks, rest); + + for (tmp = 0; tmp < rest; tmp++) + sb_breadahead(sb, i + tmp); + } + + bh = sb_bread(sb, sbi->fat_start + i); + if(!bh) + { + CurCluster += ClusPerBuf; + continue; + } + FatDataBuf = bh->b_data; + + for(j = 0; j < ClusPerBuf && CurCluster <= sbi->max_cluster; j ++, CurCluster ++) + { + NextFatItem = FAT_GetFatLinkInfo(sbi, FatDataBuf, (T_U16)j); + if(NextFatItem == 0) + { + MarkFatItem = CurCluster; + } + else if(NextFatItem >= FAT32_MAX_USER_CLUSTER) + { + continue; + } + else + { + MarkFatItem = NextFatItem; + } + if((MarkFatItem > sbi->max_cluster) && (MarkFatItem != EOF_FAT32)) + { + // printk("the cluster is error:%lu-->%lu\r\n",CurCluster, NextFatItem); + continue; + } + ret = FAT_AddMarkFatItemToBuf(SectorBitMap, pSectorMapBuf, MarkFatItem, (T_U16)ClusPerSector); + if(ret == MARK_MALLOC_ERROR) + { + // if it fails to malloc ram , we will exit + CurCluster = sbi->max_cluster; + break; + } + else if(ret == FAT_LINK_ERROR) + { + // if we find the fat error, we will fix it , + ret = FAT_CheckFatLinkError(sb, MarkFatItem, ClusPerSector); + if(ret == FAT_LINK_ERROR) + { + if(FAT_DowithFatError(sb, MarkFatItem, ClusPerBuf) == AK_FALSE) + { + //it fails to fix error, we will exit + CurCluster = sbi->max_cluster; + } + } + } + } + brelse(bh); + } + FAT_FreeChkDskBuf(pSectorMapBuf); + kfree(SectorBitMap); + + diff_time = jiffies - diff_time; + printk("Check disk finish, spend time:%dms.\n", (diff_time*1000)/HZ); + + return AK_TRUE; +} + + + diff --git a/fs/fat/chkdsk.h b/fs/fat/chkdsk.h new file mode 100644 index 00000000..98564a2e --- /dev/null +++ b/fs/fat/chkdsk.h @@ -0,0 +1,61 @@ +#ifndef _CHKDSK_H_ +#define _CHKDSK_H_ +#include +#include "fat.h" + + +#define FAT16_MAX_USER_CLUSTER 0XFFF +#define FAT32_MAX_USER_CLUSTER 0XFFFFFF8 +typedef struct tag_Medium T_MEDIUM; + +typedef struct tag_Medium *T_PMEDIUM; + + +typedef T_U32 (*F_ReadSector)(T_PMEDIUM medium, T_U8** buf, T_U32 start, T_U32 size); +typedef T_U32 (*F_WriteSector)(T_PMEDIUM medium, const T_U8 *buf, T_U32 start, T_U32 size); + +struct tag_Medium +{ + struct super_block *sb; + + T_U8 SecBit; + T_U8 PageBit; // 2^n = SecPerPage. + T_U8 SecPerPg; //PageBit -SecBit . +}; + + +#define CLUSTER_MAP_NUMBER 100 +typedef struct _CLUSTER_MAP_ARRAY_INFO *PSECTOR_MAP_ARRAY_INFO; + +typedef struct _CLUSTER_MAP_INFO +{ + T_U32 sectorAddr; //˻ӦSECOTR,ΪFAT̫,ԶΪT_U16Աʡڴ,ֵΪ-1,ʾûбʹ. + T_U32 bitNum; //¼λͼжٸλ1,CluserPerSector,ʾλͼΪȫ1״̬,ͷŴ˻. + T_U8 *bitMap; //λͼ,ָ,Ϊʱ1,Ϊ0,ȫΪ1ʱ,ͷ(sectorAddr = T_U16_MAX),Աʹ.ĴСΪBytesPerSector/8. +} CLUSTER_MAP_INFO, *PCLUSTER_MAP_INFO; + +typedef struct _CLUSTER_MAP_ARRAY_INFO +{ + T_U32 index; //ػ,ÿη100. + PSECTOR_MAP_ARRAY_INFO next; //ָһ󻺳 + PCLUSTER_MAP_INFO pMapArray; //ػ,ÿη100. + PSECTOR_MAP_ARRAY_INFO pCurSecMap; //ָǰʹõС +}SECTOR_MAP_ARRAY_INFO; + + +enum{MARK_FAT_OK, FAT_LINK_ERROR, MARK_MALLOC_ERROR, FAT_READ_ERROR}; + +#define TestBitMap(BitMap, item) (((BitMap)[(item)>>3]&(1<<((item)&7)))) +#define SetBitMap(BitMap, item) ((BitMap)[(item)>>3] |= (1<<((item)&7))) +#define ClrBitMap(BitMap, item) ((BitMap)[(item)>>3] &= ~(1<<((item)&7))) + +typedef void F_ChkDskCallback(T_VOID *pData, T_U32 percent); +T_BOOL Fat_ChkDsk(struct super_block *sb, F_ChkDskCallback pCallBack, T_VOID *CallbackData); + +//T_U32 FAT_GetFatLinkInfo(struct msdos_sb_info *sbi, T_U8 * pFatBuf, T_U16 offset); +//T_U32 FAT_GetFatLinkInfo_chkdsk(struct msdos_sb_info *sbi, T_U8 * pFatBuf, T_U16 offset); +//T_VOID FAT_SetFatLinkInfo(struct msdos_sb_info *sbi, T_U8 * pFatBuf, T_U16 offset, T_U32 newValue); + + +#endif + diff --git a/fs/fat/dir.c b/fs/fat/dir.c index aca191bd..65e174bd 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -754,6 +754,13 @@ static int fat_ioctl_readdir(struct inode *inode, struct file *filp, return ret; } +static int fat_ioctl_volume_id(struct inode *dir) +{ + struct super_block *sb = dir->i_sb; + struct msdos_sb_info *sbi = MSDOS_SB(sb); + return sbi->vol_id; +} + static long fat_dir_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { @@ -770,6 +777,8 @@ static long fat_dir_ioctl(struct file *filp, unsigned int cmd, short_only = 0; both = 1; break; + case VFAT_IOCTL_GET_VOLUME_ID: + return fat_ioctl_volume_id(inode); default: return fat_generic_ioctl(filp, cmd, arg); } diff --git a/fs/fat/fat.h b/fs/fat/fat.h index 66994f31..5ce80258 100644 --- a/fs/fat/fat.h +++ b/fs/fat/fat.h @@ -8,6 +8,8 @@ #include #include #include +#include "fat_fsck.h" +#include "fc_bmp.h" /* * vfat shortname flags @@ -22,6 +24,10 @@ #define FAT_ERRORS_PANIC 2 /* panic on error */ #define FAT_ERRORS_RO 3 /* remount r/o on error */ +#define FSCK_FAT_DIRTY (0xff) +#define FSCK_FAT_NORMAL (0x00) + + struct fat_mount_options { uid_t fs_uid; gid_t fs_gid; @@ -78,6 +84,7 @@ struct msdos_sb_info { const void *dir_ops; /* Opaque; default directory operations */ int dir_per_block; /* dir entries per block */ int dir_per_block_bits; /* log2(dir_per_block) */ + unsigned long vol_id; /* volume ID */ int fatent_shift; struct fatent_operations *fatent_ops; @@ -87,6 +94,11 @@ struct msdos_sb_info { spinlock_t inode_hash_lock; struct hlist_head inode_hashtable[FAT_HASH_SIZE]; + + /*add by lixinhai, use for chkdsk.*/ + unsigned int fsck_flags; + struct fat_chkdsk chkdsk; + struct fat_fcbmp fcbmp; }; #define FAT_CACHE_VALID 0 /* special case for valid cache */ @@ -291,6 +303,19 @@ static inline void fatent_brelse(struct fat_entry *fatent) fatent->fat_inode = NULL; } +static inline void chkdsk_set_chk_flags(struct msdos_sb_info *sbi, unsigned int flags) +{ + sbi->fsck_flags = flags; +} + + +static inline int chkdsk_disk_status(struct msdos_sb_info *sbi) +{ + return sbi->fsck_flags; +} + + + extern void fat_ent_access_init(struct super_block *sb); extern int fat_ent_read(struct inode *inode, struct fat_entry *fatent, int entry); @@ -342,6 +367,11 @@ extern void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec *ts, __le16 *time, __le16 *date, u8 *time_cs); extern int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs); +extern int fat_write_fsck_flags(struct super_block *sb, unsigned int flags); + +extern int fat_scan_disk(struct super_block *sb); +extern void fat_release_disk(struct super_block *sb); + int fat_cache_init(void); void fat_cache_destroy(void); diff --git a/fs/fat/fat_fsck.h b/fs/fat/fat_fsck.h new file mode 100644 index 00000000..73826bdc --- /dev/null +++ b/fs/fat/fat_fsck.h @@ -0,0 +1,126 @@ +#ifndef __FAT_FSCK_H__ +#define __FAT_FSCK_H__ + +/* + * sample of fat filesystem check and recovery tools. + * base of bmptree reference to bmp_tree.c. + * + * author: lixinhai + * date : 2013-11-02 + * + * Copyright (C) Anyka corporation, Inc. All rights reserved. + * */ + + +#include "bmp_tree.h" + +enum fat_fsck_sel +{ + FAT_FSCK_UNSUPPORT = 0, /*unsupport the filesystem check.*/ + FAT_FSCK_BMPTREE, /*Base of bitmap tree.*/ + FAT_FSCK_NORMAL, /*use for RTOS Alg.*/ +}; + +struct fat_chkdsk +{ + struct bmptree *bmptree; + struct super_block *sb; +}; + +#define FAT32_MAX_USER_CLUSTER 0XFFFFFF8 + +static inline void show_fat_fsck_process(unsigned long total, + unsigned long curr) +{ + if((curr % (total/50)) == 0) + printk("."); +} + +#if defined(CONFIG_FAT_FSCK) +static inline int support_fat_fsck(void) +{ + return FAT_FSCK_BMPTREE; +} + +static inline int fat_fsck_check_cluster_status(struct fat_chkdsk *chkdsk, unsigned long cluster) +{ + return bmptree_check(chkdsk->bmptree, cluster); +} + +/* + * set cluster flags when search to cluster + * */ +static inline int fat_fsck_set_cluster(struct fat_chkdsk *chkdsk, int cluster) +{ + return bmptree_set(chkdsk->bmptree, cluster); +} + +/* + * filesystem check module initilize. + * */ +static inline int fat_fsck_init(struct super_block *sb, struct fat_chkdsk *chkdsk) +{ +#if 0 + int err; + err = bmptree_testmyself(); + printk("bmptree test myself %s.\n", err ? "fail":"success"); +#endif + + chkdsk->bmptree = bmptree_init("fsck", chkdsk); + chkdsk->sb = sb; + return 0; +} + +static inline void fat_fsck_release(struct fat_chkdsk *chkdsk) +{ + bmptree_release(chkdsk->bmptree); +} + + +#elif defined(CONFIG_FAT_CHK_DISK) +#include "chkdsk.h" +static inline int support_fat_fsck(void) +{ + return FAT_FSCK_NORMAL; +} + +static inline int fat_fsck_set_cluster(struct fat_chkdsk *chkdsk, int cluster) +{ + return 0; +} + +static inline int fat_fsck_init(struct super_block *sb, struct fat_chkdsk *chkdsk) +{ + Fat_ChkDsk(sb, NULL, NULL); + return 1; /*check success, compatible for new alg*/ +} + +static inline void fat_fsck_release(struct fat_chkdsk *chkdsk) +{ +} + +#else /*FAT_NOT_CHKDSK*/ +static inline int support_fat_fsck(void) +{ + return FAT_FSCK_UNSUPPORT; +} + +static inline int fat_fsck_set_cluster(struct fat_chkdsk *chkdsk, int cluster) +{ + return 0; +} + +static inline int fat_fsck_init(struct super_block *sb, + struct fat_chkdsk *chkdsk) +{ + return -EINVAL; +} + +static inline void fat_fsck_release(struct fat_chkdsk *chkdsk) +{ +} + +#endif /*CONFIG_FAT_FSCK*/ + + +#endif diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c index 2e81ac0d..a2cc1910 100644 --- a/fs/fat/fatent.c +++ b/fs/fat/fatent.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "fat.h" struct fatent_operations { @@ -452,6 +453,8 @@ static void fat_collect_bhs(struct buffer_head **bhs, int *nr_bhs, } } +#define TIRGGER_FIND_FCBMP_THRESHOLD 3 + int fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster) { struct super_block *sb = inode->i_sb; @@ -460,6 +463,7 @@ int fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster) struct fat_entry fatent, prev_ent; struct buffer_head *bhs[MAX_BUF_PER_PAGE]; int i, count, err, nr_bhs, idx_clus; + int hit_flags = 0; BUG_ON(nr_cluster > (MAX_BUF_PER_PAGE / 2)); /* fixed limit */ @@ -476,6 +480,17 @@ int fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster) fatent_init(&fatent); fatent_set_entry(&fatent, sbi->prev_free + 1); while (count < sbi->max_cluster) { + + if(hit_flags >= TIRGGER_FIND_FCBMP_THRESHOLD) { + int entry; + entry = fcbmp_find_next_free_cluster(&sbi->fcbmp, + sbi->max_cluster - 1, fatent.entry); + if((entry >= FAT_START_ENT) && (entry < sbi->max_cluster)) + fatent.entry = entry; + + hit_flags = 0; + } + if (fatent.entry >= sbi->max_cluster) fatent.entry = FAT_START_ENT; fatent_set_entry(&fatent, fatent.entry); @@ -488,11 +503,14 @@ int fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster) if (ops->ent_get(&fatent) == FAT_ENT_FREE) { int entry = fatent.entry; + hit_flags = 0; /* make the cluster chain */ ops->ent_put(&fatent, FAT_ENT_EOF); if (prev_ent.nr_bhs) ops->ent_put(&prev_ent, entry); + fcbmp_set_cluster(&sbi->fcbmp, fatent.entry); + fat_collect_bhs(bhs, &nr_bhs, &fatent); sbi->prev_free = entry; @@ -510,6 +528,9 @@ int fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster) * so we can still use the prev_ent. */ prev_ent = fatent; + } else { + if(++hit_flags >= TIRGGER_FIND_FCBMP_THRESHOLD) + break; } count++; if (count == sbi->max_cluster) @@ -585,6 +606,9 @@ int fat_free_clusters(struct inode *inode, int cluster) } ops->ent_put(&fatent, FAT_ENT_FREE); + + fcbmp_clear_cluster(&sbi->fcbmp, fatent.entry); + if (sbi->free_clusters != -1) { sbi->free_clusters++; sb->s_dirt = 1; @@ -683,3 +707,103 @@ out: unlock_fat(sbi); return err; } + + +int fat_scan_disk(struct super_block *sb) +{ + struct msdos_sb_info *sbi = MSDOS_SB(sb); + struct fatent_operations *ops = sbi->fatent_ops; + struct fat_entry fatent; + unsigned long reada_blocks, reada_mask, cur_block; + int err = 0; + int diff_time; /*used for test.*/ + int fcbmp_flag, fsck_flag; + unsigned long entry = 0; + + lock_fat(sbi); + + /*first, set the check disk flags to dirty when mount file system.*/ + fat_write_fsck_flags(sb, FSCK_FAT_DIRTY); + fcbmp_flag = support_fcbmp(); + fsck_flag = (chkdsk_disk_status(sbi) == FSCK_FAT_DIRTY) && + (support_fat_fsck() != FAT_FSCK_UNSUPPORT); + +//fsck_flag = 1; /*FIXME:used for test.*/ + + if(fcbmp_flag && (fcbmp_init(sb, &sbi->fcbmp) != 0)) + fcbmp_flag = 0; + + if(fsck_flag && (fat_fsck_init(sb, &sbi->chkdsk) != 0)) + fsck_flag = 0; + + if(!fcbmp_flag && !fsck_flag) + goto out; + + printk("Fat file system start scan disk, total %lu cluster.\n", + sbi->max_cluster); + + diff_time = jiffies; /*record start time.*/ + + reada_blocks = FAT_READA_SIZE >> sb->s_blocksize_bits; + reada_mask = reada_blocks - 1; + cur_block = 0; + + fatent_init(&fatent); + fatent_set_entry(&fatent, FAT_START_ENT); + while (fatent.entry < sbi->max_cluster) { + /* readahead of fat blocks */ + if ((cur_block & reada_mask) == 0) { + unsigned long rest = sbi->fat_length - cur_block; + fat_ent_reada(sb, &fatent, min(reada_blocks, rest)); + } + cur_block++; + + show_fat_fsck_process(sbi->fat_length, cur_block); + err = fat_ent_read_block(sb, &fatent); + if (err) + goto out; + + do { + if (ops->ent_get(&fatent) != FAT_ENT_FREE) { + if(fcbmp_flag) + fcbmp_set_cluster(&sbi->fcbmp, fatent.entry); + entry = *fatent.u.ent32_p; + } else { + entry = fatent.entry; + } + if(likely(fsck_flag && (entry < FAT32_MAX_USER_CLUSTER) && + (entry != EOF_FAT32) && + (entry < sbi->max_cluster))) { + err = fat_fsck_set_cluster(&sbi->chkdsk, entry); + if(unlikely(err == -EEXIST)) { + printk("fix the cluser:%08lx, set the end flag.\n", entry); + ops->ent_put(&fatent, FAT_ENT_EOF); + } + } + } while (fat_ent_next(sbi, &fatent)); + } + fatent_brelse(&fatent); + + if(fsck_flag) + fat_fsck_release(&sbi->chkdsk); + + diff_time = jiffies - diff_time; + printk("Scan disk finish, spend time:%dms.\n", + (diff_time*1000)/HZ); +out: + unlock_fat(sbi); + return err; +} + + +void fat_release_disk(struct super_block *sb) +{ + struct msdos_sb_info *sbi = MSDOS_SB(sb); + + /*printk("fat filesystem release disk.\n");*/ + if(support_fcbmp()) + fcbmp_release(&sbi->fcbmp); + + fat_write_fsck_flags(sb, FSCK_FAT_NORMAL); +} + diff --git a/fs/fat/fc_bmp.h b/fs/fat/fc_bmp.h new file mode 100644 index 00000000..501cde7e --- /dev/null +++ b/fs/fat/fc_bmp.h @@ -0,0 +1,118 @@ +#ifndef __FC_BMP_H__ +#define __FC_BMP_H__ +/* + * Free cluster bitmap + * use for bitmap tree record the cluster allocation status. + * base of bmptree. + * + * author: lixinhai + * date : 2013-11-02 + * + * Copyright (C) Anyka corporation, Inc. All rights reserved. + * */ + +#include "bmp_tree.h" +#include + +struct fat_fcbmp +{ + struct bmptree *bmptree; + struct super_block *sb; +}; + +#if defined(CONFIG_FAT_FCBMP) + +/* + *fast of find next cluster in bitmap tree. + * */ +static inline int fcbmp_find_next_free_cluster(struct fat_fcbmp *fcbmp, + unsigned long max_cluster, int start) +{ + return bmptree_find_next_free_pos(fcbmp->bmptree, start, FAT_START_ENT, max_cluster); +} + + +static inline int fcbmp_check_cluster_status(struct fat_fcbmp *fcbmp, unsigned long cluster) +{ + return bmptree_check(fcbmp->bmptree, cluster); +} + +/* + * set cluster flags when alloc the cluster. + * */ +static inline int fcbmp_set_cluster(struct fat_fcbmp *fcbmp, int cluster) +{ + return bmptree_set(fcbmp->bmptree, cluster); +} + +/* + * clear cluster flags when free the cluster. + * */ +static inline int fcbmp_clear_cluster(struct fat_fcbmp *fcbmp, int cluster) +{ + return bmptree_clear(fcbmp->bmptree, cluster); +} + +/* + * fat free cluster bitmap initialize. + * */ +static inline int fcbmp_init(struct super_block *sb, struct fat_fcbmp *fcbmp) +{ +#if 0 + int err; + err = bmptree_testmyself(); + printk("bmptree test myself %s.\n", err ? "fail":"success"); +#endif + + fcbmp->bmptree = bmptree_init("fcbmp", fcbmp); + fcbmp->sb = sb; + return 0; +} + +static inline void fcbmp_release(struct fat_fcbmp *fcbmp) +{ + bmptree_release(fcbmp->bmptree); +} + +static inline int support_fcbmp(void) +{ + return 1; +} + +#else +static inline int fcbmp_find_next_free_cluster(struct fat_fcbmp *fcbmp, + unsigned long max_cluster, int start) +{ + return 0; +} + +static inline int fcbmp_check_cluster_status(struct fat_fcbmp *fcbmp, unsigned long cluster) +{ + return 0; +} + +static inline int fcbmp_set_cluster(struct fat_fcbmp *fcbmp, int cluster) +{ + return 0; +} +static inline int fcbmp_clear_cluster(struct fat_fcbmp *fcbmp, int cluster) +{ + return 0; +} + +static inline int fcbmp_init(struct super_block *sb, struct fat_fcbmp *fcbmp) +{ + return 0; +} + +static inline void fcbmp_release(struct fat_fcbmp *fcbmp) +{ +} + +static inline int support_fcbmp(void) +{ + return 0; +} +#endif + +#endif diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 21687e31..714c8922 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -28,6 +28,7 @@ #include #include #include "fat.h" +#include "chkdsk.h" #ifndef CONFIG_FAT_DEFAULT_IOCHARSET /* if user don't select VFAT, this is undefined. */ @@ -487,6 +488,7 @@ static void fat_put_super(struct super_block *sb) { struct msdos_sb_info *sbi = MSDOS_SB(sb); + fat_release_disk(sb); if (sb->s_dirt) fat_write_super(sb); @@ -1246,6 +1248,7 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat, struct inode *root_inode = NULL, *fat_inode = NULL; struct buffer_head *bh; struct fat_boot_sector *b; + struct fat_boot_bsx *bsx; struct msdos_sb_info *sbi; u16 logical_sector_size; u32 total_sectors, total_clusters, fat_clusters, rootdir_sectors; @@ -1390,6 +1393,8 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat, goto out_fail; } + bsx = (struct fat_boot_bsx *)(bh->b_data + FAT32_BSX_OFFSET); + fsinfo = (struct fat_boot_fsinfo *)fsinfo_bh->b_data; if (!IS_FSINFO(fsinfo)) { fat_msg(sb, KERN_WARNING, "Invalid FSINFO signature: " @@ -1402,11 +1407,20 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat, sbi->free_clus_valid = 1; sbi->free_clusters = le32_to_cpu(fsinfo->free_clusters); sbi->prev_free = le32_to_cpu(fsinfo->next_cluster); + + /*FIXME:add fsck flag use for check disk flag. lixinhai*/ + chkdsk_set_chk_flags(sbi, le32_to_cpu(fsinfo->reserved1[0])); } brelse(fsinfo_bh); + } else { + bsx = (struct fat_boot_bsx *)(bh->b_data + FAT16_BSX_OFFSET); } + /* interpret volume ID as a little endian 32 bit integer */ + sbi->vol_id = (((u32)bsx->vol_id[0]) | ((u32)bsx->vol_id[1] << 8) | + ((u32)bsx->vol_id[2] << 16) | ((u32)bsx->vol_id[3] << 24)); + sbi->dir_per_block = sb->s_blocksize / sizeof(struct msdos_dir_entry); sbi->dir_per_block_bits = ffs(sbi->dir_per_block) - 1; @@ -1508,6 +1522,8 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat, goto out_fail; } + fat_scan_disk(sb); + return 0; out_invalid: diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 6d93360c..766fa01f 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -97,6 +97,46 @@ int fat_clusters_flush(struct super_block *sb) return 0; } + +/* Flushes the number of free clusters on FAT32 */ +/* XXX: Need to write one per FSINFO block. Currently only writes 1 */ +int fat_write_fsck_flags(struct super_block *sb, unsigned int flags) +{ + int err = 0; + struct msdos_sb_info *sbi = MSDOS_SB(sb); + struct buffer_head *bh; + struct fat_boot_fsinfo *fsinfo; + + if (sbi->fat_bits != 32) + return 0; + + bh = sb_bread(sb, sbi->fsinfo_sector); + if (bh == NULL) { + fat_msg(sb, KERN_ERR, "bread failed in fat_write_fsck_flags"); + return -EIO; + } + + fsinfo = (struct fat_boot_fsinfo *)bh->b_data; + /* Sanity check */ + if (!IS_FSINFO(fsinfo)) { + fat_msg(sb, KERN_ERR, "Invalid FSINFO signature: " + "0x%08x, 0x%08x (sector = %lu)", + le32_to_cpu(fsinfo->signature1), + le32_to_cpu(fsinfo->signature2), + sbi->fsinfo_sector); + } else { + fsinfo->reserved1[0] = cpu_to_le32(flags); + mark_buffer_dirty(bh); + + err = sync_dirty_buffer(bh); + if(err) + fat_msg(sb, KERN_ERR, "flush disk fail, dirty of fat filesystem.\n"); + } + brelse(bh); + + return 0; +} + /* * fat_chain_add() adds a new cluster to the chain of clusters represented * by inode. diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index b35bd64f..1b21e0a0 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -1083,7 +1083,7 @@ void __mark_inode_dirty(struct inode *inode, int flags) if ((inode->i_state & flags) == flags) return; - if (unlikely(block_dump)) + if (unlikely(block_dump > 1)) block_dump___mark_inode_dirty(inode); spin_lock(&inode->i_lock); diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index f4246cfc..1f81f70d 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -19,6 +19,7 @@ #include #include #include +#include MODULE_ALIAS_MISCDEV(FUSE_MINOR); MODULE_ALIAS("devname:fuse"); @@ -387,7 +388,10 @@ __acquires(fc->lock) * Wait it out. */ spin_unlock(&fc->lock); - wait_event(req->waitq, req->state == FUSE_REQ_FINISHED); + + while (req->state != FUSE_REQ_FINISHED) + wait_event_freezable(req->waitq, + req->state == FUSE_REQ_FINISHED); spin_lock(&fc->lock); if (!req->aborted) diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index 8608f877..d77910e3 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -148,6 +148,10 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping, jffs2_dbg(1, "%s()\n", __func__); if (pageofs > inode->i_size) { + /* FIXME + * How about a huage hole ? + * If i make it happy, system will crash! + */ ret = jffs2_reserve_space(c, sizeof(ri), &alloc_len, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index dc0437e8..fdc5ef34 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -228,6 +228,7 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c, node with highest version -- i.e. the one which will end up as f->metadata. Note that such nodes won't be REF_UNCHECKED since there are no data to check anyway. */ + /* FIXME: But sometimes it is a regular file node, create by O_TRUNC flag */ if (!tn->fn->size) { if (rii->mdata_tn) { if (rii->mdata_tn->version < tn->version) { @@ -455,9 +456,11 @@ static int jffs2_build_inode_fragtree(struct jffs2_sb_info *c, if (rii->mdata_tn) { dbg_readinode("potential mdata is ver %d at %p\n", rii->mdata_tn->version, rii->mdata_tn); - high_ver = rii->mdata_tn->version; + /* FIXME: When it is not a regular file node, just do it. Otherwise, skip it */ + high_ver = rii->mdata_tn->version; rii->latest_ref = rii->mdata_tn->fn->raw; } + #ifdef JFFS2_DBG_READINODE_MESSAGES this = tn_last(&rii->tn_root); while (this) { @@ -505,6 +508,8 @@ static int jffs2_build_inode_fragtree(struct jffs2_sb_info *c, highest_version, because this one is only counting _valid_ nodes which could give the latest inode metadata */ + /* FIXME: Make sure latest_ref point to valid node, + * but not empty node */ high_ver = this->version; rii->latest_ref = this->fn->raw; } diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c index b634de4c..2aa557f9 100644 --- a/fs/jffs2/write.c +++ b/fs/jffs2/write.c @@ -359,6 +359,13 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, jffs2_dbg(2, "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, offset); + /* FIXME + * Make one write op as atomic, less data frag lose when power off. + * But, still lose some data when the file huge enough. + * And, wasted more space case of biger minisize. + * And, GC will work harder. + * And, performance is bad + */ ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) { diff --git a/fs/namespace.c b/fs/namespace.c index 4e465397..ba5b8313 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -22,7 +22,11 @@ #include #include "pnode.h" #include "internal.h" +//#include +//T_PARTITION_INFO g_partition_info = {0}; + +//extern int ak_get_partition_bak_name_idex(unsigned long partition_idex); #define HASH_SHIFT ilog2(PAGE_SIZE / sizeof(struct list_head)) #define HASH_SIZE (1UL << HASH_SHIFT) @@ -2129,6 +2133,13 @@ long do_mount(char *dev_name, char *dir_name, char *type_page, struct path path; int retval = 0; int mnt_flags = 0; + //unsigned long partition_idex = B_PARTITION_IDEX; + //char *dev_name_temp = "/dev/mtdblock"; + + int idex = 0; + + + //printk(KERN_ERR "dir_name:%s\n", dir_name); /* Discard magic */ if ((flags & MS_MGC_MSK) == MS_MGC_VAL) @@ -2142,10 +2153,32 @@ long do_mount(char *dev_name, char *dir_name, char *type_page, if (data_page) ((char *)data_page)[PAGE_SIZE - 1] = 0; + /* ... and get the mountpoint */ retval = kern_path(dir_name, LOOKUP_FOLLOW, &path); if (retval) return retval; + #if 0 + if(strlen(dir_name) == strlen("/usr") && (memcmp(dir_name, "/usr", strlen("/usr"))== 0) + && g_partition_info.partition_cnt == PARTITION_CNT + && g_partition_info.partition_name_info[partition_idex].partition_name[0] != 0) + { + char buf[10] = {0}; + unsigned long str_len = strlen(dev_name_temp); + idex = ak_get_partition_bak_name_idex(partition_idex); + if(idex != -1) + { + printk(KERN_ERR "idex:%d\n", idex); + memset(buf, 0, 10); + sprintf(buf, "%d", idex + 1); + printk(KERN_ERR "dev_name:%s\n", dev_name); + memcpy(&dev_name[str_len], buf, strlen(buf)+1); + } + printk(KERN_ERR "dev_name:%s\n", dev_name); + + + } + #endif retval = security_sb_mount(dev_name, &path, type_page, flags, data_page); diff --git a/fs/proc/base.c b/fs/proc/base.c index 9fc77b41..c8cb15dc 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -137,6 +137,12 @@ struct pid_entry { static int proc_fd_permission(struct inode *inode, int mask); +/* ANDROID is for special files in /proc. */ +#define ANDROID(NAME, MODE, OTYPE) \ + NOD(NAME, (S_IFREG|(MODE)), \ + &proc_##OTYPE##_inode_operations, \ + &proc_##OTYPE##_operations, {}) + /* * Count the number of hardlinks for the pid_entry table, excluding the . * and .. links. @@ -969,6 +975,35 @@ out: return err < 0 ? err : count; } +static int oom_adjust_permission(struct inode *inode, int mask) +{ + uid_t uid; + struct task_struct *p; + + p = get_proc_task(inode); + if(p) { + uid = task_uid(p); + put_task_struct(p); + } + + /* + * System Server (uid == 1000) is granted access to oom_adj of all + * android applications (uid > 10000) as and services (uid >= 1000) + */ + if (p && (current_fsuid() == 1000) && (uid >= 1000)) { + if (inode->i_mode >> 6 & mask) { + return 0; + } + } + + /* Fall back to default. */ + return generic_permission(inode, mask); +} + +static const struct inode_operations proc_oom_adjust_inode_operations = { + .permission = oom_adjust_permission, +}; + static const struct file_operations proc_oom_adjust_operations = { .read = oom_adjust_read, .write = oom_adjust_write, @@ -3011,7 +3046,7 @@ static const struct pid_entry tgid_base_stuff[] = { REG("cgroup", S_IRUGO, proc_cgroup_operations), #endif INF("oom_score", S_IRUGO, proc_oom_score), - REG("oom_adj", S_IRUGO|S_IWUSR, proc_oom_adjust_operations), + ANDROID("oom_adj",S_IRUGO|S_IWUSR, oom_adjust), REG("oom_score_adj", S_IRUGO|S_IWUSR, proc_oom_score_adj_operations), #ifdef CONFIG_AUDITSYSCALL REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations), diff --git a/fs/proc/version.c b/fs/proc/version.c index 76817a60..56a793c3 100644 --- a/fs/proc/version.c +++ b/fs/proc/version.c @@ -5,12 +5,16 @@ #include #include +#define _STR(x) #x +#define STR(x) _STR(x) + static int version_proc_show(struct seq_file *m, void *v) { seq_printf(m, linux_proc_banner, utsname()->sysname, utsname()->release, utsname()->version); + seq_printf(m, "-AKV_"STR(ANYKA_VERSION)"\n"); return 0; } diff --git a/fs/yaffs2/Kconfig b/fs/yaffs2/Kconfig new file mode 100644 index 00000000..aac0dbff --- /dev/null +++ b/fs/yaffs2/Kconfig @@ -0,0 +1,171 @@ +# +# YAFFS file system configurations +# + +config YAFFS_FS + tristate "YAFFS2 file system support" + default y + depends on MTD_BLOCK + select YAFFS_YAFFS1 + select YAFFS_YAFFS2 + help + YAFFS2, or Yet Another Flash Filing System, is a filing system + optimised for NAND Flash chips. + + To compile the YAFFS2 file system support as a module, choose M + here: the module will be called yaffs2. + + If unsure, say N. + + Further information on YAFFS2 is available at + . + +config YAFFS_YAFFS1 + bool "512 byte / page devices" + depends on YAFFS_FS + default y + help + Enable YAFFS1 support -- yaffs for 512 byte / page devices + + Not needed for 2K-page devices. + + If unsure, say Y. + +config YAFFS_9BYTE_TAGS + bool "Use older-style on-NAND data format with pageStatus byte" + depends on YAFFS_YAFFS1 + default n + help + + Older-style on-NAND data format has a "pageStatus" byte to record + chunk/page state. This byte is zero when the page is discarded. + Choose this option if you have existing on-NAND data using this + format that you need to continue to support. New data written + also uses the older-style format. Note: Use of this option + generally requires that MTD's oob layout be adjusted to use the + older-style format. See notes on tags formats and MTD versions + in yaffs_mtdif1.c. + + If unsure, say N. + +config YAFFS_DOES_ECC + bool "Lets Yaffs do its own ECC" + depends on YAFFS_FS && YAFFS_YAFFS1 && !YAFFS_9BYTE_TAGS + default n + help + This enables Yaffs to use its own ECC functions instead of using + the ones from the generic MTD-NAND driver. + + If unsure, say N. + +config YAFFS_ECC_WRONG_ORDER + bool "Use the same ecc byte order as Steven Hill's nand_ecc.c" + depends on YAFFS_FS && YAFFS_DOES_ECC && !YAFFS_9BYTE_TAGS + default n + help + This makes yaffs_ecc.c use the same ecc byte order as Steven + Hill's nand_ecc.c. If not set, then you get the same ecc byte + order as SmartMedia. + + If unsure, say N. + +config YAFFS_YAFFS2 + bool "2048 byte (or larger) / page devices" + depends on YAFFS_FS + default y + help + Enable YAFFS2 support -- yaffs for >= 2K bytes per page devices + + If unsure, say Y. + +config YAFFS_AUTO_YAFFS2 + bool "Autoselect yaffs2 format" + depends on YAFFS_YAFFS2 + default y + help + Without this, you need to explicitely use yaffs2 as the file + system type. With this, you can say "yaffs" and yaffs or yaffs2 + will be used depending on the device page size (yaffs on + 512-byte page devices, yaffs2 on 2K page devices). + + If unsure, say Y. + +config YAFFS_DISABLE_TAGS_ECC + bool "Disable YAFFS from doing ECC on tags by default" + depends on YAFFS_FS && YAFFS_YAFFS2 + default n + help + This defaults Yaffs to using its own ECC calculations on tags instead of + just relying on the MTD. + This behavior can also be overridden with tags_ecc_on and + tags_ecc_off mount options. + + If unsure, say N. + +config YAFFS_ALWAYS_CHECK_CHUNK_ERASED + bool "Force chunk erase check" + depends on YAFFS_FS + default n + help + Normally YAFFS only checks chunks before writing until an erased + chunk is found. This helps to detect any partially written + chunks that might have happened due to power loss. + + Enabling this forces on the test that chunks are erased in flash + before writing to them. This takes more time but is potentially + a bit more secure. + + Suggest setting Y during development and ironing out driver + issues etc. Suggest setting to N if you want faster writing. + + If unsure, say Y. + +config YAFFS_EMPTY_LOST_AND_FOUND + bool "Empty lost and found on boot" + depends on YAFFS_FS + default n + help + If this is enabled then the contents of lost and found is + automatically dumped at mount. + + If unsure, say N. + +config YAFFS_DISABLE_BLOCK_REFRESHING + bool "Disable yaffs2 block refreshing" + depends on YAFFS_FS + default n + help + If this is set, then block refreshing is disabled. + Block refreshing infrequently refreshes the oldest block in + a yaffs2 file system. This mechanism helps to refresh flash to + mitigate against data loss. This is particularly useful for MLC. + + If unsure, say N. + +config YAFFS_DISABLE_BACKGROUND + bool "Disable yaffs2 background processing" + depends on YAFFS_FS + default n + help + If this is set, then background processing is disabled. + Background processing makes many foreground activities faster. + + If unsure, say N. + +config YAFFS_DISABLE_BAD_BLOCK_MARKING + bool "Disable yaffs2 bad block marking" + depends on YAFFS_FS + default n + help + Useful during early flash bring up to prevent problems causing + lots of bad block marking. + + If unsure, say N. + +config YAFFS_XATTR + bool "Enable yaffs2 xattr support" + depends on YAFFS_FS + default y + help + If this is set then yaffs2 will provide xattr support. + If unsure, say Y. diff --git a/fs/yaffs2/Makefile b/fs/yaffs2/Makefile new file mode 100644 index 00000000..fc03c801 --- /dev/null +++ b/fs/yaffs2/Makefile @@ -0,0 +1,33 @@ +# +# Makefile for the linux YAFFS filesystem routines. +# + +obj-$(CONFIG_YAFFS_FS) += yaffs2multi.o + +# yaffs2-y := yaffs_mtdif_single.o +# yaffs2-y += yaffs_packedtags1.o +# yaffs2-y += yaffs_ecc.o yaffs_vfs_single.o yaffs_guts.o +# yaffs2-y += yaffs_packedtags2.o +# yaffs2-y += yaffs_tagscompat.o +# yaffs2-y += yaffs_tagsmarshall.o +# yaffs2-y += yaffs_checkptrw.o yaffs_nand.o +# yaffs2-y += yaffs_checkptrw.o yaffs_nand.o yaffs_nameval.o +# yaffs2-y += yaffs_allocator.o yaffs_bitmap.o yaffs_attribs.o +# yaffs2-y += yaffs_yaffs1.o +# yaffs2-y += yaffs_yaffs2.o +# yaffs2-y += yaffs_verify.o +# yaffs2-y += yaffs_summary.o + + yaffs2multi-y := yaffs_mtdif_multi.o + yaffs2multi-y += yaffs_packedtags1.o + yaffs2multi-y += yaffs_ecc.o yaffs_vfs_multi.o yaffs_guts.o + yaffs2multi-y += yaffs_packedtags2.o + yaffs2multi-y += yaffs_tagscompat.o + yaffs2multi-y += yaffs_tagsmarshall.o + yaffs2multi-y += yaffs_checkptrw.o yaffs_nand.o + yaffs2multi-y += yaffs_checkptrw.o yaffs_nand.o yaffs_nameval.o + yaffs2multi-y += yaffs_allocator.o yaffs_bitmap.o yaffs_attribs.o + yaffs2multi-y += yaffs_yaffs1.o + yaffs2multi-y += yaffs_yaffs2.o + yaffs2multi-y += yaffs_verify.o + yaffs2multi-y += yaffs_summary.o diff --git a/fs/yaffs2/yaffs_allocator.c b/fs/yaffs2/yaffs_allocator.c new file mode 100644 index 00000000..c8f2861c --- /dev/null +++ b/fs/yaffs2/yaffs_allocator.c @@ -0,0 +1,357 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "yaffs_allocator.h" +#include "yaffs_guts.h" +#include "yaffs_trace.h" +#include "yportenv.h" + +/* + * Each entry in yaffs_tnode_list and yaffs_obj_list hold blocks + * of approx 100 objects that are themn allocated singly. + * This is basically a simplified slab allocator. + * + * We don't use the Linux slab allocator because slab does not allow + * us to dump all the objects in one hit when we do a umount and tear + * down all the tnodes and objects. slab requires that we first free + * the individual objects. + * + * Once yaffs has been mainlined I shall try to motivate for a change + * to slab to provide the extra features we need here. + */ + +struct yaffs_tnode_list { + struct yaffs_tnode_list *next; + struct yaffs_tnode *tnodes; +}; + +struct yaffs_obj_list { + struct yaffs_obj_list *next; + struct yaffs_obj *objects; +}; + +struct yaffs_allocator { + int n_tnodes_created; + struct yaffs_tnode *free_tnodes; + int n_free_tnodes; + struct yaffs_tnode_list *alloc_tnode_list; + + int n_obj_created; + struct list_head free_objs; + int n_free_objects; + + struct yaffs_obj_list *allocated_obj_list; +}; + +static void yaffs_deinit_raw_tnodes(struct yaffs_dev *dev) +{ + struct yaffs_allocator *allocator = + (struct yaffs_allocator *)dev->allocator; + struct yaffs_tnode_list *tmp; + + if (!allocator) { + BUG(); + return; + } + + while (allocator->alloc_tnode_list) { + tmp = allocator->alloc_tnode_list->next; + + kfree(allocator->alloc_tnode_list->tnodes); + kfree(allocator->alloc_tnode_list); + allocator->alloc_tnode_list = tmp; + } + + allocator->free_tnodes = NULL; + allocator->n_free_tnodes = 0; + allocator->n_tnodes_created = 0; +} + +static void yaffs_init_raw_tnodes(struct yaffs_dev *dev) +{ + struct yaffs_allocator *allocator = dev->allocator; + + if (!allocator) { + BUG(); + return; + } + + allocator->alloc_tnode_list = NULL; + allocator->free_tnodes = NULL; + allocator->n_free_tnodes = 0; + allocator->n_tnodes_created = 0; +} + +static int yaffs_create_tnodes(struct yaffs_dev *dev, int n_tnodes) +{ + struct yaffs_allocator *allocator = + (struct yaffs_allocator *)dev->allocator; + int i; + struct yaffs_tnode *new_tnodes; + u8 *mem; + struct yaffs_tnode *curr; + struct yaffs_tnode *next; + struct yaffs_tnode_list *tnl; + + if (!allocator) { + BUG(); + return YAFFS_FAIL; + } + + if (n_tnodes < 1) + return YAFFS_OK; + + /* make these things */ + new_tnodes = kmalloc(n_tnodes * dev->tnode_size, GFP_NOFS); + mem = (u8 *) new_tnodes; + + if (!new_tnodes) { + yaffs_trace(YAFFS_TRACE_ERROR, + "yaffs: Could not allocate Tnodes"); + return YAFFS_FAIL; + } + + /* New hookup for wide tnodes */ + for (i = 0; i < n_tnodes - 1; i++) { + curr = (struct yaffs_tnode *)&mem[i * dev->tnode_size]; + next = (struct yaffs_tnode *)&mem[(i + 1) * dev->tnode_size]; + curr->internal[0] = next; + } + + curr = (struct yaffs_tnode *)&mem[(n_tnodes - 1) * dev->tnode_size]; + curr->internal[0] = allocator->free_tnodes; + allocator->free_tnodes = (struct yaffs_tnode *)mem; + + allocator->n_free_tnodes += n_tnodes; + allocator->n_tnodes_created += n_tnodes; + + /* Now add this bunch of tnodes to a list for freeing up. + * NB If we can't add this to the management list it isn't fatal + * but it just means we can't free this bunch of tnodes later. + */ + tnl = kmalloc(sizeof(struct yaffs_tnode_list), GFP_NOFS); + if (!tnl) { + yaffs_trace(YAFFS_TRACE_ERROR, + "Could not add tnodes to management list"); + return YAFFS_FAIL; + } else { + tnl->tnodes = new_tnodes; + tnl->next = allocator->alloc_tnode_list; + allocator->alloc_tnode_list = tnl; + } + + yaffs_trace(YAFFS_TRACE_ALLOCATE, "Tnodes added"); + + return YAFFS_OK; +} + +struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev) +{ + struct yaffs_allocator *allocator = + (struct yaffs_allocator *)dev->allocator; + struct yaffs_tnode *tn = NULL; + + if (!allocator) { + BUG(); + return NULL; + } + + /* If there are none left make more */ + if (!allocator->free_tnodes) + yaffs_create_tnodes(dev, YAFFS_ALLOCATION_NTNODES); + + if (allocator->free_tnodes) { + tn = allocator->free_tnodes; + allocator->free_tnodes = allocator->free_tnodes->internal[0]; + allocator->n_free_tnodes--; + } + + return tn; +} + +/* FreeTnode frees up a tnode and puts it back on the free list */ +void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) +{ + struct yaffs_allocator *allocator = dev->allocator; + + if (!allocator) { + BUG(); + return; + } + + if (tn) { + tn->internal[0] = allocator->free_tnodes; + allocator->free_tnodes = tn; + allocator->n_free_tnodes++; + } + dev->checkpoint_blocks_required = 0; /* force recalculation */ +} + +/*--------------- yaffs_obj alloaction ------------------------ + * + * Free yaffs_objs are stored in a list using obj->siblings. + * The blocks of allocated objects are stored in a linked list. + */ + +static void yaffs_init_raw_objs(struct yaffs_dev *dev) +{ + struct yaffs_allocator *allocator = dev->allocator; + + if (!allocator) { + BUG(); + return; + } + + allocator->allocated_obj_list = NULL; + INIT_LIST_HEAD(&allocator->free_objs); + allocator->n_free_objects = 0; +} + +static void yaffs_deinit_raw_objs(struct yaffs_dev *dev) +{ + struct yaffs_allocator *allocator = dev->allocator; + struct yaffs_obj_list *tmp; + + if (!allocator) { + BUG(); + return; + } + + while (allocator->allocated_obj_list) { + tmp = allocator->allocated_obj_list->next; + kfree(allocator->allocated_obj_list->objects); + kfree(allocator->allocated_obj_list); + allocator->allocated_obj_list = tmp; + } + + INIT_LIST_HEAD(&allocator->free_objs); + allocator->n_free_objects = 0; + allocator->n_obj_created = 0; +} + +static int yaffs_create_free_objs(struct yaffs_dev *dev, int n_obj) +{ + struct yaffs_allocator *allocator = dev->allocator; + int i; + struct yaffs_obj *new_objs; + struct yaffs_obj_list *list; + + if (!allocator) { + BUG(); + return YAFFS_FAIL; + } + + if (n_obj < 1) + return YAFFS_OK; + + /* make these things */ + new_objs = kmalloc(n_obj * sizeof(struct yaffs_obj), GFP_NOFS); + list = kmalloc(sizeof(struct yaffs_obj_list), GFP_NOFS); + + if (!new_objs || !list) { + kfree(new_objs); + new_objs = NULL; + kfree(list); + list = NULL; + yaffs_trace(YAFFS_TRACE_ALLOCATE, + "Could not allocate more objects"); + return YAFFS_FAIL; + } + + /* Hook them into the free list */ + for (i = 0; i < n_obj; i++) + list_add(&new_objs[i].siblings, &allocator->free_objs); + + allocator->n_free_objects += n_obj; + allocator->n_obj_created += n_obj; + + /* Now add this bunch of Objects to a list for freeing up. */ + + list->objects = new_objs; + list->next = allocator->allocated_obj_list; + allocator->allocated_obj_list = list; + + return YAFFS_OK; +} + +struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev) +{ + struct yaffs_obj *obj = NULL; + struct list_head *lh; + struct yaffs_allocator *allocator = dev->allocator; + + if (!allocator) { + BUG(); + return obj; + } + + /* If there are none left make more */ + if (list_empty(&allocator->free_objs)) + yaffs_create_free_objs(dev, YAFFS_ALLOCATION_NOBJECTS); + + if (!list_empty(&allocator->free_objs)) { + lh = allocator->free_objs.next; + obj = list_entry(lh, struct yaffs_obj, siblings); + list_del_init(lh); + allocator->n_free_objects--; + } + + return obj; +} + +void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj) +{ + + struct yaffs_allocator *allocator = dev->allocator; + + if (!allocator) { + BUG(); + return; + } + + /* Link into the free list. */ + list_add(&obj->siblings, &allocator->free_objs); + allocator->n_free_objects++; +} + +void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev) +{ + + if (!dev->allocator) { + BUG(); + return; + } + + yaffs_deinit_raw_tnodes(dev); + yaffs_deinit_raw_objs(dev); + kfree(dev->allocator); + dev->allocator = NULL; +} + +void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev) +{ + struct yaffs_allocator *allocator; + + if (dev->allocator) { + BUG(); + return; + } + + allocator = kmalloc(sizeof(struct yaffs_allocator), GFP_NOFS); + if (allocator) { + dev->allocator = allocator; + yaffs_init_raw_tnodes(dev); + yaffs_init_raw_objs(dev); + } +} + diff --git a/fs/yaffs2/yaffs_allocator.h b/fs/yaffs2/yaffs_allocator.h new file mode 100644 index 00000000..a8cc3226 --- /dev/null +++ b/fs/yaffs2/yaffs_allocator.h @@ -0,0 +1,30 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_ALLOCATOR_H__ +#define __YAFFS_ALLOCATOR_H__ + +#include "yaffs_guts.h" + +void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev); +void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev); + +struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev); +void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn); + +struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev); +void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj); + +#endif diff --git a/fs/yaffs2/yaffs_attribs.c b/fs/yaffs2/yaffs_attribs.c new file mode 100644 index 00000000..711941f1 --- /dev/null +++ b/fs/yaffs2/yaffs_attribs.c @@ -0,0 +1,132 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "yaffs_guts.h" +#include "yaffs_attribs.h" + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)) +#define IATTR_UID ia_uid +#define IATTR_GID ia_gid +#else +#define IATTR_UID ia_uid.val +#define IATTR_GID ia_gid.val +#endif + +void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh) +{ + obj->yst_uid = oh->yst_uid; + obj->yst_gid = oh->yst_gid; + obj->yst_atime = oh->yst_atime; + obj->yst_mtime = oh->yst_mtime; + obj->yst_ctime = oh->yst_ctime; + obj->yst_rdev = oh->yst_rdev; +} + +void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj) +{ + oh->yst_uid = obj->yst_uid; + oh->yst_gid = obj->yst_gid; + oh->yst_atime = obj->yst_atime; + oh->yst_mtime = obj->yst_mtime; + oh->yst_ctime = obj->yst_ctime; + oh->yst_rdev = obj->yst_rdev; + +} + +void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c) +{ + obj->yst_mtime = Y_CURRENT_TIME; + if (do_a) + obj->yst_atime = obj->yst_mtime; + if (do_c) + obj->yst_ctime = obj->yst_mtime; +} + +void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev) +{ + yaffs_load_current_time(obj, 1, 1); + obj->yst_rdev = rdev; + obj->yst_uid = uid; + obj->yst_gid = gid; +} + +static loff_t yaffs_get_file_size(struct yaffs_obj *obj) +{ + YCHAR *alias = NULL; + obj = yaffs_get_equivalent_obj(obj); + + switch (obj->variant_type) { + case YAFFS_OBJECT_TYPE_FILE: + return obj->variant.file_variant.file_size; + case YAFFS_OBJECT_TYPE_SYMLINK: + alias = obj->variant.symlink_variant.alias; + if (!alias) + return 0; + return strnlen(alias, YAFFS_MAX_ALIAS_LENGTH); + default: + return 0; + } +} + +int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr) +{ + unsigned int valid = attr->ia_valid; + + if (valid & ATTR_MODE) + obj->yst_mode = attr->ia_mode; + if (valid & ATTR_UID) + obj->yst_uid = attr->IATTR_UID; + if (valid & ATTR_GID) + obj->yst_gid = attr->IATTR_GID; + + if (valid & ATTR_ATIME) + obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime); + if (valid & ATTR_CTIME) + obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime); + if (valid & ATTR_MTIME) + obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime); + + if (valid & ATTR_SIZE) + yaffs_resize_file(obj, attr->ia_size); + + yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); + + return YAFFS_OK; + +} + +int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr) +{ + unsigned int valid = 0; + + attr->ia_mode = obj->yst_mode; + valid |= ATTR_MODE; + attr->IATTR_UID = obj->yst_uid; + valid |= ATTR_UID; + attr->IATTR_GID = obj->yst_gid; + valid |= ATTR_GID; + + Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime; + valid |= ATTR_ATIME; + Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime; + valid |= ATTR_CTIME; + Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime; + valid |= ATTR_MTIME; + + attr->ia_size = yaffs_get_file_size(obj); + valid |= ATTR_SIZE; + + attr->ia_valid = valid; + + return YAFFS_OK; +} diff --git a/fs/yaffs2/yaffs_attribs.h b/fs/yaffs2/yaffs_attribs.h new file mode 100644 index 00000000..5b21b085 --- /dev/null +++ b/fs/yaffs2/yaffs_attribs.h @@ -0,0 +1,28 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_ATTRIBS_H__ +#define __YAFFS_ATTRIBS_H__ + +#include "yaffs_guts.h" + +void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh); +void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj); +void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev); +void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c); +int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr); +int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr); + +#endif diff --git a/fs/yaffs2/yaffs_bitmap.c b/fs/yaffs2/yaffs_bitmap.c new file mode 100644 index 00000000..4440e930 --- /dev/null +++ b/fs/yaffs2/yaffs_bitmap.c @@ -0,0 +1,97 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "yaffs_bitmap.h" +#include "yaffs_trace.h" +/* + * Chunk bitmap manipulations + */ + +static inline u8 *yaffs_block_bits(struct yaffs_dev *dev, int blk) +{ + if (blk < dev->internal_start_block || blk > dev->internal_end_block) { + yaffs_trace(YAFFS_TRACE_ERROR, + "BlockBits block %d is not valid", + blk); + BUG(); + } + return dev->chunk_bits + + (dev->chunk_bit_stride * (blk - dev->internal_start_block)); +} + +void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk) +{ + if (blk < dev->internal_start_block || blk > dev->internal_end_block || + chunk < 0 || chunk >= dev->param.chunks_per_block) { + yaffs_trace(YAFFS_TRACE_ERROR, + "Chunk Id (%d:%d) invalid", + blk, chunk); + BUG(); + } +} + +void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk) +{ + u8 *blk_bits = yaffs_block_bits(dev, blk); + + memset(blk_bits, 0, dev->chunk_bit_stride); +} + +void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) +{ + u8 *blk_bits = yaffs_block_bits(dev, blk); + + yaffs_verify_chunk_bit_id(dev, blk, chunk); + blk_bits[chunk / 8] &= ~(1 << (chunk & 7)); +} + +void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) +{ + u8 *blk_bits = yaffs_block_bits(dev, blk); + + yaffs_verify_chunk_bit_id(dev, blk, chunk); + blk_bits[chunk / 8] |= (1 << (chunk & 7)); +} + +int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) +{ + u8 *blk_bits = yaffs_block_bits(dev, blk); + + yaffs_verify_chunk_bit_id(dev, blk, chunk); + return (blk_bits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0; +} + +int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk) +{ + u8 *blk_bits = yaffs_block_bits(dev, blk); + int i; + + for (i = 0; i < dev->chunk_bit_stride; i++) { + if (*blk_bits) + return 1; + blk_bits++; + } + return 0; +} + +int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk) +{ + u8 *blk_bits = yaffs_block_bits(dev, blk); + int i; + int n = 0; + + for (i = 0; i < dev->chunk_bit_stride; i++, blk_bits++) + n += hweight8(*blk_bits); + + return n; +} diff --git a/fs/yaffs2/yaffs_bitmap.h b/fs/yaffs2/yaffs_bitmap.h new file mode 100644 index 00000000..e26b37d8 --- /dev/null +++ b/fs/yaffs2/yaffs_bitmap.h @@ -0,0 +1,33 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +/* + * Chunk bitmap manipulations + */ + +#ifndef __YAFFS_BITMAP_H__ +#define __YAFFS_BITMAP_H__ + +#include "yaffs_guts.h" + +void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk); +void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk); +void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); +void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); +int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); +int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk); +int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk); + +#endif diff --git a/fs/yaffs2/yaffs_checkptrw.c b/fs/yaffs2/yaffs_checkptrw.c new file mode 100644 index 00000000..16ee1e06 --- /dev/null +++ b/fs/yaffs2/yaffs_checkptrw.c @@ -0,0 +1,466 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "yaffs_checkptrw.h" +#include "yaffs_getblockinfo.h" + +struct yaffs_checkpt_chunk_hdr { + int version; + int seq; + u32 sum; + u32 xor; +} ; + + +static int apply_chunk_offset(struct yaffs_dev *dev, int chunk) +{ + return chunk - dev->chunk_offset; +} + +static int apply_block_offset(struct yaffs_dev *dev, int block) +{ + return block - dev->block_offset; +} + +static void yaffs2_checkpt_init_chunk_hdr(struct yaffs_dev *dev) +{ + struct yaffs_checkpt_chunk_hdr hdr; + + hdr.version = YAFFS_CHECKPOINT_VERSION; + hdr.seq = dev->checkpt_page_seq; + hdr.sum = dev->checkpt_sum; + hdr.xor = dev->checkpt_xor; + + dev->checkpt_byte_offs = sizeof(hdr); + + memcpy(dev->checkpt_buffer, &hdr, sizeof(hdr)); +} + +static int yaffs2_checkpt_check_chunk_hdr(struct yaffs_dev *dev) +{ + struct yaffs_checkpt_chunk_hdr hdr; + + memcpy(&hdr, dev->checkpt_buffer, sizeof(hdr)); + + dev->checkpt_byte_offs = sizeof(hdr); + + return hdr.version == YAFFS_CHECKPOINT_VERSION && + hdr.seq == dev->checkpt_page_seq && + hdr.sum == dev->checkpt_sum && + hdr.xor == dev->checkpt_xor; +} + +static int yaffs2_checkpt_space_ok(struct yaffs_dev *dev) +{ + int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; + + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "checkpt blocks_avail = %d", blocks_avail); + + return (blocks_avail <= 0) ? 0 : 1; +} + +static int yaffs_checkpt_erase(struct yaffs_dev *dev) +{ + int i; + + if (!dev->drv.drv_erase_fn) + return 0; + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "checking blocks %d to %d", + dev->internal_start_block, dev->internal_end_block); + + for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { + struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); + int offset_i = apply_block_offset(dev, i); + int result; + + if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "erasing checkpt block %d", i); + + dev->n_erasures++; + + result = dev->drv.drv_erase_fn(dev, offset_i); + if(result) { + bi->block_state = YAFFS_BLOCK_STATE_EMPTY; + dev->n_erased_blocks++; + dev->n_free_chunks += + dev->param.chunks_per_block; + } else { + dev->drv.drv_mark_bad_fn(dev, offset_i); + bi->block_state = YAFFS_BLOCK_STATE_DEAD; + } + } + } + + dev->blocks_in_checkpt = 0; + + return 1; +} + +static void yaffs2_checkpt_find_erased_block(struct yaffs_dev *dev) +{ + int i; + int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; + + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "allocating checkpt block: erased %d reserved %d avail %d next %d ", + dev->n_erased_blocks, dev->param.n_reserved_blocks, + blocks_avail, dev->checkpt_next_block); + + if (dev->checkpt_next_block >= 0 && + dev->checkpt_next_block <= dev->internal_end_block && + blocks_avail > 0) { + + for (i = dev->checkpt_next_block; i <= dev->internal_end_block; + i++) { + struct yaffs_block_info *bi; + + bi = yaffs_get_block_info(dev, i); + if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { + dev->checkpt_next_block = i + 1; + dev->checkpt_cur_block = i; + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "allocating checkpt block %d", i); + return; + } + } + } + yaffs_trace(YAFFS_TRACE_CHECKPOINT, "out of checkpt blocks"); + + dev->checkpt_next_block = -1; + dev->checkpt_cur_block = -1; +} + +static void yaffs2_checkpt_find_block(struct yaffs_dev *dev) +{ + int i; + struct yaffs_ext_tags tags; + + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "find next checkpt block: start: blocks %d next %d", + dev->blocks_in_checkpt, dev->checkpt_next_block); + + if (dev->blocks_in_checkpt < dev->checkpt_max_blocks) + for (i = dev->checkpt_next_block; i <= dev->internal_end_block; + i++) { + int chunk = i * dev->param.chunks_per_block; + enum yaffs_block_state state; + u32 seq; + + dev->tagger.read_chunk_tags_fn(dev, + apply_chunk_offset(dev, chunk), + NULL, &tags); + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "find next checkpt block: search: block %d state %d oid %d seq %d eccr %d", + i, (int) state, + tags.obj_id, tags.seq_number, + tags.ecc_result); + + if (tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) + continue; + + dev->tagger.query_block_fn(dev, + apply_block_offset(dev, i), + &state, &seq); + if (state == YAFFS_BLOCK_STATE_DEAD) + continue; + + /* Right kind of block */ + dev->checkpt_next_block = tags.obj_id; + dev->checkpt_cur_block = i; + dev->checkpt_block_list[dev->blocks_in_checkpt] = i; + dev->blocks_in_checkpt++; + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "found checkpt block %d", i); + return; + } + + yaffs_trace(YAFFS_TRACE_CHECKPOINT, "found no more checkpt blocks"); + + dev->checkpt_next_block = -1; + dev->checkpt_cur_block = -1; +} + +int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing) +{ + int i; + + dev->checkpt_open_write = writing; + + /* Got the functions we need? */ + if (!dev->tagger.write_chunk_tags_fn || + !dev->tagger.read_chunk_tags_fn || + !dev->drv.drv_erase_fn || + !dev->drv.drv_mark_bad_fn) + return 0; + + if (writing && !yaffs2_checkpt_space_ok(dev)) + return 0; + + if (!dev->checkpt_buffer) + dev->checkpt_buffer = + kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); + if (!dev->checkpt_buffer) + return 0; + + dev->checkpt_page_seq = 0; + dev->checkpt_byte_count = 0; + dev->checkpt_sum = 0; + dev->checkpt_xor = 0; + dev->checkpt_cur_block = -1; + dev->checkpt_cur_chunk = -1; + dev->checkpt_next_block = dev->internal_start_block; + + if (writing) { + memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); + yaffs2_checkpt_init_chunk_hdr(dev); + return yaffs_checkpt_erase(dev); + } + + /* Opening for a read */ + /* Set to a value that will kick off a read */ + dev->checkpt_byte_offs = dev->data_bytes_per_chunk; + /* A checkpoint block list of 1 checkpoint block per 16 block is + * (hopefully) going to be way more than we need */ + dev->blocks_in_checkpt = 0; + dev->checkpt_max_blocks = + (dev->internal_end_block - dev->internal_start_block) / 16 + 2; + if (!dev->checkpt_block_list) + dev->checkpt_block_list = + kmalloc(sizeof(int) * dev->checkpt_max_blocks, GFP_NOFS); + + if (!dev->checkpt_block_list) + return 0; + + for (i = 0; i < dev->checkpt_max_blocks; i++) + dev->checkpt_block_list[i] = -1; + + return 1; +} + +int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum) +{ + u32 composite_sum; + + composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xff); + *sum = composite_sum; + return 1; +} + +static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev) +{ + int chunk; + int offset_chunk; + struct yaffs_ext_tags tags; + + if (dev->checkpt_cur_block < 0) { + yaffs2_checkpt_find_erased_block(dev); + dev->checkpt_cur_chunk = 0; + } + + if (dev->checkpt_cur_block < 0) + return 0; + + tags.is_deleted = 0; + tags.obj_id = dev->checkpt_next_block; /* Hint to next place to look */ + tags.chunk_id = dev->checkpt_page_seq + 1; + tags.seq_number = YAFFS_SEQUENCE_CHECKPOINT_DATA; + tags.n_bytes = dev->data_bytes_per_chunk; + if (dev->checkpt_cur_chunk == 0) { + /* First chunk we write for the block? Set block state to + checkpoint */ + struct yaffs_block_info *bi = + yaffs_get_block_info(dev, dev->checkpt_cur_block); + bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; + dev->blocks_in_checkpt++; + } + + chunk = + dev->checkpt_cur_block * dev->param.chunks_per_block + + dev->checkpt_cur_chunk; + + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "checkpoint wite buffer nand %d(%d:%d) objid %d chId %d", + chunk, dev->checkpt_cur_block, dev->checkpt_cur_chunk, + tags.obj_id, tags.chunk_id); + + offset_chunk = apply_chunk_offset(dev, chunk); + + dev->n_page_writes++; + + dev->tagger.write_chunk_tags_fn(dev, offset_chunk, + dev->checkpt_buffer, &tags); + dev->checkpt_page_seq++; + dev->checkpt_cur_chunk++; + if (dev->checkpt_cur_chunk >= dev->param.chunks_per_block) { + dev->checkpt_cur_chunk = 0; + dev->checkpt_cur_block = -1; + } + memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); + + yaffs2_checkpt_init_chunk_hdr(dev); + + + return 1; +} + +int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes) +{ + int i = 0; + int ok = 1; + u8 *data_bytes = (u8 *) data; + + if (!dev->checkpt_buffer) + return 0; + + if (!dev->checkpt_open_write) + return -1; + + while (i < n_bytes && ok) { + dev->checkpt_buffer[dev->checkpt_byte_offs] = *data_bytes; + dev->checkpt_sum += *data_bytes; + dev->checkpt_xor ^= *data_bytes; + + dev->checkpt_byte_offs++; + i++; + data_bytes++; + dev->checkpt_byte_count++; + + if (dev->checkpt_byte_offs < 0 || + dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) + ok = yaffs2_checkpt_flush_buffer(dev); + } + + return i; +} + +int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes) +{ + int i = 0; + struct yaffs_ext_tags tags; + int chunk; + int offset_chunk; + u8 *data_bytes = (u8 *) data; + + if (!dev->checkpt_buffer) + return 0; + + if (dev->checkpt_open_write) + return -1; + + while (i < n_bytes) { + + if (dev->checkpt_byte_offs < 0 || + dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) { + + if (dev->checkpt_cur_block < 0) { + yaffs2_checkpt_find_block(dev); + dev->checkpt_cur_chunk = 0; + } + + /* Bail out if we can't find a checpoint block */ + if (dev->checkpt_cur_block < 0) + break; + + chunk = dev->checkpt_cur_block * + dev->param.chunks_per_block + + dev->checkpt_cur_chunk; + + offset_chunk = apply_chunk_offset(dev, chunk); + dev->n_page_reads++; + + /* Read in the next chunk */ + dev->tagger.read_chunk_tags_fn(dev, + offset_chunk, + dev->checkpt_buffer, + &tags); + + /* Bail out if the chunk is corrupted. */ + if (tags.chunk_id != (dev->checkpt_page_seq + 1) || + tags.ecc_result > YAFFS_ECC_RESULT_FIXED || + tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) + break; + + /* Bail out if it is not a checkpoint chunk. */ + if(!yaffs2_checkpt_check_chunk_hdr(dev)) + break; + + dev->checkpt_page_seq++; + dev->checkpt_cur_chunk++; + + if (dev->checkpt_cur_chunk >= + dev->param.chunks_per_block) + dev->checkpt_cur_block = -1; + + } + + *data_bytes = dev->checkpt_buffer[dev->checkpt_byte_offs]; + dev->checkpt_sum += *data_bytes; + dev->checkpt_xor ^= *data_bytes; + dev->checkpt_byte_offs++; + i++; + data_bytes++; + dev->checkpt_byte_count++; + } + + return i; /* Number of bytes read */ +} + +int yaffs_checkpt_close(struct yaffs_dev *dev) +{ + int i; + + if (dev->checkpt_open_write) { + if (dev->checkpt_byte_offs != + sizeof(sizeof(struct yaffs_checkpt_chunk_hdr))) + yaffs2_checkpt_flush_buffer(dev); + } else if (dev->checkpt_block_list) { + for (i = 0; + i < dev->blocks_in_checkpt && + dev->checkpt_block_list[i] >= 0; i++) { + int blk = dev->checkpt_block_list[i]; + struct yaffs_block_info *bi = NULL; + + if (dev->internal_start_block <= blk && + blk <= dev->internal_end_block) + bi = yaffs_get_block_info(dev, blk); + if (bi && bi->block_state == YAFFS_BLOCK_STATE_EMPTY) + bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; + } + } + + dev->n_free_chunks -= + dev->blocks_in_checkpt * dev->param.chunks_per_block; + dev->n_erased_blocks -= dev->blocks_in_checkpt; + + yaffs_trace(YAFFS_TRACE_CHECKPOINT, "checkpoint byte count %d", + dev->checkpt_byte_count); + + if (dev->checkpt_buffer) + return 1; + else + return 0; +} + +int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev) +{ + /* Erase the checkpoint data */ + + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "checkpoint invalidate of %d blocks", + dev->blocks_in_checkpt); + + return yaffs_checkpt_erase(dev); +} diff --git a/fs/yaffs2/yaffs_checkptrw.h b/fs/yaffs2/yaffs_checkptrw.h new file mode 100644 index 00000000..cdbaba71 --- /dev/null +++ b/fs/yaffs2/yaffs_checkptrw.h @@ -0,0 +1,33 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_CHECKPTRW_H__ +#define __YAFFS_CHECKPTRW_H__ + +#include "yaffs_guts.h" + +int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing); + +int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes); + +int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes); + +int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum); + +int yaffs_checkpt_close(struct yaffs_dev *dev); + +int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev); + +#endif diff --git a/fs/yaffs2/yaffs_ecc.c b/fs/yaffs2/yaffs_ecc.c new file mode 100644 index 00000000..9294107c --- /dev/null +++ b/fs/yaffs2/yaffs_ecc.c @@ -0,0 +1,281 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * This code implements the ECC algorithm used in SmartMedia. + * + * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. + * The two unused bit are set to 1. + * The ECC can correct single bit errors in a 256-byte page of data. Thus, two + * such ECC blocks are used on a 512-byte NAND page. + * + */ + +#include "yportenv.h" + +#include "yaffs_ecc.h" + +/* Table generated by gen-ecc.c + * Using a table means we do not have to calculate p1..p4 and p1'..p4' + * for each byte of data. These are instead provided in a table in bits7..2. + * Bit 0 of each entry indicates whether the entry has an odd or even parity, + * and therefore this bytes influence on the line parity. + */ + +static const unsigned char column_parity_table[] = { + 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, + 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, + 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, + 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, + 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, + 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, + 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, + 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, + 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, + 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, + 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, + 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, + 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, + 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, + 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, + 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, + 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, + 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, + 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, + 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, + 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, + 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, + 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, + 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, + 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, + 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, + 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, + 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, + 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, + 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, + 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, + 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, +}; + + +/* Calculate the ECC for a 256-byte block of data */ +void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc) +{ + unsigned int i; + unsigned char col_parity = 0; + unsigned char line_parity = 0; + unsigned char line_parity_prime = 0; + unsigned char t; + unsigned char b; + + for (i = 0; i < 256; i++) { + b = column_parity_table[*data++]; + col_parity ^= b; + + if (b & 0x01) { /* odd number of bits in the byte */ + line_parity ^= i; + line_parity_prime ^= ~i; + } + } + + ecc[2] = (~col_parity) | 0x03; + + t = 0; + if (line_parity & 0x80) + t |= 0x80; + if (line_parity_prime & 0x80) + t |= 0x40; + if (line_parity & 0x40) + t |= 0x20; + if (line_parity_prime & 0x40) + t |= 0x10; + if (line_parity & 0x20) + t |= 0x08; + if (line_parity_prime & 0x20) + t |= 0x04; + if (line_parity & 0x10) + t |= 0x02; + if (line_parity_prime & 0x10) + t |= 0x01; + ecc[1] = ~t; + + t = 0; + if (line_parity & 0x08) + t |= 0x80; + if (line_parity_prime & 0x08) + t |= 0x40; + if (line_parity & 0x04) + t |= 0x20; + if (line_parity_prime & 0x04) + t |= 0x10; + if (line_parity & 0x02) + t |= 0x08; + if (line_parity_prime & 0x02) + t |= 0x04; + if (line_parity & 0x01) + t |= 0x02; + if (line_parity_prime & 0x01) + t |= 0x01; + ecc[0] = ~t; + +} + +/* Correct the ECC on a 256 byte block of data */ + +int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, + const unsigned char *test_ecc) +{ + unsigned char d0, d1, d2; /* deltas */ + + d0 = read_ecc[0] ^ test_ecc[0]; + d1 = read_ecc[1] ^ test_ecc[1]; + d2 = read_ecc[2] ^ test_ecc[2]; + + if ((d0 | d1 | d2) == 0) + return 0; /* no error */ + + if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 && + ((d1 ^ (d1 >> 1)) & 0x55) == 0x55 && + ((d2 ^ (d2 >> 1)) & 0x54) == 0x54) { + /* Single bit (recoverable) error in data */ + + unsigned byte; + unsigned bit; + + bit = byte = 0; + + if (d1 & 0x80) + byte |= 0x80; + if (d1 & 0x20) + byte |= 0x40; + if (d1 & 0x08) + byte |= 0x20; + if (d1 & 0x02) + byte |= 0x10; + if (d0 & 0x80) + byte |= 0x08; + if (d0 & 0x20) + byte |= 0x04; + if (d0 & 0x08) + byte |= 0x02; + if (d0 & 0x02) + byte |= 0x01; + + if (d2 & 0x80) + bit |= 0x04; + if (d2 & 0x20) + bit |= 0x02; + if (d2 & 0x08) + bit |= 0x01; + + data[byte] ^= (1 << bit); + + return 1; /* Corrected the error */ + } + + if ((hweight8(d0) + hweight8(d1) + hweight8(d2)) == 1) { + /* Reccoverable error in ecc */ + + read_ecc[0] = test_ecc[0]; + read_ecc[1] = test_ecc[1]; + read_ecc[2] = test_ecc[2]; + + return 1; /* Corrected the error */ + } + + /* Unrecoverable error */ + + return -1; + +} + +/* + * ECCxxxOther does ECC calcs on arbitrary n bytes of data + */ +void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, + struct yaffs_ecc_other *ecc_other) +{ + unsigned int i; + unsigned char col_parity = 0; + unsigned line_parity = 0; + unsigned line_parity_prime = 0; + unsigned char b; + + for (i = 0; i < n_bytes; i++) { + b = column_parity_table[*data++]; + col_parity ^= b; + + if (b & 0x01) { + /* odd number of bits in the byte */ + line_parity ^= i; + line_parity_prime ^= ~i; + } + + } + + ecc_other->col_parity = (col_parity >> 2) & 0x3f; + ecc_other->line_parity = line_parity; + ecc_other->line_parity_prime = line_parity_prime; +} + +int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, + struct yaffs_ecc_other *read_ecc, + const struct yaffs_ecc_other *test_ecc) +{ + unsigned char delta_col; /* column parity delta */ + unsigned delta_line; /* line parity delta */ + unsigned delta_line_prime; /* line parity delta */ + unsigned bit; + + delta_col = read_ecc->col_parity ^ test_ecc->col_parity; + delta_line = read_ecc->line_parity ^ test_ecc->line_parity; + delta_line_prime = + read_ecc->line_parity_prime ^ test_ecc->line_parity_prime; + + if ((delta_col | delta_line | delta_line_prime) == 0) + return 0; /* no error */ + + if (delta_line == ~delta_line_prime && + (((delta_col ^ (delta_col >> 1)) & 0x15) == 0x15)) { + /* Single bit (recoverable) error in data */ + + bit = 0; + + if (delta_col & 0x20) + bit |= 0x04; + if (delta_col & 0x08) + bit |= 0x02; + if (delta_col & 0x02) + bit |= 0x01; + + if (delta_line >= n_bytes) + return -1; + + data[delta_line] ^= (1 << bit); + + return 1; /* corrected */ + } + + if ((hweight32(delta_line) + + hweight32(delta_line_prime) + + hweight8(delta_col)) == 1) { + /* Reccoverable error in ecc */ + + *read_ecc = *test_ecc; + return 1; /* corrected */ + } + + /* Unrecoverable error */ + + return -1; +} diff --git a/fs/yaffs2/yaffs_ecc.h b/fs/yaffs2/yaffs_ecc.h new file mode 100644 index 00000000..17d47bd8 --- /dev/null +++ b/fs/yaffs2/yaffs_ecc.h @@ -0,0 +1,44 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +/* + * This code implements the ECC algorithm used in SmartMedia. + * + * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. + * The two unused bit are set to 1. + * The ECC can correct single bit errors in a 256-byte page of data. + * Thus, two such ECC blocks are used on a 512-byte NAND page. + * + */ + +#ifndef __YAFFS_ECC_H__ +#define __YAFFS_ECC_H__ + +struct yaffs_ecc_other { + unsigned char col_parity; + unsigned line_parity; + unsigned line_parity_prime; +}; + +void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc); +int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, + const unsigned char *test_ecc); + +void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, + struct yaffs_ecc_other *ecc); +int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, + struct yaffs_ecc_other *read_ecc, + const struct yaffs_ecc_other *test_ecc); +#endif diff --git a/fs/yaffs2/yaffs_getblockinfo.h b/fs/yaffs2/yaffs_getblockinfo.h new file mode 100644 index 00000000..8fd0802b --- /dev/null +++ b/fs/yaffs2/yaffs_getblockinfo.h @@ -0,0 +1,35 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_GETBLOCKINFO_H__ +#define __YAFFS_GETBLOCKINFO_H__ + +#include "yaffs_guts.h" +#include "yaffs_trace.h" + +/* Function to manipulate block info */ +static inline struct yaffs_block_info *yaffs_get_block_info(struct yaffs_dev + *dev, int blk) +{ + if (blk < dev->internal_start_block || blk > dev->internal_end_block) { + yaffs_trace(YAFFS_TRACE_ERROR, + "**>> yaffs: get_block_info block %d is not valid", + blk); + BUG(); + } + return &dev->block_info[blk - dev->internal_start_block]; +} + +#endif diff --git a/fs/yaffs2/yaffs_guts.c b/fs/yaffs2/yaffs_guts.c new file mode 100644 index 00000000..bf58eca1 --- /dev/null +++ b/fs/yaffs2/yaffs_guts.c @@ -0,0 +1,5152 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "yportenv.h" +#include "yaffs_trace.h" + +#include "yaffs_guts.h" +#include "yaffs_getblockinfo.h" +#include "yaffs_tagscompat.h" +#include "yaffs_tagsmarshall.h" +#include "yaffs_nand.h" +#include "yaffs_yaffs1.h" +#include "yaffs_yaffs2.h" +#include "yaffs_bitmap.h" +#include "yaffs_verify.h" +#include "yaffs_nand.h" +#include "yaffs_packedtags2.h" +#include "yaffs_nameval.h" +#include "yaffs_allocator.h" +#include "yaffs_attribs.h" +#include "yaffs_summary.h" + +/* Note YAFFS_GC_GOOD_ENOUGH must be <= YAFFS_GC_PASSIVE_THRESHOLD */ +#define YAFFS_GC_GOOD_ENOUGH 2 +#define YAFFS_GC_PASSIVE_THRESHOLD 4 + +#include "yaffs_ecc.h" + +/* Forward declarations */ + +static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, + const u8 *buffer, int n_bytes, int use_reserve); + +static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, + int buffer_size); + +/* Function to calculate chunk and offset */ + +void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, + int *chunk_out, u32 *offset_out) +{ + int chunk; + u32 offset; + + chunk = (u32) (addr >> dev->chunk_shift); + + if (dev->chunk_div == 1) { + /* easy power of 2 case */ + offset = (u32) (addr & dev->chunk_mask); + } else { + /* Non power-of-2 case */ + + loff_t chunk_base; + + chunk /= dev->chunk_div; + + chunk_base = ((loff_t) chunk) * dev->data_bytes_per_chunk; + offset = (u32) (addr - chunk_base); + } + + *chunk_out = chunk; + *offset_out = offset; +} + +/* Function to return the number of shifts for a power of 2 greater than or + * equal to the given number + * Note we don't try to cater for all possible numbers and this does not have to + * be hellishly efficient. + */ + +static inline u32 calc_shifts_ceiling(u32 x) +{ + int extra_bits; + int shifts; + + shifts = extra_bits = 0; + + while (x > 1) { + if (x & 1) + extra_bits++; + x >>= 1; + shifts++; + } + + if (extra_bits) + shifts++; + + return shifts; +} + +/* Function to return the number of shifts to get a 1 in bit 0 + */ + +static inline u32 calc_shifts(u32 x) +{ + u32 shifts; + + shifts = 0; + + if (!x) + return 0; + + while (!(x & 1)) { + x >>= 1; + shifts++; + } + + return shifts; +} + +/* + * Temporary buffer manipulations. + */ + +static int yaffs_init_tmp_buffers(struct yaffs_dev *dev) +{ + int i; + u8 *buf = (u8 *) 1; + + memset(dev->temp_buffer, 0, sizeof(dev->temp_buffer)); + + for (i = 0; buf && i < YAFFS_N_TEMP_BUFFERS; i++) { + dev->temp_buffer[i].in_use = 0; + buf = kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); + dev->temp_buffer[i].buffer = buf; + } + + return buf ? YAFFS_OK : YAFFS_FAIL; +} + +u8 *yaffs_get_temp_buffer(struct yaffs_dev * dev) +{ + int i; + + dev->temp_in_use++; + if (dev->temp_in_use > dev->max_temp) + dev->max_temp = dev->temp_in_use; + + for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { + if (dev->temp_buffer[i].in_use == 0) { + dev->temp_buffer[i].in_use = 1; + return dev->temp_buffer[i].buffer; + } + } + + yaffs_trace(YAFFS_TRACE_BUFFERS, "Out of temp buffers"); + /* + * If we got here then we have to allocate an unmanaged one + * This is not good. + */ + + dev->unmanaged_buffer_allocs++; + return kmalloc(dev->data_bytes_per_chunk, GFP_NOFS); + +} + +void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer) +{ + int i; + + dev->temp_in_use--; + + for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { + if (dev->temp_buffer[i].buffer == buffer) { + dev->temp_buffer[i].in_use = 0; + return; + } + } + + if (buffer) { + /* assume it is an unmanaged one. */ + yaffs_trace(YAFFS_TRACE_BUFFERS, + "Releasing unmanaged temp buffer"); + kfree(buffer); + dev->unmanaged_buffer_deallocs++; + } + +} + +/* + * Functions for robustisizing TODO + * + */ + +static void yaffs_handle_chunk_wr_ok(struct yaffs_dev *dev, int nand_chunk, + const u8 *data, + const struct yaffs_ext_tags *tags) +{ + (void) dev; + (void) nand_chunk; + (void) data; + (void) tags; +} + +static void yaffs_handle_chunk_update(struct yaffs_dev *dev, int nand_chunk, + const struct yaffs_ext_tags *tags) +{ + (void) dev; + (void) nand_chunk; + (void) tags; +} + +void yaffs_handle_chunk_error(struct yaffs_dev *dev, + struct yaffs_block_info *bi) +{ + if (!bi->gc_prioritise) { + bi->gc_prioritise = 1; + dev->has_pending_prioritised_gc = 1; + bi->chunk_error_strikes++; + + if (bi->chunk_error_strikes > 3) { + bi->needs_retiring = 1; /* Too many stikes, so retire */ + yaffs_trace(YAFFS_TRACE_ALWAYS, + "yaffs: Block struck out"); + + } + } +} + +static void yaffs_handle_chunk_wr_error(struct yaffs_dev *dev, int nand_chunk, + int erased_ok) +{ + int flash_block = nand_chunk / dev->param.chunks_per_block; + struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); + + yaffs_handle_chunk_error(dev, bi); + + if (erased_ok) { + /* Was an actual write failure, + * so mark the block for retirement.*/ + bi->needs_retiring = 1; + yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, + "**>> Block %d needs retiring", flash_block); + } + + /* Delete the chunk */ + yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); + yaffs_skip_rest_of_block(dev); +} + +/* + * Verification code + */ + +/* + * Simple hash function. Needs to have a reasonable spread + */ + +static inline int yaffs_hash_fn(int n) +{ + if (n < 0) + n = -n; + return n % YAFFS_NOBJECT_BUCKETS; +} + +/* + * Access functions to useful fake objects. + * Note that root might have a presence in NAND if permissions are set. + */ + +struct yaffs_obj *yaffs_root(struct yaffs_dev *dev) +{ + return dev->root_dir; +} + +struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev) +{ + return dev->lost_n_found; +} + +/* + * Erased NAND checking functions + */ + +int yaffs_check_ff(u8 *buffer, int n_bytes) +{ + /* Horrible, slow implementation */ + while (n_bytes--) { + if (*buffer != 0xff) + return 0; + buffer++; + } + return 1; +} + +static int yaffs_check_chunk_erased(struct yaffs_dev *dev, int nand_chunk) +{ + int retval = YAFFS_OK; + u8 *data = yaffs_get_temp_buffer(dev); + struct yaffs_ext_tags tags; + int result; + + result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, data, &tags); + + if (tags.ecc_result > YAFFS_ECC_RESULT_NO_ERROR) + retval = YAFFS_FAIL; + + if (!yaffs_check_ff(data, dev->data_bytes_per_chunk) || + tags.chunk_used) { + yaffs_trace(YAFFS_TRACE_NANDACCESS, + "Chunk %d not erased", nand_chunk); + retval = YAFFS_FAIL; + } + + yaffs_release_temp_buffer(dev, data); + + return retval; + +} + +static int yaffs_verify_chunk_written(struct yaffs_dev *dev, + int nand_chunk, + const u8 *data, + struct yaffs_ext_tags *tags) +{ + int retval = YAFFS_OK; + struct yaffs_ext_tags temp_tags; + u8 *buffer = yaffs_get_temp_buffer(dev); + int result; + + result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, buffer, &temp_tags); + if (memcmp(buffer, data, dev->data_bytes_per_chunk) || + temp_tags.obj_id != tags->obj_id || + temp_tags.chunk_id != tags->chunk_id || + temp_tags.n_bytes != tags->n_bytes) + retval = YAFFS_FAIL; + + yaffs_release_temp_buffer(dev, buffer); + + return retval; +} + + +int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks) +{ + int reserved_chunks; + int reserved_blocks = dev->param.n_reserved_blocks; + int checkpt_blocks; + + checkpt_blocks = yaffs_calc_checkpt_blocks_required(dev); + + reserved_chunks = + (reserved_blocks + checkpt_blocks) * dev->param.chunks_per_block; + + return (dev->n_free_chunks > (reserved_chunks + n_chunks)); +} + +static int yaffs_find_alloc_block(struct yaffs_dev *dev) +{ + int i; + struct yaffs_block_info *bi; + + if (dev->n_erased_blocks < 1) { + /* Hoosterman we've got a problem. + * Can't get space to gc + */ + yaffs_trace(YAFFS_TRACE_ERROR, + "yaffs tragedy: no more erased blocks"); + + return -1; + } + + /* Find an empty block. */ + + for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { + dev->alloc_block_finder++; + if (dev->alloc_block_finder < dev->internal_start_block + || dev->alloc_block_finder > dev->internal_end_block) { + dev->alloc_block_finder = dev->internal_start_block; + } + + bi = yaffs_get_block_info(dev, dev->alloc_block_finder); + + if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { + bi->block_state = YAFFS_BLOCK_STATE_ALLOCATING; + dev->seq_number++; + bi->seq_number = dev->seq_number; + dev->n_erased_blocks--; + yaffs_trace(YAFFS_TRACE_ALLOCATE, + "Allocated block %d, seq %d, %d left" , + dev->alloc_block_finder, dev->seq_number, + dev->n_erased_blocks); + return dev->alloc_block_finder; + } + } + + yaffs_trace(YAFFS_TRACE_ALWAYS, + "yaffs tragedy: no more erased blocks, but there should have been %d", + dev->n_erased_blocks); + + return -1; +} + +static int yaffs_alloc_chunk(struct yaffs_dev *dev, int use_reserver, + struct yaffs_block_info **block_ptr) +{ + int ret_val; + struct yaffs_block_info *bi; + + if (dev->alloc_block < 0) { + /* Get next block to allocate off */ + dev->alloc_block = yaffs_find_alloc_block(dev); + dev->alloc_page = 0; + } + + if (!use_reserver && !yaffs_check_alloc_available(dev, 1)) { + /* No space unless we're allowed to use the reserve. */ + return -1; + } + + if (dev->n_erased_blocks < dev->param.n_reserved_blocks + && dev->alloc_page == 0) + yaffs_trace(YAFFS_TRACE_ALLOCATE, "Allocating reserve"); + + /* Next page please.... */ + if (dev->alloc_block >= 0) { + bi = yaffs_get_block_info(dev, dev->alloc_block); + + ret_val = (dev->alloc_block * dev->param.chunks_per_block) + + dev->alloc_page; + bi->pages_in_use++; + yaffs_set_chunk_bit(dev, dev->alloc_block, dev->alloc_page); + + dev->alloc_page++; + + dev->n_free_chunks--; + + /* If the block is full set the state to full */ + if (dev->alloc_page >= dev->param.chunks_per_block) { + bi->block_state = YAFFS_BLOCK_STATE_FULL; + dev->alloc_block = -1; + } + + if (block_ptr) + *block_ptr = bi; + + return ret_val; + } + + yaffs_trace(YAFFS_TRACE_ERROR, + "!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!"); + + return -1; +} + +static int yaffs_get_erased_chunks(struct yaffs_dev *dev) +{ + int n; + + n = dev->n_erased_blocks * dev->param.chunks_per_block; + + if (dev->alloc_block > 0) + n += (dev->param.chunks_per_block - dev->alloc_page); + + return n; + +} + +/* + * yaffs_skip_rest_of_block() skips over the rest of the allocation block + * if we don't want to write to it. + */ +void yaffs_skip_rest_of_block(struct yaffs_dev *dev) +{ + struct yaffs_block_info *bi; + + if (dev->alloc_block > 0) { + bi = yaffs_get_block_info(dev, dev->alloc_block); + if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { + bi->block_state = YAFFS_BLOCK_STATE_FULL; + dev->alloc_block = -1; + } + } +} + +static int yaffs_write_new_chunk(struct yaffs_dev *dev, + const u8 *data, + struct yaffs_ext_tags *tags, int use_reserver) +{ + int attempts = 0; + int write_ok = 0; + int chunk; + + yaffs2_checkpt_invalidate(dev); + + do { + struct yaffs_block_info *bi = 0; + int erased_ok = 0; + + chunk = yaffs_alloc_chunk(dev, use_reserver, &bi); + if (chunk < 0) { + /* no space */ + break; + } + + /* First check this chunk is erased, if it needs + * checking. The checking policy (unless forced + * always on) is as follows: + * + * Check the first page we try to write in a block. + * If the check passes then we don't need to check any + * more. If the check fails, we check again... + * If the block has been erased, we don't need to check. + * + * However, if the block has been prioritised for gc, + * then we think there might be something odd about + * this block and stop using it. + * + * Rationale: We should only ever see chunks that have + * not been erased if there was a partially written + * chunk due to power loss. This checking policy should + * catch that case with very few checks and thus save a + * lot of checks that are most likely not needed. + * + * Mods to the above + * If an erase check fails or the write fails we skip the + * rest of the block. + */ + + /* let's give it a try */ + attempts++; + + if (dev->param.always_check_erased) + bi->skip_erased_check = 0; + + if (!bi->skip_erased_check) { + erased_ok = yaffs_check_chunk_erased(dev, chunk); + if (erased_ok != YAFFS_OK) { + yaffs_trace(YAFFS_TRACE_ERROR, + "**>> yaffs chunk %d was not erased", + chunk); + + /* If not erased, delete this one, + * skip rest of block and + * try another chunk */ + yaffs_chunk_del(dev, chunk, 1, __LINE__); + yaffs_skip_rest_of_block(dev); + continue; + } + } + + write_ok = yaffs_wr_chunk_tags_nand(dev, chunk, data, tags); + + if (!bi->skip_erased_check) + write_ok = + yaffs_verify_chunk_written(dev, chunk, data, tags); + + if (write_ok != YAFFS_OK) { + /* Clean up aborted write, skip to next block and + * try another chunk */ + yaffs_handle_chunk_wr_error(dev, chunk, erased_ok); + continue; + } + + bi->skip_erased_check = 1; + + /* Copy the data into the robustification buffer */ + yaffs_handle_chunk_wr_ok(dev, chunk, data, tags); + + } while (write_ok != YAFFS_OK && + (yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts)); + + if (!write_ok) + chunk = -1; + + if (attempts > 1) { + yaffs_trace(YAFFS_TRACE_ERROR, + "**>> yaffs write required %d attempts", + attempts); + dev->n_retried_writes += (attempts - 1); + } + + return chunk; +} + +/* + * Block retiring for handling a broken block. + */ + +static void yaffs_retire_block(struct yaffs_dev *dev, int flash_block) +{ + struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); + + yaffs2_checkpt_invalidate(dev); + + yaffs2_clear_oldest_dirty_seq(dev, bi); + + if (yaffs_mark_bad(dev, flash_block) != YAFFS_OK) { + if (yaffs_erase_block(dev, flash_block) != YAFFS_OK) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "yaffs: Failed to mark bad and erase block %d", + flash_block); + } else { + struct yaffs_ext_tags tags; + int chunk_id = + flash_block * dev->param.chunks_per_block; + + u8 *buffer = yaffs_get_temp_buffer(dev); + + memset(buffer, 0xff, dev->data_bytes_per_chunk); + memset(&tags, 0, sizeof(tags)); + tags.seq_number = YAFFS_SEQUENCE_BAD_BLOCK; + if (dev->tagger.write_chunk_tags_fn(dev, chunk_id - + dev->chunk_offset, + buffer, + &tags) != YAFFS_OK) + yaffs_trace(YAFFS_TRACE_ALWAYS, + "yaffs: Failed to write bad block marker to block %d", + flash_block); + + yaffs_release_temp_buffer(dev, buffer); + } + } + + bi->block_state = YAFFS_BLOCK_STATE_DEAD; + bi->gc_prioritise = 0; + bi->needs_retiring = 0; + + dev->n_retired_blocks++; +} + +/*---------------- Name handling functions ------------*/ + +static void yaffs_load_name_from_oh(struct yaffs_dev *dev, YCHAR *name, + const YCHAR *oh_name, int buff_size) +{ +#ifdef CONFIG_YAFFS_AUTO_UNICODE + if (dev->param.auto_unicode) { + if (*oh_name) { + /* It is an ASCII name, do an ASCII to + * unicode conversion */ + const char *ascii_oh_name = (const char *)oh_name; + int n = buff_size - 1; + while (n > 0 && *ascii_oh_name) { + *name = *ascii_oh_name; + name++; + ascii_oh_name++; + n--; + } + } else { + strncpy(name, oh_name + 1, buff_size - 1); + } + } else { +#else + (void) dev; + { +#endif + strncpy(name, oh_name, buff_size - 1); + } +} + +static void yaffs_load_oh_from_name(struct yaffs_dev *dev, YCHAR *oh_name, + const YCHAR *name) +{ +#ifdef CONFIG_YAFFS_AUTO_UNICODE + + int is_ascii; + const YCHAR *w; + + if (dev->param.auto_unicode) { + + is_ascii = 1; + w = name; + + /* Figure out if the name will fit in ascii character set */ + while (is_ascii && *w) { + if ((*w) & 0xff00) + is_ascii = 0; + w++; + } + + if (is_ascii) { + /* It is an ASCII name, so convert unicode to ascii */ + char *ascii_oh_name = (char *)oh_name; + int n = YAFFS_MAX_NAME_LENGTH - 1; + while (n > 0 && *name) { + *ascii_oh_name = *name; + name++; + ascii_oh_name++; + n--; + } + } else { + /* Unicode name, so save starting at the second YCHAR */ + *oh_name = 0; + strncpy(oh_name + 1, name, YAFFS_MAX_NAME_LENGTH - 2); + } + } else { +#else + dev = dev; + { +#endif + strncpy(oh_name, name, YAFFS_MAX_NAME_LENGTH - 1); + } +} + +static u16 yaffs_calc_name_sum(const YCHAR *name) +{ + u16 sum = 0; + u16 i = 1; + + if (!name) + return 0; + + while ((*name) && i < (YAFFS_MAX_NAME_LENGTH / 2)) { + + /* 0x1f mask is case insensitive */ + sum += ((*name) & 0x1f) * i; + i++; + name++; + } + return sum; +} + + +void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR * name) +{ + memset(obj->short_name, 0, sizeof(obj->short_name)); + + if (name && !name[0]) { + yaffs_fix_null_name(obj, obj->short_name, + YAFFS_SHORT_NAME_LENGTH); + name = obj->short_name; + } else if (name && + strnlen(name, YAFFS_SHORT_NAME_LENGTH + 1) <= + YAFFS_SHORT_NAME_LENGTH) { + strcpy(obj->short_name, name); + } + + obj->sum = yaffs_calc_name_sum(name); +} + +void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, + const struct yaffs_obj_hdr *oh) +{ +#ifdef CONFIG_YAFFS_AUTO_UNICODE + YCHAR tmp_name[YAFFS_MAX_NAME_LENGTH + 1]; + memset(tmp_name, 0, sizeof(tmp_name)); + yaffs_load_name_from_oh(obj->my_dev, tmp_name, oh->name, + YAFFS_MAX_NAME_LENGTH + 1); + yaffs_set_obj_name(obj, tmp_name); +#else + yaffs_set_obj_name(obj, oh->name); +#endif +} + +loff_t yaffs_max_file_size(struct yaffs_dev *dev) +{ + if(sizeof(loff_t) < 8) + return YAFFS_MAX_FILE_SIZE_32; + else + return ((loff_t) YAFFS_MAX_CHUNK_ID) * dev->data_bytes_per_chunk; +} + +/*-------------------- TNODES ------------------- + + * List of spare tnodes + * The list is hooked together using the first pointer + * in the tnode. + */ + +struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev) +{ + struct yaffs_tnode *tn = yaffs_alloc_raw_tnode(dev); + + if (tn) { + memset(tn, 0, dev->tnode_size); + dev->n_tnodes++; + } + + dev->checkpoint_blocks_required = 0; /* force recalculation */ + + return tn; +} + +/* FreeTnode frees up a tnode and puts it back on the free list */ +static void yaffs_free_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) +{ + yaffs_free_raw_tnode(dev, tn); + dev->n_tnodes--; + dev->checkpoint_blocks_required = 0; /* force recalculation */ +} + +static void yaffs_deinit_tnodes_and_objs(struct yaffs_dev *dev) +{ + yaffs_deinit_raw_tnodes_and_objs(dev); + dev->n_obj = 0; + dev->n_tnodes = 0; +} + +static void yaffs_load_tnode_0(struct yaffs_dev *dev, struct yaffs_tnode *tn, + unsigned pos, unsigned val) +{ + u32 *map = (u32 *) tn; + u32 bit_in_map; + u32 bit_in_word; + u32 word_in_map; + u32 mask; + + pos &= YAFFS_TNODES_LEVEL0_MASK; + val >>= dev->chunk_grp_bits; + + bit_in_map = pos * dev->tnode_width; + word_in_map = bit_in_map / 32; + bit_in_word = bit_in_map & (32 - 1); + + mask = dev->tnode_mask << bit_in_word; + + map[word_in_map] &= ~mask; + map[word_in_map] |= (mask & (val << bit_in_word)); + + if (dev->tnode_width > (32 - bit_in_word)) { + bit_in_word = (32 - bit_in_word); + word_in_map++; + mask = + dev->tnode_mask >> bit_in_word; + map[word_in_map] &= ~mask; + map[word_in_map] |= (mask & (val >> bit_in_word)); + } +} + +u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, + unsigned pos) +{ + u32 *map = (u32 *) tn; + u32 bit_in_map; + u32 bit_in_word; + u32 word_in_map; + u32 val; + + pos &= YAFFS_TNODES_LEVEL0_MASK; + + bit_in_map = pos * dev->tnode_width; + word_in_map = bit_in_map / 32; + bit_in_word = bit_in_map & (32 - 1); + + val = map[word_in_map] >> bit_in_word; + + if (dev->tnode_width > (32 - bit_in_word)) { + bit_in_word = (32 - bit_in_word); + word_in_map++; + val |= (map[word_in_map] << bit_in_word); + } + + val &= dev->tnode_mask; + val <<= dev->chunk_grp_bits; + + return val; +} + +/* ------------------- End of individual tnode manipulation -----------------*/ + +/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------ + * The look up tree is represented by the top tnode and the number of top_level + * in the tree. 0 means only the level 0 tnode is in the tree. + */ + +/* FindLevel0Tnode finds the level 0 tnode, if one exists. */ +struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, + struct yaffs_file_var *file_struct, + u32 chunk_id) +{ + struct yaffs_tnode *tn = file_struct->top; + u32 i; + int required_depth; + int level = file_struct->top_level; + + (void) dev; + + /* Check sane level and chunk Id */ + if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL) + return NULL; + + if (chunk_id > YAFFS_MAX_CHUNK_ID) + return NULL; + + /* First check we're tall enough (ie enough top_level) */ + + i = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; + required_depth = 0; + while (i) { + i >>= YAFFS_TNODES_INTERNAL_BITS; + required_depth++; + } + + if (required_depth > file_struct->top_level) + return NULL; /* Not tall enough, so we can't find it */ + + /* Traverse down to level 0 */ + while (level > 0 && tn) { + tn = tn->internal[(chunk_id >> + (YAFFS_TNODES_LEVEL0_BITS + + (level - 1) * + YAFFS_TNODES_INTERNAL_BITS)) & + YAFFS_TNODES_INTERNAL_MASK]; + level--; + } + + return tn; +} + +/* add_find_tnode_0 finds the level 0 tnode if it exists, + * otherwise first expands the tree. + * This happens in two steps: + * 1. If the tree isn't tall enough, then make it taller. + * 2. Scan down the tree towards the level 0 tnode adding tnodes if required. + * + * Used when modifying the tree. + * + * If the tn argument is NULL, then a fresh tnode will be added otherwise the + * specified tn will be plugged into the ttree. + */ + +struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, + struct yaffs_file_var *file_struct, + u32 chunk_id, + struct yaffs_tnode *passed_tn) +{ + int required_depth; + int i; + int l; + struct yaffs_tnode *tn; + u32 x; + + /* Check sane level and page Id */ + if (file_struct->top_level < 0 || + file_struct->top_level > YAFFS_TNODES_MAX_LEVEL) + return NULL; + + if (chunk_id > YAFFS_MAX_CHUNK_ID) + return NULL; + + /* First check we're tall enough (ie enough top_level) */ + + x = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; + required_depth = 0; + while (x) { + x >>= YAFFS_TNODES_INTERNAL_BITS; + required_depth++; + } + + if (required_depth > file_struct->top_level) { + /* Not tall enough, gotta make the tree taller */ + for (i = file_struct->top_level; i < required_depth; i++) { + + tn = yaffs_get_tnode(dev); + + if (tn) { + tn->internal[0] = file_struct->top; + file_struct->top = tn; + file_struct->top_level++; + } else { + yaffs_trace(YAFFS_TRACE_ERROR, + "yaffs: no more tnodes"); + return NULL; + } + } + } + + /* Traverse down to level 0, adding anything we need */ + + l = file_struct->top_level; + tn = file_struct->top; + + if (l > 0) { + while (l > 0 && tn) { + x = (chunk_id >> + (YAFFS_TNODES_LEVEL0_BITS + + (l - 1) * YAFFS_TNODES_INTERNAL_BITS)) & + YAFFS_TNODES_INTERNAL_MASK; + + if ((l > 1) && !tn->internal[x]) { + /* Add missing non-level-zero tnode */ + tn->internal[x] = yaffs_get_tnode(dev); + if (!tn->internal[x]) + return NULL; + } else if (l == 1) { + /* Looking from level 1 at level 0 */ + if (passed_tn) { + /* If we already have one, release it */ + if (tn->internal[x]) + yaffs_free_tnode(dev, + tn->internal[x]); + tn->internal[x] = passed_tn; + + } else if (!tn->internal[x]) { + /* Don't have one, none passed in */ + tn->internal[x] = yaffs_get_tnode(dev); + if (!tn->internal[x]) + return NULL; + } + } + + tn = tn->internal[x]; + l--; + } + } else { + /* top is level 0 */ + if (passed_tn) { + memcpy(tn, passed_tn, + (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8); + yaffs_free_tnode(dev, passed_tn); + } + } + + return tn; +} + +static int yaffs_tags_match(const struct yaffs_ext_tags *tags, int obj_id, + int chunk_obj) +{ + return (tags->chunk_id == chunk_obj && + tags->obj_id == obj_id && + !tags->is_deleted) ? 1 : 0; + +} + +static int yaffs_find_chunk_in_group(struct yaffs_dev *dev, int the_chunk, + struct yaffs_ext_tags *tags, int obj_id, + int inode_chunk) +{ + int j; + + for (j = 0; the_chunk && j < dev->chunk_grp_size; j++) { + if (yaffs_check_chunk_bit + (dev, the_chunk / dev->param.chunks_per_block, + the_chunk % dev->param.chunks_per_block)) { + + if (dev->chunk_grp_size == 1) + return the_chunk; + else { + yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, + tags); + if (yaffs_tags_match(tags, + obj_id, inode_chunk)) { + /* found it; */ + return the_chunk; + } + } + } + the_chunk++; + } + return -1; +} + +int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, + struct yaffs_ext_tags *tags) +{ + /*Get the Tnode, then get the level 0 offset chunk offset */ + struct yaffs_tnode *tn; + int the_chunk = -1; + struct yaffs_ext_tags local_tags; + int ret_val = -1; + struct yaffs_dev *dev = in->my_dev; + + if (!tags) { + /* Passed a NULL, so use our own tags space */ + tags = &local_tags; + } + + tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); + + if (!tn) + return ret_val; + + the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); + + ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, + inode_chunk); + return ret_val; +} + +static int yaffs_find_del_file_chunk(struct yaffs_obj *in, int inode_chunk, + struct yaffs_ext_tags *tags) +{ + /* Get the Tnode, then get the level 0 offset chunk offset */ + struct yaffs_tnode *tn; + int the_chunk = -1; + struct yaffs_ext_tags local_tags; + struct yaffs_dev *dev = in->my_dev; + int ret_val = -1; + + if (!tags) { + /* Passed a NULL, so use our own tags space */ + tags = &local_tags; + } + + tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); + + if (!tn) + return ret_val; + + the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); + + ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, + inode_chunk); + + /* Delete the entry in the filestructure (if found) */ + if (ret_val != -1) + yaffs_load_tnode_0(dev, tn, inode_chunk, 0); + + return ret_val; +} + +int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, + int nand_chunk, int in_scan) +{ + /* NB in_scan is zero unless scanning. + * For forward scanning, in_scan is > 0; + * for backward scanning in_scan is < 0 + * + * nand_chunk = 0 is a dummy insert to make sure the tnodes are there. + */ + + struct yaffs_tnode *tn; + struct yaffs_dev *dev = in->my_dev; + int existing_cunk; + struct yaffs_ext_tags existing_tags; + struct yaffs_ext_tags new_tags; + unsigned existing_serial, new_serial; + + if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) { + /* Just ignore an attempt at putting a chunk into a non-file + * during scanning. + * If it is not during Scanning then something went wrong! + */ + if (!in_scan) { + yaffs_trace(YAFFS_TRACE_ERROR, + "yaffs tragedy:attempt to put data chunk into a non-file" + ); + BUG(); + } + + yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); + return YAFFS_OK; + } + + tn = yaffs_add_find_tnode_0(dev, + &in->variant.file_variant, + inode_chunk, NULL); + if (!tn) + return YAFFS_FAIL; + + if (!nand_chunk) + /* Dummy insert, bail now */ + return YAFFS_OK; + + existing_cunk = yaffs_get_group_base(dev, tn, inode_chunk); + + if (in_scan != 0) { + /* If we're scanning then we need to test for duplicates + * NB This does not need to be efficient since it should only + * happen when the power fails during a write, then only one + * chunk should ever be affected. + * + * Correction for YAFFS2: This could happen quite a lot and we + * need to think about efficiency! TODO + * Update: For backward scanning we don't need to re-read tags + * so this is quite cheap. + */ + + if (existing_cunk > 0) { + /* NB Right now existing chunk will not be real + * chunk_id if the chunk group size > 1 + * thus we have to do a FindChunkInFile to get the + * real chunk id. + * + * We have a duplicate now we need to decide which + * one to use: + * + * Backwards scanning YAFFS2: The old one is what + * we use, dump the new one. + * YAFFS1: Get both sets of tags and compare serial + * numbers. + */ + + if (in_scan > 0) { + /* Only do this for forward scanning */ + yaffs_rd_chunk_tags_nand(dev, + nand_chunk, + NULL, &new_tags); + + /* Do a proper find */ + existing_cunk = + yaffs_find_chunk_in_file(in, inode_chunk, + &existing_tags); + } + + if (existing_cunk <= 0) { + /*Hoosterman - how did this happen? */ + + yaffs_trace(YAFFS_TRACE_ERROR, + "yaffs tragedy: existing chunk < 0 in scan" + ); + + } + + /* NB The deleted flags should be false, otherwise + * the chunks will not be loaded during a scan + */ + + if (in_scan > 0) { + new_serial = new_tags.serial_number; + existing_serial = existing_tags.serial_number; + } + + if ((in_scan > 0) && + (existing_cunk <= 0 || + ((existing_serial + 1) & 3) == new_serial)) { + /* Forward scanning. + * Use new + * Delete the old one and drop through to + * update the tnode + */ + yaffs_chunk_del(dev, existing_cunk, 1, + __LINE__); + } else { + /* Backward scanning or we want to use the + * existing one + * Delete the new one and return early so that + * the tnode isn't changed + */ + yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); + return YAFFS_OK; + } + } + + } + + if (existing_cunk == 0) + in->n_data_chunks++; + + yaffs_load_tnode_0(dev, tn, inode_chunk, nand_chunk); + + return YAFFS_OK; +} + +static void yaffs_soft_del_chunk(struct yaffs_dev *dev, int chunk) +{ + struct yaffs_block_info *the_block; + unsigned block_no; + + yaffs_trace(YAFFS_TRACE_DELETION, "soft delete chunk %d", chunk); + + block_no = chunk / dev->param.chunks_per_block; + the_block = yaffs_get_block_info(dev, block_no); + if (the_block) { + the_block->soft_del_pages++; + dev->n_free_chunks++; + yaffs2_update_oldest_dirty_seq(dev, block_no, the_block); + } +} + +/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all + * the chunks in the file. + * All soft deleting does is increment the block's softdelete count and pulls + * the chunk out of the tnode. + * Thus, essentially this is the same as DeleteWorker except that the chunks + * are soft deleted. + */ + +static int yaffs_soft_del_worker(struct yaffs_obj *in, struct yaffs_tnode *tn, + u32 level, int chunk_offset) +{ + int i; + int the_chunk; + int all_done = 1; + struct yaffs_dev *dev = in->my_dev; + + if (!tn) + return 1; + + if (level > 0) { + for (i = YAFFS_NTNODES_INTERNAL - 1; + all_done && i >= 0; + i--) { + if (tn->internal[i]) { + all_done = + yaffs_soft_del_worker(in, + tn->internal[i], + level - 1, + (chunk_offset << + YAFFS_TNODES_INTERNAL_BITS) + + i); + if (all_done) { + yaffs_free_tnode(dev, + tn->internal[i]); + tn->internal[i] = NULL; + } else { + /* Can this happen? */ + } + } + } + return (all_done) ? 1 : 0; + } + + /* level 0 */ + for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) { + the_chunk = yaffs_get_group_base(dev, tn, i); + if (the_chunk) { + yaffs_soft_del_chunk(dev, the_chunk); + yaffs_load_tnode_0(dev, tn, i, 0); + } + } + return 1; +} + +static void yaffs_remove_obj_from_dir(struct yaffs_obj *obj) +{ + struct yaffs_dev *dev = obj->my_dev; + struct yaffs_obj *parent; + + yaffs_verify_obj_in_dir(obj); + parent = obj->parent; + + yaffs_verify_dir(parent); + + if (dev && dev->param.remove_obj_fn) + dev->param.remove_obj_fn(obj); + + list_del_init(&obj->siblings); + obj->parent = NULL; + + yaffs_verify_dir(parent); +} + +void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj) +{ + if (!directory) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "tragedy: Trying to add an object to a null pointer directory" + ); + BUG(); + return; + } + if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "tragedy: Trying to add an object to a non-directory" + ); + BUG(); + } + + if (obj->siblings.prev == NULL) { + /* Not initialised */ + BUG(); + } + + yaffs_verify_dir(directory); + + yaffs_remove_obj_from_dir(obj); + + /* Now add it */ + list_add(&obj->siblings, &directory->variant.dir_variant.children); + obj->parent = directory; + + if (directory == obj->my_dev->unlinked_dir + || directory == obj->my_dev->del_dir) { + obj->unlinked = 1; + obj->my_dev->n_unlinked_files++; + obj->rename_allowed = 0; + } + + yaffs_verify_dir(directory); + yaffs_verify_obj_in_dir(obj); +} + +static int yaffs_change_obj_name(struct yaffs_obj *obj, + struct yaffs_obj *new_dir, + const YCHAR *new_name, int force, int shadows) +{ + int unlink_op; + int del_op; + struct yaffs_obj *existing_target; + + if (new_dir == NULL) + new_dir = obj->parent; /* use the old directory */ + + if (new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "tragedy: yaffs_change_obj_name: new_dir is not a directory" + ); + BUG(); + } + + unlink_op = (new_dir == obj->my_dev->unlinked_dir); + del_op = (new_dir == obj->my_dev->del_dir); + + existing_target = yaffs_find_by_name(new_dir, new_name); + + /* If the object is a file going into the unlinked directory, + * then it is OK to just stuff it in since duplicate names are OK. + * else only proceed if the new name does not exist and we're putting + * it into a directory. + */ + if (!(unlink_op || del_op || force || + shadows > 0 || !existing_target) || + new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) + return YAFFS_FAIL; + + yaffs_set_obj_name(obj, new_name); + obj->dirty = 1; + yaffs_add_obj_to_dir(new_dir, obj); + + if (unlink_op) + obj->unlinked = 1; + + /* If it is a deletion then we mark it as a shrink for gc */ + if (yaffs_update_oh(obj, new_name, 0, del_op, shadows, NULL) >= 0) + return YAFFS_OK; + + return YAFFS_FAIL; +} + +/*------------------------ Short Operations Cache ------------------------------ + * In many situations where there is no high level buffering a lot of + * reads might be short sequential reads, and a lot of writes may be short + * sequential writes. eg. scanning/writing a jpeg file. + * In these cases, a short read/write cache can provide a huge perfomance + * benefit with dumb-as-a-rock code. + * In Linux, the page cache provides read buffering and the short op cache + * provides write buffering. + * + * There are a small number (~10) of cache chunks per device so that we don't + * need a very intelligent search. + */ + +static int yaffs_obj_cache_dirty(struct yaffs_obj *obj) +{ + struct yaffs_dev *dev = obj->my_dev; + int i; + struct yaffs_cache *cache; + int n_caches = obj->my_dev->param.n_caches; + + for (i = 0; i < n_caches; i++) { + cache = &dev->cache[i]; + if (cache->object == obj && cache->dirty) + return 1; + } + + return 0; +} + +static void yaffs_flush_single_cache(struct yaffs_cache *cache, int discard) +{ + + if (!cache || cache->locked) + return; + + /* Write it out and free it up if need be.*/ + if (cache->dirty) { + yaffs_wr_data_obj(cache->object, + cache->chunk_id, + cache->data, + cache->n_bytes, + 1); + + cache->dirty = 0; + } + + if (discard) + cache->object = NULL; +} + +static void yaffs_flush_file_cache(struct yaffs_obj *obj, int discard) +{ + struct yaffs_dev *dev = obj->my_dev; + int i; + struct yaffs_cache *cache; + int n_caches = obj->my_dev->param.n_caches; + + if (n_caches < 1) + return; + + + /* Find the chunks for this object and flush them. */ + for (i = 0; i < n_caches; i++) { + cache = &dev->cache[i]; + if (cache->object == obj) + yaffs_flush_single_cache(cache, discard); + } + +} + + +void yaffs_flush_whole_cache(struct yaffs_dev *dev, int discard) +{ + struct yaffs_obj *obj; + int n_caches = dev->param.n_caches; + int i; + + /* Find a dirty object in the cache and flush it... + * until there are no further dirty objects. + */ + do { + obj = NULL; + for (i = 0; i < n_caches && !obj; i++) { + if (dev->cache[i].object && dev->cache[i].dirty) + obj = dev->cache[i].object; + } + if (obj) + yaffs_flush_file_cache(obj, discard); + } while (obj); + +} + +/* Grab us an unused cache chunk for use. + * First look for an empty one. + * Then look for the least recently used non-dirty one. + * Then look for the least recently used dirty one...., flush and look again. + */ +static struct yaffs_cache *yaffs_grab_chunk_worker(struct yaffs_dev *dev) +{ + int i; + + if (dev->param.n_caches > 0) { + for (i = 0; i < dev->param.n_caches; i++) { + if (!dev->cache[i].object) + return &dev->cache[i]; + } + } + + return NULL; +} + +static struct yaffs_cache *yaffs_grab_chunk_cache(struct yaffs_dev *dev) +{ + struct yaffs_cache *cache; + int usage; + int i; + + if (dev->param.n_caches < 1) + return NULL; + + /* First look for an unused cache */ + + cache = yaffs_grab_chunk_worker(dev); + + if (cache) + return cache; + + /* + * Thery were all in use. + * Find the LRU cache and flush it if it is dirty. + */ + + usage = -1; + cache = NULL; + + for (i = 0; i < dev->param.n_caches; i++) { + if (dev->cache[i].object && + !dev->cache[i].locked && + (dev->cache[i].last_use < usage || !cache)) { + usage = dev->cache[i].last_use; + cache = &dev->cache[i]; + } + } + +#if 1 + yaffs_flush_single_cache(cache, 1); +#else + yaffs_flush_file_cache(cache->object, 1); + cache = yaffs_grab_chunk_worker(dev); +#endif + + return cache; +} + +/* Find a cached chunk */ +static struct yaffs_cache *yaffs_find_chunk_cache(const struct yaffs_obj *obj, + int chunk_id) +{ + struct yaffs_dev *dev = obj->my_dev; + int i; + + if (dev->param.n_caches < 1) + return NULL; + + for (i = 0; i < dev->param.n_caches; i++) { + if (dev->cache[i].object == obj && + dev->cache[i].chunk_id == chunk_id) { + dev->cache_hits++; + + return &dev->cache[i]; + } + } + return NULL; +} + +/* Mark the chunk for the least recently used algorithym */ +static void yaffs_use_cache(struct yaffs_dev *dev, struct yaffs_cache *cache, + int is_write) +{ + int i; + + if (dev->param.n_caches < 1) + return; + + if (dev->cache_last_use < 0 || + dev->cache_last_use > 100000000) { + /* Reset the cache usages */ + for (i = 1; i < dev->param.n_caches; i++) + dev->cache[i].last_use = 0; + + dev->cache_last_use = 0; + } + dev->cache_last_use++; + cache->last_use = dev->cache_last_use; + + if (is_write) + cache->dirty = 1; +} + +/* Invalidate a single cache page. + * Do this when a whole page gets written, + * ie the short cache for this page is no longer valid. + */ +static void yaffs_invalidate_chunk_cache(struct yaffs_obj *object, int chunk_id) +{ + struct yaffs_cache *cache; + + if (object->my_dev->param.n_caches > 0) { + cache = yaffs_find_chunk_cache(object, chunk_id); + + if (cache) + cache->object = NULL; + } +} + +/* Invalidate all the cache pages associated with this object + * Do this whenever ther file is deleted or resized. + */ +static void yaffs_invalidate_whole_cache(struct yaffs_obj *in) +{ + int i; + struct yaffs_dev *dev = in->my_dev; + + if (dev->param.n_caches > 0) { + /* Invalidate it. */ + for (i = 0; i < dev->param.n_caches; i++) { + if (dev->cache[i].object == in) + dev->cache[i].object = NULL; + } + } +} + +static void yaffs_unhash_obj(struct yaffs_obj *obj) +{ + int bucket; + struct yaffs_dev *dev = obj->my_dev; + + /* If it is still linked into the bucket list, free from the list */ + if (!list_empty(&obj->hash_link)) { + list_del_init(&obj->hash_link); + bucket = yaffs_hash_fn(obj->obj_id); + dev->obj_bucket[bucket].count--; + } +} + +/* FreeObject frees up a Object and puts it back on the free list */ +static void yaffs_free_obj(struct yaffs_obj *obj) +{ + struct yaffs_dev *dev; + + if (!obj) { + BUG(); + return; + } + dev = obj->my_dev; + yaffs_trace(YAFFS_TRACE_OS, "FreeObject %p inode %p", + obj, obj->my_inode); + if (obj->parent) + BUG(); + if (!list_empty(&obj->siblings)) + BUG(); + + if (obj->my_inode) { + /* We're still hooked up to a cached inode. + * Don't delete now, but mark for later deletion + */ + obj->defered_free = 1; + return; + } + + yaffs_unhash_obj(obj); + + yaffs_free_raw_obj(dev, obj); + dev->n_obj--; + dev->checkpoint_blocks_required = 0; /* force recalculation */ +} + +void yaffs_handle_defered_free(struct yaffs_obj *obj) +{ + if (obj->defered_free) + yaffs_free_obj(obj); +} + +static int yaffs_generic_obj_del(struct yaffs_obj *in) +{ + /* Iinvalidate the file's data in the cache, without flushing. */ + yaffs_invalidate_whole_cache(in); + + if (in->my_dev->param.is_yaffs2 && in->parent != in->my_dev->del_dir) { + /* Move to unlinked directory so we have a deletion record */ + yaffs_change_obj_name(in, in->my_dev->del_dir, _Y("deleted"), 0, + 0); + } + + yaffs_remove_obj_from_dir(in); + yaffs_chunk_del(in->my_dev, in->hdr_chunk, 1, __LINE__); + in->hdr_chunk = 0; + + yaffs_free_obj(in); + return YAFFS_OK; + +} + +static void yaffs_soft_del_file(struct yaffs_obj *obj) +{ + if (!obj->deleted || + obj->variant_type != YAFFS_OBJECT_TYPE_FILE || + obj->soft_del) + return; + + if (obj->n_data_chunks <= 0) { + /* Empty file with no duplicate object headers, + * just delete it immediately */ + yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); + obj->variant.file_variant.top = NULL; + yaffs_trace(YAFFS_TRACE_TRACING, + "yaffs: Deleting empty file %d", + obj->obj_id); + yaffs_generic_obj_del(obj); + } else { + yaffs_soft_del_worker(obj, + obj->variant.file_variant.top, + obj->variant. + file_variant.top_level, 0); + obj->soft_del = 1; + } +} + +/* Pruning removes any part of the file structure tree that is beyond the + * bounds of the file (ie that does not point to chunks). + * + * A file should only get pruned when its size is reduced. + * + * Before pruning, the chunks must be pulled from the tree and the + * level 0 tnode entries must be zeroed out. + * Could also use this for file deletion, but that's probably better handled + * by a special case. + * + * This function is recursive. For levels > 0 the function is called again on + * any sub-tree. For level == 0 we just check if the sub-tree has data. + * If there is no data in a subtree then it is pruned. + */ + +static struct yaffs_tnode *yaffs_prune_worker(struct yaffs_dev *dev, + struct yaffs_tnode *tn, u32 level, + int del0) +{ + int i; + int has_data; + + if (!tn) + return tn; + + has_data = 0; + + if (level > 0) { + for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) { + if (tn->internal[i]) { + tn->internal[i] = + yaffs_prune_worker(dev, + tn->internal[i], + level - 1, + (i == 0) ? del0 : 1); + } + + if (tn->internal[i]) + has_data++; + } + } else { + int tnode_size_u32 = dev->tnode_size / sizeof(u32); + u32 *map = (u32 *) tn; + + for (i = 0; !has_data && i < tnode_size_u32; i++) { + if (map[i]) + has_data++; + } + } + + if (has_data == 0 && del0) { + /* Free and return NULL */ + yaffs_free_tnode(dev, tn); + tn = NULL; + } + return tn; +} + +static int yaffs_prune_tree(struct yaffs_dev *dev, + struct yaffs_file_var *file_struct) +{ + int i; + int has_data; + int done = 0; + struct yaffs_tnode *tn; + + if (file_struct->top_level < 1) + return YAFFS_OK; + + file_struct->top = + yaffs_prune_worker(dev, file_struct->top, file_struct->top_level, 0); + + /* Now we have a tree with all the non-zero branches NULL but + * the height is the same as it was. + * Let's see if we can trim internal tnodes to shorten the tree. + * We can do this if only the 0th element in the tnode is in use + * (ie all the non-zero are NULL) + */ + + while (file_struct->top_level && !done) { + tn = file_struct->top; + + has_data = 0; + for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) { + if (tn->internal[i]) + has_data++; + } + + if (!has_data) { + file_struct->top = tn->internal[0]; + file_struct->top_level--; + yaffs_free_tnode(dev, tn); + } else { + done = 1; + } + } + + return YAFFS_OK; +} + +/*-------------------- End of File Structure functions.-------------------*/ + +/* alloc_empty_obj gets us a clean Object.*/ +static struct yaffs_obj *yaffs_alloc_empty_obj(struct yaffs_dev *dev) +{ + struct yaffs_obj *obj = yaffs_alloc_raw_obj(dev); + + if (!obj) + return obj; + + dev->n_obj++; + + /* Now sweeten it up... */ + + memset(obj, 0, sizeof(struct yaffs_obj)); + obj->being_created = 1; + + obj->my_dev = dev; + obj->hdr_chunk = 0; + obj->variant_type = YAFFS_OBJECT_TYPE_UNKNOWN; + INIT_LIST_HEAD(&(obj->hard_links)); + INIT_LIST_HEAD(&(obj->hash_link)); + INIT_LIST_HEAD(&obj->siblings); + + /* Now make the directory sane */ + if (dev->root_dir) { + obj->parent = dev->root_dir; + list_add(&(obj->siblings), + &dev->root_dir->variant.dir_variant.children); + } + + /* Add it to the lost and found directory. + * NB Can't put root or lost-n-found in lost-n-found so + * check if lost-n-found exists first + */ + if (dev->lost_n_found) + yaffs_add_obj_to_dir(dev->lost_n_found, obj); + + obj->being_created = 0; + + dev->checkpoint_blocks_required = 0; /* force recalculation */ + + return obj; +} + +static int yaffs_find_nice_bucket(struct yaffs_dev *dev) +{ + int i; + int l = 999; + int lowest = 999999; + + /* Search for the shortest list or one that + * isn't too long. + */ + + for (i = 0; i < 10 && lowest > 4; i++) { + dev->bucket_finder++; + dev->bucket_finder %= YAFFS_NOBJECT_BUCKETS; + if (dev->obj_bucket[dev->bucket_finder].count < lowest) { + lowest = dev->obj_bucket[dev->bucket_finder].count; + l = dev->bucket_finder; + } + } + + return l; +} + +static int yaffs_new_obj_id(struct yaffs_dev *dev) +{ + int bucket = yaffs_find_nice_bucket(dev); + int found = 0; + struct list_head *i; + u32 n = (u32) bucket; + + /* + * Now find an object value that has not already been taken + * by scanning the list, incrementing each time by number of buckets. + */ + while (!found) { + found = 1; + n += YAFFS_NOBJECT_BUCKETS; + list_for_each(i, &dev->obj_bucket[bucket].list) { + /* Check if this value is already taken. */ + if (i && list_entry(i, struct yaffs_obj, + hash_link)->obj_id == n) + found = 0; + } + } + return n; +} + +static void yaffs_hash_obj(struct yaffs_obj *in) +{ + int bucket = yaffs_hash_fn(in->obj_id); + struct yaffs_dev *dev = in->my_dev; + + list_add(&in->hash_link, &dev->obj_bucket[bucket].list); + dev->obj_bucket[bucket].count++; +} + +struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number) +{ + int bucket = yaffs_hash_fn(number); + struct list_head *i; + struct yaffs_obj *in; + + list_for_each(i, &dev->obj_bucket[bucket].list) { + /* Look if it is in the list */ + in = list_entry(i, struct yaffs_obj, hash_link); + if (in->obj_id == number) { + /* Don't show if it is defered free */ + if (in->defered_free) + return NULL; + return in; + } + } + + return NULL; +} + +static struct yaffs_obj *yaffs_new_obj(struct yaffs_dev *dev, int number, + enum yaffs_obj_type type) +{ + struct yaffs_obj *the_obj = NULL; + struct yaffs_tnode *tn = NULL; + + if (number < 0) + number = yaffs_new_obj_id(dev); + + if (type == YAFFS_OBJECT_TYPE_FILE) { + tn = yaffs_get_tnode(dev); + if (!tn) + return NULL; + } + + the_obj = yaffs_alloc_empty_obj(dev); + if (!the_obj) { + if (tn) + yaffs_free_tnode(dev, tn); + return NULL; + } + + the_obj->fake = 0; + the_obj->rename_allowed = 1; + the_obj->unlink_allowed = 1; + the_obj->obj_id = number; + yaffs_hash_obj(the_obj); + the_obj->variant_type = type; + yaffs_load_current_time(the_obj, 1, 1); + + switch (type) { + case YAFFS_OBJECT_TYPE_FILE: + the_obj->variant.file_variant.file_size = 0; + the_obj->variant.file_variant.stored_size = 0; + the_obj->variant.file_variant.shrink_size = + yaffs_max_file_size(dev); + the_obj->variant.file_variant.top_level = 0; + the_obj->variant.file_variant.top = tn; + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + INIT_LIST_HEAD(&the_obj->variant.dir_variant.children); + INIT_LIST_HEAD(&the_obj->variant.dir_variant.dirty); + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + case YAFFS_OBJECT_TYPE_HARDLINK: + case YAFFS_OBJECT_TYPE_SPECIAL: + /* No action required */ + break; + case YAFFS_OBJECT_TYPE_UNKNOWN: + /* todo this should not happen */ + break; + } + return the_obj; +} + +static struct yaffs_obj *yaffs_create_fake_dir(struct yaffs_dev *dev, + int number, u32 mode) +{ + + struct yaffs_obj *obj = + yaffs_new_obj(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY); + + if (!obj) + return NULL; + + obj->fake = 1; /* it is fake so it might not use NAND */ + obj->rename_allowed = 0; + obj->unlink_allowed = 0; + obj->deleted = 0; + obj->unlinked = 0; + obj->yst_mode = mode; + obj->my_dev = dev; + obj->hdr_chunk = 0; /* Not a valid chunk. */ + return obj; + +} + + +static void yaffs_init_tnodes_and_objs(struct yaffs_dev *dev) +{ + int i; + + dev->n_obj = 0; + dev->n_tnodes = 0; + yaffs_init_raw_tnodes_and_objs(dev); + + for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { + INIT_LIST_HEAD(&dev->obj_bucket[i].list); + dev->obj_bucket[i].count = 0; + } +} + +struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, + int number, + enum yaffs_obj_type type) +{ + struct yaffs_obj *the_obj = NULL; + + if (number > 0) + the_obj = yaffs_find_by_number(dev, number); + + if (!the_obj) + the_obj = yaffs_new_obj(dev, number, type); + + return the_obj; + +} + +YCHAR *yaffs_clone_str(const YCHAR *str) +{ + YCHAR *new_str = NULL; + int len; + + if (!str) + str = _Y(""); + + len = strnlen(str, YAFFS_MAX_ALIAS_LENGTH); + new_str = kmalloc((len + 1) * sizeof(YCHAR), GFP_NOFS); + if (new_str) { + strncpy(new_str, str, len); + new_str[len] = 0; + } + return new_str; + +} +/* + *yaffs_update_parent() handles fixing a directories mtime and ctime when a new + * link (ie. name) is created or deleted in the directory. + * + * ie. + * create dir/a : update dir's mtime/ctime + * rm dir/a: update dir's mtime/ctime + * modify dir/a: don't update dir's mtimme/ctime + * + * This can be handled immediately or defered. Defering helps reduce the number + * of updates when many files in a directory are changed within a brief period. + * + * If the directory updating is defered then yaffs_update_dirty_dirs must be + * called periodically. + */ + +static void yaffs_update_parent(struct yaffs_obj *obj) +{ + struct yaffs_dev *dev; + + if (!obj) + return; + dev = obj->my_dev; + obj->dirty = 1; + yaffs_load_current_time(obj, 0, 1); + if (dev->param.defered_dir_update) { + struct list_head *link = &obj->variant.dir_variant.dirty; + + if (list_empty(link)) { + list_add(link, &dev->dirty_dirs); + yaffs_trace(YAFFS_TRACE_BACKGROUND, + "Added object %d to dirty directories", + obj->obj_id); + } + + } else { + yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); + } +} + +void yaffs_update_dirty_dirs(struct yaffs_dev *dev) +{ + struct list_head *link; + struct yaffs_obj *obj; + struct yaffs_dir_var *d_s; + union yaffs_obj_var *o_v; + + yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update dirty directories"); + + while (!list_empty(&dev->dirty_dirs)) { + link = dev->dirty_dirs.next; + list_del_init(link); + + d_s = list_entry(link, struct yaffs_dir_var, dirty); + o_v = list_entry(d_s, union yaffs_obj_var, dir_variant); + obj = list_entry(o_v, struct yaffs_obj, variant); + + yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update directory %d", + obj->obj_id); + + if (obj->dirty) + yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); + } +} + +/* + * Mknod (create) a new object. + * equiv_obj only has meaning for a hard link; + * alias_str only has meaning for a symlink. + * rdev only has meaning for devices (a subset of special objects) + */ + +static struct yaffs_obj *yaffs_create_obj(enum yaffs_obj_type type, + struct yaffs_obj *parent, + const YCHAR *name, + u32 mode, + u32 uid, + u32 gid, + struct yaffs_obj *equiv_obj, + const YCHAR *alias_str, u32 rdev) +{ + struct yaffs_obj *in; + YCHAR *str = NULL; + struct yaffs_dev *dev = parent->my_dev; + + /* Check if the entry exists. + * If it does then fail the call since we don't want a dup. */ + if (yaffs_find_by_name(parent, name)) + return NULL; + + if (type == YAFFS_OBJECT_TYPE_SYMLINK) { + str = yaffs_clone_str(alias_str); + if (!str) + return NULL; + } + + in = yaffs_new_obj(dev, -1, type); + + if (!in) { + kfree(str); + return NULL; + } + + in->hdr_chunk = 0; + in->valid = 1; + in->variant_type = type; + + in->yst_mode = mode; + + yaffs_attribs_init(in, gid, uid, rdev); + + in->n_data_chunks = 0; + + yaffs_set_obj_name(in, name); + in->dirty = 1; + + yaffs_add_obj_to_dir(parent, in); + + in->my_dev = parent->my_dev; + + switch (type) { + case YAFFS_OBJECT_TYPE_SYMLINK: + in->variant.symlink_variant.alias = str; + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + in->variant.hardlink_variant.equiv_obj = equiv_obj; + in->variant.hardlink_variant.equiv_id = equiv_obj->obj_id; + list_add(&in->hard_links, &equiv_obj->hard_links); + break; + case YAFFS_OBJECT_TYPE_FILE: + case YAFFS_OBJECT_TYPE_DIRECTORY: + case YAFFS_OBJECT_TYPE_SPECIAL: + case YAFFS_OBJECT_TYPE_UNKNOWN: + /* do nothing */ + break; + } + + if (yaffs_update_oh(in, name, 0, 0, 0, NULL) < 0) { + /* Could not create the object header, fail */ + yaffs_del_obj(in); + in = NULL; + } + + if (in) + yaffs_update_parent(parent); + + return in; +} + +struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, + const YCHAR *name, u32 mode, u32 uid, + u32 gid) +{ + return yaffs_create_obj(YAFFS_OBJECT_TYPE_FILE, parent, name, mode, + uid, gid, NULL, NULL, 0); +} + +struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, + u32 mode, u32 uid, u32 gid) +{ + return yaffs_create_obj(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name, + mode, uid, gid, NULL, NULL, 0); +} + +struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, + const YCHAR *name, u32 mode, u32 uid, + u32 gid, u32 rdev) +{ + return yaffs_create_obj(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode, + uid, gid, NULL, NULL, rdev); +} + +struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, + const YCHAR *name, u32 mode, u32 uid, + u32 gid, const YCHAR *alias) +{ + return yaffs_create_obj(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode, + uid, gid, NULL, alias, 0); +} + +/* yaffs_link_obj returns the object id of the equivalent object.*/ +struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR * name, + struct yaffs_obj *equiv_obj) +{ + /* Get the real object in case we were fed a hard link obj */ + equiv_obj = yaffs_get_equivalent_obj(equiv_obj); + + if (yaffs_create_obj(YAFFS_OBJECT_TYPE_HARDLINK, + parent, name, 0, 0, 0, + equiv_obj, NULL, 0)) + return equiv_obj; + + return NULL; + +} + + + +/*---------------------- Block Management and Page Allocation -------------*/ + +static void yaffs_deinit_blocks(struct yaffs_dev *dev) +{ + if (dev->block_info_alt && dev->block_info) + vfree(dev->block_info); + else + kfree(dev->block_info); + + dev->block_info_alt = 0; + + dev->block_info = NULL; + + if (dev->chunk_bits_alt && dev->chunk_bits) + vfree(dev->chunk_bits); + else + kfree(dev->chunk_bits); + dev->chunk_bits_alt = 0; + dev->chunk_bits = NULL; +} + +static int yaffs_init_blocks(struct yaffs_dev *dev) +{ + int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; + + dev->block_info = NULL; + dev->chunk_bits = NULL; + dev->alloc_block = -1; /* force it to get a new one */ + + /* If the first allocation strategy fails, thry the alternate one */ + dev->block_info = + kmalloc(n_blocks * sizeof(struct yaffs_block_info), GFP_NOFS); + if (!dev->block_info) { + dev->block_info = + vmalloc(n_blocks * sizeof(struct yaffs_block_info)); + dev->block_info_alt = 1; + } else { + dev->block_info_alt = 0; + } + + if (!dev->block_info) + goto alloc_error; + + /* Set up dynamic blockinfo stuff. Round up bytes. */ + dev->chunk_bit_stride = (dev->param.chunks_per_block + 7) / 8; + dev->chunk_bits = + kmalloc(dev->chunk_bit_stride * n_blocks, GFP_NOFS); + if (!dev->chunk_bits) { + dev->chunk_bits = + vmalloc(dev->chunk_bit_stride * n_blocks); + dev->chunk_bits_alt = 1; + } else { + dev->chunk_bits_alt = 0; + } + if (!dev->chunk_bits) + goto alloc_error; + + + memset(dev->block_info, 0, n_blocks * sizeof(struct yaffs_block_info)); + memset(dev->chunk_bits, 0, dev->chunk_bit_stride * n_blocks); + return YAFFS_OK; + +alloc_error: + yaffs_deinit_blocks(dev); + return YAFFS_FAIL; +} + + +void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no) +{ + struct yaffs_block_info *bi = yaffs_get_block_info(dev, block_no); + int erased_ok = 0; + int i; + + /* If the block is still healthy erase it and mark as clean. + * If the block has had a data failure, then retire it. + */ + + yaffs_trace(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE, + "yaffs_block_became_dirty block %d state %d %s", + block_no, bi->block_state, + (bi->needs_retiring) ? "needs retiring" : ""); + + yaffs2_clear_oldest_dirty_seq(dev, bi); + + bi->block_state = YAFFS_BLOCK_STATE_DIRTY; + + /* If this is the block being garbage collected then stop gc'ing */ + if (block_no == dev->gc_block) + dev->gc_block = 0; + + /* If this block is currently the best candidate for gc + * then drop as a candidate */ + if (block_no == dev->gc_dirtiest) { + dev->gc_dirtiest = 0; + dev->gc_pages_in_use = 0; + } + + if (!bi->needs_retiring) { + yaffs2_checkpt_invalidate(dev); + erased_ok = yaffs_erase_block(dev, block_no); + if (!erased_ok) { + dev->n_erase_failures++; + yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, + "**>> Erasure failed %d", block_no); + } + } + + /* Verify erasure if needed */ + if (erased_ok && + ((yaffs_trace_mask & YAFFS_TRACE_ERASE) || + !yaffs_skip_verification(dev))) { + for (i = 0; i < dev->param.chunks_per_block; i++) { + if (!yaffs_check_chunk_erased(dev, + block_no * dev->param.chunks_per_block + i)) { + yaffs_trace(YAFFS_TRACE_ERROR, + ">>Block %d erasure supposedly OK, but chunk %d not erased", + block_no, i); + } + } + } + + if (!erased_ok) { + /* We lost a block of free space */ + dev->n_free_chunks -= dev->param.chunks_per_block; + yaffs_retire_block(dev, block_no); + yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, + "**>> Block %d retired", block_no); + return; + } + + /* Clean it up... */ + bi->block_state = YAFFS_BLOCK_STATE_EMPTY; + bi->seq_number = 0; + dev->n_erased_blocks++; + bi->pages_in_use = 0; + bi->soft_del_pages = 0; + bi->has_shrink_hdr = 0; + bi->skip_erased_check = 1; /* Clean, so no need to check */ + bi->gc_prioritise = 0; + bi->has_summary = 0; + + yaffs_clear_chunk_bits(dev, block_no); + + yaffs_trace(YAFFS_TRACE_ERASE, "Erased block %d", block_no); +} + +static inline int yaffs_gc_process_chunk(struct yaffs_dev *dev, + struct yaffs_block_info *bi, + int old_chunk, u8 *buffer) +{ + int new_chunk; + int mark_flash = 1; + struct yaffs_ext_tags tags; + struct yaffs_obj *object; + int matching_chunk; + int ret_val = YAFFS_OK; + + memset(&tags, 0, sizeof(tags)); + yaffs_rd_chunk_tags_nand(dev, old_chunk, + buffer, &tags); + object = yaffs_find_by_number(dev, tags.obj_id); + + yaffs_trace(YAFFS_TRACE_GC_DETAIL, + "Collecting chunk in block %d, %d %d %d ", + dev->gc_chunk, tags.obj_id, + tags.chunk_id, tags.n_bytes); + + if (object && !yaffs_skip_verification(dev)) { + if (tags.chunk_id == 0) + matching_chunk = + object->hdr_chunk; + else if (object->soft_del) + /* Defeat the test */ + matching_chunk = old_chunk; + else + matching_chunk = + yaffs_find_chunk_in_file + (object, tags.chunk_id, + NULL); + + if (old_chunk != matching_chunk) + yaffs_trace(YAFFS_TRACE_ERROR, + "gc: page in gc mismatch: %d %d %d %d", + old_chunk, + matching_chunk, + tags.obj_id, + tags.chunk_id); + } + + if (!object) { + yaffs_trace(YAFFS_TRACE_ERROR, + "page %d in gc has no object: %d %d %d ", + old_chunk, + tags.obj_id, tags.chunk_id, + tags.n_bytes); + } + + if (object && + object->deleted && + object->soft_del && tags.chunk_id != 0) { + /* Data chunk in a soft deleted file, + * throw it away. + * It's a soft deleted data chunk, + * No need to copy this, just forget + * about it and fix up the object. + */ + + /* Free chunks already includes + * softdeleted chunks, how ever this + * chunk is going to soon be really + * deleted which will increment free + * chunks. We have to decrement free + * chunks so this works out properly. + */ + dev->n_free_chunks--; + bi->soft_del_pages--; + + object->n_data_chunks--; + if (object->n_data_chunks <= 0) { + /* remeber to clean up obj */ + dev->gc_cleanup_list[dev->n_clean_ups] = tags.obj_id; + dev->n_clean_ups++; + } + mark_flash = 0; + } else if (object) { + /* It's either a data chunk in a live + * file or an ObjectHeader, so we're + * interested in it. + * NB Need to keep the ObjectHeaders of + * deleted files until the whole file + * has been deleted off + */ + tags.serial_number++; + dev->n_gc_copies++; + + if (tags.chunk_id == 0) { + /* It is an object Id, + * We need to nuke the + * shrinkheader flags since its + * work is done. + * Also need to clean up + * shadowing. + */ + struct yaffs_obj_hdr *oh; + oh = (struct yaffs_obj_hdr *) buffer; + + oh->is_shrink = 0; + tags.extra_is_shrink = 0; + oh->shadows_obj = 0; + oh->inband_shadowed_obj_id = 0; + tags.extra_shadows = 0; + + /* Update file size */ + if (object->variant_type == YAFFS_OBJECT_TYPE_FILE) { + yaffs_oh_size_load(oh, + object->variant.file_variant.stored_size); + tags.extra_file_size = + object->variant.file_variant.stored_size; + } + + yaffs_verify_oh(object, oh, &tags, 1); + new_chunk = + yaffs_write_new_chunk(dev, (u8 *) oh, &tags, 1); + } else { + new_chunk = + yaffs_write_new_chunk(dev, buffer, &tags, 1); + } + + if (new_chunk < 0) { + ret_val = YAFFS_FAIL; + } else { + + /* Now fix up the Tnodes etc. */ + + if (tags.chunk_id == 0) { + /* It's a header */ + object->hdr_chunk = new_chunk; + object->serial = tags.serial_number; + } else { + /* It's a data chunk */ + yaffs_put_chunk_in_file(object, tags.chunk_id, + new_chunk, 0); + } + } + } + if (ret_val == YAFFS_OK) + yaffs_chunk_del(dev, old_chunk, mark_flash, __LINE__); + return ret_val; +} + +static int yaffs_gc_block(struct yaffs_dev *dev, int block, int whole_block) +{ + int old_chunk; + int ret_val = YAFFS_OK; + int i; + int is_checkpt_block; + int max_copies; + int chunks_before = yaffs_get_erased_chunks(dev); + int chunks_after; + struct yaffs_block_info *bi = yaffs_get_block_info(dev, block); + + is_checkpt_block = (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT); + + yaffs_trace(YAFFS_TRACE_TRACING, + "Collecting block %d, in use %d, shrink %d, whole_block %d", + block, bi->pages_in_use, bi->has_shrink_hdr, + whole_block); + + /*yaffs_verify_free_chunks(dev); */ + + if (bi->block_state == YAFFS_BLOCK_STATE_FULL) + bi->block_state = YAFFS_BLOCK_STATE_COLLECTING; + + bi->has_shrink_hdr = 0; /* clear the flag so that the block can erase */ + + dev->gc_disable = 1; + + yaffs_summary_gc(dev, block); + + if (is_checkpt_block || !yaffs_still_some_chunks(dev, block)) { + yaffs_trace(YAFFS_TRACE_TRACING, + "Collecting block %d that has no chunks in use", + block); + yaffs_block_became_dirty(dev, block); + } else { + + u8 *buffer = yaffs_get_temp_buffer(dev); + + yaffs_verify_blk(dev, bi, block); + + max_copies = (whole_block) ? dev->param.chunks_per_block : 5; + old_chunk = block * dev->param.chunks_per_block + dev->gc_chunk; + + for (/* init already done */ ; + ret_val == YAFFS_OK && + dev->gc_chunk < dev->param.chunks_per_block && + (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) && + max_copies > 0; + dev->gc_chunk++, old_chunk++) { + if (yaffs_check_chunk_bit(dev, block, dev->gc_chunk)) { + /* Page is in use and might need to be copied */ + max_copies--; + ret_val = yaffs_gc_process_chunk(dev, bi, + old_chunk, buffer); + } + } + yaffs_release_temp_buffer(dev, buffer); + } + + yaffs_verify_collected_blk(dev, bi, block); + + if (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { + /* + * The gc did not complete. Set block state back to FULL + * because checkpointing does not restore gc. + */ + bi->block_state = YAFFS_BLOCK_STATE_FULL; + } else { + /* The gc completed. */ + /* Do any required cleanups */ + for (i = 0; i < dev->n_clean_ups; i++) { + /* Time to delete the file too */ + struct yaffs_obj *object = + yaffs_find_by_number(dev, dev->gc_cleanup_list[i]); + if (object) { + yaffs_free_tnode(dev, + object->variant.file_variant.top); + object->variant.file_variant.top = NULL; + yaffs_trace(YAFFS_TRACE_GC, + "yaffs: About to finally delete object %d", + object->obj_id); + yaffs_generic_obj_del(object); + object->my_dev->n_deleted_files--; + } + + } + chunks_after = yaffs_get_erased_chunks(dev); + if (chunks_before >= chunks_after) + yaffs_trace(YAFFS_TRACE_GC, + "gc did not increase free chunks before %d after %d", + chunks_before, chunks_after); + dev->gc_block = 0; + dev->gc_chunk = 0; + dev->n_clean_ups = 0; + } + + dev->gc_disable = 0; + + return ret_val; +} + +/* + * find_gc_block() selects the dirtiest block (or close enough) + * for garbage collection. + */ + +static unsigned yaffs_find_gc_block(struct yaffs_dev *dev, + int aggressive, int background) +{ + int i; + int iterations; + unsigned selected = 0; + int prioritised = 0; + int prioritised_exist = 0; + struct yaffs_block_info *bi; + int threshold; + + /* First let's see if we need to grab a prioritised block */ + if (dev->has_pending_prioritised_gc && !aggressive) { + dev->gc_dirtiest = 0; + bi = dev->block_info; + for (i = dev->internal_start_block; + i <= dev->internal_end_block && !selected; i++) { + + if (bi->gc_prioritise) { + prioritised_exist = 1; + if (bi->block_state == YAFFS_BLOCK_STATE_FULL && + yaffs_block_ok_for_gc(dev, bi)) { + selected = i; + prioritised = 1; + } + } + bi++; + } + + /* + * If there is a prioritised block and none was selected then + * this happened because there is at least one old dirty block + * gumming up the works. Let's gc the oldest dirty block. + */ + + if (prioritised_exist && + !selected && dev->oldest_dirty_block > 0) + selected = dev->oldest_dirty_block; + + if (!prioritised_exist) /* None found, so we can clear this */ + dev->has_pending_prioritised_gc = 0; + } + + /* If we're doing aggressive GC then we are happy to take a less-dirty + * block, and search harder. + * else (leasurely gc), then we only bother to do this if the + * block has only a few pages in use. + */ + + if (!selected) { + int pages_used; + int n_blocks = + dev->internal_end_block - dev->internal_start_block + 1; + if (aggressive) { + threshold = dev->param.chunks_per_block; + iterations = n_blocks; + } else { + int max_threshold; + + if (background) + max_threshold = dev->param.chunks_per_block / 2; + else + max_threshold = dev->param.chunks_per_block / 8; + + if (max_threshold < YAFFS_GC_PASSIVE_THRESHOLD) + max_threshold = YAFFS_GC_PASSIVE_THRESHOLD; + + threshold = background ? (dev->gc_not_done + 2) * 2 : 0; + if (threshold < YAFFS_GC_PASSIVE_THRESHOLD) + threshold = YAFFS_GC_PASSIVE_THRESHOLD; + if (threshold > max_threshold) + threshold = max_threshold; + + iterations = n_blocks / 16 + 1; + if (iterations > 100) + iterations = 100; + } + + for (i = 0; + i < iterations && + (dev->gc_dirtiest < 1 || + dev->gc_pages_in_use > YAFFS_GC_GOOD_ENOUGH); + i++) { + dev->gc_block_finder++; + if (dev->gc_block_finder < dev->internal_start_block || + dev->gc_block_finder > dev->internal_end_block) + dev->gc_block_finder = + dev->internal_start_block; + + bi = yaffs_get_block_info(dev, dev->gc_block_finder); + + pages_used = bi->pages_in_use - bi->soft_del_pages; + + if (bi->block_state == YAFFS_BLOCK_STATE_FULL && + pages_used < dev->param.chunks_per_block && + (dev->gc_dirtiest < 1 || + pages_used < dev->gc_pages_in_use) && + yaffs_block_ok_for_gc(dev, bi)) { + dev->gc_dirtiest = dev->gc_block_finder; + dev->gc_pages_in_use = pages_used; + } + } + + if (dev->gc_dirtiest > 0 && dev->gc_pages_in_use <= threshold) + selected = dev->gc_dirtiest; + } + + /* + * If nothing has been selected for a while, try the oldest dirty + * because that's gumming up the works. + */ + + if (!selected && dev->param.is_yaffs2 && + dev->gc_not_done >= (background ? 10 : 20)) { + yaffs2_find_oldest_dirty_seq(dev); + if (dev->oldest_dirty_block > 0) { + selected = dev->oldest_dirty_block; + dev->gc_dirtiest = selected; + dev->oldest_dirty_gc_count++; + bi = yaffs_get_block_info(dev, selected); + dev->gc_pages_in_use = + bi->pages_in_use - bi->soft_del_pages; + } else { + dev->gc_not_done = 0; + } + } + + if (selected) { + yaffs_trace(YAFFS_TRACE_GC, + "GC Selected block %d with %d free, prioritised:%d", + selected, + dev->param.chunks_per_block - dev->gc_pages_in_use, + prioritised); + + dev->n_gc_blocks++; + if (background) + dev->bg_gcs++; + + dev->gc_dirtiest = 0; + dev->gc_pages_in_use = 0; + dev->gc_not_done = 0; + if (dev->refresh_skip > 0) + dev->refresh_skip--; + } else { + dev->gc_not_done++; + yaffs_trace(YAFFS_TRACE_GC, + "GC none: finder %d skip %d threshold %d dirtiest %d using %d oldest %d%s", + dev->gc_block_finder, dev->gc_not_done, threshold, + dev->gc_dirtiest, dev->gc_pages_in_use, + dev->oldest_dirty_block, background ? " bg" : ""); + } + + return selected; +} + +/* New garbage collector + * If we're very low on erased blocks then we do aggressive garbage collection + * otherwise we do "leasurely" garbage collection. + * Aggressive gc looks further (whole array) and will accept less dirty blocks. + * Passive gc only inspects smaller areas and only accepts more dirty blocks. + * + * The idea is to help clear out space in a more spread-out manner. + * Dunno if it really does anything useful. + */ +static int yaffs_check_gc(struct yaffs_dev *dev, int background) +{ + int aggressive = 0; + int gc_ok = YAFFS_OK; + int max_tries = 0; + int min_erased; + int erased_chunks; + int checkpt_block_adjust; + + if (dev->param.gc_control_fn && + (dev->param.gc_control_fn(dev) & 1) == 0) + return YAFFS_OK; + + if (dev->gc_disable) + /* Bail out so we don't get recursive gc */ + return YAFFS_OK; + + /* This loop should pass the first time. + * Only loops here if the collection does not increase space. + */ + + do { + max_tries++; + + checkpt_block_adjust = yaffs_calc_checkpt_blocks_required(dev); + + min_erased = + dev->param.n_reserved_blocks + checkpt_block_adjust + 1; + erased_chunks = + dev->n_erased_blocks * dev->param.chunks_per_block; + + /* If we need a block soon then do aggressive gc. */ + if (dev->n_erased_blocks < min_erased) + aggressive = 1; + else { + if (!background + && erased_chunks > (dev->n_free_chunks / 4)) + break; + + if (dev->gc_skip > 20) + dev->gc_skip = 20; + if (erased_chunks < dev->n_free_chunks / 2 || + dev->gc_skip < 1 || background) + aggressive = 0; + else { + dev->gc_skip--; + break; + } + } + + dev->gc_skip = 5; + + /* If we don't already have a block being gc'd then see if we + * should start another */ + + if (dev->gc_block < 1 && !aggressive) { + dev->gc_block = yaffs2_find_refresh_block(dev); + dev->gc_chunk = 0; + dev->n_clean_ups = 0; + } + if (dev->gc_block < 1) { + dev->gc_block = + yaffs_find_gc_block(dev, aggressive, background); + dev->gc_chunk = 0; + dev->n_clean_ups = 0; + } + + if (dev->gc_block > 0) { + dev->all_gcs++; + if (!aggressive) + dev->passive_gc_count++; + + yaffs_trace(YAFFS_TRACE_GC, + "yaffs: GC n_erased_blocks %d aggressive %d", + dev->n_erased_blocks, aggressive); + + gc_ok = yaffs_gc_block(dev, dev->gc_block, aggressive); + } + + if (dev->n_erased_blocks < (dev->param.n_reserved_blocks) && + dev->gc_block > 0) { + yaffs_trace(YAFFS_TRACE_GC, + "yaffs: GC !!!no reclaim!!! n_erased_blocks %d after try %d block %d", + dev->n_erased_blocks, max_tries, + dev->gc_block); + } + } while ((dev->n_erased_blocks < dev->param.n_reserved_blocks) && + (dev->gc_block > 0) && (max_tries < 2)); + + return aggressive ? gc_ok : YAFFS_OK; +} + +/* + * yaffs_bg_gc() + * Garbage collects. Intended to be called from a background thread. + * Returns non-zero if at least half the free chunks are erased. + */ +int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency) +{ + int erased_chunks = dev->n_erased_blocks * dev->param.chunks_per_block; + + yaffs_trace(YAFFS_TRACE_BACKGROUND, "Background gc %u", urgency); + + yaffs_check_gc(dev, 1); + return erased_chunks > dev->n_free_chunks / 2; +} + +/*-------------------- Data file manipulation -----------------*/ + +static int yaffs_rd_data_obj(struct yaffs_obj *in, int inode_chunk, u8 * buffer) +{ + int nand_chunk = yaffs_find_chunk_in_file(in, inode_chunk, NULL); + + if (nand_chunk >= 0) + return yaffs_rd_chunk_tags_nand(in->my_dev, nand_chunk, + buffer, NULL); + else { + yaffs_trace(YAFFS_TRACE_NANDACCESS, + "Chunk %d not found zero instead", + nand_chunk); + /* get sane (zero) data if you read a hole */ + memset(buffer, 0, in->my_dev->data_bytes_per_chunk); + return 0; + } + +} + +void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, + int lyn) +{ + int block; + int page; + struct yaffs_ext_tags tags; + struct yaffs_block_info *bi; + + if (chunk_id <= 0) + return; + + dev->n_deletions++; + block = chunk_id / dev->param.chunks_per_block; + page = chunk_id % dev->param.chunks_per_block; + + if (!yaffs_check_chunk_bit(dev, block, page)) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Deleting invalid chunk %d", chunk_id); + + bi = yaffs_get_block_info(dev, block); + + yaffs2_update_oldest_dirty_seq(dev, block, bi); + + yaffs_trace(YAFFS_TRACE_DELETION, + "line %d delete of chunk %d", + lyn, chunk_id); + + if (!dev->param.is_yaffs2 && mark_flash && + bi->block_state != YAFFS_BLOCK_STATE_COLLECTING) { + + memset(&tags, 0, sizeof(tags)); + tags.is_deleted = 1; + yaffs_wr_chunk_tags_nand(dev, chunk_id, NULL, &tags); + yaffs_handle_chunk_update(dev, chunk_id, &tags); + } else { + dev->n_unmarked_deletions++; + } + + /* Pull out of the management area. + * If the whole block became dirty, this will kick off an erasure. + */ + if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING || + bi->block_state == YAFFS_BLOCK_STATE_FULL || + bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || + bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { + dev->n_free_chunks++; + yaffs_clear_chunk_bit(dev, block, page); + bi->pages_in_use--; + + if (bi->pages_in_use == 0 && + !bi->has_shrink_hdr && + bi->block_state != YAFFS_BLOCK_STATE_ALLOCATING && + bi->block_state != YAFFS_BLOCK_STATE_NEEDS_SCAN) { + yaffs_block_became_dirty(dev, block); + } + } +} + +static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, + const u8 *buffer, int n_bytes, int use_reserve) +{ + /* Find old chunk Need to do this to get serial number + * Write new one and patch into tree. + * Invalidate old tags. + */ + + int prev_chunk_id; + struct yaffs_ext_tags prev_tags; + int new_chunk_id; + struct yaffs_ext_tags new_tags; + struct yaffs_dev *dev = in->my_dev; + loff_t endpos; + + yaffs_check_gc(dev, 0); + + /* Get the previous chunk at this location in the file if it exists. + * If it does not exist then put a zero into the tree. This creates + * the tnode now, rather than later when it is harder to clean up. + */ + prev_chunk_id = yaffs_find_chunk_in_file(in, inode_chunk, &prev_tags); + if (prev_chunk_id < 1 && + !yaffs_put_chunk_in_file(in, inode_chunk, 0, 0)) + return 0; + + /* Set up new tags */ + memset(&new_tags, 0, sizeof(new_tags)); + + new_tags.chunk_id = inode_chunk; + new_tags.obj_id = in->obj_id; + new_tags.serial_number = + (prev_chunk_id > 0) ? prev_tags.serial_number + 1 : 1; + new_tags.n_bytes = n_bytes; + + if (n_bytes < 1 || n_bytes > dev->data_bytes_per_chunk) { + yaffs_trace(YAFFS_TRACE_ERROR, + "Writing %d bytes to chunk!!!!!!!!!", + n_bytes); + BUG(); + } + + /* + * If this is a data chunk and the write goes past the end of the stored + * size then update the stored_size. + */ + if (inode_chunk > 0) { + endpos = (inode_chunk - 1) * dev->data_bytes_per_chunk + + n_bytes; + if (in->variant.file_variant.stored_size < endpos) + in->variant.file_variant.stored_size = endpos; + } + + new_chunk_id = + yaffs_write_new_chunk(dev, buffer, &new_tags, use_reserve); + + if (new_chunk_id > 0) { + yaffs_put_chunk_in_file(in, inode_chunk, new_chunk_id, 0); + + if (prev_chunk_id > 0) + yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); + + yaffs_verify_file_sane(in); + } + return new_chunk_id; +} + + + +static int yaffs_do_xattrib_mod(struct yaffs_obj *obj, int set, + const YCHAR *name, const void *value, int size, + int flags) +{ + struct yaffs_xattr_mod xmod; + int result; + + xmod.set = set; + xmod.name = name; + xmod.data = value; + xmod.size = size; + xmod.flags = flags; + xmod.result = -ENOSPC; + + result = yaffs_update_oh(obj, NULL, 0, 0, 0, &xmod); + + if (result > 0) + return xmod.result; + else + return -ENOSPC; +} + +static int yaffs_apply_xattrib_mod(struct yaffs_obj *obj, char *buffer, + struct yaffs_xattr_mod *xmod) +{ + int retval = 0; + int x_offs = sizeof(struct yaffs_obj_hdr); + struct yaffs_dev *dev = obj->my_dev; + int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); + char *x_buffer = buffer + x_offs; + + if (xmod->set) + retval = + nval_set(x_buffer, x_size, xmod->name, xmod->data, + xmod->size, xmod->flags); + else + retval = nval_del(x_buffer, x_size, xmod->name); + + obj->has_xattr = nval_hasvalues(x_buffer, x_size); + obj->xattr_known = 1; + xmod->result = retval; + + return retval; +} + +static int yaffs_do_xattrib_fetch(struct yaffs_obj *obj, const YCHAR *name, + void *value, int size) +{ + char *buffer = NULL; + int result; + struct yaffs_ext_tags tags; + struct yaffs_dev *dev = obj->my_dev; + int x_offs = sizeof(struct yaffs_obj_hdr); + int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); + char *x_buffer; + int retval = 0; + + if (obj->hdr_chunk < 1) + return -ENODATA; + + /* If we know that the object has no xattribs then don't do all the + * reading and parsing. + */ + if (obj->xattr_known && !obj->has_xattr) { + if (name) + return -ENODATA; + else + return 0; + } + + buffer = (char *)yaffs_get_temp_buffer(dev); + if (!buffer) + return -ENOMEM; + + result = + yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, (u8 *) buffer, &tags); + + if (result != YAFFS_OK) + retval = -ENOENT; + else { + x_buffer = buffer + x_offs; + + if (!obj->xattr_known) { + obj->has_xattr = nval_hasvalues(x_buffer, x_size); + obj->xattr_known = 1; + } + + if (name) + retval = nval_get(x_buffer, x_size, name, value, size); + else + retval = nval_list(x_buffer, x_size, value, size); + } + yaffs_release_temp_buffer(dev, (u8 *) buffer); + return retval; +} + +int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR * name, + const void *value, int size, int flags) +{ + return yaffs_do_xattrib_mod(obj, 1, name, value, size, flags); +} + +int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR * name) +{ + return yaffs_do_xattrib_mod(obj, 0, name, NULL, 0, 0); +} + +int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR * name, void *value, + int size) +{ + return yaffs_do_xattrib_fetch(obj, name, value, size); +} + +int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size) +{ + return yaffs_do_xattrib_fetch(obj, NULL, buffer, size); +} + +static void yaffs_check_obj_details_loaded(struct yaffs_obj *in) +{ + u8 *buf; + struct yaffs_obj_hdr *oh; + struct yaffs_dev *dev; + struct yaffs_ext_tags tags; + int result; + int alloc_failed = 0; + + if (!in || !in->lazy_loaded || in->hdr_chunk < 1) + return; + + dev = in->my_dev; + in->lazy_loaded = 0; + buf = yaffs_get_temp_buffer(dev); + + result = yaffs_rd_chunk_tags_nand(dev, in->hdr_chunk, buf, &tags); + oh = (struct yaffs_obj_hdr *)buf; + + in->yst_mode = oh->yst_mode; + yaffs_load_attribs(in, oh); + yaffs_set_obj_name_from_oh(in, oh); + + if (in->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { + in->variant.symlink_variant.alias = + yaffs_clone_str(oh->alias); + if (!in->variant.symlink_variant.alias) + alloc_failed = 1; /* Not returned */ + } + yaffs_release_temp_buffer(dev, buf); +} + +/* UpdateObjectHeader updates the header on NAND for an object. + * If name is not NULL, then that new name is used. + */ +int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, int force, + int is_shrink, int shadows, struct yaffs_xattr_mod *xmod) +{ + + struct yaffs_block_info *bi; + struct yaffs_dev *dev = in->my_dev; + int prev_chunk_id; + int ret_val = 0; + int result = 0; + int new_chunk_id; + struct yaffs_ext_tags new_tags; + struct yaffs_ext_tags old_tags; + const YCHAR *alias = NULL; + u8 *buffer = NULL; + YCHAR old_name[YAFFS_MAX_NAME_LENGTH + 1]; + struct yaffs_obj_hdr *oh = NULL; + loff_t file_size = 0; + + strcpy(old_name, _Y("silly old name")); + + if (in->fake && in != dev->root_dir && !force && !xmod) + return ret_val; + + yaffs_check_gc(dev, 0); + yaffs_check_obj_details_loaded(in); + + buffer = yaffs_get_temp_buffer(in->my_dev); + oh = (struct yaffs_obj_hdr *)buffer; + + prev_chunk_id = in->hdr_chunk; + + if (prev_chunk_id > 0) { + result = yaffs_rd_chunk_tags_nand(dev, prev_chunk_id, + buffer, &old_tags); + + yaffs_verify_oh(in, oh, &old_tags, 0); + memcpy(old_name, oh->name, sizeof(oh->name)); + memset(buffer, 0xff, sizeof(struct yaffs_obj_hdr)); + } else { + memset(buffer, 0xff, dev->data_bytes_per_chunk); + } + + oh->type = in->variant_type; + oh->yst_mode = in->yst_mode; + oh->shadows_obj = oh->inband_shadowed_obj_id = shadows; + + yaffs_load_attribs_oh(oh, in); + + if (in->parent) + oh->parent_obj_id = in->parent->obj_id; + else + oh->parent_obj_id = 0; + + if (name && *name) { + memset(oh->name, 0, sizeof(oh->name)); + yaffs_load_oh_from_name(dev, oh->name, name); + } else if (prev_chunk_id > 0) { + memcpy(oh->name, old_name, sizeof(oh->name)); + } else { + memset(oh->name, 0, sizeof(oh->name)); + } + + oh->is_shrink = is_shrink; + + switch (in->variant_type) { + case YAFFS_OBJECT_TYPE_UNKNOWN: + /* Should not happen */ + break; + case YAFFS_OBJECT_TYPE_FILE: + if (oh->parent_obj_id != YAFFS_OBJECTID_DELETED && + oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED) + file_size = in->variant.file_variant.stored_size; + yaffs_oh_size_load(oh, file_size); + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + oh->equiv_id = in->variant.hardlink_variant.equiv_id; + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + /* Do nothing */ + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + /* Do nothing */ + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + alias = in->variant.symlink_variant.alias; + if (!alias) + alias = _Y("no alias"); + strncpy(oh->alias, alias, YAFFS_MAX_ALIAS_LENGTH); + oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0; + break; + } + + /* process any xattrib modifications */ + if (xmod) + yaffs_apply_xattrib_mod(in, (char *)buffer, xmod); + + /* Tags */ + memset(&new_tags, 0, sizeof(new_tags)); + in->serial++; + new_tags.chunk_id = 0; + new_tags.obj_id = in->obj_id; + new_tags.serial_number = in->serial; + + /* Add extra info for file header */ + new_tags.extra_available = 1; + new_tags.extra_parent_id = oh->parent_obj_id; + new_tags.extra_file_size = file_size; + new_tags.extra_is_shrink = oh->is_shrink; + new_tags.extra_equiv_id = oh->equiv_id; + new_tags.extra_shadows = (oh->shadows_obj > 0) ? 1 : 0; + new_tags.extra_obj_type = in->variant_type; + yaffs_verify_oh(in, oh, &new_tags, 1); + + /* Create new chunk in NAND */ + new_chunk_id = + yaffs_write_new_chunk(dev, buffer, &new_tags, + (prev_chunk_id > 0) ? 1 : 0); + + if (buffer) + yaffs_release_temp_buffer(dev, buffer); + + if (new_chunk_id < 0) + return new_chunk_id; + + in->hdr_chunk = new_chunk_id; + + if (prev_chunk_id > 0) + yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); + + if (!yaffs_obj_cache_dirty(in)) + in->dirty = 0; + + /* If this was a shrink, then mark the block + * that the chunk lives on */ + if (is_shrink) { + bi = yaffs_get_block_info(in->my_dev, + new_chunk_id / + in->my_dev->param.chunks_per_block); + bi->has_shrink_hdr = 1; + } + + + return new_chunk_id; +} + +/*--------------------- File read/write ------------------------ + * Read and write have very similar structures. + * In general the read/write has three parts to it + * An incomplete chunk to start with (if the read/write is not chunk-aligned) + * Some complete chunks + * An incomplete chunk to end off with + * + * Curve-balls: the first chunk might also be the last chunk. + */ + +int yaffs_file_rd(struct yaffs_obj *in, u8 * buffer, loff_t offset, int n_bytes) +{ + int chunk; + u32 start; + int n_copy; + int n = n_bytes; + int n_done = 0; + struct yaffs_cache *cache; + struct yaffs_dev *dev; + + dev = in->my_dev; + + while (n > 0) { + yaffs_addr_to_chunk(dev, offset, &chunk, &start); + chunk++; + + /* OK now check for the curveball where the start and end are in + * the same chunk. + */ + if ((start + n) < dev->data_bytes_per_chunk) + n_copy = n; + else + n_copy = dev->data_bytes_per_chunk - start; + + cache = yaffs_find_chunk_cache(in, chunk); + + /* If the chunk is already in the cache or it is less than + * a whole chunk or we're using inband tags then use the cache + * (if there is caching) else bypass the cache. + */ + if (cache || n_copy != dev->data_bytes_per_chunk || + dev->param.inband_tags) { + if (dev->param.n_caches > 0) { + + /* If we can't find the data in the cache, + * then load it up. */ + + if (!cache) { + cache = + yaffs_grab_chunk_cache(in->my_dev); + cache->object = in; + cache->chunk_id = chunk; + cache->dirty = 0; + cache->locked = 0; + yaffs_rd_data_obj(in, chunk, + cache->data); + cache->n_bytes = 0; + } + + yaffs_use_cache(dev, cache, 0); + + cache->locked = 1; + + memcpy(buffer, &cache->data[start], n_copy); + + cache->locked = 0; + } else { + /* Read into the local buffer then copy.. */ + + u8 *local_buffer = + yaffs_get_temp_buffer(dev); + yaffs_rd_data_obj(in, chunk, local_buffer); + + memcpy(buffer, &local_buffer[start], n_copy); + + yaffs_release_temp_buffer(dev, local_buffer); + } + } else { + /* A full chunk. Read directly into the buffer. */ + yaffs_rd_data_obj(in, chunk, buffer); + } + n -= n_copy; + offset += n_copy; + buffer += n_copy; + n_done += n_copy; + } + return n_done; +} + +int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, + int n_bytes, int write_through) +{ + + int chunk; + u32 start; + int n_copy; + int n = n_bytes; + int n_done = 0; + int n_writeback; + loff_t start_write = offset; + int chunk_written = 0; + u32 n_bytes_read; + loff_t chunk_start; + struct yaffs_dev *dev; + + dev = in->my_dev; + + while (n > 0 && chunk_written >= 0) { + yaffs_addr_to_chunk(dev, offset, &chunk, &start); + + if (((loff_t)chunk) * + dev->data_bytes_per_chunk + start != offset || + start >= dev->data_bytes_per_chunk) { + yaffs_trace(YAFFS_TRACE_ERROR, + "AddrToChunk of offset %lld gives chunk %d start %d", + offset, chunk, start); + } + chunk++; /* File pos to chunk in file offset */ + + /* OK now check for the curveball where the start and end are in + * the same chunk. + */ + + if ((start + n) < dev->data_bytes_per_chunk) { + n_copy = n; + + /* Now calculate how many bytes to write back.... + * If we're overwriting and not writing to then end of + * file then we need to write back as much as was there + * before. + */ + + chunk_start = (((loff_t)(chunk - 1)) * + dev->data_bytes_per_chunk); + + if (chunk_start > in->variant.file_variant.file_size) + n_bytes_read = 0; /* Past end of file */ + else + n_bytes_read = + in->variant.file_variant.file_size - + chunk_start; + + if (n_bytes_read > dev->data_bytes_per_chunk) + n_bytes_read = dev->data_bytes_per_chunk; + + n_writeback = + (n_bytes_read > + (start + n)) ? n_bytes_read : (start + n); + + if (n_writeback < 0 || + n_writeback > dev->data_bytes_per_chunk) + BUG(); + + } else { + n_copy = dev->data_bytes_per_chunk - start; + n_writeback = dev->data_bytes_per_chunk; + } + + if (n_copy != dev->data_bytes_per_chunk || + !dev->param.cache_bypass_aligned || + dev->param.inband_tags) { + /* An incomplete start or end chunk (or maybe both + * start and end chunk), or we're using inband tags, + * or we're forcing writes through the cache, + * so we want to use the cache buffers. + */ + if (dev->param.n_caches > 0) { + struct yaffs_cache *cache; + + /* If we can't find the data in the cache, then + * load the cache */ + cache = yaffs_find_chunk_cache(in, chunk); + + if (!cache && + yaffs_check_alloc_available(dev, 1)) { + cache = yaffs_grab_chunk_cache(dev); + cache->object = in; + cache->chunk_id = chunk; + cache->dirty = 0; + cache->locked = 0; + yaffs_rd_data_obj(in, chunk, + cache->data); + } else if (cache && + !cache->dirty && + !yaffs_check_alloc_available(dev, + 1)) { + /* Drop the cache if it was a read cache + * item and no space check has been made + * for it. + */ + cache = NULL; + } + + if (cache) { + yaffs_use_cache(dev, cache, 1); + cache->locked = 1; + + memcpy(&cache->data[start], buffer, + n_copy); + + cache->locked = 0; + cache->n_bytes = n_writeback; + + if (write_through) { + chunk_written = + yaffs_wr_data_obj + (cache->object, + cache->chunk_id, + cache->data, + cache->n_bytes, 1); + cache->dirty = 0; + } + } else { + chunk_written = -1; /* fail write */ + } + } else { + /* An incomplete start or end chunk (or maybe + * both start and end chunk). Read into the + * local buffer then copy over and write back. + */ + + u8 *local_buffer = yaffs_get_temp_buffer(dev); + + yaffs_rd_data_obj(in, chunk, local_buffer); + memcpy(&local_buffer[start], buffer, n_copy); + + chunk_written = + yaffs_wr_data_obj(in, chunk, + local_buffer, + n_writeback, 0); + + yaffs_release_temp_buffer(dev, local_buffer); + } + } else { + /* A full chunk. Write directly from the buffer. */ + + chunk_written = + yaffs_wr_data_obj(in, chunk, buffer, + dev->data_bytes_per_chunk, 0); + + /* Since we've overwritten the cached data, + * we better invalidate it. */ + yaffs_invalidate_chunk_cache(in, chunk); + } + + if (chunk_written >= 0) { + n -= n_copy; + offset += n_copy; + buffer += n_copy; + n_done += n_copy; + } + } + + /* Update file object */ + + if ((start_write + n_done) > in->variant.file_variant.file_size) + in->variant.file_variant.file_size = (start_write + n_done); + + in->dirty = 1; + return n_done; +} + +int yaffs_wr_file(struct yaffs_obj *in, const u8 *buffer, loff_t offset, + int n_bytes, int write_through) +{ + yaffs2_handle_hole(in, offset); + return yaffs_do_file_wr(in, buffer, offset, n_bytes, write_through); +} + +/* ---------------------- File resizing stuff ------------------ */ + +static void yaffs_prune_chunks(struct yaffs_obj *in, loff_t new_size) +{ + + struct yaffs_dev *dev = in->my_dev; + loff_t old_size = in->variant.file_variant.file_size; + int i; + int chunk_id; + u32 dummy; + int last_del; + int start_del; + + if (old_size > 0) + yaffs_addr_to_chunk(dev, old_size - 1, &last_del, &dummy); + else + last_del = 0; + + yaffs_addr_to_chunk(dev, new_size + dev->data_bytes_per_chunk - 1, + &start_del, &dummy); + last_del++; + start_del++; + + /* Delete backwards so that we don't end up with holes if + * power is lost part-way through the operation. + */ + for (i = last_del; i >= start_del; i--) { + /* NB this could be optimised somewhat, + * eg. could retrieve the tags and write them without + * using yaffs_chunk_del + */ + + chunk_id = yaffs_find_del_file_chunk(in, i, NULL); + + if (chunk_id < 1) + continue; + + if (chunk_id < + (dev->internal_start_block * dev->param.chunks_per_block) || + chunk_id >= + ((dev->internal_end_block + 1) * + dev->param.chunks_per_block)) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "Found daft chunk_id %d for %d", + chunk_id, i); + } else { + in->n_data_chunks--; + yaffs_chunk_del(dev, chunk_id, 1, __LINE__); + } + } +} + +void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size) +{ + int new_full; + u32 new_partial; + struct yaffs_dev *dev = obj->my_dev; + + yaffs_addr_to_chunk(dev, new_size, &new_full, &new_partial); + + yaffs_prune_chunks(obj, new_size); + + if (new_partial != 0) { + int last_chunk = 1 + new_full; + u8 *local_buffer = yaffs_get_temp_buffer(dev); + + /* Rewrite the last chunk with its new size and zero pad */ + yaffs_rd_data_obj(obj, last_chunk, local_buffer); + memset(local_buffer + new_partial, 0, + dev->data_bytes_per_chunk - new_partial); + + yaffs_wr_data_obj(obj, last_chunk, local_buffer, + new_partial, 1); + + yaffs_release_temp_buffer(dev, local_buffer); + } + + obj->variant.file_variant.file_size = new_size; + obj->variant.file_variant.stored_size = new_size; + + yaffs_prune_tree(dev, &obj->variant.file_variant); +} + +int yaffs_resize_file(struct yaffs_obj *in, loff_t new_size) +{ + struct yaffs_dev *dev = in->my_dev; + loff_t old_size = in->variant.file_variant.file_size; + + yaffs_flush_file_cache(in, 1); + yaffs_invalidate_whole_cache(in); + + yaffs_check_gc(dev, 0); + + if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) + return YAFFS_FAIL; + + if (new_size == old_size) + return YAFFS_OK; + + if (new_size > old_size) { + yaffs2_handle_hole(in, new_size); + in->variant.file_variant.file_size = new_size; + } else { + /* new_size < old_size */ + yaffs_resize_file_down(in, new_size); + } + + /* Write a new object header to reflect the resize. + * show we've shrunk the file, if need be + * Do this only if the file is not in the deleted directories + * and is not shadowed. + */ + if (in->parent && + !in->is_shadowed && + in->parent->obj_id != YAFFS_OBJECTID_UNLINKED && + in->parent->obj_id != YAFFS_OBJECTID_DELETED) + yaffs_update_oh(in, NULL, 0, 0, 0, NULL); + + return YAFFS_OK; +} + +int yaffs_flush_file(struct yaffs_obj *in, + int update_time, + int data_sync, + int discard_cache) +{ + if (!in->dirty) + return YAFFS_OK; + + yaffs_flush_file_cache(in, discard_cache); + + if (data_sync) + return YAFFS_OK; + + if (update_time) + yaffs_load_current_time(in, 0, 0); + + return (yaffs_update_oh(in, NULL, 0, 0, 0, NULL) >= 0) ? + YAFFS_OK : YAFFS_FAIL; +} + + +/* yaffs_del_file deletes the whole file data + * and the inode associated with the file. + * It does not delete the links associated with the file. + */ +static int yaffs_unlink_file_if_needed(struct yaffs_obj *in) +{ + int ret_val; + int del_now = 0; + struct yaffs_dev *dev = in->my_dev; + + if (!in->my_inode) + del_now = 1; + + if (del_now) { + ret_val = + yaffs_change_obj_name(in, in->my_dev->del_dir, + _Y("deleted"), 0, 0); + yaffs_trace(YAFFS_TRACE_TRACING, + "yaffs: immediate deletion of file %d", + in->obj_id); + in->deleted = 1; + in->my_dev->n_deleted_files++; + if (dev->param.disable_soft_del || dev->param.is_yaffs2) + yaffs_resize_file(in, 0); + yaffs_soft_del_file(in); + } else { + ret_val = + yaffs_change_obj_name(in, in->my_dev->unlinked_dir, + _Y("unlinked"), 0, 0); + } + return ret_val; +} + +static int yaffs_del_file(struct yaffs_obj *in) +{ + int ret_val = YAFFS_OK; + int deleted; /* Need to cache value on stack if in is freed */ + struct yaffs_dev *dev = in->my_dev; + + if (dev->param.disable_soft_del || dev->param.is_yaffs2) + yaffs_resize_file(in, 0); + + if (in->n_data_chunks > 0) { + /* Use soft deletion if there is data in the file. + * That won't be the case if it has been resized to zero. + */ + if (!in->unlinked) + ret_val = yaffs_unlink_file_if_needed(in); + + deleted = in->deleted; + + if (ret_val == YAFFS_OK && in->unlinked && !in->deleted) { + in->deleted = 1; + deleted = 1; + in->my_dev->n_deleted_files++; + yaffs_soft_del_file(in); + } + return deleted ? YAFFS_OK : YAFFS_FAIL; + } else { + /* The file has no data chunks so we toss it immediately */ + yaffs_free_tnode(in->my_dev, in->variant.file_variant.top); + in->variant.file_variant.top = NULL; + yaffs_generic_obj_del(in); + + return YAFFS_OK; + } +} + +int yaffs_is_non_empty_dir(struct yaffs_obj *obj) +{ + return (obj && + obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) && + !(list_empty(&obj->variant.dir_variant.children)); +} + +static int yaffs_del_dir(struct yaffs_obj *obj) +{ + /* First check that the directory is empty. */ + if (yaffs_is_non_empty_dir(obj)) + return YAFFS_FAIL; + + return yaffs_generic_obj_del(obj); +} + +static int yaffs_del_symlink(struct yaffs_obj *in) +{ + kfree(in->variant.symlink_variant.alias); + in->variant.symlink_variant.alias = NULL; + + return yaffs_generic_obj_del(in); +} + +static int yaffs_del_link(struct yaffs_obj *in) +{ + /* remove this hardlink from the list associated with the equivalent + * object + */ + list_del_init(&in->hard_links); + return yaffs_generic_obj_del(in); +} + +int yaffs_del_obj(struct yaffs_obj *obj) +{ + int ret_val = -1; + + switch (obj->variant_type) { + case YAFFS_OBJECT_TYPE_FILE: + ret_val = yaffs_del_file(obj); + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + if (!list_empty(&obj->variant.dir_variant.dirty)) { + yaffs_trace(YAFFS_TRACE_BACKGROUND, + "Remove object %d from dirty directories", + obj->obj_id); + list_del_init(&obj->variant.dir_variant.dirty); + } + return yaffs_del_dir(obj); + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + ret_val = yaffs_del_symlink(obj); + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + ret_val = yaffs_del_link(obj); + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + ret_val = yaffs_generic_obj_del(obj); + break; + case YAFFS_OBJECT_TYPE_UNKNOWN: + ret_val = 0; + break; /* should not happen. */ + } + return ret_val; +} + + +static void yaffs_empty_dir_to_dir(struct yaffs_obj *from_dir, + struct yaffs_obj *to_dir) +{ + struct yaffs_obj *obj; + struct list_head *lh; + struct list_head *n; + + list_for_each_safe(lh, n, &from_dir->variant.dir_variant.children) { + obj = list_entry(lh, struct yaffs_obj, siblings); + yaffs_add_obj_to_dir(to_dir, obj); + } +} + +struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj, + enum yaffs_obj_type type) +{ + /* Tear down the old variant */ + switch (obj->variant_type) { + case YAFFS_OBJECT_TYPE_FILE: + /* Nuke file data */ + yaffs_resize_file(obj, 0); + yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); + obj->variant.file_variant.top = NULL; + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + /* Put the children in lost and found. */ + yaffs_empty_dir_to_dir(obj, obj->my_dev->lost_n_found); + if (!list_empty(&obj->variant.dir_variant.dirty)) + list_del_init(&obj->variant.dir_variant.dirty); + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + /* Nuke symplink data */ + kfree(obj->variant.symlink_variant.alias); + obj->variant.symlink_variant.alias = NULL; + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + list_del_init(&obj->hard_links); + break; + default: + break; + } + + memset(&obj->variant, 0, sizeof(obj->variant)); + + /*Set up new variant if the memset is not enough. */ + switch (type) { + case YAFFS_OBJECT_TYPE_DIRECTORY: + INIT_LIST_HEAD(&obj->variant.dir_variant.children); + INIT_LIST_HEAD(&obj->variant.dir_variant.dirty); + break; + case YAFFS_OBJECT_TYPE_FILE: + case YAFFS_OBJECT_TYPE_SYMLINK: + case YAFFS_OBJECT_TYPE_HARDLINK: + default: + break; + } + + obj->variant_type = type; + + return obj; + +} + +static int yaffs_unlink_worker(struct yaffs_obj *obj) +{ + int del_now = 0; + + if (!obj) + return YAFFS_FAIL; + + if (!obj->my_inode) + del_now = 1; + + yaffs_update_parent(obj->parent); + + if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { + return yaffs_del_link(obj); + } else if (!list_empty(&obj->hard_links)) { + /* Curve ball: We're unlinking an object that has a hardlink. + * + * This problem arises because we are not strictly following + * The Linux link/inode model. + * + * We can't really delete the object. + * Instead, we do the following: + * - Select a hardlink. + * - Unhook it from the hard links + * - Move it from its parent directory so that the rename works. + * - Rename the object to the hardlink's name. + * - Delete the hardlink + */ + + struct yaffs_obj *hl; + struct yaffs_obj *parent; + int ret_val; + YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; + + hl = list_entry(obj->hard_links.next, struct yaffs_obj, + hard_links); + + yaffs_get_obj_name(hl, name, YAFFS_MAX_NAME_LENGTH + 1); + parent = hl->parent; + + list_del_init(&hl->hard_links); + + yaffs_add_obj_to_dir(obj->my_dev->unlinked_dir, hl); + + ret_val = yaffs_change_obj_name(obj, parent, name, 0, 0); + + if (ret_val == YAFFS_OK) + ret_val = yaffs_generic_obj_del(hl); + + return ret_val; + + } else if (del_now) { + switch (obj->variant_type) { + case YAFFS_OBJECT_TYPE_FILE: + return yaffs_del_file(obj); + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + list_del_init(&obj->variant.dir_variant.dirty); + return yaffs_del_dir(obj); + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + return yaffs_del_symlink(obj); + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + return yaffs_generic_obj_del(obj); + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + case YAFFS_OBJECT_TYPE_UNKNOWN: + default: + return YAFFS_FAIL; + } + } else if (yaffs_is_non_empty_dir(obj)) { + return YAFFS_FAIL; + } else { + return yaffs_change_obj_name(obj, obj->my_dev->unlinked_dir, + _Y("unlinked"), 0, 0); + } +} + +int yaffs_unlink_obj(struct yaffs_obj *obj) +{ + if (obj && obj->unlink_allowed) + return yaffs_unlink_worker(obj); + + return YAFFS_FAIL; +} + +int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR *name) +{ + struct yaffs_obj *obj; + + obj = yaffs_find_by_name(dir, name); + return yaffs_unlink_obj(obj); +} + +/* Note: + * If old_name is NULL then we take old_dir as the object to be renamed. + */ +int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR *old_name, + struct yaffs_obj *new_dir, const YCHAR *new_name) +{ + struct yaffs_obj *obj = NULL; + struct yaffs_obj *existing_target = NULL; + int force = 0; + int result; + struct yaffs_dev *dev; + + if (!old_dir || old_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { + BUG(); + return YAFFS_FAIL; + } + if (!new_dir || new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { + BUG(); + return YAFFS_FAIL; + } + + dev = old_dir->my_dev; + +#ifdef CONFIG_YAFFS_CASE_INSENSITIVE + /* Special case for case insemsitive systems. + * While look-up is case insensitive, the name isn't. + * Therefore we might want to change x.txt to X.txt + */ + if (old_dir == new_dir && + old_name && new_name && + strcmp(old_name, new_name) == 0) + force = 1; +#endif + + if (strnlen(new_name, YAFFS_MAX_NAME_LENGTH + 1) > + YAFFS_MAX_NAME_LENGTH) + /* ENAMETOOLONG */ + return YAFFS_FAIL; + + if (old_name) + obj = yaffs_find_by_name(old_dir, old_name); + else{ + obj = old_dir; + old_dir = obj->parent; + } + + if (obj && obj->rename_allowed) { + /* Now handle an existing target, if there is one */ + existing_target = yaffs_find_by_name(new_dir, new_name); + if (yaffs_is_non_empty_dir(existing_target)) { + return YAFFS_FAIL; /* ENOTEMPTY */ + } else if (existing_target && existing_target != obj) { + /* Nuke the target first, using shadowing, + * but only if it isn't the same object. + * + * Note we must disable gc here otherwise it can mess + * up the shadowing. + * + */ + dev->gc_disable = 1; + yaffs_change_obj_name(obj, new_dir, new_name, force, + existing_target->obj_id); + existing_target->is_shadowed = 1; + yaffs_unlink_obj(existing_target); + dev->gc_disable = 0; + } + + result = yaffs_change_obj_name(obj, new_dir, new_name, 1, 0); + + yaffs_update_parent(old_dir); + if (new_dir != old_dir) + yaffs_update_parent(new_dir); + + return result; + } + return YAFFS_FAIL; +} + +/*----------------------- Initialisation Scanning ---------------------- */ + +void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, + int backward_scanning) +{ + struct yaffs_obj *obj; + + if (backward_scanning) { + /* Handle YAFFS2 case (backward scanning) + * If the shadowed object exists then ignore. + */ + obj = yaffs_find_by_number(dev, obj_id); + if (obj) + return; + } + + /* Let's create it (if it does not exist) assuming it is a file so that + * it can do shrinking etc. + * We put it in unlinked dir to be cleaned up after the scanning + */ + obj = + yaffs_find_or_create_by_number(dev, obj_id, YAFFS_OBJECT_TYPE_FILE); + if (!obj) + return; + obj->is_shadowed = 1; + yaffs_add_obj_to_dir(dev->unlinked_dir, obj); + obj->variant.file_variant.shrink_size = 0; + obj->valid = 1; /* So that we don't read any other info. */ +} + +void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list) +{ + struct list_head *lh; + struct list_head *save; + struct yaffs_obj *hl; + struct yaffs_obj *in; + + list_for_each_safe(lh, save, hard_list) { + hl = list_entry(lh, struct yaffs_obj, hard_links); + in = yaffs_find_by_number(dev, + hl->variant.hardlink_variant.equiv_id); + + if (in) { + /* Add the hardlink pointers */ + hl->variant.hardlink_variant.equiv_obj = in; + list_add(&hl->hard_links, &in->hard_links); + } else { + /* Todo Need to report/handle this better. + * Got a problem... hardlink to a non-existant object + */ + hl->variant.hardlink_variant.equiv_obj = NULL; + INIT_LIST_HEAD(&hl->hard_links); + } + } +} + +static void yaffs_strip_deleted_objs(struct yaffs_dev *dev) +{ + /* + * Sort out state of unlinked and deleted objects after scanning. + */ + struct list_head *i; + struct list_head *n; + struct yaffs_obj *l; + + if (dev->read_only) + return; + + /* Soft delete all the unlinked files */ + list_for_each_safe(i, n, + &dev->unlinked_dir->variant.dir_variant.children) { + l = list_entry(i, struct yaffs_obj, siblings); + yaffs_del_obj(l); + } + + list_for_each_safe(i, n, &dev->del_dir->variant.dir_variant.children) { + l = list_entry(i, struct yaffs_obj, siblings); + yaffs_del_obj(l); + } +} + +/* + * This code iterates through all the objects making sure that they are rooted. + * Any unrooted objects are re-rooted in lost+found. + * An object needs to be in one of: + * - Directly under deleted, unlinked + * - Directly or indirectly under root. + * + * Note: + * This code assumes that we don't ever change the current relationships + * between directories: + * root_dir->parent == unlinked_dir->parent == del_dir->parent == NULL + * lost-n-found->parent == root_dir + * + * This fixes the problem where directories might have inadvertently been + * deleted leaving the object "hanging" without being rooted in the + * directory tree. + */ + +static int yaffs_has_null_parent(struct yaffs_dev *dev, struct yaffs_obj *obj) +{ + return (obj == dev->del_dir || + obj == dev->unlinked_dir || obj == dev->root_dir); +} + +static void yaffs_fix_hanging_objs(struct yaffs_dev *dev) +{ + struct yaffs_obj *obj; + struct yaffs_obj *parent; + int i; + struct list_head *lh; + struct list_head *n; + int depth_limit; + int hanging; + + if (dev->read_only) + return; + + /* Iterate through the objects in each hash entry, + * looking at each object. + * Make sure it is rooted. + */ + + for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { + list_for_each_safe(lh, n, &dev->obj_bucket[i].list) { + obj = list_entry(lh, struct yaffs_obj, hash_link); + parent = obj->parent; + + if (yaffs_has_null_parent(dev, obj)) { + /* These directories are not hanging */ + hanging = 0; + } else if (!parent || + parent->variant_type != + YAFFS_OBJECT_TYPE_DIRECTORY) { + hanging = 1; + } else if (yaffs_has_null_parent(dev, parent)) { + hanging = 0; + } else { + /* + * Need to follow the parent chain to + * see if it is hanging. + */ + hanging = 0; + depth_limit = 100; + + while (parent != dev->root_dir && + parent->parent && + parent->parent->variant_type == + YAFFS_OBJECT_TYPE_DIRECTORY && + depth_limit > 0) { + parent = parent->parent; + depth_limit--; + } + if (parent != dev->root_dir) + hanging = 1; + } + if (hanging) { + yaffs_trace(YAFFS_TRACE_SCAN, + "Hanging object %d moved to lost and found", + obj->obj_id); + yaffs_add_obj_to_dir(dev->lost_n_found, obj); + } + } + } +} + +/* + * Delete directory contents for cleaning up lost and found. + */ +static void yaffs_del_dir_contents(struct yaffs_obj *dir) +{ + struct yaffs_obj *obj; + struct list_head *lh; + struct list_head *n; + + if (dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) + BUG(); + + list_for_each_safe(lh, n, &dir->variant.dir_variant.children) { + obj = list_entry(lh, struct yaffs_obj, siblings); + if (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) + yaffs_del_dir_contents(obj); + yaffs_trace(YAFFS_TRACE_SCAN, + "Deleting lost_found object %d", + obj->obj_id); + yaffs_unlink_obj(obj); + } +} + +static void yaffs_empty_l_n_f(struct yaffs_dev *dev) +{ + yaffs_del_dir_contents(dev->lost_n_found); +} + + +struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *directory, + const YCHAR *name) +{ + int sum; + struct list_head *i; + YCHAR buffer[YAFFS_MAX_NAME_LENGTH + 1]; + struct yaffs_obj *l; + + if (!name) + return NULL; + + if (!directory) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "tragedy: yaffs_find_by_name: null pointer directory" + ); + BUG(); + return NULL; + } + if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "tragedy: yaffs_find_by_name: non-directory" + ); + BUG(); + } + + sum = yaffs_calc_name_sum(name); + + list_for_each(i, &directory->variant.dir_variant.children) { + l = list_entry(i, struct yaffs_obj, siblings); + + if (l->parent != directory) + BUG(); + + yaffs_check_obj_details_loaded(l); + + /* Special case for lost-n-found */ + if (l->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { + if (!strcmp(name, YAFFS_LOSTNFOUND_NAME)) + return l; + } else if (l->sum == sum || l->hdr_chunk <= 0) { + /* LostnFound chunk called Objxxx + * Do a real check + */ + yaffs_get_obj_name(l, buffer, + YAFFS_MAX_NAME_LENGTH + 1); + if (!strncmp(name, buffer, YAFFS_MAX_NAME_LENGTH)) + return l; + } + } + return NULL; +} + +/* GetEquivalentObject dereferences any hard links to get to the + * actual object. + */ + +struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj) +{ + if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { + obj = obj->variant.hardlink_variant.equiv_obj; + yaffs_check_obj_details_loaded(obj); + } + return obj; +} + +/* + * A note or two on object names. + * * If the object name is missing, we then make one up in the form objnnn + * + * * ASCII names are stored in the object header's name field from byte zero + * * Unicode names are historically stored starting from byte zero. + * + * Then there are automatic Unicode names... + * The purpose of these is to save names in a way that can be read as + * ASCII or Unicode names as appropriate, thus allowing a Unicode and ASCII + * system to share files. + * + * These automatic unicode are stored slightly differently... + * - If the name can fit in the ASCII character space then they are saved as + * ascii names as per above. + * - If the name needs Unicode then the name is saved in Unicode + * starting at oh->name[1]. + + */ +static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, + int buffer_size) +{ + /* Create an object name if we could not find one. */ + if (strnlen(name, YAFFS_MAX_NAME_LENGTH) == 0) { + YCHAR local_name[20]; + YCHAR num_string[20]; + YCHAR *x = &num_string[19]; + unsigned v = obj->obj_id; + num_string[19] = 0; + while (v > 0) { + x--; + *x = '0' + (v % 10); + v /= 10; + } + /* make up a name */ + strcpy(local_name, YAFFS_LOSTNFOUND_PREFIX); + strcat(local_name, x); + strncpy(name, local_name, buffer_size - 1); + } +} + +int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR *name, int buffer_size) +{ + memset(name, 0, buffer_size * sizeof(YCHAR)); + yaffs_check_obj_details_loaded(obj); + if (obj->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { + strncpy(name, YAFFS_LOSTNFOUND_NAME, buffer_size - 1); + } else if (obj->short_name[0]) { + strcpy(name, obj->short_name); + } else if (obj->hdr_chunk > 0) { + int result; + u8 *buffer = yaffs_get_temp_buffer(obj->my_dev); + + struct yaffs_obj_hdr *oh = (struct yaffs_obj_hdr *)buffer; + + memset(buffer, 0, obj->my_dev->data_bytes_per_chunk); + + if (obj->hdr_chunk > 0) { + result = yaffs_rd_chunk_tags_nand(obj->my_dev, + obj->hdr_chunk, + buffer, NULL); + } + yaffs_load_name_from_oh(obj->my_dev, name, oh->name, + buffer_size); + + yaffs_release_temp_buffer(obj->my_dev, buffer); + } + + yaffs_fix_null_name(obj, name, buffer_size); + + return strnlen(name, YAFFS_MAX_NAME_LENGTH); +} + +loff_t yaffs_get_obj_length(struct yaffs_obj *obj) +{ + /* Dereference any hard linking */ + obj = yaffs_get_equivalent_obj(obj); + + if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) + return obj->variant.file_variant.file_size; + if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { + if (!obj->variant.symlink_variant.alias) + return 0; + return strnlen(obj->variant.symlink_variant.alias, + YAFFS_MAX_ALIAS_LENGTH); + } else { + /* Only a directory should drop through to here */ + return obj->my_dev->data_bytes_per_chunk; + } +} + +int yaffs_get_obj_link_count(struct yaffs_obj *obj) +{ + int count = 0; + struct list_head *i; + + if (!obj->unlinked) + count++; /* the object itself */ + + list_for_each(i, &obj->hard_links) + count++; /* add the hard links; */ + + return count; +} + +int yaffs_get_obj_inode(struct yaffs_obj *obj) +{ + obj = yaffs_get_equivalent_obj(obj); + + return obj->obj_id; +} + +unsigned yaffs_get_obj_type(struct yaffs_obj *obj) +{ + obj = yaffs_get_equivalent_obj(obj); + + switch (obj->variant_type) { + case YAFFS_OBJECT_TYPE_FILE: + return DT_REG; + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + return DT_DIR; + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + return DT_LNK; + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + return DT_REG; + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + if (S_ISFIFO(obj->yst_mode)) + return DT_FIFO; + if (S_ISCHR(obj->yst_mode)) + return DT_CHR; + if (S_ISBLK(obj->yst_mode)) + return DT_BLK; + if (S_ISSOCK(obj->yst_mode)) + return DT_SOCK; + return DT_REG; + break; + default: + return DT_REG; + break; + } +} + +YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj) +{ + obj = yaffs_get_equivalent_obj(obj); + if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) + return yaffs_clone_str(obj->variant.symlink_variant.alias); + else + return yaffs_clone_str(_Y("")); +} + +/*--------------------------- Initialisation code -------------------------- */ + +static int yaffs_check_dev_fns(struct yaffs_dev *dev) +{ + struct yaffs_driver *drv = &dev->drv; + struct yaffs_tags_handler *tagger = &dev->tagger; + + /* Common functions, gotta have */ + if (!drv->drv_read_chunk_fn || + !drv->drv_write_chunk_fn || + !drv->drv_erase_fn) + return 0; + + if (dev->param.is_yaffs2 && + (!drv->drv_mark_bad_fn || !drv->drv_check_bad_fn)) + return 0; + + /* Install the default tags marshalling functions if needed. */ + yaffs_tags_compat_install(dev); + yaffs_tags_marshall_install(dev); + + /* Check we now have the marshalling functions required. */ + if (!tagger->write_chunk_tags_fn || + !tagger->read_chunk_tags_fn || + !tagger->query_block_fn || + !tagger->mark_bad_fn) + return 0; + + return 1; +} + +static int yaffs_create_initial_dir(struct yaffs_dev *dev) +{ + /* Initialise the unlinked, deleted, root and lost+found directories */ + dev->lost_n_found = dev->root_dir = NULL; + dev->unlinked_dir = dev->del_dir = NULL; + dev->unlinked_dir = + yaffs_create_fake_dir(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR); + dev->del_dir = + yaffs_create_fake_dir(dev, YAFFS_OBJECTID_DELETED, S_IFDIR); + dev->root_dir = + yaffs_create_fake_dir(dev, YAFFS_OBJECTID_ROOT, + YAFFS_ROOT_MODE | S_IFDIR); + dev->lost_n_found = + yaffs_create_fake_dir(dev, YAFFS_OBJECTID_LOSTNFOUND, + YAFFS_LOSTNFOUND_MODE | S_IFDIR); + + if (dev->lost_n_found && dev->root_dir && dev->unlinked_dir + && dev->del_dir) { + yaffs_add_obj_to_dir(dev->root_dir, dev->lost_n_found); + return YAFFS_OK; + } + return YAFFS_FAIL; +} + +/* Low level init. + * Typically only used by yaffs_guts_initialise, but also used by the + * Low level yaffs driver tests. + */ + +int yaffs_guts_ll_init(struct yaffs_dev *dev) +{ + + + yaffs_trace(YAFFS_TRACE_TRACING, "yaffs: yaffs_ll_init()"); + + if (!dev) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "yaffs: Need a device" + ); + return YAFFS_FAIL; + } + + if (dev->ll_init) + return YAFFS_OK; + + dev->internal_start_block = dev->param.start_block; + dev->internal_end_block = dev->param.end_block; + dev->block_offset = 0; + dev->chunk_offset = 0; + dev->n_free_chunks = 0; + + dev->gc_block = 0; + + if (dev->param.start_block == 0) { + dev->internal_start_block = dev->param.start_block + 1; + dev->internal_end_block = dev->param.end_block + 1; + dev->block_offset = 1; + dev->chunk_offset = dev->param.chunks_per_block; + } + + /* Check geometry parameters. */ + + if ((!dev->param.inband_tags && dev->param.is_yaffs2 && + dev->param.total_bytes_per_chunk < 1024) || + (!dev->param.is_yaffs2 && + dev->param.total_bytes_per_chunk < 512) || + (dev->param.inband_tags && !dev->param.is_yaffs2) || + dev->param.chunks_per_block < 2 || + dev->param.n_reserved_blocks < 2 || + dev->internal_start_block <= 0 || + dev->internal_end_block <= 0 || + dev->internal_end_block <= + (dev->internal_start_block + dev->param.n_reserved_blocks + 2) + ) { + /* otherwise it is too small */ + yaffs_trace(YAFFS_TRACE_ALWAYS, + "NAND geometry problems: chunk size %d, type is yaffs%s, inband_tags %d ", + dev->param.total_bytes_per_chunk, + dev->param.is_yaffs2 ? "2" : "", + dev->param.inband_tags); + return YAFFS_FAIL; + } + + /* Sort out space for inband tags, if required */ + if (dev->param.inband_tags) + dev->data_bytes_per_chunk = + dev->param.total_bytes_per_chunk - + sizeof(struct yaffs_packed_tags2_tags_only); + else + dev->data_bytes_per_chunk = dev->param.total_bytes_per_chunk; + + /* Got the right mix of functions? */ + if (!yaffs_check_dev_fns(dev)) { + /* Function missing */ + yaffs_trace(YAFFS_TRACE_ALWAYS, + "device function(s) missing or wrong"); + + return YAFFS_FAIL; + } + + if (yaffs_init_nand(dev) != YAFFS_OK) { + yaffs_trace(YAFFS_TRACE_ALWAYS, "InitialiseNAND failed"); + return YAFFS_FAIL; + } + + return YAFFS_OK; +} + + +int yaffs_guts_format_dev(struct yaffs_dev *dev) +{ + int i; + enum yaffs_block_state state; + u32 dummy; + + if(yaffs_guts_ll_init(dev) != YAFFS_OK) + return YAFFS_FAIL; + + if(dev->is_mounted) + return YAFFS_FAIL; + + for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { + yaffs_query_init_block_state(dev, i, &state, &dummy); + if (state != YAFFS_BLOCK_STATE_DEAD) + yaffs_erase_block(dev, i); + } + + return YAFFS_OK; +} + + +int yaffs_guts_initialise(struct yaffs_dev *dev) +{ + int init_failed = 0; + unsigned x; + int bits; + + if(yaffs_guts_ll_init(dev) != YAFFS_OK) + return YAFFS_FAIL; + + if (dev->is_mounted) { + yaffs_trace(YAFFS_TRACE_ALWAYS, "device already mounted"); + return YAFFS_FAIL; + } + + dev->is_mounted = 1; + + /* OK now calculate a few things for the device */ + + /* + * Calculate all the chunk size manipulation numbers: + */ + x = dev->data_bytes_per_chunk; + /* We always use dev->chunk_shift and dev->chunk_div */ + dev->chunk_shift = calc_shifts(x); + x >>= dev->chunk_shift; + dev->chunk_div = x; + /* We only use chunk mask if chunk_div is 1 */ + dev->chunk_mask = (1 << dev->chunk_shift) - 1; + + /* + * Calculate chunk_grp_bits. + * We need to find the next power of 2 > than internal_end_block + */ + + x = dev->param.chunks_per_block * (dev->internal_end_block + 1); + + bits = calc_shifts_ceiling(x); + + /* Set up tnode width if wide tnodes are enabled. */ + if (!dev->param.wide_tnodes_disabled) { + /* bits must be even so that we end up with 32-bit words */ + if (bits & 1) + bits++; + if (bits < 16) + dev->tnode_width = 16; + else + dev->tnode_width = bits; + } else { + dev->tnode_width = 16; + } + + dev->tnode_mask = (1 << dev->tnode_width) - 1; + + /* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled), + * so if the bitwidth of the + * chunk range we're using is greater than 16 we need + * to figure out chunk shift and chunk_grp_size + */ + + if (bits <= dev->tnode_width) + dev->chunk_grp_bits = 0; + else + dev->chunk_grp_bits = bits - dev->tnode_width; + + dev->tnode_size = (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8; + if (dev->tnode_size < sizeof(struct yaffs_tnode)) + dev->tnode_size = sizeof(struct yaffs_tnode); + + dev->chunk_grp_size = 1 << dev->chunk_grp_bits; + + if (dev->param.chunks_per_block < dev->chunk_grp_size) { + /* We have a problem because the soft delete won't work if + * the chunk group size > chunks per block. + * This can be remedied by using larger "virtual blocks". + */ + yaffs_trace(YAFFS_TRACE_ALWAYS, "chunk group too large"); + + return YAFFS_FAIL; + } + + /* Finished verifying the device, continue with initialisation */ + + /* More device initialisation */ + dev->all_gcs = 0; + dev->passive_gc_count = 0; + dev->oldest_dirty_gc_count = 0; + dev->bg_gcs = 0; + dev->gc_block_finder = 0; + dev->buffered_block = -1; + dev->doing_buffered_block_rewrite = 0; + dev->n_deleted_files = 0; + dev->n_bg_deletions = 0; + dev->n_unlinked_files = 0; + dev->n_ecc_fixed = 0; + dev->n_ecc_unfixed = 0; + dev->n_tags_ecc_fixed = 0; + dev->n_tags_ecc_unfixed = 0; + dev->n_erase_failures = 0; + dev->n_erased_blocks = 0; + dev->gc_disable = 0; + dev->has_pending_prioritised_gc = 1; + /* Assume the worst for now, will get fixed on first GC */ + INIT_LIST_HEAD(&dev->dirty_dirs); + dev->oldest_dirty_seq = 0; + dev->oldest_dirty_block = 0; + + /* Initialise temporary buffers and caches. */ + if (!yaffs_init_tmp_buffers(dev)) + init_failed = 1; + + dev->cache = NULL; + dev->gc_cleanup_list = NULL; + + if (!init_failed && dev->param.n_caches > 0) { + int i; + void *buf; + int cache_bytes = + dev->param.n_caches * sizeof(struct yaffs_cache); + + if (dev->param.n_caches > YAFFS_MAX_SHORT_OP_CACHES) + dev->param.n_caches = YAFFS_MAX_SHORT_OP_CACHES; + + dev->cache = kmalloc(cache_bytes, GFP_NOFS); + + buf = (u8 *) dev->cache; + + if (dev->cache) + memset(dev->cache, 0, cache_bytes); + + for (i = 0; i < dev->param.n_caches && buf; i++) { + dev->cache[i].object = NULL; + dev->cache[i].last_use = 0; + dev->cache[i].dirty = 0; + dev->cache[i].data = buf = + kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); + } + if (!buf) + init_failed = 1; + + dev->cache_last_use = 0; + } + + dev->cache_hits = 0; + + if (!init_failed) { + dev->gc_cleanup_list = + kmalloc(dev->param.chunks_per_block * sizeof(u32), + GFP_NOFS); + if (!dev->gc_cleanup_list) + init_failed = 1; + } + + if (dev->param.is_yaffs2) + dev->param.use_header_file_size = 1; + + if (!init_failed && !yaffs_init_blocks(dev)) + init_failed = 1; + + yaffs_init_tnodes_and_objs(dev); + + if (!init_failed && !yaffs_create_initial_dir(dev)) + init_failed = 1; + + if (!init_failed && dev->param.is_yaffs2 && + !dev->param.disable_summary && + !yaffs_summary_init(dev)) + init_failed = 1; + + if (!init_failed) { + /* Now scan the flash. */ + if (dev->param.is_yaffs2) { + if (yaffs2_checkpt_restore(dev)) { + yaffs_check_obj_details_loaded(dev->root_dir); + yaffs_trace(YAFFS_TRACE_CHECKPOINT | + YAFFS_TRACE_MOUNT, + "yaffs: restored from checkpoint" + ); + } else { + + /* Clean up the mess caused by an aborted + * checkpoint load then scan backwards. + */ + yaffs_deinit_blocks(dev); + + yaffs_deinit_tnodes_and_objs(dev); + + dev->n_erased_blocks = 0; + dev->n_free_chunks = 0; + dev->alloc_block = -1; + dev->alloc_page = -1; + dev->n_deleted_files = 0; + dev->n_unlinked_files = 0; + dev->n_bg_deletions = 0; + + if (!init_failed && !yaffs_init_blocks(dev)) + init_failed = 1; + + yaffs_init_tnodes_and_objs(dev); + + if (!init_failed + && !yaffs_create_initial_dir(dev)) + init_failed = 1; + + if (!init_failed && !yaffs2_scan_backwards(dev)) + init_failed = 1; + } + } else if (!yaffs1_scan(dev)) { + init_failed = 1; + } + + yaffs_strip_deleted_objs(dev); + yaffs_fix_hanging_objs(dev); + if (dev->param.empty_lost_n_found) + yaffs_empty_l_n_f(dev); + } + + if (init_failed) { + /* Clean up the mess */ + yaffs_trace(YAFFS_TRACE_TRACING, + "yaffs: yaffs_guts_initialise() aborted."); + + yaffs_deinitialise(dev); + return YAFFS_FAIL; + } + + /* Zero out stats */ + dev->n_page_reads = 0; + dev->n_page_writes = 0; + dev->n_erasures = 0; + dev->n_gc_copies = 0; + dev->n_retried_writes = 0; + + dev->n_retired_blocks = 0; + + yaffs_verify_free_chunks(dev); + yaffs_verify_blocks(dev); + + /* Clean up any aborted checkpoint data */ + if (!dev->is_checkpointed && dev->blocks_in_checkpt > 0) + yaffs2_checkpt_invalidate(dev); + + yaffs_trace(YAFFS_TRACE_TRACING, + "yaffs: yaffs_guts_initialise() done."); + return YAFFS_OK; +} + +void yaffs_deinitialise(struct yaffs_dev *dev) +{ + if (dev->is_mounted) { + int i; + + yaffs_deinit_blocks(dev); + yaffs_deinit_tnodes_and_objs(dev); + yaffs_summary_deinit(dev); + + if (dev->param.n_caches > 0 && dev->cache) { + + for (i = 0; i < dev->param.n_caches; i++) { + kfree(dev->cache[i].data); + dev->cache[i].data = NULL; + } + + kfree(dev->cache); + dev->cache = NULL; + } + + kfree(dev->gc_cleanup_list); + + for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { + kfree(dev->temp_buffer[i].buffer); + dev->temp_buffer[i].buffer = NULL; + } + + kfree(dev->checkpt_buffer); + dev->checkpt_buffer = NULL; + kfree(dev->checkpt_block_list); + dev->checkpt_block_list = NULL; + + dev->is_mounted = 0; + + yaffs_deinit_nand(dev); + } +} + +int yaffs_count_free_chunks(struct yaffs_dev *dev) +{ + int n_free = 0; + int b; + struct yaffs_block_info *blk; + + blk = dev->block_info; + for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { + switch (blk->block_state) { + case YAFFS_BLOCK_STATE_EMPTY: + case YAFFS_BLOCK_STATE_ALLOCATING: + case YAFFS_BLOCK_STATE_COLLECTING: + case YAFFS_BLOCK_STATE_FULL: + n_free += + (dev->param.chunks_per_block - blk->pages_in_use + + blk->soft_del_pages); + break; + default: + break; + } + blk++; + } + return n_free; +} + +int yaffs_get_n_free_chunks(struct yaffs_dev *dev) +{ + /* This is what we report to the outside world */ + int n_free; + int n_dirty_caches; + int blocks_for_checkpt; + int i; + + n_free = dev->n_free_chunks; + n_free += dev->n_deleted_files; + + /* Now count and subtract the number of dirty chunks in the cache. */ + + for (n_dirty_caches = 0, i = 0; i < dev->param.n_caches; i++) { + if (dev->cache[i].dirty) + n_dirty_caches++; + } + + n_free -= n_dirty_caches; + + n_free -= + ((dev->param.n_reserved_blocks + 1) * dev->param.chunks_per_block); + + /* Now figure checkpoint space and report that... */ + blocks_for_checkpt = yaffs_calc_checkpt_blocks_required(dev); + + n_free -= (blocks_for_checkpt * dev->param.chunks_per_block); + + if (n_free < 0) + n_free = 0; + + return n_free; +} + + + +/* + * Marshalling functions to get loff_t file sizes into and out of + * object headers. + */ +void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize) +{ + oh->file_size_low = (fsize & 0xFFFFFFFF); + oh->file_size_high = ((fsize >> 32) & 0xFFFFFFFF); +} + +loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh) +{ + loff_t retval; + + if (sizeof(loff_t) >= 8 && ~(oh->file_size_high)) + retval = (((loff_t) oh->file_size_high) << 32) | + (((loff_t) oh->file_size_low) & 0xFFFFFFFF); + else + retval = (loff_t) oh->file_size_low; + + return retval; +} + + +void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]) +{ + int i; + struct yaffs_block_info *bi; + int s; + + for(i = 0; i < 10; i++) + bs[i] = 0; + + for(i = dev->internal_start_block; i <= dev->internal_end_block; i++) { + bi = yaffs_get_block_info(dev, i); + s = bi->block_state; + if(s > YAFFS_BLOCK_STATE_DEAD || s < YAFFS_BLOCK_STATE_UNKNOWN) + bs[0]++; + else + bs[s]++; + } +} diff --git a/fs/yaffs2/yaffs_guts.h b/fs/yaffs2/yaffs_guts.h new file mode 100644 index 00000000..e2b2fb93 --- /dev/null +++ b/fs/yaffs2/yaffs_guts.h @@ -0,0 +1,1026 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_GUTS_H__ +#define __YAFFS_GUTS_H__ + +#include "yportenv.h" + +#define YAFFS_OK 1 +#define YAFFS_FAIL 0 + +/* Give us a Y=0x59, + * Give us an A=0x41, + * Give us an FF=0xff + * Give us an S=0x53 + * And what have we got... + */ +#define YAFFS_MAGIC 0x5941ff53 + +/* + * Tnodes form a tree with the tnodes in "levels" + * Levels greater than 0 hold 8 slots which point to other tnodes. + * Those at level 0 hold 16 slots which point to chunks in NAND. + * + * A maximum level of 8 thust supports files of size up to: + * + * 2^(3*MAX_LEVEL+4) + * + * Thus a max level of 8 supports files with up to 2^^28 chunks which gives + * a maximum file size of around 512Gbytees with 2k chunks. + */ +#define YAFFS_NTNODES_LEVEL0 16 +#define YAFFS_TNODES_LEVEL0_BITS 4 +#define YAFFS_TNODES_LEVEL0_MASK 0xf + +#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2) +#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1) +#define YAFFS_TNODES_INTERNAL_MASK 0x7 +#define YAFFS_TNODES_MAX_LEVEL 8 +#define YAFFS_TNODES_MAX_BITS (YAFFS_TNODES_LEVEL0_BITS + \ + YAFFS_TNODES_INTERNAL_BITS * \ + YAFFS_TNODES_MAX_LEVEL) +#define YAFFS_MAX_CHUNK_ID ((1 << YAFFS_TNODES_MAX_BITS) - 1) + +#define YAFFS_MAX_FILE_SIZE_32 0x7fffffff + +/* Constants for YAFFS1 mode */ +#define YAFFS_BYTES_PER_SPARE 16 +#define YAFFS_BYTES_PER_CHUNK 512 +#define YAFFS_CHUNK_SIZE_SHIFT 9 +#define YAFFS_CHUNKS_PER_BLOCK 32 +#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK) + +#define YAFFS_MIN_YAFFS2_CHUNK_SIZE 1024 +#define YAFFS_MIN_YAFFS2_SPARE_SIZE 32 + + + +#define YAFFS_ALLOCATION_NOBJECTS 100 +#define YAFFS_ALLOCATION_NTNODES 100 +#define YAFFS_ALLOCATION_NLINKS 100 + +#define YAFFS_NOBJECT_BUCKETS 256 + +#define YAFFS_OBJECT_SPACE 0x40000 +#define YAFFS_MAX_OBJECT_ID (YAFFS_OBJECT_SPACE - 1) + +/* Binary data version stamps */ +#define YAFFS_SUMMARY_VERSION 1 +#define YAFFS_CHECKPOINT_VERSION 7 + +#ifdef CONFIG_YAFFS_UNICODE +#define YAFFS_MAX_NAME_LENGTH 127 +#define YAFFS_MAX_ALIAS_LENGTH 79 +#else +#define YAFFS_MAX_NAME_LENGTH 255 +#define YAFFS_MAX_ALIAS_LENGTH 159 +#endif + +#define YAFFS_SHORT_NAME_LENGTH 15 + +/* Some special object ids for pseudo objects */ +#define YAFFS_OBJECTID_ROOT 1 +#define YAFFS_OBJECTID_LOSTNFOUND 2 +#define YAFFS_OBJECTID_UNLINKED 3 +#define YAFFS_OBJECTID_DELETED 4 + +/* Fake object Id for summary data */ +#define YAFFS_OBJECTID_SUMMARY 0x10 + +/* Pseudo object ids for checkpointing */ +#define YAFFS_OBJECTID_CHECKPOINT_DATA 0x20 +#define YAFFS_SEQUENCE_CHECKPOINT_DATA 0x21 + +#define YAFFS_MAX_SHORT_OP_CACHES 20 + +#define YAFFS_N_TEMP_BUFFERS 6 + +/* We limit the number attempts at sucessfully saving a chunk of data. + * Small-page devices have 32 pages per block; large-page devices have 64. + * Default to something in the order of 5 to 10 blocks worth of chunks. + */ +#define YAFFS_WR_ATTEMPTS (5*64) + +/* Sequence numbers are used in YAFFS2 to determine block allocation order. + * The range is limited slightly to help distinguish bad numbers from good. + * This also allows us to perhaps in the future use special numbers for + * special purposes. + * EFFFFF00 allows the allocation of 8 blocks/second (~1Mbytes) for 15 years, + * and is a larger number than the lifetime of a 2GB device. + */ +#define YAFFS_LOWEST_SEQUENCE_NUMBER 0x00001000 +#define YAFFS_HIGHEST_SEQUENCE_NUMBER 0xefffff00 + +/* Special sequence number for bad block that failed to be marked bad */ +#define YAFFS_SEQUENCE_BAD_BLOCK 0xffff0000 + +/* ChunkCache is used for short read/write operations.*/ +struct yaffs_cache { + struct yaffs_obj *object; + int chunk_id; + int last_use; + int dirty; + int n_bytes; /* Only valid if the cache is dirty */ + int locked; /* Can't push out or flush while locked. */ + u8 *data; +}; + +/* yaffs1 tags structures in RAM + * NB This uses bitfield. Bitfields should not straddle a u32 boundary + * otherwise the structure size will get blown out. + */ + +struct yaffs_tags { + u32 chunk_id:20; + u32 serial_number:2; + u32 n_bytes_lsb:10; + u32 obj_id:18; + u32 ecc:12; + u32 n_bytes_msb:2; +}; + +union yaffs_tags_union { + struct yaffs_tags as_tags; + u8 as_bytes[8]; +}; + + +/* Stuff used for extended tags in YAFFS2 */ + +enum yaffs_ecc_result { + YAFFS_ECC_RESULT_UNKNOWN, + YAFFS_ECC_RESULT_NO_ERROR, + YAFFS_ECC_RESULT_FIXED, + YAFFS_ECC_RESULT_UNFIXED +}; + +/* + * Object type enum: + * When this is stored in flash we store it as a u32 instead + * to prevent any alignment change issues as compiler variants change. + */ + +enum yaffs_obj_type { + YAFFS_OBJECT_TYPE_UNKNOWN, + YAFFS_OBJECT_TYPE_FILE, + YAFFS_OBJECT_TYPE_SYMLINK, + YAFFS_OBJECT_TYPE_DIRECTORY, + YAFFS_OBJECT_TYPE_HARDLINK, + YAFFS_OBJECT_TYPE_SPECIAL +}; + +#define YAFFS_OBJECT_TYPE_MAX YAFFS_OBJECT_TYPE_SPECIAL + +struct yaffs_ext_tags { + unsigned chunk_used; /* Status of the chunk: used or unused */ + unsigned obj_id; /* If 0 this is not used */ + unsigned chunk_id; /* If 0 this is a header, else a data chunk */ + unsigned n_bytes; /* Only valid for data chunks */ + + /* The following stuff only has meaning when we read */ + enum yaffs_ecc_result ecc_result; + unsigned block_bad; + + /* YAFFS 1 stuff */ + unsigned is_deleted; /* The chunk is marked deleted */ + unsigned serial_number; /* Yaffs1 2-bit serial number */ + + /* YAFFS2 stuff */ + unsigned seq_number; /* The sequence number of this block */ + + /* Extra info if this is an object header (YAFFS2 only) */ + + unsigned extra_available; /* Extra info available if not zero */ + unsigned extra_parent_id; /* The parent object */ + unsigned extra_is_shrink; /* Is it a shrink header? */ + unsigned extra_shadows; /* Does this shadow another object? */ + + enum yaffs_obj_type extra_obj_type; /* What object type? */ + + loff_t extra_file_size; /* Length if it is a file */ + unsigned extra_equiv_id; /* Equivalent object for a hard link */ +}; + +/* Spare structure for YAFFS1 */ +struct yaffs_spare { + u8 tb0; + u8 tb1; + u8 tb2; + u8 tb3; + u8 page_status; /* set to 0 to delete the chunk */ + u8 block_status; + u8 tb4; + u8 tb5; + u8 ecc1[3]; + u8 tb6; + u8 tb7; + u8 ecc2[3]; +}; + +/*Special structure for passing through to mtd */ +struct yaffs_nand_spare { + struct yaffs_spare spare; + int eccres1; + int eccres2; +}; + +/* Block data in RAM */ + +enum yaffs_block_state { + YAFFS_BLOCK_STATE_UNKNOWN = 0, + + YAFFS_BLOCK_STATE_SCANNING, + /* Being scanned */ + + YAFFS_BLOCK_STATE_NEEDS_SCAN, + /* The block might have something on it (ie it is allocating or full, + * perhaps empty) but it needs to be scanned to determine its true + * state. + * This state is only valid during scanning. + * NB We tolerate empty because the pre-scanner might be incapable of + * deciding + * However, if this state is returned on a YAFFS2 device, + * then we expect a sequence number + */ + + YAFFS_BLOCK_STATE_EMPTY, + /* This block is empty */ + + YAFFS_BLOCK_STATE_ALLOCATING, + /* This block is partially allocated. + * At least one page holds valid data. + * This is the one currently being used for page + * allocation. Should never be more than one of these. + * If a block is only partially allocated at mount it is treated as + * full. + */ + + YAFFS_BLOCK_STATE_FULL, + /* All the pages in this block have been allocated. + * If a block was only partially allocated when mounted we treat + * it as fully allocated. + */ + + YAFFS_BLOCK_STATE_DIRTY, + /* The block was full and now all chunks have been deleted. + * Erase me, reuse me. + */ + + YAFFS_BLOCK_STATE_CHECKPOINT, + /* This block is assigned to holding checkpoint data. */ + + YAFFS_BLOCK_STATE_COLLECTING, + /* This block is being garbage collected */ + + YAFFS_BLOCK_STATE_DEAD + /* This block has failed and is not in use */ +}; + +#define YAFFS_NUMBER_OF_BLOCK_STATES (YAFFS_BLOCK_STATE_DEAD + 1) + +struct yaffs_block_info { + + s32 soft_del_pages:10; /* number of soft deleted pages */ + s32 pages_in_use:10; /* number of pages in use */ + u32 block_state:4; /* One of the above block states. */ + /* NB use unsigned because enum is sometimes + * an int */ + u32 needs_retiring:1; /* Data has failed on this block, */ + /*need to get valid data off and retire*/ + u32 skip_erased_check:1;/* Skip the erased check on this block */ + u32 gc_prioritise:1; /* An ECC check or blank check has failed. + Block should be prioritised for GC */ + u32 chunk_error_strikes:3; /* How many times we've had ecc etc + failures on this block and tried to reuse it */ + u32 has_summary:1; /* The block has a summary */ + + u32 has_shrink_hdr:1; /* This block has at least one shrink header */ + u32 seq_number; /* block sequence number for yaffs2 */ + +}; + +/* -------------------------- Object structure -------------------------------*/ +/* This is the object structure as stored on NAND */ + +struct yaffs_obj_hdr { + u32 type; /* enum yaffs_obj_type */ + + /* Apply to everything */ + int parent_obj_id; + u16 sum_no_longer_used; /* checksum of name. No longer used */ + YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; + + /* The following apply to all object types except for hard links */ + u32 yst_mode; /* protection */ + + u32 yst_uid; + u32 yst_gid; + u32 yst_atime; + u32 yst_mtime; + u32 yst_ctime; + + /* File size applies to files only */ + u32 file_size_low; + + /* Equivalent object id applies to hard links only. */ + int equiv_id; + + /* Alias is for symlinks only. */ + YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1]; + + u32 yst_rdev; /* stuff for block and char devices (major/min) */ + + u32 win_ctime[2]; + u32 win_atime[2]; + u32 win_mtime[2]; + + u32 inband_shadowed_obj_id; + u32 inband_is_shrink; + + u32 file_size_high; + u32 reserved[1]; + int shadows_obj; /* This object header shadows the + specified object if > 0 */ + + /* is_shrink applies to object headers written when wemake a hole. */ + u32 is_shrink; + +}; + +/*--------------------------- Tnode -------------------------- */ + +struct yaffs_tnode { + struct yaffs_tnode *internal[YAFFS_NTNODES_INTERNAL]; +}; + +/*------------------------ Object -----------------------------*/ +/* An object can be one of: + * - a directory (no data, has children links + * - a regular file (data.... not prunes :->). + * - a symlink [symbolic link] (the alias). + * - a hard link + */ + +/* The file variant has three file sizes: + * - file_size : size of file as written into Yaffs - including data in cache. + * - stored_size - size of file as stored on media. + * - shrink_size - size of file that has been shrunk back to. + * + * The stored_size and file_size might be different because the data written + * into the cache will increase the file_size but the stored_size will only + * change when the data is actually stored. + * + */ +struct yaffs_file_var { + loff_t file_size; + loff_t stored_size; + loff_t shrink_size; + int top_level; + struct yaffs_tnode *top; +}; + +struct yaffs_dir_var { + struct list_head children; /* list of child links */ + struct list_head dirty; /* Entry for list of dirty directories */ +}; + +struct yaffs_symlink_var { + YCHAR *alias; +}; + +struct yaffs_hardlink_var { + struct yaffs_obj *equiv_obj; + u32 equiv_id; +}; + +union yaffs_obj_var { + struct yaffs_file_var file_variant; + struct yaffs_dir_var dir_variant; + struct yaffs_symlink_var symlink_variant; + struct yaffs_hardlink_var hardlink_variant; +}; + +struct yaffs_obj { + u8 deleted:1; /* This should only apply to unlinked files. */ + u8 soft_del:1; /* it has also been soft deleted */ + u8 unlinked:1; /* An unlinked file.*/ + u8 fake:1; /* A fake object has no presence on NAND. */ + u8 rename_allowed:1; /* Some objects cannot be renamed. */ + u8 unlink_allowed:1; + u8 dirty:1; /* the object needs to be written to flash */ + u8 valid:1; /* When the file system is being loaded up, this + * object might be created before the data + * is available + * ie. file data chunks encountered before + * the header. + */ + u8 lazy_loaded:1; /* This object has been lazy loaded and + * is missing some detail */ + + u8 defered_free:1; /* Object is removed from NAND, but is + * still in the inode cache. + * Free of object is defered. + * until the inode is released. + */ + u8 being_created:1; /* This object is still being created + * so skip some verification checks. */ + u8 is_shadowed:1; /* This object is shadowed on the way + * to being renamed. */ + + u8 xattr_known:1; /* We know if this has object has xattribs + * or not. */ + u8 has_xattr:1; /* This object has xattribs. + * Only valid if xattr_known. */ + + u8 serial; /* serial number of chunk in NAND.*/ + u16 sum; /* sum of the name to speed searching */ + + struct yaffs_dev *my_dev; /* The device I'm on */ + + struct list_head hash_link; /* list of objects in hash bucket */ + + struct list_head hard_links; /* hard linked object chain*/ + + /* directory structure stuff */ + /* also used for linking up the free list */ + struct yaffs_obj *parent; + struct list_head siblings; + + /* Where's my object header in NAND? */ + int hdr_chunk; + + int n_data_chunks; /* Number of data chunks for this file. */ + + u32 obj_id; /* the object id value */ + + u32 yst_mode; + + YCHAR short_name[YAFFS_SHORT_NAME_LENGTH + 1]; + +#ifdef CONFIG_YAFFS_WINCE + u32 win_ctime[2]; + u32 win_mtime[2]; + u32 win_atime[2]; +#else + u32 yst_uid; + u32 yst_gid; + u32 yst_atime; + u32 yst_mtime; + u32 yst_ctime; +#endif + + u32 yst_rdev; + + void *my_inode; + + u32 variant_type; /* enum yaffs_object_type */ + + union yaffs_obj_var variant; + +}; + +struct yaffs_obj_bucket { + struct list_head list; + int count; +}; + +/* yaffs_checkpt_obj holds the definition of an object as dumped + * by checkpointing. + */ + +struct yaffs_checkpt_obj { + int struct_type; + u32 obj_id; + u32 parent_id; + int hdr_chunk; + u32 variant_type:3; /* enum yaffs_obj_type */ + u8 deleted:1; + u8 soft_del:1; + u8 unlinked:1; + u8 fake:1; + u8 rename_allowed:1; + u8 unlink_allowed:1; + u8 serial; + int n_data_chunks; + loff_t size_or_equiv_obj; +}; + +/*--------------------- Temporary buffers ---------------- + * + * These are chunk-sized working buffers. Each device has a few. + */ + +struct yaffs_buffer { + u8 *buffer; + int in_use; +}; + +/*----------------- Device ---------------------------------*/ + +struct yaffs_param { + const YCHAR *name; + + /* + * Entry parameters set up way early. Yaffs sets up the rest. + * The structure should be zeroed out before use so that unused + * and default values are zero. + */ + + int inband_tags; /* Use unband tags */ + u32 total_bytes_per_chunk; /* Should be >= 512, does not need to + be a power of 2 */ + int chunks_per_block; /* does not need to be a power of 2 */ + int spare_bytes_per_chunk; /* spare area size */ + int start_block; /* Start block we're allowed to use */ + int end_block; /* End block we're allowed to use */ + int n_reserved_blocks; /* Tuneable so that we can reduce + * reserved blocks on NOR and RAM. */ + + int n_caches; /* If <= 0, then short op caching is disabled, + * else the number of short op caches. + */ + int cache_bypass_aligned; /* If non-zero then bypass the cache for + * aligned writes. + */ + + int use_nand_ecc; /* Flag to decide whether or not to use + * NAND driver ECC on data (yaffs1) */ + int tags_9bytes; /* Use 9 byte tags */ + int no_tags_ecc; /* Flag to decide whether or not to do ECC + * on packed tags (yaffs2) */ + + int is_yaffs2; /* Use yaffs2 mode on this device */ + + int empty_lost_n_found; /* Auto-empty lost+found directory on mount */ + + int refresh_period; /* How often to check for a block refresh */ + + /* Checkpoint control. Can be set before or after initialisation */ + u8 skip_checkpt_rd; + u8 skip_checkpt_wr; + + int enable_xattr; /* Enable xattribs */ + + int max_objects; /* + * Set to limit the number of objects created. + * 0 = no limit. + */ + + /* The remove_obj_fn function must be supplied by OS flavours that + * need it. + * yaffs direct uses it to implement the faster readdir. + * Linux uses it to protect the directory during unlocking. + */ + void (*remove_obj_fn) (struct yaffs_obj *obj); + + /* Callback to mark the superblock dirty */ + void (*sb_dirty_fn) (struct yaffs_dev *dev); + + /* Callback to control garbage collection. */ + unsigned (*gc_control_fn) (struct yaffs_dev *dev); + + /* Debug control flags. Don't use unless you know what you're doing */ + int use_header_file_size; /* Flag to determine if we should use + * file sizes from the header */ + int disable_lazy_load; /* Disable lazy loading on this device */ + int wide_tnodes_disabled; /* Set to disable wide tnodes */ + int disable_soft_del; /* yaffs 1 only: Set to disable the use of + * softdeletion. */ + + int defered_dir_update; /* Set to defer directory updates */ + +#ifdef CONFIG_YAFFS_AUTO_UNICODE + int auto_unicode; +#endif + int always_check_erased; /* Force chunk erased check always on */ + + int disable_summary; + int disable_bad_block_marking; + +}; + +struct yaffs_driver { + int (*drv_write_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, + const u8 *data, int data_len, + const u8 *oob, int oob_len); + int (*drv_read_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, + u8 *data, int data_len, + u8 *oob, int oob_len, + enum yaffs_ecc_result *ecc_result); + int (*drv_erase_fn) (struct yaffs_dev *dev, int block_no); + int (*drv_mark_bad_fn) (struct yaffs_dev *dev, int block_no); + int (*drv_check_bad_fn) (struct yaffs_dev *dev, int block_no); + int (*drv_initialise_fn) (struct yaffs_dev *dev); + int (*drv_deinitialise_fn) (struct yaffs_dev *dev); +}; + +struct yaffs_tags_handler { + int (*write_chunk_tags_fn) (struct yaffs_dev *dev, + int nand_chunk, const u8 *data, + const struct yaffs_ext_tags *tags); + int (*read_chunk_tags_fn) (struct yaffs_dev *dev, + int nand_chunk, u8 *data, + struct yaffs_ext_tags *tags); + + int (*query_block_fn) (struct yaffs_dev *dev, int block_no, + enum yaffs_block_state *state, + u32 *seq_number); + int (*mark_bad_fn) (struct yaffs_dev *dev, int block_no); +}; + +struct yaffs_dev { + struct yaffs_param param; + struct yaffs_driver drv; + struct yaffs_tags_handler tagger; + + /* Context storage. Holds extra OS specific data for this device */ + + void *os_context; + void *driver_context; + + struct list_head dev_list; + + int ll_init; + /* Runtime parameters. Set up by YAFFS. */ + int data_bytes_per_chunk; + + /* Non-wide tnode stuff */ + u16 chunk_grp_bits; /* Number of bits that need to be resolved if + * the tnodes are not wide enough. + */ + u16 chunk_grp_size; /* == 2^^chunk_grp_bits */ + + /* Stuff to support wide tnodes */ + u32 tnode_width; + u32 tnode_mask; + u32 tnode_size; + + /* Stuff for figuring out file offset to chunk conversions */ + u32 chunk_shift; /* Shift value */ + u32 chunk_div; /* Divisor after shifting: 1 for 2^n sizes */ + u32 chunk_mask; /* Mask to use for power-of-2 case */ + + int is_mounted; + int read_only; + int is_checkpointed; + + /* Stuff to support block offsetting to support start block zero */ + int internal_start_block; + int internal_end_block; + int block_offset; + int chunk_offset; + + /* Runtime checkpointing stuff */ + int checkpt_page_seq; /* running sequence number of checkpt pages */ + int checkpt_byte_count; + int checkpt_byte_offs; + u8 *checkpt_buffer; + int checkpt_open_write; + int blocks_in_checkpt; + int checkpt_cur_chunk; + int checkpt_cur_block; + int checkpt_next_block; + int *checkpt_block_list; + int checkpt_max_blocks; + u32 checkpt_sum; + u32 checkpt_xor; + + int checkpoint_blocks_required; /* Number of blocks needed to store + * current checkpoint set */ + + /* Block Info */ + struct yaffs_block_info *block_info; + u8 *chunk_bits; /* bitmap of chunks in use */ + u8 block_info_alt:1; /* allocated using alternative alloc */ + u8 chunk_bits_alt:1; /* allocated using alternative alloc */ + int chunk_bit_stride; /* Number of bytes of chunk_bits per block. + * Must be consistent with chunks_per_block. + */ + + int n_erased_blocks; + int alloc_block; /* Current block being allocated off */ + u32 alloc_page; + int alloc_block_finder; /* Used to search for next allocation block */ + + /* Object and Tnode memory management */ + void *allocator; + int n_obj; + int n_tnodes; + + int n_hardlinks; + + struct yaffs_obj_bucket obj_bucket[YAFFS_NOBJECT_BUCKETS]; + u32 bucket_finder; + + int n_free_chunks; + + /* Garbage collection control */ + u32 *gc_cleanup_list; /* objects to delete at the end of a GC. */ + u32 n_clean_ups; + + unsigned has_pending_prioritised_gc; /* We think this device might + have pending prioritised gcs */ + unsigned gc_disable; + unsigned gc_block_finder; + unsigned gc_dirtiest; + unsigned gc_pages_in_use; + unsigned gc_not_done; + unsigned gc_block; + unsigned gc_chunk; + unsigned gc_skip; + struct yaffs_summary_tags *gc_sum_tags; + + /* Special directories */ + struct yaffs_obj *root_dir; + struct yaffs_obj *lost_n_found; + + int buffered_block; /* Which block is buffered here? */ + int doing_buffered_block_rewrite; + + struct yaffs_cache *cache; + int cache_last_use; + + /* Stuff for background deletion and unlinked files. */ + struct yaffs_obj *unlinked_dir; /* Directory where unlinked and deleted + files live. */ + struct yaffs_obj *del_dir; /* Directory where deleted objects are + sent to disappear. */ + struct yaffs_obj *unlinked_deletion; /* Current file being + background deleted. */ + int n_deleted_files; /* Count of files awaiting deletion; */ + int n_unlinked_files; /* Count of unlinked files. */ + int n_bg_deletions; /* Count of background deletions. */ + + /* Temporary buffer management */ + struct yaffs_buffer temp_buffer[YAFFS_N_TEMP_BUFFERS]; + int max_temp; + int temp_in_use; + int unmanaged_buffer_allocs; + int unmanaged_buffer_deallocs; + + /* yaffs2 runtime stuff */ + unsigned seq_number; /* Sequence number of currently + allocating block */ + unsigned oldest_dirty_seq; + unsigned oldest_dirty_block; + + /* Block refreshing */ + int refresh_skip; /* A skip down counter. + * Refresh happens when this gets to zero. */ + + /* Dirty directory handling */ + struct list_head dirty_dirs; /* List of dirty directories */ + + /* Summary */ + int chunks_per_summary; + struct yaffs_summary_tags *sum_tags; + + /* Statistics */ + u32 n_page_writes; + u32 n_page_reads; + u32 n_erasures; + u32 n_bad_queries; + u32 n_bad_markings; + u32 n_erase_failures; + u32 n_gc_copies; + u32 all_gcs; + u32 passive_gc_count; + u32 oldest_dirty_gc_count; + u32 n_gc_blocks; + u32 bg_gcs; + u32 n_retried_writes; + u32 n_retired_blocks; + u32 n_ecc_fixed; + u32 n_ecc_unfixed; + u32 n_tags_ecc_fixed; + u32 n_tags_ecc_unfixed; + u32 n_deletions; + u32 n_unmarked_deletions; + u32 refresh_count; + u32 cache_hits; + u32 tags_used; + u32 summary_used; + +}; + +/* The CheckpointDevice structure holds the device information that changes + *at runtime and must be preserved over unmount/mount cycles. + */ +struct yaffs_checkpt_dev { + int struct_type; + int n_erased_blocks; + int alloc_block; /* Current block being allocated off */ + u32 alloc_page; + int n_free_chunks; + + int n_deleted_files; /* Count of files awaiting deletion; */ + int n_unlinked_files; /* Count of unlinked files. */ + int n_bg_deletions; /* Count of background deletions. */ + + /* yaffs2 runtime stuff */ + unsigned seq_number; /* Sequence number of currently + * allocating block */ + +}; + +struct yaffs_checkpt_validity { + int struct_type; + u32 magic; + u32 version; + u32 head; +}; + +struct yaffs_shadow_fixer { + int obj_id; + int shadowed_id; + struct yaffs_shadow_fixer *next; +}; + +/* Structure for doing xattr modifications */ +struct yaffs_xattr_mod { + int set; /* If 0 then this is a deletion */ + const YCHAR *name; + const void *data; + int size; + int flags; + int result; +}; + +/*----------------------- YAFFS Functions -----------------------*/ + +int yaffs_guts_initialise(struct yaffs_dev *dev); +void yaffs_deinitialise(struct yaffs_dev *dev); + +int yaffs_get_n_free_chunks(struct yaffs_dev *dev); + +int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR * old_name, + struct yaffs_obj *new_dir, const YCHAR * new_name); + +int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR * name); +int yaffs_del_obj(struct yaffs_obj *obj); +struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj, + enum yaffs_obj_type type); + + +int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR * name, int buffer_size); +loff_t yaffs_get_obj_length(struct yaffs_obj *obj); +int yaffs_get_obj_inode(struct yaffs_obj *obj); +unsigned yaffs_get_obj_type(struct yaffs_obj *obj); +int yaffs_get_obj_link_count(struct yaffs_obj *obj); + +/* File operations */ +int yaffs_file_rd(struct yaffs_obj *obj, u8 * buffer, loff_t offset, + int n_bytes); +int yaffs_wr_file(struct yaffs_obj *obj, const u8 * buffer, loff_t offset, + int n_bytes, int write_trhrough); +int yaffs_resize_file(struct yaffs_obj *obj, loff_t new_size); + +struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, + const YCHAR *name, u32 mode, u32 uid, + u32 gid); + +int yaffs_flush_file(struct yaffs_obj *in, + int update_time, + int data_sync, + int discard_cache); + +/* Flushing and checkpointing */ +void yaffs_flush_whole_cache(struct yaffs_dev *dev, int discard); + +int yaffs_checkpoint_save(struct yaffs_dev *dev); +int yaffs_checkpoint_restore(struct yaffs_dev *dev); + +/* Directory operations */ +struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, + u32 mode, u32 uid, u32 gid); +struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *the_dir, + const YCHAR *name); +struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number); + +/* Link operations */ +struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR *name, + struct yaffs_obj *equiv_obj); + +struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj); + +/* Symlink operations */ +struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, + const YCHAR *name, u32 mode, u32 uid, + u32 gid, const YCHAR *alias); +YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj); + +/* Special inodes (fifos, sockets and devices) */ +struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, + const YCHAR *name, u32 mode, u32 uid, + u32 gid, u32 rdev); + +int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR *name, + const void *value, int size, int flags); +int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR *name, void *value, + int size); +int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size); +int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR *name); + +/* Special directories */ +struct yaffs_obj *yaffs_root(struct yaffs_dev *dev); +struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev); + +void yaffs_handle_defered_free(struct yaffs_obj *obj); + +void yaffs_update_dirty_dirs(struct yaffs_dev *dev); + +int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency); + +/* Debug dump */ +int yaffs_dump_obj(struct yaffs_obj *obj); + +void yaffs_guts_test(struct yaffs_dev *dev); +int yaffs_guts_ll_init(struct yaffs_dev *dev); + + +/* A few useful functions to be used within the core files*/ +void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, + int lyn); +int yaffs_check_ff(u8 *buffer, int n_bytes); +void yaffs_handle_chunk_error(struct yaffs_dev *dev, + struct yaffs_block_info *bi); + +u8 *yaffs_get_temp_buffer(struct yaffs_dev *dev); +void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer); + +struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, + int number, + enum yaffs_obj_type type); +int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, + int nand_chunk, int in_scan); +void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR *name); +void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, + const struct yaffs_obj_hdr *oh); +void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj); +YCHAR *yaffs_clone_str(const YCHAR *str); +void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list); +void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no); +int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, + int force, int is_shrink, int shadows, + struct yaffs_xattr_mod *xop); +void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, + int backward_scanning); +int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks); +struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev); +struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, + struct yaffs_file_var *file_struct, + u32 chunk_id, + struct yaffs_tnode *passed_tn); + +int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, + int n_bytes, int write_trhrough); +void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size); +void yaffs_skip_rest_of_block(struct yaffs_dev *dev); + +int yaffs_count_free_chunks(struct yaffs_dev *dev); + +struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, + struct yaffs_file_var *file_struct, + u32 chunk_id); + +u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, + unsigned pos); + +int yaffs_is_non_empty_dir(struct yaffs_obj *obj); + +int yaffs_guts_format_dev(struct yaffs_dev *dev); + +void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, + int *chunk_out, u32 *offset_out); +/* + * Marshalling functions to get loff_t file sizes into aand out of + * object headers. + */ +void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize); +loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh); +loff_t yaffs_max_file_size(struct yaffs_dev *dev); + +/* + * Debug function to count number of blocks in each state + * NB Needs to be called with correct number of integers + */ + +void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]); + +int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, + struct yaffs_ext_tags *tags); + +#endif diff --git a/fs/yaffs2/yaffs_linux.h b/fs/yaffs2/yaffs_linux.h new file mode 100644 index 00000000..c20ab14b --- /dev/null +++ b/fs/yaffs2/yaffs_linux.h @@ -0,0 +1,48 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_LINUX_H__ +#define __YAFFS_LINUX_H__ + +#include "yportenv.h" + +struct yaffs_linux_context { + struct list_head context_list; /* List of these we have mounted */ + struct yaffs_dev *dev; + struct super_block *super; + struct task_struct *bg_thread; /* Background thread for this device */ + int bg_running; + struct mutex gross_lock; /* Gross locking mutex*/ + u8 *spare_buffer; /* For mtdif2 use. Don't know the buffer size + * at compile time so we have to allocate it. + */ + struct list_head search_contexts; + struct task_struct *readdir_process; + unsigned mount_id; + int dirty; +}; + +#define yaffs_dev_to_lc(dev) ((struct yaffs_linux_context *)((dev)->os_context)) +#define yaffs_dev_to_mtd(dev) ((struct mtd_info *)((dev)->driver_context)) + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) +#define WRITE_SIZE_STR "writesize" +#define WRITE_SIZE(mtd) ((mtd)->writesize) +#else +#define WRITE_SIZE_STR "oobblock" +#define WRITE_SIZE(mtd) ((mtd)->oobblock) +#endif + +#endif diff --git a/fs/yaffs2/yaffs_mtdif.h b/fs/yaffs2/yaffs_mtdif.h new file mode 100644 index 00000000..9cff224c --- /dev/null +++ b/fs/yaffs2/yaffs_mtdif.h @@ -0,0 +1,25 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_MTDIF_H__ +#define __YAFFS_MTDIF_H__ + +#include "yaffs_guts.h" + +void yaffs_mtd_drv_install(struct yaffs_dev *dev); +struct mtd_info * yaffs_get_mtd_device(dev_t sdev); +void yaffs_put_mtd_device(struct mtd_info *mtd); +int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags); +#endif diff --git a/fs/yaffs2/yaffs_mtdif_multi.c b/fs/yaffs2/yaffs_mtdif_multi.c new file mode 100644 index 00000000..7c01461a --- /dev/null +++ b/fs/yaffs2/yaffs_mtdif_multi.c @@ -0,0 +1,310 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "yportenv.h" + +#include "yaffs_mtdif.h" + +#include "linux/mtd/mtd.h" +#include "linux/types.h" +#include "linux/time.h" +#include "linux/mtd/nand.h" +#include "linux/kernel.h" +#include "linux/version.h" +#include "linux/types.h" +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) +#include "uapi/linux/major.h" +#endif + +#include "yaffs_trace.h" +#include "yaffs_guts.h" +#include "yaffs_linux.h" + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) +#define MTD_OPS_AUTO_OOB MTD_OOB_AUTO +#endif + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) +#define mtd_erase(m, ei) (m)->erase(m, ei) +#define mtd_write_oob(m, addr, pops) (m)->write_oob(m, addr, pops) +#define mtd_read_oob(m, addr, pops) (m)->read_oob(m, addr, pops) +#define mtd_block_isbad(m, offs) (m)->block_isbad(m, offs) +#define mtd_block_markbad(m, offs) (m)->block_markbad(m, offs) +#endif + + + +int nandmtd_erase_block(struct yaffs_dev *dev, int block_no) +{ + struct mtd_info *mtd = yaffs_dev_to_mtd(dev); + u32 addr = + ((loff_t) block_no) * dev->param.total_bytes_per_chunk * + dev->param.chunks_per_block; + struct erase_info ei; + int retval = 0; + + ei.mtd = mtd; + ei.addr = addr; + ei.len = dev->param.total_bytes_per_chunk * dev->param.chunks_per_block; + ei.time = 1000; + ei.retries = 2; + ei.callback = NULL; + ei.priv = (u_long) dev; + + retval = mtd_erase(mtd, &ei); + + if (retval == 0) + return YAFFS_OK; + + return YAFFS_FAIL; +} + + +static int yaffs_mtd_write(struct yaffs_dev *dev, int nand_chunk, + const u8 *data, int data_len, + const u8 *oob, int oob_len) +{ + struct mtd_info *mtd = yaffs_dev_to_mtd(dev); + loff_t addr; + struct mtd_oob_ops ops; + int retval; + + yaffs_trace(YAFFS_TRACE_MTD, + "yaffs_mtd_write(%p, %d, %p, %d, %p, %d)\n", + dev, nand_chunk, data, data_len, oob, oob_len); + + if (!data || !data_len) { + data = NULL; + data_len = 0; + } + + if (!oob || !oob_len) { + oob = NULL; + oob_len = 0; + } + + addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; + memset(&ops, 0, sizeof(ops)); + ops.mode = MTD_OPS_AUTO_OOB; + ops.len = (data) ? data_len : 0; + ops.ooblen = oob_len; + ops.datbuf = (u8 *)data; + ops.oobbuf = (u8 *)oob; + + retval = mtd_write_oob(mtd, addr, &ops); + if (retval) { + yaffs_trace(YAFFS_TRACE_MTD, + "write_oob failed, chunk %d, mtd error %d", + nand_chunk, retval); + } + return retval ? YAFFS_FAIL : YAFFS_OK; +} + +static int yaffs_mtd_read(struct yaffs_dev *dev, int nand_chunk, + u8 *data, int data_len, + u8 *oob, int oob_len, + enum yaffs_ecc_result *ecc_result) +{ + struct mtd_info *mtd = yaffs_dev_to_mtd(dev); + loff_t addr; + struct mtd_oob_ops ops; + int retval; + + addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; + memset(&ops, 0, sizeof(ops)); + ops.mode = MTD_OPS_AUTO_OOB; + ops.len = (data) ? data_len : 0; + ops.ooblen = oob_len; + ops.datbuf = data; + ops.oobbuf = oob; + +#if (MTD_VERSION_CODE < MTD_VERSION(2, 6, 20)) + /* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug; + * help it out with ops.len = ops.ooblen when ops.datbuf == NULL. + */ + ops.len = (ops.datbuf) ? ops.len : ops.ooblen; +#endif + /* Read page and oob using MTD. + * Check status and determine ECC result. + */ + retval = mtd_read_oob(mtd, addr, &ops); + if (retval) + yaffs_trace(YAFFS_TRACE_MTD, + "read_oob failed, chunk %d, mtd error %d", + nand_chunk, retval); + + switch (retval) { + case 0: + /* no error */ + if(ecc_result) + *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; + break; + + case -EUCLEAN: + /* MTD's ECC fixed the data */ + if(ecc_result) + *ecc_result = YAFFS_ECC_RESULT_FIXED; + dev->n_ecc_fixed++; + break; + + case -EBADMSG: + default: + /* MTD's ECC could not fix the data */ + dev->n_ecc_unfixed++; + if(ecc_result) + *ecc_result = YAFFS_ECC_RESULT_UNFIXED; + return YAFFS_FAIL; + } + + return YAFFS_OK; +} + +static int yaffs_mtd_erase(struct yaffs_dev *dev, int block_no) +{ + struct mtd_info *mtd = yaffs_dev_to_mtd(dev); + + loff_t addr; + struct erase_info ei; + int retval = 0; + u32 block_size; + + block_size = dev->param.total_bytes_per_chunk * + dev->param.chunks_per_block; + addr = ((loff_t) block_no) * block_size; + + ei.mtd = mtd; + ei.addr = addr; + ei.len = block_size; + ei.time = 1000; + ei.retries = 2; + ei.callback = NULL; + ei.priv = (u_long) dev; + + retval = mtd_erase(mtd, &ei); + + if (retval == 0) + return YAFFS_OK; + + return YAFFS_FAIL; +} + +static int yaffs_mtd_mark_bad(struct yaffs_dev *dev, int block_no) +{ + struct mtd_info *mtd = yaffs_dev_to_mtd(dev); + int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; + int retval; + + yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", block_no); + + retval = mtd_block_markbad(mtd, (loff_t) blocksize * block_no); + return (retval) ? YAFFS_FAIL : YAFFS_OK; +} + +static int yaffs_mtd_check_bad(struct yaffs_dev *dev, int block_no) +{ + struct mtd_info *mtd = yaffs_dev_to_mtd(dev); + int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; + int retval; + + yaffs_trace(YAFFS_TRACE_MTD, "checking block %d bad", block_no); + + retval = mtd_block_isbad(mtd, (loff_t) blocksize * block_no); + return (retval) ? YAFFS_FAIL : YAFFS_OK; +} + +static int yaffs_mtd_initialise(struct yaffs_dev *dev) +{ + return YAFFS_OK; +} + +static int yaffs_mtd_deinitialise(struct yaffs_dev *dev) +{ + return YAFFS_OK; +} + + +void yaffs_mtd_drv_install(struct yaffs_dev *dev) +{ + struct yaffs_driver *drv = &dev->drv; + + drv->drv_write_chunk_fn = yaffs_mtd_write; + drv->drv_read_chunk_fn = yaffs_mtd_read; + drv->drv_erase_fn = yaffs_mtd_erase; + drv->drv_mark_bad_fn = yaffs_mtd_mark_bad; + drv->drv_check_bad_fn = yaffs_mtd_check_bad; + drv->drv_initialise_fn = yaffs_mtd_initialise; + drv->drv_deinitialise_fn = yaffs_mtd_deinitialise; +} + + +struct mtd_info * yaffs_get_mtd_device(dev_t sdev) +{ + struct mtd_info *mtd; + + mtd = yaffs_get_mtd_device(sdev); + + /* Check it's an mtd device..... */ + if (MAJOR(sdev) != MTD_BLOCK_MAJOR) + return NULL; /* This isn't an mtd device */ + + /* Check it's NAND */ + if (mtd->type != MTD_NANDFLASH) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "yaffs: MTD device is not NAND it's type %d", + mtd->type); + return NULL; + } + + yaffs_trace(YAFFS_TRACE_OS, " %s %d", WRITE_SIZE_STR, WRITE_SIZE(mtd)); + yaffs_trace(YAFFS_TRACE_OS, " oobsize %d", mtd->oobsize); + yaffs_trace(YAFFS_TRACE_OS, " erasesize %d", mtd->erasesize); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) + yaffs_trace(YAFFS_TRACE_OS, " size %u", mtd->size); +#else + yaffs_trace(YAFFS_TRACE_OS, " size %lld", mtd->size); +#endif + + return mtd; +} + +int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags) +{ + if (yaffs_version == 2) { + if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE || + mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) && + !inband_tags) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "MTD device does not have the right page sizes" + ); + return -1; + } + } else { + if (WRITE_SIZE(mtd) < YAFFS_BYTES_PER_CHUNK || + mtd->oobsize != YAFFS_BYTES_PER_SPARE) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "MTD device does not support have the right page sizes" + ); + return -1; + } + } + + return 0; +} + + +void yaffs_put_mtd_device(struct mtd_info *mtd) +{ + if(mtd) + put_mtd_device(mtd); +} diff --git a/fs/yaffs2/yaffs_mtdif_single.c b/fs/yaffs2/yaffs_mtdif_single.c new file mode 100644 index 00000000..3fdd676e --- /dev/null +++ b/fs/yaffs2/yaffs_mtdif_single.c @@ -0,0 +1,212 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "yportenv.h" + +#include "yaffs_mtdif.h" + +#include "linux/mtd/mtd.h" +#include "linux/types.h" +#include "linux/time.h" +#include "linux/mtd/nand.h" +#include "linux/kernel.h" +#include "linux/version.h" +#include "linux/types.h" + +#include "yaffs_trace.h" +#include "yaffs_guts.h" +#include "yaffs_linux.h" + + +int nandmtd_erase_block(struct yaffs_dev *dev, int block_no) +{ + struct mtd_info *mtd = yaffs_dev_to_mtd(dev); + u32 addr = + ((loff_t) block_no) * dev->param.total_bytes_per_chunk * + dev->param.chunks_per_block; + struct erase_info ei; + int retval = 0; + + ei.mtd = mtd; + ei.addr = addr; + ei.len = dev->param.total_bytes_per_chunk * dev->param.chunks_per_block; + ei.time = 1000; + ei.retries = 2; + ei.callback = NULL; + ei.priv = (u_long) dev; + + retval = mtd->_erase(mtd, &ei); + + if (retval == 0) + return YAFFS_OK; + + return YAFFS_FAIL; +} + + +static int yaffs_mtd_write(struct yaffs_dev *dev, int nand_chunk, + const u8 *data, int data_len, + const u8 *oob, int oob_len) +{ + struct mtd_info *mtd = yaffs_dev_to_mtd(dev); + loff_t addr; + struct mtd_oob_ops ops; + int retval; + + addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; + memset(&ops, 0, sizeof(ops)); + ops.mode = MTD_OPS_AUTO_OOB; + ops.len = (data) ? data_len : 0; + ops.ooblen = oob_len; + ops.datbuf = (u8 *)data; + ops.oobbuf = (u8 *)oob; + + retval = mtd->_write_oob(mtd, addr, &ops); + if (retval) { + yaffs_trace(YAFFS_TRACE_MTD, + "write_oob failed, chunk %d, mtd error %d", + nand_chunk, retval); + } + return retval ? YAFFS_FAIL : YAFFS_OK; +} + +static int yaffs_mtd_read(struct yaffs_dev *dev, int nand_chunk, + u8 *data, int data_len, + u8 *oob, int oob_len, + enum yaffs_ecc_result *ecc_result) +{ + struct mtd_info *mtd = yaffs_dev_to_mtd(dev); + loff_t addr; + struct mtd_oob_ops ops; + int retval; + + addr = ((loff_t) nand_chunk) * dev->data_bytes_per_chunk; + memset(&ops, 0, sizeof(ops)); + ops.mode = MTD_OPS_AUTO_OOB; + ops.len = (data) ? data_len : 0; + ops.ooblen = oob_len; + ops.datbuf = data; + ops.oobbuf = oob; + + /* Read page and oob using MTD. + * Check status and determine ECC result. + */ + retval = mtd->_read_oob(mtd, addr, &ops); + if (retval) + yaffs_trace(YAFFS_TRACE_MTD, + "read_oob failed, chunk %d, mtd error %d", + nand_chunk, retval); + + switch (retval) { + case 0: + /* no error */ + if(ecc_result) + *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; + break; + + case -EUCLEAN: + /* MTD's ECC fixed the data */ + if(ecc_result) + *ecc_result = YAFFS_ECC_RESULT_FIXED; + dev->n_ecc_fixed++; + break; + + case -EBADMSG: + default: + /* MTD's ECC could not fix the data */ + dev->n_ecc_unfixed++; + if(ecc_result) + *ecc_result = YAFFS_ECC_RESULT_UNFIXED; + return YAFFS_FAIL; + } + + return YAFFS_OK; +} + +static int yaffs_mtd_erase(struct yaffs_dev *dev, int block_no) +{ + struct mtd_info *mtd = yaffs_dev_to_mtd(dev); + + loff_t addr; + struct erase_info ei; + int retval = 0; + u32 block_size; + + block_size = dev->param.total_bytes_per_chunk * + dev->param.chunks_per_block; + addr = ((loff_t) block_no) * block_size; + + ei.mtd = mtd; + ei.addr = addr; + ei.len = block_size; + ei.time = 1000; + ei.retries = 2; + ei.callback = NULL; + ei.priv = (u_long) dev; + + retval = mtd->_erase(mtd, &ei); + + if (retval == 0) + return YAFFS_OK; + + return YAFFS_FAIL; +} + +static int yaffs_mtd_mark_bad(struct yaffs_dev *dev, int block_no) +{ + struct mtd_info *mtd = yaffs_dev_to_mtd(dev); + int blocksize = dev->param.chunks_per_block * dev->data_bytes_per_chunk; + int retval; + + yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", block_no); + + retval = mtd->_block_markbad(mtd, (loff_t) blocksize * block_no); + return (retval) ? YAFFS_FAIL : YAFFS_OK; +} + +static int yaffs_mtd_check_bad(struct yaffs_dev *dev, int block_no) +{ + struct mtd_info *mtd = yaffs_dev_to_mtd(dev); + int blocksize = dev->param.chunks_per_block * dev->data_bytes_per_chunk; + int retval; + + yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "checking block %d bad", block_no); + + retval = mtd->_block_isbad(mtd, (loff_t) blocksize * block_no); + return (retval) ? YAFFS_FAIL : YAFFS_OK; +} + +static int yaffs_mtd_initialise(struct yaffs_dev *dev) +{ + return YAFFS_OK; +} + +static int yaffs_mtd_deinitialise(struct yaffs_dev *dev) +{ + return YAFFS_OK; +} + + + +void yaffs_mtd_drv_install(struct yaffs_dev *dev) +{ + struct yaffs_driver *drv = &dev->drv; + + drv->drv_write_chunk_fn = yaffs_mtd_write; + drv->drv_read_chunk_fn = yaffs_mtd_read; + drv->drv_erase_fn = yaffs_mtd_erase; + drv->drv_mark_bad_fn = yaffs_mtd_mark_bad; + drv->drv_check_bad_fn = yaffs_mtd_check_bad; + drv->drv_initialise_fn = yaffs_mtd_initialise; + drv->drv_deinitialise_fn = yaffs_mtd_deinitialise; +} diff --git a/fs/yaffs2/yaffs_nameval.c b/fs/yaffs2/yaffs_nameval.c new file mode 100644 index 00000000..4bdf4ed7 --- /dev/null +++ b/fs/yaffs2/yaffs_nameval.c @@ -0,0 +1,208 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * This simple implementation of a name-value store assumes a small number of +* values and fits into a small finite buffer. + * + * Each attribute is stored as a record: + * sizeof(int) bytes record size. + * strnlen+1 bytes name null terminated. + * nbytes value. + * ---------- + * total size stored in record size + * + * This code has not been tested with unicode yet. + */ + +#include "yaffs_nameval.h" + +#include "yportenv.h" + +static int nval_find(const char *xb, int xb_size, const YCHAR *name, + int *exist_size) +{ + int pos = 0; + int size; + + memcpy(&size, xb, sizeof(int)); + while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { + if (!strncmp((YCHAR *) (xb + pos + sizeof(int)), + name, size)) { + if (exist_size) + *exist_size = size; + return pos; + } + pos += size; + if (pos < xb_size - sizeof(int)) + memcpy(&size, xb + pos, sizeof(int)); + else + size = 0; + } + if (exist_size) + *exist_size = 0; + return -ENODATA; +} + +static int nval_used(const char *xb, int xb_size) +{ + int pos = 0; + int size; + + memcpy(&size, xb + pos, sizeof(int)); + while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { + pos += size; + if (pos < xb_size - sizeof(int)) + memcpy(&size, xb + pos, sizeof(int)); + else + size = 0; + } + return pos; +} + +int nval_del(char *xb, int xb_size, const YCHAR *name) +{ + int pos = nval_find(xb, xb_size, name, NULL); + int size; + + if (pos < 0 || pos >= xb_size) + return -ENODATA; + + /* Find size, shift rest over this record, + * then zero out the rest of buffer */ + memcpy(&size, xb + pos, sizeof(int)); + memcpy(xb + pos, xb + pos + size, xb_size - (pos + size)); + memset(xb + (xb_size - size), 0, size); + return 0; +} + +int nval_set(char *xb, int xb_size, const YCHAR *name, const char *buf, + int bsize, int flags) +{ + int pos; + int namelen = strnlen(name, xb_size); + int reclen; + int size_exist = 0; + int space; + int start; + + pos = nval_find(xb, xb_size, name, &size_exist); + + if (flags & XATTR_CREATE && pos >= 0) + return -EEXIST; + if (flags & XATTR_REPLACE && pos < 0) + return -ENODATA; + + start = nval_used(xb, xb_size); + space = xb_size - start + size_exist; + + reclen = (sizeof(int) + namelen + 1 + bsize); + + if (reclen > space) + return -ENOSPC; + + if (pos >= 0) { + nval_del(xb, xb_size, name); + start = nval_used(xb, xb_size); + } + + pos = start; + + memcpy(xb + pos, &reclen, sizeof(int)); + pos += sizeof(int); + strncpy((YCHAR *) (xb + pos), name, reclen); + pos += (namelen + 1); + memcpy(xb + pos, buf, bsize); + return 0; +} + +int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, + int bsize) +{ + int pos = nval_find(xb, xb_size, name, NULL); + int size; + + if (pos >= 0 && pos < xb_size) { + + memcpy(&size, xb + pos, sizeof(int)); + pos += sizeof(int); /* advance past record length */ + size -= sizeof(int); + + /* Advance over name string */ + while (xb[pos] && size > 0 && pos < xb_size) { + pos++; + size--; + } + /*Advance over NUL */ + pos++; + size--; + + /* If bsize is zero then this is a size query. + * Return the size, but don't copy. + */ + if (!bsize) + return size; + + if (size <= bsize) { + memcpy(buf, xb + pos, size); + return size; + } + } + if (pos >= 0) + return -ERANGE; + + return -ENODATA; +} + +int nval_list(const char *xb, int xb_size, char *buf, int bsize) +{ + int pos = 0; + int size; + int name_len; + int ncopied = 0; + int filled = 0; + + memcpy(&size, xb + pos, sizeof(int)); + while (size > sizeof(int) && + size <= xb_size && + (pos + size) < xb_size && + !filled) { + pos += sizeof(int); + size -= sizeof(int); + name_len = strnlen((YCHAR *) (xb + pos), size); + if (ncopied + name_len + 1 < bsize) { + memcpy(buf, xb + pos, name_len * sizeof(YCHAR)); + buf += name_len; + *buf = '\0'; + buf++; + if (sizeof(YCHAR) > 1) { + *buf = '\0'; + buf++; + } + ncopied += (name_len + 1); + } else { + filled = 1; + } + pos += size; + if (pos < xb_size - sizeof(int)) + memcpy(&size, xb + pos, sizeof(int)); + else + size = 0; + } + return ncopied; +} + +int nval_hasvalues(const char *xb, int xb_size) +{ + return nval_used(xb, xb_size) > 0; +} diff --git a/fs/yaffs2/yaffs_nameval.h b/fs/yaffs2/yaffs_nameval.h new file mode 100644 index 00000000..951e64f8 --- /dev/null +++ b/fs/yaffs2/yaffs_nameval.h @@ -0,0 +1,28 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __NAMEVAL_H__ +#define __NAMEVAL_H__ + +#include "yportenv.h" + +int nval_del(char *xb, int xb_size, const YCHAR * name); +int nval_set(char *xb, int xb_size, const YCHAR * name, const char *buf, + int bsize, int flags); +int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, + int bsize); +int nval_list(const char *xb, int xb_size, char *buf, int bsize); +int nval_hasvalues(const char *xb, int xb_size); +#endif diff --git a/fs/yaffs2/yaffs_nand.c b/fs/yaffs2/yaffs_nand.c new file mode 100644 index 00000000..0d8499bd --- /dev/null +++ b/fs/yaffs2/yaffs_nand.c @@ -0,0 +1,122 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "yaffs_nand.h" +#include "yaffs_tagscompat.h" + +#include "yaffs_getblockinfo.h" +#include "yaffs_summary.h" + +static int apply_chunk_offset(struct yaffs_dev *dev, int chunk) +{ + return chunk - dev->chunk_offset; +} + +int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, + u8 *buffer, struct yaffs_ext_tags *tags) +{ + int result; + struct yaffs_ext_tags local_tags; + int flash_chunk = apply_chunk_offset(dev, nand_chunk); + + dev->n_page_reads++; + + /* If there are no tags provided use local tags. */ + if (!tags) + tags = &local_tags; + + result = dev->tagger.read_chunk_tags_fn(dev, flash_chunk, buffer, tags); + if (tags && tags->ecc_result > YAFFS_ECC_RESULT_NO_ERROR) { + + struct yaffs_block_info *bi; + bi = yaffs_get_block_info(dev, + nand_chunk / + dev->param.chunks_per_block); + yaffs_handle_chunk_error(dev, bi); + } + return result; +} + +int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, + int nand_chunk, + const u8 *buffer, struct yaffs_ext_tags *tags) +{ + int result; + int flash_chunk = apply_chunk_offset(dev, nand_chunk); + + dev->n_page_writes++; + + if (!tags) { + yaffs_trace(YAFFS_TRACE_ERROR, "Writing with no tags"); + BUG(); + return YAFFS_FAIL; + } + + tags->seq_number = dev->seq_number; + tags->chunk_used = 1; + yaffs_trace(YAFFS_TRACE_WRITE, + "Writing chunk %d tags %d %d", + nand_chunk, tags->obj_id, tags->chunk_id); + + result = dev->tagger.write_chunk_tags_fn(dev, flash_chunk, + buffer, tags); + + yaffs_summary_add(dev, tags, nand_chunk); + + return result; +} + +int yaffs_mark_bad(struct yaffs_dev *dev, int block_no) +{ + block_no -= dev->block_offset; + dev->n_bad_markings++; + + if (dev->param.disable_bad_block_marking) + return YAFFS_OK; + + return dev->tagger.mark_bad_fn(dev, block_no); +} + + +int yaffs_query_init_block_state(struct yaffs_dev *dev, + int block_no, + enum yaffs_block_state *state, + u32 *seq_number) +{ + block_no -= dev->block_offset; + return dev->tagger.query_block_fn(dev, block_no, state, seq_number); +} + +int yaffs_erase_block(struct yaffs_dev *dev, int block_no) +{ + int result; + + block_no -= dev->block_offset; + dev->n_erasures++; + result = dev->drv.drv_erase_fn(dev, block_no); + return result; +} + +int yaffs_init_nand(struct yaffs_dev *dev) +{ + if (dev->drv.drv_initialise_fn) + return dev->drv.drv_initialise_fn(dev); + return YAFFS_OK; +} + +int yaffs_deinit_nand(struct yaffs_dev *dev) +{ + if (dev->drv.drv_deinitialise_fn) + return dev->drv.drv_deinitialise_fn(dev); + return YAFFS_OK; +} diff --git a/fs/yaffs2/yaffs_nand.h b/fs/yaffs2/yaffs_nand.h new file mode 100644 index 00000000..804e97ad --- /dev/null +++ b/fs/yaffs2/yaffs_nand.h @@ -0,0 +1,39 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_NAND_H__ +#define __YAFFS_NAND_H__ +#include "yaffs_guts.h" + +int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, + u8 *buffer, struct yaffs_ext_tags *tags); + +int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, + int nand_chunk, + const u8 *buffer, struct yaffs_ext_tags *tags); + +int yaffs_mark_bad(struct yaffs_dev *dev, int block_no); + +int yaffs_query_init_block_state(struct yaffs_dev *dev, + int block_no, + enum yaffs_block_state *state, + unsigned *seq_number); + +int yaffs_erase_block(struct yaffs_dev *dev, int flash_block); + +int yaffs_init_nand(struct yaffs_dev *dev); +int yaffs_deinit_nand(struct yaffs_dev *dev); + +#endif diff --git a/fs/yaffs2/yaffs_packedtags1.c b/fs/yaffs2/yaffs_packedtags1.c new file mode 100644 index 00000000..dd9a331d --- /dev/null +++ b/fs/yaffs2/yaffs_packedtags1.c @@ -0,0 +1,56 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "yaffs_packedtags1.h" +#include "yportenv.h" + +static const u8 all_ff[20] = { + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff +}; + +void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, + const struct yaffs_ext_tags *t) +{ + pt->chunk_id = t->chunk_id; + pt->serial_number = t->serial_number; + pt->n_bytes = t->n_bytes; + pt->obj_id = t->obj_id; + pt->ecc = 0; + pt->deleted = (t->is_deleted) ? 0 : 1; + pt->unused_stuff = 0; + pt->should_be_ff = 0xffffffff; +} + +void yaffs_unpack_tags1(struct yaffs_ext_tags *t, + const struct yaffs_packed_tags1 *pt) +{ + + if (memcmp(all_ff, pt, sizeof(struct yaffs_packed_tags1))) { + t->block_bad = 0; + if (pt->should_be_ff != 0xffffffff) + t->block_bad = 1; + t->chunk_used = 1; + t->obj_id = pt->obj_id; + t->chunk_id = pt->chunk_id; + t->n_bytes = pt->n_bytes; + t->ecc_result = YAFFS_ECC_RESULT_NO_ERROR; + t->is_deleted = (pt->deleted) ? 0 : 1; + t->serial_number = pt->serial_number; + } else { + memset(t, 0, sizeof(struct yaffs_ext_tags)); + } +} diff --git a/fs/yaffs2/yaffs_packedtags1.h b/fs/yaffs2/yaffs_packedtags1.h new file mode 100644 index 00000000..3015d58a --- /dev/null +++ b/fs/yaffs2/yaffs_packedtags1.h @@ -0,0 +1,39 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +/* This is used to pack YAFFS1 tags, not YAFFS2 tags. */ + +#ifndef __YAFFS_PACKEDTAGS1_H__ +#define __YAFFS_PACKEDTAGS1_H__ + +#include "yaffs_guts.h" + +struct yaffs_packed_tags1 { + u32 chunk_id:20; + u32 serial_number:2; + u32 n_bytes:10; + u32 obj_id:18; + u32 ecc:12; + u32 deleted:1; + u32 unused_stuff:1; + unsigned should_be_ff; + +}; + +void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, + const struct yaffs_ext_tags *t); +void yaffs_unpack_tags1(struct yaffs_ext_tags *t, + const struct yaffs_packed_tags1 *pt); +#endif diff --git a/fs/yaffs2/yaffs_packedtags2.c b/fs/yaffs2/yaffs_packedtags2.c new file mode 100644 index 00000000..700fd4f1 --- /dev/null +++ b/fs/yaffs2/yaffs_packedtags2.c @@ -0,0 +1,199 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "yaffs_packedtags2.h" +#include "yportenv.h" +#include "yaffs_trace.h" + +/* This code packs a set of extended tags into a binary structure for + * NAND storage + */ + +/* Some of the information is "extra" struff which can be packed in to + * speed scanning + * This is defined by having the EXTRA_HEADER_INFO_FLAG set. + */ + +/* Extra flags applied to chunk_id */ + +#define EXTRA_HEADER_INFO_FLAG 0x80000000 +#define EXTRA_SHRINK_FLAG 0x40000000 +#define EXTRA_SHADOWS_FLAG 0x20000000 +#define EXTRA_SPARE_FLAGS 0x10000000 + +#define ALL_EXTRA_FLAGS 0xf0000000 + +/* Also, the top 4 bits of the object Id are set to the object type. */ +#define EXTRA_OBJECT_TYPE_SHIFT (28) +#define EXTRA_OBJECT_TYPE_MASK ((0x0f) << EXTRA_OBJECT_TYPE_SHIFT) + +static void yaffs_dump_packed_tags2_tags_only( + const struct yaffs_packed_tags2_tags_only *ptt) +{ + yaffs_trace(YAFFS_TRACE_MTD, + "packed tags obj %d chunk %d byte %d seq %d", + ptt->obj_id, ptt->chunk_id, ptt->n_bytes, ptt->seq_number); +} + +static void yaffs_dump_packed_tags2(const struct yaffs_packed_tags2 *pt) +{ + yaffs_dump_packed_tags2_tags_only(&pt->t); +} + +static void yaffs_dump_tags2(const struct yaffs_ext_tags *t) +{ + yaffs_trace(YAFFS_TRACE_MTD, + "ext.tags eccres %d blkbad %d chused %d obj %d chunk%d byte %d del %d ser %d seq %d", + t->ecc_result, t->block_bad, t->chunk_used, t->obj_id, + t->chunk_id, t->n_bytes, t->is_deleted, t->serial_number, + t->seq_number); + +} + +static int yaffs_check_tags_extra_packable(const struct yaffs_ext_tags *t) +{ + if (t->chunk_id != 0 || !t->extra_available) + return 0; + + /* Check if the file size is too long to store */ + if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE && + (t->extra_file_size >> 31) != 0) + return 0; + return 1; +} + +void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *ptt, + const struct yaffs_ext_tags *t) +{ + ptt->chunk_id = t->chunk_id; + ptt->seq_number = t->seq_number; + ptt->n_bytes = t->n_bytes; + ptt->obj_id = t->obj_id; + + /* Only store extra tags for object headers. + * If it is a file then only store if the file size is short\ + * enough to fit. + */ + if (yaffs_check_tags_extra_packable(t)) { + /* Store the extra header info instead */ + /* We save the parent object in the chunk_id */ + ptt->chunk_id = EXTRA_HEADER_INFO_FLAG | t->extra_parent_id; + if (t->extra_is_shrink) + ptt->chunk_id |= EXTRA_SHRINK_FLAG; + if (t->extra_shadows) + ptt->chunk_id |= EXTRA_SHADOWS_FLAG; + + ptt->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; + ptt->obj_id |= (t->extra_obj_type << EXTRA_OBJECT_TYPE_SHIFT); + + if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) + ptt->n_bytes = t->extra_equiv_id; + else if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE) + ptt->n_bytes = (unsigned) t->extra_file_size; + else + ptt->n_bytes = 0; + } + + yaffs_dump_packed_tags2_tags_only(ptt); + yaffs_dump_tags2(t); +} + +void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, + const struct yaffs_ext_tags *t, int tags_ecc) +{ + yaffs_pack_tags2_tags_only(&pt->t, t); +#if CONFIG_YAFFS_DOES_ECC + if (tags_ecc) + yaffs_ecc_calc_other((unsigned char *)&pt->t, + sizeof(struct yaffs_packed_tags2_tags_only), + &pt->ecc); +#endif +} + +void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, + struct yaffs_packed_tags2_tags_only *ptt) +{ + memset(t, 0, sizeof(struct yaffs_ext_tags)); + + if (ptt->seq_number == 0xffffffff) + return; + + t->block_bad = 0; + t->chunk_used = 1; + t->obj_id = ptt->obj_id; + t->chunk_id = ptt->chunk_id; + t->n_bytes = ptt->n_bytes; + t->is_deleted = 0; + t->serial_number = 0; + t->seq_number = ptt->seq_number; + + /* Do extra header info stuff */ + if (ptt->chunk_id & EXTRA_HEADER_INFO_FLAG) { + t->chunk_id = 0; + t->n_bytes = 0; + + t->extra_available = 1; + t->extra_parent_id = ptt->chunk_id & (~(ALL_EXTRA_FLAGS)); + t->extra_is_shrink = ptt->chunk_id & EXTRA_SHRINK_FLAG ? 1 : 0; + t->extra_shadows = ptt->chunk_id & EXTRA_SHADOWS_FLAG ? 1 : 0; + t->extra_obj_type = ptt->obj_id >> EXTRA_OBJECT_TYPE_SHIFT; + t->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; + + if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) + t->extra_equiv_id = ptt->n_bytes; + else + t->extra_file_size = ptt->n_bytes; + } + yaffs_dump_packed_tags2_tags_only(ptt); + yaffs_dump_tags2(t); +} + +void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, + int tags_ecc) +{ + enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_NO_ERROR; + + if (pt->t.seq_number != 0xffffffff && tags_ecc) { + /* Chunk is in use and we need to do ECC */ +#if CONFIG_YAFFS_DOES_ECC + struct yaffs_ecc_other ecc; + int result; + yaffs_ecc_calc_other((unsigned char *)&pt->t, + sizeof(struct yaffs_packed_tags2_tags_only), + &ecc); + result = + yaffs_ecc_correct_other((unsigned char *)&pt->t, + sizeof(struct yaffs_packed_tags2_tags_only), + &pt->ecc, &ecc); + switch (result) { + case 0: + ecc_result = YAFFS_ECC_RESULT_NO_ERROR; + break; + case 1: + ecc_result = YAFFS_ECC_RESULT_FIXED; + break; + case -1: + ecc_result = YAFFS_ECC_RESULT_UNFIXED; + break; + default: + ecc_result = YAFFS_ECC_RESULT_UNKNOWN; + } +#endif + } + yaffs_unpack_tags2_tags_only(t, &pt->t); + + t->ecc_result = ecc_result; + + yaffs_dump_packed_tags2(pt); + yaffs_dump_tags2(t); +} diff --git a/fs/yaffs2/yaffs_packedtags2.h b/fs/yaffs2/yaffs_packedtags2.h new file mode 100644 index 00000000..675e7194 --- /dev/null +++ b/fs/yaffs2/yaffs_packedtags2.h @@ -0,0 +1,47 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +/* This is used to pack YAFFS2 tags, not YAFFS1tags. */ + +#ifndef __YAFFS_PACKEDTAGS2_H__ +#define __YAFFS_PACKEDTAGS2_H__ + +#include "yaffs_guts.h" +#include "yaffs_ecc.h" + +struct yaffs_packed_tags2_tags_only { + unsigned seq_number; + unsigned obj_id; + unsigned chunk_id; + unsigned n_bytes; +}; + +struct yaffs_packed_tags2 { + struct yaffs_packed_tags2_tags_only t; + struct yaffs_ecc_other ecc; +}; + +/* Full packed tags with ECC, used for oob tags */ +void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, + const struct yaffs_ext_tags *t, int tags_ecc); +void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, + int tags_ecc); + +/* Only the tags part (no ECC for use with inband tags */ +void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *pt, + const struct yaffs_ext_tags *t); +void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, + struct yaffs_packed_tags2_tags_only *pt); +#endif diff --git a/fs/yaffs2/yaffs_summary.c b/fs/yaffs2/yaffs_summary.c new file mode 100644 index 00000000..3c9e7232 --- /dev/null +++ b/fs/yaffs2/yaffs_summary.c @@ -0,0 +1,312 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* Summaries write the useful part of the tags for the chunks in a block into an + * an array which is written to the last n chunks of the block. + * Reading the summaries gives all the tags for the block in one read. Much + * faster. + * + * Chunks holding summaries are marked with tags making it look like + * they are part of a fake file. + * + * The summary could also be used during gc. + * + */ + +#include "yaffs_summary.h" +#include "yaffs_packedtags2.h" +#include "yaffs_nand.h" +#include "yaffs_getblockinfo.h" +#include "yaffs_bitmap.h" + +/* + * The summary is built up in an array of summary tags. + * This gets written to the last one or two (maybe more) chunks in a block. + * A summary header is written as the first part of each chunk of summary data. + * The summary header must match or the summary is rejected. + */ + +/* Summary tags don't need the sequence number because that is redundant. */ +struct yaffs_summary_tags { + unsigned obj_id; + unsigned chunk_id; + unsigned n_bytes; +}; + +/* Summary header */ +struct yaffs_summary_header { + unsigned version; /* Must match current version */ + unsigned block; /* Must be this block */ + unsigned seq; /* Must be this sequence number */ + unsigned sum; /* Just add up all the bytes in the tags */ +}; + + +static void yaffs_summary_clear(struct yaffs_dev *dev) +{ + if (!dev->sum_tags) + return; + memset(dev->sum_tags, 0, dev->chunks_per_summary * + sizeof(struct yaffs_summary_tags)); +} + + +void yaffs_summary_deinit(struct yaffs_dev *dev) +{ + kfree(dev->sum_tags); + dev->sum_tags = NULL; + kfree(dev->gc_sum_tags); + dev->gc_sum_tags = NULL; + dev->chunks_per_summary = 0; +} + +int yaffs_summary_init(struct yaffs_dev *dev) +{ + int sum_bytes; + int chunks_used; /* Number of chunks used by summary */ + int sum_tags_bytes; + + sum_bytes = dev->param.chunks_per_block * + sizeof(struct yaffs_summary_tags); + + chunks_used = (sum_bytes + dev->data_bytes_per_chunk - 1)/ + (dev->data_bytes_per_chunk - + sizeof(struct yaffs_summary_header)); + + dev->chunks_per_summary = dev->param.chunks_per_block - chunks_used; + sum_tags_bytes = sizeof(struct yaffs_summary_tags) * + dev->chunks_per_summary; + dev->sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); + dev->gc_sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); + if (!dev->sum_tags || !dev->gc_sum_tags) { + yaffs_summary_deinit(dev); + return YAFFS_FAIL; + } + + yaffs_summary_clear(dev); + + return YAFFS_OK; +} + +static unsigned yaffs_summary_sum(struct yaffs_dev *dev) +{ + u8 *sum_buffer = (u8 *)dev->sum_tags; + int i; + unsigned sum = 0; + + i = sizeof(struct yaffs_summary_tags) * + dev->chunks_per_summary; + while (i > 0) { + sum += *sum_buffer; + sum_buffer++; + i--; + } + + return sum; +} + +static int yaffs_summary_write(struct yaffs_dev *dev, int blk) +{ + struct yaffs_ext_tags tags; + u8 *buffer; + u8 *sum_buffer = (u8 *)dev->sum_tags; + int n_bytes; + int chunk_in_nand; + int chunk_in_block; + int result; + int this_tx; + struct yaffs_summary_header hdr; + int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); + struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); + + buffer = yaffs_get_temp_buffer(dev); + n_bytes = sizeof(struct yaffs_summary_tags) * + dev->chunks_per_summary; + memset(&tags, 0, sizeof(struct yaffs_ext_tags)); + tags.obj_id = YAFFS_OBJECTID_SUMMARY; + tags.chunk_id = 1; + chunk_in_block = dev->chunks_per_summary; + chunk_in_nand = dev->alloc_block * dev->param.chunks_per_block + + dev->chunks_per_summary; + hdr.version = YAFFS_SUMMARY_VERSION; + hdr.block = blk; + hdr.seq = bi->seq_number; + hdr.sum = yaffs_summary_sum(dev); + + do { + this_tx = n_bytes; + if (this_tx > sum_bytes_per_chunk) + this_tx = sum_bytes_per_chunk; + memcpy(buffer, &hdr, sizeof(hdr)); + memcpy(buffer + sizeof(hdr), sum_buffer, this_tx); + tags.n_bytes = this_tx + sizeof(hdr); + result = yaffs_wr_chunk_tags_nand(dev, chunk_in_nand, + buffer, &tags); + + if (result != YAFFS_OK) + break; + yaffs_set_chunk_bit(dev, blk, chunk_in_block); + bi->pages_in_use++; + dev->n_free_chunks--; + + n_bytes -= this_tx; + sum_buffer += this_tx; + chunk_in_nand++; + chunk_in_block++; + tags.chunk_id++; + } while (result == YAFFS_OK && n_bytes > 0); + yaffs_release_temp_buffer(dev, buffer); + + + if (result == YAFFS_OK) + bi->has_summary = 1; + + + return result; +} + +int yaffs_summary_read(struct yaffs_dev *dev, + struct yaffs_summary_tags *st, + int blk) +{ + struct yaffs_ext_tags tags; + u8 *buffer; + u8 *sum_buffer = (u8 *)st; + int n_bytes; + int chunk_id; + int chunk_in_nand; + int chunk_in_block; + int result; + int this_tx; + struct yaffs_summary_header hdr; + struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); + int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); + int sum_tags_bytes; + + sum_tags_bytes = sizeof(struct yaffs_summary_tags) * + dev->chunks_per_summary; + buffer = yaffs_get_temp_buffer(dev); + n_bytes = sizeof(struct yaffs_summary_tags) * dev->chunks_per_summary; + chunk_in_block = dev->chunks_per_summary; + chunk_in_nand = blk * dev->param.chunks_per_block + + dev->chunks_per_summary; + chunk_id = 1; + do { + this_tx = n_bytes; + if (this_tx > sum_bytes_per_chunk) + this_tx = sum_bytes_per_chunk; + result = yaffs_rd_chunk_tags_nand(dev, chunk_in_nand, + buffer, &tags); + + if (tags.chunk_id != chunk_id || + tags.obj_id != YAFFS_OBJECTID_SUMMARY || + tags.chunk_used == 0 || + tags.ecc_result > YAFFS_ECC_RESULT_FIXED || + tags.n_bytes != (this_tx + sizeof(hdr))) + result = YAFFS_FAIL; + if (result != YAFFS_OK) + break; + + if (st == dev->sum_tags) { + /* If we're scanning then update the block info */ + yaffs_set_chunk_bit(dev, blk, chunk_in_block); + bi->pages_in_use++; + } + memcpy(&hdr, buffer, sizeof(hdr)); + memcpy(sum_buffer, buffer + sizeof(hdr), this_tx); + n_bytes -= this_tx; + sum_buffer += this_tx; + chunk_in_nand++; + chunk_in_block++; + chunk_id++; + } while (result == YAFFS_OK && n_bytes > 0); + yaffs_release_temp_buffer(dev, buffer); + + if (result == YAFFS_OK) { + /* Verify header */ + if (hdr.version != YAFFS_SUMMARY_VERSION || + hdr.seq != bi->seq_number || + hdr.sum != yaffs_summary_sum(dev)) + result = YAFFS_FAIL; + } + + if (st == dev->sum_tags && result == YAFFS_OK) + bi->has_summary = 1; + + return result; +} + +int yaffs_summary_add(struct yaffs_dev *dev, + struct yaffs_ext_tags *tags, + int chunk_in_nand) +{ + struct yaffs_packed_tags2_tags_only tags_only; + struct yaffs_summary_tags *sum_tags; + int block_in_nand = chunk_in_nand / dev->param.chunks_per_block; + int chunk_in_block = chunk_in_nand % dev->param.chunks_per_block; + + if (!dev->sum_tags) + return YAFFS_OK; + + if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { + yaffs_pack_tags2_tags_only(&tags_only, tags); + sum_tags = &dev->sum_tags[chunk_in_block]; + sum_tags->chunk_id = tags_only.chunk_id; + sum_tags->n_bytes = tags_only.n_bytes; + sum_tags->obj_id = tags_only.obj_id; + + if (chunk_in_block == dev->chunks_per_summary - 1) { + /* Time to write out the summary */ + yaffs_summary_write(dev, block_in_nand); + yaffs_summary_clear(dev); + yaffs_skip_rest_of_block(dev); + } + } + return YAFFS_OK; +} + +int yaffs_summary_fetch(struct yaffs_dev *dev, + struct yaffs_ext_tags *tags, + int chunk_in_block) +{ + struct yaffs_packed_tags2_tags_only tags_only; + struct yaffs_summary_tags *sum_tags; + if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { + sum_tags = &dev->sum_tags[chunk_in_block]; + tags_only.chunk_id = sum_tags->chunk_id; + tags_only.n_bytes = sum_tags->n_bytes; + tags_only.obj_id = sum_tags->obj_id; + yaffs_unpack_tags2_tags_only(tags, &tags_only); + return YAFFS_OK; + } + return YAFFS_FAIL; +} + +void yaffs_summary_gc(struct yaffs_dev *dev, int blk) +{ + struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); + int i; + + if (!bi->has_summary) + return; + + for (i = dev->chunks_per_summary; + i < dev->param.chunks_per_block; + i++) { + if (yaffs_check_chunk_bit(dev, blk, i)) { + yaffs_clear_chunk_bit(dev, blk, i); + bi->pages_in_use--; + dev->n_free_chunks++; + } + } +} diff --git a/fs/yaffs2/yaffs_summary.h b/fs/yaffs2/yaffs_summary.h new file mode 100644 index 00000000..be141d07 --- /dev/null +++ b/fs/yaffs2/yaffs_summary.h @@ -0,0 +1,37 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_SUMMARY_H__ +#define __YAFFS_SUMMARY_H__ + +#include "yaffs_packedtags2.h" + + +int yaffs_summary_init(struct yaffs_dev *dev); +void yaffs_summary_deinit(struct yaffs_dev *dev); + +int yaffs_summary_add(struct yaffs_dev *dev, + struct yaffs_ext_tags *tags, + int chunk_in_block); +int yaffs_summary_fetch(struct yaffs_dev *dev, + struct yaffs_ext_tags *tags, + int chunk_in_block); +int yaffs_summary_read(struct yaffs_dev *dev, + struct yaffs_summary_tags *st, + int blk); +void yaffs_summary_gc(struct yaffs_dev *dev, int blk); + + +#endif diff --git a/fs/yaffs2/yaffs_tagscompat.c b/fs/yaffs2/yaffs_tagscompat.c new file mode 100644 index 00000000..092430be --- /dev/null +++ b/fs/yaffs2/yaffs_tagscompat.c @@ -0,0 +1,381 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "yaffs_guts.h" +#include "yaffs_tagscompat.h" +#include "yaffs_ecc.h" +#include "yaffs_getblockinfo.h" +#include "yaffs_trace.h" + +static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk); + + +/********** Tags ECC calculations *********/ + + +void yaffs_calc_tags_ecc(struct yaffs_tags *tags) +{ + /* Calculate an ecc */ + unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; + unsigned i, j; + unsigned ecc = 0; + unsigned bit = 0; + + tags->ecc = 0; + + for (i = 0; i < 8; i++) { + for (j = 1; j & 0xff; j <<= 1) { + bit++; + if (b[i] & j) + ecc ^= bit; + } + } + tags->ecc = ecc; +} + +int yaffs_check_tags_ecc(struct yaffs_tags *tags) +{ + unsigned ecc = tags->ecc; + + yaffs_calc_tags_ecc(tags); + + ecc ^= tags->ecc; + + if (ecc && ecc <= 64) { + /* TODO: Handle the failure better. Retire? */ + unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; + + ecc--; + + b[ecc / 8] ^= (1 << (ecc & 7)); + + /* Now recvalc the ecc */ + yaffs_calc_tags_ecc(tags); + + return 1; /* recovered error */ + } else if (ecc) { + /* Wierd ecc failure value */ + /* TODO Need to do somethiong here */ + return -1; /* unrecovered error */ + } + return 0; +} + +/********** Tags **********/ + +static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr, + struct yaffs_tags *tags_ptr) +{ + union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; + + yaffs_calc_tags_ecc(tags_ptr); + + spare_ptr->tb0 = tu->as_bytes[0]; + spare_ptr->tb1 = tu->as_bytes[1]; + spare_ptr->tb2 = tu->as_bytes[2]; + spare_ptr->tb3 = tu->as_bytes[3]; + spare_ptr->tb4 = tu->as_bytes[4]; + spare_ptr->tb5 = tu->as_bytes[5]; + spare_ptr->tb6 = tu->as_bytes[6]; + spare_ptr->tb7 = tu->as_bytes[7]; +} + +static void yaffs_get_tags_from_spare(struct yaffs_dev *dev, + struct yaffs_spare *spare_ptr, + struct yaffs_tags *tags_ptr) +{ + union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; + int result; + + tu->as_bytes[0] = spare_ptr->tb0; + tu->as_bytes[1] = spare_ptr->tb1; + tu->as_bytes[2] = spare_ptr->tb2; + tu->as_bytes[3] = spare_ptr->tb3; + tu->as_bytes[4] = spare_ptr->tb4; + tu->as_bytes[5] = spare_ptr->tb5; + tu->as_bytes[6] = spare_ptr->tb6; + tu->as_bytes[7] = spare_ptr->tb7; + + result = yaffs_check_tags_ecc(tags_ptr); + if (result > 0) + dev->n_tags_ecc_fixed++; + else if (result < 0) + dev->n_tags_ecc_unfixed++; +} + +static void yaffs_spare_init(struct yaffs_spare *spare) +{ + memset(spare, 0xff, sizeof(struct yaffs_spare)); +} + +static int yaffs_wr_nand(struct yaffs_dev *dev, + int nand_chunk, const u8 *data, + struct yaffs_spare *spare) +{ + int data_size = dev->data_bytes_per_chunk; + + return dev->drv.drv_write_chunk_fn(dev, nand_chunk, + data, data_size, + (u8 *) spare, sizeof(*spare)); +} + +static int yaffs_rd_chunk_nand(struct yaffs_dev *dev, + int nand_chunk, + u8 *data, + struct yaffs_spare *spare, + enum yaffs_ecc_result *ecc_result, + int correct_errors) +{ + int ret_val; + struct yaffs_spare local_spare; + int data_size; + int spare_size; + int ecc_result1, ecc_result2; + u8 calc_ecc[3]; + + if (!spare) { + /* If we don't have a real spare, then we use a local one. */ + /* Need this for the calculation of the ecc */ + spare = &local_spare; + } + data_size = dev->data_bytes_per_chunk; + spare_size = sizeof(struct yaffs_spare); + + if (dev->param.use_nand_ecc) + return dev->drv.drv_read_chunk_fn(dev, nand_chunk, + data, data_size, + (u8 *) spare, spare_size, + ecc_result); + + + /* Handle the ECC at this level. */ + + ret_val = dev->drv.drv_read_chunk_fn(dev, nand_chunk, + data, data_size, + (u8 *)spare, spare_size, + NULL); + if (!data || !correct_errors) + return ret_val; + + /* Do ECC correction if needed. */ + yaffs_ecc_calc(data, calc_ecc); + ecc_result1 = yaffs_ecc_correct(data, spare->ecc1, calc_ecc); + yaffs_ecc_calc(&data[256], calc_ecc); + ecc_result2 = yaffs_ecc_correct(&data[256], spare->ecc2, calc_ecc); + + if (ecc_result1 > 0) { + yaffs_trace(YAFFS_TRACE_ERROR, + "**>>yaffs ecc error fix performed on chunk %d:0", + nand_chunk); + dev->n_ecc_fixed++; + } else if (ecc_result1 < 0) { + yaffs_trace(YAFFS_TRACE_ERROR, + "**>>yaffs ecc error unfixed on chunk %d:0", + nand_chunk); + dev->n_ecc_unfixed++; + } + + if (ecc_result2 > 0) { + yaffs_trace(YAFFS_TRACE_ERROR, + "**>>yaffs ecc error fix performed on chunk %d:1", + nand_chunk); + dev->n_ecc_fixed++; + } else if (ecc_result2 < 0) { + yaffs_trace(YAFFS_TRACE_ERROR, + "**>>yaffs ecc error unfixed on chunk %d:1", + nand_chunk); + dev->n_ecc_unfixed++; + } + + if (ecc_result1 || ecc_result2) { + /* We had a data problem on this page */ + yaffs_handle_rd_data_error(dev, nand_chunk); + } + + if (ecc_result1 < 0 || ecc_result2 < 0) + *ecc_result = YAFFS_ECC_RESULT_UNFIXED; + else if (ecc_result1 > 0 || ecc_result2 > 0) + *ecc_result = YAFFS_ECC_RESULT_FIXED; + else + *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; + + return ret_val; +} + +/* + * Functions for robustisizing + */ + +static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk) +{ + int flash_block = nand_chunk / dev->param.chunks_per_block; + + /* Mark the block for retirement */ + yaffs_get_block_info(dev, flash_block + dev->block_offset)-> + needs_retiring = 1; + yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, + "**>>Block %d marked for retirement", + flash_block); + + /* TODO: + * Just do a garbage collection on the affected block + * then retire the block + * NB recursion + */ +} + +static int yaffs_tags_compat_wr(struct yaffs_dev *dev, + int nand_chunk, + const u8 *data, const struct yaffs_ext_tags *ext_tags) +{ + struct yaffs_spare spare; + struct yaffs_tags tags; + + yaffs_spare_init(&spare); + + if (ext_tags->is_deleted) + spare.page_status = 0; + else { + tags.obj_id = ext_tags->obj_id; + tags.chunk_id = ext_tags->chunk_id; + + tags.n_bytes_lsb = ext_tags->n_bytes & (1024 - 1); + + if (dev->data_bytes_per_chunk >= 1024) + tags.n_bytes_msb = (ext_tags->n_bytes >> 10) & 3; + else + tags.n_bytes_msb = 3; + + tags.serial_number = ext_tags->serial_number; + + if (!dev->param.use_nand_ecc && data) { + yaffs_ecc_calc(data, spare.ecc1); + yaffs_ecc_calc(&data[256], spare.ecc2); + } + + yaffs_load_tags_to_spare(&spare, &tags); + } + return yaffs_wr_nand(dev, nand_chunk, data, &spare); +} + +static int yaffs_tags_compat_rd(struct yaffs_dev *dev, + int nand_chunk, + u8 *data, struct yaffs_ext_tags *ext_tags) +{ + struct yaffs_spare spare; + struct yaffs_tags tags; + enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_UNKNOWN; + static struct yaffs_spare spare_ff; + static int init; + int deleted; + + if (!init) { + memset(&spare_ff, 0xff, sizeof(spare_ff)); + init = 1; + } + + if (!yaffs_rd_chunk_nand(dev, nand_chunk, + data, &spare, &ecc_result, 1)) + return YAFFS_FAIL; + + /* ext_tags may be NULL */ + if (!ext_tags) + return YAFFS_OK; + + deleted = (hweight8(spare.page_status) < 7) ? 1 : 0; + + ext_tags->is_deleted = deleted; + ext_tags->ecc_result = ecc_result; + ext_tags->block_bad = 0; /* We're reading it */ + /* therefore it is not a bad block */ + ext_tags->chunk_used = + memcmp(&spare_ff, &spare, sizeof(spare_ff)) ? 1 : 0; + + if (ext_tags->chunk_used) { + yaffs_get_tags_from_spare(dev, &spare, &tags); + ext_tags->obj_id = tags.obj_id; + ext_tags->chunk_id = tags.chunk_id; + ext_tags->n_bytes = tags.n_bytes_lsb; + + if (dev->data_bytes_per_chunk >= 1024) + ext_tags->n_bytes |= + (((unsigned)tags.n_bytes_msb) << 10); + + ext_tags->serial_number = tags.serial_number; + } + + return YAFFS_OK; +} + +static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int flash_block) +{ + struct yaffs_spare spare; + + memset(&spare, 0xff, sizeof(struct yaffs_spare)); + + spare.block_status = 'Y'; + + yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block, NULL, + &spare); + yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block + 1, + NULL, &spare); + + return YAFFS_OK; +} + +static int yaffs_tags_compat_query_block(struct yaffs_dev *dev, + int block_no, + enum yaffs_block_state *state, + u32 *seq_number) +{ + struct yaffs_spare spare0, spare1; + static struct yaffs_spare spare_ff; + static int init; + enum yaffs_ecc_result dummy; + + if (!init) { + memset(&spare_ff, 0xff, sizeof(spare_ff)); + init = 1; + } + + *seq_number = 0; + + /* Look for bad block markers in the first two chunks */ + yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block, + NULL, &spare0, &dummy, 0); + yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block + 1, + NULL, &spare1, &dummy, 0); + + if (hweight8(spare0.block_status & spare1.block_status) < 7) + *state = YAFFS_BLOCK_STATE_DEAD; + else if (memcmp(&spare_ff, &spare0, sizeof(spare_ff)) == 0) + *state = YAFFS_BLOCK_STATE_EMPTY; + else + *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; + + return YAFFS_OK; +} + +void yaffs_tags_compat_install(struct yaffs_dev *dev) +{ + if(dev->param.is_yaffs2) + return; + if(!dev->tagger.write_chunk_tags_fn) + dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_wr; + if(!dev->tagger.read_chunk_tags_fn) + dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_rd; + if(!dev->tagger.query_block_fn) + dev->tagger.query_block_fn = yaffs_tags_compat_query_block; + if(!dev->tagger.mark_bad_fn) + dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad; +} diff --git a/fs/yaffs2/yaffs_tagscompat.h b/fs/yaffs2/yaffs_tagscompat.h new file mode 100644 index 00000000..92d298a6 --- /dev/null +++ b/fs/yaffs2/yaffs_tagscompat.h @@ -0,0 +1,44 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_TAGSCOMPAT_H__ +#define __YAFFS_TAGSCOMPAT_H__ + + +#include "yaffs_guts.h" + +#if 0 + + +int yaffs_tags_compat_wr(struct yaffs_dev *dev, + int nand_chunk, + const u8 *data, const struct yaffs_ext_tags *tags); +int yaffs_tags_compat_rd(struct yaffs_dev *dev, + int nand_chunk, + u8 *data, struct yaffs_ext_tags *tags); +int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no); +int yaffs_tags_compat_query_block(struct yaffs_dev *dev, + int block_no, + enum yaffs_block_state *state, + u32 *seq_number); + +#endif + + +void yaffs_tags_compat_install(struct yaffs_dev *dev); +void yaffs_calc_tags_ecc(struct yaffs_tags *tags); +int yaffs_check_tags_ecc(struct yaffs_tags *tags); + +#endif diff --git a/fs/yaffs2/yaffs_tagsmarshall.c b/fs/yaffs2/yaffs_tagsmarshall.c new file mode 100644 index 00000000..44a83b12 --- /dev/null +++ b/fs/yaffs2/yaffs_tagsmarshall.c @@ -0,0 +1,199 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "yaffs_guts.h" +#include "yaffs_trace.h" +#include "yaffs_packedtags2.h" + +static int yaffs_tags_marshall_write(struct yaffs_dev *dev, + int nand_chunk, const u8 *data, + const struct yaffs_ext_tags *tags) +{ + struct yaffs_packed_tags2 pt; + int retval; + + int packed_tags_size = + dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); + void *packed_tags_ptr = + dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; + + yaffs_trace(YAFFS_TRACE_MTD, + "yaffs_tags_marshall_write chunk %d data %p tags %p", + nand_chunk, data, tags); + + /* For yaffs2 writing there must be both data and tags. + * If we're using inband tags, then the tags are stuffed into + * the end of the data buffer. + */ + if (!data || !tags) + BUG(); + else if (dev->param.inband_tags) { + struct yaffs_packed_tags2_tags_only *pt2tp; + pt2tp = + (struct yaffs_packed_tags2_tags_only *)(data + + dev-> + data_bytes_per_chunk); + yaffs_pack_tags2_tags_only(pt2tp, tags); + } else { + yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc); + } + + retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk, + data, dev->param.total_bytes_per_chunk, + (dev->param.inband_tags) ? NULL : packed_tags_ptr, + (dev->param.inband_tags) ? 0 : packed_tags_size); + + return retval; +} + +static int yaffs_tags_marshall_read(struct yaffs_dev *dev, + int nand_chunk, u8 *data, + struct yaffs_ext_tags *tags) +{ + int retval = 0; + int local_data = 0; + u8 spare_buffer[100]; + enum yaffs_ecc_result ecc_result; + + struct yaffs_packed_tags2 pt; + + int packed_tags_size = + dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); + void *packed_tags_ptr = + dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; + + yaffs_trace(YAFFS_TRACE_MTD, + "yaffs_tags_marshall_read chunk %d data %p tags %p", + nand_chunk, data, tags); + + if (dev->param.inband_tags) { + if (!data) { + local_data = 1; + data = yaffs_get_temp_buffer(dev); + } + } + + if (dev->param.inband_tags || (data && !tags)) + retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, + data, dev->param.total_bytes_per_chunk, + NULL, 0, + &ecc_result); + else if (tags) + retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, + data, dev->param.total_bytes_per_chunk, + spare_buffer, packed_tags_size, + &ecc_result); + else + BUG(); + + + if (dev->param.inband_tags) { + if (tags) { + struct yaffs_packed_tags2_tags_only *pt2tp; + pt2tp = + (struct yaffs_packed_tags2_tags_only *) + &data[dev->data_bytes_per_chunk]; + yaffs_unpack_tags2_tags_only(tags, pt2tp); + } + } else if (tags) { + memcpy(packed_tags_ptr, spare_buffer, packed_tags_size); + yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc); + } + + if (local_data) + yaffs_release_temp_buffer(dev, data); + + if (tags && ecc_result == YAFFS_ECC_RESULT_UNFIXED) { + tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED; + dev->n_ecc_unfixed++; + } + + if (tags && ecc_result == -YAFFS_ECC_RESULT_FIXED) { + if (tags->ecc_result <= YAFFS_ECC_RESULT_NO_ERROR) + tags->ecc_result = YAFFS_ECC_RESULT_FIXED; + dev->n_ecc_fixed++; + } + + if (ecc_result < YAFFS_ECC_RESULT_UNFIXED) + return YAFFS_OK; + else + return YAFFS_FAIL; +} + +static int yaffs_tags_marshall_query_block(struct yaffs_dev *dev, int block_no, + enum yaffs_block_state *state, + u32 *seq_number) +{ + int retval; + + yaffs_trace(YAFFS_TRACE_MTD, "yaffs_tags_marshall_query_block %d", + block_no); + + retval = dev->drv.drv_check_bad_fn(dev, block_no); + + if (retval== YAFFS_FAIL) { + yaffs_trace(YAFFS_TRACE_MTD, "block is bad"); + + *state = YAFFS_BLOCK_STATE_DEAD; + *seq_number = 0; + } else { + struct yaffs_ext_tags t; + + yaffs_tags_marshall_read(dev, + block_no * dev->param.chunks_per_block, + NULL, &t); + + if (t.chunk_used) { + *seq_number = t.seq_number; + *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; + } else { + *seq_number = 0; + *state = YAFFS_BLOCK_STATE_EMPTY; + } + } + + yaffs_trace(YAFFS_TRACE_MTD, + "block query returns seq %d state %d", + *seq_number, *state); + + if (retval == 0) + return YAFFS_OK; + else + return YAFFS_FAIL; +} + +static int yaffs_tags_marshall_mark_bad(struct yaffs_dev *dev, int block_no) +{ + return dev->drv.drv_mark_bad_fn(dev, block_no); + +} + + +void yaffs_tags_marshall_install(struct yaffs_dev *dev) +{ + if (!dev->param.is_yaffs2) + return; + + if (!dev->tagger.write_chunk_tags_fn) + dev->tagger.write_chunk_tags_fn = yaffs_tags_marshall_write; + + if (!dev->tagger.read_chunk_tags_fn) + dev->tagger.read_chunk_tags_fn = yaffs_tags_marshall_read; + + if (!dev->tagger.query_block_fn) + dev->tagger.query_block_fn = yaffs_tags_marshall_query_block; + + if (!dev->tagger.mark_bad_fn) + dev->tagger.mark_bad_fn = yaffs_tags_marshall_mark_bad; + +} diff --git a/fs/yaffs2/yaffs_tagsmarshall.h b/fs/yaffs2/yaffs_tagsmarshall.h new file mode 100644 index 00000000..bf3e68a1 --- /dev/null +++ b/fs/yaffs2/yaffs_tagsmarshall.h @@ -0,0 +1,22 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_TAGSMARSHALL_H__ +#define __YAFFS_TAGSMARSHALL_H__ + +#include "yaffs_guts.h" +void yaffs_tags_marshall_install(struct yaffs_dev *dev); + +#endif diff --git a/fs/yaffs2/yaffs_trace.h b/fs/yaffs2/yaffs_trace.h new file mode 100644 index 00000000..fd26054d --- /dev/null +++ b/fs/yaffs2/yaffs_trace.h @@ -0,0 +1,57 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YTRACE_H__ +#define __YTRACE_H__ + +extern unsigned int yaffs_trace_mask; +extern unsigned int yaffs_wr_attempts; + +/* + * Tracing flags. + * The flags masked in YAFFS_TRACE_ALWAYS are always traced. + */ + +#define YAFFS_TRACE_OS 0x00000002 +#define YAFFS_TRACE_ALLOCATE 0x00000004 +#define YAFFS_TRACE_SCAN 0x00000008 +#define YAFFS_TRACE_BAD_BLOCKS 0x00000010 +#define YAFFS_TRACE_ERASE 0x00000020 +#define YAFFS_TRACE_GC 0x00000040 +#define YAFFS_TRACE_WRITE 0x00000080 +#define YAFFS_TRACE_TRACING 0x00000100 +#define YAFFS_TRACE_DELETION 0x00000200 +#define YAFFS_TRACE_BUFFERS 0x00000400 +#define YAFFS_TRACE_NANDACCESS 0x00000800 +#define YAFFS_TRACE_GC_DETAIL 0x00001000 +#define YAFFS_TRACE_SCAN_DEBUG 0x00002000 +#define YAFFS_TRACE_MTD 0x00004000 +#define YAFFS_TRACE_CHECKPOINT 0x00008000 + +#define YAFFS_TRACE_VERIFY 0x00010000 +#define YAFFS_TRACE_VERIFY_NAND 0x00020000 +#define YAFFS_TRACE_VERIFY_FULL 0x00040000 +#define YAFFS_TRACE_VERIFY_ALL 0x000f0000 + +#define YAFFS_TRACE_SYNC 0x00100000 +#define YAFFS_TRACE_BACKGROUND 0x00200000 +#define YAFFS_TRACE_LOCK 0x00400000 +#define YAFFS_TRACE_MOUNT 0x00800000 + +#define YAFFS_TRACE_ERROR 0x40000000 +#define YAFFS_TRACE_BUG 0x80000000 +#define YAFFS_TRACE_ALWAYS 0xf0000000 + +#endif diff --git a/fs/yaffs2/yaffs_verify.c b/fs/yaffs2/yaffs_verify.c new file mode 100644 index 00000000..e8f2f0a6 --- /dev/null +++ b/fs/yaffs2/yaffs_verify.c @@ -0,0 +1,529 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "yaffs_verify.h" +#include "yaffs_trace.h" +#include "yaffs_bitmap.h" +#include "yaffs_getblockinfo.h" +#include "yaffs_nand.h" + +int yaffs_skip_verification(struct yaffs_dev *dev) +{ + (void) dev; + return !(yaffs_trace_mask & + (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL)); +} + +static int yaffs_skip_full_verification(struct yaffs_dev *dev) +{ + (void) dev; + return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_FULL)); +} + +static int yaffs_skip_nand_verification(struct yaffs_dev *dev) +{ + (void) dev; + return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_NAND)); +} + +static const char * const block_state_name[] = { + "Unknown", + "Needs scan", + "Scanning", + "Empty", + "Allocating", + "Full", + "Dirty", + "Checkpoint", + "Collecting", + "Dead" +}; + +void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, int n) +{ + int actually_used; + int in_use; + + if (yaffs_skip_verification(dev)) + return; + + /* Report illegal runtime states */ + if (bi->block_state >= YAFFS_NUMBER_OF_BLOCK_STATES) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Block %d has undefined state %d", + n, bi->block_state); + + switch (bi->block_state) { + case YAFFS_BLOCK_STATE_UNKNOWN: + case YAFFS_BLOCK_STATE_SCANNING: + case YAFFS_BLOCK_STATE_NEEDS_SCAN: + yaffs_trace(YAFFS_TRACE_VERIFY, + "Block %d has bad run-state %s", + n, block_state_name[bi->block_state]); + } + + /* Check pages in use and soft deletions are legal */ + + actually_used = bi->pages_in_use - bi->soft_del_pages; + + if (bi->pages_in_use < 0 || + bi->pages_in_use > dev->param.chunks_per_block || + bi->soft_del_pages < 0 || + bi->soft_del_pages > dev->param.chunks_per_block || + actually_used < 0 || actually_used > dev->param.chunks_per_block) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Block %d has illegal values pages_in_used %d soft_del_pages %d", + n, bi->pages_in_use, bi->soft_del_pages); + + /* Check chunk bitmap legal */ + in_use = yaffs_count_chunk_bits(dev, n); + if (in_use != bi->pages_in_use) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Block %d has inconsistent values pages_in_use %d counted chunk bits %d", + n, bi->pages_in_use, in_use); +} + +void yaffs_verify_collected_blk(struct yaffs_dev *dev, + struct yaffs_block_info *bi, int n) +{ + yaffs_verify_blk(dev, bi, n); + + /* After collection the block should be in the erased state */ + + if (bi->block_state != YAFFS_BLOCK_STATE_COLLECTING && + bi->block_state != YAFFS_BLOCK_STATE_EMPTY) { + yaffs_trace(YAFFS_TRACE_ERROR, + "Block %d is in state %d after gc, should be erased", + n, bi->block_state); + } +} + +void yaffs_verify_blocks(struct yaffs_dev *dev) +{ + int i; + int state_count[YAFFS_NUMBER_OF_BLOCK_STATES]; + int illegal_states = 0; + + if (yaffs_skip_verification(dev)) + return; + + memset(state_count, 0, sizeof(state_count)); + + for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { + struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); + yaffs_verify_blk(dev, bi, i); + + if (bi->block_state < YAFFS_NUMBER_OF_BLOCK_STATES) + state_count[bi->block_state]++; + else + illegal_states++; + } + + yaffs_trace(YAFFS_TRACE_VERIFY, "Block summary"); + + yaffs_trace(YAFFS_TRACE_VERIFY, + "%d blocks have illegal states", + illegal_states); + if (state_count[YAFFS_BLOCK_STATE_ALLOCATING] > 1) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Too many allocating blocks"); + + for (i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++) + yaffs_trace(YAFFS_TRACE_VERIFY, + "%s %d blocks", + block_state_name[i], state_count[i]); + + if (dev->blocks_in_checkpt != state_count[YAFFS_BLOCK_STATE_CHECKPOINT]) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Checkpoint block count wrong dev %d count %d", + dev->blocks_in_checkpt, + state_count[YAFFS_BLOCK_STATE_CHECKPOINT]); + + if (dev->n_erased_blocks != state_count[YAFFS_BLOCK_STATE_EMPTY]) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Erased block count wrong dev %d count %d", + dev->n_erased_blocks, + state_count[YAFFS_BLOCK_STATE_EMPTY]); + + if (state_count[YAFFS_BLOCK_STATE_COLLECTING] > 1) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Too many collecting blocks %d (max is 1)", + state_count[YAFFS_BLOCK_STATE_COLLECTING]); +} + +/* + * Verify the object header. oh must be valid, but obj and tags may be NULL in + * which case those tests will not be performed. + */ +void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, + struct yaffs_ext_tags *tags, int parent_check) +{ + if (obj && yaffs_skip_verification(obj->my_dev)) + return; + + if (!(tags && obj && oh)) { + yaffs_trace(YAFFS_TRACE_VERIFY, + "Verifying object header tags %p obj %p oh %p", + tags, obj, oh); + return; + } + + if (oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN || + oh->type > YAFFS_OBJECT_TYPE_MAX) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Obj %d header type is illegal value 0x%x", + tags->obj_id, oh->type); + + if (tags->obj_id != obj->obj_id) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Obj %d header mismatch obj_id %d", + tags->obj_id, obj->obj_id); + + /* + * Check that the object's parent ids match if parent_check requested. + * + * Tests do not apply to the root object. + */ + + if (parent_check && tags->obj_id > 1 && !obj->parent) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Obj %d header mismatch parent_id %d obj->parent is NULL", + tags->obj_id, oh->parent_obj_id); + + if (parent_check && obj->parent && + oh->parent_obj_id != obj->parent->obj_id && + (oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED || + obj->parent->obj_id != YAFFS_OBJECTID_DELETED)) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Obj %d header mismatch parent_id %d parent_obj_id %d", + tags->obj_id, oh->parent_obj_id, + obj->parent->obj_id); + + if (tags->obj_id > 1 && oh->name[0] == 0) /* Null name */ + yaffs_trace(YAFFS_TRACE_VERIFY, + "Obj %d header name is NULL", + obj->obj_id); + + if (tags->obj_id > 1 && ((u8) (oh->name[0])) == 0xff) /* Junk name */ + yaffs_trace(YAFFS_TRACE_VERIFY, + "Obj %d header name is 0xff", + obj->obj_id); +} + +void yaffs_verify_file(struct yaffs_obj *obj) +{ + u32 x; + int required_depth; + int actual_depth; + int last_chunk; + u32 offset_in_chunk; + u32 the_chunk; + + u32 i; + struct yaffs_dev *dev; + struct yaffs_ext_tags tags; + struct yaffs_tnode *tn; + u32 obj_id; + + if (!obj) + return; + + if (yaffs_skip_verification(obj->my_dev)) + return; + + dev = obj->my_dev; + obj_id = obj->obj_id; + + + /* Check file size is consistent with tnode depth */ + yaffs_addr_to_chunk(dev, obj->variant.file_variant.file_size, + &last_chunk, &offset_in_chunk); + last_chunk++; + x = last_chunk >> YAFFS_TNODES_LEVEL0_BITS; + required_depth = 0; + while (x > 0) { + x >>= YAFFS_TNODES_INTERNAL_BITS; + required_depth++; + } + + actual_depth = obj->variant.file_variant.top_level; + + /* Check that the chunks in the tnode tree are all correct. + * We do this by scanning through the tnode tree and + * checking the tags for every chunk match. + */ + + if (yaffs_skip_nand_verification(dev)) + return; + + for (i = 1; i <= last_chunk; i++) { + tn = yaffs_find_tnode_0(dev, &obj->variant.file_variant, i); + + if (!tn) + continue; + + the_chunk = yaffs_get_group_base(dev, tn, i); + if (the_chunk > 0) { + yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, + &tags); + if (tags.obj_id != obj_id || tags.chunk_id != i) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Object %d chunk_id %d NAND mismatch chunk %d tags (%d:%d)", + obj_id, i, the_chunk, + tags.obj_id, tags.chunk_id); + } + } +} + +void yaffs_verify_link(struct yaffs_obj *obj) +{ + if (obj && yaffs_skip_verification(obj->my_dev)) + return; + + /* Verify sane equivalent object */ +} + +void yaffs_verify_symlink(struct yaffs_obj *obj) +{ + if (obj && yaffs_skip_verification(obj->my_dev)) + return; + + /* Verify symlink string */ +} + +void yaffs_verify_special(struct yaffs_obj *obj) +{ + if (obj && yaffs_skip_verification(obj->my_dev)) + return; +} + +void yaffs_verify_obj(struct yaffs_obj *obj) +{ + struct yaffs_dev *dev; + u32 chunk_min; + u32 chunk_max; + u32 chunk_id_ok; + u32 chunk_in_range; + u32 chunk_wrongly_deleted; + u32 chunk_valid; + + if (!obj) + return; + + if (obj->being_created) + return; + + dev = obj->my_dev; + + if (yaffs_skip_verification(dev)) + return; + + /* Check sane object header chunk */ + + chunk_min = dev->internal_start_block * dev->param.chunks_per_block; + chunk_max = + (dev->internal_end_block + 1) * dev->param.chunks_per_block - 1; + + chunk_in_range = (((unsigned)(obj->hdr_chunk)) >= chunk_min && + ((unsigned)(obj->hdr_chunk)) <= chunk_max); + chunk_id_ok = chunk_in_range || (obj->hdr_chunk == 0); + chunk_valid = chunk_in_range && + yaffs_check_chunk_bit(dev, + obj->hdr_chunk / dev->param.chunks_per_block, + obj->hdr_chunk % dev->param.chunks_per_block); + chunk_wrongly_deleted = chunk_in_range && !chunk_valid; + + if (!obj->fake && (!chunk_id_ok || chunk_wrongly_deleted)) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Obj %d has chunk_id %d %s %s", + obj->obj_id, obj->hdr_chunk, + chunk_id_ok ? "" : ",out of range", + chunk_wrongly_deleted ? ",marked as deleted" : ""); + + if (chunk_valid && !yaffs_skip_nand_verification(dev)) { + struct yaffs_ext_tags tags; + struct yaffs_obj_hdr *oh; + u8 *buffer = yaffs_get_temp_buffer(dev); + + oh = (struct yaffs_obj_hdr *)buffer; + + yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, buffer, &tags); + + yaffs_verify_oh(obj, oh, &tags, 1); + + yaffs_release_temp_buffer(dev, buffer); + } + + /* Verify it has a parent */ + if (obj && !obj->fake && (!obj->parent || obj->parent->my_dev != dev)) { + yaffs_trace(YAFFS_TRACE_VERIFY, + "Obj %d has parent pointer %p which does not look like an object", + obj->obj_id, obj->parent); + } + + /* Verify parent is a directory */ + if (obj->parent && + obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { + yaffs_trace(YAFFS_TRACE_VERIFY, + "Obj %d's parent is not a directory (type %d)", + obj->obj_id, obj->parent->variant_type); + } + + switch (obj->variant_type) { + case YAFFS_OBJECT_TYPE_FILE: + yaffs_verify_file(obj); + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + yaffs_verify_symlink(obj); + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + yaffs_verify_dir(obj); + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + yaffs_verify_link(obj); + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + yaffs_verify_special(obj); + break; + case YAFFS_OBJECT_TYPE_UNKNOWN: + default: + yaffs_trace(YAFFS_TRACE_VERIFY, + "Obj %d has illegaltype %d", + obj->obj_id, obj->variant_type); + break; + } +} + +void yaffs_verify_objects(struct yaffs_dev *dev) +{ + struct yaffs_obj *obj; + int i; + struct list_head *lh; + + if (yaffs_skip_verification(dev)) + return; + + /* Iterate through the objects in each hash entry */ + + for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { + list_for_each(lh, &dev->obj_bucket[i].list) { + obj = list_entry(lh, struct yaffs_obj, hash_link); + yaffs_verify_obj(obj); + } + } +} + +void yaffs_verify_obj_in_dir(struct yaffs_obj *obj) +{ + struct list_head *lh; + struct yaffs_obj *list_obj; + int count = 0; + + if (!obj) { + yaffs_trace(YAFFS_TRACE_ALWAYS, "No object to verify"); + BUG(); + return; + } + + if (yaffs_skip_verification(obj->my_dev)) + return; + + if (!obj->parent) { + yaffs_trace(YAFFS_TRACE_ALWAYS, "Object does not have parent"); + BUG(); + return; + } + + if (obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { + yaffs_trace(YAFFS_TRACE_ALWAYS, "Parent is not directory"); + BUG(); + } + + /* Iterate through the objects in each hash entry */ + + list_for_each(lh, &obj->parent->variant.dir_variant.children) { + list_obj = list_entry(lh, struct yaffs_obj, siblings); + yaffs_verify_obj(list_obj); + if (obj == list_obj) + count++; + } + + if (count != 1) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "Object in directory %d times", + count); + BUG(); + } +} + +void yaffs_verify_dir(struct yaffs_obj *directory) +{ + struct list_head *lh; + struct yaffs_obj *list_obj; + + if (!directory) { + BUG(); + return; + } + + if (yaffs_skip_full_verification(directory->my_dev)) + return; + + if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "Directory has wrong type: %d", + directory->variant_type); + BUG(); + } + + /* Iterate through the objects in each hash entry */ + + list_for_each(lh, &directory->variant.dir_variant.children) { + list_obj = list_entry(lh, struct yaffs_obj, siblings); + if (list_obj->parent != directory) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "Object in directory list has wrong parent %p", + list_obj->parent); + BUG(); + } + yaffs_verify_obj_in_dir(list_obj); + } +} + +static int yaffs_free_verification_failures; + +void yaffs_verify_free_chunks(struct yaffs_dev *dev) +{ + int counted; + int difference; + + if (yaffs_skip_verification(dev)) + return; + + counted = yaffs_count_free_chunks(dev); + + difference = dev->n_free_chunks - counted; + + if (difference) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "Freechunks verification failure %d %d %d", + dev->n_free_chunks, counted, difference); + yaffs_free_verification_failures++; + } +} + +int yaffs_verify_file_sane(struct yaffs_obj *in) +{ + (void) in; + return YAFFS_OK; +} diff --git a/fs/yaffs2/yaffs_verify.h b/fs/yaffs2/yaffs_verify.h new file mode 100644 index 00000000..4f4af8d2 --- /dev/null +++ b/fs/yaffs2/yaffs_verify.h @@ -0,0 +1,43 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_VERIFY_H__ +#define __YAFFS_VERIFY_H__ + +#include "yaffs_guts.h" + +void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, + int n); +void yaffs_verify_collected_blk(struct yaffs_dev *dev, + struct yaffs_block_info *bi, int n); +void yaffs_verify_blocks(struct yaffs_dev *dev); + +void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, + struct yaffs_ext_tags *tags, int parent_check); +void yaffs_verify_file(struct yaffs_obj *obj); +void yaffs_verify_link(struct yaffs_obj *obj); +void yaffs_verify_symlink(struct yaffs_obj *obj); +void yaffs_verify_special(struct yaffs_obj *obj); +void yaffs_verify_obj(struct yaffs_obj *obj); +void yaffs_verify_objects(struct yaffs_dev *dev); +void yaffs_verify_obj_in_dir(struct yaffs_obj *obj); +void yaffs_verify_dir(struct yaffs_obj *directory); +void yaffs_verify_free_chunks(struct yaffs_dev *dev); + +int yaffs_verify_file_sane(struct yaffs_obj *obj); + +int yaffs_skip_verification(struct yaffs_dev *dev); + +#endif diff --git a/fs/yaffs2/yaffs_vfs_multi.c b/fs/yaffs2/yaffs_vfs_multi.c new file mode 100644 index 00000000..c66dcb9c --- /dev/null +++ b/fs/yaffs2/yaffs_vfs_multi.c @@ -0,0 +1,3677 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * Acknowledgements: + * Luc van OostenRyck for numerous patches. + * Nick Bane for numerous patches. + * Nick Bane for 2.5/2.6 integration. + * Andras Toth for mknod rdev issue. + * Michael Fischer for finding the problem with inode inconsistency. + * Some code bodily lifted from JFFS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * + * This is the file system front-end to YAFFS that hooks it up to + * the VFS. + * + * Special notes: + * >> 2.4: sb->u.generic_sbp points to the struct yaffs_dev associated with + * this superblock + * >> 2.6: sb->s_fs_info points to the struct yaffs_dev associated with this + * superblock + * >> inode->u.generic_ip points to the associated struct yaffs_obj. + */ + +/* + * There are two variants of the VFS glue code. This variant should compile + * for any version of Linux. + */ +#include + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)) +#define YAFFS_COMPILE_BACKGROUND +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)) +#define YAFFS_COMPILE_FREEZER +#endif +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) +#define YAFFS_COMPILE_EXPORTFS +#endif + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)) +#define YAFFS_USE_SETATTR_COPY +#define YAFFS_USE_TRUNCATE_SETSIZE +#endif +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)) +#define YAFFS_HAS_EVICT_INODE +#endif + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) +#define YAFFS_NEW_FOLLOW_LINK 1 +#else +#define YAFFS_NEW_FOLLOW_LINK 0 +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) +#define YAFFS_HAS_WRITE_SUPER +#endif + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) +#include +#endif + +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) +#include +#endif +#include +#include +#include +#include +#include + +#if (YAFFS_NEW_FOLLOW_LINK == 1) +#include +#endif + +#ifdef YAFFS_COMPILE_EXPORTFS +#include +#endif + +#ifdef YAFFS_COMPILE_BACKGROUND +#include +#include +#endif +#ifdef YAFFS_COMPILE_FREEZER +#include +#endif + +#include + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) + +#include + +#define UnlockPage(p) unlock_page(p) +#define Page_Uptodate(page) test_bit(PG_uptodate, &(page)->flags) + +/* FIXME: use sb->s_id instead ? */ +#define yaffs_devname(sb, buf) bdevname(sb->s_bdev, buf) + +#else + +#include +#define BDEVNAME_SIZE 0 +#define yaffs_devname(sb, buf) kdevname(sb->s_dev) + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)) +/* added NCB 26/5/2006 for 2.4.25-vrs2-tcl1 kernel */ +#define __user +#endif + +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)) +#define YPROC_ROOT (&proc_root) +#else +#define YPROC_ROOT NULL +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)) +#define Y_INIT_TIMER(a) init_timer(a) +#else +#define Y_INIT_TIMER(a) init_timer_on_stack(a) +#endif + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27)) +#define YAFFS_USE_WRITE_BEGIN_END 1 +#else +#define YAFFS_USE_WRITE_BEGIN_END 0 +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) +#define YAFFS_SUPER_HAS_DIRTY +#endif + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) +#define set_nlink(inode, count) do { (inode)->i_nlink = (count); } while(0) +#endif + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 28)) +static uint32_t YCALCBLOCKS(uint64_t partition_size, uint32_t block_size) +{ + uint64_t result = partition_size; + do_div(result, block_size); + return (uint32_t) result; +} +#else +#define YCALCBLOCKS(s, b) ((s)/(b)) +#endif + +#include +#include + +#include "yportenv.h" +#include "yaffs_trace.h" +#include "yaffs_guts.h" +#include "yaffs_attribs.h" + +#include "yaffs_linux.h" + +#include "yaffs_mtdif.h" +#include "yaffs_packedtags2.h" +#include "yaffs_getblockinfo.h" + +unsigned int yaffs_trace_mask = + YAFFS_TRACE_BAD_BLOCKS | + YAFFS_TRACE_ALWAYS | + 0; + +unsigned int yaffs_wr_attempts = YAFFS_WR_ATTEMPTS; +unsigned int yaffs_auto_checkpoint = 1; +unsigned int yaffs_gc_control = 1; +unsigned int yaffs_bg_enable = 1; +unsigned int yaffs_auto_select = 1; +/* Module Parameters */ +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) +module_param(yaffs_trace_mask, uint, 0644); +module_param(yaffs_wr_attempts, uint, 0644); +module_param(yaffs_auto_checkpoint, uint, 0644); +module_param(yaffs_gc_control, uint, 0644); +module_param(yaffs_bg_enable, uint, 0644); +#else +MODULE_PARM(yaffs_trace_mask, "i"); +MODULE_PARM(yaffs_wr_attempts, "i"); +MODULE_PARM(yaffs_auto_checkpoint, "i"); +MODULE_PARM(yaffs_gc_control, "i"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) +/* use iget and read_inode */ +#define Y_IGET(sb, inum) iget((sb), (inum)) + +#else +/* Call local equivalent */ +#define YAFFS_USE_OWN_IGET +#define Y_IGET(sb, inum) yaffs_iget((sb), (inum)) + +#endif + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)) +#define yaffs_inode_to_obj_lv(iptr) ((iptr)->i_private) +#else +#define yaffs_inode_to_obj_lv(iptr) ((iptr)->u.generic_ip) +#endif + +#define yaffs_inode_to_obj(iptr) \ + ((struct yaffs_obj *)(yaffs_inode_to_obj_lv(iptr))) +#define yaffs_dentry_to_obj(dptr) yaffs_inode_to_obj((dptr)->d_inode) + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) +#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->s_fs_info) +#else +#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->u.generic_sbp) +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) +#define Y_CLEAR_INODE(i) clear_inode(i) +#else +#define Y_CLEAR_INODE(i) end_writeback(i) +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) +#define YAFFS_USE_DIR_ITERATE +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,12,0)) +#define YAFFS_NEW_PROCFS +#include +#endif + + +#define update_dir_time(dir) do {\ + (dir)->i_ctime = (dir)->i_mtime = CURRENT_TIME; \ + } while (0) + +static void yaffs_fill_inode_from_obj(struct inode *inode, + struct yaffs_obj *obj); + + +static void yaffs_gross_lock(struct yaffs_dev *dev) +{ + yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locking %p", current); + mutex_lock(&(yaffs_dev_to_lc(dev)->gross_lock)); + yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locked %p", current); +} + +static void yaffs_gross_unlock(struct yaffs_dev *dev) +{ + yaffs_trace(YAFFS_TRACE_LOCK, "yaffs unlocking %p", current); + mutex_unlock(&(yaffs_dev_to_lc(dev)->gross_lock)); +} + + +static int yaffs_readpage_nolock(struct file *f, struct page *pg) +{ + /* Lifted from jffs2 */ + + struct yaffs_obj *obj; + unsigned char *pg_buf; + int ret; + loff_t pos = ((loff_t) pg->index) << PAGE_CACHE_SHIFT; + struct yaffs_dev *dev; + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_readpage_nolock at %lld, size %08x", + (long long)pos, + (unsigned)PAGE_CACHE_SIZE); + + obj = yaffs_dentry_to_obj(f->f_dentry); + + dev = obj->my_dev; + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) + BUG_ON(!PageLocked(pg)); +#else + if (!PageLocked(pg)) + PAGE_BUG(pg); +#endif + + pg_buf = kmap(pg); + /* FIXME: Can kmap fail? */ + + yaffs_gross_lock(dev); + + ret = yaffs_file_rd(obj, pg_buf, pos, PAGE_CACHE_SIZE); + + yaffs_gross_unlock(dev); + + if (ret >= 0) + ret = 0; + + if (ret) { + ClearPageUptodate(pg); + SetPageError(pg); + } else { + SetPageUptodate(pg); + ClearPageError(pg); + } + + flush_dcache_page(pg); + kunmap(pg); + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage_nolock done"); + return ret; +} + +static int yaffs_readpage_unlock(struct file *f, struct page *pg) +{ + int ret = yaffs_readpage_nolock(f, pg); + UnlockPage(pg); + return ret; +} + +static int yaffs_readpage(struct file *f, struct page *pg) +{ + int ret; + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage"); + ret = yaffs_readpage_unlock(f, pg); + yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage done"); + return ret; +} + + +static void yaffs_set_super_dirty_val(struct yaffs_dev *dev, int val) +{ + struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev); + + if (lc) + lc->dirty = val; + +# ifdef YAFFS_SUPER_HAS_DIRTY + { + struct super_block *sb = lc->super; + + if (sb) + sb->s_dirt = val; + } +#endif + +} + +static void yaffs_set_super_dirty(struct yaffs_dev *dev) +{ + yaffs_set_super_dirty_val(dev, 1); +} + +static void yaffs_clear_super_dirty(struct yaffs_dev *dev) +{ + yaffs_set_super_dirty_val(dev, 0); +} + +static int yaffs_check_super_dirty(struct yaffs_dev *dev) +{ + struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev); + + if (lc && lc->dirty) + return 1; + +# ifdef YAFFS_SUPER_HAS_DIRTY + { + struct super_block *sb = lc->super; + + if (sb && sb->s_dirt) + return 1; + } +#endif + return 0; + +} + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) +static int yaffs_writepage(struct page *page, struct writeback_control *wbc) +#else +static int yaffs_writepage(struct page *page) +#endif +{ + struct yaffs_dev *dev; + struct address_space *mapping = page->mapping; + struct inode *inode; + unsigned long end_index; + char *buffer; + struct yaffs_obj *obj; + int n_written = 0; + unsigned n_bytes; + loff_t i_size; + + if (!mapping) + BUG(); + inode = mapping->host; + if (!inode) + BUG(); + i_size = i_size_read(inode); + + end_index = i_size >> PAGE_CACHE_SHIFT; + + if (page->index < end_index) + n_bytes = PAGE_CACHE_SIZE; + else { + n_bytes = i_size & (PAGE_CACHE_SIZE - 1); + + if (page->index > end_index || !n_bytes) { + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_writepage at %lld, inode size = %lld!!", + ((loff_t)page->index) << PAGE_CACHE_SHIFT, + inode->i_size); + yaffs_trace(YAFFS_TRACE_OS, + " -> don't care!!"); + + zero_user_segment(page, 0, PAGE_CACHE_SIZE); + set_page_writeback(page); + unlock_page(page); + end_page_writeback(page); + return 0; + } + } + + if (n_bytes != PAGE_CACHE_SIZE) + zero_user_segment(page, n_bytes, PAGE_CACHE_SIZE); + + get_page(page); + + buffer = kmap(page); + + obj = yaffs_inode_to_obj(inode); + dev = obj->my_dev; + yaffs_gross_lock(dev); + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_writepage at %lld, size %08x", + ((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes); + yaffs_trace(YAFFS_TRACE_OS, + "writepag0: obj = %lld, ino = %lld", + obj->variant.file_variant.file_size, inode->i_size); + + n_written = yaffs_wr_file(obj, buffer, + ((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes, 0); + + yaffs_set_super_dirty(dev); + + yaffs_trace(YAFFS_TRACE_OS, + "writepag1: obj = %lld, ino = %lld", + obj->variant.file_variant.file_size, inode->i_size); + + yaffs_gross_unlock(dev); + + kunmap(page); + set_page_writeback(page); + unlock_page(page); + end_page_writeback(page); + put_page(page); + + return (n_written == n_bytes) ? 0 : -ENOSPC; +} + +/* Space holding and freeing is done to ensure we have space available for write_begin/end */ +/* For now we just assume few parallel writes and check against a small number. */ +/* Todo: need to do this with a counter to handle parallel reads better */ + +static ssize_t yaffs_hold_space(struct file *f) +{ + struct yaffs_obj *obj; + struct yaffs_dev *dev; + + int n_free_chunks; + + obj = yaffs_dentry_to_obj(f->f_dentry); + + dev = obj->my_dev; + + yaffs_gross_lock(dev); + + n_free_chunks = yaffs_get_n_free_chunks(dev); + + yaffs_gross_unlock(dev); + + return (n_free_chunks > 20) ? 1 : 0; +} + +static void yaffs_release_space(struct file *f) +{ + struct yaffs_obj *obj; + struct yaffs_dev *dev; + + obj = yaffs_dentry_to_obj(f->f_dentry); + + dev = obj->my_dev; + + yaffs_gross_lock(dev); + + yaffs_gross_unlock(dev); +} + +#if (YAFFS_USE_WRITE_BEGIN_END > 0) +static int yaffs_write_begin(struct file *filp, struct address_space *mapping, + loff_t pos, unsigned len, unsigned flags, + struct page **pagep, void **fsdata) +{ + struct page *pg = NULL; + pgoff_t index = pos >> PAGE_CACHE_SHIFT; + + int ret = 0; + int space_held = 0; + + /* Get a page */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) + pg = grab_cache_page_write_begin(mapping, index, flags); +#else + pg = __grab_cache_page(mapping, index); +#endif + + *pagep = pg; + if (!pg) { + ret = -ENOMEM; + goto out; + } + yaffs_trace(YAFFS_TRACE_OS, + "start yaffs_write_begin index %d(%x) uptodate %d", + (int)index, (int)index, Page_Uptodate(pg) ? 1 : 0); + + /* Get fs space */ + space_held = yaffs_hold_space(filp); + + if (!space_held) { + ret = -ENOSPC; + goto out; + } + + /* Update page if required */ + + if (!Page_Uptodate(pg)) + ret = yaffs_readpage_nolock(filp, pg); + + if (ret) + goto out; + + /* Happy path return */ + yaffs_trace(YAFFS_TRACE_OS, "end yaffs_write_begin - ok"); + + return 0; + +out: + yaffs_trace(YAFFS_TRACE_OS, + "end yaffs_write_begin fail returning %d", ret); + if (space_held) + yaffs_release_space(filp); + if (pg) { + unlock_page(pg); + page_cache_release(pg); + } + return ret; +} + +#else + +static int yaffs_prepare_write(struct file *f, struct page *pg, + unsigned offset, unsigned to) +{ + yaffs_trace(YAFFS_TRACE_OS, "yaffs_prepair_write"); + + if (!Page_Uptodate(pg)) + return yaffs_readpage_nolock(f, pg); + return 0; +} +#endif + + +static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, + loff_t * pos) +{ + struct yaffs_obj *obj; + int n_written; + loff_t ipos; + struct inode *inode; + struct yaffs_dev *dev; + + obj = yaffs_dentry_to_obj(f->f_dentry); + + if (!obj) { + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_file_write: hey obj is null!"); + return -EINVAL; + } + + dev = obj->my_dev; + + yaffs_gross_lock(dev); + + inode = f->f_dentry->d_inode; + + if (!S_ISBLK(inode->i_mode) && f->f_flags & O_APPEND) + ipos = inode->i_size; + else + ipos = *pos; + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_file_write about to write writing %u(%x) bytes to object %d at %lld", + (unsigned)n, (unsigned)n, obj->obj_id, ipos); + + n_written = yaffs_wr_file(obj, buf, ipos, n, 0); + + yaffs_set_super_dirty(dev); + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_file_write: %d(%x) bytes written", + (unsigned)n, (unsigned)n); + + if (n_written > 0) { + ipos += n_written; + *pos = ipos; + if (ipos > inode->i_size) { + inode->i_size = ipos; + inode->i_blocks = (ipos + 511) >> 9; + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_file_write size updated to %lld bytes, %d blocks", + ipos, (int)(inode->i_blocks)); + } + + } + yaffs_gross_unlock(dev); + return (n_written == 0) && (n > 0) ? -ENOSPC : n_written; +} + + +#if (YAFFS_USE_WRITE_BEGIN_END > 0) +static int yaffs_write_end(struct file *filp, struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *pg, void *fsdadata) +{ + int ret = 0; + void *addr, *kva; + uint32_t offset_into_page = pos & (PAGE_CACHE_SIZE - 1); + + kva = kmap(pg); + addr = kva + offset_into_page; + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_write_end addr %p pos %lld n_bytes %d", + addr, pos, copied); + + ret = yaffs_file_write(filp, addr, copied, &pos); + + if (ret != copied) { + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_write_end not same size ret %d copied %d", + ret, copied); + SetPageError(pg); + } + + kunmap(pg); + + yaffs_release_space(filp); + unlock_page(pg); + page_cache_release(pg); + return ret; +} +#else + +static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, + unsigned to) +{ + void *addr, *kva; + + loff_t pos = (((loff_t) pg->index) << PAGE_CACHE_SHIFT) + offset; + int n_bytes = to - offset; + int n_written; + + kva = kmap(pg); + addr = kva + offset; + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_commit_write addr %p pos %lld n_bytes %d", + addr, pos, n_bytes); + + n_written = yaffs_file_write(f, addr, n_bytes, &pos); + + if (n_written != n_bytes) { + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_commit_write not same size n_written %d n_bytes %d", + n_written, n_bytes); + SetPageError(pg); + } + kunmap(pg); + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_commit_write returning %d", + n_written == n_bytes ? 0 : n_written); + + return n_written == n_bytes ? 0 : n_written; +} +#endif + +static struct address_space_operations yaffs_file_address_operations = { + .readpage = yaffs_readpage, + .writepage = yaffs_writepage, +#if (YAFFS_USE_WRITE_BEGIN_END > 0) + .write_begin = yaffs_write_begin, + .write_end = yaffs_write_end, +#else + .prepare_write = yaffs_prepare_write, + .commit_write = yaffs_commit_write, +#endif +}; + + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) +static int yaffs_file_flush(struct file *file, fl_owner_t id) +#else +static int yaffs_file_flush(struct file *file) +#endif +{ + struct yaffs_obj *obj = yaffs_dentry_to_obj(file->f_dentry); + + struct yaffs_dev *dev = obj->my_dev; + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_file_flush object %d (%s)", + obj->obj_id, + obj->dirty ? "dirty" : "clean"); + + yaffs_gross_lock(dev); + + yaffs_flush_file(obj, 1, 0, 0); + + yaffs_gross_unlock(dev); + + return 0; +} + + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) +static int yaffs_sync_object(struct file *file, loff_t start, loff_t end, int datasync) +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34)) +static int yaffs_sync_object(struct file *file, int datasync) +#else +static int yaffs_sync_object(struct file *file, struct dentry *dentry, + int datasync) +#endif +{ + struct yaffs_obj *obj; + struct yaffs_dev *dev; +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34)) + struct dentry *dentry = file->f_path.dentry; +#endif + + obj = yaffs_dentry_to_obj(dentry); + + dev = obj->my_dev; + + yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, + "yaffs_sync_object"); + yaffs_gross_lock(dev); + yaffs_flush_file(obj, 1, datasync, 0); + yaffs_gross_unlock(dev); + return 0; +} + + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22)) +static const struct file_operations yaffs_file_operations = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + .read = new_sync_read, + .write = new_sync_write, + .read_iter = generic_file_read_iter, + .write_iter = generic_file_write_iter, +#else + .read = do_sync_read, + .write = do_sync_write, + .aio_read = generic_file_aio_read, + .aio_write = generic_file_aio_write, +#endif + .mmap = generic_file_mmap, + .flush = yaffs_file_flush, + .fsync = yaffs_sync_object, + .splice_read = generic_file_splice_read, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + .splice_write = iter_file_splice_write, +#else + .splice_write = generic_file_splice_write, +#endif + .llseek = generic_file_llseek, +}; + +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)) + +static const struct file_operations yaffs_file_operations = { + .read = do_sync_read, + .write = do_sync_write, + .aio_read = generic_file_aio_read, + .aio_write = generic_file_aio_write, + .mmap = generic_file_mmap, + .flush = yaffs_file_flush, + .fsync = yaffs_sync_object, + .sendfile = generic_file_sendfile, +}; + +#else + +static const struct file_operations yaffs_file_operations = { + .read = generic_file_read, + .write = generic_file_write, + .mmap = generic_file_mmap, + .flush = yaffs_file_flush, + .fsync = yaffs_sync_object, +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) + .sendfile = generic_file_sendfile, +#endif +}; +#endif + + + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) +static void zero_user_segment(struct page *page, unsigned start, unsigned end) +{ + void *kaddr = kmap_atomic(page, KM_USER0); + memset(kaddr + start, 0, end - start); + kunmap_atomic(kaddr, KM_USER0); + flush_dcache_page(page); +} +#endif + + +static int yaffs_vfs_setsize(struct inode *inode, loff_t newsize) +{ +#ifdef YAFFS_USE_TRUNCATE_SETSIZE + truncate_setsize(inode, newsize); + return 0; +#else + truncate_inode_pages(&inode->i_data, newsize); + return 0; +#endif + +} + + +static int yaffs_vfs_setattr(struct inode *inode, struct iattr *attr) +{ +#ifdef YAFFS_USE_SETATTR_COPY + setattr_copy(inode, attr); + return 0; +#else + return inode_setattr(inode, attr); +#endif + +} + +static int yaffs_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + int error = 0; + struct yaffs_dev *dev; + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_setattr of object %d", + yaffs_inode_to_obj(inode)->obj_id); +#if 0 + /* Fail if a requested resize >= 2GB */ + if (attr->ia_valid & ATTR_SIZE && (attr->ia_size >> 31)) + error = -EINVAL; +#endif + + if (error == 0) + error = inode_change_ok(inode, attr); + if (error == 0) { + int result; + if (!error) { + error = yaffs_vfs_setattr(inode, attr); + yaffs_trace(YAFFS_TRACE_OS, "inode_setattr called"); + if (attr->ia_valid & ATTR_SIZE) { + yaffs_vfs_setsize(inode, attr->ia_size); + inode->i_blocks = (inode->i_size + 511) >> 9; + } + } + dev = yaffs_inode_to_obj(inode)->my_dev; + if (attr->ia_valid & ATTR_SIZE) { + yaffs_trace(YAFFS_TRACE_OS, + "resize to %d(%x)", + (int)(attr->ia_size), + (int)(attr->ia_size)); + } + yaffs_gross_lock(dev); + result = yaffs_set_attribs(yaffs_inode_to_obj(inode), attr); + if (result == YAFFS_OK) { + error = 0; + } else { + error = -EPERM; + } + yaffs_gross_unlock(dev); + + } + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_setattr done returning %d", error); + + return error; +} + +static int yaffs_setxattr(struct dentry *dentry, const char *name, + const void *value, size_t size, int flags) +{ + struct inode *inode = dentry->d_inode; + int error = 0; + struct yaffs_dev *dev; + struct yaffs_obj *obj = yaffs_inode_to_obj(inode); + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr of object %d", obj->obj_id); + + if (error == 0) { + int result; + dev = obj->my_dev; + yaffs_gross_lock(dev); + result = yaffs_set_xattrib(obj, name, value, size, flags); + if (result == YAFFS_OK) + error = 0; + else if (result < 0) + error = result; + yaffs_gross_unlock(dev); + + } + yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr done returning %d", error); + + return error; +} + +static ssize_t yaffs_getxattr(struct dentry * dentry, const char *name, + void *buff, size_t size) +{ + struct inode *inode = dentry->d_inode; + int error = 0; + struct yaffs_dev *dev; + struct yaffs_obj *obj = yaffs_inode_to_obj(inode); + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_getxattr \"%s\" from object %d", + name, obj->obj_id); + + if (error == 0) { + dev = obj->my_dev; + yaffs_gross_lock(dev); + error = yaffs_get_xattrib(obj, name, buff, size); + yaffs_gross_unlock(dev); + + } + yaffs_trace(YAFFS_TRACE_OS, "yaffs_getxattr done returning %d", error); + + return error; +} + +static int yaffs_removexattr(struct dentry *dentry, const char *name) +{ + struct inode *inode = dentry->d_inode; + int error = 0; + struct yaffs_dev *dev; + struct yaffs_obj *obj = yaffs_inode_to_obj(inode); + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_removexattr of object %d", obj->obj_id); + + if (error == 0) { + int result; + dev = obj->my_dev; + yaffs_gross_lock(dev); + result = yaffs_remove_xattrib(obj, name); + if (result == YAFFS_OK) + error = 0; + else if (result < 0) + error = result; + yaffs_gross_unlock(dev); + + } + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_removexattr done returning %d", error); + + return error; +} + +static ssize_t yaffs_listxattr(struct dentry * dentry, char *buff, size_t size) +{ + struct inode *inode = dentry->d_inode; + int error = 0; + struct yaffs_dev *dev; + struct yaffs_obj *obj = yaffs_inode_to_obj(inode); + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_listxattr of object %d", obj->obj_id); + + if (error == 0) { + dev = obj->my_dev; + yaffs_gross_lock(dev); + error = yaffs_list_xattrib(obj, buff, size); + yaffs_gross_unlock(dev); + + } + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_listxattr done returning %d", error); + + return error; +} + + +static const struct inode_operations yaffs_file_inode_operations = { + .setattr = yaffs_setattr, + .setxattr = yaffs_setxattr, + .getxattr = yaffs_getxattr, + .listxattr = yaffs_listxattr, + .removexattr = yaffs_removexattr, +}; + + +static int yaffs_readlink(struct dentry *dentry, char __user * buffer, + int buflen) +{ + unsigned char *alias; + int ret; + + struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; + + yaffs_gross_lock(dev); + + alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); + + yaffs_gross_unlock(dev); + + if (!alias) + return -ENOMEM; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0) + ret = vfs_readlink(dentry, buffer, buflen, alias); +#else + ret = readlink_copy(buffer, buflen, alias); +#endif + kfree(alias); + return ret; +} + +#if (YAFFS_NEW_FOLLOW_LINK == 1) +static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + void *ret; +#else +static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + int ret +#endif + unsigned char *alias; + int ret_int = 0; + struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; + + yaffs_gross_lock(dev); + + alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); + yaffs_gross_unlock(dev); + + if (!alias) { + ret_int = -ENOMEM; + goto out; + } +#if (YAFFS_NEW_FOLLOW_LINK == 1) + nd_set_link(nd, alias); + ret = alias; +out: + if (ret_int) + ret = ERR_PTR(ret_int); + return ret; +#else + ret = vfs_follow_link(nd, alias); + kfree(alias); +out: + if (ret_int) + ret = ret_int; + return ret; +#endif +} + + +#ifdef YAFFS_HAS_PUT_INODE + +/* For now put inode is just for debugging + * Put inode is called when the inode **structure** is put. + */ +static void yaffs_put_inode(struct inode *inode) +{ + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_put_inode: ino %d, count %d"), + (int)inode->i_ino, atomic_read(&inode->i_count); + +} +#endif + +#if (YAFFS_NEW_FOLLOW_LINK == 1) +void yaffs_put_link(struct dentry *dentry, struct nameidata *nd, void *alias) +{ + kfree(alias); +} +#endif + +static const struct inode_operations yaffs_symlink_inode_operations = { + .readlink = yaffs_readlink, + .follow_link = yaffs_follow_link, +#if (YAFFS_NEW_FOLLOW_LINK == 1) + .put_link = yaffs_put_link, +#endif + .setattr = yaffs_setattr, + .setxattr = yaffs_setxattr, + .getxattr = yaffs_getxattr, + .listxattr = yaffs_listxattr, + .removexattr = yaffs_removexattr, +}; + +#ifdef YAFFS_USE_OWN_IGET + +static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino) +{ + struct inode *inode; + struct yaffs_obj *obj; + struct yaffs_dev *dev = yaffs_super_to_dev(sb); + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_iget for %lu", ino); + + inode = iget_locked(sb, ino); + if (!inode) + return ERR_PTR(-ENOMEM); + if (!(inode->i_state & I_NEW)) + return inode; + + /* NB This is called as a side effect of other functions, but + * we had to release the lock to prevent deadlocks, so + * need to lock again. + */ + + yaffs_gross_lock(dev); + + obj = yaffs_find_by_number(dev, inode->i_ino); + + yaffs_fill_inode_from_obj(inode, obj); + + yaffs_gross_unlock(dev); + + unlock_new_inode(inode); + return inode; +} + +#else + +static void yaffs_read_inode(struct inode *inode) +{ + /* NB This is called as a side effect of other functions, but + * we had to release the lock to prevent deadlocks, so + * need to lock again. + */ + + struct yaffs_obj *obj; + struct yaffs_dev *dev = yaffs_super_to_dev(inode->i_sb); + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_read_inode for %d", (int)inode->i_ino); + + if (current != yaffs_dev_to_lc(dev)->readdir_process) + yaffs_gross_lock(dev); + + obj = yaffs_find_by_number(dev, inode->i_ino); + + yaffs_fill_inode_from_obj(inode, obj); + + if (current != yaffs_dev_to_lc(dev)->readdir_process) + yaffs_gross_unlock(dev); +} + +#endif + + + +struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev, + struct yaffs_obj *obj) +{ + struct inode *inode; + + if (!sb) { + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_get_inode for NULL super_block!!"); + return NULL; + + } + + if (!obj) { + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_get_inode for NULL object!!"); + return NULL; + + } + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_get_inode for object %d", obj->obj_id); + + inode = Y_IGET(sb, obj->obj_id); + if (IS_ERR(inode)) + return NULL; + + /* NB Side effect: iget calls back to yaffs_read_inode(). */ + /* iget also increments the inode's i_count */ + /* NB You can't be holding gross_lock or deadlock will happen! */ + + return inode; +} + + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) +#define YCRED(x) x +#else +#define YCRED(x) (x->cred) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0) +#define YPROC_uid(p) (YCRED(p)->fsuid) +#define YPROC_gid(p) (YCRED(p)->fsgid) +#define EXTRACT_gid(x) x +#define EXTRACT_uid(x) x +#define MAKE_gid(x) x +#define MAKE_uid(x) x +#else +#define YPROC_uid(p) from_kuid(&init_user_ns, YCRED(p)->fsuid) +#define YPROC_gid(p) from_kgid(&init_user_ns, YCRED(p)->fsgid) +#define EXTRACT_gid(x) from_kgid(&init_user_ns, x) +#define EXTRACT_uid(x) from_kuid(&init_user_ns, x) +#define MAKE_gid(x) make_kgid(&init_user_ns, x) +#define MAKE_uid(x) make_kuid(&init_user_ns, x) +#endif + + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) +static int yaffs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, + dev_t rdev) +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) +static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, + dev_t rdev) +#else +static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, + int rdev) +#endif +{ + struct inode *inode; + + struct yaffs_obj *obj = NULL; + struct yaffs_dev *dev; + + struct yaffs_obj *parent = yaffs_inode_to_obj(dir); + + int error = -ENOSPC; + uid_t uid = YPROC_uid(current); + gid_t gid = + (dir->i_mode & S_ISGID) ? EXTRACT_gid(dir->i_gid) : YPROC_gid(current); + + if ((dir->i_mode & S_ISGID) && S_ISDIR(mode)) + mode |= S_ISGID; + + if (parent) { + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_mknod: parent object %d type %d", + parent->obj_id, parent->variant_type); + } else { + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_mknod: could not get parent object"); + return -EPERM; + } + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_mknod: making oject for %s, mode %x dev %x", + dentry->d_name.name, mode, rdev); + + dev = parent->my_dev; + + yaffs_gross_lock(dev); + + switch (mode & S_IFMT) { + default: + /* Special (socket, fifo, device...) */ + yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making special"); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) + obj = + yaffs_create_special(parent, dentry->d_name.name, mode, uid, + gid, old_encode_dev(rdev)); +#else + obj = + yaffs_create_special(parent, dentry->d_name.name, mode, uid, + gid, rdev); +#endif + break; + case S_IFREG: /* file */ + yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making file"); + obj = yaffs_create_file(parent, dentry->d_name.name, mode, uid, + gid); + break; + case S_IFDIR: /* directory */ + yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making directory"); + obj = yaffs_create_dir(parent, dentry->d_name.name, mode, + uid, gid); + break; + case S_IFLNK: /* symlink */ + yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making symlink"); + obj = NULL; /* Do we ever get here? */ + break; + } + + /* Can not call yaffs_get_inode() with gross lock held */ + yaffs_gross_unlock(dev); + + if (obj) { + inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj); + d_instantiate(dentry, inode); + update_dir_time(dir); + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_mknod created object %d count = %d", + obj->obj_id, atomic_read(&inode->i_count)); + error = 0; + yaffs_fill_inode_from_obj(dir, parent); + } else { + yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod failed making object"); + error = -ENOMEM; + } + + return error; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) +static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) +#else +static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) +#endif +{ + int ret_val; + yaffs_trace(YAFFS_TRACE_OS, "yaffs_mkdir"); + ret_val = yaffs_mknod(dir, dentry, mode | S_IFDIR, 0); + return ret_val; +} + + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) +static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode, + bool dummy) +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) +static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode, + struct nameidata *n) +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) +static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *n) +#else +static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode) +#endif +{ + yaffs_trace(YAFFS_TRACE_OS, "yaffs_create"); + return yaffs_mknod(dir, dentry, mode | S_IFREG, 0); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) +static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, + unsigned int dummy) +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) +static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *n) +#else +static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry) +#endif +{ + struct yaffs_obj *obj; + struct inode *inode = NULL; /* NCB 2.5/2.6 needs NULL here */ + + struct yaffs_dev *dev = yaffs_inode_to_obj(dir)->my_dev; + + if (current != yaffs_dev_to_lc(dev)->readdir_process) + yaffs_gross_lock(dev); + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup for %d:%s", + yaffs_inode_to_obj(dir)->obj_id, dentry->d_name.name); + + obj = yaffs_find_by_name(yaffs_inode_to_obj(dir), dentry->d_name.name); + + obj = yaffs_get_equivalent_obj(obj); /* in case it was a hardlink */ + + /* Can't hold gross lock when calling yaffs_get_inode() */ + if (current != yaffs_dev_to_lc(dev)->readdir_process) + yaffs_gross_unlock(dev); + + if (obj) { + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_lookup found %d", obj->obj_id); + + inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); + } else { + yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup not found"); + + } + +/* added NCB for 2.5/6 compatability - forces add even if inode is + * NULL which creates dentry hash */ + d_add(dentry, inode); + + return NULL; +} + +/* + * Create a link... + */ +static int yaffs_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *dentry) +{ + struct inode *inode = old_dentry->d_inode; + struct yaffs_obj *obj = NULL; + struct yaffs_obj *link = NULL; + struct yaffs_dev *dev; + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_link"); + + obj = yaffs_inode_to_obj(inode); + dev = obj->my_dev; + + yaffs_gross_lock(dev); + + if (!S_ISDIR(inode->i_mode)) /* Don't link directories */ + link = + yaffs_link_obj(yaffs_inode_to_obj(dir), dentry->d_name.name, + obj); + + if (link) { + set_nlink(old_dentry->d_inode, yaffs_get_obj_link_count(obj)); + d_instantiate(dentry, old_dentry->d_inode); + atomic_inc(&old_dentry->d_inode->i_count); + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_link link count %d i_count %d", + old_dentry->d_inode->i_nlink, + atomic_read(&old_dentry->d_inode->i_count)); + } + + yaffs_gross_unlock(dev); + + if (link) { + update_dir_time(dir); + return 0; + } + + return -EPERM; +} + +static int yaffs_symlink(struct inode *dir, struct dentry *dentry, + const char *symname) +{ + struct yaffs_obj *obj; + struct yaffs_dev *dev; + uid_t uid = YPROC_uid(current); + gid_t gid = + (dir->i_mode & S_ISGID) ? EXTRACT_gid(dir->i_gid) : YPROC_gid(current); + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_symlink"); + + if (strnlen(dentry->d_name.name, YAFFS_MAX_NAME_LENGTH + 1) > + YAFFS_MAX_NAME_LENGTH) + return -ENAMETOOLONG; + + if (strnlen(symname, YAFFS_MAX_ALIAS_LENGTH + 1) > + YAFFS_MAX_ALIAS_LENGTH) + return -ENAMETOOLONG; + + dev = yaffs_inode_to_obj(dir)->my_dev; + yaffs_gross_lock(dev); + obj = yaffs_create_symlink(yaffs_inode_to_obj(dir), dentry->d_name.name, + S_IFLNK | S_IRWXUGO, uid, gid, symname); + yaffs_gross_unlock(dev); + + if (obj) { + struct inode *inode; + + inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); + d_instantiate(dentry, inode); + update_dir_time(dir); + yaffs_trace(YAFFS_TRACE_OS, "symlink created OK"); + return 0; + } else { + yaffs_trace(YAFFS_TRACE_OS, "symlink not created"); + } + + return -ENOMEM; +} + +/* + * The VFS layer already does all the dentry stuff for rename. + * + * NB: POSIX says you can rename an object over an old object of the same name + */ +static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + struct yaffs_dev *dev; + int ret_val = YAFFS_FAIL; + struct yaffs_obj *target; + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_rename"); + dev = yaffs_inode_to_obj(old_dir)->my_dev; + + yaffs_gross_lock(dev); + + /* Check if the target is an existing directory that is not empty. */ + target = yaffs_find_by_name(yaffs_inode_to_obj(new_dir), + new_dentry->d_name.name); + + if (target && target->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY && + !list_empty(&target->variant.dir_variant.children)) { + + yaffs_trace(YAFFS_TRACE_OS, "target is non-empty dir"); + + ret_val = YAFFS_FAIL; + } else { + /* Now does unlinking internally using shadowing mechanism */ + yaffs_trace(YAFFS_TRACE_OS, "calling yaffs_rename_obj"); + + ret_val = yaffs_rename_obj(yaffs_inode_to_obj(old_dir), + old_dentry->d_name.name, + yaffs_inode_to_obj(new_dir), + new_dentry->d_name.name); + } + yaffs_gross_unlock(dev); + + if (ret_val == YAFFS_OK) { + if (target) + inode_dec_link_count(new_dentry->d_inode); + + update_dir_time(old_dir); + if (old_dir != new_dir) + update_dir_time(new_dir); + return 0; + } else { + return -ENOTEMPTY; + } +} + + + + +static int yaffs_unlink(struct inode *dir, struct dentry *dentry) +{ + int ret_val; + + struct yaffs_dev *dev; + struct yaffs_obj *obj; + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_unlink %d:%s", + (int)(dir->i_ino), dentry->d_name.name); + obj = yaffs_inode_to_obj(dir); + dev = obj->my_dev; + + yaffs_gross_lock(dev); + + ret_val = yaffs_unlinker(obj, dentry->d_name.name); + + if (ret_val == YAFFS_OK) { + inode_dec_link_count(dentry->d_inode); + dir->i_version++; + yaffs_gross_unlock(dev); + update_dir_time(dir); + return 0; + } + yaffs_gross_unlock(dev); + return -ENOTEMPTY; +} + + + +static const struct inode_operations yaffs_dir_inode_operations = { + .create = yaffs_create, + .lookup = yaffs_lookup, + .link = yaffs_link, + .unlink = yaffs_unlink, + .symlink = yaffs_symlink, + .mkdir = yaffs_mkdir, + .rmdir = yaffs_unlink, + .mknod = yaffs_mknod, + .rename = yaffs_rename, + .setattr = yaffs_setattr, + .setxattr = yaffs_setxattr, + .getxattr = yaffs_getxattr, + .listxattr = yaffs_listxattr, + .removexattr = yaffs_removexattr, +}; + +/*-----------------------------------------------------------------*/ +/* Directory search context allows us to unlock access to yaffs during + * filldir without causing problems with the directory being modified. + * This is similar to the tried and tested mechanism used in yaffs direct. + * + * A search context iterates along a doubly linked list of siblings in the + * directory. If the iterating object is deleted then this would corrupt + * the list iteration, likely causing a crash. The search context avoids + * this by using the remove_obj_fn to move the search context to the + * next object before the object is deleted. + * + * Many readdirs (and thus seach conexts) may be alive simulateously so + * each struct yaffs_dev has a list of these. + * + * A seach context lives for the duration of a readdir. + * + * All these functions must be called while yaffs is locked. + */ + +struct yaffs_search_context { + struct yaffs_dev *dev; + struct yaffs_obj *dir_obj; + struct yaffs_obj *next_return; + struct list_head others; +}; + +/* + * yaffs_new_search() creates a new search context, initialises it and + * adds it to the device's search context list. + * + * Called at start of readdir. + */ +static struct yaffs_search_context *yaffs_new_search(struct yaffs_obj *dir) +{ + struct yaffs_dev *dev = dir->my_dev; + struct yaffs_search_context *sc = + kmalloc(sizeof(struct yaffs_search_context), GFP_NOFS); + if (sc) { + sc->dir_obj = dir; + sc->dev = dev; + if (list_empty(&sc->dir_obj->variant.dir_variant.children)) + sc->next_return = NULL; + else + sc->next_return = + list_entry(dir->variant.dir_variant.children.next, + struct yaffs_obj, siblings); + INIT_LIST_HEAD(&sc->others); + list_add(&sc->others, &(yaffs_dev_to_lc(dev)->search_contexts)); + } + return sc; +} + +/* + * yaffs_search_end() disposes of a search context and cleans up. + */ +static void yaffs_search_end(struct yaffs_search_context *sc) +{ + if (sc) { + list_del(&sc->others); + kfree(sc); + } +} + +/* + * yaffs_search_advance() moves a search context to the next object. + * Called when the search iterates or when an object removal causes + * the search context to be moved to the next object. + */ +static void yaffs_search_advance(struct yaffs_search_context *sc) +{ + if (!sc) + return; + + if (sc->next_return == NULL || + list_empty(&sc->dir_obj->variant.dir_variant.children)) + sc->next_return = NULL; + else { + struct list_head *next = sc->next_return->siblings.next; + + if (next == &sc->dir_obj->variant.dir_variant.children) + sc->next_return = NULL; /* end of list */ + else + sc->next_return = + list_entry(next, struct yaffs_obj, siblings); + } +} + +/* + * yaffs_remove_obj_callback() is called when an object is unlinked. + * We check open search contexts and advance any which are currently + * on the object being iterated. + */ +static void yaffs_remove_obj_callback(struct yaffs_obj *obj) +{ + + struct list_head *i; + struct yaffs_search_context *sc; + struct list_head *search_contexts = + &(yaffs_dev_to_lc(obj->my_dev)->search_contexts); + + /* Iterate through the directory search contexts. + * If any are currently on the object being removed, then advance + * the search context to the next object to prevent a hanging pointer. + */ + list_for_each(i, search_contexts) { + sc = list_entry(i, struct yaffs_search_context, others); + if (sc->next_return == obj) + yaffs_search_advance(sc); + } + +} + + +/*-----------------------------------------------------------------*/ + +#ifdef YAFFS_USE_DIR_ITERATE +static int yaffs_iterate(struct file *f, struct dir_context *dc) +{ + struct yaffs_obj *obj; + struct yaffs_dev *dev; + struct yaffs_search_context *sc; + unsigned long curoffs; + struct yaffs_obj *l; + int ret_val = 0; + + char name[YAFFS_MAX_NAME_LENGTH + 1]; + + obj = yaffs_dentry_to_obj(f->f_dentry); + dev = obj->my_dev; + + yaffs_gross_lock(dev); + + yaffs_dev_to_lc(dev)->readdir_process = current; + + sc = yaffs_new_search(obj); + if (!sc) { + ret_val = -ENOMEM; + goto out; + } + + if (!dir_emit_dots(f, dc)) + return 0; + + curoffs = 1; + + while (sc->next_return) { + curoffs++; + l = sc->next_return; + if (curoffs >= dc->pos) { + int this_inode = yaffs_get_obj_inode(l); + int this_type = yaffs_get_obj_type(l); + + yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_readdir: %s inode %d", + name, yaffs_get_obj_inode(l)); + + yaffs_gross_unlock(dev); + + if (!dir_emit(dc, + name, + strlen(name), + this_inode, + this_type)) { + yaffs_gross_lock(dev); + goto out; + } + + yaffs_gross_lock(dev); + + dc->pos++; + f->f_pos++; + } + yaffs_search_advance(sc); + } + +out: + yaffs_search_end(sc); + yaffs_dev_to_lc(dev)->readdir_process = NULL; + yaffs_gross_unlock(dev); + + return ret_val; +} + +#else + +static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) +{ + struct yaffs_obj *obj; + struct yaffs_dev *dev; + struct yaffs_search_context *sc; + struct inode *inode = f->f_dentry->d_inode; + unsigned long offset, curoffs; + struct yaffs_obj *l; + int ret_val = 0; + + char name[YAFFS_MAX_NAME_LENGTH + 1]; + + obj = yaffs_dentry_to_obj(f->f_dentry); + dev = obj->my_dev; + + yaffs_gross_lock(dev); + + yaffs_dev_to_lc(dev)->readdir_process = current; + + offset = f->f_pos; + + sc = yaffs_new_search(obj); + if (!sc) { + ret_val = -ENOMEM; + goto out; + } + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_readdir: starting at %d", (int)offset); + + if (offset == 0) { + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_readdir: entry . ino %d", + (int)inode->i_ino); + yaffs_gross_unlock(dev); + if (filldir(dirent, ".", 1, offset, inode->i_ino, DT_DIR) < 0) { + yaffs_gross_lock(dev); + goto out; + } + yaffs_gross_lock(dev); + offset++; + f->f_pos++; + } + if (offset == 1) { + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_readdir: entry .. ino %d", + (int)f->f_dentry->d_parent->d_inode->i_ino); + yaffs_gross_unlock(dev); + if (filldir(dirent, "..", 2, offset, + f->f_dentry->d_parent->d_inode->i_ino, + DT_DIR) < 0) { + yaffs_gross_lock(dev); + goto out; + } + yaffs_gross_lock(dev); + offset++; + f->f_pos++; + } + + curoffs = 1; + + /* If the directory has changed since the open or last call to + readdir, rewind to after the 2 canned entries. */ + if (f->f_version != inode->i_version) { + offset = 2; + f->f_pos = offset; + f->f_version = inode->i_version; + } + + while (sc->next_return) { + curoffs++; + l = sc->next_return; + if (curoffs >= offset) { + int this_inode = yaffs_get_obj_inode(l); + int this_type = yaffs_get_obj_type(l); + + yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_readdir: %s inode %d", + name, yaffs_get_obj_inode(l)); + + yaffs_gross_unlock(dev); + + if (filldir(dirent, + name, + strlen(name), + offset, this_inode, this_type) < 0) { + yaffs_gross_lock(dev); + goto out; + } + + yaffs_gross_lock(dev); + + offset++; + f->f_pos++; + } + yaffs_search_advance(sc); + } + +out: + yaffs_search_end(sc); + yaffs_dev_to_lc(dev)->readdir_process = NULL; + yaffs_gross_unlock(dev); + + return ret_val; +} + +#endif + +static const struct file_operations yaffs_dir_operations = { + .read = generic_read_dir, +#ifdef YAFFS_USE_DIR_ITERATE + .iterate = yaffs_iterate, +#else + .readdir = yaffs_readdir, +#endif + .fsync = yaffs_sync_object, + .llseek = generic_file_llseek, +}; + +static void yaffs_fill_inode_from_obj(struct inode *inode, + struct yaffs_obj *obj) +{ + if (inode && obj) { + + /* Check mode against the variant type and attempt to repair if broken. */ + u32 mode = obj->yst_mode; + switch (obj->variant_type) { + case YAFFS_OBJECT_TYPE_FILE: + if (!S_ISREG(mode)) { + obj->yst_mode &= ~S_IFMT; + obj->yst_mode |= S_IFREG; + } + + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + if (!S_ISLNK(mode)) { + obj->yst_mode &= ~S_IFMT; + obj->yst_mode |= S_IFLNK; + } + + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + if (!S_ISDIR(mode)) { + obj->yst_mode &= ~S_IFMT; + obj->yst_mode |= S_IFDIR; + } + + break; + case YAFFS_OBJECT_TYPE_UNKNOWN: + case YAFFS_OBJECT_TYPE_HARDLINK: + case YAFFS_OBJECT_TYPE_SPECIAL: + default: + /* TODO? */ + break; + } + + inode->i_flags |= S_NOATIME; + + inode->i_ino = obj->obj_id; + inode->i_mode = obj->yst_mode; + inode->i_uid = MAKE_uid(obj->yst_uid); + inode->i_gid = MAKE_gid(obj->yst_gid); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) + inode->i_blksize = inode->i_sb->s_blocksize; +#endif +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) + + inode->i_rdev = old_decode_dev(obj->yst_rdev); + inode->i_atime.tv_sec = (time_t) (obj->yst_atime); + inode->i_atime.tv_nsec = 0; + inode->i_mtime.tv_sec = (time_t) obj->yst_mtime; + inode->i_mtime.tv_nsec = 0; + inode->i_ctime.tv_sec = (time_t) obj->yst_ctime; + inode->i_ctime.tv_nsec = 0; +#else + inode->i_rdev = obj->yst_rdev; + inode->i_atime = obj->yst_atime; + inode->i_mtime = obj->yst_mtime; + inode->i_ctime = obj->yst_ctime; +#endif + inode->i_size = yaffs_get_obj_length(obj); + inode->i_blocks = (inode->i_size + 511) >> 9; + + set_nlink(inode, yaffs_get_obj_link_count(obj)); + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_fill_inode mode %x uid %d gid %d size %lld count %d", + inode->i_mode, obj->yst_uid, obj->yst_gid, + inode->i_size, atomic_read(&inode->i_count)); + + switch (obj->yst_mode & S_IFMT) { + default: /* fifo, device or socket */ +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) + init_special_inode(inode, obj->yst_mode, + old_decode_dev(obj->yst_rdev)); +#else + init_special_inode(inode, obj->yst_mode, + (dev_t) (obj->yst_rdev)); +#endif + break; + case S_IFREG: /* file */ + inode->i_op = &yaffs_file_inode_operations; + inode->i_fop = &yaffs_file_operations; + inode->i_mapping->a_ops = + &yaffs_file_address_operations; + break; + case S_IFDIR: /* directory */ + inode->i_op = &yaffs_dir_inode_operations; + inode->i_fop = &yaffs_dir_operations; + break; + case S_IFLNK: /* symlink */ + inode->i_op = &yaffs_symlink_inode_operations; + break; + } + + yaffs_inode_to_obj_lv(inode) = obj; + + obj->my_inode = inode; + + } else { + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_fill_inode invalid parameters"); + } + +} + + + +/* + * yaffs background thread functions . + * yaffs_bg_thread_fn() the thread function + * yaffs_bg_start() launches the background thread. + * yaffs_bg_stop() cleans up the background thread. + * + * NB: + * The thread should only run after the yaffs is initialised + * The thread should be stopped before yaffs is unmounted. + * The thread should not do any writing while the fs is in read only. + */ + +static unsigned yaffs_bg_gc_urgency(struct yaffs_dev *dev) +{ + unsigned erased_chunks = + dev->n_erased_blocks * dev->param.chunks_per_block; + struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); + unsigned scattered = 0; /* Free chunks not in an erased block */ + + if (erased_chunks < dev->n_free_chunks) + scattered = (dev->n_free_chunks - erased_chunks); + + if (!context->bg_running) + return 0; + else if (scattered < (dev->param.chunks_per_block * 2)) + return 0; + else if (erased_chunks > dev->n_free_chunks / 2) + return 0; + else if (erased_chunks > dev->n_free_chunks / 4) + return 1; + else + return 2; +} + +#ifdef YAFFS_COMPILE_BACKGROUND + +void yaffs_background_waker(unsigned long data) +{ + wake_up_process((struct task_struct *)data); +} + +static int yaffs_bg_thread_fn(void *data) +{ + struct yaffs_dev *dev = (struct yaffs_dev *)data; + struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); + unsigned long now = jiffies; + unsigned long next_dir_update = now; + unsigned long next_gc = now; + unsigned long expires; + unsigned int urgency; + + int gc_result; + struct timer_list timer; + + yaffs_trace(YAFFS_TRACE_BACKGROUND, + "yaffs_background starting for dev %p", (void *)dev); + +#ifdef YAFFS_COMPILE_FREEZER + set_freezable(); +#endif + while (context->bg_running) { + yaffs_trace(YAFFS_TRACE_BACKGROUND, "yaffs_background"); + + if (kthread_should_stop()) + break; + +#ifdef YAFFS_COMPILE_FREEZER + if (try_to_freeze()) + continue; +#endif + yaffs_gross_lock(dev); + + now = jiffies; + + if (time_after(now, next_dir_update) && yaffs_bg_enable) { + yaffs_update_dirty_dirs(dev); + next_dir_update = now + HZ; + } + + if (time_after(now, next_gc) && yaffs_bg_enable) { + if (!dev->is_checkpointed) { + urgency = yaffs_bg_gc_urgency(dev); + gc_result = yaffs_bg_gc(dev, urgency); + if (urgency > 1) + next_gc = now + HZ / 20 + 1; + else if (urgency > 0) + next_gc = now + HZ / 10 + 1; + else + next_gc = now + HZ * 2; + } else { + /* + * gc not running so set to next_dir_update + * to cut down on wake ups + */ + next_gc = next_dir_update; + } + } + yaffs_gross_unlock(dev); +#if 1 + expires = next_dir_update; + if (time_before(next_gc, expires)) + expires = next_gc; + if (time_before(expires, now)) + expires = now + HZ; + + Y_INIT_TIMER(&timer); + timer.expires = expires + 1; + timer.data = (unsigned long)current; + timer.function = yaffs_background_waker; + + set_current_state(TASK_INTERRUPTIBLE); + add_timer(&timer); + schedule(); + del_timer_sync(&timer); +#else + msleep(10); +#endif + } + + return 0; +} + +static int yaffs_bg_start(struct yaffs_dev *dev) +{ + int retval = 0; + struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); + + if (dev->read_only) + return -1; + + context->bg_running = 1; + + context->bg_thread = kthread_run(yaffs_bg_thread_fn, + (void *)dev, "yaffs-bg-%d", + context->mount_id); + + if (IS_ERR(context->bg_thread)) { + retval = PTR_ERR(context->bg_thread); + context->bg_thread = NULL; + context->bg_running = 0; + } + return retval; +} + +static void yaffs_bg_stop(struct yaffs_dev *dev) +{ + struct yaffs_linux_context *ctxt = yaffs_dev_to_lc(dev); + + ctxt->bg_running = 0; + + if (ctxt->bg_thread) { + kthread_stop(ctxt->bg_thread); + ctxt->bg_thread = NULL; + } +} +#else +static int yaffs_bg_thread_fn(void *data) +{ + return 0; +} + +static int yaffs_bg_start(struct yaffs_dev *dev) +{ + return 0; +} + +static void yaffs_bg_stop(struct yaffs_dev *dev) +{ +} +#endif + + +static void yaffs_flush_inodes(struct super_block *sb) +{ + struct inode *iptr; + struct yaffs_obj *obj; + + list_for_each_entry(iptr, &sb->s_inodes, i_sb_list) { + obj = yaffs_inode_to_obj(iptr); + if (obj) { + yaffs_trace(YAFFS_TRACE_OS, + "flushing obj %d", + obj->obj_id); + yaffs_flush_file(obj, 1, 0, 0); + } + } +} + +static void yaffs_flush_super(struct super_block *sb, int do_checkpoint) +{ + struct yaffs_dev *dev = yaffs_super_to_dev(sb); + if (!dev) + return; + + yaffs_flush_inodes(sb); + yaffs_update_dirty_dirs(dev); + yaffs_flush_whole_cache(dev, 1); + if (do_checkpoint) + yaffs_checkpoint_save(dev); +} + +static LIST_HEAD(yaffs_context_list); +struct mutex yaffs_context_lock; + +static void yaffs_put_super(struct super_block *sb) +{ + struct yaffs_dev *dev = yaffs_super_to_dev(sb); + struct mtd_info *mtd = yaffs_dev_to_mtd(dev); + + yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS, + "yaffs_put_super"); + + yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, + "Shutting down yaffs background thread"); + yaffs_bg_stop(dev); + yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, + "yaffs background thread shut down"); + + yaffs_gross_lock(dev); + + yaffs_flush_super(sb, 1); + + yaffs_deinitialise(dev); + + yaffs_gross_unlock(dev); + + mutex_lock(&yaffs_context_lock); + list_del_init(&(yaffs_dev_to_lc(dev)->context_list)); + mutex_unlock(&yaffs_context_lock); + + if (yaffs_dev_to_lc(dev)->spare_buffer) { + kfree(yaffs_dev_to_lc(dev)->spare_buffer); + yaffs_dev_to_lc(dev)->spare_buffer = NULL; + } + + kfree(dev); + + yaffs_put_mtd_device(mtd); + + yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS, + "yaffs_put_super done"); +} + + +static unsigned yaffs_gc_control_callback(struct yaffs_dev *dev) +{ + return yaffs_gc_control; +} + + +#ifdef YAFFS_COMPILE_EXPORTFS + +static struct inode *yaffs2_nfs_get_inode(struct super_block *sb, uint64_t ino, + uint32_t generation) +{ + return Y_IGET(sb, ino); +} + +static struct dentry *yaffs2_fh_to_dentry(struct super_block *sb, + struct fid *fid, int fh_len, + int fh_type) +{ + return generic_fh_to_dentry(sb, fid, fh_len, fh_type, + yaffs2_nfs_get_inode); +} + +static struct dentry *yaffs2_fh_to_parent(struct super_block *sb, + struct fid *fid, int fh_len, + int fh_type) +{ + return generic_fh_to_parent(sb, fid, fh_len, fh_type, + yaffs2_nfs_get_inode); +} + +struct dentry *yaffs2_get_parent(struct dentry *dentry) +{ + + struct super_block *sb = dentry->d_inode->i_sb; + struct dentry *parent = ERR_PTR(-ENOENT); + struct inode *inode; + unsigned long parent_ino; + struct yaffs_obj *d_obj; + struct yaffs_obj *parent_obj; + + d_obj = yaffs_inode_to_obj(dentry->d_inode); + + if (d_obj) { + parent_obj = d_obj->parent; + if (parent_obj) { + parent_ino = yaffs_get_obj_inode(parent_obj); + inode = Y_IGET(sb, parent_ino); + + if (IS_ERR(inode)) { + parent = ERR_CAST(inode); + } else { + parent = d_obtain_alias(inode); + if (!IS_ERR(parent)) { + parent = ERR_PTR(-ENOMEM); + iput(inode); + } + } + } + } + + return parent; +} + +/* Just declare a zero structure as a NULL value implies + * using the default functions of exportfs. + */ + +static struct export_operations yaffs_export_ops = { + .fh_to_dentry = yaffs2_fh_to_dentry, + .fh_to_parent = yaffs2_fh_to_parent, + .get_parent = yaffs2_get_parent, +}; + +#endif + +static void yaffs_unstitch_obj(struct inode *inode, struct yaffs_obj *obj) +{ + /* Clear the association between the inode and + * the struct yaffs_obj. + */ + obj->my_inode = NULL; + yaffs_inode_to_obj_lv(inode) = NULL; + + /* If the object freeing was deferred, then the real + * free happens now. + * This should fix the inode inconsistency problem. + */ + yaffs_handle_defered_free(obj); +} + +#ifdef YAFFS_HAS_EVICT_INODE +/* yaffs_evict_inode combines into one operation what was previously done in + * yaffs_clear_inode() and yaffs_delete_inode() + * + */ +static void yaffs_evict_inode(struct inode *inode) +{ + struct yaffs_obj *obj; + struct yaffs_dev *dev; + int deleteme = 0; + + obj = yaffs_inode_to_obj(inode); + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_evict_inode: ino %d, count %d %s", + (int)inode->i_ino, atomic_read(&inode->i_count), + obj ? "object exists" : "null object"); + + if (!inode->i_nlink && !is_bad_inode(inode)) + deleteme = 1; + truncate_inode_pages(&inode->i_data, 0); + Y_CLEAR_INODE(inode); + + if (deleteme && obj) { + dev = obj->my_dev; + yaffs_gross_lock(dev); + yaffs_del_obj(obj); + yaffs_gross_unlock(dev); + } + if (obj) { + dev = obj->my_dev; + yaffs_gross_lock(dev); + yaffs_unstitch_obj(inode, obj); + yaffs_gross_unlock(dev); + } +} +#else + +/* clear is called to tell the fs to release any per-inode data it holds. + * The object might still exist on disk and is just being thrown out of the cache + * or else the object has actually been deleted and we're being called via + * the chain + * yaffs_delete_inode() -> clear_inode()->yaffs_clear_inode() + */ + +static void yaffs_clear_inode(struct inode *inode) +{ + struct yaffs_obj *obj; + struct yaffs_dev *dev; + + obj = yaffs_inode_to_obj(inode); + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_clear_inode: ino %d, count %d %s", + (int)inode->i_ino, atomic_read(&inode->i_count), + obj ? "object exists" : "null object"); + + if (obj) { + dev = obj->my_dev; + yaffs_gross_lock(dev); + yaffs_unstitch_obj(inode, obj); + yaffs_gross_unlock(dev); + } + +} + +/* delete is called when the link count is zero and the inode + * is put (ie. nobody wants to know about it anymore, time to + * delete the file). + * NB Must call clear_inode() + */ +static void yaffs_delete_inode(struct inode *inode) +{ + struct yaffs_obj *obj = yaffs_inode_to_obj(inode); + struct yaffs_dev *dev; + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_delete_inode: ino %d, count %d %s", + (int)inode->i_ino, atomic_read(&inode->i_count), + obj ? "object exists" : "null object"); + + if (obj) { + dev = obj->my_dev; + yaffs_gross_lock(dev); + yaffs_del_obj(obj); + yaffs_gross_unlock(dev); + } +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) + truncate_inode_pages(&inode->i_data, 0); +#endif + clear_inode(inode); +} +#endif + + + + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) +static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; + struct super_block *sb = dentry->d_sb; +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) +static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf) +{ + struct yaffs_dev *dev = yaffs_super_to_dev(sb); +#else +static int yaffs_statfs(struct super_block *sb, struct statfs *buf) +{ + struct yaffs_dev *dev = yaffs_super_to_dev(sb); +#endif + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_statfs"); + + yaffs_gross_lock(dev); + + buf->f_type = YAFFS_MAGIC; + buf->f_bsize = sb->s_blocksize; + buf->f_namelen = 255; + + if (dev->data_bytes_per_chunk & (dev->data_bytes_per_chunk - 1)) { + /* Do this if chunk size is not a power of 2 */ + + uint64_t bytes_in_dev; + uint64_t bytes_free; + + bytes_in_dev = + ((uint64_t) + ((dev->param.end_block - dev->param.start_block + + 1))) * ((uint64_t) (dev->param.chunks_per_block * + dev->data_bytes_per_chunk)); + + do_div(bytes_in_dev, sb->s_blocksize); /* bytes_in_dev becomes the number of blocks */ + buf->f_blocks = bytes_in_dev; + + bytes_free = ((uint64_t) (yaffs_get_n_free_chunks(dev))) * + ((uint64_t) (dev->data_bytes_per_chunk)); + + do_div(bytes_free, sb->s_blocksize); + + buf->f_bfree = bytes_free; + + } else if (sb->s_blocksize > dev->data_bytes_per_chunk) { + + buf->f_blocks = + (dev->param.end_block - dev->param.start_block + 1) * + dev->param.chunks_per_block / + (sb->s_blocksize / dev->data_bytes_per_chunk); + buf->f_bfree = + yaffs_get_n_free_chunks(dev) / + (sb->s_blocksize / dev->data_bytes_per_chunk); + } else { + buf->f_blocks = + (dev->param.end_block - dev->param.start_block + 1) * + dev->param.chunks_per_block * + (dev->data_bytes_per_chunk / sb->s_blocksize); + + buf->f_bfree = + yaffs_get_n_free_chunks(dev) * + (dev->data_bytes_per_chunk / sb->s_blocksize); + } + + buf->f_files = 0; + buf->f_ffree = 0; + buf->f_bavail = buf->f_bfree; + + yaffs_gross_unlock(dev); + return 0; +} + + + +static int yaffs_do_sync_fs(struct super_block *sb, int request_checkpoint) +{ + + struct yaffs_dev *dev = yaffs_super_to_dev(sb); + unsigned int oneshot_checkpoint = (yaffs_auto_checkpoint & 4); + unsigned gc_urgent = yaffs_bg_gc_urgency(dev); + int do_checkpoint; + int dirty = yaffs_check_super_dirty(dev); + + yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, + "yaffs_do_sync_fs: gc-urgency %d %s %s%s", + gc_urgent, + dirty ? "dirty" : "clean", + request_checkpoint ? "checkpoint requested" : "no checkpoint", + oneshot_checkpoint ? " one-shot" : ""); + + yaffs_gross_lock(dev); + do_checkpoint = ((request_checkpoint && !gc_urgent) || + oneshot_checkpoint) && !dev->is_checkpointed; + + if (dirty || do_checkpoint) { + yaffs_flush_super(sb, !dev->is_checkpointed && do_checkpoint); + yaffs_clear_super_dirty(dev); + if (oneshot_checkpoint) + yaffs_auto_checkpoint &= ~4; + } + yaffs_gross_unlock(dev); + + return 0; +} + + +#ifdef YAFFS_HAS_WRITE_SUPER +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) +static void yaffs_write_super(struct super_block *sb) +#else +static int yaffs_write_super(struct super_block *sb) +#endif +{ + unsigned request_checkpoint = (yaffs_auto_checkpoint >= 2); + + yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, + "yaffs_write_super %s", + request_checkpoint ? " checkpt" : ""); + + yaffs_do_sync_fs(sb, request_checkpoint); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)) + return 0; +#endif +} +#endif + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) +static int yaffs_sync_fs(struct super_block *sb, int wait) +#else +static int yaffs_sync_fs(struct super_block *sb) +#endif +{ + unsigned request_checkpoint = (yaffs_auto_checkpoint >= 1); + + yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, + "yaffs_sync_fs%s", request_checkpoint ? " checkpt" : ""); + + yaffs_do_sync_fs(sb, request_checkpoint); + + return 0; +} + +/* the function only is used to change dev->read_only when this file system + * is remounted. + */ +static int yaffs_remount_fs(struct super_block *sb, int *flags, char *data) +{ + int read_only = 0; + struct mtd_info *mtd; + struct yaffs_dev *dev = 0; + + /* Get the device */ + mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); + if (!mtd) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "MTD device #%u doesn't appear to exist", + MINOR(sb->s_dev)); + return 1; + } + + /* Check it's NAND */ + if (mtd->type != MTD_NANDFLASH) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "MTD device is not NAND it's type %d", + mtd->type); + return 1; + } + + read_only = ((*flags & MS_RDONLY) != 0); + if (!read_only && !(mtd->flags & MTD_WRITEABLE)) { + read_only = 1; + printk(KERN_INFO + "yaffs: mtd is read only, setting superblock read only"); + *flags |= MS_RDONLY; + } + + dev = sb->s_fs_info; + dev->read_only = read_only; + + return 0; +} + +static const struct super_operations yaffs_super_ops = { + .statfs = yaffs_statfs, + +#ifndef YAFFS_USE_OWN_IGET + .read_inode = yaffs_read_inode, +#endif +#ifdef YAFFS_HAS_PUT_INODE + .put_inode = yaffs_put_inode, +#endif + .put_super = yaffs_put_super, +#ifdef YAFFS_HAS_EVICT_INODE + .evict_inode = yaffs_evict_inode, +#else + .delete_inode = yaffs_delete_inode, + .clear_inode = yaffs_clear_inode, +#endif + .sync_fs = yaffs_sync_fs, +#ifdef YAFFS_HAS_WRITE_SUPER + .write_super = yaffs_write_super, +#endif + .remount_fs = yaffs_remount_fs, +}; + +struct yaffs_options { + int inband_tags; + int skip_checkpoint_read; + int skip_checkpoint_write; + int no_cache; + int tags_ecc_on; + int tags_ecc_overridden; + int lazy_loading_enabled; + int lazy_loading_overridden; + int empty_lost_and_found; + int empty_lost_and_found_overridden; + int disable_summary; +}; + +#define MAX_OPT_LEN 30 +static int yaffs_parse_options(struct yaffs_options *options, + const char *options_str) +{ + char cur_opt[MAX_OPT_LEN + 1]; + int p; + int error = 0; + + /* Parse through the options which is a comma seperated list */ + + while (options_str && *options_str && !error) { + memset(cur_opt, 0, MAX_OPT_LEN + 1); + p = 0; + + while (*options_str == ',') + options_str++; + + while (*options_str && *options_str != ',') { + if (p < MAX_OPT_LEN) { + cur_opt[p] = *options_str; + p++; + } + options_str++; + } + + if (!strcmp(cur_opt, "inband-tags")) { + options->inband_tags = 1; + } else if (!strcmp(cur_opt, "tags-ecc-off")) { + options->tags_ecc_on = 0; + options->tags_ecc_overridden = 1; + } else if (!strcmp(cur_opt, "tags-ecc-on")) { + options->tags_ecc_on = 1; + options->tags_ecc_overridden = 1; + } else if (!strcmp(cur_opt, "lazy-loading-off")) { + options->lazy_loading_enabled = 0; + options->lazy_loading_overridden = 1; + } else if (!strcmp(cur_opt, "lazy-loading-on")) { + options->lazy_loading_enabled = 1; + options->lazy_loading_overridden = 1; + } else if (!strcmp(cur_opt, "disable-summary")) { + options->disable_summary = 1; + } else if (!strcmp(cur_opt, "empty-lost-and-found-off")) { + options->empty_lost_and_found = 0; + options->empty_lost_and_found_overridden = 1; + } else if (!strcmp(cur_opt, "empty-lost-and-found-on")) { + options->empty_lost_and_found = 1; + options->empty_lost_and_found_overridden = 1; + } else if (!strcmp(cur_opt, "no-cache")) { + options->no_cache = 1; + } else if (!strcmp(cur_opt, "no-checkpoint-read")) { + options->skip_checkpoint_read = 1; + } else if (!strcmp(cur_opt, "no-checkpoint-write")) { + options->skip_checkpoint_write = 1; + } else if (!strcmp(cur_opt, "no-checkpoint")) { + options->skip_checkpoint_read = 1; + options->skip_checkpoint_write = 1; + } else { + printk(KERN_INFO "yaffs: Bad mount option \"%s\"\n", + cur_opt); + error = 1; + } + } + + return error; +} + + +static struct dentry *yaffs_make_root(struct inode *inode) +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) + struct dentry *root = d_alloc_root(inode); + + if (!root) + iput(inode); + + return root; +#else + return d_make_root(inode); +#endif +} + + + + +static struct super_block *yaffs_internal_read_super(int yaffs_version, + struct super_block *sb, + void *data, int silent) +{ + int n_blocks; + struct inode *inode = NULL; + struct dentry *root; + struct yaffs_dev *dev = 0; + char devname_buf[BDEVNAME_SIZE + 1]; + struct mtd_info *mtd; + int err; + char *data_str = (char *)data; + struct yaffs_linux_context *context = NULL; + struct yaffs_param *param; + + int read_only = 0; + int inband_tags = 0; + + struct yaffs_options options; + + unsigned mount_id; + int found; + struct yaffs_linux_context *context_iterator; + struct list_head *l; + + if (!sb) { + printk(KERN_INFO "yaffs: sb is NULL\n"); + return NULL; + } + + sb->s_magic = YAFFS_MAGIC; + sb->s_op = &yaffs_super_ops; + sb->s_flags |= MS_NOATIME; + + read_only = ((sb->s_flags & MS_RDONLY) != 0); + +#ifdef YAFFS_COMPILE_EXPORTFS + sb->s_export_op = &yaffs_export_ops; +#endif + + if (!sb->s_dev) + printk(KERN_INFO "yaffs: sb->s_dev is NULL\n"); + else if (!yaffs_devname(sb, devname_buf)) + printk(KERN_INFO "yaffs: devname is NULL\n"); + else + printk(KERN_INFO "yaffs: dev is %d name is \"%s\" %s\n", + sb->s_dev, + yaffs_devname(sb, devname_buf), read_only ? "ro" : "rw"); + + if (!data_str) + data_str = ""; + + printk(KERN_INFO "yaffs: passed flags \"%s\"\n", data_str); + + memset(&options, 0, sizeof(options)); + + if (yaffs_parse_options(&options, data_str)) { + /* Option parsing failed */ + return NULL; + } + + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_read_super: Using yaffs%d", yaffs_version); + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_read_super: block size %d", (int)(sb->s_blocksize)); + + yaffs_trace(YAFFS_TRACE_ALWAYS, + "yaffs: Attempting MTD mount of %u.%u,\"%s\"", + MAJOR(sb->s_dev), MINOR(sb->s_dev), + yaffs_devname(sb, devname_buf)); + + /* Get the device */ + mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); + if (IS_ERR(mtd)) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "yaffs: MTD device %u either not valid or unavailable", + MINOR(sb->s_dev)); + return NULL; + } + + if (yaffs_auto_select && yaffs_version == 1 && WRITE_SIZE(mtd) >= 2048) { + yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs2"); + yaffs_version = 2; + } + + /* Added NCB 26/5/2006 for completeness */ + if (yaffs_version == 2 && !options.inband_tags + && WRITE_SIZE(mtd) == 512) { + yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs1"); + yaffs_version = 1; + } + + if (mtd->oobavail < sizeof(struct yaffs_packed_tags2) || + options.inband_tags) + inband_tags = 1; + + if(yaffs_verify_mtd(mtd, yaffs_version, inband_tags) < 0) + return NULL; + + /* OK, so if we got here, we have an MTD that's NAND and looks + * like it has the right capabilities + * Set the struct yaffs_dev up for mtd + */ + + if (!read_only && !(mtd->flags & MTD_WRITEABLE)) { + read_only = 1; + printk(KERN_INFO + "yaffs: mtd is read only, setting superblock read only\n" + ); + sb->s_flags |= MS_RDONLY; + } + + dev = kmalloc(sizeof(struct yaffs_dev), GFP_KERNEL); + context = kmalloc(sizeof(struct yaffs_linux_context), GFP_KERNEL); + + if (!dev || !context) { + kfree(dev); + kfree(context); + dev = NULL; + context = NULL; + + /* Deep shit could not allocate device structure */ + yaffs_trace(YAFFS_TRACE_ALWAYS, + "yaffs_read_super: Failed trying to allocate struct yaffs_dev." + ); + return NULL; + } + memset(dev, 0, sizeof(struct yaffs_dev)); + param = &(dev->param); + + memset(context, 0, sizeof(struct yaffs_linux_context)); + dev->os_context = context; + INIT_LIST_HEAD(&(context->context_list)); + context->dev = dev; + context->super = sb; + + dev->read_only = read_only; + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) + sb->s_fs_info = dev; +#else + sb->u.generic_sbp = dev; +#endif + + + dev->driver_context = mtd; + param->name = mtd->name; + + /* Set up the memory size parameters.... */ + + + param->n_reserved_blocks = 5; + param->n_caches = (options.no_cache) ? 0 : 10; + param->inband_tags = inband_tags; + + param->enable_xattr = 1; + if (options.lazy_loading_overridden) + param->disable_lazy_load = !options.lazy_loading_enabled; + + param->defered_dir_update = 1; + + if (options.tags_ecc_overridden) + param->no_tags_ecc = !options.tags_ecc_on; + + param->empty_lost_n_found = 1; + param->refresh_period = 500; + param->disable_summary = options.disable_summary; + + +#ifdef CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING + param->disable_bad_block_marking = 1; +#endif + if (options.empty_lost_and_found_overridden) + param->empty_lost_n_found = options.empty_lost_and_found; + + /* ... and the functions. */ + if (yaffs_version == 2) { + param->is_yaffs2 = 1; +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) + param->total_bytes_per_chunk = mtd->writesize; + param->chunks_per_block = mtd->erasesize / mtd->writesize; +#else + param->total_bytes_per_chunk = mtd->oobblock; + param->chunks_per_block = mtd->erasesize / mtd->oobblock; +#endif + n_blocks = YCALCBLOCKS(mtd->size, mtd->erasesize); + + param->start_block = 0; + param->end_block = n_blocks - 1; + } else { + param->is_yaffs2 = 0; + n_blocks = YCALCBLOCKS(mtd->size, + YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); + + param->chunks_per_block = YAFFS_CHUNKS_PER_BLOCK; + param->total_bytes_per_chunk = YAFFS_BYTES_PER_CHUNK; + } + + param->start_block = 0; + param->end_block = n_blocks - 1; + + yaffs_mtd_drv_install(dev); + + param->sb_dirty_fn = yaffs_set_super_dirty; + param->gc_control_fn = yaffs_gc_control_callback; + + yaffs_dev_to_lc(dev)->super = sb; + +#if CONFIG_YAFFS_DOES_ECC + param->use_nand_ecc = 1; +#else + param->use_nand_ecc = 0; +#endif + param->skip_checkpt_rd = options.skip_checkpoint_read; + param->skip_checkpt_wr = options.skip_checkpoint_write; + + mutex_lock(&yaffs_context_lock); + /* Get a mount id */ + found = 0; + for (mount_id = 0; !found; mount_id++) { + found = 1; + list_for_each(l, &yaffs_context_list) { + context_iterator = + list_entry(l, struct yaffs_linux_context, + context_list); + if (context_iterator->mount_id == mount_id) + found = 0; + } + } + context->mount_id = mount_id; + + list_add_tail(&(yaffs_dev_to_lc(dev)->context_list), + &yaffs_context_list); + mutex_unlock(&yaffs_context_lock); + + /* Directory search handling... */ + INIT_LIST_HEAD(&(yaffs_dev_to_lc(dev)->search_contexts)); + param->remove_obj_fn = yaffs_remove_obj_callback; + + mutex_init(&(yaffs_dev_to_lc(dev)->gross_lock)); + + yaffs_gross_lock(dev); + + err = yaffs_guts_initialise(dev); + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_read_super: guts initialised %s", + (err == YAFFS_OK) ? "OK" : "FAILED"); + + if (err == YAFFS_OK) + yaffs_bg_start(dev); + + if (!context->bg_thread) + param->defered_dir_update = 0; + + sb->s_maxbytes = yaffs_max_file_size(dev); + + /* Release lock before yaffs_get_inode() */ + yaffs_gross_unlock(dev); + + /* Create root inode */ + if (err == YAFFS_OK) + inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0, yaffs_root(dev)); + + if (!inode) + return NULL; + + inode->i_op = &yaffs_dir_inode_operations; + inode->i_fop = &yaffs_dir_operations; + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: got root inode"); + + root = yaffs_make_root(inode); + + if (!root) + return NULL; + + sb->s_root = root; + if(!dev->is_checkpointed) + yaffs_set_super_dirty(dev); + + yaffs_trace(YAFFS_TRACE_ALWAYS, + "yaffs_read_super: is_checkpointed %d", + dev->is_checkpointed); + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: done"); + return sb; +} + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) +static int yaffs_internal_read_super_mtd(struct super_block *sb, void *data, + int silent) +{ + return yaffs_internal_read_super(1, sb, data, silent) ? 0 : -EINVAL; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) +static struct dentry *yaffs_mount(struct file_system_type *fs_type, int flags, + const char *dev_name, void *data) +{ + return mount_bdev(fs_type, flags, dev_name, data, yaffs_internal_read_super_mtd); +} +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) +static int yaffs_read_super(struct file_system_type *fs, + int flags, const char *dev_name, + void *data, struct vfsmount *mnt) +{ + + return get_sb_bdev(fs, flags, dev_name, data, + yaffs_internal_read_super_mtd, mnt); +} +#else +static struct super_block *yaffs_read_super(struct file_system_type *fs, + int flags, const char *dev_name, + void *data) +{ + + return get_sb_bdev(fs, flags, dev_name, data, + yaffs_internal_read_super_mtd); +} +#endif + +static struct file_system_type yaffs_fs_type = { + .owner = THIS_MODULE, + .name = "yaffs", +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) + .mount = yaffs_mount, +#else + .get_sb = yaffs_read_super, +#endif + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, +}; +#else +static struct super_block *yaffs_read_super(struct super_block *sb, void *data, + int silent) +{ + return yaffs_internal_read_super(1, sb, data, silent); +} + +static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, + FS_REQUIRES_DEV); +#endif + + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) +static int yaffs2_internal_read_super_mtd(struct super_block *sb, void *data, + int silent) +{ + return yaffs_internal_read_super(2, sb, data, silent) ? 0 : -EINVAL; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) +static struct dentry *yaffs2_mount(struct file_system_type *fs_type, int flags, + const char *dev_name, void *data) +{ + return mount_bdev(fs_type, flags, dev_name, data, yaffs2_internal_read_super_mtd); +} +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) +static int yaffs2_read_super(struct file_system_type *fs, + int flags, const char *dev_name, void *data, + struct vfsmount *mnt) +{ + return get_sb_bdev(fs, flags, dev_name, data, + yaffs2_internal_read_super_mtd, mnt); +} +#else +static struct super_block *yaffs2_read_super(struct file_system_type *fs, + int flags, const char *dev_name, + void *data) +{ + + return get_sb_bdev(fs, flags, dev_name, data, + yaffs2_internal_read_super_mtd); +} +#endif + +static struct file_system_type yaffs2_fs_type = { + .owner = THIS_MODULE, + .name = "yaffs2", +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) + .mount = yaffs2_mount, +#else + .get_sb = yaffs2_read_super, +#endif + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, +}; +#else +static struct super_block *yaffs2_read_super(struct super_block *sb, + void *data, int silent) +{ + return yaffs_internal_read_super(2, sb, data, silent); +} + +static DECLARE_FSTYPE(yaffs2_fs_type, "yaffs2", yaffs2_read_super, + FS_REQUIRES_DEV); +#endif + + +static struct proc_dir_entry *my_proc_entry; + +static char *yaffs_dump_dev_part0(char *buf, struct yaffs_dev *dev) +{ + struct yaffs_param *param = &dev->param; + int bs[10]; + + yaffs_count_blocks_by_state(dev,bs); + + buf += sprintf(buf, "start_block.......... %d\n", param->start_block); + buf += sprintf(buf, "end_block............ %d\n", param->end_block); + buf += sprintf(buf, "total_bytes_per_chunk %d\n", + param->total_bytes_per_chunk); + buf += sprintf(buf, "use_nand_ecc......... %d\n", param->use_nand_ecc); + buf += sprintf(buf, "no_tags_ecc.......... %d\n", param->no_tags_ecc); + buf += sprintf(buf, "is_yaffs2............ %d\n", param->is_yaffs2); + buf += sprintf(buf, "inband_tags.......... %d\n", param->inband_tags); + buf += sprintf(buf, "empty_lost_n_found... %d\n", + param->empty_lost_n_found); + buf += sprintf(buf, "disable_lazy_load.... %d\n", + param->disable_lazy_load); + buf += sprintf(buf, "disable_bad_block_mrk %d\n", + param->disable_bad_block_marking); + buf += sprintf(buf, "refresh_period....... %d\n", + param->refresh_period); + buf += sprintf(buf, "n_caches............. %d\n", param->n_caches); + buf += sprintf(buf, "n_reserved_blocks.... %d\n", + param->n_reserved_blocks); + buf += sprintf(buf, "always_check_erased.. %d\n", + param->always_check_erased); + buf += sprintf(buf, "\n"); + buf += sprintf(buf, "block count by state\n"); + buf += sprintf(buf, "0:%d 1:%d 2:%d 3:%d 4:%d\n", + bs[0], bs[1], bs[2], bs[3], bs[4]); + buf += sprintf(buf, "5:%d 6:%d 7:%d 8:%d 9:%d\n", + bs[5], bs[6], bs[7], bs[8], bs[9]); + + return buf; +} + +static char *yaffs_dump_dev_part1(char *buf, struct yaffs_dev *dev) +{ + buf += sprintf(buf, "max file size....... %lld\n", + (long long) yaffs_max_file_size(dev)); + buf += sprintf(buf, "data_bytes_per_chunk. %d\n", + dev->data_bytes_per_chunk); + buf += sprintf(buf, "chunk_grp_bits....... %d\n", dev->chunk_grp_bits); + buf += sprintf(buf, "chunk_grp_size....... %d\n", dev->chunk_grp_size); + buf += sprintf(buf, "n_erased_blocks...... %d\n", dev->n_erased_blocks); + buf += sprintf(buf, "blocks_in_checkpt.... %d\n", + dev->blocks_in_checkpt); + buf += sprintf(buf, "\n"); + buf += sprintf(buf, "n_tnodes............. %d\n", dev->n_tnodes); + buf += sprintf(buf, "n_obj................ %d\n", dev->n_obj); + buf += sprintf(buf, "n_free_chunks........ %d\n", dev->n_free_chunks); + buf += sprintf(buf, "\n"); + buf += sprintf(buf, "n_page_writes........ %u\n", dev->n_page_writes); + buf += sprintf(buf, "n_page_reads......... %u\n", dev->n_page_reads); + buf += sprintf(buf, "n_erasures........... %u\n", dev->n_erasures); + buf += sprintf(buf, "n_gc_copies.......... %u\n", dev->n_gc_copies); + buf += sprintf(buf, "all_gcs.............. %u\n", dev->all_gcs); + buf += sprintf(buf, "passive_gc_count..... %u\n", + dev->passive_gc_count); + buf += sprintf(buf, "oldest_dirty_gc_count %u\n", + dev->oldest_dirty_gc_count); + buf += sprintf(buf, "n_gc_blocks.......... %u\n", dev->n_gc_blocks); + buf += sprintf(buf, "bg_gcs............... %u\n", dev->bg_gcs); + buf += sprintf(buf, "n_retried_writes..... %u\n", + dev->n_retried_writes); + buf += sprintf(buf, "n_retired_blocks..... %u\n", + dev->n_retired_blocks); + buf += sprintf(buf, "n_ecc_fixed.......... %u\n", dev->n_ecc_fixed); + buf += sprintf(buf, "n_ecc_unfixed........ %u\n", dev->n_ecc_unfixed); + buf += sprintf(buf, "n_tags_ecc_fixed..... %u\n", + dev->n_tags_ecc_fixed); + buf += sprintf(buf, "n_tags_ecc_unfixed... %u\n", + dev->n_tags_ecc_unfixed); + buf += sprintf(buf, "cache_hits........... %u\n", dev->cache_hits); + buf += sprintf(buf, "n_deleted_files...... %u\n", dev->n_deleted_files); + buf += sprintf(buf, "n_unlinked_files..... %u\n", + dev->n_unlinked_files); + buf += sprintf(buf, "refresh_count........ %u\n", dev->refresh_count); + buf += sprintf(buf, "n_bg_deletions....... %u\n", dev->n_bg_deletions); + buf += sprintf(buf, "tags_used............ %u\n", dev->tags_used); + buf += sprintf(buf, "summary_used......... %u\n", dev->summary_used); + + return buf; +} + +static int yaffs_proc_read(char *page, + char **start, + off_t offset, int count, int *eof, void *data) +{ + struct list_head *item; + char *buf = page; + int step = offset; + int n = 0; + + /* Get proc_file_read() to step 'offset' by one on each sucessive call. + * We use 'offset' (*ppos) to indicate where we are in dev_list. + * This also assumes the user has posted a read buffer large + * enough to hold the complete output; but that's life in /proc. + */ + + *(int *)start = 1; + + /* Print header first */ + if (step == 0) + buf += + sprintf(buf, "Multi-version YAFFS\n"); + else if (step == 1) + buf += sprintf(buf, "\n"); + else { + step -= 2; + + mutex_lock(&yaffs_context_lock); + + /* Locate and print the Nth entry. Order N-squared but N is small. */ + list_for_each(item, &yaffs_context_list) { + struct yaffs_linux_context *dc = + list_entry(item, struct yaffs_linux_context, + context_list); + struct yaffs_dev *dev = dc->dev; + + if (n < (step & ~1)) { + n += 2; + continue; + } + if ((step & 1) == 0) { + buf += + sprintf(buf, "\nDevice %d \"%s\"\n", n, + dev->param.name); + buf = yaffs_dump_dev_part0(buf, dev); + } else { + buf = yaffs_dump_dev_part1(buf, dev); + } + + break; + } + mutex_unlock(&yaffs_context_lock); + } + + return buf - page < count ? buf - page : count; +} + +/** + * Set the verbosity of the warnings and error messages. + * + * Note that the names can only be a..z or _ with the current code. + */ + +static struct { + char *mask_name; + unsigned mask_bitfield; +} mask_flags[] = { + {"allocate", YAFFS_TRACE_ALLOCATE}, + {"always", YAFFS_TRACE_ALWAYS}, + {"background", YAFFS_TRACE_BACKGROUND}, + {"bad_blocks", YAFFS_TRACE_BAD_BLOCKS}, + {"buffers", YAFFS_TRACE_BUFFERS}, + {"bug", YAFFS_TRACE_BUG}, + {"checkpt", YAFFS_TRACE_CHECKPOINT}, + {"deletion", YAFFS_TRACE_DELETION}, + {"erase", YAFFS_TRACE_ERASE}, + {"error", YAFFS_TRACE_ERROR}, + {"gc_detail", YAFFS_TRACE_GC_DETAIL}, + {"gc", YAFFS_TRACE_GC}, + {"lock", YAFFS_TRACE_LOCK}, + {"mtd", YAFFS_TRACE_MTD}, + {"nandaccess", YAFFS_TRACE_NANDACCESS}, + {"os", YAFFS_TRACE_OS}, + {"scan_debug", YAFFS_TRACE_SCAN_DEBUG}, + {"scan", YAFFS_TRACE_SCAN}, + {"mount", YAFFS_TRACE_MOUNT}, + {"tracing", YAFFS_TRACE_TRACING}, + {"sync", YAFFS_TRACE_SYNC}, + {"write", YAFFS_TRACE_WRITE}, + {"verify", YAFFS_TRACE_VERIFY}, + {"verify_nand", YAFFS_TRACE_VERIFY_NAND}, + {"verify_full", YAFFS_TRACE_VERIFY_FULL}, + {"verify_all", YAFFS_TRACE_VERIFY_ALL}, + {"all", 0xffffffff}, + {"none", 0}, + {NULL, 0}, +}; + +#define MAX_MASK_NAME_LENGTH 40 +static int yaffs_proc_write_trace_options(struct file *file, const char *buf, + unsigned long count) +{ + unsigned rg = 0, mask_bitfield; + char *end; + char *mask_name; + const char *x; + char substring[MAX_MASK_NAME_LENGTH + 1]; + int i; + int done = 0; + int add, len = 0; + int pos = 0; + + rg = yaffs_trace_mask; + + while (!done && (pos < count)) { + done = 1; + while ((pos < count) && isspace(buf[pos])) + pos++; + + switch (buf[pos]) { + case '+': + case '-': + case '=': + add = buf[pos]; + pos++; + break; + + default: + add = ' '; + break; + } + mask_name = NULL; + + mask_bitfield = simple_strtoul(buf + pos, &end, 0); + + if (end > buf + pos) { + mask_name = "numeral"; + len = end - (buf + pos); + pos += len; + done = 0; + } else { + for (x = buf + pos, i = 0; + (*x == '_' || (*x >= 'a' && *x <= 'z')) && + i < MAX_MASK_NAME_LENGTH; x++, i++, pos++) + substring[i] = *x; + substring[i] = '\0'; + + for (i = 0; mask_flags[i].mask_name != NULL; i++) { + if (strcmp(substring, mask_flags[i].mask_name) + == 0) { + mask_name = mask_flags[i].mask_name; + mask_bitfield = + mask_flags[i].mask_bitfield; + done = 0; + break; + } + } + } + + if (mask_name != NULL) { + done = 0; + switch (add) { + case '-': + rg &= ~mask_bitfield; + break; + case '+': + rg |= mask_bitfield; + break; + case '=': + rg = mask_bitfield; + break; + default: + rg |= mask_bitfield; + break; + } + } + } + + yaffs_trace_mask = rg | YAFFS_TRACE_ALWAYS; + + printk(KERN_DEBUG "new trace = 0x%08X\n", yaffs_trace_mask); + + if (rg & YAFFS_TRACE_ALWAYS) { + for (i = 0; mask_flags[i].mask_name != NULL; i++) { + char flag; + flag = ((rg & mask_flags[i].mask_bitfield) == + mask_flags[i].mask_bitfield) ? '+' : '-'; + printk(KERN_DEBUG "%c%s\n", flag, + mask_flags[i].mask_name); + } + } + + return count; +} + +/* Debug strings are of the form: + * .bnnn print info on block n + * .cobjn,chunkn print nand chunk id for objn:chunkn + */ + +static int yaffs_proc_debug_write(struct file *file, const char *buf, + unsigned long count) +{ + + char str[100]; + char *p0; + char *p1; + long p1_val; + long p0_val; + char cmd; + struct list_head *item; + + memset(str, 0, sizeof(str)); + memcpy(str, buf, min((size_t)count, sizeof(str) -1)); + + cmd = str[1]; + + p0 = str + 2; + + p1 = p0; + + while (*p1 && *p1 != ',') { + p1++; + } + *p1 = '\0'; + p1++; + + p0_val = simple_strtol(p0, NULL, 0); + p1_val = simple_strtol(p1, NULL, 0); + + + mutex_lock(&yaffs_context_lock); + + /* Locate and print the Nth entry. Order N-squared but N is small. */ + list_for_each(item, &yaffs_context_list) { + struct yaffs_linux_context *dc = + list_entry(item, struct yaffs_linux_context, + context_list); + struct yaffs_dev *dev = dc->dev; + + if (cmd == 'b') { + struct yaffs_block_info *bi; + + bi = yaffs_get_block_info(dev,p0_val); + + if(bi) { + printk("Block %d: state %d, retire %d, use %d, seq %d\n", + (int)p0_val, bi->block_state, + bi->needs_retiring, bi->pages_in_use, + bi->seq_number); + } + } else if (cmd == 'c') { + struct yaffs_obj *obj; + int nand_chunk; + + obj = yaffs_find_by_number(dev, p0_val); + if (!obj) + printk("No obj %d\n", (int)p0_val); + else { + if(p1_val == 0) + nand_chunk = obj->hdr_chunk; + else + nand_chunk = + yaffs_find_chunk_in_file(obj, + p1_val, NULL); + printk("Nand chunk for %d:%d is %d\n", + (int)p0_val, (int)p1_val, nand_chunk); + } + } + } + + mutex_unlock(&yaffs_context_lock); + + return count; +} + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)) +static int yaffs_proc_write(struct file *file, const char *buf, + unsigned long count, void *ppos) +#else +static ssize_t yaffs_proc_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +#endif +{ + if (buf[0] == '.') + return yaffs_proc_debug_write(file, buf, count); + return yaffs_proc_write_trace_options(file, buf, count); +} + +/* Stuff to handle installation of file systems */ +struct file_system_to_install { + struct file_system_type *fst; + int installed; +}; + +static struct file_system_to_install fs_to_install[] = { + {&yaffs_fs_type, 0}, + {&yaffs2_fs_type, 0}, + {NULL, 0} +}; + + +#ifdef YAFFS_NEW_PROCFS +static int yaffs_proc_show(struct seq_file *m, void *v) +{ + /* FIXME: Unify in a better way? */ + char buffer[512]; + char *start; + int len; + + len = yaffs_proc_read(buffer, &start, 0, sizeof(buffer), NULL, NULL); + seq_puts(m, buffer); + return 0; +} + +static int yaffs_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, yaffs_proc_show, NULL); +} + +static struct file_operations procfs_ops = { + .owner = THIS_MODULE, + .open = yaffs_proc_open, + .read = seq_read, + .write = yaffs_proc_write, +}; + +static int yaffs_procfs_init(void) +{ + /* Install the proc_fs entries */ + my_proc_entry = proc_create("yaffs", + S_IRUGO | S_IFREG, + YPROC_ROOT, + &procfs_ops); + + if (my_proc_entry) { + return 0; + } else { + return -ENOMEM; + } +} + +#else + + +static int yaffs_procfs_init(void) +{ + /* Install the proc_fs entries */ + my_proc_entry = create_proc_entry("yaffs", + S_IRUGO | S_IFREG, YPROC_ROOT); + + if (my_proc_entry) { + my_proc_entry->write_proc = yaffs_proc_write; + my_proc_entry->read_proc = yaffs_proc_read; + my_proc_entry->data = NULL; + return 0; + } else { + return -ENOMEM; + } +} + +#endif + + +static int __init init_yaffs_fs(void) +{ + int error = 0; + struct file_system_to_install *fsinst; + + yaffs_trace(YAFFS_TRACE_ALWAYS, + "yaffs Installing."); + + mutex_init(&yaffs_context_lock); + + error = yaffs_procfs_init(); + if (error) + return error; + + /* Now add the file system entries */ + + fsinst = fs_to_install; + + while (fsinst->fst && !error) { + error = register_filesystem(fsinst->fst); + if (!error) + fsinst->installed = 1; + fsinst++; + } + + /* Any errors? uninstall */ + if (error) { + fsinst = fs_to_install; + + while (fsinst->fst) { + if (fsinst->installed) { + unregister_filesystem(fsinst->fst); + fsinst->installed = 0; + } + fsinst++; + } + } + + return error; +} + +static void __exit exit_yaffs_fs(void) +{ + + struct file_system_to_install *fsinst; + + yaffs_trace(YAFFS_TRACE_ALWAYS, + "yaffs removing."); + + remove_proc_entry("yaffs", YPROC_ROOT); + + fsinst = fs_to_install; + + while (fsinst->fst) { + if (fsinst->installed) { + unregister_filesystem(fsinst->fst); + fsinst->installed = 0; + } + fsinst++; + } +} + +module_init(init_yaffs_fs) + module_exit(exit_yaffs_fs) + + MODULE_DESCRIPTION("YAFFS2 - a NAND specific flash file system"); +MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002-2011"); +MODULE_LICENSE("GPL"); diff --git a/fs/yaffs2/yaffs_vfs_single.c b/fs/yaffs2/yaffs_vfs_single.c new file mode 100644 index 00000000..0f44eeeb --- /dev/null +++ b/fs/yaffs2/yaffs_vfs_single.c @@ -0,0 +1,2606 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * Acknowledgements: + * Luc van OostenRyck for numerous patches. + * Nick Bane for numerous patches. + * Nick Bane for 2.5/2.6 integration. + * Andras Toth for mknod rdev issue. + * Michael Fischer for finding the problem with inode inconsistency. + * Some code bodily lifted from JFFS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * + * This is the file system front-end to YAFFS that hooks it up to + * the VFS. + * + * Special notes: + * >> 2.4: sb->u.generic_sbp points to the struct yaffs_dev associated with + * this superblock + * >> 2.6: sb->s_fs_info points to the struct yaffs_dev associated with this + * superblock + * >> inode->u.generic_ip points to the associated struct yaffs_obj. + */ + +/* + * NB There are two variants of Linux VFS glue code. This variant supports + * a single version and should not include any multi-version code. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "yportenv.h" +#include "yaffs_trace.h" +#include "yaffs_guts.h" +#include "yaffs_attribs.h" +#include "yaffs_linux.h" +#include "yaffs_mtdif.h" +//#include "yaffs_mtdif1.h" +//#include "yaffs_mtdif2.h" + +unsigned int yaffs_trace_mask = YAFFS_TRACE_BAD_BLOCKS | YAFFS_TRACE_ALWAYS; +unsigned int yaffs_wr_attempts = YAFFS_WR_ATTEMPTS; +unsigned int yaffs_auto_checkpoint = 1; +unsigned int yaffs_gc_control = 1; +unsigned int yaffs_bg_enable = 1; +unsigned int yaffs_auto_select = 1; + +/* Module Parameters */ +module_param(yaffs_trace_mask, uint, 0644); +module_param(yaffs_wr_attempts, uint, 0644); +module_param(yaffs_auto_checkpoint, uint, 0644); +module_param(yaffs_gc_control, uint, 0644); +module_param(yaffs_bg_enable, uint, 0644); +module_param(yaffs_auto_select, uint, 0644); + +#define yaffs_devname(sb, buf) bdevname(sb->s_bdev, buf) + +static uint32_t YCALCBLOCKS(uint64_t partition_size, uint32_t block_size) +{ + uint64_t result = partition_size; + + do_div(result, block_size); + return (uint32_t) result; +} + +#define yaffs_inode_to_obj_lv(iptr) ((iptr)->i_private) +#define yaffs_inode_to_obj(iptr)\ + ((struct yaffs_obj *)(yaffs_inode_to_obj_lv(iptr))) +#define yaffs_dentry_to_obj(dptr) yaffs_inode_to_obj((dptr)->d_inode) +#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->s_fs_info) + +#define update_dir_time(dir) do {\ + (dir)->i_ctime = (dir)->i_mtime = CURRENT_TIME; \ + } while (0) + + +static unsigned yaffs_gc_control_callback(struct yaffs_dev *dev) +{ + return yaffs_gc_control; +} + +static void yaffs_gross_lock(struct yaffs_dev *dev) +{ + yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locking %p", current); + mutex_lock(&(yaffs_dev_to_lc(dev)->gross_lock)); + yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locked %p", current); +} + +static void yaffs_gross_unlock(struct yaffs_dev *dev) +{ + yaffs_trace(YAFFS_TRACE_LOCK, "yaffs unlocking %p", current); + mutex_unlock(&(yaffs_dev_to_lc(dev)->gross_lock)); +} + +static void yaffs_fill_inode_from_obj(struct inode *inode, + struct yaffs_obj *obj); + +static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino) +{ + struct inode *inode; + struct yaffs_obj *obj; + struct yaffs_dev *dev = yaffs_super_to_dev(sb); + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_iget for %lu", ino); + + inode = iget_locked(sb, ino); + if (!inode) + return ERR_PTR(-ENOMEM); + if (!(inode->i_state & I_NEW)) + return inode; + + /* NB This is called as a side effect of other functions, but + * we had to release the lock to prevent deadlocks, so + * need to lock again. + */ + + yaffs_gross_lock(dev); + + obj = yaffs_find_by_number(dev, inode->i_ino); + yaffs_fill_inode_from_obj(inode, obj); + + yaffs_gross_unlock(dev); + + unlock_new_inode(inode); + return inode; +} + +struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev, + struct yaffs_obj *obj) +{ + struct inode *inode; + + if (!sb) { + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_get_inode for NULL super_block!!"); + return NULL; + + } + + if (!obj) { + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_get_inode for NULL object!!"); + return NULL; + + } + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_get_inode for object %d", + obj->obj_id); + + inode = yaffs_iget(sb, obj->obj_id); + if (IS_ERR(inode)) + return NULL; + + /* NB Side effect: iget calls back to yaffs_read_inode(). */ + /* iget also increments the inode's i_count */ + /* NB You can't be holding gross_lock or deadlock will happen! */ + + return inode; +} + +static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, + dev_t rdev) +{ + struct inode *inode; + struct yaffs_obj *obj = NULL; + struct yaffs_dev *dev; + struct yaffs_obj *parent = yaffs_inode_to_obj(dir); + int error; + uid_t uid = current->cred->fsuid; + gid_t gid = + (dir->i_mode & S_ISGID) ? dir->i_gid : current->cred->fsgid; + + if ((dir->i_mode & S_ISGID) && S_ISDIR(mode)) + mode |= S_ISGID; + + if (!parent) { + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_mknod: could not get parent object"); + return -EPERM; + } + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_mknod: parent object %d type %d", + parent->obj_id, parent->variant_type); + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_mknod: making oject for %s, mode %x dev %x", + dentry->d_name.name, mode, rdev); + + dev = parent->my_dev; + + yaffs_gross_lock(dev); + + if (yaffs_get_n_free_chunks(dev) < 1) { + error = -ENOSPC; + goto err_out; + } + + switch (mode & S_IFMT) { + default: + /* Special (socket, fifo, device...) */ + yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making special"); + obj = + yaffs_create_special(parent, dentry->d_name.name, mode, uid, + gid, old_encode_dev(rdev)); + break; + case S_IFREG: /* file */ + yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making file"); + obj = yaffs_create_file(parent, dentry->d_name.name, mode, uid, + gid); + break; + case S_IFDIR: /* directory */ + yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making directory"); + obj = yaffs_create_dir(parent, dentry->d_name.name, mode, + uid, gid); + break; + case S_IFLNK: /* symlink */ + yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making symlink"); + obj = NULL; /* Do we ever get here? */ + break; + } + + if (!obj) { + error = -ENOMEM; + goto err_out; + } + + /* Can not call yaffs_get_inode() with gross lock held */ + yaffs_gross_unlock(dev); + + + inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj); + d_instantiate(dentry, inode); + update_dir_time(dir); + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_mknod created object %d count = %d", + obj->obj_id, atomic_read(&inode->i_count)); + yaffs_fill_inode_from_obj(dir, parent); + return 0; + +err_out: + yaffs_gross_unlock(dev); + yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod error %d", error); + return error; + +} + +static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + return yaffs_mknod(dir, dentry, mode | S_IFDIR, 0); +} + +static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *n) +{ + return yaffs_mknod(dir, dentry, mode | S_IFREG, 0); +} + +static int yaffs_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *dentry) +{ + struct inode *inode = old_dentry->d_inode; + struct yaffs_obj *obj = NULL; + struct yaffs_obj *link = NULL; + struct yaffs_dev *dev; + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_link"); + + obj = yaffs_inode_to_obj(inode); + dev = obj->my_dev; + + yaffs_gross_lock(dev); + + if (!S_ISDIR(inode->i_mode)) /* Don't link directories */ + link = + yaffs_link_obj(yaffs_inode_to_obj(dir), dentry->d_name.name, + obj); + + if (link) { + set_nlink(old_dentry->d_inode, yaffs_get_obj_link_count(obj)); + d_instantiate(dentry, old_dentry->d_inode); + atomic_inc(&old_dentry->d_inode->i_count); + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_link link count %d i_count %d", + old_dentry->d_inode->i_nlink, + atomic_read(&old_dentry->d_inode->i_count)); + } + + yaffs_gross_unlock(dev); + + if (link) { + update_dir_time(dir); + return 0; + } + + return -EPERM; +} + +static int yaffs_symlink(struct inode *dir, struct dentry *dentry, + const char *symname) +{ + struct yaffs_obj *obj; + struct yaffs_dev *dev; + struct inode *inode; + uid_t uid = current->cred->fsuid; + gid_t gid = + (dir->i_mode & S_ISGID) ? dir->i_gid : current->cred->fsgid; + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_symlink"); + + if (strnlen(dentry->d_name.name, YAFFS_MAX_NAME_LENGTH + 1) > + YAFFS_MAX_NAME_LENGTH) + return -ENAMETOOLONG; + + if (strnlen(symname, YAFFS_MAX_ALIAS_LENGTH + 1) > + YAFFS_MAX_ALIAS_LENGTH) + return -ENAMETOOLONG; + + dev = yaffs_inode_to_obj(dir)->my_dev; + yaffs_gross_lock(dev); + obj = yaffs_create_symlink(yaffs_inode_to_obj(dir), dentry->d_name.name, + S_IFLNK | S_IRWXUGO, uid, gid, symname); + yaffs_gross_unlock(dev); + + if (!obj) { + yaffs_trace(YAFFS_TRACE_OS, "symlink not created"); + return -ENOMEM; + } + + inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); + d_instantiate(dentry, inode); + update_dir_time(dir); + yaffs_trace(YAFFS_TRACE_OS, "symlink created OK"); + + return 0; +} + +static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *n) +{ + struct yaffs_obj *obj; + struct inode *inode = NULL; + struct yaffs_dev *dev = yaffs_inode_to_obj(dir)->my_dev; + + if (current != yaffs_dev_to_lc(dev)->readdir_process) + yaffs_gross_lock(dev); + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_lookup for %d:%s", + yaffs_inode_to_obj(dir)->obj_id, dentry->d_name.name); + + obj = yaffs_find_by_name(yaffs_inode_to_obj(dir), dentry->d_name.name); + + obj = yaffs_get_equivalent_obj(obj); /* in case it was a hardlink */ + + /* Can't hold gross lock when calling yaffs_get_inode() */ + if (current != yaffs_dev_to_lc(dev)->readdir_process) + yaffs_gross_unlock(dev); + + if (obj) { + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_lookup found %d", obj->obj_id); + + inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); + + if (inode) { + yaffs_trace(YAFFS_TRACE_OS, "yaffs_loookup dentry"); + d_add(dentry, inode); + /* return dentry; */ + return NULL; + } + + } else { + yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup not found"); + + } + + d_add(dentry, inode); + + return NULL; +} + +static int yaffs_unlink(struct inode *dir, struct dentry *dentry) +{ + int ret_val; + struct yaffs_dev *dev; + struct yaffs_obj *obj; + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_unlink %d:%s", + (int)(dir->i_ino), dentry->d_name.name); + obj = yaffs_inode_to_obj(dir); + dev = obj->my_dev; + + yaffs_gross_lock(dev); + + ret_val = yaffs_unlinker(obj, dentry->d_name.name); + + if (ret_val == YAFFS_OK) { + inode_dec_link_count(dentry->d_inode); + dir->i_version++; + yaffs_gross_unlock(dev); + update_dir_time(dir); + return 0; + } + yaffs_gross_unlock(dev); + return -ENOTEMPTY; +} + +static int yaffs_sync_object(struct file *file, + loff_t start, loff_t end, int datasync) +{ + + struct yaffs_obj *obj; + struct yaffs_dev *dev; + struct dentry *dentry = file->f_path.dentry; + + obj = yaffs_dentry_to_obj(dentry); + + dev = obj->my_dev; + + yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, "yaffs_sync_object"); + yaffs_gross_lock(dev); + yaffs_flush_file(obj, 1, datasync, 0); + yaffs_gross_unlock(dev); + return 0; +} +/* + * The VFS layer already does all the dentry stuff for rename. + * + * NB: POSIX says you can rename an object over an old object of the same name + */ +static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + struct yaffs_dev *dev; + int ret_val = YAFFS_FAIL; + struct yaffs_obj *target; + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_rename"); + dev = yaffs_inode_to_obj(old_dir)->my_dev; + + yaffs_gross_lock(dev); + + /* Check if the target is an existing directory that is not empty. */ + target = yaffs_find_by_name(yaffs_inode_to_obj(new_dir), + new_dentry->d_name.name); + + if (target && target->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY && + !list_empty(&target->variant.dir_variant.children)) { + + yaffs_trace(YAFFS_TRACE_OS, "target is non-empty dir"); + + ret_val = YAFFS_FAIL; + } else { + /* Now does unlinking internally using shadowing mechanism */ + yaffs_trace(YAFFS_TRACE_OS, "calling yaffs_rename_obj"); + + ret_val = yaffs_rename_obj(yaffs_inode_to_obj(old_dir), + old_dentry->d_name.name, + yaffs_inode_to_obj(new_dir), + new_dentry->d_name.name); + } + yaffs_gross_unlock(dev); + + if (ret_val == YAFFS_OK) { + if (target) + inode_dec_link_count(new_dentry->d_inode); + + update_dir_time(old_dir); + if (old_dir != new_dir) + update_dir_time(new_dir); + return 0; + } else { + return -ENOTEMPTY; + } +} + +static int yaffs_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + int error = 0; + struct yaffs_dev *dev; + int result; + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_setattr of object %d", + yaffs_inode_to_obj(inode)->obj_id); + + /* Fail if a requested resize >= 2GB */ + if (attr->ia_valid & ATTR_SIZE && (attr->ia_size >> 31)) + error = -EINVAL; + + if (!error) + error = inode_change_ok(inode, attr); + + if (!error) { + setattr_copy(inode, attr); + yaffs_trace(YAFFS_TRACE_OS, "inode_setattr called"); + if (attr->ia_valid & ATTR_SIZE) { + truncate_setsize(inode, attr->ia_size); + inode->i_blocks = (inode->i_size + 511) >> 9; + } + dev = yaffs_inode_to_obj(inode)->my_dev; + if (attr->ia_valid & ATTR_SIZE) { + yaffs_trace(YAFFS_TRACE_OS, "resize to %d(%x)", + (int)(attr->ia_size), + (int)(attr->ia_size)); + } + + yaffs_gross_lock(dev); + result = yaffs_set_attribs(yaffs_inode_to_obj(inode), attr); + if (result != YAFFS_OK) + error = -EPERM; + yaffs_gross_unlock(dev); + } + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_setattr done returning %d", error); + + return error; +} + +static int yaffs_setxattr(struct dentry *dentry, const char *name, + const void *value, size_t size, int flags) +{ + struct inode *inode = dentry->d_inode; + int error; + struct yaffs_dev *dev; + struct yaffs_obj *obj = yaffs_inode_to_obj(inode); + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr of object %d", obj->obj_id); + + dev = obj->my_dev; + yaffs_gross_lock(dev); + error = yaffs_set_xattrib(obj, name, value, size, flags); + yaffs_gross_unlock(dev); + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr done returning %d", error); + + return error; +} + +static ssize_t yaffs_getxattr(struct dentry *dentry, const char *name, + void *buff, size_t size) +{ + struct inode *inode = dentry->d_inode; + int error; + struct yaffs_dev *dev; + struct yaffs_obj *obj = yaffs_inode_to_obj(inode); + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_getxattr \"%s\" from object %d", + name, obj->obj_id); + + dev = obj->my_dev; + yaffs_gross_lock(dev); + error = yaffs_get_xattrib(obj, name, buff, size); + yaffs_gross_unlock(dev); + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_getxattr done returning %d", error); + + return error; +} + +static int yaffs_removexattr(struct dentry *dentry, const char *name) +{ + struct inode *inode = dentry->d_inode; + int error; + struct yaffs_dev *dev; + struct yaffs_obj *obj = yaffs_inode_to_obj(inode); + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_removexattr of object %d", obj->obj_id); + + dev = obj->my_dev; + yaffs_gross_lock(dev); + error = yaffs_remove_xattrib(obj, name); + yaffs_gross_unlock(dev); + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_removexattr done returning %d", error); + + return error; +} + +static ssize_t yaffs_listxattr(struct dentry *dentry, char *buff, size_t size) +{ + struct inode *inode = dentry->d_inode; + int error; + struct yaffs_dev *dev; + struct yaffs_obj *obj = yaffs_inode_to_obj(inode); + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_listxattr of object %d", obj->obj_id); + + dev = obj->my_dev; + yaffs_gross_lock(dev); + error = yaffs_list_xattrib(obj, buff, size); + yaffs_gross_unlock(dev); + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_listxattr done returning %d", error); + + return error; +} + +static const struct inode_operations yaffs_dir_inode_operations = { + .create = yaffs_create, + .lookup = yaffs_lookup, + .link = yaffs_link, + .unlink = yaffs_unlink, + .symlink = yaffs_symlink, + .mkdir = yaffs_mkdir, + .rmdir = yaffs_unlink, + .mknod = yaffs_mknod, + .rename = yaffs_rename, + .setattr = yaffs_setattr, + .setxattr = yaffs_setxattr, + .getxattr = yaffs_getxattr, + .listxattr = yaffs_listxattr, + .removexattr = yaffs_removexattr, +}; +/*-----------------------------------------------------------------*/ +/* Directory search context allows us to unlock access to yaffs during + * filldir without causing problems with the directory being modified. + * This is similar to the tried and tested mechanism used in yaffs direct. + * + * A search context iterates along a doubly linked list of siblings in the + * directory. If the iterating object is deleted then this would corrupt + * the list iteration, likely causing a crash. The search context avoids + * this by using the remove_obj_fn to move the search context to the + * next object before the object is deleted. + * + * Many readdirs (and thus seach conexts) may be alive simulateously so + * each struct yaffs_dev has a list of these. + * + * A seach context lives for the duration of a readdir. + * + * All these functions must be called while yaffs is locked. + */ + +struct yaffs_search_context { + struct yaffs_dev *dev; + struct yaffs_obj *dir_obj; + struct yaffs_obj *next_return; + struct list_head others; +}; + +/* + * yaffs_new_search() creates a new search context, initialises it and + * adds it to the device's search context list. + * + * Called at start of readdir. + */ +static struct yaffs_search_context *yaffs_new_search(struct yaffs_obj *dir) +{ + struct yaffs_dev *dev = dir->my_dev; + struct yaffs_search_context *sc = + kmalloc(sizeof(struct yaffs_search_context), GFP_NOFS); + + if (!sc) + return NULL; + + sc->dir_obj = dir; + sc->dev = dev; + if (list_empty(&sc->dir_obj->variant.dir_variant.children)) + sc->next_return = NULL; + else + sc->next_return = + list_entry(dir->variant.dir_variant.children.next, + struct yaffs_obj, siblings); + INIT_LIST_HEAD(&sc->others); + list_add(&sc->others, &(yaffs_dev_to_lc(dev)->search_contexts)); + + return sc; +} + +/* + * yaffs_search_end() disposes of a search context and cleans up. + */ +static void yaffs_search_end(struct yaffs_search_context *sc) +{ + if (sc) { + list_del(&sc->others); + kfree(sc); + } +} + +/* + * yaffs_search_advance() moves a search context to the next object. + * Called when the search iterates or when an object removal causes + * the search context to be moved to the next object. + */ +static void yaffs_search_advance(struct yaffs_search_context *sc) +{ + if (!sc) + return; + + if (sc->next_return == NULL || + list_empty(&sc->dir_obj->variant.dir_variant.children)) + sc->next_return = NULL; + else { + struct list_head *next = sc->next_return->siblings.next; + + if (next == &sc->dir_obj->variant.dir_variant.children) + sc->next_return = NULL; /* end of list */ + else + sc->next_return = + list_entry(next, struct yaffs_obj, siblings); + } +} + +/* + * yaffs_remove_obj_callback() is called when an object is unlinked. + * We check open search contexts and advance any which are currently + * on the object being iterated. + */ +static void yaffs_remove_obj_callback(struct yaffs_obj *obj) +{ + + struct list_head *i; + struct yaffs_search_context *sc; + struct list_head *search_contexts = + &(yaffs_dev_to_lc(obj->my_dev)->search_contexts); + + /* Iterate through the directory search contexts. + * If any are currently on the object being removed, then advance + * the search context to the next object to prevent a hanging pointer. + */ + list_for_each(i, search_contexts) { + sc = list_entry(i, struct yaffs_search_context, others); + if (sc->next_return == obj) + yaffs_search_advance(sc); + } + +} + +static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) +{ + struct yaffs_obj *obj; + struct yaffs_dev *dev; + struct yaffs_search_context *sc; + struct inode *inode = f->f_dentry->d_inode; + unsigned long offset, curoffs; + struct yaffs_obj *l; + int ret_val = 0; + char name[YAFFS_MAX_NAME_LENGTH + 1]; + + obj = yaffs_dentry_to_obj(f->f_dentry); + dev = obj->my_dev; + + yaffs_gross_lock(dev); + + yaffs_dev_to_lc(dev)->readdir_process = current; + + offset = f->f_pos; + + sc = yaffs_new_search(obj); + if (!sc) { + ret_val = -ENOMEM; + goto out; + } + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_readdir: starting at %d", (int)offset); + + if (offset == 0) { + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_readdir: entry . ino %d", + (int)inode->i_ino); + yaffs_gross_unlock(dev); + if (filldir(dirent, ".", 1, offset, inode->i_ino, DT_DIR) < 0) { + yaffs_gross_lock(dev); + goto out; + } + yaffs_gross_lock(dev); + offset++; + f->f_pos++; + } + if (offset == 1) { + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_readdir: entry .. ino %d", + (int)f->f_dentry->d_parent->d_inode->i_ino); + yaffs_gross_unlock(dev); + if (filldir(dirent, "..", 2, offset, + f->f_dentry->d_parent->d_inode->i_ino, + DT_DIR) < 0) { + yaffs_gross_lock(dev); + goto out; + } + yaffs_gross_lock(dev); + offset++; + f->f_pos++; + } + + curoffs = 1; + + /* If the directory has changed since the open or last call to + readdir, rewind to after the 2 canned entries. */ + if (f->f_version != inode->i_version) { + offset = 2; + f->f_pos = offset; + f->f_version = inode->i_version; + } + + while (sc->next_return) { + curoffs++; + l = sc->next_return; + if (curoffs >= offset) { + int this_inode = yaffs_get_obj_inode(l); + int this_type = yaffs_get_obj_type(l); + + yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_readdir: %s inode %d", + name, yaffs_get_obj_inode(l)); + + yaffs_gross_unlock(dev); + + if (filldir(dirent, + name, + strlen(name), + offset, this_inode, this_type) < 0) { + yaffs_gross_lock(dev); + goto out; + } + + yaffs_gross_lock(dev); + + offset++; + f->f_pos++; + } + yaffs_search_advance(sc); + } + +out: + yaffs_search_end(sc); + yaffs_dev_to_lc(dev)->readdir_process = NULL; + yaffs_gross_unlock(dev); + + return ret_val; +} + +static const struct file_operations yaffs_dir_operations = { + .read = generic_read_dir, + .readdir = yaffs_readdir, + .fsync = yaffs_sync_object, + .llseek = generic_file_llseek, +}; + + + +static int yaffs_file_flush(struct file *file, fl_owner_t id) +{ + struct yaffs_obj *obj = yaffs_dentry_to_obj(file->f_dentry); + struct yaffs_dev *dev = obj->my_dev; + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_file_flush object %d (%s)", + obj->obj_id, obj->dirty ? "dirty" : "clean"); + + yaffs_gross_lock(dev); + + yaffs_flush_file(obj, 1, 0, 0); + + yaffs_gross_unlock(dev); + + return 0; +} + +static const struct file_operations yaffs_file_operations = { + .read = do_sync_read, + .write = do_sync_write, + .aio_read = generic_file_aio_read, + .aio_write = generic_file_aio_write, + .mmap = generic_file_mmap, + .flush = yaffs_file_flush, + .fsync = yaffs_sync_object, + .splice_read = generic_file_splice_read, + .splice_write = generic_file_splice_write, + .llseek = generic_file_llseek, +}; + + +/* ExportFS support */ +static struct inode *yaffs2_nfs_get_inode(struct super_block *sb, uint64_t ino, + uint32_t generation) +{ + return yaffs_iget(sb, ino); +} + +static struct dentry *yaffs2_fh_to_dentry(struct super_block *sb, + struct fid *fid, int fh_len, + int fh_type) +{ + return generic_fh_to_dentry(sb, fid, fh_len, fh_type, + yaffs2_nfs_get_inode); +} + +static struct dentry *yaffs2_fh_to_parent(struct super_block *sb, + struct fid *fid, int fh_len, + int fh_type) +{ + return generic_fh_to_parent(sb, fid, fh_len, fh_type, + yaffs2_nfs_get_inode); +} + +struct dentry *yaffs2_get_parent(struct dentry *dentry) +{ + + struct super_block *sb = dentry->d_inode->i_sb; + struct dentry *parent = ERR_PTR(-ENOENT); + struct inode *inode; + unsigned long parent_ino; + struct yaffs_obj *d_obj; + struct yaffs_obj *parent_obj; + + d_obj = yaffs_inode_to_obj(dentry->d_inode); + + if (d_obj) { + parent_obj = d_obj->parent; + if (parent_obj) { + parent_ino = yaffs_get_obj_inode(parent_obj); + inode = yaffs_iget(sb, parent_ino); + + if (IS_ERR(inode)) { + parent = ERR_CAST(inode); + } else { + parent = d_obtain_alias(inode); + if (!IS_ERR(parent)) { + parent = ERR_PTR(-ENOMEM); + iput(inode); + } + } + } + } + return parent; +} + +/* Just declare a zero structure as a NULL value implies + * using the default functions of exportfs. + */ + +static struct export_operations yaffs_export_ops = { + .fh_to_dentry = yaffs2_fh_to_dentry, + .fh_to_parent = yaffs2_fh_to_parent, + .get_parent = yaffs2_get_parent, +}; + + +/*-----------------------------------------------------------------*/ + +static int yaffs_readlink(struct dentry *dentry, char __user * buffer, + int buflen) +{ + unsigned char *alias; + int ret; + struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; + + yaffs_gross_lock(dev); + + alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); + + yaffs_gross_unlock(dev); + + if (!alias) + return -ENOMEM; + + ret = vfs_readlink(dentry, buffer, buflen, alias); + kfree(alias); + return ret; +} + +static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + unsigned char *alias; + void *ret; + struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; + + yaffs_gross_lock(dev); + + alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); + yaffs_gross_unlock(dev); + + if (!alias) { + ret = ERR_PTR(-ENOMEM); + goto out; + } + + nd_set_link(nd, alias); + ret = (void *)alias; +out: + return ret; +} + +void yaffs_put_link(struct dentry *dentry, struct nameidata *nd, void *alias) +{ + kfree(alias); +} + + +static void yaffs_unstitch_obj(struct inode *inode, struct yaffs_obj *obj) +{ + /* Clear the association between the inode and + * the struct yaffs_obj. + */ + obj->my_inode = NULL; + yaffs_inode_to_obj_lv(inode) = NULL; + + /* If the object freeing was deferred, then the real + * free happens now. + * This should fix the inode inconsistency problem. + */ + yaffs_handle_defered_free(obj); +} + +/* yaffs_evict_inode combines into one operation what was previously done in + * yaffs_clear_inode() and yaffs_delete_inode() + * + */ +static void yaffs_evict_inode(struct inode *inode) +{ + struct yaffs_obj *obj; + struct yaffs_dev *dev; + int deleteme = 0; + + obj = yaffs_inode_to_obj(inode); + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_evict_inode: ino %d, count %d %s", + (int)inode->i_ino, + atomic_read(&inode->i_count), + obj ? "object exists" : "null object"); + + if (!inode->i_nlink && !is_bad_inode(inode)) + deleteme = 1; + truncate_inode_pages(&inode->i_data, 0); + end_writeback(inode); + + if (deleteme && obj) { + dev = obj->my_dev; + yaffs_gross_lock(dev); + yaffs_del_obj(obj); + yaffs_gross_unlock(dev); + } + if (obj) { + dev = obj->my_dev; + yaffs_gross_lock(dev); + yaffs_unstitch_obj(inode, obj); + yaffs_gross_unlock(dev); + } + +} + +static void yaffs_touch_super(struct yaffs_dev *dev) +{ + struct super_block *sb = yaffs_dev_to_lc(dev)->super; + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_touch_super() sb = %p", sb); + if (sb) + sb->s_dirt = 1; +} + +static int yaffs_readpage_nolock(struct file *f, struct page *pg) +{ + /* Lifted from jffs2 */ + + struct yaffs_obj *obj; + unsigned char *pg_buf; + int ret; + struct yaffs_dev *dev; + loff_t pos = ((loff_t) pg->index) << PAGE_CACHE_SHIFT; + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_readpage_nolock at %lld, size %08x", + (long long)pos, + (unsigned)PAGE_CACHE_SIZE); + + + obj = yaffs_dentry_to_obj(f->f_dentry); + + dev = obj->my_dev; + + BUG_ON(!PageLocked(pg)); + + pg_buf = kmap(pg); + /* FIXME: Can kmap fail? */ + + yaffs_gross_lock(dev); + + ret = yaffs_file_rd(obj, pg_buf, pos, PAGE_CACHE_SIZE); + + yaffs_gross_unlock(dev); + + if (ret >= 0) + ret = 0; + + if (ret) { + ClearPageUptodate(pg); + SetPageError(pg); + } else { + SetPageUptodate(pg); + ClearPageError(pg); + } + + flush_dcache_page(pg); + kunmap(pg); + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage_nolock done"); + return ret; +} + +static int yaffs_readpage_unlock(struct file *f, struct page *pg) +{ + int ret = yaffs_readpage_nolock(f, pg); + + unlock_page(pg); + return ret; +} + +static int yaffs_readpage(struct file *f, struct page *pg) +{ + int ret; + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage"); + ret = yaffs_readpage_unlock(f, pg); + yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage done"); + return ret; +} + +/* writepage inspired by/stolen from smbfs */ + +static int yaffs_writepage(struct page *page, struct writeback_control *wbc) +{ + struct yaffs_dev *dev; + struct address_space *mapping = page->mapping; + struct inode *inode; + unsigned long end_index; + char *buffer; + struct yaffs_obj *obj; + int n_written = 0; + unsigned n_bytes; + loff_t i_size; + + if (!mapping) + BUG(); + inode = mapping->host; + if (!inode) + BUG(); + i_size = i_size_read(inode); + + end_index = i_size >> PAGE_CACHE_SHIFT; + + if (page->index < end_index) + n_bytes = PAGE_CACHE_SIZE; + else { + n_bytes = i_size & (PAGE_CACHE_SIZE - 1); + + if (page->index > end_index || !n_bytes) { + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_writepage at %08x, inode size = %08x!!!", + (unsigned)(page->index << PAGE_CACHE_SHIFT), + (unsigned)inode->i_size); + yaffs_trace(YAFFS_TRACE_OS, + " -> don't care!!"); + + zero_user_segment(page, 0, PAGE_CACHE_SIZE); + set_page_writeback(page); + unlock_page(page); + end_page_writeback(page); + return 0; + } + } + + if (n_bytes != PAGE_CACHE_SIZE) + zero_user_segment(page, n_bytes, PAGE_CACHE_SIZE); + + get_page(page); + + buffer = kmap(page); + + obj = yaffs_inode_to_obj(inode); + dev = obj->my_dev; + yaffs_gross_lock(dev); + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_writepage at %08x, size %08x", + (unsigned)(page->index << PAGE_CACHE_SHIFT), n_bytes); + yaffs_trace(YAFFS_TRACE_OS, + "writepag0: obj = %05x, ino = %05x", + (int)obj->variant.file_variant.file_size, (int)inode->i_size); + + n_written = yaffs_wr_file(obj, buffer, + ((loff_t)page->index) << PAGE_CACHE_SHIFT, + n_bytes, 0); + + yaffs_touch_super(dev); + + yaffs_trace(YAFFS_TRACE_OS, + "writepag1: obj = %05x, ino = %05x", + (int)obj->variant.file_variant.file_size, (int)inode->i_size); + + yaffs_gross_unlock(dev); + + kunmap(page); + set_page_writeback(page); + unlock_page(page); + end_page_writeback(page); + put_page(page); + + return (n_written == n_bytes) ? 0 : -ENOSPC; +} + +/* Space holding and freeing is done to ensure we have space available for + * write_begin/end. + * For now we just assume few parallel writes and check against a small + * number. + * Todo: need to do this with a counter to handle parallel reads better + */ + +static ssize_t yaffs_hold_space(struct file *f) +{ + struct yaffs_obj *obj; + struct yaffs_dev *dev; + int n_free_chunks; + + obj = yaffs_dentry_to_obj(f->f_dentry); + + dev = obj->my_dev; + + yaffs_gross_lock(dev); + + n_free_chunks = yaffs_get_n_free_chunks(dev); + + yaffs_gross_unlock(dev); + + return (n_free_chunks > 20) ? 1 : 0; +} + +static void yaffs_release_space(struct file *f) +{ + struct yaffs_obj *obj; + struct yaffs_dev *dev; + + obj = yaffs_dentry_to_obj(f->f_dentry); + + dev = obj->my_dev; + + yaffs_gross_lock(dev); + + yaffs_gross_unlock(dev); +} + +static int yaffs_write_begin(struct file *filp, struct address_space *mapping, + loff_t pos, unsigned len, unsigned flags, + struct page **pagep, void **fsdata) +{ + struct page *pg = NULL; + pgoff_t index = pos >> PAGE_CACHE_SHIFT; + int ret = 0; + int space_held = 0; + + /* Get a page */ + pg = grab_cache_page_write_begin(mapping, index, flags); + + *pagep = pg; + if (!pg) { + ret = -ENOMEM; + goto out; + } + yaffs_trace(YAFFS_TRACE_OS, + "start yaffs_write_begin index %d(%x) uptodate %d", + (int)index, (int)index, PageUptodate(pg) ? 1 : 0); + + /* Get fs space */ + space_held = yaffs_hold_space(filp); + + if (!space_held) { + ret = -ENOSPC; + goto out; + } + + /* Update page if required */ + + if (!PageUptodate(pg)) + ret = yaffs_readpage_nolock(filp, pg); + + if (ret) + goto out; + + /* Happy path return */ + yaffs_trace(YAFFS_TRACE_OS, "end yaffs_write_begin - ok"); + + return 0; + +out: + yaffs_trace(YAFFS_TRACE_OS, + "end yaffs_write_begin fail returning %d", ret); + if (space_held) + yaffs_release_space(filp); + if (pg) { + unlock_page(pg); + page_cache_release(pg); + } + return ret; +} + +static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, + loff_t *pos) +{ + struct yaffs_obj *obj; + int n_written, ipos; + struct inode *inode; + struct yaffs_dev *dev; + + obj = yaffs_dentry_to_obj(f->f_dentry); + + if (!obj) { + /* This should not happen */ + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_file_write: hey obj is null!"); + return -EINVAL; + } + + dev = obj->my_dev; + + yaffs_gross_lock(dev); + + inode = f->f_dentry->d_inode; + + if (!S_ISBLK(inode->i_mode) && f->f_flags & O_APPEND) + ipos = inode->i_size; + else + ipos = *pos; + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_file_write about to write writing %u(%x) bytes to object %d at %d(%x)", + (unsigned)n, (unsigned)n, obj->obj_id, ipos, ipos); + + n_written = yaffs_wr_file(obj, buf, ipos, n, 0); + + yaffs_touch_super(dev); + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_file_write: %d(%x) bytes written", + (unsigned)n, (unsigned)n); + + if (n_written > 0) { + ipos += n_written; + *pos = ipos; + if (ipos > inode->i_size) { + inode->i_size = ipos; + inode->i_blocks = (ipos + 511) >> 9; + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_file_write size updated to %d bytes, %d blocks", + ipos, (int)(inode->i_blocks)); + } + } + yaffs_gross_unlock(dev); + return (n_written == 0) && (n > 0) ? -ENOSPC : n_written; +} + +static int yaffs_write_end(struct file *filp, struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *pg, void *fsdadata) +{ + int ret = 0; + void *addr, *kva; + uint32_t offset_into_page = pos & (PAGE_CACHE_SIZE - 1); + + kva = kmap(pg); + addr = kva + offset_into_page; + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_write_end addr %p pos %x n_bytes %d", + addr, (unsigned)pos, copied); + + ret = yaffs_file_write(filp, addr, copied, &pos); + + if (ret != copied) { + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_write_end not same size ret %d copied %d", + ret, copied); + SetPageError(pg); + } + + kunmap(pg); + + yaffs_release_space(filp); + unlock_page(pg); + page_cache_release(pg); + return ret; +} + +static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; + struct super_block *sb = dentry->d_sb; + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_statfs"); + + yaffs_gross_lock(dev); + + buf->f_type = YAFFS_MAGIC; + buf->f_bsize = sb->s_blocksize; + buf->f_namelen = 255; + + if (dev->data_bytes_per_chunk & (dev->data_bytes_per_chunk - 1)) { + /* Do this if chunk size is not a power of 2 */ + + uint64_t bytes_in_dev; + uint64_t bytes_free; + + bytes_in_dev = + ((uint64_t) + ((dev->param.end_block - dev->param.start_block + 1))) * + ((uint64_t) (dev->param.chunks_per_block * + dev->data_bytes_per_chunk)); + + do_div(bytes_in_dev, sb->s_blocksize); + /* bytes_in_dev becomes the number of blocks */ + buf->f_blocks = bytes_in_dev; + + bytes_free = ((uint64_t) (yaffs_get_n_free_chunks(dev))) * + ((uint64_t) (dev->data_bytes_per_chunk)); + + do_div(bytes_free, sb->s_blocksize); + + buf->f_bfree = bytes_free; + + } else if (sb->s_blocksize > dev->data_bytes_per_chunk) { + + buf->f_blocks = + (dev->param.end_block - dev->param.start_block + 1) * + dev->param.chunks_per_block / + (sb->s_blocksize / dev->data_bytes_per_chunk); + buf->f_bfree = + yaffs_get_n_free_chunks(dev) / + (sb->s_blocksize / dev->data_bytes_per_chunk); + } else { + buf->f_blocks = + (dev->param.end_block - dev->param.start_block + 1) * + dev->param.chunks_per_block * + (dev->data_bytes_per_chunk / sb->s_blocksize); + + buf->f_bfree = + yaffs_get_n_free_chunks(dev) * + (dev->data_bytes_per_chunk / sb->s_blocksize); + } + + buf->f_files = 0; + buf->f_ffree = 0; + buf->f_bavail = buf->f_bfree; + + yaffs_gross_unlock(dev); + return 0; +} + +static void yaffs_flush_inodes(struct super_block *sb) +{ + struct inode *iptr; + struct yaffs_obj *obj; + + list_for_each_entry(iptr, &sb->s_inodes, i_sb_list) { + obj = yaffs_inode_to_obj(iptr); + if (obj) { + yaffs_trace(YAFFS_TRACE_OS, + "flushing obj %d", obj->obj_id); + yaffs_flush_file(obj, 1, 0, 0); + } + } +} + +static void yaffs_flush_super(struct super_block *sb, int do_checkpoint) +{ + struct yaffs_dev *dev = yaffs_super_to_dev(sb); + + if (!dev) + return; + + yaffs_flush_inodes(sb); + yaffs_update_dirty_dirs(dev); + yaffs_flush_whole_cache(dev, 1); + if (do_checkpoint) + yaffs_checkpoint_save(dev); +} + +static unsigned yaffs_bg_gc_urgency(struct yaffs_dev *dev) +{ + unsigned erased_chunks = + dev->n_erased_blocks * dev->param.chunks_per_block; + struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); + unsigned scattered = 0; /* Free chunks not in an erased block */ + + if (erased_chunks < dev->n_free_chunks) + scattered = (dev->n_free_chunks - erased_chunks); + + if (!context->bg_running) + return 0; + else if (scattered < (dev->param.chunks_per_block * 2)) + return 0; + else if (erased_chunks > dev->n_free_chunks / 2) + return 0; + else if (erased_chunks > dev->n_free_chunks / 4) + return 1; + else + return 2; +} + +static int yaffs_do_sync_fs(struct super_block *sb, int request_checkpoint) +{ + + struct yaffs_dev *dev = yaffs_super_to_dev(sb); + unsigned int oneshot_checkpoint = (yaffs_auto_checkpoint & 4); + unsigned gc_urgent = yaffs_bg_gc_urgency(dev); + int do_checkpoint; + + yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, + "yaffs_do_sync_fs: gc-urgency %d %s %s%s", + gc_urgent, + sb->s_dirt ? "dirty" : "clean", + request_checkpoint ? "checkpoint requested" : "no checkpoint", + oneshot_checkpoint ? " one-shot" : ""); + + yaffs_gross_lock(dev); + do_checkpoint = ((request_checkpoint && !gc_urgent) || + oneshot_checkpoint) && !dev->is_checkpointed; + + if (sb->s_dirt || do_checkpoint) { + yaffs_flush_super(sb, !dev->is_checkpointed && do_checkpoint); + sb->s_dirt = 0; + if (oneshot_checkpoint) + yaffs_auto_checkpoint &= ~4; + } + yaffs_gross_unlock(dev); + + return 0; +} + +/* + * yaffs background thread functions . + * yaffs_bg_thread_fn() the thread function + * yaffs_bg_start() launches the background thread. + * yaffs_bg_stop() cleans up the background thread. + * + * NB: + * The thread should only run after the yaffs is initialised + * The thread should be stopped before yaffs is unmounted. + * The thread should not do any writing while the fs is in read only. + */ + +void yaffs_background_waker(unsigned long data) +{ + wake_up_process((struct task_struct *)data); +} + +static int yaffs_bg_thread_fn(void *data) +{ + struct yaffs_dev *dev = (struct yaffs_dev *)data; + struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); + unsigned long now = jiffies; + unsigned long next_dir_update = now; + unsigned long next_gc = now; + unsigned long expires; + unsigned int urgency; + int gc_result; + struct timer_list timer; + + yaffs_trace(YAFFS_TRACE_BACKGROUND, + "yaffs_background starting for dev %p", (void *)dev); + + set_freezable(); + while (context->bg_running) { + yaffs_trace(YAFFS_TRACE_BACKGROUND, "yaffs_background"); + + if (kthread_should_stop()) + break; + + if (try_to_freeze()) + continue; + + yaffs_gross_lock(dev); + + now = jiffies; + + if (time_after(now, next_dir_update) && yaffs_bg_enable) { + yaffs_update_dirty_dirs(dev); + next_dir_update = now + HZ; + } + + if (time_after(now, next_gc) && yaffs_bg_enable) { + if (!dev->is_checkpointed) { + urgency = yaffs_bg_gc_urgency(dev); + gc_result = yaffs_bg_gc(dev, urgency); + if (urgency > 1) + next_gc = now + HZ / 20 + 1; + else if (urgency > 0) + next_gc = now + HZ / 10 + 1; + else + next_gc = now + HZ * 2; + } else { + /* + * gc not running so set to next_dir_update + * to cut down on wake ups + */ + next_gc = next_dir_update; + } + } + yaffs_gross_unlock(dev); + expires = next_dir_update; + if (time_before(next_gc, expires)) + expires = next_gc; + if (time_before(expires, now)) + expires = now + HZ; + + init_timer_on_stack(&timer); + timer.expires = expires + 1; + timer.data = (unsigned long)current; + timer.function = yaffs_background_waker; + + set_current_state(TASK_INTERRUPTIBLE); + add_timer(&timer); + schedule(); + del_timer_sync(&timer); + } + + return 0; +} + +static int yaffs_bg_start(struct yaffs_dev *dev) +{ + int retval = 0; + struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); + + if (dev->read_only) + return -1; + + context->bg_running = 1; + + context->bg_thread = kthread_run(yaffs_bg_thread_fn, + (void *)dev, "yaffs-bg-%d", + context->mount_id); + + if (IS_ERR(context->bg_thread)) { + retval = PTR_ERR(context->bg_thread); + context->bg_thread = NULL; + context->bg_running = 0; + } + return retval; +} + +static void yaffs_bg_stop(struct yaffs_dev *dev) +{ + struct yaffs_linux_context *ctxt = yaffs_dev_to_lc(dev); + + ctxt->bg_running = 0; + + if (ctxt->bg_thread) { + kthread_stop(ctxt->bg_thread); + ctxt->bg_thread = NULL; + } +} + +static void yaffs_write_super(struct super_block *sb) +{ + unsigned request_checkpoint = (yaffs_auto_checkpoint >= 2); + + yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, + "yaffs_write_super%s", + request_checkpoint ? " checkpt" : ""); + + yaffs_do_sync_fs(sb, request_checkpoint); + +} + +static int yaffs_sync_fs(struct super_block *sb, int wait) +{ + unsigned request_checkpoint = (yaffs_auto_checkpoint >= 1); + + yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, + "yaffs_sync_fs%s", request_checkpoint ? " checkpt" : ""); + + yaffs_do_sync_fs(sb, request_checkpoint); + + return 0; +} + +static LIST_HEAD(yaffs_context_list); +struct mutex yaffs_context_lock; + +struct yaffs_options { + int inband_tags; + int skip_checkpoint_read; + int skip_checkpoint_write; + int no_cache; + int tags_ecc_on; + int tags_ecc_overridden; + int lazy_loading_enabled; + int lazy_loading_overridden; + int empty_lost_and_found; + int empty_lost_and_found_overridden; +}; + +#define MAX_OPT_LEN 30 +static int yaffs_parse_options(struct yaffs_options *options, + const char *options_str) +{ + char cur_opt[MAX_OPT_LEN + 1]; + int p; + int error = 0; + + /* Parse through the options which is a comma seperated list */ + + while (options_str && *options_str && !error) { + memset(cur_opt, 0, MAX_OPT_LEN + 1); + p = 0; + + while (*options_str == ',') + options_str++; + + while (*options_str && *options_str != ',') { + if (p < MAX_OPT_LEN) { + cur_opt[p] = *options_str; + p++; + } + options_str++; + } + + if (!strcmp(cur_opt, "inband-tags")) { + options->inband_tags = 1; + } else if (!strcmp(cur_opt, "tags-ecc-off")) { + options->tags_ecc_on = 0; + options->tags_ecc_overridden = 1; + } else if (!strcmp(cur_opt, "tags-ecc-on")) { + options->tags_ecc_on = 1; + options->tags_ecc_overridden = 1; + } else if (!strcmp(cur_opt, "lazy-loading-off")) { + options->lazy_loading_enabled = 0; + options->lazy_loading_overridden = 1; + } else if (!strcmp(cur_opt, "lazy-loading-on")) { + options->lazy_loading_enabled = 1; + options->lazy_loading_overridden = 1; + } else if (!strcmp(cur_opt, "empty-lost-and-found-off")) { + options->empty_lost_and_found = 0; + options->empty_lost_and_found_overridden = 1; + } else if (!strcmp(cur_opt, "empty-lost-and-found-on")) { + options->empty_lost_and_found = 1; + options->empty_lost_and_found_overridden = 1; + } else if (!strcmp(cur_opt, "no-cache")) { + options->no_cache = 1; + } else if (!strcmp(cur_opt, "no-checkpoint-read")) { + options->skip_checkpoint_read = 1; + } else if (!strcmp(cur_opt, "no-checkpoint-write")) { + options->skip_checkpoint_write = 1; + } else if (!strcmp(cur_opt, "no-checkpoint")) { + options->skip_checkpoint_read = 1; + options->skip_checkpoint_write = 1; + } else { + printk(KERN_INFO "yaffs: Bad mount option \"%s\"\n", + cur_opt); + error = 1; + } + } + return error; +} + +static const struct address_space_operations yaffs_file_address_operations = { + .readpage = yaffs_readpage, + .writepage = yaffs_writepage, + .write_begin = yaffs_write_begin, + .write_end = yaffs_write_end, +}; + + + +static const struct inode_operations yaffs_file_inode_operations = { + .setattr = yaffs_setattr, + .setxattr = yaffs_setxattr, + .getxattr = yaffs_getxattr, + .listxattr = yaffs_listxattr, + .removexattr = yaffs_removexattr, +}; + +static const struct inode_operations yaffs_symlink_inode_operations = { + .readlink = yaffs_readlink, + .follow_link = yaffs_follow_link, + .put_link = yaffs_put_link, + .setattr = yaffs_setattr, + .setxattr = yaffs_setxattr, + .getxattr = yaffs_getxattr, + .listxattr = yaffs_listxattr, + .removexattr = yaffs_removexattr, +}; + +static void yaffs_fill_inode_from_obj(struct inode *inode, + struct yaffs_obj *obj) +{ + u32 mode; + + if (!inode || !obj) { + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_fill_inode invalid parameters"); + return; + } + + /* Check mode against the variant type + * and attempt to repair if broken. */ + mode = obj->yst_mode; + + switch (obj->variant_type) { + case YAFFS_OBJECT_TYPE_FILE: + if (!S_ISREG(mode)) { + obj->yst_mode &= ~S_IFMT; + obj->yst_mode |= S_IFREG; + } + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + if (!S_ISLNK(mode)) { + obj->yst_mode &= ~S_IFMT; + obj->yst_mode |= S_IFLNK; + } + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + if (!S_ISDIR(mode)) { + obj->yst_mode &= ~S_IFMT; + obj->yst_mode |= S_IFDIR; + } + break; + case YAFFS_OBJECT_TYPE_UNKNOWN: + case YAFFS_OBJECT_TYPE_HARDLINK: + case YAFFS_OBJECT_TYPE_SPECIAL: + default: + /* TODO? */ + break; + } + + inode->i_flags |= S_NOATIME; + inode->i_ino = obj->obj_id; + inode->i_mode = obj->yst_mode; + inode->i_uid = obj->yst_uid; + inode->i_gid = obj->yst_gid; + + inode->i_rdev = old_decode_dev(obj->yst_rdev); + + inode->i_atime.tv_sec = (time_t) (obj->yst_atime); + inode->i_atime.tv_nsec = 0; + inode->i_mtime.tv_sec = (time_t) obj->yst_mtime; + inode->i_mtime.tv_nsec = 0; + inode->i_ctime.tv_sec = (time_t) obj->yst_ctime; + inode->i_ctime.tv_nsec = 0; + inode->i_size = yaffs_get_obj_length(obj); + inode->i_blocks = (inode->i_size + 511) >> 9; + set_nlink(inode, yaffs_get_obj_link_count(obj)); + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_fill_inode mode %x uid %d gid %d size %d count %d", + inode->i_mode, inode->i_uid, inode->i_gid, + (int)inode->i_size, atomic_read(&inode->i_count)); + + switch (obj->yst_mode & S_IFMT) { + default: /* fifo, device or socket */ + init_special_inode(inode, obj->yst_mode, + old_decode_dev(obj->yst_rdev)); + break; + case S_IFREG: /* file */ + inode->i_op = &yaffs_file_inode_operations; + inode->i_fop = &yaffs_file_operations; + inode->i_mapping->a_ops = &yaffs_file_address_operations; + break; + case S_IFDIR: /* directory */ + inode->i_op = &yaffs_dir_inode_operations; + inode->i_fop = &yaffs_dir_operations; + break; + case S_IFLNK: /* symlink */ + inode->i_op = &yaffs_symlink_inode_operations; + break; + } + + yaffs_inode_to_obj_lv(inode) = obj; + obj->my_inode = inode; +} + +static void yaffs_put_super(struct super_block *sb) +{ + struct yaffs_dev *dev = yaffs_super_to_dev(sb); + struct mtd_info *mtd = yaffs_dev_to_mtd(dev); + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_put_super"); + + yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, + "Shutting down yaffs background thread"); + yaffs_bg_stop(dev); + yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, + "yaffs background thread shut down"); + + yaffs_gross_lock(dev); + + yaffs_flush_super(sb, 1); + + if (yaffs_dev_to_lc(dev)->put_super_fn) + yaffs_dev_to_lc(dev)->put_super_fn(sb); + + yaffs_deinitialise(dev); + + yaffs_gross_unlock(dev); + mutex_lock(&yaffs_context_lock); + list_del_init(&(yaffs_dev_to_lc(dev)->context_list)); + mutex_unlock(&yaffs_context_lock); + + if (yaffs_dev_to_lc(dev)->spare_buffer) { + kfree(yaffs_dev_to_lc(dev)->spare_buffer); + yaffs_dev_to_lc(dev)->spare_buffer = NULL; + } + + kfree(dev); + + if (mtd && mtd->sync) + mtd->sync(mtd); + + if (mtd) + put_mtd_device(mtd); +} + +/* the function only is used to change dev->read_only when this file system + * is remounted. + */ +static int yaffs_remount_fs(struct super_block *sb, int *flags, char *data) +{ + int read_only = 0; + struct mtd_info *mtd; + struct yaffs_dev *dev = 0; + + /* Get the device */ + mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); + if (!mtd) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "MTD device #%u doesn't appear to exist", + MINOR(sb->s_dev)); + return 1; + } + + /* Check it's NAND */ + if (mtd->type != MTD_NANDFLASH) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "MTD device is not NAND it's type %d", + mtd->type); + return 1; + } + + read_only = ((*flags & MS_RDONLY) != 0); + if (!read_only && !(mtd->flags & MTD_WRITEABLE)) { + read_only = 1; + printk(KERN_INFO + "yaffs: mtd is read only, setting superblock read only"); + *flags |= MS_RDONLY; + } + + dev = sb->s_fs_info; + dev->read_only = read_only; + + return 0; +} + + +static const struct super_operations yaffs_super_ops = { + .statfs = yaffs_statfs, + .put_super = yaffs_put_super, + .evict_inode = yaffs_evict_inode, + .sync_fs = yaffs_sync_fs, + .write_super = yaffs_write_super, + .remount_fs = yaffs_remount_fs, +}; + +static struct super_block *yaffs_internal_read_super(int yaffs_version, + struct super_block *sb, + void *data, int silent) +{ + int n_blocks; + struct inode *inode = NULL; + struct dentry *root; + struct yaffs_dev *dev = 0; + char devname_buf[BDEVNAME_SIZE + 1]; + struct mtd_info *mtd; + int err; + char *data_str = (char *)data; + struct yaffs_linux_context *context = NULL; + struct yaffs_param *param; + int read_only = 0; + struct yaffs_options options; + unsigned mount_id; + int found; + struct yaffs_linux_context *context_iterator; + struct list_head *l; + + if (!sb) { + printk(KERN_INFO "yaffs: sb is NULL\n"); + return NULL; + } + + sb->s_magic = YAFFS_MAGIC; + sb->s_op = &yaffs_super_ops; + sb->s_flags |= MS_NOATIME; + + read_only = ((sb->s_flags & MS_RDONLY) != 0); + + sb->s_export_op = &yaffs_export_ops; + + if (!sb->s_dev) + printk(KERN_INFO "yaffs: sb->s_dev is NULL\n"); + else if (!yaffs_devname(sb, devname_buf)) + printk(KERN_INFO "yaffs: devname is NULL\n"); + else + printk(KERN_INFO "yaffs: dev is %d name is \"%s\" %s\n", + sb->s_dev, + yaffs_devname(sb, devname_buf), read_only ? "ro" : "rw"); + + if (!data_str) + data_str = ""; + + printk(KERN_INFO "yaffs: passed flags \"%s\"\n", data_str); + + memset(&options, 0, sizeof(options)); + + if (yaffs_parse_options(&options, data_str)) + /* Option parsing failed */ + return NULL; + + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_read_super: Using yaffs%d", yaffs_version); + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_read_super: block size %d", (int)(sb->s_blocksize)); + + yaffs_trace(YAFFS_TRACE_ALWAYS, + "Attempting MTD mount of %u.%u,\"%s\"", + MAJOR(sb->s_dev), MINOR(sb->s_dev), + yaffs_devname(sb, devname_buf)); + + /* Check it's an mtd device..... */ + if (MAJOR(sb->s_dev) != MTD_BLOCK_MAJOR) + return NULL; /* This isn't an mtd device */ + + /* Get the device */ + mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); + if (IS_ERR(mtd)) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "MTD device #%u doesn't appear to exist", + MINOR(sb->s_dev)); + return NULL; + } + /* Check it's NAND */ + if (mtd->type != MTD_NANDFLASH) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "MTD device is not NAND it's type %d", + mtd->type); + return NULL; + } + + yaffs_trace(YAFFS_TRACE_OS, " erase %p", mtd->erase); + yaffs_trace(YAFFS_TRACE_OS, " read %p", mtd->read); + yaffs_trace(YAFFS_TRACE_OS, " write %p", mtd->write); + yaffs_trace(YAFFS_TRACE_OS, " readoob %p", mtd->read_oob); + yaffs_trace(YAFFS_TRACE_OS, " writeoob %p", mtd->write_oob); + yaffs_trace(YAFFS_TRACE_OS, " block_isbad %p", mtd->block_isbad); + yaffs_trace(YAFFS_TRACE_OS, " block_markbad %p", mtd->block_markbad); + yaffs_trace(YAFFS_TRACE_OS, " writesize %d", mtd->writesize); + yaffs_trace(YAFFS_TRACE_OS, " oobsize %d", mtd->oobsize); + yaffs_trace(YAFFS_TRACE_OS, " erasesize %d", mtd->erasesize); + yaffs_trace(YAFFS_TRACE_OS, " size %lld", mtd->size); + + if (yaffs_auto_select && yaffs_version == 1 && mtd->writesize >= 2048) { + yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs2"); + yaffs_version = 2; + } + + if (yaffs_auto_select && yaffs_version == 2 && !options.inband_tags && + mtd->writesize == 512) { + yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs1"); + yaffs_version = 1; + } + + if (yaffs_version == 2) { + /* Check for version 2 style functions */ + if (!mtd->erase || + !mtd->block_isbad || + !mtd->block_markbad || + !mtd->read || + !mtd->write || !mtd->read_oob || !mtd->write_oob) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "MTD device does not support required functions"); + return NULL; + } + + if ((mtd->writesize < YAFFS_MIN_YAFFS2_CHUNK_SIZE || + mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) && + !options.inband_tags) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "MTD device does not have the right page sizes"); + return NULL; + } + } else { + /* Check for V1 style functions */ + if (!mtd->erase || + !mtd->read || + !mtd->write || !mtd->read_oob || !mtd->write_oob) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "MTD device does not support required functions"); + return NULL; + } + + if (mtd->writesize < YAFFS_BYTES_PER_CHUNK || + mtd->oobsize != YAFFS_BYTES_PER_SPARE) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "MTD device does not support have the right page sizes"); + return NULL; + } + } + + /* OK, so if we got here, we have an MTD that's NAND and looks + * like it has the right capabilities + * Set the struct yaffs_dev up for mtd + */ + + if (!read_only && !(mtd->flags & MTD_WRITEABLE)) { + read_only = 1; + printk(KERN_INFO + "yaffs: mtd is read only, setting superblock read only"); + sb->s_flags |= MS_RDONLY; + } + + dev = kmalloc(sizeof(struct yaffs_dev), GFP_KERNEL); + context = kmalloc(sizeof(struct yaffs_linux_context), GFP_KERNEL); + + if (!dev || !context) { + kfree(dev); + kfree(context); + dev = NULL; + context = NULL; + + /* Deep shit could not allocate device structure */ + yaffs_trace(YAFFS_TRACE_ALWAYS, + "yaffs_read_super failed trying to allocate yaffs_dev"); + return NULL; + } + memset(dev, 0, sizeof(struct yaffs_dev)); + param = &(dev->param); + + memset(context, 0, sizeof(struct yaffs_linux_context)); + dev->os_context = context; + INIT_LIST_HEAD(&(context->context_list)); + context->dev = dev; + context->super = sb; + + dev->read_only = read_only; + + sb->s_fs_info = dev; + + dev->driver_context = mtd; + param->name = mtd->name; + + /* Set up the memory size parameters.... */ + + n_blocks = + YCALCBLOCKS(mtd->size, + (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK)); + + param->start_block = 0; + param->end_block = n_blocks - 1; + param->chunks_per_block = YAFFS_CHUNKS_PER_BLOCK; + param->total_bytes_per_chunk = YAFFS_BYTES_PER_CHUNK; + param->n_reserved_blocks = 5; + param->n_caches = (options.no_cache) ? 0 : 10; + param->inband_tags = options.inband_tags; + + param->disable_lazy_load = 1; + param->enable_xattr = 1; + if (options.lazy_loading_overridden) + param->disable_lazy_load = !options.lazy_loading_enabled; + + param->defered_dir_update = 1; + + if (options.tags_ecc_overridden) + param->no_tags_ecc = !options.tags_ecc_on; + + param->empty_lost_n_found = 1; + + param->refresh_period = 500; + + if (options.empty_lost_and_found_overridden) + param->empty_lost_n_found = options.empty_lost_and_found; + + /* ... and the functions. */ + if (yaffs_version == 2) { + param->write_chunk_tags_fn = nandmtd2_write_chunk_tags; + param->read_chunk_tags_fn = nandmtd2_read_chunk_tags; + param->bad_block_fn = nandmtd2_mark_block_bad; + param->query_block_fn = nandmtd2_query_block; + yaffs_dev_to_lc(dev)->spare_buffer = + kmalloc(mtd->oobsize, GFP_NOFS); + param->is_yaffs2 = 1; + param->total_bytes_per_chunk = mtd->writesize; + param->chunks_per_block = mtd->erasesize / mtd->writesize; + n_blocks = YCALCBLOCKS(mtd->size, mtd->erasesize); + + param->start_block = 0; + param->end_block = n_blocks - 1; + } else { + /* use the MTD interface in yaffs_mtdif1.c */ + param->write_chunk_tags_fn = nandmtd1_write_chunk_tags; + param->read_chunk_tags_fn = nandmtd1_read_chunk_tags; + param->bad_block_fn = nandmtd1_mark_block_bad; + param->query_block_fn = nandmtd1_query_block; + param->is_yaffs2 = 0; + } + /* ... and common functions */ + param->erase_fn = nandmtd_erase_block; + param->initialise_flash_fn = nandmtd_initialise; + + yaffs_dev_to_lc(dev)->put_super_fn = yaffs_mtd_put_super; + + param->sb_dirty_fn = yaffs_touch_super; + param->gc_control_fn = yaffs_gc_control_callback; + + yaffs_dev_to_lc(dev)->super = sb; + + param->use_nand_ecc = 1; + + param->skip_checkpt_rd = options.skip_checkpoint_read; + param->skip_checkpt_wr = options.skip_checkpoint_write; + + mutex_lock(&yaffs_context_lock); + /* Get a mount id */ + for (mount_id = 0, found = 0; !found; mount_id++) { + found = 1; + list_for_each(l, &yaffs_context_list) { + context_iterator = + list_entry(l, struct yaffs_linux_context, + context_list); + if (context_iterator->mount_id == mount_id) + found = 0; + } + } + context->mount_id = mount_id; + + list_add_tail(&(yaffs_dev_to_lc(dev)->context_list), + &yaffs_context_list); + mutex_unlock(&yaffs_context_lock); + + /* Directory search handling... */ + INIT_LIST_HEAD(&(yaffs_dev_to_lc(dev)->search_contexts)); + param->remove_obj_fn = yaffs_remove_obj_callback; + + mutex_init(&(yaffs_dev_to_lc(dev)->gross_lock)); + + yaffs_gross_lock(dev); + + err = yaffs_guts_initialise(dev); + + yaffs_trace(YAFFS_TRACE_OS, + "yaffs_read_super: guts initialised %s", + (err == YAFFS_OK) ? "OK" : "FAILED"); + + if (err == YAFFS_OK) + yaffs_bg_start(dev); + + if (!context->bg_thread) + param->defered_dir_update = 0; + + sb->s_maxbytes = yaffs_max_file_size(dev); + + /* Release lock before yaffs_get_inode() */ + yaffs_gross_unlock(dev); + + /* Create root inode */ + if (err == YAFFS_OK) + inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0, yaffs_root(dev)); + + if (!inode) + return NULL; + + inode->i_op = &yaffs_dir_inode_operations; + inode->i_fop = &yaffs_dir_operations; + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: got root inode"); + + root = d_alloc_root(inode); + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: d_alloc_root done"); + + if (!root) { + iput(inode); + return NULL; + } + sb->s_root = root; + sb->s_dirt = !dev->is_checkpointed; + yaffs_trace(YAFFS_TRACE_ALWAYS, + "yaffs_read_super: is_checkpointed %d", + dev->is_checkpointed); + + yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: done"); + return sb; +} + +static int yaffs_internal_read_super_mtd(struct super_block *sb, void *data, + int silent) +{ + return yaffs_internal_read_super(1, sb, data, silent) ? 0 : -EINVAL; +} + +static struct dentry *yaffs_mount(struct file_system_type *fs, + int flags, const char *dev_name, + void *data) +{ + return mount_bdev(fs, flags, dev_name, data, + yaffs_internal_read_super_mtd); +} + +static struct file_system_type yaffs_fs_type = { + .owner = THIS_MODULE, + .name = "yaffs", + .mount = yaffs_mount, + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, +}; + +static int yaffs2_internal_read_super_mtd(struct super_block *sb, void *data, + int silent) +{ + return yaffs_internal_read_super(2, sb, data, silent) ? 0 : -EINVAL; +} + +static struct dentry *yaffs2_mount(struct file_system_type *fs, + int flags, const char *dev_name, void *data) +{ + return mount_bdev(fs, flags, dev_name, data, + yaffs2_internal_read_super_mtd); +} + +static struct file_system_type yaffs2_fs_type = { + .owner = THIS_MODULE, + .name = "yaffs2", + .mount = yaffs2_mount, + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, +}; + + +static struct proc_dir_entry *my_proc_entry; + +static char *yaffs_dump_dev_part0(char *buf, struct yaffs_dev *dev) +{ + struct yaffs_param *param = &dev->param; + + buf += sprintf(buf, "start_block........... %d\n", param->start_block); + buf += sprintf(buf, "end_block............. %d\n", param->end_block); + buf += sprintf(buf, "total_bytes_per_chunk. %d\n", + param->total_bytes_per_chunk); + buf += sprintf(buf, "use_nand_ecc.......... %d\n", + param->use_nand_ecc); + buf += sprintf(buf, "no_tags_ecc........... %d\n", param->no_tags_ecc); + buf += sprintf(buf, "is_yaffs2............. %d\n", param->is_yaffs2); + buf += sprintf(buf, "inband_tags........... %d\n", param->inband_tags); + buf += sprintf(buf, "empty_lost_n_found.... %d\n", + param->empty_lost_n_found); + buf += sprintf(buf, "disable_lazy_load..... %d\n", + param->disable_lazy_load); + buf += sprintf(buf, "refresh_period........ %d\n", + param->refresh_period); + buf += sprintf(buf, "n_caches.............. %d\n", param->n_caches); + buf += sprintf(buf, "n_reserved_blocks..... %d\n", + param->n_reserved_blocks); + buf += sprintf(buf, "always_check_erased... %d\n", + param->always_check_erased); + + return buf; +} + +static char *yaffs_dump_dev_part1(char *buf, struct yaffs_dev *dev) +{ + buf += sprintf(buf, "max file size......... %lld\n", + (long long) yaffs_max_file_size(dev)); + buf += sprintf(buf, "data_bytes_per_chunk.. %d\n", + dev->data_bytes_per_chunk); + buf += sprintf(buf, "chunk_grp_bits........ %d\n", dev->chunk_grp_bits); + buf += sprintf(buf, "chunk_grp_size........ %d\n", dev->chunk_grp_size); + buf += + sprintf(buf, "n_erased_blocks....... %d\n", dev->n_erased_blocks); + buf += + sprintf(buf, "blocks_in_checkpt..... %d\n", dev->blocks_in_checkpt); + buf += sprintf(buf, "\n"); + buf += sprintf(buf, "n_tnodes.............. %d\n", dev->n_tnodes); + buf += sprintf(buf, "n_obj................. %d\n", dev->n_obj); + buf += sprintf(buf, "n_free_chunks......... %d\n", dev->n_free_chunks); + buf += sprintf(buf, "\n"); + buf += sprintf(buf, "n_page_writes......... %u\n", dev->n_page_writes); + buf += sprintf(buf, "n_page_reads.......... %u\n", dev->n_page_reads); + buf += sprintf(buf, "n_erasures............ %u\n", dev->n_erasures); + buf += sprintf(buf, "n_gc_copies........... %u\n", dev->n_gc_copies); + buf += sprintf(buf, "all_gcs............... %u\n", dev->all_gcs); + buf += + sprintf(buf, "passive_gc_count...... %u\n", dev->passive_gc_count); + buf += + sprintf(buf, "oldest_dirty_gc_count. %u\n", + dev->oldest_dirty_gc_count); + buf += sprintf(buf, "n_gc_blocks........... %u\n", dev->n_gc_blocks); + buf += sprintf(buf, "bg_gcs................ %u\n", dev->bg_gcs); + buf += + sprintf(buf, "n_retried_writes...... %u\n", dev->n_retried_writes); + buf += + sprintf(buf, "n_retired_blocks...... %u\n", dev->n_retired_blocks); + buf += sprintf(buf, "n_ecc_fixed........... %u\n", dev->n_ecc_fixed); + buf += sprintf(buf, "n_ecc_unfixed......... %u\n", dev->n_ecc_unfixed); + buf += + sprintf(buf, "n_tags_ecc_fixed...... %u\n", dev->n_tags_ecc_fixed); + buf += + sprintf(buf, "n_tags_ecc_unfixed.... %u\n", + dev->n_tags_ecc_unfixed); + buf += sprintf(buf, "cache_hits............ %u\n", dev->cache_hits); + buf += + sprintf(buf, "n_deleted_files....... %u\n", dev->n_deleted_files); + buf += + sprintf(buf, "n_unlinked_files...... %u\n", dev->n_unlinked_files); + buf += sprintf(buf, "refresh_count......... %u\n", dev->refresh_count); + buf += sprintf(buf, "n_bg_deletions........ %u\n", dev->n_bg_deletions); + + return buf; +} + +static int yaffs_proc_read(char *page, + char **start, + off_t offset, int count, int *eof, void *data) +{ + struct list_head *item; + char *buf = page; + int step = offset; + int n = 0; + + /* Get proc_file_read() to step 'offset' by one on each sucessive call. + * We use 'offset' (*ppos) to indicate where we are in dev_list. + * This also assumes the user has posted a read buffer large + * enough to hold the complete output; but that's life in /proc. + */ + + *(int *)start = 1; + + /* Print header first */ + if (step == 0) + buf += sprintf(buf, "YAFFS built:" __DATE__ " " __TIME__ "\n"); + else if (step == 1) + buf += sprintf(buf, "\n"); + else { + step -= 2; + + mutex_lock(&yaffs_context_lock); + + /* Locate and print the Nth entry. + * Order N-squared but N is small. */ + list_for_each(item, &yaffs_context_list) { + struct yaffs_linux_context *dc = + list_entry(item, struct yaffs_linux_context, + context_list); + struct yaffs_dev *dev = dc->dev; + + if (n < (step & ~1)) { + n += 2; + continue; + } + if ((step & 1) == 0) { + buf += + sprintf(buf, "\nDevice %d \"%s\"\n", n, + dev->param.name); + buf = yaffs_dump_dev_part0(buf, dev); + } else { + buf = yaffs_dump_dev_part1(buf, dev); + } + + break; + } + mutex_unlock(&yaffs_context_lock); + } + + return buf - page < count ? buf - page : count; +} + + + +/* Stuff to handle installation of file systems */ +struct file_system_to_install { + struct file_system_type *fst; + int installed; +}; + +static struct file_system_to_install fs_to_install[] = { + {&yaffs_fs_type, 0}, + {&yaffs2_fs_type, 0}, + {NULL, 0} +}; + +static int __init init_yaffs_fs(void) +{ + int error = 0; + struct file_system_to_install *fsinst; + + yaffs_trace(YAFFS_TRACE_ALWAYS, + "yaffs built " __DATE__ " " __TIME__ " Installing."); + + mutex_init(&yaffs_context_lock); + + /* Install the proc_fs entries */ + my_proc_entry = create_proc_entry("yaffs", + S_IRUGO | S_IFREG, NULL); + + if (my_proc_entry) { + my_proc_entry->write_proc = NULL; + my_proc_entry->read_proc = yaffs_proc_read; + my_proc_entry->data = NULL; + } else { + return -ENOMEM; + } + + + /* Now add the file system entries */ + + fsinst = fs_to_install; + + while (fsinst->fst && !error) { + error = register_filesystem(fsinst->fst); + if (!error) + fsinst->installed = 1; + fsinst++; + } + + /* Any errors? uninstall */ + if (error) { + fsinst = fs_to_install; + + while (fsinst->fst) { + if (fsinst->installed) { + unregister_filesystem(fsinst->fst); + fsinst->installed = 0; + } + fsinst++; + } + } + + return error; +} + +static void __exit exit_yaffs_fs(void) +{ + + struct file_system_to_install *fsinst; + + yaffs_trace(YAFFS_TRACE_ALWAYS, + "yaffs built " __DATE__ " " __TIME__ " removing."); + + remove_proc_entry("yaffs", NULL); + + fsinst = fs_to_install; + + while (fsinst->fst) { + if (fsinst->installed) { + unregister_filesystem(fsinst->fst); + fsinst->installed = 0; + } + fsinst++; + } +} + +module_init(init_yaffs_fs) +module_exit(exit_yaffs_fs) + +MODULE_DESCRIPTION("YAFFS2 - a NAND specific flash file system"); +MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002-2011"); +MODULE_LICENSE("GPL"); diff --git a/fs/yaffs2/yaffs_yaffs1.c b/fs/yaffs2/yaffs_yaffs1.c new file mode 100644 index 00000000..4f2e7687 --- /dev/null +++ b/fs/yaffs2/yaffs_yaffs1.c @@ -0,0 +1,422 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "yaffs_yaffs1.h" +#include "yportenv.h" +#include "yaffs_trace.h" +#include "yaffs_bitmap.h" +#include "yaffs_getblockinfo.h" +#include "yaffs_nand.h" +#include "yaffs_attribs.h" + +int yaffs1_scan(struct yaffs_dev *dev) +{ + struct yaffs_ext_tags tags; + int blk; + int result; + int chunk; + int c; + int deleted; + enum yaffs_block_state state; + LIST_HEAD(hard_list); + struct yaffs_block_info *bi; + u32 seq_number; + struct yaffs_obj_hdr *oh; + struct yaffs_obj *in; + struct yaffs_obj *parent; + int alloc_failed = 0; + struct yaffs_shadow_fixer *shadow_fixers = NULL; + u8 *chunk_data; + + yaffs_trace(YAFFS_TRACE_SCAN, + "yaffs1_scan starts intstartblk %d intendblk %d...", + dev->internal_start_block, dev->internal_end_block); + + chunk_data = yaffs_get_temp_buffer(dev); + + dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; + + /* Scan all the blocks to determine their state */ + bi = dev->block_info; + for (blk = dev->internal_start_block; blk <= dev->internal_end_block; + blk++) { + yaffs_clear_chunk_bits(dev, blk); + bi->pages_in_use = 0; + bi->soft_del_pages = 0; + + yaffs_query_init_block_state(dev, blk, &state, &seq_number); + + bi->block_state = state; + bi->seq_number = seq_number; + + if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) + bi->block_state = state = YAFFS_BLOCK_STATE_DEAD; + + yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, + "Block scanning block %d state %d seq %d", + blk, state, seq_number); + + if (state == YAFFS_BLOCK_STATE_DEAD) { + yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, + "block %d is bad", blk); + } else if (state == YAFFS_BLOCK_STATE_EMPTY) { + yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); + dev->n_erased_blocks++; + dev->n_free_chunks += dev->param.chunks_per_block; + } + bi++; + } + + /* For each block.... */ + for (blk = dev->internal_start_block; + !alloc_failed && blk <= dev->internal_end_block; blk++) { + + cond_resched(); + + bi = yaffs_get_block_info(dev, blk); + state = bi->block_state; + + deleted = 0; + + /* For each chunk in each block that needs scanning.... */ + for (c = 0; + !alloc_failed && c < dev->param.chunks_per_block && + state == YAFFS_BLOCK_STATE_NEEDS_SCAN; c++) { + /* Read the tags and decide what to do */ + chunk = blk * dev->param.chunks_per_block + c; + + result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, + &tags); + + /* Let's have a good look at this chunk... */ + + if (tags.ecc_result == YAFFS_ECC_RESULT_UNFIXED || + tags.is_deleted) { + /* YAFFS1 only... + * A deleted chunk + */ + deleted++; + dev->n_free_chunks++; + } else if (!tags.chunk_used) { + /* An unassigned chunk in the block + * This means that either the block is empty or + * this is the one being allocated from + */ + + if (c == 0) { + /* We're looking at the first chunk in + *the block so the block is unused */ + state = YAFFS_BLOCK_STATE_EMPTY; + dev->n_erased_blocks++; + } else { + /* this is the block being allocated */ + yaffs_trace(YAFFS_TRACE_SCAN, + " Allocating from %d %d", + blk, c); + state = YAFFS_BLOCK_STATE_ALLOCATING; + dev->alloc_block = blk; + dev->alloc_page = c; + dev->alloc_block_finder = blk; + + } + + dev->n_free_chunks += + (dev->param.chunks_per_block - c); + } else if (tags.chunk_id > 0) { + /* chunk_id > 0 so it is a data chunk... */ + unsigned int endpos; + + yaffs_set_chunk_bit(dev, blk, c); + bi->pages_in_use++; + + in = yaffs_find_or_create_by_number(dev, + tags.obj_id, + YAFFS_OBJECT_TYPE_FILE); + /* PutChunkIntoFile checks for a clash + * (two data chunks with the same chunk_id). + */ + + if (!in) + alloc_failed = 1; + + if (in) { + if (!yaffs_put_chunk_in_file + (in, tags.chunk_id, chunk, 1)) + alloc_failed = 1; + } + + endpos = + (tags.chunk_id - 1) * + dev->data_bytes_per_chunk + + tags.n_bytes; + if (in && + in->variant_type == + YAFFS_OBJECT_TYPE_FILE && + in->variant.file_variant.stored_size < + endpos) { + in->variant.file_variant.stored_size = + endpos; + if (!dev->param.use_header_file_size) { + in->variant. + file_variant.file_size = + in->variant. + file_variant.stored_size; + } + + } + } else { + /* chunk_id == 0, so it is an ObjectHeader. + * Make the object + */ + yaffs_set_chunk_bit(dev, blk, c); + bi->pages_in_use++; + + result = yaffs_rd_chunk_tags_nand(dev, chunk, + chunk_data, + NULL); + + oh = (struct yaffs_obj_hdr *)chunk_data; + + in = yaffs_find_by_number(dev, tags.obj_id); + if (in && in->variant_type != oh->type) { + /* This should not happen, but somehow + * Wev'e ended up with an obj_id that + * has been reused but not yet deleted, + * and worse still it has changed type. + * Delete the old object. + */ + + yaffs_del_obj(in); + in = NULL; + } + + in = yaffs_find_or_create_by_number(dev, + tags.obj_id, + oh->type); + + if (!in) + alloc_failed = 1; + + if (in && oh->shadows_obj > 0) { + + struct yaffs_shadow_fixer *fixer; + fixer = + kmalloc(sizeof + (struct yaffs_shadow_fixer), + GFP_NOFS); + if (fixer) { + fixer->next = shadow_fixers; + shadow_fixers = fixer; + fixer->obj_id = tags.obj_id; + fixer->shadowed_id = + oh->shadows_obj; + yaffs_trace(YAFFS_TRACE_SCAN, + " Shadow fixer: %d shadows %d", + fixer->obj_id, + fixer->shadowed_id); + + } + + } + + if (in && in->valid) { + /* We have already filled this one. + * We have a duplicate and need to + * resolve it. */ + + unsigned existing_serial = in->serial; + unsigned new_serial = + tags.serial_number; + + if (((existing_serial + 1) & 3) == + new_serial) { + /* Use new one - destroy the + * exisiting one */ + yaffs_chunk_del(dev, + in->hdr_chunk, + 1, __LINE__); + in->valid = 0; + } else { + /* Use existing - destroy + * this one. */ + yaffs_chunk_del(dev, chunk, 1, + __LINE__); + } + } + + if (in && !in->valid && + (tags.obj_id == YAFFS_OBJECTID_ROOT || + tags.obj_id == + YAFFS_OBJECTID_LOSTNFOUND)) { + /* We only load some info, don't fiddle + * with directory structure */ + in->valid = 1; + in->variant_type = oh->type; + + in->yst_mode = oh->yst_mode; + yaffs_load_attribs(in, oh); + in->hdr_chunk = chunk; + in->serial = tags.serial_number; + + } else if (in && !in->valid) { + /* we need to load this info */ + + in->valid = 1; + in->variant_type = oh->type; + + in->yst_mode = oh->yst_mode; + yaffs_load_attribs(in, oh); + in->hdr_chunk = chunk; + in->serial = tags.serial_number; + + yaffs_set_obj_name_from_oh(in, oh); + in->dirty = 0; + + /* directory stuff... + * hook up to parent + */ + + parent = + yaffs_find_or_create_by_number + (dev, oh->parent_obj_id, + YAFFS_OBJECT_TYPE_DIRECTORY); + if (!parent) + alloc_failed = 1; + if (parent && parent->variant_type == + YAFFS_OBJECT_TYPE_UNKNOWN) { + /* Set up as a directory */ + parent->variant_type = + YAFFS_OBJECT_TYPE_DIRECTORY; + INIT_LIST_HEAD(&parent-> + variant.dir_variant. + children); + } else if (!parent || + parent->variant_type != + YAFFS_OBJECT_TYPE_DIRECTORY) { + /* Hoosterman, a problem.... + * We're trying to use a + * non-directory as a directory + */ + + yaffs_trace(YAFFS_TRACE_ERROR, + "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." + ); + parent = dev->lost_n_found; + } + + yaffs_add_obj_to_dir(parent, in); + + switch (in->variant_type) { + case YAFFS_OBJECT_TYPE_UNKNOWN: + /* Todo got a problem */ + break; + case YAFFS_OBJECT_TYPE_FILE: + if (dev->param. + use_header_file_size) + in->variant. + file_variant.file_size + = yaffs_oh_to_size(oh); + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + in->variant. + hardlink_variant.equiv_id = + oh->equiv_id; + list_add(&in->hard_links, + &hard_list); + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + /* Do nothing */ + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + /* Do nothing */ + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + in->variant.symlink_variant. + alias = + yaffs_clone_str(oh->alias); + if (!in->variant. + symlink_variant.alias) + alloc_failed = 1; + break; + } + } + } + } + + if (state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { + /* If we got this far while scanning, + * then the block is fully allocated. */ + state = YAFFS_BLOCK_STATE_FULL; + } + + if (state == YAFFS_BLOCK_STATE_ALLOCATING) { + /* If the block was partially allocated then + * treat it as fully allocated. */ + state = YAFFS_BLOCK_STATE_FULL; + dev->alloc_block = -1; + } + + bi->block_state = state; + + /* Now let's see if it was dirty */ + if (bi->pages_in_use == 0 && + !bi->has_shrink_hdr && + bi->block_state == YAFFS_BLOCK_STATE_FULL) + yaffs_block_became_dirty(dev, blk); + } + + /* Ok, we've done all the scanning. + * Fix up the hard link chains. + * We should now have scanned all the objects, now it's time to add + * these hardlinks. + */ + + yaffs_link_fixup(dev, &hard_list); + + /* + * Fix up any shadowed objects. + * There should not be more than one of these. + */ + { + struct yaffs_shadow_fixer *fixer; + struct yaffs_obj *obj; + + while (shadow_fixers) { + fixer = shadow_fixers; + shadow_fixers = fixer->next; + /* Complete the rename transaction by deleting the + * shadowed object then setting the object header + to unshadowed. + */ + obj = yaffs_find_by_number(dev, fixer->shadowed_id); + if (obj) + yaffs_del_obj(obj); + + obj = yaffs_find_by_number(dev, fixer->obj_id); + + if (obj) + yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); + + kfree(fixer); + } + } + + yaffs_release_temp_buffer(dev, chunk_data); + + if (alloc_failed) + return YAFFS_FAIL; + + yaffs_trace(YAFFS_TRACE_SCAN, "yaffs1_scan ends"); + + return YAFFS_OK; +} diff --git a/fs/yaffs2/yaffs_yaffs1.h b/fs/yaffs2/yaffs_yaffs1.h new file mode 100644 index 00000000..97e2fdd0 --- /dev/null +++ b/fs/yaffs2/yaffs_yaffs1.h @@ -0,0 +1,22 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_YAFFS1_H__ +#define __YAFFS_YAFFS1_H__ + +#include "yaffs_guts.h" +int yaffs1_scan(struct yaffs_dev *dev); + +#endif diff --git a/fs/yaffs2/yaffs_yaffs2.c b/fs/yaffs2/yaffs_yaffs2.c new file mode 100644 index 00000000..f2f98a4c --- /dev/null +++ b/fs/yaffs2/yaffs_yaffs2.c @@ -0,0 +1,1533 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "yaffs_guts.h" +#include "yaffs_trace.h" +#include "yaffs_yaffs2.h" +#include "yaffs_checkptrw.h" +#include "yaffs_bitmap.h" +#include "yaffs_nand.h" +#include "yaffs_getblockinfo.h" +#include "yaffs_verify.h" +#include "yaffs_attribs.h" +#include "yaffs_summary.h" + +/* + * Checkpoints are really no benefit on very small partitions. + * + * To save space on small partitions don't bother with checkpoints unless + * the partition is at least this big. + */ +#define YAFFS_CHECKPOINT_MIN_BLOCKS 60 +#define YAFFS_SMALL_HOLE_THRESHOLD 4 + +/* + * Oldest Dirty Sequence Number handling. + */ + +/* yaffs_calc_oldest_dirty_seq() + * yaffs2_find_oldest_dirty_seq() + * Calculate the oldest dirty sequence number if we don't know it. + */ +void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev) +{ + int i; + unsigned seq; + unsigned block_no = 0; + struct yaffs_block_info *b; + + if (!dev->param.is_yaffs2) + return; + + /* Find the oldest dirty sequence number. */ + seq = dev->seq_number + 1; + b = dev->block_info; + for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { + if (b->block_state == YAFFS_BLOCK_STATE_FULL && + (b->pages_in_use - b->soft_del_pages) < + dev->param.chunks_per_block && + b->seq_number < seq) { + seq = b->seq_number; + block_no = i; + } + b++; + } + + if (block_no) { + dev->oldest_dirty_seq = seq; + dev->oldest_dirty_block = block_no; + } +} + +void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev) +{ + if (!dev->param.is_yaffs2) + return; + + if (!dev->oldest_dirty_seq) + yaffs_calc_oldest_dirty_seq(dev); +} + +/* + * yaffs_clear_oldest_dirty_seq() + * Called when a block is erased or marked bad. (ie. when its seq_number + * becomes invalid). If the value matches the oldest then we clear + * dev->oldest_dirty_seq to force its recomputation. + */ +void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, + struct yaffs_block_info *bi) +{ + + if (!dev->param.is_yaffs2) + return; + + if (!bi || bi->seq_number == dev->oldest_dirty_seq) { + dev->oldest_dirty_seq = 0; + dev->oldest_dirty_block = 0; + } +} + +/* + * yaffs2_update_oldest_dirty_seq() + * Update the oldest dirty sequence number whenever we dirty a block. + * Only do this if the oldest_dirty_seq is actually being tracked. + */ +void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, + struct yaffs_block_info *bi) +{ + if (!dev->param.is_yaffs2) + return; + + if (dev->oldest_dirty_seq) { + if (dev->oldest_dirty_seq > bi->seq_number) { + dev->oldest_dirty_seq = bi->seq_number; + dev->oldest_dirty_block = block_no; + } + } +} + +int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi) +{ + + if (!dev->param.is_yaffs2) + return 1; /* disqualification only applies to yaffs2. */ + + if (!bi->has_shrink_hdr) + return 1; /* can gc */ + + yaffs2_find_oldest_dirty_seq(dev); + + /* Can't do gc of this block if there are any blocks older than this + * one that have discarded pages. + */ + return (bi->seq_number <= dev->oldest_dirty_seq); +} + +/* + * yaffs2_find_refresh_block() + * periodically finds the oldest full block by sequence number for refreshing. + * Only for yaffs2. + */ +u32 yaffs2_find_refresh_block(struct yaffs_dev *dev) +{ + u32 b; + u32 oldest = 0; + u32 oldest_seq = 0; + struct yaffs_block_info *bi; + + if (!dev->param.is_yaffs2) + return oldest; + + /* + * If refresh period < 10 then refreshing is disabled. + */ + if (dev->param.refresh_period < 10) + return oldest; + + /* + * Fix broken values. + */ + if (dev->refresh_skip > dev->param.refresh_period) + dev->refresh_skip = dev->param.refresh_period; + + if (dev->refresh_skip > 0) + return oldest; + + /* + * Refresh skip is now zero. + * We'll do a refresh this time around.... + * Update the refresh skip and find the oldest block. + */ + dev->refresh_skip = dev->param.refresh_period; + dev->refresh_count++; + bi = dev->block_info; + for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { + + if (bi->block_state == YAFFS_BLOCK_STATE_FULL) { + + if (oldest < 1 || bi->seq_number < oldest_seq) { + oldest = b; + oldest_seq = bi->seq_number; + } + } + bi++; + } + + if (oldest > 0) { + yaffs_trace(YAFFS_TRACE_GC, + "GC refresh count %d selected block %d with seq_number %d", + dev->refresh_count, oldest, oldest_seq); + } + + return oldest; +} + +int yaffs2_checkpt_required(struct yaffs_dev *dev) +{ + int nblocks; + + if (!dev->param.is_yaffs2) + return 0; + + nblocks = dev->internal_end_block - dev->internal_start_block + 1; + + return !dev->param.skip_checkpt_wr && + !dev->read_only && (nblocks >= YAFFS_CHECKPOINT_MIN_BLOCKS); +} + +int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev) +{ + int retval; + int n_bytes = 0; + int n_blocks; + int dev_blocks; + + if (!dev->param.is_yaffs2) + return 0; + + if (!dev->checkpoint_blocks_required && yaffs2_checkpt_required(dev)) { + /* Not a valid value so recalculate */ + dev_blocks = dev->param.end_block - dev->param.start_block + 1; + n_bytes += sizeof(struct yaffs_checkpt_validity); + n_bytes += sizeof(struct yaffs_checkpt_dev); + n_bytes += dev_blocks * sizeof(struct yaffs_block_info); + n_bytes += dev_blocks * dev->chunk_bit_stride; + n_bytes += + (sizeof(struct yaffs_checkpt_obj) + sizeof(u32)) * + dev->n_obj; + n_bytes += (dev->tnode_size + sizeof(u32)) * dev->n_tnodes; + n_bytes += sizeof(struct yaffs_checkpt_validity); + n_bytes += sizeof(u32); /* checksum */ + + /* Round up and add 2 blocks to allow for some bad blocks, + * so add 3 */ + + n_blocks = + (n_bytes / + (dev->data_bytes_per_chunk * + dev->param.chunks_per_block)) + 3; + + dev->checkpoint_blocks_required = n_blocks; + } + + retval = dev->checkpoint_blocks_required - dev->blocks_in_checkpt; + if (retval < 0) + retval = 0; + return retval; +} + +/*--------------------- Checkpointing --------------------*/ + +static int yaffs2_wr_checkpt_validity_marker(struct yaffs_dev *dev, int head) +{ + struct yaffs_checkpt_validity cp; + + memset(&cp, 0, sizeof(cp)); + + cp.struct_type = sizeof(cp); + cp.magic = YAFFS_MAGIC; + cp.version = YAFFS_CHECKPOINT_VERSION; + cp.head = (head) ? 1 : 0; + + return (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)) ? 1 : 0; +} + +static int yaffs2_rd_checkpt_validity_marker(struct yaffs_dev *dev, int head) +{ + struct yaffs_checkpt_validity cp; + int ok; + + ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); + + if (ok) + ok = (cp.struct_type == sizeof(cp)) && + (cp.magic == YAFFS_MAGIC) && + (cp.version == YAFFS_CHECKPOINT_VERSION) && + (cp.head == ((head) ? 1 : 0)); + return ok ? 1 : 0; +} + +static void yaffs2_dev_to_checkpt_dev(struct yaffs_checkpt_dev *cp, + struct yaffs_dev *dev) +{ + cp->n_erased_blocks = dev->n_erased_blocks; + cp->alloc_block = dev->alloc_block; + cp->alloc_page = dev->alloc_page; + cp->n_free_chunks = dev->n_free_chunks; + + cp->n_deleted_files = dev->n_deleted_files; + cp->n_unlinked_files = dev->n_unlinked_files; + cp->n_bg_deletions = dev->n_bg_deletions; + cp->seq_number = dev->seq_number; + +} + +static void yaffs_checkpt_dev_to_dev(struct yaffs_dev *dev, + struct yaffs_checkpt_dev *cp) +{ + dev->n_erased_blocks = cp->n_erased_blocks; + dev->alloc_block = cp->alloc_block; + dev->alloc_page = cp->alloc_page; + dev->n_free_chunks = cp->n_free_chunks; + + dev->n_deleted_files = cp->n_deleted_files; + dev->n_unlinked_files = cp->n_unlinked_files; + dev->n_bg_deletions = cp->n_bg_deletions; + dev->seq_number = cp->seq_number; +} + +static int yaffs2_wr_checkpt_dev(struct yaffs_dev *dev) +{ + struct yaffs_checkpt_dev cp; + u32 n_bytes; + u32 n_blocks = dev->internal_end_block - dev->internal_start_block + 1; + int ok; + + /* Write device runtime values */ + yaffs2_dev_to_checkpt_dev(&cp, dev); + cp.struct_type = sizeof(cp); + + ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); + if (!ok) + return 0; + + /* Write block info */ + n_bytes = n_blocks * sizeof(struct yaffs_block_info); + ok = (yaffs2_checkpt_wr(dev, dev->block_info, n_bytes) == n_bytes); + if (!ok) + return 0; + + /* Write chunk bits */ + n_bytes = n_blocks * dev->chunk_bit_stride; + ok = (yaffs2_checkpt_wr(dev, dev->chunk_bits, n_bytes) == n_bytes); + + return ok ? 1 : 0; +} + +static int yaffs2_rd_checkpt_dev(struct yaffs_dev *dev) +{ + struct yaffs_checkpt_dev cp; + u32 n_bytes; + u32 n_blocks = + (dev->internal_end_block - dev->internal_start_block + 1); + int ok; + + ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); + if (!ok) + return 0; + + if (cp.struct_type != sizeof(cp)) + return 0; + + yaffs_checkpt_dev_to_dev(dev, &cp); + + n_bytes = n_blocks * sizeof(struct yaffs_block_info); + + ok = (yaffs2_checkpt_rd(dev, dev->block_info, n_bytes) == n_bytes); + + if (!ok) + return 0; + + n_bytes = n_blocks * dev->chunk_bit_stride; + + ok = (yaffs2_checkpt_rd(dev, dev->chunk_bits, n_bytes) == n_bytes); + + return ok ? 1 : 0; +} + +static void yaffs2_obj_checkpt_obj(struct yaffs_checkpt_obj *cp, + struct yaffs_obj *obj) +{ + cp->obj_id = obj->obj_id; + cp->parent_id = (obj->parent) ? obj->parent->obj_id : 0; + cp->hdr_chunk = obj->hdr_chunk; + cp->variant_type = obj->variant_type; + cp->deleted = obj->deleted; + cp->soft_del = obj->soft_del; + cp->unlinked = obj->unlinked; + cp->fake = obj->fake; + cp->rename_allowed = obj->rename_allowed; + cp->unlink_allowed = obj->unlink_allowed; + cp->serial = obj->serial; + cp->n_data_chunks = obj->n_data_chunks; + + if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) + cp->size_or_equiv_obj = obj->variant.file_variant.file_size; + else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) + cp->size_or_equiv_obj = obj->variant.hardlink_variant.equiv_id; +} + +static int yaffs2_checkpt_obj_to_obj(struct yaffs_obj *obj, + struct yaffs_checkpt_obj *cp) +{ + struct yaffs_obj *parent; + + if (obj->variant_type != cp->variant_type) { + yaffs_trace(YAFFS_TRACE_ERROR, + "Checkpoint read object %d type %d chunk %d does not match existing object type %d", + cp->obj_id, cp->variant_type, cp->hdr_chunk, + obj->variant_type); + return 0; + } + + obj->obj_id = cp->obj_id; + + if (cp->parent_id) + parent = yaffs_find_or_create_by_number(obj->my_dev, + cp->parent_id, + YAFFS_OBJECT_TYPE_DIRECTORY); + else + parent = NULL; + + if (parent) { + if (parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "Checkpoint read object %d parent %d type %d chunk %d Parent type, %d, not directory", + cp->obj_id, cp->parent_id, + cp->variant_type, cp->hdr_chunk, + parent->variant_type); + return 0; + } + yaffs_add_obj_to_dir(parent, obj); + } + + obj->hdr_chunk = cp->hdr_chunk; + obj->variant_type = cp->variant_type; + obj->deleted = cp->deleted; + obj->soft_del = cp->soft_del; + obj->unlinked = cp->unlinked; + obj->fake = cp->fake; + obj->rename_allowed = cp->rename_allowed; + obj->unlink_allowed = cp->unlink_allowed; + obj->serial = cp->serial; + obj->n_data_chunks = cp->n_data_chunks; + + if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) { + obj->variant.file_variant.file_size = cp->size_or_equiv_obj; + obj->variant.file_variant.stored_size = cp->size_or_equiv_obj; + } else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { + obj->variant.hardlink_variant.equiv_id = cp->size_or_equiv_obj; + } + if (obj->hdr_chunk > 0) + obj->lazy_loaded = 1; + return 1; +} + +static int yaffs2_checkpt_tnode_worker(struct yaffs_obj *in, + struct yaffs_tnode *tn, u32 level, + int chunk_offset) +{ + int i; + struct yaffs_dev *dev = in->my_dev; + int ok = 1; + u32 base_offset; + + if (!tn) + return 1; + + if (level > 0) { + for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) { + if (!tn->internal[i]) + continue; + ok = yaffs2_checkpt_tnode_worker(in, + tn->internal[i], + level - 1, + (chunk_offset << + YAFFS_TNODES_INTERNAL_BITS) + i); + } + return ok; + } + + /* Level 0 tnode */ + base_offset = chunk_offset << YAFFS_TNODES_LEVEL0_BITS; + ok = (yaffs2_checkpt_wr(dev, &base_offset, sizeof(base_offset)) == + sizeof(base_offset)); + if (ok) + ok = (yaffs2_checkpt_wr(dev, tn, dev->tnode_size) == + dev->tnode_size); + + return ok; +} + +static int yaffs2_wr_checkpt_tnodes(struct yaffs_obj *obj) +{ + u32 end_marker = ~0; + int ok = 1; + + if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) + return ok; + + ok = yaffs2_checkpt_tnode_worker(obj, + obj->variant.file_variant.top, + obj->variant.file_variant. + top_level, 0); + if (ok) + ok = (yaffs2_checkpt_wr(obj->my_dev, &end_marker, + sizeof(end_marker)) == sizeof(end_marker)); + + return ok ? 1 : 0; +} + +static int yaffs2_rd_checkpt_tnodes(struct yaffs_obj *obj) +{ + u32 base_chunk; + int ok = 1; + struct yaffs_dev *dev = obj->my_dev; + struct yaffs_file_var *file_stuct_ptr = &obj->variant.file_variant; + struct yaffs_tnode *tn; + int nread = 0; + + ok = (yaffs2_checkpt_rd(dev, &base_chunk, sizeof(base_chunk)) == + sizeof(base_chunk)); + + while (ok && (~base_chunk)) { + nread++; + /* Read level 0 tnode */ + + tn = yaffs_get_tnode(dev); + if (tn) + ok = (yaffs2_checkpt_rd(dev, tn, dev->tnode_size) == + dev->tnode_size); + else + ok = 0; + + if (tn && ok) + ok = yaffs_add_find_tnode_0(dev, + file_stuct_ptr, + base_chunk, tn) ? 1 : 0; + + if (ok) + ok = (yaffs2_checkpt_rd + (dev, &base_chunk, + sizeof(base_chunk)) == sizeof(base_chunk)); + } + + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "Checkpoint read tnodes %d records, last %d. ok %d", + nread, base_chunk, ok); + + return ok ? 1 : 0; +} + +static int yaffs2_wr_checkpt_objs(struct yaffs_dev *dev) +{ + struct yaffs_obj *obj; + struct yaffs_checkpt_obj cp; + int i; + int ok = 1; + struct list_head *lh; + + /* Iterate through the objects in each hash entry, + * dumping them to the checkpointing stream. + */ + + for (i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++) { + list_for_each(lh, &dev->obj_bucket[i].list) { + obj = list_entry(lh, struct yaffs_obj, hash_link); + if (!obj->defered_free) { + yaffs2_obj_checkpt_obj(&cp, obj); + cp.struct_type = sizeof(cp); + + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "Checkpoint write object %d parent %d type %d chunk %d obj addr %p", + cp.obj_id, cp.parent_id, + cp.variant_type, cp.hdr_chunk, obj); + + ok = (yaffs2_checkpt_wr(dev, &cp, + sizeof(cp)) == sizeof(cp)); + + if (ok && + obj->variant_type == + YAFFS_OBJECT_TYPE_FILE) + ok = yaffs2_wr_checkpt_tnodes(obj); + } + } + } + + /* Dump end of list */ + memset(&cp, 0xff, sizeof(struct yaffs_checkpt_obj)); + cp.struct_type = sizeof(cp); + + if (ok) + ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); + + return ok ? 1 : 0; +} + +static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev) +{ + struct yaffs_obj *obj; + struct yaffs_checkpt_obj cp; + int ok = 1; + int done = 0; + LIST_HEAD(hard_list); + + + while (ok && !done) { + ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); + if (cp.struct_type != sizeof(cp)) { + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "struct size %d instead of %d ok %d", + cp.struct_type, (int)sizeof(cp), ok); + ok = 0; + } + + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "Checkpoint read object %d parent %d type %d chunk %d ", + cp.obj_id, cp.parent_id, cp.variant_type, + cp.hdr_chunk); + + if (ok && cp.obj_id == ~0) { + done = 1; + } else if (ok) { + obj = + yaffs_find_or_create_by_number(dev, cp.obj_id, + cp.variant_type); + if (obj) { + ok = yaffs2_checkpt_obj_to_obj(obj, &cp); + if (!ok) + break; + if (obj->variant_type == + YAFFS_OBJECT_TYPE_FILE) { + ok = yaffs2_rd_checkpt_tnodes(obj); + } else if (obj->variant_type == + YAFFS_OBJECT_TYPE_HARDLINK) { + list_add(&obj->hard_links, &hard_list); + } + } else { + ok = 0; + } + } + } + + if (ok) + yaffs_link_fixup(dev, &hard_list); + + return ok ? 1 : 0; +} + +static int yaffs2_wr_checkpt_sum(struct yaffs_dev *dev) +{ + u32 checkpt_sum; + int ok; + + yaffs2_get_checkpt_sum(dev, &checkpt_sum); + + ok = (yaffs2_checkpt_wr(dev, &checkpt_sum, sizeof(checkpt_sum)) == + sizeof(checkpt_sum)); + + if (!ok) + return 0; + + return 1; +} + +static int yaffs2_rd_checkpt_sum(struct yaffs_dev *dev) +{ + u32 checkpt_sum0; + u32 checkpt_sum1; + int ok; + + yaffs2_get_checkpt_sum(dev, &checkpt_sum0); + + ok = (yaffs2_checkpt_rd(dev, &checkpt_sum1, sizeof(checkpt_sum1)) == + sizeof(checkpt_sum1)); + + if (!ok) + return 0; + + if (checkpt_sum0 != checkpt_sum1) + return 0; + + return 1; +} + +static int yaffs2_wr_checkpt_data(struct yaffs_dev *dev) +{ + int ok = 1; + + if (!yaffs2_checkpt_required(dev)) { + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "skipping checkpoint write"); + ok = 0; + } + + if (ok) + ok = yaffs2_checkpt_open(dev, 1); + + if (ok) { + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "write checkpoint validity"); + ok = yaffs2_wr_checkpt_validity_marker(dev, 1); + } + if (ok) { + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "write checkpoint device"); + ok = yaffs2_wr_checkpt_dev(dev); + } + if (ok) { + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "write checkpoint objects"); + ok = yaffs2_wr_checkpt_objs(dev); + } + if (ok) { + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "write checkpoint validity"); + ok = yaffs2_wr_checkpt_validity_marker(dev, 0); + } + + if (ok) + ok = yaffs2_wr_checkpt_sum(dev); + + if (!yaffs_checkpt_close(dev)) + ok = 0; + + if (ok) + dev->is_checkpointed = 1; + else + dev->is_checkpointed = 0; + + return dev->is_checkpointed; +} + +static int yaffs2_rd_checkpt_data(struct yaffs_dev *dev) +{ + int ok = 1; + + if (!dev->param.is_yaffs2) + ok = 0; + + if (ok && dev->param.skip_checkpt_rd) { + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "skipping checkpoint read"); + ok = 0; + } + + if (ok) + ok = yaffs2_checkpt_open(dev, 0); /* open for read */ + + if (ok) { + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "read checkpoint validity"); + ok = yaffs2_rd_checkpt_validity_marker(dev, 1); + } + if (ok) { + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "read checkpoint device"); + ok = yaffs2_rd_checkpt_dev(dev); + } + if (ok) { + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "read checkpoint objects"); + ok = yaffs2_rd_checkpt_objs(dev); + } + if (ok) { + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "read checkpoint validity"); + ok = yaffs2_rd_checkpt_validity_marker(dev, 0); + } + + if (ok) { + ok = yaffs2_rd_checkpt_sum(dev); + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "read checkpoint checksum %d", ok); + } + + if (!yaffs_checkpt_close(dev)) + ok = 0; + + if (ok) + dev->is_checkpointed = 1; + else + dev->is_checkpointed = 0; + + return ok ? 1 : 0; +} + +void yaffs2_checkpt_invalidate(struct yaffs_dev *dev) +{ + if (dev->is_checkpointed || dev->blocks_in_checkpt > 0) { + dev->is_checkpointed = 0; + yaffs2_checkpt_invalidate_stream(dev); + } + if (dev->param.sb_dirty_fn) + dev->param.sb_dirty_fn(dev); +} + +int yaffs_checkpoint_save(struct yaffs_dev *dev) +{ + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "save entry: is_checkpointed %d", + dev->is_checkpointed); + + yaffs_verify_objects(dev); + yaffs_verify_blocks(dev); + yaffs_verify_free_chunks(dev); + + if (!dev->is_checkpointed) { + yaffs2_checkpt_invalidate(dev); + yaffs2_wr_checkpt_data(dev); + } + + yaffs_trace(YAFFS_TRACE_CHECKPOINT | YAFFS_TRACE_MOUNT, + "save exit: is_checkpointed %d", + dev->is_checkpointed); + + return dev->is_checkpointed; +} + +int yaffs2_checkpt_restore(struct yaffs_dev *dev) +{ + int retval; + + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "restore entry: is_checkpointed %d", + dev->is_checkpointed); + + retval = yaffs2_rd_checkpt_data(dev); + + if (dev->is_checkpointed) { + yaffs_verify_objects(dev); + yaffs_verify_blocks(dev); + yaffs_verify_free_chunks(dev); + } + + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "restore exit: is_checkpointed %d", + dev->is_checkpointed); + + return retval; +} + +int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size) +{ + /* if new_size > old_file_size. + * We're going to be writing a hole. + * If the hole is small then write zeros otherwise write a start + * of hole marker. + */ + loff_t old_file_size; + loff_t increase; + int small_hole; + int result = YAFFS_OK; + struct yaffs_dev *dev = NULL; + u8 *local_buffer = NULL; + int small_increase_ok = 0; + + if (!obj) + return YAFFS_FAIL; + + if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) + return YAFFS_FAIL; + + dev = obj->my_dev; + + /* Bail out if not yaffs2 mode */ + if (!dev->param.is_yaffs2) + return YAFFS_OK; + + old_file_size = obj->variant.file_variant.file_size; + + if (new_size <= old_file_size) + return YAFFS_OK; + + increase = new_size - old_file_size; + + if (increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->data_bytes_per_chunk && + yaffs_check_alloc_available(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1)) + small_hole = 1; + else + small_hole = 0; + + if (small_hole) + local_buffer = yaffs_get_temp_buffer(dev); + + if (local_buffer) { + /* fill hole with zero bytes */ + loff_t pos = old_file_size; + int this_write; + int written; + memset(local_buffer, 0, dev->data_bytes_per_chunk); + small_increase_ok = 1; + + while (increase > 0 && small_increase_ok) { + this_write = increase; + if (this_write > dev->data_bytes_per_chunk) + this_write = dev->data_bytes_per_chunk; + written = + yaffs_do_file_wr(obj, local_buffer, pos, this_write, + 0); + if (written == this_write) { + pos += this_write; + increase -= this_write; + } else { + small_increase_ok = 0; + } + } + + yaffs_release_temp_buffer(dev, local_buffer); + + /* If out of space then reverse any chunks we've added */ + if (!small_increase_ok) + yaffs_resize_file_down(obj, old_file_size); + } + + if (!small_increase_ok && + obj->parent && + obj->parent->obj_id != YAFFS_OBJECTID_UNLINKED && + obj->parent->obj_id != YAFFS_OBJECTID_DELETED) { + /* Write a hole start header with the old file size */ + yaffs_update_oh(obj, NULL, 0, 1, 0, NULL); + } + + return result; +} + +struct yaffs_block_index { + int seq; + int block; +}; + +static int yaffs2_ybicmp(const void *a, const void *b) +{ + int aseq = ((struct yaffs_block_index *)a)->seq; + int bseq = ((struct yaffs_block_index *)b)->seq; + int ablock = ((struct yaffs_block_index *)a)->block; + int bblock = ((struct yaffs_block_index *)b)->block; + + if (aseq == bseq) + return ablock - bblock; + + return aseq - bseq; +} + +static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, + struct yaffs_block_info *bi, + int blk, int chunk_in_block, + int *found_chunks, + u8 *chunk_data, + struct list_head *hard_list, + int summary_available) +{ + struct yaffs_obj_hdr *oh; + struct yaffs_obj *in; + struct yaffs_obj *parent; + int equiv_id; + loff_t file_size; + int is_shrink; + int is_unlinked; + struct yaffs_ext_tags tags; + int result; + int alloc_failed = 0; + int chunk = blk * dev->param.chunks_per_block + chunk_in_block; + struct yaffs_file_var *file_var; + struct yaffs_hardlink_var *hl_var; + struct yaffs_symlink_var *sl_var; + + if (summary_available) { + result = yaffs_summary_fetch(dev, &tags, chunk_in_block); + tags.seq_number = bi->seq_number; + } + + if (!summary_available || tags.obj_id == 0) { + result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags); + dev->tags_used++; + } else { + dev->summary_used++; + } + + /* Let's have a good look at this chunk... */ + + if (!tags.chunk_used) { + /* An unassigned chunk in the block. + * If there are used chunks after this one, then + * it is a chunk that was skipped due to failing + * the erased check. Just skip it so that it can + * be deleted. + * But, more typically, We get here when this is + * an unallocated chunk and his means that + * either the block is empty or this is the one + * being allocated from + */ + + if (*found_chunks) { + /* This is a chunk that was skipped due + * to failing the erased check */ + } else if (chunk_in_block == 0) { + /* We're looking at the first chunk in + * the block so the block is unused */ + bi->block_state = YAFFS_BLOCK_STATE_EMPTY; + dev->n_erased_blocks++; + } else { + if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || + bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { + if (dev->seq_number == bi->seq_number) { + /* Allocating from this block*/ + yaffs_trace(YAFFS_TRACE_SCAN, + " Allocating from %d %d", + blk, chunk_in_block); + + bi->block_state = + YAFFS_BLOCK_STATE_ALLOCATING; + dev->alloc_block = blk; + dev->alloc_page = chunk_in_block; + dev->alloc_block_finder = blk; + } else { + /* This is a partially written block + * that is not the current + * allocation block. + */ + yaffs_trace(YAFFS_TRACE_SCAN, + "Partially written block %d detected. gc will fix this.", + blk); + } + } + } + + dev->n_free_chunks++; + + } else if (tags.ecc_result == + YAFFS_ECC_RESULT_UNFIXED) { + yaffs_trace(YAFFS_TRACE_SCAN, + " Unfixed ECC in chunk(%d:%d), chunk ignored", + blk, chunk_in_block); + dev->n_free_chunks++; + } else if (tags.obj_id > YAFFS_MAX_OBJECT_ID || + tags.chunk_id > YAFFS_MAX_CHUNK_ID || + tags.obj_id == YAFFS_OBJECTID_SUMMARY || + (tags.chunk_id > 0 && + tags.n_bytes > dev->data_bytes_per_chunk) || + tags.seq_number != bi->seq_number) { + yaffs_trace(YAFFS_TRACE_SCAN, + "Chunk (%d:%d) with bad tags:obj = %d, chunk_id = %d, n_bytes = %d, ignored", + blk, chunk_in_block, tags.obj_id, + tags.chunk_id, tags.n_bytes); + dev->n_free_chunks++; + } else if (tags.chunk_id > 0) { + /* chunk_id > 0 so it is a data chunk... */ + loff_t endpos; + loff_t chunk_base = (tags.chunk_id - 1) * + dev->data_bytes_per_chunk; + + *found_chunks = 1; + + yaffs_set_chunk_bit(dev, blk, chunk_in_block); + bi->pages_in_use++; + + in = yaffs_find_or_create_by_number(dev, + tags.obj_id, + YAFFS_OBJECT_TYPE_FILE); + if (!in) + /* Out of memory */ + alloc_failed = 1; + + if (in && + in->variant_type == YAFFS_OBJECT_TYPE_FILE && + chunk_base < in->variant.file_variant.shrink_size) { + /* This has not been invalidated by + * a resize */ + if (!yaffs_put_chunk_in_file(in, tags.chunk_id, + chunk, -1)) + alloc_failed = 1; + + /* File size is calculated by looking at + * the data chunks if we have not + * seen an object header yet. + * Stop this practice once we find an + * object header. + */ + endpos = chunk_base + tags.n_bytes; + + if (!in->valid && + in->variant.file_variant.stored_size < endpos) { + in->variant.file_variant. + stored_size = endpos; + in->variant.file_variant. + file_size = endpos; + } + } else if (in) { + /* This chunk has been invalidated by a + * resize, or a past file deletion + * so delete the chunk*/ + yaffs_chunk_del(dev, chunk, 1, __LINE__); + } + } else { + /* chunk_id == 0, so it is an ObjectHeader. + * Thus, we read in the object header and make + * the object + */ + *found_chunks = 1; + + yaffs_set_chunk_bit(dev, blk, chunk_in_block); + bi->pages_in_use++; + + oh = NULL; + in = NULL; + + if (tags.extra_available) { + in = yaffs_find_or_create_by_number(dev, + tags.obj_id, + tags.extra_obj_type); + if (!in) + alloc_failed = 1; + } + + if (!in || + (!in->valid && dev->param.disable_lazy_load) || + tags.extra_shadows || + (!in->valid && (tags.obj_id == YAFFS_OBJECTID_ROOT || + tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND))) { + + /* If we don't have valid info then we + * need to read the chunk + * TODO In future we can probably defer + * reading the chunk and living with + * invalid data until needed. + */ + + result = yaffs_rd_chunk_tags_nand(dev, + chunk, + chunk_data, + NULL); + + oh = (struct yaffs_obj_hdr *)chunk_data; + + if (dev->param.inband_tags) { + /* Fix up the header if they got + * corrupted by inband tags */ + oh->shadows_obj = + oh->inband_shadowed_obj_id; + oh->is_shrink = + oh->inband_is_shrink; + } + + if (!in) { + in = yaffs_find_or_create_by_number(dev, + tags.obj_id, oh->type); + if (!in) + alloc_failed = 1; + } + } + + if (!in) { + /* TODO Hoosterman we have a problem! */ + yaffs_trace(YAFFS_TRACE_ERROR, + "yaffs tragedy: Could not make object for object %d at chunk %d during scan", + tags.obj_id, chunk); + return YAFFS_FAIL; + } + + if (in->valid) { + /* We have already filled this one. + * We have a duplicate that will be + * discarded, but we first have to suck + * out resize info if it is a file. + */ + if ((in->variant_type == YAFFS_OBJECT_TYPE_FILE) && + ((oh && oh->type == YAFFS_OBJECT_TYPE_FILE) || + (tags.extra_available && + tags.extra_obj_type == YAFFS_OBJECT_TYPE_FILE) + )) { + loff_t this_size = (oh) ? + yaffs_oh_to_size(oh) : + tags.extra_file_size; + u32 parent_obj_id = (oh) ? + oh->parent_obj_id : + tags.extra_parent_id; + + is_shrink = (oh) ? + oh->is_shrink : + tags.extra_is_shrink; + + /* If it is deleted (unlinked + * at start also means deleted) + * we treat the file size as + * being zeroed at this point. + */ + if (parent_obj_id == YAFFS_OBJECTID_DELETED || + parent_obj_id == YAFFS_OBJECTID_UNLINKED) { + this_size = 0; + is_shrink = 1; + } + + if (is_shrink && + in->variant.file_variant.shrink_size > + this_size) + in->variant.file_variant.shrink_size = + this_size; + + if (is_shrink) + bi->has_shrink_hdr = 1; + } + /* Use existing - destroy this one. */ + yaffs_chunk_del(dev, chunk, 1, __LINE__); + } + + if (!in->valid && in->variant_type != + (oh ? oh->type : tags.extra_obj_type)) { + yaffs_trace(YAFFS_TRACE_ERROR, + "yaffs tragedy: Bad type, %d != %d, for object %d at chunk %d during scan", + oh ? oh->type : tags.extra_obj_type, + in->variant_type, tags.obj_id, + chunk); + in = yaffs_retype_obj(in, oh ? oh->type : tags.extra_obj_type); + } + + if (!in->valid && + (tags.obj_id == YAFFS_OBJECTID_ROOT || + tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND)) { + /* We only load some info, don't fiddle + * with directory structure */ + in->valid = 1; + + if (oh) { + in->yst_mode = oh->yst_mode; + yaffs_load_attribs(in, oh); + in->lazy_loaded = 0; + } else { + in->lazy_loaded = 1; + } + in->hdr_chunk = chunk; + + } else if (!in->valid) { + /* we need to load this info */ + in->valid = 1; + in->hdr_chunk = chunk; + if (oh) { + in->variant_type = oh->type; + in->yst_mode = oh->yst_mode; + yaffs_load_attribs(in, oh); + + if (oh->shadows_obj > 0) + yaffs_handle_shadowed_obj(dev, + oh->shadows_obj, 1); + + yaffs_set_obj_name_from_oh(in, oh); + parent = yaffs_find_or_create_by_number(dev, + oh->parent_obj_id, + YAFFS_OBJECT_TYPE_DIRECTORY); + file_size = yaffs_oh_to_size(oh); + is_shrink = oh->is_shrink; + equiv_id = oh->equiv_id; + } else { + in->variant_type = tags.extra_obj_type; + parent = yaffs_find_or_create_by_number(dev, + tags.extra_parent_id, + YAFFS_OBJECT_TYPE_DIRECTORY); + file_size = tags.extra_file_size; + is_shrink = tags.extra_is_shrink; + equiv_id = tags.extra_equiv_id; + in->lazy_loaded = 1; + } + in->dirty = 0; + + if (!parent) + alloc_failed = 1; + + /* directory stuff... + * hook up to parent + */ + + if (parent && + parent->variant_type == YAFFS_OBJECT_TYPE_UNKNOWN) { + /* Set up as a directory */ + parent->variant_type = + YAFFS_OBJECT_TYPE_DIRECTORY; + INIT_LIST_HEAD(&parent-> + variant.dir_variant.children); + } else if (!parent || + parent->variant_type != + YAFFS_OBJECT_TYPE_DIRECTORY) { + /* Hoosterman, another problem.... + * Trying to use a non-directory as a directory + */ + + yaffs_trace(YAFFS_TRACE_ERROR, + "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." + ); + parent = dev->lost_n_found; + } + yaffs_add_obj_to_dir(parent, in); + + is_unlinked = (parent == dev->del_dir) || + (parent == dev->unlinked_dir); + + if (is_shrink) + /* Mark the block */ + bi->has_shrink_hdr = 1; + + /* Note re hardlinks. + * Since we might scan a hardlink before its equivalent + * object is scanned we put them all in a list. + * After scanning is complete, we should have all the + * objects, so we run through this list and fix up all + * the chains. + */ + + switch (in->variant_type) { + case YAFFS_OBJECT_TYPE_UNKNOWN: + /* Todo got a problem */ + break; + case YAFFS_OBJECT_TYPE_FILE: + file_var = &in->variant.file_variant; + if (file_var->stored_size < file_size) { + /* This covers the case where the file + * size is greater than the data held. + * This will happen if the file is + * resized to be larger than its + * current data extents. + */ + file_var->file_size = file_size; + file_var->stored_size = file_size; + } + + if (file_var->shrink_size > file_size) + file_var->shrink_size = file_size; + + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + hl_var = &in->variant.hardlink_variant; + if (!is_unlinked) { + hl_var->equiv_id = equiv_id; + list_add(&in->hard_links, hard_list); + } + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + /* Do nothing */ + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + /* Do nothing */ + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + sl_var = &in->variant.symlink_variant; + if (oh) { + sl_var->alias = + yaffs_clone_str(oh->alias); + if (!sl_var->alias) + alloc_failed = 1; + } + break; + } + } + } + return alloc_failed ? YAFFS_FAIL : YAFFS_OK; +} + +int yaffs2_scan_backwards(struct yaffs_dev *dev) +{ + int blk; + int block_iter; + int start_iter; + int end_iter; + int n_to_scan = 0; + enum yaffs_block_state state; + int c; + LIST_HEAD(hard_list); + struct yaffs_block_info *bi; + u32 seq_number; + int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; + u8 *chunk_data; + int found_chunks; + int alloc_failed = 0; + struct yaffs_block_index *block_index = NULL; + int alt_block_index = 0; + int summary_available; + + yaffs_trace(YAFFS_TRACE_SCAN, + "yaffs2_scan_backwards starts intstartblk %d intendblk %d...", + dev->internal_start_block, dev->internal_end_block); + + dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; + + block_index = + kmalloc(n_blocks * sizeof(struct yaffs_block_index), GFP_NOFS); + + if (!block_index) { + block_index = + vmalloc(n_blocks * sizeof(struct yaffs_block_index)); + alt_block_index = 1; + } + + if (!block_index) { + yaffs_trace(YAFFS_TRACE_SCAN, + "yaffs2_scan_backwards() could not allocate block index!" + ); + return YAFFS_FAIL; + } + + dev->blocks_in_checkpt = 0; + + chunk_data = yaffs_get_temp_buffer(dev); + + /* Scan all the blocks to determine their state */ + bi = dev->block_info; + for (blk = dev->internal_start_block; blk <= dev->internal_end_block; + blk++) { + yaffs_clear_chunk_bits(dev, blk); + bi->pages_in_use = 0; + bi->soft_del_pages = 0; + + yaffs_query_init_block_state(dev, blk, &state, &seq_number); + + bi->block_state = state; + bi->seq_number = seq_number; + + if (bi->seq_number == YAFFS_SEQUENCE_CHECKPOINT_DATA) + bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; + if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) + bi->block_state = YAFFS_BLOCK_STATE_DEAD; + + yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, + "Block scanning block %d state %d seq %d", + blk, bi->block_state, seq_number); + + if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { + dev->blocks_in_checkpt++; + + } else if (bi->block_state == YAFFS_BLOCK_STATE_DEAD) { + yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, + "block %d is bad", blk); + } else if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { + yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); + dev->n_erased_blocks++; + dev->n_free_chunks += dev->param.chunks_per_block; + } else if (bi->block_state == + YAFFS_BLOCK_STATE_NEEDS_SCAN) { + /* Determine the highest sequence number */ + if (seq_number >= YAFFS_LOWEST_SEQUENCE_NUMBER && + seq_number < YAFFS_HIGHEST_SEQUENCE_NUMBER) { + block_index[n_to_scan].seq = seq_number; + block_index[n_to_scan].block = blk; + n_to_scan++; + if (seq_number >= dev->seq_number) + dev->seq_number = seq_number; + } else { + /* TODO: Nasty sequence number! */ + yaffs_trace(YAFFS_TRACE_SCAN, + "Block scanning block %d has bad sequence number %d", + blk, seq_number); + } + } + bi++; + } + + yaffs_trace(YAFFS_TRACE_ALWAYS, "%d blocks to be sorted...", n_to_scan); + + cond_resched(); + + /* Sort the blocks by sequence number */ + sort(block_index, n_to_scan, sizeof(struct yaffs_block_index), + yaffs2_ybicmp, NULL); + + cond_resched(); + + yaffs_trace(YAFFS_TRACE_SCAN, "...done"); + + /* Now scan the blocks looking at the data. */ + start_iter = 0; + end_iter = n_to_scan - 1; + yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "%d blocks to scan", n_to_scan); + + /* For each block.... backwards */ + for (block_iter = end_iter; + !alloc_failed && block_iter >= start_iter; + block_iter--) { + /* Cooperative multitasking! This loop can run for so + long that watchdog timers expire. */ + cond_resched(); + + /* get the block to scan in the correct order */ + blk = block_index[block_iter].block; + bi = yaffs_get_block_info(dev, blk); + + summary_available = yaffs_summary_read(dev, dev->sum_tags, blk); + + /* For each chunk in each block that needs scanning.... */ + found_chunks = 0; + if (summary_available) + c = dev->chunks_per_summary - 1; + else + c = dev->param.chunks_per_block - 1; + + for (/* c is already initialised */; + !alloc_failed && c >= 0 && + (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || + bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING); + c--) { + /* Scan backwards... + * Read the tags and decide what to do + */ + if (yaffs2_scan_chunk(dev, bi, blk, c, + &found_chunks, chunk_data, + &hard_list, summary_available) == + YAFFS_FAIL) + alloc_failed = 1; + } + + if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { + /* If we got this far while scanning, then the block + * is fully allocated. */ + bi->block_state = YAFFS_BLOCK_STATE_FULL; + } + + /* Now let's see if it was dirty */ + if (bi->pages_in_use == 0 && + !bi->has_shrink_hdr && + bi->block_state == YAFFS_BLOCK_STATE_FULL) { + yaffs_block_became_dirty(dev, blk); + } + } + + yaffs_skip_rest_of_block(dev); + + if (alt_block_index) + vfree(block_index); + else + kfree(block_index); + + /* Ok, we've done all the scanning. + * Fix up the hard link chains. + * We have scanned all the objects, now it's time to add these + * hardlinks. + */ + yaffs_link_fixup(dev, &hard_list); + + yaffs_release_temp_buffer(dev, chunk_data); + + if (alloc_failed) + return YAFFS_FAIL; + + yaffs_trace(YAFFS_TRACE_SCAN, "yaffs2_scan_backwards ends"); + + return YAFFS_OK; +} diff --git a/fs/yaffs2/yaffs_yaffs2.h b/fs/yaffs2/yaffs_yaffs2.h new file mode 100644 index 00000000..2363bfd8 --- /dev/null +++ b/fs/yaffs2/yaffs_yaffs2.h @@ -0,0 +1,39 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_YAFFS2_H__ +#define __YAFFS_YAFFS2_H__ + +#include "yaffs_guts.h" + +void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev); +void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev); +void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, + struct yaffs_block_info *bi); +void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, + struct yaffs_block_info *bi); +int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi); +u32 yaffs2_find_refresh_block(struct yaffs_dev *dev); +int yaffs2_checkpt_required(struct yaffs_dev *dev); +int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev); + +void yaffs2_checkpt_invalidate(struct yaffs_dev *dev); +int yaffs2_checkpt_save(struct yaffs_dev *dev); +int yaffs2_checkpt_restore(struct yaffs_dev *dev); + +int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size); +int yaffs2_scan_backwards(struct yaffs_dev *dev); + +#endif diff --git a/fs/yaffs2/yportenv.h b/fs/yaffs2/yportenv.h new file mode 100644 index 00000000..d4a91780 --- /dev/null +++ b/fs/yaffs2/yportenv.h @@ -0,0 +1,20 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Waldemar Rymarkiewicz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifdef YAFFS_CURRENT + #include "yportenv_single.h" +#else + #include "yportenv_multi.h" +#endif diff --git a/fs/yaffs2/yportenv_multi.h b/fs/yaffs2/yportenv_multi.h new file mode 100644 index 00000000..9fade6d4 --- /dev/null +++ b/fs/yaffs2/yportenv_multi.h @@ -0,0 +1,91 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YPORTENV_H__ +#define __YPORTENV_H__ + +/* + * Define the MTD version in terms of Linux Kernel versions + * This allows yaffs to be used independantly of the kernel + * as well as with it. + */ + +#define MTD_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) + +#ifdef YAFFS_OUT_OF_TREE +#include "moduleconfig.h" +#endif + +#include +#define MTD_VERSION_CODE LINUX_VERSION_CODE + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* These type wrappings are used to support Unicode names in WinCE. */ +#define YCHAR char +#define YUCHAR unsigned char +#define _Y(x) x + +#define YAFFS_LOSTNFOUND_NAME "lost+found" +#define YAFFS_LOSTNFOUND_PREFIX "obj" + + +#define YAFFS_ROOT_MODE 0755 +#define YAFFS_LOSTNFOUND_MODE 0700 + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) +#define Y_CURRENT_TIME CURRENT_TIME.tv_sec +#define Y_TIME_CONVERT(x) (x).tv_sec +#else +#define Y_CURRENT_TIME CURRENT_TIME +#define Y_TIME_CONVERT(x) (x) +#endif + +#define compile_time_assertion(assertion) \ + ({ int x = __builtin_choose_expr(assertion, 0, (void)0); (void) x; }) + + +#define yaffs_printf(msk, fmt, ...) \ + printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__) + +#if 0 +#define yaffs_trace(msk, fmt, ...) do { \ + printk(KERN_ALERT "yaffs: " fmt "\n", ##__VA_ARGS__); \ +} while (0) + +#else +#define yaffs_trace(msk, fmt, ...) do { \ + if (yaffs_trace_mask & (msk)) \ + printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__); \ +} while (0) +#endif + +#endif diff --git a/fs/yaffs2/yportenv_single.h b/fs/yaffs2/yportenv_single.h new file mode 100644 index 00000000..76a79149 --- /dev/null +++ b/fs/yaffs2/yportenv_single.h @@ -0,0 +1,65 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YPORTENV_LINUX_H__ +#define __YPORTENV_LINUX_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* These type wrappings are used to support Unicode names in WinCE. */ +#define YCHAR char +#define YUCHAR unsigned char +#define _Y(x) x + +#define YAFFS_LOSTNFOUND_NAME "lost+found" +#define YAFFS_LOSTNFOUND_PREFIX "obj" + + +#define YAFFS_ROOT_MODE 0755 +#define YAFFS_LOSTNFOUND_MODE 0700 + +#define Y_CURRENT_TIME CURRENT_TIME.tv_sec +#define Y_TIME_CONVERT(x) (x).tv_sec + +#define compile_time_assertion(assertion) \ + ({ int x = __builtin_choose_expr(assertion, 0, (void)0); (void) x; }) + +#define yaffs_printf(msk, fmt, ...) \ + printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__) + +#ifdef CONFIG_YAFFS_DEBUG +#define yaffs_trace(msk, fmt, ...) do { \ + if (yaffs_trace_mask & (msk)) \ + printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__); \ +} while (0) +#else +#define yaffs_trace(msk, fmt, ...) do { \ +} while (0) +#endif + +#endif diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 8aeadf6b..8aafa37c 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -803,3 +803,13 @@ BSS(bss_align) \ . = ALIGN(stop_align); \ VMLINUX_SYMBOL(__bss_stop) = .; + +#define L2MEM(x) \ + _start_##x = .; \ + .l2mem_##x 0x48000000 : AT(_start_##x) { \ + __l2mem_start_##x = .;\ + *(.l2mem_##x) \ + __l2mem_end_##x = .;\ + } \ + . = _start_##x + __l2mem_end_##x - __l2mem_start_##x;\ + _end_##x = .; diff --git a/include/crypto/cryptodev.h b/include/crypto/cryptodev.h new file mode 100644 index 00000000..4fe29d63 --- /dev/null +++ b/include/crypto/cryptodev.h @@ -0,0 +1,300 @@ +/* This is a source compatible implementation with the original API of + * cryptodev by Angelos D. Keromytis, found at openbsd cryptodev.h. + * Placed under public domain */ + +#ifndef L_CRYPTODEV_H +#define L_CRYPTODEV_H + +#include +#ifndef __KERNEL__ +#define __user +#endif + +/* API extensions for linux */ +#define CRYPTO_HMAC_MAX_KEY_LEN 512 +#define CRYPTO_CIPHER_MAX_KEY_LEN 64 + +/* All the supported algorithms + */ +enum cryptodev_crypto_op_t { + CRYPTO_DES_CBC = 1, + CRYPTO_3DES_CBC = 2, + CRYPTO_BLF_CBC = 3, + CRYPTO_CAST_CBC = 4, + CRYPTO_SKIPJACK_CBC = 5, + CRYPTO_MD5_HMAC = 6, + CRYPTO_SHA1_HMAC = 7, + CRYPTO_RIPEMD160_HMAC = 8, + CRYPTO_MD5_KPDK = 9, + CRYPTO_SHA1_KPDK = 10, + CRYPTO_RIJNDAEL128_CBC = 11, + CRYPTO_AES_CBC = CRYPTO_RIJNDAEL128_CBC, + CRYPTO_ARC4 = 12, + CRYPTO_MD5 = 13, + CRYPTO_SHA1 = 14, + CRYPTO_DEFLATE_COMP = 15, + CRYPTO_NULL = 16, + CRYPTO_LZS_COMP = 17, + CRYPTO_SHA2_256_HMAC = 18, + CRYPTO_SHA2_384_HMAC = 19, + CRYPTO_SHA2_512_HMAC = 20, + CRYPTO_AES_CTR = 21, + CRYPTO_AES_XTS = 22, + CRYPTO_AES_ECB = 23, + CRYPTO_AES_GCM = 50, + CRYPTO_AES_CFB, + CRYPTO_AES_OFB, + CRYPTO_DES_ECB, + CRYPTO_DES_CFB, + CRYPTO_DES_OFB, + CRYPTO_3DES_ECB, + CRYPTO_3DES_CFB, + CRYPTO_3DES_OFB, + + CRYPTO_CAMELLIA_CBC = 101, + CRYPTO_RIPEMD160, + CRYPTO_SHA2_224, + CRYPTO_SHA2_256, + CRYPTO_SHA2_384, + CRYPTO_SHA2_512, + CRYPTO_SHA2_224_HMAC, + CRYPTO_ALGORITHM_ALL, /* Keep updated - see below */ +}; + +#define CRYPTO_ALGORITHM_MAX (CRYPTO_ALGORITHM_ALL - 1) + +/* Values for ciphers */ +#define DES_BLOCK_LEN 8 +#define DES3_BLOCK_LEN 8 +#define RIJNDAEL128_BLOCK_LEN 16 +#define AES_BLOCK_LEN RIJNDAEL128_BLOCK_LEN +#define CAMELLIA_BLOCK_LEN 16 +#define BLOWFISH_BLOCK_LEN 8 +#define SKIPJACK_BLOCK_LEN 8 +#define CAST128_BLOCK_LEN 8 + +/* the maximum of the above */ +#define EALG_MAX_BLOCK_LEN 16 + +/* Values for hashes/MAC */ +#define AALG_MAX_RESULT_LEN 64 + +/* maximum length of verbose alg names (depends on CRYPTO_MAX_ALG_NAME) */ +#define CRYPTODEV_MAX_ALG_NAME 64 + +#define HASH_MAX_LEN 64 + +/* input of CIOCGSESSION */ +struct session_op { + /* Specify either cipher or mac + */ + __u32 cipher; /* cryptodev_crypto_op_t */ + __u32 mac; /* cryptodev_crypto_op_t */ + + __u32 keylen; + __u8 __user *key; + __u32 mackeylen; + __u8 __user *mackey; + + __u32 ses; /* session identifier */ +}; + +struct session_info_op { + __u32 ses; /* session identifier */ + + /* verbose names for the requested ciphers */ + struct alg_info { + char cra_name[CRYPTODEV_MAX_ALG_NAME]; + char cra_driver_name[CRYPTODEV_MAX_ALG_NAME]; + } cipher_info, hash_info; + + __u16 alignmask; /* alignment constraints */ + __u32 flags; /* SIOP_FLAGS_* */ +}; + +/* If this flag is set then this algorithm uses + * a driver only available in kernel (software drivers, + * or drivers based on instruction sets do not set this flag). + * + * If multiple algorithms are involved (as in AEAD case), then + * if one of them is kernel-driver-only this flag will be set. + */ +#define SIOP_FLAG_KERNEL_DRIVER_ONLY 1 + +#define COP_ENCRYPT 0 +#define COP_DECRYPT 1 + +/* input of CIOCCRYPT */ +struct crypt_op { + __u32 ses; /* session identifier */ + __u16 op; /* COP_ENCRYPT or COP_DECRYPT */ + __u16 flags; /* see COP_FLAG_* */ + __u32 len; /* length of source data */ + __u8 __user *src; /* source data */ + __u8 __user *dst; /* pointer to output data */ + /* pointer to output data for hash/MAC operations */ + __u8 __user *mac; + /* initialization vector for encryption operations */ + __u8 __user *iv; +}; + +/* input of CIOCAUTHCRYPT */ +struct crypt_auth_op { + __u32 ses; /* session identifier */ + __u16 op; /* COP_ENCRYPT or COP_DECRYPT */ + __u16 flags; /* see COP_FLAG_AEAD_* */ + __u32 len; /* length of source data */ + __u32 auth_len; /* length of auth data */ + __u8 __user *auth_src; /* authenticated-only data */ + + /* The current implementation is more efficient if data are + * encrypted in-place (src==dst). */ + __u8 __user *src; /* data to be encrypted and authenticated */ + __u8 __user *dst; /* pointer to output data. Must have + * space for tag. For TLS this should be at least + * len + tag_size + block_size for padding */ + + __u8 __user *tag; /* where the tag will be copied to. TLS mode + * doesn't use that as tag is copied to dst. + * SRTP mode copies tag there. */ + __u32 tag_len; /* the length of the tag. Use zero for digest size or max tag. */ + + /* initialization vector for encryption operations */ + __u8 __user *iv; + __u32 iv_len; +}; + +/* In plain AEAD mode the following are required: + * flags : 0 + * iv : the initialization vector (12 bytes) + * auth_len: the length of the data to be authenticated + * auth_src: the data to be authenticated + * len : length of data to be encrypted + * src : the data to be encrypted + * dst : space to hold encrypted data. It must have + * at least a size of len + tag_size. + * tag_size: the size of the desired authentication tag or zero to use + * the maximum tag output. + * + * Note tag isn't being used because the Linux AEAD interface + * copies the tag just after data. + */ + +/* In TLS mode (used for CBC ciphers that required padding) + * the following are required: + * flags : COP_FLAG_AEAD_TLS_TYPE + * iv : the initialization vector + * auth_len: the length of the data to be authenticated only + * len : length of data to be encrypted + * auth_src: the data to be authenticated + * src : the data to be encrypted + * dst : space to hold encrypted data (preferably in-place). It must have + * at least a size of len + tag_size + blocksize. + * tag_size: the size of the desired authentication tag or zero to use + * the default mac output. + * + * Note that the padding used is the minimum padding. + */ + +/* In SRTP mode the following are required: + * flags : COP_FLAG_AEAD_SRTP_TYPE + * iv : the initialization vector + * auth_len: the length of the data to be authenticated. This must + * include the SRTP header + SRTP payload (data to be encrypted) + rest + * + * len : length of data to be encrypted + * auth_src: pointer the data to be authenticated. Should point at the same buffer as src. + * src : pointer to the data to be encrypted. + * dst : This is mandatory to be the same as src (in-place only). + * tag_size: the size of the desired authentication tag or zero to use + * the default mac output. + * tag : Pointer to an address where the authentication tag will be copied. + */ + + +/* struct crypt_op flags */ + +#define COP_FLAG_NONE (0 << 0) /* totally no flag */ +#define COP_FLAG_UPDATE (1 << 0) /* multi-update hash mode */ +#define COP_FLAG_FINAL (1 << 1) /* multi-update final hash mode */ +#define COP_FLAG_WRITE_IV (1 << 2) /* update the IV during operation */ +#define COP_FLAG_NO_ZC (1 << 3) /* do not zero-copy */ +#define COP_FLAG_AEAD_TLS_TYPE (1 << 4) /* authenticate and encrypt using the + * TLS protocol rules */ +#define COP_FLAG_AEAD_SRTP_TYPE (1 << 5) /* authenticate and encrypt using the + * SRTP protocol rules */ +#define COP_FLAG_RESET (1 << 6) /* multi-update reset the state. + * should be used in combination + * with COP_FLAG_UPDATE */ + + +/* Stuff for bignum arithmetic and public key + * cryptography - not supported yet by linux + * cryptodev. + */ + +#define CRYPTO_ALG_FLAG_SUPPORTED 1 +#define CRYPTO_ALG_FLAG_RNG_ENABLE 2 +#define CRYPTO_ALG_FLAG_DSA_SHA 4 + +struct crparam { + __u8 *crp_p; + __u32 crp_nbits; +}; + +#define CRK_MAXPARAM 8 + +/* input of CIOCKEY */ +struct crypt_kop { + __u32 crk_op; /* cryptodev_crk_ot_t */ + __u32 crk_status; + __u16 crk_iparams; + __u16 crk_oparams; + __u32 crk_pad1; + struct crparam crk_param[CRK_MAXPARAM]; +}; + +enum cryptodev_crk_op_t { + CRK_MOD_EXP = 0, + CRK_MOD_EXP_CRT = 1, + CRK_DSA_SIGN = 2, + CRK_DSA_VERIFY = 3, + CRK_DH_COMPUTE_KEY = 4, + CRK_ALGORITHM_ALL +}; + +#define CRK_ALGORITHM_MAX (CRK_ALGORITHM_ALL-1) + +/* features to be queried with CIOCASYMFEAT ioctl + */ +#define CRF_MOD_EXP (1 << CRK_MOD_EXP) +#define CRF_MOD_EXP_CRT (1 << CRK_MOD_EXP_CRT) +#define CRF_DSA_SIGN (1 << CRK_DSA_SIGN) +#define CRF_DSA_VERIFY (1 << CRK_DSA_VERIFY) +#define CRF_DH_COMPUTE_KEY (1 << CRK_DH_COMPUTE_KEY) + + +/* ioctl's. Compatible with old linux cryptodev.h + */ +#define CRIOGET _IOWR('c', 101, __u32) +#define CIOCGSESSION _IOWR('c', 102, struct session_op) +#define CIOCFSESSION _IOW('c', 103, __u32) +#define CIOCCRYPT _IOWR('c', 104, struct crypt_op) +#define CIOCKEY _IOWR('c', 105, struct crypt_kop) +#define CIOCASYMFEAT _IOR('c', 106, __u32) +#define CIOCGSESSINFO _IOWR('c', 107, struct session_info_op) + +/* to indicate that CRIOGET is not required in linux + */ +#define CRIOGET_NOT_NEEDED 1 + +/* additional ioctls for AEAD */ +#define CIOCAUTHCRYPT _IOWR('c', 109, struct crypt_auth_op) + +/* additional ioctls for asynchronous operation. + * These are conditionally enabled since version 1.6. + */ +#define CIOCASYNCCRYPT _IOW('c', 110, struct crypt_op) +#define CIOCASYNCFETCH _IOR('c', 111, struct crypt_op) + +#endif /* L_CRYPTODEV_H */ diff --git a/include/linux/Kbuild b/include/linux/Kbuild index f2f73f9b..4bf41009 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -183,6 +183,8 @@ header-y += if_plip.h header-y += if_ppp.h header-y += if_pppol2tp.h header-y += if_pppox.h +header-y += if_pppolac.h +header-y += if_pppopns.h header-y += if_slip.h header-y += if_strip.h header-y += if_team.h @@ -375,6 +377,7 @@ header-y += tty.h header-y += types.h header-y += udf_fs_i.h header-y += udp.h +header-y += uhid.h header-y += uinput.h header-y += uio.h header-y += ultrasound.h diff --git a/include/linux/akm8975.h b/include/linux/akm8975.h new file mode 100644 index 00000000..6a7c4326 --- /dev/null +++ b/include/linux/akm8975.h @@ -0,0 +1,87 @@ +/* + * Definitions for akm8975 compass chip. + */ +#ifndef AKM8975_H +#define AKM8975_H + +#include + +/*! \name AK8975 operation mode + \anchor AK8975_Mode + Defines an operation mode of the AK8975.*/ +/*! @{*/ +#define AK8975_MODE_SNG_MEASURE 0x01 +#define AK8975_MODE_SELF_TEST 0x08 +#define AK8975_MODE_FUSE_ACCESS 0x0F +#define AK8975_MODE_POWER_DOWN 0x00 +/*! @}*/ + +#define RBUFF_SIZE 8 /* Rx buffer size */ + +/*! \name AK8975 register address +\anchor AK8975_REG +Defines a register address of the AK8975.*/ +/*! @{*/ +#define AK8975_REG_WIA 0x00 +#define AK8975_REG_INFO 0x01 +#define AK8975_REG_ST1 0x02 +#define AK8975_REG_HXL 0x03 +#define AK8975_REG_HXH 0x04 +#define AK8975_REG_HYL 0x05 +#define AK8975_REG_HYH 0x06 +#define AK8975_REG_HZL 0x07 +#define AK8975_REG_HZH 0x08 +#define AK8975_REG_ST2 0x09 +#define AK8975_REG_CNTL 0x0A +#define AK8975_REG_RSV 0x0B +#define AK8975_REG_ASTC 0x0C +#define AK8975_REG_TS1 0x0D +#define AK8975_REG_TS2 0x0E +#define AK8975_REG_I2CDIS 0x0F +/*! @}*/ + +/*! \name AK8975 fuse-rom address +\anchor AK8975_FUSE +Defines a read-only address of the fuse ROM of the AK8975.*/ +/*! @{*/ +#define AK8975_FUSE_ASAX 0x10 +#define AK8975_FUSE_ASAY 0x11 +#define AK8975_FUSE_ASAZ 0x12 +/*! @}*/ + +#define AKMIO 0xA1 + +/* IOCTLs for AKM library */ +#define ECS_IOCTL_WRITE _IOW(AKMIO, 0x02, char[5]) +#define ECS_IOCTL_READ _IOWR(AKMIO, 0x03, char[5]) +#define ECS_IOCTL_GETDATA _IOR(AKMIO, 0x08, char[RBUFF_SIZE]) +#define ECS_IOCTL_SET_YPR _IOW(AKMIO, 0x0C, short[12]) +#define ECS_IOCTL_GET_OPEN_STATUS _IOR(AKMIO, 0x0D, int) +#define ECS_IOCTL_GET_CLOSE_STATUS _IOR(AKMIO, 0x0E, int) +#define ECS_IOCTL_GET_DELAY _IOR(AKMIO, 0x30, short) + +/* IOCTLs for APPs */ +#define ECS_IOCTL_APP_SET_MFLAG _IOW(AKMIO, 0x11, short) +#define ECS_IOCTL_APP_GET_MFLAG _IOW(AKMIO, 0x12, short) +#define ECS_IOCTL_APP_SET_AFLAG _IOW(AKMIO, 0x13, short) +#define ECS_IOCTL_APP_GET_AFLAG _IOR(AKMIO, 0x14, short) +#define ECS_IOCTL_APP_SET_DELAY _IOW(AKMIO, 0x18, short) +#define ECS_IOCTL_APP_GET_DELAY ECS_IOCTL_GET_DELAY +/* Set raw magnetic vector flag */ +#define ECS_IOCTL_APP_SET_MVFLAG _IOW(AKMIO, 0x19, short) +/* Get raw magnetic vector flag */ +#define ECS_IOCTL_APP_GET_MVFLAG _IOR(AKMIO, 0x1A, short) +#define ECS_IOCTL_APP_SET_TFLAG _IOR(AKMIO, 0x15, short) + + +struct akm8975_platform_data { + int intr; + + int (*init)(void); + void (*exit)(void); + int (*power_on)(void); + int (*power_off)(void); +}; + +#endif + diff --git a/include/linux/akuio_driver.h b/include/linux/akuio_driver.h new file mode 100644 index 00000000..880bf83d --- /dev/null +++ b/include/linux/akuio_driver.h @@ -0,0 +1,41 @@ +/** + * @filename include/linux/akuio_driver.h + * @brief This file provide the ioctl definitions of akuio driver for hw codec. + * Copyright (C) 2011 Anyka(Guangzhou) Microelectronics Technology CO.,LTD. + * @author Jacky Lau + * @date 2011-07-05 + * @version 0.1 + * @ref Please refer to uio_ak98_vcodec.c + */ + +#ifndef _AKUIO_DRIVER_H +#define _AKUIO_DRIVER_H + +/* struct used by AKUIO_SYSREG_WRITE */ +struct akuio_sysreg_write_t +{ + unsigned int paddr; + unsigned int val; + unsigned int mask; +}; + +#define DBG(fmt...) //printk(fmt) + +/* write system register */ +#define AKUIO_SYSREG_WRITE _IOW('U', 100, struct akuio_sysreg_write_t) + +/* wait for a interrupt occur */ +#define AKUIO_WAIT_IRQ _IOR('U', 101, int) + +/* invalidate the l2 cache in ak98 */ +#define AKUIO_INVALIDATE_L2CACHE _IOR('U', 102, int) + +/* incalidate the l1 cache in ak98 */ +#define AKUIO_INVALIDATE_L1CACHE _IOR('U', 103, int) + +#define AKUIO_UNBLOCK_CHANNEL _IOR('U', 104, int) + +#define AKUIO_VIDEO_RESET _IOR('U', 105, int) + + +#endif diff --git a/include/linux/alarmtimer.h b/include/linux/alarmtimer.h index 975009e1..96c5c249 100644 --- a/include/linux/alarmtimer.h +++ b/include/linux/alarmtimer.h @@ -76,4 +76,7 @@ static inline int alarmtimer_callback_running(struct alarm *timer) } +/* Provide way to access the rtc device being used by alarmtimers */ +struct rtc_device *alarmtimer_get_rtcdev(void); + #endif diff --git a/include/linux/amba/mmci.h b/include/linux/amba/mmci.h index 32a89cf5..9d1d9caf 100644 --- a/include/linux/amba/mmci.h +++ b/include/linux/amba/mmci.h @@ -5,6 +5,15 @@ #define AMBA_MMCI_H #include +#include +#include + +struct embedded_sdio_data { + struct sdio_cis cis; + struct sdio_cccr cccr; + struct sdio_embedded_func *funcs; + int num_funcs; +}; /* @@ -73,6 +82,9 @@ struct mmci_platform_data { bool (*dma_filter)(struct dma_chan *chan, void *filter_param); void *dma_rx_param; void *dma_tx_param; + unsigned int status_irq; + struct embedded_sdio_data *embedded_sdio; + int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id); }; #endif diff --git a/include/linux/android_aid.h b/include/linux/android_aid.h new file mode 100644 index 00000000..06264b8b --- /dev/null +++ b/include/linux/android_aid.h @@ -0,0 +1,28 @@ +/* include/linux/android_aid.h + * + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _LINUX_ANDROID_AID_H +#define _LINUX_ANDROID_AID_H + +/* AIDs that the kernel treats differently */ +#define AID_OBSOLETE_000 3001 /* was NET_BT_ADMIN */ +#define AID_OBSOLETE_001 3002 /* was NET_BT */ +#define AID_INET 3003 +#define AID_NET_RAW 3004 +#define AID_NET_ADMIN 3005 +#define AID_NET_BW_STATS 3006 /* read bandwidth statistics */ +#define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */ + +#endif diff --git a/include/linux/capability.h b/include/linux/capability.h index 12d52ded..c398cff3 100644 --- a/include/linux/capability.h +++ b/include/linux/capability.h @@ -360,8 +360,11 @@ struct cpu_vfs_cap_data { #define CAP_WAKE_ALARM 35 +/* Allow preventing system suspends while epoll events are pending */ -#define CAP_LAST_CAP CAP_WAKE_ALARM +#define CAP_EPOLLWAKEUP 36 + +#define CAP_LAST_CAP CAP_EPOLLWAKEUP #define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP) diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 5a85b341..b2a37357 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -84,12 +84,6 @@ enum { CSS_REMOVED, /* This CSS is dead */ }; -/* Caller must verify that the css is not for root cgroup */ -static inline void __css_get(struct cgroup_subsys_state *css, int count) -{ - atomic_add(count, &css->refcnt); -} - /* * Call css_get() to hold a reference on the css; it can be used * for a reference obtained via: @@ -97,6 +91,7 @@ static inline void __css_get(struct cgroup_subsys_state *css, int count) * - task->cgroups for a locked task */ +extern void __css_get(struct cgroup_subsys_state *css, int count); static inline void css_get(struct cgroup_subsys_state *css) { /* We don't need to reference count the root state */ @@ -143,10 +138,7 @@ static inline void css_put(struct cgroup_subsys_state *css) enum { /* Control Group is dead */ CGRP_REMOVED, - /* - * Control Group has previously had a child cgroup or a task, - * but no longer (only if CGRP_NOTIFY_ON_RELEASE is set) - */ + /* Control Group has ever had a child cgroup or a task */ CGRP_RELEASABLE, /* Control Group requires release notifications to userspace */ CGRP_NOTIFY_ON_RELEASE, @@ -255,6 +247,7 @@ struct css_set { /* For RCU-protected deletion */ struct rcu_head rcu_head; + struct work_struct work; }; /* @@ -455,6 +448,7 @@ struct cgroup_subsys { struct cgroup_subsys_state *(*create)(struct cgroup *cgrp); int (*pre_destroy)(struct cgroup *cgrp); void (*destroy)(struct cgroup *cgrp); + int (*allow_attach)(struct cgroup *cgrp, struct cgroup_taskset *tset); int (*can_attach)(struct cgroup *cgrp, struct cgroup_taskset *tset); void (*cancel_attach)(struct cgroup *cgrp, struct cgroup_taskset *tset); void (*attach)(struct cgroup *cgrp, struct cgroup_taskset *tset); diff --git a/include/linux/cpu.h b/include/linux/cpu.h index 78ed62f3..caec85be 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -213,4 +213,11 @@ static inline int disable_nonboot_cpus(void) { return 0; } static inline void enable_nonboot_cpus(void) {} #endif /* !CONFIG_PM_SLEEP_SMP */ +#define IDLE_START 1 +#define IDLE_END 2 + +void idle_notifier_register(struct notifier_block *n); +void idle_notifier_unregister(struct notifier_block *n); +void idle_notifier_call_chain(unsigned long val); + #endif /* _LINUX_CPU_H_ */ diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index b60f6ba0..d1c3bb0e 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -364,6 +364,9 @@ extern struct cpufreq_governor cpufreq_gov_ondemand; #elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE) extern struct cpufreq_governor cpufreq_gov_conservative; #define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_conservative) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE) +extern struct cpufreq_governor cpufreq_gov_interactive; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_interactive) #endif diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 6c26a3da..5ab71833 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -57,6 +57,7 @@ struct cpuidle_state { /* Idle State Flags */ #define CPUIDLE_FLAG_TIME_VALID (0x01) /* is residency time measurable? */ +#define CPUIDLE_FLAG_COUPLED (0x02) /* state applies to multiple cpus */ #define CPUIDLE_DRIVER_FLAGS_MASK (0xFFFF0000) @@ -100,6 +101,12 @@ struct cpuidle_device { struct list_head device_list; struct kobject kobj; struct completion kobj_unregister; + +#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED + int safe_state_index; + cpumask_t coupled_cpus; + struct cpuidle_coupled *coupled; +#endif }; DECLARE_PER_CPU(struct cpuidle_device *, cpuidle_devices); @@ -176,6 +183,10 @@ static inline int cpuidle_play_dead(void) {return -ENODEV; } #endif +#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED +void cpuidle_coupled_parallel_barrier(struct cpuidle_device *dev, atomic_t *a); +#endif + /****************************** * CPUIDLE GOVERNOR INTERFACE * ******************************/ diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index 3efbfc21..1f78d159 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -61,6 +61,10 @@ struct dma_buf_attachment; * This Callback must not sleep. * @kmap: maps a page from the buffer into kernel address space. * @kunmap: [optional] unmaps a page from the buffer. + * @mmap: used to expose the backing storage to userspace. Note that the + * mapping needs to be coherent - if the exporter doesn't directly + * support this, it needs to fake coherency by shooting down any ptes + * when transitioning away from the cpu domain. */ struct dma_buf_ops { int (*attach)(struct dma_buf *, struct device *, @@ -92,6 +96,8 @@ struct dma_buf_ops { void (*kunmap_atomic)(struct dma_buf *, unsigned long, void *); void *(*kmap)(struct dma_buf *, unsigned long); void (*kunmap)(struct dma_buf *, unsigned long, void *); + + int (*mmap)(struct dma_buf *, struct vm_area_struct *vma); }; /** @@ -167,6 +173,9 @@ void *dma_buf_kmap_atomic(struct dma_buf *, unsigned long); void dma_buf_kunmap_atomic(struct dma_buf *, unsigned long, void *); void *dma_buf_kmap(struct dma_buf *, unsigned long); void dma_buf_kunmap(struct dma_buf *, unsigned long, void *); + +int dma_buf_mmap(struct dma_buf *, struct vm_area_struct *, + unsigned long); #else static inline struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf, @@ -248,6 +257,13 @@ static inline void dma_buf_kunmap(struct dma_buf *dmabuf, unsigned long pnum, void *vaddr) { } + +static inline int dma_buf_mmap(struct dma_buf *dmabuf, + struct vm_area_struct *vma, + unsigned long pgoff) +{ + return -ENODEV; +} #endif /* CONFIG_DMA_SHARED_BUFFER */ #endif /* __DMA_BUF_H__ */ diff --git a/include/linux/eventpoll.h b/include/linux/eventpoll.h index 657ab55b..6f8be328 100644 --- a/include/linux/eventpoll.h +++ b/include/linux/eventpoll.h @@ -26,6 +26,18 @@ #define EPOLL_CTL_DEL 2 #define EPOLL_CTL_MOD 3 +/* + * Request the handling of system wakeup events so as to prevent system suspends + * from happening while those events are being processed. + * + * Assuming neither EPOLLET nor EPOLLONESHOT is set, system suspends will not be + * re-allowed until epoll_wait is called again after consuming the wakeup + * event(s). + * + * Requires CAP_EPOLLWAKEUP + */ +#define EPOLLWAKEUP (1 << 29) + /* Set the One Shot behaviour for the target file descriptor */ #define EPOLLONESHOT (1 << 30) diff --git a/include/linux/freezer.h b/include/linux/freezer.h index ee899329..b79e4d87 100644 --- a/include/linux/freezer.h +++ b/include/linux/freezer.h @@ -41,6 +41,17 @@ extern int freeze_kernel_threads(void); extern void thaw_processes(void); extern void thaw_kernel_threads(void); +/* + * HACK: prevent sleeping while atomic warnings due to ARM signal handling + * disabling irqs + */ +static inline bool try_to_freeze_nowarn(void) +{ + if (likely(!freezing(current))) + return false; + return __refrigerator(false); +} + static inline bool try_to_freeze(void) { might_sleep(); diff --git a/include/linux/gpio_event.h b/include/linux/gpio_event.h new file mode 100644 index 00000000..2613fc5e --- /dev/null +++ b/include/linux/gpio_event.h @@ -0,0 +1,170 @@ +/* include/linux/gpio_event.h + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _LINUX_GPIO_EVENT_H +#define _LINUX_GPIO_EVENT_H + +#include + +struct gpio_event_input_devs { + int count; + struct input_dev *dev[]; +}; +enum { + GPIO_EVENT_FUNC_UNINIT = 0x0, + GPIO_EVENT_FUNC_INIT = 0x1, + GPIO_EVENT_FUNC_SUSPEND = 0x2, + GPIO_EVENT_FUNC_RESUME = 0x3, +}; +struct gpio_event_info { + int (*func)(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, + void **data, int func); + int (*event)(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, + void **data, unsigned int dev, unsigned int type, + unsigned int code, int value); /* out events */ + bool no_suspend; +}; + +struct gpio_event_platform_data { + const char *name; + struct gpio_event_info **info; + size_t info_count; + int (*power)(const struct gpio_event_platform_data *pdata, bool on); + const char *names[]; /* If name is NULL, names contain a NULL */ + /* terminated list of input devices to create */ +}; + +#define GPIO_EVENT_DEV_NAME "gpio-event" + +/* Key matrix */ + +enum gpio_event_matrix_flags { + /* unset: drive active output low, set: drive active output high */ + GPIOKPF_ACTIVE_HIGH = 1U << 0, + GPIOKPF_DEBOUNCE = 1U << 1, + GPIOKPF_REMOVE_SOME_PHANTOM_KEYS = 1U << 2, + GPIOKPF_REMOVE_PHANTOM_KEYS = GPIOKPF_REMOVE_SOME_PHANTOM_KEYS | + GPIOKPF_DEBOUNCE, + GPIOKPF_DRIVE_INACTIVE = 1U << 3, + GPIOKPF_LEVEL_TRIGGERED_IRQ = 1U << 4, + GPIOKPF_PRINT_UNMAPPED_KEYS = 1U << 16, + GPIOKPF_PRINT_MAPPED_KEYS = 1U << 17, + GPIOKPF_PRINT_PHANTOM_KEYS = 1U << 18, +}; + +#define MATRIX_CODE_BITS (10) +#define MATRIX_KEY_MASK ((1U << MATRIX_CODE_BITS) - 1) +#define MATRIX_KEY(dev, code) \ + (((dev) << MATRIX_CODE_BITS) | (code & MATRIX_KEY_MASK)) + +extern int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func); +struct gpio_event_matrix_info { + /* initialize to gpio_event_matrix_func */ + struct gpio_event_info info; + /* size must be ninputs * noutputs */ + const unsigned short *keymap; + unsigned int *input_gpios; + unsigned int *output_gpios; + unsigned int ninputs; + unsigned int noutputs; + /* time to wait before reading inputs after driving each output */ + ktime_t settle_time; + /* time to wait before scanning the keypad a second time */ + ktime_t debounce_delay; + ktime_t poll_time; + unsigned flags; +}; + +/* Directly connected inputs and outputs */ + +enum gpio_event_direct_flags { + GPIOEDF_ACTIVE_HIGH = 1U << 0, +/* GPIOEDF_USE_DOWN_IRQ = 1U << 1, */ +/* GPIOEDF_USE_IRQ = (1U << 2) | GPIOIDF_USE_DOWN_IRQ, */ + GPIOEDF_PRINT_KEYS = 1U << 8, + GPIOEDF_PRINT_KEY_DEBOUNCE = 1U << 9, + GPIOEDF_PRINT_KEY_UNSTABLE = 1U << 10, +}; + +struct gpio_event_direct_entry { + uint32_t gpio:16; + uint32_t code:10; + uint32_t dev:6; +}; + +/* inputs */ +extern int gpio_event_input_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func); +struct gpio_event_input_info { + /* initialize to gpio_event_input_func */ + struct gpio_event_info info; + ktime_t debounce_time; + ktime_t poll_time; + uint16_t flags; + uint16_t type; + const struct gpio_event_direct_entry *keymap; + size_t keymap_size; +}; + +/* outputs */ +extern int gpio_event_output_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func); +extern int gpio_event_output_event(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, + unsigned int dev, unsigned int type, + unsigned int code, int value); +struct gpio_event_output_info { + /* initialize to gpio_event_output_func and gpio_event_output_event */ + struct gpio_event_info info; + uint16_t flags; + uint16_t type; + const struct gpio_event_direct_entry *keymap; + size_t keymap_size; +}; + + +/* axes */ + +enum gpio_event_axis_flags { + GPIOEAF_PRINT_UNKNOWN_DIRECTION = 1U << 16, + GPIOEAF_PRINT_RAW = 1U << 17, + GPIOEAF_PRINT_EVENT = 1U << 18, +}; + +extern int gpio_event_axis_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func); +struct gpio_event_axis_info { + /* initialize to gpio_event_axis_func */ + struct gpio_event_info info; + uint8_t count; /* number of gpios for this axis */ + uint8_t dev; /* device index when using multiple input devices */ + uint8_t type; /* EV_REL or EV_ABS */ + uint16_t code; + uint16_t decoded_size; + uint16_t (*map)(struct gpio_event_axis_info *info, uint16_t in); + uint32_t *gpio; + uint32_t flags; +}; +#define gpio_axis_2bit_gray_map gpio_axis_4bit_gray_map +#define gpio_axis_3bit_gray_map gpio_axis_4bit_gray_map +uint16_t gpio_axis_4bit_gray_map( + struct gpio_event_axis_info *info, uint16_t in); +uint16_t gpio_axis_5bit_singletrack_map( + struct gpio_event_axis_info *info, uint16_t in); + +#endif diff --git a/include/linux/hid.h b/include/linux/hid.h index 3a95da60..773b4acf 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -618,6 +618,8 @@ struct hid_usage_id { * @input_mapping: invoked on input registering before mapping an usage * @input_mapped: invoked on input registering after mapping an usage * @feature_mapping: invoked on feature registering + * @input_register: called just before input device is registered after reports + * are parsed. * @suspend: invoked on suspend (NULL means nop) * @resume: invoked on resume if device was not reset (NULL means nop) * @reset_resume: invoked on resume if device was reset (NULL means nop) @@ -664,6 +666,8 @@ struct hid_driver { void (*feature_mapping)(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage); + int (*input_register)(struct hid_device *hdev, struct hid_input + *hidinput); #ifdef CONFIG_PM int (*suspend)(struct hid_device *hdev, pm_message_t message); int (*resume)(struct hid_device *hdev); diff --git a/include/linux/if_pppolac.h b/include/linux/if_pppolac.h new file mode 100644 index 00000000..c06bd6c8 --- /dev/null +++ b/include/linux/if_pppolac.h @@ -0,0 +1,33 @@ +/* include/linux/if_pppolac.h + * + * Header for PPP on L2TP Access Concentrator / PPPoLAC Socket (RFC 2661) + * + * Copyright (C) 2009 Google, Inc. + * Author: Chia-chi Yeh + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __LINUX_IF_PPPOLAC_H +#define __LINUX_IF_PPPOLAC_H + +#include +#include + +struct sockaddr_pppolac { + sa_family_t sa_family; /* AF_PPPOX */ + unsigned int sa_protocol; /* PX_PROTO_OLAC */ + int udp_socket; + struct __attribute__((packed)) { + __u16 tunnel, session; + } local, remote; +} __attribute__((packed)); + +#endif /* __LINUX_IF_PPPOLAC_H */ diff --git a/include/linux/if_pppopns.h b/include/linux/if_pppopns.h new file mode 100644 index 00000000..0cf34b4d --- /dev/null +++ b/include/linux/if_pppopns.h @@ -0,0 +1,32 @@ +/* include/linux/if_pppopns.h + * + * Header for PPP on PPTP Network Server / PPPoPNS Socket (RFC 2637) + * + * Copyright (C) 2009 Google, Inc. + * Author: Chia-chi Yeh + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __LINUX_IF_PPPOPNS_H +#define __LINUX_IF_PPPOPNS_H + +#include +#include + +struct sockaddr_pppopns { + sa_family_t sa_family; /* AF_PPPOX */ + unsigned int sa_protocol; /* PX_PROTO_OPNS */ + int tcp_socket; + __u16 local; + __u16 remote; +} __attribute__((packed)); + +#endif /* __LINUX_IF_PPPOPNS_H */ diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h index b5f927f5..1597b8bc 100644 --- a/include/linux/if_pppox.h +++ b/include/linux/if_pppox.h @@ -28,6 +28,8 @@ #include #endif /* __KERNEL__ */ #include +#include +#include /* For user-space programs to pick up these definitions * which they wouldn't get otherwise without defining __KERNEL__ @@ -61,7 +63,9 @@ struct pptp_addr { #define PX_PROTO_OE 0 /* Currently just PPPoE */ #define PX_PROTO_OL2TP 1 /* Now L2TP also */ #define PX_PROTO_PPTP 2 -#define PX_MAX_PROTO 3 +#define PX_PROTO_OLAC 3 +#define PX_PROTO_OPNS 4 +#define PX_MAX_PROTO 5 struct sockaddr_pppox { __kernel_sa_family_t sa_family; /* address family, AF_PPPOX */ @@ -168,6 +172,25 @@ struct pptp_opt { u32 seq_sent, seq_recv; int ppp_flags; }; + +struct pppolac_opt { + __u32 local; + __u32 remote; + __u32 recv_sequence; + __u32 xmit_sequence; + atomic_t sequencing; + int (*backlog_rcv)(struct sock *sk_udp, struct sk_buff *skb); +}; + +struct pppopns_opt { + __u16 local; + __u16 remote; + __u32 recv_sequence; + __u32 xmit_sequence; + void (*data_ready)(struct sock *sk_raw, int length); + int (*backlog_rcv)(struct sock *sk_raw, struct sk_buff *skb); +}; + #include struct pppox_sock { @@ -178,6 +201,8 @@ struct pppox_sock { union { struct pppoe_opt pppoe; struct pptp_opt pptp; + struct pppolac_opt lac; + struct pppopns_opt pns; } proto; __be16 num; }; diff --git a/include/linux/input.h b/include/linux/input.h index a8167145..49fb20e3 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -154,6 +154,9 @@ struct input_keymap_entry { #define EVIOCGRAB _IOW('E', 0x90, int) /* Grab/Release device */ +#define EVIOCGSUSPENDBLOCK _IOR('E', 0x91, int) /* get suspend block enable */ +#define EVIOCSSUSPENDBLOCK _IOW('E', 0x91, int) /* set suspend block enable */ + #define EVIOCSCLOCKID _IOW('E', 0xa0, int) /* Set clockid to be used for timestamps */ /* diff --git a/include/linux/input/akmatrix_keypad.h b/include/linux/input/akmatrix_keypad.h new file mode 100644 index 00000000..49dae8d3 --- /dev/null +++ b/include/linux/input/akmatrix_keypad.h @@ -0,0 +1,105 @@ +#ifndef _MATRIX_KEYPAD_H +#define _MATRIX_KEYPAD_H + +#include +#include +#include + +#define MATRIX_MAX_ROWS 16 +#define MATRIX_MAX_COLS 16 + +#define KEY(row, col, val) ((((row) & (MATRIX_MAX_ROWS - 1)) << 24) |\ + (((col) & (MATRIX_MAX_COLS - 1)) << 16) |\ + (val & 0xffff)) + +#define KEY_ROW(k) (((k) >> 24) & 0xff) +#define KEY_COL(k) (((k) >> 16) & 0xff) +#define KEY_VAL(k) ((k) & 0xffff) + +#define MATRIX_SCAN_CODE(row, col, row_shift) (((row) << (row_shift)) + (col)) + +/** + * struct matrix_keymap_data - keymap for matrix keyboards + * @keymap: pointer to array of uint32 values encoded with KEY() macro + * representing keymap + * @keymap_size: number of entries (initialized) in this keymap + * + * This structure is supposed to be used by platform code to supply + * keymaps to drivers that implement matrix-like keypads/keyboards. + */ +struct matrix_keymap_data { + const uint32_t *keymap; + unsigned int keymap_size; +}; + +/** + * struct matrix_keypad_platform_data - platform-dependent keypad data + * @keymap_data: pointer to &matrix_keymap_data + * @row_gpios: pointer to array of gpio numbers representing rows + * @col_gpios: pointer to array of gpio numbers reporesenting colums + * @num_row_gpios: actual number of row gpios used by device + * @num_col_gpios: actual number of col gpios used by device + * @col_scan_delay_us: delay, measured in microseconds, that is + * needed before we can keypad after activating column gpio + * @debounce_ms: debounce interval in milliseconds + * + * This structure represents platform-specific data that use used by + * matrix_keypad driver to perform proper initialization. + */ +struct matrix_keypad_platform_data { + const struct matrix_keymap_data *keymap_data; + + const unsigned int *row_gpios; + const unsigned int *col_gpios; + + unsigned int num_row_gpios; + unsigned int num_col_gpios; + + unsigned int col_scan_delay_us; + + /* key debounce interval in milli-second */ + unsigned int debounce_ms; + + bool active_low; + bool wakeup; + + /* add for anyka */ + struct gpio_info row_gpios_cfginfo; + struct gpio_info col_gpios_cfginfo; + /* true: one column selecting line is grounded */ + bool grounding; +}; + +/** + * matrix_keypad_build_keymap - convert platform keymap into matrix keymap + * @keymap_data: keymap supplied by the platform code + * @row_shift: number of bits to shift row value by to advance to the next + * line in the keymap + * @keymap: expanded version of keymap that is suitable for use by + * matrix keyboad driver + * @keybit: pointer to bitmap of keys supported by input device + * + * This function converts platform keymap (encoded with KEY() macro) into + * an array of keycodes that is suitable for using in a standard matrix + * keyboard driver that uses row and col as indices. + */ +static inline void +matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data, + unsigned int row_shift, + unsigned short *keymap, unsigned long *keybit) +{ + int i; + + for (i = 0; i < keymap_data->keymap_size; i++) { + unsigned int key = keymap_data->keymap[i]; + unsigned int row = KEY_ROW(key); + unsigned int col = KEY_COL(key); + unsigned short code = KEY_VAL(key); + + keymap[MATRIX_SCAN_CODE(row, col, row_shift)] = code; + __set_bit(code, keybit); + } + __clear_bit(KEY_RESERVED, keybit); +} + +#endif /* _MATRIX_KEYPAD_H */ diff --git a/include/linux/ion.h b/include/linux/ion.h new file mode 100644 index 00000000..8414a6d9 --- /dev/null +++ b/include/linux/ion.h @@ -0,0 +1,373 @@ +/* + * include/linux/ion.h + * + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _LINUX_ION_H +#define _LINUX_ION_H + +#include + +struct ion_handle; +/** + * enum ion_heap_types - list of all possible types of heaps + * @ION_HEAP_TYPE_SYSTEM: memory allocated via vmalloc + * @ION_HEAP_TYPE_SYSTEM_CONTIG: memory allocated via kmalloc + * @ION_HEAP_TYPE_CARVEOUT: memory allocated from a prereserved + * carveout heap, allocations are physically + * contiguous + * @ION_NUM_HEAPS: helper for iterating over heaps, a bit mask + * is used to identify the heaps, so only 32 + * total heap types are supported + */ +enum ion_heap_type { + ION_HEAP_TYPE_SYSTEM, + ION_HEAP_TYPE_SYSTEM_CONTIG, + ION_HEAP_TYPE_CARVEOUT, + ION_HEAP_TYPE_CHUNK, + ION_HEAP_TYPE_CUSTOM, /* must be last so device specific heaps always + are at the end of this enum */ + ION_NUM_HEAPS = 16, +}; + +#define ION_HEAP_SYSTEM_MASK (1 << ION_HEAP_TYPE_SYSTEM) +#define ION_HEAP_SYSTEM_CONTIG_MASK (1 << ION_HEAP_TYPE_SYSTEM_CONTIG) +#define ION_HEAP_CARVEOUT_MASK (1 << ION_HEAP_TYPE_CARVEOUT) + +#define ION_NUM_HEAP_IDS sizeof(unsigned int) * 8 + +/** + * allocation flags - the lower 16 bits are used by core ion, the upper 16 + * bits are reserved for use by the heaps themselves. + */ +#define ION_FLAG_CACHED 1 /* mappings of this buffer should be + cached, ion will do cache + maintenance when the buffer is + mapped for dma */ +#define ION_FLAG_CACHED_NEEDS_SYNC 2 /* mappings of this buffer will created + at mmap time, if this is set + caches must be managed manually */ + +#ifdef __KERNEL__ +struct ion_device; +struct ion_heap; +struct ion_mapper; +struct ion_client; +struct ion_buffer; + +/* This should be removed some day when phys_addr_t's are fully + plumbed in the kernel, and all instances of ion_phys_addr_t should + be converted to phys_addr_t. For the time being many kernel interfaces + do not accept phys_addr_t's that would have to */ +#define ion_phys_addr_t unsigned long + +/** + * struct ion_platform_heap - defines a heap in the given platform + * @type: type of the heap from ion_heap_type enum + * @id: unique identifier for heap. When allocating higher numbers + * will be allocated from first. At allocation these are passed + * as a bit mask and therefore can not exceed ION_NUM_HEAP_IDS. + * @name: used for debug purposes + * @base: base address of heap in physical memory if applicable + * @size: size of the heap in bytes if applicable + * @align: required alignment in physical memory if applicable + * @priv: private info passed from the board file + * + * Provided by the board file. + */ +struct ion_platform_heap { + enum ion_heap_type type; + unsigned int id; + const char *name; + ion_phys_addr_t base; + size_t size; + ion_phys_addr_t align; + void *priv; +}; + +/** + * struct ion_platform_data - array of platform heaps passed from board file + * @nr: number of structures in the array + * @heaps: array of platform_heap structions + * + * Provided by the board file in the form of platform data to a platform device. + */ +struct ion_platform_data { + int nr; + struct ion_platform_heap heaps[]; +}; + +/** + * ion_reserve() - reserve memory for ion heaps if applicable + * @data: platform data specifying starting physical address and + * size + * + * Calls memblock reserve to set aside memory for heaps that are + * located at specific memory addresses or of specfic sizes not + * managed by the kernel + */ +void ion_reserve(struct ion_platform_data *data); + +/** + * ion_client_create() - allocate a client and returns it + * @dev: the global ion device + * @heap_type_mask: mask of heaps this client can allocate from + * @name: used for debugging + */ +struct ion_client *ion_client_create(struct ion_device *dev, + const char *name); + +/** + * ion_client_destroy() - free's a client and all it's handles + * @client: the client + * + * Free the provided client and all it's resources including + * any handles it is holding. + */ +void ion_client_destroy(struct ion_client *client); + +/** + * ion_alloc - allocate ion memory + * @client: the client + * @len: size of the allocation + * @align: requested allocation alignment, lots of hardware blocks + * have alignment requirements of some kind + * @heap_id_mask: mask of heaps to allocate from, if multiple bits are set + * heaps will be tried in order from highest to lowest + * id + * @flags: heap flags, the low 16 bits are consumed by ion, the + * high 16 bits are passed on to the respective heap and + * can be heap custom + * + * Allocate memory in one of the heaps provided in heap mask and return + * an opaque handle to it. + */ +struct ion_handle *ion_alloc(struct ion_client *client, size_t len, + size_t align, unsigned int heap_id_mask, + unsigned int flags); + +/** + * ion_free - free a handle + * @client: the client + * @handle: the handle to free + * + * Free the provided handle. + */ +void ion_free(struct ion_client *client, struct ion_handle *handle); + +/** + * ion_phys - returns the physical address and len of a handle + * @client: the client + * @handle: the handle + * @addr: a pointer to put the address in + * @len: a pointer to put the length in + * + * This function queries the heap for a particular handle to get the + * handle's physical address. It't output is only correct if + * a heap returns physically contiguous memory -- in other cases + * this api should not be implemented -- ion_sg_table should be used + * instead. Returns -EINVAL if the handle is invalid. This has + * no implications on the reference counting of the handle -- + * the returned value may not be valid if the caller is not + * holding a reference. + */ +int ion_phys(struct ion_client *client, struct ion_handle *handle, + ion_phys_addr_t *addr, size_t *len); + +/** + * ion_map_dma - return an sg_table describing a handle + * @client: the client + * @handle: the handle + * + * This function returns the sg_table describing + * a particular ion handle. + */ +struct sg_table *ion_sg_table(struct ion_client *client, + struct ion_handle *handle); + +/** + * ion_map_kernel - create mapping for the given handle + * @client: the client + * @handle: handle to map + * + * Map the given handle into the kernel and return a kernel address that + * can be used to access this address. + */ +void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle); + +/** + * ion_unmap_kernel() - destroy a kernel mapping for a handle + * @client: the client + * @handle: handle to unmap + */ +void ion_unmap_kernel(struct ion_client *client, struct ion_handle *handle); + +/** + * ion_share_dma_buf() - share buffer as dma-buf + * @client: the client + * @handle: the handle + */ +struct dma_buf *ion_share_dma_buf(struct ion_client *client, + struct ion_handle *handle); + +/** + * ion_share_dma_buf_fd() - given an ion client, create a dma-buf fd + * @client: the client + * @handle: the handle + */ +int ion_share_dma_buf_fd(struct ion_client *client, struct ion_handle *handle); + +/** + * ion_import_dma_buf() - given an dma-buf fd from the ion exporter get handle + * @client: the client + * @fd: the dma-buf fd + * + * Given an dma-buf fd that was allocated through ion via ion_share_dma_buf, + * import that fd and return a handle representing it. If a dma-buf from + * another exporter is passed in this function will return ERR_PTR(-EINVAL) + */ +struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd); + +#endif /* __KERNEL__ */ + +/** + * DOC: Ion Userspace API + * + * create a client by opening /dev/ion + * most operations handled via following ioctls + * + */ + +/** + * struct ion_allocation_data - metadata passed from userspace for allocations + * @len: size of the allocation + * @align: required alignment of the allocation + * @heap_id_mask: mask of heap ids to allocate from + * @flags: flags passed to heap + * @handle: pointer that will be populated with a cookie to use to + * refer to this allocation + * + * Provided by userspace as an argument to the ioctl + */ +struct ion_allocation_data { + size_t len; + size_t align; + unsigned int heap_id_mask; + unsigned int flags; + struct ion_handle *handle; +}; + +/** + * struct ion_fd_data - metadata passed to/from userspace for a handle/fd pair + * @handle: a handle + * @fd: a file descriptor representing that handle + * + * For ION_IOC_SHARE or ION_IOC_MAP userspace populates the handle field with + * the handle returned from ion alloc, and the kernel returns the file + * descriptor to share or map in the fd field. For ION_IOC_IMPORT, userspace + * provides the file descriptor and the kernel returns the handle. + */ +struct ion_fd_data { + struct ion_handle *handle; + int fd; +}; + +/** + * struct ion_handle_data - a handle passed to/from the kernel + * @handle: a handle + */ +struct ion_handle_data { + struct ion_handle *handle; +}; + +/** + * struct ion_custom_data - metadata passed to/from userspace for a custom ioctl + * @cmd: the custom ioctl function to call + * @arg: additional data to pass to the custom ioctl, typically a user + * pointer to a predefined structure + * + * This works just like the regular cmd and arg fields of an ioctl. + */ +struct ion_custom_data { + unsigned int cmd; + unsigned long arg; +}; + +#define ION_IOC_MAGIC 'I' + +/** + * DOC: ION_IOC_ALLOC - allocate memory + * + * Takes an ion_allocation_data struct and returns it with the handle field + * populated with the opaque handle for the allocation. + */ +#define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \ + struct ion_allocation_data) + +/** + * DOC: ION_IOC_FREE - free memory + * + * Takes an ion_handle_data struct and frees the handle. + */ +#define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data) + +/** + * DOC: ION_IOC_MAP - get a file descriptor to mmap + * + * Takes an ion_fd_data struct with the handle field populated with a valid + * opaque handle. Returns the struct with the fd field set to a file + * descriptor open in the current address space. This file descriptor + * can then be used as an argument to mmap. + */ +#define ION_IOC_MAP _IOWR(ION_IOC_MAGIC, 2, struct ion_fd_data) + +/** + * DOC: ION_IOC_SHARE - creates a file descriptor to use to share an allocation + * + * Takes an ion_fd_data struct with the handle field populated with a valid + * opaque handle. Returns the struct with the fd field set to a file + * descriptor open in the current address space. This file descriptor + * can then be passed to another process. The corresponding opaque handle can + * be retrieved via ION_IOC_IMPORT. + */ +#define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data) + +/** + * DOC: ION_IOC_IMPORT - imports a shared file descriptor + * + * Takes an ion_fd_data struct with the fd field populated with a valid file + * descriptor obtained from ION_IOC_SHARE and returns the struct with the handle + * filed set to the corresponding opaque handle. + */ +#define ION_IOC_IMPORT _IOWR(ION_IOC_MAGIC, 5, struct ion_fd_data) + +/** + * DOC: ION_IOC_SYNC - syncs a shared file descriptors to memory + * + * Deprecated in favor of using the dma_buf api's correctly (syncing + * will happend automatically when the buffer is mapped to a device). + * If necessary should be used after touching a cached buffer from the cpu, + * this will make the buffer in memory coherent. + */ +#define ION_IOC_SYNC _IOWR(ION_IOC_MAGIC, 7, struct ion_fd_data) + +/** + * DOC: ION_IOC_CUSTOM - call architecture specific ion ioctl + * + * Takes the argument of the architecture specific ioctl to call and + * passes appropriate userdata for that ioctl + */ +#define ION_IOC_CUSTOM _IOWR(ION_IOC_MAGIC, 6, struct ion_custom_data) + +#endif /* _LINUX_ION_H */ diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 645231c3..747404ab 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -705,6 +705,9 @@ static inline void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) { } extern int do_sysinfo(struct sysinfo *info); +/* To identify board information in panic logs, set this */ +extern char *mach_panic_string; + #endif /* __KERNEL__ */ #endif diff --git a/include/linux/keychord.h b/include/linux/keychord.h new file mode 100644 index 00000000..856a5850 --- /dev/null +++ b/include/linux/keychord.h @@ -0,0 +1,52 @@ +/* + * Key chord input driver + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * +*/ + +#ifndef __LINUX_KEYCHORD_H_ +#define __LINUX_KEYCHORD_H_ + +#include + +#define KEYCHORD_VERSION 1 + +/* + * One or more input_keychord structs are written to /dev/keychord + * at once to specify the list of keychords to monitor. + * Reading /dev/keychord returns the id of a keychord when the + * keychord combination is pressed. A keychord is signalled when + * all of the keys in the keycode list are in the pressed state. + * The order in which the keys are pressed does not matter. + * The keychord will not be signalled if keys not in the keycode + * list are pressed. + * Keychords will not be signalled on key release events. + */ +struct input_keychord { + /* should be KEYCHORD_VERSION */ + __u16 version; + /* + * client specified ID, returned from read() + * when this keychord is pressed. + */ + __u16 id; + + /* number of keycodes in this keychord */ + __u16 count; + + /* variable length array of keycodes */ + __u16 keycodes[]; +}; + +#endif /* __LINUX_KEYCHORD_H_ */ diff --git a/include/linux/keyreset.h b/include/linux/keyreset.h new file mode 100644 index 00000000..a2ac49e5 --- /dev/null +++ b/include/linux/keyreset.h @@ -0,0 +1,28 @@ +/* + * include/linux/keyreset.h - platform data structure for resetkeys driver + * + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _LINUX_KEYRESET_H +#define _LINUX_KEYRESET_H + +#define KEYRESET_NAME "keyreset" + +struct keyreset_platform_data { + int (*reset_fn)(void); + int *keys_up; + int keys_down[]; /* 0 terminated */ +}; + +#endif /* _LINUX_KEYRESET_H */ diff --git a/include/linux/mm.h b/include/linux/mm.h index 441a5641..01ebcc59 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -873,6 +873,7 @@ extern bool skip_free_areas_node(unsigned int flags, int nid); int shmem_lock(struct file *file, int lock, struct user_struct *user); struct file *shmem_file_setup(const char *name, loff_t size, unsigned long flags); +void shmem_set_file(struct vm_area_struct *vma, struct file *file); int shmem_zero_setup(struct vm_area_struct *); extern int can_do_mlock(void); diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 1b431c72..884c08bb 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -103,6 +103,7 @@ struct mmc_data { unsigned int timeout_clks; /* data timeout (in clocks) */ unsigned int blksz; /* data block size */ unsigned int blocks; /* number of blocks */ + unsigned int retries; /* max number of retries */ unsigned int error; /* data error */ unsigned int flags; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 0707d228..5674504e 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -289,12 +290,17 @@ struct mmc_host { int claim_cnt; /* "claim" nesting count */ struct delayed_work detect; + struct wake_lock detect_wake_lock; int detect_change; /* card detect flag */ struct mmc_hotplug hotplug; const struct mmc_bus_ops *bus_ops; /* current bus driver */ unsigned int bus_refs; /* reference counter */ + unsigned int bus_resume_flags; +#define MMC_BUSRESUME_MANUAL_RESUME (1 << 0) +#define MMC_BUSRESUME_NEEDS_RESUME (1 << 1) + unsigned int sdio_irqs; struct task_struct *sdio_irq_thread; bool sdio_irq_pending; @@ -320,6 +326,15 @@ struct mmc_host { unsigned int actual_clock; /* Actual HC clock rate */ +#ifdef CONFIG_MMC_EMBEDDED_SDIO + struct { + struct sdio_cis *cis; + struct sdio_cccr *cccr; + struct sdio_embedded_func *funcs; + int num_funcs; + } embedded_sdio_data; +#endif + unsigned long private[0] ____cacheline_aligned; }; @@ -328,6 +343,14 @@ extern int mmc_add_host(struct mmc_host *); extern void mmc_remove_host(struct mmc_host *); extern void mmc_free_host(struct mmc_host *); +#ifdef CONFIG_MMC_EMBEDDED_SDIO +extern void mmc_set_embedded_sdio_data(struct mmc_host *host, + struct sdio_cis *cis, + struct sdio_cccr *cccr, + struct sdio_embedded_func *funcs, + int num_funcs); +#endif + static inline void *mmc_priv(struct mmc_host *host) { return (void *)host->private; @@ -338,6 +361,18 @@ static inline void *mmc_priv(struct mmc_host *host) #define mmc_dev(x) ((x)->parent) #define mmc_classdev(x) (&(x)->class_dev) #define mmc_hostname(x) (dev_name(&(x)->class_dev)) +#define mmc_bus_needs_resume(host) ((host)->bus_resume_flags & MMC_BUSRESUME_NEEDS_RESUME) +#define mmc_bus_manual_resume(host) ((host)->bus_resume_flags & MMC_BUSRESUME_MANUAL_RESUME) + +static inline void mmc_set_bus_resume_policy(struct mmc_host *host, int manual) +{ + if (manual) + host->bus_resume_flags |= MMC_BUSRESUME_MANUAL_RESUME; + else + host->bus_resume_flags &= ~MMC_BUSRESUME_MANUAL_RESUME; +} + +extern int mmc_resume_bus(struct mmc_host *host); extern int mmc_suspend_host(struct mmc_host *); extern int mmc_resume_host(struct mmc_host *); diff --git a/include/linux/mmc/pm.h b/include/linux/mmc/pm.h index 4a139204..6e2d6a13 100644 --- a/include/linux/mmc/pm.h +++ b/include/linux/mmc/pm.h @@ -26,5 +26,6 @@ typedef unsigned int mmc_pm_flag_t; #define MMC_PM_KEEP_POWER (1 << 0) /* preserve card power during suspend */ #define MMC_PM_WAKE_SDIO_IRQ (1 << 1) /* wake up host system on SDIO IRQ assertion */ +#define MMC_PM_IGNORE_PM_NOTIFY (1 << 2) /* ignore mmc pm notify */ #endif /* LINUX_MMC_PM_H */ diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h index 50f0bc95..dc680c4b 100644 --- a/include/linux/mmc/sdio_func.h +++ b/include/linux/mmc/sdio_func.h @@ -22,6 +22,14 @@ struct sdio_func; typedef void (sdio_irq_handler_t)(struct sdio_func *); +/* + * Structure used to hold embedded SDIO device data from platform layer + */ +struct sdio_embedded_func { + uint8_t f_class; + uint32_t f_maxblksize; +}; + /* * SDIO function CIS tuple (unknown to the core) */ @@ -130,6 +138,8 @@ extern int sdio_release_irq(struct sdio_func *func); extern unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz); extern u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret); +extern u8 sdio_readb_ext(struct sdio_func *func, unsigned int addr, int *err_ret, + unsigned in); extern u16 sdio_readw(struct sdio_func *func, unsigned int addr, int *err_ret); extern u32 sdio_readl(struct sdio_func *func, unsigned int addr, int *err_ret); diff --git a/include/linux/msdos_fs.h b/include/linux/msdos_fs.h index 34066e65..f38d4f0a 100644 --- a/include/linux/msdos_fs.h +++ b/include/linux/msdos_fs.h @@ -101,6 +101,7 @@ struct __fat_dirent { /* has used 0x72 ('r') in collision, so skip a few */ #define FAT_IOCTL_GET_ATTRIBUTES _IOR('r', 0x10, __u32) #define FAT_IOCTL_SET_ATTRIBUTES _IOW('r', 0x11, __u32) +#define VFAT_IOCTL_GET_VOLUME_ID _IOR('r', 0x12, __u32) struct fat_boot_sector { __u8 ignored[3]; /* Boot strap short or near jump */ @@ -138,6 +139,17 @@ struct fat_boot_fsinfo { __le32 reserved2[4]; }; +struct fat_boot_bsx { + __u8 drive; /* drive number */ + __u8 reserved1; + __u8 signature; /* extended boot signature */ + __u8 vol_id[4]; /* volume ID */ + __u8 vol_label[11]; /* volume label */ + __u8 type[8]; /* file system type */ +}; +#define FAT16_BSX_OFFSET 36 /* offset of fat_boot_bsx in FAT12 and FAT16 */ +#define FAT32_BSX_OFFSET 64 /* offset of fat_boot_bsx in FAT32 */ + struct msdos_dir_entry { __u8 name[MSDOS_NAME];/* name and extension */ __u8 attr; /* attribute bits */ diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index cf5ea8cd..adc86d3b 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -67,6 +67,22 @@ struct mtd_erase_region_info { unsigned long *lockmap; /* If keeping bitmap of locks */ }; +/* + * oob operation modes + * + * MTD_OOB_PLACE: oob data are placed at the given offset + * MTD_OOB_AUTO: oob data are automatically placed at the free areas + * which are defined by the ecclayout + * MTD_OOB_RAW: mode to read raw data+oob in one chunk. The oob data + * is inserted into the data. Thats a raw image of the + * flash contents. + */ +typedef enum { + MTD_OOB_PLACE, + MTD_OOB_AUTO, + MTD_OOB_RAW, +} mtd_oob_mode_t; + /** * struct mtd_oob_ops - oob operation operands * @mode: operation mode @@ -224,6 +240,9 @@ struct mtd_info { int (*_get_device) (struct mtd_info *mtd); void (*_put_device) (struct mtd_info *mtd); + /*Get device chip ID.*/ + int (*get_device_id) (struct mtd_info *mtd); + /* Backing device capabilities for this device * - provides mmap capabilities */ diff --git a/include/linux/netfilter/xt_CONNMARK.h b/include/linux/netfilter/xt_CONNMARK.h index 2f2e48ec..efc17a83 100644 --- a/include/linux/netfilter/xt_CONNMARK.h +++ b/include/linux/netfilter/xt_CONNMARK.h @@ -1,6 +1,31 @@ -#ifndef _XT_CONNMARK_H_target -#define _XT_CONNMARK_H_target +#ifndef _XT_CONNMARK_H +#define _XT_CONNMARK_H -#include +#include -#endif /*_XT_CONNMARK_H_target*/ +/* Copyright (C) 2002,2004 MARA Systems AB + * by Henrik Nordstrom + * + * 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. + */ + +enum { + XT_CONNMARK_SET = 0, + XT_CONNMARK_SAVE, + XT_CONNMARK_RESTORE +}; + +struct xt_connmark_tginfo1 { + __u32 ctmark, ctmask, nfmask; + __u8 mode; +}; + +struct xt_connmark_mtinfo1 { + __u32 mark, mask; + __u8 invert; +}; + +#endif /*_XT_CONNMARK_H*/ diff --git a/include/linux/netfilter/xt_IDLETIMER.h b/include/linux/netfilter/xt_IDLETIMER.h index 208ae938..faaa28b3 100644 --- a/include/linux/netfilter/xt_IDLETIMER.h +++ b/include/linux/netfilter/xt_IDLETIMER.h @@ -4,6 +4,7 @@ * Header file for Xtables timer target module. * * Copyright (C) 2004, 2010 Nokia Corporation + * * Written by Timo Teras * * Converted to x_tables and forward-ported to 2.6.34 @@ -32,12 +33,19 @@ #include #define MAX_IDLETIMER_LABEL_SIZE 28 +#define NLMSG_MAX_SIZE 64 + +#define NL_EVENT_TYPE_INACTIVE 0 +#define NL_EVENT_TYPE_ACTIVE 1 struct idletimer_tg_info { __u32 timeout; char label[MAX_IDLETIMER_LABEL_SIZE]; + /* Use netlink messages for notification in addition to sysfs */ + __u8 send_nl_msg; + /* for kernel module internal use only */ struct idletimer_tg *timer __attribute__((aligned(8))); }; diff --git a/include/linux/netfilter/xt_MARK.h b/include/linux/netfilter/xt_MARK.h index 41c456de..ecadc40d 100644 --- a/include/linux/netfilter/xt_MARK.h +++ b/include/linux/netfilter/xt_MARK.h @@ -1,6 +1,15 @@ -#ifndef _XT_MARK_H_target -#define _XT_MARK_H_target +#ifndef _XT_MARK_H +#define _XT_MARK_H -#include +#include -#endif /*_XT_MARK_H_target */ +struct xt_mark_tginfo2 { + __u32 mark, mask; +}; + +struct xt_mark_mtinfo1 { + __u32 mark, mask; + __u8 invert; +}; + +#endif /*_XT_MARK_H*/ diff --git a/include/linux/netfilter/xt_dscp.h b/include/linux/netfilter/xt_dscp.h index 15f8932a..648e0b3b 100644 --- a/include/linux/netfilter/xt_dscp.h +++ b/include/linux/netfilter/xt_dscp.h @@ -1,31 +1,26 @@ -/* x_tables module for matching the IPv4/IPv6 DSCP field +/* x_tables module for setting the IPv4/IPv6 DSCP field * * (C) 2002 Harald Welte + * based on ipt_FTOS.c (C) 2000 by Matthew G. Marsh * This software is distributed under GNU GPL v2, 1991 * * See RFC2474 for a description of the DSCP field within the IP Header. * - * xt_dscp.h,v 1.3 2002/08/05 19:00:21 laforge Exp + * xt_DSCP.h,v 1.7 2002/03/14 12:03:13 laforge Exp */ -#ifndef _XT_DSCP_H -#define _XT_DSCP_H - +#ifndef _XT_DSCP_TARGET_H +#define _XT_DSCP_TARGET_H +#include #include -#define XT_DSCP_MASK 0xfc /* 11111100 */ -#define XT_DSCP_SHIFT 2 -#define XT_DSCP_MAX 0x3f /* 00111111 */ - -/* match info */ -struct xt_dscp_info { +/* target info */ +struct xt_DSCP_info { __u8 dscp; - __u8 invert; }; -struct xt_tos_match_info { - __u8 tos_mask; +struct xt_tos_target_info { __u8 tos_value; - __u8 invert; + __u8 tos_mask; }; -#endif /* _XT_DSCP_H */ +#endif /* _XT_DSCP_TARGET_H */ diff --git a/include/linux/netfilter/xt_qtaguid.h b/include/linux/netfilter/xt_qtaguid.h new file mode 100644 index 00000000..ca60fbde --- /dev/null +++ b/include/linux/netfilter/xt_qtaguid.h @@ -0,0 +1,13 @@ +#ifndef _XT_QTAGUID_MATCH_H +#define _XT_QTAGUID_MATCH_H + +/* For now we just replace the xt_owner. + * FIXME: make iptables aware of qtaguid. */ +#include + +#define XT_QTAGUID_UID XT_OWNER_UID +#define XT_QTAGUID_GID XT_OWNER_GID +#define XT_QTAGUID_SOCKET XT_OWNER_SOCKET +#define xt_qtaguid_match_info xt_owner_match_info + +#endif /* _XT_QTAGUID_MATCH_H */ diff --git a/include/linux/netfilter/xt_quota2.h b/include/linux/netfilter/xt_quota2.h new file mode 100644 index 00000000..eadc6903 --- /dev/null +++ b/include/linux/netfilter/xt_quota2.h @@ -0,0 +1,25 @@ +#ifndef _XT_QUOTA_H +#define _XT_QUOTA_H + +enum xt_quota_flags { + XT_QUOTA_INVERT = 1 << 0, + XT_QUOTA_GROW = 1 << 1, + XT_QUOTA_PACKET = 1 << 2, + XT_QUOTA_NO_CHANGE = 1 << 3, + XT_QUOTA_MASK = 0x0F, +}; + +struct xt_quota_counter; + +struct xt_quota_mtinfo2 { + char name[15]; + u_int8_t flags; + + /* Comparison-invariant */ + aligned_u64 quota; + + /* Used internally by the kernel */ + struct xt_quota_counter *master __attribute__((aligned(8))); +}; + +#endif /* _XT_QUOTA_H */ diff --git a/include/linux/netfilter/xt_rateest.h b/include/linux/netfilter/xt_rateest.h index d40a6196..6605e20a 100644 --- a/include/linux/netfilter/xt_rateest.h +++ b/include/linux/netfilter/xt_rateest.h @@ -1,37 +1,15 @@ -#ifndef _XT_RATEEST_MATCH_H -#define _XT_RATEEST_MATCH_H +#ifndef _XT_RATEEST_TARGET_H +#define _XT_RATEEST_TARGET_H #include -enum xt_rateest_match_flags { - XT_RATEEST_MATCH_INVERT = 1<<0, - XT_RATEEST_MATCH_ABS = 1<<1, - XT_RATEEST_MATCH_REL = 1<<2, - XT_RATEEST_MATCH_DELTA = 1<<3, - XT_RATEEST_MATCH_BPS = 1<<4, - XT_RATEEST_MATCH_PPS = 1<<5, -}; - -enum xt_rateest_match_mode { - XT_RATEEST_MATCH_NONE, - XT_RATEEST_MATCH_EQ, - XT_RATEEST_MATCH_LT, - XT_RATEEST_MATCH_GT, -}; - -struct xt_rateest_match_info { - char name1[IFNAMSIZ]; - char name2[IFNAMSIZ]; - __u16 flags; - __u16 mode; - __u32 bps1; - __u32 pps1; - __u32 bps2; - __u32 pps2; +struct xt_rateest_target_info { + char name[IFNAMSIZ]; + __s8 interval; + __u8 ewma_log; /* Used internally by the kernel */ - struct xt_rateest *est1 __attribute__((aligned(8))); - struct xt_rateest *est2 __attribute__((aligned(8))); + struct xt_rateest *est __attribute__((aligned(8))); }; -#endif /* _XT_RATEEST_MATCH_H */ +#endif /* _XT_RATEEST_TARGET_H */ diff --git a/include/linux/netfilter/xt_socket.h b/include/linux/netfilter/xt_socket.h index 26d7217b..63594564 100644 --- a/include/linux/netfilter/xt_socket.h +++ b/include/linux/netfilter/xt_socket.h @@ -11,4 +11,10 @@ struct xt_socket_mtinfo1 { __u8 flags; }; +void xt_socket_put_sk(struct sock *sk); +struct sock *xt_socket_get4_sk(const struct sk_buff *skb, + struct xt_action_param *par); +struct sock *xt_socket_get6_sk(const struct sk_buff *skb, + struct xt_action_param *par); + #endif /* _XT_SOCKET_H */ diff --git a/include/linux/netfilter/xt_tcpmss.h b/include/linux/netfilter/xt_tcpmss.h index fbac56b9..9a6960af 100644 --- a/include/linux/netfilter/xt_tcpmss.h +++ b/include/linux/netfilter/xt_tcpmss.h @@ -1,11 +1,12 @@ -#ifndef _XT_TCPMSS_MATCH_H -#define _XT_TCPMSS_MATCH_H +#ifndef _XT_TCPMSS_H +#define _XT_TCPMSS_H #include -struct xt_tcpmss_match_info { - __u16 mss_min, mss_max; - __u8 invert; +struct xt_tcpmss_info { + __u16 mss; }; -#endif /*_XT_TCPMSS_MATCH_H*/ +#define XT_TCPMSS_CLAMP_PMTU 0xffff + +#endif /* _XT_TCPMSS_H */ diff --git a/include/linux/netfilter_ipv4/ipt_ECN.h b/include/linux/netfilter_ipv4/ipt_ECN.h index bb88d531..0e0c063d 100644 --- a/include/linux/netfilter_ipv4/ipt_ECN.h +++ b/include/linux/netfilter_ipv4/ipt_ECN.h @@ -1,33 +1,15 @@ -/* Header file for iptables ipt_ECN target - * - * (C) 2002 by Harald Welte - * - * This software is distributed under GNU GPL v2, 1991 - * - * ipt_ECN.h,v 1.3 2002/05/29 12:17:40 laforge Exp -*/ -#ifndef _IPT_ECN_TARGET_H -#define _IPT_ECN_TARGET_H - -#include -#include - -#define IPT_ECN_IP_MASK (~XT_DSCP_MASK) - -#define IPT_ECN_OP_SET_IP 0x01 /* set ECN bits of IPv4 header */ -#define IPT_ECN_OP_SET_ECE 0x10 /* set ECE bit of TCP header */ -#define IPT_ECN_OP_SET_CWR 0x20 /* set CWR bit of TCP header */ - -#define IPT_ECN_OP_MASK 0xce - -struct ipt_ECN_info { - __u8 operation; /* bitset of operations */ - __u8 ip_ect; /* ECT codepoint of IPv4 header, pre-shifted */ - union { - struct { - __u8 ece:1, cwr:1; /* TCP ECT bits */ - } tcp; - } proto; +#ifndef _IPT_ECN_H +#define _IPT_ECN_H + +#include +#define ipt_ecn_info xt_ecn_info + +enum { + IPT_ECN_IP_MASK = XT_ECN_IP_MASK, + IPT_ECN_OP_MATCH_IP = XT_ECN_OP_MATCH_IP, + IPT_ECN_OP_MATCH_ECE = XT_ECN_OP_MATCH_ECE, + IPT_ECN_OP_MATCH_CWR = XT_ECN_OP_MATCH_CWR, + IPT_ECN_OP_MATCH_MASK = XT_ECN_OP_MATCH_MASK, }; -#endif /* _IPT_ECN_TARGET_H */ +#endif /* IPT_ECN_H */ diff --git a/include/linux/netfilter_ipv4/ipt_ttl.h b/include/linux/netfilter_ipv4/ipt_ttl.h index 37bee444..f6ac169d 100644 --- a/include/linux/netfilter_ipv4/ipt_ttl.h +++ b/include/linux/netfilter_ipv4/ipt_ttl.h @@ -1,5 +1,5 @@ -/* IP tables module for matching the value of the TTL - * (C) 2000 by Harald Welte */ +/* TTL modification module for IP tables + * (C) 2000 by Harald Welte */ #ifndef _IPT_TTL_H #define _IPT_TTL_H @@ -7,14 +7,14 @@ #include enum { - IPT_TTL_EQ = 0, /* equals */ - IPT_TTL_NE, /* not equals */ - IPT_TTL_LT, /* less than */ - IPT_TTL_GT, /* greater than */ + IPT_TTL_SET = 0, + IPT_TTL_INC, + IPT_TTL_DEC }; +#define IPT_TTL_MAXMODE IPT_TTL_DEC -struct ipt_ttl_info { +struct ipt_TTL_info { __u8 mode; __u8 ttl; }; diff --git a/include/linux/netfilter_ipv6/ip6t_hl.h b/include/linux/netfilter_ipv6/ip6t_hl.h index 6e76dbc6..ebd8ead1 100644 --- a/include/linux/netfilter_ipv6/ip6t_hl.h +++ b/include/linux/netfilter_ipv6/ip6t_hl.h @@ -1,6 +1,6 @@ -/* ip6tables module for matching the Hop Limit value +/* Hop Limit modification module for ip6tables * Maciej Soltysiak - * Based on HW's ttl module */ + * Based on HW's TTL module */ #ifndef _IP6T_HL_H #define _IP6T_HL_H @@ -8,14 +8,14 @@ #include enum { - IP6T_HL_EQ = 0, /* equals */ - IP6T_HL_NE, /* not equals */ - IP6T_HL_LT, /* less than */ - IP6T_HL_GT, /* greater than */ + IP6T_HL_SET = 0, + IP6T_HL_INC, + IP6T_HL_DEC }; +#define IP6T_HL_MAXMODE IP6T_HL_DEC -struct ip6t_hl_info { +struct ip6t_HL_info { __u8 mode; __u8 hop_limit; }; diff --git a/include/linux/nmi.h b/include/linux/nmi.h index db50840e..c8f8aa03 100644 --- a/include/linux/nmi.h +++ b/include/linux/nmi.h @@ -14,8 +14,11 @@ * may be used to reset the timeout - for code which intentionally * disables interrupts for a long time. This call is stateless. */ -#if defined(CONFIG_HAVE_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR) +#if defined(CONFIG_HAVE_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR_NMI) #include +#endif + +#if defined(CONFIG_HAVE_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR) extern void touch_nmi_watchdog(void); #else static inline void touch_nmi_watchdog(void) diff --git a/include/linux/persistent_ram.h b/include/linux/persistent_ram.h new file mode 100644 index 00000000..22422171 --- /dev/null +++ b/include/linux/persistent_ram.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __LINUX_PERSISTENT_RAM_H__ +#define __LINUX_PERSISTENT_RAM_H__ + +#include +#include +#include +#include + +struct persistent_ram_buffer; + +struct persistent_ram_descriptor { + const char *name; + phys_addr_t size; +}; + +struct persistent_ram { + phys_addr_t start; + phys_addr_t size; + + int ecc_block_size; + int ecc_size; + int ecc_symsize; + int ecc_poly; + + int num_descs; + struct persistent_ram_descriptor *descs; + + struct list_head node; +}; + +struct persistent_ram_zone { + struct list_head node; + void *vaddr; + struct persistent_ram_buffer *buffer; + size_t buffer_size; + + /* ECC correction */ + bool ecc; + char *par_buffer; + char *par_header; + struct rs_control *rs_decoder; + int corrected_bytes; + int bad_blocks; + int ecc_block_size; + int ecc_size; + int ecc_symsize; + int ecc_poly; + + char *old_log; + size_t old_log_size; + size_t old_log_footer_size; + bool early; +}; + +int persistent_ram_early_init(struct persistent_ram *ram); + +struct persistent_ram_zone *persistent_ram_init_ringbuffer(struct device *dev, + bool ecc); + +int persistent_ram_write(struct persistent_ram_zone *prz, const void *s, + unsigned int count); + +size_t persistent_ram_old_size(struct persistent_ram_zone *prz); +void *persistent_ram_old(struct persistent_ram_zone *prz); +void persistent_ram_free_old(struct persistent_ram_zone *prz); +ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz, + char *str, size_t len); + +#endif diff --git a/include/linux/platform_data/android_battery.h b/include/linux/platform_data/android_battery.h new file mode 100644 index 00000000..f6c8298f --- /dev/null +++ b/include/linux/platform_data/android_battery.h @@ -0,0 +1,47 @@ +/* + * android_battery.h + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _LINUX_ANDROID_BATTERY_H +#define _LINUX_ANDROID_BATTERY_H + +enum { + CHARGE_SOURCE_NONE = 0, + CHARGE_SOURCE_AC, + CHARGE_SOURCE_USB, +}; + +struct android_bat_callbacks { + void (*charge_source_changed) + (struct android_bat_callbacks *, int); + void (*battery_set_full)(struct android_bat_callbacks *); +}; + +struct android_bat_platform_data { + void (*register_callbacks)(struct android_bat_callbacks *); + void (*unregister_callbacks)(void); + void (*set_charging_current) (int); + void (*set_charging_enable) (int); + int (*poll_charge_source) (void); + int (*get_capacity) (void); + int (*get_temperature) (int *); + int (*get_voltage_now)(void); + int (*get_current_now)(int *); + + int temp_high_threshold; + int temp_high_recovery; + int temp_low_recovery; + int temp_low_threshold; + + unsigned long full_charging_time; + unsigned long recharging_time; + unsigned int recharging_voltage; +}; + +#endif diff --git a/include/linux/platform_data/ds2482.h b/include/linux/platform_data/ds2482.h new file mode 100644 index 00000000..5a6879e2 --- /dev/null +++ b/include/linux/platform_data/ds2482.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2012 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __PLATFORM_DATA_DS2482__ +#define __PLATFORM_DATA_DS2482__ + +struct ds2482_platform_data { + int slpz_gpio; +}; + +#endif /* __PLATFORM_DATA_DS2482__ */ diff --git a/include/linux/pm.h b/include/linux/pm.h index 715305e0..f067e60a 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -544,8 +544,6 @@ struct dev_pm_info { unsigned long active_jiffies; unsigned long suspended_jiffies; unsigned long accounting_timestamp; - ktime_t suspend_time; - s64 max_time_suspended_ns; struct dev_pm_qos_request *pq_req; #endif struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */ diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 91f82861..30f794eb 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -14,6 +14,7 @@ #include #include #include +#include enum gpd_status { GPD_STATE_ACTIVE = 0, /* PM domain is active */ @@ -70,9 +71,9 @@ struct generic_pm_domain { int (*power_on)(struct generic_pm_domain *domain); s64 power_on_latency_ns; struct gpd_dev_ops dev_ops; - s64 break_even_ns; /* Power break even for the entire domain. */ s64 max_off_time_ns; /* Maximum allowed "suspended" time. */ - ktime_t power_off_time; + bool max_off_time_changed; + bool cached_power_down_ok; struct device_node *of_node; /* Node in device tree */ }; @@ -93,13 +94,17 @@ struct gpd_timing_data { s64 start_latency_ns; s64 save_state_latency_ns; s64 restore_state_latency_ns; - s64 break_even_ns; + s64 effective_constraint_ns; + bool constraint_changed; + bool cached_stop_ok; }; struct generic_pm_domain_data { struct pm_domain_data base; struct gpd_dev_ops ops; struct gpd_timing_data td; + struct notifier_block nb; + struct mutex lock; bool need_restore; bool always_on; }; @@ -141,6 +146,7 @@ static inline int pm_genpd_of_add_device(struct device_node *genpd_node, extern int pm_genpd_remove_device(struct generic_pm_domain *genpd, struct device *dev); extern void pm_genpd_dev_always_on(struct device *dev, bool val); +extern void pm_genpd_dev_need_restore(struct device *dev, bool val); extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, struct generic_pm_domain *new_subdomain); extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, @@ -184,6 +190,7 @@ static inline int pm_genpd_remove_device(struct generic_pm_domain *genpd, return -ENOSYS; } static inline void pm_genpd_dev_always_on(struct device *dev, bool val) {} +static inline void pm_genpd_dev_need_restore(struct device *dev, bool val) {} static inline int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, struct generic_pm_domain *new_sd) { diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h index 609daae7..f271860c 100644 --- a/include/linux/pm_runtime.h +++ b/include/linux/pm_runtime.h @@ -150,9 +150,6 @@ static inline void pm_runtime_set_autosuspend_delay(struct device *dev, static inline unsigned long pm_runtime_autosuspend_expiration( struct device *dev) { return 0; } -static inline void pm_runtime_update_max_time_suspended(struct device *dev, - s64 delta_ns) {} - #endif /* !CONFIG_PM_RUNTIME */ static inline int pm_runtime_idle(struct device *dev) diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h index d9f05113..569781fa 100644 --- a/include/linux/pm_wakeup.h +++ b/include/linux/pm_wakeup.h @@ -33,12 +33,15 @@ * * @total_time: Total time this wakeup source has been active. * @max_time: Maximum time this wakeup source has been continuously active. - * @last_time: Monotonic clock when the wakeup source's was activated last time. + * @last_time: Monotonic clock when the wakeup source's was touched last time. + * @prevent_sleep_time: Total time this source has been preventing autosleep. * @event_count: Number of signaled wakeup events. * @active_count: Number of times the wakeup sorce was activated. * @relax_count: Number of times the wakeup sorce was deactivated. - * @hit_count: Number of times the wakeup sorce might abort system suspend. + * @expire_count: Number of times the wakeup source's timeout has expired. + * @wakeup_count: Number of times the wakeup source might abort suspend. * @active: Status of the wakeup source. + * @has_timeout: The wakeup source has been activated with a timeout. */ struct wakeup_source { const char *name; @@ -49,11 +52,15 @@ struct wakeup_source { ktime_t total_time; ktime_t max_time; ktime_t last_time; + ktime_t start_prevent_time; + ktime_t prevent_sleep_time; unsigned long event_count; unsigned long active_count; unsigned long relax_count; - unsigned long hit_count; - unsigned int active:1; + unsigned long expire_count; + unsigned long wakeup_count; + bool active:1; + bool autosleep_enabled:1; }; #ifdef CONFIG_PM_SLEEP diff --git a/include/linux/power/smb347-charger.h b/include/linux/power/smb347-charger.h index b3cb20da..e9aab944 100644 --- a/include/linux/power/smb347-charger.h +++ b/include/linux/power/smb347-charger.h @@ -110,8 +110,14 @@ struct smb347_charger_platform_data { bool use_mains; bool use_usb; bool use_usb_otg; + bool disable_automatic_recharge; int irq_gpio; + bool disable_stat_interrupts; enum smb347_chg_enable enable_control; + bool usb_mode_pin_ctrl; + char **supplied_to; + size_t num_supplicants; + int en_gpio; }; #endif /* SMB347_CHARGER_H */ diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index c38c13db..e1f54479 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -124,6 +124,10 @@ enum power_supply_property { POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */ POWER_SUPPLY_PROP_SCOPE, + /* Local extensions */ + POWER_SUPPLY_PROP_USB_HC, + POWER_SUPPLY_PROP_USB_OTG, + POWER_SUPPLY_PROP_CHARGE_ENABLED, /* Properties of type `const char *' */ POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MANUFACTURER, @@ -172,6 +176,8 @@ struct power_supply { /* private */ struct device *dev; struct work_struct changed_work; + spinlock_t changed_lock; + bool changed; #ifdef CONFIG_LEDS_TRIGGERS struct led_trigger *charging_full_trig; diff --git a/include/linux/sched.h b/include/linux/sched.h index 3dd0efbb..937ab61f 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1804,6 +1804,9 @@ static inline void put_task_struct(struct task_struct *t) extern void task_times(struct task_struct *p, cputime_t *ut, cputime_t *st); extern void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t *st); +extern int task_free_register(struct notifier_block *n); +extern int task_free_unregister(struct notifier_block *n); + /* * Per process flags */ diff --git a/include/linux/security.h b/include/linux/security.h index 673afbb8..b62f3969 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1381,6 +1381,11 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) struct security_operations { char name[SECURITY_NAME_MAX + 1]; + int (*binder_set_context_mgr) (struct task_struct *mgr); + int (*binder_transaction) (struct task_struct *from, struct task_struct *to); + int (*binder_transfer_binder) (struct task_struct *from, struct task_struct *to); + int (*binder_transfer_file) (struct task_struct *from, struct task_struct *to, struct file *file); + int (*ptrace_access_check) (struct task_struct *child, unsigned int mode); int (*ptrace_traceme) (struct task_struct *parent); int (*capget) (struct task_struct *target, @@ -1664,6 +1669,10 @@ extern void __init security_fixup_ops(struct security_operations *ops); /* Security operations */ +int security_binder_set_context_mgr(struct task_struct *mgr); +int security_binder_transaction(struct task_struct *from, struct task_struct *to); +int security_binder_transfer_binder(struct task_struct *from, struct task_struct *to); +int security_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file); int security_ptrace_access_check(struct task_struct *child, unsigned int mode); int security_ptrace_traceme(struct task_struct *parent); int security_capget(struct task_struct *target, @@ -1842,6 +1851,26 @@ static inline int security_init(void) return 0; } +static inline int security_binder_set_context_mgr(struct task_struct *mgr) +{ + return 0; +} + +static inline int security_binder_transaction(struct task_struct *from, struct task_struct *to) +{ + return 0; +} + +static inline int security_binder_transfer_binder(struct task_struct *from, struct task_struct *to) +{ + return 0; +} + +static inline int security_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file) +{ + return 0; +} + static inline int security_ptrace_access_check(struct task_struct *child, unsigned int mode) { diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 2db407a4..c1df3fe2 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -213,6 +213,12 @@ /* Energy Micro efm32 SoC */ #define PORT_EFMUART 100 +/* Anyka AK39xx UART */ +#define PORT_AK39 101 + +/* Anyka GPIO UART */ +#define PORT_GPIO 102 + #ifdef __KERNEL__ #include @@ -252,6 +258,7 @@ struct uart_ops { void (*pm)(struct uart_port *, unsigned int state, unsigned int oldstate); int (*set_wake)(struct uart_port *, unsigned int state); + void (*wake_peer)(struct uart_port *); /* * Return a string describing the type of the port diff --git a/include/linux/sockios.h b/include/linux/sockios.h index 7997a506..f7ffe36d 100644 --- a/include/linux/sockios.h +++ b/include/linux/sockios.h @@ -65,6 +65,7 @@ #define SIOCDIFADDR 0x8936 /* delete PA address */ #define SIOCSIFHWBROADCAST 0x8937 /* set hardware broadcast addr */ #define SIOCGIFCOUNT 0x8938 /* get number of devices */ +#define SIOCKILLADDR 0x8939 /* kill sockets with this local addr */ #define SIOCGIFBR 0x8940 /* Bridging support */ #define SIOCSIFBR 0x8941 /* Set bridging options */ diff --git a/include/linux/spi/flash.h b/include/linux/spi/flash.h index 3f22932e..7a76e33d 100644 --- a/include/linux/spi/flash.h +++ b/include/linux/spi/flash.h @@ -3,6 +3,14 @@ struct mtd_partition; + + +#define FLASH_BUS_WIDTH_1WIRE (1<<0) +#define FLASH_BUS_WIDTH_2WIRE (1<<1) +#define FLASH_BUS_WIDTH_4WIRE (1<<2) + + + /** * struct flash_platform_data: board-specific flash data * @name: optional flash device name (eg, as used with mtdparts=) @@ -25,6 +33,7 @@ struct flash_platform_data { char *type; + u8 bus_width; /* we'll likely add more ... use JEDEC IDs, etc */ }; diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index fa702aeb..10fedb05 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -72,7 +72,7 @@ struct spi_device { struct spi_master *master; u32 max_speed_hz; u8 chip_select; - u8 mode; + u16 mode; #define SPI_CPHA 0x01 /* clock phase */ #define SPI_CPOL 0x02 /* clock polarity */ #define SPI_MODE_0 (0|0) /* (original MicroWire) */ @@ -85,6 +85,7 @@ struct spi_device { #define SPI_LOOP 0x20 /* loopback mode */ #define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */ #define SPI_READY 0x80 /* slave pulls low to pause */ + u8 bits_per_word; int irq; void *controller_state; @@ -507,9 +508,15 @@ struct spi_transfer { u16 delay_usecs; u32 speed_hz; + u8 xfer_mode; +#define XFER_1DATAWIRE 0x00 +#define XFER_2DATAWIRE 0x01 +#define XFER_4DATAWIRE 0x02 + struct list_head transfer_list; }; + /** * struct spi_message - one multi-segment SPI transaction * @transfers: list of transfer segments in this transaction @@ -806,7 +813,7 @@ struct spi_board_info { /* mode becomes spi_device.mode, and is essential for chips * where the default of SPI_CS_HIGH = 0 is wrong. */ - u8 mode; + u16 mode; /* ... may need additional spi_device chip config data here. * avoid stuff protocol drivers can set; but include stuff diff --git a/include/linux/suspend.h b/include/linux/suspend.h index ac1c114c..cd83059f 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -356,8 +356,9 @@ extern int unregister_pm_notifier(struct notifier_block *nb); extern bool events_check_enabled; extern bool pm_wakeup_pending(void); -extern bool pm_get_wakeup_count(unsigned int *count); +extern bool pm_get_wakeup_count(unsigned int *count, bool block); extern bool pm_save_wakeup_count(unsigned int count); +extern void pm_wakep_autosleep_enabled(bool set); static inline void lock_system_sleep(void) { @@ -407,6 +408,17 @@ static inline void unlock_system_sleep(void) {} #endif /* !CONFIG_PM_SLEEP */ +#ifdef CONFIG_PM_AUTOSLEEP + +/* kernel/power/autosleep.c */ +void queue_up_suspend_work(void); + +#else /* !CONFIG_PM_AUTOSLEEP */ + +static inline void queue_up_suspend_work(void) {} + +#endif /* !CONFIG_PM_AUTOSLEEP */ + #ifdef CONFIG_ARCH_SAVE_PAGE_KEYS /* * The ARCH_SAVE_PAGE_KEYS functions can be used by an architecture diff --git a/include/linux/sw_sync.h b/include/linux/sw_sync.h new file mode 100644 index 00000000..bd6f2089 --- /dev/null +++ b/include/linux/sw_sync.h @@ -0,0 +1,58 @@ +/* + * include/linux/sw_sync.h + * + * Copyright (C) 2012 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _LINUX_SW_SYNC_H +#define _LINUX_SW_SYNC_H + +#include + +#ifdef __KERNEL__ + +#include + +struct sw_sync_timeline { + struct sync_timeline obj; + + u32 value; +}; + +struct sw_sync_pt { + struct sync_pt pt; + + u32 value; +}; + +struct sw_sync_timeline *sw_sync_timeline_create(const char *name); +void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc); + +struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value); + +#endif /* __KERNEL __ */ + +struct sw_sync_create_fence_data { + __u32 value; + char name[32]; + __s32 fence; /* fd of new fence */ +}; + +#define SW_SYNC_IOC_MAGIC 'W' + +#define SW_SYNC_IOC_CREATE_FENCE _IOWR(SW_SYNC_IOC_MAGIC, 0,\ + struct sw_sync_create_fence_data) +#define SW_SYNC_IOC_INC _IOW(SW_SYNC_IOC_MAGIC, 1, __u32) + + +#endif /* _LINUX_SW_SYNC_H */ diff --git a/include/linux/switch.h b/include/linux/switch.h new file mode 100644 index 00000000..3e4c748e --- /dev/null +++ b/include/linux/switch.h @@ -0,0 +1,53 @@ +/* + * Switch class driver + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * +*/ + +#ifndef __LINUX_SWITCH_H__ +#define __LINUX_SWITCH_H__ + +struct switch_dev { + const char *name; + struct device *dev; + int index; + int state; + + ssize_t (*print_name)(struct switch_dev *sdev, char *buf); + ssize_t (*print_state)(struct switch_dev *sdev, char *buf); +}; + +struct gpio_switch_platform_data { + const char *name; + unsigned gpio; + + /* if NULL, switch_dev.name will be printed */ + const char *name_on; + const char *name_off; + /* if NULL, "0" or "1" will be printed */ + const char *state_on; + const char *state_off; +}; + +extern int switch_dev_register(struct switch_dev *sdev); +extern void switch_dev_unregister(struct switch_dev *sdev); + +static inline int switch_get_state(struct switch_dev *sdev) +{ + return sdev->state; +} + +extern void switch_set_state(struct switch_dev *sdev, int state); + +#endif /* __LINUX_SWITCH_H__ */ diff --git a/include/linux/synaptics_i2c_rmi.h b/include/linux/synaptics_i2c_rmi.h new file mode 100644 index 00000000..5539cc52 --- /dev/null +++ b/include/linux/synaptics_i2c_rmi.h @@ -0,0 +1,55 @@ +/* + * include/linux/synaptics_i2c_rmi.h - platform data structure for f75375s sensor + * + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _LINUX_SYNAPTICS_I2C_RMI_H +#define _LINUX_SYNAPTICS_I2C_RMI_H + +#define SYNAPTICS_I2C_RMI_NAME "synaptics-rmi-ts" + +enum { + SYNAPTICS_FLIP_X = 1UL << 0, + SYNAPTICS_FLIP_Y = 1UL << 1, + SYNAPTICS_SWAP_XY = 1UL << 2, + SYNAPTICS_SNAP_TO_INACTIVE_EDGE = 1UL << 3, +}; + +struct synaptics_i2c_rmi_platform_data { + uint32_t version; /* Use this entry for panels with */ + /* (major << 8 | minor) version or above. */ + /* If non-zero another array entry follows */ + int (*power)(int on); /* Only valid in first array entry */ + uint32_t flags; + unsigned long irqflags; + uint32_t inactive_left; /* 0x10000 = screen width */ + uint32_t inactive_right; /* 0x10000 = screen width */ + uint32_t inactive_top; /* 0x10000 = screen height */ + uint32_t inactive_bottom; /* 0x10000 = screen height */ + uint32_t snap_left_on; /* 0x10000 = screen width */ + uint32_t snap_left_off; /* 0x10000 = screen width */ + uint32_t snap_right_on; /* 0x10000 = screen width */ + uint32_t snap_right_off; /* 0x10000 = screen width */ + uint32_t snap_top_on; /* 0x10000 = screen height */ + uint32_t snap_top_off; /* 0x10000 = screen height */ + uint32_t snap_bottom_on; /* 0x10000 = screen height */ + uint32_t snap_bottom_off; /* 0x10000 = screen height */ + uint32_t fuzz_x; /* 0x10000 = screen width */ + uint32_t fuzz_y; /* 0x10000 = screen height */ + int fuzz_p; + int fuzz_w; + int8_t sensitivity_adjust; +}; + +#endif /* _LINUX_SYNAPTICS_I2C_RMI_H */ diff --git a/include/linux/sync.h b/include/linux/sync.h new file mode 100644 index 00000000..5f493638 --- /dev/null +++ b/include/linux/sync.h @@ -0,0 +1,427 @@ +/* + * include/linux/sync.h + * + * Copyright (C) 2012 Google, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _LINUX_SYNC_H +#define _LINUX_SYNC_H + +#include +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include +#include + +struct sync_timeline; +struct sync_pt; +struct sync_fence; + +/** + * struct sync_timeline_ops - sync object implementation ops + * @driver_name: name of the implentation + * @dup: duplicate a sync_pt + * @has_signaled: returns: + * 1 if pt has signaled + * 0 if pt has not signaled + * <0 on error + * @compare: returns: + * 1 if b will signal before a + * 0 if a and b will signal at the same time + * -1 if a will signabl before b + * @free_pt: called before sync_pt is freed + * @release_obj: called before sync_timeline is freed + * @print_obj: deprecated + * @print_pt: deprecated + * @fill_driver_data: write implmentation specific driver data to data. + * should return an error if there is not enough room + * as specified by size. This information is returned + * to userspace by SYNC_IOC_FENCE_INFO. + * @timeline_value_str: fill str with the value of the sync_timeline's counter + * @pt_value_str: fill str with the value of the sync_pt + */ +struct sync_timeline_ops { + const char *driver_name; + + /* required */ + struct sync_pt *(*dup)(struct sync_pt *pt); + + /* required */ + int (*has_signaled)(struct sync_pt *pt); + + /* required */ + int (*compare)(struct sync_pt *a, struct sync_pt *b); + + /* optional */ + void (*free_pt)(struct sync_pt *sync_pt); + + /* optional */ + void (*release_obj)(struct sync_timeline *sync_timeline); + + /* deprecated */ + void (*print_obj)(struct seq_file *s, + struct sync_timeline *sync_timeline); + + /* deprecated */ + void (*print_pt)(struct seq_file *s, struct sync_pt *sync_pt); + + /* optional */ + int (*fill_driver_data)(struct sync_pt *syncpt, void *data, int size); + + /* optional */ + void (*timeline_value_str)(struct sync_timeline *timeline, char *str, + int size); + + /* optional */ + void (*pt_value_str)(struct sync_pt *pt, char *str, int size); +}; + +/** + * struct sync_timeline - sync object + * @kref: reference count on fence. + * @ops: ops that define the implementaiton of the sync_timeline + * @name: name of the sync_timeline. Useful for debugging + * @destoryed: set when sync_timeline is destroyed + * @child_list_head: list of children sync_pts for this sync_timeline + * @child_list_lock: lock protecting @child_list_head, destroyed, and + * sync_pt.status + * @active_list_head: list of active (unsignaled/errored) sync_pts + * @sync_timeline_list: membership in global sync_timeline_list + */ +struct sync_timeline { + struct kref kref; + const struct sync_timeline_ops *ops; + char name[32]; + + /* protected by child_list_lock */ + bool destroyed; + + struct list_head child_list_head; + spinlock_t child_list_lock; + + struct list_head active_list_head; + spinlock_t active_list_lock; + + struct list_head sync_timeline_list; +}; + +/** + * struct sync_pt - sync point + * @parent: sync_timeline to which this sync_pt belongs + * @child_list: membership in sync_timeline.child_list_head + * @active_list: membership in sync_timeline.active_list_head + * @signaled_list: membership in temorary signaled_list on stack + * @fence: sync_fence to which the sync_pt belongs + * @pt_list: membership in sync_fence.pt_list_head + * @status: 1: signaled, 0:active, <0: error + * @timestamp: time which sync_pt status transitioned from active to + * singaled or error. + */ +struct sync_pt { + struct sync_timeline *parent; + struct list_head child_list; + + struct list_head active_list; + struct list_head signaled_list; + + struct sync_fence *fence; + struct list_head pt_list; + + /* protected by parent->active_list_lock */ + int status; + + ktime_t timestamp; +}; + +/** + * struct sync_fence - sync fence + * @file: file representing this fence + * @kref: referenace count on fence. + * @name: name of sync_fence. Useful for debugging + * @pt_list_head: list of sync_pts in ths fence. immutable once fence + * is created + * @waiter_list_head: list of asynchronous waiters on this fence + * @waiter_list_lock: lock protecting @waiter_list_head and @status + * @status: 1: signaled, 0:active, <0: error + * + * @wq: wait queue for fence signaling + * @sync_fence_list: membership in global fence list + */ +struct sync_fence { + struct file *file; + struct kref kref; + char name[32]; + + /* this list is immutable once the fence is created */ + struct list_head pt_list_head; + + struct list_head waiter_list_head; + spinlock_t waiter_list_lock; /* also protects status */ + int status; + + wait_queue_head_t wq; + + struct list_head sync_fence_list; +}; + +struct sync_fence_waiter; +typedef void (*sync_callback_t)(struct sync_fence *fence, + struct sync_fence_waiter *waiter); + +/** + * struct sync_fence_waiter - metadata for asynchronous waiter on a fence + * @waiter_list: membership in sync_fence.waiter_list_head + * @callback: function pointer to call when fence signals + * @callback_data: pointer to pass to @callback + */ +struct sync_fence_waiter { + struct list_head waiter_list; + + sync_callback_t callback; +}; + +static inline void sync_fence_waiter_init(struct sync_fence_waiter *waiter, + sync_callback_t callback) +{ + waiter->callback = callback; +} + +/* + * API for sync_timeline implementers + */ + +/** + * sync_timeline_create() - creates a sync object + * @ops: specifies the implemention ops for the object + * @size: size to allocate for this obj + * @name: sync_timeline name + * + * Creates a new sync_timeline which will use the implemetation specified by + * @ops. @size bytes will be allocated allowing for implemntation specific + * data to be kept after the generic sync_timeline stuct. + */ +struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops, + int size, const char *name); + +/** + * sync_timeline_destory() - destorys a sync object + * @obj: sync_timeline to destroy + * + * A sync implemntation should call this when the @obj is going away + * (i.e. module unload.) @obj won't actually be freed until all its childern + * sync_pts are freed. + */ +void sync_timeline_destroy(struct sync_timeline *obj); + +/** + * sync_timeline_signal() - signal a status change on a sync_timeline + * @obj: sync_timeline to signal + * + * A sync implemntation should call this any time one of it's sync_pts + * has signaled or has an error condition. + */ +void sync_timeline_signal(struct sync_timeline *obj); + +/** + * sync_pt_create() - creates a sync pt + * @parent: sync_pt's parent sync_timeline + * @size: size to allocate for this pt + * + * Creates a new sync_pt as a chiled of @parent. @size bytes will be + * allocated allowing for implemntation specific data to be kept after + * the generic sync_timeline struct. + */ +struct sync_pt *sync_pt_create(struct sync_timeline *parent, int size); + +/** + * sync_pt_free() - frees a sync pt + * @pt: sync_pt to free + * + * This should only be called on sync_pts which have been created but + * not added to a fence. + */ +void sync_pt_free(struct sync_pt *pt); + +/** + * sync_fence_create() - creates a sync fence + * @name: name of fence to create + * @pt: sync_pt to add to the fence + * + * Creates a fence containg @pt. Once this is called, the fence takes + * ownership of @pt. + */ +struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt); + +/* + * API for sync_fence consumers + */ + +/** + * sync_fence_merge() - merge two fences + * @name: name of new fence + * @a: fence a + * @b: fence b + * + * Creates a new fence which contains copies of all the sync_pts in both + * @a and @b. @a and @b remain valid, independent fences. + */ +struct sync_fence *sync_fence_merge(const char *name, + struct sync_fence *a, struct sync_fence *b); + +/** + * sync_fence_fdget() - get a fence from an fd + * @fd: fd referencing a fence + * + * Ensures @fd references a valid fence, increments the refcount of the backing + * file, and returns the fence. + */ +struct sync_fence *sync_fence_fdget(int fd); + +/** + * sync_fence_put() - puts a refernnce of a sync fence + * @fence: fence to put + * + * Puts a reference on @fence. If this is the last reference, the fence and + * all it's sync_pts will be freed + */ +void sync_fence_put(struct sync_fence *fence); + +/** + * sync_fence_install() - installs a fence into a file descriptor + * @fence: fence to instal + * @fd: file descriptor in which to install the fence + * + * Installs @fence into @fd. @fd's should be acquired through get_unused_fd(). + */ +void sync_fence_install(struct sync_fence *fence, int fd); + +/** + * sync_fence_wait_async() - registers and async wait on the fence + * @fence: fence to wait on + * @waiter: waiter callback struck + * + * Returns 1 if @fence has already signaled. + * + * Registers a callback to be called when @fence signals or has an error. + * @waiter should be initialized with sync_fence_waiter_init(). + */ +int sync_fence_wait_async(struct sync_fence *fence, + struct sync_fence_waiter *waiter); + +/** + * sync_fence_cancel_async() - cancels an async wait + * @fence: fence to wait on + * @waiter: waiter callback struck + * + * returns 0 if waiter was removed from fence's async waiter list. + * returns -ENOENT if waiter was not found on fence's async waiter list. + * + * Cancels a previously registered async wait. Will fail gracefully if + * @waiter was never registered or if @fence has already signaled @waiter. + */ +int sync_fence_cancel_async(struct sync_fence *fence, + struct sync_fence_waiter *waiter); + +/** + * sync_fence_wait() - wait on fence + * @fence: fence to wait on + * @tiemout: timeout in ms + * + * Wait for @fence to be signaled or have an error. Waits indefinitely + * if @timeout < 0 + */ +int sync_fence_wait(struct sync_fence *fence, long timeout); + +#endif /* __KERNEL__ */ + +/** + * struct sync_merge_data - data passed to merge ioctl + * @fd2: file descriptor of second fence + * @name: name of new fence + * @fence: returns the fd of the new fence to userspace + */ +struct sync_merge_data { + __s32 fd2; /* fd of second fence */ + char name[32]; /* name of new fence */ + __s32 fence; /* fd on newly created fence */ +}; + +/** + * struct sync_pt_info - detailed sync_pt information + * @len: length of sync_pt_info including any driver_data + * @obj_name: name of parent sync_timeline + * @driver_name: name of driver implmenting the parent + * @status: status of the sync_pt 0:active 1:signaled <0:error + * @timestamp_ns: timestamp of status change in nanoseconds + * @driver_data: any driver dependant data + */ +struct sync_pt_info { + __u32 len; + char obj_name[32]; + char driver_name[32]; + __s32 status; + __u64 timestamp_ns; + + __u8 driver_data[0]; +}; + +/** + * struct sync_fence_info_data - data returned from fence info ioctl + * @len: ioctl caller writes the size of the buffer its passing in. + * ioctl returns length of sync_fence_data reutnred to userspace + * including pt_info. + * @name: name of fence + * @status: status of fence. 1: signaled 0:active <0:error + * @pt_info: a sync_pt_info struct for every sync_pt in the fence + */ +struct sync_fence_info_data { + __u32 len; + char name[32]; + __s32 status; + + __u8 pt_info[0]; +}; + +#define SYNC_IOC_MAGIC '>' + +/** + * DOC: SYNC_IOC_WAIT - wait for a fence to signal + * + * pass timeout in milliseconds. Waits indefinitely timeout < 0. + */ +#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __s32) + +/** + * DOC: SYNC_IOC_MERGE - merge two fences + * + * Takes a struct sync_merge_data. Creates a new fence containing copies of + * the sync_pts in both the calling fd and sync_merge_data.fd2. Returns the + * new fence's fd in sync_merge_data.fence + */ +#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 1, struct sync_merge_data) + +/** + * DOC: SYNC_IOC_FENCE_INFO - get detailed information on a fence + * + * Takes a struct sync_fence_info_data with extra space allocated for pt_info. + * Caller should write the size of the buffer into len. On return, len is + * updated to reflect the total size of the sync_fence_info_data including + * pt_info. + * + * pt_info is a buffer containing sync_pt_infos for every sync_pt in the fence. + * To itterate over the sync_pt_infos, use the sync_pt_info.len field. + */ +#define SYNC_IOC_FENCE_INFO _IOWR(SYNC_IOC_MAGIC, 2,\ + struct sync_fence_info_data) + +#endif /* _LINUX_SYNC_H */ diff --git a/include/linux/uhid.h b/include/linux/uhid.h new file mode 100644 index 00000000..9c6974f1 --- /dev/null +++ b/include/linux/uhid.h @@ -0,0 +1,104 @@ +#ifndef __UHID_H_ +#define __UHID_H_ + +/* + * User-space I/O driver support for HID subsystem + * Copyright (c) 2012 David Herrmann + */ + +/* + * 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. + */ + +/* + * Public header for user-space communication. We try to keep every structure + * aligned but to be safe we also use __attribute__((__packed__)). Therefore, + * the communication should be ABI compatible even between architectures. + */ + +#include +#include + +enum uhid_event_type { + UHID_CREATE, + UHID_DESTROY, + UHID_START, + UHID_STOP, + UHID_OPEN, + UHID_CLOSE, + UHID_OUTPUT, + UHID_OUTPUT_EV, + UHID_INPUT, + UHID_FEATURE, + UHID_FEATURE_ANSWER, +}; + +struct uhid_create_req { + __u8 name[128]; + __u8 phys[64]; + __u8 uniq[64]; + __u8 __user *rd_data; + __u16 rd_size; + + __u16 bus; + __u32 vendor; + __u32 product; + __u32 version; + __u32 country; +} __attribute__((__packed__)); + +#define UHID_DATA_MAX 4096 + +enum uhid_report_type { + UHID_FEATURE_REPORT, + UHID_OUTPUT_REPORT, + UHID_INPUT_REPORT, +}; + +struct uhid_input_req { + __u8 data[UHID_DATA_MAX]; + __u16 size; +} __attribute__((__packed__)); + +struct uhid_output_req { + __u8 data[UHID_DATA_MAX]; + __u16 size; + __u8 rtype; +} __attribute__((__packed__)); + +struct uhid_output_ev_req { + __u16 type; + __u16 code; + __s32 value; +} __attribute__((__packed__)); + +struct uhid_feature_req { + __u32 id; + __u8 rnum; + __u8 rtype; +} __attribute__((__packed__)); + +struct uhid_feature_answer_req { + __u32 id; + __u16 err; + __u16 size; + __u8 data[UHID_DATA_MAX]; +}; + +struct uhid_event { + __u32 type; + + union { + struct uhid_create_req create; + struct uhid_input_req input; + struct uhid_output_req output; + struct uhid_output_ev_req output_ev; + struct uhid_feature_req feature; + struct uhid_feature_answer_req feature_answer; + } u; +} __attribute__((__packed__)); + +#endif /* __UHID_H_ */ diff --git a/include/linux/uid_stat.h b/include/linux/uid_stat.h new file mode 100644 index 00000000..6bd6c4e5 --- /dev/null +++ b/include/linux/uid_stat.h @@ -0,0 +1,29 @@ +/* include/linux/uid_stat.h + * + * Copyright (C) 2008-2009 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __uid_stat_h +#define __uid_stat_h + +/* Contains definitions for resource tracking per uid. */ + +#ifdef CONFIG_UID_STAT +int uid_stat_tcp_snd(uid_t uid, int size); +int uid_stat_tcp_rcv(uid_t uid, int size); +#else +#define uid_stat_tcp_snd(uid, size) do {} while (0); +#define uid_stat_tcp_rcv(uid, size) do {} while (0); +#endif + +#endif /* _LINUX_UID_STAT_H */ diff --git a/include/linux/uio_driver.h b/include/linux/uio_driver.h index 1ad47244..f72d1ad5 100644 --- a/include/linux/uio_driver.h +++ b/include/linux/uio_driver.h @@ -14,6 +14,7 @@ #ifndef _UIO_DRIVER_H_ #define _UIO_DRIVER_H_ +#include #include #include @@ -41,7 +42,7 @@ struct uio_mem { struct uio_map *map; }; -#define MAX_UIO_MAPS 5 +#define MAX_UIO_MAPS 16 struct uio_portio; @@ -95,6 +96,7 @@ struct uio_info { int (*open)(struct uio_info *info, struct inode *inode); int (*release)(struct uio_info *info, struct inode *inode); int (*irqcontrol)(struct uio_info *info, s32 irq_on); + int (*ioctl)(struct uio_info *info, unsigned int cmd, unsigned long arg); }; extern int __must_check diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index a316fba7..6938a860 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -242,6 +242,9 @@ int usb_add_config(struct usb_composite_dev *, struct usb_configuration *, int (*)(struct usb_configuration *)); +int usb_remove_config(struct usb_composite_dev *, + struct usb_configuration *); + /** * struct usb_composite_driver - groups configurations into a gadget * @name: For diagnostics, identifies the driver. diff --git a/include/linux/usb/f_accessory.h b/include/linux/usb/f_accessory.h new file mode 100644 index 00000000..61ebe0aa --- /dev/null +++ b/include/linux/usb/f_accessory.h @@ -0,0 +1,146 @@ +/* + * Gadget Function Driver for Android USB accessories + * + * Copyright (C) 2011 Google, Inc. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __LINUX_USB_F_ACCESSORY_H +#define __LINUX_USB_F_ACCESSORY_H + +/* Use Google Vendor ID when in accessory mode */ +#define USB_ACCESSORY_VENDOR_ID 0x18D1 + + +/* Product ID to use when in accessory mode */ +#define USB_ACCESSORY_PRODUCT_ID 0x2D00 + +/* Product ID to use when in accessory mode and adb is enabled */ +#define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01 + +/* Indexes for strings sent by the host via ACCESSORY_SEND_STRING */ +#define ACCESSORY_STRING_MANUFACTURER 0 +#define ACCESSORY_STRING_MODEL 1 +#define ACCESSORY_STRING_DESCRIPTION 2 +#define ACCESSORY_STRING_VERSION 3 +#define ACCESSORY_STRING_URI 4 +#define ACCESSORY_STRING_SERIAL 5 + +/* Control request for retrieving device's protocol version + * + * requestType: USB_DIR_IN | USB_TYPE_VENDOR + * request: ACCESSORY_GET_PROTOCOL + * value: 0 + * index: 0 + * data version number (16 bits little endian) + * 1 for original accessory support + * 2 adds HID and device to host audio support + */ +#define ACCESSORY_GET_PROTOCOL 51 + +/* Control request for host to send a string to the device + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SEND_STRING + * value: 0 + * index: string ID + * data zero terminated UTF8 string + * + * The device can later retrieve these strings via the + * ACCESSORY_GET_STRING_* ioctls + */ +#define ACCESSORY_SEND_STRING 52 + +/* Control request for starting device in accessory mode. + * The host sends this after setting all its strings to the device. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_START + * value: 0 + * index: 0 + * data none + */ +#define ACCESSORY_START 53 + +/* Control request for registering a HID device. + * Upon registering, a unique ID is sent by the accessory in the + * value parameter. This ID will be used for future commands for + * the device + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_REGISTER_HID_DEVICE + * value: Accessory assigned ID for the HID device + * index: total length of the HID report descriptor + * data none + */ +#define ACCESSORY_REGISTER_HID 54 + +/* Control request for unregistering a HID device. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_REGISTER_HID + * value: Accessory assigned ID for the HID device + * index: 0 + * data none + */ +#define ACCESSORY_UNREGISTER_HID 55 + +/* Control request for sending the HID report descriptor. + * If the HID descriptor is longer than the endpoint zero max packet size, + * the descriptor will be sent in multiple ACCESSORY_SET_HID_REPORT_DESC + * commands. The data for the descriptor must be sent sequentially + * if multiple packets are needed. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SET_HID_REPORT_DESC + * value: Accessory assigned ID for the HID device + * index: offset of data in descriptor + * (needed when HID descriptor is too big for one packet) + * data the HID report descriptor + */ +#define ACCESSORY_SET_HID_REPORT_DESC 56 + +/* Control request for sending HID events. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SEND_HID_EVENT + * value: Accessory assigned ID for the HID device + * index: 0 + * data the HID report for the event + */ +#define ACCESSORY_SEND_HID_EVENT 57 + +/* Control request for setting the audio mode. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SET_AUDIO_MODE + * value: 0 - no audio + * 1 - device to host, 44100 16-bit stereo PCM + * index: 0 + * data none + */ +#define ACCESSORY_SET_AUDIO_MODE 58 + +/* ioctls for retrieving strings set by the host */ +#define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256]) +#define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256]) +#define ACCESSORY_GET_STRING_DESCRIPTION _IOW('M', 3, char[256]) +#define ACCESSORY_GET_STRING_VERSION _IOW('M', 4, char[256]) +#define ACCESSORY_GET_STRING_URI _IOW('M', 5, char[256]) +#define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256]) +/* returns 1 if there is a start request pending */ +#define ACCESSORY_IS_START_REQUESTED _IO('M', 7) +/* returns audio mode (set via the ACCESSORY_SET_AUDIO_MODE control request) */ +#define ACCESSORY_GET_AUDIO_MODE _IO('M', 8) + +#endif /* __LINUX_USB_F_ACCESSORY_H */ diff --git a/include/linux/usb/f_mtp.h b/include/linux/usb/f_mtp.h new file mode 100644 index 00000000..72a432e2 --- /dev/null +++ b/include/linux/usb/f_mtp.h @@ -0,0 +1,75 @@ +/* + * Gadget Function Driver for MTP + * + * Copyright (C) 2010 Google, Inc. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __LINUX_USB_F_MTP_H +#define __LINUX_USB_F_MTP_H + +#include + +#ifdef __KERNEL__ + +struct mtp_data_header { + /* length of packet, including this header */ + uint32_t length; + /* container type (2 for data packet) */ + uint16_t type; + /* MTP command code */ + uint16_t command; + /* MTP transaction ID */ + uint32_t transaction_id; +}; + +#endif /* __KERNEL__ */ + +struct mtp_file_range { + /* file descriptor for file to transfer */ + int fd; + /* offset in file for start of transfer */ + loff_t offset; + /* number of bytes to transfer */ + int64_t length; + /* MTP command ID for data header, + * used only for MTP_SEND_FILE_WITH_HEADER + */ + uint16_t command; + /* MTP transaction ID for data header, + * used only for MTP_SEND_FILE_WITH_HEADER + */ + uint32_t transaction_id; +}; + +struct mtp_event { + /* size of the event */ + size_t length; + /* event data to send */ + void *data; +}; + +/* Sends the specified file range to the host */ +#define MTP_SEND_FILE _IOW('M', 0, struct mtp_file_range) +/* Receives data from the host and writes it to a file. + * The file is created if it does not exist. + */ +#define MTP_RECEIVE_FILE _IOW('M', 1, struct mtp_file_range) +/* Sends an event to the host via the interrupt endpoint */ +#define MTP_SEND_EVENT _IOW('M', 3, struct mtp_event) +/* Sends the specified file range to the host, + * with a 12 byte MTP data packet header at the beginning. + */ +#define MTP_SEND_FILE_WITH_HEADER _IOW('M', 4, struct mtp_file_range) + +#endif /* __LINUX_USB_F_MTP_H */ diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 47428388..69648311 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -249,6 +249,7 @@ struct usb_serial_driver { int (*suspend)(struct usb_serial *serial, pm_message_t message); int (*resume)(struct usb_serial *serial); + int (*reset_resume)(struct usb_serial *serial); /* add for 4G module */ /* serial function calls */ /* Called by console and by the tty layer */ diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index c9c9a468..38ea5643 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1191,6 +1191,13 @@ struct v4l2_querymenu { #define V4L2_CID_MAX_CTRLS 1024 #define V4L2_CID_BASE (V4L2_CTRL_CLASS_USER | 0x900) #define V4L2_CID_USER_BASE V4L2_CID_BASE +#define V4L2_CID_SCENE (V4L2_CID_USER_BASE + 0) +#define V4L2_CID_FOCUS (V4L2_CID_USER_BASE + 1) +#define V4L2_CID_FLASH (V4L2_CID_USER_BASE + 2) +#define V4L2_CID_PREVIEW (V4L2_CID_USER_BASE + 3) +#define V4L2_CID_PICTURE (V4L2_CID_USER_BASE + 4) +#define V4L2_CID_FRAME (V4L2_CID_USER_BASE + 5) //use to report how many frames are abandoned before ecoding +#define V4L2_CID_NIGHTMODE (V4L2_CID_USER_BASE + 6) /* IDs reserved for driver specific controls */ #define V4L2_CID_PRIVATE_BASE 0x08000000 diff --git a/include/linux/vt.h b/include/linux/vt.h index 30a8dd9c..5387b6cf 100644 --- a/include/linux/vt.h +++ b/include/linux/vt.h @@ -7,8 +7,8 @@ * resizing). */ #define MIN_NR_CONSOLES 1 /* must be at least 1 */ -#define MAX_NR_CONSOLES 63 /* serial lines start at 64 */ -#define MAX_NR_USER_CONSOLES 63 /* must be root to allocate above this */ +#define MAX_NR_CONSOLES 2 /* serial lines start at 64 */ +#define MAX_NR_USER_CONSOLES 2 /* must be root to allocate above this */ /* Note: the ioctl VT_GETSTATE does not work for consoles 16 and higher (since it returns a short) */ diff --git a/include/linux/wakelock.h b/include/linux/wakelock.h new file mode 100644 index 00000000..f4a698a2 --- /dev/null +++ b/include/linux/wakelock.h @@ -0,0 +1,67 @@ +/* include/linux/wakelock.h + * + * Copyright (C) 2007-2012 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _LINUX_WAKELOCK_H +#define _LINUX_WAKELOCK_H + +#include +#include + +/* A wake_lock prevents the system from entering suspend or other low power + * states when active. If the type is set to WAKE_LOCK_SUSPEND, the wake_lock + * prevents a full system suspend. + */ + +enum { + WAKE_LOCK_SUSPEND, /* Prevent suspend */ + WAKE_LOCK_TYPE_COUNT +}; + +struct wake_lock { + struct wakeup_source ws; +}; + +static inline void wake_lock_init(struct wake_lock *lock, int type, + const char *name) +{ + wakeup_source_init(&lock->ws, name); +} + +static inline void wake_lock_destroy(struct wake_lock *lock) +{ + wakeup_source_trash(&lock->ws); +} + +static inline void wake_lock(struct wake_lock *lock) +{ + __pm_stay_awake(&lock->ws); +} + +static inline void wake_lock_timeout(struct wake_lock *lock, long timeout) +{ + __pm_wakeup_event(&lock->ws, jiffies_to_msecs(timeout)); +} + +static inline void wake_unlock(struct wake_lock *lock) +{ + __pm_relax(&lock->ws); +} + +static inline int wake_lock_active(struct wake_lock *lock) +{ + return lock->ws.active; +} + +#endif diff --git a/include/linux/wifi_tiwlan.h b/include/linux/wifi_tiwlan.h new file mode 100644 index 00000000..f07e0679 --- /dev/null +++ b/include/linux/wifi_tiwlan.h @@ -0,0 +1,27 @@ +/* include/linux/wifi_tiwlan.h + * + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef _LINUX_WIFI_TIWLAN_H_ +#define _LINUX_WIFI_TIWLAN_H_ + +#include + +#define WMPA_NUMBER_OF_SECTIONS 3 +#define WMPA_NUMBER_OF_BUFFERS 160 +#define WMPA_SECTION_HEADER 24 +#define WMPA_SECTION_SIZE_0 (WMPA_NUMBER_OF_BUFFERS * 64) +#define WMPA_SECTION_SIZE_1 (WMPA_NUMBER_OF_BUFFERS * 256) +#define WMPA_SECTION_SIZE_2 (WMPA_NUMBER_OF_BUFFERS * 2048) + +#endif diff --git a/include/linux/wireless.h b/include/linux/wireless.h index 4395b28b..f78d7bf8 100644 --- a/include/linux/wireless.h +++ b/include/linux/wireless.h @@ -537,7 +537,7 @@ #define IW_SCAN_TYPE_ACTIVE 0 #define IW_SCAN_TYPE_PASSIVE 1 /* Maximum size of returned data */ -#define IW_SCAN_MAX_DATA 4096 /* In bytes */ +#define IW_SCAN_MAX_DATA 65535 /* In bytes */ /* Scan capability flags - in (struct iw_range *)->scan_capa */ #define IW_SCAN_CAPA_NONE 0x00 diff --git a/include/linux/wl127x-rfkill.h b/include/linux/wl127x-rfkill.h new file mode 100644 index 00000000..9057ec63 --- /dev/null +++ b/include/linux/wl127x-rfkill.h @@ -0,0 +1,35 @@ +/* + * Bluetooth TI wl127x rfkill power control via GPIO + * + * Copyright (C) 2009 Motorola, Inc. + * Copyright (C) 2008 Texas Instruments + * Initial code: Pavan Savoy (wl127x_power.c) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _LINUX_WL127X_RFKILL_H +#define _LINUX_WL127X_RFKILL_H + +#include + +struct wl127x_rfkill_platform_data { + int nshutdown_gpio; + + struct rfkill *rfkill; /* for driver only */ +}; + +#endif diff --git a/include/linux/wlan_plat.h b/include/linux/wlan_plat.h new file mode 100644 index 00000000..40ec3482 --- /dev/null +++ b/include/linux/wlan_plat.h @@ -0,0 +1,27 @@ +/* include/linux/wlan_plat.h + * + * Copyright (C) 2010 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef _LINUX_WLAN_PLAT_H_ +#define _LINUX_WLAN_PLAT_H_ + +struct wifi_platform_data { + int (*set_power)(int val); + int (*set_reset)(int val); + int (*set_carddetect)(int val); + void *(*mem_prealloc)(int section, unsigned long size); + int (*get_mac_addr)(unsigned char *buf); + void *(*get_country_code)(char *ccode); +}; + +#endif diff --git a/include/mach-anyka/Kbuild b/include/mach-anyka/Kbuild new file mode 100644 index 00000000..293bc3a9 --- /dev/null +++ b/include/mach-anyka/Kbuild @@ -0,0 +1,6 @@ +header-y += partition_init.h +header-y += partition_lib.h +header-y += ak_partition_table.h +header-y += anyka_types.h +header-y += nand_list.h + diff --git a/include/mach-anyka/aec_interface.h b/include/mach-anyka/aec_interface.h new file mode 100644 index 00000000..e5aed96d --- /dev/null +++ b/include/mach-anyka/aec_interface.h @@ -0,0 +1,193 @@ + +/** +* @file aec_interface.h +* @brief Anyka AEC Module interfaces header file. +* +* This file declare Anyka AEC Module interfaces. +* Copyright (C) 2011 Anyka (Guangzhou) Microelectronics Technology Co., Ltd. +* @author Tang Xuechai +* @date 2013-07-15 +* @version V1.0.0 +* @ref +*/ + +#ifndef _AEC_INTERFACE_H_ +#define _AEC_INTERFACE_H_ + +#include "anyka_types.h" + +#define AECS_VERSION_STRING (T_U8 *)"EchoS Version V1.0.04_svn95" + + +typedef T_pVOID (*AEC_CALLBACK_FUN_MALLOC)(T_U32 size); +typedef T_VOID (*AEC_CALLBACK_FUN_FREE)(T_pVOID mem); +typedef T_VOID (*AEC_CALLBACK_FUN_PRINTF)(T_pCSTR format, ...); +typedef T_VOID (*AEC_CALLBACK_FUN_RTC_DELAY) (T_U32 ulTicks); +typedef T_VOID (*AEC_CALLBACK_FUN_NOTIFY) (T_U32 event); + +typedef enum +{ + AEC_TYPE_UNKNOWN, + AEC_TYPE_1, + AEC_TYPE_2 +}T_AEC_TYPE; + +typedef struct +{ + AEC_CALLBACK_FUN_MALLOC Malloc; + AEC_CALLBACK_FUN_FREE Free; + AEC_CALLBACK_FUN_PRINTF printf; + AEC_CALLBACK_FUN_RTC_DELAY delay; + AEC_CALLBACK_FUN_NOTIFY notify; +}T_AEC_CB_FUNS; + +typedef struct +{ + T_U32 m_Type; //չãAEC㷨T_AEC_TYPEö֮һ + T_U32 m_SampleRate; //sample rate, sample per second + T_U16 m_Channels; //channel number + T_U16 m_BitsPerSample; //bits per sample + + union + { + struct + { + T_U32 m_aecBypass; + T_U32 m_framelen; //NN + T_U32 m_tail; //TAIL + + /* ADCڲ */ + /* adcֵ Ĭֵ 512micȹϵȽϴ*/ + T_U32 AdcMinSpeechPow; + /* adcĹʱ䣬(ms)ΪλĬֵ 920ԼǷɱϡ*/ + T_U32 AdcSpeechHoldTime; + /* adcڻϵĬ (T_U32)(1.8*(1<<14)) + ԽֵҪԽСȻܻҪʵAdcMinSpeechPow */ + T_U32 AdcSpeechMultiple; + /* adcıʱʱ䣬(ms)ΪλĬֵ 5000 */ + T_S32 AdcConvergTime; + /* adcʼһʱݶöʱ䳤ȣ(ms)ΪλĬֵ 100 */ + T_U32 AdcCutTime; + + /* DACڲ */ + /* Էֻ˴ֵ Ĭֵ 512 */ + T_U32 DacMinSpeechPow; + /* Էֻ˴Ĺʱ䣬(ms)ΪλĬֵ 920 */ + T_U32 DacSpeechHoldTime; + /* ԷֻڻϵĬ(T_U32)(1.8*(1<<14)) + ԽֵҪԽСȻܻҪʵDacMinSpeechPow */ + T_U32 DacSpeechMultiple; + //Էֻıʱʱ䣬(ms)ΪλĬֵ5000 + T_U32 DacConvergTime; + + //dac޻ֵĬ0 + T_U16 DacFadeOutThreshold; + //DAC á1024ʱŴ=1>1024ʱŴ>1; <1024ʱŴ<1 + T_U16 DacVolume; + + // for agc + T_U8 m_PreprocessEna; + T_U16 AGClevel; + T_U16 maxGain; // Ŵ + T_U16 minGain; // СŴ + }m_aec; + }m_Private; +}T_AEC_IN_INFO; + +typedef struct +{ + T_AEC_CB_FUNS cb_fun; + T_AEC_IN_INFO m_info; +}T_AEC_INPUT; + +typedef struct +{ + T_VOID *buf_near; //near, mic + T_U32 len_near; + T_VOID *buf_far; //far, speaker + T_U32 len_far; + T_VOID *buf_out; // output of aec + T_U32 len_out; +}T_AEC_BUF; + + +/** +* @brief open the AEC's device. +* @author Tang Xuechai +* @date 2013-07-15 +* @param [in] info: input information. Please refer "T_AEC_INPUT" for detail +* @return T_VOID * +* @retval the pointer to AEC's memory +*/ +T_VOID *AECLib_Open(T_AEC_INPUT *info); + +/** +* @brief close the AEC's device. +* @author Tang Xuechai +* @date 2013-07-15 +* @param [in] p_aec: the pointer to AEC's memory, get from AECLib_Open +* @return T_S32 +* @retval AK_TRUE : close success +* @retval AK_FLASE : close fail +*/ +T_S32 AECLib_Close(T_VOID *p_aec); + +/** +* @brief process ADC's data. Should be called in ADC's interrupt +* @author Tang Xuechai +* @date 2013-07-15 +* @param [in] p_aec: the pointer to AEC's memory, get from AECLib_Open +* @param [in] in: ADC's pcm data +* @param [in] len: ADC's pcm data length +* @return T_VOID +*/ +T_VOID AECLib_DacInt(T_VOID *p_aec, T_U8 *in, T_S32 len); + +/** +* @brief process DAC's data. Should be called in DAC's interrupt +* @author Tang Xuechai +* @date 2013-07-15 +* @param [in] p_aec: the pointer to AEC's memory, get from AECLib_Open +* @param [in] in: DAC's pcm data +* @param [in] len: DAC's pcm data length +* @return T_VOID +*/ +T_VOID AECLib_AdcInt(T_VOID *p_aec, T_U8 *in, T_S32 len); + +/** +* @brief main process of AEC. Should be called before transmit data to far end +* @author Tang Xuechai +* @date 2013-07-15 +* @param [in] p_aec: the pointer to AEC's memory, get from AECLib_Open +* @param [in] p_aec_buf: buffer structure for saving output pcm data. +* @return T_S32 +* @retval <0: error +* @retval >=0: output pcm data length (bytes count) +*/ +T_S32 AECLib_Control(T_VOID *p_aec, T_AEC_BUF *p_aec_buf); + +/** +* @brief pre process DAC/ADC data. Should be called while received fardata's packet +* @author Tang Xuechai +* @date 2013-08-13 +* @param [in] p_aec: the pointer to AEC's memory, get from AECLib_Open +* @param [in] p_aec_buf: buffer structure for saving output pcm data. +* @return T_S32 +* @retval <0: error +* @retval >=0: output pcm data length (bytes count) +*/ +T_S32 AECLib_FarPreprocess(T_VOID *p_aec, T_AEC_BUF *p_aec_buf); + +/** +* @brief set DAC's volume +* @author Tang Xuechai +* @date 2013-07-15 +* @param [in] p_aec: the pointer to AEC's memory, get from AECLib_Open +* @param [in] volume: the object volume. +* @return T_S32 +* @retval AK_TRUE : success +* @retval AK_FLASE : fail +*/ +T_S32 AECLib_SetDaVolume(T_VOID *p_aec, T_U16 volume); + +#endif \ No newline at end of file diff --git a/include/mach-anyka/ak_partition_table.h b/include/mach-anyka/ak_partition_table.h new file mode 100644 index 00000000..26fccdc4 --- /dev/null +++ b/include/mach-anyka/ak_partition_table.h @@ -0,0 +1,43 @@ +#ifndef _AK_PARTITION_TABLE_H_ +#define _AK_PARTITION_TABLE_H_ + +#include + + +#define KERNEL_PARTITION_IDEX 0 +#define A_PARTITION_IDEX 1 +#define B_PARTITION_IDEX 2 + + + + + +#define PARTITION_CNT 3 + +typedef struct +{ + unsigned char update_flag[6]; +}T_PARTITION_NAME_INFO; + +typedef struct +{ + unsigned long partition_cnt; + T_PARTITION_NAME_INFO partition_name_info[PARTITION_CNT];//kernel, a,b +}T_PARTITION_INFO; + + + +/***************************************************************** + *@brief:ak_partition_table_sys_create + *@author:cao_donghua + *@date:2017-02-22 + *@param *part_tab:partition table sdram first address + *@return:int + *@retval:0:success/ other value:fail + ******************************************************************/ + +int ak_partition_table_sys_create(T_PARTITION_TABLE_CONFIG *part_tab); + +#endif //_AK_PARTITION_TABLE_H_ + + diff --git a/include/mach-anyka/anyka_types.h b/include/mach-anyka/anyka_types.h new file mode 100644 index 00000000..35005024 --- /dev/null +++ b/include/mach-anyka/anyka_types.h @@ -0,0 +1,71 @@ +/** @file + * @brief Define the register operator for system + * + * Copyright (C) 2006 Anyka (GuangZhou) Software Technology Co., Ltd. + * @author + * @date 2006-01-16 + * @version 1.0 + */ + +#ifndef _ANYKA_TYPES_H_ +#define _ANYKA_TYPES_H_ + +/** @defgroup ANYKA_CPU + * @ingroup M3PLATFORM + */ +/*@{*/ + +/* preliminary type definition for global area */ +typedef unsigned char T_U8; /* unsigned 8 bit integer */ +typedef unsigned short T_U16; /* unsigned 16 bit integer */ +typedef unsigned long T_U32; /* unsigned 32 bit integer */ +typedef signed char T_S8; /* signed 8 bit integer */ +typedef signed short T_S16; /* signed 16 bit integer */ +typedef signed long T_S32; /* signed 32 bit integer */ +typedef void T_VOID; /* void */ +typedef unsigned long long T_U64; //64bit + +#define T_U8_MAX ((T_U8)0xff) // maximum T_U8 value +#define T_U16_MAX ((T_U16)0xffff) // maximum T_U16 value +#define T_U32_MAX ((T_U32)0xffffffff) // maximum T_U32 value +#define T_S8_MIN ((T_S8)(-127-1)) // minimum T_S8 value +#define T_S8_MAX ((T_S8)127) // maximum T_S8 value +#define T_S16_MIN ((T_S16)(-32767L-1L)) // minimum T_S16 value +#define T_S16_MAX ((T_S16)(32767L)) // maximum T_S16 value +#define T_S32_MIN ((T_S32)(-2147483647L-1L)) // minimum T_S32 value +#define T_S32_MAX ((T_S32)(2147483647L)) // maximum T_S32 value + +/* basal type definition for global area */ +typedef T_S8 T_CHR; /* char */ +typedef T_U8 T_BOOL; /* BOOL type */ + +typedef T_VOID * T_pVOID; /* pointer of void data */ +typedef const T_VOID * T_pCVOID; /* const pointer of void data */ + +typedef T_S8 * T_pSTR; /* pointer of string */ +typedef const T_S8 * T_pCSTR; /* const pointer of string */ + + +typedef T_U16 T_WCHR; /**< unicode char */ +typedef T_U16 * T_pWSTR; /* pointer of unicode string */ +typedef const T_U16 * T_pCWSTR; /* const pointer of unicode string */ + + +typedef T_U8 * T_pDATA; /* pointer of data */ +typedef const T_U8 * T_pCDATA; /* const pointer of data */ + +typedef T_U32 T_COLOR; /* color value */ + +typedef T_U32 T_HANDLE; /* a handle */ + +#define AK_FALSE 0 +#define AK_TRUE 1 +#undef AK_NULL +#define AK_NULL ((T_pVOID)(0)) + +#define AK_EMPTY +/*@}*/ + + +#endif // _ANYKA_TYPES_H_ + diff --git a/include/mach-anyka/echo_interface.h b/include/mach-anyka/echo_interface.h new file mode 100644 index 00000000..0939454f --- /dev/null +++ b/include/mach-anyka/echo_interface.h @@ -0,0 +1,126 @@ + + +#ifndef _AEC_INTERFACE_H_ +#define _AEC_INTERFACE_H_ + +#include "anyka_types.h" + +#define ECHO_LIB_VERSION_STRING (T_U8 *)"Echo Version V1.2.04" + + +#define AK32Q15(x) ((T_S32)((x)*(1<<15))) +#define AK16Q10(x) ((T_S16)((x)*(1<<10))) + +// chip binding +typedef enum +{ + ECHO_CHIP_UNKNOW = 0, + ECHO_CHIP_AK10XXC = 1, + ECHO_CHIP_AK37XXC = 2, + ECHO_CHIP_AK39XX = 3, + ECHO_CHIP_AK10XXT = 4, + ECHO_CHIP_AK39XXE = 5, + ECHO_CHIP_AK37XXC_RTOS = 6, + ECHO_CHIP_AK39XXEV2 = 7, + ECHO_CHIP_AK10XXD = 8, + ECHO_CHIP_AK39XXEV2_RTOS = 9, + ECHO_CHIP_AK39XXEV3 = 10, + ECHO_CHIP_AK39XXEV3_RTOS = 11, + ECHO_CHIP_AK10XXE = 12, + ECHO_CHIP_AK39XXEV5 = 13, + ECHO_CHIP_AK37XXD = 14, + ECHO_CHIP_AK10XXF = 15 +}ECHO_CHIP_ID; + +typedef T_pVOID (*AEC_CALLBACK_FUN_MALLOC)(T_U32 size); +typedef T_VOID (*AEC_CALLBACK_FUN_FREE)(T_pVOID mem); +typedef T_S32 (*AEC_CALLBACK_FUN_PRINTF)(T_pCSTR format, ...); +typedef T_VOID (*AEC_CALLBACK_FUN_RTC_DELAY) (T_U32 ulTicks); +typedef T_VOID (*AEC_CALLBACK_FUN_NOTIFY) (T_U32 event); + +typedef struct +{ + AEC_CALLBACK_FUN_MALLOC Malloc; + AEC_CALLBACK_FUN_FREE Free; + AEC_CALLBACK_FUN_PRINTF printf; + AEC_CALLBACK_FUN_RTC_DELAY delay; + AEC_CALLBACK_FUN_NOTIFY notify; +}T_AEC_CB_FUNS; + +typedef enum +{ + AEC_TYPE_UNKNOWN , + AEC_TYPE_1 +}T_AEC_TYPE; + +typedef struct { + T_U32 m_aecEna; // enable AEC + T_U32 m_PreprocessEna; // enable noise reduction + T_U32 m_agcEna; // enable AGC + + T_U32 m_framelen; // frame size in samples, 128 is optimal + T_U32 m_tail; // tail size in samples + T_U32 m_agcLevel; // agc's target level, 0: use default. use AK32Q15 + T_U16 m_maxGain; // agc's max_gain, Q0 + T_U16 m_minGain; // agc's min_gain, use AK16Q10 + T_U32 m_farThreshold; // max amplitude of far signal, 1~32767(Q15), 0: use default. use AK32Q15 + T_S16 m_farDigiGain; // digtal gain for far signal, Q10, 0: use default. use AK16Q10 + T_S16 m_nearDigiGain; // digtal gain for near signal, Q10, 0: use default. use AK16Q10 + T_S16 m_noiseSuppressDb; // attenuation of noise in dB (negative number), 0: use default + T_S16 m_nearSensitivity; // sensitivity of near-end speech [1:100], 0: use default +}T_AEC_PARAMS; + +typedef struct +{ + T_pCSTR strVersion; // must be ECHO_LIB_VERSION_STRING + ECHO_CHIP_ID chip; + T_U32 m_Type; //չãAEC㷨T_AEC_TYPEö֮һ + T_U32 m_SampleRate; //sample rate, sample per second + T_U16 m_Channels; //channel number + T_U16 m_BitsPerSample; //bits per sample + + union { + T_AEC_PARAMS m_aec; + }m_Private; +}T_AEC_IN_INFO; + +typedef struct +{ + T_VOID *buf_near; //near, mic + T_U32 len_near; + T_VOID *buf_far; //far, speaker + T_U32 len_far; + T_VOID *buf_out; // output of aec + T_U32 len_out; +}T_AEC_BUF; + +typedef struct +{ + T_AEC_CB_FUNS cb_fun; + T_AEC_IN_INFO m_info; +}T_AEC_INPUT; + +typedef enum +{ + AEC_REQ_UNKNOWN , + AEC_REQ_GET_SAMPLING_RATE, // out param: int + AEC_REQ_GET_FILTER_SIZE, // internal use + AEC_REQ_GET_FILTER, // internal use + AEC_REQ_GET_PARAMS, // out param: T_AEC_PARAMS + AEC_REQ_SET_PARAMS, // in param: T_AEC_PARAMS +}T_AEC_REQ_TYPE; + +typedef struct +{ + T_U32 m_req_type; // Ҫʲô飬Ƿò, T_AEC_REQ_TYPE + T_U8 *addr; // ҪûȡIJַ + T_U32 addrLen; // addrָĵַռֽ +}T_AEC_REQUEST; + +T_VOID *AECLib_Open(T_AEC_INPUT *p_aec_input); +T_S32 AECLib_Close(T_VOID *p_aec); +T_S32 AECLib_Control(T_VOID *p_aec, T_AEC_BUF *p_aec_buf); +T_S32 AECLib_Request(T_VOID *p_aec, T_AEC_REQUEST *p_aec_req); +T_S8 *AECLib_GetVersionInfo(T_VOID); + +#endif diff --git a/include/mach-anyka/fha_asa.h b/include/mach-anyka/fha_asa.h new file mode 100644 index 00000000..5a30deaa --- /dev/null +++ b/include/mach-anyka/fha_asa.h @@ -0,0 +1,81 @@ +#ifndef _FHA_ASA_H_ +#define _FHA_ASA_H_ + +#define ASA_FILE_FAIL 0 +#define ASA_FILE_SUCCESS 1 +#define ASA_FILE_EXIST 2 +#define ASA_MODE_OPEN 0 +#define ASA_MODE_CREATE 1 + +#define ASA_FORMAT_NORMAL 0 +#define ASA_FORMAT_EWR 1 +#define ASA_FORMAT_RESTORE 2 + +/************************************************************************ + * NAME: FHA_asa_scan + * FUNCTION scan nand flash security area + * PARAM: [in] bMount -- if buffer is enough, set AK_TURE, scan speedup + * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL +**************************************************************************/ +T_U32 FHA_asa_scan(T_BOOL bMount); + +/************************************************************************ + * NAME: FHA_asa_format + * FUNCTION nand flash format security area + * PARAM: [in] type -- ASA_FORMAT_NORMAL: only scan initial bad blocks + * ASA_FORMAT_EWR: Erase-wirte-read test + * ASA_FORMAT_RESTORE: do nothing + * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL +**************************************************************************/ +T_U32 FHA_asa_format(T_U32 type); + +/************************************************************************ + * NAME: FHA_set_bad_block + * FUNCTION set nand flash bad block + * PARAM: [in] block -- the bad block item + * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL +**************************************************************************/ +T_U32 FHA_set_bad_block(T_U32 block); + +/************************************************************************ + * NAME: FHA_check_bad_block + * FUNCTION check nand flash bad block + * PARAM: [in] block -- need to check block item + * RETURN: is bad block return FHA_SUCCESS, or else retuen FHA_ FAIL +**************************************************************************/ +T_BOOL FHA_check_bad_block(T_U32 block); + +/************************************************************************ + * NAME: FHA_get_bad_block + * FUNCTION get bad block information of one or more blocks + * PARAM: [in] start_block -- start block + * [out]pData -------- buffer used to store bad blocks information data + * [in] blk_cnt ------ how many blocks you want to know + * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL +**************************************************************************/ +T_U32 FHA_get_bad_block(T_U32 start_block, T_U8 *pData, T_U32 blk_cnt); + +/************************************************************************ + * NAME: FHA_asa_write_file + * FUNCTION write important infomation to security area, data_len can't too large + * PARAM: [in] file_name -- asa file name + * [in] pData ------ buffer used to store information data + * [in] data_len --- need to store data length + * [in] mode-------- operation mode + * ASA_MODE_OPEN------open + * ASA_MODE_CREATE----create + * RETURN: success return ASA_FILE_SUCCESS, fail retuen ASA_FILE_FAIL +**************************************************************************/ +T_U32 FHA_asa_write_file(T_U8 file_name[], const T_U8 *pData, T_U32 data_len, T_U8 mode); + +/************************************************************************ + * NAME: FHA_asa_read_file + * FUNCTION get infomation from security area by input file name + * PARAM: [in] file_name -- asa file name + * [out]pData ------ buffer used to store information data + * [in] data_len --- need to get data length + * RETURN: success return ASA_FILE_SUCCESS, fail retuen ASA_FILE_FAIL +**************************************************************************/ +T_U32 FHA_asa_read_file(T_U8 file_name[], T_U8 *pData, T_U32 data_len); +#endif // + diff --git a/include/mach-anyka/ispdrv_interface.h b/include/mach-anyka/ispdrv_interface.h new file mode 100644 index 00000000..2bab6ee8 --- /dev/null +++ b/include/mach-anyka/ispdrv_interface.h @@ -0,0 +1,81 @@ +#ifndef __ISPDRV_INTERFACE__ +#define __ISPDRV_INTERFACE__ + +#include + +enum sensor_get_param { + GET_MIPI_MHZ = 0, + GET_VSYNC_ACTIVE_MS, + GET_CUR_FPS, + GET_MIPI_LANE, + GET_INTERFACE, + GET_SENSOR_IO_LEVEL +}; + +enum sensor_interface { + DVP_INTERFACE = 0, + MIPI_INTERFACE +}; + +enum sensor_io_level { + SENSOR_IO_LEVEL_1V8 = 0, // 1.8V IO + SENSOR_IO_LEVEL_2V5, // 2.5V IO + SENSOR_IO_LEVEL_3V3 // 3.3V IO +}; + + +int ispdrv_irq_work(void); + +int ispdrv_awb_work(void); + +int ispdrv_ae_work(void); + +void ispdrv_vo_get_flip_mirror(int *flip_en, int *mirror_en, int *height_block_num); + +int ispdrv_vo_get_using_frame_buf_id(void); + +int ispdrv_vo_set_main_channel_scale(int width, int height); + +int ispdrv_vo_set_sub_channel_scale(int width, int height); + +int ispdrv_vi_set_crop(int sx, int sy, int width, int height); + +int ispdrv_vi_apply_mode(enum isp_working_mode mode); + +int ispdrv_vo_enable_buffer(enum buffer_id id); + +int ispdrv_vo_disable_buffer(enum buffer_id id); + +int ispdrv_vo_set_buffer_addr(enum buffer_id id,unsigned long yaddr_main_chan_addr, + unsigned long yaddr_sub_chan_addr); + +int ispdrv_vo_enable_irq_status(int bit); + +int ispdrv_vo_clear_irq_status(int bit); + +int ispdrv_vo_check_irq_status(void); + +int ispdrv_vi_start_capturing(void); + +int ispdrv_vi_stop_capturing(void); + +int ispdrv_is_continuous(void); + +int ispdrv_register_sensor(void *sensor_info); + +void *ispdrv_get_sensor(int *index); + +void ispdrv_remove_all_sensors(void); + +AK_ISP_PCLK_POLAR ispdrv_get_pclk_polar(void); + +int ispdrv_set_isp_pause(void); +int ispdrv_set_isp_resume(void); + +int ispdrv_set_td(void); +int ispdrv_reload_td(void); + +int ispdrv_get_yuvaddr_and_mdinfo(int id, void **yuv, void **mdinfo); +int ispdrv_vo_get_inputdataw(void); + +#endif diff --git a/include/mach-anyka/mac.h b/include/mach-anyka/mac.h new file mode 100644 index 00000000..e5c585c7 --- /dev/null +++ b/include/mach-anyka/mac.h @@ -0,0 +1,20 @@ +#ifndef __AK_MAC_H +#define __AK_MAC_H + +/* platfrom data for platfrom device structure's platfrom_data field */ + +#define MAC_ADDR_LEN 6 +#define MAC_ADDR_STRING_LEN (MAC_ADDR_LEN * 3 - 1) + + +struct ak_mac_data { + unsigned int flags; + unsigned char dev_addr[MAC_ADDR_LEN]; + void (* gpio_init) (const struct gpio_info *); + struct gpio_info pwr_gpio; // power up + struct gpio_info phy_rst_gpio; // PHY RESET gpio +}; + + +#endif /* __AK_MAC_H */ + diff --git a/include/mach-anyka/medialib_global.h b/include/mach-anyka/medialib_global.h new file mode 100644 index 00000000..351cecb6 --- /dev/null +++ b/include/mach-anyka/medialib_global.h @@ -0,0 +1,229 @@ +/** + * @file medialib_global.h + * @brief Define the global public types for media lib, video lib and audio lib + * + * Copyright (C) 2020 Anyka (Guangzhou) Microelectronics Technology Co., Ltd. + * @author Huang Liang + * @update date 2020-01-07 + * @version 4.0 + */ + +#ifndef _MEDIA_LIB_GLOBAL_H_ +#define _MEDIA_LIB_GLOBAL_H_ + +#include "anyka_types.h" + +typedef struct +{ + T_U16 ResourceID; + T_U8 *Buff; + T_U32 Resource_len; +}T_AUDIO_LOADRESOURCE_CB_PARA; + +typedef T_VOID (*MEDIALIB_CALLBACK_FUN_PRINTF)(T_pCSTR format, ...); + +#if 0 +typedef T_S32 SEM_ID; +typedef SEM_ID (*MEDIALIB_CALLBACK_FUN_SEM_CREATE)(T_U32 nSemType, T_U32 nMaxLockCount); +typedef T_BOOL (*MEDIALIB_CALLBACK_FUN_SEM_TAKE)(SEM_ID semID, T_S32 nTimeOut); +typedef T_BOOL (*MEDIALIB_CALLBACK_FUN_SEM_GIVE)(SEM_ID semID); +typedef T_BOOL (*MEDIALIB_CALLBACK_FUN_SEM_FLUSH)(SEM_ID semID); +typedef T_BOOL (*MEDIALIB_CALLBACK_FUN_SEM_DELETE)(SEM_ID semID); +#endif + +//typedef T_S32 (*MEDIALIB_CALLBACK_FUN_OPEN)(T_pVOID lpData); +typedef T_S32 (*MEDIALIB_CALLBACK_FUN_READ)(T_S32 hFile, T_pVOID buf, T_S32 size); +typedef T_S32 (*MEDIALIB_CALLBACK_FUN_WRITE)(T_S32 hFile, T_pVOID buf, T_S32 size); +typedef T_S32 (*MEDIALIB_CALLBACK_FUN_SEEK)(T_S32 hFile, T_S32 offset, T_S32 whence); +typedef T_S32 (*MEDIALIB_CALLBACK_FUN_TELL)(T_S32 hFile); +//typedef T_VOID (*MEDIALIB_CALLBACK_FUN_CLOSE)(T_S32 hFile); + +typedef T_pVOID (*MEDIALIB_CALLBACK_FUN_MALLOC)(T_U32 size); +typedef T_VOID (*MEDIALIB_CALLBACK_FUN_FREE)(T_pVOID mem); + +typedef T_VOID (*MEDIALIB_CALLBACK_FUN_LOADRESOURCE)(T_AUDIO_LOADRESOURCE_CB_PARA *pPara); //Դ +typedef T_VOID (*MEDIALIB_CALLBACK_FUN_RELEASERESOURCE)(T_U8 *Buff); //ͷԴ + +typedef T_BOOL (*MEDIALIB_CALLBACK_FUN_RTC_DELAY) (T_U32 ulTicks); + +//typedef T_VOID (*MEDIALIB_CALLBACK_FUN_SHOW_FRAME)(T_pDATA srcImg, T_U16 src_width, T_U16 src_height); + +typedef T_pVOID (*MEDIALIB_CALLBACK_FUN_DMA_MEMCPY)(T_pVOID dest, T_pCVOID src, T_U32 size); +typedef T_VOID (*MEDIALIB_CALLBACK_FUN_MMU_INVALIDATEDCACHE)(void); +typedef T_BOOL (*MEDIALIB_CALLBACK_FUN_CHECK_DEC_BUF)(T_pDATA pBuf); + +typedef T_S32 (*MEDIALIB_CALLBACK_FUN_FILE_HANDLE_EXIST)(T_S32 hFile); +typedef T_U32 (*MEDIALIB_CALLBACK_FUN_FILE_GET_LENGTH)(T_S32 hFile); + +//just for audio codec lib +typedef T_S32 (*MEDIALIB_CALLBACK_FUN_CMMBSYNCTIME)(T_VOID *pHandle, T_U32 timestamp); +typedef T_VOID (*MEDIALIB_CALLBACK_FUN_CMMBAUDIORECDATA)(T_VOID *pHandle, T_U8 *buf, T_S32 len); +//end of just for audio codec lib + +typedef T_BOOL (*MEDIALIB_CALLBACK_FUN_FILESYS_ISBUSY)(void); + +typedef T_pVOID (*MEDIALIB_CALLBACK_FUN_DMA_MALLOC)(T_U32 size); +typedef T_VOID (*MEDIALIB_CALLBACK_FUN_DMA_FREE)(T_pVOID mem); + +typedef T_pVOID (*MEDIALIB_CALLBACK_FUN_VADDR_TO_PADDR)(T_pVOID mem); +typedef T_U32 (*MEDIALIB_CALLBACK_FUN_MAP_ADDR)(T_U32 phyAddr, T_U32 size); +typedef T_VOID (*MEDIALIB_CALLBACK_FUN_UNMAP_ADDR)(T_U32 addr, T_U32 size); + +//common register operate +typedef T_VOID (*MEDIALIB_CALLBACK_FUN_REG_BITS_WRITE)(T_U32 phyAddr, T_U32 val, T_U32 mask); + +//hardware mutex +typedef T_pVOID (*MEDIALIB_CALLBACK_FUN_VIDEO_HW_LOCK)(T_S32 hw_id); +typedef T_S32 (*MEDIALIB_CALLBACK_FUN_VIDEO_HW_UNLOCK)(T_pVOID hLock); + +//invalid Dcache +typedef T_VOID (*MEDIALIB_CALLBACK_FUN_INVALID_DCACHE) (T_VOID); +typedef T_VOID (*MEDIALIB_CALLBACK_FUN_FLUSH_DCACHE_RANGE) (T_U32 start, T_U32 size); // start should be 32byte aligned + +typedef enum +{ + MEDIALIB_AUDIO_TYPE_UNKNOWN = 0, + MEDIALIB_AUDIO_TYPE_MIDI , + MEDIALIB_AUDIO_TYPE_MP3 , + MEDIALIB_AUDIO_TYPE_AMR , + MEDIALIB_AUDIO_TYPE_AAC , + MEDIALIB_AUDIO_TYPE_WMA , + MEDIALIB_AUDIO_TYPE_PCM , + MEDIALIB_AUDIO_TYPE_ADPCM_IMA , + MEDIALIB_AUDIO_TYPE_ADPCM_MS , + MEDIALIB_AUDIO_TYPE_ADPCM_FLASH , + MEDIALIB_AUDIO_TYPE_APE , + MEDIALIB_AUDIO_TYPE_FLAC , + MEDIALIB_AUDIO_TYPE_OGG_FLAC , + MEDIALIB_AUDIO_TYPE_RA8LBR , + MEDIALIB_AUDIO_TYPE_DRA, + MEDIALIB_AUDIO_TYPE_OGG_VORBIS, + MEDIALIB_AUDIO_TYPE_AC3, + MEDIALIB_AUDIO_TYPE_PCM_ALAW, + MEDIALIB_AUDIO_TYPE_PCM_ULAW, + MEDIALIB_AUDIO_TYPE_SBC, + MEDIALIB_AUDIO_TYPE_MSBC, + MEDIALIB_AUDIO_TYPE_SPEEX, + MEDIALIB_AUDIO_TYPE_SPEEX_WB, + MEDIALIB_AUDIO_TYPE_OPUS, + MEDIALIB_AUDIO_TYPE_G722 +}T_eMEDIALIB_AUDIO_TYPE; + +typedef enum +{ + VIDEO_DRV_UNKNOWN = 0, + VIDEO_DRV_H263, + VIDEO_DRV_MPEG, + VIDEO_DRV_FLV263, + VIDEO_DRV_H264, //4 + VIDEO_DRV_RV, + VIDEO_DRV_AVC1, //6 + VIDEO_DRV_MJPEG, + VIDEO_DRV_MPEG2, + VIDEO_DRV_H264DMX, + VIDEO_DRV_H265, //10 + VIDEO_DRV_HVC1, //11 +}T_eVIDEO_DRV_TYPE; + +typedef enum +{ + MEDIALIB_ROTATE_0, + MEDIALIB_ROTATE_90, + MEDIALIB_ROTATE_180, + MEDIALIB_ROTATE_270 +}T_eMEDIALIB_ROTATE; + +typedef enum +{ + AUDIOLIB_CHIP_UNKNOW, + AUDIOLIB_CHIP_AK10XX, + AUDIOLIB_CHIP_AK10XXC, + AUDIOLIB_CHIP_AK10XXL, + AUDIOLIB_CHIP_AK10XXT, + AUDIOLIB_CHIP_AK11XX, + AUDIOLIB_CHIP_AK37XX, + AUDIOLIB_CHIP_AK37XXL, + AUDIOLIB_CHIP_AK37XXC, + AUDIOLIB_CHIP_AK39XX, + AUDIOLIB_CHIP_AK98XX, + AUDIOLIB_CHIP_AK39XXE, + AUDIOLIB_CHIP_AK10XXT2, + AUDIOLIB_CHIP_AK39XXEV2, + AUDIOLIB_CHIP_AK39XXEV3, + AUDIOLIB_CHIP_AK10XXT3, + AUDIOLIB_CHIP_AK39XXEV5, + AUDIOLIB_CHIP_AK37XXD, + AUDIOLIB_CHIP_AK10XXF +}T_AUDIO_CHIP_ID; + +typedef struct +{ + T_U32 m_SampleRate; //sample rate, sample per second + T_U16 m_Channels; //channel number + T_U16 m_BitsPerSample; //bits per sample +}T_AUDIO_OUT_INFO; + +typedef struct +{ + T_U16 m_OutWidth; //output width + T_U16 m_OutHeight; //output height +}T_VIDEO_OUT_INFO; + +typedef struct +{ + T_S32 real_time; + + union { + struct{ + T_U32 nCurBitIndex; //Ҫ + T_U32 nFrameIndex; //ڼ֡ + } m_ape; + struct{ + T_U32 Indx; //seekλÿʼǰpageеpacket + T_U32 offset; //seek󣬵ǰpageʣҪݴС + T_BOOL flag; //seek־λseekʱ1 + T_U32 last_granu; //һpageʱsample + T_U32 now_granu; //굱ǰpageʱӳsample + T_BOOL is_eos; //־λ + T_U32 re_data; //seek󣬵ǰpageвpacketĴС + T_U32 pack_no; //seekλڵǰpageеĵڼpacket + T_U32 list[255]; //¼һogg pageÿpacketƫλ + }m_speex; + struct{ + T_U32 Indx; //seekλÿʼǰpageеpacket + T_U32 offset; //seek󣬵ǰpageʣҪݴС + T_BOOL flag; //seek־λseekʱ1 + T_U32 last_granu; //һpageʱsample + T_U32 now_granu; //굱ǰpageʱӳsample + T_BOOL is_eos; //־λ + T_U32 re_data; //seek󣬵ǰpageвpacketĴС + T_U32 pack_no; //seekλڵǰpageеĵڼpacket + T_U32 list[255]; //¼һogg pageÿpacketƫλ + }m_speexwb; + struct{ + T_U8 secUse; //ѾȡsectionĿ + T_U8 secLen; //һpageаsection + T_U8 tmpSec; //ѾsectionĿ + T_BOOL is_eos; //Dzһpage + T_BOOL is_bos; //Dzǵһpage + T_U8 endpack; //ǰpageһpacketλ + //sampleһ64λĿǰֻȡ32λ + T_U32 gos; //굱ǰpageܵsample32λ + T_U32 high_gos; //굱ǰpageܵsample32λ(ʱãԺҪ) + T_U8 list[255]; //¼һpageÿsectionĴСһpageຬ255section + }m_vorbis; + struct{ + T_U32 Indx; //seekλÿʼǰpageеpacket + T_U32 offset; //seek󣬵ǰpageʣҪݴС + T_BOOL flag; //seek־λseekʱ1 + T_U32 last_granu; //һpageʱsample + T_U32 now_granu; //굱ǰpageʱӳsample + T_BOOL is_eos; //־λ + T_U32 re_data; //seek󣬵ǰpageвpacketĴС + T_U32 pack_no; //seekλڵǰpageеĵڼpacket + T_U32 list[255]; //¼һogg pageÿpacketƫλ + }m_opus; + }m_Private; +}T_AUDIO_SEEK_INFO; + +#endif//_MEDIA_LIB_GLOBAL_H_ diff --git a/include/mach-anyka/nand_list.h b/include/mach-anyka/nand_list.h new file mode 100644 index 00000000..6f2f609d --- /dev/null +++ b/include/mach-anyka/nand_list.h @@ -0,0 +1,85 @@ +/** + * @filename nand_list.h + * @brief: AK3223M interrupt + * + * This file describe what is in the table of nand list + * Copyright (C) 2006 Anyka (GuangZhou) Software Technology Co., Ltd. + * @author zhaojiahuan + * @modify chenyanyan + * @date 2007-1-10 + * @version 1.0 + * @ref + */ + +#ifndef __CHIP_NFC_3224__ +#define __CHIP_NFC_3224__ + +#include "anyka_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @defgroup NandFlash Architecture NandFlash Interface + * @ingroup Architecture + */ +/*@{*/ + +/** +* @BRIEF Nandflash info define +* @AUTHOR zhaojiahuan +* @DATE 2006-7-17 +*/ + +typedef struct +Nand_phy_info{ + T_U32 chip_id;//chip id + T_U16 page_size; //page size + T_U16 page_per_blk; //page of one blok + T_U16 blk_num;//total block number + T_U16 group_blk_num;//the same concept as die, according to nand's struture + T_U16 plane_blk_num; + T_U8 spare_size;//spareСĵλ255 Byte + T_U8 col_cycle;//column address cycle + T_U8 lst_col_mask;//last column addrress cycle mask bit + T_U8 row_cycle;//row address cycle + T_U8 delay_cnt;//Rb delay, unit is 1024 asic clock, default value corresponds to 84MHz + T_U8 custom_nd;//nand type flag, used to detect the original invilid block + //currently there are 7 types, more types might be added when new nand come out + //˵ǰһpage,һpageеλ, ЩλòΪ0xFFblockdz + //NAND_TYPE_SAMSUNG: 0x1 СҳSLC([0,1],[517]), ҳSLC([0,1],[2048]), MLC([127], [2048/4096]) + //NAND_TYPE_HYNIX: 0x2 СҳSLC([0,1],[517]), ҳSLC([0,1],[2048]), MLC([125,127], [2048/4096]) + //NAND_TYPE_TOSHIBA: 0x3 СҳSLC([0,1],[0,512]), ҳSLC([0,1],[0,2048]), MLC([127], [0,2048/4096]) + //NAND_TYPE_TOSHIBA_EXT: 0x4 СҳSLC(), ҳSLC(), MLC([0,127/255], [0,2048/4096/8192]) + //NAND_TYPE_MICRON: 0x5 СҳSLC([0,1],[512]), ҳSLC([0,1],[2048]), MLC([0,1], [2048/4096]) + //NAND_TYPE_ST: 0x6 СҳSLC([0,1],[517]), ҳSLC([0],[2048,2053]), MLC([127], [0]) + //NAND_TYPE_MICRON_4K 0x7 СҳSLC(), ҳSLC(), MLC([0], [4096 ~ 4096+218]) + T_U32 flag;//character bits, 4λʾplaneԣλʾǷҪblock˳дpage + //bit31ʾǷcopyback1ʾcopyback + //bit30ʾǷֻһplane1ʾֻһplane + //bit29ʾǷǰplane1ʾǰplane + //bit28ʾǷżplane1ʾżplane + + //bitΪ˽pageblockַӵĿbit: + //bit11ʾblock number per dieǷҪϹToshiba TH58NVG6D2ETA202048 block/die(ʵ2084 block/die) + //Ϊ˶һdieblockҪΪ4096 block/dieײ + //bit10ʾpage numberǷҪϹTLC192page/blockΪ˶һblockҪΪ256page/block + + //bit8~9ʾspareСĸλλ256 Bytesspare_sizeΪT_U8Աʾnand400ֽڵspareС + //bit4-7ʾECCͣ0Ϊ4 bit/512B1Ϊ8 bit/512B2Ϊ12 bit/512B3Ϊ16 bit/512B4Ϊ24 bit/1024B5Ϊ32 bit/1024B + //bit0ʾͬһblockǷҪ˳дpage1ʾҪ˳дnandΪMLC + //ע: (bit29bit28)Ϊ'11'ʾchip4planeżҲǰplane + + T_U32 cmd_len;//nandflash command length + T_U32 data_len;//nandflash data length + T_U8 des_str[32];//descriptor string +}T_NAND_PHY_INFO; + +#define ERROR_CHIP_ID 0xFFFFFFFF + +/*@}*/ +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/mach-anyka/partition_init.h b/include/mach-anyka/partition_init.h new file mode 100644 index 00000000..c9f5e738 --- /dev/null +++ b/include/mach-anyka/partition_init.h @@ -0,0 +1,125 @@ +#ifndef _PARTITION_INIT_H_ +#define _PARTITION_INIT_H_ + +#include "nand_list.h" + + +typedef struct +{ + unsigned long chip_id; + unsigned long total_size; ///< flash total size in bytes + unsigned long page_size; ///< total bytes per page + unsigned long program_size; ///< program size at 02h command + unsigned long erase_size; ///< erase size at d8h command + unsigned long clock; ///< spi clock, 0 means use default clock + + //chip character bits: + //bit 0: under_protect flag, the serial flash under protection or not when power on + //bit 1: fast read flag + unsigned char flag; ///< chip character bits + unsigned char protect_mask; ///< protect mask bits in status register:BIT2:BP0, + //BIT3:BP1, BIT4:BP2, BIT5:BP3, BIT7:BPL + unsigned char reserved1; + unsigned char reserved2; + unsigned char des_str[32]; // +}T_SFLASH_INFO; + + +/************************************************************************ + * NAME: FHA_Erase + * FUNCTION callback function, medium erase + * PARAM: [in] nChip--meidum chip + * [in] nPage--medium page + * RETURN: success return 0, fail retuen -1 +**************************************************************************/ +typedef int (*FHA_Erase)(unsigned long nChip, unsigned long block); + +/************************************************************************ + * NAME: FHA_Write + * FUNCTION callback function, medium write + * PARAM: [in] nChip-----meidum chip + * [in] nPage-----medium page + * [in] pData-----need to write data pointer addr + * [in] nDataLen--need to write data length + * nand(unit byte) + * SD(unit sector count(1sec = 512byte)) + * SPI(unit page count, page size in platform define, generally is 256bytes) + * [in] pOob------Spare areaOut Of Band, only nand use + * [in] nOobLen---Spare area length + * [in] eDataType-burn medium data type + * nand -- E_FHA_DATA_TYPE + * SD----- MEDIUM_EMMC + * SPI---- MEDIUM_SPIFLASH + * RETURN: success return 0, fail retuen -1 +**************************************************************************/ +typedef int (*FHA_Write)(unsigned long nChip, unsigned long nPage, const unsigned char *pData, unsigned long nDataLen, unsigned char *pOob, unsigned long nOobLen, unsigned long eDataType); + +/************************************************************************ + * NAME: FHA_Read + * FUNCTION callback function, medium read + * PARAM: [in] nChip-----meidum chip + * [in] nPage-----medium page + * [out]pData-----need to read data pointer addr + * [in] nDataLen--need to ren data length + * nand(unit byte) + * SD(unit sector count(1sec = 512byte)) + * SPI(unit page count, page size in platform define, generally is 256bytes) + * [out]pOob------Spare areaOut Of Band, only nand use + * [in] nOobLen---Spare area length + * [in] eDataType-burn medium data type + * nand -- E_FHA_DATA_TYPE + * SD----- MEDIUM_EMMC + * SPI---- MEDIUM_SPIFLASH + * RETURN: success return 0, fail retuen -1 +**************************************************************************/ +typedef int (*FHA_Read)(unsigned long nChip, unsigned long nPage, unsigned char *pData, unsigned long nDataLen, unsigned char *pOob, unsigned long nOobLen , unsigned long eDataType); + +/************************************************************************ + * NAME: FHA_ReadNandBytes + * FUNCTION callback function, nand read no ECC + * PARAM: [in] nChip-------meidum chip + * [in] rowAddr-----nand physical row addr + * [in] columnAddr--nand physical cloumn addr + * [out] pData------need to read data pointer addr + * [in] nDataLen----need to ren data length + * nand(unit byte) + * RETURN: success return 0, fail retuen -1 +**************************************************************************/ +typedef int (*FHA_ReadNandBytes)(unsigned long nChip, unsigned long rowAddr, unsigned long columnAddr, unsigned char *pData, unsigned long nDataLen); + +typedef void *(*FHA_RamAlloc)(unsigned long size); +typedef void *(*FHA_RamFree)(void * var); +typedef void *(*FHA_MemSet)(void * pBuf, signed long value, unsigned long count); +typedef void *(*FHA_MemCpy)(void * dst, const void * src, unsigned long count); +typedef signed long (*FHA_MemCmp)(const void * pbuf1, const void * pbuf2, unsigned long count); +typedef void *(*FHA_MemMov)(void * dst, const void * src, unsigned long count); +typedef signed long (*FHA_Printf)(const signed char *s, ...); + +typedef struct tag_FHA_LibCallback +{ + FHA_Erase Erase; + FHA_Write Write; + FHA_Read Read; + FHA_ReadNandBytes ReadNandBytes; + FHA_RamAlloc RamAlloc; + FHA_RamFree RamFree; + FHA_MemSet MemSet; + FHA_MemCpy MemCpy; + FHA_MemCmp MemCmp; + FHA_MemMov MemMov; + FHA_Printf Printf; +}T_FHA_LIB_CALLBACK, *T_PFHA_LIB_CALLBACK; + + + +/************************************************************************ + * NAME: Sflash_Init + * FUNCTION partition init + * PARAM: [in] T_pVOID SpiInfo + * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL +**************************************************************************/ +int partition_init(T_PFHA_LIB_CALLBACK pCB, void *pPhyInfo, unsigned long table_page); + +#endif //_FHA_BINBURN_H_ + + diff --git a/include/mach-anyka/partition_lib.h b/include/mach-anyka/partition_lib.h new file mode 100644 index 00000000..6e270310 --- /dev/null +++ b/include/mach-anyka/partition_lib.h @@ -0,0 +1,275 @@ +#ifndef _PARTITION_LIB_H_ +#define _PARTITION_LIB_H_ + + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +#ifdef CONFIG_MTD_AK_SPIFLASH +#define SUPPORT_SPIFLASH +#endif + +#ifdef CONFIG_MTD_AK_SPINAND +#define SUPPORT_SPINAND +#endif + + +#define PARTITION_NAME_LEN 6 + +typedef enum +{ + PART_NO_HIDDEN = 0, + PART_HIDDEN, +}PART_HIDDEN_TYPE; + + +typedef enum +{ + PART_ONLY_READ = 0, + PART_READ_WRITE, +}PART_R_OR_W_TYPE; + + +typedef enum +{ + PART_DATA_TYPE = 0, + PART_BIN_TYPE, + PART_FS_TYPE, +}PART_TYPE; + +typedef enum +{ + FS_SQSH4 = 0, + FS_JFFS2, + FS_YAFFS2, +}FS_TYPE; + + +typedef enum +{ + MEDIUM_SPINOR = 0, + MEDIUM_SPINAND, +}MEDIUM_TYPE; + +typedef enum +{ + PART_SPIFLASH = 0, + PART_SPINAND, + PART_SPINOR_SPINAND, +}PART_MEDIUM; + + + + +typedef struct +{ + unsigned long page_size; /*spi page size*/ + unsigned long pages_per_block;/*page per block*/ + unsigned long total_size;/*spiflash size*/ +}T_SPI_INIT_INFO, *T_PSPI_INIT_INFO; + +typedef struct +{ + unsigned char name[PARTITION_NAME_LEN]; //̷ + unsigned long Size; //С + unsigned long ld_addr; //еַ + unsigned char type; //ͣdata , bin, fs + unsigned char r_w_flag; // r_w or onlyread + unsigned char hidden_flag; // hidden_flag or no hidden_flag + unsigned char backup; //Ƿ񱸷 + unsigned char check; //Ƚ + unsigned char mtd_idex; //mtdidex + unsigned char fs_type; //ļϵͳ + unsigned char medium_flag:1; //0 -> not need medium_tpye,1->need medium_type + unsigned char medium_type:1; //0->spi, 1->spinand + unsigned char rev1:6; + unsigned long flash_startpos; //˷flash +}T_CREAT_PARTITION_INFO; + + +typedef struct +{ + unsigned long file_length; //bin:file_length fs:δ + unsigned long ld_addr; //bin: ld_addr fs:δ + unsigned long backup_pos; //bin:backup_page fs:δ //ֽΪλ + unsigned char check; //bin:check fs:δ + unsigned char mtd_idex; + unsigned char medium_flag:1; //介质是否区分的标志,0 -》not need medium_tpye,1->need medium_type + unsigned char medium_type:1; //0表示spi, 1表示spinand + unsigned char rev1:6; + unsigned char rev2; +}T_BIN_CONFIG; + +typedef struct +{ + unsigned long file_length; // ļϵͳľļЧȣĿǰضõ + unsigned char check; // ǷȽ + unsigned char mtd_idex; //mtdidex + unsigned char fs_type; //ļϵͳ + unsigned char medium_flag:1; //介质是否区分的标志,0 -》not need medium_tpye,1->need medium_type + unsigned char medium_type:1; //0表示spi, 1表示spinand + unsigned char rev1:6; + unsigned long rev2; // + unsigned long rev3; // +}T_FS_CONFIG; + +typedef struct +{ + unsigned long pos; // + unsigned char table[512]; +}T_PARTITION_TABLE_CONFIG; + + +typedef struct +{ + unsigned long parameter1; //bin:file_length fs:δ + unsigned long parameter2; //bin: ld_addr fs:δ + unsigned long parameter3; //bin:backup_page fs:δ + unsigned long parameter4; //bin:check fs:δ +}T_EX_PARTITION_CONFIG; + +typedef struct +{ + unsigned char type; //data,/bin/fs , E_PARTITION_TYPE + volatile unsigned char r_w_flag:4; //only read or write + unsigned char hidden_flag:4; //hidden or no hidden + unsigned char name[PARTITION_NAME_LEN]; // + unsigned long ksize; //СKΪλ + unsigned long start_pos; //ĿʼλãֽΪλ +}T_PARTITION_CONFIG; + + +typedef struct +{ + T_PARTITION_CONFIG partition_info; + T_EX_PARTITION_CONFIG ex_partition_info; +}T_PARTITION_TABLE_INFO; + + + +/************************************************************************ + * NAME: partition_getversion + * FUNCTION get partition vesion + * PARAM: void + * RETURN: version +**************************************************************************/ +unsigned char *partition_getversion(void); + +/************************************************************************ + * NAME: Sflash_Creat + * FUNCTION partition creat + * PARAM: [in] T_CREAT_PARTITION_INFO *partition + * RETURN: success handle, fail retuen null +**************************************************************************/ +void *partition_creat(T_CREAT_PARTITION_INFO *partition); + + +/************************************************************************ + * NAME: Sflash_Close + * FUNCTION partition close + * PARAM: [in] T_PARTITION_TABLE_INFO *pFile + * RETURN: success return 0, fail retuen -1 +**************************************************************************/ +int partition_close(void *handle); + +/************************************************************************ + * NAME: Sflash_Open + * FUNCTION partition open + * PARAM: [in] unsigned char *partition_name + * RETURN: success return T_PARTITION_TABLE_INFO *pFile, fail retuen AK_NULL +**************************************************************************/ +void *partition_open(unsigned char *name); + +/************************************************************************ + * NAME: Sflash_Write + * FUNCTION partition write + * PARAM: [in] T_PARTITION_TABLE_INFO *pFile, + [in] unsigned char *data, + [in] unsigned LONG data_len + * RETURN: success handle, fail retuen null +**************************************************************************/ +int partition_write(void *handle, unsigned char *data, unsigned long data_len); + + +/************************************************************************ + * NAME: Sflash_Read + * FUNCTION partition read + * PARAM: [in] T_PARTITION_TABLE_INFO *pFile, + out] unsigned char *data, + [in] unsigned LONG data_len + * RETURN: success return 0, fail retuen -1 +**************************************************************************/ +int partition_read(void *handle, unsigned char *data, unsigned long data_len); + + +/************************************************************************ + * NAME: Partition_Get_Attr + * FUNCTION get partition ex_attr + * PARAM: [in] T_U32pFile, + [out] T_pVOID ex_arrt + * RETURN: success return 0, fail retuen -1 +**************************************************************************/ + +int partition_get_attr(void *handle, T_EX_PARTITION_CONFIG *ex_arrt); + + +/************************************************************************ + * NAME: Partition_Set_Attr + * FUNCTION set partition ex_attr + * PARAM: [in] T_U32pFile, + [out] T_pVOID ex_arrt + * RETURN: success return 0, fail retuen -1 +**************************************************************************/ +int partition_set_attr(void *handle, T_EX_PARTITION_CONFIG *ex_arrt); + + +/************************************************************************ + * NAME: Partition_Get_Partition_Table + * FUNCTION get the partition table + * PARAM: T_PARTITION_TABLE_CONFIG *part_talbe + * RETURN: success return 0, fail retuen -1 +**************************************************************************/ +//spiflashspinandpage size is diffrent +int partition_get_partition_table(T_PARTITION_TABLE_CONFIG *part_talbe, PART_MEDIUM medium_type); +int partition_get_partition_backup_table(T_PARTITION_TABLE_CONFIG *part_talbe, PART_MEDIUM medium_type); + + +/************************************************************************ + * NAME: partition_get_data_size + * FUNCTION get the partition data size + * PARAM: T_PARTITION_TABLE_CONFIG *part_talbe + * RETURN: success return data_size, fail retuen 0 +**************************************************************************/ +unsigned long partition_get_data_size(void *handle); + + +/************************************************************************ + * NAME: partition_get_ksize + * FUNCTION get partition size ,K + * PARAM: void *handle + * RETURN: success return ksize, fail retuen 0 +**************************************************************************/ + +unsigned long partition_get_ksize(void *handle); + + +/////////////////////////////////////////////////////////////////////////////////////// +//˽ӿṩ¼ʹã +//ȫ¼ʱһmacַ +//ʱbootĴС֮ǰһôλþͲһ +//ڶmacַʱҪþɵķµķ +void partition_set_partition_startpage(unsigned long start_page, PART_MEDIUM medium_type); +void partition_set_partition_startpage_spinand(unsigned long *start_page, unsigned long *backup_start_page); + + +//ӿֻӦspiflash +//Żܵ⣬¼ʱpartitionжβдռһЩʱ䣬¼ʱֻʱٽаЩдȥ +int partition_burn_partitiontab_info_init_spiflash(unsigned char burn_mode); +int partition_burn_partitiontab_info_end_spiflash(void); + + + +#endif //_FHA_BINBURN_H_ + + diff --git a/include/mach-anyka/sdfilter.h b/include/mach-anyka/sdfilter.h new file mode 100644 index 00000000..2ca56e5a --- /dev/null +++ b/include/mach-anyka/sdfilter.h @@ -0,0 +1,764 @@ +/** + * @file sdfilter.h + * @brief Anyka Sound Device Module interfaces header file. + * + * This file declare Anyka Sound Device Module interfaces.\n + * Copyright (C) 2008 Anyka (GuangZhou) Software Technology Co., Ltd. + * @author Deng Zhou + * @date 2008-04-10 + * @version V0.0.1 + * @ref + */ + +#ifndef __SOUND_FILTER_H__ +#define __SOUND_FILTER_H__ + +#include "medialib_global.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** @defgroup Audio Filter library + * @ingroup ENG + */ +/*@{*/ + +/* @{@name Define audio version*/ +/** Use this to define version string */ +/* ע⣺ṹ޸ģ޸а汾 */ +#define AUDIO_FILTER_VERSION_STRING (T_U8 *)"AudioFilter Version V1.13.09" +/** @} */ + + +#define _SD_VOLCTL_VOLDB_Q 10 +#define _SD_EQ_MAX_BANDS 10 +#define _SD_MDRC_MAX_BANDS 4 +#define _SD_REECHO_DECAY_Q 10 + +typedef enum +{ + _SD_FILTER_UNKNOWN , + _SD_FILTER_EQ , + _SD_FILTER_WSOLA , + _SD_FILTER_RESAMPLE, + _SD_FILTER_3DSOUND, + _SD_FILTER_DENOICE, + _SD_FILTER_AGC, + _SD_FILTER_VOICECHANGE, + _SD_FILTER_PCMMIXER, + _SD_FILTER_3DENHANCE, + _SD_FILTER_MVBASS, + _SD_FILTER_ASLC, + _SD_FILTER_TONE_DETECTION, + _SD_FILTER_VOLUME_CONTROL, + _SD_FILTER_REECHO, + _SD_FILTER_MDRC, + _SD_FILTER_DEVOCAL, + _SD_FILTER_TYPE_MAX +}T_AUDIO_FILTER_TYPE; + +typedef enum +{ + _SD_EQ_MODE_NORMAL, + _SD_EQ_MODE_CLASSIC, + _SD_EQ_MODE_JAZZ, + _SD_EQ_MODE_POP, + _SD_EQ_MODE_ROCK, + _SD_EQ_MODE_EXBASS, + _SD_EQ_MODE_SOFT, + _SD_EQ_USER_DEFINE, +} T_EQ_MODE; + +//to define the filter type +typedef enum +{ + FILTER_TYPE_NO , + FILTER_TYPE_HPF , + FILTER_TYPE_LPF , + FILTER_TYPE_HSF , + FILTER_TYPE_LSF , + FILTER_TYPE_PF1 //PeaKing filter +}T_EQ_FILTER_TYPE; + + +typedef enum +{ + _SD_WSOLA_0_5 , + _SD_WSOLA_0_6 , + _SD_WSOLA_0_7 , + _SD_WSOLA_0_8 , + _SD_WSOLA_0_9 , + _SD_WSOLA_1_0 , + _SD_WSOLA_1_1 , + _SD_WSOLA_1_2 , + _SD_WSOLA_1_3 , + _SD_WSOLA_1_4 , + _SD_WSOLA_1_5 , + _SD_WSOLA_1_6 , + _SD_WSOLA_1_7 , + _SD_WSOLA_1_8 , + _SD_WSOLA_1_9 , + _SD_WSOLA_2_0 +}T_WSOLA_TEMPO; + +typedef enum +{ + _SD_WSOLA_ARITHMATIC_0 , // 0:WSOLA, fast but tone bab + _SD_WSOLA_ARITHMATIC_1 // 1:PJWSOLA, slow but tone well +}T_WSOLA_ARITHMATIC; + + +typedef enum +{ + RESAMPLE_ARITHMETIC_0 = 0, // 0: ʺã̶ֻܹ֮ + RESAMPLE_ARITHMETIC_1 // 1: ʲ֮ +}RESAMPLE_ARITHMETIC; + +typedef enum +{ + _SD_OUTSR_UNKNOW = 0, + _SD_OUTSR_48KHZ = 1, + _SD_OUTSR_44KHZ, + _SD_OUTSR_32KHZ, + _SD_OUTSR_24KHZ, + _SD_OUTSR_22KHZ, + _SD_OUTSR_16KHZ, + _SD_OUTSR_12KHZ, + _SD_OUTSR_11KHZ, + _SD_OUTSR_8KHZ +}T_RES_OUTSR; + +typedef enum +{ + PITCH_NORMAL = 0, + PITCH_CHILD_VOICE , + PITCH_MACHINE_VOICE, + PITCH_ECHO_EFFECT, + PITCH_ROBOT_VOICE, + PITCH_RESERVE +}T_PITCH_MODES; + +typedef enum +{ + VOLCTL_VOL_MUTIPLE = 0, + VOLCTL_VOL_DB = 2, +}VOLCTL_VOL_MODE; + +typedef struct +{ + int num; + struct + { + int x; + int y; + }stone[10]; + int lookAheadTime; //ms + int gainAttackTime; //ms + int gainReleaseTime; //ms +}T_FILTER_MILESTONE; + +typedef struct +{ + T_U8 bands; + /* + whether need to bypass total limiter + 0: do total limit; + 1: bypass total limit + */ + T_U8 limiterBypass; + /* + whether does bandi Chorus to output + 0: bandi chorus + 1: bandi doff + */ + T_U8 bandiDoff[_SD_MDRC_MAX_BANDS]; + /* + whether does bandi's DRC bypass + 0: do bandi's DRC; + 1: bypass bandi's DRC + */ + T_U8 bandiDrcBypass[_SD_MDRC_MAX_BANDS]; + + /* set boundary freqs */ + T_U32 boundaryFreqs[_SD_MDRC_MAX_BANDS-1]; + + // define bands' drc para + int bandsLookAheadTime; //ms + struct + { + struct + { + int x; + int y; + }stone[2]; + int gainAttackTime; //ms + int gainReleaseTime; //ms + }drcband[_SD_MDRC_MAX_BANDS]; + + // define output limiter para + struct + { + struct + { + int x; + int y; + }stone[2]; + int lookAheadTime; //ms + int gainAttackTime; //ms + int gainReleaseTime; //ms + }drctotal; +}T_FILTER_MDRC_PARA; + + +typedef struct +{ + MEDIALIB_CALLBACK_FUN_MALLOC Malloc; + MEDIALIB_CALLBACK_FUN_FREE Free; + MEDIALIB_CALLBACK_FUN_PRINTF printf; + MEDIALIB_CALLBACK_FUN_FLUSH_DCACHE_RANGE flushDCache; + MEDIALIB_CALLBACK_FUN_RTC_DELAY delay; + MEDIALIB_CALLBACK_FUN_INVALID_DCACHE invDcache; +}T_AUDIO_FILTER_CB_FUNS; + +struct sd_param_eq { + T_U32 eqmode; // T_EQ_MODE + + /* + ֵ(db)ע⣺preGain ֵʽΪ (T_S16)(x.xxx*(1<<10)) + */ + T_S16 preGain; //-12 <= x.xxx <= 12 + + // For User Presets + T_U32 bands; //1~10 + T_U32 bandfreqs[_SD_EQ_MAX_BANDS]; + /* + ÿƵֵע⣺bandgains ֵʽΪ (T_S16)(x.xxx*(1<<10)) + */ + T_S16 bandgains[_SD_EQ_MAX_BANDS]; // -32.0 < x.xxx < 32.0 + /* + ÿƵQֵע⣺ + 1. bandQֵʽΪ (T_U16)(x.xxx*(1<<10)) + 2. bandQΪ0ÿڲĬֵΪ (T_U16)(1.22*(1<<10)) + 3. x.xxx < /(2*ƵƵ), x.xxxֵС64.000 + */ + T_U16 bandQ[_SD_EQ_MAX_BANDS]; // q < sr/(2*f) + T_U16 bandTypes[_SD_EQ_MAX_BANDS]; // T_EQ_FILTER_TYPE + + /* + ڵ"_SD_Filter_SetParam()"ıEQʱģʽƽɲ + */ + T_U8 smoothEna; // 0-ƽ 1-ƽ + T_U16 smoothTime; //ƽʱ(ms)0ÿڲĬֵ(256.0*1000/) + + /*** for ffeq dc_remove ***/ + T_U8 dcRmEna; + T_U32 dcfb; + + /*** for EQ aslc ***/ + T_U8 aslcEna; + T_U16 aslcLevelMax; + + /*** hw specific params ***/ + T_U8 numFrameDescriptor; // frame descriptor ĸ + T_U16 frameSize; // frame Сsamples +}; +struct sd_param_devocal { + T_U16 frameSize; // frame Сsamples + T_U16 bassFreq; // Ƶ + T_U16 trebleFreq;// Ƶ + T_U16 strength; // 1~5, bigger is more cancelling +}; +struct sd_param_wsola { + T_U32 tempo; // T_WSOLA_TEMPO + T_U32 arithmeticChoice; // T_WSOLA_ARITHMATIC +}; +struct sd_param_3dsound { + T_U8 is3DSurround; +}; +struct sd_param_resample { + //Ŀ 1:48k 2:44k 3:32k 4:24K 5:22K 6:16K 7:12K 8:11K 9:8K + T_U32 outSrindex; // T_RES_OUTSR + + //볤(bytes)openʱҪ̬ݡ + //زʱ볤Ȳֵܳ + T_U32 maxinputlen; + + // outSrindexֻenumеļϣĿenumֵ֮ʱ + // DzʵˣֱĿʵֵ8000 16000 ... + // ЧoutSrindex=0 + T_U32 outSrFree; + + T_U32 reSampleArithmetic; + T_U32 outChannel; +}; +struct sd_param_agc { + T_U16 AGClevel; // make sure AGClevel < 32767 + /* used in AGC_1 */ + T_U32 max_noise; + T_U32 min_noise; + /* used in AGC_2 */ + T_U8 noiseReduceDis; // ǷԴĽ빦 + T_U8 agcDis; // ǷԴAGC + /* + agcPostEnaagcDis==0£ǷAGC2AGC + 0ʾڿagcfilter_controlѾagcģ + 1: ʾֻҪagcgainֵҪagcagcĵߺ + */ + T_U8 agcPostEna; + T_U16 maxGain; // Ŵ + T_U16 minGain; // СŴ + T_U32 dc_freq; // hz + T_U32 nr_range; // 1~300,ԽͽЧԽ +}; +struct sd_param_nr { + T_U32 ASLC_ena; // 0:disable aslc; 1:enable aslc + T_U32 NR_Level; // 0 ~ 4 Խ,Խ +}; +struct sd_param_pitch { + T_U32 pitchMode; // T_PITCH_MODES + /* + ֻ PITCH_CHILD_VOICE==pitchMode ʱpitchTempoЧ + pitchTempo ķΧ[0-10], 0~555~10 + */ + T_U8 pitchTempo; +}; +struct sd_param_reecho { + /*ǷʹܻЧ1Ϊʹܣ0Ϊر*/ + T_S32 reechoEna; + /* + ˥ӣʽΪ (T_S32)(0.xx * (1<<_SD_REECHO_DECAY_Q)) + ҪòΪ0.32 ֵ (T_S32)(0.32 * (1<<_SD_REECHO_DECAY_Q)) + */ + T_S32 degree; //0-޻Ч + /* + ÷С0-300 + */ + T_U16 roomsize; //0-Ĭֵ(71) + /* + ʱ(ms)೤ʱʧ + ע⣺õԽҪĻԽԲ̫ + һ㽨1000ڣڴ㹻ҲôЩ + ڴ治Сֵ + */ + T_U16 reechoTime; //0-Ĭֵ(840) + /* + ǷҪԭʼͬʱ + 0: ԭʼĶǷ֮ + 1ͷ֮һ + */ + T_U8 needMainBody; //0 or 1 +}; +struct sd_param_3DEnhance { + /* + ֵ(db) + ע⣺preGain ֵʽΪ (T_S16)(x.xxx*(1<<10)) + -12 <= x.xxx <= 12 + */ + T_S16 preGain; + T_S16 cutOffFreq; + /* + 3Dȣ + ע: depthֵʽΪ (T_S16)(x.xxx*(1<<10)), + -1 < x.xxx < 1 + */ + T_S16 depth; + /*** for 3D Enhance's aslc, resvered***/ + T_U8 aslcEna; + T_U16 aslcLevelMax; +}; +struct sd_param_mvBass { + /* + ֵ(db) + ע⣺preGain ֵʽΪ (T_S16)(x.xxx*(1<<10)) + -12 <= x.xxx <= 12 + */ + T_S16 preGain; + T_S16 cutOffFreq; + /* + ǿȣ + ע: bassGain ֵʽΪ (T_S16)(x.xxx*(1<<10)), + 0 < x.xxx < 12 + */ + T_S16 bassGain; + /*** for MVBass's aslc ***/ + T_U8 aslcEna; + T_U16 aslcLevelMax; +}; +struct sd_param_aslc { + T_BOOL aslcEna; + T_U16 aslcLimitLevel; //޷ֵ + T_U16 aslcStartLevel; //Ҫ޷ʼ + /* + jointChannels: + 0: 㣬洦 + 1: ϵȡֵϲȻһ棬ͬݣ + 2: ݽѰֵ㣬Ȼһֱֵ洦 + */ + T_U16 jointChannels; + + /* + maxLeninpcmݳȣֽΪλ + ΪòʱԤȰ󳤶ȷڴ档 + ݳȷıʱηڴ棬ڴʧܵ + */ + T_U16 maxLenin; +}; +struct sd_param_volumeControl { + /* + set volume mode:: + VOLCTL_VOL_MUTIPLE: ֵ volume ֵⲿֵ + VOLCTL_VOL_DB: ֵ voldb ֵ ⲿdbֵ + */ + T_U16 setVolMode; + + /* + ֵ, (T_U16)(x.xx*(1<<10)), x.xx=[0.00~7.99]ʾ + õֵҪ1.00*(1<<10)Ϊܻᵼʧ + */ + T_U16 volume; + + /* + DB, ֵʽΪ(T_S32)(x.xx*(1<<10)), x.xx=[-60.00~8.00] + õֵҪ0dbΪܻᵼʧ + x.xxx<=-79db, x.xxx>8.0, ܻᵼ + */ + T_S32 voldb; + + /* Ϊ˷ֹ任̲pipaƽƽĹʱ */ + T_U16 volSmoothTime; //ms +}; +struct sd_param_toneDetection { + T_U32 baseFreq; +}; +struct sd_param_mdrc { + /* + maxLeninpcmݳȣֽΪλ + ΪòʱԤȰ󳤶ȷڴ档 + ݳȷıʱηڴ棬ڴʧܵ + */ + T_U16 maxLenin; +}; + +typedef struct +{ + T_U32 m_Type; //T_AUDIO_FILTER_TYPE + T_U32 m_SampleRate; //sample rate, sample per second + T_U16 m_Channels; //channel number + T_U16 m_BitsPerSample; //bits per sample + + union { + struct sd_param_eq m_eq; + struct sd_param_devocal m_devocal; + struct sd_param_wsola m_wsola; + struct sd_param_3dsound m_3dsound; + struct sd_param_resample m_resample; + struct sd_param_agc m_agc; + struct sd_param_nr m_NR; + struct sd_param_pitch m_pitch; + struct sd_param_reecho m_reecho; + struct sd_param_3DEnhance m_3DEnhance; + struct sd_param_mvBass m_mvBass; + struct sd_param_aslc m_aslc; + struct sd_param_volumeControl m_volumeControl; + struct sd_param_toneDetection m_toneDetection; + struct sd_param_mdrc m_mdrc; + }m_Private; +}T_AUDIO_FILTER_IN_INFO; + +typedef struct +{ + const char *strVersion; + T_AUDIO_CHIP_ID chip; + T_AUDIO_FILTER_CB_FUNS cb_fun; + T_AUDIO_FILTER_IN_INFO m_info; + + const T_VOID *ploginInfo; +}T_AUDIO_FILTER_INPUT; + +typedef struct +{ + T_VOID *buf_in; + T_U32 len_in; + T_VOID *meta_in; + + T_VOID *buf_out; + T_U32 len_out; + T_VOID *meta_out; + + T_VOID *buf_in2; //for mix pcm samples + T_U32 len_in2; +}T_AUDIO_FILTER_BUF_STRC; + +typedef struct +{ + T_AUDIO_FILTER_CB_FUNS cb; + T_U32 m_Type; +}T_AUDIO_FILTER_LOG_INPUT; + +////////////////////////////////////////////////////////////////////////// + +/** + * @brief ȡЧ汾Ϣ. + * @author Deng Zhou + * @date 2009-04-21 + * @param [in] T_VOID + * @return T_S8 * + * @retval Ч汾 + */ +T_S8 *_SD_GetAudioFilterVersionInfo(void); + +/** + * @brief ȡЧ汾Ϣ, ֧Щ. + * @author Tang Xuechai + * @date 2014-05-05 + * @param [in] T_AUDIO_FILTER_CB_FUNS + * @return T_S8 * + * @retval ؿ汾 + */ +T_S8 *_SD_GetAudioFilterVersions(T_AUDIO_FILTER_CB_FUNS *cb); + +/** + * @brief ͷļ汾Ƿ汾ƥ + * @author Huang Liang + * @date 2019-08-09 + * @param [in] filter_input + * @return T_S32 + * @retval T_TRUE or T_FALSE + */ +T_S32 _SD_CheckAudioFilterVersion(T_AUDIO_FILTER_INPUT *filter_input); + +/** + * @brief Ч豸. + * @author Deng Zhou + * @date 2008-04-10 + * @param [in] filter_input: + * Чṹ + * @return T_VOID * + * @retval Чڲṹָ룬ձʾʧ + */ +T_VOID *_SD_Filter_Open(T_AUDIO_FILTER_INPUT *filter_input); + +/** + * @brief Ч. + * @author Deng Zhou + * @date 2008-04-10 + * @param [in] audio_filter: + * Чڲ뱣ṹ + * @param [in] audio_filter_buf: + * bufferṹ + * @return T_S32 + * @retval Ч⴦ƵݴСbyteΪλ + */ +T_S32 _SD_Filter_Control(T_VOID *audio_filter, T_AUDIO_FILTER_BUF_STRC *audio_filter_buf); + +/** + * @brief رЧ豸. + * @author Deng Zhou + * @date 2008-04-10 + * @param [in] audio_decode: + * Чڲ뱣ṹ + * @return T_S32 + * @retval AK_TRUE : رճɹ + * @retval AK_FALSE : ر쳣 + */ +T_S32 _SD_Filter_Close(T_VOID *audio_filter); + +/** + * @brief Ч:ٶ,EQģʽ. + * m_SampleRate,m_BitsPerSample,m_Channels1Ϊ0,򲻸ıκЧ,AK_TRUE + * @author Wang Bo + * @date 2008-10-07 + * @param [in] audio_filter: + * Чڲ뱣ṹ + * @param [in] info: + * ЧϢṹ + * @return T_S32 + * @retval AK_TRUE : óɹ + * @retval AK_FALSE : 쳣 + */ +T_S32 _SD_Filter_SetParam(T_VOID *audio_filter, T_AUDIO_FILTER_IN_INFO *info); + +/** + * @brief ASLCģ޷. + * @author Tang Xuechai + * @date 2015-04-17 + * @param [in] audio_filter: Чڲ뱣ṹ + * @param [in] fmileStones: ASLC޷߲οƵӿ˵ĵ + * @return T_S32 + * @retval AK_TRUE : óɹ + * @retval AK_FALSE : 쳣 + */ +T_S32 _SD_Filter_SetAslcMileStones(T_VOID *audio_filter, T_FILTER_MILESTONE *fmileStones); + +/** + * @brief ASLCģľֵ֮. + * @author Tang Xuechai + * @date 2018-01-22 + * @param [in] audio_filter: Чڲ뱣ṹ + * @param [in] silenceLevel: ֵpcmֵСֵΪǾ + * @return T_S32 + * @retval AK_TRUE : óɹ + * @retval AK_FALSE : 쳣 + */ +T_S32 _SD_Filter_SetAslcSilenceLevel(T_VOID *audio_filter, T_U32 silenceLevel); + +/** + * @brief ASLCģľ֮ʱֵ. + * @author Tang Xuechai + * @date 2018-01-22 + * @param [in] audio_filter: Чڲ뱣ṹ + * @param [in] silenceTime: ʱֵʱ䳬ֵΪ뾲״̬ + * @return T_S32 + * @retval AK_TRUE : óɹ + * @retval AK_FALSE : 쳣 + */ +T_S32 _SD_Filter_SetAslcSilenceTime(T_VOID *audio_filter, T_U32 silenceTime); + +/** + * @brief MDRCģ޷. + * @author Tang Xuechai + * @date 2017-07-21 + * @param [in] audio_filter: Чڲ뱣ṹ + * @param [in] fmdrc: MDRCIJοƵӿ˵ĵ + * @return T_S32 + * @retval AK_TRUE : óɹ + * @retval AK_FALSE : 쳣 + */ +T_S32 _SD_Filter_SetMdrcPara(T_VOID *audio_filter, T_FILTER_MDRC_PARA *fmdrc); + +/** + * @brief ز + * @author Tang_Xuechai + * @date 2013-07-03 + * @param [in] audio_filter: + * Чڲ뱣ṹ + * @param [out] dstData + * pcm + * @param [in] srcData: + * pcm + * @param [in] srcLen + * pcmݵbyte + * @return T_S32 + * @retval >=0 : زpcmݵbyte + * @retval <0 : زʧ + */ +T_S32 _SD_Filter_Audio_Scale(T_VOID *audio_filter, T_S16 dstData[], T_S16 srcData[], T_U32 srcLen); + + +/** +* @brief EQƵתΪʱ. +* @author Tang Xuechai +* @date 2015-03-24 +* @param [in] audio_filter: +* Чڲṹ_SD_Filter_Openķָ +* @param [in] info: +* ЧϢṹ +* @return T_VOID * +* @retval EQڲȡʱָ룬ձʾʧ +*/ +T_VOID *_SD_Filter_GetEqTimePara(T_VOID *audio_filter, T_AUDIO_FILTER_IN_INFO *info); + +/** +* @brief ѵǰҪʹõEQʱݸEQ. +* @author Tang Xuechai +* @date 2015-03-24 +* @param [in] audio_filter: +* Чڲṹ_SD_Filter_Openķָ +* @param [in] peqTime: +* ʱָ +* @return T_S32 +* @retval AK_TRUE : óɹ +* @retval AK_FALSE: 쳣 +*/ +T_S32 _SD_Filter_SetEqTimePara(T_VOID *audio_filter, T_VOID *peqTime); + +/** +* @brief ͷEQʱռõĿռ. +* @author Tang Xuechai +* @date 2015-03-24 +* @param [in] audio_filter: +* Чڲṹ_SD_Filter_Openķָ +* @param [in] peqTime: +* ʱָ +* @return T_S32 +* @retval AK_TRUE : óɹ +* @retval AK_FALSE: 쳣 +*/ +T_S32 _SD_Filter_DestoryEqTimePara(T_VOID *audio_filter, T_VOID *peqTime); + + +/** + * @brief ģֵ. + * @author Tang Xuechai + * @date 2015-08-11 + * @param [in] audio_filter: Чڲ뱣ṹ + * @param [in] volume: Ŀֵ + * ֵ, (T_U16)(x.xx*(1<<10)), x.xx=[0.00~7.99]ʾ + * õֵҪ1.00*(1<<10)Ϊܻᵼʧ + * @return T_S32 + * @retval AK_TRUE : óɹ + * @retval AK_FALSE : 쳣 + */ +T_S32 _SD_Filter_SetVolume(T_VOID *audio_filter, T_U16 volume); + +/** + * @brief ģֵ. + * @author Tang Xuechai + * @date 2015-08-11 + * @param [in] audio_filter: Чڲ뱣ṹ + * @param [in] volume: ĿDBֵ + * DB, ֵʽΪ(T_S32)(x.xx*(1<<10)), x.xx=[-100.00~8.00], [-60.00~8.00]֮䲽1dbЧ + * õֵҪ0dbΪܻᵼʧ + * x.xxx<=-79db, x.xxx>8.0, ܻᵼ + * @return T_S32 + * @retval AK_TRUE : óɹ + * @retval AK_FALSE : 쳣 + */ + +T_S32 _SD_Filter_SetVolumeDB(T_VOID *audio_filter, T_S32 volume); + +/** +* @brief Ч: 仯ƽʱ +* @param [in] audio_filter: Чڲ뱣ṹ +* @param [in] stime: Ҫõƽʱ䣬λmsָ0dbҪʱ䡣 +* @return T_S32 +* @retval AK_TRUE : óɹ +* @retval AK_FALSE : 쳣 +*/ +T_S32 _SD_Filter_Volctl_SetSmoothTime(T_VOID *audio_filter, T_U32 stime); + +/** +* @brief ȡЧ: ȡǰЧֵ +* @param [in] audio_filter: Чڲ뱣ṹ +* @return T_S32 +* @retval >=0 : ȡֵ +* @retval <0: ȡʧ +*/ +T_S32 _SD_Filter_Volctl_GetCurVolume(T_VOID *audio_filter); + +const T_VOID *_SD_EQ_login(T_AUDIO_FILTER_LOG_INPUT *plogInput); +const T_VOID *_SD_3DEnhance_login(T_AUDIO_FILTER_LOG_INPUT *plogInput); +const T_VOID *_SD_3DSound_login(T_AUDIO_FILTER_LOG_INPUT *plogInput); +const T_VOID *_SD_ASLC_login(T_AUDIO_FILTER_LOG_INPUT *plogInput); +const T_VOID *_SD_mvBass_login(T_AUDIO_FILTER_LOG_INPUT *plogInput); +const T_VOID *_SD_NR_login(T_AUDIO_FILTER_LOG_INPUT *plogInput); +const T_VOID *_SD_AGC_login(T_AUDIO_FILTER_LOG_INPUT *plogInput); +const T_VOID *_SD_VolCtl_login(T_AUDIO_FILTER_LOG_INPUT *plogInput); +const T_VOID *_SD_WSOLA_login(T_AUDIO_FILTER_LOG_INPUT *plogInput); +const T_VOID *_SD_pitch_login(T_AUDIO_FILTER_LOG_INPUT *plogInput); +const T_VOID *_SD_Mixer_login(T_AUDIO_FILTER_LOG_INPUT *plogInput); +const T_VOID *_SD_Reecho_login(T_AUDIO_FILTER_LOG_INPUT *plogInput); +const T_VOID *_SD_Resample_login(T_AUDIO_FILTER_LOG_INPUT *plogInput); +const T_VOID *_SD_toneDetection_login(T_AUDIO_FILTER_LOG_INPUT *plogInput); +const T_VOID *_SD_MDRC_login(T_AUDIO_FILTER_LOG_INPUT *plogInput); +const T_VOID *_SD_Devocal_login(T_AUDIO_FILTER_LOG_INPUT *plogInput); + +#ifdef __cplusplus +} +#endif + +#endif +/* end of sdfilter.h */ +/*@}*/ diff --git a/include/mach-anyka/spinand_badblock.h b/include/mach-anyka/spinand_badblock.h new file mode 100644 index 00000000..a396e7a8 --- /dev/null +++ b/include/mach-anyka/spinand_badblock.h @@ -0,0 +1,163 @@ +#ifndef _SPINAND_BADBLOCK_H_ +#define _SPINAND_BADBLOCK_H_ + + + +#include "partition_lib.h" + + +#define HEAD_SIZE 8 //asa head info size + + +#define ASA_MAX_BLOCK_TRY 50 //define asa block max try use +#define ASA_BLOCK_COUNT 10 //define asa block max count + +#define ASA_FORMAT_NORMAL 0 +#define ASA_FORMAT_EWR 1 +#define ASA_FORMAT_RESTORE 2 + +typedef enum +{ +//nand flash + NAND_TYPE_UNKNOWN, + NAND_TYPE_SAMSUNG, + NAND_TYPE_HYNIX, + NAND_TYPE_TOSHIBA, + NAND_TYPE_TOSHIBA_EXT, + NAND_TYPE_MICRON, + NAND_TYPE_ST, + NAND_TYPE_MICRON_4K, + NAND_TYPE_MIRA, + +// spi nand flash + SPINAND_TYPE_MIRA=128, + SPINAND_TYPE_GD, +}E_NAND_TYPE; + + + + +typedef struct { + unsigned char head_str[8]; + unsigned long verify[2]; + unsigned long item_num; + unsigned long info_end; +} +T_ASA_HEAD; + +typedef struct +{ + unsigned short page_start; + unsigned short page_count; + unsigned short info_start; + unsigned short info_len; + }T_ASA_ITEM; + + + typedef struct +{ + unsigned char file_name[8]; + unsigned long file_length; + unsigned long start_page; + unsigned long end_page; +}T_ASA_FILE_INFO; + + +typedef struct tag_ASA_Param +{ + unsigned short PagePerBlock; + unsigned short BytesPerPage; + unsigned short BlockNum; //blocks of one chip +}T_ASA_PARAM; + +typedef struct tag_ASA_Block +{ + unsigned char asa_blocks[ASA_BLOCK_COUNT]; //氲ȫ + unsigned char asa_count; //ʼԺڰȫblockָȫÿ + unsigned char asa_head; //ȫµĿ + unsigned char asa_block_cnt; //ȫп + unsigned long write_time; //鱻д +}T_ASA_BLOCK; + + + +/************************************************************************ + * NAME: spinand_babblock_tbl_init + * FUNCTION bad block table init + * PARAM: [in] unsigned char burn_flag + [in] unsigned long asa_start_block + * RETURN: success return 0, fail retuen -1 +**************************************************************************/ +int spinand_babblock_tbl_init(unsigned char new_burn_mode, unsigned char burn_flag, unsigned long asa_start_block); + + +/************************************************************************ + * NAME: spinand_babblock_tbl_creat + * FUNCTION bad block table creat + * PARAM: [in] unsigned long type + [in] unsigned long asa_start_block + * RETURN: success return 0, fail retuen -1 +**************************************************************************/ +int spinand_babblock_tbl_creat(unsigned long type, unsigned long asa_start_block); + +/************************************************************************ + * NAME: spinand_set_badblock + * FUNCTION set bad block table + * PARAM: [in] unsigned long block + * RETURN: success return 0, fail retuen -1 +**************************************************************************/ +int spinand_set_badblock(unsigned long block); + +/************************************************************************ + * NAME: spinand_get_badblock + * FUNCTION get bad block table data + * PARAM: [in] unsigned long start_block + [out] unsigned char pData[] -------- buffer used to store bad blocks information data + [in] unsigned long blk_cnt + * RETURN: success return 0, fail retuen -1 +**************************************************************************/ +int spinand_get_badblock(unsigned long start_block, unsigned char pData[], unsigned long blk_cnt); + +/************************************************************************ + * NAME: spinand_is_badblock + * FUNCTION check is or not bad block + * PARAM: [in] unsigned long block + * RETURN: is return 0, fail retuen -1 +**************************************************************************/ +int spinand_is_badblock(unsigned long block); + + +/************************************************************************ + * NAME: spinand_check_babblock + * FUNCTION check is or not bad block + * PARAM: [in] unsigned long block + * RETURN: is return 0, fail retuen -1 +**************************************************************************/ +//ûл½жϴ˿Ƿ񻵿 +//ɴ˴˽ӿڻждIJ +//Դ˿ֻΪȫ¼ʱʹã +int spinand_check_babblock(unsigned long block); +int spinand_check_Initial_babblock(unsigned long block); + +/************************************************************************ + * NAME: spinand_read_asa_data + * FUNCTION read asa data + * PARAM: [in]unsigned char *data, unsigned long data_len + * RETURN: is return 0, fail retuen -1 +**************************************************************************/ +int spinand_read_asa_data(unsigned char *data, unsigned long data_len); + + +/************************************************************************ + * NAME: spinand_write_asa_data + * FUNCTION write asa data + * PARAM: unsigned char *data, unsigned long data_len + * RETURN: is return 0, fail retuen -1 +**************************************************************************/ +int spinand_write_asa_data(unsigned char *data, unsigned long data_len); + + + + +#endif + diff --git a/include/net/activity_stats.h b/include/net/activity_stats.h new file mode 100644 index 00000000..10e4c150 --- /dev/null +++ b/include/net/activity_stats.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2010 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + * Author: Mike Chan (mike@android.com) + */ + +#ifndef __activity_stats_h +#define __activity_stats_h + +#ifdef CONFIG_NET_ACTIVITY_STATS +void activity_stats_update(void); +#else +#define activity_stats_update(void) {} +#endif + +#endif /* _NET_ACTIVITY_STATS_H */ diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index d47e523c..78132a8a 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -174,8 +174,10 @@ enum { #define ESCO_2EV5 0x0100 #define ESCO_3EV5 0x0200 -#define SCO_ESCO_MASK (ESCO_HV1 | ESCO_HV2 | ESCO_HV3) -#define EDR_ESCO_MASK (ESCO_2EV3 | ESCO_3EV3 | ESCO_2EV5 | ESCO_3EV5) +#define SCO_ESCO_MASK (ESCO_HV1 | ESCO_HV2 | ESCO_HV3) +#define EDR_ESCO_MASK (ESCO_2EV3 | ESCO_3EV3 | ESCO_2EV5 | ESCO_3EV5) +#define ALL_ESCO_MASK (SCO_ESCO_MASK | ESCO_EV3 | ESCO_EV4 | ESCO_EV5 | \ + EDR_ESCO_MASK) /* ACL flags */ #define ACL_START_NO_FLUSH 0x00 @@ -1392,6 +1394,9 @@ struct hci_conn_info { __u8 out; __u16 state; __u32 link_mode; + __u32 mtu; + __u32 cnt; + __u32 pkts; }; struct hci_dev_req { diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index db1c5df4..392b2cab 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -561,7 +561,8 @@ void hci_add_sco(struct hci_conn *conn, __u16 handle); void hci_setup_sync(struct hci_conn *conn, __u16 handle); void hci_sco_setup(struct hci_conn *conn, __u8 status); -struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst); +struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, + __u16 pkt_type, bdaddr_t *dst); int hci_conn_del(struct hci_conn *conn); void hci_conn_hash_flush(struct hci_dev *hdev); void hci_conn_check_pending(struct hci_dev *hdev); @@ -570,8 +571,9 @@ struct hci_chan *hci_chan_create(struct hci_conn *conn); int hci_chan_del(struct hci_chan *chan); void hci_chan_list_flush(struct hci_conn *conn); -struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, - __u8 sec_level, __u8 auth_type); +struct hci_conn *hci_connect(struct hci_dev *hdev, int type, + __u16 pkt_type, bdaddr_t *dst, + __u8 sec_level, __u8 auth_type); int hci_conn_check_link_mode(struct hci_conn *conn); int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level); int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type); @@ -598,7 +600,7 @@ static inline void hci_conn_put(struct hci_conn *conn) if (conn->state == BT_CONNECTED) { timeo = msecs_to_jiffies(conn->disc_timeout); if (!conn->out) - timeo *= 2; + timeo *= 20; } else { timeo = msecs_to_jiffies(10); } diff --git a/include/net/bluetooth/sco.h b/include/net/bluetooth/sco.h index 1e35c436..6d1857ab 100644 --- a/include/net/bluetooth/sco.h +++ b/include/net/bluetooth/sco.h @@ -37,6 +37,7 @@ struct sockaddr_sco { sa_family_t sco_family; bdaddr_t sco_bdaddr; + __u16 sco_pkt_type; }; /* SCO socket options */ @@ -72,7 +73,8 @@ struct sco_conn { struct sco_pinfo { struct bt_sock bt; - __u32 flags; + __u16 pkt_type; + struct sco_conn *conn; }; diff --git a/include/net/tcp.h b/include/net/tcp.h index 2757a115..ed5b0c70 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1456,6 +1456,8 @@ extern struct sk_buff **tcp4_gro_receive(struct sk_buff **head, extern int tcp_gro_complete(struct sk_buff *skb); extern int tcp4_gro_complete(struct sk_buff *skb); +extern int tcp_nuke_addr(struct net *net, struct sockaddr *addr); + #ifdef CONFIG_PROC_FS extern int tcp4_proc_init(void); extern void tcp4_proc_exit(void); diff --git a/include/plat-anyka/adkey.h b/include/plat-anyka/adkey.h new file mode 100644 index 00000000..0e98c1a9 --- /dev/null +++ b/include/plat-anyka/adkey.h @@ -0,0 +1,78 @@ +/* + * include/plat-anyka/adkey.h + */ + +#ifndef __ADKEY_H +#define __ADKEY_H + +#include +#include "notify.h" + +typedef enum { + PLUGIN_NODEV = 0x0, + PLUGIN_DEV1 = 0x1, //dev1 + PLUGIN_DEV2 = 0x2, //dev2 + PLUGIN_DEV3 = 0x4, //dev3 + PLUGIN_DEV4 = 0x8, //dev4 + PLUGIN_DEV12 = PLUGIN_DEV1|PLUGIN_DEV2, //dev12 + PLUGIN_DEV13 = PLUGIN_DEV1|PLUGIN_DEV3, //dev13 + PLUGIN_DEV23 = PLUGIN_DEV2|PLUGIN_DEV3, //dev23 + PLUGIN_DEV123 = PLUGIN_DEV1|PLUGIN_DEV2|PLUGIN_DEV3, //dev123 + + PLUGIN_INVALID, +} T_ad_check; + + +#define PLUGIN_MMC PLUGIN_DEV1 //sd card +#define PLUGIN_SDIO PLUGIN_DEV2 //sdio dev +#define PLUGIN_AC PLUGIN_DEV3 //ac +#define PLUGIN_MMC_SDIO PLUGIN_DEV12 //mmc+ac +#define PLUGIN_MMC_AC PLUGIN_DEV13 //hp+mmc +#define PLUGIN_SDIO_AC PLUGIN_DEV23 //sdio+ac +#define PLUGIN_MMC_SDIO_AC PLUGIN_DEV123 //mmc+sdio+ac + +#define ADDETECT_MMC_PLUGIN ADDEDECT_DEV1_PLUGIN +#define ADDETECT_MMC_PLUGOUT ADDEDECT_DEV1_PLUGOUT +#define ADDETECT_SDIO_PLUGIN ADDEDECT_DEV2_PLUGIN +#define ADDETECT_SDIO_PLUGOUT ADDEDECT_DEV2_PLUGOUT +#define ADDETECT_AC_PLUGIN ADDEDECT_DEV3_PLUGIN +#define ADDETECT_AC_PLUGOUT ADDEDECT_DEV3_PLUGOUT + +struct adgpio_key { + int code; /* Key code */ + int min; /* min voltage */ + int max; /* max voltage */ +}; + +struct multi_addetect { + int unpress_min; + int unpress_max; + struct adgpio_key *fixkeys; + T_ad_check plugdev; +}; + +struct analog_gpio_key { + char *desc; /* key description */ + int interval; /* the value is poll adkey */ + int debounce_interval; /* press key delay */ + + struct multi_addetect *addet; + int naddet; + int nkey; /* key number */ + + int chanel; /* AD channel */ + int wakeup; /* enable ad-key wakeup from standby */ +}; + +#if defined(CONFIG_ARCH_AK39) +static inline void adkey_wakeup_enable(int en) +{ +} +#else +static inline void adkey_wakeup_enable(int en) +{ +} +#endif + +#endif /* end __ADKEY_H */ + diff --git a/include/plat-anyka/ak_camera.h b/include/plat-anyka/ak_camera.h new file mode 100644 index 00000000..79739537 --- /dev/null +++ b/include/plat-anyka/ak_camera.h @@ -0,0 +1,142 @@ +/* + * ak_camera.h - AK camera driver header file + * + * Copyright (c) 2008, Paulius Zaleckas + * Copyright (C) 2009, Darius Augulis + * + * Based on PXA camera.h file: + * Copyright (C) 2003, Intel Corporation + * Copyright (C) 2008, Guennadi Liakhovetski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __ASM_ARCH_CAMERA_H_ +#define __ASM_ARCH_CAMERA_H_ + +#include +#include +#include + +#define AK_CAM_DRV_NAME "ak_camera" +#define MAX_VIDEO_MEM 16 + +#if 0 +#define CSI_BUS_FLAGS (SOCAM_MASTER | SOCAM_HSYNC_ACTIVE_HIGH | \ + SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW | \ + SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | \ + SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_LOW | \ + SOCAM_DATAWIDTH_8) +#endif + +#define CSI_BUS_FLAGS (V4L2_MBUS_MASTER | V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ + V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | \ + V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | \ + V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_LOW) + +#define AK_CAMERA_MASTER 1 +#define AK_CAMERA_DATAWIDTH_4 2 +#define AK_CAMERA_DATAWIDTH_5 4 +#define AK_CAMERA_DATAWIDTH_8 8 +#define AK_CAMERA_DATAWIDTH_9 0x10 +#define AK_CAMERA_DATAWIDTH_10 0x20 + +#define AK_CAMERA_VSYNC_HIGH 0x100 +#define AK_CAMERA_PCLK_RISING 0x200 +#define AK_CAMERA_DATA_HIGH 0x400 + +/* Image Sensor Command Register */ +#define CICR_DATA_FMT (0x3 << 0) +#define CICR_HSCAL_EN (1 << 2) +#define CICR_VSCAL_EN (1 << 3) +#define CICR_MODE (1 << 4) +#define CICR_FULL_RANGE_YUV (1 << 5) +#define CICR_DATA_FMT_VAL(x) (((x) << 0) & CICR_DATA_FMT) + + +/* Personal */ + + +/** @{@name IMAGE sensor module register and bit map define + */ +/* image capturing command */ +#define IMG_CMD 0x0000 +/* Source/Destination image horizontal length */ +#define IMG_HINFO1 0x0004 +/* Horizontal scalling information */ +#define IMG_HINFO2 0x0008 +/* Source/Destination image vertical length */ +#define IMG_VINFO1 0x000C +/* Horizontal scalling information */ +#define IMG_VINFO2 0x0010 + +/* DMA starting address of external RAM for Y component of odd frame */ +#define IMG_YODD 0x0018 +#define IMG_UODD 0x001c +#define IMG_VODD 0x0020 +#define IMG_RGBODD 0x0024 +#define IMG_YEVE 0x0028 +#define IMG_UEVE 0x002c +#define IMG_VEVE 0x0030 +#define IMG_RGBEVE 0x0034 +/* Image sensor configuration */ +#define IMG_CONFIG 0x0040 +/* Status of the current frame */ +#define IMG_STATUS 0x0060 +/* The line number of a frame when JPEG-compressed format */ +#define IMG_NUM 0x0080 +/** @} */ + + +/** @{@name IMAGE sensor module register and bit map define + */ +/* Multiple function control register */ +#define MUL_FUN_CTL_REG (AK_VA_SYSCTRL + 0x0058) +/* Multiple function control register2 */ +#define MUL_FUN_CTL_REG2 (AK_VA_SYSCTRL + 0x0014) +/** @} */ +#define MUL_RESET_CTRL_REG (AK_VA_SYSCTRL + 0x0010) + +/* camera private cid & data defined*/ +/***********************************/ +enum camera_pcid { + PCID_CH2_OUTPUT_FMT = 1, + PCID_A_FRAME_RAW, +}; + +/* move to ispdrv_interface.h +enum camera_interface { + DVP_INTERFACE = 0, + MIPI_INTERFACE +};*/ + +struct pcid_ch2_output_fmt_data { + int type; + int width; + int height; +}; +/***********************************/ + +struct captureSync{ + unsigned long long adcCapture_bytes; + struct timeval tv; + unsigned int rate; + unsigned int frame_bits; +}; + + +/** + * struct ak_camera_pdata - AK camera platform data + * @mclk: master clock frequency in MHz units + * @flags: AK camera platform flags + */ +struct ak_camera_pdata { + unsigned long mclk; + unsigned long flags; + //enum camera_interface interface; //setup interface in sensor drivers +}; + +#endif /* __ASM_ARCH_CAMERA_H_ */ + diff --git a/include/plat-anyka/ak_crypto.h b/include/plat-anyka/ak_crypto.h new file mode 100644 index 00000000..6f7c669c --- /dev/null +++ b/include/plat-anyka/ak_crypto.h @@ -0,0 +1,118 @@ +#ifndef __AK_CRYPTO_H__ +#define __AK_CRYPTO_H__ + +/* Encryption control register */ +#define AKENC_CONTROL_REG (0x000) + +#define AK_ENC_IV_BIT_SEQ (1<<22) +#define AK_ENC_KEY_BIT_SEQ (1<<21) +#define AK_ENC_CLK_EN (1<<20) + +#define AK_ENC_OUTPUT_BYTE_SEQ (1<<19) +#define AK_ENC_OUTPUT_BIT_SEQ (1<<18) +#define AK_ENC_INPUT_BYTE_SEQ (1<<17) +#define AK_ENC_INPUT_BIT_SEQ (1<<16) + +#define AK_CRYPT_BIT_SEQ_MASK (AK_ENC_INPUT_BIT_SEQ | AK_ENC_INPUT_BYTE_SEQ | \ + AK_ENC_OUTPUT_BYTE_SEQ | AK_ENC_OUTPUT_BIT_SEQ | \ + AK_ENC_KEY_BIT_SEQ | AK_ENC_IV_BIT_SEQ) + +#define AK_CRYPT_AES_BIT_SEQ (AK_ENC_INPUT_BIT_SEQ | AK_ENC_OUTPUT_BIT_SEQ | \ + AK_ENC_IV_BIT_SEQ) +#define AK_CRYPT_DES_BIT_SEQ (AK_ENC_INPUT_BIT_SEQ | AK_ENC_OUTPUT_BIT_SEQ |\ + AK_ENC_KEY_BIT_SEQ | AK_ENC_IV_BIT_SEQ) + + +#define AK_ENC_IV_MODE (1<<14) +#define AK_ENC_MULT_GRP_CIPHER (1<<13) + +#define AK_ENC_ALG_SEL(s) ((s)<<10) +#define ENC_TYPE_ALG_DES (0b000) +#define ENC_TYPE_ALG_3DES_3KEY (0b001) +#define ENC_TYPE_ALG_3DES_2KEY (0b010) +#define ENC_TYPE_ALG_AES128 (0b011) +#define ENC_TYPE_ALG_AES192 (0b100) +#define ENC_TYPE_ALG_AES256 (0b101) + + +#define AK_ENC_WIDTH_SEL(s) ((s)<<8) +#define ENC_WIDTH_DES_64BIT (0b00) +#define ENC_WIDTH_DES_8BIT (0b01) +#define ENC_WIDTH_DES_1BIT (0b10) + +#define ENC_WIDTH_AES_128BIT (0b00) +#define ENC_WIDTH_AES_8BIT (0b01) +#define ENC_WIDTH_AES_1BIT (0b10) + + +#define AK_ENC_OPT_MODE(s) ((s)<<5) +#define ENC_WORK_MODE_ECB (0b000) +#define ENC_WORK_MODE_CBC (0b001) +#define ENC_WORK_MODE_CFB (0b010) +#define ENC_WORK_MODE_OFB (0b011) +#define ENC_WORK_MODE_CTR (0b100) + +#define AK_ENC_TIMEOUT_INT_EN (1<<4) +#define AK_ENC_INT_EN (1<<3) +#define AK_ENC_OPT_STATUS (1<<2) +#define AK_ENC_STOP (1<<1) +#define AK_ENC_START (1<<0) + + +#define AKENC_INT_STATUS_REG (0x004) +#define AK_ENC_ENCRYPT_TIMEOUT (1<<1) +#define AK_ENC_ENCRYPT_DONE (1<<0) + + +#define AKENC_TIMEOUT_REG (0x008) + +/*AKENC_GRP_INPUT_REG1: 's' equals 1, s index range:1~4*/ +#define AKENC_GRP_INPUT_REG(s) (0x00c + (((s)-1)<<2)) +#define AKENC_VEC_INPUT_REG(s) (0x01c + (((s)-1)<<2)) +#define AKENC_KEY_INPUT_REG(s) (0x02c + (((s)-1)<<2)) +#define AKENC_GRP_OUTPUT_REG(s) (0x04c + (((s)-1)<<2)) + +#define AKENC_PLAINT_ADDR_REG (0x05c) +#define AKENC_DATALEN_REG (0x060) +#define AKENC_CIPHER_ADDR_REG (0x064) + + +#define MAX_TIMEOUT (0xffffff) + +#define CRYPTO_QUEUE_LEN (5) +#define AK_CRA_PRIORITY (50) + +#define DES_KEY (8) +#define DES_KEY_2 (2*DES_KEY) +#define DES_KEY_3 (3*DES_KEY) + + +#define FLAGS_MODE_MASK (0x03ff) +#define FLAGS_WORK_MODE_MASK (0x1f) + +#define FLAGS_ECB BIT(0) +#define FLAGS_CBC BIT(1) +#define FLAGS_CFB BIT(2) +#define FLAGS_OFB BIT(3) +#define FLAGS_CTR BIT(4) + +#define FLAGS_ENCRYPT BIT(5) +#define FLAGS_DECRYPT BIT(6) + +#define FLAGS_ALG_MODE_MASK (0x7<<8) +#define FLAGS_AES BIT(8) +#define FLAGS_DES BIT(9) +#define FLAGS_3DES BIT(10) + + +enum crypto_encrypt_mode { + CRYPTO_SINGLE_GROUP_MODE, + CRYPTO_MULTI_GROUP_MODE, +}; + +struct ak_crypto_plat_data { + enum crypto_encrypt_mode encrypt_mode; +}; + +#endif + diff --git a/include/plat-anyka/ak_isp_drv.h b/include/plat-anyka/ak_isp_drv.h new file mode 100644 index 00000000..e0429105 --- /dev/null +++ b/include/plat-anyka/ak_isp_drv.h @@ -0,0 +1,1485 @@ +#ifndef _AK_ISP_DRV_H_ +#define _AK_ISP_DRV_H_ + +#include + +#define ISP_DRV_LIB_VER "ak_isp_drv_lib V3.1.7" + +enum isp_working_mode { + ISP_JPEG_MODE, //JPEG compression frame mode + ISP_YUV_OUT, //YUV single frame mode. is not support minor channel + ISP_YUV_VIDEO_OUT, //YUV video frame mode + ISP_RGB_OUT, //RGB single frame mode + ISP_RGB_VIDEO_OUT, //RGB video frame mode +}; + +enum buffer_id { + BUFFER_ONE, + BUFFER_TWO, + BUFFER_THREE, + BUFFER_FOUR, +}; + +typedef struct ak_isp_blc { + T_U16 black_level_enable; //ʹλ + T_U16 bl_r_a; //[ 0,1023] + T_U16 bl_gr_a; //[ 0,1023] + T_U16 bl_gb_a; //[ 0,1023] + T_U16 bl_b_a; //[ 0,1023] + T_S16 bl_r_offset; //[-2048,2047] + T_S16 bl_gr_offset; //[-2048,2047] + T_S16 bl_gb_offset; //[-2048,2047] + T_S16 bl_b_offset; //[-2048,2047] +}AK_ISP_BLC; + +typedef struct ak_isp_blc_attr { + T_U16 blc_mode; //0ģʽ1ֶģʽ + AK_ISP_BLC m_blc; + AK_ISP_BLC linkage_blc[9]; +}AK_ISP_BLC_ATTR; + +typedef struct { + T_U16 coef_b[10]; //[0,255] + T_U16 coef_c[10]; //[0,1023] +}lens_coef; + +typedef struct ak_isp_lsc_attr { + T_U16 enable; + //the reference point of lens correction + T_U16 xref; //[0,4096] + T_U16 yref; //[0,4096] + T_U16 lsc_shift; //[015] + lens_coef lsc_r_coef; + lens_coef lsc_gr_coef; + lens_coef lsc_gb_coef; + lens_coef lsc_b_coef; + //the range of ten segment + T_U16 range[10]; //[01023] +}AK_ISP_LSC_ATTR; + +typedef struct ak_isp_raw_lut_attr { + T_U16 raw_r[129]; //10bit + T_U16 raw_g[129]; //10bit + T_U16 raw_b[129]; //10bit + T_U16 r_key[16]; + T_U16 g_key[16]; + T_U16 b_key[16]; + T_U16 raw_gamma_enable; +}AK_ISP_RAW_LUT_ATTR; + +typedef struct ak_isp_rgb_gamma_attr { + T_U16 r_gamma[129]; //10bit + T_U16 g_gamma[129]; //10bit + T_U16 b_gamma [129]; //10bit + T_U16 r_key[16]; + T_U16 g_key[16]; + T_U16 b_key[16]; + T_U16 rgb_gamma_enable; //ʹܣһֱ +} AK_ISP_RGB_GAMMA_ATTR; + +typedef struct ak_isp_y_gamma_attr { + T_U16 ygamma[129]; //10bit + T_U16 ygamma_key[16]; //ߵĹؼ + T_U16 ygamma_uv_adjust_enable; + T_U16 ygamma_uv_adjust_level; + T_U16 ygamma_cnoise_yth1; //Ygammaɫֵ + T_U16 ygamma_cnoise_yth2; //Ygammaɫֵ + T_U16 ygamma_cnoise_slop; + T_U16 ygamma_cnoise_gain ; //UVϵ +}AK_ISP_Y_GAMMA_ATTR; + +typedef struct ak_isp_nr1 { + T_U16 nr1_enable; //ʹλ + T_U16 nr1_weight_rtbl[17]; //10bit + T_U16 nr1_weight_gtbl[17]; //10bit + T_U16 nr1_weight_btbl[17]; //10bit + T_U16 nr1_k; //[0,15] + T_U16 nr1_lc_lut[17]; //10bit + T_U16 nr1_lc_lut_key[16]; + T_U16 nr1_calc_g_k; + T_U16 nr1_calc_r_k; + T_U16 nr1_calc_b_k; +}AK_ISP_NR1; + +typedef struct ak_isp_s_nr1_attr { + T_U16 nr1_mode; //nr1 ģʽԶģʽ + AK_ISP_NR1 manual_nr1; + AK_ISP_NR1 linkage_nr1[9]; // +}AK_ISP_NR1_ATTR; + +typedef struct ak_isp_nr2 { + T_U16 nr2_enable; + T_U16 nr2_weight_tbl[17]; //10bit + T_U16 nr2_k; //[0,15] + T_U16 nr2_calc_y_k; + T_U16 y_dpc_enable; + T_U16 y_dpc_th; + T_U16 y_black_dpc_enable; + T_U16 y_white_dpc_enable; +}AK_ISP_NR2; + +typedef struct ak_isp_nr2_attr { + T_U16 nr2_mode; //ֶģʽ + AK_ISP_NR2 manual_nr2; + AK_ISP_NR2 linkage_nr2[9]; +}AK_ISP_NR2_ATTR; + +typedef struct ak_isp_3d_nr_ref_attr { + T_U32 yaddr_3d; + T_U32 ysize_3d; + T_U32 uaddr_3d; + T_U32 usize_3d; + T_U32 vaddr_3d; + T_U32 vsize_3d; +}AK_ISP_3D_NR_REF_ATTR; + +#if 0 +typedef struct ak_isp_3d_nr { + T_U16 uv_min_enable; + T_U16 tnr_y_enable; + T_U16 tnr_uv_enable; + + T_U16 mc_VAR_y_th; //[0, 4095] + T_U16 t_filter_y_k; //[0-127] + T_U16 s_filter_var_gain; //0-15 + T_U16 s_filter_var_th; //0-255 + + T_U16 s_filter_y_th1; //0-1023 + T_U16 s_filter_y_th2; //0-1023 + T_U16 s_filter_y_k1; //0-128 + T_U16 s_filter_y_k2; //0-128 + T_U16 s_filter_y_kstep; //0-31 + + T_U16 mc_SAD_uv_th; //[0, 4095] + T_U16 t_filter_uv_k; //[0, 127] + T_U16 s_filter_uv_th1; //[0, 128] + T_U16 s_filter_uv_th2; //[0, 128] + T_U16 s_filter_uv_k1; //[0,128] + T_U16 s_filter_uv_k2; //[0,128] + T_U16 s_filter_uv_kstep; //[0,31] + + T_U16 MD_th; //˶ֵ [0-127] + T_U16 updata_ref_y; //Y + T_U16 updata_ref_uv; //uv + T_U16 tnr_refFrame_format; //ο֡ĸʽ + + T_U16 tnr_skin_detect; + T_U16 tnr_skin_k; +}AK_ISP_3D_NR; +#endif + +typedef struct ak_isp_3d_nr { + T_U16 uv_min_enable; + T_U16 tnr_y_enable; + T_U16 tnr_uv_enable; + T_U16 updata_ref_y; //Y + T_U16 updata_ref_uv; //uv + T_U16 tnr_refFrame_format; //ο֡ĸʽ + T_U16 tnr_t_y_ex_k_cfg;//lz0499 9_6 + T_U16 y_2dnr_enable; + T_U16 uv_2dnr_enable; + + T_U16 uvnr_k; //[0, 15] + T_U16 uvlp_k; //[0, 15] + T_U16 t_uv_k; //[0, 127] + T_U16 t_uv_minstep; //[0,31] + T_U16 t_uv_mf_th1; //[0, 8191] + T_U16 t_uv_mf_th2; //[0, 8191] + T_U16 t_uv_diffth_k1; //[0, 255] + T_U16 t_uv_diffth_k2; //[0, 255] + T_U16 t_uv_diffth_slop;//[0, 255] + T_U16 t_uv_mc_k; //[0-31] + T_U16 t_uv_ac_th; //[0, 1023] + + T_U16 ynr_weight_tbl[17]; + T_U16 ynr_calc_k; + T_U16 ynr_k; //[0, 15] + T_U16 ynr_diff_shift; //[0,1] + T_U16 ylp_k; //[0, 15] + T_U16 t_y_th1; //[0, 255] + T_U16 t_y_k1; //[0, 127] + T_U16 t_y_k2; //[0, 127] + T_U16 t_y_kslop; //[0, 127] + T_U16 t_y_minstep; //[0-31] + T_U16 t_y_mf_th1; //[0, 8191] + T_U16 t_y_mf_th2; //[0, 8191] + T_U16 t_y_diffth_k1; //[0, 255] + T_U16 t_y_diffth_k2; //[0, 255] + T_U16 t_y_diffth_slop;//[0, 255] + T_U16 t_y_mc_k; //[0-31] + T_U16 t_y_ac_th; //[0, 1023] + + T_U32 md_th; //[0, 65535] ˶ֵ [0-127] +}AK_ISP_3D_NR; + +typedef struct ak_isp_3d_nr_attr { + T_U16 _3d_nr_mode; + AK_ISP_3D_NR manual_3d_nr; + AK_ISP_3D_NR linkage_3d_nr[9]; +}AK_ISP_3D_NR_ATTR; + +typedef struct ak_isp_3d_nr_stat_info { + T_U16 MD_stat_max; + T_U16 MD_stat[24][32]; //˶ֿlz0499 9_12 + T_U32 MD_level; //˶ +}AK_ISP_3D_NR_STAT_INFO; + +typedef struct ak_isp_gb { + + T_U16 gb_enable; //ʹλ + T_U16 gb_en_th; //[0,255] + T_U16 gb_kstep; //[0,15] + T_U16 gb_threshold; //[0,1023 +} AK_ISP_GB; + +typedef struct ak_isp_gb_attr { + T_U16 gb_mode; //ģʽѡֶ + AK_ISP_GB manual_gb; + AK_ISP_GB linkage_gb[9]; +} AK_ISP_GB_ATTR; + +typedef struct ak_isp_demo_attr { + T_U16 dm_HV_th; //бϵ + T_U16 dm_rg_thre; //[0 1023] + T_U16 dm_bg_thre; //[0 1023] + T_U16 dm_hf_th1; //[0, 1023] + T_U16 dm_hf_th2; //[0, 1023] + + T_U16 dm_rg_gain; //[0 255] + T_U16 dm_bg_gain; //[0 255] + T_U16 dm_gr_gain; //[0 255] + T_U16 dm_gb_gain; //[0 255] + //T_U16 cfa_mode; +}AK_ISP_DEMO_ATTR; + +typedef struct ak_isp_ccm { + T_U16 cc_enable; //ɫУʹ + T_U16 cc_cnoise_yth; //ȿ + T_U16 cc_cnoise_gain; //ȿ + T_U16 cc_cnoise_slop; //ȿ + T_S16 ccm[3][3]; //[-2048, 2047] +}AK_ISP_CCM; + +typedef struct ak_isp_ccm_attr { + T_U16 cc_mode; //ɫУֶ + AK_ISP_CCM manual_ccm; + AK_ISP_CCM ccm[4]; //ĸ +}AK_ISP_CCM_ATTR; + +typedef struct ak_isp_wdr { + T_U16 hdr_uv_adjust_level; //uv̶, [0,31] + T_U16 hdr_cnoise_suppress_slop; //б + T_U16 wdr_enable; + + T_U16 wdr_th1; //0-1023 + T_U16 wdr_th2; //0-1023 + T_U16 wdr_th3; //0-1023 + T_U16 wdr_th4; //0-1023 + T_U16 wdr_th5; //0-1023 + + //T_U16 wdr_light_weight; + + T_U16 area_tb1[65]; // 10bit + T_U16 area_tb2[65]; // 10bit + T_U16 area_tb3[65]; // 10bit + T_U16 area_tb4[65]; // 10bit + T_U16 area_tb5[65]; // 10bit + T_U16 area_tb6[65]; // 10bit + + T_U16 area1_key[16]; + T_U16 area2_key[16]; + T_U16 area3_key[16]; + T_U16 area4_key[16]; + T_U16 area5_key[16]; + T_U16 area6_key[16]; + + T_U16 hdr_uv_adjust_enable; //uvʹ + T_U16 hdr_cnoise_suppress_yth1; //ɫֵ1 + T_U16 hdr_cnoise_suppress_yth2; //ɫֵ2 + T_U16 hdr_cnoise_suppress_gain; //ɫ +}AK_ISP_WDR; + +typedef struct ak_isp_wdr_attr { + T_U16 wdr_mode; //ģʽѡֶ + AK_ISP_WDR manual_wdr; + AK_ISP_WDR linkage_wdr[9]; +}AK_ISP_WDR_ATTR; + +typedef struct ak_isp_wdr_ex_attr { + T_U16 hdr_blkW; + T_U16 hdr_blkH; + T_U16 hdr_reverseW_g; //[0,511] + T_U16 hdr_reverseW_shift; //[0,7]; + T_U16 hdr_reverseH_g; //[0,511] + T_U16 hdr_reverseH_shift; //[0,7] + T_U16 hdr_weight_g; //[0,511] + T_U16 hdr_weight_shift; //[0,7] + T_U16 hdr_weight_k; //[0,15] +}AK_ISP_WDR_EX_ATTR; + +typedef struct ak_isp_sharp { + + T_U16 mf_hpf_k; //[0,127] + T_U16 mf_hpf_shift; //[0,15] + + T_U16 hf_hpf_k; //[0,127] + T_U16 hf_hpf_shift; //[0,15] + + T_U16 sharp_method; //[0,3] + T_U16 sharp_skin_gain_weaken; //[03] + + T_U16 sharp_skin_gain_th; //[0, 255] + T_U16 sharp_skin_detect_enable; + T_U16 ysharp_enable; //[0,1] + T_S16 MF_HPF_LUT[256]; //[-256,255] + T_S16 HF_HPF_LUT[256]; //[-256,255] + T_U16 MF_LUT_KEY[16]; + T_U16 HF_LUT_KEY[16]; +}AK_ISP_SHARP; + +typedef struct ak_isp_sharp_attr{ + T_U16 ysharp_mode; + AK_ISP_SHARP manual_sharp_attr; + AK_ISP_SHARP linkage_sharp_attr[9]; +}AK_ISP_SHARP_ATTR; + +typedef struct ak_isp_sharp_ex_attr { + T_S16 mf_HPF[6]; // + //M13,M14,M15, + //M11,M12,M14, + //M10,M11,M13, + T_S16 hf_HPF[3]; // + //M22 M21,M22, + //M21,M20, M21, + T_U16 sharp_skin_max_th; //[0, 255] + T_U16 sharp_skin_min_th; //[0, 255] + T_U16 sharp_skin_v_max_th; //[0, 255] + T_U16 sharp_skin_v_min_th; //[0, 255] + T_U16 sharp_skin_y_max_th; //[0, 255] + T_U16 sharp_skin_y_min_th; //[0, 255] + //U16 mf_hpf_k; + //U16 sharp_method; +}AK_ISP_SHARP_EX_ATTR; + +typedef struct ak_isp_saturation { + T_U16 SE_enable; // ʹλ + T_U16 SE_th1; //[0, 1023] + T_U16 SE_th2; //[0, 1023] + T_U16 SE_th3; //[0, 1023] + T_U16 SE_th4; //[0, 1023] + T_U16 SE_scale_slop1; //[0, 255] + T_U16 SE_scale_slop2; //[0, 255] + T_U16 SE_scale1; //[0,255] + T_U16 SE_scale2; //[0,255] + T_U16 SE_scale3; //[0,255] +}AK_ISP_SATURATION; + +typedef struct ak_isp_saturation_attr { + T_U16 SE_mode; //Ͷģʽ + AK_ISP_SATURATION manual_sat; + AK_ISP_SATURATION linkage_sat[9]; +}AK_ISP_SATURATION_ATTR; + +typedef struct ak_isp_contrast { + T_U16 y_contrast; //[0,511] + T_S16 y_shift; //[0, 511] +}AK_ISP_CONTRAST; + +typedef struct ak_isp_auto_contrast +{ + T_U16 dark_pixel_area; //[0, 511] + T_U16 dark_pixel_rate; //[1, 256] + T_U16 shift_max; //[0, 127] +}AK_ISP_AUTO_CONTRAST; + +typedef struct ak_isp_contrast_ATTR { + T_U16 cc_mode; //ģʽѡֶ + AK_ISP_CONTRAST manual_contrast; + AK_ISP_AUTO_CONTRAST linkage_contrast[9]; +}AK_ISP_CONTRAST_ATTR; + +typedef struct ak_isp_fcs { + T_U16 fcs_th; //[0, 255] + T_U16 fcs_gain_slop; //[0,63] + T_U16 fcs_enable; //ʹλ + T_U16 fcs_uv_nr_enable; //ʹλ + T_U16 fcs_uv_nr_th; //[0, 1023] +}AK_ISP_FCS; + +typedef struct ak_isp_fcs_attr { + T_U16 fcs_mode; //ģʽѡֶ + AK_ISP_FCS manual_fcs; + AK_ISP_FCS linkage_fcs[9]; +}AK_ISP_FCS_ATTR; + +typedef struct ak_isp_hue { + T_U16 hue_sat_en; //hueʹ + T_S8 hue_lut_a[65]; //[-128, 127] + T_S8 hue_lut_b[65]; //[-128, 127] + T_U8 hue_lut_s[65]; //[0, 255] +}AK_ISP_HUE; + +typedef struct ak_isp_hue_attr { + T_U16 hue_mode; //ֶ + AK_ISP_HUE manual_hue; + AK_ISP_HUE hue[4]; //ĸ +}AK_ISP_HUE_ATTR; + +typedef struct ak_isp_rgb2yuv_attr { + T_U16 mode; //bt601 bt709 +}AK_ISP_RGB2YUV_ATTR; + +typedef struct ak_isp_effect_attr { + T_U16 y_a; // [0, 255] + T_S16 y_b; //[-128, 127] + T_S16 uv_a; //[-256, 255] + T_S16 uv_b; //[-256, 255] + T_U16 dark_margin_en; //ڱʹ +}AK_ISP_EFFECT_ATTR; + +typedef struct ak_isp_ddpc { + T_U16 ddpc_enable; //̬ʹλ + T_U16 ddpc_th; //10bit + T_U16 white_dpc_enable; //׵ʹλ + T_U16 black_dpc_enable; //ڵʹλ +}AK_ISP_DDPC; + +typedef struct ak_isp_ddpc_attr { + T_U16 ddpc_mode; //ģʽѡֶ + AK_ISP_DDPC manual_ddpc; + AK_ISP_DDPC linkage_ddpc[9]; +}AK_ISP_DDPC_ATTR; + +typedef struct ak_isp_sdpc_attr { + T_U32 sdpc_enable; //̬ʹλ + + /* ֵ̬Ϊ1024 + ݸʽ{6h0 ,y_position[9:0],5'h0,x_position[10:0]} */ + T_U32 sdpc_table[1024]; +}AK_ISP_SDPC_ATTR; + +typedef struct ak_isp_af_attr { + T_U16 af_win0_left; //[0, 1024] lz0499 9_6 + T_U16 af_win0_right; //[0, 1024] + T_U16 af_win0_top; //[0, 768] + T_U16 af_win0_bottom; //[0, 768] + + T_U16 af_win1_left; //[0, 1024] + T_U16 af_win1_right; //[0, 1024] + T_U16 af_win1_top; //[[0, 768] + T_U16 af_win1_bottom; //[0, 768] + + T_U16 af_win2_left; //[0, 1024] + T_U16 af_win2_right; //[0, 1024] + T_U16 af_win2_top; //[0, 768] + T_U16 af_win2_bottom; //[0, 768] + + T_U16 af_win3_left; //[0, 1024] + T_U16 af_win3_right; //[0, 1024] + T_U16 af_win3_top; //[0, 768] + T_U16 af_win3_bottom; //[0, 768] + + T_U16 af_win4_left; //[0, 1024] + T_U16 af_win4_right; //[0, 1024] + T_U16 af_win4_top; //[0, 768] + T_U16 af_win4_bottom; //[0, 768] + + T_U16 af_th; //[0, 128] +}AK_ISP_AF_ATTR; + +typedef struct ak_isp_af_stat_info { + T_U32 af_statics[5]; //ͳƽ +}AK_ISP_AF_STAT_INFO; + +typedef struct ak_isp_weight_attr { + T_U16 zone_weight[8][16]; //Ȩϵ +}AK_ISP_WEIGHT_ATTR; + +typedef struct ak_isp_wb_type_attr { + T_U16 wb_type; +}AK_ISP_WB_TYPE_ATTR; + +typedef struct ak_isp_awb_algo { + short current_r_gain; + short current_b_gain; + short current_g_gain; + short target_r_gain; + short target_b_gain; + short target_g_gain; + short calc_r_gain; + short calc_b_gain; + short calc_g_gain; +}AK_ISP_AWB_ALGO; + +typedef struct ak_isp_mwb_attr { + T_U16 r_gain; + T_U16 g_gain; + T_U16 b_gain; + T_S16 r_offset; + T_S16 g_offset; + T_S16 b_offset; +}AK_ISP_MWB_ATTR; + +typedef struct ak_isp_awb_attr { + T_U16 g_weight[16]; + T_U16 y_low; //y_low<=y_high + T_U16 y_high; + T_U16 err_est; + T_U16 gr_low[10]; //gr_low[i]<=gr_high[i] + T_U16 gb_low[10]; //gb_low[i]<=gb_high[i] + T_U16 gr_high[10]; + T_U16 gb_high[10]; + T_U16 rb_low[10]; //rb_low[i]<=rb_high[i] + T_U16 rb_high[10]; + + //awbҪõIJ + T_U16 auto_wb_step; //ƽⲽ + T_U16 total_cnt_thresh; //ظֵ + T_U16 colortemp_stable_cnt_thresh; //ȶ֡֡һΪɫ¸ı + T_U16 colortemp_envi[10]; +}AK_ISP_AWB_ATTR; + +typedef struct ak_isp_awb_ctrl { + int rgain_max; + int rgain_min; + int ggain_max; + int ggain_min; + int bgain_max; + int bgain_min; + int rgain_ex; + int bgain_ex; +}AK_ISP_AWB_CTRL; + +typedef struct ak_isp_awb_ex_attr { + int awb_ex_ctrl_enable; + AK_ISP_AWB_CTRL awb_ctrl[10]; +}AK_ISP_AWB_EX_ATTR; + +typedef struct ak_isp_awb_stat_info { + //ƽͳƽ + T_U32 total_R[10]; + T_U32 total_G[10]; + T_U32 total_B[10]; + T_U32 total_cnt[10]; + //Զƽ㷨İƽֵ + T_U16 r_gain; + T_U16 g_gain; + T_U16 b_gain; + T_S16 r_offset; + T_S16 g_offset; + T_S16 b_offset; + T_U16 current_colortemp_index; //ɫ±ǣDz滷仯ɫָꡣ + T_U16 colortemp_stable_cnt[10]; //ÿһɫȶ֡ +}AK_ISP_AWB_STAT_INFO; + +typedef struct ak_isp_wb_gain { + T_U16 r_gain; + T_U16 g_gain; + T_U16 b_gain; + T_S16 r_offset; + T_S16 g_offset; + T_S16 b_offset; +}AK_ISP_WB_GAIN; + +typedef struct ak_isp_raw_hist_attr { + T_U16 enable; +}AK_ISP_RAW_HIST_ATTR; + +typedef struct ak_isp_raw_hist_stat_info { + T_U32 raw_g_hist[256]; + T_U32 raw_g_total; +}AK_ISP_RAW_HIST_STAT_INFO; + +typedef struct ak_isp_rgb_hist_attr { + T_U16 enable; +}AK_ISP_RGB_HIST_ATTR; + +typedef struct ak_isp_rgb_hist_stat_info { + T_U32 rgb_hist[256]; + T_U32 rgb_total; +}AK_ISP_RGB_HIST_STAT_INFO; + +typedef struct ak_isp_yuv_hist_attr { + T_U16 enable; +}AK_ISP_YUV_HIST_ATTR; + +typedef struct ak_isp_yuv_hist_stat_info { + T_U32 y_hist[256]; + T_U32 y_total; +}AK_ISP_YUV_HIST_STAT_INFO; + +typedef struct ak_isp_exp_type { + T_U16 exp_type; +}AK_ISP_EXP_TYPE; + +typedef struct ak_isp_manual_ae { + T_U32 exp_time; + T_U32 a_gain; + T_U32 d_gain; + T_U32 isp_d_gain; +}AK_ISP_MAE_ATTR; + +typedef struct ak_isp_ae_attr { + T_U32 exp_time_max; //عʱֵ + T_U32 exp_time_min; //عʱСֵ + T_U32 d_gain_max; //ֵ + T_U32 d_gain_min; //Сֵ + T_U32 isp_d_gain_min; //ispС + T_U32 isp_d_gain_max; //ispֵ + T_U32 a_gain_max; //ģֵ + T_U32 a_gain_min; //ģСֵ + T_U32 exp_step; //ûع + T_U32 exp_stable_range; //ȶΧ + T_U32 target_lumiance; //Ŀ + T_U32 envi_gain_range[10][2]; + T_U32 hist_weight[16]; + T_U32 OE_suppress_en; + T_U32 OE_detect_scope; //[0,255] + T_U32 OE_rate_max; //[0, 255] + T_U32 OE_rate_min; //[0, 255] +}AK_ISP_AE_ATTR; + +typedef struct ak_isp_ae_run_info { + T_U8 current_calc_avg_lumi; //ڵļֵ + T_U8 current_calc_avg_compensation_lumi; //عⲹֵ + + T_U8 current_darked_flag; //ҹı + + T_S32 current_a_gain; //ģֵ + T_S32 current_d_gain; //ֵ + T_S32 current_isp_d_gain; //ispֵ + T_S32 current_exp_time; //عʱֵ + + T_U32 current_a_gain_step; //ڵģIJ + T_U32 current_d_gain_step; //IJ + T_U32 current_isp_d_gain_step; //ispIJ + T_U32 current_exp_time_step; //عʱIJ +}AK_ISP_AE_RUN_INFO; + +typedef struct ak_isp_aec_algo { + T_U32 aec_status; //ԶعƵ״̬ȶ߲ȶ + T_U32 aec_locked; //Զع + + T_U8 exp_time_need_updata; //Ҫ£iicд + T_U8 a_gain_need_updata; //Ҫ£iicд + T_U8 d_gain_need_updata; //Ҫ£ֻҪʱд + T_U8 isp_d_gain_need_updata; //Ҫ + T_U8 lock_range; +}AK_ISP_AEC_ALGO; + +//֡ʿƽṹ +typedef struct ak_isp_frame_rate_attr { + T_U32 hight_light_frame_rate ; + T_U32 hight_light_max_exp_time ; + T_U32 hight_light_to_low_light_gain; + T_U32 low_light_frame_rate; + T_U32 low_light_max_exp_time; + T_U32 low_light_to_hight_light_gain; +}AK_ISP_FRAME_RATE_ATTR; + +typedef struct ak_isp_misc_attr { + T_U16 hsyn_pol; + T_U16 vsync_pol; + T_U16 pclk_pol; + T_U16 test_pattern_en; + T_U16 test_pattern_cfg; + T_U16 cfa_mode; + T_U16 inputdataw; + T_U16 one_line_cycle; + T_U16 hblank_cycle; + T_U16 frame_start_delay_en; + T_U16 frame_start_delay_num; + T_U16 flip_en; + T_U16 mirror_en; + T_U16 twoframe_merge_en;//lz0499 9_12 + T_U16 mipi_line_end_sel; //lz0499 9_21 + T_U16 mipi_line_end_cnt_en_cfg; //lz0499 9_21 + T_U16 mipi_count_time; //lz0499 9_21 +} AK_ISP_MISC_ATTR; + +typedef struct ak_isp_mask_area { + T_U16 start_xpos; + T_U16 end_xpos; + T_U16 start_ypos; + T_U16 end_ypos; + T_BOOL enable; +}AK_ISP_MASK_AREA; + +typedef struct ak_isp_mask_area_attr { + AK_ISP_MASK_AREA mask_area[8]; +}AK_ISP_MASK_AREA_ATTR; + +typedef struct ak_isp_main_chan_mask_area_attr { + AK_ISP_MASK_AREA mask_area[4]; +}AK_ISP_MAIN_CHAN_MASK_AREA_ATTR; + +typedef struct ak_isp_sub_chan_mask_area_attr { + AK_ISP_MASK_AREA mask_area[4]; +}AK_ISP_SUB_CHAN_MASK_AREA_ATTR; + +typedef struct ak_isp_mask_color_attr { + T_U8 color_type; //0:ָڵɫ1 + T_U8 mk_alpha; + T_U8 y_mk_color; + T_U8 u_mk_color; + T_U8 v_mk_color; +}AK_ISP_MASK_COLOR_ATTR; + +//enum for sub sample +typedef enum { + SUBSMP_1X, /*no sub sample*/ + SUBSMP_2X, /*sub sample 1/2 * 1/2 */ + SUBSMP_4X, /*sub sample 1/4 * 1/4 */ + SUBSMP_8X /*sub sample 1/8 * 1/8 */ +} T_SUBSMP_RTO; + +typedef enum mask_num{ + MAIN_CHAN_ONE=0, + MAIN_CHAN_TWO, + MAIN_CHAN_THREE, + MAIN_CHAN_FOURE, + SUB_CHAN_ONE, + SUB_CHAN_TWO, + SUB_CHAN_THREE, + SUB_CHAN_FOURE, +}MASK_NUM; + +typedef enum osd_channel { + OSD_CHN0 = 0, + OSD_CHN1, + OSD_CHN2, + OSD_CHN_NUM +} OSD_CHANNEL; + +typedef struct ak_isp_osd_color_table_attr { + T_U32 color_table[16]; +}AK_ISP_OSD_COLOR_TABLE_ATTR; + + +// lz0499 9_26 have some problem need to fix +typedef struct ak_isp_osd_context_attr { + OSD_CHANNEL chn; + T_U32 *osd_context_addr; + T_U32 osd_width; + T_U32 osd_height; + T_U16 start_xpos; + T_U16 start_ypos; + T_U16 alpha; + T_BOOL enable; +}AK_ISP_OSD_CONTEXT_ATTR; + +typedef struct ak_isp_osd_mem_attr { + OSD_CHANNEL chn; + T_U8 *dma_paddr; + T_U8 *dma_vaddr; + T_U32 size; +}AK_ISP_OSD_MEM_ATTR; + +typedef enum ak_isp_pclk_polar { + POLAR_ERR = 0, + POLAR_RISING, + POLAR_FALLING, +}AK_ISP_PCLK_POLAR; + +int ak_isp_vi_start_capturing(void); +int ak_isp_vi_stop_capturing(void); +int ak_isp_vi_set_input_size(int width, int height); +int ak_isp_vi_set_crop(int sx, int sy, int width, int height); +int ak_isp_vi_apply_mode(enum isp_working_mode); +int ak_isp_vo_set_misc_attr(AK_ISP_MISC_ATTR *p_misc); +int ak_isp_vo_get_misc_attr(AK_ISP_MISC_ATTR *p_misc); +int ak_isp_vo_get_inputdataw(void); + +int ak_isp_set_flip_mirror(int flip_en, int mirror_en); +void ak_isp_get_flip_mirror(int * flip_en, int * mirror_en, int * height_block_num); + +int ak_isp_vo_set_main_channel_scale(int width, int height); +int ak_isp_vo_get_main_channel_scale(int *width, int *height); + +int ak_isp_vo_set_sub_channel_scale(int width, int height); +int ak_isp_vo_get_sub_channel_scale(int *width, int *height); + +int ak_isp_vo_check_irq_status (void); +int ak_isp_vo_clear_irq_status(int bit); +int ak_isp_vo_enable_irq_status(int bit); +int ak_isp_vo_set_cfg_reg(int regaddr, int value, int bitmask); + +int ak_isp_vo_enable_buffer(enum buffer_id id); +int ak_isp_vo_disable_buffer(enum buffer_id id); +int ak_isp_vo_set_buffer_addr(enum buffer_id id, + unsigned long yaddr_main_chan_addr, unsigned long yaddr_sub_chan_addr/*,unsigned long yaddr_sub_chan3_addr*/); +int ak_isp_vo_get_using_frame_buf_id(void); +int ak_isp_vo_update_setting(void); +int ak_isp_is_continuous(void); + +//int ak_isp_vpp_set_osd(AK_ISP_OSD_ATTR *p_osd); + +//int ak_isp_vpp_set_occlusion_attr(AK_ISP_OCCLUSION_ATTR *p_occ); +//int ak_isp_vpp_occlusion_color_attr(AK_ISP_OCCLUSION_COLOR *p_occ_color); + +/** + * @brief: set blc param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *p_blc: blc param + */ +int ak_isp_vp_set_blc_attr(AK_ISP_BLC_ATTR *p_blc); + +/** + * @brief: get blc param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_blc: blc param + */ +int ak_isp_vp_get_blc_attr(AK_ISP_BLC_ATTR *p_blc); + +/** + * @brief: set lsc param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_lsc: lsc param + */ +int ak_isp_vp_set_lsc_attr(AK_ISP_LSC_ATTR *p_lsc); + +/** + * @brief: get lsc param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_lsc: lsc param + */ +int ak_isp_vp_get_lsc_attr(AK_ISP_LSC_ATTR *p_lsc); + +/** + * @brief: set rgb gamma param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *p_rgb_gamma: rgb gamma param + */ +int ak_isp_vp_set_rgb_gamma_attr(AK_ISP_RGB_GAMMA_ATTR *p_rgb_gamma); + +/** + * @brief: get rgb gamma param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_rgb_gamma: rgb gamma param + */ +int ak_isp_vp_get_rgb_gamma_attr(AK_ISP_RGB_GAMMA_ATTR *p_rgb_gamma); + +/** + * @brief: set y gamma param + * @author: lz + * @date: 2016-8-26 + * @param [in] *p_y_gamma: y gamma param + */ +int ak_isp_vp_set_y_gamma_attr(AK_ISP_Y_GAMMA_ATTR *p_y_gamma); + +/** + * @brief: get y gamma param + * @author: lz + * @date: 2016-8-26 + * @param [out] *p_y_gamma: y gamma param + */ +int ak_isp_vp_get_y_gamma_attr(AK_ISP_Y_GAMMA_ATTR *p_y_gamma); + +/** + * @brief: set raw gamma param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *p_raw_lut: raw gamma param + */ +int ak_isp_vp_set_raw_lut_attr(AK_ISP_RAW_LUT_ATTR *p_raw_lut); + +/** + * @brief: get raw gamma param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_raw_lut: raw gamma param + */ +int ak_isp_vp_get_raw_lut_attr(AK_ISP_RAW_LUT_ATTR *p_raw_lut); + +/** + * @brief: set dpc param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *p_dpc: dpc param + */ +int ak_isp_vp_set_dpc_attr(AK_ISP_DDPC_ATTR *p_dpc); + +/** + * @brief: get dpc param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_dpc: dpc param + */ +int ak_isp_vp_get_dpc_attr(AK_ISP_DDPC_ATTR *p_dpc); + +/** + * @brief: set sdpc param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *p_sdpc: sdpc param + */ +int ak_isp_vp_set_sdpc_attr(AK_ISP_SDPC_ATTR *p_sdpc); + +/** + * @brief: get sdpc param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_sdpc: sdpc param + */ +int ak_isp_vp_get_sdpc_attr(AK_ISP_SDPC_ATTR *p_sdpc); + +/** + * @brief: set raw noise remove param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *nr1: raw noise remove param + */ +int ak_isp_vp_set_nr1_attr(AK_ISP_NR1_ATTR *p_nr1); + +/** + * @brief: get raw noise remove param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *nr1: raw noise remove param + */ +int ak_isp_vp_get_nr1_attr(AK_ISP_NR1_ATTR *p_nr1); + +/** + * @brief: set green balance param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *p_gb: green balance param + */ +int ak_isp_vp_set_gb_attr(AK_ISP_GB_ATTR *p_gb); + +/** + * @brief: get green balance param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_gb: green balance param + */ +int ak_isp_vp_get_gb_attr(AK_ISP_GB_ATTR *p_gb); + +/** + * @brief: set demosaic param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *p_demo: demosaic param + */ +int ak_isp_vp_set_demo_attr(AK_ISP_DEMO_ATTR *p_demo); + +/** + * @brief: get demosaic param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_demo: demosaic param + */ +int ak_isp_vp_get_demo_attr(AK_ISP_DEMO_ATTR *p_demo); + +/** + * @brief: set color correct param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *p_ccm: color correct param + */ +int ak_isp_vp_set_ccm_attr(AK_ISP_CCM_ATTR *p_ccm); + +/** + * @brief: set color correct param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_ccm: color correct param + */ +int ak_isp_vp_get_ccm_attr(AK_ISP_CCM_ATTR *p_ccm); + +/** + * @brief: set wdr param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *p_wdr: wdr param + */ +int ak_isp_vp_set_wdr_attr(AK_ISP_WDR_ATTR *p_wdr); + +/** + * @brief: get wdr param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_wdr: wdr param + */ +int ak_isp_vp_get_wdr_attr(AK_ISP_WDR_ATTR *p_wdr); + +/** + * @brief: set yuv noise remove param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *p_nr2: noise remove param + */ +int ak_isp_vp_set_nr2_attr(AK_ISP_NR2_ATTR *p_nr2); + +/** + * @brief: get yuv noise remove param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_nr2: noise remove param + */ +int ak_isp_vp_get_nr2_attr(AK_ISP_NR2_ATTR *p_nr2); + +/** + * @brief: set 3d noise remove param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *p_3d_nr: 3d noise remove param + */ +int ak_isp_vp_set_3d_nr_attr(AK_ISP_3D_NR_ATTR *p_3d_nr); + +/** + * @brief: get 3d noise remove param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_3d_nr: 3d noise remove param + */ +int ak_isp_vp_get_3d_nr_attr(AK_ISP_3D_NR_ATTR *p_3d_nr); + +/** + * @brief: set 3d noise remove reference param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *p_ref: 3d noise remove referenc param + */ +int ak_isp_vp_set_3d_nr_ref_addr(AK_ISP_3D_NR_REF_ATTR *p_ref); + +/** + * @brief: get 3d noise remove reference param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_ref: 3d noise remove referenc param + */ +int ak_isp_vp_get_3d_nr_ref_addr(AK_ISP_3D_NR_REF_ATTR *p_ref); + +/** + * @brief: get 3d noise remove statics param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_3d_nr_stat_info: 3d noise remove statics param + */ +int ak_isp_vp_get_3d_nr_stat_info(AK_ISP_3D_NR_STAT_INFO * p_3d_nr_stat_info); + +/** + * @brief: set sharp param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *p_sharp: sharp param + */ +int ak_isp_vp_set_sharp_attr(AK_ISP_SHARP_ATTR *p_sharp); + +/** + * @brief: get sharp param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_sharp: sharp param + */ +int ak_isp_vp_get_sharp_attr(AK_ISP_SHARP_ATTR* p_sharp); + +/** + * @brief: set sharp other param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *p_sharp_ex: sharp other param + */ +int ak_isp_vp_set_sharp_ex_attr(AK_ISP_SHARP_EX_ATTR *p_sharp_ex); + +/** + * @brief: get sharp other param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_sharp_ex: sharp other param + */ +int ak_isp_vp_get_sharp_ex_attr(AK_ISP_SHARP_EX_ATTR* p_sharp_ex); + +/** + * @brief: set false color param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *fcs: false color param + */ +int ak_isp_vp_set_fcs_attr(AK_ISP_FCS_ATTR *p_fcs); + +/** + * @brief: get false color param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *fcs: false color param + */ +int ak_isp_vp_get_fcs_attr(AK_ISP_FCS_ATTR *p_fcs); + +/** + * @brief: set hue param + * @author: lz + * @date: 2016-8-26 + * @param [in] *p_hue:hue param + */ +int ak_isp_vp_set_hue_attr(AK_ISP_HUE_ATTR *p_hue); + +/** + * @brief: gethue param + * @author: lz + * @date: 2016-8-26 + * @param [in] *p_hue:hue param + */ +int ak_isp_vp_get_hue_attr(AK_ISP_HUE_ATTR *p_hue); + +/** + * @brief: set satruration param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *p_sat: satruration param + */ +int ak_isp_vp_set_saturation_attr(AK_ISP_SATURATION_ATTR *p_sat); + +/** + * @brief: get satruration param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_sat: satruration param + */ +int ak_isp_vp_get_saturation_attr(AK_ISP_SATURATION_ATTR *p_sat); + +int ak_isp_vp_set_contrast_attr(AK_ISP_CONTRAST_ATTR *p_contrast); +int ak_isp_vp_get_contrast_attr(AK_ISP_CONTRAST_ATTR *p_contrast); + +int ak_isp_vp_set_rgb2yuv_attr(AK_ISP_RGB2YUV_ATTR*p_rgb2yuv_attr); +int ak_isp_vp_get_rgb2yuv_attr(AK_ISP_RGB2YUV_ATTR*p_rgb2yuv_attr); +int ak_isp_vp_set_effect_attr(AK_ISP_EFFECT_ATTR *p_effect_attr); +int ak_isp_vp_get_effect_attr(AK_ISP_EFFECT_ATTR *p_effect_attr); + +int ak_isp_vpp_set_mask_area_attr(AK_ISP_MASK_AREA_ATTR *p_mask, MASK_NUM num); +int ak_isp_vpp_get_mask_area_attr(AK_ISP_MASK_AREA_ATTR *p_mask, MASK_NUM num); +int ak_isp_vpp_set_mask_color(AK_ISP_MASK_COLOR_ATTR *p_mask); +int ak_isp_vpp_get_mask_color(AK_ISP_MASK_COLOR_ATTR *p_mask); +int ak_isp_vpp_set_main_chan_mask_area_attr(AK_ISP_MAIN_CHAN_MASK_AREA_ATTR *p_mask); +int ak_isp_vpp_get_main_chan_mask_area_attr(AK_ISP_MAIN_CHAN_MASK_AREA_ATTR *p_mask); +int ak_isp_vpp_set_sub_chan_mask_area_attr(AK_ISP_SUB_CHAN_MASK_AREA_ATTR *p_mask); +int ak_isp_vpp_get_sub_chan_mask_area_attr(AK_ISP_SUB_CHAN_MASK_AREA_ATTR *p_mask); + + +int ak_isp_vpp_set_osd_color_table_attr(AK_ISP_OSD_COLOR_TABLE_ATTR *p_isp_color_table); +int ak_isp_vpp_set_main_channel_osd_context_attr(AK_ISP_OSD_CONTEXT_ATTR *p_context); +int ak_isp_vpp_set_sub_channel_osd_context_attr(AK_ISP_OSD_CONTEXT_ATTR *p_context); + + +int ak_isp_vpp_set_main_channel_osd_mem_attr(AK_ISP_OSD_MEM_ATTR *p_mem); +int ak_isp_vpp_set_sub_channel_osd_mem_attr(AK_ISP_OSD_MEM_ATTR *p_mem); + + + +/** + * @brief: set auto focus param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *p_af: af param + */ +int ak_isp_vp_set_af_attr(AK_ISP_AF_ATTR *p_af); + +int ak_isp_vp_set_af_win34_attr( AK_ISP_AF_ATTR *p_af); + +/** + * @brief: get auto focus param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_af: af param + */ +int ak_isp_vp_get_af_attr(AK_ISP_AF_ATTR *p_af); + +/** + * @brief: get auto focus statics info param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_af_stat_info: af_stat param + */ +int ak_isp_vp_get_af_stat_info(AK_ISP_AF_STAT_INFO *p_af_stat_info); + +/** + * @brief: set white balance type + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *p_type: white balance type param + */ +int ak_isp_vp_set_wb_type(AK_ISP_WB_TYPE_ATTR *p_type); +/** + * @brief: get white balance type + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_type: white balance type param + */ +int ak_isp_vp_get_wb_type(AK_ISP_WB_TYPE_ATTR *p_type); + +/** + * @brief: set manual white balance + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *p_mwb: manual white balance type param + */ +int ak_isp_vp_set_mwb_attr(AK_ISP_MWB_ATTR *p_mwb); + +/** + * @brief: get manual white balance + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *p_mwb: manual white balance type param + */ +int ak_isp_vp_get_mwb_attr(AK_ISP_MWB_ATTR *p_mwb); + +/** + * @brief: set auto white balance + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *p_awb: auto white balance param + */ +int ak_isp_vp_set_awb_attr(AK_ISP_AWB_ATTR *p_awb); + +/** + * @brief: get auto white balance + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_awb: auto white balance param + */ +int ak_isp_vp_get_awb_attr(AK_ISP_AWB_ATTR *p_awb); + +/** + * @brief: set auto white balance ex attr + * @author: wyf + * @date: 2016-5-16 + * @param [in] *p_awb: auto white balance param + */ +int ak_isp_vp_set_awb_ex_attr(AK_ISP_AWB_EX_ATTR *p_awb); + +/** + * @brief: get auto white balance ex attr + * @author: wyf + * @date: 2016-5-16 + * @param [in] *p_awb: auto white balance param + */ +int ak_isp_vp_get_awb_ex_attr(AK_ISP_AWB_EX_ATTR *p_awb); + +/** + * @brief: get awb statics info + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_awb_stat_info: awb statics info param + */ +int ak_isp_vp_get_awb_stat_info(AK_ISP_AWB_STAT_INFO *p_awb_stat_info); + +/** + * @brief: set exp type + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *p_exp_type: exp type param + */ +int ak_isp_vp_set_exp_type(AK_ISP_EXP_TYPE* p_exp_type); + +/** + * @brief: get exp type + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_exp_type: exp type param + */ +int ak_isp_vp_get_exp_type( AK_ISP_EXP_TYPE* p_exp_type); + + +/** + * @brief: set manual exposure param + * @author: lizhi + * @date: 2020-06-01 + * @param [in] *p_mae: manual exposure param + */ +int ak_isp_vp_set_mae_attr( AK_ISP_MAE_ATTR *p_mae); + + +/** + * @brief: get manual exposure param + * @author: lizhi + * @date: 2020-06-01 + * @param [in] *p_mae: manual exposure param + */ +int ak_isp_vp_get_mae_attr(AK_ISP_MAE_ATTR *p_mae); + + +/** + * @brief: set auto exposure param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *p_ae: auto exposure param + */ +int ak_isp_vp_set_ae_attr( AK_ISP_AE_ATTR *p_ae); + +/** + * @brief: get auto exposure param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *p_ae: auto exposure param + */ +int ak_isp_vp_get_ae_attr(AK_ISP_AE_ATTR *p_ae); + +/** + * @brief: set frame rate param + * @author: lz + * @date: 2016-8-29 + * @param [in] *p_frame_rate: frame rate param + */ +int ak_isp_vp_set_frame_rate( AK_ISP_FRAME_RATE_ATTR*p_frame_rate); + +/** + * @brief: set frame rate param + * @author: lz + * @date: 2016-8-29 + * @param [in] *p_frame_rate: frame rate param + */ +int ak_isp_vp_get_frame_rate(AK_ISP_FRAME_RATE_ATTR*p_frame_rate); + +/** + * @brief: get auto exposure running info + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_ae_stat: auto exposure running info param + */ +int ak_isp_vp_get_ae_run_info(AK_ISP_AE_RUN_INFO*p_ae_stat); + +/** + * @brief: get raw hist running info + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_raw_hist_stat: raw hist info param + */ +int ak_isp_vp_get_raw_hist_stat_info(AK_ISP_RAW_HIST_STAT_INFO *p_raw_hist_stat); + +/** + * @brief: get rgb hist running info + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_rgb_hist_stat: rgb hist info param + */ +int ak_isp_vp_get_rgb_hist_stat_info(AK_ISP_RGB_HIST_STAT_INFO *p_rgb_hist_stat); + +/** + * @brief: get rgb hist running info + * @author: xiepenghe + * @date: 2016-5-06 + * @param [out] *p_yuv_hist_stat: yuv hist info param + */ +int ak_isp_vp_get_yuv_hist_stat_info(AK_ISP_YUV_HIST_STAT_INFO *p_yuv_hist_stat); + +int ak_isp_vp_set_rgb_hist_attr(AK_ISP_RGB_HIST_ATTR *p_rgb_hist); +int ak_isp_vp_get_rgb_hist_attr(AK_ISP_RGB_HIST_ATTR *p_rgb_hist); + + +int ak_isp_vp_set_yuv_hist_attr(AK_ISP_YUV_HIST_ATTR *p_yuv_hist); +int ak_isp_vp_get_yuv_hist_attr(AK_ISP_YUV_HIST_ATTR *p_yuv_hist); + +/** + * @brief: set zone weight param + * @author: xiepenghe + * @date: 2016-5-06 + * @param [in] *p_weight: weight param + */ +int ak_isp_vp_set_zone_weight(AK_ISP_WEIGHT_ATTR *p_weight); +int ak_isp_vp_get_zone_weight( AK_ISP_WEIGHT_ATTR *p_weight); +int ak_isp_vp_set_raw_hist_attr(AK_ISP_RAW_HIST_ATTR *p_raw_hist); +int ak_isp_vp_get_raw_hist_attr(AK_ISP_RAW_HIST_ATTR *p_raw_hist); + +/* Pclk_Polar not isp function, but described in PG */ +AK_ISP_PCLK_POLAR ak_isp_get_pclk_polar(void); + +int ak_isp_set_isp_capturing(int resume); + +int ak_isp_irq_work(void); +int ak_isp_ae_work(void); +int ak_isp_awb_work(void); + +typedef void (*ISPDRV_CB_PRINTK)(char * format, ...); +typedef void (*ISPDRV_CB_MEMCPY)(void *dst, void *src, unsigned long sz); +typedef void (*ISPDRV_CB_MEMSET)(void *ptr, unsigned char value, unsigned long sz); +typedef void* (*ISPDRV_CB_MALLOC)(unsigned long sz); +typedef void (*ISPDRV_CB_FREE)(void *ptr); +typedef void* (*ISPDRV_CB_DMAMALLOC)(unsigned long sz, void *handle); +typedef void (*ISPDRV_CB_DMAFREE)(void *ptr, unsigned long sz, unsigned long handle); +typedef void (*ISPDRV_CB_MSLEEP)(int ms); + +typedef struct { + ISPDRV_CB_PRINTK cb_printk; + ISPDRV_CB_MEMCPY cb_memcpy; + ISPDRV_CB_MEMSET cb_memset; + ISPDRV_CB_MALLOC cb_malloc; + ISPDRV_CB_FREE cb_free; + ISPDRV_CB_DMAMALLOC cb_dmamalloc; + ISPDRV_CB_DMAFREE cb_dmafree; + ISPDRV_CB_MSLEEP cb_msleep; +}AK_ISP_FUNC_CB; + +typedef struct sensor_reg_info { + T_U16 reg_addr; + T_U16 value; +}AK_ISP_SENSOR_REG_INFO; + +typedef struct sensor_init_para { + T_U16 num; + AK_ISP_SENSOR_REG_INFO *reg_info; +}AK_ISP_SENSOR_INIT_PARA; + +enum sensor_bus_type { + BUS_TYPE_RAW, + BUS_TYPE_YUV, + BUS_TYPE_NUM +}; + +enum scene { + SCENE_INDOOR = 0, + SCENE_OUTDOOR +}; + +typedef struct sensor_callback { + int (*sensor_init_func)(const AK_ISP_SENSOR_INIT_PARA *para); + int (*sensor_read_reg_func)(const int reg_addr); + int (*sensor_write_reg_func)(const int reg_addr, int value); + int (*sensor_read_id_func)(void); //no use IIC bus + int (*sensor_update_a_gain_func)(const unsigned int a_gain); + int (*sensor_update_d_gain_func)(const unsigned int d_gain); + int (*sensor_updata_exp_time_func)(unsigned int exp_time); + int (*sensor_timer_func)(void); + + int (*sensor_probe_id_func)(void); //use IIC bus + int (*sensor_get_resolution_func)(int *width, int *height); + int (*sensor_get_mclk_func)(void); + int (*sensor_get_fps_func)(void); + int (*sensor_get_valid_coordinate_func)(int *x, int *y); + enum sensor_bus_type (*sensor_get_bus_type_func)(void); + int (*sensor_get_parameter_func)(int param, void *value); + + int (*sensor_set_power_on_func)(const int pwdn_pin, const int reset_pin); + int (*sensor_set_power_off_func)(const int pwdn_pin, const int reset_pin); + int (*sensor_set_fps_func)(const int fps); + int (*sensor_set_standby_in_func)(const int pwdn_pin, const int reset_pin); + int (*sensor_set_standby_out_func)(const int pwdn_pin, const int reset_pin); +}AK_ISP_SENSOR_CB; + +int isp2_module_init(AK_ISP_FUNC_CB *cb, AK_ISP_SENSOR_CB *sensor_cb, void *reg_base); +void isp2_module_fini(void); + +void isp2_print_reg_table(void); + +int ak_isp_register_sensor(void *sensor_info); +void *ak_isp_get_sensor(int *index); +void ak_isp_remove_all_sensors(void); +int ak_isp_set_td(void); +int ak_isp_reload_td(void); +enum scene ak_isp_get_scene(void); +int ak_isp_get_iso(void); + + +int ak_isp_vpp_mainchn_osdmem_useok(void); + +int ak_isp_vpp_subchn_osdmem_useok(void); + +int ak_isp_get_yuvaddr_and_mdinfo(enum buffer_id id, void **yuv, void **mdinfo); + +#endif diff --git a/include/plat-anyka/ak_motor.h b/include/plat-anyka/ak_motor.h new file mode 100644 index 00000000..6967ccf4 --- /dev/null +++ b/include/plat-anyka/ak_motor.h @@ -0,0 +1,149 @@ +#ifndef __AK_MOTOR_H__ +#define __AK_MOTOR_H__ + +#ifdef __KERNEL__ +enum ak_motor_phase { + AK_MOTOR_PHASE_A = 0, + AK_MOTOR_PHASE_B, + AK_MOTOR_PHASE_C, + AK_MOTOR_PHASE_D, + AK_MOTOR_PHASE_NUM, +}; + +enum ak_motor_hit { + AK_MOTOR_HIT_LEFT = 0, + AK_MOTOR_HIT_RIGHT, + AK_MOTOR_HIT_NUM, +}; + + +struct ak_motor_plat_data +{ + struct gpio_info gpio_phase[AK_MOTOR_PHASE_NUM]; + struct gpio_info gpio_hit[AK_MOTOR_HIT_NUM]; + unsigned int irq_hit_type[AK_MOTOR_HIT_NUM]; + void (* gpio_init) (const struct gpio_info *); + + unsigned int angular_speed; +}; +#endif + +#define AK_MOTOR_IOC_MAGIC 'm' +#define AK_MOTOR_SET_ANG_SPEED _IOW(AK_MOTOR_IOC_MAGIC, 11, int) +#define AK_MOTOR_GET_ANG_SPEED _IOR(AK_MOTOR_IOC_MAGIC, 12, int) +#define AK_MOTOR_TURN_CLKWISE _IOW(AK_MOTOR_IOC_MAGIC, 13, int) +#define AK_MOTOR_TURN_ANTICLKWISE _IOW(AK_MOTOR_IOC_MAGIC, 14, int) +#define AK_MOTOR_GET_HIT_STATUS _IOW(AK_MOTOR_IOC_MAGIC, 15, int) +#define AK_MOTOR_TURN_STOP _IOW(AK_MOTOR_IOC_MAGIC, 16, int) + +#define AK_MOTOR_EVENT_HIT (1) +#define AK_MOTOR_EVENT_UNHIT (1<<1) +#define AK_MOTOR_EVENT_STOP (1<<2) + +#define AK_MOTOR_HITTING_LEFT (1<<0) +#define AK_MOTOR_HITTING_RIGHT (1<<1) + +#define AK_MOTOR_MIN_SPEED (1) +#define AK_MOTOR_MAX_SPEED (200) +struct notify_data +{ + int hit_num; + int event; + int remain_steps; +}; + + +/// Anycloud V500 Porting. +/// /* 1. argument for start up */ +/* + * MOTOR_PARM: set paramters + */ +#define MOTOR_PARM _IOW(AK_MOTOR_IOC_MAGIC, 0x00, int) + +/* 2. argument for real-time */ +/* + * MOTOR_SPEED_STEP: set speed in step(hz) + * MOTOR_SPEED_ANGLE: set speed in angle + * */ +#define MOTOR_SPEED_STEP _IOW(AK_MOTOR_IOC_MAGIC, 0x20, int) +#define MOTOR_SPEED_ANGLE _IOW(AK_MOTOR_IOC_MAGIC, 0x21, int) + +/* 3. base command */ +/* + * MOTOR_MOVE_LIMIT: move clkwise or anticlkwise + * clkwise: val > 0, anticlkwise: val < 0 + * MOTOR_MOVE_NOLIMIT: move clkwise or anticlkwise + * clkwise: val > 0, anticlkwise: val < 0 + * MOTOR_STOP: set stop and wait for be stop finish + * MOTOR_GET_STATUS: get status + * */ +#define MOTOR_MOVE_LIMIT _IOW(AK_MOTOR_IOC_MAGIC, 0x40, int) +#define MOTOR_MOVE_NOLIMIT _IOW(AK_MOTOR_IOC_MAGIC, 0x41, int) +#define MOTOR_STOP _IOW(AK_MOTOR_IOC_MAGIC, 0x42, int) +#define MOTOR_GET_STATUS _IOW(AK_MOTOR_IOC_MAGIC, 0x43, int) + +/* 4. extern command */ +/* + * MOTOR_RESET: first go to ACTIVAL boundary then go to middle + * MOTOR_MIDDLE: turn to middle directly + * MOTOR_CRUISE: turn to the clkwise an anticlkwise until set stop + * MOTOR_BOUNDARY: move to ACTIVAL boundary + * in clkwise or anticlkwise until be stop + * */ +#define MOTOR_RESET _IOW(AK_MOTOR_IOC_MAGIC, 0x60, int) +#define MOTOR_MIDDLE _IOW(AK_MOTOR_IOC_MAGIC, 0x61, int) +#define MOTOR_CRUISE _IOW(AK_MOTOR_IOC_MAGIC, 0x62, int) +#define MOTOR_BOUNDARY _IOW(AK_MOTOR_IOC_MAGIC, 0x63, int) + +/* + * motor_status: + * @MOTOR_IS_STOP: motor is stoped now + * @MOTOR_IS_RUNNING: motor is running now + * */ +enum motor_status { + MOTOR_IS_STOP = 0, + MOTOR_IS_RUNNING, +}; + +/* + * struct motor_parm - motor parameters + * @pos: the current position + * @speed_step: speed of step(hz) + * @steps_one_circel: steps one circel + * @total_steps: steps motor can run + * @boundary_steps: reserved boundary steps + */ +struct motor_parm { + int pos; + int speed_step; + int steps_one_circle; + int total_steps; + int boundary_steps; +}; + + +/* + * motor_message - message + * @status: motor working status + * @pos: the current position + * @speed_step: speed of step(hz) + * @speed_angle: speed of angle + * @steps_one_circel: steps one circel + * @total_steps: steps motor can run + * @boundary_steps: reserved boundary steps + * @attach_timer: attach to hardware timer + */ +struct motor_message { + enum motor_status status; + int pos; + int speed_step; + int speed_angle; + int steps_one_circle; + int total_steps; + int boundary_steps; + int attach_timer; +}; + + + +#endif diff --git a/include/plat-anyka/ak_sensor.h b/include/plat-anyka/ak_sensor.h new file mode 100644 index 00000000..ae8b2115 --- /dev/null +++ b/include/plat-anyka/ak_sensor.h @@ -0,0 +1,108 @@ +/* gc0308 Camera + * + * Copyright (C) 2008 Renesas Solutions Corp. + * Kuninori Morimoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __AK_SENSOR_H__ +#define __AK_SENSOR_H__ + +#include +#include +#include + +#define SENSOR_MAX_SUPPORT 20 + +#define V4L2_CID_SENSOR_POWERON (V4L2_CID_USER_CLASS + 1) +#define V4L2_CID_SENSOR_POWEROFF (V4L2_CID_USER_CLASS + 2) +#define V4L2_CID_SENSOR_STANDBYIN (V4L2_CID_USER_CLASS + 3) +#define V4L2_CID_SENSOR_STANDBYOUT (V4L2_CID_USER_CLASS + 4) +#define V4L2_CID_SENSOR_MCLK (V4L2_CID_USER_CLASS + 5) +#define V4L2_CID_SENSOR_ID (V4L2_CID_USER_CLASS + 6) +#define V4L2_CID_SENSOR_FPS (V4L2_CID_USER_CLASS + 7) +#define V4L2_CID_SENSOR_RESOLUTION (V4L2_CID_USER_CLASS + 8) +#define V4L2_CID_SENSOR_OFFSET (V4L2_CID_USER_CLASS + 9) + +#define SENSOR_SET_PIN_VAL(reset_pin, pwdn_pin) \ + ( (((reset_pin) & 0xff) << 8) | ((pwdn_pin) & 0xff) ) + +#define SENSOR_GET_PWDN_PIN(val) \ + ( (val) & 0xff ) + +#define SENSOR_GET_RESET_PIN(val) \ + ( ((val) >> 8) & 0xff ) + +typedef enum sensor_mirror { + SENSOR_MIRROR_NORMAL = 0, + SENSOR_MIRROR_V, + SENSOR_MIRROR_H, + SENSOR_MIRROR_FLIP, +} T_SENSOR_MIRROR; + +/* + * aksensor camera info + */ +struct aksensor_camera_info { + unsigned int buswidth; + unsigned int flags; + unsigned int pin_pwdn; + unsigned int pin_reset; + struct soc_camera_link link; +// struct aksensor_edge_ctrl edgectrl; +}; + +struct aksensor_color_format { + enum v4l2_mbus_pixelcode code; + enum v4l2_colorspace colorspace; +}; + +struct aksensor_win_size { + char *name; + __s32 left; + __s32 top; + __s32 width; + __s32 height; +}; + +struct sensor_info { + const char *sensor_name; + int sensor_id; + const struct v4l2_ctrl_config *ctrls; + int nr_ctrls; + const struct aksensor_color_format *formats; + int num_formats; + const struct aksensor_win_size *resolution; + int num_resolution; + AK_ISP_SENSOR_CB *sensor_callback; +}; + +typedef struct sensor_i2c_data_s { + u8 u8DevAddr; + u32 u32RegAddr; + u32 u32RegAddrByteNum; + u32 u32Data; + u32 u32DataByteNum; + u32 reserved[2]; +} T_SENSOR_I2C_DATA_S; + +/** + * @brief register a camera device, which will be probed at camera open + * @author dengzhou/wudaochao + * @date 2012-11-06 + * @param[in] struct sensor_info + * @param[in] handler camera interface + * @retval 0 register successfully + * @retval -1 register unsuccessfully + */ +int register_sensor(struct sensor_info *si); + +int aksensor_module_init(void); +void aksensor_module_exit(void); +AK_ISP_SENSOR_CB *ak_sensor_get_sensor_cb(void); + +#endif /* END __AKSENSOR_H__ */ + diff --git a/include/plat-anyka/ak_sensor_i2c.h b/include/plat-anyka/ak_sensor_i2c.h new file mode 100644 index 00000000..1b56bf9c --- /dev/null +++ b/include/plat-anyka/ak_sensor_i2c.h @@ -0,0 +1,13 @@ +#ifndef __AK_SENSOR_I2C_H__ +#define __AK_SENSOR_I2C_H__ + +#include +#include + +s32 ak_sensor_i2c_set_client(struct i2c_client *client); + +s32 sensor_read_register(T_SENSOR_I2C_DATA_S *pI2cData); + +s32 sensor_write_register(T_SENSOR_I2C_DATA_S *pI2cData); + +#endif diff --git a/include/plat-anyka/akgpios.h b/include/plat-anyka/akgpios.h new file mode 100644 index 00000000..ab08cd83 --- /dev/null +++ b/include/plat-anyka/akgpios.h @@ -0,0 +1,40 @@ +#ifndef __AKAPPLY_GPIO_H +#define __AKAPPLY_GPIO_H + + +#define APPLY_IOC_MAGIC 'f' + +/* + Command definitions which are supported by this dirver +*/ +#define GET_GPIO_NUM _IOR(APPLY_IOC_MAGIC, 40, __u8) +#define GET_AVAILABLE_GPIO _IOR(APPLY_IOC_MAGIC, 41, __u8) +#define SET_GPIO_FUNC _IOW(APPLY_IOC_MAGIC, 42, __u8) +#define GET_GPIO_VALUE _IOWR(APPLY_IOC_MAGIC, 43, __u8) +#define SET_GPIO_IRQ _IOW(APPLY_IOC_MAGIC, 44, __u8) +#define LISTEN_GPIO_IRQ _IOW(APPLY_IOC_MAGIC, 45, __u8) +#define DELETE_GPIO_IRQ _IOW(APPLY_IOC_MAGIC, 46, __u8) +#define SET_GPIO_LEVEL _IOW(APPLY_IOC_MAGIC, 47, __u8) +#define SET_GROUP_GPIO_LEVEL _IOW(APPLY_IOC_MAGIC, 48, __u8) + + +#ifdef __KERNEL__ + +struct custom_gpio_data { + unsigned int *gpiopin; + unsigned int ngpiopin; +}; + +struct gpio_ginfo { + int pin; + int value; +}; + +struct gpio_group_info { + struct gpio_ginfo *gpio; + int gpio_num; +}; + +#endif /* end __KERNEL__ */ + +#endif /* end AKAPPLY_GPIO_H */ diff --git a/include/plat-anyka/akmci.h b/include/plat-anyka/akmci.h new file mode 100644 index 00000000..f9b09e7f --- /dev/null +++ b/include/plat-anyka/akmci.h @@ -0,0 +1,242 @@ +/* + * include/plat-anyka/akmci.h - Anyka MMC/SD driver + * + * Copyright (C) 2010 Anyka, Ltd, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __AK_MCI_H__ +#define __AK_MCI_H__ + +#define MCI_CLK_REG 0x004 +#define MMC_CLK_DIVL(x) ((x) & 0xff) +#define MMC_CLK_DIVH(x) (((x) & 0xff) << 8) +#define MCI_CLK_ENABLE (1 << 16) +#define MCI_CLK_PWRSAVE (1 << 17) +#define MCI_FAIL_TRIGGER (1 << 19) +#define MCI_ENABLE (1 << 20) + +#define MCI_ARGUMENT_REG 0x008 +#define MCI_COMMAND_REG 0x00c +#define MCI_CPSM_ENABLE (1 << 0) +#define MCI_CPSM_CMD(x) (((x) & 0x3f) << 1) +#define MCI_CPSM_RESPONSE (1 << 7) +#define MCI_CPSM_LONGRSP (1 << 8) +#define MCI_CPSM_PENDING (1 << 9) +#define MCI_CPSM_RSPCRC_NOCHK (1 << 10) +#define MCI_CPSM_WITHDATA (1 << 11) + +#define MCI_RESPCMD_REG 0x010 +#define MCI_RESPONSE0_REG 0x014 +#define MCI_RESPONSE1_REG 0x018 +#define MCI_RESPONSE2_REG 0x01c +#define MCI_RESPONSE3_REG 0x020 +#define MCI_DATATIMER_REG 0x024 +#define MCI_DATALENGTH_REG 0x028 +#define MCI_DATACTRL_REG 0x02c +#define MCI_DPSM_ENABLE (1 << 0) +#define MCI_DPSM_DIRECTION (1 << 1) +#define MCI_DPSM_STREAM (1 << 2) +#define MCI_DPSM_BUSMODE(x) (((x) & 0x3) << 3) +#define MCI_DPSM_BLOCKSIZE(x) (((x) & 0xfff) << 16) + +#define MCI_DATACNT_REG 0x030 +#define MCI_STATUS_REG 0x034 +#define MCI_RESPCRCFAIL (1 << 0) +#define MCI_DATACRCFAIL (1 << 1) +#define MCI_RESPTIMEOUT (1 << 2) +#define MCI_DATATIMEOUT (1 << 3) +#define MCI_RESPEND (1 << 4) +#define MCI_CMDSENT (1 << 5) +#define MCI_DATAEND (1 << 6) +#define MCI_DATABLOCKEND (1 << 7) +#define MCI_STARTBIT_ERR (1 << 8) +#define MCI_CMDACTIVE (1 << 9) +#define MCI_TXACTIVE (1 << 10) +#define MCI_RXACTIVE (1 << 11) +#define MCI_FIFOFULL (1 << 12) +#define MCI_FIFOEMPTY (1 << 13) +#define MCI_FIFOHALFFULL (1 << 14) +#define MCI_FIFOHALFEMPTY (1 << 15) +#define MCI_DATATRANS_FINISH (1 << 16) +#define MCI_SDIOINT (1 << 17) + +#define MCI_MASK_REG 0x038 +#define MCI_RESPCRCFAILMASK (1 << 0) +#define MCI_DATACRCFAILMASK (1 << 1) +#define MCI_RESPTIMEOUTMASK (1 << 2) +#define MCI_DATATIMEOUTMASK (1 << 3) +#define MCI_RESPENDMASK (1 << 4) +#define MCI_CMDSENTMASK (1 << 5) +#define MCI_DATAENDMASK (1 << 6) +#define MCI_DATABLOCKENDMASK (1 << 7) +#define MCI_STARTBIT_ERRMASK (1 << 8) +#define MCI_CMDACTIVEMASK (1 << 9) +#define MCI_TXACTIVEMASK (1 << 10) +#define MCI_RXACTIVEMASK (1 << 11) +#define MCI_FIFOFULLMASK (1 << 12) +#define MCI_FIFOEMPTYMASK (1 << 13) +#define MCI_FIFOHALFFULLMASK (1 << 14) +#define MCI_FIFOHALFEMPTYMASK (1 << 15) +#define MCI_DATATRANS_FINISHMASK (1 << 16) +#define MCI_SDIOINTMASK (1 << 17) + +#define MCI_DMACTRL_REG 0x03c +#define MCI_DMA_BUFEN (1 << 0) +#define MCI_DMA_ADDR(x) (((x) & 0x7fff) << 1) +#define MCI_DMA_EN (1 << 16) +#define MCI_DMA_SIZE(x) (((x) & 0x7fff) << 17) + +#define MCI_FIFO_REG 0x040 + +#define SDIO_INTRCTR_REG 0x000 +#define SDIO_INTR_CTR_ENABLE (1 << 8) +#define SDIO_INTR_ENABLE (1 << 17) + + +#define MCI_CMDIRQMASKS \ + (MCI_CMDSENTMASK|MCI_RESPENDMASK| \ + MCI_RESPCRCFAILMASK|MCI_RESPTIMEOUTMASK) + +#define MCI_DATAIRQMASKS \ + (MCI_DATAEND| \ + MCI_DATACRCFAILMASK|MCI_DATATIMEOUTMASK) + +/*| \ + MCI_STARTBIT_ERRMASK) +*/ + +/* + * The size of the FIFO in bytes. + */ +#define MCI_FIFOSIZE 4 +#define MCI_FIFOHALFSIZE (MCI_FIFOSIZE / 2) + +#define NR_SG 16 + +#define L2BASE 0x2002c000 +#define L2FIFO_DMACONF 0x80 +#define L2FIFO_CONF1 0x88 +#define L2FIFO_ASSIGN1 0x90 +#define L2FIFO_INTEN 0x9c + +#define L2FIFOBASE 0x48000000 +#define L2ADDR(n) (L2FIFOBASE + 512 * (n)) +#define MCI_L2FIFO_NUM 2 /* #6 l2fifo */ +#define MCI_L2FIFO_SIZE 512 + +#define L2DMA_MAX_SIZE (64*255) + +#define L2_DMA_ALIGN (512) + +enum akmci_detect_mode { + AKMCI_PLUGIN_ALWAY, + AKMCI_DETECT_MODE_GPIO, + AKMCI_DETECT_MODE_AD, +}; + +enum akmci_xfer_mode { + AKMCI_XFER_UNKNOWN, + AKMCI_XFER_L2DMA, + AKMCI_XFER_L2PIO, + AKMCI_XFER_INNERPIO, + +}; + +struct ak_mci_platform_data { + int irq_cd_type; + int cap_highspeed; + int detect_mode; + int xfer_mode; + int mci_mode; + u32 max_speed_hz; + int bus_width; + void (* gpio_init) (const struct gpio_info *); + struct gpio_info gpio_cd; /* card detect pin */ + struct gpio_info gpio_wp; /* write protect pin */ + struct gpio_info gpio_pwr; ///< Power Controller +}; + +struct clk; + +#define MAX_STATUS_COUNT (1<<10) +#define MAX_STATUS_MASK (MAX_STATUS_COUNT - 1) + +#define MCI_BUS_WIDTH_1 0 +#define MCI_BUS_WIDTH_4 1 + +#define MCI_SLOT_NUM 2 +#define MCI_MODE_MMC_SD 0 +#define MCI_MODE_SDIO 1 + + +/* the 0x800000 value is reference RTOS platform, + * timeout is 419ms */ +//#define TRANS_DATA_TIMEOUT 0x800000 //0xffffffff /*0x800000*/ +#define TRANS_DATA_TIMEOUT 0x17D7840 //1s 25M + + +#define MAX_MCI_REQ_SIZE (65536) +/* as l2 fifo limit to 512 bytes */ +#define MAX_MCI_BLOCK_SIZE (512) + +struct power_gpio_attribute { + struct kobj_attribute k_attr; + unsigned int gpio; + unsigned int value; +}; + +struct akmci_host { + struct platform_device *pdev; + struct ak_mci_platform_data *plat; + struct mmc_request *mrq; + struct mmc_host *mmc; + struct mmc_data *data; + struct mmc_command *cmd; + void __iomem *base; + struct resource *mem; + struct clk *clk; + unsigned long asic_clk; + unsigned char bus_mode; + unsigned char bus_width; + unsigned long bus_clock; + unsigned long clkreg; + + int mci_mode; + int irq_mci; + int irq_cd; + int irq_cd_type; + int gpio_cd; + int gpio_wp; + int detect_mode; + int xfer_mode; + int data_err_flag; + u8 l2buf_id; + spinlock_t lock; + int card_status; + unsigned int data_xfered; + unsigned int sg_len; + struct scatterlist *sg_ptr; + unsigned int sg_off; + unsigned int size; + + struct timer_list detect_timer; + struct notifier_block detect_nb; + int plugin_flag; + wait_queue_head_t intr_data_wait; + unsigned int irq_status; + unsigned long pending_events; +#ifdef CONFIG_CPU_FREQ + struct notifier_block freq_transition; + struct semaphore freq_lock; +#endif + + struct power_gpio_attribute power; +}; + + + +#endif /* end __AK_MCI_H__ */ + diff --git a/include/plat-anyka/anyka_types.h b/include/plat-anyka/anyka_types.h new file mode 100644 index 00000000..e2d3d1fb --- /dev/null +++ b/include/plat-anyka/anyka_types.h @@ -0,0 +1,70 @@ +/** @file + * @brief Define the register operator for system + * + * Copyright (C) 2006 Anyka (GuangZhou) Software Technology Co., Ltd. + * @author + * @date 2006-01-16 + * @version 1.0 + */ + +#ifndef _ANYKA_TYPES_H_ +#define _ANYKA_TYPES_H_ + +/** @defgroup ANYKA_CPU + * @ingroup M3PLATFORM + */ +/*@{*/ + +/* preliminary type definition for global area */ +typedef unsigned char T_U8; /* unsigned 8 bit integer */ +typedef unsigned short T_U16; /* unsigned 16 bit integer */ +typedef unsigned long T_U32; /* unsigned 32 bit integer */ +typedef signed char T_S8; /* signed 8 bit integer */ +typedef signed short T_S16; /* signed 16 bit integer */ +typedef signed long T_S32; /* signed 32 bit integer */ +typedef void T_VOID; /* void */ + +#define T_U8_MAX ((T_U8)0xff) // maximum T_U8 value +#define T_U16_MAX ((T_U16)0xffff) // maximum T_U16 value +#define T_U32_MAX ((T_U32)0xffffffff) // maximum T_U32 value +#define T_S8_MIN ((T_S8)(-127-1)) // minimum T_S8 value +#define T_S8_MAX ((T_S8)127) // maximum T_S8 value +#define T_S16_MIN ((T_S16)(-32767L-1L)) // minimum T_S16 value +#define T_S16_MAX ((T_S16)(32767L)) // maximum T_S16 value +#define T_S32_MIN ((T_S32)(-2147483647L-1L)) // minimum T_S32 value +#define T_S32_MAX ((T_S32)(2147483647L)) // maximum T_S32 value + +/* basal type definition for global area */ +typedef T_S8 T_CHR; /* char */ +typedef T_U8 T_BOOL; /* BOOL type */ + +typedef T_VOID * T_pVOID; /* pointer of void data */ +typedef const T_VOID * T_pCVOID; /* const pointer of void data */ + +typedef T_S8 * T_pSTR; /* pointer of string */ +typedef const T_S8 * T_pCSTR; /* const pointer of string */ + + +typedef T_U16 T_WCHR; /**< unicode char */ +typedef T_U16 * T_pWSTR; /* pointer of unicode string */ +typedef const T_U16 * T_pCWSTR; /* const pointer of unicode string */ + + +typedef T_U8 * T_pDATA; /* pointer of data */ +typedef const T_U8 * T_pCDATA; /* const pointer of data */ + +typedef T_U32 T_COLOR; /* color value */ + +typedef T_U32 T_HANDLE; /* a handle */ + +#define AK_FALSE 0 +#define AK_TRUE 1 +#undef AK_NULL +#define AK_NULL ((T_pVOID)(0)) + +#define AK_EMPTY +/*@}*/ + + +#endif // _ANYKA_TYPES_H_ + diff --git a/include/plat-anyka/bat.h b/include/plat-anyka/bat.h new file mode 100644 index 00000000..3fbdb6de --- /dev/null +++ b/include/plat-anyka/bat.h @@ -0,0 +1,85 @@ +#ifndef __AK_BAT_H_ +#define __AK_BAT_H_ __FILE__ + +#include +#include +#include +#include + +#define poweroff_enable 1 +#define poweroff_disable 0 + +#define BAT_CHARGE_ADC_DETECT 0 +#define BAT_CHARGE_GPIO_DETECT 1 + +struct bat_gpio{ + int is_detect_mode; /* ac detect mode: ADC or GPIO */ + int active; /* active value */ + int irq; /* use for ac in or out irq */ + int delay; /* delay irq handler */ + struct gpio_info pindata; /* battery gpios info */ + +}; + +struct bat_info{ + int charge_full_pin; /* use for juge if battery charge full */ + int power_on_voltage; /* if (voltage<=power_on_voltage), disable power on */ + int power_on_correct; /* correct power on voltage */ + int charge_min_voltage; /* charge minute voltage */ + int max_voltage; /* max battery voltage */ + int min_voltage; /* min battery voltage */ + int power_off; /* enable or disable machine power off in battery driver */ + int full_capacity; + int voltage_sample; + int poweroff_cap; + int low_cap; + int recover_cap; + int cpower_on_voltage; /* if (voltage<=cpower_on_voltage in charge), disable power on */ + unsigned int full_delay;/* time to up to full capacity, unit: minute */ + int full_voltage; /* if voltage > full_voltage in full capacity, when in discharge,display full */ +}; + +struct read_voltage_sample{ + int index; // voltage[6] index + int max; // sample max voltage + int min; // sample min voltage + int sum; // sum of read voltage + int sample; /* read how many voltge sample */ + int design_max; +}; + + +struct bat_ad4{ + int up_resistance; /* read ad4 voltage resistance */ + int dw_resistance; + int voltage_correct; /* correct voltage from adc1-4*/ + int adc_avdd; /* adc avdd */ +}; + +struct ak_bat_mach_info { + + void (* gpio_init) (const struct gpio_info *); + struct bat_gpio usb_gpio; + struct bat_gpio ac_gpio; + struct bat_gpio full_gpio; + struct bat_info bat_mach_info; + struct bat_ad4 bat_adc; +}; + + +int ak_bat_read_voltage(struct ak_bat_mach_info *info); + +#ifdef CONFIG_STARTUP_CHECK_VOLTAGE +void ak_mach_check_bat_vol(struct ak_bat_mach_info *batinfo, + struct ak_gpio_keys_button *pwr_gpio, int len); +#else +static inline void ak_mach_check_bat_vol(struct ak_bat_mach_info *batinfo, + struct ak_gpio_keys_button *pwr_gpio, int len) +{ +} +#endif + + +#endif /* __AK_BAT_H_ */ + + diff --git a/include/plat-anyka/drv_module_lock.h b/include/plat-anyka/drv_module_lock.h new file mode 100644 index 00000000..9f83886c --- /dev/null +++ b/include/plat-anyka/drv_module_lock.h @@ -0,0 +1,143 @@ +#ifndef __DRV_MODULE_LOCK__ +#define __DRV_MODULE_LOCK__ + +#include + +/** + * @brief driver module define. + * define all the moudle + */ +typedef enum +{ + DRV_MODULE_AD = 0, + DRV_MODULE_DA, + DRV_MODULE_I2S, + DRV_MODULE_UART, + DRV_MODULE_CAMERA, + DRV_MODULE_DETECT, + DRV_MODULE_RTC, + DRV_MODULE_KEYPAD, + DRV_MODULE_VTIMER, + DRV_MODULE_TOUCH_SCREEN, + + DRV_MODULE_UVC, + DRV_MODULE_USB_DISK, + DRV_MODULE_USB_CDC, + DRV_MODULE_USB_CAMERA, + DRV_MODULE_USB_ANYKA, + DRV_MODULE_USB_CMMB, + + DRV_MODULE_UDISK_HOST, + DRV_MODULE_UVC_HOST, + + DRV_MODULE_USB_BUS, + + DRV_MODULE_FREQ, + DRV_MODULE_HTIMER, + DRV_MODULE_LCD_RGB, + DRV_MODULE_LCD_MPU, + DRV_MODULE_NAND, + DRV_MODULE_SDMMC, + DRV_MODULE_SPI, + DRV_MODULE_SDIO, + DRV_MODULE_I2C, + + DRV_MODULE_GPIO_EDGE, + + DRV_MODULE_JPEG_CODEC +} E_DRV_MODULE; + +typedef enum { + AK_MODULE_LOCK_1, + AK_MODULE_LOCK_2, + AK_MODULE_LOCK_3, + AK_MODULE_LOCK_4, + AK_MODULE_LOCK_5, + AK_MODULE_LOCK_6, + AK_MODULE_LOCK_7, + AK_MODULE_LOCK_8, + AK_MODULE_LOCK_9, + AK_MODULE_LOCK_10, + AK_MODULE_COUNT, +} E_LOCK_NAME; + +typedef enum { + TYPE_LOCK_SEMAPHORE, + TYPE_LOCK_MUTEX, + TYPE_LOCK_SPINLOCK, + TYPE_LOCK_SPINLOCK_IRQ +} E_LOCK_TYPE; + +struct ak_drv_module_lock { + E_DRV_MODULE drv_mod_name; + E_LOCK_NAME lock_name; + E_LOCK_TYPE lock_type; + unsigned long flags; + T_GPIO_SHAREPIN_CFG sharepin_cfg; +}; + +struct ak_drv_sharepin_lock { + E_DRV_MODULE drv_mod_name; + E_LOCK_NAME lock_name; + E_LOCK_TYPE lock_type; + unsigned long flags; + void (*config_share_pin)(void); +}; + + +/** + * function: acquire lock. protect driver module to do normally. + * as GPIOs are part of drivers module. + * param: [in]driver module name + * ret: void + */ +int ak_drv_module_lock(E_DRV_MODULE drv_mod_name); + +/** + * function: release lock. protect driver module to do normally. + * as GPIOs are part of drivers module. + * param: [in]driver module name + * ret: void + */ +void ak_drv_module_unlock(E_DRV_MODULE drv_mod_name); + +/** + * function: acquire lock. protect driver module to do normally. + * as the GPIO is defined only at board(name is product also). + * param: [in]driver module name + * ret: + * 0: if acquire lock successed; + * <0: indicate the driver is not need lock + */ +int ak_drv_sharepin_lock(E_DRV_MODULE drv_mod_name); + +/** + * function: release lock. protect driver module to do normally. + * as the GPIO is defined only at board(name is product also). + * param: [in]driver module name + * ret: + * 0: if acquire lock successed; + * <0: indicate the driver is not need lock + */ +void ak_drv_sharepin_unlock(E_DRV_MODULE drv_mod_name); + +/** + * function: acquire lock. protect driver module to do normally. + * param: [in]driver module name + * ret: void + */ +void ak_drv_module_protect(E_DRV_MODULE drv_mod_name); + +/** + * function: release lock. protect driver module to do normally. + * param: [in]driver module name + * ret: void + */ +void ak_drv_module_unprotect(E_DRV_MODULE drv_mod_name); + +void ak_set_sharepin_lock_table( + struct ak_drv_sharepin_lock *shpin_lock_table, int count, void **lock_array); + + +#endif /* __DRV_MODULE_LOCK__ */ + diff --git a/include/plat-anyka/fha_asa.h b/include/plat-anyka/fha_asa.h new file mode 100644 index 00000000..5a30deaa --- /dev/null +++ b/include/plat-anyka/fha_asa.h @@ -0,0 +1,81 @@ +#ifndef _FHA_ASA_H_ +#define _FHA_ASA_H_ + +#define ASA_FILE_FAIL 0 +#define ASA_FILE_SUCCESS 1 +#define ASA_FILE_EXIST 2 +#define ASA_MODE_OPEN 0 +#define ASA_MODE_CREATE 1 + +#define ASA_FORMAT_NORMAL 0 +#define ASA_FORMAT_EWR 1 +#define ASA_FORMAT_RESTORE 2 + +/************************************************************************ + * NAME: FHA_asa_scan + * FUNCTION scan nand flash security area + * PARAM: [in] bMount -- if buffer is enough, set AK_TURE, scan speedup + * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL +**************************************************************************/ +T_U32 FHA_asa_scan(T_BOOL bMount); + +/************************************************************************ + * NAME: FHA_asa_format + * FUNCTION nand flash format security area + * PARAM: [in] type -- ASA_FORMAT_NORMAL: only scan initial bad blocks + * ASA_FORMAT_EWR: Erase-wirte-read test + * ASA_FORMAT_RESTORE: do nothing + * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL +**************************************************************************/ +T_U32 FHA_asa_format(T_U32 type); + +/************************************************************************ + * NAME: FHA_set_bad_block + * FUNCTION set nand flash bad block + * PARAM: [in] block -- the bad block item + * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL +**************************************************************************/ +T_U32 FHA_set_bad_block(T_U32 block); + +/************************************************************************ + * NAME: FHA_check_bad_block + * FUNCTION check nand flash bad block + * PARAM: [in] block -- need to check block item + * RETURN: is bad block return FHA_SUCCESS, or else retuen FHA_ FAIL +**************************************************************************/ +T_BOOL FHA_check_bad_block(T_U32 block); + +/************************************************************************ + * NAME: FHA_get_bad_block + * FUNCTION get bad block information of one or more blocks + * PARAM: [in] start_block -- start block + * [out]pData -------- buffer used to store bad blocks information data + * [in] blk_cnt ------ how many blocks you want to know + * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL +**************************************************************************/ +T_U32 FHA_get_bad_block(T_U32 start_block, T_U8 *pData, T_U32 blk_cnt); + +/************************************************************************ + * NAME: FHA_asa_write_file + * FUNCTION write important infomation to security area, data_len can't too large + * PARAM: [in] file_name -- asa file name + * [in] pData ------ buffer used to store information data + * [in] data_len --- need to store data length + * [in] mode-------- operation mode + * ASA_MODE_OPEN------open + * ASA_MODE_CREATE----create + * RETURN: success return ASA_FILE_SUCCESS, fail retuen ASA_FILE_FAIL +**************************************************************************/ +T_U32 FHA_asa_write_file(T_U8 file_name[], const T_U8 *pData, T_U32 data_len, T_U8 mode); + +/************************************************************************ + * NAME: FHA_asa_read_file + * FUNCTION get infomation from security area by input file name + * PARAM: [in] file_name -- asa file name + * [out]pData ------ buffer used to store information data + * [in] data_len --- need to get data length + * RETURN: success return ASA_FILE_SUCCESS, fail retuen ASA_FILE_FAIL +**************************************************************************/ +T_U32 FHA_asa_read_file(T_U8 file_name[], T_U8 *pData, T_U32 data_len); +#endif // + diff --git a/include/plat-anyka/gpio_keys.h b/include/plat-anyka/gpio_keys.h new file mode 100644 index 00000000..0da5c6e2 --- /dev/null +++ b/include/plat-anyka/gpio_keys.h @@ -0,0 +1,36 @@ +#ifndef _GPIO_KEYS_H +#define _GPIO_KEYS_H + +struct device; + +struct gpio_keys_button { + /* Configuration parameters */ + unsigned int code; /* input event code (KEY_*, SW_*) */ + int gpio; /* -1 if this key does not support gpio */ + int active_low; + const char *desc; + unsigned int type; /* input event type (EV_KEY, EV_SW, EV_ABS) */ + int wakeup; /* configure the button as a wake-up source */ + int debounce_interval; /* debounce ticks interval in msecs */ + bool can_disable; + int value; /* axis value for EV_ABS */ + unsigned int irq; /* Irq number in case of interrupt keys */ + + char pulldown; //pulldown function flag + char pullup; //pullup function flag + char dir; //direction input/output + char int_pol; //interrupt polarity +}; + +struct akgpio_keys_platform_data { + struct gpio_keys_button *buttons; + int nbuttons; + unsigned int poll_interval; /* polling interval in msecs - + for polling driver only */ + unsigned int rep:1; /* enable input subsystem auto repeat */ + int (*enable)(struct device *dev); + void (*disable)(struct device *dev); + const char *name; /* input device name */ +}; + +#endif diff --git a/include/plat-anyka/isp_interface.h b/include/plat-anyka/isp_interface.h new file mode 100644 index 00000000..b5b7ac1c --- /dev/null +++ b/include/plat-anyka/isp_interface.h @@ -0,0 +1,286 @@ +#ifndef _ISP_INTERFACE_H +#define _ISP_INTERFACE_H + +#define ISP_PARM_MODE 0x01 +#define ISP_PARM_CHANNEL2 0x02 +#define ISP_PARM_OSD 0x04 +#define ISP_PARM_OCCLUSION 0x08 +#define ISP_PARM_OCCLUSION_COLOR 0x10 +#define ISP_PARM_ZOOM 0x20 + +enum isp_working_mode { + ISP_JPEG_MODE, // JPEG compression frame mode + ISP_JPEG_VIDEO, // JPEG video frame mode + ISP_YUV_OUT, // YUV single frame mode. is not support minor channel + ISP_YUV_BYPASS, // YUV single frame mode and bypass + ISP_YUV_MERGER_OUT, + ISP_YUV_BIG, // YUV single frame and big image mode + ISP_YUV_VIDEO_OUT, // YUV video frame mode + ISP_YUV_VIDEO_BYPASS, // YUV video frame mode and bypass + ISP_YUV_VIDEO_MERGER_OUT, + ISP_RGB_OUT, // RGB single frame mode + ISP_RGB_VIDEO_OUT, // RGB video frame mode + ISP_RGB_BIG, // RGB single frame and big image mode +}; + +enum isp_mode_class { + ISP_RGB_CLASS, + ISP_YUV_CLASS, + ISP_JPEG_CLASS, +}; + +struct isp_channel2_info { + int type; + int width; + int height; + int enable; +}; + +struct isp_mode_info { + int type; + enum isp_working_mode mode; +}; + +struct isp_osd_info { + int type; + int channel; + int color_depth; + int color_transparency; + int start_xpos; + int end_xpos; + int start_ypos; + int end_ypos; + int enable; + unsigned long phys_addr; + unsigned long virt_addr; +}; + +struct isp_occlusion_info { + int type; + int channel; + int number; + int start_xpos; + int end_xpos; + int start_ypos; + int end_ypos; + int enable; +}; + +struct isp_occlusion_color { + int type; + int color_type; + int transparency; + int y_component; + int u_component; + int v_component; +}; + +struct isp_zoom_info { + int type; + int channel; // value: 1 indicate isp master channel, 2 indicate the second channel + int cut_xpos; + int cut_ypos; + int cut_width; + int cut_height; + int out_width; + int out_height; +}; + + +/* response pc tool control command */ +#define ISP_CID_BLACK_BALANCE 0x60 //black balance +#define ISP_CID_LENS 0x61 //lens correct +#define ISP_CID_DEMOSAIC 0x62 //demosaic +#define ISP_CID_RGB_FILTER 0x63 //RGB filter, noise reduce +#define ISP_CID_UV_FILTER 0x64 //uv filter +#define ISP_CID_DEFECT_PIXEL 0x65 //bad color correct +#define ISP_CID_WHITE_BALANCE 0x66 //white balance +#define ISP_CID_AUTO_WHITE_BALANCE 0x67 //auto white balance +#define ISP_CID_COLOR 0x68 //color correct +#define ISP_CID_GAMMA 0x69 //gamma calculate +#define ISP_CID_BRIGHTNESS_ENHANCE 0x70 //brightness edge enhancement +#define ISP_CID_SATURATION 0x72 //saturation +#define ISP_CID_HISTOGRAM 0x73 //histogram +#define ISP_CID_SPECIAL_EFFECT 0x74 //special effect +#define ISP_CID_SET_SENSOR_PARAM 0x75 //set sensor parameter +#define ISP_CID_GET_SENSOR_PARAM 0x76 //get sensor parameter + +//ycx +#define ISP_CID_AE_CTRL_PARAM 0x77 //get AE parameter +#define ISP_CID_CC_AWB_PARAM 0x78 //get AE parameter + + +/* response pc tool control command structure define */ +struct isp_black_balance { + int type; + int enable; + unsigned int r_offset; //register table 0x20: offset register 13 [31:22] + unsigned int g_offset; //register table 0x20: offset register 14 [31:22] + unsigned int b_offset; //register table 0x20: offset register 15 [31:22] +}; + +struct isp_lens_correct { + int type; + int enable; //register table 0x20: offset register 24 [31] + int lens_coefa[10]; //register table 0x20: start offset register 13 [21:0] + int lens_coefb[10]; + int lens_coefc[10]; + unsigned int lens_range[10]; + unsigned int lens_xref; //register table 0x20: offset register 23 [31:22] + unsigned int lens_yref; //register table 0x20: offset register 23 [31:22] +}; + +//demosaic +struct isp_demosaic { + int type; + int enable; //register table 0x20: offset register 2 [12] + unsigned int threshold; //register table 0x20: offset register 2 [11:0] +}; + +// noise reduce +struct isp_rgb_filter { + int type; + int enable; //register table 0x24: offset register 1 [31] + unsigned int threshold; //register table 0x24: offset register 1 [21:12] +}; + +//uv iso filter +struct isp_uv_filter { + int type; + int enable; //register table 0x24: offset register 13 [30] +}; + +// defect pixel +struct isp_defect_pixel { + int type; + int enable; //register table 0x24: offset register 1 [30] + unsigned int threshold; //register table 0x24: offset register 1 [29:28] +}; + +struct isp_white_balance { + int type; + int enable; + unsigned int co_r; //register table 0x24: offset register 0 [11:0] + unsigned int co_g; //register table 0x24: offset register 0 [23:12] + unsigned int co_b; //register table 0x24: offset register 1 [11:0] +}; + +struct isp_auto_white_balance { + int type; + int enable; //register table 0x20: offset register 25 [31] + int awb_step; + int index; + unsigned int gr_low; // 0.8125 + unsigned int gr_high; // 1.555 + unsigned int gb_low; // 0.863 + unsigned int gb_high; // 1.820 + unsigned int grb_low; // 0.461 + unsigned int grb_high; // 1.559 + //range of pixel + unsigned int r_low; + unsigned int r_high; + unsigned int g_low; + unsigned int g_high; + unsigned int b_low; + unsigned int b_high; + + unsigned int co_r; //register table 0x24: offset register 0 [11:0] + unsigned int co_g; //register table 0x24: offset register 0 [23:12] + unsigned int co_b; //register table 0x24: offset register 1 [11:0] +}; + +struct isp_color_correct { + int type; + int enable; //register table 0x24: offset register 11 [31] + unsigned int cc_thrs_low; + unsigned int cc_thrs_high; + int ccMtrx[3][3]; +}; + +struct isp_gamma_calculate { + int type; + int enable; //register table 0x24: offset register 11 [30] + int is_sync; //count: 0: 1 + unsigned int gamma[32]; //register table 0x28: 64 register +}; + +//brightness edge enhancement +struct isp_brightness_enhance { + int type; + int enable; //register table 0x24: offset register 11 [29] + int ygain; //register table 0x24: offset register 12 [31:24] + unsigned int y_thrs; //register table 0x24: offset register 12 [22:12] + unsigned int y_edgek; //register table 0x24: offset register 12 [10:0] +}; + +struct isp_saturation { + int type; + int enable; //register table 0x24: offset register 13 [31] + int Khigh; //register table 0x24: offset register 13 [29:20], (0.1~3) *256 _8_8 + int Klow; //register table 0x24: offset register 13 [19:10], register value (0.1~3) + int Kslope; //register table 0x24: offset register 13 [9:0], (ih-il)*256/(ch-cl) + unsigned int Chigh; //register table 0x24: offset register 14 [31:24],0~255 + unsigned int Clow; //register table 0x24: offset register 14 [23:16], 0~255 +}; + +struct isp_histogram { + int type; + int enable; // + int sync; //sync: 0: 1 + unsigned int hist[32]; // +}; + +struct isp_special_effect { + int type; + int enable; //register table 0x24: offset register 11 [28] + int solar_enable; //register table 0x24: offset register 11 [27] + unsigned int solar_thrs; //register table 0x24: offset register 0 [31:24] + int y_eff_coefa; //register table 0x24: offset register 14 [15:8] + unsigned int y_eff_coefb; //register table 0x24: offset register 14 [7:0] + int u_eff_coefa; //register table 0x24: offset register 15 [31:24] + unsigned int u_eff_coefb; //register table 0x24: offset register 15 [23:16] + int v_eff_coefa; //register table 0x24: offset register 15 [15:8] + unsigned int v_eff_coefb; //register table 0x24: offset register 15 [7:0] +}; + +//uv iso filter +struct isp_image_fog { + int type; + int enable; //register table 0x24: offset register 13 [30] +}; + +struct isp_config_sensor_reg { + int type; + int enable; + unsigned int cmd; + unsigned int data; +}; + + +struct isp_ae_attr { + int type; + int enable; // enable AE + + unsigned int ae_target; + unsigned int ae_tolerance; + unsigned int ae_step; + unsigned int ae_timemax; + unsigned int ae_timemin; + + int enableExpLinkage; + int enableExpCompensation; + int enableExpGama; +}; + + +struct isp_color_correct_awb { + int type; + int color_temperture_index; + unsigned int cc_thrs_low; + unsigned int cc_thrs_high; + int ccMtrx[3][3]; +}; + + +#endif + diff --git a/include/plat-anyka/notify.h b/include/plat-anyka/notify.h new file mode 100644 index 00000000..b230fd4e --- /dev/null +++ b/include/plat-anyka/notify.h @@ -0,0 +1,36 @@ +/* + * include/plat-anyka/notify.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __NOTIFY_H_ +#define __NOTIFY_H_ + +#define POWER_EVENT_AC_PLUGIN 0x01 +#define POWER_EVENT_AC_PLUGOUT 0x02 +#define POWER_EVENT_USB_PLUGIN 0x03 +#define POWER_EVENT_USB_PLUGOUT 0x04 + +#define ADDEDECT_DEV1_PLUGIN 0x10 +#define ADDEDECT_DEV1_PLUGOUT 0x11 +#define ADDEDECT_DEV2_PLUGIN 0x12 +#define ADDEDECT_DEV2_PLUGOUT 0x13 +#define ADDEDECT_DEV3_PLUGIN 0x14 +#define ADDEDECT_DEV3_PLUGOUT 0x15 +#define ADDEDECT_DEV4_PLUGIN 0x16 +#define ADDEDECT_DEV4_PLUGOUT 0x17 + +int power_register_client(struct notifier_block *nb); +int power_unregister_client(struct notifier_block *nb); +int power_notifier_call_chain(unsigned long val, void *v); + +int addetect_register_client(struct notifier_block *nb); +int addetect_unregister_client(struct notifier_block *nb); +int addetect_notifier_call_chain(unsigned long val, void *v); + +#endif /* __NOTIFY_H_ */ + diff --git a/include/plat-anyka/otg-hshcd.h b/include/plat-anyka/otg-hshcd.h new file mode 100644 index 00000000..495927ba --- /dev/null +++ b/include/plat-anyka/otg-hshcd.h @@ -0,0 +1,29 @@ +/* + * AKOTG HS register declarations and HCD data structures + */ + +#ifndef __ANYKA_OTG_HS_H_ +#define __ANYKA_OTG_HS_H_ + + +#include +#include + +#define AKOTG_HC_HCD + +#define USB_OP_MOD_REG (AK_VA_SYSCTRL + 0x58) +#define USB_MODULE_RESET_REG (AK_VA_SYSCTRL + 0x20) + + +#define USB_HC_BASE_ADDR (AK_VA_USB) +#define H_MAXPACKET 512 /* bytes in fifo */ + +struct akotghc_usb_platform_data { + void (* gpio_init)(const struct gpio_info *info); + struct gpio_info gpio_pwr_on; + struct gpio_info gpio_pwr_off; + struct gpio_info switch_onboard; + struct gpio_info switch_extport; +}; + +#endif /* __ANYKA_OTG_HS_H_ */ diff --git a/include/plat-anyka/spi-anyka-slave.h b/include/plat-anyka/spi-anyka-slave.h new file mode 100644 index 00000000..02f6aee7 --- /dev/null +++ b/include/plat-anyka/spi-anyka-slave.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) anyka 2012 + * Wangsheng Gao + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ +#ifndef SPI_ANYKA_SLAVE_H_ +#define SPI_ANYKA_SLAVE_H_ + + +/* + * Spi command definitions + * Support 4294967295 commands + */ + + #define OPEN_WIFI 0x00000001 + #define CLOSE_WIFI 0x00000002 + + #define MAX_COMMAND 0xffffffff + +#endif /*SPI_ANYKA_SLAVE_H_*/ diff --git a/include/plat-anyka/tscp2007.h b/include/plat-anyka/tscp2007.h new file mode 100644 index 00000000..9c59df7d --- /dev/null +++ b/include/plat-anyka/tscp2007.h @@ -0,0 +1,46 @@ +#ifndef __LINUX_I2C_TSC2007_H +#define __LINUX_I2C_TSC2007_H + +enum { + ORIGIN_TOPLEFT = 1, + ORIGIN_BOTTOMLEFT, + ORIGIN_TOPRIGHT, + ORIGIN_BOTTOMRIGHT, +}; + +struct cp2007_intpin_info { + unsigned int pin; + char pulldown; //pulldown function flag + char pullup; //pullup function flag + char dir; //direction input/output + char int_pol; //interrupt polarity + unsigned int rst_pin; + int rst_time; + char rst_dir; + char rst_pol; + char rst_not; +}; + + +struct tscp2007_platform_data { + u16 model; /* 2007. */ + u16 x_plate_ohms; /* must be non-zero value */ + u16 max_rt; /* max. resistance above which samples are ignored */ + unsigned long poll_delay; /* delay (in ms) after pen-down event + before polling starts */ + unsigned long poll_period; /* time (in ms) between samples */ + int fuzzx; /* fuzz factor for X, Y and pressure axes */ + int fuzzy; + int fuzzz; + + struct cp2007_intpin_info intpin_info; + + int (*get_pendown_state)(unsigned int pin); + void (*clear_penirq)(void); /* If needed, clear 2nd level interrupt source */ + int (*init_platform_hw)(const struct cp2007_intpin_info *intpin_info); + void (*exit_platform_hw)(void); + + void (*reset_platform_hw)(const struct cp2007_intpin_info *intpin_info); +}; + +#endif /* __LINUX_I2C_TSC2007_H */ \ No newline at end of file diff --git a/include/plat-anyka/udc.h b/include/plat-anyka/udc.h new file mode 100644 index 00000000..aeef52af --- /dev/null +++ b/include/plat-anyka/udc.h @@ -0,0 +1,240 @@ +#ifndef __UDC_H__ +#define __UDC_H__ + +#include + +#define USB_OP_MOD_REG (AK_VA_SYSCTRL + 0x58) +#define USB_MODULE_RESET_REG (AK_VA_SYSCTRL + 0x20) + +/* Anyka usb register */ +#define USB_FUNCTION_ADDR 0x0 +#define USB_POWER_CTRL 0x1 +#define USB_INTERRUPT_1 0x2 +#define USB_INTERRUPT_2 0x4 +#define USB_INTERRUPT_TX 0x6 +#define USB_INTERRUPT_RX 0x8 +#define USB_INTERRUPT_COMM 0xA +#define USB_INTERRUPT_USB 0xB +#define USB_FRAME_NUM 0xC +#define USB_EP_INDEX 0xE +#define USB_TEST_MODE 0xF +#define USB_TX_MAX 0x10 +#define USB_CTRL_1 0x12 +#define USB_CTRL_1_2 0x13 +#define USB_RX_MAX 0x14 +#define USB_CTRL_2 0x16 +#define USB_CTRL_2_2 0x17 +#define USB_EP_COUNT 0x18 +#define USB_CFG_INFO 0x1F +#define USB_EP0_FIFO 0x20 +#define USB_EP1_FIFO 0x24 +#define USB_EP2_FIFO 0x28 +#define USB_EP3_FIFO 0x2C +#define USB_EP4_FIFO 0x30 +#define USB_EP5_FIFO 0x34 +#define USB_DEVICE_CTRL 0x60 +#define USB_DMA_INTR 0x200 +#define USB_DMA_CTRL1 0x204 +#define USB_DMA_CTRL2 0x214 +#define USB_DMA_CTRL3 0x224 +#define USB_DMA_CTRL4 0x234 +#define USB_DMA_ADDR1 0x208 +#define USB_DMA_ADDR2 0x218 +#define USB_DMA_ADDR3 0x228 +#define USB_DMA_ADDR4 0x238 +#define USB_DMA_COUNT1 0x20C +#define USB_DMA_COUNT2 0x21C +#define USB_DMA_COUNT3 0x22C +#define USB_DMA_COUNT4 0x23C +#define USB_EP0_NUM 0x330 +#define USB_EP2_NUM 0x334 +#define USB_MODE_STATUS 0x344 + +#define USB_LINESTATE_WP (3 << 15) +#define USB_DP_PU_EN (1 << 14) +#define USB_ID_CFG (3 << 12) +#define USB_PHY_CFG (0x3f << 6) +#define USB_SUSPEND_EN (1 << 2) +#define USB_ID_CLIENT (3) +#define USB_ID_HOST (1) +#define USB_PHY_CLIENT (23) +#define USB_PHY_HOST (31) +#define USB_ISO_UPDATE (1 << 7) +#define USB_HSPEED_EN (1 << 5) +#define USB_HSPEED_MODE (1 << 4) +#define USB_RESUME_EN (1 << 2) +#define USB_SUSPEND_MODE (1 << 1) +#define USB_SUSPENDM_EN (1 << 0) + +#define USB_ENABLE_DMA (1) +#define USB_DIRECTION_RX (0<<1) +#define USB_DIRECTION_TX (1<<1) +#define USB_DMA_MODE1 (1<<2) +#define USB_DMA_MODE0 (0<<2) +#define USB_DMA_INT_ENABLE (1<<3) +#define USB_DMA_INT_DISABLE (0<<3) +#define USB_DMA_BUS_ERROR (1<<8) +#define USB_DMA_BUS_MODE0 (0<<9) +#define USB_DMA_BUS_MODE1 (1<<9) +#define USB_DMA_BUS_MODE2 (2<<9) +#define USB_DMA_BUS_MODE3 (3<<9) +#define DMA_CHANNEL1_INT (1) +#define DMA_CHANNEL2_INT (2) +#define DMA_CHANNEL3_INT (4) +#define DMA_CHANNEL4_INT (8) +#define USB_EP0_INDEX (0) +#define USB_EP1_INDEX (1 << 0) +#define USB_EP2_INDEX (1 << 1) +#define USB_EP3_INDEX ((1 << 1)|(1 << 0)) +#define USB_EP4_INDEX (1 << 2) +#define USB_EP5_INDEX ((1 << 2)|(1 << 0)) + +// #define USB_11 [> usb1.1 <] + +#define EP0_FIFO_SIZE 64 /* control, not 64byte? */ +#define EP1_FIFO_SIZE 64 /* interrupt */ +#define EP2_FIFO_SIZE 512 /* ep2 in bulk */ +#define EP3_FIFO_SIZE 512 /* ep3 out bulk */ +#define EP4_FIFO_SIZE 512 /* ep4 in bulk */ +#define EP5_FIFO_SIZE 512 /* ep5 out bulk */ + +#define USB_TXCSR_AUTOSET (0x80) +#define USB_TXCSR_ISO (0x40) +#define USB_TXCSR_MODE1 (0x20) +#define USB_TXCSR_DMAREQENABLE (0x10) +#define USB_TXCSR_FRCDATATOG (0x8) +#define USB_TXCSR_DMAREQMODE1 (0x4) +#define USB_TXCSR_DMAREQMODE0 (0x0) + +struct akudc_request; + +/* the struct of endpoint for Anyka udc */ +struct akudc_ep { + struct usb_ep ep; /* the basic endpoint */ + struct usb_gadget *gadget; /* the gadget for udc */ + struct usb_endpoint_descriptor *desc; /* the descriptor for this endpoint */ + + struct list_head queue; /* the queue head for request of this endpoint */ + + struct work_struct work; + struct akudc_request *req; /* req be about to handle */ + + struct akudc_udc *udc; /* the udc struct */ + int maxpacket; + volatile int done; /* whether the current request is complete */ + unsigned int bufaddr; + dma_addr_t bufphys; + + unsigned irq_enable; /* whether the endpoint cpu irq is enable */ + volatile unsigned stopped; /*whether the endpoint is stopped */ + // unsigned stopped:1; + unsigned is_in:1; /* whether the endpoint is in */ + unsigned is_iso:1; + // unsigned fifo_bank:1; + u8 l2_buf_id; /* the l2 buffer id for this endpoint */ +}; + +struct akudc_request { + struct list_head queue; /* ep's requests */ + struct usb_request req; + int status; +}; + +/* the status flag for ep0 Control SETUP Transaction */ +enum ep0_status { + EP0_IDLE, + EP0_IN_DATA_PHASE, + EP0_OUT_DATA_PHASE, + EP0_END_XFER, + EP0_STALL, +}; +#if 0 +static const char *ep0states[]= { + "EP0_IDLE", + "EP0_IN_DATA_PHASE", + "EP0_OUT_DATA_PHASE", + "EP0_END_XFER", + "EP0_STALL", +}; +#endif +struct usb_l2 { + void *buf; + dma_addr_t phys; +}; + +static const char * const ep_name[] = { + "ep0", + "ep1-int", + "ep2in-bulk", + "ep3out-bulk", + "ep4in-bulk", + "ep5out-bulk", +}; + +#define ENDPOINTS_NUM ARRAY_SIZE(ep_name) + +struct akudc_udc { + struct usb_gadget gadget; /* the core struct for anyka udc */ + struct usb_gadget_driver *driver; /* the core struct for function drivers */ + + struct akudc_ep ep[ENDPOINTS_NUM]; /* the endpoints for udc */ + enum ep0_status ep0_status; + + void __iomem *baseaddr; + + unsigned int mcu_irq; /* the irq for cpu irq */ + unsigned int dma_irq; /* the irq for l2 dma irq */ + struct clk *clk; /* the clk for udc */ + char addr; /* assigned device address */ + u16 devstatus; + int ep0state; /* the status flag for ep0 Control SETUP Transaction */ + unsigned req_std : 1; + unsigned req_config : 1; + unsigned req_pending : 1; + unsigned high_speed: 1; + struct proc_dir_entry *pde; +}; + + +/* usb dosen't have vbus and id pin,so these bit must be set */ +static inline void set_usb_as_slave(void) +{ + unsigned long con; + + con = __raw_readl(USB_OP_MOD_REG); + con &= ~(0xff << 6); + con |= ((0x3 << 12)|(0x17 << 6)); + __raw_writel(con, USB_OP_MOD_REG); +} + +static inline void usb_vbus_wakeup(int enable) +{ +#if defined (CONFIG_ARCH_AK37) + /* open usb detect */ + if(enable) { + REG32(AK_VA_SYSCTRL + 0xd8) &= ~(1 << 0); //falling-trig + + REG32(AK_VA_SYSCTRL + 0xd8) |= (1 << 2); //clear usb det + REG32(AK_VA_SYSCTRL + 0xd8) &= ~(1 << 2); + + REG32(AK_VA_SYSCTRL + 0xd8) |= (1 << 4); //enable usb detect + } else { + REG32(AK_VA_SYSCTRL + 0xd8) |= (1 << 2); //clear usb det + REG32(AK_VA_SYSCTRL + 0xd8) &= ~(1 << 2); + + REG32(AK_VA_SYSCTRL + 0xd8) &= ~(1 << 4); //disable usb detect + } +#endif +} + +static void udc_reg_writel(unsigned long __iomem *__reg, + unsigned long value, int bits, int index); +//static unsigned long udc_reg_readl(unsigned long __iomem *__reg, +// int bits, int index); + +static void udc_reinit(struct akudc_udc *udc); +static void akudc_enable(struct akudc_udc *udc); +static void akudc_disable(struct akudc_udc *udc); +static void done(struct akudc_ep *ep, struct akudc_request *req, int status); + +#endif /* __UDC_H__ */ diff --git a/include/plat-anyka/usb-hc.h b/include/plat-anyka/usb-hc.h new file mode 100644 index 00000000..4361f37e --- /dev/null +++ b/include/plat-anyka/usb-hc.h @@ -0,0 +1,912 @@ +#ifndef _ANYKA_USB_HC_H_ +#define _ANYKA_USB_HC_H_ + + +#include +#include +#include +#include + +#include +#include +#include "usb-reg-define.h" + +#include "otg-hshcd.h" + +#define DYNAMIC_EPFIFO +/* + * IN V300, we have 5 epfifo. epfifo 0 using for Control transfer. + * epfifo1-epfifi4 usnig for Interrupt/Bulk/Isochronous tranfer. + * so, MAX_EP_NUM is 4 (EP4) + */ +#define MAX_EP_NUM (4) +#define USBDMA_CHANNEL_NUM (4) + +#define LOG2_PERIODIC_SIZE 10 /* arbitrary; this matches OHCI */ +#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE) + + /* usb 1.1 says max 90% of a frame is available for periodic transfers. + * this driver doesn't promise that much since it's got to handle an + * IRQ per packet; irq handling latencies also use up that time. + * + * NOTE: the periodic schedule is a sparse tree, with the load for + * each branch minimized. see fig 3.5 in the OHCI spec for example. + */ +#define MAX_PERIODIC_LOAD 500 /* out of 1000 usec */ + + /*-------------------------------------------------------------------------*/ + + /*Common Registers Access - Just use base address plus offset */ + + static DEFINE_SPINLOCK(USB_HC_REG_LOCK); + +#define hc_readb(reg) __raw_readb(USB_HC_BASE_ADDR + (reg)) +#define hc_writeb(val, reg) __raw_writeb(val, USB_HC_BASE_ADDR + (reg)) + +#define hc_readw(reg) __raw_readw(USB_HC_BASE_ADDR + (reg)) +#define hc_writew(val, reg) __raw_writew(val, USB_HC_BASE_ADDR + (reg)) + +#define hc_readl(reg) __raw_readl(USB_HC_BASE_ADDR + (reg)) +#define hc_writel(val, reg) __raw_writel(val, USB_HC_BASE_ADDR + (reg)) + + struct akotghc_ep { + struct usb_host_endpoint *hep; + struct usb_device *udev; + + u16 maxpacket; + u8 epnum; + u8 epfifo; + u8 nextpid; + + u16 error_count; + u16 nak_count; + u16 length; /* of current packet */ + + bool bdma; + + /* periodic schedule */ + u32 period; + u32 branch; + u32 load; + struct akotghc_ep *next; + + /* async schedule */ + struct list_head schedule; + }; + +/** + struct for usb dma channel + */ + struct akotg_dma + { + u8 channel; //channel num + u8 status; //channel status + u8 dir; //transfer direction, + u8 epfifo; //dma is bind to which usb ep fifo + u8 l2buffer; //l2 buffer which is bind to ep fifo + u32 addr; //inner addr for dma, start with 0x70000000 + u32 count; //dma count + u32 phyaddr; //phycical address for dma + }; + + struct akotg_usbhc { + spinlock_t lock; + + struct clk *clk; + + /* sw model */ + struct timer_list timer; + struct akotghc_ep *next_periodic; + struct akotghc_ep *next_async_ep0; + struct akotghc_ep *next_async_epx[MAX_EP_NUM]; + + struct akotghc_ep *active_ep0; + struct akotghc_ep *active_epx[MAX_EP_NUM]; + unsigned long jiffies_ep0; + unsigned long jiffies_epx[MAX_EP_NUM]; + + int mcu_irq; + + //dma + int dma_irq; //irq alloced for dma + struct akotg_dma dma_channel[USBDMA_CHANNEL_NUM]; + + u32 port_status; + u16 frame; + + /* async schedule: control, bulk */ + struct list_head async_ep0; + struct list_head async_epx[MAX_EP_NUM]; + + /* periodic schedule: interrupt, iso */ + u16 load[PERIODIC_SIZE]; + struct akotghc_ep *periodic[PERIODIC_SIZE]; + unsigned periodic_count; + }; + + + struct epfifo_mapping { + int epfifo; + int used; /* 0 - Unused, 1 - used */ + int epnum; /* USB Device endpoint number: 1 ~ 16 */ + int direction; /* 0 - In, 1 - Out */ +}; + +struct akotghc_epfifo_mapping { + spinlock_t lock; + struct epfifo_mapping mapping[MAX_EP_NUM]; +}; + +/** + enum for DMA channel status + */ +enum +{ + USB_DMA_CHANNEL_IDLE, //idle + USB_DMA_CHANNEL_ALLOC, //alloced for a specific ep fifo but not under tranfering + USB_DMA_CHANNEL_TRANS //there is a dma tranfering in this channel +}; + +irqreturn_t akotg_usbhc_irq(struct usb_hcd *hcd); +irqreturn_t akotg_dma_irq(int irqnum, void *__hcd); + +int akotg_usbhc_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags); +int akotg_usbhc_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status); +void akotg_usbhc_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *hep); +void akotg_usbhc_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep); +int akotg_usbhc_get_frame(struct usb_hcd *hcd); +int akotg_usbhc_hub_status_data(struct usb_hcd *hcd, char *buf); +void akotg_usbhc_hub_descriptor (struct akotg_usbhc *akotghc, struct usb_hub_descriptor *desc); +void akotg_usbhc_timer(unsigned long _akotghs); +int akotg_usbhc_hub_control( + struct usb_hcd *hcd, + u16 typeReq, + u16 wValue, + u16 wIndex, + char *buf, + u16 wLength +); + +int akotg_usbhc_bus_suspend(struct usb_hcd *hcd); +int akotg_usbhc_bus_resume(struct usb_hcd *hcd); +void akotg_usbhc_stop(struct usb_hcd *hcd); +int akotg_usbhc_start(struct usb_hcd *hcd); +int akotg_usbhc_reset(struct usb_hcd *hcd); + + +void port_power(struct akotg_usbhc *akotghc, int is_on); + +static inline struct akotg_usbhc *hcd_to_akotg_usbhc(struct usb_hcd *hcd) +{ + return (struct akotg_usbhc *) (hcd->hcd_priv); +} + +static inline struct usb_hcd *akotg_usbhc_to_hcd(struct akotg_usbhc *akotghc) +{ + return container_of((void *) akotghc, struct usb_hcd, hcd_priv); +} + + +/* + * Part II: Index Registers Access - Spinlock+IRQ protection + */ +//static DEFINE_SPINLOCK(fsh_reg_lock); + +static inline unsigned char hc_index_readb(int epindex, int reg) +{ + unsigned long flags; + unsigned char val; + + spin_lock_irqsave(&USB_HC_REG_LOCK, flags); + + hc_writeb(epindex, USB_REG_INDEX); + val = hc_readb(reg); + + spin_unlock_irqrestore(&USB_HC_REG_LOCK, flags); + + return val; +} + +static inline void hc_index_writeb(int epindex, unsigned char val, int reg) +{ + unsigned long flags; + + spin_lock_irqsave(&USB_HC_REG_LOCK, flags); + + hc_writeb(epindex, USB_REG_INDEX); + hc_writeb(val, reg); + + spin_unlock_irqrestore(&USB_HC_REG_LOCK, flags); +} + +static inline unsigned short hc_index_readw(int epindex, int reg) +{ + unsigned long flags; + unsigned short val; + + spin_lock_irqsave(&USB_HC_REG_LOCK, flags); + + hc_writeb(epindex, USB_REG_INDEX); + val = hc_readw(reg); + + spin_unlock_irqrestore(&USB_HC_REG_LOCK, flags); + + return val; + +} + +static inline void hc_index_writew(int epindex, unsigned short val, int reg) +{ + unsigned long flags; + + spin_lock_irqsave(&USB_HC_REG_LOCK, flags); + + hc_writeb(epindex, USB_REG_INDEX); + hc_writew(val, reg); + + spin_unlock_irqrestore(&USB_HC_REG_LOCK, flags); +} + +static inline unsigned long hc_index_readl(int epindex, int reg) +{ + unsigned long flags; + unsigned long val; + + spin_lock_irqsave(&USB_HC_REG_LOCK, flags); + + hc_writeb(epindex, USB_REG_INDEX); + val = hc_readl(reg); + + spin_unlock_irqrestore(&USB_HC_REG_LOCK, flags); + + return val; +} + +static inline void hc_index_writel(int epindex, unsigned long val, int reg) +{ + unsigned long flags; + + spin_lock_irqsave(&USB_HC_REG_LOCK, flags); + + hc_writeb(epindex, USB_REG_INDEX); + hc_writel(val, reg); + + spin_unlock_irqrestore(&USB_HC_REG_LOCK, flags); + +} +static inline void flush_ep0_fifo(void) +{ + unsigned long flags; + + spin_lock_irqsave(&USB_HC_REG_LOCK, flags); + + hc_writeb(0, USB_REG_INDEX); + if (hc_readb(USB_REG_TXCSR1) & (USB_CSR01_RXPKTRDY | USB_CSR01_TXPKTRDY)) + hc_writeb(USB_CSR02_FLUSHFIFO, USB_REG_TXCSR2); + + spin_unlock_irqrestore(&USB_HC_REG_LOCK, flags); +} + +static inline void flush_epx_tx_fifo(int i) +{ + unsigned long flags; + + spin_lock_irqsave(&USB_HC_REG_LOCK, flags); + + hc_writeb(i, USB_REG_INDEX); + if (hc_readb(USB_REG_TXCSR1) & (USB_CSR01_RXPKTRDY)) + hc_writeb(USB_TXCSR1_FLUSHFIFO, USB_REG_TXCSR1); + + spin_unlock_irqrestore(&USB_HC_REG_LOCK, flags); + +} + +static inline void flush_epx_rx_fifo(int i) +{ + unsigned long flags; + + spin_lock_irqsave(&USB_HC_REG_LOCK, flags); + + hc_writeb(i, USB_REG_INDEX); + if (hc_readb(USB_REG_RXCSR1) & (USB_RXCSR1_RXPKTRDY)) + hc_writeb(USB_RXCSR1_FLUSHFIFO, USB_REG_RXCSR1); + + spin_unlock_irqrestore(&USB_HC_REG_LOCK, flags); +} + +static inline void flush_epx_fifo(int i) +{ + flush_epx_tx_fifo(i); + flush_epx_rx_fifo(i); +} + +static inline void flush_ep_fifo(int i) +{ + if (i == 0) + flush_ep0_fifo(); + else + flush_epx_fifo(i); +} + +static inline void set_epx_rx_mode(int i) +{ + u8 regval; + unsigned long flags; + + spin_lock_irqsave(&USB_HC_REG_LOCK, flags); + + hc_writeb(i, USB_REG_INDEX); + regval = hc_readb(USB_REG_TXCSR2); + regval &= ~USB_TXCSR2_MODE; + hc_writeb(regval, USB_REG_TXCSR2); + + spin_unlock_irqrestore(&USB_HC_REG_LOCK, flags); +} + +static inline void set_epx_tx_mode(int i) +{ + u8 regval; + unsigned long flags; + + spin_lock_irqsave(&USB_HC_REG_LOCK, flags); + + hc_writeb(i, USB_REG_INDEX); + regval = hc_readb(USB_REG_TXCSR2); + regval |= USB_TXCSR2_MODE; + hc_writeb(regval, USB_REG_TXCSR2); + + spin_unlock_irqrestore(&USB_HC_REG_LOCK, flags); +} + +static inline void clear_epx_tx_data_toggle(int i) +{ + hc_index_writeb(i, USB_TXCSR1_CLRDATATOG, USB_REG_TXCSR1); + //hc_index_writeb(i, USB_RXCSR2_DMAREQENAB, USB_REG_TXCSR2); //??? +} + +static inline void clear_epx_rx_data_toggle(int i) +{ + hc_index_writeb(i, USB_RXCSR1_CLRDATATOG, USB_REG_RXCSR1); +} + +/* + * Valid types: + * 1 - Isochronous + * 2 - Bulk + * 3 - Interrupt + * Invalid type: + * 0 - Illegal + */ +static inline void set_epx_tx_type(int i, int epnum, int type) +{ + BUG_ON(i < 0 || i > MAX_EP_NUM); + BUG_ON(type < 0 || type > 3); + + hc_index_writeb(i, type << 4 | epnum, USB_REG_TXTYPE); +} + +static inline void set_epx_rx_type(int i, int epnum, int type) +{ + BUG_ON(i < 0 || i > MAX_EP_NUM); + BUG_ON(type < 0 || type > 3); + + hc_index_writeb(i, type << 4 | epnum, USB_REG_RXTYPE); +} + +static inline void enable_ep0_interrupt(void) +{ + u8 regval; + unsigned long flags; + + local_irq_save(flags); + + regval = hc_readb(USB_REG_INTRTXE); + regval |= USB_INTR_EP0; + hc_writeb(regval, USB_REG_INTRTXE); + + local_irq_restore(flags); +} + +static inline void enable_epx_tx_interrupt(int i) +{ + u8 regval; + unsigned long flags; + + local_irq_save(flags); + + regval = hc_readb(USB_REG_INTRTXE); + regval |= (1 << i); + hc_writeb(regval, USB_REG_INTRTXE); + + local_irq_restore(flags); +} + +static inline void enable_epx_rx_interrupt(int i) +{ + u8 regval; + unsigned long flags; + + local_irq_save(flags); + + regval = hc_readb(USB_REG_INTRRXE); + regval |= (1 << i); + hc_writeb(regval, USB_REG_INTRRXE); + + local_irq_restore(flags); +} + +static inline void disable_ep0_interrupt(void) +{ + u8 regval; + unsigned long flags; + + local_irq_save(flags); + + regval = hc_readb(USB_REG_INTRTXE); + regval &= ~USB_INTR_EP0; + hc_writeb(regval, USB_REG_INTRTXE); + + local_irq_restore(flags); +} + +static inline void disable_epx_tx_interrupt(int i) +{ + u8 regval; + unsigned long flags; + + local_irq_save(flags); + + regval = hc_readb(USB_REG_INTRTXE); + regval &= ~(1 << i); + hc_writeb(regval, USB_REG_INTRTXE); + + local_irq_restore(flags); +} + +static inline void disable_epx_rx_interrupt(int i) +{ + u8 regval; + unsigned long flags; + + local_irq_save(flags); + + regval = hc_readb(USB_REG_INTRRXE); + regval &= ~(1 << i); + hc_writeb(regval, USB_REG_INTRRXE); + + local_irq_restore(flags); +} + +static inline void disable_epx_interrupt(int i) +{ + disable_epx_tx_interrupt(i); + disable_epx_rx_interrupt(i); +} + +static inline void disable_ep_interrupt(int i) +{ + BUG_ON(i < 0 || i > MAX_EP_NUM); + + if (i == 0) { + disable_ep0_interrupt(); + } else { + disable_epx_interrupt(i); + } +} + +static inline void clear_all_interrupts(void) +{ + hc_writeb(0x0, USB_REG_INTRUSBE); + hc_writew(0x0, USB_REG_INTRTXE); + hc_writew(0x0, USB_REG_INTRRXE); + + hc_readb(USB_REG_INTRUSB); + hc_readw(USB_REG_INTRTX); + hc_readw(USB_REG_INTRRX); +} + +static inline void reset_endpoint(int i) +{ + BUG_ON(i < 0 || i > MAX_EP_NUM); + + disable_ep_interrupt(i); + if (i == 0) { + flush_ep0_fifo(); + } else { + flush_epx_fifo(i); + set_epx_rx_type(i, 0, 0); + set_epx_tx_type(i, 0, 0); + } +} + +static inline void reset_endpoints(void) +{ + int i; + + for (i = 0; i < MAX_EP_NUM + 1; i++) { + reset_endpoint(i); + } +} + + +static inline u8 akotg_alloc_l2_buffer(int epfifo) +{ + l2_device_t l2addr[] = {ADDR_USB_EP1, ADDR_USB_EP2, ADDR_USB_EP3,ADDR_USB_EP4}; + + //check param + if((epfifo < 1) || (epfifo > MAX_EP_NUM)) + return BUF_NULL; + + //alloc l2 buffer + return l2_alloc_nowait(l2addr[epfifo-1]); +} + +static inline void akotg_free_l2_buffer(int epfifo) +{ + l2_device_t l2addr[] = {ADDR_USB_EP1, ADDR_USB_EP2, ADDR_USB_EP3,ADDR_USB_EP4}; + + /* + * check param. + * In V300, we have epfifo0、epfifo1、epfifo2、epfifo3、epfifo4. + * But only epfifo1、epfifo2、epfifo3、epfifo4 can use L2 DMA. + */ + if((epfifo < 1) || (epfifo > MAX_EP_NUM)) + return; + + //alloc l2 buffer + l2_free(l2addr[epfifo-1]); +} + + + +static inline void akotg_dma_init(struct akotg_usbhc *otg) +{ + int i; + + memset(otg->dma_channel, 0, sizeof(otg->dma_channel)); + + for(i = 0; i < USBDMA_CHANNEL_NUM; i++) + { + otg->dma_channel[i].channel = i; + otg->dma_channel[i].status = USB_DMA_CHANNEL_IDLE; + otg->dma_channel[i].l2buffer = BUF_NULL; + } +} + + +/** + config dma register +*/ +static inline bool akotg_dma_config(struct akotg_dma *dma) +{ + u8 channel = dma->channel; + u8 dir = dma->dir; + u8 epfifo = dma->epfifo; + + u32 dmactrl; + + + //config dma address and count + hc_writel(dma->addr, USB_DMA_ADDR(channel)); + hc_writel(dma->count, USB_DMA_COUNT(channel)); + + //config dma control + dmactrl = (USB_ENABLE_DMA|dir|USB_DMA_MODE1|USB_DMA_INT_ENABLE|(epfifo <<4)|USB_DMA_BUS_MODE3); + //pr_err("akotg_dma_config, dmactrl=%x\n", dmactrl); + hc_writel(dmactrl, USB_DMA_CTRL(channel)); + + dma->status = USB_DMA_CHANNEL_TRANS; + + return true; +} + + +/** + config dma struct +*/ +static inline bool akotg_dma_set_struct(struct akotg_dma *dma, u8 dir, u8 epfifo, u8 l2buffer, u32 count, dma_addr_t phyaddr) +{ + if((epfifo < 1) || (epfifo > MAX_EP_NUM)) + return false; + + dma->dir = dir; + dma->count = count; + dma->epfifo = epfifo; + dma->l2buffer = l2buffer; + + //config dma addr + dma->addr = 0x70000000 + 0x1000000*(epfifo-1); + dma->phyaddr = phyaddr; + + return true; +} + + + +/** + alloc dma channel for epfifo +*/ +static inline struct akotg_dma * akotg_dma_alloc(struct akotg_usbhc *otg, u8 epfifo) +{ + struct akotg_dma *dma; + u8 l2buffer; + + if((epfifo < 1) || (epfifo > MAX_EP_NUM)) + return NULL; + + //alloc dma channel, do simple now, + //we'll complete it later + dma = &otg->dma_channel[epfifo-1]; + if(dma->status != USB_DMA_CHANNEL_IDLE){ + return NULL; + } + + //alloc l2 buffer + l2buffer = akotg_alloc_l2_buffer(epfifo); + if(BUF_NULL == l2buffer){ + return NULL; + } + + //printk("<%d:%d>\n", epfifo, l2buffer); + + dma->status = USB_DMA_CHANNEL_ALLOC; + dma->l2buffer = l2buffer; + + return dma; +} + +/** + get dma struct for channel i +*/ +static inline struct akotg_dma *akotg_dma_get_struct(struct akotg_usbhc *otg, u8 channel) +{ + if(channel > USBDMA_CHANNEL_NUM) + return NULL; + + return &otg->dma_channel[channel]; +} + +/** + the usb dma trans will be stopped if a short packet is received, + we can use this func to check the real number of data been received +*/ +static inline u32 akotg_dma_get_trans_length(struct akotg_usbhc *otg, u8 epfifo) +{ + int i; + struct akotg_dma *dma; + u32 addr; + + for(i = 0; i < USBDMA_CHANNEL_NUM; i++) + { + dma = &otg->dma_channel[i]; + + if((dma->epfifo == epfifo) && (dma->status == USB_DMA_CHANNEL_TRANS)) + { + addr = hc_readl(USB_DMA_ADDR(dma->channel)); + return (addr - dma->addr); + } + } + + return 0; +} + +/** + clear dma, + epfifo: 0-clear all, >0 clear dma for epfifo +*/ +static inline void akotg_dma_clear(struct akotg_usbhc *otg, u8 epfifo) +{ + int i; + struct akotg_dma *dma; + + for(i = 0; i < USBDMA_CHANNEL_NUM; i++) + { + dma = &otg->dma_channel[i]; + + if((epfifo != 0) && (dma->epfifo != epfifo)){ + continue; + } + + if(dma->l2buffer != BUF_NULL) + { + //l2 free + akotg_free_l2_buffer(dma->epfifo); + } + + hc_writel(0, USB_DMA_CTRL(i)); + + dma->status = USB_DMA_CHANNEL_IDLE; + dma->l2buffer = BUF_NULL; + } + + +} + +static inline void init_epfifo_mapping(struct akotghc_epfifo_mapping *akotg_mapping) +{ + int i; + struct epfifo_mapping *mapping; + + spin_lock_init(&akotg_mapping->lock); + + for (i = 0; i < MAX_EP_NUM; i++) { + mapping = &akotg_mapping->mapping[i]; + mapping->epfifo = i + 1; /* EPFIFO1 ~ MAX_EP_NUM(EPFIFO4) is mapping */ + mapping->used = 0; + mapping->epnum = 0; + mapping->direction = 0; + } +} + +static inline bool __is_epnum_mapped( + struct akotghc_epfifo_mapping *akotg_mapping, int epnum, int direction) +{ + int i; + struct epfifo_mapping *mapping; + + if(epnum == 0) + return true; + + for (i = 0; i < MAX_EP_NUM; i++) { + mapping = &akotg_mapping->mapping[i]; + if (mapping->used + && (mapping->epnum == epnum) + && (mapping->direction == direction)) { + return true; + } + } + + return false; +} + +static inline bool is_epnum_mapped(struct akotghc_epfifo_mapping *akotg_mapping, + int epnum, int direction) +{ + bool ret; + unsigned long flags; + + BUG_ON(akotg_mapping == NULL); + + if(epnum == 0) + return true; + + spin_lock_irqsave(&akotg_mapping->lock, flags); + + ret = __is_epnum_mapped(akotg_mapping, epnum, direction); + + spin_unlock_irqrestore(&akotg_mapping->lock, flags); + + return ret; + +} + +static inline bool __map_epnum_to_epfifo( + struct akotghc_epfifo_mapping *akotg_mapping, + int epnum, int direction, int *epfifo) +{ + int i; + struct epfifo_mapping *mapping; + + if (__is_epnum_mapped(akotg_mapping, epnum, direction)) + return false; + + //allocate 512(or 1024) byte size of filo to epnum + for (i = 0; i < MAX_EP_NUM; i++) { + mapping = &akotg_mapping->mapping[i]; + if (!mapping->used) { + mapping->used = 1; + mapping->epnum = epnum; + mapping->direction = direction; + *epfifo = mapping->epfifo; + return true; + } + } + + return false; +} + +static inline bool map_epnum_to_epfifo(struct akotghc_epfifo_mapping *akotg_mapping, + int epnum, int direction, int *epfifo) +{ + bool ret; + unsigned long flags; + + BUG_ON(akotg_mapping == NULL); + + if (is_epnum_mapped(akotg_mapping, epnum, direction)) + return false; + + spin_lock_irqsave(&akotg_mapping->lock, flags); + + ret = __map_epnum_to_epfifo(akotg_mapping, epnum, direction, epfifo); + + spin_unlock_irqrestore(&akotg_mapping->lock, flags); + + return ret; +} + +static inline bool epfifo_to_epnum(struct akotghc_epfifo_mapping *akotg_mapping, int epfifo, int *epnum, int *direction) +{ + int ret; + unsigned long flags; + struct epfifo_mapping *mapping; + + ret = false; + + spin_lock_irqsave(&akotg_mapping->lock, flags); + + mapping = &akotg_mapping->mapping[epfifo]; + if (mapping->used) { + *epnum = mapping->epnum; + *direction = mapping->direction; + ret = true; + } else { + *epnum = 0; + *direction = 0; + ret = false; + } + + spin_unlock_irqrestore(&akotg_mapping->lock, flags); + + return ret; +} + +static inline bool epnum_to_epfifo(struct akotghc_epfifo_mapping *akotg_mapping, int epnum, int direction, int *epfifo) +{ + int i; + unsigned long flags; + struct epfifo_mapping *mapping; + + if (epnum == 0) { + *epfifo = 0; + return true; + } + + spin_lock_irqsave(&akotg_mapping->lock, flags); + + for (i = 0; i < MAX_EP_NUM; i++) { + mapping = &akotg_mapping->mapping[i]; + if (mapping->used && (mapping->epnum == epnum) && (mapping->direction == direction)) { + *epfifo = mapping->epfifo; + spin_unlock_irqrestore(&akotg_mapping->lock, flags); + return true; + } + } + + spin_unlock_irqrestore(&akotg_mapping->lock, flags); + + return false; +} + +static inline void set_usb_as_host(void) +{ + unsigned long con; + + con = __raw_readl(USB_OP_MOD_REG); + con &= ~(0xff << 6); + con |= (0x1 << 12)|(0x1f << 6); + __raw_writel(con, USB_OP_MOD_REG); +} + +static inline void usb_reset_phy(struct akotg_usbhc *otghc) +{ + unsigned long con; + + con = __raw_readl(USB_OP_MOD_REG); + con &= ~(0x7 << 0); + con |= (1 << 0); + __raw_writel(con, USB_OP_MOD_REG); +} + +/* power up and set usb suspend */ +static inline void usb_power_up(struct akotg_usbhc *otghc) +{ + unsigned long con; + + con = __raw_readl(USB_OP_MOD_REG); + con &= ~(0x7 << 0); + con |= (3 << 1); + __raw_writel(con, USB_OP_MOD_REG); +} + +#endif diff --git a/include/plat-anyka/usb-reg-define.h b/include/plat-anyka/usb-reg-define.h new file mode 100644 index 00000000..3b8779ad --- /dev/null +++ b/include/plat-anyka/usb-reg-define.h @@ -0,0 +1,262 @@ +#ifndef _ANYKA_USB_REG_DEFINE_H_ +#define _ANYKA_USB_REG_DEFINE_H_ + + /** Control Registers*/ +#define USB_REG_FADDR (0x0000) // 8-bit +#define USB_REG_POWER (0x0001) // 8-bit +#define USB_REG_INTRTX (0x0002) // 16-bit, read clear +#define USB_REG_INTRRX (0x0004) // 16-bit, read clear +#define USB_REG_INTRTXE (0x0006) // 16-bit +#define USB_REG_INTRRXE (0x0008) // 16-bit +#define USB_REG_INTRUSB (0x000A) // 8-bit, read clear +#define USB_REG_INTRUSBE (0x000B) // 8-bit +#define USB_REG_FRAME (0x000C) // 16-bit +#define USB_REG_INDEX (0x000E) // 8-bit +#define USB_REG_TESEMODE (0x000F) // 8-bit +#define USB_REG_DEVCTL (0x0060) // 8-bit + +/** Endpoint Control/Status Registers */ +#define USB_REG_TXMAXP (0x0010) // 16-bit, when index == 1~4 +#define USB_REG_TXCSR1 (0x0012) // 8-bit, when index == 1~4 +#define USB_REG_TXCSR2 (0x0013) // 8-bit, when index == 1~4 +#define USB_REG_RXMAXP (0x0014) // 16-bit, when index == 1~4 +#define USB_REG_RXCSR1 (0x0016) // 8-bit, when index == 1~4 +#define USB_REG_RXCSR2 (0x0017) // 8-bit, when index == 1~4 +#define USB_REG_COUNT0 (0x0018) // 16-bit, when index == 0 +#define USB_REG_RXCOUNT (0x0018) // 16-bit, when index == 1~4 +#define USB_REG_TXTYPE (0x001A) // 8-bit, when index == 1~4, host only +#define USB_REG_NAKLIMIT0 (0x001B) // 8-bit, when index == 0, host only +#define USB_REG_TXINTERVAL (0x001B) // 8-bit, when index == 1~4, host only +#define USB_REG_RXTYPE (0x001C) // 8-bit, when index == 1~4, host only +#define USB_REG_RXINTERVAL (0x001D) // 8-bit, when index == 1~4, host only +#define USB_REG_CONFIGDATA (0x001F) // 8-bit, when index == 0 +#define USB_REG_FIFOSIZE (0x001F) // 8-bit, when index == 1~4 + +/** USB DMA */ +#define USB_DMA_INTR (0x0200) +#define USB_DMA_CTRL_BASE (0x0204) +#define USB_DMA_ADDR_BASE (0x0208) +#define USB_DMA_COUNT_BASE (0x020c) +#define USB_DMA_CTRL(n) (USB_DMA_CTRL_BASE+(n)*0x10) +#define USB_DMA_ADDR(n) (USB_DMA_ADDR_BASE+(n)*0x10) +#define USB_DMA_COUNT(n) (USB_DMA_COUNT_BASE+(n)*0x10) + +#define USB_REG_REQPKTCNT_BASE (0x0304) +#define USB_REG_REQPKTCNT(ep) (USB_REG_REQPKTCNT_BASE + (ep-1)*0x4) + +/** FIFOs Entry */ +#define USB_FIFO_EP0 (0x0020) // 8- / 16- / 32-bit +#define USB_FIFO_EP1 (0x0024) // 8- / 16- / 32-bit +#define USB_FIFO_EP2 (0x0028) // 8- / 16- / 32-bit +#define USB_FIFO_EP3 (0x002C) // 8- / 16- / 32-bit +#define USB_FIFO_EP4 (0x0030) // 8- / 16- / 32-bit +#define USB_FIFO_EP5 (0x0034) // 8- / 16- / 32-bit + +/*DMA controler registers.*/ +#define DMA_INTR_STAT 0x0 /*DMA interrupt status.*/ + +#define DMA_CTRL_REG1 0x04 /*DMA channel 1 control.*/ +#define DMA_CTRL_REG2 0x14 /*DMA channel 2 control.*/ +#define DMA_CTRL_REG3 0x24 /*DMA channel 3 control.*/ +#define DMA_CTRL_REG4 0x34 /*DMA channel 4 control.*/ + +#define DMA_ADDR_REG1 0x08 /*DMA channel 1 AHB memory address.*/ +#define DMA_ADDR_REG2 0x18 /*DMA channel 2 AHB memory address.*/ +#define DMA_ADDR_REG3 0x28 /*DMA channel 3 AHB memory address.*/ +#define DMA_ADDR_REG4 0x38 /*DMA channel 4 AHB memory address.*/ + +#define DMA_CUNT_REG1 0x0c /*DMA channel 1 byte count.*/ +#define DMA_CUNT_REG2 0x1c /*DMA channel 2 byte count.*/ +#define DMA_CUNT_REG3 0x2c /*DMA channel 3 byte count.*/ +#define DMA_CUNT_REG4 0x3c /*DMA channel 4 byte count.*/ + + +//********************** bit fields defs*********************** + +#define USB_ENABLE_DMA (1) +#define USB_DIRECTION_RX (0<<1) +#define USB_DIRECTION_TX (1<<1) +#define USB_DMA_MODE1 (1<<2) +#define USB_DMA_MODE0 (0<<2) +#define USB_DMA_INT_ENABLE (1<<3) +#define USB_DMA_INT_DISABLE (0<<3) +#define USB_DMA_BUS_ERROR (1<<8) +#define USB_DMA_BUS_MODE0 (0<<9) +#define USB_DMA_BUS_MODE1 (1<<9) +#define USB_DMA_BUS_MODE2 (2<<9) +#define USB_DMA_BUS_MODE3 (3<<9) + +// RTC_USB_CONFIG_REG +#define SESSEND (1 << 31) // 0:above/1:below Session End threshold +#define AVALID (1 << 30) +#define VBUSVALID (1 << 29) +#define SESSEND_SEL (1 << 28) +#define AVALID_SEL (1 << 27) +#define VBUSVALID_SEL (1 << 26) + + +/** POWER Control register */ +#define USB_POWER_ENSUSPEND (1 << 0) +#define USB_POWER_SUSPENDM (1 << 1) // host/client +#define USB_POWER_RESUME (1 << 2) // host/client +#define USB_POWER_RESET (1 << 3) +#define USB_HOSG_HIGH_SPEED (1 << 5) + + /** CSR01 register */ + // mode-agnostic +#define USB_CSR01_RXPKTRDY (1 << 0) // r / clear +#define USB_CSR01_TXPKTRDY (1 << 1) // r / set + // Client mode +#define USB_CSR01_P_SENTSTALL (1 << 2) +#define USB_CSR01_P_DATAEND (1 << 3) +#define USB_CSR01_P_SETUPEND (1 << 4) +#define USB_CSR01_P_SENDSTALL (1 << 5) +#define USB_CSR01_P_SVDRXPKTRDY (1 << 6) +#define USB_CSR01_P_SVDSETUPEND (1 << 7) + // Host mode +#define USB_CSR01_H_RXSTALL (1 << 2) // r / clear +#define USB_CSR01_H_SETUPPKT (1 << 3) // r / w +#define USB_CSR01_H_ERROR (1 << 4) // r / clear +#define USB_CSR01_H_REQPKT (1 << 5) // r / w +#define USB_CSR01_H_STATUSPKT (1 << 6) // r / w +#define USB_CSR01_H_NAKTIMEOUT (1 << 7) // r / clear + + /** CSR02 register */ + // mode-agnostic +#define USB_CSR02_FLUSHFIFO (1 << 0) // set + // Client mode (none) + // Host mode +#define USB_CSR02_H_DISPING (1 << 3) // r / w + + + /** TXCSR1 register */ + // mode-agnostic +#define USB_TXCSR1_TXPKTRDY (1 << 0) +#define USB_TXCSR1_FIFONOTEMPTY (1 << 1) +#define USB_TXCSR1_FLUSHFIFO (1 << 3) +#define USB_TXCSR1_CLRDATATOG (1 << 6) + // Client mode +#define USB_TXCSR1_P_UNDERRUN (1 << 2) +#define USB_TXCSR1_P_SENDSTALL (1 << 4) +#define USB_TXCSR1_P_SENTSTALL (1 << 5) +#define USB_TXCSR1_P_INCOMPTX (1 << 7) + // Host MODE +#define USB_TXCSR1_H_ERROR (1 << 2) +#define USB_TXCSR1_H_RXSTALL (1 << 5) +#define USB_TXCSR1_H_NAKTIMEOUT (1 << 7) // for Bulk Endpoint +#define USB_TXCSR1_H_INCOMPTX (1 << 7) // for Interrupt Endpoint + + /** TXCSR2 register */ + // mode-agnostic +#define USB_TXCSR2_DMAMODE1 (1 << 2) +#define USB_TXCSR2_FRCDATATOG (1 << 3) +#define USB_TXCSR2_DMAENAB (1 << 4) +#define USB_TXCSR2_MODE (1 << 5) + // Host mode +#define USB_TXCSR2_MODE_TX (1 << 5) +#define USB_TXCSR2_AUTOSET (1 << 7) + // Client mode +#define USB_TXCSR2_P_ISO (1 << 6) + + /** RXCSR1 register */ + // mode-agnostic +#define USB_RXCSR1_RXPKTRDY (1 << 0) // r / clear +#define USB_RXCSR1_FIFOFULL (1 << 1) // r +#define USB_RXCSR1_DATAERR (1 << 3) // r / clear(host), for ISO only +#define USB_RXCSR1_FLUSHFIFO (1 << 4) // set +#define USB_RXCSR1_CLRDATATOG (1 << 7) // set + // Client mode +#define USB_RXCSR1_P_OVERRUN (1 << 2) // r / clear, for ISO only +#define USB_RXCSR1_P_SENDSTALL (1 << 5) // r / w +#define USB_RXCSR1_P_SENTSTALL (1 << 6) // r / clear + // Host MODE +#define USB_RXCSR1_H_ERROR (1 << 2) // r / clear +#define USB_RXCSR1_H_NAKTIMEOUT (1 << 3) // r / clear, for BULK +#define USB_RXCSR1_H_REQPKT (1 << 5) // r / w +#define USB_RXCSR1_H_RXSTALL (1 << 6) // r / clear + + /** RXCSR2 register */ + // mode-agnostic +#define USB_RXCSR2_INCOMPRX (1 << 0) // r +#define USB_RXCSR2_DMAMODE0 (0 << 3) +#define USB_RXCSR2_DMAMODE1 (1 << 3) // r / w +#define USB_RXCSR2_DMAREQENAB (1 << 5) // r / w +#define USB_RXCSR2_AUTOCLEAR (1 << 7) // r / w + +#define USB_RXCSR2_AUTOREQ (1 << 6) +#define USB_RXCSR2_DMAENAB (1 << 5) +#define USB_RXCSR2_DISNYET (1 << 4) +#define USB_RXCSR2_DMAMODE (1 << 3) + + // Client mode +#define USB_RXCSR2_P_DISNYET (1 << 4) // for Bulk/Interrupt transaction +#define USB_RXCSR2_P_PIDERROR (1 << 4) // for ISO transaction +#define USB_RXCSR2_P_ISO (1 << 6) + // Host Mode +#define USB_RXCSR2_H_PIDERROR (1 << 4) // r / w, for ISO only +#define USB_RXCSR2_H_AUTOREQ (1 << 6) // r / w + + + /** IntrUSB/IntrUSBE register */ +#define USB_INTR_SUSPEND (1 << 0) // client +#define USB_INTR_RESUME (1 << 1) +#define USB_INTR_RESET (1 << 2) // client +#define USB_INTR_BABBLE (1 << 2) // host +#define USB_INTR_SOF (1 << 3) +#define USB_INTR_CONNECT (1 << 4) // host +#define USB_INTR_DISCONNECT (1 << 5) // host/client +#define USB_INTR_SESSREQ (1 << 6) // A device +#define USB_INTR_VBUSERROR (1 << 7) // A device + + + /** IntrTX register */ + /** IntrRX register */ + /** IntrTXE register */ + /** IntrRXE register */ +#define USB_INTR_EP0 (1 << 0) +#define USB_INTR_EP1 (1 << 1) +#define USB_INTR_EP2 (1 << 2) +#define USB_INTR_EP3 (1 << 3) +#define USB_INTR_EP4 (1 << 4) +#define USB_INTR_EP5 (1 << 5) +#define USB_INTR_EPX_MASK (USB_INTR_EP1|USB_INTR_EP2|USB_INTR_EP3|USB_INTR_EP4|USB_INTR_EP5) + + /** IntrUSB/IntrUSBE register */ +#define USB_INTR_SUSPEND (1 << 0) // client +#define USB_INTR_RESUME (1 << 1) +#define USB_INTR_RESET (1 << 2) // client +#define USB_INTR_BABBLE (1 << 2) // host +#define USB_INTR_SOF (1 << 3) +#define USB_INTR_CONNECT (1 << 4) // host +#define USB_INTR_DISCONNECT (1 << 5) // host/client +#define USB_INTR_SESSREQ (1 << 6) // A device +#define USB_INTR_VBUSERROR (1 << 7) // A device + + /** RXMAXP register */ +#define USB_RXMAXP_MASK (0x07FF) +#define USB_RXMAXP(cnt) ((cnt) & 0x07FF) + + /** TXTYPE register */ +#define USB_TXTYPE_EPNUM_MASK (0xf << 0) +#define USB_TXTYPE_EPNUM(ep) (((ep)&0xf) << 0) +#define USB_TXTYPE_PROTOCOL_ILLEGAL (0 << 4) +#define USB_TXTYPE_PROTOCOL_ISO (1 << 4) +#define USB_TXTYPE_PROTOCOL_BULK (2 << 4) +#define USB_TXTYPE_PROTOCOL_INTR (3 << 4) + + /** DevCtl register */ +#define USB_DEVCTL_SESSION (1 << 0) // host/client +#define USB_DEVCTL_HOSTREQ (1 << 1) // B device +#define USB_DEVCTL_HOSTMODE_MASK (1 << 2) + +#define USB_DEVCTL_HOSTMODE_HOST (1 << 2) +#define USB_DEVCTL_VBUS_MASK (3 << 3) + +#define USB_DEVCTL_LSDEV (1 << 5) +#define USB_DEVCTL_FSDEV (1 << 6) // host +#define USB_DEVCTL_BDEVICE_MASK (1 << 7) + +#define USB_DEVCTL_BDEVICE_B (1 << 7) + +#endif /* _ANYKA_USB_REG_DEFINE_H_ */ + diff --git a/include/plat-anyka/usbburn.h b/include/plat-anyka/usbburn.h new file mode 100644 index 00000000..08f2917d --- /dev/null +++ b/include/plat-anyka/usbburn.h @@ -0,0 +1,16 @@ +#ifndef __USB_BURN__ +#define __USB_BURN__ + +extern int sense_data; // the CSW status + +extern struct semaphore sense_data_lock; // synchronization sense_data + +/* the write function for the usb file_storage */ +extern int usbburn_write(void *buf, size_t count); + +/* the read function for the usb file_storage */ +extern int usbburn_read(void *buf, size_t count); + +extern void usbburn_ioctl(void); + +#endif /* __USB_BURN__ */ diff --git a/include/plat-anyka/user_gpio.h b/include/plat-anyka/user_gpio.h new file mode 100644 index 00000000..7b0992e2 --- /dev/null +++ b/include/plat-anyka/user_gpio.h @@ -0,0 +1,11 @@ +#ifndef __EXPORT_GPIO__ +#define __EXPORT_GPIO__ + +#include + +struct ak_user_gpio_pdata { + struct user_gpio_info *user_gpios; + int nr_user_gpios; +}; + +#endif diff --git a/include/plat-anyka/wifi.h b/include/plat-anyka/wifi.h new file mode 100644 index 00000000..350cfd56 --- /dev/null +++ b/include/plat-anyka/wifi.h @@ -0,0 +1,28 @@ +/* + * include/plat-anyka/wifi.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __WIFI_H__ +#define __WIFI_H__ + +#include + +struct akwifi_platform_data { + struct gpio_info gpio_cs; + struct gpio_info gpio_on; + struct gpio_info gpio_off; + int power_on_delay; + int power_off_delay; + int total_usb_ep_num; + void (* gpio_init) (const struct gpio_info *); +}; + + struct gpio_info * Akwifi_gpio_cs(void); + +#endif /* __WIFI_H__ */ + diff --git a/include/plat-anyka/wrap_sensor.h b/include/plat-anyka/wrap_sensor.h new file mode 100644 index 00000000..e161d49e --- /dev/null +++ b/include/plat-anyka/wrap_sensor.h @@ -0,0 +1,114 @@ +#ifndef __WRAP_SENSOR__ +#define __WRAP_SENSOR__ + +#include +#include +#include +#include + +#define GPIO_I2C_SCL INVALID_GPIO +#define GPIO_I2C_SDA INVALID_GPIO + +/** + * @brief millisecond delay + * @author dengzhou + * @date 2012-03-16 + * @param[in] minisecond minisecond delay number + * @return T_VOID + */ +T_VOID mini_delay(T_U32 minisecond); + +/** + * @brief anyka specific printf + * @author dengzhou + * @date 2012-03-16 + * @param[in] level forbidden level + * @param[in] mStr module string + * @param[in] s format string + * @return T_S32 + * @retval 0 is print ok, -1 is forbidden to print + */ +T_S32 akprintf(T_U8 level, T_pCSTR mStr, T_pCSTR s, ...); + +/** + * @brief write data to SCCB device + * + * write size length data to daddr's raddr register + * @author dengzhou + * @date 2012-03-16 + * @param[in] daddr SCCB device address + * @param[in] raddr register address + * @param[in] data write data's point + * @param[in] size write data's length + * @return T_BOOL return write success or failed + * @retval AK_FALSE operate failed + * @retval AK_TRUE operate success + */ +T_BOOL sccb_write_data(T_U8 daddr, T_U8 raddr, T_U8 *data, T_U32 size); +T_BOOL sccb_write_short(T_U8 daddr, T_U16 raddr, T_U8 *data, T_U32 size); +T_BOOL sccb_write_word(T_U8 daddr, T_U16 raddr, T_U16 *data, T_U32 size); + +/** + * @brief read data from SCCB device function + * + * read data from daddr's raddr register + * @author dengzhou + * @date 2012-03-16 + * @param[in] daddr SCCB device address + * @param[in] raddr register address + * @return T_U8 + * @retval read-back data + */ +T_U8 sccb_read_data(T_U8 daddr, T_U8 raddr); +T_U8 sccb_read_short(T_U8 daddr, T_U16 raddr); +T_U16 sccb_read_word(T_U8 daddr, T_U16 raddr); + +/*@{*/ +/** + * @brief SCCB interface initialize function + * + * setup SCCB interface + * @author dengzhou + * @date 2012-03-16 + * @param[in] pin_scl the pin assigned to SCL + * @param[in] pin_sda the pin assigned to SDA + * @return T_VOID + */ +T_VOID sccb_init(T_U32 pin_scl, T_U32 pin_sda); + + +/** + * @brief set client handle + * @author dengzhou + * @date 2012-03-19 + * @param[in] client handle of I2C open + * @return T_VOID + */ +void sensor_set_handle(struct i2c_client *client); + +/** + * @brief get GPIO pin value + * @author dengzhou + * @date 2012-03-16 + * @param GPIO pin type + * @return GPIO pin value + * @retval + */ +T_U32 cam_getpin(T_CAMERA_PINTYPE pin_type); + +#define GPIO_CAMERA_AVDD cam_getpin(PIN_AVDD) +#define GPIO_CAMERA_CHIP_ENABLE cam_getpin(PIN_POWER) +#define GPIO_CAMERA_RESET cam_getpin(PIN_RESET) + +#define GPIO_DIR_INPUT AK_GPIO_DIR_INPUT +#define GPIO_DIR_OUTPUT AK_GPIO_DIR_OUTPUT +#define GPIO_LEVEL_LOW AK_GPIO_OUT_LOW +#define GPIO_LEVEL_HIGH AK_GPIO_OUT_HIGH + +#define gpio_set_pin_dir(pin,dir) ak_gpio_cfgpin(pin,dir) +#define gpio_set_pin_level(pin,level) ak_gpio_setpin(pin,level) +#define gpio_set_pin_as_gpio(pin) ak_setpin_as_gpio(pin) +#define gpio_pin_get_ActiveLevel(pin) ak_gpio_getpin(pin) + +#endif + diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h index f34a5a87..11a4fd05 100644 --- a/include/scsi/scsi.h +++ b/include/scsi/scsi.h @@ -146,6 +146,13 @@ struct scsi_cmnd; #define SYNCHRONIZE_CACHE_16 0x91 #define WRITE_SAME_16 0x93 #define SERVICE_ACTION_IN 0x9e + +#ifdef CONFIG_USB_AKUDC_PRODUCER +//modified by anyka Zhang Jingyuan +#define SCSI_ANYKA_UBOOT 0xf1 +//end of modified by anyka Zhang Jingyuan +#endif + /* values for service action in */ #define SAI_READ_CAPACITY_16 0x10 #define SAI_GET_LBA_STATUS 0x12 diff --git a/include/sound/ak_pcm.h b/include/sound/ak_pcm.h new file mode 100644 index 00000000..559dd858 --- /dev/null +++ b/include/sound/ak_pcm.h @@ -0,0 +1,51 @@ +#ifndef _AK_PCM_H +#define _AK_PCM_H + +#include +#include + + +struct ak_codec_dai; + +struct ak_codec_ops { + void (*dac_init) (struct ak_codec_dai *dai); + void (*dac_exit) (struct ak_codec_dai *dai); + + void (*adc_init) (struct ak_codec_dai *dai); + void (*adc_exit) (struct ak_codec_dai *dai); + + void (*set_dac_samplerate)(struct ak_codec_dai *dai, unsigned int rate); + unsigned long (*set_adc_samplerate)(struct ak_codec_dai *dai, unsigned int rate); + + void (*set_dac_channels)(struct ak_codec_dai *dai, unsigned int channels); + void (*set_adc_channels)(struct ak_codec_dai *dai, unsigned int channels); + + void (*playback_start)(struct ak_codec_dai *dai); + void (*playback_end)(struct ak_codec_dai *dai); + + void (*capture_start)(struct ak_codec_dai *dai); + void (*capture_end)(struct ak_codec_dai *dai); + + void (*start_to_play) (struct ak_codec_dai *dai, unsigned int channels, unsigned int rate); +}; + +struct ak_proc_entry { + const char *name; + void (*cb)(struct snd_info_entry *, struct snd_info_buffer *); +}; + +struct ak_codec_dai { + int num_kcontrols; + struct snd_kcontrol_new *kcontrols; + int num_pentries; + void *entries_private; + struct ak_proc_entry *pentries; + struct ak_codec_ops *ops; + int aec_flag; + +}; + +int ak_codec_register(struct ak_codec_dai *dai); +void ak_codec_ctl_event(unsigned int iface, unsigned int event, const char* ctl_name); + +#endif diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 8da3c240..055242e6 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -432,6 +432,11 @@ enum snd_soc_dapm_type { snd_soc_dapm_dai, /* link to DAI structure */ }; +enum snd_soc_dapm_subclass { + SND_SOC_DAPM_CLASS_INIT = 0, + SND_SOC_DAPM_CLASS_PCM = 1, +}; + /* * DAPM audio route definition. * diff --git a/include/sound/soc.h b/include/sound/soc.h index 2ebf7877..66fd9bc9 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -288,6 +288,11 @@ enum snd_soc_pcm_subclass { SND_SOC_PCM_CLASS_BE = 1, }; +enum snd_soc_card_subclass { + SND_SOC_CARD_CLASS_INIT = 0, + SND_SOC_CARD_CLASS_PCM = 1, +}; + int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id, int source, unsigned int freq, int dir); int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source, @@ -800,6 +805,7 @@ struct snd_soc_card { struct list_head list; struct mutex mutex; + struct mutex dapm_mutex; bool instantiated; diff --git a/include/sound/wm8523.h b/include/sound/wm8523.h new file mode 100644 index 00000000..069fc61b --- /dev/null +++ b/include/sound/wm8523.h @@ -0,0 +1,159 @@ +/* + * wm8523.h -- WM8423 ASoC driver + * + * Copyright 2009 Wolfson Microelectronics, plc + * + * Author: Mark Brown + * + * Based on wm8753.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _WM8523_H +#define _WM8523_H + +/* + * Register values. + */ +#define WM8523_DEVICE_ID 0x00 +#define WM8523_REVISION 0x01 +#define WM8523_PSCTRL1 0x02 +#define WM8523_AIF_CTRL1 0x03 +#define WM8523_AIF_CTRL2 0x04 +#define WM8523_DAC_CTRL3 0x05 +#define WM8523_DAC_GAINL 0x06 +#define WM8523_DAC_GAINR 0x07 +#define WM8523_ZERO_DETECT 0x08 + +#define WM8523_REGISTER_COUNT 9 +#define WM8523_MAX_REGISTER 0x08 + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - DEVICE_ID + */ +#define WM8523_CHIP_ID_MASK 0xFFFF /* CHIP_ID - [15:0] */ +#define WM8523_CHIP_ID_SHIFT 0 /* CHIP_ID - [15:0] */ +#define WM8523_CHIP_ID_WIDTH 16 /* CHIP_ID - [15:0] */ + +/* + * R1 (0x01) - REVISION + */ +#define WM8523_CHIP_REV_MASK 0x0007 /* CHIP_REV - [2:0] */ +#define WM8523_CHIP_REV_SHIFT 0 /* CHIP_REV - [2:0] */ +#define WM8523_CHIP_REV_WIDTH 3 /* CHIP_REV - [2:0] */ + +/* + * R2 (0x02) - PSCTRL1 + */ +#define WM8523_SYS_ENA_MASK 0x0003 /* SYS_ENA - [1:0] */ +#define WM8523_SYS_ENA_SHIFT 0 /* SYS_ENA - [1:0] */ +#define WM8523_SYS_ENA_WIDTH 2 /* SYS_ENA - [1:0] */ + +/* + * R3 (0x03) - AIF_CTRL1 + */ +#define WM8523_TDM_MODE_MASK 0x1800 /* TDM_MODE - [12:11] */ +#define WM8523_TDM_MODE_SHIFT 11 /* TDM_MODE - [12:11] */ +#define WM8523_TDM_MODE_WIDTH 2 /* TDM_MODE - [12:11] */ +#define WM8523_TDM_SLOT_MASK 0x0600 /* TDM_SLOT - [10:9] */ +#define WM8523_TDM_SLOT_SHIFT 9 /* TDM_SLOT - [10:9] */ +#define WM8523_TDM_SLOT_WIDTH 2 /* TDM_SLOT - [10:9] */ +#define WM8523_DEEMPH 0x0100 /* DEEMPH */ +#define WM8523_DEEMPH_MASK 0x0100 /* DEEMPH */ +#define WM8523_DEEMPH_SHIFT 8 /* DEEMPH */ +#define WM8523_DEEMPH_WIDTH 1 /* DEEMPH */ +#define WM8523_AIF_MSTR 0x0080 /* AIF_MSTR */ +#define WM8523_AIF_MSTR_MASK 0x0080 /* AIF_MSTR */ +#define WM8523_AIF_MSTR_SHIFT 7 /* AIF_MSTR */ +#define WM8523_AIF_MSTR_WIDTH 1 /* AIF_MSTR */ +#define WM8523_LRCLK_INV 0x0040 /* LRCLK_INV */ +#define WM8523_LRCLK_INV_MASK 0x0040 /* LRCLK_INV */ +#define WM8523_LRCLK_INV_SHIFT 6 /* LRCLK_INV */ +#define WM8523_LRCLK_INV_WIDTH 1 /* LRCLK_INV */ +#define WM8523_BCLK_INV 0x0020 /* BCLK_INV */ +#define WM8523_BCLK_INV_MASK 0x0020 /* BCLK_INV */ +#define WM8523_BCLK_INV_SHIFT 5 /* BCLK_INV */ +#define WM8523_BCLK_INV_WIDTH 1 /* BCLK_INV */ +#define WM8523_WL_MASK 0x0018 /* WL - [4:3] */ +#define WM8523_WL_SHIFT 3 /* WL - [4:3] */ +#define WM8523_WL_WIDTH 2 /* WL - [4:3] */ +#define WM8523_FMT_MASK 0x0007 /* FMT - [2:0] */ +#define WM8523_FMT_SHIFT 0 /* FMT - [2:0] */ +#define WM8523_FMT_WIDTH 3 /* FMT - [2:0] */ + +/* + * R4 (0x04) - AIF_CTRL2 + */ +#define WM8523_DAC_OP_MUX_MASK 0x00C0 /* DAC_OP_MUX - [7:6] */ +#define WM8523_DAC_OP_MUX_SHIFT 6 /* DAC_OP_MUX - [7:6] */ +#define WM8523_DAC_OP_MUX_WIDTH 2 /* DAC_OP_MUX - [7:6] */ +#define WM8523_BCLKDIV_MASK 0x0038 /* BCLKDIV - [5:3] */ +#define WM8523_BCLKDIV_SHIFT 3 /* BCLKDIV - [5:3] */ +#define WM8523_BCLKDIV_WIDTH 3 /* BCLKDIV - [5:3] */ +#define WM8523_SR_MASK 0x0007 /* SR - [2:0] */ +#define WM8523_SR_SHIFT 0 /* SR - [2:0] */ +#define WM8523_SR_WIDTH 3 /* SR - [2:0] */ + +/* + * R5 (0x05) - DAC_CTRL3 + */ +#define WM8523_ZC 0x0010 /* ZC */ +#define WM8523_ZC_MASK 0x0010 /* ZC */ +#define WM8523_ZC_SHIFT 4 /* ZC */ +#define WM8523_ZC_WIDTH 1 /* ZC */ +#define WM8523_DACR 0x0008 /* DACR */ +#define WM8523_DACR_MASK 0x0008 /* DACR */ +#define WM8523_DACR_SHIFT 3 /* DACR */ +#define WM8523_DACR_WIDTH 1 /* DACR */ +#define WM8523_DACL 0x0004 /* DACL */ +#define WM8523_DACL_MASK 0x0004 /* DACL */ +#define WM8523_DACL_SHIFT 2 /* DACL */ +#define WM8523_DACL_WIDTH 1 /* DACL */ +#define WM8523_VOL_UP_RAMP 0x0002 /* VOL_UP_RAMP */ +#define WM8523_VOL_UP_RAMP_MASK 0x0002 /* VOL_UP_RAMP */ +#define WM8523_VOL_UP_RAMP_SHIFT 1 /* VOL_UP_RAMP */ +#define WM8523_VOL_UP_RAMP_WIDTH 1 /* VOL_UP_RAMP */ +#define WM8523_VOL_DOWN_RAMP 0x0001 /* VOL_DOWN_RAMP */ +#define WM8523_VOL_DOWN_RAMP_MASK 0x0001 /* VOL_DOWN_RAMP */ +#define WM8523_VOL_DOWN_RAMP_SHIFT 0 /* VOL_DOWN_RAMP */ +#define WM8523_VOL_DOWN_RAMP_WIDTH 1 /* VOL_DOWN_RAMP */ + +/* + * R6 (0x06) - DAC_GAINL + */ +#define WM8523_DACL_VU 0x0200 /* DACL_VU */ +#define WM8523_DACL_VU_MASK 0x0200 /* DACL_VU */ +#define WM8523_DACL_VU_SHIFT 9 /* DACL_VU */ +#define WM8523_DACL_VU_WIDTH 1 /* DACL_VU */ +#define WM8523_DACL_VOL_MASK 0x01FF /* DACL_VOL - [8:0] */ +#define WM8523_DACL_VOL_SHIFT 0 /* DACL_VOL - [8:0] */ +#define WM8523_DACL_VOL_WIDTH 9 /* DACL_VOL - [8:0] */ + +/* + * R7 (0x07) - DAC_GAINR + */ +#define WM8523_DACR_VU 0x0200 /* DACR_VU */ +#define WM8523_DACR_VU_MASK 0x0200 /* DACR_VU */ +#define WM8523_DACR_VU_SHIFT 9 /* DACR_VU */ +#define WM8523_DACR_VU_WIDTH 1 /* DACR_VU */ +#define WM8523_DACR_VOL_MASK 0x01FF /* DACR_VOL - [8:0] */ +#define WM8523_DACR_VOL_SHIFT 0 /* DACR_VOL - [8:0] */ +#define WM8523_DACR_VOL_WIDTH 9 /* DACR_VOL - [8:0] */ + +/* + * R8 (0x08) - ZERO_DETECT + */ +#define WM8523_ZD_COUNT_MASK 0x0003 /* ZD_COUNT - [1:0] */ +#define WM8523_ZD_COUNT_SHIFT 0 /* ZD_COUNT - [1:0] */ +#define WM8523_ZD_COUNT_WIDTH 2 /* ZD_COUNT - [1:0] */ + +#endif + + diff --git a/include/trace/events/cpufreq_interactive.h b/include/trace/events/cpufreq_interactive.h new file mode 100644 index 00000000..951e6ca1 --- /dev/null +++ b/include/trace/events/cpufreq_interactive.h @@ -0,0 +1,112 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM cpufreq_interactive + +#if !defined(_TRACE_CPUFREQ_INTERACTIVE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_CPUFREQ_INTERACTIVE_H + +#include + +DECLARE_EVENT_CLASS(set, + TP_PROTO(u32 cpu_id, unsigned long targfreq, + unsigned long actualfreq), + TP_ARGS(cpu_id, targfreq, actualfreq), + + TP_STRUCT__entry( + __field( u32, cpu_id ) + __field(unsigned long, targfreq ) + __field(unsigned long, actualfreq ) + ), + + TP_fast_assign( + __entry->cpu_id = (u32) cpu_id; + __entry->targfreq = targfreq; + __entry->actualfreq = actualfreq; + ), + + TP_printk("cpu=%u targ=%lu actual=%lu", + __entry->cpu_id, __entry->targfreq, + __entry->actualfreq) +); + +DEFINE_EVENT(set, cpufreq_interactive_setspeed, + TP_PROTO(u32 cpu_id, unsigned long targfreq, + unsigned long actualfreq), + TP_ARGS(cpu_id, targfreq, actualfreq) +); + +DECLARE_EVENT_CLASS(loadeval, + TP_PROTO(unsigned long cpu_id, unsigned long load, + unsigned long curtarg, unsigned long curactual, + unsigned long newtarg), + TP_ARGS(cpu_id, load, curtarg, curactual, newtarg), + + TP_STRUCT__entry( + __field(unsigned long, cpu_id ) + __field(unsigned long, load ) + __field(unsigned long, curtarg ) + __field(unsigned long, curactual ) + __field(unsigned long, newtarg ) + ), + + TP_fast_assign( + __entry->cpu_id = cpu_id; + __entry->load = load; + __entry->curtarg = curtarg; + __entry->curactual = curactual; + __entry->newtarg = newtarg; + ), + + TP_printk("cpu=%lu load=%lu cur=%lu actual=%lu targ=%lu", + __entry->cpu_id, __entry->load, __entry->curtarg, + __entry->curactual, __entry->newtarg) +); + +DEFINE_EVENT(loadeval, cpufreq_interactive_target, + TP_PROTO(unsigned long cpu_id, unsigned long load, + unsigned long curtarg, unsigned long curactual, + unsigned long newtarg), + TP_ARGS(cpu_id, load, curtarg, curactual, newtarg) +); + +DEFINE_EVENT(loadeval, cpufreq_interactive_already, + TP_PROTO(unsigned long cpu_id, unsigned long load, + unsigned long curtarg, unsigned long curactual, + unsigned long newtarg), + TP_ARGS(cpu_id, load, curtarg, curactual, newtarg) +); + +DEFINE_EVENT(loadeval, cpufreq_interactive_notyet, + TP_PROTO(unsigned long cpu_id, unsigned long load, + unsigned long curtarg, unsigned long curactual, + unsigned long newtarg), + TP_ARGS(cpu_id, load, curtarg, curactual, newtarg) +); + +TRACE_EVENT(cpufreq_interactive_boost, + TP_PROTO(const char *s), + TP_ARGS(s), + TP_STRUCT__entry( + __string(s, s) + ), + TP_fast_assign( + __assign_str(s, s); + ), + TP_printk("%s", __get_str(s)) +); + +TRACE_EVENT(cpufreq_interactive_unboost, + TP_PROTO(const char *s), + TP_ARGS(s), + TP_STRUCT__entry( + __string(s, s) + ), + TP_fast_assign( + __assign_str(s, s); + ), + TP_printk("%s", __get_str(s)) +); + +#endif /* _TRACE_CPUFREQ_INTERACTIVE_H */ + +/* This part must be outside protection */ +#include diff --git a/include/trace/events/gpu.h b/include/trace/events/gpu.h new file mode 100644 index 00000000..09efa71d --- /dev/null +++ b/include/trace/events/gpu.h @@ -0,0 +1,142 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM gpu + +#if !defined(_TRACE_GPU_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_GPU_H + +#include +#include + +#define show_secs_from_ns(ns) \ + ({ \ + u64 t = ns + (NSEC_PER_USEC / 2); \ + do_div(t, NSEC_PER_SEC); \ + }) + +#define show_usecs_from_ns(ns) \ + ({ \ + u64 t = ns + (NSEC_PER_USEC / 2) ; \ + u32 rem; \ + do_div(t, NSEC_PER_USEC); \ + rem = do_div(t, USEC_PER_SEC); \ + }) + +/* + * The gpu_sched_switch event indicates that a switch from one GPU context to + * another occurred on one of the GPU hardware blocks. + * + * The gpu_name argument identifies the GPU hardware block. Each independently + * scheduled GPU hardware block should have a different name. This may be used + * in different ways for different GPUs. For example, if a GPU includes + * multiple processing cores it may use names "GPU 0", "GPU 1", etc. If a GPU + * includes a separately scheduled 2D and 3D hardware block, it might use the + * names "2D" and "3D". + * + * The timestamp argument is the timestamp at which the switch occurred on the + * GPU. These timestamps are in units of nanoseconds and must use + * approximately the same time as sched_clock, though they need not come from + * any CPU clock. The timestamps for a single hardware block must be + * monotonically nondecreasing. This means that if a variable compensation + * offset is used to translate from some other clock to the sched_clock, then + * care must be taken when increasing that offset, and doing so may result in + * multiple events with the same timestamp. + * + * The next_ctx_id argument identifies the next context that was running on + * the GPU hardware block. A value of 0 indicates that the hardware block + * will be idle. + * + * The next_prio argument indicates the priority of the next context at the + * time of the event. The exact numeric values may mean different things for + * different GPUs, but they should follow the rule that lower values indicate a + * higher priority. + * + * The next_job_id argument identifies the batch of work that the GPU will be + * working on. This should correspond to a job_id that was previously traced + * as a gpu_job_enqueue event when the batch of work was created. + */ +TRACE_EVENT(gpu_sched_switch, + + TP_PROTO(const char *gpu_name, u64 timestamp, + u32 next_ctx_id, s32 next_prio, u32 next_job_id), + + TP_ARGS(gpu_name, timestamp, next_ctx_id, next_prio, next_job_id), + + TP_STRUCT__entry( + __string( gpu_name, gpu_name ) + __field( u64, timestamp ) + __field( u32, next_ctx_id ) + __field( s32, next_prio ) + __field( u32, next_job_id ) + ), + + TP_fast_assign( + __assign_str(gpu_name, gpu_name); + __entry->timestamp = timestamp; + __entry->next_ctx_id = next_ctx_id; + __entry->next_prio = next_prio; + __entry->next_job_id = next_job_id; + ), + + TP_printk("gpu_name=%s ts=%5llu.%06lu next_ctx_id=%lu next_prio=%ld " + "next_job_id=%lu", + __get_str(gpu_name), + (unsigned long long)show_secs_from_ns(__entry->timestamp), + (unsigned long)show_usecs_from_ns(__entry->timestamp), + (unsigned long)__entry->next_ctx_id, + (long)__entry->next_prio, + (unsigned long)__entry->next_job_id) +); + +/* + * The gpu_job_enqueue event indicates that a batch of work has been queued up + * to be processed by the GPU. This event is not intended to indicate that + * the batch of work has been submitted to the GPU hardware, but rather that + * it has been submitted to the GPU kernel driver. + * + * This event should be traced on the thread that initiated the work being + * queued. For example, if a batch of work is submitted to the kernel by a + * userland thread, the event should be traced on that thread. + * + * The ctx_id field identifies the GPU context in which the batch of work + * being queued is to be run. + * + * The job_id field identifies the batch of work being queued within the given + * GPU context. The first batch of work submitted for a given GPU context + * should have a job_id of 0, and each subsequent batch of work should + * increment the job_id by 1. + * + * The type field identifies the type of the job being enqueued. The job + * types may be different for different GPU hardware. For example, a GPU may + * differentiate between "2D", "3D", and "compute" jobs. + */ +TRACE_EVENT(gpu_job_enqueue, + + TP_PROTO(u32 ctx_id, u32 job_id, const char *type), + + TP_ARGS(ctx_id, job_id, type), + + TP_STRUCT__entry( + __field( u32, ctx_id ) + __field( u32, job_id ) + __string( type, type ) + ), + + TP_fast_assign( + __entry->ctx_id = ctx_id; + __entry->job_id = job_id; + __assign_str(type, type); + ), + + TP_printk("ctx_id=%lu job_id=%lu type=%s", + (unsigned long)__entry->ctx_id, + (unsigned long)__entry->job_id, + __get_str(type)) +); + +#undef show_secs_from_ns +#undef show_usecs_from_ns + +#endif /* _TRACE_GPU_H */ + +/* This part must be outside protection */ +#include diff --git a/include/trace/events/power.h b/include/trace/events/power.h index cae9a94f..243c677c 100644 --- a/include/trace/events/power.h +++ b/include/trace/events/power.h @@ -65,6 +65,40 @@ TRACE_EVENT(machine_suspend, TP_printk("state=%lu", (unsigned long)__entry->state) ); +DECLARE_EVENT_CLASS(wakeup_source, + + TP_PROTO(const char *name, unsigned int state), + + TP_ARGS(name, state), + + TP_STRUCT__entry( + __string( name, name ) + __field( u64, state ) + ), + + TP_fast_assign( + __assign_str(name, name); + __entry->state = state; + ), + + TP_printk("%s state=0x%lx", __get_str(name), + (unsigned long)__entry->state) +); + +DEFINE_EVENT(wakeup_source, wakeup_source_activate, + + TP_PROTO(const char *name, unsigned int state), + + TP_ARGS(name, state) +); + +DEFINE_EVENT(wakeup_source, wakeup_source_deactivate, + + TP_PROTO(const char *name, unsigned int state), + + TP_ARGS(name, state) +); + #ifdef CONFIG_EVENT_POWER_TRACING_DEPRECATED /* @@ -204,6 +238,25 @@ DEFINE_EVENT(clock, clock_set_rate, TP_ARGS(name, state, cpu_id) ); +TRACE_EVENT(clock_set_parent, + + TP_PROTO(const char *name, const char *parent_name), + + TP_ARGS(name, parent_name), + + TP_STRUCT__entry( + __string( name, name ) + __string( parent_name, parent_name ) + ), + + TP_fast_assign( + __assign_str(name, name); + __assign_str(parent_name, parent_name); + ), + + TP_printk("%s parent=%s", __get_str(name), __get_str(parent_name)) +); + /* * The power domain events are used for power domains transitions */ diff --git a/include/trace/events/sync.h b/include/trace/events/sync.h new file mode 100644 index 00000000..f31bc63c --- /dev/null +++ b/include/trace/events/sync.h @@ -0,0 +1,82 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM sync + +#if !defined(_TRACE_SYNC_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_SYNC_H + +#include +#include + +TRACE_EVENT(sync_timeline, + TP_PROTO(struct sync_timeline *timeline), + + TP_ARGS(timeline), + + TP_STRUCT__entry( + __string(name, timeline->name) + __array(char, value, 32) + ), + + TP_fast_assign( + __assign_str(name, timeline->name); + if (timeline->ops->timeline_value_str) { + timeline->ops->timeline_value_str(timeline, + __entry->value, + sizeof(__entry->value)); + } else { + __entry->value[0] = '\0'; + } + ), + + TP_printk("name=%s value=%s", __get_str(name), __entry->value) +); + +TRACE_EVENT(sync_wait, + TP_PROTO(struct sync_fence *fence, int begin), + + TP_ARGS(fence, begin), + + TP_STRUCT__entry( + __string(name, fence->name) + __field(s32, status) + __field(u32, begin) + ), + + TP_fast_assign( + __assign_str(name, fence->name); + __entry->status = fence->status; + __entry->begin = begin; + ), + + TP_printk("%s name=%s state=%d", __entry->begin ? "begin" : "end", + __get_str(name), __entry->status) +); + +TRACE_EVENT(sync_pt, + TP_PROTO(struct sync_pt *pt), + + TP_ARGS(pt), + + TP_STRUCT__entry( + __string(timeline, pt->parent->name) + __array(char, value, 32) + ), + + TP_fast_assign( + __assign_str(timeline, pt->parent->name); + if (pt->parent->ops->pt_value_str) { + pt->parent->ops->pt_value_str(pt, + __entry->value, + sizeof(__entry->value)); + } else { + __entry->value[0] = '\0'; + } + ), + + TP_printk("name=%s value=%s", __get_str(timeline), __entry->value) + ); + +#endif /* if !defined(_TRACE_SYNC_H) || defined(TRACE_HEADER_MULTI_READ) */ + +/* This part must be outside protection */ +#include diff --git a/init/Kconfig b/init/Kconfig index 6cfd71d0..a7cffc83 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -953,6 +953,12 @@ config SYSCTL config ANON_INODES bool +config PANIC_TIMEOUT + int "Default panic timeout" + default 0 + help + Set default panic timeout. + menuconfig EXPERT bool "Configure standard kernel features (expert users)" # Unhide debug options, to make the on-by-default options visible diff --git a/kernel/cgroup.c b/kernel/cgroup.c index a4c47d1b..c5d69222 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -287,6 +287,33 @@ static void cgroup_release_agent(struct work_struct *work); static DECLARE_WORK(release_agent_work, cgroup_release_agent); static void check_for_release(struct cgroup *cgrp); +/* + * A queue for waiters to do rmdir() cgroup. A tasks will sleep when + * cgroup->count == 0 && list_empty(&cgroup->children) && subsys has some + * reference to css->refcnt. In general, this refcnt is expected to goes down + * to zero, soon. + * + * CGRP_WAIT_ON_RMDIR flag is set under cgroup's inode->i_mutex; + */ +static DECLARE_WAIT_QUEUE_HEAD(cgroup_rmdir_waitq); + +static void cgroup_wakeup_rmdir_waiter(struct cgroup *cgrp) +{ + if (unlikely(test_and_clear_bit(CGRP_WAIT_ON_RMDIR, &cgrp->flags))) + wake_up_all(&cgroup_rmdir_waitq); +} + +void cgroup_exclude_rmdir(struct cgroup_subsys_state *css) +{ + css_get(css); +} + +void cgroup_release_and_wakeup_rmdir(struct cgroup_subsys_state *css) +{ + cgroup_wakeup_rmdir_waiter(css->cgroup); + css_put(css); +} + /* Link structure for associating css_set objects with cgroups */ struct cg_cgroup_link { /* @@ -346,60 +373,51 @@ static struct hlist_head *css_set_hash(struct cgroup_subsys_state *css[]) return &css_set_table[index]; } -/* We don't maintain the lists running through each css_set to its - * task until after the first call to cgroup_iter_start(). This - * reduces the fork()/exit() overhead for people who have cgroups - * compiled into their kernel but not actually in use */ -static int use_task_css_set_links __read_mostly; - -static void __put_css_set(struct css_set *cg, int taskexit) +static void free_css_set_work(struct work_struct *work) { + struct css_set *cg = container_of(work, struct css_set, work); struct cg_cgroup_link *link; struct cg_cgroup_link *saved_link; - /* - * Ensure that the refcount doesn't hit zero while any readers - * can see it. Similar to atomic_dec_and_lock(), but for an - * rwlock - */ - if (atomic_add_unless(&cg->refcount, -1, 1)) - return; - write_lock(&css_set_lock); - if (!atomic_dec_and_test(&cg->refcount)) { - write_unlock(&css_set_lock); - return; - } - - /* This css_set is dead. unlink it and release cgroup refcounts */ - hlist_del(&cg->hlist); - css_set_count--; + write_lock(&css_set_lock); list_for_each_entry_safe(link, saved_link, &cg->cg_links, cg_link_list) { struct cgroup *cgrp = link->cgrp; list_del(&link->cg_link_list); list_del(&link->cgrp_link_list); - /* * We may not be holding cgroup_mutex, and if cgrp->count is * dropped to 0 the cgroup can be destroyed at any time, hence * rcu_read_lock is used to keep it alive. */ rcu_read_lock(); - if (atomic_dec_and_test(&cgrp->count) && - notify_on_release(cgrp)) { - if (taskexit) - set_bit(CGRP_RELEASABLE, &cgrp->flags); + if (atomic_dec_and_test(&cgrp->count)) { check_for_release(cgrp); + cgroup_wakeup_rmdir_waiter(cgrp); } rcu_read_unlock(); kfree(link); } - write_unlock(&css_set_lock); - kfree_rcu(cg, rcu_head); + + kfree(cg); } +static void free_css_set_rcu(struct rcu_head *obj) +{ + struct css_set *cg = container_of(obj, struct css_set, rcu_head); + + INIT_WORK(&cg->work, free_css_set_work); + schedule_work(&cg->work); +} + +/* We don't maintain the lists running through each css_set to its + * task until after the first call to cgroup_iter_start(). This + * reduces the fork()/exit() overhead for people who have cgroups + * compiled into their kernel but not actually in use */ +static int use_task_css_set_links __read_mostly; + /* * refcounted get/put for css_set objects */ @@ -408,14 +426,26 @@ static inline void get_css_set(struct css_set *cg) atomic_inc(&cg->refcount); } -static inline void put_css_set(struct css_set *cg) +static void put_css_set(struct css_set *cg) { - __put_css_set(cg, 0); -} + /* + * Ensure that the refcount doesn't hit zero while any readers + * can see it. Similar to atomic_dec_and_lock(), but for an + * rwlock + */ + if (atomic_add_unless(&cg->refcount, -1, 1)) + return; + write_lock(&css_set_lock); + if (!atomic_dec_and_test(&cg->refcount)) { + write_unlock(&css_set_lock); + return; + } -static inline void put_css_set_taskexit(struct css_set *cg) -{ - __put_css_set(cg, 1); + hlist_del(&cg->hlist); + css_set_count--; + + write_unlock(&css_set_lock); + call_rcu(&cg->rcu_head, free_css_set_rcu); } /* @@ -747,9 +777,9 @@ static struct cgroup *task_cgroup_from_root(struct task_struct *task, * cgroup_attach_task(), which overwrites one tasks cgroup pointer with * another. It does so using cgroup_mutex, however there are * several performance critical places that need to reference - * task->cgroup without the expense of grabbing a system global + * task->cgroups without the expense of grabbing a system global * mutex. Therefore except as noted below, when dereferencing or, as - * in cgroup_attach_task(), modifying a task'ss cgroup pointer we use + * in cgroup_attach_task(), modifying a task's cgroups pointer we use * task_lock(), which acts on a spinlock (task->alloc_lock) already in * the task_struct routinely used for such matters. * @@ -938,33 +968,6 @@ static void cgroup_d_remove_dir(struct dentry *dentry) remove_dir(dentry); } -/* - * A queue for waiters to do rmdir() cgroup. A tasks will sleep when - * cgroup->count == 0 && list_empty(&cgroup->children) && subsys has some - * reference to css->refcnt. In general, this refcnt is expected to goes down - * to zero, soon. - * - * CGRP_WAIT_ON_RMDIR flag is set under cgroup's inode->i_mutex; - */ -static DECLARE_WAIT_QUEUE_HEAD(cgroup_rmdir_waitq); - -static void cgroup_wakeup_rmdir_waiter(struct cgroup *cgrp) -{ - if (unlikely(test_and_clear_bit(CGRP_WAIT_ON_RMDIR, &cgrp->flags))) - wake_up_all(&cgroup_rmdir_waitq); -} - -void cgroup_exclude_rmdir(struct cgroup_subsys_state *css) -{ - css_get(css); -} - -void cgroup_release_and_wakeup_rmdir(struct cgroup_subsys_state *css) -{ - cgroup_wakeup_rmdir_waiter(css->cgroup); - css_put(css); -} - /* * Call with cgroup_mutex held. Drops reference counts on modules, including * any duplicate ones that parse_cgroupfs_options took. If this function @@ -1896,6 +1899,7 @@ int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk) struct cgroupfs_root *root = cgrp->root; struct cgroup_taskset tset = { }; struct css_set *newcg; + struct css_set *cg; /* @tsk either already exited or can't exit until the end */ if (tsk->flags & PF_EXITING) @@ -1931,14 +1935,20 @@ int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk) goto out; } + task_lock(tsk); + cg = tsk->cgroups; + get_css_set(cg); + task_unlock(tsk); + cgroup_task_migrate(cgrp, oldcgrp, tsk, newcg); for_each_subsys(root, ss) { if (ss->attach) ss->attach(cgrp, &tset); } - - synchronize_rcu(); + set_bit(CGRP_RELEASABLE, &cgrp->flags); + /* put_css_set will not destroy cg until after an RCU grace period */ + put_css_set(cg); /* * wake up rmdir() waiter. the rmdir should fail since the cgroup @@ -2139,6 +2149,24 @@ out_free_group_list: return retval; } +static int cgroup_allow_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) +{ + struct cgroup_subsys *ss; + int ret; + + for_each_subsys(cgrp->root, ss) { + if (ss->allow_attach) { + ret = ss->allow_attach(cgrp, tset); + if (ret) + return ret; + } else { + return -EACCES; + } + } + + return 0; +} + /* * Find the task_struct of the task to attach by vpid and pass it along to the * function to attach either it or all tasks in its threadgroup. Will lock @@ -2170,9 +2198,18 @@ retry_find_task: if (cred->euid && cred->euid != tcred->uid && cred->euid != tcred->suid) { - rcu_read_unlock(); - ret = -EACCES; - goto out_unlock_cgroup; + /* + * if the default permission check fails, give each + * cgroup a chance to extend the permission check + */ + struct cgroup_taskset tset = { }; + tset.single.task = tsk; + tset.single.cgrp = cgrp; + ret = cgroup_allow_attach(cgrp, &tset); + if (ret) { + rcu_read_unlock(); + goto out_unlock_cgroup; + } } } else tsk = current; @@ -3789,6 +3826,8 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, if (err < 0) goto err_remove; + set_bit(CGRP_RELEASABLE, &parent->flags); + /* The cgroup directory was pre-locked for us */ BUG_ON(!mutex_is_locked(&cgrp->dentry->d_inode->i_mutex)); @@ -3920,6 +3959,21 @@ static int cgroup_clear_css_refs(struct cgroup *cgrp) return !failed; } +/* checks if all of the css_sets attached to a cgroup have a refcount of 0. + * Must be called with css_set_lock held */ +static int cgroup_css_sets_empty(struct cgroup *cgrp) +{ + struct cg_cgroup_link *link; + + list_for_each_entry(link, &cgrp->css_sets, cgrp_link_list) { + struct css_set *cg = link->cg; + if (atomic_read(&cg->refcount) > 0) + return 0; + } + + return 1; +} + static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry) { struct cgroup *cgrp = dentry->d_fsdata; @@ -3932,7 +3986,7 @@ static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry) /* the vfs holds both inode->i_mutex already */ again: mutex_lock(&cgroup_mutex); - if (atomic_read(&cgrp->count) != 0) { + if (!cgroup_css_sets_empty(cgrp)) { mutex_unlock(&cgroup_mutex); return -EBUSY; } @@ -3965,7 +4019,7 @@ again: mutex_lock(&cgroup_mutex); parent = cgrp->parent; - if (atomic_read(&cgrp->count) || !list_empty(&cgrp->children)) { + if (!cgroup_css_sets_empty(cgrp) || !list_empty(&cgrp->children)) { clear_bit(CGRP_WAIT_ON_RMDIR, &cgrp->flags); mutex_unlock(&cgroup_mutex); return -EBUSY; @@ -4005,7 +4059,6 @@ again: cgroup_d_remove_dir(d); dput(d); - set_bit(CGRP_RELEASABLE, &parent->flags); check_for_release(parent); /* @@ -4616,7 +4669,7 @@ void cgroup_exit(struct task_struct *tsk, int run_callbacks) task_unlock(tsk); if (cg) - put_css_set_taskexit(cg); + put_css_set(cg); } /** @@ -4669,6 +4722,14 @@ static void check_for_release(struct cgroup *cgrp) } } +/* Caller must verify that the css is not for root cgroup */ +void __css_get(struct cgroup_subsys_state *css, int count) +{ + atomic_add(count, &css->refcnt); + set_bit(CGRP_RELEASABLE, &css->cgroup->flags); +} +EXPORT_SYMBOL_GPL(__css_get); + /* Caller must verify that the css is not for root cgroup */ void __css_put(struct cgroup_subsys_state *css, int count) { @@ -4677,10 +4738,7 @@ void __css_put(struct cgroup_subsys_state *css, int count) rcu_read_lock(); val = atomic_sub_return(count, &css->refcnt); if (val == 1) { - if (notify_on_release(cgrp)) { - set_bit(CGRP_RELEASABLE, &cgrp->flags); - check_for_release(cgrp); - } + check_for_release(cgrp); cgroup_wakeup_rmdir_waiter(cgrp); } rcu_read_unlock(); diff --git a/kernel/cpu.c b/kernel/cpu.c index 2060c6e5..fb4a5acc 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -668,3 +668,23 @@ void init_cpu_online(const struct cpumask *src) { cpumask_copy(to_cpumask(cpu_online_bits), src); } + +static ATOMIC_NOTIFIER_HEAD(idle_notifier); + +void idle_notifier_register(struct notifier_block *n) +{ + atomic_notifier_chain_register(&idle_notifier, n); +} +EXPORT_SYMBOL_GPL(idle_notifier_register); + +void idle_notifier_unregister(struct notifier_block *n) +{ + atomic_notifier_chain_unregister(&idle_notifier, n); +} +EXPORT_SYMBOL_GPL(idle_notifier_unregister); + +void idle_notifier_call_chain(unsigned long val) +{ + atomic_notifier_call_chain(&idle_notifier, val, NULL); +} +EXPORT_SYMBOL_GPL(idle_notifier_call_chain); diff --git a/kernel/debug/debug_core.c b/kernel/debug/debug_core.c index 0557f24c..35b94ace 100644 --- a/kernel/debug/debug_core.c +++ b/kernel/debug/debug_core.c @@ -85,6 +85,10 @@ static int kgdb_use_con; bool dbg_is_early = true; /* Next cpu to become the master debug core */ int dbg_switch_cpu; +/* Flag for entering kdb when a panic occurs */ +static bool break_on_panic = true; +/* Flag for entering kdb when an exception occurs */ +static bool break_on_exception = true; /* Use kdb or gdbserver mode */ int dbg_kdb_mode = 1; @@ -99,6 +103,8 @@ early_param("kgdbcon", opt_kgdb_con); module_param(kgdb_use_con, int, 0644); module_param(kgdbreboot, int, 0644); +module_param(break_on_panic, bool, 0644); +module_param(break_on_exception, bool, 0644); /* * Holds information about breakpoints in a kernel. These breakpoints are @@ -673,6 +679,9 @@ kgdb_handle_exception(int evector, int signo, int ecode, struct pt_regs *regs) struct kgdb_state kgdb_var; struct kgdb_state *ks = &kgdb_var; + if (unlikely(signo != SIGTRAP && !break_on_exception)) + return 1; + ks->cpu = raw_smp_processor_id(); ks->ex_vector = evector; ks->signo = signo; @@ -759,6 +768,9 @@ static int kgdb_panic_event(struct notifier_block *self, unsigned long val, void *data) { + if (!break_on_panic) + return NOTIFY_DONE; + if (dbg_kdb_mode) kdb_printf("PANIC: %s\n", (char *)data); kgdb_breakpoint(); diff --git a/kernel/debug/kdb/kdb_io.c b/kernel/debug/kdb/kdb_io.c index 572e604c..81b31bba 100644 --- a/kernel/debug/kdb/kdb_io.c +++ b/kernel/debug/kdb/kdb_io.c @@ -216,7 +216,7 @@ static char *kdb_read(char *buffer, size_t bufsize) int i; int diag, dtab_count; int key; - + static int last_crlf; diag = kdbgetintenv("DTABCOUNT", &dtab_count); if (diag) @@ -237,6 +237,9 @@ poll_again: return buffer; if (key != 9) tab = 0; + if (key != 10 && key != 13) + last_crlf = 0; + switch (key) { case 8: /* backspace */ if (cp > buffer) { @@ -254,7 +257,12 @@ poll_again: *cp = tmp; } break; - case 13: /* enter */ + case 10: /* new line */ + case 13: /* carriage return */ + /* handle \n after \r */ + if (last_crlf && last_crlf != key) + break; + last_crlf = key; *lastchar++ = '\n'; *lastchar++ = '\0'; if (!KDB_STATE(KGDB_TRANS)) { diff --git a/kernel/fork.c b/kernel/fork.c index 81633337..bc3398ee 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -158,6 +158,9 @@ struct kmem_cache *vm_area_cachep; /* SLAB cache for mm_struct structures (tsk->mm) */ static struct kmem_cache *mm_cachep; +/* Notifier list called when a task struct is freed */ +static ATOMIC_NOTIFIER_HEAD(task_free_notifier); + static void account_kernel_stack(struct thread_info *ti, int account) { struct zone *zone = page_zone(virt_to_page(ti)); @@ -188,6 +191,18 @@ static inline void put_signal_struct(struct signal_struct *sig) free_signal_struct(sig); } +int task_free_register(struct notifier_block *n) +{ + return atomic_notifier_chain_register(&task_free_notifier, n); +} +EXPORT_SYMBOL(task_free_register); + +int task_free_unregister(struct notifier_block *n) +{ + return atomic_notifier_chain_unregister(&task_free_notifier, n); +} +EXPORT_SYMBOL(task_free_unregister); + void __put_task_struct(struct task_struct *tsk) { WARN_ON(!tsk->exit_state); @@ -199,6 +214,7 @@ void __put_task_struct(struct task_struct *tsk) delayacct_tsk_free(tsk); put_signal_struct(tsk->signal); + atomic_notifier_call_chain(&task_free_notifier, 0, tsk); if (!profile_handoff_task(tsk)) free_task(tsk); } @@ -677,7 +693,8 @@ struct mm_struct *mm_access(struct task_struct *task, unsigned int mode) mm = get_task_mm(task); if (mm && mm != current->mm && - !ptrace_may_access(task, mode)) { + !ptrace_may_access(task, mode) && + !capable(CAP_SYS_RESOURCE)) { mmput(mm); mm = ERR_PTR(-EACCES); } diff --git a/kernel/irq/pm.c b/kernel/irq/pm.c index 15e53b17..fe4b09cf 100644 --- a/kernel/irq/pm.c +++ b/kernel/irq/pm.c @@ -104,8 +104,13 @@ int check_wakeup_irqs(void) for_each_irq_desc(irq, desc) { if (irqd_is_wakeup_set(&desc->irq_data)) { - if (desc->istate & IRQS_PENDING) + if (desc->istate & IRQS_PENDING) { + pr_info("Wakeup IRQ %d %s pending, suspend aborted\n", + irq, + desc->action && desc->action->name ? + desc->action->name : ""); return -EBUSY; + } continue; } /* diff --git a/kernel/panic.c b/kernel/panic.c index 9ed023b8..90fd4431 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -27,13 +27,19 @@ #define PANIC_TIMER_STEP 100 #define PANIC_BLINK_SPD 18 +/* Machine specific panic information string */ +char *mach_panic_string; + int panic_on_oops; static unsigned long tainted_mask; static int pause_on_oops; static int pause_on_oops_flag; static DEFINE_SPINLOCK(pause_on_oops_lock); -int panic_timeout; +#ifndef CONFIG_PANIC_TIMEOUT +#define CONFIG_PANIC_TIMEOUT 0 +#endif +int panic_timeout = CONFIG_PANIC_TIMEOUT; EXPORT_SYMBOL_GPL(panic_timeout); ATOMIC_NOTIFIER_HEAD(panic_notifier_list); @@ -375,6 +381,11 @@ late_initcall(init_oops_id); void print_oops_end_marker(void) { init_oops_id(); + + if (mach_panic_string) + printk(KERN_WARNING "Board Information: %s\n", + mach_panic_string); + printk(KERN_WARNING "---[ end trace %016llx ]---\n", (unsigned long long)oops_id); } diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index deb5461e..904fc660 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -18,6 +18,14 @@ config SUSPEND_FREEZER Turning OFF this setting is NOT recommended! If in doubt, say Y. +config HAS_WAKELOCK + bool + default y + +config WAKELOCK + bool + default y + config HIBERNATE_CALLBACKS bool @@ -103,6 +111,33 @@ config PM_SLEEP_SMP select HOTPLUG select HOTPLUG_CPU +config PM_AUTOSLEEP + bool "Opportunistic sleep" + depends on PM_SLEEP + default n + ---help--- + Allow the kernel to trigger a system transition into a global sleep + state automatically whenever there are no active wakeup sources. + +config PM_WAKELOCKS + bool "User space wakeup sources interface" + depends on PM_SLEEP + default n + ---help--- + Allow user space to create, activate and deactivate wakeup source + objects with the help of a sysfs-based interface. + +config PM_WAKELOCKS_LIMIT + int "Maximum number of user space wakeup sources (0 = no limit)" + range 0 100000 + default 100 + depends on PM_WAKELOCKS + +config PM_WAKELOCKS_GC + bool "Garbage collector for user space wakeup sources" + depends on PM_WAKELOCKS + default y + config PM_RUNTIME bool "Run-time PM core functionality" depends on !IA64_HP_SIM @@ -243,3 +278,10 @@ config PM_GENERIC_DOMAINS_RUNTIME config CPU_PM bool depends on SUSPEND || CPU_IDLE + +config SUSPEND_TIME + bool "Log time spent in suspend" + ---help--- + Prints the time spent in suspend in the kernel log, and + keeps statistics on the time spent in suspend in + /sys/kernel/debug/suspend_time diff --git a/kernel/power/Makefile b/kernel/power/Makefile index 66d808ec..8450b85d 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -9,5 +9,8 @@ obj-$(CONFIG_SUSPEND) += suspend.o obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ block_io.o +obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o +obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o +obj-$(CONFIG_SUSPEND_TIME) += suspend_time.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o diff --git a/kernel/power/autosleep.c b/kernel/power/autosleep.c new file mode 100644 index 00000000..ca304046 --- /dev/null +++ b/kernel/power/autosleep.c @@ -0,0 +1,127 @@ +/* + * kernel/power/autosleep.c + * + * Opportunistic sleep support. + * + * Copyright (C) 2012 Rafael J. Wysocki + */ + +#include +#include +#include + +#include "power.h" + +static suspend_state_t autosleep_state; +static struct workqueue_struct *autosleep_wq; +/* + * Note: it is only safe to mutex_lock(&autosleep_lock) if a wakeup_source + * is active, otherwise a deadlock with try_to_suspend() is possible. + * Alternatively mutex_lock_interruptible() can be used. This will then fail + * if an auto_sleep cycle tries to freeze processes. + */ +static DEFINE_MUTEX(autosleep_lock); +static struct wakeup_source *autosleep_ws; + +static void try_to_suspend(struct work_struct *work) +{ + unsigned int initial_count, final_count; + + if (!pm_get_wakeup_count(&initial_count, true)) + goto out; + + mutex_lock(&autosleep_lock); + + if (!pm_save_wakeup_count(initial_count)) { + mutex_unlock(&autosleep_lock); + goto out; + } + + if (autosleep_state == PM_SUSPEND_ON) { + mutex_unlock(&autosleep_lock); + return; + } + if (autosleep_state >= PM_SUSPEND_MAX) + hibernate(); + else + pm_suspend(autosleep_state); + + mutex_unlock(&autosleep_lock); + + if (!pm_get_wakeup_count(&final_count, false)) + goto out; + + /* + * If the wakeup occured for an unknown reason, wait to prevent the + * system from trying to suspend and waking up in a tight loop. + */ + if (final_count == initial_count) + schedule_timeout_uninterruptible(HZ / 2); + + out: + queue_up_suspend_work(); +} + +static DECLARE_WORK(suspend_work, try_to_suspend); + +void queue_up_suspend_work(void) +{ + if (!work_pending(&suspend_work) && autosleep_state > PM_SUSPEND_ON) + queue_work(autosleep_wq, &suspend_work); +} + +suspend_state_t pm_autosleep_state(void) +{ + return autosleep_state; +} + +int pm_autosleep_lock(void) +{ + return mutex_lock_interruptible(&autosleep_lock); +} + +void pm_autosleep_unlock(void) +{ + mutex_unlock(&autosleep_lock); +} + +int pm_autosleep_set_state(suspend_state_t state) +{ + +#ifndef CONFIG_HIBERNATION + if (state >= PM_SUSPEND_MAX) + return -EINVAL; +#endif + + __pm_stay_awake(autosleep_ws); + + mutex_lock(&autosleep_lock); + + autosleep_state = state; + + __pm_relax(autosleep_ws); + + if (state > PM_SUSPEND_ON) { + pm_wakep_autosleep_enabled(true); + queue_up_suspend_work(); + } else { + pm_wakep_autosleep_enabled(false); + } + + mutex_unlock(&autosleep_lock); + return 0; +} + +int __init pm_autosleep_init(void) +{ + autosleep_ws = wakeup_source_register("autosleep"); + if (!autosleep_ws) + return -ENOMEM; + + autosleep_wq = alloc_ordered_workqueue("autosleep", 0); + if (autosleep_wq) + return 0; + + wakeup_source_unregister(autosleep_ws); + return -ENOMEM; +} diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 52a18173..586521aa 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include "power.h" @@ -728,6 +730,17 @@ static int software_resume(void) /* Check if the device is there */ swsusp_resume_device = name_to_dev_t(resume_file); + + /* + * name_to_dev_t is ineffective to verify parition if resume_file is in + * integer format. (e.g. major:minor) + */ + if (isdigit(resume_file[0]) && resume_wait) { + int partno; + while (!get_gendisk(swsusp_resume_device, &partno)) + msleep(10); + } + if (!swsusp_resume_device) { /* * Some device discovery might still be in progress; we need diff --git a/kernel/power/main.c b/kernel/power/main.c index 1c12581f..428f8a03 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -269,8 +269,7 @@ static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr, return (s - buf); } -static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, - const char *buf, size_t n) +static suspend_state_t decode_state(const char *buf, size_t n) { #ifdef CONFIG_SUSPEND suspend_state_t state = PM_SUSPEND_STANDBY; @@ -278,27 +277,48 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, #endif char *p; int len; - int error = -EINVAL; p = memchr(buf, '\n', n); len = p ? p - buf : n; - /* First, check if we are requested to hibernate */ - if (len == 4 && !strncmp(buf, "disk", len)) { - error = hibernate(); - goto Exit; - } + /* Check hibernation first. */ + if (len == 4 && !strncmp(buf, "disk", len)) + return PM_SUSPEND_MAX; #ifdef CONFIG_SUSPEND - for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) { - if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) { - error = pm_suspend(state); - break; - } - } + for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) + if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) + return state; #endif - Exit: + return PM_SUSPEND_ON; +} + +static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t n) +{ + suspend_state_t state; + int error; + + error = pm_autosleep_lock(); + if (error) + return error; + + if (pm_autosleep_state() > PM_SUSPEND_ON) { + error = -EBUSY; + goto out; + } + + state = decode_state(buf, n); + if (state < PM_SUSPEND_MAX) + error = pm_suspend(state); + else if (state == PM_SUSPEND_MAX) + error = hibernate(); + else + error = -EINVAL; + + out: + pm_autosleep_unlock(); return error ? error : n; } @@ -339,7 +359,8 @@ static ssize_t wakeup_count_show(struct kobject *kobj, { unsigned int val; - return pm_get_wakeup_count(&val) ? sprintf(buf, "%u\n", val) : -EINTR; + return pm_get_wakeup_count(&val, true) ? + sprintf(buf, "%u\n", val) : -EINTR; } static ssize_t wakeup_count_store(struct kobject *kobj, @@ -347,15 +368,106 @@ static ssize_t wakeup_count_store(struct kobject *kobj, const char *buf, size_t n) { unsigned int val; + int error; + + error = pm_autosleep_lock(); + if (error) + return error; + + if (pm_autosleep_state() > PM_SUSPEND_ON) { + error = -EBUSY; + goto out; + } + error = -EINVAL; if (sscanf(buf, "%u", &val) == 1) { if (pm_save_wakeup_count(val)) - return n; + error = n; } - return -EINVAL; + + out: + pm_autosleep_unlock(); + return error; } power_attr(wakeup_count); + +#ifdef CONFIG_PM_AUTOSLEEP +static ssize_t autosleep_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + suspend_state_t state = pm_autosleep_state(); + + if (state == PM_SUSPEND_ON) + return sprintf(buf, "off\n"); + +#ifdef CONFIG_SUSPEND + if (state < PM_SUSPEND_MAX) + return sprintf(buf, "%s\n", valid_state(state) ? + pm_states[state] : "error"); +#endif +#ifdef CONFIG_HIBERNATION + return sprintf(buf, "disk\n"); +#else + return sprintf(buf, "error"); +#endif +} + +static ssize_t autosleep_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + suspend_state_t state = decode_state(buf, n); + int error; + + if (state == PM_SUSPEND_ON + && strcmp(buf, "off") && strcmp(buf, "off\n")) + return -EINVAL; + + error = pm_autosleep_set_state(state); + return error ? error : n; +} + +power_attr(autosleep); +#endif /* CONFIG_PM_AUTOSLEEP */ + +#ifdef CONFIG_PM_WAKELOCKS +static ssize_t wake_lock_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return pm_show_wakelocks(buf, true); +} + +static ssize_t wake_lock_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + int error = pm_wake_lock(buf); + return error ? error : n; +} + +power_attr(wake_lock); + +static ssize_t wake_unlock_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return pm_show_wakelocks(buf, false); +} + +static ssize_t wake_unlock_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + int error = pm_wake_unlock(buf); + return error ? error : n; +} + +power_attr(wake_unlock); + +#endif /* CONFIG_PM_WAKELOCKS */ #endif /* CONFIG_PM_SLEEP */ #ifdef CONFIG_PM_TRACE @@ -409,6 +521,13 @@ static struct attribute * g[] = { #ifdef CONFIG_PM_SLEEP &pm_async_attr.attr, &wakeup_count_attr.attr, +#ifdef CONFIG_PM_AUTOSLEEP + &autosleep_attr.attr, +#endif +#ifdef CONFIG_PM_WAKELOCKS + &wake_lock_attr.attr, + &wake_unlock_attr.attr, +#endif #ifdef CONFIG_PM_DEBUG &pm_test_attr.attr, #endif @@ -444,7 +563,10 @@ static int __init pm_init(void) power_kobj = kobject_create_and_add("power", NULL); if (!power_kobj) return -ENOMEM; - return sysfs_create_group(power_kobj, &attr_group); + error = sysfs_create_group(power_kobj, &attr_group); + if (error) + return error; + return pm_autosleep_init(); } core_initcall(pm_init); diff --git a/kernel/power/power.h b/kernel/power/power.h index 98f3622d..b0bd4bea 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -264,3 +264,30 @@ static inline void suspend_thaw_processes(void) { } #endif + +#ifdef CONFIG_PM_AUTOSLEEP + +/* kernel/power/autosleep.c */ +extern int pm_autosleep_init(void); +extern int pm_autosleep_lock(void); +extern void pm_autosleep_unlock(void); +extern suspend_state_t pm_autosleep_state(void); +extern int pm_autosleep_set_state(suspend_state_t state); + +#else /* !CONFIG_PM_AUTOSLEEP */ + +static inline int pm_autosleep_init(void) { return 0; } +static inline int pm_autosleep_lock(void) { return 0; } +static inline void pm_autosleep_unlock(void) {} +static inline suspend_state_t pm_autosleep_state(void) { return PM_SUSPEND_ON; } + +#endif /* !CONFIG_PM_AUTOSLEEP */ + +#ifdef CONFIG_PM_WAKELOCKS + +/* kernel/power/wakelock.c */ +extern ssize_t pm_show_wakelocks(char *buf, bool show_active); +extern int pm_wake_lock(const char *buf); +extern int pm_wake_unlock(const char *buf); + +#endif /* !CONFIG_PM_WAKELOCKS */ diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index c8b7446b..e97a689f 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "power.h" @@ -303,6 +304,18 @@ static int enter_state(suspend_state_t state) return error; } +static void pm_suspend_marker(char *annotation) +{ + struct timespec ts; + struct rtc_time tm; + + getnstimeofday(&ts); + rtc_time_to_tm(ts.tv_sec, &tm); + pr_info("PM: suspend %s %d-%02d-%02d %02d:%02d:%02d.%09lu UTC\n", + annotation, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); +} + /** * pm_suspend - Externally visible function for suspending the system. * @state: System sleep state to enter. @@ -317,6 +330,7 @@ int pm_suspend(suspend_state_t state) if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX) return -EINVAL; + pm_suspend_marker("entry"); error = enter_state(state); if (error) { suspend_stats.fail++; @@ -324,6 +338,7 @@ int pm_suspend(suspend_state_t state) } else { suspend_stats.success++; } + pm_suspend_marker("exit"); return error; } EXPORT_SYMBOL(pm_suspend); diff --git a/kernel/power/suspend_time.c b/kernel/power/suspend_time.c new file mode 100644 index 00000000..d2a65da9 --- /dev/null +++ b/kernel/power/suspend_time.c @@ -0,0 +1,111 @@ +/* + * debugfs file to track time spent in suspend + * + * Copyright (c) 2011, Google, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +static struct timespec suspend_time_before; +static unsigned int time_in_suspend_bins[32]; + +#ifdef CONFIG_DEBUG_FS +static int suspend_time_debug_show(struct seq_file *s, void *data) +{ + int bin; + seq_printf(s, "time (secs) count\n"); + seq_printf(s, "------------------\n"); + for (bin = 0; bin < 32; bin++) { + if (time_in_suspend_bins[bin] == 0) + continue; + seq_printf(s, "%4d - %4d %4u\n", + bin ? 1 << (bin - 1) : 0, 1 << bin, + time_in_suspend_bins[bin]); + } + return 0; +} + +static int suspend_time_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, suspend_time_debug_show, NULL); +} + +static const struct file_operations suspend_time_debug_fops = { + .open = suspend_time_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init suspend_time_debug_init(void) +{ + struct dentry *d; + + d = debugfs_create_file("suspend_time", 0755, NULL, NULL, + &suspend_time_debug_fops); + if (!d) { + pr_err("Failed to create suspend_time debug file\n"); + return -ENOMEM; + } + + return 0; +} + +late_initcall(suspend_time_debug_init); +#endif + +static int suspend_time_syscore_suspend(void) +{ + read_persistent_clock(&suspend_time_before); + + return 0; +} + +static void suspend_time_syscore_resume(void) +{ + struct timespec after; + + read_persistent_clock(&after); + + after = timespec_sub(after, suspend_time_before); + + time_in_suspend_bins[fls(after.tv_sec)]++; + + pr_info("Suspended for %lu.%03lu seconds\n", after.tv_sec, + after.tv_nsec / NSEC_PER_MSEC); +} + +static struct syscore_ops suspend_time_syscore_ops = { + .suspend = suspend_time_syscore_suspend, + .resume = suspend_time_syscore_resume, +}; + +static int suspend_time_syscore_init(void) +{ + register_syscore_ops(&suspend_time_syscore_ops); + + return 0; +} + +static void suspend_time_syscore_exit(void) +{ + unregister_syscore_ops(&suspend_time_syscore_ops); +} +module_init(suspend_time_syscore_init); +module_exit(suspend_time_syscore_exit); diff --git a/kernel/power/swap.c b/kernel/power/swap.c index eef311a5..11e22c06 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -6,7 +6,7 @@ * * Copyright (C) 1998,2001-2005 Pavel Machek * Copyright (C) 2006 Rafael J. Wysocki - * Copyright (C) 2010 Bojan Smojver + * Copyright (C) 2010-2012 Bojan Smojver * * This file is released under the GPLv2. * @@ -282,14 +282,17 @@ static int write_page(void *buf, sector_t offset, struct bio **bio_chain) return -ENOSPC; if (bio_chain) { - src = (void *)__get_free_page(__GFP_WAIT | __GFP_HIGH); + src = (void *)__get_free_page(__GFP_WAIT | __GFP_NOWARN | + __GFP_NORETRY); if (src) { copy_page(src, buf); } else { ret = hib_wait_on_bio_chain(bio_chain); /* Free pages */ if (ret) return ret; - src = (void *)__get_free_page(__GFP_WAIT | __GFP_HIGH); + src = (void *)__get_free_page(__GFP_WAIT | + __GFP_NOWARN | + __GFP_NORETRY); if (src) { copy_page(src, buf); } else { @@ -367,12 +370,17 @@ static int swap_write_page(struct swap_map_handle *handle, void *buf, clear_page(handle->cur); handle->cur_swap = offset; handle->k = 0; - } - if (bio_chain && low_free_pages() <= handle->reqd_free_pages) { - error = hib_wait_on_bio_chain(bio_chain); - if (error) - goto out; - handle->reqd_free_pages = reqd_free_pages(); + + if (bio_chain && low_free_pages() <= handle->reqd_free_pages) { + error = hib_wait_on_bio_chain(bio_chain); + if (error) + goto out; + /* + * Recalculate the number of required free pages, to + * make sure we never take more than half. + */ + handle->reqd_free_pages = reqd_free_pages(); + } } out: return error; @@ -419,8 +427,9 @@ static int swap_writer_finish(struct swap_map_handle *handle, /* Maximum number of threads for compression/decompression. */ #define LZO_THREADS 3 -/* Maximum number of pages for read buffering. */ -#define LZO_READ_PAGES (MAP_PAGE_ENTRIES * 8) +/* Minimum/maximum number of pages for read buffering. */ +#define LZO_MIN_RD_PAGES 1024 +#define LZO_MAX_RD_PAGES 8192 /** @@ -630,12 +639,6 @@ static int save_image_lzo(struct swap_map_handle *handle, } } - /* - * Adjust number of free pages after all allocations have been done. - * We don't want to run out of pages when writing. - */ - handle->reqd_free_pages = reqd_free_pages(); - /* * Start the CRC32 thread. */ @@ -657,6 +660,12 @@ static int save_image_lzo(struct swap_map_handle *handle, goto out_clean; } + /* + * Adjust the number of required free pages after all allocations have + * been done. We don't want to run out of pages when writing. + */ + handle->reqd_free_pages = reqd_free_pages(); + printk(KERN_INFO "PM: Using %u thread(s) for compression.\n" "PM: Compressing and saving image data (%u pages) ... ", @@ -1067,7 +1076,7 @@ static int load_image_lzo(struct swap_map_handle *handle, unsigned i, thr, run_threads, nr_threads; unsigned ring = 0, pg = 0, ring_size = 0, have = 0, want, need, asked = 0; - unsigned long read_pages; + unsigned long read_pages = 0; unsigned char **page = NULL; struct dec_data *data = NULL; struct crc_data *crc = NULL; @@ -1079,7 +1088,7 @@ static int load_image_lzo(struct swap_map_handle *handle, nr_threads = num_online_cpus() - 1; nr_threads = clamp_val(nr_threads, 1, LZO_THREADS); - page = vmalloc(sizeof(*page) * LZO_READ_PAGES); + page = vmalloc(sizeof(*page) * LZO_MAX_RD_PAGES); if (!page) { printk(KERN_ERR "PM: Failed to allocate LZO page\n"); ret = -ENOMEM; @@ -1144,15 +1153,22 @@ static int load_image_lzo(struct swap_map_handle *handle, } /* - * Adjust number of pages for read buffering, in case we are short. + * Set the number of pages for read buffering. + * This is complete guesswork, because we'll only know the real + * picture once prepare_image() is called, which is much later on + * during the image load phase. We'll assume the worst case and + * say that none of the image pages are from high memory. */ - read_pages = (nr_free_pages() - snapshot_get_image_size()) >> 1; - read_pages = clamp_val(read_pages, LZO_CMP_PAGES, LZO_READ_PAGES); + if (low_free_pages() > snapshot_get_image_size()) + read_pages = (low_free_pages() - snapshot_get_image_size()) / 2; + read_pages = clamp_val(read_pages, LZO_MIN_RD_PAGES, LZO_MAX_RD_PAGES); for (i = 0; i < read_pages; i++) { page[i] = (void *)__get_free_page(i < LZO_CMP_PAGES ? __GFP_WAIT | __GFP_HIGH : - __GFP_WAIT); + __GFP_WAIT | __GFP_NOWARN | + __GFP_NORETRY); + if (!page[i]) { if (i < LZO_CMP_PAGES) { ring_size = i; diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c new file mode 100644 index 00000000..c8fba338 --- /dev/null +++ b/kernel/power/wakelock.c @@ -0,0 +1,259 @@ +/* + * kernel/power/wakelock.c + * + * User space wakeup sources support. + * + * Copyright (C) 2012 Rafael J. Wysocki + * + * This code is based on the analogous interface allowing user space to + * manipulate wakelocks on Android. + */ + +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_MUTEX(wakelocks_lock); + +struct wakelock { + char *name; + struct rb_node node; + struct wakeup_source ws; +#ifdef CONFIG_PM_WAKELOCKS_GC + struct list_head lru; +#endif +}; + +static struct rb_root wakelocks_tree = RB_ROOT; + +ssize_t pm_show_wakelocks(char *buf, bool show_active) +{ + struct rb_node *node; + struct wakelock *wl; + char *str = buf; + char *end = buf + PAGE_SIZE; + + mutex_lock(&wakelocks_lock); + + for (node = rb_first(&wakelocks_tree); node; node = rb_next(node)) { + wl = rb_entry(node, struct wakelock, node); + if (wl->ws.active == show_active) + str += scnprintf(str, end - str, "%s ", wl->name); + } + if (str > buf) + str--; + + str += scnprintf(str, end - str, "\n"); + + mutex_unlock(&wakelocks_lock); + return (str - buf); +} + +#if CONFIG_PM_WAKELOCKS_LIMIT > 0 +static unsigned int number_of_wakelocks; + +static inline bool wakelocks_limit_exceeded(void) +{ + return number_of_wakelocks > CONFIG_PM_WAKELOCKS_LIMIT; +} + +static inline void increment_wakelocks_number(void) +{ + number_of_wakelocks++; +} + +static inline void decrement_wakelocks_number(void) +{ + number_of_wakelocks--; +} +#else /* CONFIG_PM_WAKELOCKS_LIMIT = 0 */ +static inline bool wakelocks_limit_exceeded(void) { return false; } +static inline void increment_wakelocks_number(void) {} +static inline void decrement_wakelocks_number(void) {} +#endif /* CONFIG_PM_WAKELOCKS_LIMIT */ + +#ifdef CONFIG_PM_WAKELOCKS_GC +#define WL_GC_COUNT_MAX 100 +#define WL_GC_TIME_SEC 300 + +static LIST_HEAD(wakelocks_lru_list); +static unsigned int wakelocks_gc_count; + +static inline void wakelocks_lru_add(struct wakelock *wl) +{ + list_add(&wl->lru, &wakelocks_lru_list); +} + +static inline void wakelocks_lru_most_recent(struct wakelock *wl) +{ + list_move(&wl->lru, &wakelocks_lru_list); +} + +static void wakelocks_gc(void) +{ + struct wakelock *wl, *aux; + ktime_t now; + + if (++wakelocks_gc_count <= WL_GC_COUNT_MAX) + return; + + now = ktime_get(); + list_for_each_entry_safe_reverse(wl, aux, &wakelocks_lru_list, lru) { + u64 idle_time_ns; + bool active; + + spin_lock_irq(&wl->ws.lock); + idle_time_ns = ktime_to_ns(ktime_sub(now, wl->ws.last_time)); + active = wl->ws.active; + spin_unlock_irq(&wl->ws.lock); + + if (idle_time_ns < ((u64)WL_GC_TIME_SEC * NSEC_PER_SEC)) + break; + + if (!active) { + wakeup_source_remove(&wl->ws); + rb_erase(&wl->node, &wakelocks_tree); + list_del(&wl->lru); + kfree(wl->name); + kfree(wl); + decrement_wakelocks_number(); + } + } + wakelocks_gc_count = 0; +} +#else /* !CONFIG_PM_WAKELOCKS_GC */ +static inline void wakelocks_lru_add(struct wakelock *wl) {} +static inline void wakelocks_lru_most_recent(struct wakelock *wl) {} +static inline void wakelocks_gc(void) {} +#endif /* !CONFIG_PM_WAKELOCKS_GC */ + +static struct wakelock *wakelock_lookup_add(const char *name, size_t len, + bool add_if_not_found) +{ + struct rb_node **node = &wakelocks_tree.rb_node; + struct rb_node *parent = *node; + struct wakelock *wl; + + while (*node) { + int diff; + + parent = *node; + wl = rb_entry(*node, struct wakelock, node); + diff = strncmp(name, wl->name, len); + if (diff == 0) { + if (wl->name[len]) + diff = -1; + else + return wl; + } + if (diff < 0) + node = &(*node)->rb_left; + else + node = &(*node)->rb_right; + } + if (!add_if_not_found) + return ERR_PTR(-EINVAL); + + if (wakelocks_limit_exceeded()) + return ERR_PTR(-ENOSPC); + + /* Not found, we have to add a new one. */ + wl = kzalloc(sizeof(*wl), GFP_KERNEL); + if (!wl) + return ERR_PTR(-ENOMEM); + + wl->name = kstrndup(name, len, GFP_KERNEL); + if (!wl->name) { + kfree(wl); + return ERR_PTR(-ENOMEM); + } + wl->ws.name = wl->name; + wakeup_source_add(&wl->ws); + rb_link_node(&wl->node, parent, node); + rb_insert_color(&wl->node, &wakelocks_tree); + wakelocks_lru_add(wl); + increment_wakelocks_number(); + return wl; +} + +int pm_wake_lock(const char *buf) +{ + const char *str = buf; + struct wakelock *wl; + u64 timeout_ns = 0; + size_t len; + int ret = 0; + + while (*str && !isspace(*str)) + str++; + + len = str - buf; + if (!len) + return -EINVAL; + + if (*str && *str != '\n') { + /* Find out if there's a valid timeout string appended. */ + ret = kstrtou64(skip_spaces(str), 10, &timeout_ns); + if (ret) + return -EINVAL; + } + + mutex_lock(&wakelocks_lock); + + wl = wakelock_lookup_add(buf, len, true); + if (IS_ERR(wl)) { + ret = PTR_ERR(wl); + goto out; + } + if (timeout_ns) { + u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1; + + do_div(timeout_ms, NSEC_PER_MSEC); + __pm_wakeup_event(&wl->ws, timeout_ms); + } else { + __pm_stay_awake(&wl->ws); + } + + wakelocks_lru_most_recent(wl); + + out: + mutex_unlock(&wakelocks_lock); + return ret; +} + +int pm_wake_unlock(const char *buf) +{ + struct wakelock *wl; + size_t len; + int ret = 0; + + len = strlen(buf); + if (!len) + return -EINVAL; + + if (buf[len-1] == '\n') + len--; + + if (!len) + return -EINVAL; + + mutex_lock(&wakelocks_lock); + + wl = wakelock_lookup_add(buf, len, false); + if (IS_ERR(wl)) { + ret = PTR_ERR(wl); + goto out; + } + __pm_relax(&wl->ws); + + wakelocks_lru_most_recent(wl); + wakelocks_gc(); + + out: + mutex_unlock(&wakelocks_lock); + return ret; +} diff --git a/kernel/printk.c b/kernel/printk.c index e95c6622..ffe4b65a 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -56,6 +56,10 @@ void asmlinkage __attribute__((weak)) early_printk(const char *fmt, ...) #define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT) +#ifdef CONFIG_DEBUG_LL +extern void printascii(char *); +#endif + /* printk's without a loglevel use this.. */ #define DEFAULT_MESSAGE_LOGLEVEL CONFIG_DEFAULT_MESSAGE_LOGLEVEL @@ -293,6 +297,53 @@ static inline void boot_delay_msec(void) } #endif +/* + * Return the number of unread characters in the log buffer. + */ +static int log_buf_get_len(void) +{ + return logged_chars; +} + +/* + * Clears the ring-buffer + */ +void log_buf_clear(void) +{ + logged_chars = 0; +} + +/* + * Copy a range of characters from the log buffer. + */ +int log_buf_copy(char *dest, int idx, int len) +{ + int ret, max; + bool took_lock = false; + + if (!oops_in_progress) { + raw_spin_lock_irq(&logbuf_lock); + took_lock = true; + } + + max = log_buf_get_len(); + if (idx < 0 || idx >= max) { + ret = -1; + } else { + if (len > max - idx) + len = max - idx; + ret = len; + idx += (log_end - max); + while (len-- > 0) + dest[len] = LOG_BUF(idx + len); + } + + if (took_lock) + raw_spin_unlock_irq(&logbuf_lock); + + return ret; +} + #ifdef CONFIG_SECURITY_DMESG_RESTRICT int dmesg_restrict = 1; #else @@ -895,6 +946,10 @@ asmlinkage int vprintk(const char *fmt, va_list args) printed_len += vscnprintf(printk_buf + printed_len, sizeof(printk_buf) - printed_len, fmt, args); +#ifdef CONFIG_DEBUG_LL + printascii(printk_buf); +#endif + p = printk_buf; /* Read log level and handle special printk prefix */ @@ -1172,7 +1227,6 @@ static int __cpuinit console_cpu_notify(struct notifier_block *self, switch (action) { case CPU_ONLINE: case CPU_DEAD: - case CPU_DYING: case CPU_DOWN_FAILED: case CPU_UP_CANCELED: console_lock(); diff --git a/kernel/sched/core.c b/kernel/sched/core.c index e1718bc3..d10a575b 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -7243,13 +7243,24 @@ static inline int preempt_count_equals(int preempt_offset) return (nested == preempt_offset); } +static int __might_sleep_init_called; +int __init __might_sleep_init(void) +{ + __might_sleep_init_called = 1; + return 0; +} +early_initcall(__might_sleep_init); + void __might_sleep(const char *file, int line, int preempt_offset) { static unsigned long prev_jiffy; /* ratelimiting */ rcu_sleep_check(); /* WARN_ON_ONCE() by default, no rate limit reqd. */ if ((preempt_count_equals(preempt_offset) && !irqs_disabled()) || - system_state != SYSTEM_RUNNING || oops_in_progress) + oops_in_progress) + return; + if (system_state != SYSTEM_RUNNING && + (!__might_sleep_init_called || system_state != SYSTEM_BOOTING)) return; if (time_before(jiffies, prev_jiffy + HZ) && prev_jiffy) return; @@ -7809,6 +7820,23 @@ static void cpu_cgroup_destroy(struct cgroup *cgrp) sched_destroy_group(tg); } +static int +cpu_cgroup_allow_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) +{ + const struct cred *cred = current_cred(), *tcred; + struct task_struct *task; + + cgroup_taskset_for_each(task, cgrp, tset) { + tcred = __task_cred(task); + + if ((current != task) && !capable(CAP_SYS_NICE) && + cred->euid != tcred->uid && cred->euid != tcred->suid) + return -EACCES; + } + + return 0; +} + static int cpu_cgroup_can_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) { @@ -8170,6 +8198,7 @@ struct cgroup_subsys cpu_cgroup_subsys = { .destroy = cpu_cgroup_destroy, .can_attach = cpu_cgroup_can_attach, .attach = cpu_cgroup_attach, + .allow_attach = cpu_cgroup_allow_attach, .exit = cpu_cgroup_exit, .populate = cpu_cgroup_populate, .subsys_id = cpu_cgroup_subsys_id, diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index ead03360..e8a7a2c0 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -1983,6 +1983,8 @@ static void watchdog(struct rq *rq, struct task_struct *p) static void task_tick_rt(struct rq *rq, struct task_struct *p, int queued) { + struct sched_rt_entity *rt_se = &p->rt; + update_curr_rt(rq); watchdog(rq, p); @@ -2000,12 +2002,15 @@ static void task_tick_rt(struct rq *rq, struct task_struct *p, int queued) p->rt.time_slice = RR_TIMESLICE; /* - * Requeue to the end of queue if we are not the only element - * on the queue: + * Requeue to the end of queue if we (and all of our ancestors) are the + * only element on the queue */ - if (p->rt.run_list.prev != p->rt.run_list.next) { - requeue_task_rt(rq, p, 0); - set_tsk_need_resched(p); + for_each_sched_rt_entity(rt_se) { + if (rt_se->run_list.prev != rt_se->run_list.next) { + requeue_task_rt(rq, p, 0); + set_tsk_need_resched(p); + return; + } } } diff --git a/kernel/signal.c b/kernel/signal.c index a4363a98..563b7711 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2208,7 +2208,7 @@ relock: * Now that we woke up, it's crucial if we're supposed to be * frozen that we freeze now before running anything substantial. */ - try_to_freeze(); + try_to_freeze_nowarn(); spin_lock_irq(&sighand->siglock); /* diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 4ab11879..49f47258 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -102,6 +102,7 @@ extern char core_pattern[]; extern unsigned int core_pipe_limit; extern int pid_max; extern int min_free_kbytes; +extern int min_free_order_shift; extern int pid_max_min, pid_max_max; extern int sysctl_drop_caches; extern int percpu_pagelist_fraction; @@ -1198,6 +1199,13 @@ static struct ctl_table vm_table[] = { .proc_handler = min_free_kbytes_sysctl_handler, .extra1 = &zero, }, + { + .procname = "min_free_order_shift", + .data = &min_free_order_shift, + .maxlen = sizeof(min_free_order_shift), + .mode = 0644, + .proc_handler = &proc_dointvec + }, { .procname = "percpu_pagelist_fraction", .data = &percpu_pagelist_fraction, diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c index 8a538c55..f979d852 100644 --- a/kernel/time/alarmtimer.c +++ b/kernel/time/alarmtimer.c @@ -46,6 +46,8 @@ static struct alarm_base { static ktime_t freezer_delta; static DEFINE_SPINLOCK(freezer_delta_lock); +static struct wakeup_source *ws; + #ifdef CONFIG_RTC_CLASS /* rtc timer and device for setting alarm wakeups at suspend */ static struct rtc_timer rtctimer; @@ -59,7 +61,7 @@ static DEFINE_SPINLOCK(rtcdev_lock); * If one has not already been chosen, it checks to see if a * functional rtc device is available. */ -static struct rtc_device *alarmtimer_get_rtcdev(void) +struct rtc_device *alarmtimer_get_rtcdev(void) { unsigned long flags; struct rtc_device *ret; @@ -115,7 +117,7 @@ static void alarmtimer_rtc_interface_remove(void) class_interface_unregister(&alarmtimer_rtc_interface); } #else -static inline struct rtc_device *alarmtimer_get_rtcdev(void) +struct rtc_device *alarmtimer_get_rtcdev(void) { return NULL; } @@ -250,6 +252,7 @@ static int alarmtimer_suspend(struct device *dev) unsigned long flags; struct rtc_device *rtc; int i; + int ret; spin_lock_irqsave(&freezer_delta_lock, flags); min = freezer_delta; @@ -279,8 +282,10 @@ static int alarmtimer_suspend(struct device *dev) if (min.tv64 == 0) return 0; - /* XXX - Should we enforce a minimum sleep time? */ - WARN_ON(min.tv64 < NSEC_PER_SEC); + if (ktime_to_ns(min) < 2 * NSEC_PER_SEC) { + __pm_wakeup_event(ws, 2 * MSEC_PER_SEC); + return -EBUSY; + } /* Setup an rtc timer to fire that far in the future */ rtc_timer_cancel(rtc, &rtctimer); @@ -288,9 +293,11 @@ static int alarmtimer_suspend(struct device *dev) now = rtc_tm_to_ktime(tm); now = ktime_add(now, min); - rtc_timer_start(rtc, &rtctimer, now, ktime_set(0, 0)); - - return 0; + /* Set alarm, if in the past reject suspend briefly to handle */ + ret = rtc_timer_start(rtc, &rtctimer, now, ktime_set(0, 0)); + if (ret < 0) + __pm_wakeup_event(ws, 1 * MSEC_PER_SEC); + return ret; } #else static int alarmtimer_suspend(struct device *dev) @@ -821,6 +828,7 @@ static int __init alarmtimer_init(void) error = PTR_ERR(pdev); goto out_drv; } + ws = wakeup_source_register("alarmtimer"); return 0; out_drv: diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index eff0b1e9..0a462de7 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -1193,7 +1193,7 @@ void get_monotonic_boottime(struct timespec *ts) } while (read_seqretry(&timekeeper.lock, seq)); set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec + sleep.tv_sec, - ts->tv_nsec + tomono.tv_nsec + sleep.tv_nsec + nsecs); + (s64)ts->tv_nsec + tomono.tv_nsec + sleep.tv_nsec + nsecs); } EXPORT_SYMBOL_GPL(get_monotonic_boottime); diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index a1d2849f..59a80f8a 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -69,6 +69,9 @@ config EVENT_TRACING select CONTEXT_SWITCH_TRACER bool +config GPU_TRACEPOINTS + bool + config EVENT_POWER_TRACING_DEPRECATED depends on EVENT_TRACING bool "Deprecated power event trace API, to be removed" diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index 5f39a07f..fedcd61d 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile @@ -61,5 +61,6 @@ endif ifeq ($(CONFIG_TRACING),y) obj-$(CONFIG_KGDB_KDB) += trace_kdb.o endif +obj-$(CONFIG_GPU_TRACEPOINTS) += gpu-traces.o libftrace-y := ftrace.o diff --git a/kernel/trace/gpu-traces.c b/kernel/trace/gpu-traces.c new file mode 100644 index 00000000..a4b3f00f --- /dev/null +++ b/kernel/trace/gpu-traces.c @@ -0,0 +1,23 @@ +/* + * GPU tracepoints + * + * Copyright (C) 2013 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include + +#define CREATE_TRACE_POINTS +#include + +EXPORT_TRACEPOINT_SYMBOL(gpu_sched_switch); +EXPORT_TRACEPOINT_SYMBOL(gpu_job_enqueue); diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 55e4d4c5..418c79b0 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -482,6 +482,7 @@ static const char *trace_options[] = { "overwrite", "disable_on_free", "irq-info", + "print-tgid", NULL }; @@ -970,6 +971,7 @@ void tracing_reset_current_online_cpus(void) static unsigned map_pid_to_cmdline[PID_MAX_DEFAULT+1]; static unsigned map_cmdline_to_pid[SAVED_CMDLINES]; static char saved_cmdlines[SAVED_CMDLINES][TASK_COMM_LEN]; +static unsigned saved_tgids[SAVED_CMDLINES]; static int cmdline_idx; static arch_spinlock_t trace_cmdline_lock = __ARCH_SPIN_LOCK_UNLOCKED; @@ -1117,6 +1119,7 @@ static void trace_save_cmdline(struct task_struct *tsk) } memcpy(&saved_cmdlines[idx], tsk->comm, TASK_COMM_LEN); + saved_tgids[idx] = tsk->tgid; arch_spin_unlock(&trace_cmdline_lock); } @@ -1152,6 +1155,25 @@ void trace_find_cmdline(int pid, char comm[]) preempt_enable(); } +int trace_find_tgid(int pid) +{ + unsigned map; + int tgid; + + preempt_disable(); + arch_spin_lock(&trace_cmdline_lock); + map = map_pid_to_cmdline[pid]; + if (map != NO_CMDLINE_MAP) + tgid = saved_tgids[map]; + else + tgid = -1; + + arch_spin_unlock(&trace_cmdline_lock); + preempt_enable(); + + return tgid; +} + void tracing_record_cmdline(struct task_struct *tsk) { if (atomic_read(&trace_record_cmdline_disabled) || !tracer_enabled || @@ -1960,6 +1982,13 @@ static void print_func_help_header(struct trace_array *tr, struct seq_file *m) seq_puts(m, "# | | | | |\n"); } +static void print_func_help_header_tgid(struct trace_array *tr, struct seq_file *m) +{ + print_event_info(tr, m); + seq_puts(m, "# TASK-PID TGID CPU# TIMESTAMP FUNCTION\n"); + seq_puts(m, "# | | | | | |\n"); +} + static void print_func_help_header_irq(struct trace_array *tr, struct seq_file *m) { print_event_info(tr, m); @@ -1972,6 +2001,18 @@ static void print_func_help_header_irq(struct trace_array *tr, struct seq_file * seq_puts(m, "# | | | |||| | |\n"); } +static void print_func_help_header_irq_tgid(struct trace_array *tr, struct seq_file *m) +{ + print_event_info(tr, m); + seq_puts(m, "# _-----=> irqs-off\n"); + seq_puts(m, "# / _----=> need-resched\n"); + seq_puts(m, "# | / _---=> hardirq/softirq\n"); + seq_puts(m, "# || / _--=> preempt-depth\n"); + seq_puts(m, "# ||| / delay\n"); + seq_puts(m, "# TASK-PID TGID CPU# |||| TIMESTAMP FUNCTION\n"); + seq_puts(m, "# | | | | |||| | |\n"); +} + void print_trace_header(struct seq_file *m, struct trace_iterator *iter) { @@ -2264,9 +2305,15 @@ void trace_default_header(struct seq_file *m) } else { if (!(trace_flags & TRACE_ITER_VERBOSE)) { if (trace_flags & TRACE_ITER_IRQ_INFO) - print_func_help_header_irq(iter->tr, m); + if (trace_flags & TRACE_ITER_TGID) + print_func_help_header_irq_tgid(iter->tr, m); + else + print_func_help_header_irq(iter->tr, m); else - print_func_help_header(iter->tr, m); + if (trace_flags & TRACE_ITER_TGID) + print_func_help_header_tgid(iter->tr, m); + else + print_func_help_header(iter->tr, m); } } } @@ -2898,9 +2945,53 @@ tracing_saved_cmdlines_read(struct file *file, char __user *ubuf, } static const struct file_operations tracing_saved_cmdlines_fops = { - .open = tracing_open_generic, - .read = tracing_saved_cmdlines_read, - .llseek = generic_file_llseek, + .open = tracing_open_generic, + .read = tracing_saved_cmdlines_read, + .llseek = generic_file_llseek, +}; + +static ssize_t +tracing_saved_tgids_read(struct file *file, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + char *file_buf; + char *buf; + int len = 0; + int pid; + int i; + + file_buf = kmalloc(SAVED_CMDLINES*(16+1+16), GFP_KERNEL); + if (!file_buf) + return -ENOMEM; + + buf = file_buf; + + for (i = 0; i < SAVED_CMDLINES; i++) { + int tgid; + int r; + + pid = map_cmdline_to_pid[i]; + if (pid == -1 || pid == NO_CMDLINE_MAP) + continue; + + tgid = trace_find_tgid(pid); + r = sprintf(buf, "%d %d\n", pid, tgid); + buf += r; + len += r; + } + + len = simple_read_from_buffer(ubuf, cnt, ppos, + file_buf, len); + + kfree(file_buf); + + return len; +} + +static const struct file_operations tracing_saved_tgids_fops = { + .open = tracing_open_generic, + .read = tracing_saved_tgids_read, + .llseek = generic_file_llseek, }; static ssize_t @@ -4736,6 +4827,9 @@ static __init int tracer_init_debugfs(void) trace_create_file("saved_cmdlines", 0444, d_tracer, NULL, &tracing_saved_cmdlines_fops); + trace_create_file("saved_tgids", 0444, d_tracer, + NULL, &tracing_saved_tgids_fops); + trace_create_file("trace_clock", 0644, d_tracer, NULL, &trace_clock_fops); diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index f95d65da..6d83991c 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -456,6 +456,7 @@ static inline void __trace_stack(struct trace_array *tr, unsigned long flags, extern cycle_t ftrace_now(int cpu); extern void trace_find_cmdline(int pid, char comm[]); +extern int trace_find_tgid(int pid); #ifdef CONFIG_DYNAMIC_FTRACE extern unsigned long ftrace_update_tot_cnt; @@ -667,6 +668,7 @@ enum trace_iterator_flags { TRACE_ITER_OVERWRITE = 0x200000, TRACE_ITER_STOP_ON_FREE = 0x400000, TRACE_ITER_IRQ_INFO = 0x800000, + TRACE_ITER_TGID = 0x1000000, }; /* diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c index a7d2a4c6..f7f1f37a 100644 --- a/kernel/trace/trace_functions_graph.c +++ b/kernel/trace/trace_functions_graph.c @@ -46,6 +46,8 @@ struct fgraph_data { #define TRACE_GRAPH_PRINT_DURATION 0x10 #define TRACE_GRAPH_PRINT_ABS_TIME 0x20 #define TRACE_GRAPH_PRINT_IRQS 0x40 +#define TRACE_GRAPH_PRINT_FLAT 0x80 + static struct tracer_opt trace_opts[] = { /* Display overruns? (for self-debug purpose) */ @@ -62,6 +64,8 @@ static struct tracer_opt trace_opts[] = { { TRACER_OPT(funcgraph-abstime, TRACE_GRAPH_PRINT_ABS_TIME) }, /* Display interrupts */ { TRACER_OPT(funcgraph-irqs, TRACE_GRAPH_PRINT_IRQS) }, + /* Use standard trace formatting rather than hierarchical */ + { TRACER_OPT(funcgraph-flat, TRACE_GRAPH_PRINT_FLAT) }, { } /* Empty entry */ }; @@ -1222,6 +1226,9 @@ print_graph_function_flags(struct trace_iterator *iter, u32 flags) int cpu = iter->cpu; int ret; + if (flags & TRACE_GRAPH_PRINT_FLAT) + return TRACE_TYPE_UNHANDLED; + if (data && per_cpu_ptr(data->cpu_data, cpu)->ignore) { per_cpu_ptr(data->cpu_data, cpu)->ignore = 0; return TRACE_TYPE_HANDLED; @@ -1279,13 +1286,6 @@ print_graph_function(struct trace_iterator *iter) return print_graph_function_flags(iter, tracer_flags.val); } -static enum print_line_t -print_graph_function_event(struct trace_iterator *iter, int flags, - struct trace_event *event) -{ - return print_graph_function(iter); -} - static void print_lat_header(struct seq_file *s, u32 flags) { static const char spaces[] = " " /* 16 spaces */ @@ -1352,6 +1352,11 @@ void print_graph_headers_flags(struct seq_file *s, u32 flags) { struct trace_iterator *iter = s->private; + if (flags & TRACE_GRAPH_PRINT_FLAT) { + trace_default_header(s); + return; + } + if (!(trace_flags & TRACE_ITER_CONTEXT_INFO)) return; @@ -1422,20 +1427,6 @@ static int func_graph_set_flag(u32 old_flags, u32 bit, int set) return 0; } -static struct trace_event_functions graph_functions = { - .trace = print_graph_function_event, -}; - -static struct trace_event graph_trace_entry_event = { - .type = TRACE_GRAPH_ENT, - .funcs = &graph_functions, -}; - -static struct trace_event graph_trace_ret_event = { - .type = TRACE_GRAPH_RET, - .funcs = &graph_functions -}; - static struct tracer graph_trace __read_mostly = { .name = "function_graph", .open = graph_trace_open, @@ -1458,16 +1449,6 @@ static __init int init_graph_trace(void) { max_bytes_for_cpu = snprintf(NULL, 0, "%d", nr_cpu_ids - 1); - if (!register_ftrace_event(&graph_trace_entry_event)) { - pr_warning("Warning: could not register graph trace events\n"); - return 1; - } - - if (!register_ftrace_event(&graph_trace_ret_event)) { - pr_warning("Warning: could not register graph trace events\n"); - return 1; - } - return register_tracer(&graph_trace); } diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index df611a0e..cb29ce20 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c @@ -630,11 +630,25 @@ int trace_print_context(struct trace_iterator *iter) unsigned long secs = (unsigned long)t; char comm[TASK_COMM_LEN]; int ret; + int tgid; trace_find_cmdline(entry->pid, comm); - ret = trace_seq_printf(s, "%16s-%-5d [%03d] ", - comm, entry->pid, iter->cpu); + ret = trace_seq_printf(s, "%16s-%-5d ", comm, entry->pid); + if (!ret) + return 0; + + if (trace_flags & TRACE_ITER_TGID) { + tgid = trace_find_tgid(entry->pid); + if (tgid < 0) + ret = trace_seq_puts(s, "(-----) "); + else + ret = trace_seq_printf(s, "(%5d) ", tgid); + if (!ret) + return 0; + } + + ret = trace_seq_printf(s, "[%03d] ", iter->cpu); if (!ret) return 0; @@ -966,6 +980,168 @@ static struct trace_event trace_fn_event = { .funcs = &trace_fn_funcs, }; +/* TRACE_GRAPH_ENT */ +static enum print_line_t trace_graph_ent_trace(struct trace_iterator *iter, int flags, + struct trace_event *event) +{ + struct trace_seq *s = &iter->seq; + struct ftrace_graph_ent_entry *field; + + trace_assign_type(field, iter->ent); + + if (!trace_seq_puts(s, "graph_ent: func=")) + return TRACE_TYPE_PARTIAL_LINE; + + if (!seq_print_ip_sym(s, field->graph_ent.func, flags)) + return TRACE_TYPE_PARTIAL_LINE; + + if (!trace_seq_puts(s, "\n")) + return TRACE_TYPE_PARTIAL_LINE; + + return TRACE_TYPE_HANDLED; +} + +static enum print_line_t trace_graph_ent_raw(struct trace_iterator *iter, int flags, + struct trace_event *event) +{ + struct ftrace_graph_ent_entry *field; + + trace_assign_type(field, iter->ent); + + if (!trace_seq_printf(&iter->seq, "%lx %d\n", + field->graph_ent.func, + field->graph_ent.depth)) + return TRACE_TYPE_PARTIAL_LINE; + + return TRACE_TYPE_HANDLED; +} + +static enum print_line_t trace_graph_ent_hex(struct trace_iterator *iter, int flags, + struct trace_event *event) +{ + struct ftrace_graph_ent_entry *field; + struct trace_seq *s = &iter->seq; + + trace_assign_type(field, iter->ent); + + SEQ_PUT_HEX_FIELD_RET(s, field->graph_ent.func); + SEQ_PUT_HEX_FIELD_RET(s, field->graph_ent.depth); + + return TRACE_TYPE_HANDLED; +} + +static enum print_line_t trace_graph_ent_bin(struct trace_iterator *iter, int flags, + struct trace_event *event) +{ + struct ftrace_graph_ent_entry *field; + struct trace_seq *s = &iter->seq; + + trace_assign_type(field, iter->ent); + + SEQ_PUT_FIELD_RET(s, field->graph_ent.func); + SEQ_PUT_FIELD_RET(s, field->graph_ent.depth); + + return TRACE_TYPE_HANDLED; +} + +static struct trace_event_functions trace_graph_ent_funcs = { + .trace = trace_graph_ent_trace, + .raw = trace_graph_ent_raw, + .hex = trace_graph_ent_hex, + .binary = trace_graph_ent_bin, +}; + +static struct trace_event trace_graph_ent_event = { + .type = TRACE_GRAPH_ENT, + .funcs = &trace_graph_ent_funcs, +}; + +/* TRACE_GRAPH_RET */ +static enum print_line_t trace_graph_ret_trace(struct trace_iterator *iter, int flags, + struct trace_event *event) +{ + struct trace_seq *s = &iter->seq; + struct trace_entry *entry = iter->ent; + struct ftrace_graph_ret_entry *field; + + trace_assign_type(field, entry); + + if (!trace_seq_puts(s, "graph_ret: func=")) + return TRACE_TYPE_PARTIAL_LINE; + + if (!seq_print_ip_sym(s, field->ret.func, flags)) + return TRACE_TYPE_PARTIAL_LINE; + + if (!trace_seq_puts(s, "\n")) + return TRACE_TYPE_PARTIAL_LINE; + + return TRACE_TYPE_HANDLED; +} + +static enum print_line_t trace_graph_ret_raw(struct trace_iterator *iter, int flags, + struct trace_event *event) +{ + struct ftrace_graph_ret_entry *field; + + trace_assign_type(field, iter->ent); + + if (!trace_seq_printf(&iter->seq, "%lx %lld %lld %ld %d\n", + field->ret.func, + field->ret.calltime, + field->ret.rettime, + field->ret.overrun, + field->ret.depth)); + return TRACE_TYPE_PARTIAL_LINE; + + return TRACE_TYPE_HANDLED; +} + +static enum print_line_t trace_graph_ret_hex(struct trace_iterator *iter, int flags, + struct trace_event *event) +{ + struct ftrace_graph_ret_entry *field; + struct trace_seq *s = &iter->seq; + + trace_assign_type(field, iter->ent); + + SEQ_PUT_HEX_FIELD_RET(s, field->ret.func); + SEQ_PUT_HEX_FIELD_RET(s, field->ret.calltime); + SEQ_PUT_HEX_FIELD_RET(s, field->ret.rettime); + SEQ_PUT_HEX_FIELD_RET(s, field->ret.overrun); + SEQ_PUT_HEX_FIELD_RET(s, field->ret.depth); + + return TRACE_TYPE_HANDLED; +} + +static enum print_line_t trace_graph_ret_bin(struct trace_iterator *iter, int flags, + struct trace_event *event) +{ + struct ftrace_graph_ret_entry *field; + struct trace_seq *s = &iter->seq; + + trace_assign_type(field, iter->ent); + + SEQ_PUT_FIELD_RET(s, field->ret.func); + SEQ_PUT_FIELD_RET(s, field->ret.calltime); + SEQ_PUT_FIELD_RET(s, field->ret.rettime); + SEQ_PUT_FIELD_RET(s, field->ret.overrun); + SEQ_PUT_FIELD_RET(s, field->ret.depth); + + return TRACE_TYPE_HANDLED; +} + +static struct trace_event_functions trace_graph_ret_funcs = { + .trace = trace_graph_ret_trace, + .raw = trace_graph_ret_raw, + .hex = trace_graph_ret_hex, + .binary = trace_graph_ret_bin, +}; + +static struct trace_event trace_graph_ret_event = { + .type = TRACE_GRAPH_RET, + .funcs = &trace_graph_ret_funcs, +}; + /* TRACE_CTX an TRACE_WAKE */ static enum print_line_t trace_ctxwake_print(struct trace_iterator *iter, char *delim) @@ -1298,6 +1474,8 @@ static struct trace_event trace_print_event = { static struct trace_event *events[] __initdata = { &trace_fn_event, + &trace_graph_ent_event, + &trace_graph_ret_event, &trace_ctx_event, &trace_wake_event, &trace_stack_event, diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 991aa938..19c81d8e 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -39,6 +39,8 @@ static DEFINE_PER_CPU(bool, hard_watchdog_warn); static DEFINE_PER_CPU(bool, watchdog_nmi_touch); static DEFINE_PER_CPU(unsigned long, hrtimer_interrupts); static DEFINE_PER_CPU(unsigned long, hrtimer_interrupts_saved); +#endif +#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI static DEFINE_PER_CPU(struct perf_event *, watchdog_ev); #endif @@ -174,7 +176,7 @@ void touch_softlockup_watchdog_sync(void) __raw_get_cpu_var(watchdog_touch_ts) = 0; } -#ifdef CONFIG_HARDLOCKUP_DETECTOR +#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI /* watchdog detector functions */ static int is_hardlockup(void) { @@ -188,6 +190,61 @@ static int is_hardlockup(void) } #endif +#ifdef CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU +static int is_hardlockup_other_cpu(int cpu) +{ + unsigned long hrint = per_cpu(hrtimer_interrupts, cpu); + + if (per_cpu(hrtimer_interrupts_saved, cpu) == hrint) + return 1; + + per_cpu(hrtimer_interrupts_saved, cpu) = hrint; + return 0; +} + +static void watchdog_check_hardlockup_other_cpu(void) +{ + int cpu; + + /* + * Test for hardlockups every 3 samples. The sample period is + * watchdog_thresh * 2 / 5, so 3 samples gets us back to slightly over + * watchdog_thresh (over by 20%). + */ + if (__this_cpu_read(hrtimer_interrupts) % 3 != 0) + return; + + /* check for a hardlockup on the next cpu */ + cpu = cpumask_next(smp_processor_id(), cpu_online_mask); + if (cpu >= nr_cpu_ids) + cpu = cpumask_first(cpu_online_mask); + if (cpu == smp_processor_id()) + return; + + if (per_cpu(watchdog_nmi_touch, cpu) == true) { + per_cpu(watchdog_nmi_touch, cpu) = false; + return; + } + + if (is_hardlockup_other_cpu(cpu)) { + /* only warn once */ + if (per_cpu(hard_watchdog_warn, cpu) == true) + return; + + if (hardlockup_panic) + panic("Watchdog detected hard LOCKUP on cpu %d", cpu); + else + WARN(1, "Watchdog detected hard LOCKUP on cpu %d", cpu); + + per_cpu(hard_watchdog_warn, cpu) = true; + } else { + per_cpu(hard_watchdog_warn, cpu) = false; + } +} +#else +static inline void watchdog_check_hardlockup_other_cpu(void) { return; } +#endif + static int is_softlockup(unsigned long touch_ts) { unsigned long now = get_timestamp(smp_processor_id()); @@ -199,7 +256,7 @@ static int is_softlockup(unsigned long touch_ts) return 0; } -#ifdef CONFIG_HARDLOCKUP_DETECTOR +#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI static struct perf_event_attr wd_hw_attr = { .type = PERF_TYPE_HARDWARE, @@ -247,6 +304,9 @@ static void watchdog_overflow_callback(struct perf_event *event, __this_cpu_write(hard_watchdog_warn, false); return; } +#endif + +#ifdef CONFIG_HARDLOCKUP_DETECTOR static void watchdog_interrupt_count(void) { __this_cpu_inc(hrtimer_interrupts); @@ -265,6 +325,9 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer) /* kick the hardlockup detector */ watchdog_interrupt_count(); + /* test for hardlockups on the next cpu */ + watchdog_check_hardlockup_other_cpu(); + /* kick the softlockup detector */ wake_up_process(__this_cpu_read(softlockup_watchdog)); @@ -359,7 +422,7 @@ static int watchdog(void *unused) } -#ifdef CONFIG_HARDLOCKUP_DETECTOR +#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI static int watchdog_nmi_enable(int cpu) { struct perf_event_attr *wd_attr; @@ -430,6 +493,18 @@ static void watchdog_prepare_cpu(int cpu) WARN_ON(per_cpu(softlockup_watchdog, cpu)); hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); hrtimer->function = watchdog_timer_fn; + +#ifdef CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU + /* + * The new cpu will be marked online before the first hrtimer interrupt + * runs on it. If another cpu tests for a hardlockup on the new cpu + * before it has run its first hrtimer, it will get a false positive. + * Touch the watchdog on the new cpu to delay the first check for at + * least 3 sampling periods to guarantee one hrtimer has run on the new + * cpu. + */ + per_cpu(watchdog_nmi_touch, cpu) = true; +#endif } static int watchdog_enable(int cpu) diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 6777153f..bbdea1d8 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -178,14 +178,24 @@ config LOCKUP_DETECTOR The overhead should be minimal. A periodic hrtimer runs to generate interrupts and kick the watchdog task every 4 seconds. An NMI is generated every 10 seconds or so to check for hardlockups. + If NMIs are not available on the platform, every 12 seconds the + hrtimer interrupt on one cpu will be used to check for hardlockups + on the next cpu. The frequency of hrtimer and NMI events and the soft and hard lockup thresholds can be controlled through the sysctl watchdog_thresh. -config HARDLOCKUP_DETECTOR +config HARDLOCKUP_DETECTOR_NMI def_bool LOCKUP_DETECTOR && PERF_EVENTS && HAVE_PERF_EVENTS_NMI && \ !HAVE_NMI_WATCHDOG +config HARDLOCKUP_DETECTOR_OTHER_CPU + def_bool LOCKUP_DETECTOR && SMP && !HARDLOCKUP_DETECTOR_NMI && \ + !HAVE_NMI_WATCHDOG + +config HARDLOCKUP_DETECTOR + def_bool HARDLOCKUP_DETECTOR_NMI || HARDLOCKUP_DETECTOR_OTHER_CPU + config BOOTPARAM_HARDLOCKUP_PANIC bool "Panic (Reboot) On Hard Lockups" depends on LOCKUP_DETECTOR @@ -676,8 +686,9 @@ config DEBUG_LOCKING_API_SELFTESTS mutexes and rwsems. config STACKTRACE - bool + bool "Stacktrace" depends on STACKTRACE_SUPPORT + default y config DEBUG_STACK_USAGE bool "Stack utilization instrumentation" diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 533ea80f..035fbe94 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -192,6 +192,7 @@ static char * const zone_names[MAX_NR_ZONES] = { }; int min_free_kbytes = 1024; +int min_free_order_shift = 1; static unsigned long __meminitdata nr_kernel_pages; static unsigned long __meminitdata nr_all_pages; @@ -1565,7 +1566,7 @@ static bool __zone_watermark_ok(struct zone *z, int order, unsigned long mark, free_pages -= z->free_area[o].nr_free << o; /* Require fewer higher order pages to be free */ - min >>= 1; + min >>= min_free_order_shift; if (free_pages <= min) return false; diff --git a/mm/shmem.c b/mm/shmem.c index 58c4a477..44895669 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2621,6 +2621,15 @@ put_memory: } EXPORT_SYMBOL_GPL(shmem_file_setup); +void shmem_set_file(struct vm_area_struct *vma, struct file *file) +{ + if (vma->vm_file) + fput(vma->vm_file); + vma->vm_file = file; + vma->vm_ops = &shmem_vm_ops; + vma->vm_flags |= VM_CAN_NONLINEAR; +} + /** * shmem_zero_setup - setup a shared anonymous mapping * @vma: the vma to be mmapped is prepared by do_mmap_pgoff @@ -2634,11 +2643,7 @@ int shmem_zero_setup(struct vm_area_struct *vma) if (IS_ERR(file)) return PTR_ERR(file); - if (vma->vm_file) - fput(vma->vm_file); - vma->vm_file = file; - vma->vm_ops = &shmem_vm_ops; - vma->vm_flags |= VM_CAN_NONLINEAR; + shmem_set_file(vma, file); return 0; } diff --git a/mm/vmscan.c b/mm/vmscan.c index e6ca5051..9b0c9bbd 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -201,6 +202,38 @@ static unsigned long zone_nr_lru_pages(struct mem_cgroup_zone *mz, return zone_page_state(mz->zone, NR_LRU_BASE + lru); } +struct dentry *debug_file; + +static int debug_shrinker_show(struct seq_file *s, void *unused) +{ + struct shrinker *shrinker; + struct shrink_control sc; + + sc.gfp_mask = -1; + sc.nr_to_scan = 0; + + down_read(&shrinker_rwsem); + list_for_each_entry(shrinker, &shrinker_list, list) { + int num_objs; + + num_objs = shrinker->shrink(shrinker, &sc); + seq_printf(s, "%pf %d\n", shrinker->shrink, num_objs); + } + up_read(&shrinker_rwsem); + return 0; +} + +static int debug_shrinker_open(struct inode *inode, struct file *file) +{ + return single_open(file, debug_shrinker_show, inode->i_private); +} + +static const struct file_operations debug_shrinker_fops = { + .open = debug_shrinker_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; /* * Add a shrinker callback to be called from the vm @@ -214,6 +247,15 @@ void register_shrinker(struct shrinker *shrinker) } EXPORT_SYMBOL(register_shrinker); +static int __init add_shrinker_debug(void) +{ + debugfs_create_file("shrinker", 0644, NULL, NULL, + &debug_shrinker_fops); + return 0; +} + +late_initcall(add_shrinker_debug); + /* * Remove one */ diff --git a/net/Kconfig b/net/Kconfig index e07272d0..c8ee300c 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -79,6 +79,20 @@ source "net/netlabel/Kconfig" endif # if INET +config ANDROID_PARANOID_NETWORK + bool "Only allow certain groups to create sockets" + default y + help + none + +config NET_ACTIVITY_STATS + bool "Network activity statistics tracking" + default y + help + Network activity statistics are useful for tracking wireless + modem activity on 2G, 3G, 4G wireless networks. Counts number of + transmissions and groups them in specified time buckets. + config NETWORK_SECMARK bool "Security Marking" help @@ -218,7 +232,7 @@ source "net/batman-adv/Kconfig" source "net/openvswitch/Kconfig" config RPS - boolean + boolean "RPS" depends on SMP && SYSFS && USE_GENERIC_SMP_HELPERS default y diff --git a/net/Makefile b/net/Makefile index ad432fa4..6865dab6 100644 --- a/net/Makefile +++ b/net/Makefile @@ -70,3 +70,4 @@ obj-$(CONFIG_CEPH_LIB) += ceph/ obj-$(CONFIG_BATMAN_ADV) += batman-adv/ obj-$(CONFIG_NFC) += nfc/ obj-$(CONFIG_OPENVSWITCH) += openvswitch/ +obj-$(CONFIG_NET_ACTIVITY_STATS) += activity_stats.o diff --git a/net/activity_stats.c b/net/activity_stats.c new file mode 100644 index 00000000..8a3e9347 --- /dev/null +++ b/net/activity_stats.c @@ -0,0 +1,115 @@ +/* net/activity_stats.c + * + * Copyright (C) 2010 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + * Author: Mike Chan (mike@android.com) + */ + +#include +#include +#include + +/* + * Track transmission rates in buckets (power of 2). + * 1,2,4,8...512 seconds. + * + * Buckets represent the count of network transmissions at least + * N seconds apart, where N is 1 << bucket index. + */ +#define BUCKET_MAX 10 + +/* Track network activity frequency */ +static unsigned long activity_stats[BUCKET_MAX]; +static ktime_t last_transmit; +static ktime_t suspend_time; +static DEFINE_SPINLOCK(activity_lock); + +void activity_stats_update(void) +{ + int i; + unsigned long flags; + ktime_t now; + s64 delta; + + spin_lock_irqsave(&activity_lock, flags); + now = ktime_get(); + delta = ktime_to_ns(ktime_sub(now, last_transmit)); + + for (i = BUCKET_MAX - 1; i >= 0; i--) { + /* + * Check if the time delta between network activity is within the + * minimum bucket range. + */ + if (delta < (1000000000ULL << i)) + continue; + + activity_stats[i]++; + last_transmit = now; + break; + } + spin_unlock_irqrestore(&activity_lock, flags); +} + +static int activity_stats_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int i; + int len; + char *p = page; + + /* Only print if offset is 0, or we have enough buffer space */ + if (off || count < (30 * BUCKET_MAX + 22)) + return -ENOMEM; + + len = snprintf(p, count, "Min Bucket(sec) Count\n"); + count -= len; + p += len; + + for (i = 0; i < BUCKET_MAX; i++) { + len = snprintf(p, count, "%15d %lu\n", 1 << i, activity_stats[i]); + count -= len; + p += len; + } + *eof = 1; + + return p - page; +} + +static int activity_stats_notifier(struct notifier_block *nb, + unsigned long event, void *dummy) +{ + switch (event) { + case PM_SUSPEND_PREPARE: + suspend_time = ktime_get_real(); + break; + + case PM_POST_SUSPEND: + suspend_time = ktime_sub(ktime_get_real(), suspend_time); + last_transmit = ktime_sub(last_transmit, suspend_time); + } + + return 0; +} + +static struct notifier_block activity_stats_notifier_block = { + .notifier_call = activity_stats_notifier, +}; + +static int __init activity_stats_init(void) +{ + create_proc_read_entry("activity", S_IRUGO, + init_net.proc_net_stat, activity_stats_read_proc, NULL); + return register_pm_notifier(&activity_stats_notifier_block); +} + +subsys_initcall(activity_stats_init); + diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 6fb68a97..f7fa8087 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -40,6 +40,11 @@ #include +#ifndef CONFIG_BT_SOCK_DEBUG +#undef BT_DBG +#define BT_DBG(D...) +#endif + #define VERSION "2.16" /* Bluetooth sockets */ @@ -122,11 +127,40 @@ int bt_sock_unregister(int proto) } EXPORT_SYMBOL(bt_sock_unregister); +#ifdef CONFIG_PARANOID_NETWORK +static inline int current_has_bt_admin(void) +{ + return !current_euid(); +} + +static inline int current_has_bt(void) +{ + return current_has_bt_admin(); +} +# else +static inline int current_has_bt_admin(void) +{ + return 1; +} + +static inline int current_has_bt(void) +{ + return 1; +} +#endif + static int bt_sock_create(struct net *net, struct socket *sock, int proto, int kern) { int err; + if (proto == BTPROTO_RFCOMM || proto == BTPROTO_SCO || + proto == BTPROTO_L2CAP) { + if (!current_has_bt()) + return -EPERM; + } else if (!current_has_bt_admin()) + return -EPERM; + if (net != &init_net) return -EAFNOSUPPORT; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 39b2baf6..0e8f89f9 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -361,7 +361,8 @@ static void hci_conn_auto_accept(unsigned long arg) &conn->dst); } -struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) +struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, + __u16 pkt_type, bdaddr_t *dst) { struct hci_conn *conn; @@ -389,14 +390,22 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK; break; case SCO_LINK: - if (lmp_esco_capable(hdev)) - conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) | - (hdev->esco_type & EDR_ESCO_MASK); - else - conn->pkt_type = hdev->pkt_type & SCO_PTYPE_MASK; - break; + if (!pkt_type) + pkt_type = SCO_ESCO_MASK; case ESCO_LINK: - conn->pkt_type = hdev->esco_type & ~EDR_ESCO_MASK; + if (!pkt_type) + pkt_type = ALL_ESCO_MASK; + if (lmp_esco_capable(hdev)) { + /* HCI Setup Synchronous Connection Command uses + reverse logic on the EDR_ESCO_MASK bits */ + conn->pkt_type = (pkt_type ^ EDR_ESCO_MASK) & + hdev->esco_type; + } else { + /* Legacy HCI Add Sco Connection Command uses a + shifted bitmask */ + conn->pkt_type = (pkt_type << 5) & hdev->pkt_type & + SCO_PTYPE_MASK; + } break; } @@ -514,7 +523,9 @@ EXPORT_SYMBOL(hci_get_route); /* Create SCO, ACL or LE connection. * Device _must_ be locked */ -struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type) +struct hci_conn *hci_connect(struct hci_dev *hdev, int type, + __u16 pkt_type, bdaddr_t *dst, + __u8 sec_level, __u8 auth_type) { struct hci_conn *acl; struct hci_conn *sco; @@ -533,7 +544,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 if (!entry) return ERR_PTR(-EHOSTUNREACH); - le = hci_conn_add(hdev, LE_LINK, dst); + le = hci_conn_add(hdev, LE_LINK, 0, dst); if (!le) return ERR_PTR(-ENOMEM); @@ -548,7 +559,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); if (!acl) { - acl = hci_conn_add(hdev, ACL_LINK, dst); + acl = hci_conn_add(hdev, ACL_LINK, 0, dst); if (!acl) return ERR_PTR(-ENOMEM); } @@ -567,7 +578,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sco = hci_conn_hash_lookup_ba(hdev, type, dst); if (!sco) { - sco = hci_conn_add(hdev, type, dst); + sco = hci_conn_add(hdev, type, pkt_type, dst); if (!sco) { hci_conn_put(acl); return ERR_PTR(-ENOMEM); @@ -874,6 +885,15 @@ int hci_get_conn_list(void __user *arg) (ci + n)->out = c->out; (ci + n)->state = c->state; (ci + n)->link_mode = c->link_mode; + if (c->type == SCO_LINK) { + (ci + n)->mtu = hdev->sco_mtu; + (ci + n)->cnt = hdev->sco_cnt; + (ci + n)->pkts = hdev->sco_pkts; + } else { + (ci + n)->mtu = hdev->acl_mtu; + (ci + n)->cnt = hdev->acl_cnt; + (ci + n)->pkts = hdev->acl_pkts; + } if (++n >= req.conn_num) break; } @@ -910,6 +930,15 @@ int hci_get_conn_info(struct hci_dev *hdev, void __user *arg) ci.out = conn->out; ci.state = conn->state; ci.link_mode = conn->link_mode; + if (req.type == SCO_LINK) { + ci.mtu = hdev->sco_mtu; + ci.cnt = hdev->sco_cnt; + ci.pkts = hdev->sco_pkts; + } else { + ci.mtu = hdev->acl_mtu; + ci.cnt = hdev->acl_cnt; + ci.pkts = hdev->acl_pkts; + } } hci_dev_unlock(hdev); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index ef45e10c..aba4fccd 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1213,7 +1213,7 @@ static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) } } else { if (!conn) { - conn = hci_conn_add(hdev, ACL_LINK, &cp->bdaddr); + conn = hci_conn_add(hdev, ACL_LINK, 0, &cp->bdaddr); if (conn) { conn->out = true; conn->link_mode |= HCI_LM_MASTER; @@ -1641,7 +1641,7 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status) } } else { if (!conn) { - conn = hci_conn_add(hdev, LE_LINK, &cp->peer_addr); + conn = hci_conn_add(hdev, LE_LINK, 0, &cp->peer_addr); if (conn) { conn->dst_type = cp->peer_addr_type; conn->out = true; @@ -1816,6 +1816,15 @@ unlock: hci_conn_check_pending(hdev); } +static inline bool is_sco_active(struct hci_dev *hdev) +{ + if (hci_conn_hash_lookup_state(hdev, SCO_LINK, BT_CONNECTED) || + (hci_conn_hash_lookup_state(hdev, ESCO_LINK, + BT_CONNECTED))) + return true; + return false; +} + static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_conn_request *ev = (void *) skb->data; @@ -1840,7 +1849,8 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr); if (!conn) { - conn = hci_conn_add(hdev, ev->link_type, &ev->bdaddr); + /* pkt_type not yet used for incoming connections */ + conn = hci_conn_add(hdev, ev->link_type, 0, &ev->bdaddr); if (!conn) { BT_ERR("No memory for new connection"); hci_dev_unlock(hdev); @@ -1858,7 +1868,8 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk bacpy(&cp.bdaddr, &ev->bdaddr); - if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER)) + if (lmp_rswitch_capable(hdev) && ((mask & HCI_LM_MASTER) + || is_sco_active(hdev))) cp.role = 0x00; /* Become master */ else cp.role = 0x01; /* Remain slave */ @@ -2945,6 +2956,7 @@ static inline void hci_sync_conn_complete_evt(struct hci_dev *hdev, struct sk_bu hci_conn_add_sysfs(conn); break; + case 0x10: /* Connection Accept Timeout */ case 0x11: /* Unsupported Feature or Parameter Value */ case 0x1c: /* SCO interval rejected */ case 0x1a: /* Unsupported Remote Feature */ @@ -3290,7 +3302,7 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &ev->bdaddr); if (!conn) { - conn = hci_conn_add(hdev, LE_LINK, &ev->bdaddr); + conn = hci_conn_add(hdev, LE_LINK, 0, &ev->bdaddr); if (!conn) { BT_ERR("No memory for new connection"); hci_dev_unlock(hdev); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index fa07aed3..e49f200c 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1220,10 +1220,10 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *d auth_type = l2cap_get_auth_type(chan); if (chan->dcid == L2CAP_CID_LE_DATA) - hcon = hci_connect(hdev, LE_LINK, dst, + hcon = hci_connect(hdev, LE_LINK, 0, dst, chan->sec_level, auth_type); else - hcon = hci_connect(hdev, ACL_LINK, dst, + hcon = hci_connect(hdev, ACL_LINK, 0, dst, chan->sec_level, auth_type); if (IS_ERR(hcon)) { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 8f3d9dc7..00879b82 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1891,11 +1891,11 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, auth_type = HCI_AT_DEDICATED_BONDING_MITM; if (cp->addr.type == MGMT_ADDR_BREDR) - conn = hci_connect(hdev, ACL_LINK, &cp->addr.bdaddr, sec_level, - auth_type); + conn = hci_connect(hdev, ACL_LINK, 0, &cp->addr.bdaddr, + sec_level, auth_type); else - conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr, sec_level, - auth_type); + conn = hci_connect(hdev, LE_LINK, 0, &cp->addr.bdaddr, + sec_level, auth_type); memset(&rp, 0, sizeof(rp)); bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 8a602388..9652707b 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -462,7 +462,6 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err) switch (d->state) { case BT_CONNECT: - case BT_CONFIG: if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) { set_bit(RFCOMM_AUTH_REJECT, &d->flags); rfcomm_schedule(); diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index f6ab1290..458cec07 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -177,6 +177,7 @@ static int sco_connect(struct sock *sk) { bdaddr_t *src = &bt_sk(sk)->src; bdaddr_t *dst = &bt_sk(sk)->dst; + __u16 pkt_type = sco_pi(sk)->pkt_type; struct sco_conn *conn; struct hci_conn *hcon; struct hci_dev *hdev; @@ -192,10 +193,12 @@ static int sco_connect(struct sock *sk) if (lmp_esco_capable(hdev) && !disable_esco) type = ESCO_LINK; - else + else { type = SCO_LINK; + pkt_type &= SCO_ESCO_MASK; + } - hcon = hci_connect(hdev, type, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING); + hcon = hci_connect(hdev, type, pkt_type, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING); if (IS_ERR(hcon)) { err = PTR_ERR(hcon); goto done; @@ -462,18 +465,22 @@ static int sco_sock_create(struct net *net, struct socket *sock, int protocol, return 0; } -static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) +static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) { - struct sockaddr_sco *sa = (struct sockaddr_sco *) addr; + struct sockaddr_sco sa; struct sock *sk = sock->sk; - bdaddr_t *src = &sa->sco_bdaddr; - int err = 0; + bdaddr_t *src = &sa.sco_bdaddr; + int len, err = 0; - BT_DBG("sk %p %s", sk, batostr(&sa->sco_bdaddr)); + BT_DBG("sk %p %s", sk, batostr(&sa.sco_bdaddr)); if (!addr || addr->sa_family != AF_BLUETOOTH) return -EINVAL; + memset(&sa, 0, sizeof(sa)); + len = min_t(unsigned int, sizeof(sa), alen); + memcpy(&sa, addr, len); + lock_sock(sk); if (sk->sk_state != BT_OPEN) { @@ -487,7 +494,8 @@ static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_le err = -EADDRINUSE; } else { /* Save source address */ - bacpy(&bt_sk(sk)->src, &sa->sco_bdaddr); + bacpy(&bt_sk(sk)->src, &sa.sco_bdaddr); + sco_pi(sk)->pkt_type = sa.sco_pkt_type; sk->sk_state = BT_BOUND; } @@ -500,27 +508,34 @@ done: static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) { - struct sockaddr_sco *sa = (struct sockaddr_sco *) addr; struct sock *sk = sock->sk; - int err = 0; - + struct sockaddr_sco sa; + int len, err = 0; BT_DBG("sk %p", sk); - if (alen < sizeof(struct sockaddr_sco) || - addr->sa_family != AF_BLUETOOTH) + if (!addr || addr->sa_family != AF_BLUETOOTH) return -EINVAL; - if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) - return -EBADFD; - - if (sk->sk_type != SOCK_SEQPACKET) - return -EINVAL; + memset(&sa, 0, sizeof(sa)); + len = min_t(unsigned int, sizeof(sa), alen); + memcpy(&sa, addr, len); lock_sock(sk); + if (sk->sk_type != SOCK_SEQPACKET) { + err = -EINVAL; + goto done; + } + + if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) { + err = -EBADFD; + goto done; + } + /* Set destination address and psm */ - bacpy(&bt_sk(sk)->dst, &sa->sco_bdaddr); + bacpy(&bt_sk(sk)->dst, &sa.sco_bdaddr); + sco_pi(sk)->pkt_type = sa.sco_pkt_type; err = sco_connect(sk); if (err) @@ -627,6 +642,7 @@ static int sco_sock_getname(struct socket *sock, struct sockaddr *addr, int *len bacpy(&sa->sco_bdaddr, &bt_sk(sk)->dst); else bacpy(&sa->sco_bdaddr, &bt_sk(sk)->src); + sa->sco_pkt_type = sco_pi(sk)->pkt_type; return 0; } diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index ba829de8..9ec0822f 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -38,16 +38,17 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) } #endif - u64_stats_update_begin(&brstats->syncp); - brstats->tx_packets++; - brstats->tx_bytes += skb->len; - u64_stats_update_end(&brstats->syncp); - BR_INPUT_SKB_CB(skb)->brdev = dev; skb_reset_mac_header(skb); skb_pull(skb, ETH_HLEN); + u64_stats_update_begin(&brstats->syncp); + brstats->tx_packets++; + /* Exclude ETH_HLEN from byte stats for consistency with Rx chain */ + brstats->tx_bytes += skb->len; + u64_stats_update_end(&brstats->syncp); + rcu_read_lock(); if (is_broadcast_ether_addr(dest)) br_flood_deliver(br, skb); diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index ff75d3bb..c6f177cf 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -14,6 +14,7 @@ obj-y := route.o inetpeer.o protocol.o \ inet_fragment.o ping.o obj-$(CONFIG_SYSCTL) += sysctl_net_ipv4.o +obj-$(CONFIG_SYSFS) += sysfs_net_ipv4.o obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_IP_MULTIPLE_TABLES) += fib_rules.o obj-$(CONFIG_IP_MROUTE) += ipmr.o diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 78ec2980..de5ec030 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -119,6 +119,19 @@ #include #endif +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +#include + +static inline int current_has_network(void) +{ + return in_egroup_p(AID_INET) || capable(CAP_NET_RAW); +} +#else +static inline int current_has_network(void) +{ + return 1; +} +#endif /* The inetsw table contains everything that inet_create needs to * build a new socket. @@ -264,6 +277,7 @@ static inline int inet_netns_ok(struct net *net, int protocol) return ipprot->netns_ok; } + /* * Create an inet socket. */ @@ -280,6 +294,9 @@ static int inet_create(struct net *net, struct socket *sock, int protocol, int try_loading_module = 0; int err; + if (!current_has_network()) + return -EACCES; + if (unlikely(!inet_ehash_secret)) if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM) build_ehash_secret(); @@ -886,6 +903,7 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) case SIOCSIFPFLAGS: case SIOCGIFPFLAGS: case SIOCSIFFLAGS: + case SIOCKILLADDR: err = devinet_ioctl(net, cmd, (void __user *)arg); break; default: diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 6e447ff9..8a9aab37 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -58,6 +58,7 @@ #include #include +#include #include #include #include @@ -734,6 +735,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) case SIOCSIFBRDADDR: /* Set the broadcast address */ case SIOCSIFDSTADDR: /* Set the destination address */ case SIOCSIFNETMASK: /* Set the netmask for the interface */ + case SIOCKILLADDR: /* Nuke all sockets on this address */ ret = -EACCES; if (!capable(CAP_NET_ADMIN)) goto out; @@ -785,7 +787,8 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) } ret = -EADDRNOTAVAIL; - if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) + if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS + && cmd != SIOCKILLADDR) goto done; switch (cmd) { @@ -911,6 +914,9 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) inet_insert_ifa(ifa); } break; + case SIOCKILLADDR: /* Nuke all connections on this address */ + ret = tcp_nuke_addr(net, (struct sockaddr *) sin); + break; } done: rtnl_unlock(); diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index fcc543cd..a1dce8a2 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig @@ -123,6 +123,18 @@ config IP_NF_TARGET_REJECT To compile it as a module, choose M here. If unsure, say N. +config IP_NF_TARGET_REJECT_SKERR + bool "Force socket error when rejecting with icmp*" + depends on IP_NF_TARGET_REJECT + default n + help + This option enables turning a "--reject-with icmp*" into a matching + socket error also. + The REJECT target normally allows sending an ICMP message. But it + leaves the local socket unaware of any ingress rejects. + + If unsure, say N. + config IP_NF_TARGET_ULOG tristate "ULOG target support" default m if NETFILTER_ADVANCED=n diff --git a/net/ipv4/netfilter/ipt_REJECT.c b/net/ipv4/netfilter/ipt_REJECT.c index 51f13f8e..9dd754c7 100644 --- a/net/ipv4/netfilter/ipt_REJECT.c +++ b/net/ipv4/netfilter/ipt_REJECT.c @@ -128,6 +128,14 @@ static void send_reset(struct sk_buff *oldskb, int hook) static inline void send_unreach(struct sk_buff *skb_in, int code) { icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0); +#ifdef CONFIG_IP_NF_TARGET_REJECT_SKERR + if (skb_in->sk) { + skb_in->sk->sk_err = icmp_err_convert[code].errno; + skb_in->sk->sk_error_report(skb_in->sk); + pr_debug("ipt_REJECT: sk_err=%d for skb=%p sk=%p\n", + skb_in->sk->sk_err, skb_in, skb_in->sk); + } +#endif } static unsigned int diff --git a/net/ipv4/sysfs_net_ipv4.c b/net/ipv4/sysfs_net_ipv4.c new file mode 100644 index 00000000..0cbbf100 --- /dev/null +++ b/net/ipv4/sysfs_net_ipv4.c @@ -0,0 +1,88 @@ +/* + * net/ipv4/sysfs_net_ipv4.c + * + * sysfs-based networking knobs (so we can, unlike with sysctl, control perms) + * + * Copyright (C) 2008 Google, Inc. + * + * Robert Love + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#define CREATE_IPV4_FILE(_name, _var) \ +static ssize_t _name##_show(struct kobject *kobj, \ + struct kobj_attribute *attr, char *buf) \ +{ \ + return sprintf(buf, "%d\n", _var); \ +} \ +static ssize_t _name##_store(struct kobject *kobj, \ + struct kobj_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + int val, ret; \ + ret = sscanf(buf, "%d", &val); \ + if (ret != 1) \ + return -EINVAL; \ + if (val < 0) \ + return -EINVAL; \ + _var = val; \ + return count; \ +} \ +static struct kobj_attribute _name##_attr = \ + __ATTR(_name, 0644, _name##_show, _name##_store) + +CREATE_IPV4_FILE(tcp_wmem_min, sysctl_tcp_wmem[0]); +CREATE_IPV4_FILE(tcp_wmem_def, sysctl_tcp_wmem[1]); +CREATE_IPV4_FILE(tcp_wmem_max, sysctl_tcp_wmem[2]); + +CREATE_IPV4_FILE(tcp_rmem_min, sysctl_tcp_rmem[0]); +CREATE_IPV4_FILE(tcp_rmem_def, sysctl_tcp_rmem[1]); +CREATE_IPV4_FILE(tcp_rmem_max, sysctl_tcp_rmem[2]); + +static struct attribute *ipv4_attrs[] = { + &tcp_wmem_min_attr.attr, + &tcp_wmem_def_attr.attr, + &tcp_wmem_max_attr.attr, + &tcp_rmem_min_attr.attr, + &tcp_rmem_def_attr.attr, + &tcp_rmem_max_attr.attr, + NULL +}; + +static struct attribute_group ipv4_attr_group = { + .attrs = ipv4_attrs, +}; + +static __init int sysfs_ipv4_init(void) +{ + struct kobject *ipv4_kobject; + int ret; + + ipv4_kobject = kobject_create_and_add("ipv4", kernel_kobj); + if (!ipv4_kobject) + return -ENOMEM; + + ret = sysfs_create_group(ipv4_kobject, &ipv4_attr_group); + if (ret) { + kobject_put(ipv4_kobject); + return ret; + } + + return 0; +} + +subsys_initcall(sysfs_ipv4_init); diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 01870bd2..8429ac5e 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -268,11 +268,15 @@ #include #include #include +#include #include #include #include #include +#include +#include +#include #include #include @@ -1115,6 +1119,9 @@ out: if (copied) tcp_push(sk, flags, mss_now, tp->nonagle); release_sock(sk); + + if (copied > 0) + uid_stat_tcp_snd(current_uid(), copied); return copied; do_fault: @@ -1389,8 +1396,11 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc, tcp_rcv_space_adjust(sk); /* Clean up data we have read: This will do ACK frames. */ - if (copied > 0) + if (copied > 0) { tcp_cleanup_rbuf(sk, copied); + uid_stat_tcp_rcv(current_uid(), copied); + } + return copied; } EXPORT_SYMBOL(tcp_read_sock); @@ -1779,6 +1789,9 @@ skip_copy: tcp_cleanup_rbuf(sk, copied); release_sock(sk); + + if (copied > 0) + uid_stat_tcp_rcv(current_uid(), copied); return copied; out: @@ -1787,6 +1800,8 @@ out: recv_urg: err = tcp_recv_urg(sk, msg, len, flags); + if (err > 0) + uid_stat_tcp_rcv(current_uid(), err); goto out; } EXPORT_SYMBOL(tcp_recvmsg); @@ -3336,3 +3351,107 @@ void __init tcp_init(void) tcp_secret_retiring = &tcp_secret_two; tcp_secret_secondary = &tcp_secret_two; } + +static int tcp_is_local(struct net *net, __be32 addr) { + struct rtable *rt; + struct flowi4 fl4 = { .daddr = addr }; + rt = ip_route_output_key(net, &fl4); + if (IS_ERR_OR_NULL(rt)) + return 0; + return rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK); +} + +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +static int tcp_is_local6(struct net *net, struct in6_addr *addr) { + struct rt6_info *rt6 = rt6_lookup(net, addr, addr, 0, 0); + return rt6 && rt6->dst.dev && (rt6->dst.dev->flags & IFF_LOOPBACK); +} +#endif + +/* + * tcp_nuke_addr - destroy all sockets on the given local address + * if local address is the unspecified address (0.0.0.0 or ::), destroy all + * sockets with local addresses that are not configured. + */ +int tcp_nuke_addr(struct net *net, struct sockaddr *addr) +{ + int family = addr->sa_family; + unsigned int bucket; + + struct in_addr *in; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + struct in6_addr *in6; +#endif + if (family == AF_INET) { + in = &((struct sockaddr_in *)addr)->sin_addr; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + } else if (family == AF_INET6) { + in6 = &((struct sockaddr_in6 *)addr)->sin6_addr; +#endif + } else { + return -EAFNOSUPPORT; + } + + for (bucket = 0; bucket < tcp_hashinfo.ehash_mask; bucket++) { + struct hlist_nulls_node *node; + struct sock *sk; + spinlock_t *lock = inet_ehash_lockp(&tcp_hashinfo, bucket); + +restart: + spin_lock_bh(lock); + sk_nulls_for_each(sk, node, &tcp_hashinfo.ehash[bucket].chain) { + struct inet_sock *inet = inet_sk(sk); + + if (sysctl_ip_dynaddr && sk->sk_state == TCP_SYN_SENT) + continue; + if (sock_flag(sk, SOCK_DEAD)) + continue; + + if (family == AF_INET) { + __be32 s4 = inet->inet_rcv_saddr; + if (s4 == LOOPBACK4_IPV6) + continue; + + if (in->s_addr != s4 && + !(in->s_addr == INADDR_ANY && + !tcp_is_local(net, s4))) + continue; + } + +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + if (family == AF_INET6) { + struct in6_addr *s6; + if (!inet->pinet6) + continue; + + s6 = &inet->pinet6->rcv_saddr; + if (ipv6_addr_type(s6) == IPV6_ADDR_MAPPED) + continue; + + if (!ipv6_addr_equal(in6, s6) && + !(ipv6_addr_equal(in6, &in6addr_any) && + !tcp_is_local6(net, s6))) + continue; + } +#endif + + sock_hold(sk); + spin_unlock_bh(lock); + + local_bh_disable(); + bh_lock_sock(sk); + sk->sk_err = ETIMEDOUT; + sk->sk_error_report(sk); + + tcp_done(sk); + bh_unlock_sock(sk); + local_bh_enable(); + sock_put(sk); + + goto restart; + } + spin_unlock_bh(lock); + } + + return 0; +} diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 8ed1b930..29625e9a 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -62,6 +62,20 @@ #include #include +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +#include + +static inline int current_has_network(void) +{ + return in_egroup_p(AID_INET) || capable(CAP_NET_RAW); +} +#else +static inline int current_has_network(void) +{ + return 1; +} +#endif + MODULE_AUTHOR("Cast of dozens"); MODULE_DESCRIPTION("IPv6 protocol stack for Linux"); MODULE_LICENSE("GPL"); @@ -108,6 +122,9 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol, int try_loading_module = 0; int err; + if (!current_has_network()) + return -EACCES; + if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM && !inet_ehash_secret) @@ -477,6 +494,21 @@ int inet6_getname(struct socket *sock, struct sockaddr *uaddr, EXPORT_SYMBOL(inet6_getname); +int inet6_killaddr_ioctl(struct net *net, void __user *arg) { + struct in6_ifreq ireq; + struct sockaddr_in6 sin6; + + if (!capable(CAP_NET_ADMIN)) + return -EACCES; + + if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq))) + return -EFAULT; + + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = ireq.ifr6_addr; + return tcp_nuke_addr(net, (struct sockaddr *) &sin6); +} + int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; @@ -501,6 +533,8 @@ int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) return addrconf_del_ifaddr(net, (void __user *) arg); case SIOCSIFDSTADDR: return addrconf_set_dstaddr(net, (void __user *) arg); + case SIOCKILLADDR: + return inet6_killaddr_ioctl(net, (void __user *) arg); default: if (!sk->sk_prot->ioctl) return -ENOIOCTLCMD; diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index d33cddd1..acd7c9e1 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig @@ -175,6 +175,18 @@ config IP6_NF_TARGET_REJECT To compile it as a module, choose M here. If unsure, say N. +config IP6_NF_TARGET_REJECT_SKERR + bool "Force socket error when rejecting with icmp*" + depends on IP6_NF_TARGET_REJECT + default n + help + This option enables turning a "--reject-with icmp*" into a matching + socket error also. + The REJECT target normally allows sending an ICMP message. But it + leaves the local socket unaware of any ingress rejects. + + If unsure, say N. + config IP6_NF_MANGLE tristate "Packet mangling" default m if NETFILTER_ADVANCED=n diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 9d4e1555..e641f8fa 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -2279,16 +2279,15 @@ static void __exit ip6_tables_fini(void) * "No next header". * * If target header is found, its offset is set in *offset and return protocol - * number. Otherwise, return -1. + * number. Otherwise, return -ENOENT or -EBADMSG. * * If the first fragment doesn't contain the final protocol header or * NEXTHDR_NONE it is considered invalid. * * Note that non-1st fragment is special case that "the protocol number * of last header" is "next header" field in Fragment header. In this case, - * *offset is meaningless and fragment offset is stored in *fragoff if fragoff - * isn't NULL. - * + * *offset is meaningless. If fragoff is not NULL, the fragment offset is + * stored in *fragoff; if it is NULL, return -EINVAL. */ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, int target, unsigned short *fragoff) @@ -2329,9 +2328,12 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, if (target < 0 && ((!ipv6_ext_hdr(hp->nexthdr)) || hp->nexthdr == NEXTHDR_NONE)) { - if (fragoff) + if (fragoff) { *fragoff = _frag_off; - return hp->nexthdr; + return hp->nexthdr; + } else { + return -EINVAL; + } } return -ENOENT; } diff --git a/net/ipv6/netfilter/ip6t_REJECT.c b/net/ipv6/netfilter/ip6t_REJECT.c index aad2fa41..09155e34 100644 --- a/net/ipv6/netfilter/ip6t_REJECT.c +++ b/net/ipv6/netfilter/ip6t_REJECT.c @@ -178,6 +178,15 @@ send_unreach(struct net *net, struct sk_buff *skb_in, unsigned char code, skb_in->dev = net->loopback_dev; icmpv6_send(skb_in, ICMPV6_DEST_UNREACH, code, 0); +#ifdef CONFIG_IP6_NF_TARGET_REJECT_SKERR + if (skb_in->sk) { + icmpv6_err_convert(ICMPV6_DEST_UNREACH, code, + &skb_in->sk->sk_err); + skb_in->sk->sk_error_report(skb_in->sk); + pr_debug("ip6t_REJECT: sk_err=%d for skb=%p sk=%p\n", + skb_in->sk->sk_err, skb_in, skb_in->sk); + } +#endif } static unsigned int diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 493490f0..5a272c65 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1973,7 +1973,8 @@ void rt6_purge_dflt_routers(struct net *net) restart: read_lock_bh(&table->tb6_lock); for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { - if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) { + if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) && + (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) { dst_hold(&rt->dst); read_unlock_bh(&table->tb6_lock); ip6_del_rt(rt); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index abc31d7b..d4112406 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3055,7 +3055,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_bss *bss = (void *)cbss->priv; - struct sta_info *sta; + struct sta_info *sta = NULL; bool have_sta = false; int err; diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 0c6f67e8..ce2976c0 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -967,6 +967,8 @@ config NETFILTER_XT_MATCH_OWNER based on who created the socket: the user or group. It is also possible to check whether a socket actually exists. + Conflicts with '"quota, tag, uid" match' + config NETFILTER_XT_MATCH_POLICY tristate 'IPsec "policy" match support' depends on XFRM @@ -1000,6 +1002,22 @@ config NETFILTER_XT_MATCH_PKTTYPE To compile it as a module, choose M here. If unsure, say N. +config NETFILTER_XT_MATCH_QTAGUID + bool '"quota, tag, owner" match and stats support' + depends on NETFILTER_XT_MATCH_SOCKET + depends on NETFILTER_XT_MATCH_OWNER=n + help + This option replaces the `owner' match. In addition to matching + on uid, it keeps stats based on a tag assigned to a socket. + The full tag is comprised of a UID and an accounting tag. + The tags are assignable to sockets from user space (e.g. a download + manager can assign the socket to another UID for accounting). + Stats and control are done via /proc/net/xt_qtaguid/. + It replaces owner as it takes the same arguments, but should + really be recognized by the iptables tool. + + If unsure, say `N'. + config NETFILTER_XT_MATCH_QUOTA tristate '"quota" match support' depends on NETFILTER_ADVANCED @@ -1010,6 +1028,30 @@ config NETFILTER_XT_MATCH_QUOTA If you want to compile it as a module, say M here and read . If unsure, say `N'. +config NETFILTER_XT_MATCH_QUOTA2 + tristate '"quota2" match support' + depends on NETFILTER_ADVANCED + help + This option adds a `quota2' match, which allows to match on a + byte counter correctly and not per CPU. + It allows naming the quotas. + This is based on http://xtables-addons.git.sourceforge.net + + If you want to compile it as a module, say M here and read + . If unsure, say `N'. + +config NETFILTER_XT_MATCH_QUOTA2_LOG + bool '"quota2" Netfilter LOG support' + depends on NETFILTER_XT_MATCH_QUOTA2 + depends on IP_NF_TARGET_ULOG=n # not yes, not module, just no + default n + help + This option allows `quota2' to log ONCE when a quota limit + is passed. It logs via NETLINK using the NETLINK_NFLOG family. + It logs similarly to how ipt_ULOG would without data. + + If unsure, say `N'. + config NETFILTER_XT_MATCH_RATEEST tristate '"rateest" match support' depends on NETFILTER_ADVANCED diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index ca367658..452e84de 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -101,7 +101,9 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_OWNER) += xt_owner.o obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o obj-$(CONFIG_NETFILTER_XT_MATCH_POLICY) += xt_policy.o +obj-$(CONFIG_NETFILTER_XT_MATCH_QTAGUID) += xt_qtaguid_print.o xt_qtaguid.o obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA) += xt_quota.o +obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA2) += xt_quota2.o obj-$(CONFIG_NETFILTER_XT_MATCH_RATEEST) += xt_rateest.o obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o obj-$(CONFIG_NETFILTER_XT_MATCH_RECENT) += xt_recent.o diff --git a/net/netfilter/xt_DSCP.c b/net/netfilter/xt_DSCP.c index ae827165..64670fc5 100644 --- a/net/netfilter/xt_DSCP.c +++ b/net/netfilter/xt_DSCP.c @@ -1,14 +1,11 @@ -/* x_tables module for setting the IPv4/IPv6 DSCP field, Version 1.8 +/* IP tables module for matching the value of the IPv4/IPv6 DSCP field * * (C) 2002 by Harald Welte - * based on ipt_FTOS.c (C) 2000 by Matthew G. Marsh * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * See RFC2474 for a description of the DSCP field within the IP Header. -*/ + */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include @@ -17,148 +14,102 @@ #include #include -#include +#include MODULE_AUTHOR("Harald Welte "); -MODULE_DESCRIPTION("Xtables: DSCP/TOS field modification"); +MODULE_DESCRIPTION("Xtables: DSCP/TOS field match"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("ipt_DSCP"); -MODULE_ALIAS("ip6t_DSCP"); -MODULE_ALIAS("ipt_TOS"); -MODULE_ALIAS("ip6t_TOS"); +MODULE_ALIAS("ipt_dscp"); +MODULE_ALIAS("ip6t_dscp"); +MODULE_ALIAS("ipt_tos"); +MODULE_ALIAS("ip6t_tos"); -static unsigned int -dscp_tg(struct sk_buff *skb, const struct xt_action_param *par) +static bool +dscp_mt(const struct sk_buff *skb, struct xt_action_param *par) { - const struct xt_DSCP_info *dinfo = par->targinfo; + const struct xt_dscp_info *info = par->matchinfo; u_int8_t dscp = ipv4_get_dsfield(ip_hdr(skb)) >> XT_DSCP_SHIFT; - if (dscp != dinfo->dscp) { - if (!skb_make_writable(skb, sizeof(struct iphdr))) - return NF_DROP; - - ipv4_change_dsfield(ip_hdr(skb), (__u8)(~XT_DSCP_MASK), - dinfo->dscp << XT_DSCP_SHIFT); - - } - return XT_CONTINUE; + return (dscp == info->dscp) ^ !!info->invert; } -static unsigned int -dscp_tg6(struct sk_buff *skb, const struct xt_action_param *par) +static bool +dscp_mt6(const struct sk_buff *skb, struct xt_action_param *par) { - const struct xt_DSCP_info *dinfo = par->targinfo; + const struct xt_dscp_info *info = par->matchinfo; u_int8_t dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> XT_DSCP_SHIFT; - if (dscp != dinfo->dscp) { - if (!skb_make_writable(skb, sizeof(struct ipv6hdr))) - return NF_DROP; - - ipv6_change_dsfield(ipv6_hdr(skb), (__u8)(~XT_DSCP_MASK), - dinfo->dscp << XT_DSCP_SHIFT); - } - return XT_CONTINUE; + return (dscp == info->dscp) ^ !!info->invert; } -static int dscp_tg_check(const struct xt_tgchk_param *par) +static int dscp_mt_check(const struct xt_mtchk_param *par) { - const struct xt_DSCP_info *info = par->targinfo; + const struct xt_dscp_info *info = par->matchinfo; if (info->dscp > XT_DSCP_MAX) { pr_info("dscp %x out of range\n", info->dscp); return -EDOM; } - return 0; -} - -static unsigned int -tos_tg(struct sk_buff *skb, const struct xt_action_param *par) -{ - const struct xt_tos_target_info *info = par->targinfo; - struct iphdr *iph = ip_hdr(skb); - u_int8_t orig, nv; - - orig = ipv4_get_dsfield(iph); - nv = (orig & ~info->tos_mask) ^ info->tos_value; - - if (orig != nv) { - if (!skb_make_writable(skb, sizeof(struct iphdr))) - return NF_DROP; - iph = ip_hdr(skb); - ipv4_change_dsfield(iph, 0, nv); - } - return XT_CONTINUE; + return 0; } -static unsigned int -tos_tg6(struct sk_buff *skb, const struct xt_action_param *par) +static bool tos_mt(const struct sk_buff *skb, struct xt_action_param *par) { - const struct xt_tos_target_info *info = par->targinfo; - struct ipv6hdr *iph = ipv6_hdr(skb); - u_int8_t orig, nv; - - orig = ipv6_get_dsfield(iph); - nv = (orig & ~info->tos_mask) ^ info->tos_value; - - if (orig != nv) { - if (!skb_make_writable(skb, sizeof(struct iphdr))) - return NF_DROP; - iph = ipv6_hdr(skb); - ipv6_change_dsfield(iph, 0, nv); - } - - return XT_CONTINUE; + const struct xt_tos_match_info *info = par->matchinfo; + + if (par->family == NFPROTO_IPV4) + return ((ip_hdr(skb)->tos & info->tos_mask) == + info->tos_value) ^ !!info->invert; + else + return ((ipv6_get_dsfield(ipv6_hdr(skb)) & info->tos_mask) == + info->tos_value) ^ !!info->invert; } -static struct xt_target dscp_tg_reg[] __read_mostly = { +static struct xt_match dscp_mt_reg[] __read_mostly = { { - .name = "DSCP", + .name = "dscp", .family = NFPROTO_IPV4, - .checkentry = dscp_tg_check, - .target = dscp_tg, - .targetsize = sizeof(struct xt_DSCP_info), - .table = "mangle", + .checkentry = dscp_mt_check, + .match = dscp_mt, + .matchsize = sizeof(struct xt_dscp_info), .me = THIS_MODULE, }, { - .name = "DSCP", + .name = "dscp", .family = NFPROTO_IPV6, - .checkentry = dscp_tg_check, - .target = dscp_tg6, - .targetsize = sizeof(struct xt_DSCP_info), - .table = "mangle", + .checkentry = dscp_mt_check, + .match = dscp_mt6, + .matchsize = sizeof(struct xt_dscp_info), .me = THIS_MODULE, }, { - .name = "TOS", + .name = "tos", .revision = 1, .family = NFPROTO_IPV4, - .table = "mangle", - .target = tos_tg, - .targetsize = sizeof(struct xt_tos_target_info), + .match = tos_mt, + .matchsize = sizeof(struct xt_tos_match_info), .me = THIS_MODULE, }, { - .name = "TOS", + .name = "tos", .revision = 1, .family = NFPROTO_IPV6, - .table = "mangle", - .target = tos_tg6, - .targetsize = sizeof(struct xt_tos_target_info), + .match = tos_mt, + .matchsize = sizeof(struct xt_tos_match_info), .me = THIS_MODULE, }, }; -static int __init dscp_tg_init(void) +static int __init dscp_mt_init(void) { - return xt_register_targets(dscp_tg_reg, ARRAY_SIZE(dscp_tg_reg)); + return xt_register_matches(dscp_mt_reg, ARRAY_SIZE(dscp_mt_reg)); } -static void __exit dscp_tg_exit(void) +static void __exit dscp_mt_exit(void) { - xt_unregister_targets(dscp_tg_reg, ARRAY_SIZE(dscp_tg_reg)); + xt_unregister_matches(dscp_mt_reg, ARRAY_SIZE(dscp_mt_reg)); } -module_init(dscp_tg_init); -module_exit(dscp_tg_exit); +module_init(dscp_mt_init); +module_exit(dscp_mt_exit); diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c index f407ebc1..f4ba8634 100644 --- a/net/netfilter/xt_IDLETIMER.c +++ b/net/netfilter/xt_IDLETIMER.c @@ -5,6 +5,7 @@ * After timer expires a kevent will be sent. * * Copyright (C) 2004, 2010 Nokia Corporation + * * Written by Timo Teras * * Converted to x_tables and reworked for upstream inclusion @@ -38,8 +39,10 @@ #include #include #include +#include #include #include +#include struct idletimer_tg_attr { struct attribute attr; @@ -56,6 +59,8 @@ struct idletimer_tg { struct idletimer_tg_attr attr; unsigned int refcnt; + bool send_nl_msg; + bool active; }; static LIST_HEAD(idletimer_tg_list); @@ -63,6 +68,32 @@ static DEFINE_MUTEX(list_mutex); static struct kobject *idletimer_tg_kobj; +static void notify_netlink_uevent(const char *label, struct idletimer_tg *timer) +{ + char label_msg[NLMSG_MAX_SIZE]; + char state_msg[NLMSG_MAX_SIZE]; + char *envp[] = { label_msg, state_msg, NULL }; + int res; + + res = snprintf(label_msg, NLMSG_MAX_SIZE, "LABEL=%s", + label); + if (NLMSG_MAX_SIZE <= res) { + pr_err("message too long (%d)", res); + return; + } + res = snprintf(state_msg, NLMSG_MAX_SIZE, "STATE=%s", + timer->active ? "active" : "inactive"); + if (NLMSG_MAX_SIZE <= res) { + pr_err("message too long (%d)", res); + return; + } + pr_debug("putting nlmsg: <%s> <%s>\n", label_msg, state_msg); + kobject_uevent_env(idletimer_tg_kobj, KOBJ_CHANGE, envp); + return; + + +} + static struct idletimer_tg *__idletimer_tg_find_by_label(const char *label) { @@ -83,6 +114,7 @@ static ssize_t idletimer_tg_show(struct kobject *kobj, struct attribute *attr, { struct idletimer_tg *timer; unsigned long expires = 0; + unsigned long now = jiffies; mutex_lock(&list_mutex); @@ -92,11 +124,15 @@ static ssize_t idletimer_tg_show(struct kobject *kobj, struct attribute *attr, mutex_unlock(&list_mutex); - if (time_after(expires, jiffies)) + if (time_after(expires, now)) return sprintf(buf, "%u\n", - jiffies_to_msecs(expires - jiffies) / 1000); + jiffies_to_msecs(expires - now) / 1000); - return sprintf(buf, "0\n"); + if (timer->send_nl_msg) + return sprintf(buf, "0 %d\n", + jiffies_to_msecs(now - expires) / 1000); + else + return sprintf(buf, "0\n"); } static void idletimer_tg_work(struct work_struct *work) @@ -105,6 +141,9 @@ static void idletimer_tg_work(struct work_struct *work) work); sysfs_notify(idletimer_tg_kobj, NULL, timer->attr.attr.name); + + if (timer->send_nl_msg) + notify_netlink_uevent(timer->attr.attr.name, timer); } static void idletimer_tg_expired(unsigned long data) @@ -113,6 +152,7 @@ static void idletimer_tg_expired(unsigned long data) pr_debug("timer %s expired\n", timer->attr.attr.name); + timer->active = false; schedule_work(&timer->work); } @@ -145,6 +185,8 @@ static int idletimer_tg_create(struct idletimer_tg_info *info) setup_timer(&info->timer->timer, idletimer_tg_expired, (unsigned long) info->timer); info->timer->refcnt = 1; + info->timer->send_nl_msg = (info->send_nl_msg == 0) ? false : true; + info->timer->active = true; mod_timer(&info->timer->timer, msecs_to_jiffies(info->timeout * 1000) + jiffies); @@ -168,14 +210,24 @@ static unsigned int idletimer_tg_target(struct sk_buff *skb, const struct xt_action_param *par) { const struct idletimer_tg_info *info = par->targinfo; + unsigned long now = jiffies; pr_debug("resetting timer %s, timeout period %u\n", info->label, info->timeout); BUG_ON(!info->timer); + info->timer->active = true; + + if (time_before(info->timer->timer.expires, now)) { + schedule_work(&info->timer->work); + pr_debug("Starting timer %s (Expired, Jiffies): %lu, %lu\n", + info->label, info->timer->timer.expires, now); + } + + /* TODO: Avoid modifying timers on each packet */ mod_timer(&info->timer->timer, - msecs_to_jiffies(info->timeout * 1000) + jiffies); + msecs_to_jiffies(info->timeout * 1000) + now); return XT_CONTINUE; } @@ -184,8 +236,9 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) { struct idletimer_tg_info *info = par->targinfo; int ret; + unsigned long now = jiffies; - pr_debug("checkentry targinfo%s\n", info->label); + pr_debug("checkentry targinfo %s\n", info->label); if (info->timeout == 0) { pr_debug("timeout value is zero\n"); @@ -204,8 +257,16 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) info->timer = __idletimer_tg_find_by_label(info->label); if (info->timer) { info->timer->refcnt++; + info->timer->active = true; + + if (time_before(info->timer->timer.expires, now)) { + schedule_work(&info->timer->work); + pr_debug("Starting Checkentry timer (Expired, Jiffies): %lu, %lu\n", + info->timer->timer.expires, now); + } + mod_timer(&info->timer->timer, - msecs_to_jiffies(info->timeout * 1000) + jiffies); + msecs_to_jiffies(info->timeout * 1000) + now); pr_debug("increased refcnt of timer %s to %u\n", info->label, info->timer->refcnt); @@ -219,6 +280,7 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) } mutex_unlock(&list_mutex); + return 0; } @@ -240,7 +302,7 @@ static void idletimer_tg_destroy(const struct xt_tgdtor_param *par) kfree(info->timer); } else { pr_debug("decreased refcnt of timer %s to %u\n", - info->label, info->timer->refcnt); + info->label, info->timer->refcnt); } mutex_unlock(&list_mutex); @@ -248,6 +310,7 @@ static void idletimer_tg_destroy(const struct xt_tgdtor_param *par) static struct xt_target idletimer_tg __read_mostly = { .name = "IDLETIMER", + .revision = 1, .family = NFPROTO_UNSPEC, .target = idletimer_tg_target, .targetsize = sizeof(struct idletimer_tg_info), @@ -313,3 +376,4 @@ MODULE_DESCRIPTION("Xtables: idle time monitor"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("ipt_IDLETIMER"); MODULE_ALIAS("ip6t_IDLETIMER"); +MODULE_ALIAS("arpt_IDLETIMER"); diff --git a/net/netfilter/xt_TCPMSS.c b/net/netfilter/xt_TCPMSS.c index 190ad37c..c53d4d18 100644 --- a/net/netfilter/xt_TCPMSS.c +++ b/net/netfilter/xt_TCPMSS.c @@ -1,320 +1,110 @@ -/* - * This is a module which is used for setting the MSS option in TCP packets. - * - * Copyright (C) 2000 Marc Boucher +/* Kernel module to match TCP MSS values. */ + +/* Copyright (C) 2000 Marc Boucher + * Portions (C) 2005 by Harald Welte * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include + #include #include -#include -#include -#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Marc Boucher "); -MODULE_DESCRIPTION("Xtables: TCP Maximum Segment Size (MSS) adjustment"); -MODULE_ALIAS("ipt_TCPMSS"); -MODULE_ALIAS("ip6t_TCPMSS"); +MODULE_DESCRIPTION("Xtables: TCP MSS match"); +MODULE_ALIAS("ipt_tcpmss"); +MODULE_ALIAS("ip6t_tcpmss"); -static inline unsigned int -optlen(const u_int8_t *opt, unsigned int offset) +static bool +tcpmss_mt(const struct sk_buff *skb, struct xt_action_param *par) { - /* Beware zero-length options: make finite progress */ - if (opt[offset] <= TCPOPT_NOP || opt[offset+1] == 0) - return 1; - else - return opt[offset+1]; -} - -static int -tcpmss_mangle_packet(struct sk_buff *skb, - const struct xt_tcpmss_info *info, - unsigned int in_mtu, - unsigned int tcphoff, - unsigned int minlen) -{ - struct tcphdr *tcph; - unsigned int tcplen, i; - __be16 oldval; - u16 newmss; - u8 *opt; - - if (!skb_make_writable(skb, skb->len)) - return -1; - - tcplen = skb->len - tcphoff; - tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff); - - /* Header cannot be larger than the packet */ - if (tcplen < tcph->doff*4) - return -1; - - if (info->mss == XT_TCPMSS_CLAMP_PMTU) { - if (dst_mtu(skb_dst(skb)) <= minlen) { - if (net_ratelimit()) - pr_err("unknown or invalid path-MTU (%u)\n", - dst_mtu(skb_dst(skb))); - return -1; - } - if (in_mtu <= minlen) { - if (net_ratelimit()) - pr_err("unknown or invalid path-MTU (%u)\n", - in_mtu); - return -1; - } - newmss = min(dst_mtu(skb_dst(skb)), in_mtu) - minlen; - } else - newmss = info->mss; - - opt = (u_int8_t *)tcph; - for (i = sizeof(struct tcphdr); i < tcph->doff*4; i += optlen(opt, i)) { - if (opt[i] == TCPOPT_MSS && tcph->doff*4 - i >= TCPOLEN_MSS && - opt[i+1] == TCPOLEN_MSS) { - u_int16_t oldmss; - - oldmss = (opt[i+2] << 8) | opt[i+3]; - - /* Never increase MSS, even when setting it, as - * doing so results in problems for hosts that rely - * on MSS being set correctly. - */ - if (oldmss <= newmss) - return 0; - - opt[i+2] = (newmss & 0xff00) >> 8; - opt[i+3] = newmss & 0x00ff; - - inet_proto_csum_replace2(&tcph->check, skb, - htons(oldmss), htons(newmss), - 0); - return 0; + const struct xt_tcpmss_match_info *info = par->matchinfo; + const struct tcphdr *th; + struct tcphdr _tcph; + /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */ + const u_int8_t *op; + u8 _opt[15 * 4 - sizeof(_tcph)]; + unsigned int i, optlen; + + /* If we don't have the whole header, drop packet. */ + th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph); + if (th == NULL) + goto dropit; + + /* Malformed. */ + if (th->doff*4 < sizeof(*th)) + goto dropit; + + optlen = th->doff*4 - sizeof(*th); + if (!optlen) + goto out; + + /* Truncated options. */ + op = skb_header_pointer(skb, par->thoff + sizeof(*th), optlen, _opt); + if (op == NULL) + goto dropit; + + for (i = 0; i < optlen; ) { + if (op[i] == TCPOPT_MSS + && (optlen - i) >= TCPOLEN_MSS + && op[i+1] == TCPOLEN_MSS) { + u_int16_t mssval; + + mssval = (op[i+2] << 8) | op[i+3]; + + return (mssval >= info->mss_min && + mssval <= info->mss_max) ^ info->invert; } + if (op[i] < 2) + i++; + else + i += op[i+1] ? : 1; } +out: + return info->invert; - /* There is data after the header so the option can't be added - without moving it, and doing so may make the SYN packet - itself too large. Accept the packet unmodified instead. */ - if (tcplen > tcph->doff*4) - return 0; - - /* - * MSS Option not found ?! add it.. - */ - if (skb_tailroom(skb) < TCPOLEN_MSS) { - if (pskb_expand_head(skb, 0, - TCPOLEN_MSS - skb_tailroom(skb), - GFP_ATOMIC)) - return -1; - tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff); - } - - skb_put(skb, TCPOLEN_MSS); - - opt = (u_int8_t *)tcph + sizeof(struct tcphdr); - memmove(opt + TCPOLEN_MSS, opt, tcplen - sizeof(struct tcphdr)); - - inet_proto_csum_replace2(&tcph->check, skb, - htons(tcplen), htons(tcplen + TCPOLEN_MSS), 1); - opt[0] = TCPOPT_MSS; - opt[1] = TCPOLEN_MSS; - opt[2] = (newmss & 0xff00) >> 8; - opt[3] = newmss & 0x00ff; - - inet_proto_csum_replace4(&tcph->check, skb, 0, *((__be32 *)opt), 0); - - oldval = ((__be16 *)tcph)[6]; - tcph->doff += TCPOLEN_MSS/4; - inet_proto_csum_replace2(&tcph->check, skb, - oldval, ((__be16 *)tcph)[6], 0); - return TCPOLEN_MSS; -} - -static u_int32_t tcpmss_reverse_mtu(const struct sk_buff *skb, - unsigned int family) -{ - struct flowi fl; - const struct nf_afinfo *ai; - struct rtable *rt = NULL; - u_int32_t mtu = ~0U; - - if (family == PF_INET) { - struct flowi4 *fl4 = &fl.u.ip4; - memset(fl4, 0, sizeof(*fl4)); - fl4->daddr = ip_hdr(skb)->saddr; - } else { - struct flowi6 *fl6 = &fl.u.ip6; - - memset(fl6, 0, sizeof(*fl6)); - fl6->daddr = ipv6_hdr(skb)->saddr; - } - rcu_read_lock(); - ai = nf_get_afinfo(family); - if (ai != NULL) - ai->route(&init_net, (struct dst_entry **)&rt, &fl, false); - rcu_read_unlock(); - - if (rt != NULL) { - mtu = dst_mtu(&rt->dst); - dst_release(&rt->dst); - } - return mtu; -} - -static unsigned int -tcpmss_tg4(struct sk_buff *skb, const struct xt_action_param *par) -{ - struct iphdr *iph = ip_hdr(skb); - __be16 newlen; - int ret; - - ret = tcpmss_mangle_packet(skb, par->targinfo, - tcpmss_reverse_mtu(skb, PF_INET), - iph->ihl * 4, - sizeof(*iph) + sizeof(struct tcphdr)); - if (ret < 0) - return NF_DROP; - if (ret > 0) { - iph = ip_hdr(skb); - newlen = htons(ntohs(iph->tot_len) + ret); - csum_replace2(&iph->check, iph->tot_len, newlen); - iph->tot_len = newlen; - } - return XT_CONTINUE; -} - -#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) -static unsigned int -tcpmss_tg6(struct sk_buff *skb, const struct xt_action_param *par) -{ - struct ipv6hdr *ipv6h = ipv6_hdr(skb); - u8 nexthdr; - __be16 frag_off; - int tcphoff; - int ret; - - nexthdr = ipv6h->nexthdr; - tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr, &frag_off); - if (tcphoff < 0) - return NF_DROP; - ret = tcpmss_mangle_packet(skb, par->targinfo, - tcpmss_reverse_mtu(skb, PF_INET6), - tcphoff, - sizeof(*ipv6h) + sizeof(struct tcphdr)); - if (ret < 0) - return NF_DROP; - if (ret > 0) { - ipv6h = ipv6_hdr(skb); - ipv6h->payload_len = htons(ntohs(ipv6h->payload_len) + ret); - } - return XT_CONTINUE; -} -#endif - -/* Must specify -p tcp --syn */ -static inline bool find_syn_match(const struct xt_entry_match *m) -{ - const struct xt_tcp *tcpinfo = (const struct xt_tcp *)m->data; - - if (strcmp(m->u.kernel.match->name, "tcp") == 0 && - tcpinfo->flg_cmp & TCPHDR_SYN && - !(tcpinfo->invflags & XT_TCP_INV_FLAGS)) - return true; - +dropit: + par->hotdrop = true; return false; } -static int tcpmss_tg4_check(const struct xt_tgchk_param *par) -{ - const struct xt_tcpmss_info *info = par->targinfo; - const struct ipt_entry *e = par->entryinfo; - const struct xt_entry_match *ematch; - - if (info->mss == XT_TCPMSS_CLAMP_PMTU && - (par->hook_mask & ~((1 << NF_INET_FORWARD) | - (1 << NF_INET_LOCAL_OUT) | - (1 << NF_INET_POST_ROUTING))) != 0) { - pr_info("path-MTU clamping only supported in " - "FORWARD, OUTPUT and POSTROUTING hooks\n"); - return -EINVAL; - } - xt_ematch_foreach(ematch, e) - if (find_syn_match(ematch)) - return 0; - pr_info("Only works on TCP SYN packets\n"); - return -EINVAL; -} - -#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) -static int tcpmss_tg6_check(const struct xt_tgchk_param *par) -{ - const struct xt_tcpmss_info *info = par->targinfo; - const struct ip6t_entry *e = par->entryinfo; - const struct xt_entry_match *ematch; - - if (info->mss == XT_TCPMSS_CLAMP_PMTU && - (par->hook_mask & ~((1 << NF_INET_FORWARD) | - (1 << NF_INET_LOCAL_OUT) | - (1 << NF_INET_POST_ROUTING))) != 0) { - pr_info("path-MTU clamping only supported in " - "FORWARD, OUTPUT and POSTROUTING hooks\n"); - return -EINVAL; - } - xt_ematch_foreach(ematch, e) - if (find_syn_match(ematch)) - return 0; - pr_info("Only works on TCP SYN packets\n"); - return -EINVAL; -} -#endif - -static struct xt_target tcpmss_tg_reg[] __read_mostly = { +static struct xt_match tcpmss_mt_reg[] __read_mostly = { { + .name = "tcpmss", .family = NFPROTO_IPV4, - .name = "TCPMSS", - .checkentry = tcpmss_tg4_check, - .target = tcpmss_tg4, - .targetsize = sizeof(struct xt_tcpmss_info), + .match = tcpmss_mt, + .matchsize = sizeof(struct xt_tcpmss_match_info), .proto = IPPROTO_TCP, .me = THIS_MODULE, }, -#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) { + .name = "tcpmss", .family = NFPROTO_IPV6, - .name = "TCPMSS", - .checkentry = tcpmss_tg6_check, - .target = tcpmss_tg6, - .targetsize = sizeof(struct xt_tcpmss_info), + .match = tcpmss_mt, + .matchsize = sizeof(struct xt_tcpmss_match_info), .proto = IPPROTO_TCP, .me = THIS_MODULE, }, -#endif }; -static int __init tcpmss_tg_init(void) +static int __init tcpmss_mt_init(void) { - return xt_register_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg)); + return xt_register_matches(tcpmss_mt_reg, ARRAY_SIZE(tcpmss_mt_reg)); } -static void __exit tcpmss_tg_exit(void) +static void __exit tcpmss_mt_exit(void) { - xt_unregister_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg)); + xt_unregister_matches(tcpmss_mt_reg, ARRAY_SIZE(tcpmss_mt_reg)); } -module_init(tcpmss_tg_init); -module_exit(tcpmss_tg_exit); +module_init(tcpmss_mt_init); +module_exit(tcpmss_mt_exit); diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c new file mode 100644 index 00000000..495b62ea --- /dev/null +++ b/net/netfilter/xt_qtaguid.c @@ -0,0 +1,2990 @@ +/* + * Kernel iptables module to track stats for packets based on user tags. + * + * (C) 2011 Google, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * There are run-time debug flags enabled via the debug_mask module param, or + * via the DEFAULT_DEBUG_MASK. See xt_qtaguid_internal.h. + */ +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) +#include +#endif + +#include +#include "xt_qtaguid_internal.h" +#include "xt_qtaguid_print.h" + +/* + * We only use the xt_socket funcs within a similar context to avoid unexpected + * return values. + */ +#define XT_SOCKET_SUPPORTED_HOOKS \ + ((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_IN)) + + +static const char *module_procdirname = "xt_qtaguid"; +static struct proc_dir_entry *xt_qtaguid_procdir; + +static unsigned int proc_iface_perms = S_IRUGO; +module_param_named(iface_perms, proc_iface_perms, uint, S_IRUGO | S_IWUSR); + +static struct proc_dir_entry *xt_qtaguid_stats_file; +static unsigned int proc_stats_perms = S_IRUGO; +module_param_named(stats_perms, proc_stats_perms, uint, S_IRUGO | S_IWUSR); + +static struct proc_dir_entry *xt_qtaguid_ctrl_file; + +/* Everybody can write. But proc_ctrl_write_limited is true by default which + * limits what can be controlled. See the can_*() functions. + */ +static unsigned int proc_ctrl_perms = S_IRUGO | S_IWUGO; +module_param_named(ctrl_perms, proc_ctrl_perms, uint, S_IRUGO | S_IWUSR); + +/* Limited by default, so the gid of the ctrl and stats proc entries + * will limit what can be done. See the can_*() functions. + */ +static bool proc_stats_readall_limited = true; +static bool proc_ctrl_write_limited = true; + +module_param_named(stats_readall_limited, proc_stats_readall_limited, bool, + S_IRUGO | S_IWUSR); +module_param_named(ctrl_write_limited, proc_ctrl_write_limited, bool, + S_IRUGO | S_IWUSR); + +/* + * Limit the number of active tags (via socket tags) for a given UID. + * Multiple processes could share the UID. + */ +static int max_sock_tags = DEFAULT_MAX_SOCK_TAGS; +module_param(max_sock_tags, int, S_IRUGO | S_IWUSR); + +/* + * After the kernel has initiallized this module, it is still possible + * to make it passive. + * Setting passive to Y: + * - the iface stats handling will not act on notifications. + * - iptables matches will never match. + * - ctrl commands silently succeed. + * - stats are always empty. + * This is mostly usefull when a bug is suspected. + */ +static bool module_passive; +module_param_named(passive, module_passive, bool, S_IRUGO | S_IWUSR); + +/* + * Control how qtaguid data is tracked per proc/uid. + * Setting tag_tracking_passive to Y: + * - don't create proc specific structs to track tags + * - don't check that active tag stats exceed some limits. + * - don't clean up socket tags on process exits. + * This is mostly usefull when a bug is suspected. + */ +static bool qtu_proc_handling_passive; +module_param_named(tag_tracking_passive, qtu_proc_handling_passive, bool, + S_IRUGO | S_IWUSR); + +#define QTU_DEV_NAME "xt_qtaguid" + +uint qtaguid_debug_mask = DEFAULT_DEBUG_MASK; +module_param_named(debug_mask, qtaguid_debug_mask, uint, S_IRUGO | S_IWUSR); + +/*---------------------------------------------------------------------------*/ +static const char *iface_stat_procdirname = "iface_stat"; +static struct proc_dir_entry *iface_stat_procdir; +/* + * The iface_stat_all* will go away once userspace gets use to the new fields + * that have a format line. + */ +static const char *iface_stat_all_procfilename = "iface_stat_all"; +static struct proc_dir_entry *iface_stat_all_procfile; +static const char *iface_stat_fmt_procfilename = "iface_stat_fmt"; +static struct proc_dir_entry *iface_stat_fmt_procfile; + + +/* + * Ordering of locks: + * outer locks: + * iface_stat_list_lock + * sock_tag_list_lock + * inner locks: + * uid_tag_data_tree_lock + * tag_counter_set_list_lock + * Notice how sock_tag_list_lock is held sometimes when uid_tag_data_tree_lock + * is acquired. + * + * Call tree with all lock holders as of 2012-04-27: + * + * iface_stat_fmt_proc_read() + * iface_stat_list_lock + * (struct iface_stat) + * + * qtaguid_ctrl_proc_read() + * sock_tag_list_lock + * (sock_tag_tree) + * (struct proc_qtu_data->sock_tag_list) + * prdebug_full_state() + * sock_tag_list_lock + * (sock_tag_tree) + * uid_tag_data_tree_lock + * (uid_tag_data_tree) + * (proc_qtu_data_tree) + * iface_stat_list_lock + * + * qtaguid_stats_proc_read() + * iface_stat_list_lock + * struct iface_stat->tag_stat_list_lock + * + * qtudev_open() + * uid_tag_data_tree_lock + * + * qtudev_release() + * sock_tag_data_list_lock + * uid_tag_data_tree_lock + * prdebug_full_state() + * sock_tag_list_lock + * uid_tag_data_tree_lock + * iface_stat_list_lock + * + * iface_netdev_event_handler() + * iface_stat_create() + * iface_stat_list_lock + * iface_stat_update() + * iface_stat_list_lock + * + * iface_inetaddr_event_handler() + * iface_stat_create() + * iface_stat_list_lock + * iface_stat_update() + * iface_stat_list_lock + * + * iface_inet6addr_event_handler() + * iface_stat_create_ipv6() + * iface_stat_list_lock + * iface_stat_update() + * iface_stat_list_lock + * + * qtaguid_mt() + * account_for_uid() + * if_tag_stat_update() + * get_sock_stat() + * sock_tag_list_lock + * struct iface_stat->tag_stat_list_lock + * tag_stat_update() + * get_active_counter_set() + * tag_counter_set_list_lock + * tag_stat_update() + * get_active_counter_set() + * tag_counter_set_list_lock + * + * + * qtaguid_ctrl_parse() + * ctrl_cmd_delete() + * sock_tag_list_lock + * tag_counter_set_list_lock + * iface_stat_list_lock + * struct iface_stat->tag_stat_list_lock + * uid_tag_data_tree_lock + * ctrl_cmd_counter_set() + * tag_counter_set_list_lock + * ctrl_cmd_tag() + * sock_tag_list_lock + * (sock_tag_tree) + * get_tag_ref() + * uid_tag_data_tree_lock + * (uid_tag_data_tree) + * uid_tag_data_tree_lock + * (proc_qtu_data_tree) + * ctrl_cmd_untag() + * sock_tag_list_lock + * uid_tag_data_tree_lock + * + */ +static LIST_HEAD(iface_stat_list); +static DEFINE_SPINLOCK(iface_stat_list_lock); + +static struct rb_root sock_tag_tree = RB_ROOT; +static DEFINE_SPINLOCK(sock_tag_list_lock); + +static struct rb_root tag_counter_set_tree = RB_ROOT; +static DEFINE_SPINLOCK(tag_counter_set_list_lock); + +static struct rb_root uid_tag_data_tree = RB_ROOT; +static DEFINE_SPINLOCK(uid_tag_data_tree_lock); + +static struct rb_root proc_qtu_data_tree = RB_ROOT; +/* No proc_qtu_data_tree_lock; use uid_tag_data_tree_lock */ + +static struct qtaguid_event_counts qtu_events; +/*----------------------------------------------*/ +static bool can_manipulate_uids(void) +{ + /* root pwnd */ + return in_egroup_p(xt_qtaguid_ctrl_file->gid) + || unlikely(!current_fsuid()) || unlikely(!proc_ctrl_write_limited) + || unlikely(current_fsuid() == xt_qtaguid_ctrl_file->uid); +} + +static bool can_impersonate_uid(uid_t uid) +{ + return uid == current_fsuid() || can_manipulate_uids(); +} + +static bool can_read_other_uid_stats(uid_t uid) +{ + /* root pwnd */ + return in_egroup_p(xt_qtaguid_stats_file->gid) + || unlikely(!current_fsuid()) || uid == current_fsuid() + || unlikely(!proc_stats_readall_limited) + || unlikely(current_fsuid() == xt_qtaguid_ctrl_file->uid); +} + +static inline void dc_add_byte_packets(struct data_counters *counters, int set, + enum ifs_tx_rx direction, + enum ifs_proto ifs_proto, + int bytes, + int packets) +{ + counters->bpc[set][direction][ifs_proto].bytes += bytes; + counters->bpc[set][direction][ifs_proto].packets += packets; +} + +static struct tag_node *tag_node_tree_search(struct rb_root *root, tag_t tag) +{ + struct rb_node *node = root->rb_node; + + while (node) { + struct tag_node *data = rb_entry(node, struct tag_node, node); + int result; + RB_DEBUG("qtaguid: tag_node_tree_search(0x%llx): " + " node=%p data=%p\n", tag, node, data); + result = tag_compare(tag, data->tag); + RB_DEBUG("qtaguid: tag_node_tree_search(0x%llx): " + " data.tag=0x%llx (uid=%u) res=%d\n", + tag, data->tag, get_uid_from_tag(data->tag), result); + if (result < 0) + node = node->rb_left; + else if (result > 0) + node = node->rb_right; + else + return data; + } + return NULL; +} + +static void tag_node_tree_insert(struct tag_node *data, struct rb_root *root) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + + /* Figure out where to put new node */ + while (*new) { + struct tag_node *this = rb_entry(*new, struct tag_node, + node); + int result = tag_compare(data->tag, this->tag); + RB_DEBUG("qtaguid: %s(): tag=0x%llx" + " (uid=%u)\n", __func__, + this->tag, + get_uid_from_tag(this->tag)); + parent = *new; + if (result < 0) + new = &((*new)->rb_left); + else if (result > 0) + new = &((*new)->rb_right); + else + BUG(); + } + + /* Add new node and rebalance tree. */ + rb_link_node(&data->node, parent, new); + rb_insert_color(&data->node, root); +} + +static void tag_stat_tree_insert(struct tag_stat *data, struct rb_root *root) +{ + tag_node_tree_insert(&data->tn, root); +} + +static struct tag_stat *tag_stat_tree_search(struct rb_root *root, tag_t tag) +{ + struct tag_node *node = tag_node_tree_search(root, tag); + if (!node) + return NULL; + return rb_entry(&node->node, struct tag_stat, tn.node); +} + +static void tag_counter_set_tree_insert(struct tag_counter_set *data, + struct rb_root *root) +{ + tag_node_tree_insert(&data->tn, root); +} + +static struct tag_counter_set *tag_counter_set_tree_search(struct rb_root *root, + tag_t tag) +{ + struct tag_node *node = tag_node_tree_search(root, tag); + if (!node) + return NULL; + return rb_entry(&node->node, struct tag_counter_set, tn.node); + +} + +static void tag_ref_tree_insert(struct tag_ref *data, struct rb_root *root) +{ + tag_node_tree_insert(&data->tn, root); +} + +static struct tag_ref *tag_ref_tree_search(struct rb_root *root, tag_t tag) +{ + struct tag_node *node = tag_node_tree_search(root, tag); + if (!node) + return NULL; + return rb_entry(&node->node, struct tag_ref, tn.node); +} + +static struct sock_tag *sock_tag_tree_search(struct rb_root *root, + const struct sock *sk) +{ + struct rb_node *node = root->rb_node; + + while (node) { + struct sock_tag *data = rb_entry(node, struct sock_tag, + sock_node); + if (sk < data->sk) + node = node->rb_left; + else if (sk > data->sk) + node = node->rb_right; + else + return data; + } + return NULL; +} + +static void sock_tag_tree_insert(struct sock_tag *data, struct rb_root *root) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + + /* Figure out where to put new node */ + while (*new) { + struct sock_tag *this = rb_entry(*new, struct sock_tag, + sock_node); + parent = *new; + if (data->sk < this->sk) + new = &((*new)->rb_left); + else if (data->sk > this->sk) + new = &((*new)->rb_right); + else + BUG(); + } + + /* Add new node and rebalance tree. */ + rb_link_node(&data->sock_node, parent, new); + rb_insert_color(&data->sock_node, root); +} + +static void sock_tag_tree_erase(struct rb_root *st_to_free_tree) +{ + struct rb_node *node; + struct sock_tag *st_entry; + + node = rb_first(st_to_free_tree); + while (node) { + st_entry = rb_entry(node, struct sock_tag, sock_node); + node = rb_next(node); + CT_DEBUG("qtaguid: %s(): " + "erase st: sk=%p tag=0x%llx (uid=%u)\n", __func__, + st_entry->sk, + st_entry->tag, + get_uid_from_tag(st_entry->tag)); + rb_erase(&st_entry->sock_node, st_to_free_tree); + sockfd_put(st_entry->socket); + kfree(st_entry); + } +} + +static struct proc_qtu_data *proc_qtu_data_tree_search(struct rb_root *root, + const pid_t pid) +{ + struct rb_node *node = root->rb_node; + + while (node) { + struct proc_qtu_data *data = rb_entry(node, + struct proc_qtu_data, + node); + if (pid < data->pid) + node = node->rb_left; + else if (pid > data->pid) + node = node->rb_right; + else + return data; + } + return NULL; +} + +static void proc_qtu_data_tree_insert(struct proc_qtu_data *data, + struct rb_root *root) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + + /* Figure out where to put new node */ + while (*new) { + struct proc_qtu_data *this = rb_entry(*new, + struct proc_qtu_data, + node); + parent = *new; + if (data->pid < this->pid) + new = &((*new)->rb_left); + else if (data->pid > this->pid) + new = &((*new)->rb_right); + else + BUG(); + } + + /* Add new node and rebalance tree. */ + rb_link_node(&data->node, parent, new); + rb_insert_color(&data->node, root); +} + +static void uid_tag_data_tree_insert(struct uid_tag_data *data, + struct rb_root *root) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + + /* Figure out where to put new node */ + while (*new) { + struct uid_tag_data *this = rb_entry(*new, + struct uid_tag_data, + node); + parent = *new; + if (data->uid < this->uid) + new = &((*new)->rb_left); + else if (data->uid > this->uid) + new = &((*new)->rb_right); + else + BUG(); + } + + /* Add new node and rebalance tree. */ + rb_link_node(&data->node, parent, new); + rb_insert_color(&data->node, root); +} + +static struct uid_tag_data *uid_tag_data_tree_search(struct rb_root *root, + uid_t uid) +{ + struct rb_node *node = root->rb_node; + + while (node) { + struct uid_tag_data *data = rb_entry(node, + struct uid_tag_data, + node); + if (uid < data->uid) + node = node->rb_left; + else if (uid > data->uid) + node = node->rb_right; + else + return data; + } + return NULL; +} + +/* + * Allocates a new uid_tag_data struct if needed. + * Returns a pointer to the found or allocated uid_tag_data. + * Returns a PTR_ERR on failures, and lock is not held. + * If found is not NULL: + * sets *found to true if not allocated. + * sets *found to false if allocated. + */ +struct uid_tag_data *get_uid_data(uid_t uid, bool *found_res) +{ + struct uid_tag_data *utd_entry; + + /* Look for top level uid_tag_data for the UID */ + utd_entry = uid_tag_data_tree_search(&uid_tag_data_tree, uid); + DR_DEBUG("qtaguid: get_uid_data(%u) utd=%p\n", uid, utd_entry); + + if (found_res) + *found_res = utd_entry; + if (utd_entry) + return utd_entry; + + utd_entry = kzalloc(sizeof(*utd_entry), GFP_ATOMIC); + if (!utd_entry) { + pr_err("qtaguid: get_uid_data(%u): " + "tag data alloc failed\n", uid); + return ERR_PTR(-ENOMEM); + } + + utd_entry->uid = uid; + utd_entry->tag_ref_tree = RB_ROOT; + uid_tag_data_tree_insert(utd_entry, &uid_tag_data_tree); + DR_DEBUG("qtaguid: get_uid_data(%u) new utd=%p\n", uid, utd_entry); + return utd_entry; +} + +/* Never returns NULL. Either PTR_ERR or a valid ptr. */ +static struct tag_ref *new_tag_ref(tag_t new_tag, + struct uid_tag_data *utd_entry) +{ + struct tag_ref *tr_entry; + int res; + + if (utd_entry->num_active_tags + 1 > max_sock_tags) { + pr_info("qtaguid: new_tag_ref(0x%llx): " + "tag ref alloc quota exceeded. max=%d\n", + new_tag, max_sock_tags); + res = -EMFILE; + goto err_res; + + } + + tr_entry = kzalloc(sizeof(*tr_entry), GFP_ATOMIC); + if (!tr_entry) { + pr_err("qtaguid: new_tag_ref(0x%llx): " + "tag ref alloc failed\n", + new_tag); + res = -ENOMEM; + goto err_res; + } + tr_entry->tn.tag = new_tag; + /* tr_entry->num_sock_tags handled by caller */ + utd_entry->num_active_tags++; + tag_ref_tree_insert(tr_entry, &utd_entry->tag_ref_tree); + DR_DEBUG("qtaguid: new_tag_ref(0x%llx): " + " inserted new tag ref %p\n", + new_tag, tr_entry); + return tr_entry; + +err_res: + return ERR_PTR(res); +} + +static struct tag_ref *lookup_tag_ref(tag_t full_tag, + struct uid_tag_data **utd_res) +{ + struct uid_tag_data *utd_entry; + struct tag_ref *tr_entry; + bool found_utd; + uid_t uid = get_uid_from_tag(full_tag); + + DR_DEBUG("qtaguid: lookup_tag_ref(tag=0x%llx (uid=%u))\n", + full_tag, uid); + + utd_entry = get_uid_data(uid, &found_utd); + if (IS_ERR_OR_NULL(utd_entry)) { + if (utd_res) + *utd_res = utd_entry; + return NULL; + } + + tr_entry = tag_ref_tree_search(&utd_entry->tag_ref_tree, full_tag); + if (utd_res) + *utd_res = utd_entry; + DR_DEBUG("qtaguid: lookup_tag_ref(0x%llx) utd_entry=%p tr_entry=%p\n", + full_tag, utd_entry, tr_entry); + return tr_entry; +} + +/* Never returns NULL. Either PTR_ERR or a valid ptr. */ +static struct tag_ref *get_tag_ref(tag_t full_tag, + struct uid_tag_data **utd_res) +{ + struct uid_tag_data *utd_entry; + struct tag_ref *tr_entry; + + DR_DEBUG("qtaguid: get_tag_ref(0x%llx)\n", + full_tag); + spin_lock_bh(&uid_tag_data_tree_lock); + tr_entry = lookup_tag_ref(full_tag, &utd_entry); + BUG_ON(IS_ERR_OR_NULL(utd_entry)); + if (!tr_entry) + tr_entry = new_tag_ref(full_tag, utd_entry); + + spin_unlock_bh(&uid_tag_data_tree_lock); + if (utd_res) + *utd_res = utd_entry; + DR_DEBUG("qtaguid: get_tag_ref(0x%llx) utd=%p tr=%p\n", + full_tag, utd_entry, tr_entry); + return tr_entry; +} + +/* Checks and maybe frees the UID Tag Data entry */ +static void put_utd_entry(struct uid_tag_data *utd_entry) +{ + /* Are we done with the UID tag data entry? */ + if (RB_EMPTY_ROOT(&utd_entry->tag_ref_tree) && + !utd_entry->num_pqd) { + DR_DEBUG("qtaguid: %s(): " + "erase utd_entry=%p uid=%u " + "by pid=%u tgid=%u uid=%u\n", __func__, + utd_entry, utd_entry->uid, + current->pid, current->tgid, current_fsuid()); + BUG_ON(utd_entry->num_active_tags); + rb_erase(&utd_entry->node, &uid_tag_data_tree); + kfree(utd_entry); + } else { + DR_DEBUG("qtaguid: %s(): " + "utd_entry=%p still has %d tags %d proc_qtu_data\n", + __func__, utd_entry, utd_entry->num_active_tags, + utd_entry->num_pqd); + BUG_ON(!(utd_entry->num_active_tags || + utd_entry->num_pqd)); + } +} + +/* + * If no sock_tags are using this tag_ref, + * decrements refcount of utd_entry, removes tr_entry + * from utd_entry->tag_ref_tree and frees. + */ +static void free_tag_ref_from_utd_entry(struct tag_ref *tr_entry, + struct uid_tag_data *utd_entry) +{ + DR_DEBUG("qtaguid: %s(): %p tag=0x%llx (uid=%u)\n", __func__, + tr_entry, tr_entry->tn.tag, + get_uid_from_tag(tr_entry->tn.tag)); + if (!tr_entry->num_sock_tags) { + BUG_ON(!utd_entry->num_active_tags); + utd_entry->num_active_tags--; + rb_erase(&tr_entry->tn.node, &utd_entry->tag_ref_tree); + DR_DEBUG("qtaguid: %s(): erased %p\n", __func__, tr_entry); + kfree(tr_entry); + } +} + +static void put_tag_ref_tree(tag_t full_tag, struct uid_tag_data *utd_entry) +{ + struct rb_node *node; + struct tag_ref *tr_entry; + tag_t acct_tag; + + DR_DEBUG("qtaguid: %s(tag=0x%llx (uid=%u))\n", __func__, + full_tag, get_uid_from_tag(full_tag)); + acct_tag = get_atag_from_tag(full_tag); + node = rb_first(&utd_entry->tag_ref_tree); + while (node) { + tr_entry = rb_entry(node, struct tag_ref, tn.node); + node = rb_next(node); + if (!acct_tag || tr_entry->tn.tag == full_tag) + free_tag_ref_from_utd_entry(tr_entry, utd_entry); + } +} + +static int read_proc_u64(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + uint64_t value; + char *p = page; + uint64_t *iface_entry = data; + + if (!data) + return 0; + + value = *iface_entry; + p += sprintf(p, "%llu\n", value); + len = (p - page) - off; + *eof = (len <= count) ? 1 : 0; + *start = page + off; + return len; +} + +static int read_proc_bool(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + bool value; + char *p = page; + bool *bool_entry = data; + + if (!data) + return 0; + + value = *bool_entry; + p += sprintf(p, "%u\n", value); + len = (p - page) - off; + *eof = (len <= count) ? 1 : 0; + *start = page + off; + return len; +} + +static int get_active_counter_set(tag_t tag) +{ + int active_set = 0; + struct tag_counter_set *tcs; + + MT_DEBUG("qtaguid: get_active_counter_set(tag=0x%llx)" + " (uid=%u)\n", + tag, get_uid_from_tag(tag)); + /* For now we only handle UID tags for active sets */ + tag = get_utag_from_tag(tag); + spin_lock_bh(&tag_counter_set_list_lock); + tcs = tag_counter_set_tree_search(&tag_counter_set_tree, tag); + if (tcs) + active_set = tcs->active_set; + spin_unlock_bh(&tag_counter_set_list_lock); + return active_set; +} + +/* + * Find the entry for tracking the specified interface. + * Caller must hold iface_stat_list_lock + */ +static struct iface_stat *get_iface_entry(const char *ifname) +{ + struct iface_stat *iface_entry; + + /* Find the entry for tracking the specified tag within the interface */ + if (ifname == NULL) { + pr_info("qtaguid: iface_stat: get() NULL device name\n"); + return NULL; + } + + /* Iterate over interfaces */ + list_for_each_entry(iface_entry, &iface_stat_list, list) { + if (!strcmp(ifname, iface_entry->ifname)) + goto done; + } + iface_entry = NULL; +done: + return iface_entry; +} + +/* This is for fmt2 only */ +static int pp_iface_stat_line(bool header, char *outp, + int char_count, struct iface_stat *iface_entry) +{ + int len; + if (header) { + len = snprintf(outp, char_count, + "ifname " + "total_skb_rx_bytes total_skb_rx_packets " + "total_skb_tx_bytes total_skb_tx_packets " + "rx_tcp_bytes rx_tcp_packets " + "rx_udp_bytes rx_udp_packets " + "rx_other_bytes rx_other_packets " + "tx_tcp_bytes tx_tcp_packets " + "tx_udp_bytes tx_udp_packets " + "tx_other_bytes tx_other_packets\n" + ); + } else { + struct data_counters *cnts; + int cnt_set = 0; /* We only use one set for the device */ + cnts = &iface_entry->totals_via_skb; + len = snprintf( + outp, char_count, + "%s " + "%llu %llu %llu %llu %llu %llu %llu %llu " + "%llu %llu %llu %llu %llu %llu %llu %llu\n", + iface_entry->ifname, + dc_sum_bytes(cnts, cnt_set, IFS_RX), + dc_sum_packets(cnts, cnt_set, IFS_RX), + dc_sum_bytes(cnts, cnt_set, IFS_TX), + dc_sum_packets(cnts, cnt_set, IFS_TX), + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); + } + return len; +} + +static int iface_stat_fmt_proc_read(char *page, char **num_items_returned, + off_t items_to_skip, int char_count, + int *eof, void *data) +{ + char *outp = page; + int item_index = 0; + int len; + int fmt = (int)data; /* The data is just 1 (old) or 2 (uses fmt) */ + struct iface_stat *iface_entry; + struct rtnl_link_stats64 dev_stats, *stats; + struct rtnl_link_stats64 no_dev_stats = {0}; + + if (unlikely(module_passive)) { + *eof = 1; + return 0; + } + + CT_DEBUG("qtaguid:proc iface_stat_fmt " + "pid=%u tgid=%u uid=%u " + "page=%p *num_items_returned=%p off=%ld " + "char_count=%d *eof=%d\n", + current->pid, current->tgid, current_fsuid(), + page, *num_items_returned, + items_to_skip, char_count, *eof); + + if (*eof) + return 0; + + if (fmt == 2 && item_index++ >= items_to_skip) { + len = pp_iface_stat_line(true, outp, char_count, NULL); + if (len >= char_count) { + *outp = '\0'; + return outp - page; + } + outp += len; + char_count -= len; + (*num_items_returned)++; + } + + /* + * This lock will prevent iface_stat_update() from changing active, + * and in turn prevent an interface from unregistering itself. + */ + spin_lock_bh(&iface_stat_list_lock); + list_for_each_entry(iface_entry, &iface_stat_list, list) { + if (item_index++ < items_to_skip) + continue; + + if (iface_entry->active) { + stats = dev_get_stats(iface_entry->net_dev, + &dev_stats); + } else { + stats = &no_dev_stats; + } + /* + * If the meaning of the data changes, then update the fmtX + * string. + */ + if (fmt == 1) { + len = snprintf( + outp, char_count, + "%s %d " + "%llu %llu %llu %llu " + "%llu %llu %llu %llu\n", + iface_entry->ifname, + iface_entry->active, + iface_entry->totals_via_dev[IFS_RX].bytes, + iface_entry->totals_via_dev[IFS_RX].packets, + iface_entry->totals_via_dev[IFS_TX].bytes, + iface_entry->totals_via_dev[IFS_TX].packets, + stats->rx_bytes, stats->rx_packets, + stats->tx_bytes, stats->tx_packets + ); + } else { + len = pp_iface_stat_line(false, outp, char_count, + iface_entry); + } + if (len >= char_count) { + spin_unlock_bh(&iface_stat_list_lock); + *outp = '\0'; + return outp - page; + } + outp += len; + char_count -= len; + (*num_items_returned)++; + } + spin_unlock_bh(&iface_stat_list_lock); + + *eof = 1; + return outp - page; +} + +static void iface_create_proc_worker(struct work_struct *work) +{ + struct proc_dir_entry *proc_entry; + struct iface_stat_work *isw = container_of(work, struct iface_stat_work, + iface_work); + struct iface_stat *new_iface = isw->iface_entry; + + /* iface_entries are not deleted, so safe to manipulate. */ + proc_entry = proc_mkdir(new_iface->ifname, iface_stat_procdir); + if (IS_ERR_OR_NULL(proc_entry)) { + pr_err("qtaguid: iface_stat: create_proc(): alloc failed.\n"); + kfree(isw); + return; + } + + new_iface->proc_ptr = proc_entry; + + create_proc_read_entry("tx_bytes", proc_iface_perms, proc_entry, + read_proc_u64, + &new_iface->totals_via_dev[IFS_TX].bytes); + create_proc_read_entry("rx_bytes", proc_iface_perms, proc_entry, + read_proc_u64, + &new_iface->totals_via_dev[IFS_RX].bytes); + create_proc_read_entry("tx_packets", proc_iface_perms, proc_entry, + read_proc_u64, + &new_iface->totals_via_dev[IFS_TX].packets); + create_proc_read_entry("rx_packets", proc_iface_perms, proc_entry, + read_proc_u64, + &new_iface->totals_via_dev[IFS_RX].packets); + create_proc_read_entry("active", proc_iface_perms, proc_entry, + read_proc_bool, &new_iface->active); + + IF_DEBUG("qtaguid: iface_stat: create_proc(): done " + "entry=%p dev=%s\n", new_iface, new_iface->ifname); + kfree(isw); +} + +/* + * Will set the entry's active state, and + * update the net_dev accordingly also. + */ +static void _iface_stat_set_active(struct iface_stat *entry, + struct net_device *net_dev, + bool activate) +{ + if (activate) { + entry->net_dev = net_dev; + entry->active = true; + IF_DEBUG("qtaguid: %s(%s): " + "enable tracking. rfcnt=%d\n", __func__, + entry->ifname, + percpu_read(*net_dev->pcpu_refcnt)); + } else { + entry->active = false; + entry->net_dev = NULL; + IF_DEBUG("qtaguid: %s(%s): " + "disable tracking. rfcnt=%d\n", __func__, + entry->ifname, + percpu_read(*net_dev->pcpu_refcnt)); + + } +} + +/* Caller must hold iface_stat_list_lock */ +static struct iface_stat *iface_alloc(struct net_device *net_dev) +{ + struct iface_stat *new_iface; + struct iface_stat_work *isw; + + new_iface = kzalloc(sizeof(*new_iface), GFP_ATOMIC); + if (new_iface == NULL) { + pr_err("qtaguid: iface_stat: create(%s): " + "iface_stat alloc failed\n", net_dev->name); + return NULL; + } + new_iface->ifname = kstrdup(net_dev->name, GFP_ATOMIC); + if (new_iface->ifname == NULL) { + pr_err("qtaguid: iface_stat: create(%s): " + "ifname alloc failed\n", net_dev->name); + kfree(new_iface); + return NULL; + } + spin_lock_init(&new_iface->tag_stat_list_lock); + new_iface->tag_stat_tree = RB_ROOT; + _iface_stat_set_active(new_iface, net_dev, true); + + /* + * ipv6 notifier chains are atomic :( + * No create_proc_read_entry() for you! + */ + isw = kmalloc(sizeof(*isw), GFP_ATOMIC); + if (!isw) { + pr_err("qtaguid: iface_stat: create(%s): " + "work alloc failed\n", new_iface->ifname); + _iface_stat_set_active(new_iface, net_dev, false); + kfree(new_iface->ifname); + kfree(new_iface); + return NULL; + } + isw->iface_entry = new_iface; + INIT_WORK(&isw->iface_work, iface_create_proc_worker); + schedule_work(&isw->iface_work); + list_add(&new_iface->list, &iface_stat_list); + return new_iface; +} + +static void iface_check_stats_reset_and_adjust(struct net_device *net_dev, + struct iface_stat *iface) +{ + struct rtnl_link_stats64 dev_stats, *stats; + bool stats_rewound; + + stats = dev_get_stats(net_dev, &dev_stats); + /* No empty packets */ + stats_rewound = + (stats->rx_bytes < iface->last_known[IFS_RX].bytes) + || (stats->tx_bytes < iface->last_known[IFS_TX].bytes); + + IF_DEBUG("qtaguid: %s(%s): iface=%p netdev=%p " + "bytes rx/tx=%llu/%llu " + "active=%d last_known=%d " + "stats_rewound=%d\n", __func__, + net_dev ? net_dev->name : "?", + iface, net_dev, + stats->rx_bytes, stats->tx_bytes, + iface->active, iface->last_known_valid, stats_rewound); + + if (iface->active && iface->last_known_valid && stats_rewound) { + pr_warn_once("qtaguid: iface_stat: %s(%s): " + "iface reset its stats unexpectedly\n", __func__, + net_dev->name); + + iface->totals_via_dev[IFS_TX].bytes += + iface->last_known[IFS_TX].bytes; + iface->totals_via_dev[IFS_TX].packets += + iface->last_known[IFS_TX].packets; + iface->totals_via_dev[IFS_RX].bytes += + iface->last_known[IFS_RX].bytes; + iface->totals_via_dev[IFS_RX].packets += + iface->last_known[IFS_RX].packets; + iface->last_known_valid = false; + IF_DEBUG("qtaguid: %s(%s): iface=%p " + "used last known bytes rx/tx=%llu/%llu\n", __func__, + iface->ifname, iface, iface->last_known[IFS_RX].bytes, + iface->last_known[IFS_TX].bytes); + } +} + +/* + * Create a new entry for tracking the specified interface. + * Do nothing if the entry already exists. + * Called when an interface is configured with a valid IP address. + */ +static void iface_stat_create(struct net_device *net_dev, + struct in_ifaddr *ifa) +{ + struct in_device *in_dev = NULL; + const char *ifname; + struct iface_stat *entry; + __be32 ipaddr = 0; + struct iface_stat *new_iface; + + IF_DEBUG("qtaguid: iface_stat: create(%s): ifa=%p netdev=%p\n", + net_dev ? net_dev->name : "?", + ifa, net_dev); + if (!net_dev) { + pr_err("qtaguid: iface_stat: create(): no net dev\n"); + return; + } + + ifname = net_dev->name; + if (!ifa) { + in_dev = in_dev_get(net_dev); + if (!in_dev) { + pr_err("qtaguid: iface_stat: create(%s): no inet dev\n", + ifname); + return; + } + IF_DEBUG("qtaguid: iface_stat: create(%s): in_dev=%p\n", + ifname, in_dev); + for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { + IF_DEBUG("qtaguid: iface_stat: create(%s): " + "ifa=%p ifa_label=%s\n", + ifname, ifa, + ifa->ifa_label ? ifa->ifa_label : "(null)"); + if (ifa->ifa_label && !strcmp(ifname, ifa->ifa_label)) + break; + } + } + + if (!ifa) { + IF_DEBUG("qtaguid: iface_stat: create(%s): no matching IP\n", + ifname); + goto done_put; + } + ipaddr = ifa->ifa_local; + + spin_lock_bh(&iface_stat_list_lock); + entry = get_iface_entry(ifname); + if (entry != NULL) { + IF_DEBUG("qtaguid: iface_stat: create(%s): entry=%p\n", + ifname, entry); + iface_check_stats_reset_and_adjust(net_dev, entry); + _iface_stat_set_active(entry, net_dev, true); + IF_DEBUG("qtaguid: %s(%s): " + "tracking now %d on ip=%pI4\n", __func__, + entry->ifname, true, &ipaddr); + goto done_unlock_put; + } + + new_iface = iface_alloc(net_dev); + IF_DEBUG("qtaguid: iface_stat: create(%s): done " + "entry=%p ip=%pI4\n", ifname, new_iface, &ipaddr); +done_unlock_put: + spin_unlock_bh(&iface_stat_list_lock); +done_put: + if (in_dev) + in_dev_put(in_dev); +} + +static void iface_stat_create_ipv6(struct net_device *net_dev, + struct inet6_ifaddr *ifa) +{ + struct in_device *in_dev; + const char *ifname; + struct iface_stat *entry; + struct iface_stat *new_iface; + int addr_type; + + IF_DEBUG("qtaguid: iface_stat: create6(): ifa=%p netdev=%p->name=%s\n", + ifa, net_dev, net_dev ? net_dev->name : ""); + if (!net_dev) { + pr_err("qtaguid: iface_stat: create6(): no net dev!\n"); + return; + } + ifname = net_dev->name; + + in_dev = in_dev_get(net_dev); + if (!in_dev) { + pr_err("qtaguid: iface_stat: create6(%s): no inet dev\n", + ifname); + return; + } + + IF_DEBUG("qtaguid: iface_stat: create6(%s): in_dev=%p\n", + ifname, in_dev); + + if (!ifa) { + IF_DEBUG("qtaguid: iface_stat: create6(%s): no matching IP\n", + ifname); + goto done_put; + } + addr_type = ipv6_addr_type(&ifa->addr); + + spin_lock_bh(&iface_stat_list_lock); + entry = get_iface_entry(ifname); + if (entry != NULL) { + IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, + ifname, entry); + iface_check_stats_reset_and_adjust(net_dev, entry); + _iface_stat_set_active(entry, net_dev, true); + IF_DEBUG("qtaguid: %s(%s): " + "tracking now %d on ip=%pI6c\n", __func__, + entry->ifname, true, &ifa->addr); + goto done_unlock_put; + } + + new_iface = iface_alloc(net_dev); + IF_DEBUG("qtaguid: iface_stat: create6(%s): done " + "entry=%p ip=%pI6c\n", ifname, new_iface, &ifa->addr); + +done_unlock_put: + spin_unlock_bh(&iface_stat_list_lock); +done_put: + in_dev_put(in_dev); +} + +static struct sock_tag *get_sock_stat_nl(const struct sock *sk) +{ + MT_DEBUG("qtaguid: get_sock_stat_nl(sk=%p)\n", sk); + return sock_tag_tree_search(&sock_tag_tree, sk); +} + +static struct sock_tag *get_sock_stat(const struct sock *sk) +{ + struct sock_tag *sock_tag_entry; + MT_DEBUG("qtaguid: get_sock_stat(sk=%p)\n", sk); + if (!sk) + return NULL; + spin_lock_bh(&sock_tag_list_lock); + sock_tag_entry = get_sock_stat_nl(sk); + spin_unlock_bh(&sock_tag_list_lock); + return sock_tag_entry; +} + +static int ipx_proto(const struct sk_buff *skb, + struct xt_action_param *par) +{ + int thoff, tproto; + + switch (par->family) { + case NFPROTO_IPV6: + tproto = ipv6_find_hdr(skb, &thoff, -1, NULL); + if (tproto < 0) + MT_DEBUG("%s(): transport header not found in ipv6" + " skb=%p\n", __func__, skb); + break; + case NFPROTO_IPV4: + tproto = ip_hdr(skb)->protocol; + break; + default: + tproto = IPPROTO_RAW; + } + return tproto; +} + +static void +data_counters_update(struct data_counters *dc, int set, + enum ifs_tx_rx direction, int proto, int bytes) +{ + switch (proto) { + case IPPROTO_TCP: + dc_add_byte_packets(dc, set, direction, IFS_TCP, bytes, 1); + break; + case IPPROTO_UDP: + dc_add_byte_packets(dc, set, direction, IFS_UDP, bytes, 1); + break; + case IPPROTO_IP: + default: + dc_add_byte_packets(dc, set, direction, IFS_PROTO_OTHER, bytes, + 1); + break; + } +} + +/* + * Update stats for the specified interface. Do nothing if the entry + * does not exist (when a device was never configured with an IP address). + * Called when an device is being unregistered. + */ +static void iface_stat_update(struct net_device *net_dev, bool stash_only) +{ + struct rtnl_link_stats64 dev_stats, *stats; + struct iface_stat *entry; + + stats = dev_get_stats(net_dev, &dev_stats); + spin_lock_bh(&iface_stat_list_lock); + entry = get_iface_entry(net_dev->name); + if (entry == NULL) { + IF_DEBUG("qtaguid: iface_stat: update(%s): not tracked\n", + net_dev->name); + spin_unlock_bh(&iface_stat_list_lock); + return; + } + + IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, + net_dev->name, entry); + if (!entry->active) { + IF_DEBUG("qtaguid: %s(%s): already disabled\n", __func__, + net_dev->name); + spin_unlock_bh(&iface_stat_list_lock); + return; + } + + if (stash_only) { + entry->last_known[IFS_TX].bytes = stats->tx_bytes; + entry->last_known[IFS_TX].packets = stats->tx_packets; + entry->last_known[IFS_RX].bytes = stats->rx_bytes; + entry->last_known[IFS_RX].packets = stats->rx_packets; + entry->last_known_valid = true; + IF_DEBUG("qtaguid: %s(%s): " + "dev stats stashed rx/tx=%llu/%llu\n", __func__, + net_dev->name, stats->rx_bytes, stats->tx_bytes); + spin_unlock_bh(&iface_stat_list_lock); + return; + } + entry->totals_via_dev[IFS_TX].bytes += stats->tx_bytes; + entry->totals_via_dev[IFS_TX].packets += stats->tx_packets; + entry->totals_via_dev[IFS_RX].bytes += stats->rx_bytes; + entry->totals_via_dev[IFS_RX].packets += stats->rx_packets; + /* We don't need the last_known[] anymore */ + entry->last_known_valid = false; + _iface_stat_set_active(entry, net_dev, false); + IF_DEBUG("qtaguid: %s(%s): " + "disable tracking. rx/tx=%llu/%llu\n", __func__, + net_dev->name, stats->rx_bytes, stats->tx_bytes); + spin_unlock_bh(&iface_stat_list_lock); +} + +/* + * Update stats for the specified interface from the skb. + * Do nothing if the entry + * does not exist (when a device was never configured with an IP address). + * Called on each sk. + */ +static void iface_stat_update_from_skb(const struct sk_buff *skb, + struct xt_action_param *par) +{ + struct iface_stat *entry; + const struct net_device *el_dev; + enum ifs_tx_rx direction = par->in ? IFS_RX : IFS_TX; + int bytes = skb->len; + int proto; + + if (!skb->dev) { + MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum); + el_dev = par->in ? : par->out; + } else { + const struct net_device *other_dev; + el_dev = skb->dev; + other_dev = par->in ? : par->out; + if (el_dev != other_dev) { + MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs " + "par->(in/out)=%p %s\n", + par->hooknum, el_dev, el_dev->name, other_dev, + other_dev->name); + } + } + + if (unlikely(!el_dev)) { + pr_err("qtaguid[%d]: %s(): no par->in/out?!!\n", + par->hooknum, __func__); + BUG(); + } else if (unlikely(!el_dev->name)) { + pr_err("qtaguid[%d]: %s(): no dev->name?!!\n", + par->hooknum, __func__); + BUG(); + } else { + proto = ipx_proto(skb, par); + MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", + par->hooknum, el_dev->name, el_dev->type, + par->family, proto); + } + + spin_lock_bh(&iface_stat_list_lock); + entry = get_iface_entry(el_dev->name); + if (entry == NULL) { + IF_DEBUG("qtaguid: iface_stat: %s(%s): not tracked\n", + __func__, el_dev->name); + spin_unlock_bh(&iface_stat_list_lock); + return; + } + + IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, + el_dev->name, entry); + + data_counters_update(&entry->totals_via_skb, 0, direction, proto, + bytes); + spin_unlock_bh(&iface_stat_list_lock); +} + +static void tag_stat_update(struct tag_stat *tag_entry, + enum ifs_tx_rx direction, int proto, int bytes) +{ + int active_set; + active_set = get_active_counter_set(tag_entry->tn.tag); + MT_DEBUG("qtaguid: tag_stat_update(tag=0x%llx (uid=%u) set=%d " + "dir=%d proto=%d bytes=%d)\n", + tag_entry->tn.tag, get_uid_from_tag(tag_entry->tn.tag), + active_set, direction, proto, bytes); + data_counters_update(&tag_entry->counters, active_set, direction, + proto, bytes); + if (tag_entry->parent_counters) + data_counters_update(tag_entry->parent_counters, active_set, + direction, proto, bytes); +} + +/* + * Create a new entry for tracking the specified {acct_tag,uid_tag} within + * the interface. + * iface_entry->tag_stat_list_lock should be held. + */ +static struct tag_stat *create_if_tag_stat(struct iface_stat *iface_entry, + tag_t tag) +{ + struct tag_stat *new_tag_stat_entry = NULL; + IF_DEBUG("qtaguid: iface_stat: %s(): ife=%p tag=0x%llx" + " (uid=%u)\n", __func__, + iface_entry, tag, get_uid_from_tag(tag)); + new_tag_stat_entry = kzalloc(sizeof(*new_tag_stat_entry), GFP_ATOMIC); + if (!new_tag_stat_entry) { + pr_err("qtaguid: iface_stat: tag stat alloc failed\n"); + goto done; + } + new_tag_stat_entry->tn.tag = tag; + tag_stat_tree_insert(new_tag_stat_entry, &iface_entry->tag_stat_tree); +done: + return new_tag_stat_entry; +} + +static void if_tag_stat_update(const char *ifname, uid_t uid, + const struct sock *sk, enum ifs_tx_rx direction, + int proto, int bytes) +{ + struct tag_stat *tag_stat_entry; + tag_t tag, acct_tag; + tag_t uid_tag; + struct data_counters *uid_tag_counters; + struct sock_tag *sock_tag_entry; + struct iface_stat *iface_entry; + struct tag_stat *new_tag_stat = NULL; + MT_DEBUG("qtaguid: if_tag_stat_update(ifname=%s " + "uid=%u sk=%p dir=%d proto=%d bytes=%d)\n", + ifname, uid, sk, direction, proto, bytes); + + + iface_entry = get_iface_entry(ifname); + if (!iface_entry) { + pr_err("qtaguid: iface_stat: stat_update() %s not found\n", + ifname); + return; + } + /* It is ok to process data when an iface_entry is inactive */ + + MT_DEBUG("qtaguid: iface_stat: stat_update() dev=%s entry=%p\n", + ifname, iface_entry); + + /* + * Look for a tagged sock. + * It will have an acct_uid. + */ + sock_tag_entry = get_sock_stat(sk); + if (sock_tag_entry) { + tag = sock_tag_entry->tag; + acct_tag = get_atag_from_tag(tag); + uid_tag = get_utag_from_tag(tag); + } else { + acct_tag = make_atag_from_value(0); + tag = combine_atag_with_uid(acct_tag, uid); + uid_tag = make_tag_from_uid(uid); + } + MT_DEBUG("qtaguid: iface_stat: stat_update(): " + " looking for tag=0x%llx (uid=%u) in ife=%p\n", + tag, get_uid_from_tag(tag), iface_entry); + /* Loop over tag list under this interface for {acct_tag,uid_tag} */ + spin_lock_bh(&iface_entry->tag_stat_list_lock); + + tag_stat_entry = tag_stat_tree_search(&iface_entry->tag_stat_tree, + tag); + if (tag_stat_entry) { + /* + * Updating the {acct_tag, uid_tag} entry handles both stats: + * {0, uid_tag} will also get updated. + */ + tag_stat_update(tag_stat_entry, direction, proto, bytes); + spin_unlock_bh(&iface_entry->tag_stat_list_lock); + return; + } + + /* Loop over tag list under this interface for {0,uid_tag} */ + tag_stat_entry = tag_stat_tree_search(&iface_entry->tag_stat_tree, + uid_tag); + if (!tag_stat_entry) { + /* Here: the base uid_tag did not exist */ + /* + * No parent counters. So + * - No {0, uid_tag} stats and no {acc_tag, uid_tag} stats. + */ + new_tag_stat = create_if_tag_stat(iface_entry, uid_tag); + if (!new_tag_stat) + goto unlock; + uid_tag_counters = &new_tag_stat->counters; + } else { + uid_tag_counters = &tag_stat_entry->counters; + } + + if (acct_tag) { + /* Create the child {acct_tag, uid_tag} and hook up parent. */ + new_tag_stat = create_if_tag_stat(iface_entry, tag); + if (!new_tag_stat) + goto unlock; + new_tag_stat->parent_counters = uid_tag_counters; + } else { + /* + * For new_tag_stat to be still NULL here would require: + * {0, uid_tag} exists + * and {acct_tag, uid_tag} doesn't exist + * AND acct_tag == 0. + * Impossible. This reassures us that new_tag_stat + * below will always be assigned. + */ + BUG_ON(!new_tag_stat); + } + tag_stat_update(new_tag_stat, direction, proto, bytes); +unlock: + spin_unlock_bh(&iface_entry->tag_stat_list_lock); +} + +static int iface_netdev_event_handler(struct notifier_block *nb, + unsigned long event, void *ptr) { + struct net_device *dev = ptr; + + if (unlikely(module_passive)) + return NOTIFY_DONE; + + IF_DEBUG("qtaguid: iface_stat: netdev_event(): " + "ev=0x%lx/%s netdev=%p->name=%s\n", + event, netdev_evt_str(event), dev, dev ? dev->name : ""); + + switch (event) { + case NETDEV_UP: + iface_stat_create(dev, NULL); + atomic64_inc(&qtu_events.iface_events); + break; + case NETDEV_DOWN: + case NETDEV_UNREGISTER: + iface_stat_update(dev, event == NETDEV_DOWN); + atomic64_inc(&qtu_events.iface_events); + break; + } + return NOTIFY_DONE; +} + +static int iface_inet6addr_event_handler(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct inet6_ifaddr *ifa = ptr; + struct net_device *dev; + + if (unlikely(module_passive)) + return NOTIFY_DONE; + + IF_DEBUG("qtaguid: iface_stat: inet6addr_event(): " + "ev=0x%lx/%s ifa=%p\n", + event, netdev_evt_str(event), ifa); + + switch (event) { + case NETDEV_UP: + BUG_ON(!ifa || !ifa->idev); + dev = (struct net_device *)ifa->idev->dev; + iface_stat_create_ipv6(dev, ifa); + atomic64_inc(&qtu_events.iface_events); + break; + case NETDEV_DOWN: + case NETDEV_UNREGISTER: + BUG_ON(!ifa || !ifa->idev); + dev = (struct net_device *)ifa->idev->dev; + iface_stat_update(dev, event == NETDEV_DOWN); + atomic64_inc(&qtu_events.iface_events); + break; + } + return NOTIFY_DONE; +} + +static int iface_inetaddr_event_handler(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct in_ifaddr *ifa = ptr; + struct net_device *dev; + + if (unlikely(module_passive)) + return NOTIFY_DONE; + + IF_DEBUG("qtaguid: iface_stat: inetaddr_event(): " + "ev=0x%lx/%s ifa=%p\n", + event, netdev_evt_str(event), ifa); + + switch (event) { + case NETDEV_UP: + BUG_ON(!ifa || !ifa->ifa_dev); + dev = ifa->ifa_dev->dev; + iface_stat_create(dev, ifa); + atomic64_inc(&qtu_events.iface_events); + break; + case NETDEV_DOWN: + case NETDEV_UNREGISTER: + BUG_ON(!ifa || !ifa->ifa_dev); + dev = ifa->ifa_dev->dev; + iface_stat_update(dev, event == NETDEV_DOWN); + atomic64_inc(&qtu_events.iface_events); + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block iface_netdev_notifier_blk = { + .notifier_call = iface_netdev_event_handler, +}; + +static struct notifier_block iface_inetaddr_notifier_blk = { + .notifier_call = iface_inetaddr_event_handler, +}; + +static struct notifier_block iface_inet6addr_notifier_blk = { + .notifier_call = iface_inet6addr_event_handler, +}; + +static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) +{ + int err; + + iface_stat_procdir = proc_mkdir(iface_stat_procdirname, parent_procdir); + if (!iface_stat_procdir) { + pr_err("qtaguid: iface_stat: init failed to create proc entry\n"); + err = -1; + goto err; + } + + iface_stat_all_procfile = create_proc_entry(iface_stat_all_procfilename, + proc_iface_perms, + parent_procdir); + if (!iface_stat_all_procfile) { + pr_err("qtaguid: iface_stat: init " + " failed to create stat_old proc entry\n"); + err = -1; + goto err_zap_entry; + } + iface_stat_all_procfile->read_proc = iface_stat_fmt_proc_read; + iface_stat_all_procfile->data = (void *)1; /* fmt1 */ + + iface_stat_fmt_procfile = create_proc_entry(iface_stat_fmt_procfilename, + proc_iface_perms, + parent_procdir); + if (!iface_stat_fmt_procfile) { + pr_err("qtaguid: iface_stat: init " + " failed to create stat_all proc entry\n"); + err = -1; + goto err_zap_all_stats_entry; + } + iface_stat_fmt_procfile->read_proc = iface_stat_fmt_proc_read; + iface_stat_fmt_procfile->data = (void *)2; /* fmt2 */ + + + err = register_netdevice_notifier(&iface_netdev_notifier_blk); + if (err) { + pr_err("qtaguid: iface_stat: init " + "failed to register dev event handler\n"); + goto err_zap_all_stats_entries; + } + err = register_inetaddr_notifier(&iface_inetaddr_notifier_blk); + if (err) { + pr_err("qtaguid: iface_stat: init " + "failed to register ipv4 dev event handler\n"); + goto err_unreg_nd; + } + + err = register_inet6addr_notifier(&iface_inet6addr_notifier_blk); + if (err) { + pr_err("qtaguid: iface_stat: init " + "failed to register ipv6 dev event handler\n"); + goto err_unreg_ip4_addr; + } + return 0; + +err_unreg_ip4_addr: + unregister_inetaddr_notifier(&iface_inetaddr_notifier_blk); +err_unreg_nd: + unregister_netdevice_notifier(&iface_netdev_notifier_blk); +err_zap_all_stats_entries: + remove_proc_entry(iface_stat_fmt_procfilename, parent_procdir); +err_zap_all_stats_entry: + remove_proc_entry(iface_stat_all_procfilename, parent_procdir); +err_zap_entry: + remove_proc_entry(iface_stat_procdirname, parent_procdir); +err: + return err; +} + +static struct sock *qtaguid_find_sk(const struct sk_buff *skb, + struct xt_action_param *par) +{ + struct sock *sk; + unsigned int hook_mask = (1 << par->hooknum); + + MT_DEBUG("qtaguid: find_sk(skb=%p) hooknum=%d family=%d\n", skb, + par->hooknum, par->family); + + /* + * Let's not abuse the the xt_socket_get*_sk(), or else it will + * return garbage SKs. + */ + if (!(hook_mask & XT_SOCKET_SUPPORTED_HOOKS)) + return NULL; + + switch (par->family) { + case NFPROTO_IPV6: + sk = xt_socket_get6_sk(skb, par); + break; + case NFPROTO_IPV4: + sk = xt_socket_get4_sk(skb, par); + break; + default: + return NULL; + } + + /* + * Seems to be issues on the file ptr for TCP_TIME_WAIT SKs. + * http://kerneltrap.org/mailarchive/linux-netdev/2010/10/21/6287959 + * Not fixed in 3.0-r3 :( + */ + if (sk) { + MT_DEBUG("qtaguid: %p->sk_proto=%u " + "->sk_state=%d\n", sk, sk->sk_protocol, sk->sk_state); + if (sk->sk_state == TCP_TIME_WAIT) { + xt_socket_put_sk(sk); + sk = NULL; + } + } + return sk; +} + +static void account_for_uid(const struct sk_buff *skb, + const struct sock *alternate_sk, uid_t uid, + struct xt_action_param *par) +{ + const struct net_device *el_dev; + + if (!skb->dev) { + MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum); + el_dev = par->in ? : par->out; + } else { + const struct net_device *other_dev; + el_dev = skb->dev; + other_dev = par->in ? : par->out; + if (el_dev != other_dev) { + MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs " + "par->(in/out)=%p %s\n", + par->hooknum, el_dev, el_dev->name, other_dev, + other_dev->name); + } + } + + if (unlikely(!el_dev)) { + pr_info("qtaguid[%d]: no par->in/out?!!\n", par->hooknum); + } else if (unlikely(!el_dev->name)) { + pr_info("qtaguid[%d]: no dev->name?!!\n", par->hooknum); + } else { + int proto = ipx_proto(skb, par); + MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", + par->hooknum, el_dev->name, el_dev->type, + par->family, proto); + + if_tag_stat_update(el_dev->name, uid, + skb->sk ? skb->sk : alternate_sk, + par->in ? IFS_RX : IFS_TX, + proto, skb->len); + } +} + +static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct xt_qtaguid_match_info *info = par->matchinfo; + const struct file *filp; + bool got_sock = false; + struct sock *sk; + uid_t sock_uid; + bool res; + + if (unlikely(module_passive)) + return (info->match ^ info->invert) == 0; + + MT_DEBUG("qtaguid[%d]: entered skb=%p par->in=%p/out=%p fam=%d\n", + par->hooknum, skb, par->in, par->out, par->family); + + atomic64_inc(&qtu_events.match_calls); + if (skb == NULL) { + res = (info->match ^ info->invert) == 0; + goto ret_res; + } + + switch (par->hooknum) { + case NF_INET_PRE_ROUTING: + case NF_INET_POST_ROUTING: + atomic64_inc(&qtu_events.match_calls_prepost); + iface_stat_update_from_skb(skb, par); + /* + * We are done in pre/post. The skb will get processed + * further alter. + */ + res = (info->match ^ info->invert); + goto ret_res; + break; + /* default: Fall through and do UID releated work */ + } + + sk = skb->sk; + if (sk == NULL) { + /* + * A missing sk->sk_socket happens when packets are in-flight + * and the matching socket is already closed and gone. + */ + sk = qtaguid_find_sk(skb, par); + /* + * If we got the socket from the find_sk(), we will need to put + * it back, as nf_tproxy_get_sock_v4() got it. + */ + got_sock = sk; + if (sk) + atomic64_inc(&qtu_events.match_found_sk_in_ct); + else + atomic64_inc(&qtu_events.match_found_no_sk_in_ct); + } else { + atomic64_inc(&qtu_events.match_found_sk); + } + MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d fam=%d proto=%d\n", + par->hooknum, sk, got_sock, par->family, ipx_proto(skb, par)); + if (sk != NULL) { + MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n", + par->hooknum, sk, sk->sk_socket, + sk->sk_socket ? sk->sk_socket->file : (void *)-1LL); + filp = sk->sk_socket ? sk->sk_socket->file : NULL; + MT_DEBUG("qtaguid[%d]: filp...uid=%u\n", + par->hooknum, filp ? filp->f_cred->fsuid : -1); + } + + if (sk == NULL || sk->sk_socket == NULL) { + /* + * Here, the qtaguid_find_sk() using connection tracking + * couldn't find the owner, so for now we just count them + * against the system. + */ + /* + * TODO: unhack how to force just accounting. + * For now we only do iface stats when the uid-owner is not + * requested. + */ + if (!(info->match & XT_QTAGUID_UID)) + account_for_uid(skb, sk, 0, par); + MT_DEBUG("qtaguid[%d]: leaving (sk?sk->sk_socket)=%p\n", + par->hooknum, + sk ? sk->sk_socket : NULL); + res = (info->match ^ info->invert) == 0; + atomic64_inc(&qtu_events.match_no_sk); + goto put_sock_ret_res; + } else if (info->match & info->invert & XT_QTAGUID_SOCKET) { + res = false; + goto put_sock_ret_res; + } + filp = sk->sk_socket->file; + if (filp == NULL) { + MT_DEBUG("qtaguid[%d]: leaving filp=NULL\n", par->hooknum); + account_for_uid(skb, sk, 0, par); + res = ((info->match ^ info->invert) & + (XT_QTAGUID_UID | XT_QTAGUID_GID)) == 0; + atomic64_inc(&qtu_events.match_no_sk_file); + goto put_sock_ret_res; + } + sock_uid = filp->f_cred->fsuid; + /* + * TODO: unhack how to force just accounting. + * For now we only do iface stats when the uid-owner is not requested + */ + if (!(info->match & XT_QTAGUID_UID)) + account_for_uid(skb, sk, sock_uid, par); + + /* + * The following two tests fail the match when: + * id not in range AND no inverted condition requested + * or id in range AND inverted condition requested + * Thus (!a && b) || (a && !b) == a ^ b + */ + if (info->match & XT_QTAGUID_UID) + if ((filp->f_cred->fsuid >= info->uid_min && + filp->f_cred->fsuid <= info->uid_max) ^ + !(info->invert & XT_QTAGUID_UID)) { + MT_DEBUG("qtaguid[%d]: leaving uid not matching\n", + par->hooknum); + res = false; + goto put_sock_ret_res; + } + if (info->match & XT_QTAGUID_GID) + if ((filp->f_cred->fsgid >= info->gid_min && + filp->f_cred->fsgid <= info->gid_max) ^ + !(info->invert & XT_QTAGUID_GID)) { + MT_DEBUG("qtaguid[%d]: leaving gid not matching\n", + par->hooknum); + res = false; + goto put_sock_ret_res; + } + + MT_DEBUG("qtaguid[%d]: leaving matched\n", par->hooknum); + res = true; + +put_sock_ret_res: + if (got_sock) + xt_socket_put_sk(sk); +ret_res: + MT_DEBUG("qtaguid[%d]: left %d\n", par->hooknum, res); + return res; +} + +#ifdef DDEBUG +/* This function is not in xt_qtaguid_print.c because of locks visibility */ +static void prdebug_full_state(int indent_level, const char *fmt, ...) +{ + va_list args; + char *fmt_buff; + char *buff; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + fmt_buff = kasprintf(GFP_ATOMIC, + "qtaguid: %s(): %s {\n", __func__, fmt); + BUG_ON(!fmt_buff); + va_start(args, fmt); + buff = kvasprintf(GFP_ATOMIC, + fmt_buff, args); + BUG_ON(!buff); + pr_debug("%s", buff); + kfree(fmt_buff); + kfree(buff); + va_end(args); + + spin_lock_bh(&sock_tag_list_lock); + prdebug_sock_tag_tree(indent_level, &sock_tag_tree); + spin_unlock_bh(&sock_tag_list_lock); + + spin_lock_bh(&sock_tag_list_lock); + spin_lock_bh(&uid_tag_data_tree_lock); + prdebug_uid_tag_data_tree(indent_level, &uid_tag_data_tree); + prdebug_proc_qtu_data_tree(indent_level, &proc_qtu_data_tree); + spin_unlock_bh(&uid_tag_data_tree_lock); + spin_unlock_bh(&sock_tag_list_lock); + + spin_lock_bh(&iface_stat_list_lock); + prdebug_iface_stat_list(indent_level, &iface_stat_list); + spin_unlock_bh(&iface_stat_list_lock); + + pr_debug("qtaguid: %s(): }\n", __func__); +} +#else +static void prdebug_full_state(int indent_level, const char *fmt, ...) {} +#endif + +/* + * Procfs reader to get all active socket tags using style "1)" as described in + * fs/proc/generic.c + */ +static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, + off_t items_to_skip, int char_count, int *eof, + void *data) +{ + char *outp = page; + int len; + uid_t uid; + struct rb_node *node; + struct sock_tag *sock_tag_entry; + int item_index = 0; + int indent_level = 0; + long f_count; + + if (unlikely(module_passive)) { + *eof = 1; + return 0; + } + + if (*eof) + return 0; + + CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u " + "page=%p off=%ld char_count=%d *eof=%d\n", + current->pid, current->tgid, current_fsuid(), + page, items_to_skip, char_count, *eof); + + spin_lock_bh(&sock_tag_list_lock); + for (node = rb_first(&sock_tag_tree); + node; + node = rb_next(node)) { + if (item_index++ < items_to_skip) + continue; + sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); + uid = get_uid_from_tag(sock_tag_entry->tag); + CT_DEBUG("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%u) " + "pid=%u\n", + sock_tag_entry->sk, + sock_tag_entry->tag, + uid, + sock_tag_entry->pid + ); + f_count = atomic_long_read( + &sock_tag_entry->socket->file->f_count); + len = snprintf(outp, char_count, + "sock=%p tag=0x%llx (uid=%u) pid=%u " + "f_count=%lu\n", + sock_tag_entry->sk, + sock_tag_entry->tag, uid, + sock_tag_entry->pid, f_count); + if (len >= char_count) { + spin_unlock_bh(&sock_tag_list_lock); + *outp = '\0'; + return outp - page; + } + outp += len; + char_count -= len; + (*num_items_returned)++; + } + spin_unlock_bh(&sock_tag_list_lock); + + if (item_index++ >= items_to_skip) { + len = snprintf(outp, char_count, + "events: sockets_tagged=%llu " + "sockets_untagged=%llu " + "counter_set_changes=%llu " + "delete_cmds=%llu " + "iface_events=%llu " + "match_calls=%llu " + "match_calls_prepost=%llu " + "match_found_sk=%llu " + "match_found_sk_in_ct=%llu " + "match_found_no_sk_in_ct=%llu " + "match_no_sk=%llu " + "match_no_sk_file=%llu\n", + atomic64_read(&qtu_events.sockets_tagged), + atomic64_read(&qtu_events.sockets_untagged), + atomic64_read(&qtu_events.counter_set_changes), + atomic64_read(&qtu_events.delete_cmds), + atomic64_read(&qtu_events.iface_events), + atomic64_read(&qtu_events.match_calls), + atomic64_read(&qtu_events.match_calls_prepost), + atomic64_read(&qtu_events.match_found_sk), + atomic64_read(&qtu_events.match_found_sk_in_ct), + atomic64_read( + &qtu_events.match_found_no_sk_in_ct), + atomic64_read(&qtu_events.match_no_sk), + atomic64_read(&qtu_events.match_no_sk_file)); + if (len >= char_count) { + *outp = '\0'; + return outp - page; + } + outp += len; + char_count -= len; + (*num_items_returned)++; + } + + /* Count the following as part of the last item_index */ + if (item_index > items_to_skip) { + prdebug_full_state(indent_level, "proc ctrl"); + } + + *eof = 1; + return outp - page; +} + +/* + * Delete socket tags, and stat tags associated with a given + * accouting tag and uid. + */ +static int ctrl_cmd_delete(const char *input) +{ + char cmd; + uid_t uid; + uid_t entry_uid; + tag_t acct_tag; + tag_t tag; + int res, argc; + struct iface_stat *iface_entry; + struct rb_node *node; + struct sock_tag *st_entry; + struct rb_root st_to_free_tree = RB_ROOT; + struct tag_stat *ts_entry; + struct tag_counter_set *tcs_entry; + struct tag_ref *tr_entry; + struct uid_tag_data *utd_entry; + + argc = sscanf(input, "%c %llu %u", &cmd, &acct_tag, &uid); + CT_DEBUG("qtaguid: ctrl_delete(%s): argc=%d cmd=%c " + "user_tag=0x%llx uid=%u\n", input, argc, cmd, + acct_tag, uid); + if (argc < 2) { + res = -EINVAL; + goto err; + } + if (!valid_atag(acct_tag)) { + pr_info("qtaguid: ctrl_delete(%s): invalid tag\n", input); + res = -EINVAL; + goto err; + } + if (argc < 3) { + uid = current_fsuid(); + } else if (!can_impersonate_uid(uid)) { + pr_info("qtaguid: ctrl_delete(%s): " + "insufficient priv from pid=%u tgid=%u uid=%u\n", + input, current->pid, current->tgid, current_fsuid()); + res = -EPERM; + goto err; + } + + tag = combine_atag_with_uid(acct_tag, uid); + CT_DEBUG("qtaguid: ctrl_delete(%s): " + "looking for tag=0x%llx (uid=%u)\n", + input, tag, uid); + + /* Delete socket tags */ + spin_lock_bh(&sock_tag_list_lock); + node = rb_first(&sock_tag_tree); + while (node) { + st_entry = rb_entry(node, struct sock_tag, sock_node); + entry_uid = get_uid_from_tag(st_entry->tag); + node = rb_next(node); + if (entry_uid != uid) + continue; + + CT_DEBUG("qtaguid: ctrl_delete(%s): st tag=0x%llx (uid=%u)\n", + input, st_entry->tag, entry_uid); + + if (!acct_tag || st_entry->tag == tag) { + rb_erase(&st_entry->sock_node, &sock_tag_tree); + /* Can't sockfd_put() within spinlock, do it later. */ + sock_tag_tree_insert(st_entry, &st_to_free_tree); + tr_entry = lookup_tag_ref(st_entry->tag, NULL); + BUG_ON(tr_entry->num_sock_tags <= 0); + tr_entry->num_sock_tags--; + /* + * TODO: remove if, and start failing. + * This is a hack to work around the fact that in some + * places we have "if (IS_ERR_OR_NULL(pqd_entry))" + * and are trying to work around apps + * that didn't open the /dev/xt_qtaguid. + */ + if (st_entry->list.next && st_entry->list.prev) + list_del(&st_entry->list); + } + } + spin_unlock_bh(&sock_tag_list_lock); + + sock_tag_tree_erase(&st_to_free_tree); + + /* Delete tag counter-sets */ + spin_lock_bh(&tag_counter_set_list_lock); + /* Counter sets are only on the uid tag, not full tag */ + tcs_entry = tag_counter_set_tree_search(&tag_counter_set_tree, tag); + if (tcs_entry) { + CT_DEBUG("qtaguid: ctrl_delete(%s): " + "erase tcs: tag=0x%llx (uid=%u) set=%d\n", + input, + tcs_entry->tn.tag, + get_uid_from_tag(tcs_entry->tn.tag), + tcs_entry->active_set); + rb_erase(&tcs_entry->tn.node, &tag_counter_set_tree); + kfree(tcs_entry); + } + spin_unlock_bh(&tag_counter_set_list_lock); + + /* + * If acct_tag is 0, then all entries belonging to uid are + * erased. + */ + spin_lock_bh(&iface_stat_list_lock); + list_for_each_entry(iface_entry, &iface_stat_list, list) { + spin_lock_bh(&iface_entry->tag_stat_list_lock); + node = rb_first(&iface_entry->tag_stat_tree); + while (node) { + ts_entry = rb_entry(node, struct tag_stat, tn.node); + entry_uid = get_uid_from_tag(ts_entry->tn.tag); + node = rb_next(node); + + CT_DEBUG("qtaguid: ctrl_delete(%s): " + "ts tag=0x%llx (uid=%u)\n", + input, ts_entry->tn.tag, entry_uid); + + if (entry_uid != uid) + continue; + if (!acct_tag || ts_entry->tn.tag == tag) { + CT_DEBUG("qtaguid: ctrl_delete(%s): " + "erase ts: %s 0x%llx %u\n", + input, iface_entry->ifname, + get_atag_from_tag(ts_entry->tn.tag), + entry_uid); + rb_erase(&ts_entry->tn.node, + &iface_entry->tag_stat_tree); + kfree(ts_entry); + } + } + spin_unlock_bh(&iface_entry->tag_stat_list_lock); + } + spin_unlock_bh(&iface_stat_list_lock); + + /* Cleanup the uid_tag_data */ + spin_lock_bh(&uid_tag_data_tree_lock); + node = rb_first(&uid_tag_data_tree); + while (node) { + utd_entry = rb_entry(node, struct uid_tag_data, node); + entry_uid = utd_entry->uid; + node = rb_next(node); + + CT_DEBUG("qtaguid: ctrl_delete(%s): " + "utd uid=%u\n", + input, entry_uid); + + if (entry_uid != uid) + continue; + /* + * Go over the tag_refs, and those that don't have + * sock_tags using them are freed. + */ + put_tag_ref_tree(tag, utd_entry); + put_utd_entry(utd_entry); + } + spin_unlock_bh(&uid_tag_data_tree_lock); + + atomic64_inc(&qtu_events.delete_cmds); + res = 0; + +err: + return res; +} + +static int ctrl_cmd_counter_set(const char *input) +{ + char cmd; + uid_t uid = 0; + tag_t tag; + int res, argc; + struct tag_counter_set *tcs; + int counter_set; + + argc = sscanf(input, "%c %d %u", &cmd, &counter_set, &uid); + CT_DEBUG("qtaguid: ctrl_counterset(%s): argc=%d cmd=%c " + "set=%d uid=%u\n", input, argc, cmd, + counter_set, uid); + if (argc != 3) { + res = -EINVAL; + goto err; + } + if (counter_set < 0 || counter_set >= IFS_MAX_COUNTER_SETS) { + pr_info("qtaguid: ctrl_counterset(%s): invalid counter_set range\n", + input); + res = -EINVAL; + goto err; + } + if (!can_manipulate_uids()) { + pr_info("qtaguid: ctrl_counterset(%s): " + "insufficient priv from pid=%u tgid=%u uid=%u\n", + input, current->pid, current->tgid, current_fsuid()); + res = -EPERM; + goto err; + } + + tag = make_tag_from_uid(uid); + spin_lock_bh(&tag_counter_set_list_lock); + tcs = tag_counter_set_tree_search(&tag_counter_set_tree, tag); + if (!tcs) { + tcs = kzalloc(sizeof(*tcs), GFP_ATOMIC); + if (!tcs) { + spin_unlock_bh(&tag_counter_set_list_lock); + pr_err("qtaguid: ctrl_counterset(%s): " + "failed to alloc counter set\n", + input); + res = -ENOMEM; + goto err; + } + tcs->tn.tag = tag; + tag_counter_set_tree_insert(tcs, &tag_counter_set_tree); + CT_DEBUG("qtaguid: ctrl_counterset(%s): added tcs tag=0x%llx " + "(uid=%u) set=%d\n", + input, tag, get_uid_from_tag(tag), counter_set); + } + tcs->active_set = counter_set; + spin_unlock_bh(&tag_counter_set_list_lock); + atomic64_inc(&qtu_events.counter_set_changes); + res = 0; + +err: + return res; +} + +static int ctrl_cmd_tag(const char *input) +{ + char cmd; + int sock_fd = 0; + uid_t uid = 0; + tag_t acct_tag = make_atag_from_value(0); + tag_t full_tag; + struct socket *el_socket; + int res, argc; + struct sock_tag *sock_tag_entry; + struct tag_ref *tag_ref_entry; + struct uid_tag_data *uid_tag_data_entry; + struct proc_qtu_data *pqd_entry; + + /* Unassigned args will get defaulted later. */ + argc = sscanf(input, "%c %d %llu %u", &cmd, &sock_fd, &acct_tag, &uid); + CT_DEBUG("qtaguid: ctrl_tag(%s): argc=%d cmd=%c sock_fd=%d " + "acct_tag=0x%llx uid=%u\n", input, argc, cmd, sock_fd, + acct_tag, uid); + if (argc < 2) { + res = -EINVAL; + goto err; + } + el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ + if (!el_socket) { + pr_info("qtaguid: ctrl_tag(%s): failed to lookup" + " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n", + input, sock_fd, res, current->pid, current->tgid, + current_fsuid()); + goto err; + } + CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%ld ->sk=%p\n", + input, atomic_long_read(&el_socket->file->f_count), + el_socket->sk); + if (argc < 3) { + acct_tag = make_atag_from_value(0); + } else if (!valid_atag(acct_tag)) { + pr_info("qtaguid: ctrl_tag(%s): invalid tag\n", input); + res = -EINVAL; + goto err_put; + } + CT_DEBUG("qtaguid: ctrl_tag(%s): " + "pid=%u tgid=%u uid=%u euid=%u fsuid=%u " + "ctrl.gid=%u in_group()=%d in_egroup()=%d\n", + input, current->pid, current->tgid, current_uid(), + current_euid(), current_fsuid(), + xt_qtaguid_ctrl_file->gid, + in_group_p(xt_qtaguid_ctrl_file->gid), + in_egroup_p(xt_qtaguid_ctrl_file->gid)); + if (argc < 4) { + uid = current_fsuid(); + } else if (!can_impersonate_uid(uid)) { + pr_info("qtaguid: ctrl_tag(%s): " + "insufficient priv from pid=%u tgid=%u uid=%u\n", + input, current->pid, current->tgid, current_fsuid()); + res = -EPERM; + goto err_put; + } + full_tag = combine_atag_with_uid(acct_tag, uid); + + spin_lock_bh(&sock_tag_list_lock); + sock_tag_entry = get_sock_stat_nl(el_socket->sk); + tag_ref_entry = get_tag_ref(full_tag, &uid_tag_data_entry); + if (IS_ERR(tag_ref_entry)) { + res = PTR_ERR(tag_ref_entry); + spin_unlock_bh(&sock_tag_list_lock); + goto err_put; + } + tag_ref_entry->num_sock_tags++; + if (sock_tag_entry) { + struct tag_ref *prev_tag_ref_entry; + + CT_DEBUG("qtaguid: ctrl_tag(%s): retag for sk=%p " + "st@%p ...->f_count=%ld\n", + input, el_socket->sk, sock_tag_entry, + atomic_long_read(&el_socket->file->f_count)); + /* + * This is a re-tagging, so release the sock_fd that was + * locked at the time of the 1st tagging. + * There is still the ref from this call's sockfd_lookup() so + * it can be done within the spinlock. + */ + sockfd_put(sock_tag_entry->socket); + prev_tag_ref_entry = lookup_tag_ref(sock_tag_entry->tag, + &uid_tag_data_entry); + BUG_ON(IS_ERR_OR_NULL(prev_tag_ref_entry)); + BUG_ON(prev_tag_ref_entry->num_sock_tags <= 0); + prev_tag_ref_entry->num_sock_tags--; + sock_tag_entry->tag = full_tag; + } else { + CT_DEBUG("qtaguid: ctrl_tag(%s): newtag for sk=%p\n", + input, el_socket->sk); + sock_tag_entry = kzalloc(sizeof(*sock_tag_entry), + GFP_ATOMIC); + if (!sock_tag_entry) { + pr_err("qtaguid: ctrl_tag(%s): " + "socket tag alloc failed\n", + input); + spin_unlock_bh(&sock_tag_list_lock); + res = -ENOMEM; + goto err_tag_unref_put; + } + sock_tag_entry->sk = el_socket->sk; + sock_tag_entry->socket = el_socket; + sock_tag_entry->pid = current->tgid; + sock_tag_entry->tag = combine_atag_with_uid(acct_tag, + uid); + spin_lock_bh(&uid_tag_data_tree_lock); + pqd_entry = proc_qtu_data_tree_search( + &proc_qtu_data_tree, current->tgid); + /* + * TODO: remove if, and start failing. + * At first, we want to catch user-space code that is not + * opening the /dev/xt_qtaguid. + */ + if (IS_ERR_OR_NULL(pqd_entry)) + pr_warn_once( + "qtaguid: %s(): " + "User space forgot to open /dev/xt_qtaguid? " + "pid=%u tgid=%u uid=%u\n", __func__, + current->pid, current->tgid, + current_fsuid()); + else + list_add(&sock_tag_entry->list, + &pqd_entry->sock_tag_list); + spin_unlock_bh(&uid_tag_data_tree_lock); + + sock_tag_tree_insert(sock_tag_entry, &sock_tag_tree); + atomic64_inc(&qtu_events.sockets_tagged); + } + spin_unlock_bh(&sock_tag_list_lock); + /* We keep the ref to the socket (file) until it is untagged */ + CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->f_count=%ld\n", + input, sock_tag_entry, + atomic_long_read(&el_socket->file->f_count)); + return 0; + +err_tag_unref_put: + BUG_ON(tag_ref_entry->num_sock_tags <= 0); + tag_ref_entry->num_sock_tags--; + free_tag_ref_from_utd_entry(tag_ref_entry, uid_tag_data_entry); +err_put: + CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->f_count=%ld\n", + input, atomic_long_read(&el_socket->file->f_count) - 1); + /* Release the sock_fd that was grabbed by sockfd_lookup(). */ + sockfd_put(el_socket); + return res; + +err: + CT_DEBUG("qtaguid: ctrl_tag(%s): done.\n", input); + return res; +} + +static int ctrl_cmd_untag(const char *input) +{ + char cmd; + int sock_fd = 0; + struct socket *el_socket; + int res, argc; + struct sock_tag *sock_tag_entry; + struct tag_ref *tag_ref_entry; + struct uid_tag_data *utd_entry; + struct proc_qtu_data *pqd_entry; + + argc = sscanf(input, "%c %d", &cmd, &sock_fd); + CT_DEBUG("qtaguid: ctrl_untag(%s): argc=%d cmd=%c sock_fd=%d\n", + input, argc, cmd, sock_fd); + if (argc < 2) { + res = -EINVAL; + goto err; + } + el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ + if (!el_socket) { + pr_info("qtaguid: ctrl_untag(%s): failed to lookup" + " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n", + input, sock_fd, res, current->pid, current->tgid, + current_fsuid()); + goto err; + } + CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%ld ->sk=%p\n", + input, atomic_long_read(&el_socket->file->f_count), + el_socket->sk); + spin_lock_bh(&sock_tag_list_lock); + sock_tag_entry = get_sock_stat_nl(el_socket->sk); + if (!sock_tag_entry) { + spin_unlock_bh(&sock_tag_list_lock); + res = -EINVAL; + goto err_put; + } + /* + * The socket already belongs to the current process + * so it can do whatever it wants to it. + */ + rb_erase(&sock_tag_entry->sock_node, &sock_tag_tree); + + tag_ref_entry = lookup_tag_ref(sock_tag_entry->tag, &utd_entry); + BUG_ON(!tag_ref_entry); + BUG_ON(tag_ref_entry->num_sock_tags <= 0); + spin_lock_bh(&uid_tag_data_tree_lock); + pqd_entry = proc_qtu_data_tree_search( + &proc_qtu_data_tree, current->tgid); + /* + * TODO: remove if, and start failing. + * At first, we want to catch user-space code that is not + * opening the /dev/xt_qtaguid. + */ + if (IS_ERR_OR_NULL(pqd_entry)) + pr_warn_once("qtaguid: %s(): " + "User space forgot to open /dev/xt_qtaguid? " + "pid=%u tgid=%u uid=%u\n", __func__, + current->pid, current->tgid, current_fsuid()); + else + list_del(&sock_tag_entry->list); + spin_unlock_bh(&uid_tag_data_tree_lock); + /* + * We don't free tag_ref from the utd_entry here, + * only during a cmd_delete(). + */ + tag_ref_entry->num_sock_tags--; + spin_unlock_bh(&sock_tag_list_lock); + /* + * Release the sock_fd that was grabbed at tag time, + * and once more for the sockfd_lookup() here. + */ + sockfd_put(sock_tag_entry->socket); + CT_DEBUG("qtaguid: ctrl_untag(%s): done. st@%p ...->f_count=%ld\n", + input, sock_tag_entry, + atomic_long_read(&el_socket->file->f_count) - 1); + sockfd_put(el_socket); + + kfree(sock_tag_entry); + atomic64_inc(&qtu_events.sockets_untagged); + + return 0; + +err_put: + CT_DEBUG("qtaguid: ctrl_untag(%s): done. socket->...->f_count=%ld\n", + input, atomic_long_read(&el_socket->file->f_count) - 1); + /* Release the sock_fd that was grabbed by sockfd_lookup(). */ + sockfd_put(el_socket); + return res; + +err: + CT_DEBUG("qtaguid: ctrl_untag(%s): done.\n", input); + return res; +} + +static int qtaguid_ctrl_parse(const char *input, int count) +{ + char cmd; + int res; + + CT_DEBUG("qtaguid: ctrl(%s): pid=%u tgid=%u uid=%u\n", + input, current->pid, current->tgid, current_fsuid()); + + cmd = input[0]; + /* Collect params for commands */ + switch (cmd) { + case 'd': + res = ctrl_cmd_delete(input); + break; + + case 's': + res = ctrl_cmd_counter_set(input); + break; + + case 't': + res = ctrl_cmd_tag(input); + break; + + case 'u': + res = ctrl_cmd_untag(input); + break; + + default: + res = -EINVAL; + goto err; + } + if (!res) + res = count; +err: + CT_DEBUG("qtaguid: ctrl(%s): res=%d\n", input, res); + return res; +} + +#define MAX_QTAGUID_CTRL_INPUT_LEN 255 +static int qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + char input_buf[MAX_QTAGUID_CTRL_INPUT_LEN]; + + if (unlikely(module_passive)) + return count; + + if (count >= MAX_QTAGUID_CTRL_INPUT_LEN) + return -EINVAL; + + if (copy_from_user(input_buf, buffer, count)) + return -EFAULT; + + input_buf[count] = '\0'; + return qtaguid_ctrl_parse(input_buf, count); +} + +struct proc_print_info { + char *outp; + char **num_items_returned; + struct iface_stat *iface_entry; + struct tag_stat *ts_entry; + int item_index; + int items_to_skip; + int char_count; +}; + +static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) +{ + int len; + struct data_counters *cnts; + + if (!ppi->item_index) { + if (ppi->item_index++ < ppi->items_to_skip) + return 0; + len = snprintf(ppi->outp, ppi->char_count, + "idx iface acct_tag_hex uid_tag_int cnt_set " + "rx_bytes rx_packets " + "tx_bytes tx_packets " + "rx_tcp_bytes rx_tcp_packets " + "rx_udp_bytes rx_udp_packets " + "rx_other_bytes rx_other_packets " + "tx_tcp_bytes tx_tcp_packets " + "tx_udp_bytes tx_udp_packets " + "tx_other_bytes tx_other_packets\n"); + } else { + tag_t tag = ppi->ts_entry->tn.tag; + uid_t stat_uid = get_uid_from_tag(tag); + /* Detailed tags are not available to everybody */ + if (get_atag_from_tag(tag) + && !can_read_other_uid_stats(stat_uid)) { + CT_DEBUG("qtaguid: stats line: " + "%s 0x%llx %u: insufficient priv " + "from pid=%u tgid=%u uid=%u stats.gid=%u\n", + ppi->iface_entry->ifname, + get_atag_from_tag(tag), stat_uid, + current->pid, current->tgid, current_fsuid(), + xt_qtaguid_stats_file->gid); + return 0; + } + if (ppi->item_index++ < ppi->items_to_skip) + return 0; + cnts = &ppi->ts_entry->counters; + len = snprintf( + ppi->outp, ppi->char_count, + "%d %s 0x%llx %u %u " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu\n", + ppi->item_index, + ppi->iface_entry->ifname, + get_atag_from_tag(tag), + stat_uid, + cnt_set, + dc_sum_bytes(cnts, cnt_set, IFS_RX), + dc_sum_packets(cnts, cnt_set, IFS_RX), + dc_sum_bytes(cnts, cnt_set, IFS_TX), + dc_sum_packets(cnts, cnt_set, IFS_TX), + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); + } + return len; +} + +static bool pp_sets(struct proc_print_info *ppi) +{ + int len; + int counter_set; + for (counter_set = 0; counter_set < IFS_MAX_COUNTER_SETS; + counter_set++) { + len = pp_stats_line(ppi, counter_set); + if (len >= ppi->char_count) { + *ppi->outp = '\0'; + return false; + } + if (len) { + ppi->outp += len; + ppi->char_count -= len; + (*ppi->num_items_returned)++; + } + } + return true; +} + +/* + * Procfs reader to get all tag stats using style "1)" as described in + * fs/proc/generic.c + * Groups all protocols tx/rx bytes. + */ +static int qtaguid_stats_proc_read(char *page, char **num_items_returned, + off_t items_to_skip, int char_count, int *eof, + void *data) +{ + struct proc_print_info ppi; + int len; + + ppi.outp = page; + ppi.item_index = 0; + ppi.char_count = char_count; + ppi.num_items_returned = num_items_returned; + ppi.items_to_skip = items_to_skip; + + if (unlikely(module_passive)) { + len = pp_stats_line(&ppi, 0); + /* The header should always be shorter than the buffer. */ + BUG_ON(len >= ppi.char_count); + (*num_items_returned)++; + *eof = 1; + return len; + } + + CT_DEBUG("qtaguid:proc stats pid=%u tgid=%u uid=%u " + "page=%p *num_items_returned=%p off=%ld " + "char_count=%d *eof=%d\n", + current->pid, current->tgid, current_fsuid(), + page, *num_items_returned, + items_to_skip, char_count, *eof); + + if (*eof) + return 0; + + /* The idx is there to help debug when things go belly up. */ + len = pp_stats_line(&ppi, 0); + /* Don't advance the outp unless the whole line was printed */ + if (len >= ppi.char_count) { + *ppi.outp = '\0'; + return ppi.outp - page; + } + if (len) { + ppi.outp += len; + ppi.char_count -= len; + (*num_items_returned)++; + } + + spin_lock_bh(&iface_stat_list_lock); + list_for_each_entry(ppi.iface_entry, &iface_stat_list, list) { + struct rb_node *node; + spin_lock_bh(&ppi.iface_entry->tag_stat_list_lock); + for (node = rb_first(&ppi.iface_entry->tag_stat_tree); + node; + node = rb_next(node)) { + ppi.ts_entry = rb_entry(node, struct tag_stat, tn.node); + if (!pp_sets(&ppi)) { + spin_unlock_bh( + &ppi.iface_entry->tag_stat_list_lock); + spin_unlock_bh(&iface_stat_list_lock); + return ppi.outp - page; + } + } + spin_unlock_bh(&ppi.iface_entry->tag_stat_list_lock); + } + spin_unlock_bh(&iface_stat_list_lock); + + *eof = 1; + return ppi.outp - page; +} + +/*------------------------------------------*/ +static int qtudev_open(struct inode *inode, struct file *file) +{ + struct uid_tag_data *utd_entry; + struct proc_qtu_data *pqd_entry; + struct proc_qtu_data *new_pqd_entry; + int res; + bool utd_entry_found; + + if (unlikely(qtu_proc_handling_passive)) + return 0; + + DR_DEBUG("qtaguid: qtudev_open(): pid=%u tgid=%u uid=%u\n", + current->pid, current->tgid, current_fsuid()); + + spin_lock_bh(&uid_tag_data_tree_lock); + + /* Look for existing uid data, or alloc one. */ + utd_entry = get_uid_data(current_fsuid(), &utd_entry_found); + if (IS_ERR_OR_NULL(utd_entry)) { + res = PTR_ERR(utd_entry); + goto err_unlock; + } + + /* Look for existing PID based proc_data */ + pqd_entry = proc_qtu_data_tree_search(&proc_qtu_data_tree, + current->tgid); + if (pqd_entry) { + pr_err("qtaguid: qtudev_open(): %u/%u %u " + "%s already opened\n", + current->pid, current->tgid, current_fsuid(), + QTU_DEV_NAME); + res = -EBUSY; + goto err_unlock_free_utd; + } + + new_pqd_entry = kzalloc(sizeof(*new_pqd_entry), GFP_ATOMIC); + if (!new_pqd_entry) { + pr_err("qtaguid: qtudev_open(): %u/%u %u: " + "proc data alloc failed\n", + current->pid, current->tgid, current_fsuid()); + res = -ENOMEM; + goto err_unlock_free_utd; + } + new_pqd_entry->pid = current->tgid; + INIT_LIST_HEAD(&new_pqd_entry->sock_tag_list); + new_pqd_entry->parent_tag_data = utd_entry; + utd_entry->num_pqd++; + + proc_qtu_data_tree_insert(new_pqd_entry, + &proc_qtu_data_tree); + + spin_unlock_bh(&uid_tag_data_tree_lock); + DR_DEBUG("qtaguid: tracking data for uid=%u in pqd=%p\n", + current_fsuid(), new_pqd_entry); + file->private_data = new_pqd_entry; + return 0; + +err_unlock_free_utd: + if (!utd_entry_found) { + rb_erase(&utd_entry->node, &uid_tag_data_tree); + kfree(utd_entry); + } +err_unlock: + spin_unlock_bh(&uid_tag_data_tree_lock); + return res; +} + +static int qtudev_release(struct inode *inode, struct file *file) +{ + struct proc_qtu_data *pqd_entry = file->private_data; + struct uid_tag_data *utd_entry = pqd_entry->parent_tag_data; + struct sock_tag *st_entry; + struct rb_root st_to_free_tree = RB_ROOT; + struct list_head *entry, *next; + struct tag_ref *tr; + + if (unlikely(qtu_proc_handling_passive)) + return 0; + + /* + * Do not trust the current->pid, it might just be a kworker cleaning + * up after a dead proc. + */ + DR_DEBUG("qtaguid: qtudev_release(): " + "pid=%u tgid=%u uid=%u " + "pqd_entry=%p->pid=%u utd_entry=%p->active_tags=%d\n", + current->pid, current->tgid, pqd_entry->parent_tag_data->uid, + pqd_entry, pqd_entry->pid, utd_entry, + utd_entry->num_active_tags); + + spin_lock_bh(&sock_tag_list_lock); + spin_lock_bh(&uid_tag_data_tree_lock); + + list_for_each_safe(entry, next, &pqd_entry->sock_tag_list) { + st_entry = list_entry(entry, struct sock_tag, list); + DR_DEBUG("qtaguid: %s(): " + "erase sock_tag=%p->sk=%p pid=%u tgid=%u uid=%u\n", + __func__, + st_entry, st_entry->sk, + current->pid, current->tgid, + pqd_entry->parent_tag_data->uid); + + utd_entry = uid_tag_data_tree_search( + &uid_tag_data_tree, + get_uid_from_tag(st_entry->tag)); + BUG_ON(IS_ERR_OR_NULL(utd_entry)); + DR_DEBUG("qtaguid: %s(): " + "looking for tag=0x%llx in utd_entry=%p\n", __func__, + st_entry->tag, utd_entry); + tr = tag_ref_tree_search(&utd_entry->tag_ref_tree, + st_entry->tag); + BUG_ON(!tr); + BUG_ON(tr->num_sock_tags <= 0); + tr->num_sock_tags--; + free_tag_ref_from_utd_entry(tr, utd_entry); + + rb_erase(&st_entry->sock_node, &sock_tag_tree); + list_del(&st_entry->list); + /* Can't sockfd_put() within spinlock, do it later. */ + sock_tag_tree_insert(st_entry, &st_to_free_tree); + + /* + * Try to free the utd_entry if no other proc_qtu_data is + * using it (num_pqd is 0) and it doesn't have active tags + * (num_active_tags is 0). + */ + put_utd_entry(utd_entry); + } + + rb_erase(&pqd_entry->node, &proc_qtu_data_tree); + BUG_ON(pqd_entry->parent_tag_data->num_pqd < 1); + pqd_entry->parent_tag_data->num_pqd--; + put_utd_entry(pqd_entry->parent_tag_data); + kfree(pqd_entry); + file->private_data = NULL; + + spin_unlock_bh(&uid_tag_data_tree_lock); + spin_unlock_bh(&sock_tag_list_lock); + + + sock_tag_tree_erase(&st_to_free_tree); + + prdebug_full_state(0, "%s(): pid=%u tgid=%u", __func__, + current->pid, current->tgid); + return 0; +} + +/*------------------------------------------*/ +static const struct file_operations qtudev_fops = { + .owner = THIS_MODULE, + .open = qtudev_open, + .release = qtudev_release, +}; + +static struct miscdevice qtu_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = QTU_DEV_NAME, + .fops = &qtudev_fops, + /* How sad it doesn't allow for defaults: .mode = S_IRUGO | S_IWUSR */ +}; + +/*------------------------------------------*/ +static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir) +{ + int ret; + *res_procdir = proc_mkdir(module_procdirname, init_net.proc_net); + if (!*res_procdir) { + pr_err("qtaguid: failed to create proc/.../xt_qtaguid\n"); + ret = -ENOMEM; + goto no_dir; + } + + xt_qtaguid_ctrl_file = create_proc_entry("ctrl", proc_ctrl_perms, + *res_procdir); + if (!xt_qtaguid_ctrl_file) { + pr_err("qtaguid: failed to create xt_qtaguid/ctrl " + " file\n"); + ret = -ENOMEM; + goto no_ctrl_entry; + } + xt_qtaguid_ctrl_file->read_proc = qtaguid_ctrl_proc_read; + xt_qtaguid_ctrl_file->write_proc = qtaguid_ctrl_proc_write; + + xt_qtaguid_stats_file = create_proc_entry("stats", proc_stats_perms, + *res_procdir); + if (!xt_qtaguid_stats_file) { + pr_err("qtaguid: failed to create xt_qtaguid/stats " + "file\n"); + ret = -ENOMEM; + goto no_stats_entry; + } + xt_qtaguid_stats_file->read_proc = qtaguid_stats_proc_read; + /* + * TODO: add support counter hacking + * xt_qtaguid_stats_file->write_proc = qtaguid_stats_proc_write; + */ + return 0; + +no_stats_entry: + remove_proc_entry("ctrl", *res_procdir); +no_ctrl_entry: + remove_proc_entry("xt_qtaguid", NULL); +no_dir: + return ret; +} + +static struct xt_match qtaguid_mt_reg __read_mostly = { + /* + * This module masquerades as the "owner" module so that iptables + * tools can deal with it. + */ + .name = "owner", + .revision = 1, + .family = NFPROTO_UNSPEC, + .match = qtaguid_mt, + .matchsize = sizeof(struct xt_qtaguid_match_info), + .me = THIS_MODULE, +}; + +static int __init qtaguid_mt_init(void) +{ + if (qtaguid_proc_register(&xt_qtaguid_procdir) + || iface_stat_init(xt_qtaguid_procdir) + || xt_register_match(&qtaguid_mt_reg) + || misc_register(&qtu_device)) + return -1; + return 0; +} + +/* + * TODO: allow unloading of the module. + * For now stats are permanent. + * Kconfig forces'y/n' and never an 'm'. + */ + +module_init(qtaguid_mt_init); +MODULE_AUTHOR("jpa "); +MODULE_DESCRIPTION("Xtables: socket owner+tag matching and associated stats"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ipt_owner"); +MODULE_ALIAS("ip6t_owner"); +MODULE_ALIAS("ipt_qtaguid"); +MODULE_ALIAS("ip6t_qtaguid"); diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h new file mode 100644 index 00000000..6dc14a9c --- /dev/null +++ b/net/netfilter/xt_qtaguid_internal.h @@ -0,0 +1,352 @@ +/* + * Kernel iptables module to track stats for packets based on user tags. + * + * (C) 2011 Google, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __XT_QTAGUID_INTERNAL_H__ +#define __XT_QTAGUID_INTERNAL_H__ + +#include +#include +#include +#include + +/* Iface handling */ +#define IDEBUG_MASK (1<<0) +/* Iptable Matching. Per packet. */ +#define MDEBUG_MASK (1<<1) +/* Red-black tree handling. Per packet. */ +#define RDEBUG_MASK (1<<2) +/* procfs ctrl/stats handling */ +#define CDEBUG_MASK (1<<3) +/* dev and resource tracking */ +#define DDEBUG_MASK (1<<4) + +/* E.g (IDEBUG_MASK | CDEBUG_MASK | DDEBUG_MASK) */ +#define DEFAULT_DEBUG_MASK 0 + +/* + * (Un)Define these *DEBUG to compile out/in the pr_debug calls. + * All undef: text size ~ 0x3030; all def: ~ 0x4404. + */ +#define IDEBUG +#define MDEBUG +#define RDEBUG +#define CDEBUG +#define DDEBUG + +#define MSK_DEBUG(mask, ...) do { \ + if (unlikely(qtaguid_debug_mask & (mask))) \ + pr_debug(__VA_ARGS__); \ + } while (0) +#ifdef IDEBUG +#define IF_DEBUG(...) MSK_DEBUG(IDEBUG_MASK, __VA_ARGS__) +#else +#define IF_DEBUG(...) no_printk(__VA_ARGS__) +#endif +#ifdef MDEBUG +#define MT_DEBUG(...) MSK_DEBUG(MDEBUG_MASK, __VA_ARGS__) +#else +#define MT_DEBUG(...) no_printk(__VA_ARGS__) +#endif +#ifdef RDEBUG +#define RB_DEBUG(...) MSK_DEBUG(RDEBUG_MASK, __VA_ARGS__) +#else +#define RB_DEBUG(...) no_printk(__VA_ARGS__) +#endif +#ifdef CDEBUG +#define CT_DEBUG(...) MSK_DEBUG(CDEBUG_MASK, __VA_ARGS__) +#else +#define CT_DEBUG(...) no_printk(__VA_ARGS__) +#endif +#ifdef DDEBUG +#define DR_DEBUG(...) MSK_DEBUG(DDEBUG_MASK, __VA_ARGS__) +#else +#define DR_DEBUG(...) no_printk(__VA_ARGS__) +#endif + +extern uint qtaguid_debug_mask; + +/*---------------------------------------------------------------------------*/ +/* + * Tags: + * + * They represent what the data usage counters will be tracked against. + * By default a tag is just based on the UID. + * The UID is used as the base for policing, and can not be ignored. + * So a tag will always at least represent a UID (uid_tag). + * + * A tag can be augmented with an "accounting tag" which is associated + * with a UID. + * User space can set the acct_tag portion of the tag which is then used + * with sockets: all data belonging to that socket will be counted against the + * tag. The policing is then based on the tag's uid_tag portion, + * and stats are collected for the acct_tag portion separately. + * + * There could be + * a: {acct_tag=1, uid_tag=10003} + * b: {acct_tag=2, uid_tag=10003} + * c: {acct_tag=3, uid_tag=10003} + * d: {acct_tag=0, uid_tag=10003} + * a, b, and c represent tags associated with specific sockets. + * d is for the totals for that uid, including all untagged traffic. + * Typically d is used with policing/quota rules. + * + * We want tag_t big enough to distinguish uid_t and acct_tag. + * It might become a struct if needed. + * Nothing should be using it as an int. + */ +typedef uint64_t tag_t; /* Only used via accessors */ + +#define TAG_UID_MASK 0xFFFFFFFFULL +#define TAG_ACCT_MASK (~0xFFFFFFFFULL) + +static inline int tag_compare(tag_t t1, tag_t t2) +{ + return t1 < t2 ? -1 : t1 == t2 ? 0 : 1; +} + +static inline tag_t combine_atag_with_uid(tag_t acct_tag, uid_t uid) +{ + return acct_tag | uid; +} +static inline tag_t make_tag_from_uid(uid_t uid) +{ + return uid; +} +static inline uid_t get_uid_from_tag(tag_t tag) +{ + return tag & TAG_UID_MASK; +} +static inline tag_t get_utag_from_tag(tag_t tag) +{ + return tag & TAG_UID_MASK; +} +static inline tag_t get_atag_from_tag(tag_t tag) +{ + return tag & TAG_ACCT_MASK; +} + +static inline bool valid_atag(tag_t tag) +{ + return !(tag & TAG_UID_MASK); +} +static inline tag_t make_atag_from_value(uint32_t value) +{ + return (uint64_t)value << 32; +} +/*---------------------------------------------------------------------------*/ + +/* + * Maximum number of socket tags that a UID is allowed to have active. + * Multiple processes belonging to the same UID contribute towards this limit. + * Special UIDs that can impersonate a UID also contribute (e.g. download + * manager, ...) + */ +#define DEFAULT_MAX_SOCK_TAGS 1024 + +/* + * For now we only track 2 sets of counters. + * The default set is 0. + * Userspace can activate another set for a given uid being tracked. + */ +#define IFS_MAX_COUNTER_SETS 2 + +enum ifs_tx_rx { + IFS_TX, + IFS_RX, + IFS_MAX_DIRECTIONS +}; + +/* For now, TCP, UDP, the rest */ +enum ifs_proto { + IFS_TCP, + IFS_UDP, + IFS_PROTO_OTHER, + IFS_MAX_PROTOS +}; + +struct byte_packet_counters { + uint64_t bytes; + uint64_t packets; +}; + +struct data_counters { + struct byte_packet_counters bpc[IFS_MAX_COUNTER_SETS][IFS_MAX_DIRECTIONS][IFS_MAX_PROTOS]; +}; + +static inline uint64_t dc_sum_bytes(struct data_counters *counters, + int set, + enum ifs_tx_rx direction) +{ + return counters->bpc[set][direction][IFS_TCP].bytes + + counters->bpc[set][direction][IFS_UDP].bytes + + counters->bpc[set][direction][IFS_PROTO_OTHER].bytes; +} + +static inline uint64_t dc_sum_packets(struct data_counters *counters, + int set, + enum ifs_tx_rx direction) +{ + return counters->bpc[set][direction][IFS_TCP].packets + + counters->bpc[set][direction][IFS_UDP].packets + + counters->bpc[set][direction][IFS_PROTO_OTHER].packets; +} + + +/* Generic X based nodes used as a base for rb_tree ops */ +struct tag_node { + struct rb_node node; + tag_t tag; +}; + +struct tag_stat { + struct tag_node tn; + struct data_counters counters; + /* + * If this tag is acct_tag based, we need to count against the + * matching parent uid_tag. + */ + struct data_counters *parent_counters; +}; + +struct iface_stat { + struct list_head list; /* in iface_stat_list */ + char *ifname; + bool active; + /* net_dev is only valid for active iface_stat */ + struct net_device *net_dev; + + struct byte_packet_counters totals_via_dev[IFS_MAX_DIRECTIONS]; + struct data_counters totals_via_skb; + /* + * We keep the last_known, because some devices reset their counters + * just before NETDEV_UP, while some will reset just before + * NETDEV_REGISTER (which is more normal). + * So now, if the device didn't do a NETDEV_UNREGISTER and we see + * its current dev stats smaller that what was previously known, we + * assume an UNREGISTER and just use the last_known. + */ + struct byte_packet_counters last_known[IFS_MAX_DIRECTIONS]; + /* last_known is usable when last_known_valid is true */ + bool last_known_valid; + + struct proc_dir_entry *proc_ptr; + + struct rb_root tag_stat_tree; + spinlock_t tag_stat_list_lock; +}; + +/* This is needed to create proc_dir_entries from atomic context. */ +struct iface_stat_work { + struct work_struct iface_work; + struct iface_stat *iface_entry; +}; + +/* + * Track tag that this socket is transferring data for, and not necessarily + * the uid that owns the socket. + * This is the tag against which tag_stat.counters will be billed. + * These structs need to be looked up by sock and pid. + */ +struct sock_tag { + struct rb_node sock_node; + struct sock *sk; /* Only used as a number, never dereferenced */ + /* The socket is needed for sockfd_put() */ + struct socket *socket; + /* Used to associate with a given pid */ + struct list_head list; /* in proc_qtu_data.sock_tag_list */ + pid_t pid; + + tag_t tag; +}; + +struct qtaguid_event_counts { + /* Various successful events */ + atomic64_t sockets_tagged; + atomic64_t sockets_untagged; + atomic64_t counter_set_changes; + atomic64_t delete_cmds; + atomic64_t iface_events; /* Number of NETDEV_* events handled */ + + atomic64_t match_calls; /* Number of times iptables called mt */ + /* Number of times iptables called mt from pre or post routing hooks */ + atomic64_t match_calls_prepost; + /* + * match_found_sk_*: numbers related to the netfilter matching + * function finding a sock for the sk_buff. + * Total skbs processed is sum(match_found*). + */ + atomic64_t match_found_sk; /* An sk was already in the sk_buff. */ + /* The connection tracker had or didn't have the sk. */ + atomic64_t match_found_sk_in_ct; + atomic64_t match_found_no_sk_in_ct; + /* + * No sk could be found. No apparent owner. Could happen with + * unsolicited traffic. + */ + atomic64_t match_no_sk; + /* + * The file ptr in the sk_socket wasn't there. + * This might happen for traffic while the socket is being closed. + */ + atomic64_t match_no_sk_file; +}; + +/* Track the set active_set for the given tag. */ +struct tag_counter_set { + struct tag_node tn; + int active_set; +}; + +/*----------------------------------------------*/ +/* + * The qtu uid data is used to track resources that are created directly or + * indirectly by processes (uid tracked). + * It is shared by the processes with the same uid. + * Some of the resource will be counted to prevent further rogue allocations, + * some will need freeing once the owner process (uid) exits. + */ +struct uid_tag_data { + struct rb_node node; + uid_t uid; + + /* + * For the uid, how many accounting tags have been set. + */ + int num_active_tags; + /* Track the number of proc_qtu_data that reference it */ + int num_pqd; + struct rb_root tag_ref_tree; + /* No tag_node_tree_lock; use uid_tag_data_tree_lock */ +}; + +struct tag_ref { + struct tag_node tn; + + /* + * This tracks the number of active sockets that have a tag on them + * which matches this tag_ref.tn.tag. + * A tag ref can live on after the sockets are untagged. + * A tag ref can only be removed during a tag delete command. + */ + int num_sock_tags; +}; + +struct proc_qtu_data { + struct rb_node node; + pid_t pid; + + struct uid_tag_data *parent_tag_data; + + /* Tracks the sock_tags that need freeing upon this proc's death */ + struct list_head sock_tag_list; + /* No spinlock_t sock_tag_list_lock; use the global one. */ +}; + +/*----------------------------------------------*/ +#endif /* ifndef __XT_QTAGUID_INTERNAL_H__ */ diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c new file mode 100644 index 00000000..f6a00a35 --- /dev/null +++ b/net/netfilter/xt_qtaguid_print.c @@ -0,0 +1,566 @@ +/* + * Pretty printing Support for iptables xt_qtaguid module. + * + * (C) 2011 Google, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * Most of the functions in this file just waste time if DEBUG is not defined. + * The matching xt_qtaguid_print.h will static inline empty funcs if the needed + * debug flags ore not defined. + * Those funcs that fail to allocate memory will panic as there is no need to + * hobble allong just pretending to do the requested work. + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include + + +#include "xt_qtaguid_internal.h" +#include "xt_qtaguid_print.h" + +#ifdef DDEBUG + +static void _bug_on_err_or_null(void *ptr) +{ + if (IS_ERR_OR_NULL(ptr)) { + pr_err("qtaguid: kmalloc failed\n"); + BUG(); + } +} + +char *pp_tag_t(tag_t *tag) +{ + char *res; + + if (!tag) + res = kasprintf(GFP_ATOMIC, "tag_t@null{}"); + else + res = kasprintf(GFP_ATOMIC, + "tag_t@%p{tag=0x%llx, uid=%u}", + tag, *tag, get_uid_from_tag(*tag)); + _bug_on_err_or_null(res); + return res; +} + +char *pp_data_counters(struct data_counters *dc, bool showValues) +{ + char *res; + + if (!dc) + res = kasprintf(GFP_ATOMIC, "data_counters@null{}"); + else if (showValues) + res = kasprintf( + GFP_ATOMIC, "data_counters@%p{" + "set0{" + "rx{" + "tcp{b=%llu, p=%llu}, " + "udp{b=%llu, p=%llu}," + "other{b=%llu, p=%llu}}, " + "tx{" + "tcp{b=%llu, p=%llu}, " + "udp{b=%llu, p=%llu}," + "other{b=%llu, p=%llu}}}, " + "set1{" + "rx{" + "tcp{b=%llu, p=%llu}, " + "udp{b=%llu, p=%llu}," + "other{b=%llu, p=%llu}}, " + "tx{" + "tcp{b=%llu, p=%llu}, " + "udp{b=%llu, p=%llu}," + "other{b=%llu, p=%llu}}}}", + dc, + dc->bpc[0][IFS_RX][IFS_TCP].bytes, + dc->bpc[0][IFS_RX][IFS_TCP].packets, + dc->bpc[0][IFS_RX][IFS_UDP].bytes, + dc->bpc[0][IFS_RX][IFS_UDP].packets, + dc->bpc[0][IFS_RX][IFS_PROTO_OTHER].bytes, + dc->bpc[0][IFS_RX][IFS_PROTO_OTHER].packets, + dc->bpc[0][IFS_TX][IFS_TCP].bytes, + dc->bpc[0][IFS_TX][IFS_TCP].packets, + dc->bpc[0][IFS_TX][IFS_UDP].bytes, + dc->bpc[0][IFS_TX][IFS_UDP].packets, + dc->bpc[0][IFS_TX][IFS_PROTO_OTHER].bytes, + dc->bpc[0][IFS_TX][IFS_PROTO_OTHER].packets, + dc->bpc[1][IFS_RX][IFS_TCP].bytes, + dc->bpc[1][IFS_RX][IFS_TCP].packets, + dc->bpc[1][IFS_RX][IFS_UDP].bytes, + dc->bpc[1][IFS_RX][IFS_UDP].packets, + dc->bpc[1][IFS_RX][IFS_PROTO_OTHER].bytes, + dc->bpc[1][IFS_RX][IFS_PROTO_OTHER].packets, + dc->bpc[1][IFS_TX][IFS_TCP].bytes, + dc->bpc[1][IFS_TX][IFS_TCP].packets, + dc->bpc[1][IFS_TX][IFS_UDP].bytes, + dc->bpc[1][IFS_TX][IFS_UDP].packets, + dc->bpc[1][IFS_TX][IFS_PROTO_OTHER].bytes, + dc->bpc[1][IFS_TX][IFS_PROTO_OTHER].packets); + else + res = kasprintf(GFP_ATOMIC, "data_counters@%p{...}", dc); + _bug_on_err_or_null(res); + return res; +} + +char *pp_tag_node(struct tag_node *tn) +{ + char *tag_str; + char *res; + + if (!tn) { + res = kasprintf(GFP_ATOMIC, "tag_node@null{}"); + _bug_on_err_or_null(res); + return res; + } + tag_str = pp_tag_t(&tn->tag); + res = kasprintf(GFP_ATOMIC, + "tag_node@%p{tag=%s}", + tn, tag_str); + _bug_on_err_or_null(res); + kfree(tag_str); + return res; +} + +char *pp_tag_ref(struct tag_ref *tr) +{ + char *tn_str; + char *res; + + if (!tr) { + res = kasprintf(GFP_ATOMIC, "tag_ref@null{}"); + _bug_on_err_or_null(res); + return res; + } + tn_str = pp_tag_node(&tr->tn); + res = kasprintf(GFP_ATOMIC, + "tag_ref@%p{%s, num_sock_tags=%d}", + tr, tn_str, tr->num_sock_tags); + _bug_on_err_or_null(res); + kfree(tn_str); + return res; +} + +char *pp_tag_stat(struct tag_stat *ts) +{ + char *tn_str; + char *counters_str; + char *parent_counters_str; + char *res; + + if (!ts) { + res = kasprintf(GFP_ATOMIC, "tag_stat@null{}"); + _bug_on_err_or_null(res); + return res; + } + tn_str = pp_tag_node(&ts->tn); + counters_str = pp_data_counters(&ts->counters, true); + parent_counters_str = pp_data_counters(ts->parent_counters, false); + res = kasprintf(GFP_ATOMIC, + "tag_stat@%p{%s, counters=%s, parent_counters=%s}", + ts, tn_str, counters_str, parent_counters_str); + _bug_on_err_or_null(res); + kfree(tn_str); + kfree(counters_str); + kfree(parent_counters_str); + return res; +} + +char *pp_iface_stat(struct iface_stat *is) +{ + char *res; + if (!is) { + res = kasprintf(GFP_ATOMIC, "iface_stat@null{}"); + } else { + struct data_counters *cnts = &is->totals_via_skb; + res = kasprintf(GFP_ATOMIC, "iface_stat@%p{" + "list=list_head{...}, " + "ifname=%s, " + "total_dev={rx={bytes=%llu, " + "packets=%llu}, " + "tx={bytes=%llu, " + "packets=%llu}}, " + "total_skb={rx={bytes=%llu, " + "packets=%llu}, " + "tx={bytes=%llu, " + "packets=%llu}}, " + "last_known_valid=%d, " + "last_known={rx={bytes=%llu, " + "packets=%llu}, " + "tx={bytes=%llu, " + "packets=%llu}}, " + "active=%d, " + "net_dev=%p, " + "proc_ptr=%p, " + "tag_stat_tree=rb_root{...}}", + is, + is->ifname, + is->totals_via_dev[IFS_RX].bytes, + is->totals_via_dev[IFS_RX].packets, + is->totals_via_dev[IFS_TX].bytes, + is->totals_via_dev[IFS_TX].packets, + dc_sum_bytes(cnts, 0, IFS_RX), + dc_sum_packets(cnts, 0, IFS_RX), + dc_sum_bytes(cnts, 0, IFS_TX), + dc_sum_packets(cnts, 0, IFS_TX), + is->last_known_valid, + is->last_known[IFS_RX].bytes, + is->last_known[IFS_RX].packets, + is->last_known[IFS_TX].bytes, + is->last_known[IFS_TX].packets, + is->active, + is->net_dev, + is->proc_ptr); + } + _bug_on_err_or_null(res); + return res; +} + +char *pp_sock_tag(struct sock_tag *st) +{ + char *tag_str; + char *res; + + if (!st) { + res = kasprintf(GFP_ATOMIC, "sock_tag@null{}"); + _bug_on_err_or_null(res); + return res; + } + tag_str = pp_tag_t(&st->tag); + res = kasprintf(GFP_ATOMIC, "sock_tag@%p{" + "sock_node=rb_node{...}, " + "sk=%p socket=%p (f_count=%lu), list=list_head{...}, " + "pid=%u, tag=%s}", + st, st->sk, st->socket, atomic_long_read( + &st->socket->file->f_count), + st->pid, tag_str); + _bug_on_err_or_null(res); + kfree(tag_str); + return res; +} + +char *pp_uid_tag_data(struct uid_tag_data *utd) +{ + char *res; + + if (!utd) + res = kasprintf(GFP_ATOMIC, "uid_tag_data@null{}"); + else + res = kasprintf(GFP_ATOMIC, "uid_tag_data@%p{" + "uid=%u, num_active_acct_tags=%d, " + "num_pqd=%d, " + "tag_node_tree=rb_root{...}, " + "proc_qtu_data_tree=rb_root{...}}", + utd, utd->uid, + utd->num_active_tags, utd->num_pqd); + _bug_on_err_or_null(res); + return res; +} + +char *pp_proc_qtu_data(struct proc_qtu_data *pqd) +{ + char *parent_tag_data_str; + char *res; + + if (!pqd) { + res = kasprintf(GFP_ATOMIC, "proc_qtu_data@null{}"); + _bug_on_err_or_null(res); + return res; + } + parent_tag_data_str = pp_uid_tag_data(pqd->parent_tag_data); + res = kasprintf(GFP_ATOMIC, "proc_qtu_data@%p{" + "node=rb_node{...}, pid=%u, " + "parent_tag_data=%s, " + "sock_tag_list=list_head{...}}", + pqd, pqd->pid, parent_tag_data_str + ); + _bug_on_err_or_null(res); + kfree(parent_tag_data_str); + return res; +} + +/*------------------------------------------*/ +void prdebug_sock_tag_tree(int indent_level, + struct rb_root *sock_tag_tree) +{ + struct rb_node *node; + struct sock_tag *sock_tag_entry; + char *str; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (RB_EMPTY_ROOT(sock_tag_tree)) { + str = "sock_tag_tree=rb_root{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "sock_tag_tree=rb_root{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + for (node = rb_first(sock_tag_tree); + node; + node = rb_next(node)) { + sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); + str = pp_sock_tag(sock_tag_entry); + pr_debug("%*d: %s,\n", indent_level*2, indent_level, str); + kfree(str); + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_sock_tag_list(int indent_level, + struct list_head *sock_tag_list) +{ + struct sock_tag *sock_tag_entry; + char *str; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (list_empty(sock_tag_list)) { + str = "sock_tag_list=list_head{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "sock_tag_list=list_head{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + list_for_each_entry(sock_tag_entry, sock_tag_list, list) { + str = pp_sock_tag(sock_tag_entry); + pr_debug("%*d: %s,\n", indent_level*2, indent_level, str); + kfree(str); + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_proc_qtu_data_tree(int indent_level, + struct rb_root *proc_qtu_data_tree) +{ + char *str; + struct rb_node *node; + struct proc_qtu_data *proc_qtu_data_entry; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (RB_EMPTY_ROOT(proc_qtu_data_tree)) { + str = "proc_qtu_data_tree=rb_root{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "proc_qtu_data_tree=rb_root{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + for (node = rb_first(proc_qtu_data_tree); + node; + node = rb_next(node)) { + proc_qtu_data_entry = rb_entry(node, + struct proc_qtu_data, + node); + str = pp_proc_qtu_data(proc_qtu_data_entry); + pr_debug("%*d: %s,\n", indent_level*2, indent_level, + str); + kfree(str); + indent_level++; + prdebug_sock_tag_list(indent_level, + &proc_qtu_data_entry->sock_tag_list); + indent_level--; + + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree) +{ + char *str; + struct rb_node *node; + struct tag_ref *tag_ref_entry; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (RB_EMPTY_ROOT(tag_ref_tree)) { + str = "tag_ref_tree{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "tag_ref_tree{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + for (node = rb_first(tag_ref_tree); + node; + node = rb_next(node)) { + tag_ref_entry = rb_entry(node, + struct tag_ref, + tn.node); + str = pp_tag_ref(tag_ref_entry); + pr_debug("%*d: %s,\n", indent_level*2, indent_level, + str); + kfree(str); + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_uid_tag_data_tree(int indent_level, + struct rb_root *uid_tag_data_tree) +{ + char *str; + struct rb_node *node; + struct uid_tag_data *uid_tag_data_entry; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (RB_EMPTY_ROOT(uid_tag_data_tree)) { + str = "uid_tag_data_tree=rb_root{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "uid_tag_data_tree=rb_root{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + for (node = rb_first(uid_tag_data_tree); + node; + node = rb_next(node)) { + uid_tag_data_entry = rb_entry(node, struct uid_tag_data, + node); + str = pp_uid_tag_data(uid_tag_data_entry); + pr_debug("%*d: %s,\n", indent_level*2, indent_level, str); + kfree(str); + if (!RB_EMPTY_ROOT(&uid_tag_data_entry->tag_ref_tree)) { + indent_level++; + prdebug_tag_ref_tree(indent_level, + &uid_tag_data_entry->tag_ref_tree); + indent_level--; + } + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_tag_stat_tree(int indent_level, + struct rb_root *tag_stat_tree) +{ + char *str; + struct rb_node *node; + struct tag_stat *ts_entry; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (RB_EMPTY_ROOT(tag_stat_tree)) { + str = "tag_stat_tree{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "tag_stat_tree{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + for (node = rb_first(tag_stat_tree); + node; + node = rb_next(node)) { + ts_entry = rb_entry(node, struct tag_stat, tn.node); + str = pp_tag_stat(ts_entry); + pr_debug("%*d: %s\n", indent_level*2, indent_level, + str); + kfree(str); + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_iface_stat_list(int indent_level, + struct list_head *iface_stat_list) +{ + char *str; + struct iface_stat *iface_entry; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (list_empty(iface_stat_list)) { + str = "iface_stat_list=list_head{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "iface_stat_list=list_head{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + list_for_each_entry(iface_entry, iface_stat_list, list) { + str = pp_iface_stat(iface_entry); + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + kfree(str); + + spin_lock_bh(&iface_entry->tag_stat_list_lock); + if (!RB_EMPTY_ROOT(&iface_entry->tag_stat_tree)) { + indent_level++; + prdebug_tag_stat_tree(indent_level, + &iface_entry->tag_stat_tree); + indent_level--; + } + spin_unlock_bh(&iface_entry->tag_stat_list_lock); + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +#endif /* ifdef DDEBUG */ +/*------------------------------------------*/ +static const char * const netdev_event_strings[] = { + "netdev_unknown", + "NETDEV_UP", + "NETDEV_DOWN", + "NETDEV_REBOOT", + "NETDEV_CHANGE", + "NETDEV_REGISTER", + "NETDEV_UNREGISTER", + "NETDEV_CHANGEMTU", + "NETDEV_CHANGEADDR", + "NETDEV_GOING_DOWN", + "NETDEV_CHANGENAME", + "NETDEV_FEAT_CHANGE", + "NETDEV_BONDING_FAILOVER", + "NETDEV_PRE_UP", + "NETDEV_PRE_TYPE_CHANGE", + "NETDEV_POST_TYPE_CHANGE", + "NETDEV_POST_INIT", + "NETDEV_UNREGISTER_BATCH", + "NETDEV_RELEASE", + "NETDEV_NOTIFY_PEERS", + "NETDEV_JOIN", +}; + +const char *netdev_evt_str(int netdev_event) +{ + if (netdev_event < 0 + || netdev_event >= ARRAY_SIZE(netdev_event_strings)) + return "bad event num"; + return netdev_event_strings[netdev_event]; +} diff --git a/net/netfilter/xt_qtaguid_print.h b/net/netfilter/xt_qtaguid_print.h new file mode 100644 index 00000000..b63871a0 --- /dev/null +++ b/net/netfilter/xt_qtaguid_print.h @@ -0,0 +1,120 @@ +/* + * Pretty printing Support for iptables xt_qtaguid module. + * + * (C) 2011 Google, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __XT_QTAGUID_PRINT_H__ +#define __XT_QTAGUID_PRINT_H__ + +#include "xt_qtaguid_internal.h" + +#ifdef DDEBUG + +char *pp_tag_t(tag_t *tag); +char *pp_data_counters(struct data_counters *dc, bool showValues); +char *pp_tag_node(struct tag_node *tn); +char *pp_tag_ref(struct tag_ref *tr); +char *pp_tag_stat(struct tag_stat *ts); +char *pp_iface_stat(struct iface_stat *is); +char *pp_sock_tag(struct sock_tag *st); +char *pp_uid_tag_data(struct uid_tag_data *qtd); +char *pp_proc_qtu_data(struct proc_qtu_data *pqd); + +/*------------------------------------------*/ +void prdebug_sock_tag_list(int indent_level, + struct list_head *sock_tag_list); +void prdebug_sock_tag_tree(int indent_level, + struct rb_root *sock_tag_tree); +void prdebug_proc_qtu_data_tree(int indent_level, + struct rb_root *proc_qtu_data_tree); +void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree); +void prdebug_uid_tag_data_tree(int indent_level, + struct rb_root *uid_tag_data_tree); +void prdebug_tag_stat_tree(int indent_level, + struct rb_root *tag_stat_tree); +void prdebug_iface_stat_list(int indent_level, + struct list_head *iface_stat_list); + +#else + +/*------------------------------------------*/ +static inline char *pp_tag_t(tag_t *tag) +{ + return NULL; +} +static inline char *pp_data_counters(struct data_counters *dc, bool showValues) +{ + return NULL; +} +static inline char *pp_tag_node(struct tag_node *tn) +{ + return NULL; +} +static inline char *pp_tag_ref(struct tag_ref *tr) +{ + return NULL; +} +static inline char *pp_tag_stat(struct tag_stat *ts) +{ + return NULL; +} +static inline char *pp_iface_stat(struct iface_stat *is) +{ + return NULL; +} +static inline char *pp_sock_tag(struct sock_tag *st) +{ + return NULL; +} +static inline char *pp_uid_tag_data(struct uid_tag_data *qtd) +{ + return NULL; +} +static inline char *pp_proc_qtu_data(struct proc_qtu_data *pqd) +{ + return NULL; +} + +/*------------------------------------------*/ +static inline +void prdebug_sock_tag_list(int indent_level, + struct list_head *sock_tag_list) +{ +} +static inline +void prdebug_sock_tag_tree(int indent_level, + struct rb_root *sock_tag_tree) +{ +} +static inline +void prdebug_proc_qtu_data_tree(int indent_level, + struct rb_root *proc_qtu_data_tree) +{ +} +static inline +void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree) +{ +} +static inline +void prdebug_uid_tag_data_tree(int indent_level, + struct rb_root *uid_tag_data_tree) +{ +} +static inline +void prdebug_tag_stat_tree(int indent_level, + struct rb_root *tag_stat_tree) +{ +} +static inline +void prdebug_iface_stat_list(int indent_level, + struct list_head *iface_stat_list) +{ +} +#endif +/*------------------------------------------*/ +const char *netdev_evt_str(int netdev_event); +#endif /* ifndef __XT_QTAGUID_PRINT_H__ */ diff --git a/net/netfilter/xt_quota2.c b/net/netfilter/xt_quota2.c new file mode 100644 index 00000000..fb2ef46b --- /dev/null +++ b/net/netfilter/xt_quota2.c @@ -0,0 +1,382 @@ +/* + * xt_quota2 - enhanced xt_quota that can count upwards and in packets + * as a minimal accounting match. + * by Jan Engelhardt , 2008 + * + * Originally based on xt_quota.c: + * netfilter module to enforce network quotas + * Sam Johnston + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License; either + * version 2 of the License, as published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG +#include +#endif + +/** + * @lock: lock to protect quota writers from each other + */ +struct xt_quota_counter { + u_int64_t quota; + spinlock_t lock; + struct list_head list; + atomic_t ref; + char name[sizeof(((struct xt_quota_mtinfo2 *)NULL)->name)]; + struct proc_dir_entry *procfs_entry; +}; + +#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG +/* Harald's favorite number +1 :D From ipt_ULOG.C */ +static int qlog_nl_event = 112; +module_param_named(event_num, qlog_nl_event, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(event_num, + "Event number for NETLINK_NFLOG message. 0 disables log." + "111 is what ipt_ULOG uses."); +static struct sock *nflognl; +#endif + +static LIST_HEAD(counter_list); +static DEFINE_SPINLOCK(counter_list_lock); + +static struct proc_dir_entry *proc_xt_quota; +static unsigned int quota_list_perms = S_IRUGO | S_IWUSR; +static unsigned int quota_list_uid = 0; +static unsigned int quota_list_gid = 0; +module_param_named(perms, quota_list_perms, uint, S_IRUGO | S_IWUSR); +module_param_named(uid, quota_list_uid, uint, S_IRUGO | S_IWUSR); +module_param_named(gid, quota_list_gid, uint, S_IRUGO | S_IWUSR); + + +#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG +static void quota2_log(unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const char *prefix) +{ + ulog_packet_msg_t *pm; + struct sk_buff *log_skb; + size_t size; + struct nlmsghdr *nlh; + + if (!qlog_nl_event) + return; + + size = NLMSG_SPACE(sizeof(*pm)); + size = max(size, (size_t)NLMSG_GOODSIZE); + log_skb = alloc_skb(size, GFP_ATOMIC); + if (!log_skb) { + pr_err("xt_quota2: cannot alloc skb for logging\n"); + return; + } + + /* NLMSG_PUT() uses "goto nlmsg_failure" */ + nlh = NLMSG_PUT(log_skb, /*pid*/0, /*seq*/0, qlog_nl_event, + sizeof(*pm)); + pm = NLMSG_DATA(nlh); + if (skb->tstamp.tv64 == 0) + __net_timestamp((struct sk_buff *)skb); + pm->data_len = 0; + pm->hook = hooknum; + if (prefix != NULL) + strlcpy(pm->prefix, prefix, sizeof(pm->prefix)); + else + *(pm->prefix) = '\0'; + if (in) + strlcpy(pm->indev_name, in->name, sizeof(pm->indev_name)); + else + pm->indev_name[0] = '\0'; + + if (out) + strlcpy(pm->outdev_name, out->name, sizeof(pm->outdev_name)); + else + pm->outdev_name[0] = '\0'; + + NETLINK_CB(log_skb).dst_group = 1; + pr_debug("throwing 1 packets to netlink group 1\n"); + netlink_broadcast(nflognl, log_skb, 0, 1, GFP_ATOMIC); + +nlmsg_failure: /* Used within NLMSG_PUT() */ + pr_debug("xt_quota2: error during NLMSG_PUT\n"); +} +#else +static void quota2_log(unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const char *prefix) +{ +} +#endif /* if+else CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG */ + +static int quota_proc_read(char *page, char **start, off_t offset, + int count, int *eof, void *data) +{ + struct xt_quota_counter *e = data; + int ret; + + spin_lock_bh(&e->lock); + ret = snprintf(page, PAGE_SIZE, "%llu\n", e->quota); + spin_unlock_bh(&e->lock); + return ret; +} + +static int quota_proc_write(struct file *file, const char __user *input, + unsigned long size, void *data) +{ + struct xt_quota_counter *e = data; + char buf[sizeof("18446744073709551616")]; + + if (size > sizeof(buf)) + size = sizeof(buf); + if (copy_from_user(buf, input, size) != 0) + return -EFAULT; + buf[sizeof(buf)-1] = '\0'; + + spin_lock_bh(&e->lock); + e->quota = simple_strtoull(buf, NULL, 0); + spin_unlock_bh(&e->lock); + return size; +} + +static struct xt_quota_counter * +q2_new_counter(const struct xt_quota_mtinfo2 *q, bool anon) +{ + struct xt_quota_counter *e; + unsigned int size; + + /* Do not need all the procfs things for anonymous counters. */ + size = anon ? offsetof(typeof(*e), list) : sizeof(*e); + e = kmalloc(size, GFP_KERNEL); + if (e == NULL) + return NULL; + + e->quota = q->quota; + spin_lock_init(&e->lock); + if (!anon) { + INIT_LIST_HEAD(&e->list); + atomic_set(&e->ref, 1); + strlcpy(e->name, q->name, sizeof(e->name)); + } + return e; +} + +/** + * q2_get_counter - get ref to counter or create new + * @name: name of counter + */ +static struct xt_quota_counter * +q2_get_counter(const struct xt_quota_mtinfo2 *q) +{ + struct proc_dir_entry *p; + struct xt_quota_counter *e = NULL; + struct xt_quota_counter *new_e; + + if (*q->name == '\0') + return q2_new_counter(q, true); + + /* No need to hold a lock while getting a new counter */ + new_e = q2_new_counter(q, false); + if (new_e == NULL) + goto out; + + spin_lock_bh(&counter_list_lock); + list_for_each_entry(e, &counter_list, list) + if (strcmp(e->name, q->name) == 0) { + atomic_inc(&e->ref); + spin_unlock_bh(&counter_list_lock); + kfree(new_e); + pr_debug("xt_quota2: old counter name=%s", e->name); + return e; + } + e = new_e; + pr_debug("xt_quota2: new_counter name=%s", e->name); + list_add_tail(&e->list, &counter_list); + /* The entry having a refcount of 1 is not directly destructible. + * This func has not yet returned the new entry, thus iptables + * has not references for destroying this entry. + * For another rule to try to destroy it, it would 1st need for this + * func* to be re-invoked, acquire a new ref for the same named quota. + * Nobody will access the e->procfs_entry either. + * So release the lock. */ + spin_unlock_bh(&counter_list_lock); + + /* create_proc_entry() is not spin_lock happy */ + p = e->procfs_entry = create_proc_entry(e->name, quota_list_perms, + proc_xt_quota); + + if (IS_ERR_OR_NULL(p)) { + spin_lock_bh(&counter_list_lock); + list_del(&e->list); + spin_unlock_bh(&counter_list_lock); + goto out; + } + p->data = e; + p->read_proc = quota_proc_read; + p->write_proc = quota_proc_write; + p->uid = quota_list_uid; + p->gid = quota_list_gid; + return e; + + out: + kfree(e); + return NULL; +} + +static int quota_mt2_check(const struct xt_mtchk_param *par) +{ + struct xt_quota_mtinfo2 *q = par->matchinfo; + + pr_debug("xt_quota2: check() flags=0x%04x", q->flags); + + if (q->flags & ~XT_QUOTA_MASK) + return -EINVAL; + + q->name[sizeof(q->name)-1] = '\0'; + if (*q->name == '.' || strchr(q->name, '/') != NULL) { + printk(KERN_ERR "xt_quota.3: illegal name\n"); + return -EINVAL; + } + + q->master = q2_get_counter(q); + if (q->master == NULL) { + printk(KERN_ERR "xt_quota.3: memory alloc failure\n"); + return -ENOMEM; + } + + return 0; +} + +static void quota_mt2_destroy(const struct xt_mtdtor_param *par) +{ + struct xt_quota_mtinfo2 *q = par->matchinfo; + struct xt_quota_counter *e = q->master; + + if (*q->name == '\0') { + kfree(e); + return; + } + + spin_lock_bh(&counter_list_lock); + if (!atomic_dec_and_test(&e->ref)) { + spin_unlock_bh(&counter_list_lock); + return; + } + + list_del(&e->list); + remove_proc_entry(e->name, proc_xt_quota); + spin_unlock_bh(&counter_list_lock); + kfree(e); +} + +static bool +quota_mt2(const struct sk_buff *skb, struct xt_action_param *par) +{ + struct xt_quota_mtinfo2 *q = (void *)par->matchinfo; + struct xt_quota_counter *e = q->master; + bool ret = q->flags & XT_QUOTA_INVERT; + + spin_lock_bh(&e->lock); + if (q->flags & XT_QUOTA_GROW) { + /* + * While no_change is pointless in "grow" mode, we will + * implement it here simply to have a consistent behavior. + */ + if (!(q->flags & XT_QUOTA_NO_CHANGE)) { + e->quota += (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; + } + ret = true; + } else { + if (e->quota >= skb->len) { + if (!(q->flags & XT_QUOTA_NO_CHANGE)) + e->quota -= (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; + ret = !ret; + } else { + /* We are transitioning, log that fact. */ + if (e->quota) { + quota2_log(par->hooknum, + skb, + par->in, + par->out, + q->name); + } + /* we do not allow even small packets from now on */ + e->quota = 0; + } + } + spin_unlock_bh(&e->lock); + return ret; +} + +static struct xt_match quota_mt2_reg[] __read_mostly = { + { + .name = "quota2", + .revision = 3, + .family = NFPROTO_IPV4, + .checkentry = quota_mt2_check, + .match = quota_mt2, + .destroy = quota_mt2_destroy, + .matchsize = sizeof(struct xt_quota_mtinfo2), + .me = THIS_MODULE, + }, + { + .name = "quota2", + .revision = 3, + .family = NFPROTO_IPV6, + .checkentry = quota_mt2_check, + .match = quota_mt2, + .destroy = quota_mt2_destroy, + .matchsize = sizeof(struct xt_quota_mtinfo2), + .me = THIS_MODULE, + }, +}; + +static int __init quota_mt2_init(void) +{ + int ret; + pr_debug("xt_quota2: init()"); + +#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG + nflognl = netlink_kernel_create(&init_net, + NETLINK_NFLOG, 1, NULL, + NULL, THIS_MODULE); + if (!nflognl) + return -ENOMEM; +#endif + + proc_xt_quota = proc_mkdir("xt_quota", init_net.proc_net); + if (proc_xt_quota == NULL) + return -EACCES; + + ret = xt_register_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); + if (ret < 0) + remove_proc_entry("xt_quota", init_net.proc_net); + pr_debug("xt_quota2: init() %d", ret); + return ret; +} + +static void __exit quota_mt2_exit(void) +{ + xt_unregister_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); + remove_proc_entry("xt_quota", init_net.proc_net); +} + +module_init(quota_mt2_init); +module_exit(quota_mt2_exit); +MODULE_DESCRIPTION("Xtables: countdown quota match; up counter"); +MODULE_AUTHOR("Sam Johnston "); +MODULE_AUTHOR("Jan Engelhardt "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ipt_quota2"); +MODULE_ALIAS("ip6t_quota2"); diff --git a/net/netfilter/xt_rateest.c b/net/netfilter/xt_rateest.c index ed0db15a..f264032b 100644 --- a/net/netfilter/xt_rateest.c +++ b/net/netfilter/xt_rateest.c @@ -8,150 +8,188 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include -#include +#include #include +static DEFINE_MUTEX(xt_rateest_mutex); -static bool -xt_rateest_mt(const struct sk_buff *skb, struct xt_action_param *par) +#define RATEEST_HSIZE 16 +static struct hlist_head rateest_hash[RATEEST_HSIZE] __read_mostly; +static unsigned int jhash_rnd __read_mostly; +static bool rnd_inited __read_mostly; + +static unsigned int xt_rateest_hash(const char *name) { - const struct xt_rateest_match_info *info = par->matchinfo; - struct gnet_stats_rate_est *r; - u_int32_t bps1, bps2, pps1, pps2; - bool ret = true; - - spin_lock_bh(&info->est1->lock); - r = &info->est1->rstats; - if (info->flags & XT_RATEEST_MATCH_DELTA) { - bps1 = info->bps1 >= r->bps ? info->bps1 - r->bps : 0; - pps1 = info->pps1 >= r->pps ? info->pps1 - r->pps : 0; - } else { - bps1 = r->bps; - pps1 = r->pps; - } - spin_unlock_bh(&info->est1->lock); - - if (info->flags & XT_RATEEST_MATCH_ABS) { - bps2 = info->bps2; - pps2 = info->pps2; - } else { - spin_lock_bh(&info->est2->lock); - r = &info->est2->rstats; - if (info->flags & XT_RATEEST_MATCH_DELTA) { - bps2 = info->bps2 >= r->bps ? info->bps2 - r->bps : 0; - pps2 = info->pps2 >= r->pps ? info->pps2 - r->pps : 0; - } else { - bps2 = r->bps; - pps2 = r->pps; + return jhash(name, FIELD_SIZEOF(struct xt_rateest, name), jhash_rnd) & + (RATEEST_HSIZE - 1); +} + +static void xt_rateest_hash_insert(struct xt_rateest *est) +{ + unsigned int h; + + h = xt_rateest_hash(est->name); + hlist_add_head(&est->list, &rateest_hash[h]); +} + +struct xt_rateest *xt_rateest_lookup(const char *name) +{ + struct xt_rateest *est; + struct hlist_node *n; + unsigned int h; + + h = xt_rateest_hash(name); + mutex_lock(&xt_rateest_mutex); + hlist_for_each_entry(est, n, &rateest_hash[h], list) { + if (strcmp(est->name, name) == 0) { + est->refcnt++; + mutex_unlock(&xt_rateest_mutex); + return est; } - spin_unlock_bh(&info->est2->lock); } + mutex_unlock(&xt_rateest_mutex); + return NULL; +} +EXPORT_SYMBOL_GPL(xt_rateest_lookup); - switch (info->mode) { - case XT_RATEEST_MATCH_LT: - if (info->flags & XT_RATEEST_MATCH_BPS) - ret &= bps1 < bps2; - if (info->flags & XT_RATEEST_MATCH_PPS) - ret &= pps1 < pps2; - break; - case XT_RATEEST_MATCH_GT: - if (info->flags & XT_RATEEST_MATCH_BPS) - ret &= bps1 > bps2; - if (info->flags & XT_RATEEST_MATCH_PPS) - ret &= pps1 > pps2; - break; - case XT_RATEEST_MATCH_EQ: - if (info->flags & XT_RATEEST_MATCH_BPS) - ret &= bps1 == bps2; - if (info->flags & XT_RATEEST_MATCH_PPS) - ret &= pps1 == pps2; - break; +void xt_rateest_put(struct xt_rateest *est) +{ + mutex_lock(&xt_rateest_mutex); + if (--est->refcnt == 0) { + hlist_del(&est->list); + gen_kill_estimator(&est->bstats, &est->rstats); + /* + * gen_estimator est_timer() might access est->lock or bstats, + * wait a RCU grace period before freeing 'est' + */ + kfree_rcu(est, rcu); } - - ret ^= info->flags & XT_RATEEST_MATCH_INVERT ? true : false; - return ret; + mutex_unlock(&xt_rateest_mutex); } +EXPORT_SYMBOL_GPL(xt_rateest_put); -static int xt_rateest_mt_checkentry(const struct xt_mtchk_param *par) +static unsigned int +xt_rateest_tg(struct sk_buff *skb, const struct xt_action_param *par) { - struct xt_rateest_match_info *info = par->matchinfo; - struct xt_rateest *est1, *est2; - int ret = -EINVAL; + const struct xt_rateest_target_info *info = par->targinfo; + struct gnet_stats_basic_packed *stats = &info->est->bstats; - if (hweight32(info->flags & (XT_RATEEST_MATCH_ABS | - XT_RATEEST_MATCH_REL)) != 1) - goto err1; + spin_lock_bh(&info->est->lock); + stats->bytes += skb->len; + stats->packets++; + spin_unlock_bh(&info->est->lock); - if (!(info->flags & (XT_RATEEST_MATCH_BPS | XT_RATEEST_MATCH_PPS))) - goto err1; + return XT_CONTINUE; +} - switch (info->mode) { - case XT_RATEEST_MATCH_EQ: - case XT_RATEEST_MATCH_LT: - case XT_RATEEST_MATCH_GT: - break; - default: - goto err1; +static int xt_rateest_tg_checkentry(const struct xt_tgchk_param *par) +{ + struct xt_rateest_target_info *info = par->targinfo; + struct xt_rateest *est; + struct { + struct nlattr opt; + struct gnet_estimator est; + } cfg; + int ret; + + if (unlikely(!rnd_inited)) { + get_random_bytes(&jhash_rnd, sizeof(jhash_rnd)); + rnd_inited = true; + } + + est = xt_rateest_lookup(info->name); + if (est) { + /* + * If estimator parameters are specified, they must match the + * existing estimator. + */ + if ((!info->interval && !info->ewma_log) || + (info->interval != est->params.interval || + info->ewma_log != est->params.ewma_log)) { + xt_rateest_put(est); + return -EINVAL; + } + info->est = est; + return 0; } - ret = -ENOENT; - est1 = xt_rateest_lookup(info->name1); - if (!est1) + ret = -ENOMEM; + est = kzalloc(sizeof(*est), GFP_KERNEL); + if (!est) goto err1; - est2 = NULL; - if (info->flags & XT_RATEEST_MATCH_REL) { - est2 = xt_rateest_lookup(info->name2); - if (!est2) - goto err2; - } + strlcpy(est->name, info->name, sizeof(est->name)); + spin_lock_init(&est->lock); + est->refcnt = 1; + est->params.interval = info->interval; + est->params.ewma_log = info->ewma_log; + + cfg.opt.nla_len = nla_attr_size(sizeof(cfg.est)); + cfg.opt.nla_type = TCA_STATS_RATE_EST; + cfg.est.interval = info->interval; + cfg.est.ewma_log = info->ewma_log; + + ret = gen_new_estimator(&est->bstats, &est->rstats, + &est->lock, &cfg.opt); + if (ret < 0) + goto err2; - info->est1 = est1; - info->est2 = est2; + info->est = est; + xt_rateest_hash_insert(est); return 0; err2: - xt_rateest_put(est1); + kfree(est); err1: return ret; } -static void xt_rateest_mt_destroy(const struct xt_mtdtor_param *par) +static void xt_rateest_tg_destroy(const struct xt_tgdtor_param *par) { - struct xt_rateest_match_info *info = par->matchinfo; + struct xt_rateest_target_info *info = par->targinfo; - xt_rateest_put(info->est1); - if (info->est2) - xt_rateest_put(info->est2); + xt_rateest_put(info->est); } -static struct xt_match xt_rateest_mt_reg __read_mostly = { - .name = "rateest", +static struct xt_target xt_rateest_tg_reg __read_mostly = { + .name = "RATEEST", .revision = 0, .family = NFPROTO_UNSPEC, - .match = xt_rateest_mt, - .checkentry = xt_rateest_mt_checkentry, - .destroy = xt_rateest_mt_destroy, - .matchsize = sizeof(struct xt_rateest_match_info), + .target = xt_rateest_tg, + .checkentry = xt_rateest_tg_checkentry, + .destroy = xt_rateest_tg_destroy, + .targetsize = sizeof(struct xt_rateest_target_info), .me = THIS_MODULE, }; -static int __init xt_rateest_mt_init(void) +static int __init xt_rateest_tg_init(void) { - return xt_register_match(&xt_rateest_mt_reg); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(rateest_hash); i++) + INIT_HLIST_HEAD(&rateest_hash[i]); + + return xt_register_target(&xt_rateest_tg_reg); } -static void __exit xt_rateest_mt_fini(void) +static void __exit xt_rateest_tg_fini(void) { - xt_unregister_match(&xt_rateest_mt_reg); + xt_unregister_target(&xt_rateest_tg_reg); } + MODULE_AUTHOR("Patrick McHardy "); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("xtables rate estimator match"); -MODULE_ALIAS("ipt_rateest"); -MODULE_ALIAS("ip6t_rateest"); -module_init(xt_rateest_mt_init); -module_exit(xt_rateest_mt_fini); +MODULE_DESCRIPTION("Xtables: packet rate estimator"); +MODULE_ALIAS("ipt_RATEEST"); +MODULE_ALIAS("ip6t_RATEEST"); +module_init(xt_rateest_tg_init); +module_exit(xt_rateest_tg_fini); diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index 72bb07f5..1e48fcf2 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c @@ -35,7 +35,7 @@ #include #endif -static void +void xt_socket_put_sk(struct sock *sk) { if (sk->sk_state == TCP_TIME_WAIT) @@ -43,6 +43,7 @@ xt_socket_put_sk(struct sock *sk) else sock_put(sk); } +EXPORT_SYMBOL(xt_socket_put_sk); static int extract_icmp4_fields(const struct sk_buff *skb, @@ -101,9 +102,8 @@ extract_icmp4_fields(const struct sk_buff *skb, return 0; } -static bool -socket_match(const struct sk_buff *skb, struct xt_action_param *par, - const struct xt_socket_mtinfo1 *info) +struct sock* +xt_socket_get4_sk(const struct sk_buff *skb, struct xt_action_param *par) { const struct iphdr *iph = ip_hdr(skb); struct udphdr _hdr, *hp = NULL; @@ -120,7 +120,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_hdr), &_hdr); if (hp == NULL) - return false; + return NULL; protocol = iph->protocol; saddr = iph->saddr; @@ -131,9 +131,9 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, } else if (iph->protocol == IPPROTO_ICMP) { if (extract_icmp4_fields(skb, &protocol, &saddr, &daddr, &sport, &dport)) - return false; + return NULL; } else { - return false; + return NULL; } #ifdef XT_SOCKET_HAVE_CONNTRACK @@ -157,6 +157,23 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), protocol, saddr, daddr, sport, dport, par->in, NFT_LOOKUP_ANY); + + pr_debug("proto %hhu %pI4:%hu -> %pI4:%hu (orig %pI4:%hu) sock %p\n", + protocol, &saddr, ntohs(sport), + &daddr, ntohs(dport), + &iph->daddr, hp ? ntohs(hp->dest) : 0, sk); + + return sk; +} +EXPORT_SYMBOL(xt_socket_get4_sk); + +static bool +socket_match(const struct sk_buff *skb, struct xt_action_param *par, + const struct xt_socket_mtinfo1 *info) +{ + struct sock *sk; + + sk = xt_socket_get4_sk(skb, par); if (sk != NULL) { bool wildcard; bool transparent = true; @@ -179,11 +196,6 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, sk = NULL; } - pr_debug("proto %hhu %pI4:%hu -> %pI4:%hu (orig %pI4:%hu) sock %p\n", - protocol, &saddr, ntohs(sport), - &daddr, ntohs(dport), - &iph->daddr, hp ? ntohs(hp->dest) : 0, sk); - return (sk != NULL); } @@ -255,8 +267,8 @@ extract_icmp6_fields(const struct sk_buff *skb, return 0; } -static bool -socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) +struct sock* +xt_socket_get6_sk(const struct sk_buff *skb, struct xt_action_param *par) { struct ipv6hdr *iph = ipv6_hdr(skb); struct udphdr _hdr, *hp = NULL; @@ -264,7 +276,6 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) struct in6_addr *daddr, *saddr; __be16 dport, sport; int thoff, tproto; - const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo; tproto = ipv6_find_hdr(skb, &thoff, -1, NULL); if (tproto < 0) { @@ -276,7 +287,7 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr); if (hp == NULL) - return false; + return NULL; saddr = &iph->saddr; sport = hp->source; @@ -286,13 +297,30 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) } else if (tproto == IPPROTO_ICMPV6) { if (extract_icmp6_fields(skb, thoff, &tproto, &saddr, &daddr, &sport, &dport)) - return false; + return NULL; } else { - return false; + return NULL; } sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto, saddr, daddr, sport, dport, par->in, NFT_LOOKUP_ANY); + pr_debug("proto %hhd %pI6:%hu -> %pI6:%hu " + "(orig %pI6:%hu) sock %p\n", + tproto, saddr, ntohs(sport), + daddr, ntohs(dport), + &iph->daddr, hp ? ntohs(hp->dest) : 0, sk); + return sk; +} +EXPORT_SYMBOL(xt_socket_get6_sk); + +static bool +socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) +{ + struct sock *sk; + const struct xt_socket_mtinfo1 *info; + + info = (struct xt_socket_mtinfo1 *) par->matchinfo; + sk = xt_socket_get6_sk(skb, par); if (sk != NULL) { bool wildcard; bool transparent = true; @@ -315,12 +343,6 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) sk = NULL; } - pr_debug("proto %hhd %pI6:%hu -> %pI6:%hu " - "(orig %pI6:%hu) sock %p\n", - tproto, saddr, ntohs(sport), - daddr, ntohs(dport), - &iph->daddr, hp ? ntohs(hp->dest) : 0, sk); - return (sk != NULL); } #endif diff --git a/net/rfkill/Kconfig b/net/rfkill/Kconfig index 78efe895..8e12c8a2 100644 --- a/net/rfkill/Kconfig +++ b/net/rfkill/Kconfig @@ -10,6 +10,11 @@ menuconfig RFKILL To compile this driver as a module, choose M here: the module will be called rfkill. +config RFKILL_PM + bool "Power off on suspend" + depends on RFKILL && PM + default y + # LED trigger support config RFKILL_LEDS bool diff --git a/net/rfkill/core.c b/net/rfkill/core.c index f9749617..04afd89a 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -770,6 +770,7 @@ void rfkill_pause_polling(struct rfkill *rfkill) } EXPORT_SYMBOL(rfkill_pause_polling); +#ifdef CONFIG_RFKILL_PM void rfkill_resume_polling(struct rfkill *rfkill) { BUG_ON(!rfkill); @@ -804,14 +805,17 @@ static int rfkill_resume(struct device *dev) return 0; } +#endif static struct class rfkill_class = { .name = "rfkill", .dev_release = rfkill_release, .dev_attrs = rfkill_dev_attrs, .dev_uevent = rfkill_dev_uevent, +#ifdef CONFIG_RFKILL_PM .suspend = rfkill_suspend, .resume = rfkill_resume, +#endif }; bool rfkill_blocked(struct rfkill *rfkill) diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index 2e4444fe..8db24da2 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -160,3 +160,14 @@ config LIB80211_DEBUG from lib80211. If unsure, say N. + +config CFG80211_ALLOW_RECONNECT + bool "Allow reconnect while already connected" + depends on CFG80211 + default n + help + cfg80211 stack doesn't allow to connect if you are already + connected. This option allows to make a connection in this case. + + Select this option ONLY for wlan drivers that are specifically + built for such purposes. diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 70faadf1..e40104f5 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -18,7 +18,7 @@ #include "nl80211.h" #include "wext-compat.h" -#define IEEE80211_SCAN_RESULT_EXPIRE (15 * HZ) +#define IEEE80211_SCAN_RESULT_EXPIRE (3 * HZ) void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak) { diff --git a/net/wireless/sme.c b/net/wireless/sme.c index f7e937ff..bbbed736 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -689,8 +689,10 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) return; +#ifndef CONFIG_CFG80211_ALLOW_RECONNECT if (wdev->sme_state != CFG80211_SME_CONNECTED) return; +#endif if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); @@ -767,10 +769,14 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, ASSERT_WDEV_LOCK(wdev); +#ifndef CONFIG_CFG80211_ALLOW_RECONNECT if (wdev->sme_state != CFG80211_SME_IDLE) return -EALREADY; if (WARN_ON(wdev->connect_keys)) { +#else + if (wdev->connect_keys) { +#endif kfree(wdev->connect_keys); wdev->connect_keys = NULL; } diff --git a/samples/uhid/Makefile b/samples/uhid/Makefile new file mode 100644 index 00000000..c95a6965 --- /dev/null +++ b/samples/uhid/Makefile @@ -0,0 +1,10 @@ +# kbuild trick to avoid linker error. Can be omitted if a module is built. +obj- := dummy.o + +# List of programs to build +hostprogs-y := uhid-example + +# Tell kbuild to always build the programs +always := $(hostprogs-y) + +HOSTCFLAGS_uhid-example.o += -I$(objtree)/usr/include diff --git a/samples/uhid/uhid-example.c b/samples/uhid/uhid-example.c new file mode 100644 index 00000000..03ce3c05 --- /dev/null +++ b/samples/uhid/uhid-example.c @@ -0,0 +1,381 @@ +/* + * UHID Example + * + * Copyright (c) 2012 David Herrmann + * + * The code may be used by anyone for any purpose, + * and can serve as a starting point for developing + * applications using uhid. + */ + +/* UHID Example + * This example emulates a basic 3 buttons mouse with wheel over UHID. Run this + * program as root and then use the following keys to control the mouse: + * q: Quit the application + * 1: Toggle left button (down, up, ...) + * 2: Toggle right button + * 3: Toggle middle button + * a: Move mouse left + * d: Move mouse right + * w: Move mouse up + * s: Move mouse down + * r: Move wheel up + * f: Move wheel down + * + * If uhid is not available as /dev/uhid, then you can pass a different path as + * first argument. + * If is not installed in /usr, then compile this with: + * gcc -o ./uhid_test -Wall -I./include ./samples/uhid/uhid-example.c + * And ignore the warning about kernel headers. However, it is recommended to + * use the installed uhid.h if available. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* HID Report Desciptor + * We emulate a basic 3 button mouse with wheel. This is the report-descriptor + * as the kernel will parse it: + * + * INPUT[INPUT] + * Field(0) + * Physical(GenericDesktop.Pointer) + * Application(GenericDesktop.Mouse) + * Usage(3) + * Button.0001 + * Button.0002 + * Button.0003 + * Logical Minimum(0) + * Logical Maximum(1) + * Report Size(1) + * Report Count(3) + * Report Offset(0) + * Flags( Variable Absolute ) + * Field(1) + * Physical(GenericDesktop.Pointer) + * Application(GenericDesktop.Mouse) + * Usage(3) + * GenericDesktop.X + * GenericDesktop.Y + * GenericDesktop.Wheel + * Logical Minimum(-128) + * Logical Maximum(127) + * Report Size(8) + * Report Count(3) + * Report Offset(8) + * Flags( Variable Relative ) + * + * This is the mapping that we expect: + * Button.0001 ---> Key.LeftBtn + * Button.0002 ---> Key.RightBtn + * Button.0003 ---> Key.MiddleBtn + * GenericDesktop.X ---> Relative.X + * GenericDesktop.Y ---> Relative.Y + * GenericDesktop.Wheel ---> Relative.Wheel + * + * This information can be verified by reading /sys/kernel/debug/hid//rdesc + * This file should print the same information as showed above. + */ + +static unsigned char rdesc[] = { + 0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x09, 0x01, + 0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03, + 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, + 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x01, + 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x38, + 0x15, 0x80, 0x25, 0x7f, 0x75, 0x08, 0x95, 0x03, + 0x81, 0x06, 0xc0, 0xc0, +}; + +static int uhid_write(int fd, const struct uhid_event *ev) +{ + ssize_t ret; + + ret = write(fd, ev, sizeof(*ev)); + if (ret < 0) { + fprintf(stderr, "Cannot write to uhid: %m\n"); + return -errno; + } else if (ret != sizeof(*ev)) { + fprintf(stderr, "Wrong size written to uhid: %ld != %lu\n", + ret, sizeof(ev)); + return -EFAULT; + } else { + return 0; + } +} + +static int create(int fd) +{ + struct uhid_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_CREATE; + strcpy((char*)ev.u.create.name, "test-uhid-device"); + ev.u.create.rd_data = rdesc; + ev.u.create.rd_size = sizeof(rdesc); + ev.u.create.bus = BUS_USB; + ev.u.create.vendor = 0x15d9; + ev.u.create.product = 0x0a37; + ev.u.create.version = 0; + ev.u.create.country = 0; + + return uhid_write(fd, &ev); +} + +static void destroy(int fd) +{ + struct uhid_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_DESTROY; + + uhid_write(fd, &ev); +} + +static int event(int fd) +{ + struct uhid_event ev; + ssize_t ret; + + memset(&ev, 0, sizeof(ev)); + ret = read(fd, &ev, sizeof(ev)); + if (ret == 0) { + fprintf(stderr, "Read HUP on uhid-cdev\n"); + return -EFAULT; + } else if (ret < 0) { + fprintf(stderr, "Cannot read uhid-cdev: %m\n"); + return -errno; + } else if (ret != sizeof(ev)) { + fprintf(stderr, "Invalid size read from uhid-dev: %ld != %lu\n", + ret, sizeof(ev)); + return -EFAULT; + } + + switch (ev.type) { + case UHID_START: + fprintf(stderr, "UHID_START from uhid-dev\n"); + break; + case UHID_STOP: + fprintf(stderr, "UHID_STOP from uhid-dev\n"); + break; + case UHID_OPEN: + fprintf(stderr, "UHID_OPEN from uhid-dev\n"); + break; + case UHID_CLOSE: + fprintf(stderr, "UHID_CLOSE from uhid-dev\n"); + break; + case UHID_OUTPUT: + fprintf(stderr, "UHID_OUTPUT from uhid-dev\n"); + break; + case UHID_OUTPUT_EV: + fprintf(stderr, "UHID_OUTPUT_EV from uhid-dev\n"); + break; + default: + fprintf(stderr, "Invalid event from uhid-dev: %u\n", ev.type); + } + + return 0; +} + +static bool btn1_down; +static bool btn2_down; +static bool btn3_down; +static signed char abs_hor; +static signed char abs_ver; +static signed char wheel; + +static int send_event(int fd) +{ + struct uhid_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_INPUT; + ev.u.input.size = 4; + + if (btn1_down) + ev.u.input.data[0] |= 0x1; + if (btn2_down) + ev.u.input.data[0] |= 0x2; + if (btn3_down) + ev.u.input.data[0] |= 0x4; + + ev.u.input.data[1] = abs_hor; + ev.u.input.data[2] = abs_ver; + ev.u.input.data[3] = wheel; + + return uhid_write(fd, &ev); +} + +static int keyboard(int fd) +{ + char buf[128]; + ssize_t ret, i; + + ret = read(STDIN_FILENO, buf, sizeof(buf)); + if (ret == 0) { + fprintf(stderr, "Read HUP on stdin\n"); + return -EFAULT; + } else if (ret < 0) { + fprintf(stderr, "Cannot read stdin: %m\n"); + return -errno; + } + + for (i = 0; i < ret; ++i) { + switch (buf[i]) { + case '1': + btn1_down = !btn1_down; + ret = send_event(fd); + if (ret) + return ret; + break; + case '2': + btn2_down = !btn2_down; + ret = send_event(fd); + if (ret) + return ret; + break; + case '3': + btn3_down = !btn3_down; + ret = send_event(fd); + if (ret) + return ret; + break; + case 'a': + abs_hor = -20; + ret = send_event(fd); + abs_hor = 0; + if (ret) + return ret; + break; + case 'd': + abs_hor = 20; + ret = send_event(fd); + abs_hor = 0; + if (ret) + return ret; + break; + case 'w': + abs_ver = -20; + ret = send_event(fd); + abs_ver = 0; + if (ret) + return ret; + break; + case 's': + abs_ver = 20; + ret = send_event(fd); + abs_ver = 0; + if (ret) + return ret; + break; + case 'r': + wheel = 1; + ret = send_event(fd); + wheel = 0; + if (ret) + return ret; + break; + case 'f': + wheel = -1; + ret = send_event(fd); + wheel = 0; + if (ret) + return ret; + break; + case 'q': + return -ECANCELED; + default: + fprintf(stderr, "Invalid input: %c\n", buf[i]); + } + } + + return 0; +} + +int main(int argc, char **argv) +{ + int fd; + const char *path = "/dev/uhid"; + struct pollfd pfds[2]; + int ret; + struct termios state; + + ret = tcgetattr(STDIN_FILENO, &state); + if (ret) { + fprintf(stderr, "Cannot get tty state\n"); + } else { + state.c_lflag &= ~ICANON; + state.c_cc[VMIN] = 1; + ret = tcsetattr(STDIN_FILENO, TCSANOW, &state); + if (ret) + fprintf(stderr, "Cannot set tty state\n"); + } + + if (argc >= 2) { + if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { + fprintf(stderr, "Usage: %s [%s]\n", argv[0], path); + return EXIT_SUCCESS; + } else { + path = argv[1]; + } + } + + fprintf(stderr, "Open uhid-cdev %s\n", path); + fd = open(path, O_RDWR | O_CLOEXEC); + if (fd < 0) { + fprintf(stderr, "Cannot open uhid-cdev %s: %m\n", path); + return EXIT_FAILURE; + } + + fprintf(stderr, "Create uhid device\n"); + ret = create(fd); + if (ret) { + close(fd); + return EXIT_FAILURE; + } + + pfds[0].fd = STDIN_FILENO; + pfds[0].events = POLLIN; + pfds[1].fd = fd; + pfds[1].events = POLLIN; + + fprintf(stderr, "Press 'q' to quit...\n"); + while (1) { + ret = poll(pfds, 2, -1); + if (ret < 0) { + fprintf(stderr, "Cannot poll for fds: %m\n"); + break; + } + if (pfds[0].revents & POLLHUP) { + fprintf(stderr, "Received HUP on stdin\n"); + break; + } + if (pfds[1].revents & POLLHUP) { + fprintf(stderr, "Received HUP on uhid-cdev\n"); + break; + } + + if (pfds[0].revents & POLLIN) { + ret = keyboard(fd); + if (ret) + break; + } + if (pfds[1].revents & POLLIN) { + ret = event(fd); + if (ret) + break; + } + } + + fprintf(stderr, "Destroy uhid device\n"); + destroy(fd); + return EXIT_SUCCESS; +} diff --git a/scripts/setlocalversion b/scripts/setlocalversion index bd6dca8a..849dbe95 100644 --- a/scripts/setlocalversion +++ b/scripts/setlocalversion @@ -154,7 +154,7 @@ res="$(collect_files localversion*)" if test ! "$srctree" -ef .; then res="$res$(collect_files "$srctree"/localversion*)" fi - +LOCALVERSION= # CONFIG_LOCALVERSION and LOCALVERSION (if set) res="${res}${CONFIG_LOCALVERSION}${LOCALVERSION}" diff --git a/security/capability.c b/security/capability.c index 5bb21b1c..fc991bca 100644 --- a/security/capability.c +++ b/security/capability.c @@ -12,6 +12,26 @@ #include +static int cap_binder_set_context_mgr(struct task_struct *mgr) +{ + return 0; +} + +static int cap_binder_transaction(struct task_struct *from, struct task_struct *to) +{ + return 0; +} + +static int cap_binder_transfer_binder(struct task_struct *from, struct task_struct *to) +{ + return 0; +} + +static int cap_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file) +{ + return 0; +} + static int cap_syslog(int type) { return 0; @@ -877,6 +897,10 @@ static void cap_audit_rule_free(void *lsmrule) void __init security_fixup_ops(struct security_operations *ops) { + set_to_cap_if_null(ops, binder_set_context_mgr); + set_to_cap_if_null(ops, binder_transaction); + set_to_cap_if_null(ops, binder_transfer_binder); + set_to_cap_if_null(ops, binder_transfer_file); set_to_cap_if_null(ops, ptrace_access_check); set_to_cap_if_null(ops, ptrace_traceme); set_to_cap_if_null(ops, capget); diff --git a/security/commoncap.c b/security/commoncap.c index 71a166a0..0051ac2d 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -31,6 +31,10 @@ #include #include +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +#include +#endif + /* * If a non-root user executes a setuid-root binary in * !secure(SECURE_NOROOT) mode, then we raise capabilities. @@ -76,6 +80,13 @@ int cap_netlink_send(struct sock *sk, struct sk_buff *skb) int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, int cap, int audit) { +#ifdef CONFIG_ANDROID_PARANOID_NETWORK + if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) + return 0; + if (cap == CAP_NET_ADMIN && in_egroup_p(AID_NET_ADMIN)) + return 0; +#endif + for (;;) { /* The creator of the user namespace has all caps. */ if (targ_ns != &init_user_ns && targ_ns->creator == cred->user) diff --git a/security/security.c b/security/security.c index bf619ffc..cecd55e5 100644 --- a/security/security.c +++ b/security/security.c @@ -130,6 +130,26 @@ int __init register_security(struct security_operations *ops) /* Security operations */ +int security_binder_set_context_mgr(struct task_struct *mgr) +{ + return security_ops->binder_set_context_mgr(mgr); +} + +int security_binder_transaction(struct task_struct *from, struct task_struct *to) +{ + return security_ops->binder_transaction(from, to); +} + +int security_binder_transfer_binder(struct task_struct *from, struct task_struct *to) +{ + return security_ops->binder_transfer_binder(from, to); +} + +int security_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file) +{ + return security_ops->binder_transfer_file(from, to, file); +} + int security_ptrace_access_check(struct task_struct *child, unsigned int mode) { return security_ops->ptrace_access_check(child, mode); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 56262223..1ae096ed 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1818,6 +1818,67 @@ static inline u32 open_file_to_av(struct file *file) /* Hook functions begin here. */ +static int selinux_binder_set_context_mgr(struct task_struct *mgr) +{ + u32 mysid = current_sid(); + u32 mgrsid = task_sid(mgr); + + return avc_has_perm(mysid, mgrsid, SECCLASS_BINDER, BINDER__SET_CONTEXT_MGR, NULL); +} + +static int selinux_binder_transaction(struct task_struct *from, struct task_struct *to) +{ + u32 mysid = current_sid(); + u32 fromsid = task_sid(from); + u32 tosid = task_sid(to); + int rc; + + if (mysid != fromsid) { + rc = avc_has_perm(mysid, fromsid, SECCLASS_BINDER, BINDER__IMPERSONATE, NULL); + if (rc) + return rc; + } + + return avc_has_perm(fromsid, tosid, SECCLASS_BINDER, BINDER__CALL, NULL); +} + +static int selinux_binder_transfer_binder(struct task_struct *from, struct task_struct *to) +{ + u32 fromsid = task_sid(from); + u32 tosid = task_sid(to); + return avc_has_perm(fromsid, tosid, SECCLASS_BINDER, BINDER__TRANSFER, NULL); +} + +static int selinux_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file) +{ + u32 sid = task_sid(to); + struct file_security_struct *fsec = file->f_security; + struct inode *inode = file->f_path.dentry->d_inode; + struct inode_security_struct *isec = inode->i_security; + struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; + int rc; + + COMMON_AUDIT_DATA_INIT(&ad, PATH); + ad.u.path = file->f_path; + ad.selinux_audit_data = &sad; + + if (sid != fsec->sid) { + rc = avc_has_perm(sid, fsec->sid, + SECCLASS_FD, + FD__USE, + &ad); + if (rc) + return rc; + } + + if (unlikely(IS_PRIVATE(inode))) + return 0; + + return avc_has_perm(sid, isec->sid, isec->sclass, file_to_av(file), + &ad); +} + static int selinux_ptrace_access_check(struct task_struct *child, unsigned int mode) { @@ -5523,6 +5584,11 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer) static struct security_operations selinux_ops = { .name = "selinux", + .binder_set_context_mgr = selinux_binder_set_context_mgr, + .binder_transaction = selinux_binder_transaction, + .binder_transfer_binder = selinux_binder_transfer_binder, + .binder_transfer_file = selinux_binder_transfer_file, + .ptrace_access_check = selinux_ptrace_access_check, .ptrace_traceme = selinux_ptrace_traceme, .capget = selinux_capget, diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index b8c53723..4a4a9aeb 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -149,5 +149,6 @@ struct security_class_mapping secclass_map[] = { { "kernel_service", { "use_as_override", "create_files_as", NULL } }, { "tun_socket", { COMMON_SOCK_PERMS, NULL } }, + { "binder", { "impersonate", "call", "set_context_mgr", "transfer", NULL } }, { NULL } }; diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig index 885683a3..5727625a 100644 --- a/sound/arm/Kconfig +++ b/sound/arm/Kconfig @@ -11,6 +11,50 @@ menuconfig SND_ARM if SND_ARM +config SND_AK_PCM + tristate "Anyka ADC/DAC sound driver" + select SND_PCM + help + ALSA sound driver fo the AK ADC/DAC. + +choice + prompt "Select codec" + depends on SND_AK_PCM + +config CODEC_AK39 + bool "AK39 internal codec" + depends on SND_AK_PCM + help + ak39 SoC audio codec. +config CODEC_WM8523 + bool "WM8523 external codec(master mode)" + depends on I2C && SND_AK_PCM + help + Ultra low power DAC from Wolfson Microelectronics plc. + Note that this codec work in i2s master mode now. +endchoice + +choice + prompt "HP/Speaker switch mode" + depends on SND_AK_PCM + +config SPKHP_SWITCH_AUTO + bool "auto switch in audio driver(default, for both linux and android)" + +config SPKHP_SWITCH_MIXER + bool "notify,but not switch in audio driver(with ALSA lib amixer)" + +config SPKHP_SWITCH_UEVENT + bool "detect HP by UEVENT(only for android)" + +endchoice + +config SUPPORT_AEC + bool "Support Accoustic Echo Cancllation" + depends on SND_AK_PCM + help + Support Accoustic Echo Cancllation. + config SND_ARMAACI tristate "ARM PrimeCell PL041 AC Link support" depends on ARM_AMBA diff --git a/sound/arm/Makefile b/sound/arm/Makefile index 8c0c851d..7a34aaba 100644 --- a/sound/arm/Makefile +++ b/sound/arm/Makefile @@ -14,3 +14,12 @@ snd-pxa2xx-lib-$(CONFIG_SND_PXA2XX_LIB_AC97) += pxa2xx-ac97-lib.o obj-$(CONFIG_SND_PXA2XX_AC97) += snd-pxa2xx-ac97.o snd-pxa2xx-ac97-objs := pxa2xx-ac97.o + +obj-$(CONFIG_SND_AK_PCM) += snd-akpcm.o +snd-akpcm-objs := ak_pcm.o + +obj-$(CONFIG_CODEC_AK39) += snd-codec-ak39.o +snd-codec-ak39-objs := ak39_codec.o + +obj-$(CONFIG_CODEC_WM8523) += snd-codec-wm8523.o +snd-codec-wm8523-objs := wm8523.o diff --git a/sound/arm/ak39_codec.c b/sound/arm/ak39_codec.c new file mode 100644 index 00000000..55bee230 --- /dev/null +++ b/sound/arm/ak39_codec.c @@ -0,0 +1,2220 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include +#include + + +//#define SND_CODEC_DBG + +#ifdef SND_CODEC_DBG +#ifdef __KERNEL__ +#define PDEBUG(fmt, args...) printk(KERN_INFO "ak39 codec:" fmt, ##args) +#else +#define PDEBUG(fmt, args...) fprintf(stderr, "%s %d:" fmt,__FILE__, __LINE__, ## args) +#endif +#else +#define PDEBUG(fmt, args...) +#endif + + +enum mixer_vol_port { + MIXER_PORT_HP_VOL = 0, + MIXER_PORT_LINEIN_VOL, + MIXER_PORT_MIC_VOL, + MIXER_PORT_VOL_COUNT, +}; + + +enum mixer_dst_port { + MIXER_ADDR_DST_HP = 0, + MIXER_ADDR_DST_ADC2, + MIXER_DST_COUNT, +}; + +enum mixer_detect_port { + MIXER_ADDR_HPDET = 0, + MIXER_ADDR_MICDET, + MIXER_ADDR_LINEINDET, + MIXER_SWITCH_COUNT, +}; + + +enum mixer_outmode { + MIXER_ADDR_OUTMODE_DAC = 0, + MIXER_OUTMODE_COUNT, +}; + +enum mixer_chnl_duration { + MIXER_ADDR_PLAY_DURATION = 0, + MIXER_DURATION_COUNT, +}; + +#define MAX_DACCLK 24000000 + +#define HEADPHONE_DEFAULT_VOL (HEADPHONE_GAIN_MAX - 2) +#define LINEIN_DEFAULT_VOL 10 +#define MIC_DEFAULT_VOL 6 + +#define PLAYMODE_STATUS_HP (1) +#define PLAYMODE_STATUS_SPEAKER (2) + +#define OUTMODE_AUTO (0) +#define OUTMODE_HP (1) +#define OUTMODE_LINEOUT (2) +#define OUTMODE_MIN OUTMODE_AUTO +#define OUTMODE_MAX OUTMODE_HP + +#define CHNLDURATION_CONSTANT 0 +#define CHNLDURATION_EVEROPEN 1 +#define CHNLDURATION_MIN CHNLDURATION_CONSTANT +#define CHNLDURATION_MAX CHNLDURATION_EVEROPEN + +struct ak39_codec { + struct ak_codec_dai dai; + void __iomem *analog_ctrl_base; + void __iomem *adda_cfg_base; + struct delayed_work d_work; + + int mixer_volume[MIXER_PORT_VOL_COUNT]; + int mixer_route[MIXER_DST_COUNT]; + int mixer_switch[MIXER_SWITCH_COUNT]; + int mixer_outmode[MIXER_OUTMODE_COUNT]; + int mixer_ch_duration[MIXER_DURATION_COUNT]; + + struct gpio_info hpdet_gpio; + struct gpio_info spkrshdn_gpio; + struct gpio_info hpmute_gpio; + + int hp_det_irq; + int irq_hp_on_type; + int hp_on_value; + int playmode; + int hpmute_en_val; + + unsigned used_hp_mute:1; // whether to use hardware de-pipa or not + unsigned outputing:1; + unsigned dac_state:1; + unsigned adc2_state:1; +}; + +struct ak39_volume_info +{ + int vol_default; + int vol_min; + int vol_max; +}; + + +#define VOL_INFO_DEFAULT 0 +#define VOL_INFO_MIN 1 +#define VOL_INFO_MAX 2 + +static struct ak39_volume_info vol_info[MIXER_PORT_VOL_COUNT] = { + [MIXER_PORT_HP_VOL] = { + .vol_default = HEADPHONE_DEFAULT_VOL, + .vol_min = HEADPHONE_GAIN_MIN, + .vol_max = HEADPHONE_GAIN_MAX, + }, + [MIXER_PORT_LINEIN_VOL] = { + .vol_default = LINEIN_DEFAULT_VOL, + .vol_min = LINEIN_GAIN_MIN, + .vol_max = LINEIN_GAIN_MAX, + }, + [MIXER_PORT_MIC_VOL] = { + .vol_default = MIC_DEFAULT_VOL, + .vol_min = MIC_GAIN_MIN, + .vol_max = MIC_GAIN_MAX, + } + +}; + +static int default_route[MIXER_DST_COUNT] = { + [MIXER_ADDR_DST_HP] = SOURCE_DAC, + [MIXER_ADDR_DST_ADC2] = SOURCE_MIC, +}; + +extern unsigned long playback_statu; + +#if 0 +static void dump_codec_reg(struct ak39_codec *codec) +{ + u32 reg_val; + + reg_val = REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG); + printk("\nCLOCK_CTRL_REG(0x0800%04x):%08x.\n", CLOCK_CTRL_REG, reg_val); + + reg_val = REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG); + printk("HIGHSPEED_CLOCK_CTRL_REG(0x0800%04x):%08x.\n", HIGHSPEED_CLOCK_CTRL_REG, reg_val); + + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + printk("\nANALOG_CTRL_REG1(0x0800%04x):%08x.\n", ANALOG_CTRL_REG1, reg_val); + + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); + printk("ANALOG_CTRL_REG2(0x0800%04x):%08x.\n", ANALOG_CTRL_REG2, reg_val); + + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG3); + printk("ANALOG_CTRL_REG3(0x0800%04x):%08x.\n", ANALOG_CTRL_REG3, reg_val); + + reg_val = REG32(codec->adda_cfg_base + ADC2_CONFIG_REG); + printk("ADC2_CONFIG_REG(0x2011%04x):%08x.\n", ADC2_CONFIG_REG, reg_val); + + reg_val = REG32(codec->adda_cfg_base + ADC2_DATA_REG); + printk("ADC2_DATA_REG(0x2011%04x):%08x.\n", ADC2_DATA_REG, reg_val); + + reg_val = REG32(codec->adda_cfg_base + DAC_CONFIG_REG); + printk("DAC_CONFIG_REG(0x2011%04x):%08x.\n", DAC_CONFIG_REG, reg_val); + + reg_val = REG32(codec->analog_ctrl_base + ADC1_CONF_REG1); + printk("ADC1_CONF_REG1:(0x0800%04x):%08x.\n", ADC1_CONF_REG1, reg_val); +} +#endif +static inline struct ak39_codec *to_ak39_codec(struct ak_codec_dai *dai) +{ + return dai ? container_of(dai, struct ak39_codec, dai): NULL; +} + + +static inline void pd_ref_enable(struct ak39_codec *codec) +{ + u32 reg_val; + + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_val &= ~PD_REF; //power on codec + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; +} + +static inline void pd_ref_disable(struct ak39_codec *codec) +{ + u32 reg_val; + + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_val |= PD_REF; //power off codec + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; +} + +#if 0 +static void SetHpDisChgCur(unsigned long value) +{ + int reg_val; + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_val &= ~(0x7 << 29); + reg_val |= (value << 29); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; +} + + +static void set_cur_pmos(struct ak39_codec *codec, unsigned long value) +{ + int reg_val; + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_val &= ~(0xf << 25); + reg_val |= (value << 25); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; +} +#endif + +static void set_cur_vcm2_dischg(struct ak39_codec *codec, unsigned long value) +{ + int reg_val; + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_val &= ~(0x1f << 4); + reg_val |= (value << 4); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; +} + +static void set_cur_vcm2(struct ak39_codec *codec, unsigned long value) +{ + int reg_val; + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); + reg_val &= ~(0x1F << 16); + reg_val |= (value << 16); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) = reg_val; +} + + +/** + * @brief select HP in signal + * @author + * @date + * @param[in] (HP_In_Signal)signal: signal desired + * @return void + */ +void ak39_set_hp_in(struct ak39_codec *codec, unsigned long signal) +{ + unsigned long reg_val; + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_val &= ~(0x7 << 12); + reg_val |= ((signal&0x7) << 12); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; +} + + +/** + * @brief set ADC2 source + * @author + * @date + * @param[in] signal: DAC|LINEIN|MIC + * @return void + */ +void ak39_set_adc2_in(struct ak39_codec *codec, unsigned long signal) +{ + unsigned long reg_val; + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); + reg_val &= ~(0x7 << 2); + reg_val |= ((signal&0x7) << 2); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) = reg_val; +} + + +/** + * @brief set HP gain + * @author + * @date + * @param[in] x: (0.x)times + * @return void + */ +void ak39_set_hp_gain(struct ak39_codec *codec, unsigned long gain) +{ + unsigned long reg_value; + unsigned long gain_table[6] = {0x0, 0x18, 0x14, 0x12, 0x11, 0x10}; + + reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_value &= ~MASK_HP_GAIN; + reg_value |= HP_GAIN(gain_table[gain]); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; +} + +/** + * @brief set mic gain + * @author + * @date + * @param[in] gain: 0~7 + * @return void + */ +void ak39_set_mic_gain(struct ak39_codec *codec, unsigned long gain) +{ + unsigned long reg_val = 0; + + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); + reg_val &= ~(0x7<<10); + reg_val |= ((gain&0x7)<<10); + reg_val |= (1<<15); //double Mic gain + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) = reg_val; +} + +/** + * @brief set linein gain + * @author + * @date + * @param[in] gain: 0~15 + * @return void + */ +void ak39_set_linein_gain(struct ak39_codec *codec, unsigned long gain) +{ + unsigned long reg_val = 0; + + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); + reg_val &= ~(0xF<<6); + reg_val |= ((gain&0xF)<<6); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) = reg_val; +} + +enum depipa_noise_ctrl { + DEPIPA_NOISE_NOT_USE, + DEPIPA_NOISE_USE_3KOHM_RESISTOR, + DEPIPA_NOISE_USE_1KOHM_RESISTOR, + DEPIPA_NOISE_USE_MAX_RESISTOR, +}; + +static void set_bit_depipa_noise_ctrl(struct ak39_codec *codec, int mode) +{ + unsigned long reg_val = 0; + + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + + if(mode == DEPIPA_NOISE_USE_MAX_RESISTOR) { + reg_val |= (PRE_EN1); + reg_val |= (PRE_EN2); + } else if(mode == DEPIPA_NOISE_USE_3KOHM_RESISTOR) { + reg_val |= (PRE_EN1); + reg_val &= ~(PRE_EN2); + } else if(mode == DEPIPA_NOISE_USE_1KOHM_RESISTOR) { + reg_val &= ~(PRE_EN1); + reg_val |= (PRE_EN2); + } else { + reg_val &= ~(PRE_EN1); + reg_val &= ~(PRE_EN2); + } + + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; +} + + +static void ak39_set_vcm_ref_power(struct ak39_codec *codec, bool bOn) +{ + unsigned long reg_val; + if(bOn) + { + //add power control here for MIC-to-Lineout channel + //power on REF + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_val &= ~(PD_REF | PL_VCM2 ); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; + + //power on vcm2/vcm3 + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_val &= ~(PD_VCM2 | PD_VCM3); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; + } + else + { + //power off vcm2/vcm3 + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_val |= (PD_VCM2); + reg_val |= (PD_VCM3); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; + + //power off codec + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_val |= (PD_REF | PL_VCM2); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; + } +} + + +/** + * @brief power on HP + * @author + * @date + * @param[in] bool poweron or poweroff + * @ [in] bool use delay to de-pipa or not + * @return void + */ +void ak39_set_hp_power(struct ak39_codec *codec, bool bOn, bool soft_de_pipa) +{ + int reg_value; + + printk("ak39_set_hp_power %d,soft_de_pipa=%d\n",bOn,soft_de_pipa); + if(bOn) + { +// REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) |= (PL_VCM2); + set_bit_depipa_noise_ctrl(codec, DEPIPA_NOISE_USE_MAX_RESISTOR); + + mdelay(10); + + pd_ref_enable(codec); + + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) |= (0x7UL << 29); + + mdelay(20); + reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_value &= ~(PD1_HP); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; + + mdelay(10); + + reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_value &= ~(PD2_HP); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; + + ak39_set_vcm_ref_power(codec, 1); + + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~(0x1f << 4); + set_cur_vcm2(codec, 0x1e); + mdelay(100); + + set_bit_depipa_noise_ctrl(codec, DEPIPA_NOISE_NOT_USE); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~(RST_DAC); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~(0x7UL << 29); + } + else + { + set_bit_depipa_noise_ctrl(codec, DEPIPA_NOISE_USE_3KOHM_RESISTOR); + + if (!codec->adc2_state) { + //off mute + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~(0x7 << 12); + // REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) |= (0x1f << 4); + + ak39_set_vcm_ref_power(codec, 0); + set_cur_vcm2_dischg(codec, 0x1f); + mdelay(50); + } + + //power off hp + reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_value |= ((PD2_HP)); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; + + //power off hp + reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_value |= (PD1_HP); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; + } +} + + +/** + * @brief set linein interface power + * @author + * @date + * @param[in] bTrue: 1-power on; 0-power off + * @return void + */ +static void ak39_set_linein_power(struct ak39_codec *codec, bool bOn) +{ + unsigned long reg_val = 0; + if(bOn) + { + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); + reg_val &= ~(1 << 14); //power on the channel + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) = reg_val; + } + else + { + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); + reg_val |= (1 << 14); //power off the channel + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) = reg_val; + } +} + +/** + * @brief set DAC to lineout + * @author + * @date + * @param[in] bTrue: 1-connet DACto lineout; 0-disconnect + * @return void + */ +static void ak39_set_mic_power(struct ak39_codec *codec, bool bOn) +{ + unsigned long reg_val = 0; + if(bOn) + { + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); + reg_val |= VDD_MIC_SEL; + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) = reg_val; + + //power on mic interface + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); + reg_val &= ~(0x1 << 23); //power on differential mic + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) = reg_val; + } + else + { + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); + reg_val |= (0x1 << 23); //power off differential mic + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) = reg_val; + } +} + +static void ak39_set_sel_vref(struct ak39_codec *codec, bool bOn) +{ + unsigned long reg_val; + reg_val = REG32(codec->analog_ctrl_base + ADC1_CONF_REG1); + if(bOn) + reg_val |= SEL_VREF; + else + reg_val &= ~SEL_VREF; + + REG32(codec->analog_ctrl_base + ADC1_CONF_REG1) = reg_val; +} + + +static void set_dac_highspeed(struct ak39_codec *codec, + unsigned long sample_rate) +{ + static unsigned long rate_list[] = + {8000, 16000, 24000, 48000, 96000}; + static unsigned long dac_hclk[sizeof(rate_list)/sizeof(rate_list[0])] = + {6, 12, 18, 36, 72}; + + unsigned long reg_value; + unsigned long pllclk; + unsigned long hclk_div; + unsigned long hclk; + unsigned long i; + + pllclk = ak_get_asic_pll_clk(); + pllclk /= 1000000; + + printk("seths sr %lu,pll %lu\n",sample_rate,pllclk); + for(i=0; i < sizeof(rate_list)/sizeof(rate_list[0]); ++i) + { + if(sample_rate <= rate_list[i]) + { + break; + } + } + + if(sizeof(rate_list)/sizeof(rate_list[0]) == i) + { + hclk = dac_hclk[i-1]; + } + else + { + hclk = dac_hclk[i]; + } + + hclk_div = pllclk / hclk; + //04V 3771 chip: when the divider of the DAC CLK is even number, + //the DAC MODULE is more steady + hclk_div &= ~1; + + if(0 != hclk_div) + { + hclk_div = hclk_div - 1; + } + + reg_value = REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG); + reg_value &= ~(MASK_DAC_HCLK_DIV); + reg_value |= DAC_HSDIV_VLD | DAC_HCLK_DIV(hclk_div); + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) = reg_value; + + i = 0; + while(REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) & (DAC_HSDIV_VLD)) + { + ++i; + if(i > 100000) + { + printk("set high speed clk reg fail\n"); + return ; + } + } +} + +static void set_adc_highspeed(struct ak39_codec *codec, + unsigned long sample_rate) +{ + unsigned long reg_value; + unsigned long pllclk; + unsigned long hclk_div; + unsigned long hclk; + unsigned long i; + + pllclk = ak_get_asic_pll_clk(); + for (i=1; i<=0x40; i++) + { + hclk = pllclk/i; + if ((hclk<=62000000)&&(hclk>=28000000)) + { + break; + } + } + hclk_div = i-1; + + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= ~(1<<29); + + i = 0; + while(REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) & (ADC2_HSDIV_VLD)) + { + ++i; + if(i > 100000) + { + printk("adc set high speed clk reg fail\n"); + return ; + } + } + + reg_value = REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG); + reg_value &= ~(MASK_ADC2_HCLK_DIV); + reg_value |= ADC2_HSDIV_VLD | ADC2_HCLK_DIV(hclk_div); + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) = reg_value; + + i = 0; + while(REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) & (ADC2_HSDIV_VLD)) + { + ++i; + if(i > 100000) + { + printk("adc set high speed clk reg fail\n"); + return ; + } + } + + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) |= (1<<29); +} + +/** + * @brief set ADC2 mode and clk_div + * @author + * @date + * @param[in] des_sr: desired rate to be set + * @param[out] mode_sel: reg(0x08000064) bit[12] + * @param[out] mclkdiv: reg(0x08000008) bit[11:4] + * @return void + */ +static unsigned long get_adc2_osr_div(struct ak39_codec *codec, unsigned char *mode_sel, + unsigned char *mclkdiv, unsigned long des_sr) +{ + unsigned short k, max_div; + unsigned short OSR_value=256; + unsigned long SR_save, out_sr=0; + signed long a, b; + unsigned long clk168m; + unsigned long perfect_pll; + + clk168m = ak_get_asic_pll_clk(); + max_div = 0x40; + SR_save = 0; + *mode_sel = 0; + *mclkdiv = 0; + + if (des_sr > 24000) + { + OSR_value = 256; + *mode_sel = 1; //48k mode + } + else + { + OSR_value = 512; + *mode_sel = 0; //16k mode + } + + for(k=0; k0)? a : (-a); + b = SR_save - des_sr; + b = (b>0)? b : (-b); + if (a=0; k--) //DAC_DIV value + { + out_sr = clk168m/(k+1); + if (out_sr > MAX_DACCLK) + break; + out_sr = out_sr/OSR_table[j]; + a = out_sr-des_sr; + a = (a>0)? a : (-a); + b = SR_save-des_sr; + b = (b>0)? b : (-b); + if (a vol_info[port].vol_max) + vol = vol_info[port].vol_max; + + return vol; +} + +static inline int get_volume_info(int port, int type) +{ + int vol; + + switch(type){ + case VOL_INFO_DEFAULT: + vol = vol_info[port].vol_default; + break; + case VOL_INFO_MIN: + vol = vol_info[port].vol_min; + break; + case VOL_INFO_MAX: + vol = vol_info[port].vol_max; + break; + default: + vol = 0; + break; + } + return vol; +} + + +/* + *set mixer volume by port, + *return old volume; + * + * */ +static inline int set_mixer_volume(struct ak39_codec *codec, + int port, int volume) +{ + int old_vol; + + old_vol = codec->mixer_volume[port]; + codec->mixer_volume[port] = volume; + + return old_vol; +} + +static inline int get_mixer_volume(struct ak39_codec *codec, + int port) +{ + return codec->mixer_volume[port]; +} + +/** + * @brief set hp/linein/mic gain + * @author lixinhai + * @date 2013-08-02 + * @input codec: handle. + * @input port: control port:hp, linein or mic. + * @input gain: gain value. + * @return void + */ +static void ak39_set_gain(struct ak39_codec *codec, int port, int gain) +{ + switch(port) { + case MIXER_PORT_HP_VOL: + ak39_set_hp_gain(codec, gain); + break; + case MIXER_PORT_LINEIN_VOL: + ak39_set_linein_gain(codec, gain); + break; + case MIXER_PORT_MIC_VOL: + ak39_set_mic_gain(codec, gain); + break; + } +} + +/** + * @brief set playback mode: headphone or speaker + * @author lixinhai + * @date 2013-08-02 + * @input codec: handle. + * @input plug_in: headphone is plug in. + * @return void + */ +static inline void set_playmode_switch(struct ak39_codec *codec, + int plug_in) +{ + if(codec->playmode != PLAYMODE_AUTO_SWITCH) + return; + + if(plug_in) + codec->mixer_switch[MIXER_ADDR_HPDET] = PLAYMODE_STATUS_HP; + else + codec->mixer_switch[MIXER_ADDR_HPDET] = PLAYMODE_STATUS_SPEAKER; +} + +/** + * @brief get playback mode: headphone or speaker + * @author lixinhai + * @date 2013-08-02 + * @input codec: handle. + * @return void + */ +static int ak39_codec_get_playmode(struct ak39_codec *codec) +{ + if(codec->playmode == PLAYMODE_HP) { + return PLAYMODE_STATUS_HP; + } else if(codec->playmode == PLAYMODE_SPEAKER) + return PLAYMODE_STATUS_SPEAKER; + else + return codec->mixer_switch[MIXER_ADDR_HPDET]; +} + +/** + * @brief set route source on destation. + * @author lixinhai + * @date 2013-08-02 + * @input codec: handle. + * @input dst: destation + * @input src: source + * @input src_mask: source mask. + * @return new destation of route. + */ +static inline int set_route_src_on_dst(struct ak39_codec *codec, + int dst, int src, int src_mask) +{ + codec->mixer_route[dst] &= ~src_mask; + codec->mixer_route[dst] |= src; + + PDEBUG("%s: destion:%d, src:%d, src_mask:%d, mixer source:%d.\n", + __func__, dst, src, src_mask, codec->mixer_route[dst]); + return codec->mixer_route[dst]; +} + +static inline int get_route_src_on_dst(struct ak39_codec *codec, + int dst, int src_mask) +{ + return codec->mixer_route[dst] & src_mask; +} + + +static inline int check_route_src_on_dst(struct ak39_codec *codec, + int dst, int src) +{ + return !!(codec->mixer_route[dst] & src); +} + +static int calc_route_src_status(struct ak39_codec *codec, int dst, int src, int src_mask) +{ + int i; + int status = 0; + + for(i=0; imixer_route[i] & (src & src_mask); + else + status |= codec->mixer_route[i] & src_mask; + } + + return !!status; +} + + +/** + * @brief set input device power + * @author + * @date + * @param[in] src: DAC|LINEIN|MIC + * addr:route No. + * CurSrc:all three route's current src + * @return void + */ +static void ak39_set_src_power(struct ak39_codec *codec, int dst, int src) +{ + int s_dac, s_linein, s_mic, s_src; + + s_dac = calc_route_src_status(codec, dst, src, SOURCE_DAC); + s_linein = calc_route_src_status(codec, dst, src, SOURCE_LINEIN); + s_mic = calc_route_src_status(codec, dst, src, SOURCE_MIC); + + + s_src = s_dac|s_linein|s_mic; + + PDEBUG("%s: src:%d, s_dac:%d, s_linein:%d, s_mic:%d.\n", + __func__, src, s_dac, s_linein, s_mic); + + ak39_set_sel_vref(codec, s_src); + ak39_set_vcm_ref_power(codec, s_src); + + //set DAC power in PCM interface function: codec_playback_prepare() + ak39_set_linein_power(codec, s_linein); + ak39_set_mic_power(codec, s_mic); +} + + +static int ak39_codec_stream_control(struct ak39_codec *codec, + int dst, int running) +{ + int src; + + src = get_route_src_on_dst(codec, dst, SOURCE_MIXED_ALL_MASK); + PDEBUG("%s: dst:%d, src:%d, status:%s.\n", + __func__, dst, src, running ? "running":"stop"); + + if(!running) + src = 0; + + switch(dst) { + case MIXER_ADDR_DST_HP: + if(codec->used_hp_mute) + ak_gpio_setpin(codec->hpmute_gpio.pin, codec->hpmute_en_val); + + ak39_set_hp_power(codec, !!src, !codec->used_hp_mute); + + if(codec->used_hp_mute) { + int dis_val; + + dis_val = (codec->hpmute_en_val == AK_GPIO_OUT_HIGH)? + AK_GPIO_OUT_LOW:AK_GPIO_OUT_HIGH; + ak_gpio_setpin(codec->hpmute_gpio.pin, dis_val); + } + ak39_set_hp_in(codec, src); + ak39_set_src_power(codec, dst, src); + + break; + case MIXER_ADDR_DST_ADC2: + ak39_set_adc2_in(codec, src); + ak39_set_src_power(codec, dst, src); + //dump_codec_reg(codec); + break; + default: + break; + } + + return 0; +} + + +/** + * @brief open a dac device + * @author + * @date + * @return void + */ +void ak39_codec_dac_open(struct ak_codec_dai *dai) +{ + int reg_value; + int i; + struct ak39_codec *codec = to_ak39_codec(dai); + + if(codec->dac_state) + return; + + codec->dac_state = 1; + + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= ~(1<<4); + REG32(codec->analog_ctrl_base + CLOCK_GATE_CTRL_REG) &= ~(1<<4); + + REG32(codec->adda_cfg_base + DAC_CONFIG_REG) |= MUTE; + + REG32(codec->adda_cfg_base + DAC_CONFIG_REG) &= ~DAC_CTRL_EN; + REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) &= ~DAC_CLK_EN; + + REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) |= DAC_DIV_VLD; + i = 0; + while(REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) & (DAC_DIV_VLD)) + { + ++i; + if(i > 100000) + { + printk("set da clk reg fail\n"); + return ; + } + } + + // to enable DAC CLK + REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) |= DAC_CLK_EN; + + //soft reset DAC + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= ~(DAC_SOFT_RST); + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) |= DAC_SOFT_RST; + + //to enable internal dac/adc via i2s + REG32(codec->analog_ctrl_base + MULTIPLE_FUN_CTRL_REG1) |= IN_DAAD_EN; + + //set default dac div and osr, for de pipa noise + + //I2S config reg + REG32(codec->adda_cfg_base + I2S_CONFIG_REG) &= (~I2S_CONFIG_WORDLENGTH_MASK); + REG32(codec->adda_cfg_base + I2S_CONFIG_REG) |= (16<<0); + + //config I2S interface DAC + REG32(codec->adda_cfg_base + DAC_CONFIG_REG) |= DAC_CTRL_EN; + + //enable accept data from l2 + REG32(codec->adda_cfg_base + DAC_CONFIG_REG) |= L2_EN; + REG32(codec->adda_cfg_base + DAC_CONFIG_REG) |= FORMAT; + REG32(codec->adda_cfg_base + DAC_CONFIG_REG) &= ~ARM_INT; + REG32(codec->adda_cfg_base + I2S_CONFIG_REG) &= ~POLARITY_SEL; + + REG32(codec->analog_ctrl_base + FADEOUT_CTRL_REG) |= DAC_FILTER_EN; + + REG32(codec->adda_cfg_base + DAC_CONFIG_REG) &= ~MUTE; + + + reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_value &= ~( PD_OP | PD_CK); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; +} + + +/** + * @brief Close a dac device + * @author + * @date + * @return void + */ +void ak39_codec_dac_close(struct ak_codec_dai *dai) +{ + int reg_val; + struct ak39_codec *codec = to_ak39_codec(dai); + + if(!codec->dac_state) + return; + + codec->dac_state = 0; + REG32(codec->adda_cfg_base + DAC_CONFIG_REG) &= (~L2_EN); + + // to power off DACs/DAC clock + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_val |= (PD_OP); + reg_val |= (PD_CK); + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; + + mdelay(5); + + REG32(codec->analog_ctrl_base + MULTIPLE_FUN_CTRL_REG1) &= (~(IN_DAAD_EN));// tdisable internal DAC/ADC + REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) &= (~(DAC_CLK_EN|DAC_DIV_VLD));// disable DAC CLK + REG32(codec->analog_ctrl_base + DAC_CONFIG_REG) &= (~DAC_CTRL_EN);// to disable DACs +} + +/** + * @brief Set sample rate + * @author + * @date + * @param[in] samplerate: desired sample rate + * @return void + */ +void ak39_codec_set_dac_samplerate(struct ak_codec_dai *dai, unsigned int samplerate) +{ + unsigned char osr, mclkdiv; + struct ak39_codec *codec = to_ak39_codec(dai); + + get_dac_osv_div(codec, &osr, &mclkdiv, samplerate); + + //disable HCLK, and disable DAC interface + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) &= ~(DAC_HCLK_EN); + REG32(codec->analog_ctrl_base + DAC_CONFIG_REG) &= ~(DAC_CTRL_EN); + + set_dac_highspeed(codec, samplerate); + + // NOTE: should reset DAC!!! + // REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= (~DAC_SOFT_RST); +// mdelay(5); + + //set OSR + REG32(codec->analog_ctrl_base + FADEOUT_CTRL_REG) &= (~OSR_MASK); + REG32(codec->analog_ctrl_base + FADEOUT_CTRL_REG) |= (OSR(osr)); + mdelay(2); + + //set DIV + REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) &= (~(MASK_DAC_DIV_INT)); + REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) |= (DAC_DIV_INT(mclkdiv)); + + REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) &= (~(MASK_DAC_DIV_FRAC)); + REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) |= (DAC_DIV_VLD); + mdelay(2); + + //to reset dac + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) |= DAC_SOFT_RST; + + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= (~DAC_SOFT_RST); + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) |= DAC_SOFT_RST; + mdelay(5); + //enable HCLK, and enable DAC interface + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) |= (DAC_HCLK_EN); + REG32(codec->analog_ctrl_base + DAC_CONFIG_REG) |= (DAC_CTRL_EN); + mdelay(5); +} + +/** + * @brief Set DAC channels: mono,stereo + * @author + * @date + * @param[in] bool mono: true-mono, false-stereo + * @return void + */ +void ak39_codec_set_dac_channels(struct ak_codec_dai *dai, unsigned int chnl) +{ + +} + + +/** + * @brief open ADC2 + * @author + * @date + * @param[in] void + * @return void + */ +void ak39_codec_adc2_open(struct ak_codec_dai *dai) +{ + struct ak39_codec *codec = to_ak39_codec(dai); + + //unsigned long i; + if(codec->adc2_state) + return; + + codec->adc2_state = 1; + + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= ~(1<<3); + REG32(codec->analog_ctrl_base + CLOCK_GATE_CTRL_REG) &= ~(1<<3); + + // soft reset ADC2 controller + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) |= (ADC2_SOFT_RST); + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= ~(ADC2_SOFT_RST); + +// set_cur_pmos(codec, 0x0); //must be set to 0 when ADC2 is working + + REG32(codec->analog_ctrl_base + MULTIPLE_FUN_CTRL_REG1) |= IN_DAAD_EN; //enable internal + + //disable vcm2 discharge + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~(0x1f << 4); + + //SelVcm3 + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) |= VCM3_SEL; + + //PowerOn Vcm2/3 + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~PD_VCM3; + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~PD_VCM2; + + //SetVcmNormal + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~PL_VCM2; + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) &= ~PL_VCM3; + + //EnableAdc2Limit + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) |= ADC_LIM; + + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) &= ~HOST_RD_INT_EN; + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) |= CH_POLARITY_SEL;//Receive the left channel data when the lrclk is high + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) &= ~I2S_EN; //Internal ADC MODE + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) &= ~WORD_LENGTH_MASK; + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) |= (0xF << 8); //WORD LENGTH IS 16 BIT + + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) |= (ADC2_SOFT_RST); + + //Enable adc controller + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) |= ADC2_CTRL_EN; + + //Enable l2 + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) |= ADC2MODE_L2_EN; + + //Power on adc2 conversion + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) &= ~PD_S2D; + + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) &= ~PD_ADC2; + + mdelay(1); + pd_ref_enable(codec); +} + +/** + * @brief close ADC2 + * @author + * @date + * @param[in] void + * @return void + */ +void ak39_codec_adc2_close(struct ak_codec_dai *dai) +{ + unsigned long reg_val; + struct ak39_codec *codec = to_ak39_codec(dai); + + if(!codec->adc2_state) + return; + + codec->adc2_state = 0; + + //disable l2 + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) &= ~ADC2MODE_L2_EN; + + //disable adc2 clk + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) &= ~ADC2_CLK_EN; + + //disable ADC2 interface + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) &= ~ADC2_CTRL_EN; + + //Power off adc2 + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) |= PD_ADC2; + + if((2 << 12) != (REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) & (2 << 12))) + { + if(!(REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) & DAC_CLK_EN)) + { + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_val |= PD_VCM3; + reg_val |= PD_VCM2; + reg_val |= PL_VCM2; + //reg_val |= PD_REF; + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; + pd_ref_disable(codec); + + } + } + REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) |= (DAC_DIV_VLD); //inhabit adc2 clock + +} + +/** + * @brief set ADC2 sample rate + * @author + * @date + * @param[in] samplerate: desired rate to be set + * @return void + */ +unsigned long ak39_codec_set_adc2_samplerate(struct ak_codec_dai *dai, + unsigned int samplerate) +{ + unsigned char mode_sel = 0; + unsigned char save_div = 0; + unsigned long out_sr = 0; + unsigned long i = 0; + struct ak39_codec *codec = to_ak39_codec(dai); + + //disable HCLK, and disable ADC interface + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) &= ~(ADC2_CTRL_EN); + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) &= ~(ADC2_HCLK_EN); + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) &= ~(ADC2_CLK_EN); + + set_adc_highspeed(codec, samplerate); + + out_sr = get_adc2_osr_div(codec, &mode_sel, &save_div, samplerate); + + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= ~(1<<27); //reset adc from adc clk + i = 0; + while(REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) & (ADC2_DIV_VLD)) + { + ++i; + if(i > 100000) + { + printk("adc set sr fail\n"); + return 0; + } + } + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) &= ~(MASK_ADC2_DIV); + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) |= ADC2_DIV(save_div); + + REG32(codec->analog_ctrl_base + FADEOUT_CTRL_REG) &= ~(1<analog_ctrl_base + FADEOUT_CTRL_REG) |= (mode_sel << ADC2_OSR_BIT); + + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) |= (ADC2_DIV_VLD); + i = 0; + while(REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) & (ADC2_DIV_VLD)) + { + ++i; + if(i > 100000) + { + printk("adc set clk reg fail\n"); + return 0; + } + } + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) |= (1<<27); //release the reset adc from adc clk + + + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) |= (ADC2_CLK_EN);//enable ADC2 Clock + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) |= (ADC2_HCLK_EN);//enable ADC2 Clock + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) |= (ADC2_CTRL_EN); + return out_sr; +} + + +/** + * @brief set ADC23 channel + * @author + * @date + * @param[in] chnl: 1-mono; 2-stereo + * @return void + */ +void ak39_codec_set_adc2_channels(struct ak_codec_dai *dai, unsigned int chnl) +{ + +} + + +/** + * @brief cfg shutdown speaker GPIO + * @author + * @date + * @param[in] bOn: 1-power on; 0-power off + * @return void + */ +void ak39_codec_speak_on(struct ak39_codec *codec, bool bOn) +{ + unsigned int pin = codec->spkrshdn_gpio.pin; + ak_setpin_as_gpio(pin); + ak_gpio_cfgpin(pin, AK_GPIO_DIR_OUTPUT); + ak_gpio_setpin(pin, bOn); +} + + +/** + * @brief when playback start, ak39_codec_playback_start must be called to start output channel + * @author Cheng JunYi + * @revisor Wu Daochao(2012-08-23) + * @date + * @return void + */ +static void ak39_codec_playback_start(struct ak_codec_dai *dai) +{ + struct ak39_codec *codec = to_ak39_codec(dai); + + if(codec->mixer_ch_duration[MIXER_ADDR_PLAY_DURATION] == CHNLDURATION_EVEROPEN) + return; + + ak39_codec_get_playmode(codec); + ak39_codec_stream_control(codec, MIXER_ADDR_DST_HP, 1); + + codec->outputing = true; + +} + +void ak39_start_to_play(struct ak_codec_dai *dai, + unsigned int channels, unsigned int samplerate) +{ + ak39_codec_set_dac_samplerate(dai, samplerate); + ak39_codec_set_dac_channels(dai, channels); + ak39_codec_dac_open(dai); + ak39_codec_playback_start(dai); +} + + +static void ak39_codec_playback_stop(struct ak_codec_dai *dai) +{ + struct ak39_codec *codec = to_ak39_codec(dai); + + //if we want to open some channel for ever, return + if(codec->mixer_ch_duration[MIXER_ADDR_PLAY_DURATION] == CHNLDURATION_EVEROPEN) + return; + + /*close the dac output*/ + ak39_codec_stream_control(codec, MIXER_ADDR_DST_HP, 0); + codec->outputing = false; +} + + +static void ak39_codec_capture_start(struct ak_codec_dai *dai) +{ + struct ak39_codec *codec = to_ak39_codec(dai); + + ak39_codec_stream_control(codec, MIXER_ADDR_DST_ADC2, 1); +} + +static void ak39_codec_capture_stop(struct ak_codec_dai *dai) +{ + struct ak39_codec *codec = to_ak39_codec(dai); + + ak39_codec_stream_control(codec, MIXER_ADDR_DST_ADC2, 0); +} + +/**********************HPDet switch**************************/ + +static int set_hpdet_status(struct ak39_codec *codec) +{ + int plug_in = 0; + int irq_type; + + if(ak_gpio_getpin(codec->hpdet_gpio.pin) == codec->hp_on_value) + { + //hp is plugged in + plug_in = 1; + irq_type = (codec->irq_hp_on_type == IRQ_TYPE_LEVEL_LOW) ? + IRQ_TYPE_LEVEL_HIGH:IRQ_TYPE_LEVEL_LOW; + } + else + { + //hp is pulled out + plug_in = 0; + irq_type = codec->irq_hp_on_type; + } + + printk("detect the headphone plug %s.\n", plug_in ? "on":"out"); + irq_set_irq_type(codec->hp_det_irq, irq_type); + + set_playmode_switch(codec, plug_in); + + ak39_codec_speak_on(codec, !plug_in); + return 0; +} + + +/** + * @brief when hp state is changed,schedule hp_det_worker to config output \ + channel to speaker or not + * @author Cheng Mingjuan + * @date + * @return void + */ +static void hp_det_worker(struct work_struct *work) +{ + struct ak39_codec *codec = container_of(work, struct ak39_codec,d_work.work); + + set_hpdet_status(codec); + + ak_codec_ctl_event(SNDRV_CTL_ELEM_IFACE_HWDEP, + SNDRV_CTL_EVENT_MASK_VALUE, "HPDet switch"); + + enable_irq(codec->hp_det_irq); +} + +/** + * @brief hp det + * @author Cheng Mingjuan + * @date + * @return void + */ +static irqreturn_t ak39_codec_hpdet_irq(int irq, void *dev_id) +{ + struct ak39_codec *codec = dev_id; + + disable_irq_nosync(irq); + + schedule_delayed_work(&codec->d_work, msecs_to_jiffies(100)); + return IRQ_HANDLED; +} + + +/************** + * mixer interface + **************/ + + +/***********************config channel duration*******************************/ +#define AK39PCM_OUTPUTCHNL_DURATION(xname, xindex, addr) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .name = xname, .index = xindex, \ + .info = codec_ChnlDuration_info, \ + .get = codec_ChnlDuration_get, \ + .put = codec_ChnlDuration_put, \ + .private_value = addr \ +} + +/** + * @brief info callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_ChnlDuration_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int addr = kcontrol->private_value; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + if(MIXER_ADDR_PLAY_DURATION == addr) + { + uinfo->value.integer.min = CHNLDURATION_MIN; + uinfo->value.integer.max = CHNLDURATION_MAX; + } + else + { + return -EINVAL; + } + + return 0; +} + +/** + * @brief get callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_ChnlDuration_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); + struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); + + int addr = kcontrol->private_value; + if(addr != MIXER_ADDR_PLAY_DURATION) + { + return -EINVAL; + } + + ucontrol->value.integer.value[0] = codec->mixer_ch_duration[addr]; + return 0; +} + +/** + * @brief put callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_ChnlDuration_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); + struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); + + int addr = kcontrol->private_value; + int duration = ucontrol->value.integer.value[0]; + + printk("ak39 codec: ChnlDuration_put duration=%d\n",duration); + if(duration == codec->mixer_ch_duration[MIXER_ADDR_PLAY_DURATION]){ + return 0; + } + if(addr != MIXER_ADDR_PLAY_DURATION) { + return -EINVAL; + } + if((duration > CHNLDURATION_MAX)||(duration < CHNLDURATION_MIN)) { + return -EINVAL; + } + + codec->mixer_ch_duration[MIXER_ADDR_PLAY_DURATION] = duration; + if((duration == CHNLDURATION_CONSTANT) && + (!test_bit(0, &playback_statu))) { + ak39_codec_playback_stop(dai); + } + + return 0; +} + +/*********************select fixed output channel**********************************/ +#define AK39PCM_DAC_OUT_MODE(xname, xindex, addr) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .name = xname, .index = xindex, \ + .info = codec_dac_outmode_info, \ + .get = codec_dac_outmode_get, \ + .put = codec_dac_outmode_put, \ + .private_value = addr \ +} + +/** + * @brief info callback. + * becsuse of arch-ak39 has dac output only, + * this interface is not used. + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_dac_outmode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int addr = kcontrol->private_value; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + if(MIXER_ADDR_OUTMODE_DAC == addr) + { + uinfo->value.integer.min = OUTMODE_MIN; + uinfo->value.integer.max = OUTMODE_MAX; + } + else + { + return -EINVAL; + } + + return 0; +} + +/** + * @brief get callback + * becsuse of arch-ak39 has dac output only, + * this interface is not used. + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_dac_outmode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int addr = kcontrol->private_value; + + if(addr != MIXER_ADDR_OUTMODE_DAC) + { + return -EINVAL; + } + + ucontrol->value.integer.value[0] = OUTMODE_HP; + return 0; +} + +/** + * @brief put callback + * becsuse of arch-ak39 has dac output only, + * this interface is not used. + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_dac_outmode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + printk("ak39 platform not support out mode setting.\n"); + return 0; +} + +/**********************hp detect (read-only)****************************/ +#define AK39PCM_SWITCH(xname, xindex, addr) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ, \ + .name = xname, .index = xindex, \ + .info = codec_switch_info, \ + .get = codec_switch_get, \ + .private_value = addr \ +} + +/** + * @brief info callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int addr = kcontrol->private_value; + if(MIXER_ADDR_HPDET != addr) + { + return -EINVAL; + } + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +/** + * @brief get callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); + struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); + + int addr = kcontrol->private_value; + if(MIXER_ADDR_HPDET != addr) + { + return -EINVAL; + } + + ucontrol->value.integer.value[0] = codec->mixer_switch[addr]; + PDEBUG("%s enter, value:%lu.\n", __func__, ucontrol->value.integer.value[0]); + return 0; +} + +/***************************VOLUME*********************/ +#define AK39PCM_VOLUME(xname, xindex, addr) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .name = xname, .index = xindex, \ + .info = codec_volume_info, \ + .get = codec_volume_get, \ + .put = codec_volume_put, \ + .private_value = addr \ +} + +/** + * @brief info callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int port = kcontrol->private_value; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + + + if(port < 0 || port >= MIXER_PORT_VOL_COUNT) { + printk(KERN_ERR "%s port %d is invaild.\n", __func__, port); + return -EINVAL; + } + uinfo->value.integer.min = get_volume_info(port, VOL_INFO_MIN); + uinfo->value.integer.max = get_volume_info(port, VOL_INFO_MAX); + + return 0; +} + +/** + * @brief get callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); + struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); + + int port = kcontrol->private_value; + if(port < 0 || port >= MIXER_PORT_VOL_COUNT) { + printk(KERN_ERR "%s port %d is invaild.\n", __func__, port); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = get_mixer_volume(codec, port); + PDEBUG("%s enter, port:%d, volume:%lu.\n", + __func__, port, ucontrol->value.integer.value[0]); + return 0; +} + +/** + * @brief put callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); + struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); + + int port = kcontrol->private_value; + int vol, old_vol; + int change = 0; + + vol = ucontrol->value.integer.value[0]; + + PDEBUG("%s enter, port:%d, volume:%d.\n", __func__, port, vol); + vol = volume_repair_range(port, vol); + + old_vol = set_mixer_volume(codec, port, vol); + if(vol != old_vol) { + change = 1; + ak39_set_gain(codec, port, vol); + } + + return change; +} + +/***************************ROUTE**************************/ +#define AK39PCM_ROUTE(xname, xindex, addr) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .name = xname, \ + .index = xindex, \ + .info = codec_route_info, \ + .get = codec_route_get, \ + .put = codec_route_put, \ + .private_value = addr \ +} + +/** + * @brief info callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_route_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int addr = kcontrol->private_value; + + if(addr >= MIXER_DST_COUNT) + { + return -EINVAL; + } + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = SIGNAL_SRC_MAX; + + PDEBUG("%s enter, min:0, max:%d.\n", __func__, SIGNAL_SRC_MAX); + return 0; +} + +/** + * @brief get callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_route_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); + struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); + int dst = kcontrol->private_value; + + if(dst >= MIXER_DST_COUNT) + { + return -EINVAL; + } + + ucontrol->value.integer.value[0] = get_route_src_on_dst(codec, dst, SOURCE_MIXED_ALL_MASK); + PDEBUG("%s enter, value:%lu.\n", __func__, ucontrol->value.integer.value[0]); + return 0; +} + +/** + * @brief put callback + * @author Cheng Mingjuan + * @date + * @return void + */ +/*application has to call this callback with params value=0 to power down input&output devices*/ +static int codec_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); + struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); + int change = 1; + unsigned long dst = kcontrol->private_value; + int src = 0; + + src = ucontrol->value.integer.value[0]; + PDEBUG("%s:set route: dst:%lu, src:%d.\n", __func__, dst, src); + + if((src < SIGNAL_SRC_MUTE) || (src > SIGNAL_SRC_MAX)) + return -EINVAL; + + if(dst == MIXER_ADDR_DST_HP) { + int plug_in = 0; + + if(codec->playmode == PLAYMODE_HP) + plug_in = 1; + else if(codec->playmode == PLAYMODE_SPEAKER) + plug_in = 0; + + set_playmode_switch(codec, plug_in); + + /*power on the speaker when headphone plug out.*/ + ak39_codec_speak_on(codec, !plug_in); + + }else if(dst == MIXER_ADDR_DST_ADC2) { + } + + if(get_route_src_on_dst(codec, dst, SOURCE_MIXED_ALL_MASK) == src) { + change = 0; + } + set_route_src_on_dst(codec, dst, src, SOURCE_MIXED_ALL_MASK); + + return change; +} + +/***************************ROUTE**************************/ +#define AK39PCM_AEC(xname, xindex, addr) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .name = xname, \ + .index = xindex, \ + .info = codec_aec_info, \ + .get = codec_aec_get, \ + .put = codec_aec_put, \ + .private_value = addr \ +} + +/** + * @brief info callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_aec_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + //int port = kcontrol->private_value; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + + printk( "get the aec info \n" ); + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 10; + + return 0; +} + +/** + * @brief get callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_aec_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); + //struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); + + //int port = kcontrol->private_value; + ucontrol->value.integer.value[0] = dai->aec_flag; + //printk("%s enter, %d\n", __func__, ucontrol->value.integer.value[0]); + return 0; +} + +/** + * @brief put callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int codec_aec_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); + //struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); + + int src = ucontrol->value.integer.value[0]; + int port = kcontrol->private_value; + + + printk("%s enter, %d %d \n", __func__, port, src); + dai->aec_flag = src; + + return 1; +} + + + + +static struct ak_proc_entry codec_pentries[] = { +}; + +static struct snd_kcontrol_new codec_controls[] = { + AK39PCM_VOLUME("Headphone Playback Volume", 0, MIXER_PORT_HP_VOL), + AK39PCM_VOLUME("LineIn Capture Volume", 0, MIXER_PORT_LINEIN_VOL), + AK39PCM_VOLUME("Mic Capture Volume", 0, MIXER_PORT_MIC_VOL), + AK39PCM_ROUTE("Headphone Playback Route", 0, MIXER_ADDR_DST_HP), + AK39PCM_ROUTE("ADC Capture Route", 0, MIXER_ADDR_DST_ADC2), + /*AK39PCM_ROUTE("LineIn Capture Route", 0, MIXER_ADDR_LINEINSRC),*/ + AK39PCM_SWITCH("HPDet switch", 0, MIXER_ADDR_HPDET), + AK39PCM_DAC_OUT_MODE("DAC out mode", 0, MIXER_ADDR_OUTMODE_DAC), + AK39PCM_OUTPUTCHNL_DURATION("duration of output channel", 0, MIXER_ADDR_PLAY_DURATION), + AK39PCM_AEC("Set the aec", 0, 0), +}; + +struct ak_codec_ops ak39_codec_ops = { + .dac_init = ak39_codec_dac_open, + .dac_exit = ak39_codec_dac_close, + .adc_init = ak39_codec_adc2_open, + .adc_exit = ak39_codec_adc2_close, + .set_dac_samplerate = ak39_codec_set_dac_samplerate, + .set_adc_samplerate = ak39_codec_set_adc2_samplerate, + .set_dac_channels = ak39_codec_set_dac_channels, + .set_adc_channels = ak39_codec_set_adc2_channels, + .playback_start = ak39_codec_playback_start, + .playback_end = ak39_codec_playback_stop, + .capture_start = ak39_codec_capture_start, + .capture_end = ak39_codec_capture_stop, + .start_to_play = ak39_start_to_play, +}; + + +static int ak39_codec_probe(struct platform_device *pdev) +{ + int err = 0; + int i; + struct ak39_codec_platform_data *pdata = pdev->dev.platform_data; + struct ak39_codec *codec; + struct resource *res; + + printk("ak39_codec_probe enter.\n"); + + codec = kzalloc(sizeof(*codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "akpcm_AnalogCtrlRegs"); + if(!res) + { + printk(KERN_ERR "no memory resource for analog_ctrl_res\n"); + err = -ENXIO; + goto out_free_codec; + } + + codec->analog_ctrl_base = ioremap(res->start, res->end - res->start + 1); + if (!codec->analog_ctrl_base) { + printk(KERN_ERR "could not remap analog_ctrl_res memory"); + err = -ENXIO; + goto out_free_codec; + } + + + //get ADC2 mode registers + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "akpcm_ADC2ModeCfgRegs"); + if(!res) + { + printk(KERN_ERR "no memory resource for adda_cfg_res\n"); + err = -ENXIO; + goto out_unremap_analog; + } + codec->adda_cfg_base = ioremap(res->start, res->end - res->start + 1); + if (!codec->adda_cfg_base) { + printk(KERN_ERR "could not remap adda_cfg_res memory"); + err = -ENXIO; + goto out_unremap_analog; + } + + + for(i=0; imixer_outmode[MIXER_ADDR_OUTMODE_DAC] = OUTMODE_HP; + codec->mixer_ch_duration[MIXER_ADDR_PLAY_DURATION] = CHNLDURATION_CONSTANT; + + /*FIXME:can add the code for worker vaild when switch auto mode*/ + INIT_DELAYED_WORK(&codec->d_work, hp_det_worker); + + codec->playmode = pdata->boutput_only; + + //config hp det gpio + codec->hpdet_gpio = pdata->hpdet_gpio; + + //config speaker shutdown gpio + codec->spkrshdn_gpio = pdata->spk_down_gpio; + ak_gpio_set(&(codec->spkrshdn_gpio)); + + //config speaker shutdown gpio + codec->spkrshdn_gpio = pdata->spk_down_gpio; + ak_gpio_set(&(codec->spkrshdn_gpio)); + + //set hp det pin irq + if(codec->hpdet_gpio.pin >= 0) { + ak_gpio_set(&(codec->hpdet_gpio)); + + if(AK_GPIO_OUT_LOW == codec->hp_on_value) + codec->irq_hp_on_type = IRQ_TYPE_LEVEL_LOW; + else if(AK_GPIO_OUT_HIGH == codec->hp_on_value) + codec->irq_hp_on_type = IRQ_TYPE_LEVEL_HIGH; + + codec->hp_det_irq = pdata->hpdet_irq; + codec->hp_on_value = pdata->hp_on_value; + + set_hpdet_status(codec); + err = request_irq(codec->hp_det_irq, ak39_codec_hpdet_irq, + IRQF_DISABLED, pdev->name, codec); + if(err) + { + printk(KERN_ERR "request irq error!"); + goto err_out; + } + } + + codec->dai.ops = &ak39_codec_ops; + codec->dai.num_kcontrols = ARRAY_SIZE(codec_controls); + codec->dai.kcontrols = codec_controls; + codec->dai.num_pentries = ARRAY_SIZE(codec_pentries); + codec->dai.pentries = codec_pentries; + codec->dai.entries_private = codec; + + if(ak_codec_register(&codec->dai)) + goto err_out; + + codec->used_hp_mute = pdata->bIsHPmuteUsed; + codec->hpmute_en_val = pdata->hp_mute_enable_value; + + if(codec->used_hp_mute){ + //config hp mute gpio + codec->hpmute_gpio = pdata->hpmute_gpio; + ak_gpio_set(&(codec->hpmute_gpio)); + } + + codec->outputing = false; + + platform_set_drvdata(pdev, codec); + return 0; + +err_out: +//out_unremap_adda: + iounmap(codec->adda_cfg_base); +out_unremap_analog: + iounmap(codec->analog_ctrl_base); +out_free_codec: + kfree(codec); + return err; +} + +static int ak39_codec_remove(struct platform_device *pdev) +{ + struct ak39_codec *codec = platform_get_drvdata(pdev); + + free_irq(codec->hp_det_irq, codec); + + /*FIXME:can add the code for worker vaild when switch auto mode*/ + cancel_delayed_work_sync(&codec->d_work); + + iounmap(codec->analog_ctrl_base); + iounmap(codec->adda_cfg_base); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver ak39_codec_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "ak39-codec", + }, + .probe = ak39_codec_probe, + .remove = ak39_codec_remove, +}; + +static int __init ak39_codec_init(void) +{ + return platform_driver_register(&ak39_codec_driver); +} + +static void __exit ak39_codec_exit(void) +{ + platform_driver_unregister(&ak39_codec_driver); +} +module_init(ak39_codec_init); +module_exit(ak39_codec_exit); + +MODULE_AUTHOR("anyka"); +MODULE_DESCRIPTION("ak39 codec Driver"); +MODULE_LICENSE("GPL"); + diff --git a/sound/arm/ak_pcm.c b/sound/arm/ak_pcm.c new file mode 100644 index 00000000..d116f4d6 --- /dev/null +++ b/sound/arm/ak_pcm.c @@ -0,0 +1,1893 @@ +/* + * akpcm soundcard + * Copyright (c) by Anyka, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This 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 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include + +#include +#include +#include +#include +#include +#include + +//#define CONFIG_PCM_DUMP 1 +#define AK_PCM_DELAY_CLOSE_DAC + +unsigned long playback_statu; + +struct snd_akpcm { + struct snd_card *card; + struct snd_pcm *pcm; + spinlock_t mixer_lock; + struct snd_pcm_substream *playbacksubstrm; + struct snd_pcm_substream *capturesubstrm; + struct completion playbackHWDMA_completion; + struct completion captureHWDMA_completion; + u8 L2BufID_For_DAC; + u8 L2BufID_For_ADC23; + snd_pcm_uframes_t PlaybackCurrPos; + snd_pcm_uframes_t CaptureCurrPos; + unsigned long playbackStrmDMARunning;//bit[0]:strm state(running or not). bit[1]:DMA state(running or finished). bit[2]:strm state(suspend in frequest changed). + unsigned long captureStrmDMARunning;//bit[0]:strm state(running or not). bit[1]:DMA state(running or finished) + + struct tasklet_struct fetch_tasklet; + void * pp_buf_addr[2]; //Pingpong Buffer address + int playing_idx; + snd_pcm_uframes_t optimal_period_size; + int optimal_period_bytes; + bool use_optimal_period; + + struct timer_list stopoutput_work_timer; + struct tasklet_struct close_dac; + + struct ak_codec_dai *dai; + struct ak_codec_ops *ops; + +#ifdef CONFIG_CPU_FREQ + struct notifier_block freq_transition; +#endif + +//#ifdef CONFIG_SUPPORT_AEC + struct tasklet_struct capture_aec_tasklet; + struct tasklet_struct playback_aec_tasklet; + T_AEC_INPUT p_aecin; + T_AEC_BUF p_aecbufs; + T_VOID *pfilter; + unsigned char *playback_data; + unsigned char *capture_data; + unsigned char *temp; +//#endif + +#ifdef CONFIG_PCM_DUMP + void* pcmDumpDataBuffer; + bool enableDump; + unsigned long pcmDumpSize; + struct timeval pcmDumpTime; +#endif + + struct delayed_work ds_work; //delay start work +}; + +struct captureSync{ + unsigned long long adcCapture_bytes; + struct timeval tv; + unsigned int rate; + unsigned int frame_bits; +}; + +struct captureSync capSync; + +static unsigned long long dac_clock; + + +/************* + * PCM interface + *************/ +#define USE_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE) +#define USE_RATE (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_96000) + +#define CAPTURE_USE_RATE_MIN 5500 +#define CAPTURE_USE_RATE_MAX 96000 + +#define PLAYBACK_USE_RATE_MIN 5500 +#define PLAYBACK_USE_RATE_MAX 192000 + +#define CAPTURE_USE_CHANNELS_MIN 1 +#define CAPTURE_USE_CHANNELS_MAX 1 + +#define PLAYBACK_USE_CHANNELS_MIN 2 +#define PLAYBACK_USE_CHANNELS_MAX 2 + +#define akpcm_playback_buf_bytes_max (64*1024) +#define akpcm_playback_period_bytes_min 512 + +#ifdef CONFIG_SUPPORT_AEC +#define akpcm_playback_period_bytes_max 512 +#else +#define akpcm_playback_period_bytes_max 32768 +#endif + +#define akpcm_playback_period_aligned 128 +#define akpcm_playback_periods_min 4 + +#define akpcm_playback_periods_max 2048 + +#define DELAY_TIME_FOR_CLOSING_DAC (HZ * 30) + + +#define akpcm_capture_buf_bytes_max (64*1024) +#define akpcm_capture_period_bytes_min 512 +#define akpcm_capture_periods_min 4 +#define akpcm_capture_periods_max 64 + +#ifdef CONFIG_PCM_DUMP +#define akpcm_dump_data_size (2*1024*1024) +#endif + + +//#ifdef CONFIG_SUPPORT_AEC +#define AEC_NN 128 +#define AEC_TAIL (AEC_NN*10) +#define AEC_CHANNELS 1 +#define AEC_SAMPLERATE 8000 +#define AEC_BITSPERSAMPLE 16 +//#endif + + + static DEFINE_MUTEX(reboot_lock); + static struct snd_akpcm *reboot_info = NULL; + + /** + * @brief tell camera driver the audio capture samples for AV sync + * @author + * @date + * @input void + * @output void * + * @return void + */ +void *getRecordSyncSamples(void) +{ + return &capSync; +} +EXPORT_SYMBOL(getRecordSyncSamples); + +/* return the dac_clock */ +unsigned long long getPlaybackEclapseTime(void) +{ + return dac_clock; +} +EXPORT_SYMBOL(getPlaybackEclapseTime); + +void ak_close_dac_timer(unsigned long data) +{ + struct snd_akpcm *akpcm = (struct snd_akpcm *)data; + + if (akpcm->ops->playback_end) { + akpcm->ops->playback_end(akpcm->dai); + } +} + +//#ifdef CONFIG_SUPPORT_AEC +void ak37pcm_playback_aec(unsigned long data) +{ + struct snd_akpcm *ak37pcm = (struct snd_akpcm *)data; + struct snd_pcm_substream *substream = ak37pcm->playbacksubstrm; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_pcm_substream *capture_substream = ak37pcm->capturesubstrm; + struct snd_pcm_runtime *capture_runtime; + + unsigned int playback_pos; + unsigned long period_bytes = frames_to_bytes(runtime,runtime->period_size); + unsigned long buffer_bytes = frames_to_bytes(runtime,runtime->buffer_size); + + + //Check if the capture substream is opened yet + if (!capture_substream) + return; + + //FIXME: What is the relationship between substream and its belonging runtime + capture_runtime = capture_substream->runtime; + if (!capture_runtime) + return; + + //Check if the AEC library has been successfully opened. + if (runtime->rate != AEC_SAMPLERATE || period_bytes != akpcm_playback_period_bytes_min || !ak37pcm->pfilter) + return; + + //Make sure that the DMA of capturing is running + if (test_bit(0,&ak37pcm->captureStrmDMARunning) && + test_bit(1,&ak37pcm->captureStrmDMARunning)) { + + //FIXME: Figure out the position of last period that has just been finished + if (ak37pcm->PlaybackCurrPos <= 0) { + playback_pos = buffer_bytes - period_bytes; + } else { + playback_pos = ak37pcm->PlaybackCurrPos - period_bytes; + } + + //Something must go wrong. + if (playback_pos % akpcm_playback_period_bytes_min != 0 || playback_pos < 0) + printk("ak37pcm_playback_aec==>playback_pos=%d\n", playback_pos); + + /* + * FIXME: The AEC Lib just supports single channel by now. But we make a trick here by + * cheating the AEC Lib. Tha input data for the AEC Lib is actully stereo. By doing that, the + * system load could be lowered a little bit. + */ +#if 0 + short *to = ak37pcm->playback_data; + short *from = vaddr + playback_pos; + int count = period_bytes / channels / 2; + if (count != AEC_NN) + printk("ak37pcm_playback_aec==>count=%d\n", count); + for (i = 0; i < count; i++) { + to[i] = from[i * 2]; + } +#endif + /*We assume the channels is stereo basing on the truth */ + #if 0 + ak37pcm->p_aecbufs.buf_near = ak37pcm->p_aecbufs.buf_out = NULL; + ak37pcm->p_aecbufs.len_near = ak37pcm->p_aecbufs.len_out = 0; + ak37pcm->p_aecbufs.buf_far = vaddr + playback_pos; + ak37pcm->p_aecbufs.len_far = period_bytes / channels; + ret = AECLib_Control(ak37pcm->pfilter, &ak37pcm->p_aecbufs); + if (ret < 0) + printk("p=%d\n", ret); + #endif + } +} + +void ak37pcm_capture_aec(unsigned long data) +{ + struct snd_akpcm *ak37pcm = (struct snd_akpcm *)data; + struct snd_pcm_substream *substream = ak37pcm->capturesubstrm; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_pcm_substream *playback_substream = ak37pcm->playbacksubstrm; + struct snd_pcm_runtime *playback_runtime; + + //dma_addr_t vaddr = runtime->dma_area; + dma_addr_t paddr = runtime->dma_addr; + int period_bytes = frames_to_bytes(runtime,runtime->period_size); + int buffer_bytes = frames_to_bytes(runtime,runtime->buffer_size); + int capture_pos; + int ret; + + //Check if the playback substream is open + if (!playback_substream) + return; + + //FIXME: What is the relationship between substream and its belonging runtime + playback_runtime = playback_substream->runtime; + if (!playback_runtime) + return; + + /* + *Check if the AEC library has been successfully opened. + */ + if (runtime->rate != AEC_SAMPLERATE || period_bytes != akpcm_capture_period_bytes_min || !ak37pcm->pfilter) + return; + +// if (test_bit(0,&ak37pcm->playbackStrmDMARunning) && +// test_bit(1,&ak37pcm->playbackStrmDMARunning)) + { + + if (ak37pcm->CaptureCurrPos == 0) { + capture_pos = buffer_bytes - period_bytes; + } else { + capture_pos = ak37pcm->CaptureCurrPos - period_bytes; + } +#if 0 + ak37pcm->p_aecbufs.buf_near = vaddr + capture_pos; + ak37pcm->p_aecbufs.len_near = period_bytes; + ak37pcm->p_aecbufs.buf_out = ak37pcm->p_aecbufs.buf_near; + ak37pcm->p_aecbufs.len_out = ak37pcm->p_aecbufs.len_near; + ak37pcm->p_aecbufs.buf_far = NULL; + ak37pcm->p_aecbufs.len_far = 0; + if (ak37pcm->p_aecbufs.len_near != akpcm_capture_period_bytes_min) + printk("ak37pcm_capture_aec==>len_near=%d\n", period_bytes); + + ret = AECLib_Control(ak37pcm->pfilter, &ak37pcm->p_aecbufs); + if (ret < 0) + printk("c=%d\n", ret); +#endif + ak37pcm->p_aecbufs.buf_out = phys_to_virt(paddr+ak37pcm->CaptureCurrPos); + ak37pcm->p_aecbufs.len_out = akpcm_capture_period_bytes_min; + ret = AECLib_Control(ak37pcm->pfilter, &ak37pcm->p_aecbufs); + } +} +//#endif + +/** + * @brief create new mixer interface + * @author Cheng Mingjuan + * @date + * @return void + */ +static struct snd_card *cur_card; +static struct snd_pcm *cur_pcm; +static struct ak_codec_dai *cur_codec; +int ak_codec_register(struct ak_codec_dai *dai) +{ + int i, err; + struct snd_akpcm *akpcm; + struct snd_info_entry *entry; + + if (!dai) + return -1; + + if (!dai->ops) + return -1; + + if (!cur_card || !cur_pcm) { + printk("AK PCM: No card, No cur_pcm(failed)\n"); + return -1; + } + + akpcm = cur_card->private_data; + + if (cur_codec) { + printk("AK PCM: some codec has registered first(failed)\n"); + return -1; + } else { + cur_codec = dai; + akpcm->dai = dai; + akpcm->ops = dai->ops; + } + + strcpy(cur_card->mixername, "akpcm Mixer"); + + for (i = 0; i < dai->num_kcontrols; i++) { + err = snd_ctl_add(cur_card, snd_ctl_new1(&dai->kcontrols[i], dai)); + if (err < 0) + return err; + } + printk("AK PCM: create num_kcontrols=%d\n", dai->num_kcontrols); + + // register a proc file to tell user whether the chip DAC module has been fixed + for (i = 0; i < dai->num_pentries; i++) { + snd_card_proc_new (cur_card, dai->pentries[i].name, &entry); + snd_info_set_text_ops(entry, dai->entries_private, dai->pentries[i].cb); + } + + return 0; +} + +void ak_codec_ctl_event(unsigned int iface, unsigned int event, const char* ctl_name) +{ + struct snd_ctl_elem_id elem_id; + struct snd_kcontrol *ctl_switch; + + /* find the corresponding switch control */ + memset(&elem_id, 0, sizeof(elem_id)); + elem_id.iface = iface; + + strcpy(elem_id.name, ctl_name); + + ctl_switch = snd_ctl_find_id(cur_card, &elem_id); + + if (ctl_switch) + snd_ctl_notify(cur_card, event, &ctl_switch->id); +} + +void alsabuf_to_ppbuf(unsigned long data) +{ + struct snd_akpcm *akpcm = (struct snd_akpcm *)data; + struct snd_pcm_substream *substream = akpcm->playbacksubstrm; + struct snd_pcm_runtime *runtime = substream->runtime; + void *vaddr = runtime->dma_area; + snd_pcm_uframes_t avail; + int bytespersample = frames_to_bytes(runtime, 1); + int buffer_bytes = frames_to_bytes(runtime,runtime->buffer_size); + int period_bytes = akpcm->optimal_period_bytes; + int pend_bytes; + int dst_pos, cur_pos; + void *dst_addr = NULL; + + if (akpcm->playing_idx == 1) + dst_addr = akpcm->pp_buf_addr[0]; + else + dst_addr = akpcm->pp_buf_addr[1]; + + akpcm->PlaybackCurrPos += period_bytes; + if(akpcm->PlaybackCurrPos >= buffer_bytes) + { + akpcm->PlaybackCurrPos = akpcm->PlaybackCurrPos - buffer_bytes; + } + + avail = snd_pcm_playback_avail(runtime) + akpcm->optimal_period_size; + pend_bytes = (runtime->buffer_size - avail) * bytespersample; + + if (avail >= runtime->stop_threshold) { + printk("optimal=%u, original=%u, left=%u\n", (unsigned int)akpcm->optimal_period_size, + (unsigned int)runtime->period_size, (unsigned int)(runtime->buffer_size - snd_pcm_playback_avail(runtime))); + return; + } + + dst_pos = akpcm->PlaybackCurrPos + period_bytes; + cur_pos = akpcm->PlaybackCurrPos; + if (dst_pos > buffer_bytes) { + memcpy(dst_addr, vaddr + cur_pos, (buffer_bytes - cur_pos)); + memcpy(dst_addr + (buffer_bytes - cur_pos), vaddr, dst_pos - buffer_bytes); + } else { + memcpy(dst_addr, vaddr + cur_pos, period_bytes); + } + + if (pend_bytes < period_bytes) + memset(dst_addr + pend_bytes, 0, period_bytes - pend_bytes); +} + + +/* from the previous interrupt hanlder */ +void dac_exit_tasklet(unsigned long data) +{ + struct snd_akpcm *akpcm = (struct snd_akpcm *)data; + + if (akpcm->ops->dac_exit) + akpcm->ops->dac_exit(akpcm->dai); +} + +void akpcm_playback_interrupt_optimize(unsigned long data) +{ + struct snd_akpcm *akpcm = (struct snd_akpcm *)data; + struct snd_pcm_substream *substream = akpcm->playbacksubstrm; + struct snd_pcm_runtime *runtime = substream->runtime; + u8 id = akpcm->L2BufID_For_DAC; + int bytespersample = frames_to_bytes(runtime, 1); + void *pdst_addr; + + snd_pcm_period_elapsed(substream); + + dac_clock += akpcm->optimal_period_bytes / bytespersample * 1000000 / runtime->rate; + + if(test_bit(0,&playback_statu) + && snd_pcm_playback_avail(runtime) < runtime->stop_threshold)//output stream is running + { + if (akpcm->playing_idx == 0) + akpcm->playing_idx = 1; + else + akpcm->playing_idx = 0; + + pdst_addr = (void *)virt_to_phys(akpcm->pp_buf_addr[akpcm->playing_idx]); + l2_combuf_dma((unsigned long)pdst_addr, id, akpcm->optimal_period_bytes, + (l2_dma_transfer_direction_t)MEM2BUF,1); + + tasklet_schedule(&akpcm->fetch_tasklet); + } + else //output strm has been stopped + { + printk("output stream stopped\n"); + clear_bit(1,&akpcm->playbackStrmDMARunning); //DMA has finished + complete(&(akpcm->playbackHWDMA_completion)); + +#if defined(AK_PCM_DELAY_CLOSE_DAC) + /* delay to close output channel */ + akpcm->stopoutput_work_timer.expires = jiffies + DELAY_TIME_FOR_CLOSING_DAC; + add_timer(&akpcm->stopoutput_work_timer); + + //close the dac in order to eliminate the noise caused by interrupt of + // DAC data transfer + tasklet_schedule(&akpcm->close_dac); +#endif + } +} + + +/** + * @brief DMA transfer for playback + * @author Cheng Mingjuan + * @date + * @return void + */ +void akpcm_playback_interrupt(unsigned long data) +{ + struct snd_akpcm *akpcm = (struct snd_akpcm *)data; + struct snd_pcm_substream *substream = akpcm->playbacksubstrm; + struct snd_pcm_runtime *runtime = substream->runtime; + dma_addr_t paddr = runtime->dma_addr; + void *vaddr = runtime->dma_area; + + u8 id = akpcm->L2BufID_For_DAC; + unsigned long period_bytes = 0; + unsigned long buffer_bytes = 0; + int bytespersample = frames_to_bytes(runtime, 1); + snd_pcm_uframes_t avail; + unsigned long pend_bytes; + + period_bytes = frames_to_bytes(runtime,runtime->period_size); + buffer_bytes = frames_to_bytes(runtime,runtime->buffer_size); + akpcm->PlaybackCurrPos += period_bytes; + if(akpcm->PlaybackCurrPos >= buffer_bytes) + { + akpcm->PlaybackCurrPos = 0; + } + snd_pcm_period_elapsed(substream); + + dac_clock += period_bytes / bytespersample * 1000000 / runtime->rate; + avail = snd_pcm_playback_avail(runtime); + pend_bytes = (runtime->buffer_size - avail) * bytespersample; +#if 0 + int i; + unsigned short s; + + printk("_________________\n"); + for (i=0; i<64; i++) + { + s = *(unsigned short *)__va(paddr+akpcm->CaptureCurrPos+i*2); + if (s < 0x8000) + printk("%d\n", s); + else + printk("-%d\n", (unsigned short)(~s+1)); + } + printk("_________________\n"); +#endif + + +#ifdef CONFING_SUPPORT_AEC +// tasklet_schedule(&akpcm->playback_aec_tasklet); +#endif + + if(test_bit(0,&playback_statu) && avail < runtime->stop_threshold)//output stream is running + { + //printk(KERN_ERR "begin next dma"); + if (pend_bytes < period_bytes) + memset(vaddr+akpcm->PlaybackCurrPos + pend_bytes, 0, period_bytes - pend_bytes); + + //#ifdef CONFIG_SUPPORT_AEC + if( akpcm->dai->aec_flag == 1) + { + AECLib_DacInt(akpcm->pfilter, phys_to_virt(paddr+akpcm->PlaybackCurrPos), period_bytes); + } + //memcpy(akpcm->playback_data, akpcm->temp, period_bytes); + //#endif + + l2_combuf_dma(paddr+akpcm->PlaybackCurrPos, id, period_bytes, + (l2_dma_transfer_direction_t)MEM2BUF,1); + +#ifdef CONFIG_PCM_DUMP + if(akpcm->enableDump){ + unsigned char *pcmDump_dma_area=runtime->dma_area; + if((akpcm->pcmDumpSize+akpcm_playback_period_bytes_min)<=akpcm_dump_data_size){ + //printk(KERN_ERR "akpcm->dumpsize=%d,akpcm->PlaybackCurrPos=%d \n",akpcm->dumpsize,akpcm->PlaybackCurrPos); + memcpy(akpcm->pcmDumpDataBuffer+akpcm->pcmDumpSize,pcmDump_dma_area+akpcm->PlaybackCurrPos,akpcm_playback_period_bytes_min); + akpcm->pcmDumpSize+=akpcm_playback_period_bytes_min; + }else{ + printk(KERN_ERR "akpcm->enableDump false\n"); + akpcm->enableDump=false; + } + } +#endif + + } + else //output strm has been stopped + { + printk("output stream stopped\n"); + clear_bit(1,&akpcm->playbackStrmDMARunning); //DMA has finished + complete(&(akpcm->playbackHWDMA_completion)); + +#if defined(AK_PCM_DELAY_CLOSE_DAC) + /* delay to close output channel */ + akpcm->stopoutput_work_timer.expires = jiffies + DELAY_TIME_FOR_CLOSING_DAC; + add_timer(&akpcm->stopoutput_work_timer); + + //close the dac in order to eliminate the noise caused by interrupt of + // DAC data transfer + tasklet_schedule(&akpcm->close_dac); +#endif + } +} + +/** + * @brief DMA transfer for capture + * @author Cheng Mingjuan + * @date + * @return void + */ +void akpcm_capture_interrupt(unsigned long data) +{ + struct snd_akpcm *akpcm = (struct snd_akpcm *)data; + struct snd_pcm_substream *substream = akpcm->capturesubstrm; + struct snd_pcm_runtime *runtime = substream->runtime; + dma_addr_t paddr = runtime->dma_addr; + u8 id = akpcm->L2BufID_For_ADC23; + + unsigned long period_bytes = 0; + unsigned long buffer_bytes = 0; + period_bytes = frames_to_bytes(runtime,runtime->period_size); + buffer_bytes = frames_to_bytes(runtime,runtime->buffer_size); + do_gettimeofday(&capSync.tv); + capSync.adcCapture_bytes += period_bytes; + akpcm->CaptureCurrPos += period_bytes; + if(akpcm->CaptureCurrPos >= buffer_bytes) + { + akpcm->CaptureCurrPos = 0; + } + snd_pcm_period_elapsed(substream); + +//#ifdef CONFIG_SUPPORT_AEC + if( akpcm->dai->aec_flag == 1) + { + tasklet_schedule(&akpcm->capture_aec_tasklet); + } +//#endif + if(test_bit(0,&akpcm->captureStrmDMARunning)) //input stream is running + { + + //#ifdef CONFIG_SUPPORT_AEC + if( akpcm->dai->aec_flag == 1) + { + AECLib_AdcInt(akpcm->pfilter, akpcm->capture_data, akpcm_capture_period_bytes_min); + + + l2_combuf_dma(virt_to_phys(akpcm->capture_data), id, akpcm_capture_period_bytes_min, + (l2_dma_transfer_direction_t)BUF2MEM,1); + } + //#else + else + { + l2_combuf_dma(paddr+akpcm->CaptureCurrPos, id, akpcm_capture_period_bytes_min, + (l2_dma_transfer_direction_t)BUF2MEM,1); + } + //#endif + } + else //input stream has been stopped + { + printk("input stream stopped\n"); + clear_bit(1,&akpcm->captureStrmDMARunning); + complete(&(akpcm->captureHWDMA_completion)); + + /* FIXME */ + if (akpcm->ops->adc_exit) + akpcm->ops->adc_exit(akpcm->dai); + } +} + +/** + * @brief trigger callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int akpcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + dma_addr_t paddr = runtime->dma_addr; + void *vaddr = runtime->dma_area; + snd_pcm_uframes_t avail; + int avail_bytes, pend_bytes, period_bytes; + int start_pos0, start_pos1; + int bytespersample = frames_to_bytes(runtime, 1); + + void *vdst_addr0 = akpcm->pp_buf_addr[0]; + void *vdst_addr1 = akpcm->pp_buf_addr[1]; + void *pdst_addr0 = (void *)virt_to_phys(vdst_addr0); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + { + u8 id = akpcm->L2BufID_For_DAC; + +#if defined(AK_PCM_DELAY_CLOSE_DAC) + del_timer(&akpcm->stopoutput_work_timer); +#endif + set_bit(0,&playback_statu); //set bit to inform that playback stream is running + set_bit(1,&akpcm->playbackStrmDMARunning); //set bit to inform that DMA is working + init_completion(&(akpcm->playbackHWDMA_completion)); + + if (akpcm->use_optimal_period) { + avail = snd_pcm_playback_avail(runtime); + avail_bytes = avail * bytespersample; + pend_bytes = (runtime->buffer_size - avail) * bytespersample; + period_bytes = akpcm->optimal_period_bytes; + start_pos0 = akpcm->PlaybackCurrPos; + start_pos1 = akpcm->PlaybackCurrPos + period_bytes; + + if (pend_bytes >= 2 * period_bytes) { + memcpy(vdst_addr0, vaddr + start_pos0, period_bytes); + memcpy(vdst_addr1, vaddr + start_pos1, period_bytes); + } else if (pend_bytes > period_bytes) { + memcpy(vdst_addr0, vaddr + start_pos0, period_bytes); + memcpy(vdst_addr1, vaddr + start_pos1, period_bytes); + memset(vdst_addr1 + pend_bytes - period_bytes, 0, 2 * period_bytes - pend_bytes); + } else { + memcpy(vdst_addr0, vaddr + start_pos0, period_bytes); + memset(vdst_addr0 + pend_bytes, 0, period_bytes - pend_bytes); + memset(vdst_addr1, 0, period_bytes); + } + + akpcm->playing_idx = 0; + akpcm->PlaybackCurrPos += period_bytes; + l2_clr_status(id); + l2_combuf_dma((unsigned long)pdst_addr0, id, period_bytes, + (l2_dma_transfer_direction_t)MEM2BUF,1);//start dma + }else { + l2_clr_status(id); + l2_combuf_dma(paddr + akpcm->PlaybackCurrPos, id, frames_to_bytes(runtime,runtime->period_size), + (l2_dma_transfer_direction_t)MEM2BUF,1);//start dma + + } + +#ifdef CONFIG_PCM_DUMP + if(akpcm->enableDump){ + unsigned char *pcmDump_dma_area=runtime->dma_area; + if((akpcm->pcmDumpSize+akpcm_playback_period_bytes_min)<=akpcm_dump_data_size){ + //printk(KERN_ERR "akpcm->dumpsize=%d,akpcm->PlaybackCurrPos=%d \n",akpcm->dumpsize,akpcm->PlaybackCurrPos); + memcpy(akpcm->pcmDumpDataBuffer+akpcm->pcmDumpSize,pcmDump_dma_area+akpcm->PlaybackCurrPos,akpcm_playback_period_bytes_min); + akpcm->pcmDumpSize+=akpcm_playback_period_bytes_min; + }else{ + printk(KERN_ERR "akpcm->enableDump false\n"); + akpcm->enableDump=false; + } + } +#endif + return 0; + } + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + clear_bit(0,&playback_statu); //stop playback stream + return 0; + } + return -EINVAL; +} + +/** + * @brief trigger callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int akpcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); + //struct snd_pcm_runtime *runtime = substream->runtime; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + { + schedule_delayed_work(&akpcm->ds_work, msecs_to_jiffies(300)); + return 0; + } + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + clear_bit(0,&akpcm->captureStrmDMARunning); //stop capture stream + return 0; + } + return -EINVAL; +} + +static void delay_start_work(struct work_struct *work) +{ + struct snd_akpcm *akpcm = container_of(work, struct snd_akpcm, ds_work.work); + struct snd_pcm_substream *substream = akpcm->capturesubstrm; + struct snd_pcm_runtime *runtime = substream->runtime; + + dma_addr_t paddr = runtime->dma_addr; + u8 id = akpcm->L2BufID_For_ADC23; + set_bit(0,&akpcm->captureStrmDMARunning); //set bit to inform that capture stream is running + set_bit(1,&akpcm->captureStrmDMARunning); //set bit to inform that DMA is working + init_completion(&(akpcm->captureHWDMA_completion)); + l2_clr_status(id); + l2_combuf_dma(paddr, id, akpcm_capture_period_bytes_min, + (l2_dma_transfer_direction_t)BUF2MEM,1); //start dma + +} + +/** + * @brief prepare callback,open DAC, power on hp/speaker + * @author Cheng Mingjuan + * @date + * @return void + */ +static int akpcm_playback_prepare(struct snd_pcm_substream *substream) +{ + struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int bytespersample = frames_to_bytes(runtime, 1); + int last_period_bytes = akpcm->optimal_period_bytes; + + if(test_bit(1,&akpcm->playbackStrmDMARunning)) + { + wait_for_completion(&(akpcm->playbackHWDMA_completion)); + } + akpcm->PlaybackCurrPos = 0; + + if (((runtime->period_size % akpcm_playback_period_aligned) != 0) || + ((runtime->buffer_size % runtime->period_size) != 0)) { + + /*Calculate the optimal period size depending on the aligned period size*/ + if (runtime->period_size % akpcm_playback_period_aligned == 0) { + //Doesn't need changed the original period_size + akpcm->optimal_period_size = runtime->period_size; + } else { + akpcm->optimal_period_size = runtime->period_size + akpcm_playback_period_aligned + - (runtime->period_size % akpcm_playback_period_aligned); + } + akpcm->optimal_period_bytes = akpcm->optimal_period_size * bytespersample; + + /* Allocate or reallocate contigous for Pingpong Buffer */ + if ((last_period_bytes != 0) && (last_period_bytes != akpcm->optimal_period_bytes)) { + if(akpcm->pp_buf_addr[0]) + snd_free_pages(akpcm->pp_buf_addr[0], GFP_KERNEL); + + akpcm->pp_buf_addr[0] = snd_malloc_pages(akpcm->optimal_period_bytes * 2, GFP_KERNEL); + + printk(KERN_ERR"Pingpong Buffer Warning: Reallocate memory[old=%d, new=%d]\n", + last_period_bytes, akpcm->optimal_period_bytes); + } else { + if (akpcm->pp_buf_addr[0] == NULL) + akpcm->pp_buf_addr[0] = snd_malloc_pages(akpcm->optimal_period_bytes * 2, GFP_KERNEL); + } + + /* Decide whether to start Pingpong Buffer */ + if (akpcm->pp_buf_addr[0] == NULL) { + printk(KERN_ERR"Pingpong Buffer Warning: allocate memory failed!\n"); + akpcm->optimal_period_size = 0; + akpcm->optimal_period_bytes = 0; + akpcm->use_optimal_period = false; + } else { + akpcm->pp_buf_addr[1] = akpcm->pp_buf_addr[0] + akpcm->optimal_period_bytes; + akpcm->use_optimal_period = true; + } + } else { + akpcm->optimal_period_size = 0; + akpcm->optimal_period_bytes = 0; + akpcm->use_optimal_period = false; + } + + if (akpcm->use_optimal_period) { + l2_set_dma_callback(akpcm->L2BufID_For_DAC, akpcm_playback_interrupt_optimize,(unsigned long)akpcm); + }else + l2_set_dma_callback(akpcm->L2BufID_For_DAC, akpcm_playback_interrupt,(unsigned long)akpcm); + + /* FIXME */ + if (akpcm->ops->start_to_play) { + akpcm->ops->start_to_play(akpcm->dai, runtime->channels, runtime->rate); + } + + return 0; +} + +/** + * @brief prepare callback,open ADC23, power on mic + * @author Cheng Mingjuan + * @date + * @return void + */ +static int akpcm_capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + + l2_set_dma_callback(akpcm->L2BufID_For_ADC23,akpcm_capture_interrupt,(unsigned long)akpcm); + do_gettimeofday(&capSync.tv); + capSync.adcCapture_bytes = 0; + capSync.frame_bits = runtime->frame_bits; + capSync.rate = runtime->rate; + + akpcm->CaptureCurrPos = 0; + /* FIXME */ + if (akpcm->ops->set_adc_samplerate) + capSync.rate = akpcm->ops->set_adc_samplerate(akpcm->dai, runtime->rate); + + if (akpcm->ops->set_adc_channels) + akpcm->ops->set_adc_channels(akpcm->dai, runtime->channels); + + if (akpcm->ops->adc_init) + akpcm->ops->adc_init(akpcm->dai); + + if (akpcm->ops->capture_start) + akpcm->ops->capture_start(akpcm->dai); + + + return 0; +} + +/** + * @brief pointer callback,updata ringbuffer pointer + * @author Cheng Mingjuan + * @date + * @return void + */ +static snd_pcm_uframes_t akpcm_playback_pointer(struct snd_pcm_substream *substream) +{ + struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + return(bytes_to_frames(runtime,akpcm->PlaybackCurrPos)); //updata ringbuffer pointer +} + +/** + * @brief pointer callback, updata ringbuffer pointer + * @author Cheng Mingjuan + * @date + * @return void + */ +static snd_pcm_uframes_t akpcm_capture_pointer(struct snd_pcm_substream *substream) +{ + struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + return(bytes_to_frames(runtime,akpcm->CaptureCurrPos)); //updata ringbuffer pointer +} + +static struct snd_pcm_hardware akpcm_playback_hardware = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = USE_FORMATS, + .rates = USE_RATE, + .rate_min = PLAYBACK_USE_RATE_MIN, + .rate_max = PLAYBACK_USE_RATE_MAX, + .channels_min = PLAYBACK_USE_CHANNELS_MIN, + .channels_max = PLAYBACK_USE_CHANNELS_MAX, + .buffer_bytes_max = akpcm_playback_buf_bytes_max, + .period_bytes_min = akpcm_playback_period_bytes_min, + .period_bytes_max = akpcm_playback_period_bytes_max, + .periods_min = akpcm_playback_periods_min, + .periods_max = akpcm_playback_periods_max, + .fifo_size = 0, +}; + +static struct snd_pcm_hardware akpcm_capture_hardware = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = USE_FORMATS, + .rates = USE_RATE, + .rate_min = CAPTURE_USE_RATE_MIN, + .rate_max = CAPTURE_USE_RATE_MAX, + .channels_min = CAPTURE_USE_CHANNELS_MIN, + .channels_max = CAPTURE_USE_CHANNELS_MAX, + .buffer_bytes_max = akpcm_capture_buf_bytes_max, + .period_bytes_min = akpcm_capture_period_bytes_min, + .period_bytes_max = akpcm_capture_period_bytes_min, + .periods_min = akpcm_capture_periods_min, + .periods_max = akpcm_capture_periods_max, + .fifo_size = 0, +}; + +/** + * @brief hw_params callback, malloc ringbuffer + * @author Cheng Mingjuan + * @date + * @return void + */ +static int akpcm_playback_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); + if(BUF_NULL==akpcm->L2BufID_For_DAC) + { + akpcm->L2BufID_For_DAC = l2_alloc((l2_device_t)ADDR_DAC); //alloc l2 buffer for DAC + if(BUF_NULL==akpcm->L2BufID_For_DAC) + { + printk(KERN_ERR "alloc L2 buffer for DAC error!"); + return -ENOMEM; + } + } + return(snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params))); +} + +/** + * @brief hw_params callback, malloc ringbuffer + * @author Cheng Mingjuan + * @date + * @return void + */ +static int akpcm_capture_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); + if(BUF_NULL==akpcm->L2BufID_For_ADC23) + { + akpcm->L2BufID_For_ADC23 = l2_alloc((l2_device_t)ADDR_ADC); //alloc l2 buffer for ADC23 + if(BUF_NULL==akpcm->L2BufID_For_ADC23) + { + printk(KERN_ERR "alloc L2 buffer for DAC error!"); + return -ENOMEM; + } + } + return(snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params))); +} + +/** + * @brief hw_free callback, free ringbuffer + * @author Cheng Mingjuan + * @date + * @return void + */ +static int akpcm_playback_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); + + if(test_bit(1,&akpcm->playbackStrmDMARunning)) + { + wait_for_completion(&(akpcm->playbackHWDMA_completion)); + } + if(BUF_NULL!=akpcm->L2BufID_For_DAC) + { + /* FIXME */ +#if !defined(AK_PCM_DELAY_CLOSE_DAC) + if (akpcm->ops->playback_end) { + akpcm->ops->playback_end(akpcm->dai); + } +#endif + if (akpcm->ops->dac_exit) { + akpcm->ops->dac_exit(akpcm->dai); + } + + l2_free((l2_device_t)ADDR_DAC); + akpcm->L2BufID_For_DAC = BUF_NULL; + } + return snd_pcm_lib_free_pages(substream); +} + +/** + * @brief hw_free callback, free ringbuffer + * @author Cheng Mingjuan + * @date + * @return void + */ +static int akpcm_capture_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); + + if(test_bit(1,&akpcm->captureStrmDMARunning)) + { + wait_for_completion(&(akpcm->captureHWDMA_completion)); + } + if(BUF_NULL!=akpcm->L2BufID_For_ADC23) + { + /* FIXME */ + if (akpcm->ops->adc_exit) + akpcm->ops->adc_exit(akpcm->dai); + if (akpcm->ops->capture_end) + akpcm->ops->capture_end(akpcm->dai); + + l2_free((l2_device_t)ADDR_ADC); + akpcm->L2BufID_For_ADC23 = BUF_NULL; + } + return(snd_pcm_lib_free_pages(substream)); +} + +/** + * @brief open callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int akpcm_playback_open(struct snd_pcm_substream *substream) +{ + struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + + akpcm->playbacksubstrm = substream; + runtime->hw = akpcm_playback_hardware; + dac_clock = 0; + + if (akpcm->pp_buf_addr[0]) { + printk(KERN_ERR"akpcm_playback_open==>pp_buf_addr[0]=0x%08x, memory leak happened\n", (unsigned int)akpcm->pp_buf_addr[0]); + } else { + akpcm->pp_buf_addr[0] = NULL; + akpcm->pp_buf_addr[1] = NULL; + } + akpcm->optimal_period_size = 0; + akpcm->optimal_period_bytes = 0; + + //#ifdef CONFIG_SUPPORT_AEC + if (!akpcm->playback_data) + { + akpcm->playback_data = kmalloc(akpcm_playback_period_bytes_min * 2, GFP_KERNEL); + if (!akpcm->playback_data) + { + printk("ak37pcm_playback_open==>allocate memory for playback_data failed!\n"); + } + } + //#endif + return 0; +} + +/** + * @brief open callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int akpcm_capture_open(struct snd_pcm_substream *substream) +{ + struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + + akpcm->capturesubstrm = substream; + runtime->hw = akpcm_capture_hardware; + akpcm->CaptureCurrPos = 0; + +//#ifdef CONFIG_SUPPORT_AEC + if(!akpcm->capture_data) + { + akpcm->capture_data = kmalloc(akpcm_capture_period_bytes_min, GFP_KERNEL); + if (!akpcm->capture_data) + { + printk("akpcm_capture_open==>allocate memory for capture_data failed!\n"); + } + } + if(!akpcm->temp) + { + akpcm->temp= kmalloc(akpcm_capture_period_bytes_min, GFP_KERNEL); + if (!akpcm->temp) + { + printk("akpcm_capture_open==>allocate memory for temp failed!\n"); + } + //memset(akpcm->temp, 0x) + } +//#endif + return 0; +} + +/** + * @brief close callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int akpcm_playback_close(struct snd_pcm_substream *substream) +{ + struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); + + akpcm->playbacksubstrm=NULL; + dac_clock = 0; + + if (akpcm->pp_buf_addr[0] != NULL) { + snd_free_pages(akpcm->pp_buf_addr[0], GFP_KERNEL); + akpcm->pp_buf_addr[0] = NULL; + akpcm->pp_buf_addr[1] = NULL; + } + + //#ifdef CONFIG_SUPPORT_AEC + if (akpcm->playback_data) + { + kfree(akpcm->playback_data); + akpcm->playback_data = NULL; + } + //#endif + + return 0; +} + +/** + * @brief close callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int akpcm_capture_close(struct snd_pcm_substream *substream) +{ + struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); + capSync.adcCapture_bytes = 0; + akpcm->capturesubstrm=NULL; + return 0; +} + +/** + * @brief mmap callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int akpcm_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + return remap_pfn_range(vma, vma->vm_start, + substream->dma_buffer.addr >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, vma->vm_page_prot); +} + + +static struct snd_pcm_ops akpcm_playback_ops = { + .open = akpcm_playback_open, + .close = akpcm_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = akpcm_playback_hw_params, + .hw_free = akpcm_playback_hw_free, + .prepare = akpcm_playback_prepare, + .trigger = akpcm_playback_trigger, + .pointer = akpcm_playback_pointer, + .mmap = akpcm_pcm_mmap, +}; + +static struct snd_pcm_ops akpcm_capture_ops = { + .open = akpcm_capture_open, + .close = akpcm_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = akpcm_capture_hw_params, + .hw_free = akpcm_capture_hw_free, + .prepare = akpcm_capture_prepare, + .trigger = akpcm_capture_trigger, + .pointer = akpcm_capture_pointer, + .mmap = akpcm_pcm_mmap, +}; + + +#ifdef CONFIG_CPU_FREQ + +#define freq_to_akpcm(_n) container_of(_n, struct snd_akpcm, freq_transition) + +/** + * @brief handle cpu frequency changing + * Note: need to handle in PLL changed mode only + * @author Cao LianMing + * @date 2011-07-26 + * @parm [in] nb : the nitifier_block data struct + * @parm [in] val : CPUFREQ_PRECHANGE / CPUFREQ_POSTCHANGE + * @parm [in] data : argument + * @return int : if successful return 0, otherwise return nagative + * @retval 0 : handle successful + * @retval <0 : handle failed + */ +static int akpcm_cpufreq_transition(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct snd_akpcm *akpcm = freq_to_akpcm(nb); + struct cpufreq_freqs *freqs = data; + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; + + if (val == CPUFREQ_PRECHANGE) { + if (freqs->old_cpufreq.pll_sel != freqs->new_cpufreq.pll_sel) { + if (test_bit(0,&playback_statu)) { + printk("---- stop pcm playback\n"); + + substream = akpcm->playbacksubstrm; + /* suspend PCM data transmission */ + akpcm_playback_trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND); + set_bit(2, &akpcm->playbackStrmDMARunning); + wait_for_completion(&(akpcm->playbackHWDMA_completion)); + } else { + clear_bit(2, &akpcm->playbackStrmDMARunning); + } + } + } else if (val == CPUFREQ_POSTCHANGE) { + if (freqs->old_cpufreq.pll_sel != freqs->new_cpufreq.pll_sel) { + if (test_bit(2,&akpcm->playbackStrmDMARunning)) { + printk("---- restart pcm playback\n"); + + substream = akpcm->playbacksubstrm; + runtime = substream->runtime; + /* reconfig sample rate after PLL has been changed */ + /* FIXME */ + if (akpcm->ops->set_dac_samplerate) + akpcm->ops->set_dac_samplerate(akpcm->dai, runtime->rate); + + /* resume PCM data transmission */ + akpcm_playback_trigger(substream, SNDRV_PCM_TRIGGER_RESUME); + clear_bit(2, &akpcm->playbackStrmDMARunning); + } + } + } + + return 0; +} + +/** + * @brief register the akpcm cpufreq changing handle function to the cpufreq core + * @author Cao LianMing + * @date 2011-07-26 + * @parm [in] akpcm : the private data struct of the akpcm driver + * @return int : if register successful return 0, otherwise, return negative + * @retval 0 : successful + * @retval <0 : failed + */ +static inline int akpcm_cpufreq_register(struct snd_akpcm *akpcm) +{ + akpcm->freq_transition.notifier_call = akpcm_cpufreq_transition; + + return cpufreq_register_notifier(&akpcm->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +/** + * @brief unregister the akpcm cpufreq changing handle function from the cpufreq core + * @author Cao LianMing + * @date 2011-07-26 + * @parm [in] akpcm : the private data struct of the akpcm driver + * @return int : if unregister successful return 0, otherwise, return negative + * @retval 0 : successful + * @retval <0 : failed + */ +static inline void akpcm_cpufreq_deregister(struct snd_akpcm *akpcm) +{ + cpufreq_unregister_notifier(&akpcm->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +#else +static inline int akpcm_cpufreq_register(struct snd_akpcm *akpcm) +{ + return 0; +} + +static inline void akpcm_cpufreq_deregister(struct snd_akpcm *akpcm) +{ +} +#endif + +/** + * @brief Shutdown all ak ad/da modules + * @author Cheng JunYi + * @date 2011-09-27 + * @parm [in] in_akpcm : the private data struct of akpcm driver + * @return void + * @retval + */ +static inline void akpcm_close(struct snd_akpcm *in_akpcm) +{ + + struct snd_akpcm *akpcm = in_akpcm; + if(test_bit(1,&akpcm->playbackStrmDMARunning)) + { + wait_for_completion(&(akpcm->playbackHWDMA_completion)); + } + + if(test_bit(1,&akpcm->captureStrmDMARunning)) + { + wait_for_completion(&(akpcm->captureHWDMA_completion)); + } + + //close analog module + /* FIXME */ + if (akpcm->ops->dac_exit) + akpcm->ops->dac_exit(akpcm->dai); + if (akpcm->ops->playback_end) + akpcm->ops->playback_end(akpcm->dai); + if (akpcm->ops->adc_exit) + akpcm->ops->adc_exit(akpcm->dai); + if (akpcm->ops->capture_end) + akpcm->ops->capture_end(akpcm->dai); +} + + +/** + * @brief create new card + * @author Cheng Mingjuan + * @date + * @return void + */ +static int __devinit snd_card_akpcm_pcm(struct snd_akpcm *akpcm, int device, + int substreams) +{ + struct snd_pcm *pcm; + struct snd_pcm_substream *substream; + int err; + err = snd_pcm_new(akpcm->card, "akpcm PCM", device, + substreams, substreams, &pcm); + if (err < 0) + return err; + akpcm->pcm = pcm; + cur_pcm = pcm; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &akpcm_playback_ops); //register callbacks + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &akpcm_capture_ops);//register callbacks + pcm->private_data = akpcm; + pcm->info_flags = 0; + strcpy(pcm->name, "akpcm PCM"); + + substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, akpcm->card->dev, + akpcm_playback_buf_bytes_max, akpcm_playback_buf_bytes_max); + + substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, akpcm->card->dev, + akpcm_capture_buf_bytes_max, akpcm_capture_buf_bytes_max); + return 0; +} + +/************** + * proc interface + **************/ +#ifdef CONFIG_PCM_DUMP + +/** + * @brief Dump the ak ad/da registers to the proc file + * @author Cheng JunYi + * @date 2011-08-04 + * @parm [in] entry : snd handle of akpcm driver + * @parm [in] buffer : buffer of the proc file + * @return void + * @retval + */ +static void akpcm_registers_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) +{ + + //snd_iprintf(buffer,"ADC23 clock Register2 =0x%lu\n",REG32(RegAddr.pAddress0800 + CLK_DIV_REG2)); + + //snd_iprintf(buffer,"Multi-func Control Register1 =0x%lu\n",REG32(RegAddr.pAddress0800 + MULTIPLE_FUN_CTRL_REG1)); + + //snd_iprintf(buffer,"Analog Control Register1 =0x%lu\n",REG32(RegAddr.pAddress0800 + ANALOG_CTRL_REG1_READ)); + //snd_iprintf(buffer,"Analog Control Register2 =0x%lu\n",REG32(RegAddr.pAddress0800 + ANALOG_CTRL_REG2_READ)); + //snd_iprintf(buffer,"Analog Control Register3 =0x%lu\n",REG32(RegAddr.pAddress0800 + ANALOG_CTRL_REG3)); + //snd_iprintf(buffer,"Analog Control Register4 =0x%lu\n",REG32(RegAddr.pAddress0800 + ANALOG_CTRL_REG4)); + + //snd_iprintf(buffer,"ADC2 configuration Register =0x%lu\n",REG32(RegAddr.pAddress2002D + ADC2MODE_CFG_REG)); + //snd_iprintf(buffer,"ADC2 Data Register =0x%lu\n",REG32(RegAddr.pAddress2002D + I2S_CONFIG_REG)); + + //snd_iprintf(buffer,"DAC configuration Register =0x%lu\n",REG32(RegAddr.pAddress2002E + DAC_CONFIG_REG)); + //snd_iprintf(buffer,"CPU Data Register =0x%lu\n",REG32(RegAddr.pAddress2002E + CPU_DATA_REG)); + +} + + +/** + * @brief Handle the write operation of proc file /proc/asound/card0/pcm-dumpctrl + * Start or stop dump data to the pcm-dumpdata file + * @author Cheng JunYi + * @date 2011-08-04 + * @parm [in] entry : snd handle of akpcm driver + * @parm [in] buffer : "open" means start dump pcm data to /proc/asound/card0/pcm-dumpdata; + "close" means stop dump pcm data to /proc/asound/card0/pcm-dumpdata + * @return void + * @retval + */ +void akpcm_dumpctrl_write(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + char line[256]; + char str[256]; + struct snd_akpcm *akpcm=entry->private_data; + snd_info_get_line(buffer, line, 256); + snd_info_get_str(str, line, 256); + + if(strncmp("open", str, strlen("open"))==0){ + + memset(akpcm->pcmDumpDataBuffer,0,akpcm_dump_data_size); + akpcm->pcmDumpSize=0; + akpcm->enableDump=true; + do_gettimeofday(&akpcm->pcmDumpTime); + printk(KERN_ERR "akpcm->enableDump true\n"); + + }else if(strncmp("close", str, strlen("close"))==0){ + + akpcm->enableDump=false; + akpcm->pcmDumpSize=0; + printk(KERN_ERR "akpcm->enableDump false\n"); + + } +} + + +/** + * @brief Handle the read operation of proc file /proc/asound/card0/pcm-dumpctrl + * Show the last dump time + * @author Cheng JunYi + * @date 2011-08-04 + * @parm [in] entry : snd handle of akpcm driver + * @parm [in] buffer : buffer of the proc file, will return the last dump time + * @return void + * @retval + */ +static void akpcm_dumpctrl_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) +{ + + struct snd_akpcm *akpcm=entry->private_data; + struct tm tm_result; + + time_to_tm(akpcm->pcmDumpTime.tv_sec,0,&tm_result); + snd_iprintf(buffer, "last dump timestamp: %lu-%02d-%02d %02d:%02d:%02d UTC\n",tm_result.tm_year + 1900, tm_result.tm_mon + 1, tm_result.tm_mday, tm_result.tm_hour,tm_result.tm_min,tm_result.tm_sec); + +} + + +/** + * @brief Handle the read operation of proc file /proc/asound/card0/pcm-dumpdata + * Return the pcm data to application + * @author Cheng JunYi + * @date 2011-08-04 + * @parm [in] entry : snd handle of akpcm driver + * @parm [in] buffer : buffer of the proc file, will return the pcm data + * @return void + * @retval + */ +static long akpcm_dumpdata_read(struct snd_info_entry *entry, + void *file_private_data, + struct file *file, char __user *buf, + unsigned long count, unsigned long pos) +{ + + long size; + + struct snd_akpcm *akpcm=entry->private_data; + + size = count; + if (pos + size > akpcm_dump_data_size) + size = akpcm_dump_data_size - pos; + + if (size > 0) { + if (copy_to_user(buf,akpcm->pcmDumpDataBuffer+pos,size)) + return -EFAULT; + } + + return size; +} + + +static struct snd_info_entry_ops akpcm_dumpdata_proc_ops = { + .read = akpcm_dumpdata_read, +}; + +#endif + + +/** + * @brief Handle the reboot event + * @author Cheng JunYi + * @date 2011-09-27 + * @parm [in] nb : the nitifier_block data struct + * @parm [in] code : message + * @parm [in] unused : argument, unused + * @return int : handle successful or not + * @retval NOTIFY_DONE : handle successful + */ +static int akpcm_reboot_notify(struct notifier_block *nb, + unsigned long code, void *unused) +{ + struct snd_akpcm *akpcm; + + printk(KERN_ERR "akpcm_reboot_notify \n"); + + mutex_lock(&reboot_lock); + + if (!reboot_info) + goto out; + + akpcm = reboot_info; + akpcm_close(akpcm); + + out: + printk(KERN_ERR "akpcm_reboot_notify out\n"); + mutex_unlock(&reboot_lock); + + return NOTIFY_DONE; +} + +static struct notifier_block akpcm_reboot_notifier = { + .notifier_call = akpcm_reboot_notify, +}; + + +//#ifdef CONFIG_SUPPORT_AEC +T_pVOID akpcm_capture_aec_kmalloc(T_U32 size) +{ + return kmalloc(size, GFP_KERNEL | GFP_ATOMIC); +} +T_VOID akpcm_capture_aec_kfree(T_pVOID mem) +{ + kfree(mem); +} +//#endif + +/** + * @brief Init the device which was probed, and register a snd device + * @author Cheng MingJuan + * @date 2010-11-20 + * @parm [in] pdev : the device definition + * @return int : if init successful return 0, otherwise, return negative + * @retval 0 : successful + * @retval <0 : failed + */ +static int __devinit snd_akpcm_probe(struct platform_device *devptr) +{ + struct snd_card *card; + struct snd_akpcm *akpcm; + //struct snd_info_entry *entry; + int dev_id, err; + + memset(&capSync, 0, sizeof(capSync)); + //get analog control registers + + dev_id = devptr->id; + if (dev_id < 0) + dev_id = 0; + err = snd_card_create(dev_id, NULL, THIS_MODULE, sizeof(struct snd_akpcm), &card); + if (err < 0) + return err; + + snd_card_set_dev(card, &devptr->dev); + + akpcm = card->private_data; + akpcm->card = card; + cur_card = card; + + //init l2 buf for audio + akpcm->L2BufID_For_DAC = BUF_NULL; + akpcm->L2BufID_For_ADC23 = BUF_NULL; + + err = snd_card_akpcm_pcm(akpcm, 0, 1); + if (err < 0) + goto __out_free_card; + + //init tiimer to stop output channel + init_timer(&akpcm->stopoutput_work_timer); + akpcm->stopoutput_work_timer.function = ak_close_dac_timer; + akpcm->stopoutput_work_timer.data = (unsigned long)akpcm; + + akpcm->fetch_tasklet.func = alsabuf_to_ppbuf; + akpcm->fetch_tasklet.data = (unsigned long)akpcm; + akpcm->pp_buf_addr[0] = NULL; + + akpcm->close_dac.func = dac_exit_tasklet; + akpcm->close_dac.data = (unsigned long)akpcm; + + +//#ifdef CONFIG_SUPPORT_AEC + akpcm->capture_aec_tasklet.func = ak37pcm_capture_aec; + akpcm->capture_aec_tasklet.data = (unsigned long)akpcm; + + akpcm->playback_aec_tasklet.func = ak37pcm_playback_aec; + akpcm->playback_aec_tasklet.data = (unsigned long)akpcm; +//#endif + + INIT_DELAYED_WORK(&akpcm->ds_work, delay_start_work); + + clear_bit(0,&playback_statu); + clear_bit(1,&akpcm->playbackStrmDMARunning); + clear_bit(0,&akpcm->captureStrmDMARunning); + clear_bit(1,&akpcm->captureStrmDMARunning); + +#ifdef CONFIG_PCM_DUMP + + snd_card_proc_new (akpcm->card, "registers", &entry); + snd_info_set_text_ops(entry, akpcm, akpcm_registers_read); + + akpcm->pcmDumpDataBuffer = vmalloc(akpcm_dump_data_size); + if (!akpcm->pcmDumpDataBuffer) + return -ENOMEM; + + memset(akpcm->pcmDumpDataBuffer, 0, akpcm_dump_data_size); + + snd_card_proc_new (akpcm->card, "pcm-dumpctrl", &entry); + entry->private_data = akpcm; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.write = akpcm_dumpctrl_write; + snd_info_set_text_ops(entry, akpcm, akpcm_dumpctrl_read); + + if (! snd_card_proc_new(akpcm->card, "pcm-dumpdata", &entry)) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = akpcm; + entry->c.ops = &akpcm_dumpdata_proc_ops; + entry->size = akpcm_dump_data_size; + entry->mode = S_IFREG|S_IRUGO; + } + +#endif + + strcpy(card->driver, "akpcm"); + strcpy(card->shortname, "Ak AD/DA"); + sprintf(card->longname, "Ak ADC DAC pcm input & output module %i", dev_id + 1); + + err = akpcm_cpufreq_register(akpcm); + if (err < 0) { + printk(KERN_ERR "Failed to register cpufreq\n"); + } + + mutex_lock(&reboot_lock); + reboot_info = akpcm; + mutex_unlock(&reboot_lock); + +//#ifdef CONFIG_SUPPORT_AEC + memset(&akpcm->p_aecin, 0, sizeof(akpcm->p_aecin)); + memset(&akpcm->p_aecbufs, 0, sizeof(akpcm->p_aecbufs)); + akpcm->p_aecin.cb_fun.Malloc = akpcm_capture_aec_kmalloc; + akpcm->p_aecin.cb_fun.Free = akpcm_capture_aec_kfree; + akpcm->p_aecin.cb_fun.printf = (AEC_CALLBACK_FUN_PRINTF)printk; + //akpcm->p_aecin.m_info.m_Type = AEC_TYPE_2; + //akpcm->p_aecin.m_info.m_BitsPerSample = AEC_BITSPERSAMPLE; + akpcm->p_aecin.m_info.m_Channels = AEC_CHANNELS; + akpcm->p_aecin.m_info.m_SampleRate = AEC_SAMPLERATE; + + //akpcm->p_aecin.m_info.m_Private.m_aec.m_PreprocessEna = 0; + //akpcm->p_aecin.m_info.m_Private.m_aec.m_framelen = AEC_NN; + akpcm->p_aecin.m_info.m_Private.m_aec.m_tail = 1280; + akpcm->p_aecin.m_info.m_Private.m_aec.m_aecBypass = 0; + akpcm->p_aecin.m_info.m_Private.m_aec.m_framelen = 256; + akpcm->p_aecin.m_info.m_Private.m_aec.m_PreprocessEna = 1; + akpcm->p_aecin.m_info.m_Private.m_aec.AGClevel = 24576; + akpcm->p_aecin.m_info.m_Private.m_aec.maxGain = 3; + akpcm->p_aecin.m_info.m_Private.m_aec.DacVolume = 1024; + akpcm->p_aecin.m_info.m_Private.m_aec.AdcCutTime = 100; + akpcm->p_aecin.m_info.m_Private.m_aec.AdcMinSpeechPow = 1024; + akpcm->p_aecin.m_info.m_Private.m_aec.DacMinSpeechPow = 512; + akpcm->p_aecin.m_info.m_Private.m_aec.AdcSpeechMultiple = (T_U32)(1.8*(1<<14)); + akpcm->p_aecin.m_info.m_Private.m_aec.DacSpeechMultiple = (T_U32)(1.8*(1<<14)); + akpcm->p_aecin.m_info.m_Private.m_aec.AdcSpeechHoldTime = 900; + akpcm->p_aecin.m_info.m_Private.m_aec.DacSpeechHoldTime = 900; + akpcm->p_aecin.m_info.m_Private.m_aec.AdcConvergTime = 10000; + akpcm->p_aecin.m_info.m_Private.m_aec.DacConvergTime = 10000; + akpcm->pfilter = AECLib_Open(&akpcm->p_aecin); + if (akpcm->pfilter) { + printk("AEC function has been successfully activated!\n"); + } else { + printk("Failed to initialize AEC Lib, AEC function is closed\n"); + } +//#endif + + + + err = snd_card_register(card); + if (err == 0) { + platform_set_drvdata(devptr, card); + return 0; + } + +__out_free_card: + snd_card_free(card); + + return err; +} + +/** + * @brief De-init the device which will be removed, and unregister the snd device + * @author Cheng MingJuan + * @date 2010-11-22 + * @parm [in] pdev : the device definition + * @return int : if handle successful return 0, otherwise, return negative + * @retval 0 : successful + * @retval <0 : failed + */ +static int __devexit snd_akpcm_remove(struct platform_device *devptr) +{ + struct snd_card *card = platform_get_drvdata(devptr); + struct snd_akpcm *akpcm = card->private_data; + + akpcm_close(akpcm); + + akpcm_cpufreq_deregister(akpcm); + + del_timer(&akpcm->stopoutput_work_timer); + +//#ifdef CONFIG_SUPPORT_AEC + AECLib_Close(akpcm->pfilter); + kfree(akpcm->capture_data); + kfree(akpcm->playback_data); +//#endif + /* FIXME */ + snd_card_set_dev(card, NULL); + snd_card_free(card); + platform_set_drvdata(devptr, NULL); + return 0; +} + +#ifdef CONFIG_PM +/** + * @brief suspend callback + * @author Cheng Mingjuan + * @revisor Wu Daochao(2012-09-24) + * @date + * @return void + */ +static int snd_ak_suspend(struct platform_device *pdev, pm_message_t msg) +{ + struct snd_card *card = platform_get_drvdata(pdev); + struct snd_akpcm *akpcm = card->private_data; + + /* FIXME */ + akpcm_close(akpcm); + + return 0; +} + +/** + * @brief resume callback + * @author Cheng Mingjuan + * @date + * @return void + */ +static int snd_ak_resume(struct platform_device *pdev) +{ + return 0; +} +#else +#define snd_ak_suspend NULL +#define snd_ak_resume NULL +#endif + + +#define SND_AKPCM_DRIVER "snd_akpcm" + +static struct platform_driver snd_akpcm_driver = { + .probe = snd_akpcm_probe, + .remove = __devexit_p(snd_akpcm_remove), + .suspend = snd_ak_suspend, + .resume = snd_ak_resume, + .driver = { + .name = SND_AKPCM_DRIVER + }, +}; + +/** + * @brief register driver + * @author Cheng Mingjuan + * @date + * @return void + */ +static int __init alsa_card_akpcm_init(void) +{ + int err; + + err = platform_driver_register(&snd_akpcm_driver); + if (err < 0) + return err; + + register_reboot_notifier(&akpcm_reboot_notifier); + + return 0; +} + +/** + * @brief unregister driver + * @author Cheng Mingjuan + * @date + * @return void + */ +static void __exit alsa_card_akpcm_exit(void) +{ + platform_driver_unregister(&snd_akpcm_driver); + unregister_reboot_notifier(&akpcm_reboot_notifier); +} + +module_init(alsa_card_akpcm_init); +module_exit(alsa_card_akpcm_exit); + +MODULE_AUTHOR("Anyka, Inc."); +MODULE_DESCRIPTION("akpcm soundcard"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{ALSA,akpcm soundcard}}"); + diff --git a/sound/arm/wm8523.c b/sound/arm/wm8523.c new file mode 100644 index 00000000..e39d9d3f --- /dev/null +++ b/sound/arm/wm8523.c @@ -0,0 +1,855 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define AK_MCLK (12000000) +#define DAC_INIT 0 +#define WM8523_INIT 1 +//ADC inner module +typedef enum _ADC_INNER_MODULE_TYPE +{ + ADC_CONTROLLER = 0, + ADC_FILTER_WITH_CLK, + ADC_FILTER_WITH_HSCLK, + ADC_ANALOG, + ADC_ALL_MODULE, +}T_ADC_INNER_MODULE; + +//DAC inner module +typedef enum _DAC_INNER_MODULE_TYPE +{ + DAC_CONTROLLER = 0, + DAC_FILTER_WITH_CLK, + DAC_FILTER_WITH_HSCLK, + DAC_ANALOG, + DAC_ALL_MODULE, +}T_DAC_INNER_MODULE; + +struct ak39_codec { + struct ak_codec_dai dai; + void __iomem *analog_ctrl_base; + void __iomem *adda_cfg_base; + struct delayed_work d_work; + + + + struct gpio_info hpdet_gpio; + struct gpio_info spkrshdn_gpio; + struct gpio_info hpmute_gpio; + + int hp_det_irq; + int irq_hp_on_type; + int hp_on_value; + int playmode; + int hpmute_en_val; + + unsigned used_hp_mute:1; // whether to use hardware de-pipa or not + unsigned outputing:1; + unsigned dac_state:1; + unsigned adc2_state:1; +}; + +struct wm8523_data { + unsigned short i2s_mclk; + unsigned short aif2; + unsigned int sample_rate; + unsigned int channels; + + unsigned long status; + struct work_struct work; + struct ak_codec_dai dai; +}; + +static unsigned int g_i2s_mclk = 11289600; //default clock for 44.1khz +static struct i2c_client *wm8523_client; +static struct wm8523_data *wm8523_data_p; + +static inline struct wm8523_data *to_ak39_codec(struct ak_codec_dai *dai) +{ + return dai ? container_of(dai, struct wm8523_data, dai): NULL; +} + +static struct { + int div; + int bclk_div; +} bclk_divs[] = { + { 32, 2 }, + { 64, 3 }, + { 128, 4 }, +}; + +static unsigned short wm8523_read(u8 reg) +{ + return swab16(i2c_smbus_read_word_data(wm8523_client, reg)); +} + +static int wm8523_write(u8 reg, u16 value) +{ + return i2c_smbus_write_word_data(wm8523_client, reg, swab16(value)); +} + +/** + * @brief open ADC2 + * @author + * @date + * @param[in] void + * @return void + */ +void ak39_codec_adc2_open(struct ak_codec_dai *dai) +{ + struct ak39_codec *codec = to_ak39_codec(dai); + + //unsigned long i; + if(codec->adc2_state) + return; + + codec->adc2_state = 1; + + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= ~(1<<3); + REG32(codec->analog_ctrl_base + CLOCK_GATE_CTRL_REG) &= ~(1<<3); + + // soft reset ADC2 controller + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) |= (ADC2_SOFT_RST); + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= ~(ADC2_SOFT_RST); + +// set_cur_pmos(codec, 0x0); //must be set to 0 when ADC2 is working + + REG32(codec->analog_ctrl_base + MULTIPLE_FUN_CTRL_REG1) |= IN_DAAD_EN; //enable internal + + //disable vcm2 discharge + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~(0x1f << 4); + + //SelVcm3 + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) |= VCM3_SEL; + + //PowerOn Vcm2/3 + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~PD_VCM3; + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~PD_VCM2; + + //SetVcmNormal + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~PL_VCM2; + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) &= ~PL_VCM3; + + //EnableAdc2Limit + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) |= ADC_LIM; + + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) &= ~HOST_RD_INT_EN; + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) |= CH_POLARITY_SEL;//Receive the left channel data when the lrclk is high + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) &= ~I2S_EN; //Internal ADC MODE + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) &= ~WORD_LENGTH_MASK; + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) |= (0xF << 8); //WORD LENGTH IS 16 BIT + + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) |= (ADC2_SOFT_RST); + + //Enable adc controller + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) |= ADC2_CTRL_EN; + + //Enable l2 + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) |= ADC2MODE_L2_EN; + + //Power on adc2 conversion + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) &= ~PD_S2D; + + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) &= ~PD_ADC2; + + mdelay(1); + pd_ref_enable(codec); +} + +/** + * @brief close ADC2 + * @author + * @date + * @param[in] void + * @return void + */ +void ak39_codec_adc2_close(struct ak_codec_dai *dai) +{ + unsigned long reg_val; + struct ak39_codec *codec = to_ak39_codec(dai); + + if(!codec->adc2_state) + return; + + codec->adc2_state = 0; + + //disable l2 + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) &= ~ADC2MODE_L2_EN; + + //disable adc2 clk + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) &= ~ADC2_CLK_EN; + + //disable ADC2 interface + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) &= ~ADC2_CTRL_EN; + + //Power off adc2 + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) |= PD_ADC2; + + if((2 << 12) != (REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) & (2 << 12))) + { + if(!(REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) & DAC_CLK_EN)) + { + reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); + reg_val |= PD_VCM3; + reg_val |= PD_VCM2; + reg_val |= PL_VCM2; + //reg_val |= PD_REF; + REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; + pd_ref_disable(codec); + + } + } + REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) |= (DAC_DIV_VLD); //inhabit adc2 clock + +} + +/** + * @brief set ADC2 sample rate + * @author + * @date + * @param[in] samplerate: desired rate to be set + * @return void + */ +unsigned long ak39_codec_set_adc2_samplerate(struct ak_codec_dai *dai, + unsigned int samplerate) +{ + unsigned char mode_sel = 0; + unsigned char save_div = 0; + unsigned long out_sr = 0; + unsigned long i = 0; + struct ak39_codec *codec = to_ak39_codec(dai); + + //disable HCLK, and disable ADC interface + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) &= ~(ADC2_CTRL_EN); + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) &= ~(ADC2_HCLK_EN); + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) &= ~(ADC2_CLK_EN); + + set_adc_highspeed(codec, samplerate); + + out_sr = get_adc2_osr_div(codec, &mode_sel, &save_div, samplerate); + + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= ~(1<<27); //reset adc from adc clk + i = 0; + while(REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) & (ADC2_DIV_VLD)) + { + ++i; + if(i > 100000) + { + printk("adc set sr fail\n"); + return 0; + } + } + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) &= ~(MASK_ADC2_DIV); + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) |= ADC2_DIV(save_div); + + REG32(codec->analog_ctrl_base + FADEOUT_CTRL_REG) &= ~(1<analog_ctrl_base + FADEOUT_CTRL_REG) |= (mode_sel << ADC2_OSR_BIT); + + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) |= (ADC2_DIV_VLD); + i = 0; + while(REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) & (ADC2_DIV_VLD)) + { + ++i; + if(i > 100000) + { + printk("adc set clk reg fail\n"); + return 0; + } + } + REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) |= (1<<27); //release the reset adc from adc clk + + + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) |= (ADC2_CLK_EN);//enable ADC2 Clock + REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) |= (ADC2_HCLK_EN);//enable ADC2 Clock + REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) |= (ADC2_CTRL_EN); + return out_sr; +} + + +/** + * @brief set ADC23 channel + * @author + * @date + * @param[in] chnl: 1-mono; 2-stereo + * @return void + */ +void ak39_codec_set_adc2_channels(struct ak_codec_dai *dai, unsigned int chnl) +{ + +} + + +/** + * @brief cfg shutdown speaker GPIO + * @author + * @date + * @param[in] bOn: 1-power on; 0-power off + * @return void + */ +void ak39_codec_speak_on(struct ak39_codec *codec, bool bOn) +{ + unsigned int pin = codec->spkrshdn_gpio.pin; + ak_setpin_as_gpio(pin); + ak_gpio_cfgpin(pin, AK_GPIO_DIR_OUTPUT); + ak_gpio_setpin(pin, bOn); +} + +static unsigned int clk_get_core_pll_freq(void) +{ + unsigned int m, n, od; + unsigned int pll_cfg_val; + unsigned int core_pll_freq; + + pll_cfg_val = REG32(AK_VA_SYSCTRL + 0x08) ; + m = (pll_cfg_val & 0xfe); + n = ((pll_cfg_val & 0xf00)>>8); + od = ((pll_cfg_val & 0x3000)>>12); + + core_pll_freq = (m * 12 * 1000000) / (n * (1<status)) { + + #if 0 + wm8523_write(WM8523_DEVICE_ID, 0x0000); //reset all register of wm8523 to default value + wm8523_write(WM8523_PSCTRL1, 0x0003); //power up UN-mute + wm8523_write(WM8523_AIF_CTRL1, 0x00AA); //master mode, 16 bits, i2s format, BCLK inverted + wm8523_write(WM8523_AIF_CTRL2, wm8523_data_p->aif2); //stereo, bclk_div, lrclk_ratio, set by aif2 + wm8523_write(WM8523_DAC_CTRL3, 0); // + #endif + + REG32(AK_VA_SYSCTRL + 0x58) &= ~(0x7 << 25); //mask I2SSR and I2SST & disable inter DAC + REG32(AK_VA_SYSCTRL + 0x58) |= (1<<26); //to extern DAC via I2S slave mode + + /* disable pulldown */ + REG32(AK_VA_SYSCTRL + 0x80) |= (1 << 8); + REG32(AK_VA_SYSCTRL + 0x80) |= (1 << 9); + REG32(AK_VA_SYSCTRL + 0x80) |= (1 << 10); + + REG32(AK_VA_SYSCTRL + 0x80) |= (1 << 7); + REG32(AK_VA_SYSCTRL + 0x80) |= (1 << 12); + + + /* set share pin as i2s */ + + REG32(AK_VA_SYSCTRL + 0x74) |= (1 << 8); + REG32(AK_VA_SYSCTRL + 0x74) |= (1 << 9); + REG32(AK_VA_SYSCTRL + 0x74) |= (1 << 10); + + REG32(AK_VA_SYSCTRL + 0x74) |= (1 << 11); + REG32(AK_VA_SYSCTRL + 0x74) |= (1 << 13); + + + //must enable DAC controller clock first , or DAC_CTRL_REG1 and DAC_CTRL_REG2 can't be congigured + REG32(AK_VA_SYSCTRL + 0x1C) &= ~(1<<4); + REG32(AK_VA_SYSCTRL + 0x20) &= ~(1<<4); + + unsigned int m_DAC_CTRL_I2S_CFG_REG = ioremap(0x20110000, 0x10); + //config the dac word length , + REG32(m_DAC_CTRL_I2S_CFG_REG +0x04) &= (~0x1f<<0); + REG32(m_DAC_CTRL_I2S_CFG_REG +0x04) |= 24;//16; + + + //REG32(m_DAC_CTRL_I2S_CFG_REG) &= (~(1<<4)); //normal mode, for 24 bits I2S data + REG32(m_DAC_CTRL_I2S_CFG_REG) |= (1<<4); //memory saving mode, for 16 bits I2S data + + REG32(m_DAC_CTRL_I2S_CFG_REG) &= (~((1<<2) | (0x1<<3))); //disable MUTE + + //enalbe dac controller, enable l2 mode, Normat format + REG32(m_DAC_CTRL_I2S_CFG_REG) |= ((0x1<<0) | (1<<1)); + + //set MCLK clock + //I2SSetTransMclk(wm8523_data_p->i2s_mclk); + + //printk(KERN_ERR "wm8523_data_p->i2s_mclk= %d\n",g_i2s_mclk); + I2SSetTransMclk(g_i2s_mclk); + + REG32(AK_VA_SYSCTRL + 0x58) &= (~(3<<30)); + REG32(AK_VA_SYSCTRL + 0x58) |= (1<<31); //use I2S_MCLK = DAC_CLK + + #if 0 + printk(KERN_ERR "REG32(I2S_CFG_REG) = 0x%x\n", REG32(AK_VA_SYSCTRL + 0x58)); + printk(KERN_ERR "REG32(SHARE_PIN_CFG1_REG) = 0x%x\n", REG32(AK_VA_SYSCTRL + 0x74) ); + printk(KERN_ERR "REG32(DAC_CTRL_I2S_CFG_REG) = 0x%x\n", REG32(m_DAC_CTRL_I2S_CFG_REG+0x04)); + printk(KERN_ERR "REG32(DAC_CTRL_CFG_REG) = 0x%x\n", REG32(m_DAC_CTRL_I2S_CFG_REG)); + #endif + } +} + +static void ak39_dac_exit(void) +{ + int RegValue; + + if (test_and_clear_bit(DAC_INIT, &wm8523_data_p->status)) { + printk(KERN_ERR "ak39_dac_exit\n"); + } +#if 0 + if (test_and_clear_bit(DAC_INIT, &wm8523_data_p->status)) { + REG32(RegAddr.pAddress2002E + DAC_CONFIG_REG) &= (~L2_EN); + + RegValue = REG32(RegAddr.pAddress0800 + ANALOG_CTRL_REG1_READ); + RegValue |= (PD_OP); + RegValue |= (PD_CK); + REG32(RegAddr.pAddress0800 + ANALOG_CTRL_REG1_WRITE) = RegValue; + + /* too long */ + mdelay(10); + + REG32(RegAddr.pAddress0800 + CLK_DIV_REG2) &= (~DAC_CLK_EN); + REG32(RegAddr.pAddress0800 + CLK_DIV_REG2) |= DAC_GATE; + REG32(RegAddr.pAddress0800 + ANALOG_CTRL_REG4) &= (~DAC_EN); + } + #endif +} + +static void wm8523_playback_start(void) +{ + //if (!test_and_set_bit(WM8523_INIT, &wm8523_data_p->status)) { + printk(KERN_ERR "wm8523_playback_start\n"); + #if 1 + wm8523_write(WM8523_DEVICE_ID, 0x0000); //reset all register of wm8523 to default value + wm8523_write(WM8523_PSCTRL1, 0x0003); //power up UN-mute + wm8523_write(WM8523_AIF_CTRL1, 0x00AA); //master mode, 16 bits, i2s format, BCLK inverted + wm8523_write(WM8523_AIF_CTRL2, wm8523_data_p->aif2); //stereo, bclk_div, lrclk_ratio, set by aif2 + wm8523_write(WM8523_DAC_CTRL3, 0); // + #endif + + //do not need to set WM8523_DAC_GAINL, WM8523_DAC_GAINR,WM8523_ZERO_DETECT, use default value +#if 0 + printk(KERN_EMERG "WM8523_DEVICE_ID=%04x\n", wm8523_read(WM8523_DEVICE_ID)); + printk(KERN_EMERG "WM8523_REVISION=%04x\n", wm8523_read(WM8523_REVISION)); + printk(KERN_EMERG "WM8523_PSCTRL1=%04x\n", wm8523_read(WM8523_PSCTRL1)); + printk(KERN_EMERG "WM8523_AIF_CTRL1=%04x\n", wm8523_read(WM8523_AIF_CTRL1)); + printk(KERN_EMERG "WM8523_AIF_CTRL2=%04x\n", wm8523_read(WM8523_AIF_CTRL2)); + printk(KERN_EMERG "WM8523_DAC_CTRL3=%04x\n", wm8523_read(WM8523_DAC_CTRL3)); + printk(KERN_EMERG "WM8523_DAC_GAINL=%04x\n", wm8523_read(WM8523_DAC_GAINL)); + printk(KERN_EMERG "WM8523_DAC_GAINR=%04x\n", wm8523_read(WM8523_DAC_GAINR)); + printk(KERN_EMERG "WM8523_ZERO_DETECT=%04x\n", wm8523_read(WM8523_ZERO_DETECT)); +#endif +// } +} + +static void wm8523_playback_end(void) +{ + if (test_and_clear_bit(WM8523_INIT, &wm8523_data_p->status)) { + //set wm8523 register here + + } +} + +static void wm8523_set_samplerate(unsigned int rate) +{ + int i; + unsigned int sys_clk; + unsigned int bclk; + unsigned int aif2_sr =0; + unsigned int aif2_bclkdiv =3; + //unsigned int sys_fs_rate[] = {64, 128, 192, 256, 384, 512, 768, 1024, 1408, 1536}; + unsigned int sys_fs_rate[] = {128, 192, 256, 384, 512, 768, 1152}; + + //save i2s mclk + + if (rate == wm8523_data_p->sample_rate) + { + printk(KERN_ERR "wm8523_set_samplerate, rate not changed,rate=%d\n",rate); + return; + } + + if (!(rate % 4000)) + { + sys_clk = 12288000; + } + else if (!(rate % 11025)) + { + sys_clk = 11289600; + } + else + { + printk(KERN_ERR "wm8523_set_samplerate,invalid rate=%d\n",rate); + return; + } + + //save i2s mclk + g_i2s_mclk = sys_clk; + + for (i = 0; i < 7; i++) { + if ((sys_clk / sys_fs_rate[i]) == rate) + break; + } + + if (i == 7) + { + printk(KERN_ERR "wm8523_set_samplerate, sys_fs_rate not valid\n"); + return; + } + else + { + aif2_sr = i+1; + } + + switch (rate) { + case 8000: + case 11025: + case 12000: + case 16000: + case 22050: + case 24000: + case 32000: + case 44100: + case 48000: + case 64000: + case 88200: + case 96000: + break; + default: + printk("==>Did not support this samplerate %u\n", rate); + return; + } + + //bclk = (wm8523_data_p->channels * 16) * rate; //for 24 bits scenario + bclk = (wm8523_data_p->channels * 32) * rate; //for 24 bits scenario, set to 32 for I2S DOUT width + + if(sys_clk/4 == bclk) + { + aif2_bclkdiv = 0; + } + else if(sys_clk/8 == bclk) + { + aif2_bclkdiv = 1; + } + else if(32*rate == bclk) + { + aif2_bclkdiv = 2; + } + else if(64*rate == bclk) + { + aif2_bclkdiv = 3; + } + else if(128*rate == bclk) + { + aif2_bclkdiv = 4; + } + else + { + printk("wm8523_set_samplerate, error\n"); + return; + } + + wm8523_data_p->sample_rate = rate; + wm8523_data_p->aif2 = (aif2_bclkdiv<channels = channels; +} + +static void wm8523_start_to_play(struct ak_codec_dai *dai, + unsigned int channels, unsigned int samplerate) +{ + + + wm8523_set_channels(channels); + wm8523_set_samplerate(samplerate); + ak39_dac_init(dai); + wm8523_playback_start(); +} + + +static struct ak_codec_ops wm8523_ops = { + .dac_init = ak39_dac_init, + .dac_exit = ak39_dac_exit, + .adc_init = ak39_codec_adc2_open, + .adc_exit = ak39_codec_adc2_close, + .set_dac_samplerate = wm8523_set_samplerate, + .set_adc_samplerate = ak39_codec_set_adc2_samplerate, + .set_dac_channels = wm8523_set_channels, + .set_adc_channels = ak39_codec_set_adc2_channels, + .playback_start = wm8523_playback_start, + .playback_end = wm8523_playback_end, + .capture_start = ak39_codec_capture_start, + .capture_end = ak39_codec_capture_stop, + .start_to_play = wm8523_start_to_play, +}; + +static struct snd_kcontrol_new wm8523_controls[] = {}; + +static int wm8523_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int ret = 0; + u16 value = 0; + unsigned long clk168m; + struct wm8523_data *wm8523; + struct wm8523_platform_data *pdata = client->dev.platform_data; + + printk("===>%s: wm8523 init\n", __func__); + + + //if (!pdata) + // return -ENODEV; + + wm8523 = kzalloc(sizeof(struct wm8523_data), GFP_KERNEL); + if (!wm8523) { + printk("alloc wm8523 memory failed\n"); + return -ENOMEM; + } + + wm8523_client = client; + wm8523_data_p = wm8523; + i2c_set_clientdata(client, wm8523); + //printk("=========read ID=================\r\n"); + //mdelay(5000); + /* read chip id */ + #if 1 + value = wm8523_read(WM8523_DEVICE_ID); + printk("=========read ID,0x%x=================\r\n", value); + if (value != 0x8523) { + printk("wm8523 did not work, ID is %x\n", value); + //return -ENODEV; + } + #endif + #if 0 + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "akpcm_AnalogCtrlRegs"); + if(!res) + { + printk(KERN_ERR "no memory resource for analog_ctrl_res\n"); + err = -ENXIO; + goto out_free_codec; + } + + wm8523->analog_ctrl_base = ioremap(res->start, res->end - res->start + 1); + if (!wm8523->analog_ctrl_base) { + printk(KERN_ERR "could not remap analog_ctrl_res memory"); + err = -ENXIO; + goto out_free_codec; + } + + #endif + + + wm8523->dai.ops = &wm8523_ops; + wm8523->dai.num_kcontrols = ARRAY_SIZE(wm8523_controls); + wm8523->dai.kcontrols = wm8523_controls; + ret = ak_codec_register(&wm8523->dai); + if (ret) { + printk("register codec ops failed\n"); + goto err_out; + } + + #if 0 + /* we need a 12MHz MCLK */ + clk168m = ak_get_asic_pll_clk(); + wm8523->mclk_div = clk168m / AK_MCLK - 1; + if ((wm8523->mclk_div + 1) * AK_MCLK != clk168m) + printk("Warning: clk168m can not div to %uHz\n", AK_MCLK); + #endif + //printk("===>%s: wm8523 end\n", __func__); + return 0; + +err_out: + kfree(wm8523); + + return ret; +} + +static int wm8523_remove(struct i2c_client *client) +{ + struct wm8523_data *wm8523 = i2c_get_clientdata(client); + + kfree(wm8523); + return 0; +} + +static const struct i2c_device_id wm8523_id[] = { + { "wm8523", 0 }, + { } +}; + +static struct i2c_driver wm8523_driver = { + .probe = wm8523_probe, + .remove = wm8523_remove, + .id_table = wm8523_id, + .driver = { + .owner = THIS_MODULE, + .name = "wm8523", + }, +}; + +static int __init wm8523_init(void) +{ + printk("==============wm8523====================\r\n"); + return i2c_add_driver(&wm8523_driver); +} + +static void __exit wm8523_exit(void) +{ + i2c_del_driver(&wm8523_driver); +} + +module_init(wm8523_init); +module_exit(wm8523_exit); + +MODULE_AUTHOR("zhongjunchao@anyka.oa"); +MODULE_DESCRIPTION("WM8523 DAC Driver"); +MODULE_LICENSE("GPL"); + + + diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index c88d9741..18711a50 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1412,7 +1412,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) struct snd_soc_dai_link *dai_link; int ret, i, order; - mutex_lock(&card->mutex); + mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT); if (card->instantiated) { mutex_unlock(&card->mutex); @@ -3123,6 +3123,7 @@ int snd_soc_register_card(struct snd_soc_card *card) INIT_LIST_HEAD(&card->dapm_dirty); card->instantiated = 0; mutex_init(&card->mutex); + mutex_init(&card->dapm_mutex); mutex_lock(&client_mutex); list_add(&card->list, &card_list); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 9ae82a4e..6201fc54 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1947,6 +1947,8 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, */ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) { + int ret; + /* * Suppress early reports (eg, jacks syncing their state) to avoid * silly DAPM runs during card startup. @@ -1954,7 +1956,10 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) if (!dapm->card || !dapm->card->instantiated) return 0; - return dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); + ret = dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); + mutex_unlock(&dapm->card->dapm_mutex); + return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_sync); @@ -2118,19 +2123,21 @@ err: int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route, int num) { - int i, ret; + int i, ret = 0; + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); for (i = 0; i < num; i++) { ret = snd_soc_dapm_add_route(dapm, route); if (ret < 0) { dev_err(dapm->dev, "Failed to add route %s->%s\n", route->source, route->sink); - return ret; + break; } route++; } + mutex_unlock(&dapm->card->dapm_mutex); - return 0; + return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes); @@ -2201,12 +2208,14 @@ int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm, int i, err; int ret = 0; + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); for (i = 0; i < num; i++) { err = snd_soc_dapm_weak_route(dapm, route); if (err) ret = err; route++; } + mutex_unlock(&dapm->card->dapm_mutex); return ret; } @@ -2225,6 +2234,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) struct snd_soc_dapm_widget *w; unsigned int val; + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); + list_for_each_entry(w, &dapm->card->widgets, list) { if (w->new) @@ -2234,8 +2245,10 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) w->kcontrols = kzalloc(w->num_kcontrols * sizeof(struct snd_kcontrol *), GFP_KERNEL); - if (!w->kcontrols) + if (!w->kcontrols) { + mutex_unlock(&dapm->card->dapm_mutex); return -ENOMEM; + } } switch(w->id) { @@ -2275,6 +2288,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) } dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); + mutex_unlock(&dapm->card->dapm_mutex); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); @@ -2334,6 +2348,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; + struct snd_soc_card *card = codec->card; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; @@ -2360,7 +2375,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, /* old connection must be powered down */ connect = invert ? 1 : 0; - mutex_lock(&codec->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); change = snd_soc_test_bits(widget->codec, reg, mask, val); if (change) { @@ -2382,7 +2397,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, } } - mutex_unlock(&codec->mutex); + mutex_unlock(&card->dapm_mutex); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw); @@ -2431,6 +2446,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; + struct snd_soc_card *card = codec->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, mux, change; unsigned int mask, bitmask; @@ -2451,7 +2467,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, mask |= (bitmask - 1) << e->shift_r; } - mutex_lock(&codec->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); change = snd_soc_test_bits(widget->codec, e->reg, mask, val); if (change) { @@ -2473,7 +2489,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, } } - mutex_unlock(&codec->mutex); + mutex_unlock(&card->dapm_mutex); return change; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); @@ -2510,6 +2526,7 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; + struct snd_soc_card *card = codec->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; int change; @@ -2519,7 +2536,7 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, if (ucontrol->value.enumerated.item[0] >= e->max) return -EINVAL; - mutex_lock(&codec->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); change = widget->value != ucontrol->value.enumerated.item[0]; if (change) { @@ -2532,7 +2549,7 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, } } - mutex_unlock(&codec->mutex); + mutex_unlock(&card->dapm_mutex); return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt); @@ -2597,6 +2614,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; + struct snd_soc_card *card = codec->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, mux, change; unsigned int mask; @@ -2615,7 +2633,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, mask |= e->mask << e->shift_r; } - mutex_lock(&codec->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); change = snd_soc_test_bits(widget->codec, e->reg, mask, val); if (change) { @@ -2637,7 +2655,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, } } - mutex_unlock(&codec->mutex); + mutex_unlock(&card->dapm_mutex); return change; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double); @@ -2674,12 +2692,12 @@ int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol, struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); const char *pin = (const char *)kcontrol->private_value; - mutex_lock(&card->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); ucontrol->value.integer.value[0] = snd_soc_dapm_get_pin_status(&card->dapm, pin); - mutex_unlock(&card->mutex); + mutex_unlock(&card->dapm_mutex); return 0; } @@ -2697,17 +2715,16 @@ int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol, struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); const char *pin = (const char *)kcontrol->private_value; - mutex_lock(&card->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); if (ucontrol->value.integer.value[0]) snd_soc_dapm_enable_pin(&card->dapm, pin); else snd_soc_dapm_disable_pin(&card->dapm, pin); - snd_soc_dapm_sync(&card->dapm); - - mutex_unlock(&card->mutex); + mutex_unlock(&card->dapm_mutex); + snd_soc_dapm_sync(&card->dapm); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_pin_switch); @@ -2824,18 +2841,22 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm, { struct snd_soc_dapm_widget *w; int i; + int ret = 0; + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); for (i = 0; i < num; i++) { w = snd_soc_dapm_new_control(dapm, widget); if (!w) { dev_err(dapm->dev, "ASoC: Failed to create DAPM control %s\n", widget->name); - return -ENOMEM; + ret = -ENOMEM; + break; } widget++; } - return 0; + mutex_unlock(&dapm->card->dapm_mutex); + return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls); @@ -2989,11 +3010,11 @@ static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm, int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, struct snd_soc_dai *dai, int event) { - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_card *card = rtd->card; - mutex_lock(&codec->mutex); - soc_dapm_stream_event(&codec->dapm, stream, dai, event); - mutex_unlock(&codec->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); + soc_dapm_stream_event(&card->dapm, stream, dai, event); + mutex_unlock(&card->dapm_mutex); return 0; }